API Versioning
API versioning lets an API make breaking changes without breaking existing clients via URI, header, or media-type versions. Version only on breaking changes, keep active versions few, and pair with a clear deprecation policy.
API versioning is the discipline of evolving an API's contract while keeping existing clients working. Because published APIs have consumers you do not control, a breaking change — renaming a field, removing an endpoint, changing semantics — must be introduced in a way that lets old and new clients coexist during a migration window. Versioning provides that coexistence.
How It Works
Several mechanisms exist. URI versioning puts the version in the path (/v1/orders, /v2/orders); it is explicit, cache-friendly, and the most common, at the cost of arguably violating the idea that a URI identifies one resource. Header versioning sends the version in a custom header (X-API-Version: 2), keeping URLs stable but being less visible and harder to test by hand. Media-type (content) versioning uses the Accept header (application/vnd.example.v2+json), the most RESTful approach but the most complex. Query-parameter versioning (?version=2) is simple but easy to omit.
Good practice is to version only on breaking changes and to add new optional fields without bumping the version (tolerant readers). A clear deprecation policy — sunset dates, Deprecation/Sunset headers, and communication — is as important as the mechanism. GraphQL takes a different route, favoring continuous evolution with field deprecation over discrete versions.
When to Use It
Version any long-lived, externally consumed API: public APIs, partner integrations, and internal APIs with many independent consumers. Introduce a new version when you must make a backward-incompatible change. For purely additive changes, evolve in place rather than version, to avoid proliferating versions you must maintain.
Trade-offs
Every active version is code you must run, test, and secure, so versions are a maintenance liability — keep the number small and retire old ones. URI versioning leaks version into resource identity; header/media-type versioning is cleaner but less discoverable and harder to cache and debug. Without a real deprecation process, old versions live forever. Over-versioning (bumping for trivial changes) is a common mistake.
Related Patterns
HATEOAS aims to reduce versioning needs by letting clients follow links instead of hard-coding structure. A backend-for-frontend can absorb version differences per client. An anti-corruption layer can translate between versions internally. Pagination contracts are a frequent subject of versioning, and REST→GraphQL migrations replace versioning with field-level deprecation.
Example
# URI versioning — explicit and cache-friendly
GET /v2/orders/1042
# Media-type versioning — stable URL, version negotiated
GET /orders/1042
Accept: application/vnd.example.v2+json
# Signalling deprecation of v1
HTTP/1.1 200 OK
Deprecation: true
Sunset: Wed, 31 Dec 2026 23:59:59 GMT
Clients on v1 keep working while being told, via standard headers, when the version will be withdrawn.