Back

API Versioning Strategies That Age Well

When to version an API, how URI, header and additive evolution compare, and how to deprecate without breaking the integrations you rely on.

API Versioning Strategies That Age Well
Written by
BSH Technologies
Published on2026-06-02

The best API version is often the one you never had to cut

API versioning exists to let your service evolve without breaking the clients already depending on it. But a new version is a tax — on you to maintain it and on every consumer to migrate. So the first question is not which versioning scheme to use; it is whether this change needs a new version at all. A surprising amount of API evolution can happen without one, if you understand the difference between breaking and non-breaking change. Get that distinction right and you will mint far fewer versions, which means far less long-term maintenance and far less migration pain pushed onto the teams that integrate with you.

Know exactly what counts as a breaking change

A change is safe — backward-compatible — if existing clients keep working untouched. A change is breaking if it can make an existing client fail.

  • Safe: adding a new optional field to a response, adding a new endpoint, adding an optional request parameter with a sensible default.
  • Breaking: removing or renaming a field, changing a field's type, making an optional parameter required, changing default behaviour, or altering error codes clients branch on.

If you design responses so clients ignore fields they do not recognise, you can add to your API almost indefinitely without versioning. Most day-to-day evolution is additive, and additive change needs no version bump. This is also a contract you set on day one: tell consumers in your documentation that you may add fields and that they must tolerate unknown ones. A client that hard-fails on an unexpected field is fragile by its own choice, and setting that expectation early frees you to keep improving responses without cutting a version each time.

URI versioning is the honest default

When you do need a new version, putting it in the path — something like a v1 and v2 segment in the URL — is the most widely understood approach. It is visible in logs, easy to route, trivial to test in a browser, and impossible to apply by accident. The criticism that it is not theoretically pure matters far less in practice than the fact that every engineer who looks at the request immediately knows which version is in play.

Header-based versioning, where the version travels in a custom header or an Accept media type, keeps URLs stable and is more flexible, but it hides the version from casual inspection and makes debugging and caching fiddlier. A request that looks identical in the logs might be hitting two different behaviours depending on a header nobody printed. It is a reasonable choice for API-first products with disciplined clients and good tooling; for most teams the path-based approach causes fewer surprises and far fewer confused support tickets.

Version the whole API, not every endpoint

Resist the temptation to version individual endpoints independently. A matrix of endpoint-level versions becomes impossible to document, test, or reason about. Version the API surface as a whole, evolve it additively for as long as you can, and only mint a new global version when you genuinely must break compatibility. Consumers can hold a clear mental model of v1 and v2; they cannot hold one of forty endpoints each at their own version. The cognitive load of a per-endpoint version matrix lands on everyone — your team writing the docs, your support staff answering questions, and every developer trying to integrate.

Deprecation is a process, not an announcement

Shipping a new version is the easy half. Retiring the old one is where discipline pays off, because someone is always still using it.

  • Announce deprecation with a concrete date, not a vague intention.
  • Send a deprecation signal in responses — a standard Deprecation or Sunset header — so client developers see it in their own logs.
  • Instrument usage so you know who is still on the old version and can contact them directly.
  • Give a generous, clearly communicated window before you remove anything.

Turning off a version while real traffic still flows through it is how you turn a routine upgrade into an outage for a customer who never saw the memo. Usage metrics turn deprecation from a guessing game into a managed process: you can see the old version's traffic decline toward zero and reach out to the last stragglers by name before you pull the plug.

Document the contract, including how it changes

A versioned API is a promise, and the documentation is where that promise is written down. Keep an OpenAPI specification per version, maintain a changelog that records what moved between versions, and make the deprecation timeline easy to find. Consumers integrate faster and complain less when the contract and its evolution are explicit rather than discovered through failed requests. A machine-readable specification also lets clients generate their own bindings, which means a well-documented additive change can reach consumers with almost no manual work on their side.

How BSH can help

BSH Technologies designs and maintains APIs that other systems and teams depend on, with versioning and deprecation strategies built to avoid nasty surprises. If you are launching a public API, opening internal services to partners, or stuck supporting versions you can no longer safely retire, we can help you set a clear contract, evolve it additively, and sunset old versions without breaking the integrations that matter. Let us talk through your API roadmap.

From the blog

View all posts
Designing Multi-Tenant SaaS That Scales
Software Dev

Designing Multi-Tenant SaaS That Scales

Choosing an isolation model, keeping tenant data separate, and dodging the noisy-neighbour and migration traps that bite SaaS later.

BSH Technologies
BSH Technologies · 2026-06-14
Hitting Green Core Web Vitals in Next.js
Software Dev

Hitting Green Core Web Vitals in Next.js

A practical guide to LCP, INP and CLS in Next.js — image handling, font loading, the App Router boundary, and costly third-party scripts.

BSH Technologies
BSH Technologies · 2026-06-10