Four dominant API versioning patterns still shape most production platforms in 2026. Use this cheat sheet to ship breaking changes safely. Read now.
The four patterns that still dominate
Most production APIs still land on one of four versioning shapes. Path versioning puts the version in the URL, such as /v1/orders. Query-parameter versioning leaves the path stable and passes something like ?api-version=2. Header versioning keeps the URL clean and moves the choice into a custom header or the Accept value. Media-type versioning is a stricter form of that idea: the client asks for a specific representation, for example an application type that embeds the version in the media type string.
None of these is universally “correct.” Path versions are obvious in logs, docs, and support tickets. Query parameters are easy to default and roll out gradually. Headers and media types keep resource identity stable, which helps caches and hypermedia-style clients, but they are harder to discover and easier to misconfigure. Pick the pattern your clients can actually see, set, and debug—not the one that looks cleanest on a whiteboard.
What counts as a breaking change
A change is breaking when a well-behaved existing client can start failing, getting wrong data, or losing a guarantee it previously relied on. Removing or renaming fields, tightening validation, changing error shapes, altering auth requirements, shifting default sort or pagination, or redefining status codes all qualify. Additive changes—new optional fields, new endpoints, new enum values that old clients ignore—usually do not, if clients were built to tolerate unknown properties.
Document the contract explicitly: required vs optional fields, nullability, idempotency, rate-limit behavior, and how unknown fields are handled. If that contract is fuzzy, every release becomes a negotiation. Versioning only works when both sides share a clear definition of “compatible.”
Shipping a break without stranding clients
Treat a major version as a parallel surface, not a flip of a switch. Introduce the new version beside the old one, migrate traffic deliberately, and retire the old surface on a published timeline. Give clients a migration path they can complete in their own release cycle: dual-write or dual-read windows where needed, clear deprecation headers or response metadata, and changelog entries that name the old field, the new field, and the last date the old behavior will work.
- Announce the break early with examples of before/after requests and responses.
- Keep the old version frozen for fixes only; put new features on the new version so migration has a payoff.
- Measure adoption by version in telemetry so sunsets are based on real traffic, not guesswork.
- Provide a short compatibility shim only when it is temporary and explicitly dated—not as a permanent second brain in the server.
Operational rules that keep versions maintainable
Limit how many active major versions you support at once. Every concurrent version multiplies test matrix, security patches, and docs. Prefer coarse, rare major bumps over a long trail of half-compatible minors. Inside a major version, stay additive; when you must break, cut a new major and stop extending the old one.
Wire version selection in one place—gateway, router, or content-negotiation layer—so handlers do not invent their own rules. Log the resolved version on every request. In tests, pin clients to a version and add contract tests that fail when a supposed non-breaking change actually removes or renames something. Versioning is not a labeling trick; it is a product process for changing public interfaces without surprising the people who depend on them.