Skip to content

Versioning Strategies

When a change would break existing clients, you need a versioning strategy so old and new clients can coexist. There are three common places to put the version.

First, decide whether you even need a new version. Non-breaking (additive) changes never require one:

  • Adding a new endpoint or a new optional field.
  • Adding a new optional query parameter.
  • Adding a new value to an output-only enum (if clients are told to tolerate unknowns).

Breaking changes do require one: removing or renaming a field, changing a type, making an optional field required, or changing the meaning of a response.

  • URI versioningGET /v1/articles. Most visible and easiest to route and cache; the version is right there in the URL. Downside: the same resource lives at two URIs across versions.
  • Header versioning — a custom or standard header like API-Version: 2. Keeps URIs clean, but the version is invisible in logs and harder to try in a browser.
  • Media-type versioning — negotiate via Accept: application/vnd.example.article+json;version=2. The most RESTful (the version is part of the representation), but the most awkward to use.
GET /v1/articles # URI versioning
GET /articles # header versioning
API-Version: 2
GET /articles # media-type versioning
Accept: application/vnd.example.article+json;version=2

For most teams, URI versioning wins on pragmatism: it is obvious, cache-friendly, and trivial to test. Whatever you choose, version the whole API at a coarse grain (v1, v2), not each endpoint independently — per-endpoint versions explode the support matrix.

Which is a breaking change that warrants a new version?
Which versioning strategy puts the version directly in the path?
At what granularity should you version?