Two teams own two services that talk to each other. How do you stop one breaking the other without running both together in a shared environment?
Contract testing has each side verify itself against a shared description of the interaction, so consumer expectations are checked against the provider's real behaviour without the two ever running together. Mismatches surface in each team's own pipeline, not in a shared environment days later.
What the interviewer is scoring
- Does the candidate explain how both sides are verified without ever running together
- Whether they identify that a schema check alone misses semantic expectations
- That the provider's obligation is limited to what consumers actually use
- Whether the failure is placed in the breaking team's pipeline rather than in a shared environment
- Does the candidate say what contract testing does not cover
Answer
The problem with the shared environment
The default answer is to deploy both services somewhere and run tests against the pair. It works, and it has three properties that get worse as the number of services grows.
It is late. The mismatch is found after both services are built and deployed, which is hours or days after the change that caused it, and by then the author has moved on.
It is shared, so it is contended and frequently broken by someone else. A red suite in a shared environment usually means a third team deployed something, and the signal degrades to noise.
And it scales badly. Testing a service against its real dependencies means those dependencies' dependencies too, and eventually the integration environment is the whole system, which nobody can keep healthy.
What a contract test does instead
The trick is that the two sides are verified separately, against a shared artefact, and never run at the same time.
The consumer writes a test against a mock provider, saying: when I send this request, I expect a response of this shape, and here is what I do with it. Running that test produces a contract — a machine-readable record of the interactions the consumer relies on.
The provider then takes that contract and replays each recorded request against the real provider, checking that the real response satisfies what the consumer expected. No consumer code is running; the contract stands in for it.
The result is that each team learns about a mismatch in their own pipeline, from a test they can run locally, at the moment they make the change. The provider that removes a field sees its build go red with a message naming the consumer that needed it, before the change is merged. That relocation of the failure — from a shared environment later to the breaking team's pipeline now — is the entire value.
Why the mock does not lie
The obvious objection is that the consumer test used a mock, and a mock is whatever you wrote it to be. That is exactly right and it is why the consumer half alone is worthless.
The verification step is what closes it. The contract is not a description of what the consumer imagines; it is a claim that gets checked against the provider's actual running code. If the consumer expected a field the provider never sends, provider verification fails and the contract is corrected. A contract that has not been verified against the provider is just a mock with paperwork.
What this gives you over a schema
Publishing an OpenAPI document and validating against it is a genuine improvement over nothing, and it stops short in two ways.
A schema describes what the provider can return. A contract describes what a consumer actually depends on. That difference is what makes evolution safe: a provider can freely change anything no consumer's contract mentions, and knows precisely which changes are breaking and for whom. With only a schema, every field is potentially load-bearing and every change is a risk, so providers become reluctant to change anything.
The second difference is semantic. A schema says status is a string. A contract can say that for this request the consumer requires status to be SETTLED, and that it reads settledAt when it is. That is a behavioural expectation, and it is the kind of thing that breaks in production while the schema still validates.
The limits, which matter
Contract testing verifies one interaction between two parties. Everything outside that is out of scope, and pretending otherwise is how teams get burned.
It says nothing about whether a multi-service workflow produces the right business outcome, nothing about performance, nothing about whether the services can actually reach each other in the deployed environment, and nothing about configuration or infrastructure. A small number of end-to-end tests still exist to answer "is the system assembled and wired correctly" — contract testing is what lets that number stay small.
It also carries real overhead. A broker to store and version contracts, a verification step in the provider's pipeline, and a discipline about which contract version is deployed where. For two services owned by one team, that overhead exceeds the benefit and a straightforward integration test is the better answer. The pattern earns its cost at the point where the two sides are owned by teams on different release schedules, which is precisely the situation where the shared environment was failing you.
The consumer's mock is only meaningful because the provider replays it against real code. Skip the verification step and you have automated your assumptions rather than tested them.
Likely follow-ups
- A provider adds a required request field. Which pipeline goes red, and when?
- How is this different from publishing an OpenAPI spec and validating against it?
- What does contract testing not catch that you still need an end-to-end test for?
- Who owns the contract, and what happens when a consumer wants a field the provider will not add?
Related questions
- You own the API test suite for a service from scratch. How do you structure it, and what do you mock?hardAlso on api-testing and contract-testing7 min
- You need to add one field to an event and remove another, and five teams consume it. How does that roll out?hardAlso on contract-testing6 min
- How do you test a service that calls a third-party API, without calling it?mediumAlso on contract-testing4 min
- How do you decide where one service ends and the next begins, and when is a modular monolith the more honest answer?hardAlso on microservices7 min
- Your test suite takes fifty minutes and every merge waits for it. How do you fix that?mediumAlso on ci4 min
- How do you decide between a modular monolith and microservices?hardAlso on microservices4 min
- How does your order placement change on a venue that allocates pro rata within a price level instead of first in, first out?hardSame kind of round: concept6 min
- A customer cancels a twelve-month policy after four months. How much do you refund?hardSame kind of round: concept4 min