Roughly 62% of contract test failures in mature microservice organizations are caught not by the contract tests themselves, but by production incidents that happen after those tests passed. That number should make you uncomfortable. It means your pact suite is doing CI theater, not CI work.
The mechanism is almost always the same. A consumer team records an interaction against a provider at version 1.4. The provider ships 1.7 six weeks later. Nobody reruns the consumer verification. The stub is still green in the consumer's pipeline. The pact broker shows a passing badge. And then a shape change in the orderStatus field, or a quietly dropped enum value in paymentMethod, slips through to production because the thing you were trusting to catch it was frozen at a snapshot that no longer exists.
This is contract test drift. It is the dominant failure mode of consumer-driven contract testing in practice, not misconfiguration, not missing coverage, not tooling gaps.
The core problem is that most teams treat contract verification as a one-time setup task. They wire up Pact, publish a broker URL, watch the badge go green, and move on. What they do not build is the continuous re-execution loop that keeps stubs current as the provider evolves. The stub is only valid at the moment it was generated. Everything after that is optimism.
Pact's own documentation is explicit about this: verification must run on the provider side on every merge, and the results must publish back to the broker. In practice, less than half the teams I have seen in the wild actually do this. The consumer publishes pacts faithfully. The provider runs verification once during initial setup, forgets to wire it into their merge gate, and then the drift starts accumulating silently. Six months later, the consumer's recorded interactions describe a contract that the provider stopped honoring somewhere around sprint 14.
The situation gets worse with AI-assisted refactoring. When an engineer uses a code generation tool to restructure a service layer, the tool optimizes for internal coherence, not for external API contracts. A field gets renamed from customerId to customer_id for consistency with a new database schema. The response shape changes. The provider's tests still pass because they test behavior, not serialization fidelity. The consumer's stub still shows the old field name. The pact broker shows green because no one ran provider verification after the refactor. This is the specific failure mode that makes contract drift a 2024 problem and not just a 2018 problem.
The fix is not complicated. It requires treating stub currency as a first-class CI health metric with two mandatory gates.
The first gate belongs to the provider. Every time code merges to the provider's main branch, provider verification must run against all consumer pacts registered in the broker, and the result must be published. If a consumer pact fails verification, the provider merge fails. No exceptions, no snooze buttons. This forces the provider team to either fix the contract violation or negotiate a version bump with the consumer team before the change lands. That conversation is the point. That conversation is what contract testing is supposed to create.
The second gate belongs to the consumer. Every time the consumer's pipeline runs, it should replay its recorded interactions not just against its local stubs, but against a live provider sandbox pinned to the provider's latest verified version. This is where most teams stop short. They run Pact tests against WireMock or a local mock, declare victory, and ship. A local mock is just the stub running against itself. It proves nothing about the provider's actual behavior. The consumer gate only has integrity if the sandbox it runs against is continuously provisioned from the provider's real artifact.
This is exactly the kind of behavioral check that OpenThunder is built to surface: not just whether your tests pass in isolation, but whether the contracts those tests encode still match the live system on the other side of the wire.
Stub age is a metric worth tracking explicitly. If your pact broker does not show you the age of the last successful verification for each consumer-provider pair, instrument it yourself. A verification that is more than two provider release cycles old should trigger a warning. One that is more than five cycles old should block deployment. This sounds aggressive. It is. A stale verification is not a passing test. It is an unknown test with a green label on it.
LATEST is almost always the wrong tag to use in your can-i-deploy checks. Pin to a named environment tag, production or staging, that only advances when the provider's verification passes against that environment's actual deployed artifact. That way, can-i-deploy reflects the state of the system that real users are hitting, not the state of the system that someone hopes will be deployed next Tuesday.
Teams that do this well share one discipline: they treat the pact broker as a living deployment dependency graph, not a test report archive. Every time a provider deploys, they check which consumers are in production and verify against those pacts. Every time a consumer deploys, they check whether the provider version they depend on has verified their pacts. The broker becomes a coordination layer, not a badge farm.
The cost of getting this right is a few hours of pipeline plumbing and a cultural norm that providers own their consumer contracts as much as they own their own tests. The cost of getting it wrong is a class of production bug that is genuinely hard to debug: one where all your tests are green, your deploys look clean, and the failure only surfaces when two independently healthy services talk to each other in production.
OpenThunder surfaces this category of failure by running static, dynamic, and behavioral checks across your pipeline on every change. If your verified pact age is drifting, it flags it before it costs you.
Put a verification gate on your pipeline
Contract drift is a pipeline discipline problem, and the fix belongs in your CI configuration, not in a post-incident review. OpenThunder runs static, dynamic, and behavioral checks on every change and turns failures into fixable findings. Try it here.
A green pact badge is only as trustworthy as the last time the provider actually ran verification against it.