Instrumented isn't observable: notes from the post-sales side
The SDK is installed and the data is flowing, yet nobody opens the dashboards. Here's why observability is an adoption problem and how I approach it after the contract is signed.
There’s a moment in almost every observability rollout that I’ve learned to watch for. The SDK is installed, events are flowing, and ingestion graphs are climbing. Someone on the customer side says, “Great, we’re instrumented.”
Then, a few months later, an incident happens and the team debugs it the old way: grepping logs, asking in Slack, and reproducing it by hand. The tool they bought to answer “what broke and why?” sits in a browser tab nobody opened.
Nothing was wrong with the product or the integration. The team was instrumented, but it wasn’t observable. Closing that gap is most of what post-sales work actually is.
Deployed isn’t the same as useful
Pre-sales ends with a technical win: the proof of concept worked and the data showed up. Post-sales starts with a harder question: will this change how the team finds and fixes problems?
Those are different bars. The first is about data getting in. The second is about decisions coming out. Most of the failure modes I see come from treating the first as a proxy for the second:
- Default dashboards that nobody chose. They answer generic questions, not this team’s questions.
- Alerts without owners. Notifications go to a shared channel, everyone assumes someone else is on it, and the channel gets muted.
- Telemetry without context. Every error looks the same because nothing says which customer, release, or feature it hit.
- One champion. A single engineer set it up, understands it, and is the only person who logs in.
None of these are instrumentation problems. They’re workflow problems that happen to involve telemetry.
Start from questions, not signals
The “three pillars” framing (logs, metrics, traces) is a useful description of data types. It’s a poor way to plan a rollout, because nobody wakes up at 2 a.m. wanting “more traces.” They want an answer.
So the first working session I run isn’t about SDK configuration. It’s about questions. I ask each group who will touch the tool what it needs to answer in under five minutes:
| Who | The question they actually have |
|---|---|
| On-call engineer | Is this new, is it getting worse, and what changed? |
| Service owner | Which of my issues matter most this week? |
| Engineering manager | Are we shipping regressions, and how fast do we recover? |
| Support / CS | Is this customer’s complaint a real bug, and is it known? |
| Product | Did the new feature break anything for the users who got it? |
Every row points at specific instrumentation. “What changed?” means release tracking. “Which customer?” means user and account context. “Users who got the feature” means feature-flag context on the telemetry. Work backwards from the questions and the instrumentation plan more or less writes itself.
Context is the multiplier
Raw telemetry tells you that something happened. Context tells you who it happened to and why it matters. Of all the levers I have after a deal closes, getting teams to add a small amount of business context to their spans probably has the highest return.
With OpenTelemetry it’s a few lines at the edge of a request:
import { trace } from '@opentelemetry/api';
// Call once per request, after auth has resolved the account.
export function annotateRequest(account: Account, flags: FlagSet) {
const span = trace.getActiveSpan();
if (!span) return;
span.setAttributes({
'app.account.tier': account.tier, // 'free' | 'team' | 'enterprise'
'app.account.id': account.id, // high-cardinality: fine on spans
'app.flag.new_checkout': flags.newCheckout, // who got the new code path?
'app.region': account.dataRegion,
});
}
Release information belongs on the resource, not on every span. Set service.version once at startup, from your build’s commit SHA or version tag, so every signal from the process carries it.
Now the questions from the table have direct answers. You can filter errors to enterprise accounts, compare latency for users with and without the new checkout, and see which release introduced a regression.
A few guardrails I always bring up:
- Namespace your custom attributes (
app.*or your company prefix) so they don’t collide with OpenTelemetry semantic conventions. - High cardinality belongs on spans and events, not metric labels. An account ID on a span is useful; an account ID as a metric dimension can blow up your bill.
- Context is not a license to collect personal data. Use an internal account ID, not an email address. Decide before rollout what’s allowed in telemetry, because scrubbing it later is harder. This is where observability and data governance meet, and I’ll write more about that.
Wire signals into a workflow
Telemetry becomes useful when it’s part of a loop someone owns. The loop I help teams build looks like this:
- Detect: an alert fires on a symptom users feel, such as error rate, latency, or a failed critical flow, rather than on every exception.
- Route: ownership rules send it to the team that owns that code path, not to a shared channel.
- Triage: the owner sees release, account tier, and flag context immediately and can tell whether it’s new, growing, or high-impact.
- Diagnose: they move from the issue to the trace, then to the relevant logs or session context, without copy-pasting IDs between tools.
- Fix and verify: the fix ships in a tracked release, and the issue is confirmed resolved in that release, not just marked closed.
If any step requires tribal knowledge, the loop breaks under pressure. My goal in the first 90 days is for every step to work without me in the room.
Measure adoption, not ingestion
It’s tempting to report on how much data is flowing. Volume is a cost signal, not a value signal. These are the numbers I’d rather see in a quarterly review:
| Metric | Why it matters |
|---|---|
| Weekly active users of the tool, by team | Tells you whether it’s one champion or an engineering habit |
| % of alerts with a named owning team | Shows whether routing is real |
| % of services reporting release / version | Required before you can answer “what changed?” |
| Time from alert to first human look | The earliest and most honest signal of trust |
| Issues resolved in a tracked release | Separates fixes you verified from fixes you hope worked |
| Dashboards viewed in the last 30 days | Lets you find and delete the ones nobody uses |
When these trend the right way, the outcomes that leadership cares about, like faster recovery and fewer regressions reaching customers, tend to follow.
A starting checklist
If you’ve just rolled out an observability platform, or inherited one nobody uses, here’s where I’d start:
- Run a one-hour “questions” session with on-call, a service owner, support, and product.
- Set
service.versioneverywhere and connect deploys to releases. - Add three to five business-context attributes at the request edge, with an agreed PII policy.
- Replace “alert on every error” with a few symptom-based alerts that each have an owner.
- Build one dashboard per audience from the questions list, and archive the defaults.
- Review the adoption metrics above monthly for the first quarter.
Observability tooling is excellent now. What decides whether it pays off is everything around the tool: the questions, the context, the ownership, and the habits. That’s the part post-sales exists for, and it’s the part I find most interesting.
Next up: where observability data meets AI data governance, and why “just send everything” stops being a safe default once models are reading your telemetry.