Gå til toppen
Matthew Manickakumar

Your microservices passed every test. Then production broke anyway.

Here’s why – and how contract testing fixes it.

You’ve been there. Two teams. Two services. Both green in CI. Both deployed on Friday. Monday morning: production is down. The order service can’t read product prices. Turns out the product team renamed a JSON field over the weekend. Nobody told the order team. Nobody could tell them – there was no mechanism to.

This isn’t a communication failure. It’s an architecture failure. And it has a name: integration drift.

The invisible gap in your test pyramid
Most engineering teams test well at the unit level. They mock dependencies, write fast tests, and get high coverage. The problem is that mocks lie.

When the order service mocks the product service response, it uses the field price. When the product team renames that field to unit_price in their codebase, your mock doesn’t know. Your unit tests don’t know. Your entire test suite doesn’t know – until a real request hits a real endpoint in production.

This is the gap contract testing fills.

What contract testing actually is
Contract testing is a technique where two services agree on a normal, version-controlled description of their interaction – the contract – and both independently verify they honor it.

The contract captures one specific thing: what the consumer (caller) actually needs from the provider (API). Not everything the API returns. Just the fields the consumer uses. There are two sides to every contract:

The consumer defines it. The order service writes a test that says: “When I call GET/products/42, I expect a 200 response with an id (number), a name (string), and a price (number).”
That expectation is saved as a JSON contract file.

The provider verifies it. The product service fetches that contract file and runs its real code against it. If the response doesn’t match – wrong field name, wrong type, missing field – the test fails. Before deployment. Before anything reaches production.
The most widely used tool for this is Pact, which supports JavaScript, Java, Python, Go, .NET, and more.

Why this matters more than you think

It decouples teams. The order team doesn’t need to run the product service locally to test their integration. They test against the mock, generate a contract, and trust the pipeline to verify it. Both teams deploy independently, which is the whole point of microservices.

It makes breaking changes visible immediately. The moment a provider change would break a consumer, the provider’s own CI pipeline fails. The developer who made the change sees it. They fix it before it becomes anyone else’s problem.

It creates a living document of your API surface. The Pact Broker – the shared registry where contracts are stored – becomes a map of which services depend on what. Want to know who calls your /products/:id endpoint and what fields they use? The broker tells you instantly.

It shifts the cost curve. A production incident costs hours of engineer time, customer-facing downtime, and trust. A failed contract test costs two minutes of reading a diff.

What the industry has learned
Three organisations tell the story well – and they come at it from very different angles.

Spotify arrived at contract testing through a fundamental rethink of how testing should work in a microservices world. Their engineering team introduced the “Testing Honeycomb” – an inversion of the classic test pyramid that places integration tests, not unit tests, at the centre of gravity. Their insight: in a microservices architecture, the most complex and failure-prone part of the system isn’t inside any individual service. It’s at the boundaries where services talk to each other. Unit tests, by design, mock those boundaries away. Spotify explicitly named consumer-driven contract testing (Pact) as the natural next step to give teams genuine confidence that they weren’t accidentally breaking those contracts with every release.

Delivery Hero – the global food delivery group behind foodpanda – has the hard numbers. After analysing production incidents across 2023 and 2024, their engineering team found that 5% of all incidents were caused by contract violations between microservices: teams changing their API responses without any mechanism to alert the services consuming them. In practical terms, that translated to approximately 60,000 lost customer orders. After adopting PactFlow contract testing, they reduced production incidents attributable to integration failures by 14%.

Boost Insurance, a New York-based insurtech platform, tells a similar story at smaller scale. Running 15+ microservices, they were experiencing one service outage per month and roughly twelve production issues per quarter. After adopting Pact, outages dropped to virtually zero and production issues fell to two per quarter. Their engineering manager said he would happily eliminate end-to-end testing entirely in favour of contracts.Three different industries. Three different scales. The same underlying problem – and the same category of fix.

The workflow in practice

Here’s the shape of it, end to end:

1. Consumer writes an interaction test
The test defines what the consumer sends and what it expects back. Pact spins up a local mock server, runs the test against it, and saves the contract as a JSON file.

2. The contract is published to a broker
After the consumer’s tests pass in CI, the contract file is pushed to a central Pact Broker (or PactFlow, the hosted version). This is the handshake point between the two pipelines.

3. The provider fetches and verifies
When the provider’s CI runs, it fetches all contracts from the broker and replays each recorded request against the real running service. If any response doesn’t match the contract, the build fails and verification is published as failed.

4. can-i-deploy gates every deployment
Before either service deploys to production, a single command checks the broker: “Is the version of this service compatible with everything it depends on in production right now?” If the answer is no, deployment is blocked. That’s it. That’s the safety net.

What it doesn’t do
Contract testing is precise and honest about its scope. It doesn’t test business logic. It doesn’t test performance. It doesn’t replace integration environments for complex user journeys. What it does – and does exceptionally well – is verify that two services agree on the shape and structure of their API interactions. Think of it as a prenuptial agreement between your services. Both sides commit to their expectations up front, in writing, and verify they’re still honouring them with every change.

When should you adopt it?
Not every team needs this immediately. The honest signal is: have you been burned by an integration failure in production that unit tests didn’t catch? If the answer is yes, that incident cost more than a month of contract test maintenance. The sweet spot is typically teams with four or more independently deployed services, multiple teams working on different services, and CI/CD pipelines already in place. Without pipelines, the workflow loses most of its value – contracts need to be verified automatically on every change, not manually.

A mindset shift worth making
The hardest part of contract testing isn’t the tooling. It’s the mindset. It asks teams to think about their API from the consumer’s perspective first. What does the caller actually use? Just those fields. Just that shape. Define it explicitly, version it, and let the pipeline enforce it.
Spotify’s framing is useful here: stop treating the microservice as a unit and start treating the contract as the unit. When your tests verify the interaction point rather than the implementation detail, you gain something rare in distributed systems – the freedom to change things on one side without breaking the other, and the confidence to know for certain before you deploy.

That produces cleaner APIs, smaller surface areas, and fewer surprise breakages. And it produces something that most engineering teams scaling microservices genuinely struggle to maintain: trust between teams that independent deployments won’t silently take down services they didn’t touch. 

If you’re running microservices and relying solely on unit tests and occasional integration environments to catch cross-service failures – you’re carrying a risk that compounds as your system grows. Contract testing doesn’t remove all risk. But it closes the exact gap where most productionincidents actually happen. Delivery Hero found that out by counting 60,000 lost orders. You don’t have to.

References & further reading

Spotify Engineering – Testing of Microservices (Testing Honeycomb origin, 2018)

Delivery Hero Tech – How Contract Testing Helped Our Organization to Prevent Production Incidents (2024)

PactFlow – Boost Insurance Case Study

Pact – pact.io (open source, multi-language)

PactFlow – pactflow.io (hosted Pact Broker with UI)