Concepts
Trace context propagation
Intuition
It is like a baggage tag that accompanies your suitcase on every flight connection: the tag does not change owner, it travels along with the suitcase from plane to plane, and it is only because of it that, at the end of the trip, someone can reconstruct the suitcase's whole journey even though it passed through different airlines that never spoke directly to each other.
How it works
Each span of a trace in Dapper is generated independently by the service that executes it, but for these spans, spread across dozens of different processes and machines, to later be assembled into a single tree, the trace identifier and the parent span identifier have to travel along with the request itself, not separately. It is exactly this context — which trace and which parent span a given piece of code belongs to — that Dapper propagates without requiring extra code from the developer: the instrumentation of common libraries (RPC layer, threading, control flow) carries this context across thread boundaries, asynchronous callbacks, and network calls between processes. When a service A calls a service B via RPC, the instrumented RPC library embeds A's trace id and span id in the call itself, and service B, when processing it, creates its own span using that received span id as its parent id. It is this chain of parent ids propagated from process to process that makes it possible to join, after the fact, spans created independently in different services into a single coherent causal tree.
In practice
This mechanism is the reason modern tracing systems need to propagate headers such as trace-id and span-id (parent) on every HTTP or RPC call between services — if a single hop in the chain (a proxy, a message queue, a new process) fails to pass this context along, the trace breaks into disconnected pieces and the full tree of the request can no longer be reconstructed.
Use it in a design
Practice the cases that rely on this concept in the System Design school.