awesome-everything RU
↑ Back to the climb

Engineering Practice

can-i-deploy and contract versioning: the deployment-safety gate

Crux A verified pact proves two versions agree, not that this consumer is safe against what''''s running now. Version pacts by git SHA, record what''''s deployed where, and gate deploys on can-i-deploy — it reads the matrix for your version against the provider actually in the target
Your altitude — climbing toward senior
ZeroJuniorMiddleSenior
You are at senior altitude — in orbit
◷ 17 min

Priya’s checkout-web adds a feature that needs a new field, discount_cents. She updates the consumer test, the pact regenerates, and CI publishes it to the broker. Sam’s pricing provider hasn’t shipped discount_cents yet — the verification job against pricing’s latest build is green for the old interactions and the new one is still flagged pending, so nobody’s build is red. Priya’s pipeline is all green, so it deploys checkout-web to prod. Forty seconds later the error rate climbs: the pricing instance actually running in prod is three versions behind pricing’s latest, and it returns no discount_cents. The pact was verified — just not against the version that was there. Contract testing did its job and the deploy still broke prod, because the gate was asking the wrong question.

A green pact is a pairwise fact, not a deploy-safety fact

Lesson 03 left you with a broker full of verification results. Each result is a precise, narrow statement: consumer version X’s pact was verified against provider version Y, and it passed. That is genuinely useful, but it answers a question nobody is actually asking at deploy time. The deploy-time question is: if I push consumer version X to production right now, will it work against the provider version that is physically running in production? The verification you have is for some version Y — typically the provider’s latest CI build — which may be several deploys ahead of what’s in prod.

This is the gap the Hook falls through. The pact was verified against pricing’s newest build, which already had discount_cents. But prod was running an older pricing, and the broker’s individual verification results never said a word about which provider version was deployed where. A pile of green checkmarks tells you which pairs of versions have been tested together; it does not tell you whether the specific pair you’re about to create in production — new consumer, old provider — is one of the green ones. You need a tool that crosses your version against the target environment’s actual occupants, and that tool is can-i-deploy.

The matrix: every tested pair, and what’s deployed where

The broker assembles all of this into the matrix — a table of every consumer version × provider version that has ever been tested against each other, with the pass/fail of each pairing. Two things feed it. First, verification results: when a pact for consumer version X is verified by provider version Y, the broker records the cell (X, Y) → pass/fail. Second, deployment records: when you actually deploy a version into an environment, you tell the broker, so it knows that prod currently holds, say, pricing v56. can-i-deploy then does one query: take the version I want to deploy, find the versions of every integrated app currently in the target environment, and check that every required cell between them is green.

Matrix for consumer checkout-web × provider pricing. Prod currently runs pricing v56.
checkout-web versionpricing versionVerified?can-i-deploy to prod (pricing=v56)?
v22 (a1b2c3d)v56 (deployed in prod)passYes — pair is green, exit 0
v23 (e4f5a6b)v56 (deployed in prod)passYes — pair is green, exit 0
v24 (7c8d9e0) — adds discount_centsv56 (deployed in prod)no resultNo — pair unverified, exit 1, deploy blocked
v24 (7c8d9e0) — adds discount_centsv59 (latest CI, not deployed)passIrrelevant — v59 isn’t in prod

The last two rows are the whole lesson. checkout-web v24 is verified — against pricing v59, the build that already has discount_cents. But v59 isn’t deployed; prod still runs v56. can-i-deploy --pacticipant checkout-web --version 7c8d9e0 --to-environment production ignores the v59 result entirely, finds no green cell for (v24, v56), exits non-zero, and stops the pipeline. The exact deploy that paged Priya in the Hook is the deploy this gate refuses.

Versions must be the git SHA, and deployments must be recorded

The matrix only works if two pieces of bookkeeping are right. First, the version number. Pact’s strong recommendation is that a pacticipant version is the git commit SHA, or contains it (e.g. 0.0.10+76a39e5). The reason is mechanical: the version must change exactly when the pact could change. If you version by package.json semver, two different builds with different contracts can share version 2.3.0, and the broker will happily reuse a stale verification result for the wrong code. The git SHA changes on every commit, is known at deploy time, and lets you post the verification status back as a commit status. Feature branches automatically get distinct versions from main. The SHA is the only identifier that’s guaranteed to track the contract.

Second, deployment tracking. The broker can’t know what’s in prod unless you tell it, at the end of every deploy, with pact-broker record-deployment --pacticipant checkout-web --version 7c8d9e0 --environment production. There are two flavors. record-deployment is for things deployed to a known instance — services, consumers, APIs — and it automatically marks the previous version in that environment as no longer deployed, because only one version runs at a time. record-release is for artifacts you publish but don’t replace — mobile apps in a store, libraries in a registry — where many released versions coexist, so it does not undeploy the prior one. (Older setups used environment tags like prod for the same purpose; tags still work, and brokers since 2.81.0 auto-convert an environment-named tag into a deployment, but deployed/released versions are the model going forward.)

Why this works

Why call record-deployment after the deploy succeeds, not before? Because the broker’s record is meant to reflect physical reality — what is actually serving traffic — so that the next team’s can-i-deploy gets a true answer. If you record the deployment before the rollout finishes and the rollout then fails, the broker now believes prod runs a version that isn’t there, and someone else’s gate will green-light a deploy against a provider that doesn’t exist yet. Record it at the very end, when there’s no chance of failure and the old version is fully drained. The matrix is only as honest as the deployment records you feed it; a lie about what’s deployed defeats the gate as surely as skipping it.

Independent deployability is the entire payoff

Step back and notice what this buys. Without the gate, “is it safe to deploy?” can only be answered by deploying everything together in lockstep, or by booting the whole world in a shared environment — the exact N² problem the first lesson killed. With the gate, each service answers the question for itself, locally, against the recorded truth of every environment: I can deploy my new version iff every app already in the target env has a green pact with me. That is independent deployability — the ability to ship one service on its own schedule without coordinating a flight of releases — and it is the whole reason the unit exists. Contract tests catch the break; can-i-deploy is what lets you act on that knowledge safely, one service at a time.

The failure mode when you skip it is precisely the Hook: a consumer whose new expectations the prod provider has never satisfied sails through CI (its pact is verified against the provider’s latest, not its deployed version) and breaks at runtime — reintroducing the exact production outage contract testing was supposed to prevent. The verification was real; the deploy decision ignored it. The gate’s job is to make the deploy decision use the matrix instead of vibes.

Order the steps

Order the steps that make can-i-deploy a trustworthy deploy gate:

  1. 1 Consumer CI versions the pact by git SHA and publishes it to the broker
  2. 2 Provider CI verifies the pact and the broker records the (consumer, provider) cell as pass/fail
  3. 3 Before deploying, the pipeline runs can-i-deploy --version SHA --to-environment production
  4. 4 The broker checks the matrix for that consumer version against the provider version currently in prod
  5. 5 On exit 0 the deploy proceeds; the pipeline then calls record-deployment so the next team's gate sees the truth
Quiz

A consumer's pact is verified green against the provider's latest CI build. Why is that not enough to safely deploy the consumer?

Quiz

Why does Pact recommend the pacticipant version be (or contain) the git commit SHA rather than a semver like 2.3.0?

Pick the best fit

Your provider verifies pacts in CI but no one runs can-i-deploy and no one records deployments. A consumer adds a field the prod provider doesn't return yet. How do you close the gap?

Recall before you leave
  1. 01
    Walk through exactly what can-i-deploy checks, and why a green verification against the provider's latest build is not sufficient to deploy a consumer.
  2. 02
    Why version pacts by git SHA, and what's the difference between record-deployment and record-release? Why call them at the end of a rollout?
Recap

After provider verification you have a broker full of green results, but each one is a pairwise fact — consumer version X agrees with provider version Y — and that is not the question deploy time asks. Deploy time asks whether your version is safe against the provider version physically running in the target environment, which may be several deploys behind the latest CI build the pact was verified against. can-i-deploy closes that gap: it reads the broker’s matrix (every tested consumer×provider pair, plus what’s deployed where), crosses the version you’re shipping against the versions currently in the target env, and exits non-zero if any required pair is unverified, blocking the deploy. For the gate to be trustworthy, two records must be right: version pacts by git SHA so the version changes whenever the contract could and a stale result is never reused, and record what’s deployed — record-deployment for services (which undeploys the previous version) and record-release for store artifacts (which doesn’t) — called at the end of each rollout so the broker reflects reality. The payoff is independent deployability: each service answers its own deploy-safety question in seconds from recorded facts, with no shared environment and no lockstep release. Skip the gate and you ship a consumer whose new expectations the deployed provider never verified — green pipeline, broken prod — recreating the exact outage contract testing was built to prevent.

Connected lessons
Continue the climb ↑Evolving contracts safely and the limits of contract testing
shortcuts expand
search
K
prev piece
k
next piece
j
cycle tier
t
this menu
?
sources4
expand
  1. 01
  2. 02
  3. 03
  4. 04

Trademarks belong to their respective owners. Editorial reference only.