API Versioning: 9 Rules for Changes Without Breaking Clients
API versioning is the practice of managing changes to an API contract so existing clients can continue working as a service evolves. An API may be used by a mobile app, a partner integration, an internal service, or customer-written scripts. A change that seems small to the team maintaining the API can break consumers that depend on its current behavior.
This guide explains which changes need a new version, common versioning approaches, nine rules for safe evolution, and how to communicate and retire older versions responsibly.
What counts as an API contract?
An API contract includes more than endpoint paths. It can define request and response fields, authentication, validation, error formats, pagination, ordering, rate limits, and the meaning of an operation. Clients build software around these expectations. If an API begins behaving differently without notice, consumers may fail even when the URL remains unchanged.
Microsoft’s API design guidance frames an API as a contract between services and clients and recommends backward-compatible changes where possible. Google’s API versioning guidance describes how one large API ecosystem communicates major versions and deprecations. These are examples, not universal standards, so teams should document a policy that fits their consumers.
Breaking and nonbreaking changes
A change is usually breaking when a previously valid client request or response no longer works as expected. Removing or renaming a field, changing a field’s type, changing an operation’s meaning, or tightening validation can break clients. A new optional response field may be safe if clients ignore fields they do not recognize, but that assumption must be checked against the actual consumer ecosystem.
Even additive changes can surprise consumers. Some clients reject unknown fields, compare entire JSON responses, or use generated models with strict parsing. Therefore, define compatibility from observed client behavior and test representative consumers before release.
Common API versioning strategies
Version in the URL path
A path such as /v1/orders or /v2/orders makes the version visible and easy to route. It is widely understood and works well when the full API surface changes together. The trade-off is that the service may need to support multiple route sets while consumers migrate.
Version in a query parameter
A request can include a value such as ?api-version=2026-01. This keeps the resource path stable and makes the requested contract explicit. It also adds a required parameter that clients must manage, so defaults and validation need careful handling.
Version in a header
A header can select a contract while leaving the URL unchanged. This can keep resource paths tidy, but the version is less visible in a browser address or basic request log. Ensure that gateways, caches, observability tools, and support teams preserve the header.
Version by media type
A content-negotiation header can indicate the representation or version the client accepts. This supports versioning at the representation level, but it can be harder for some consumers to discover and debug. Consistent examples and client libraries help.
Pick one primary method for an API family and apply it consistently. Microsoft’s REST API guidelines discuss version placement in paths or query parameters, while Google AIP-185 describes a major-version path convention for REST interfaces. The best scheme is one clients can understand and your organization can support.
Nine rules for safe API evolution
1. Write down what is compatible
Publish examples of breaking and nonbreaking changes. Cover fields, errors, authorization, pagination, ordering, and behavior. This gives product, engineering, and partner teams a shared basis for review.
2. Prefer additive changes
Add optional fields, new endpoints, or new operations when they meet the need without changing existing behavior. Keep defaults stable and ensure old clients continue to work. Adding a required request field is usually not additive from a consumer’s point of view.
3. Use a new major version for incompatible contracts
When a breaking change is necessary, provide a new version while preserving the old one for a stated transition period. Do not silently repurpose a field or endpoint and assume every consumer will adapt at the same time.
4. Keep versions supportable
Each active version creates testing, documentation, deployment, and support work. Avoid maintaining many slightly different versions indefinitely. Group changes into deliberate releases, and plan migrations before introducing another version.
5. Detect consumers before changing behavior
Review access logs, API keys, client identifiers, and support history to understand who uses the endpoint. Logs may not identify every consumer, especially where traffic passes through shared credentials, so combine data with direct communication.
6. Provide a clear migration path
Document the old and new behavior, include request and response examples, and list the code changes consumers must make. When practical, provide migration tooling, a test environment, or a compatibility layer that lowers the effort.
7. Test real clients and contracts
Use contract tests, schema validation, and integration tests to detect mismatches before deployment. Test the consumer as well as the provider. A server-side test cannot prove that a mobile app, partner, or older library handles the change correctly.
8. Communicate deprecations with dates and ownership
State which version is deprecated, the sunset date, the reason, and the replacement. Give consumers enough time to plan, and provide a contact or support route. For widely used public APIs, the transition may need to span several release cycles.
9. Measure migration and retire deliberately
Track requests by version and client, notify remaining consumers, and confirm the new version meets the intended need. Retire the old version only after the stated policy is met and operational owners are ready for the change.
How to plan a version migration
- Inventory the change. List endpoints, fields, semantics, and client groups affected.
- Classify compatibility. Review the documented policy and ask consumer teams about edge behavior.
- Choose the version boundary. Keep unrelated changes out of the release when that makes migration harder.
- Implement both paths. Route old and new clients to the correct contract and use shared business logic where safe.
- Test compatibility. Run provider, consumer, and end-to-end contract checks.
- Announce and support. Publish dates, examples, and a way to report migration problems.
- Observe traffic. Track adoption and errors without logging sensitive request data.
- Retire and remove. Shut down the old version on schedule, then delete unused compatibility code and update documentation.
Versioning and implementation releases are different
API versioning communicates changes in the external contract. Internal software releases may use a separate build number, semantic version, or deployment identifier. A code deployment that does not change the public contract may not need a new API version. Conversely, a breaking contract change needs clear consumer handling even if the underlying service was deployed as a minor release.
Keep contract versioning, client SDK versions, and deployment versions distinct in documentation and monitoring. This helps teams answer which client called which contract and which implementation served the request.
Example: migrate clients from v1 to v2
Assume an order API returns a single delivery address, but a new product needs separate billing and shipping addresses. Renaming the old field in place could break applications that deserialize the original response. A safer approach is to define a v2 contract, document the new fields, and run both versions while consumers update.
Publish example requests and responses, a migration table, and a test endpoint. Add telemetry that distinguishes v1 from v2 and identifies consumers where possible. Notify owners directly, monitor error rates and adoption, and set a retirement date consistent with published policy. Before shutdown, confirm that remaining traffic is understood and that the replacement supports the same business need.
Deprecation is a product process
Consumers may need a release cycle, security review, budget, or mobile app update to migrate. A deprecation notice should identify the affected functionality, the reason for the change, the replacement, the key dates, and the support channel. Avoid announcing a sunset without a workable alternative or clear evidence that clients can meet the deadline.
For internal APIs, service owners can coordinate migrations directly with consuming teams. For public APIs, the provider may need dashboards, email notices, changelog entries, and a longer transition. A documented support window prevents every version from becoming permanent while giving clients predictable time to respond.
Versioning governance without slowing every change
Review contract changes during design and code review. Maintain a checklist for field removal, type changes, error semantics, pagination, authorization, and default behavior. Schema-diff tools can flag potential breakages, but reviewers must still understand business meaning and client expectations.
Keep API documentation, SDKs, examples, and changelogs aligned. If only the schema changes while examples remain stale, consumers may implement the wrong contract. Assign a single team to own each public interface and publish a support matrix for active versions.
A clear API documentation plan helps consumers understand a version’s behavior and migration path. Pair documentation with contract tests so the published examples match what the service actually returns.
Set a support policy before publishing versions
A version policy should state how long stable versions remain available, how the organization announces deprecation, what security fixes older versions receive, and how exceptions are handled. These details matter because teams otherwise make inconsistent promises to different clients. Align the policy with customer agreements, regulatory duties, and the effort required to migrate client software.
For internal APIs, use service ownership maps to find consumers and coordinate upgrade work. For external APIs, publish status in the developer portal and changelog, and send notice through the channels customers actually monitor. Keep a record of who received the message and when it was sent.
Use telemetry to make retirement decisions
Track requests by API version, client application, and error category. Avoid putting credentials or sensitive payloads in logs. When traffic remains on an older version, investigate whether the client owner is unknown, the migration guide is incomplete, or the replacement lacks a required feature. Address the reason before extending a deadline by default.
After retirement, review support tickets and integration failures. Feed the lessons into the next migration plan. An API versioning policy improves over time when it reflects how consumers actually upgrade, not only how the service team expects them to behave.
Keep version information consistent across the API specification, SDK release notes, and partner API documentation. Clear examples reduce misunderstandings and make client testing more reliable.
Frequently Asked Questions
When should an API get a new version?
Create a new version when a change is incompatible with clients that rely on the current contract. Prefer backward-compatible additions when they can satisfy the business need.
Are new fields always backward compatible?
No. Many clients ignore unknown fields, but strict parsers or generated models may fail. Test actual consumers and document the compatibility assumptions.
Should API versions use dates or numbers?
Either can work if clients and operators understand the scheme. Choose one convention, define what changes trigger a version, and use it consistently.
How long should an old API version stay available?
Set the period based on customer contracts, migration effort, risk, and support policy. Communicate a clear deadline and monitor actual usage before retirement.
Conclusion
API versioning lets a service evolve while giving consumers time and clarity to adapt. Define compatibility, favor additive changes, test real clients, and treat deprecation as a managed migration. A consistent policy reduces surprises and helps teams make improvements without losing the trust of the systems that depend on them.