Breaking Change
Concept
A change to a published contract that stops working code on the other side, handled by splitting versions or announcing it far enough in advance.
Why it exists
Renaming something in code only you use is nothing at all. Every place that needs fixing sits in one repository, so you find them and fix them together.
Opening an application programming interface (API) and letting others connect to it changes that. An API is the agreed counter through which programs exchange things, and the code calling that counter is not in your repository. Theo deleted a field from a response that looked like nobody was reading it and shipped. Tests passed, the service stayed healthy. The next morning three integration partners wrote in to say their payment screens had stopped. All three had been reading that field.
The awkward part of an incident like this is that nobody made a mistake. Theo judged the field unused; the partners read exactly what the documentation described. What went wrong is that something one side thought it could change alone turned out to be an agreement between two.
So changes got sorted into two kinds: the ones that end at your own boundary, and the ones that stop working code elsewhere. Naming the second kind means the question can be asked in one sentence before a deploy.
How it works
Removing or altering something the caller already expects breaks them; adding something that wasn't there before doesn't. The whole test comes out of that one line. Breaking changes are usually one of four shapes.
- A field disappears from the response or gets renamed. Turn
emailintoemail_addressand the caller reads a field that no longer exists and paints an empty value on the screen. - A new required input appears. A request that worked yesterday is refused today, and a status code like 400 comes back to say the request was malformed.
- The format or meaning of a value changes. Timestamps switch from milliseconds to seconds, or a date gets written in a different format.
- An address or a default behavior changes. A counter that returned 100 records at a time starts returning 20, and anything that called it once and stopped walks away missing the other 80.
The third shape is the hard one. The first two throw errors and surface quickly, but a response with a changed format processes without complaint. A document uploaded yesterday shows a creation time in 1970 while the logs record nothing but successes, and days later someone reports that the sort order looks wrong.
The opposite property is called backward compatibility. Adding a field, adding an optional input, opening a new address — none of it touches callers still using the old shape. Instead of removing a field, you add the new one and keep sending the old one alongside it for a while.
Adding isn't unconditionally safe, though. If the receiving side is written to error on any field it doesn't recognize, an addition becomes an incident too. That's why counters exchanging human-readable formats like JSON write the rule down first: unknown fields are ignored.
Where it shows up
Anywhere somebody depends on a contract, the same problem appears. Three places come up most.
- Public APIs. When a counter built in a style like REST, where resources live at addresses, has been opened to the world, there is no way to know everyone using it — so instead of breaking it, API versioning keeps the old edition running next to the new one.
- Libraries and SDKs. Here the signal is the first digit under semantic versioning. Going from 2.4.1 to 3.0.0 announces that a breaking change is inside, and callers watch that digit to decide whether to hold an upgrade.
- Services inside one company. Column names in a database and the shape of events passing between services have the same character, and the moment two services deploy on different schedules they are no different from a public API.
When a change has to break something, it runs through a sequence.
- Publish a deprecation notice first. Say that something is going away and what to move to instead.
- Set a grace period. Six to twelve months is common, and the old path keeps working throughout.
- Count who is still calling. Request logs show how many are left, and when only a few remain you contact them directly.
- Remove it on the date you named.
Writing the contract in a machine-readable form like OpenAPI hands this judgment to tooling. Comparing the previous file against the new one surfaces removed fields and newly required inputs, which shrinks the set of things a person misses while scanning by eye.
How it sounds at work
"This is a breaking change. It isn't just a field rename — code on the other side stops running."
Pointing out that a small-looking edit is in fact the kind that halts outside services.
"We need a major version bump. Ship it as a minor and anything that auto-upgrades breaks."
Which digit of the version number moves is the same decision as whether a warning goes out.
"Let's publish the deprecation notice and give it six months."
Don't remove it now; give people time to migrate and take it out afterward.
At Pagedeck, Theo asks this question first in the pre-deploy review. Since the last incident, a change that removes anything from a response needs one more approval. Mia reschedules whenever an external integration is involved, because a change that needs a grace period isn't a task for this week — it's a six-month one.
Sometimes the news arrives through Sofia first. When a customer with an integration shares their own release schedule, the removal date gets moved to fit it.
What people get wrong
The most common reading is that a full green test run means it's safe. Everyone learns that passing checks before a deploy means no problems, so the inference is natural. But the code that breaks isn't in your repository. Your tests confirm the way you call your own service, and nothing checks which fields a partner reads. Leave that assumption in place and incidents keep getting discovered the morning after a deploy, through someone else's support ticket.
The other one is treating an announcement as making a change non-breaking. Telling people in advance feels like discharging the obligation. What an announcement changes is timing, not nature. The change still stops other people's code; the notice only offers a window to move before it does. Blur the two and a one-day notice starts sounding acceptable, and the idea of a grace period disappears.
Easily confused with
| Term | What it is | How it differs |
|---|---|---|
| Semantic versioning | A rule for expressing the nature of a change in three digits | A notation for announcing whether a breaking change is present. If a breaking change is the fact, this is the way that fact gets written down |
| API versioning | Splitting editions when a contract changes | A way to change things without breaking them. Leave the old edition running and open the new one separately, and the same change stops nobody |
Where to go next
- API
start with what is actually being broken and how the counter is put together
- REST
see the shape of counter that gets broken most often
- Deprecation
see the procedure that runs when something has to break
Last updated 2026-09-16