API Versioning Strategies That Don't Break Your Consumers
API versioning is the discipline that separates API providers who have operated public APIs for several years from those who have not. Early-stage API design focuses on what the API should do. Mature API design focuses on how the API will evolve without breaking the consumers who have built on top of it. The difference in focus reflects a difference in experience with the consequences of getting it wrong.
Breaking changes are changes that require consumers to modify their integration to continue functioning. They include removing endpoints, removing fields from responses, changing field names, changing data types, changing authentication requirements, and changing the semantics of existing parameters. Every team that has shipped a breaking change to a public API without a migration path has received the same feedback from affected consumers, expressed with varying degrees of professionalism.
URL Path Versioning
Including the version number in the URL path — /v1/users, /v2/users — is the most common versioning approach and the one that causes the least confusion among API consumers. The version is visible in every request. Consumers know exactly which version they are calling. Different versions can be deployed independently. Traffic can be routed to different backends based on the URL prefix.
The operational cost is maintaining multiple versions simultaneously. A team that ships a v2 must decide how long to support v1. Consumers who have not migrated will continue calling v1. The backend logic for v1 must be maintained until v1 is deprecated and eventually removed. The deprecation timeline must be communicated clearly and enforced consistently, which requires organizational discipline that not all API teams apply.
URL path versioning works well for major version changes — architectural changes that affect multiple endpoints, changes to authentication models, changes to fundamental resource representations. It is heavy-handed for minor changes that affect a single endpoint or field.
Header and Query Parameter Versioning
Versioning through request headers — Accept: application/vnd.api+json;version=2 — or query parameters — /users?version=2 — keeps the URL stable while allowing version selection. The technical implementation is equivalent to URL path versioning for routing purposes. The consumer experience is worse: the version is less visible, more easily forgotten, and harder to test with simple tools like curl.
Header versioning is common in enterprise APIs where URL stability is a requirement and the consumer audience is sophisticated enough to manage headers correctly. For developer-facing public APIs, URL path versioning’s visibility advantage is usually worth its verbosity cost.
Additive Versioning and the Expand-Contract Pattern
The most sustainable approach to API evolution is avoiding versioning entirely through disciplined non-breaking change management. Adding new fields to response payloads, adding new optional request parameters, adding new endpoints — these are all backward-compatible changes that do not require version increments. Consumers who ignore new fields in responses continue to function. Consumers who do not send new optional parameters receive behavior consistent with the parameter’s documented default.
The expand-contract pattern applies this principle to breaking changes: introduce the new behavior alongside the old, migrate consumers to the new behavior during a transition period, then remove the old behavior after a documented deprecation timeline. A field rename becomes: add the new field name, document both as equivalent, deprecate the old name, remove it after the deprecation window.
This approach requires the discipline to never make breaking changes without a transition period and the organizational commitment to honor deprecation timelines. It is more demanding than URL versioning because it requires careful field-level change management rather than the coarser-grained version isolation that URL versioning provides. When applied consistently, it produces APIs that evolve without forcing consumer-side changes for the majority of updates.
Semantic Versioning for APIs
Semantic versioning — major.minor.patch version numbers with defined rules about which increments indicate breaking changes — provides a communication framework that helps consumers understand the impact of upgrades. A major version increment signals breaking changes requiring migration. A minor version increment signals backward-compatible additions. A patch increment signals bug fixes.
The discipline of applying semantic versioning to APIs requires the same rigor it requires for libraries: correctly classifying every change as breaking, additive, or corrective, and incrementing the version number accordingly. Teams that treat all changes as patch increments or that increment the major version for non-breaking changes undermine the communication value of the versioning scheme.
The version number communicates intent to consumers. That communication is only valuable if the intent is accurate.