Why architecture matters here
The architecture matters because the catalog's entire value is trust, and trust is binary. A catalog that is 95% accurate is not 95% as useful as a perfect one — it is nearly useless, because a responder cannot know which 5% is wrong, so they must verify every lookup out of band, which defeats the purpose. The one moment a catalog earns its keep is the incident, when someone needs to know the owner and runbook of an unfamiliar service right now, and that is precisely the moment a stale entry does maximum harm: it sends the page to a team that no longer owns the service, or to a person who left, and the outage lengthens while the wrong people scramble. The architecture has to make correctness structural, not aspirational.
It matters because ownership is the single most important and most perishable fact in the catalog. Teams reorganize, services transfer, people change roles — ownership drifts faster than almost any other metadata. If ownership is recorded in a central registry disconnected from the code, it rots the moment an org change happens and no one updates the wiki. Binding ownership to a descriptor in the service's own repository ties it to the artifact that already moves when ownership really changes: when a team takes over a service, they take over the repo, and the descriptor comes with it. Ownership stays correct because it is stored where ownership is actually exercised.
The architecture also matters because the dependency graph it encodes powers reasoning the catalog alone cannot. Once every service declares its upstreams and downstreams, you can answer questions that are otherwise guesswork: if this database degrades, which services are affected and who do I notify? What is the blast radius of deprecating this API? Which services have no owner at all? The dependency edges turn a flat list into a graph you can traverse for impact analysis, coordinated migrations, and incident scoping — but only if the edges are kept current, which again pushes the declarations into code where they change alongside the actual dependencies.
Finally, it matters because the catalog is the integration point that everything else hangs off. On-call routing reads it to page the right team; CI/CD reads it to know a service's tier and deployment policy; incident tooling reads it to assemble the responder list and the runbook; scorecards read it to grade maturity. Each of those integrations amplifies both the value of a correct catalog and the damage of a wrong one. Getting the architecture right — code-resident, CI-validated, single-authoritative — is what lets you safely build all of those on top; getting it wrong turns one stale field into a cascade of misrouted pages and broken automation.
The architecture: every piece explained
Top row: the service entity and its facets. The core object is the service entity — a named unit of software with a stable identifier, a tier or criticality, and a link to its source repository. Hanging off it are the facets that make it useful. Ownership names the team responsible and, crucially, the on-call rotation to page — not an individual, whose accountability evaporates when they leave, but a durable team and schedule. Dependencies record the upstream services this one calls and the downstream ones that call it, forming the edges of the estate-wide graph. Metadata carries the operational essentials: the SLO, a link to the runbook, documentation, dashboards, and whatever else a responder or a new engineer needs to orient fast.
Middle row: registration as code, validated in CI. Rather than editing a central database, each service is registered in code — a descriptor file (a catalog-info style document) committed into the service's own repository, owned and changed by the same team in the same pull requests as the code. On every change, CI validation checks that the descriptor conforms to the schema, that it names a real owning team, and that referenced dependencies and links resolve — rejecting a change that would register an orphan or a malformed entry. The validated descriptors are ingested into the catalog, which exposes a portal and API: search, the dependency graph, and scorecards for humans, and a programmatic interface for machines. Its consumers are the systems that depend on ground truth — on-call routing, CI/CD, incident response — reading the catalog rather than hard-coding ownership anywhere.
Bottom-left: freshness by construction. Because the entry lives with the code, is owned by the same team, and is gated by CI, it stays fresh as a side effect of normal development: renaming a service, changing its owner, or adding a dependency happens in a pull request that touches the descriptor, and CI refuses to merge an invalid one. Nobody has to remember to update a separate wiki, because the source of truth is the artifact they are already editing.
Bottom-right and ops: the failure the whole design guards against. A stale catalog is worse than nothing: an empty field prompts a responder to go find the answer, but a confidently wrong owner sends the page to the wrong team at 3am and is trusted precisely when it must not be. The ops strip names the disciplines that keep the map true: governing the schema so it stays consistent and usable, detecting orphaned services with no owner, running scorecards that grade and drive maturity, alarming on drift between declared and observed reality (a service deploying that is not in the catalog), and managing lifecycle so deprecated and archived services are marked and removed rather than lingering as misleading entries.
End-to-end flow
Trace the life of a catalog entry from a new service's first commit, through an incident, an org change, and eventual decommissioning.
Birth via template: an engineer scaffolds a new payment-reconciliation service from a golden software template. The template generates the repository already containing a catalog-info descriptor pre-filled with the service name, the owning team, the on-call rotation, its tier, and links to the runbook stub and dashboard. The very first CI run validates the descriptor and ingests it, so the service appears in the catalog and portal before it has even shipped. Registration is not a separate ceremony someone might forget; it is a byproduct of creating the service the standard way.
Growing dependencies: as the service is built, it starts calling a ledger API and a fraud-scoring service. The engineers add those as declared upstream dependencies in the descriptor, in the same pull requests that add the client code. CI validates that the named dependencies exist in the catalog. Now the dependency graph shows the new service's upstreams, and — symmetrically — the ledger and fraud services' downstream lists include it, so the owners of those services can see who depends on them before they change or deprecate anything.
The incident: months later, at 3am, an alert fires on the reconciliation service. The responder, who has never seen it before, opens the catalog, and in seconds has the owning team, the on-call to escalate to, the runbook, the SLO, and the dependency graph showing that the ledger API is upstream. They notice the ledger is also alerting, escalate to its owner, and follow the runbook — all driven by catalog data that is correct because it was maintained alongside the code the whole time. The catalog turns an unfamiliar page into a navigable situation.
The org change: a reorganization moves the reconciliation service from the payments team to a new settlements team. Because ownership lives in the descriptor in the repo, the transfer is a single pull request that updates the owner and on-call fields — reviewed, CI-validated, merged. The catalog, on-call routing, and scorecards all reflect the new owner immediately, because they all read the same source of truth. Contrast the alternative: a central wiki that someone was supposed to update and didn't, leaving the next incident to page a team that no longer owns the service.
Drift caught, then retirement: a scorecard sweep flags that a different service is deploying to production but has no catalog entry — an orphan the drift alarm surfaces so the platform team can chase down an owner before it becomes a 3am mystery. Finally, when the reconciliation service is superseded, its owners mark it deprecated in the descriptor, and after the grace period the entry is archived rather than deleted — its history preserved, its dependency edges removed, and its absence from the active catalog signaling clearly that it should no longer be called. The lifecycle closes cleanly instead of leaving a ghost entry that misleads future responders.