Contracts, Specs and Contract Testing
You'll learn to
- -Treat the OpenAPI document as the contract and keep it in version control and a registry
- -Tell breaking changes from safe ones, and detect them automatically in CI
- -Explain consumer-driven contract tests and why they let you delete a field safely
- -Publish a changelog and a pinning policy so clients can plan upgrades
APIs rarely break clients by crashing. They break them by changing shape: a field renamed, a type tightened, a new required parameter. The server is perfectly healthy and every client that relied on the old shape stops working. The cure is to make the shape an explicit contract that both sides can check by machine.
An Analogy: The Building Blueprint
The electrician, the plumber and the inspector all work from the same blueprint. If someone quietly moves a wall in the building without updating the blueprint, the plumbing no longer fits and nobody finds out until the pipes go in. A written API contract is the blueprint, and a contract test is the inspector who compares the building to it every time.
The Spec Is the Source of Truth
An OpenAPI document describes every path, parameter, request and response schema, status code and security scheme. Teams either write it first (design-first: review it, then generate server stubs, docs and client SDKs from it) or generate it from code (code-first). Either way the spec lives in version control, every change goes through review, and each released version is published to a registry so consumers can find it. A linter enforces the house style: naming, pagination shape, error format, required descriptions.
What Counts as a Breaking Change
- -Breaking: removing or renaming a field, changing a field's type, making an optional request field required, adding a new required request field, tightening validation, or changing what a status code means.
- -Usually safe: adding a new endpoint, adding an optional request field, or adding a new response field (clients must ignore unknown fields, and the contract should say so).
- -Careful: adding a value to an enum that clients switch on can break a strict client, so document how unknown values should be handled.
Humans miss these in review, so the pipeline compares the new spec with the last released one and fails the build on a breaking change that is not part of a new version. The block below does a tiny version of that comparison. Press Run.
Four problems are flagged: total changed type, status was removed (it looks like it was renamed to state, which is the same break), note was removed, and coupon became a new required field. The optional tip is fine. Real tools such as OpenAPI diff checkers do the same over the full schema, including nested objects and response codes.
Consumer-Driven Contract Tests
A spec says what the provider offers. It does not say which parts consumers actually use. In a consumer-driven contract test, each consumer publishes the requests it makes and the fields it reads, and the provider's pipeline replays those expectations against every build. If you delete a field, the build fails only if some consumer still reads it. That turns 'can we remove this?' from a guess into a lookup, and it runs in seconds without a shared test environment.
Spec-based tests are a cheaper cousin: in CI, call the real service and validate every response against the response schema in the spec. That catches the case where the code and the spec have drifted, in either direction.
The pictures follow a spec change from the pull request to a client that is about to lose an old version. Each stage is a place where a breaking change is stopped or announced.
A breaking change needs a conscious decision, not an accident.
A field can be removed only when no consumer reads it.
Old and new run side by side until traffic on the old one reaches zero.
Registry, Changelog and Pinning
- -Keep every released spec in a registry, so a client can see exactly what version 2024-06 promised.
- -Generate a changelog from the spec diff: added, changed, deprecated, removed. Humans add the 'why' and the migration steps.
- -Let clients pin a version (a header or a path) so an upgrade is their choice, and announce retirement with a Deprecation header (it is deprecated, and since when), a Sunset header (the date it stops working) and a changelog, well ahead.
- -Run old and new versions side by side until measured traffic on the old one reaches zero.
Make the contract check part of the merge button: lint the spec, diff it against the last release, run the consumer contracts, and validate responses. A breaking change then needs a conscious decision and a new version, not an accident.
Interview Signal is part of Pro
See a real weak answer next to a real strong one for this exact topic.
Quiz is part of Pro
Test what you just read with a short quiz, and bank the XP.
Contract Tests and the Spec Registry and Version Pinning and Changelog in the API Design Lab.
Discussion0
Join the Discussion
Sign in to leave comments, reply to others, or like insights.
No comments yet. Be the first to start the thread!