A breaking change is any edit to an API description after which a client written against the old description stops working against the new one. Every other edit is safe. That sounds obvious, but in practice people sort changes by how big they look: a removed endpoint feels dangerous, a new enum value feels harmless. The better question is which way the data travels. A client sends requests and reads responses, so the same edit can be safe on one side and harmful on the other. Below you will find the direction rule, the edits that are most often misjudged, and a short routine for reviewing pull requests.
Why the version number is not enough
An API is a contract that other people write code against and then put into production, expecting it to keep working. Google's API design guidance says it plainly: a minor or patch release must leave current client code working, and older clients have to keep working against newer servers that share the major version (Google AIP-180). Semantic versioning draws the same line from the other side. Incompatible changes raise the major number, compatible additions raise the minor one, and compatible fixes raise the patch one (semver.org).
Both documents assume you can already separate compatible edits from incompatible ones. The numbers on a release only help once that judgement has been made, and making it by eye is where mistakes happen. The same guidance admits that the list it gives is indicative rather than complete, so treat any list, including this one, as a starting point for thinking.
The direction rule
Imagine a client built strictly from the old description. It sends only what the old description allows, and it prepares to receive only what the old description promises. Now ask what the new description does to that client.
Requests (what clients send) can become more permissive without harm, because everything the client already sends is still accepted. They break when they become stricter, because something the client used to send is now refused.
Responses (what clients read) can become more restrictive without harm, because the client already coped with the wider set of possibilities. They break when they become wider, because the client meets a shape it was never told about.
Everything below is this one idea applied to a specific edit.
Request side: stricter means broken
Each of these edits narrows what the server accepts, so existing callers fail:
- A new mandatory query parameter, request header or body property. Google's guidance forbids adding mandatory fields to existing requests (Google AIP-180). The client has nothing to send for it, so each call fails validation.
- An optional field turned into a required field. This is the same break, only quieter, because nothing was added to the file.
- An enum the client sends loses one of its values.
- Tighter limits, such as a lower ceiling, a higher floor, a shorter length cap or a stricter pattern.
- A type change after which some formerly valid values are refused.
- Null that used to be accepted and now is not, or
additionalPropertiesset to false so extra fields are rejected.
The mirror images are normally safe: an extra optional parameter, one more value in a request enum, looser limits, a mandatory field relaxed to optional. Looser limits deserve one caution: Google's guidance advises against raising the size limit of string fields accepted as input, and against changing the set of valid resource names in either direction, so check those separately (Google AIP-180). Google's guidance allows new values in enums that only requests use, which is the same direction rule in a different vocabulary (Google AIP-180).
Response side: wider means broken
Each of these edits widens what the client might receive:
- A field that disappears or gets another name. Google puts it bluntly: a rename is a remove plus an add, so the old name must stay (Google AIP-180). Optional response fields count as well, since clients may read them.
- A field that was always present becomes optional, or may now be null. Code that reads it without a check fails at the first record where it is missing.
- A type change that can now produce values the old type could not. An integer becoming a general number is the classic response-side case.
- An extra value in a response enum. The same guidance warns that user code may not handle new values gracefully and that such enums should say they can grow (Google AIP-180). If your description promises a closed list, adding to it breaks the clients that took you at your word.
Safe on this side: an extra field, an optional field that becomes guaranteed, fewer enum values, null ruled out, tighter limits.
Edits that look scary or harmless but are not
Renaming a path variable. Changing /tickets/{ticketId} to /tickets/{id} looks like a URL change, but the URL a client calls is identical. The OpenAPI specification itself counts two templates that differ solely in the variable name as one identical path (OpenAPI 3.1.0). Nothing breaks on the wire, although code generated from the description may still change its parameter names. Changing the shape of the path, by adding, removing or moving a segment, does break.
Changing a default. Setting a new default for an optional parameter does not touch the clients that pass a value, but every client that leaves it out now gets different behaviour. Google's guidance lists changed defaults as breaking for this reason (Google AIP-180). Merely writing down the default the server already used changes nothing.
Replacing a success status. A client that checks for the old success code now sees an unexpected one, so swapping or removing a documented success status is breaking. Documenting a new error response is not, because it only tells readers what could arrive, and no client could have depended on it.
Adding a security requirement. If an operation that was open now needs a credential, callers without it are refused. Removing a requirement is safe.
Moving a field between allOf branches, reformatting, reordering keys, turning YAML into JSON. The set of accepted and returned values is unchanged, which makes it no change at all. Judge what the schema permits, whatever its layout.
Descriptions, examples, tags and a deprecated flag. None of these change behaviour. Marking something deprecated is a warning to humans, not a break.
The shared schema trap
Most real descriptions put objects in a shared place and point at them with $ref. When one of those objects changes, the diff touches a single spot, yet every operation that uses it has changed, and each one is judged by the way that operation uses it. The same object may appear as a request body here and as a response there. A new required property is then a break for the first and nothing for the second, while a removed property is the opposite. A review that reads only the edited lines misses all of this, so trace each reference to its users.
Nullability is a related trap. Depending on the OpenAPI version, the same meaning can be written in more than one way. Moving a description from one spelling to the other is not a change; adding or dropping nullability is.
A check you can run
Run this routine on any pull request that touches an API file:
- Take the old and new versions side by side and note the format of each.
- For every difference, decide whether it sits on the request or the response side, tracing references to find each use.
- Apply the direction rule and mark each difference as breaking or safe.
- For each breaking one, write the field name, what it was, what it is now, and which callers fail.
- If nothing breaks, report that plainly and skip the safe edits.
To test your process, take a file, add one optional request field, and separately make an existing request field mandatory. A sound review flags only the second edit. Then add a response enum value; a sound review flags that too.
When this does not apply
Here the description is treated as the contract. If a server behaves differently from its description, the clients you break are the ones relying on actual behaviour, and no file comparison will show it. Clients that tolerate unknown fields and unknown enum values may survive edits listed here as breaking, and the same is true of internal APIs where one team controls both sides and can update them together; Google's guidance itself says such narrower cases should weigh their own requirements (Google AIP-180). Some edits depend on judgement, for instance a field whose meaning shifts while its type stays put. No experiments were run behind this text; it repeats public guidance and common review habits, so treat the lists as prompts and not as proof.
Another contract that is easy to get wrong across origins is covered in our walkthrough of which CORS settings let foreign pages read private replies.
Checking a change automatically
If you would rather have the comparison done for you, our api-breaking-change-check skill takes an old and a new OpenAPI description, in either YAML or JSON, or a pair of JSON Schemas for one request or response. It reports only the edits that hurt existing clients, one line per problem, carrying a fixed label, the place, the before and after, and the affected callers. It also follows shared references to every user, and skips safe edits such as a renamed path variable or a reformatted file. If nothing hurts, it says so in one short sentence.
Read this post as Markdown: /blog/openapi-breaking-changes.md · Atom feed.
