Engineering

API Versioning Without Breaking Every Client: A Practical Framework

By Priya Nair, Senior Backend Engineer·Engineering·September 26, 2024

Most teams don't have an API versioning strategy until the first time a schema change breaks a client they didn't know existed. The change itself is usually reasonable — a field renamed for clarity, a type tightened, a deprecated endpoint removed. The actual failure is that nobody could say, before shipping it, exactly which callers depended on the old shape.

The Real Problem Isn't Versioning Strategy, It's Visibility

Teams debate URL versioning versus header versioning versus no versioning at all, but that debate is downstream of a more basic gap: most APIs don't track which clients call which fields, so a "safe" change is really a guess. Whatever versioning scheme you pick, it only works if you can answer "who calls this" before you change it, not after something breaks.

Additive Changes Are Free. Everything Else Isn't.

Adding a new optional field or a new endpoint doesn't break an existing client that ignores unknown fields — most JSON clients do this by default. Removing a field, changing a type, or changing what a status code means always has the potential to break something, no matter how minor the change looks in a diff. Treating those two categories differently is most of what a versioning strategy actually needs to get right.

A Framework That Actually Holds Up

Version the Contract, Not Every Endpoint Individually

We version the API as a whole (/v1, /v2) rather than individual endpoints, so clients reason about one compatibility boundary instead of tracking version numbers per resource. A new major version is warranted only when a genuinely breaking change is unavoidable — most feature work should fit inside the existing version as additive changes.

Deprecate With a Real Date, Not an Indefinite Warning

A deprecation notice with no removal date trains clients to ignore deprecation notices, because nothing has ever actually broken after one. We set a concrete sunset date when a field or endpoint is deprecated, publish it in the response itself (a Deprecation header, not just documentation nobody reads), and hold the date — inconsistently enforced deadlines cost more trust than a shorter, real one.

Track Consumers Before You Touch Anything

Before a breaking change ships, we confirm which clients call the affected field or endpoint — through API gateway logs, request analytics, or a required client identifier on every call if neither of those exist yet. "We don't know who's still using this" isn't a reason to ship the change anyway; it's a reason to add the visibility first.

What This Looks Like Day to Day

  • New fields ship as optional additions on the current version, with no version bump required
  • Breaking changes are batched behind a new major version rather than trickled into the current one, so clients migrate once instead of chasing a moving target
  • Old versions stay live and supported through their published sunset date, then are actually retired on schedule — not extended indefinitely because someone's still using it

A Practical Example

A logistics client's partner-facing API had accumulated three years of ad hoc changes with no consistent versioning, and a planned schema cleanup risked breaking an unknown number of integration partners who'd built against whatever the API happened to look like when they onboarded. Before making any change, we added request-level client identification through the API gateway and spent two weeks just watching which fields were actually in use. The data showed a handful of partners depending on a field the team had assumed was unused and ready to remove. We shipped the cleanup as a new major version instead, migrated the identified partners individually with a real sunset date on the old version, and retired it on schedule with zero broken integrations — because the visibility work happened before the change, not as incident response after it.

The Actual Lesson

API breakage is rarely a versioning-scheme problem. It's a visibility problem — teams ship changes without being able to say who's actually affected, then find out from an angry integration partner instead of a report. Get consumer visibility in place first, and most versioning decisions become straightforward.

If your team is maintaining an API with unclear consumer visibility, our engineering team can help instrument that before your next breaking change ships instead of after.

#Architecture
Back to all articles

Related Articles