How API Breaking Changes Affect Downstream Services
An API change is breaking when some existing caller stops working without being changed. The difficulty in a distributed system is not recognising a breaking change in the abstract; it is knowing who the callers are. This article covers what counts as breaking, how teams make changes safely, and what tooling can and cannot tell you.
What counts as breaking
- Removing or renaming a route, field or enum value.
- Changing the method or the path shape (
/invoices/{id}to/invoices/by-id/{id}). - Making an optional request field required, or tightening its validation (a stricter regex, a shorter max length).
- Changing a field's type or the meaning of its value.
- Changing error behaviour callers depend on, such as status codes.
Adding an optional request field or a new response field is usually safe, unless a strict client rejects unknown fields. 'Usually' is doing real work in that sentence.
A scenario
billing exposes GET /v1/invoices. web calls it directly, mobile-bff proxies it, and a partner script calls it from outside. Renaming the path to /v2/invoices breaks all three. Only the first two are in your repositories; the third is invisible to any code search.
How teams change contracts safely
- Expand, then contract. Serve old and new for a period, move callers, then remove the old.
- Version the surface. Put breaking changes behind a new version and deprecate the old, with a published sunset date.
- Consumer-driven contract tests. Callers publish what they rely on, and the provider's build fails if a change violates it. Tools such as Pact do this; it is the strongest option for callers you control.
- Schema diffing in CI. Compare OpenAPI documents between commits and fail on removals or type changes.
- Telemetry. Count calls per route and caller before removing anything.
# using the openapi-diff style of tools
openapi-diff openapi.main.yaml openapi.pr.yaml --fail-on-incompatibleThe consumer-discovery problem
All of the above assume you know the callers. In practice a registry of 'who calls what' is either missing or stale. Traffic telemetry finds real callers but only after the fact; static analysis finds repository callers but misses everything outside them; a hand-maintained list rots. Most teams end up combining a recorded consumer list with telemetry.
Trade-offs
- Dual-serving costs maintenance and testing for as long as both exist.
- Contract tests need callers to participate; they do not help with external clients.
- A recorded consumer list is only as good as its upkeep.
