Skip to content
API Design Learn/Operating APIs in Production
Browsing as a guest. Sign in to save your progress and earn XP as you complete chapters.

Contracts, Specs and Contract Testing

5 min read

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.

Press Run: a toy spec diff that flags the four breaking changes and ignores the added optional field

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.

1The spec is reviewed like code
Author
Spec in version control
CI checks

A breaking change needs a conscious decision, not an accident.

AuthorSpec in version control- pull requestSpec in version controlCI checks- lint and diff against the last release
2Consumer contracts run against every build
Consumer contracts
Provider CI
Spec registry

A field can be removed only when no consumer reads it.

Consumer contractsProvider CI- requests and fields they readProvider CISpec registry- publish the passing spec
3Clients pin a version and see the sunset coming
Client pinned to v1
API
Version 1
Version 2

Old and new run side by side until traffic on the old one reaches zero.

Client pinned to v1API- Accept-Version: 1APIVersion 1- adds Deprecation and Sunset headersAPIVersion 2- new clients

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.

Ready to Build This?

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.

Sign In to ScaleDojo

No comments yet. Be the first to start the thread!