Cloud FinOps & Infrastructure ScalingPlaybook3 min readUpdated September 2026

Catching a Breaking API Change Before It Ships

Integration bugs between two services are some of the most expensive to find late, because each service's own tests pass fine on their own, and the mismatch only shows up when they actually talk to each other in production. Contract testing exists specifically to catch that gap earlier.

Here's how it actually works and where teams get the setup wrong.

Vendors Covered in this Article

Disclosure: We may earn a commission if you buy through some links on this page. It doesn't change what we recommend.

What a contract test actually checks that a unit test doesn't

A unit test checks that a service behaves correctly in isolation. A contract test checks that a service's actual request and response shapes still match what the other side expects, without needing both services running together in a shared environment. It catches exactly the kind of change, a renamed field, a type that changed from a string to a number, a field that quietly became optional, that unit tests on either side would miss entirely.

Think of it as a test of the agreement between two services, rather than a test of either service's internal logic. It matters most exactly where independent teams own each side of an integration, since neither team can see the other's own test suite directly, and a contract test becomes the fastest shared signal that the agreement between them has quietly drifted.

Consumer-driven contracts: letting the consumer define what it needs

In a consumer-driven approach, the service consuming an API writes down exactly what it actually uses from the response, not every field the provider happens to return. The provider then runs that consumer's expectations against its own real implementation before shipping a change.

This matters because it stops a provider from being blocked by a consumer's use of a field the consumer doesn't actually care about, while still catching changes that would genuinely break something a consumer depends on.

Where teams get the setup wrong

The most common mistake is writing contract tests that check the entire response shape field by field, which makes the test brittle and prone to failing on harmless additions, like a new optional field the consumer never reads. Write the test to check only what the consumer actually depends on, so it fails for changes that matter and stays quiet for ones that don't.

The second common mistake is running contract tests only occasionally, or only manually before a big release, rather than as a standard part of every relevant pull request, which defeats the point of catching the break early.

Building this into a review process people will actually follow

Contract testing works best paired with a lightweight, written process: whenever a service's request or response shape changes, its contract tests run automatically against every known consumer before the change can merge. Document that process clearly enough that a new engineer can follow it without asking, since a process that lives only in one person's head disappears the moment that person is out.

Keep the process itself short. A one-page checklist that engineers actually read beats a longer document that becomes something people skip past. Review it with both teams a few weeks after adopting it, and cut anything that turned out to be more ceremony than protection.

A worked example: a field that quietly changed shape

Say a service changes a customer identifier field from a numeric type to a string, a reasonable change on its own. Without a contract test, this ships cleanly because the service's own tests never check how a consumer parses that field. A consumer relying on the old numeric type breaks in production the next time it tries to use that value, and the failure shows up far from the actual change, which makes it slower to trace back to the real cause.

Deciding which integrations are worth this investment

Contract testing has real setup cost, so it's worth prioritizing for integrations where a break is expensive: anything customer-facing, anything involving money, or any integration that's broken before without anyone catching it until a customer reported it. A rarely-changing internal integration between two services owned by the same small team, where a break is quick to notice and fix directly, often doesn't need the same level of investment.

Start contract testing where it pays off, and keep the tests lean:

  • Prioritize integrations where a break is expensive, especially anything customer-facing or tied to revenue, before spending effort on low-risk internal calls.
  • Add contract tests for integrations that have slipped through to production before, since they have already shown they can break unnoticed.
  • Let each consumer record only the fields it actually uses, so the test is not brittle and does not fail on harmless changes.
  • Run the contract check before merging, so whoever changes a request or response shape sees a break at the point of change.
  • Treat an intentional breaking change as a coordinated conversation between both teams, not a reason to disable the failing test.
Executive Capability Standard

What Good Looks Like

Good here means a change to a service's request or response shape that would break a real consumer gets caught automatically before it merges, not discovered in production after a consumer's request starts failing.

Building The Capability (5-Stage Skill Ladder)

1. Learn:Map your service-to-service integrations and identify which ones have broken silently before without an automated test catching it.
2. Do Manually:Write a contract test by hand for your highest-risk integration, checking only the fields the consumer actually uses.
3. Delegate:Give the teams on each side of a key integration joint ownership of keeping its contract tests current as both sides evolve.
4. Automate:Wire contract tests into your pipeline so they run automatically on any pull request touching a shared API's request or response shape.
5. Buy:Bring in outside help to set up a contract testing framework once you're coordinating across enough services that manual test-writing can't keep pace.

How to Get Started

Disclosure: We may earn a commission if you buy through some links on this page. It doesn't change what we recommend.

Process Street

Process Street fits well for writing down the checklist that turns contract testing from an idea a few engineers understand into a repeatable step every relevant pull request actually follows.

Visit Process Street→

Frequently Asked Questions

Is contract testing the same thing as integration testing?

No. Integration testing runs real services together to check they work correctly as a whole, which is slower and needs a full environment. Contract testing checks the shape of the agreement between services without needing both running together, which makes it faster and easier to run on every relevant change.

Do we need contract tests for every internal service?

Not necessarily. Prioritize integrations where a break is expensive or has slipped through before, especially anything customer-facing or tied to revenue. A low-stakes internal integration between two services owned by the same team can often rely on quick manual detection instead.

What happens when a contract test fails, who fixes it?

Whoever is making the change that broke the contract, ideally caught before merging. If the change is intentional and the consumer genuinely needs to update, that's a coordinated conversation between both teams, not something to push through by disabling the test.

About the numbers

This guide doesn't quote a sourced benchmark. Figures in it are estimates or general guidance, so check them against your own numbers.

Related Guides