The monolith is not going anywhere this year, and it is where your incidents happen. The good news: you can instrument it richly without paying for it in latency, if you are deliberate.
Start at the seams
You do not need a span around every function. You need spans at the boundaries where time actually goes: inbound requests, database calls, cache lookups, and outbound HTTP. Those four cover most of what you will ever debug.
Watch the hot path
Instrumentation is not free. A span carries allocation, serialization, and export cost. On a function called ten thousand times a second, that adds up fast.
- Sample aggressively on hot paths — you do not need every iteration to understand the shape.
- Prefer cheap counters to full spans where you only need a rate.
- Batch and export off the request path, never inline.
Measure the overhead you added
Instrument the instrumentation. We keep a benchmark that runs the critical path with tracing on and off, so we can prove the overhead stays under budget:
traces
| where service == "monolith"
| stats self_time = sum(duration - child_duration) by span.name
| sort self_time desc
| limit 20
Rich where it counts
Spend your span budget where incidents cluster. The checkout path deserves fine-grained tracing; the admin report that runs twice a day does not. Instrumentation is a resource to allocate, not a box to check.