Field Note 47 current
Govern the contract between teams, not the code inside them
TL;DR
Draw the org so each team owns its services end to end, and put one team in front as the gateway that dispatches to the rest. Then govern exactly one thing centrally: the contract at the boundary — the schema, the identity model, the error and idempotency semantics, the quotas, the versioning and deprecation rules. That contract is the API of your organisation.
Do not mandate the libraries, frameworks, or processes a team uses to satisfy it. Ship a reference implementation — an SDK, a service template, a “paved road” — as an opt-in blueprint, not a fence. A team may adopt it wholesale, fork it, or reimplement it in another language; you don’t care, because the gateway enforces the contract at runtime, so you never need to enforce the code behind it. Verify behaviour at the boundary, not the provenance of what produced it.
That single move — central contract, sovereign internals — is what lets every team refactor, rewrite (ZFN-23), re-language, and ship on its own cadence without asking anyone’s permission. The interface is the unit of governance; everything else is local.
Context
This is Conway’s law (How Do Committees Invent?Melvin E. Conway — How Do Committees Invent? (1968)The original paper behind 'Conway's law': any organisation that designs a system will produce a design whose structure is a copy of the organisation's communication structure. The team boundaries you draw become the interfaces in your system whether you intend them to or not.melconway.com ↗) used on purpose instead of suffered by accident. The boundary between two teams becomes an interface in your system whether you design it or not — so design it, and put your governance there. The architecture you want falls out of the org you draw.
The common mistake is governing the wrong layer. A platform team mandates “everyone uses our HTTP client, our logging library, our framework, our deploy pipeline,” believing it’s manufacturing consistency. What it’s actually manufacturing is synchronous coupling dressed up as a standard. Every team now upgrades on the platform team’s cadence; a bug or CVE in the shared library freezes the whole org until one team cuts a patch and everyone redeploys in lockstep; a team that wants a different language is locked out; and the platform team becomes a bottleneck sitting in everyone’s critical path. You set out to build independent teams and you built a distributed monolith with extra steps and worse latency.
Meanwhile the thing that actually matters — the contract at the boundary — is too often left vague, “specified” by reading the other team’s source and guessing. So you end up with the worst of both worlds: rigid, mandated implementations and loose, undocumented interfaces. It should be the exact opposite. Pin the interface hard; leave the implementation free.
A gateway makes this tractable because it’s a real runtime chokepoint — the one place every request already passes through. It’s the natural home for the things that genuinely must be uniform (authn, identity propagation, quotas per ZFN-18, schema validation, telemetry conventions) precisely because you can enforce them there instead of trusting that everyone imported the right helper. It’s ZFN-34’s “single audited gateway” idea pointed inward, at your own teams.
Recommendation
Centralise the contract; decentralise everything behind it. Make the gateway enforce the contract, and make the blueprint optional.
-
Teams own their services end to end — design, build, run, on-call (ZFN-31). Ownership includes the freedom to choose how, which is the half people forget.
-
One team owns the gateway, and its product is the contract and the dispatch fabric — not the services. This is a platform-owned capability in the sense of ZFN-5: a thing the org consumes self-service, run as a product with versions, docs, and a support story. The gateway team’s customers are the other teams.
-
Write the contract down precisely, because vagueness re-couples everyone. Schema-first, with generated clients (ZFN-14); standard protocols rather than bespoke ones (ZFN-30); explicit error semantics and idempotency, with read-only and idempotent operations annotated (ZFN-19); identity propagation rules; quotas and flow-control behaviour (ZFN-13, ZFN-18). If it isn’t in the contract, it isn’t guaranteed — and if a team relies on something not in the contract, that’s the bug.
-
Enforce the contract at the gateway, at runtime. This is the keystone. You check that an endpoint honours the contract — validates against the schema, returns the agreed errors, behaves idempotently — not that it was built with your code. Idempotency is a property of the endpoint, not of which library produced it. Once enforcement lives at the boundary, mandating the implementation becomes not just unnecessary but actively counterproductive. This is the modern echo of the Amazon API mandateThe Amazon API mandate (recounted in Steve Yegge's 'Platforms Rant', 2011)Amazon's ~2002 directive: all teams expose data and functionality through service interfaces only, with no back-door access; teams may build whatever they like behind the interface. The interface is the contract; everything behind it is the team's own business.gist.github.com ↗: interface-only, no back doors, build whatever you like behind it.
-
Provide the blueprint as a paved road, not a law. Ship a reference SDK and a service template that satisfy the contract out of the box, handle the boring correctness (retries, idempotency keys, auth, telemetry), and make the right thing the easy thing. Then let teams take it or leave it — that’s a Thinnest Viable PlatformTeam Topologies — Skelton & Pais (2019)Defines four team types and three interaction modes, and argues a platform should be run as an internal product — a 'Thinnest Viable Platform' that stream-aligned teams consume self-service to reduce cognitive load. The platform earns adoption by being good, not by mandate.teamtopologies.com ↗ that earns adoption by being good. If your blueprint is genuinely better than what a team would write, most teams will use it, and the few who don’t usually have a real reason (a different language, a latency budget, a domain they understand more deeply). A blueprint is a gift; a mandate is a leash.
-
Keep the gateway thin — defend this with your life. It routes, authenticates, propagates identity, meters, validates schema, sheds load, and observes. It does not hold business logic. The moment cross-team logic starts accreting in the gateway, you’ve quietly recentralised: it becomes the scariest deploy in the building, every team is coupled to it again, and you’re back to the monolith you were trying to avoid. Boundary concerns only — keep the control-plane and data-plane split honest (ZFN-16).
Identity is the sharp, concrete example of all of this. When the gateway dispatches a request downstream, it must forward the caller’s identity to the service — delegate, never impersonate (ZFN-38) — and must never collapse every request into an anonymous “gateway” system actor (ZFN-40). That’s a contract rule, and the gateway is exactly where you enforce it. How each downstream service then consumes the propagated principal is entirely its own business. Central rule, sovereign implementation — the whole pattern in one example.
From the field
The failure mode that converted me wasn’t a gateway — it was a mandated shared library. One “blessed” client, imported everywhere, required. It was good code. Then a vulnerability landed in one of its transitive dependencies, and because every service was pinned to it, the entire estate was exposed at once and nothing could ship a fix until the platform team cut a release and every team redeployed in lockstep. A mandate had turned dozens of independent services into a single fate- sharing unit — the exact opposite of why we’d split them up. The contract those services spoke at the boundary hadn’t changed at all; we’d coupled ourselves on the implementation for no interface reason. After that I stopped mandating libraries and started mandating contracts. Same library, offered as a default instead of a law, would have let each team patch and ship on its own clock.
Consequences
Easier:
- Teams ship on their own cadence and rewrite their internals freely (ZFN-23) — adopt a new language, swap a datastore, re-architect — without a cross-team migration, because nothing outside depends on how they did it.
- The gateway team scales, because it owns a contract and a thin fabric rather than sitting in the release path of every service. Its blast radius and its workload are bounded by the boundary.
- You get uniform external behaviour — auth, quotas, identity propagation, telemetry — without uniform internals. The things that must be consistent are, and the things that needn’t be aren’t.
- Onboarding has an obvious on-ramp: the paved road gets a team to a conformant service fast, and opting off it later is cheap because the contract, not the code, is what actually holds.
Harder:
- You have to actually specify the contract — rigorously, including the error and identity and idempotency semantics people like to leave implicit. A vague contract leaks implementation details and silently re-couples teams; precision is now real, ongoing work.
- You need contract and conformance testing at the boundary instead of leaning on a shared library’s correctness. The gateway-enforced checks and a conformance suite are the price of not mandating code.
- The blueprint has to be genuinely excellent and genuinely maintained, or teams route around it and you get worse fragmentation than a mandate would have. An optional paved road only works if it’s the path of least resistance.
- You accept duplication: several teams each re-implement bits of the paved road in their own stack. That redundancy is the deliberate price of decoupling — pay it on purpose, don’t let it shame you back into a mandate.
New obligations:
- Run the contract as a versioned product: documented, with a deprecation policy, sunset timelines, and a clear owner for breaking changes — recorded as decisions (ZFN-1) so the why survives. The contract is now a first-class artefact with a lifecycle, not a wiki page.
- Hold the line on a thin gateway. “Just put this one bit of logic in the gateway, it’s easier” is how the distributed monolith grows back. Every addition to the gateway is a new thing every team is coupled to; treat it as such.
- Treat the blueprint as a maintained product with its own roadmap, not a one-time scaffold you bless and abandon. A paved road that rots becomes a dirt track everyone drives around.
References
- Conway — How Do Committees Invent? (1968) — why team boundaries become system interfaces; the case for governing them deliberately.
- Team Topologies — Skelton & Pais — platform-as-product and the Thinnest Viable Platform; a blueprint earns adoption rather than compelling it.
- The Amazon API mandate — interface-only, no back doors, build whatever you like behind it; this note is that mandate plus “and here’s a paved road.”
- ZFN-5 — the gateway as a platform-owned, self-service capability.
- ZFN-34 — the single-audited-gateway pattern, here pointed inward at your own teams.
- ZFN-14 and ZFN-30 — the contract is a schema, and a standard one where possible.
- ZFN-18, ZFN-19, ZFN-13 — boundary behaviours the gateway enforces.
- ZFN-38 and ZFN-40 — propagate identity across the dispatch; don’t impersonate, don’t go anonymous.
- ZFN-23 and ZFN-31 — what independent teams get to do once only the contract is fixed: own and rewrite their internals.
- ZFN-16 — keep the gateway a thin control/dispatch layer, not a place business logic accretes.
Changelog
- 2026-06-29: First published as a Field Note.