API Governance

Private does not mean unimportant: an internal API with forty consumers is harder to change than a public one with four. Ownership, a catalog, the dependency graph, a compatibility matrix, per-consumer usage telemetry and an evidence-gated migration plan are how an organization evolves its contracts on purpose.

API catalog

Owner · version · consumers · SLO · documentation · deprecation status — and the dependency graph every entry sits in.

Projects API

stable
Owner
Collaboration
Version
v1
Consumers
Web BFF, Mobile BFF, Partner SDK, CLI
SLO
p95 < 250 ms · 99.95%
Documentation
Public reference + changelog
Depends on
Identity API, Notifications API
Depended on by
Web BFF, Mobile BFF, Search API
Dependency depth
2 hops to the bottom of the stack
Deprecation
none

Highest consumer count in the company; the `owner_id` field is mid-deprecation in favour of memberships with the owner role.

Dependency graph · the highlighted API and every path through it
Web appMobile appsPartner SDKLegacy Billing v1 (deprecated)Web BFFMobile BFFPayments API v2Search API v1Projects API v1Notifications API (beta)Identity API v3

Every hop is a place a breaking change or an outage can travel. Identity sits under everything, which is why it is the most conservatively evolved API in the company — see Architecture · API Gateway and Service Discovery for the runtime side of this graph.

Compatibility matrix

Which client versions work against which API versions. Plan the columns before you plan the release.

ClientProjects v1 (owner_id)Projects v1 (memberships, owner_id kept)Projects v2 (memberships only)Why
Web app (current)✓ works✓ works✓ worksReads owner from memberships since release 2026.07.
Mobile 5.x✓ works✓ works✓ worksShips the memberships reader; unknown roles rendered as "member".
Mobile 4.x✓ works✓ works✗ breaksReads owner_id directly and crashes on an unknown role value. 12 % of mobile traffic.
Partner SDK v3✓ works✓ works△ partialGenerated from the v1 schema; tolerates new fields, but its typed role enum rejects new values.
CLI✓ works✓ works✓ worksReads only ids and names; indifferent to ownership shape.

The middle column is the dual-support state: both shapes served from one source of truth. It is the only column every client passes — which is why it exists, and why it is temporary. Lesson: API Migration: Running the Change End to End →

Consumer telemetry before removal

A field is removable when its usage reads zero per consumer — not when most consumers have migrated.

Consumer-driven evolution

Can project.owner_id be removed?

replacement: memberships[].role == "owner"
Web app80% · migratedMigrated 2026.07
Mobile (4.x + 5.x)12% · still reads it4.x still reads it; 5.x does not. 4.x is 3.1 % of all traffic and declining ~0.4 pt/month.
CLI8% · migratedNever read it
Verdict: not yet

Not removable. A field is removable when its per-consumer usage reads zero for the migration window — not when most consumers have moved. Mobile 4.x can be nudged (forced-update prompt, Deprecation header, a dated sunset) but not skipped.

Migration planner

Introduce → dual support → measure → deprecate → window → remove. Each step has an exit criterion.

Step 1 · Introduce the replacement

Ship memberships with an owner role alongside owner_id. Nothing is removed; the new shape is additive and documented as the preferred one.

Exit criterion — you may not skip to the next step until

The new shape is in the docs, the SDK, and the changelog; old clients see no difference.

Each step is gated by evidence, not by calendar. Skipping "measure" is how "deprecated" becomes "we broke mobile".

Review modes

Every API in the catalog should survive all four lenses before it is called reviewed.

Review modes

Run the design through four lenses

Depth lives in Security Engineering →. Each question links to the lesson that teaches it; tick the ones your contract already answers.

Lessons behind governance