Engineering Practice
Contract testing: build the gate and survive a breaking change
Reading about contract testing is not the same as living through the moment can-i-deploy turns red on a Friday and tells you the rename you were about to ship would break prod. Build a real consumer/provider pair with a broker, prove the gate works, then drive a breaking field rename through it the way a senior actually ships one — without a flag day and without an outage.
Turn the unit’s mental model into a working pipeline: a consumer-driven pact, provider verification with state handlers, a broker holding the verification matrix, a can-i-deploy gate keyed to git SHAs and fed by deployment records — then exercise it by catching a breaking change and shipping it safely with expand-then-contract.
Stand up a small two-service system (a consumer and a provider, e.g. checkout-web reading prices from a pricing API) with a Pact broker, a working can-i-deploy gate, and prove end to end that the gate blocks a breaking change and that expand-then-contract ships it safely — with evidence at every step.
- A green run: the consumer pact is published, the provider verifies it (including at least one provider-state interaction seeded by a handler), and can-i-deploy exits 0 for the compatible pair.
- A red run captured as evidence: the one-PR field rename fails provider verification and can-i-deploy exits non-zero, with the broker matrix showing the unverified pair — proving the gate catches the break before deploy.
- The expand-then-contract sequence demonstrated end to end: provider with both fields ships (can-i-deploy green), consumer migrates independently, old field dropped only after the matrix shows nothing in prod reads it — no step ever required both teams to deploy in lockstep.
- A one-paragraph write-up explaining why verification checks the minimal expected shape, why can-i-deploy asks a different question than a green verification, and why record-deployment must run at rollout end.
- Wire a broker webhook (contract_requiring_verification_published) so a new consumer pact automatically triggers the provider's verification build, and show the two pipelines never call each other directly.
- Add a consumer-led change before the provider implements it and use pending (or WIP) pacts to show the provider's main build stays green while still reporting the failing result back to the consumer.
- Add a deliberately wrong-but-well-shaped value (e.g. a negative price) and show every contract still passes — then add a thin e2e smoke test that catches it, demonstrating the shape-not-semantics limit in practice.
- Stand up bi-directional contract testing for one pair (provider publishes an OpenAPI spec, broker compares the pact against it) and contrast its coverage and trust model with the classic provider-verification path you built.
This is the loop you will run on any platform of independently deployable services: the consumer records actual usage by type into a pact, the provider verifies it by replaying against the real service with state handlers seeding the data, the broker holds the verification matrix, and can-i-deploy — keyed to git SHAs and fed by honest deployment records — turns that matrix into a deploy gate. Once you have felt the gate catch a breaking rename and then shipped that same rename safely with expand-then-contract, the production version stops being theory: catch the break before deploy, evolve without a flag day, and keep a thin e2e smoke layer for the semantics contracts can never see.