# What Counts as a Breaking Change in an API — Routebase

> The changes that break a live consumer, the ones that only feel dangerous, the asymmetry between request and response, and how to classify them automatically.

Canonical page: https://routebase.dev/guides/api-versioning/api-breaking-changes/
Chapter 1 of 12 · API Versioning · Last reviewed 2026-09-13 · The Routebase Team

Half of all versioning anxiety comes from not having a shared definition of the word breaking. One engineer calls a new optional field a breaking change, another ships a renamed response field on a Friday, and both are arguing from intuition.

The definition worth using is narrow and testable. A change is breaking when a caller who was correct before the change is no longer correct after it, without that caller touching a line of code.

## The classification

Structural breaking changes fall into a small, closed set, and every one of them has the same shape. Something a consumer relied on is gone, is different, or is now mandatory.

| Change | Why it breaks |
|---|---|
| An endpoint is removed | Every call to it starts failing |
| A path or a method changes | The old address answers nothing |
| A parameter is removed | Callers still sending it may be rejected |
| A parameter or field changes type | Values that were valid stop being valid |
| An optional input becomes required | Callers that omit it start failing |
| A new required parameter or body field appears | Every existing call is now incomplete |
| A response field is removed | Consumers reading it get nothing |
| A response field changes type | Consumers parsing it fail or coerce silently |
| An enum value is removed | Callers sending it are rejected |

The safe set is just as short. Adding an optional parameter, adding a response field, adding an enum value, relaxing a required field to optional, and editing descriptions or examples all leave existing callers correct.

Deprecation belongs in neither column. Marking something deprecated changes no behaviour at all, and it is still worth counting separately, because it is the announcement that a future release will change behaviour.

## The asymmetry that explains the rules

The two lists look arbitrary at first. Adding a required request field breaks consumers while adding a response field does not, and removing a request field is usually safe while removing a response field is not.

One rule produces all four results. Whatever the other side has to give you may only get easier over time, and whatever the other side receives from you may only get richer.

_Figure: A required field added to a request breaks existing callers, while the same field added to a response leaves them working, because obligations may only shrink and payloads may only grow._

That single sentence is worth teaching to a whole team, because it turns a memorised table into something people can derive at the moment they need it.

## The changes that only feel dangerous

Three changes generate more debate than they deserve.

**Adding an optional response field.** Safe for any consumer that ignores unknown properties, and unsafe for one that validates against a closed schema. Since you cannot audit every consumer, state the expectation in your own documentation. A consumer who then chooses strict validation has chosen it knowingly.

**Adding an enum value.** Safe on the request side, because nothing existing becomes invalid. On the response side it depends on how consumers parse it, and a client whose language maps enums exhaustively will fall over on an unrecognised member. Publishing that new values may appear is what keeps this additive.

**Reordering fields or array members.** JSON object members carry no order, so reordering a response object is not a change at all. Reordering an array is a real change of meaning whenever consumers rely on position, and it does not show up in any schema diff.

## The breaks a diff cannot see

Structural rules catch most of the damage, and they are blind to an entire second class of change.

A field called `status` that returned `active` and `cancelled` and now also returns `paused` has grown an enum value, which the diff will report. A field called `amount` that quietly switches from cents to a decimal string is caught too. But a field called `amount` that stays an integer and switches from cents to whole currency units looks identical in every diff, and it will produce hundredfold billing errors in production.

Semantic changes of that kind are always breaking, and they are always invisible to tooling. The same goes for tightening validation on an input you previously accepted loosely, for changing the default value of an optional parameter, and for changing which errors an endpoint returns in a given situation.

The practical answer is to treat those as declarations. Whoever makes the change says so, and the classification tool covers the structural half so the humans have attention left for this half.

## Why the rules have to be rules

A classification that depends on judgement produces a different answer depending on who is asked and how close the release is. That is not a small problem, because the classification decides whether you cut a major version.

Fixed rules give you three things. Two people comparing the same pair of contracts reach the same conclusion, and the check runs in a pipeline without anybody calling a meeting. The argument then moves from whether a change is breaking to whether you want to ship it anyway. The blog post on [versioning without breaking your consumers](/blog/api-versioning-without-breaking-consumers/) works through how that plays out in a release.

Once the classification is automatic, the interesting question becomes what you do with the answer. A breaking change with an additive alternative is a design choice, which [backward compatible change](/guides/api-versioning/backward-compatible-api-changes/) covers. A breaking change with no alternative is a version, which is the subject of [when you must version](/guides/api-versioning/when-to-version-an-api/). Some breaks are not visible in a schema at all, such as retiring an error code a client branches on, and [designing API errors](/guides/api-design/designing-api-errors/) covers why that catalogue is part of the contract.

## In Routebase

Comparing two versions of a specification classifies every difference against a fixed rule set, so the same pair of versions produces the same verdict every time. The summary counts breaking changes, improvements and deprecations, and the detail tabs break the result down by endpoint and by schema, down to the level of individual fields, parameters and responses.

_Screenshot: Comparing two versions classifies every difference and counts the breaking ones before anything is released._

The same classification runs where it matters most. A merge request between a spec branch and its draft reports the breaking changes it would introduce. The release wizard warns you when the version you are about to promote breaks against whatever that environment runs today. The classification rules and the full breaking and non-breaking tables are documented in the [Spec Versioning guide](https://docs.routebase.dev/versioning/).

## Frequently asked questions

### What is a breaking change in an API?

A breaking change is any change that can stop working code from working, without that code being edited. Removing an endpoint, renaming a field, changing a type, adding a required input and removing an enum value all qualify. The test is not whether the change is large, it is whether a caller who was correct yesterday is still correct today.

### Is adding a field to an API response a breaking change?

Adding an optional field to a response is safe for any consumer that ignores what it does not read, which is what a tolerant reader does. It becomes breaking in practice when consumers validate responses against a closed schema that rejects unknown properties, or when a strongly typed client was generated with strict deserialization. That is why the additive rule is worth stating in your own documentation.

### Why is adding a required request field breaking but adding a response field not?

The asymmetry follows from who has to supply what. Whatever the caller must provide may only ever get easier, because an existing call has to stay valid. Whatever the caller receives may only ever get richer, because an existing reader has to keep finding what it looked for. Reverse either one and somebody's working code stops working.

### How do you detect breaking changes automatically?

Compare the two contract documents rather than the two codebases, and apply a fixed rule set to the differences. Structural changes such as removals, type changes and new required inputs are decidable from an OpenAPI diff alone. What a diff cannot see is a change of meaning behind an unchanged shape, so that class still needs a human to declare it.

---

[Routebase](https://routebase.dev/) — [Sign up](https://app.routebase.dev/): Every account starts with a 14-day Pro trial — no credit card required.
