Contract & Schema-First Design
Make a machine-readable schema the source of truth, design for tolerant additive evolution, and enforce compatibility with consumer-driven contract tests in CI.
The contract is a schema, not a wiki page
A contract is the promise your API makes to its consumers about shape, names, types, and behavior. The durable version of that promise is a machine-readable schema that is the single source of truth, not prose in a wiki and not whatever the code happens to return today.
Schema-first means you write the schema before or alongside the code and generate everything else from it: OpenAPI for REST, a Protobuf IDL for gRPC, or SDL for GraphQL. From that one artifact you generate typed server stubs, client SDKs in every language, request validation, and reference docs. The payoff is that the contract cannot silently drift from the implementation, because the implementation is generated from (or validated against) the contract in CI.
Disciplined naming and typing
- Resources are nouns, not verbs:
POST /orders, notPOST /createOrder. The HTTP method already carries the verb. - Casing is consistent everywhere (pick
snake_caseorcamelCaseand never mix). - Types are explicit, including nullability. A field is either always present or documented optional; "sometimes null, sometimes missing" is how clients break.
- Enums are closed sets with documented values, and unknown values are handled by tolerant readers rather than crashing.
- Units and formats are explicit:
amount_centsnotamount, ISO-8601 timestamps, currency codes.
Design for evolution
You want additive, non-breaking change to be the default: adding an optional field or a new endpoint must never break an existing consumer. The tolerant-reader pattern (ignore fields you do not recognize, do not choke on extra data) is what makes that safe on the consumer side. In Protobuf you never renumber or reuse a field tag; in GraphQL you deprecate a field rather than delete it; in REST you add fields rather than repurpose them.
A consumer team depends on your order API and follows the tolerant-reader pattern. Sort each schema change.
Enforcement: contract tests in CI
Enforcement is where teams actually get burned. Consumer-driven contract testing (Pact is the common tool) captures each consumer's real expectations as a contract and replays them against the provider in CI. If a provider is about to ship a change that violates a consumer's expectation, the build fails before deploy, not at 2am in production. This is the single highest-leverage practice for teams shipping independent services.
Interview nuance: when asked "how do you keep two teams' services compatible," the strong answer is "schema as source of truth plus consumer-driven contract tests in CI," not "we coordinate releases." Coordination does not scale past a handful of services.
Recap: make a machine-readable schema the source of truth, name and type it for tolerant additive evolution, and enforce it with consumer-driven contract tests in CI.
Apply
Your turn
The task this lesson builds to.
Design the contract for a 'create order' endpoint: resource naming, request/response schema, required vs optional fields, and how a client discovers it.
Think about
- What is the source of truth for the contract, and how is it enforced?
- How do you design for additive, non-breaking evolution?
- How do consumer-driven contract tests catch breakage in CI?
Practice
Make it stick
A second problem on the same idea, so it survives past today.
Design the contract governance for a company with 200 microservices owned by 40 teams, where the payments team's PaymentIntent message is consumed by 15 other services. Explain how you prevent one team's schema change from breaking the other 14 consumers, and how the contract stays the source of truth at that scale.
Think about
- Where do the schemas live so no team can drift from the wire contract?
- What mechanically blocks a breaking change before merge, without a meeting?
- How does a field ever actually get removed at this scale?