Crux Read real Pact consumer tests, a provider verification setup, and a contract-break diff, then predict the behaviour and pick the highest-leverage fix.
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 14 min
Contracts are read in code and diffs, not in prose. Read the consumer test, the provider verification wiring, and a real contract-break, then choose the fix a senior engineer would make first.
Goal
Practise the loop you run in every contract incident: read the test, predict what the generated pact will assert, and reach for the fix that keeps the gate precise — without re-coupling the two teams.
What does the pact this test generates actually assert about amount_cents, and what is the consequence?
Heads-up integer(1299) is a type matcher; 1299 is the example, not a pinned value. Pinning would over-specify and fail on benign price changes — the brittleness matchers exist to avoid.
Heads-up The pact records the interaction the consumer's client actually performed against the mock. The expect() guards the test, but the matcher in willRespondWith is what defines the recorded type constraint.
Heads-up given() records a provider-state string for setup during verification — it asserts nothing about stored values. The body matcher defines the response shape; the example value is not a data assertion.
The consumer pact above uses given('a price with id 42 exists'), but this verifier has no stateHandlers. What happens, and what is the fix?
Heads-up The verifier never writes the provider's store from pact examples; those describe the expected response. Without a handler, no data is seeded and the replayed request can't return 200.
Heads-up Interactions with states aren't skipped — they're replayed, and the precondition simply isn't met, so they fail. Skipping would silently drop coverage; Pact runs them and reports the failure.
Heads-up publishVerificationResult only controls whether the pass/fail result is sent to the broker — it does not make a failing interaction pass. The missing handler still fails the comparison.
Three deployed consumers read amount_cents. This rename ships in one PR. The provider's own unit tests are green. What does the contract gate do, and how should the rename actually be shipped?
Heads-up The provider's own tests don't know three consumers read amount_cents — that's the original e2e gap. Verification against the consumer pacts fails because the old field name is gone from the response.
Heads-up Lockstep flag-day coordination is the exact pain contracts exist to remove. expand-then-contract lets each side move independently by keeping both shapes alive during the migration window.
Heads-up Pending is for new consumer expectations a provider hasn't built yet, not for masking a genuine breaking change to fields consumers depend on. Marking real breakage pending re-creates the outage.
Snippet 4 — the can-i-deploy gate
# in the consumer's deploy pipeline, keyed to the commit being shippedpact-broker can-i-deploy \ --pacticipant checkout-web --version "$GIT_SHA" \ --to-environment production# exit 0 => deploy; non-zero => blocked# ...then, at the very end of a successful rollout:pact-broker record-deployment \ --pacticipant checkout-web --version "$GIT_SHA" \ --environment production
Quiz
Completed
Why must record-deployment run at the very end of a successful rollout, and why is can-i-deploy keyed to the git SHA rather than a semver?
Heads-up Order matters: a deployment recorded before a failed rollout makes the broker believe prod runs a version that isn't there. And the SHA's value is uniqueness per commit, not its length.
Heads-up can-i-deploy reads what's already deployed before this deploy, so recording happens after. And SHAs don't sort by time at all — their value is tracking the contract exactly.
Heads-up The matrix can't know what's deployed where from verification alone — verification is a pairwise fact. record-deployment is what tells the broker which provider version is actually in each environment.
Recap
Every contract incident is read in code: a consumer test’s type matchers define what the pact actually asserts (the type, not the example value); a missing stateHandler fails verification for a precondition the provider never seeded, not for a broken contract; a one-PR rename of a consumed field fails verification for every consumer and must ship as expand-then-contract; and the deploy gate only works when versions are git SHAs and record-deployment runs at the end of a successful rollout so the matrix reflects reality. Read the test and the diff, predict the gate’s behaviour, then fix at the level that keeps the gate both precise and decoupled.