Context Propagation Across Services
Service A calls service B over HTTP. How does B know the request belongs to an existing trace? What actually travels on the wire?
Service A calls service B over HTTP. How does B know the request belongs to an existing trace? What actually travels on the wire?
A single HTTP header called traceparent, defined by the W3C Trace Context spec. It looks like 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01: version, the 128-bit trace ID, the parent span ID, and flags such as the sampled bit. On the sending side, the OpenTelemetry SDK's propagator injects this header into the outgoing request. On the receiving side, B's instrumentation extracts it, so the server span B creates uses the same trace ID and points at A's span as its parent. There is also an optional baggage header for passing your own key-value pairs across services, like a tenant ID. The places this breaks are the interesting part: async work that spawns a thread or goroutine without carrying the context along, message queues where you have to inject the context into message headers yourself, and proxies or legacy services that strip or drop unknown headers. Mixed environments are another trap: older systems use Zipkin's B3 headers, so during a migration you run a composite propagator that reads and writes both formats. The symptom of broken propagation is always the same: traces that cut off at a service boundary, with the downstream work showing up as orphaned root spans.
Propagation is where tracing theory meets reality. Anyone can say 'the trace ID flows between services', but candidates who have actually run tracing in production know the traceparent header by name and have debugged broken traces caused by a proxy stripping headers or a queue consumer starting fresh traces. Listen for the failure modes: async boundaries, message queues, B3 versus W3C. A candidate who only describes the happy path has probably never had to fix a trace that went dark halfway through.
What the traceparent header looks like on the wire
Manually injecting context into a queue message
Composite propagator for a B3 to W3C migration
- Believing the backend stitches traces together by timestamps or IP addresses instead of an explicit header carried with the request
- Forgetting that queues, cron handoffs, and background jobs need manual context injection because there is no HTTP request to carry the header
- Putting sensitive or bulky data in baggage without realizing it is forwarded to every downstream service on every call
- A trace consistently ends at one specific service and the downstream spans show up as separate root traces. How do you debug that?
- How would you propagate context through Kafka, where there is no HTTP request between producer and consumer?
- What is baggage useful for, and what is the risk of putting too much in it?
More Observability interview questions
Also worth your time on this topic
Distributed Tracing with OpenTelemetry: From Instrumentation to Visualization
A practical checklist for adding OpenTelemetry tracing to your services, shipping spans through the Collector, and turning that data into something you can actually debug with.
90-150 minutes
Traces and Spans Explained
A request hits your API gateway, which calls two backend services, and one of those queries a database. Walk me through what that looks like as a distributed trace. What is a span, and how do spans connect to each other?
junior
Distributed Tracing with OpenTelemetry: From Instrumentation to Visualization
A walkthrough of instrumenting a real service with OpenTelemetry, running the Collector, and finding the slow span in Jaeger when a request hops across five microservices.