intermediate

Spans

Represent timed units of work with names, attributes, status, parent-child relationships, and useful boundaries.

A span is a timed unit of work with a name, start/end timestamps, attributes, status, and optional parent-child links. Good span boundaries match operations engineers care about: HTTP handler, DB query, cache fetch, external API call.

					await tracer.startActiveSpan('stripe.charge', async (span) => {
  span.setAttribute('payment.provider', 'stripe');
  try {
    await stripe.charges.create(payload);
    span.setStatus({ code: SpanStatusCode.OK });
  } catch (err) {
    span.recordException(err);
    span.setStatus({ code: SpanStatusCode.ERROR });
    throw err;
  }
});
				

| Attribute | Example | |-----------|---------| | http.route | `/checkout` | | db.system | `postgresql` | | messaging.destination | `receipts` |

Avoid high-cardinality attributes (raw SQL with literals, full URLs with IDs). Use semantic conventions (OpenTelemetry) for interoperability.

On interviews: span naming, parent/child vs links for fan-out, and marking internal vs client spans.

Common pitfalls: one giant span per request hiding DB time; missing error status on failed spans; attributes that explode cardinality in the backend.

The trade-off is diagnostic detail versus trace size and indexing limits.

Checklist:

  • Name spans after operations, not classes.
  • Set error status and record exceptions.
  • Keep attributes low-cardinality.
  • Nest spans to show critical path.