Agreeing the shape before the substance
A Socratic walk-through of agreeing the shape before the substance — reasoned out one step at a time, not lectured.
The question we started with
THE QUESTION #Why settle the form of a conversation before either side is able to hold it?
There is a sequence that feels natural: build the thing, see what it turns out to be, then describe it. Documentation follows implementation, because how could you accurately describe something that does not exist yet? Writing the description first looks like guessing.
Contract-first design inverts that. Two teams sit down and write the interface — every operation, every field, every error, every type — and agree to it before either has written a line that does the work. It looks like premature commitment to a design nobody has validated. So the question is what makes it pay, and the answer only becomes visible once you ask a different question: not what is easiest to write first, but what is most expensive to change later.
Reasoning it through
REASONING #Consider two teams building either side of an interface, and suppose they begin coding immediately. Each must guess the other's decisions to make progress — is the identifier a string or a number, is the timestamp in UTC with an offset, is an empty result an empty list or a 404, what happens on a duplicate submission? Each guess is embedded in code that grows around it. Where the guesses disagree, nothing reveals it until integration, which is typically the point at which both sides are nearly finished and the schedule has no slack. The bug is cheap to fix and expensive to reach.
Now ask what the same disagreement costs if it surfaces during a half-hour review of a document. It costs an edit. That gap — between an edit and a two-week integration untangling — is the whole economic case, and it is the same argument as any other shift-left practice: defect cost rises steeply with how much work has been built on top of the defect.
But there is a second effect that matters more, and it is not about defects at all. Once the interface is written down in a machine-readable form — OpenAPI for HTTP, a Protobuf .proto for RPC, AsyncAPI for messaging — it stops being prose and becomes an artefact other tools consume. From it you can generate a mock server that returns valid responses today, so the client team can build and test against something real while the server is still empty. You can generate client stubs and server skeletons so nobody hand-writes serialisation. You can generate documentation that cannot drift, because it is derived. And you can generate tests that check a running implementation against the specification.
Notice what that changes: the two teams are no longer blocked on each other at all. Sequential work has become parallel work, and the coordination that used to happen through meetings and guesses now happens through a file. That — not the defects avoided — is usually the larger win.
There is a third effect, subtler. Writing the interface first forces the design conversation to happen in the vocabulary of the consumer. What does the caller need, in what shape, with what errors it can act on? Design-by-implementation tends to leak the server's internals outward: the response mirrors the database rows, the error codes mirror the exception hierarchy, and the caller is left translating someone else's model. The spec-first ordering does not guarantee a good interface, but it puts the conversation at the right altitude at a moment when changing your mind is still free.
So what is the cost? Two real ones, and they should be stated plainly. First, you can only specify what you understand, and in genuinely exploratory work you do not yet understand it — a contract written too early gets frozen around a misconception, and the shape's cheapness to change decays exactly as the ecosystem of generated code around it grows. Second, and more insidious: a specification is only a contract if something enforces it. Left unchecked, implementations drift from the document, and a stale spec is worse than none, because it is trusted. The remedy is mechanical — generate from the spec rather than beside it, and run contract tests in continuous integration so a divergence fails a build rather than a customer.
The analogy
THE ANALOGY #Think of two builders constructing a bridge from opposite banks. They do not each start laying stone and hope to meet. They agree first on where the join will be and at what height, to the millimetre, and each then works independently against those figures. The agreement contains no bridge — it is a few numbers — yet it is what makes independent work possible at all, and it is settled before anyone knows exactly how their half will be built.
a bridge's join is surveyed against physical reality that will not move, whereas an interface's requirements shift as the product is understood — so unlike the surveyors, the two teams need a deliberate way to renegotiate and version the agreement, and treating it as immutable is a distinct failure from never having written it.
Clarifying the model
THE MODEL #Three refinements.
First, "contract" is the operative word, not "documentation". A document describes; a contract constrains, has parties, and has consequences when broken. If nothing in your pipeline fails when the implementation deviates, you have written documentation and called it a contract.
Second, the sequence matters more than the format. Whether the artefact is OpenAPI, Protobuf, JSON Schema, or AsyncAPI is a technology choice; the load-bearing decision is that the interface is agreed and machine-readable before the work, so that generation and mocking are possible. A spec generated from annotations in finished code gives you the documentation benefit and none of the parallelism benefit, because it cannot exist until the code does.
Third, there is a legitimate alternative worth naming rather than dismissing: consumer-driven contract testing, where each consumer records the expectations it actually relies on and the provider verifies against that set. It answers a different question — not "what did we agree?" but "what would I break?" — and it is particularly apt when one provider serves many consumers with divergent needs. The two compose well; they are not rivals.
A picture of it
THE PICTURE #How to readThe trunk is the specification, not the product. Both side branches begin only after the first spec commit, and each proceeds without ever reading the other's work — that independence is the payoff. The second trunk commit is a renegotiation, and notice both branches must merge it back in before finishing: the contract changing is a normal event with a defined propagation path, which is precisely what the bridge analogy could not offer.
What became clearer
WHAT CLEARED #Fixing the shape early is not a bet that you already know the answer. It is a way of moving the most expensive decision — the one both sides build on top of — to the moment when it is cheapest to change, and of converting it into an artefact machines can act on, so two teams can work at once instead of in sequence. It earns that only while the contract is enforced; unenforced, it degrades into documentation that is trusted precisely because it looks authoritative.
Where to go next
ONWARD #- Versioning and deprecation: how a contract changes without breaking existing callers.
- Consumer-driven contract testing, and when it fits better than a single central specification.
- Schema evolution rules in Protobuf and Avro, where compatibility is a checkable property.
Key terms
TERMS #| Term | What it means |
|---|---|
| Contract-first (design-first) | agreeing a machine-readable interface specification before implementing either side. |
| OpenAPI / AsyncAPI / Protobuf | specification formats for HTTP APIs, event-driven APIs, and RPC interfaces respectively. |
| Mock server | a server generated from a specification that returns valid responses, unblocking client work. |
| Contract test | an automated check that a running implementation still conforms to the agreed specification. |
| Consumer-driven contract | expectations recorded by each consumer and verified against the provider. |
Every term the collection defines is gathered in the glossary.