A shared dashboard is not an owner
Observability talk in 2026 assumes you will instrument everything with OpenTelemetry, route it through a collector, and let the platform answer questions later. That is fine instrumentation advice. It is incomplete product advice. BuilderHelp spans a Next.js system of record, an Expo field app, a marketing site, and a Python OCR worker. When a photographed invoice fails to become a budget line, the useful question is not "do we have a dashboard." It is "which surface owns the alert, and who gets paged for the path that crosses all four."
Four surfaces make four places to look
A receipt path on BuilderHelp does not live in one process. The field app captures the photo on a jobsite phone. The OCR service extracts line items. The web API validates and writes the budget change. Marketing is out of the path, which is good — but it still has its own deploy errors that should never page the same people who own payments.
If each repo dumps errors into one shared project named "BuilderHelp," the on-call board becomes a rumor mill. A marketing 404, a cold-start timeout in OCR, and a Stripe webhook failure all look like the same product screaming. Independent deploys bought us blast-radius isolation. A single undifferentiated error stream gives that isolation back.
So the first rule is boring: every deployable surface has its own service identity. In OpenTelemetry terms that means a stable service.name per surface — builderhelp-web, builderhelp-field, builderhelp-ocr, builderhelp-marketing — plus deployment.environment.name and a build or release id. Sentry projects follow the same cut. One product, four names you can filter.
Name the path, not only the process
Service names tell you where something failed. They do not tell you which user journey broke. A span that dies in the OCR worker might be a bad photo, a model regression, or an API that started rejecting a field the worker still sends. The person who can act is different in each case.
We treat cross-surface work as a named path with an owner before it ships: receipt ingest, payment attempt, session restore, content publish. That owner is a person or a small rota, not "the platform team" and not "whoever merged last." The path gets a correlation id the clients already have — request id, job id, order id — and every hop logs it. When Sentry and the API logs disagree, that id is the join key.
This is the same instinct as putting capability gates in the API rather than in a Next.js flag. The place that sees every client is where you can attach a single story. Observability without that join is four honest local truths and one confused incident channel.
Alerts need an undo story too
Not every red chart deserves a page. Marketing deploy noise should open a ticket. A stuck PrintNode job or a failing payment webhook should wake someone. Multi-surface products fail when the severity model is copied from the easiest surface — usually the web app — and applied to paths that cannot roll back the same way.
Before we add an alert, we write what "acknowledge" means: can we disable a flag, redeploy a worker, ask crews to stay on the previous binary, or only watch and reconcile? If the only remediation is "look at the dashboard," the alert is documentation wearing a pager badge.
OpenTelemetry collectors and vendor AI summaries help after you know who owns the signal. They do not invent ownership. Routing every span into one lake and hoping an investigation agent assigns a team is how you get polished timelines with nobody responsible for the fix.
Resource attributes are the org chart
The useful part of the 2026 OpenTelemetry conversation is not another vendor bake-off. It is treating resource attributes as the contract between code and on-call. service.name identifies the deployable. A team or path attribute identifies who cares. service.version or a release sha tells you whether the binary in a framer's pocket matches the commit you just blamed.
We set those at boot from environment variables, not from a shared defaults file that every surface forgets to override. unknown_service in a trace is a failed deploy checklist item, same as a missing Sanity token. If the collector has to guess the owner with a transform rule, the producer already lost the chance to be honest.
Cardinality stays intentional. We do not put user emails or jobsite names on resources. We do put surface, environment, and release. High-cardinality identity belongs on the span or log event for the specific request, joined later by the correlation id.
What we write down before the first exporter
During scoping for a multi-surface change, we ask four questions that belong next to the kill criteria and the rollout checklist: which services emit for this path, which id ties the hops together, who is paged for a user-visible failure, and what a silent failure looks like if mobile never gets the response.
If the honest answer to "who owns this alert" is a shared dashboard or a channel nobody watches, the instrumentation plan is not done. Ship the exporter after the owner and the join key exist. The dashboard comes last, as a view over decisions you already made — not as a substitute for them.
Independent repos only stay an advantage when failure ownership is as explicit as deploy ownership. Otherwise you traded merge conflicts for an incident that bounces between four codebases while the crew in the field waits on a receipt that never landed.
Questions
- How should a multi-repo product split observability projects?
- One service identity per deployable surface — separate OpenTelemetry service.name values and usually separate error projects — plus a correlation id that follows the user path across hops. One shared "product" bucket erases the deploy boundaries you created on purpose.
- What belongs on an OpenTelemetry resource vs on a span?
- Resources carry stable identity: service name, environment, release/version, owning team or path. Spans and logs carry the per-request facts and the correlation id. Putting user- or job-level identity on the resource explodes cardinality and still does not name an owner.
- When is a dashboard enough without paging?
- When remediation is optional or delayed — marketing noise, low-urgency content drift, or a path with no user-visible failure. Page only when someone can act and the undo story is clear. If acknowledging an alert only means opening more charts, it should not page.
Sources
- OpenTelemetry — Resources
- OpenTelemetry semantic conventions — service
- BuilderHelp case study — Four surfaces that forced explicit deploy — and alert — ownership.
- Elastic — Observability trends for 2026 (OpenTelemetry) — Industry context: OTel as default instrumentation, ownership still a product problem.