API Breaking Changes Detector: The Six Schema Diffs That Quietly Break Every Client

API Breaking Changes Detector: The Six Schema Diffs That Quietly Break Every Client

You published version 2.0 of your API. The CHANGELOG says “additive changes, no breaking modifications.” The Slack channel is quiet. Three days later, support has 47 tickets, all variations of the same complaint: a field that used to be there is no longer there, or a field that used to be a string is now a number, or a status code that used to be 200 is now 204, and the client SDK reads response.body like it always has. The breaking change was hiding in plain sight — the diff said “additive,” but the diff was wrong about what “additive” meant for the clients that depend on the old shape.

This is the problem the API Breaking Changes Detector & Migration Planner at Elysia Tools was built to catch. You paste the old OpenAPI 3.x spec and the new one, and it walks the diff in a way that respects what a real client depends on — not just what the spec author thinks is additive. It also grades client impact and prints a migration plan you can hand to the team that owns the consumers. It is not a magic wand. It is a careful reader of a problem that has fooled every API team at least once.

The two kinds of “additive” that aren’t equal

The OpenAPI community uses the word “additive” the way a hotel uses “complimentary breakfast” — as a label that covers a wide range of expectations. From the spec author’s chair, additive means “I added a field, the spec is a superset, old clients should keep working.” From the client’s chair, additive means “the JSON I parse and the field I read did not change.” Those two definitions are not the same. Adding a new required field, for example, is additive in the spec sense but breaking in the client sense if the client sends a body that does not contain it. Renaming a field is technically additive if the old name still validates, but the field is unreadable to every client that hardcoded the old key. The detector sees both. Your eye, reading the diff, will probably see neither.

The six schema changes that quietly break every API client

The six schema changes that actually break clients

After watching dozens of API diffs run through the detector, the same six patterns account for almost every “this should have been backward-compatible” outage. They are not all obvious from the spec — that is the whole problem.

  • Removing a field from a response. The spec still validates, the response is still 200, the client still gets JSON — but the key it reads is gone. The most expensive kind of breaking change, because the client does not fail loudly; it just renders an empty value somewhere in the UI.
  • Removing a field from a request body. The opposite direction, but worse in a way: the client sends a field the server no longer cares about, the server silently drops it, and the client never finds out that the workflow it was triggering no longer fires.
  • Tightening a type. A field that was integer is now string, or a field that was number is now an enum of three values, or a field that was optional is now required. The old payload still passes schema validation in your test suite if the test was checking the new shape against the new spec — which is the only test that ever passes.
  • Widening a response type. The opposite trap. A field that was a single object is now a union of two objects, and the client that deserializes into the original shape throws an exception it has never thrown before.
  • Renaming without aliasing. The spec calls it customer_id, the old code calls it user_id, and the server still accepts both at the transport layer. From the spec’s perspective the new name is the canonical one and the old name is undocumented drift. From the client’s perspective, the field is gone.
  • Changing a status code. 200 becomes 204, 400 becomes 422, 404 becomes 410. The body is identical but the client’s status-code switch routes the response to a different handler, and now the same request is treated as success in one path and as an error in another.
Spec says additive, client says broken - the breaking change detector

Why a deprecation window is not a safety net

The most common false comfort in API design is the deprecation note. “We will keep legacy_id for 6 months, then remove it” sounds like a contract. It is a contract only if the consumers actually have time to migrate, and most teams discover the gap when a CLI script written by an analyst three years ago keeps calling the old field. The detector does not enforce deprecation windows — no tool can, because the window is a human process — but it does flag every field that has been deprecated and is approaching its removal date, so the migration plan you ship can call them out by name. That is usually the difference between a smooth transition and a postmortem.

Three impact buckets for API schema diffs - high medium and low

How to read the impact grade the detector prints

The detector’s output sorts changes into three impact buckets, and reading those buckets correctly is the difference between a migration plan that is a wall of text and one that is a checklist. The high-impact bucket contains the six patterns above — anything that can silently change the shape of a response or a request is high impact, regardless of whether the spec still validates. The medium-impact bucket contains things like adding a new endpoint, changing a default value, or adding an optional field to a request — they do not break existing callers, but they change the contract in ways that documentation and SDK generators need to track. The low-impact bucket is mostly cosmetic: description text, example values, server URL changes that point at the same host. If your diff has more than a handful of high-impact items, you do not have a 2.x release; you have a 3.0 release hiding inside a 2.x tag. Bump the major.

What a real migration plan looks like

A migration plan that actually gets adopted has three things: a list of consumers, a list of breaking changes each consumer is exposed to, and a deadline. The detector’s output gives you the second; the first is something only your team can build, ideally from access logs and SDK download records. The deadline is a function of the consumer’s release cadence, not yours — if your biggest consumer ships a major every 6 months, your 6-month deprecation window is a 12-month cliff from their perspective. The plan the detector prints is a starting point. The final version is the one you negotiated with the people whose pager will ring if you get it wrong.

If you remember nothing else, remember this: a breaking change is not a line in a CHANGELOG. It is a moment when a previously-working request, made by code that was written and shipped months ago, suddenly returns a different shape. The detector at Elysia Tools is honest about which diffs cross that line and which are just noise. Run it before you publish a new version of the spec, not after the support tickets start. The point of contract tooling is not to make you a more careful API designer. It is to make the difference between a thoughtful rename and a 3 a.m. pager the kind of thing you can see coming.

Explore more tools at elysiatools.com.

Comments

No comments yet. Why don’t you start the discussion?

    Leave a Reply

    Your email address will not be published. Required fields are marked *