Breaking Changes Without Versioning
Changing an API contract in place without versioning silently breaks existing clients and erodes trust. Use explicit versioning, evolve additively for backward compatibility, and follow a documented deprecation policy with CI compatibility checks.
This anti-pattern is altering a published API's contract — renaming or removing fields, changing types, tightening validation, removing endpoints — without a versioning strategy or deprecation period. Clients built against the old contract break without warning.
Why It Happens
In-place edits are the easiest way to ship a change. When the API and its main consumer live in the same codebase, teams forget that other integrators exist. Versioning is seen as overhead, and deprecation discipline takes coordination. "It's a small change" is a frequent justification — but for an integrator, any contract change can be fatal.
Why It Hurts
Consumers integrate against a contract and reasonably assume stability. A breaking change made in place causes their code to fail — often silently, as a renamed field simply arrives null, or a stricter validation rejects previously valid calls. The failure surfaces in their production, not yours, frequently at the worst time. It triggers emergency fixes, support escalations, and lasting distrust. Public and partner APIs are especially damaged, because consumers cannot all upgrade on your schedule.
Warning Signs
- Fields are renamed or removed directly in the live schema.
- Endpoints disappear without a deprecation notice.
- New required parameters are added to existing operations.
- There is no version identifier and no documented change policy.
Better Alternatives
Adopt an explicit versioning scheme (URI path, header, or media type) so incompatible changes go to a new version while the old keeps working. Favor backward-compatible evolution: add fields rather than rename, make new parameters optional, never repurpose existing ones. Publish a deprecation policy with timelines and sunset headers, and communicate changes to consumers ahead of time. Where contracts are formalized (OpenAPI, schema), run compatibility checks in CI.
How to Refactor Out of It
Freeze the current contract as v1 and route all existing clients to it. Introduce breaking changes only behind a new version, running both in parallel through a deprecation window. Add automated backward-compatibility checks that fail the build on a breaking diff. Instrument usage so you know which clients still call deprecated paths before retiring them. Establish a written deprecation policy. The principle is simple: once published, a contract is a promise — evolve it additively or version it, but never break it in place.