All articles

Guides

Trace the Boundary, Not Every Function

More spans do not automatically produce better traces. Instrument the boundaries where latency, ownership, or failure semantics change—and make each span earn its place.

· Vight engineering · 7 min read

Tracing guide

Instrument the change in meaning.

Distributed tracing fails in two opposite ways. With too few spans, a request disappears into an unnamed block of application time. With too many, the waterfall becomes a call graph made of tiny operations that hides the boundaries an operator actually cares about.

The goal is not maximum span count. It is enough structure to assign time, failure, and ownership as a request crosses the system.

01

A span should answer an operational question

Create a span when work crosses a process, network, queue, datastore, trust boundary, or meaningful internal phase. These transitions can change who owns the latency, how failures propagate, and what remediation is available.

Do not create spans merely because a function exists. A two-microsecond mapper, getter, or validation helper rarely changes an incident hypothesis. If it matters only to a profiler, it probably belongs in a profile rather than a distributed trace.

Time

Would this span reveal where a meaningful share of request latency accumulated?

Failure

Does this operation introduce a distinct failure or retry mode?

Ownership

Does crossing this boundary change the team or system that can fix it?

02

Name operations by their stable shape

Span names become group-by dimensions. Use low-cardinality operation names such as HTTP method plus route template, database operation plus collection, or queue operation plus destination. Put request-specific values in attributes only when they are safe and useful.

Naming a span with a raw URL, user ID, order number, or SQL statement creates a new operation for every request. That makes aggregates expensive, comparisons weak, and sensitive-data mistakes more likely.

03

Record the attempt and the final outcome

Retries complicate a trace because one logical operation can contain several physical attempts. Represent the logical operation as the parent and attempts as children when each attempt has meaningful timing or a different endpoint. Record retry count and final outcome so the waterfall explains the user-visible duration.

Errors need the same discipline. Mark the span that owns the failed operation, attach a bounded exception event where useful, and avoid marking every ancestor as an independent failure. The trace should show propagation, not multiply one exception into five incidents.

04

Validate instrumentation with real questions

Take a slow request, a dependency failure, a cancellation, and an asynchronous handoff. For each one, ask whether the trace identifies the affected route, the dominant time, the first failed boundary, and the service that owns the next action.

If an answer requires reading source code to decode span names, the telemetry contract is incomplete. If the answer is buried under hundreds of internal spans, the contract is too noisy. Useful tracing lives between those extremes.

A span earns its place when it can change your answer to one of three questions: where did the time go, where did the failure begin, and who can act next?