Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams (Part 2)
Part 1 established a clean local loop. REST Assured runs in under five seconds on the developer's machine. Postman pulls from the live OpenAPI spec. The gate is reliable, and the contract is grounded in actual code.
Now add 50 developers to that picture.
Suddenly the local loop is not enough. One developer's intentional optimization is another downstream team's production regression. And here is the uncomfortable truth: the test suite itself becomes the attack surface.
The Trap Nobody Talks About
When a REST Assured test fails because a developer changed API behavior, there are two ways to make it green again. Fix the code, or fix the test.
Fixing the test is faster. It feels harmless. The build goes green. The PR merges. And somewhere downstream, a team that depended on the original behavior starts seeing failures they did not cause and cannot explain.
This is not a discipline problem. It is a structural one. When a single developer can silently redefine an API contract by updating an assertion, the test suite stops being a gate and starts being a liability.
The Opposite Trap Is Just as Bad
The instinctive overcorrection is to declare all existing tests immutable. Nobody touches them. Ever. Only new tests get added.
This sounds safe. It is not.
When an API genuinely needs to evolve, the old test stays active and stays failing. Teams start using @Ignore just to get deployments through. The test suite becomes a graveyard of outdated assertions that nobody trusts and nobody deletes. This is test rot, and it is just as damaging as silent contract mutation, just slower.
The dilemma looks like this:
| Approach | The Benefit | The Trap |
|---|---|---|
| Never change old tests | Prevents silent contract rewrites | Causes permanent build failures when changes are intentional. The suite becomes a historical graveyard. |
| Freely modify existing tests | Keeps the suite clean and passing | One developer can redefine the contract for the entire organization without downstream teams knowing. |
Neither extreme works at scale. The answer is a governance layer that distinguishes between the two cases explicitly.
The Architectural Solution: Three Levers
You do not need to throw away REST Assured or install a complex contract broker to solve this. Three levers, used together, handle the governance problem with tooling teams already have.
Lever 1: API Versioning as a Hard Rule
When a functional change modifies payload structure, field behavior, or pagination logic, the existing endpoint is not touched. It stays exactly as it is, along with its REST Assured tests.
The change ships under a new version.
/api/v1/users and its test suite remain intact and passing. /api/v2/users is introduced with a brand new test class that reflects the new behavior. Downstream teams consuming v1 are never broken. They migrate to v2 on their own timeline.
This rule eliminates the core dilemma entirely. There is no decision to make about whether to update an old test, because the old endpoint and its tests are never touched.
Lever 2: CODEOWNERS to Enforce Review on Core Contracts
GitHub and GitLab both support a CODEOWNERS file that assigns mandatory reviewers to specific directories. This is the enforcement mechanism for the versioning rule.
Place the core REST Assured contract tests under a protected path:
src/test/java/com/yourorg/contracts/
Then add a CODEOWNERS entry:
src/test/java/com/yourorg/contracts/ @api-architecture-team
Now any PR that modifies a file inside that directory cannot merge without explicit approval from the API architecture group. A developer can freely add new test files for new features anywhere else. But touching an existing contract test requires a deliberate, cross-team sign-off.
The gate is no longer social. It is structural.
Lever 3: Deprecation Windows Instead of Deletion
When old functionality genuinely needs to retire, the process is not a silent test deletion. It is a staged, visible wind-down.
Mark the old endpoint as @Deprecated in the Spring Boot controller. Log a deprecation warning on every call so downstream teams see it in their monitoring. Schedule the removal for a future sprint with a communicated deadline. When that sprint arrives, the old endpoint and its REST Assured tests are deleted together, in the same PR, with full visibility.
Nobody is surprised. Nobody discovers a broken contract in production. The retirement is as explicit as the original contract.
When You Need More: Consumer-Driven Contract Testing
The three levers above handle the majority of real-world cases without additional infrastructure. But if your organization has many independent teams consuming the same APIs, and the coordination cost of manual review is becoming a bottleneck, the next step is Consumer-Driven Contract Testing using Pact or Spring Cloud Contract.
The model works like this. Each downstream team that consumes an API publishes a consumer contract that explicitly declares what it expects. When Team A's developer makes a change that affects /api/v1/users, the CI pipeline automatically pulls all active consumer contracts from a shared broker and runs them against the new code. If any consumer contract fails, the merge is blocked and the affected team is notified before anything ships.
The key difference from the lever approach is that enforcement becomes fully automated. No human reviewer needs to catch the conflict. The pipeline catches it.
This is the right investment when teams are large enough that CODEOWNERS review becomes a bottleneck, or when downstream consumers are external and cannot be coordinated manually.
What This Looks Like in Practice
A developer on Team A refactors the user pagination logic and opens a PR.
Without governance: the test fails, they update the assertion, CI goes green, Team B finds out when their service breaks in staging.
With governance: the test fails. The CODEOWNERS rule blocks the PR from merging without architecture review. The reviewer looks at the change, determines it is a functional behavioral shift, and asks the developer to version the endpoint. /api/v2/users is introduced. Team B is notified of the new version. Team A ships. Nobody breaks.
The difference is not more process. It is the right structure in the right place.
What Comes Next
At this point the framework handles individual developers and distributed teams. The contract is protected, the versioning rule is enforced, and the governance layer is structural rather than cultural.
Part 3 introduces a different kind of contributor: AI-assisted developers using tools like Cursor or Copilot, who are generating code faster than any review process was designed to handle. The same OpenAPI spec that anchors Postman and REST Assured becomes the semantic anchor that keeps AI-generated code from hallucinating payload shapes and silently drifting from the contract.
Part 3: The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor
The Zero-Drift API Series
- Intro: The Zero-Drift API Series: Stop Trusting a Green Build You Can't Explain
- Part 1: A Guide to Stop Breaking Merges: Unifying Postman and REST Assured in Spring Boot
- Part 2: Who Approved This Change? Managing API Contracts and Test Rot in Large Engineering Teams
- Part 3: The AI Superpower: How Vibe Coders Use OpenAPI as a Semantic Anchor
- Part 4: Locking Down the Pipeline: Enforcing Contract Integrity Against Autonomous AI Agents
Top comments (0)