IAnswerable

Version Your API Before You Need To

20 Feb 2026

You don’t need an API versioning strategy on day one. You need one the first time you want to change a response shape and you already have clients depending on the old one. Decide before that day arrives, because deciding during an incident goes badly.

URL versioning vs header versioning

URL versioning Header versioning
Example /v2/orders Accept: application/vnd.api+json; version=2
Discoverable Yes, visible in every request No, hidden in headers
Caching Trivial, different URL = different cache key Needs Vary header handling
Client burden Change the path Change a header client libraries often bury
Common in Public APIs (Stripe, GitHub) Content-negotiation-heavy APIs

For a small team, use URL versioning. It’s the one your clients will actually get right. Header versioning is more “correct” in a REST-purist sense, but it fails silently — a client that forgets the header just gets whatever the default version is, with no error. URL versioning fails loudly: hit /v3/orders when only /v1 and /v2 exist, get a 404.

Version the whole API, not individual endpoints

Don’t let /v1/orders and /v2/users coexist under one deploy with no relationship between them. Pick a single version number for the whole surface. It’s less flexible in theory, more predictable in practice — a client that’s on v1 knows every endpoint behaves the v1 way, full stop.

/v1/orders
/v1/users
/v1/payments

/v2/orders   <- new shape
/v2/users    <- unchanged, but still under v2
/v2/payments <- unchanged, but still under v2

What actually forces a new version

Not every change needs one. A new optional field, a new endpoint, a relaxed validation rule — none of these break existing clients. Reserve a version bump for:

If you’re not sure, ask: would a client written against the old docs, unchanged, start failing? If no, ship it without a version bump.

The migration path that doesn’t break clients

  1. Ship v2 alongside v1. Never replace v1 in place — that’s not a version, that’s a breaking change with better branding.
  2. Log every request’s version so you know who’s still on v1 before you touch it.
app.use((req, res, next) => {
  const version = req.path.split('/')[1] // 'v1' or 'v2'
  metrics.increment(`api.requests.${version}`)
  next()
})
  1. Set a deprecation date and put it in the response, not just the docs:
Sunset: Sat, 31 Jan 2026 00:00:00 GMT
Link: <https://api.example.com/v2/orders>; rel="successor-version"
  1. Email the clients still hitting v1 two weeks before you flip the switch. Your metrics from step 2 tell you exactly who to email — don’t guess.
  2. Turn off v1 only after traffic to it is near zero, not on a fixed calendar date regardless of usage.

The failure mode to avoid

The common mistake is skipping step 1 — patching v1 in place because “it’s a small change.” It never stays small. Six months later you have an API where the docs say one thing and three different client versions each expect something slightly different, and nobody can tell you which behavior is load-bearing for whom. A version bump costs you a few hours of routing setup. Silent breakage costs you a support queue.