A change is breaking if a well-behaved client that worked yesterday can fail or misbehave today without changing a line of its own code. Additive changes — a new field, a new endpoint — are almost always safe to ship without warning anyone. Removing, renaming, retyping, tightening, or reordering anything a client depends on is almost always breaking.
Quick Answer: A change is breaking if an existing, correctly-implemented client could fail or misbehave after you ship it — without touching its own code. Additive changes (new optional field, new endpoint, new enum value) are usually safe. Removing, renaming, retyping, tightening, or reordering anything a client already relies on is usually breaking.
What Actually Makes an API Change "Breaking"?
A change is breaking when it violates an assumption a client was reasonable to make from your published contract — not when it merely surprises you as the API's owner. The test isn't "did we mean to break anyone," it's "could a client that followed the spec, and only the spec, keep working."
This is the core insight behind Postel's Law, the design principle that has quietly governed interoperable systems since the earliest internet protocols:
"Be conservative in what you do, be liberal in what you accept from others." — Jon Postel, RFC 761 (1980), later generalized into the Robustness Principle that shaped TCP, HTTP, and most of the protocols APIs are built on.
Applied to APIs, Postel's Law is a two-sided obligation. Your server should be conservative in what it promises — a narrow, stable, well-documented contract — and liberal in what it accepts from clients. Your clients, in turn, should be liberal in what they read (ignoring unknown fields, tolerating new enum values) and conservative in what they send. Most "who broke the API" arguments are really disputes over which side skipped its half of that bargain.
To classify any specific change, run it through three lenses:
- Structural — did the shape of the request or response change in a way that breaks parsing (a field disappears, a type changes, a required field appears)?
- Semantic — does an existing field or status code now mean something different, even though its shape is untouched?
- Behavioral — does previously valid input now get rejected, or does previously invalid input now silently succeed?
A change that passes all three cleanly is safe. A change that fails any one of them, for any client that followed the documented contract, is breaking — regardless of how small the code diff looks. That's why writing the contract itself precisely matters as much as writing the implementation behind it; see our field guide to specifying an API contract as a PM for how to make the promise unambiguous enough that "breaking" has a clear referee.
The Safe List: Changes You Can Ship Without a Version Bump
Safe changes share one property: they only ever add, and nothing a client already parses, validates, or depends on changes shape, meaning, or behavior. If every existing request and response remains interpretable exactly as before, you can ship it silently and just note it in a changelog.
- Adding a new optional field to a response body
- Adding a new optional request parameter with a sensible default
- Adding an entirely new endpoint or resource (see our guide to modeling API resources as nouns if you're deciding what the new resource should even be called)
- Adding a new HTTP method to a resource that didn't previously support it
- Adding a new, non-required request header
- Relaxing a validation rule so it accepts a strictly larger set of previously-rejected input
- Adding new optional filter, sort, or expansion query parameters
- Adding new keys to an error response body (while keeping existing keys intact)
- Adding a new webhook event type that clients can choose to subscribe to
The unifying test: could a client that ignores what it doesn't recognize keep working exactly as before? If yes, ship it. If a client would have to change even one line to keep working, it's not on this list — it belongs in the next one.
The Breaking List: Changes That Need a Version or a Deprecation Window
Breaking changes are the mirror image of the safe list: removing, renaming, restricting, or reordering anything a client already reads, writes, or relies on positionally. These need either a new API version, a long-lead deprecation notice, or both — never a silent Tuesday-afternoon deploy.
| Change | Classification | Why it breaks clients |
|---|---|---|
| Remove a field from a response | Breaking | Clients that read it get undefined/null or a parse error |
| Rename a field | Breaking | Functionally a remove + add; old field name vanishes |
| Change a field's data type (string → number) | Breaking | Strongly-typed clients fail deserialization |
| Make an optional request field required | Breaking | Existing requests that omit it now fail validation |
| Tighten a validation rule | Breaking | Previously-valid input is now rejected |
| Change the meaning of a field without renaming it | Breaking | Clients silently misinterpret correct-looking data |
| Reorder positional parameters (RPC-style) | Breaking | Values land in the wrong slot with no error at all |
| Change a status code for an existing scenario | Breaking | Retry logic, error handling, and monitoring keyed on the old code all misfire |
| Remove or rename an endpoint | Breaking | Existing calls 404 immediately |
| Lower a default page size or rate limit | Breaking | Clients tuned to the old default silently under- or over-fetch |
| Add a new required field to a request | Breaking | Every existing integration that doesn't send it now fails |
Two of these deserve emphasis because teams underrate them. Reordering positional parameters is the most dangerous entry on this list precisely because it fails silently — a value lands in the wrong field instead of throwing an error, and nobody notices until data is already corrupted downstream. Changing a status code, similarly, looks cosmetic in a diff but breaks every piece of client logic — retries, alerting, circuit breakers — that branches on the old value.
The Sneaky Middle: Enums, Nullability, and Other Changes That Look Safe
Some changes read as purely additive at the schema level but break real clients because of how people actually write code — exhaustive switch statements with no default branch, generated types that assume non-null, or brittle string-matching on error text. These are the changes that pass code review and still cause an incident three weeks later.
Enum additions are the textbook case. Adding a new value to an enum field is additive from the server's point of view — nothing existing changed. But a client written in a strongly-typed language (Java, Kotlin, Swift, TypeScript with literal unions) that pattern-matches over every known value, with no default or else arm, will throw or fall through unexpectedly the moment your new value appears in production.
Google's API design guidance (AIP-180, on backward compatibility) explicitly flags enum extension as a change that requires care. Many teams solve it by contractually requiring clients to treat unrecognized enum values as an "unknown" case rather than an error.
Nullability changes are the sneakiest entry on this entire list, because the two directions are not symmetric:
- Non-nullable → nullable (a field that was guaranteed present can now be
null) feels like loosening a constraint, but it's actually removing a guarantee the client was relying on. Typed clients generated from your schema — via OpenAPI codegen or a GraphQL schema — will crash on an unexpectednullwhere they expected a value. Treat this as breaking. - Nullable → non-nullable (a field that could be
nullnow never is) is usually safe for readers, since they get a stronger guarantee than before, but can surprise clients that had a distinct code path for the null case.
| Change | Looks like | Actually is | Safer pattern |
|---|---|---|---|
| Add a new enum value | Additive | Breaking for exhaustive-switch clients | Require clients to handle an "unknown" default case, or gate new values behind a version |
| Field goes from non-null to nullable | Loosening a constraint | Breaking — removes a client guarantee | Introduce a new optional field instead of relaxing the existing one |
| Change a default value for an omitted parameter | No schema change at all | Breaking behaviorally | Require the parameter explicitly instead of silently changing what "omitted" means |
| Change sort order or pagination page size defaults | Internal tuning | Breaking for anything depending on order or count | Make the new behavior opt-in via an explicit parameter |
| Change error message text (not the error code) | Cosmetic | Breaking if any client parses message strings | Document that only the error code is contract-stable, never the message |
How exposed you are to this class of bug depends partly on which API paradigm you picked in the first place. GraphQL's explicit nullability annotations in the schema (String! versus String) surface a nullability change at schema-validation time, before it ever reaches a client. A plain REST JSON body hides the same change until a client actually tries to deserialize an unexpected null. If you're still deciding between paradigms, our decision framework for REST, RPC, and GraphQL walks through exactly this kind of tradeoff.
A Change-Classification Flowchart You Can Run in a Minute
Before shipping any contract change, run it through five ordered yes/no questions, without skipping ahead to the one that flatters your deadline. The first "yes" you hit is your classification — stop there and act on it. If you reach the end with no "yes" at all, the change is safe to ship without a version bump.
1. Does it remove or rename anything a client can currently read or send?
YES → BREAKING. Needs a new version or a deprecation window.
NO ↓
2. Does it make a previously optional thing required (field, param, header)?
YES → BREAKING.
NO ↓
3. Does it reject input that was previously accepted (tighter validation,
narrower type, new non-null constraint)?
YES → BREAKING.
NO ↓
4. Does it change the *meaning* or *default behavior* of something a client
already depends on, even though the shape is unchanged?
(nullability loosened, default value changed, sort order changed,
enum consumed by an exhaustive switch)
YES → SNEAKY-BREAKING. Treat as breaking unless you can prove every
real client tolerates it.
NO ↓
5. Does it only add something new (field, endpoint, enum value, header)
that a client can safely ignore?
YES → SAFE. Ship it, but still document it in a changelog.
Run this against the actual diff of the request and response contract, not against your intention for the change. Intent is where teams talk themselves into believing a rename is "basically the same field" — the flowchart doesn't care about intent, only about what a client parsing bytes actually experiences.
Operationalizing the Rulebook: From Judgment Call to Repeatable Process
A rulebook only works if someone actually runs it before shipping, which means the check has to live inside your workflow, not on a wiki page nobody reopens under deadline pressure. The most reliable pattern is a mechanical contract diff — old spec versus proposed spec — reviewed against the questions above and gated like any other migration.
In practice, that means a few concrete habits:
- Diff the machine-readable contract, not the code. Tools like
oasdiffor theopenapi-difffamily compare twoOpenAPIspecs and flag structural changes automatically — catching removed fields and type changes long before a human reviewer would notice them in a large pull request. - Treat the deprecation window as a real commitment, not a suggestion. Google's API guidance (
AIP-181, on stability levels) and most enterprise API standards — including Microsoft's REST API Guidelines and Zalando's RESTful API and Event Guidelines — specify minimum notice periods measured in months, not days, before a deprecated field or endpoint can actually disappear. - Prefer additive, dated versioning over strict semantic versioning for the wire contract.
Semantic Versioning(semver.org) works well for a library you ship as one artifact, but an API has thousands of independently-upgrading consumers who can't all bump a major version on your schedule. Stripe's public API versioning model is the widely-cited example here: new fields get added to every dated version silently, while a genuinely breaking change ships behind a new dated version that old integrations simply never see unless they opt in. - Signal deprecation in the response itself, using the
DeprecationandSunsetHTTP headers (both documented in IETF drafts adopted across the industry), so tooling and not just documentation can detect an integration is calling something on borrowed time. - Reserve an explicit "unknown" handling contract for enums, so new values are additive by agreement rather than by hope.
It's worth remembering that every integration calling your API is executing someone else's job to be done — a breaking change doesn't just generate a bug report, it stops that job mid-execution, often in production, often for the customer least equipped to debug it. The later an integration sits in its own customer journey with your API — deeply embedded, three years old, maintained by someone who didn't write the original code — the more expensive the same "small" breaking change becomes to absorb.
Key Takeaways
- A change is breaking if a spec-compliant client could fail without changing its own code — intent doesn't matter, only what the client actually experiences.
- Additive changes are almost always safe: new optional fields, new endpoints, new non-required headers, and relaxed validation all pass the test.
- Removal, renaming, retyping, tightening, and reordering are almost always breaking, and deserve a version bump or a real deprecation window, never a silent deploy.
- Enum additions and nullability loosening are the sneakiest gray-zone changes — they look additive at the schema level but break exhaustive
switchstatements and typed clients that assume non-null. - Postel's Law gives you the two-sided obligation: be conservative in what your API promises, be liberal in what your clients tolerate.
- Run every change through a five-question flowchart against the actual contract diff, not your intention for the change — intent is where teams talk themselves into shipping a breaking rename.
- Diffing the machine-readable contract (OpenAPI, GraphQL SDL) catches what a code review misses, and tools built for that comparison should be part of your pre-ship checklist, not an afterthought.
Frequently Asked Questions
What Is a Breaking API Change, Exactly?
A breaking API change is any change that can cause an existing, spec-compliant client to fail, error, or misbehave without the client changing its own code. That includes removing or renaming fields, tightening validation, changing a data type, or making an optional parameter required. The test is what the client experiences, not what the API owner intended.
Is Adding a New Field to an API Response a Breaking Change?
No — adding a new optional field to a response is one of the safest changes you can make, since well-behaved clients ignore fields they don't recognize. It only becomes risky if you make the new field required, or if it changes the meaning of an existing field it's paired with. Document it in a changelog anyway so integrators aren't surprised.
Is Adding a Value to an Enum a Breaking Change?
It depends on how clients consume the enum. It's additive at the schema level, but it's functionally breaking for any client using an exhaustive switch or pattern match with no default case, which is common in strongly-typed languages. The safer pattern is to contractually require clients to handle an "unknown" fallback for any value they don't recognize.
How Long Should an API Deprecation Window Be?
Most established API guidelines — including Google's AIP-181 and Microsoft's REST API Guidelines — specify deprecation notice periods measured in months rather than days, often six months or more for widely-used fields or endpoints. The right number depends on your integrators' release cadence: a field used only by internal services can deprecate faster than one embedded in third-party production systems.
Should I Version My API Using Semantic Versioning?
Semantic Versioning works well for libraries and SDKs shipped as a single artifact, but it maps awkwardly onto a live API contract with many independently-upgrading consumers. Most high-traffic APIs — Stripe's dated-version model is the widely-referenced example — favor an additive-only default plus explicit deprecation headers over forcing every consumer through a major-version migration at once.