Journal

API Versioning: Plan a Safe Consumer Migration

Published by Tahseen K. on Engineering & Architecture / Delivery & Quality

What does API versioning need to protect?

API versioning gives consumers a way to use a defined interface while the provider changes it. For a breaking change, keep the old contract available during an agreed migration period, make the new contract selectable, and retire the old one only after checking who still uses it. A version label alone does not prove that clients have migrated or that both versions behave correctly.

The practical decision is not just where to put v1 or v2. An engineering lead needs to know which requests, responses, events, and defaults each consumer uses; who owns the migration; and what evidence permits retirement. Use the custom integration plan to identify connected systems first, then use the worksheet below for a specific API change.

Which changes need a new contract?

Treat a change as breaking when a consumer following the current contract may need to change to keep working. Removing a field, changing its meaning or type, adding a required request field, or changing an error a client relies on can break that consumer. An apparently additive response field can also cause trouble if a client rejects unknown fields. Check the actual contract and consumer behavior before deciding that a change is compatible.

Microsoft’s API design guidance discusses backward-compatible changes and versioning when an interface must change. A version does not replace a written compatibility policy: state which fields and behaviors clients may depend on, how long versions are supported, and how consumers receive notice of changes.

Some changes can remain compatible through an optional field or an additional endpoint. That avoids running two contracts for a change that does not require it. When an incompatible change is necessary, a separate selectable version gives consumers time to update. The provider must still maintain and secure each supported version.

How should consumers select a version?

Choose a selector that your clients, gateways, caches, documentation, and monitoring can handle consistently. The selector identifies the contract; it does not decide whether a change is compatible.

Selector Example Tradeoff to check
URL path /v2/orders Easy to see in a request; clients and links must change when the path changes.
Query parameter /orders?version=2 Keeps the resource path stable; make sure gateways and caches distinguish the parameter.
Request header A documented version header Keeps version out of the URL; logs, clients, and caches must retain the relevant header.
Media type A versioned Accept header Can express a representation version; requires clients and intermediaries to handle content negotiation.

Define what happens when a request has no version, an unsupported version, or conflicting selectors. An automatic default that changes to the newest version can break an unpinned client. A fixed default can preserve behavior, but the provider must track those requests before retiring it. Return a documented error for unsupported versions and include the selected version in safe operational records, without logging credentials or sensitive payloads.

Providers make different choices. For example, Stripe documents an account default and an explicit request header, with separate rules for webhook endpoints and some client libraries. Those are Stripe’s rules, not a universal API behavior. Check the actual provider’s contract when consuming an API.

Build a consumer and compatibility record

Before announcing a retirement, list each known consumer and the contract it actually uses. Include batch jobs, mobile apps, external partners, webhooks, and integrations owned by another team. Request logs can reveal active traffic, but they do not prove that an infrequently used integration no longer exists. Assign an owner to investigate unknown traffic and consumers that cannot be contacted.

Use this proposed record for each consumer and operation:

Record Decision or evidence to keep
Consumer and owner Application, environment, contact and business owner; mark unknown owners for investigation.
Current contract Version or default, operations, fields, events and authentication used.
Change and impact Request, response, error or event differences; identify data and workflow consequences.
Target and test Target version, test environment, contract cases and acceptance owner.
Migration state Planned release, observed target-version use and unresolved failures.
Retirement decision Notice, support deadline, exceptions and evidence that permits shutdown.

Avoid treating a successful HTTP response as proof of compatibility. A response can succeed while a changed amount, status, or field meaning produces the wrong business result. Agree on expected data and side effects with the consumer. The separate API integration testing guide covers how to exercise those contracts and failure cases.

Walk through a hypothetical change

Suppose an orders API returns a single status field, and a new contract separates payment status from fulfillment status. A reporting job uses the old field; a partner application also reads it but deploys on a different schedule. This example is a planning exercise, not a claim about a Hapy client or tested system.

  1. Define both contracts. Document how v1 computes status and how v2 represents payment and fulfillment. State what happens when an order’s states do not map cleanly to the old field.
  2. Keep v1 behavior stable. Serve the documented old representation during the support period. Test the mapping against representative states; do not silently reinterpret the old field as one of the new fields.
  3. Assign migration owners. The reporting team checks its calculations against v2. The partner confirms its release window and test results. Record any unknown consumer using the default version.
  4. Compare business outcomes. In a test environment, check paid, unpaid, partially fulfilled, cancelled, and ambiguous orders. Compare expected reports and workflow decisions, not only response shapes. Use approved test data.
  5. Decide whether to retire. Require owner acceptance, an agreed period of observed traffic, resolution of unknown or exceptional consumers, and a rollback or recovery plan. If the partner cannot migrate, agree on a supported exception or a different date; do not assume silence means consent.

The right observation period depends on how often each integration runs. A monthly job can be missed by a short quiet period. Monitoring is evidence for a retirement decision, not proof that an unseen client cannot return.

How do you retire an old version?

Publish the support deadline, the changes clients must make, and a contact for migration help. Notify known owners directly and repeat notice through the channels they use. Record what happens after the deadline, including any approved exception and how to recover if a consumer was missed.

An API can also use the Sunset HTTP header to signal that a resource is expected to become unavailable at a specified time. The header is a useful machine-readable signal, but it does not establish that a client saw the notice or completed its migration. Confirm that clients and intermediaries can receive it; use direct communication as well.

Before turning off a version, review security and operational obligations for both versions. If a serious vulnerability prevents keeping the old version available, communicate the risk and agree on an emergency response. Compatibility cannot justify leaving an unsafe interface running indefinitely.

What should the release plan test?

Test version selection and the missing-version default, unsupported versions, and each breaking request and response change. Test old and new contracts against the same meaningful scenarios, including validation errors and authorization. If the API sends events, test the event contract and its version selection separately; a successful request upgrade does not necessarily upgrade a webhook.

Track traffic and errors by selected version and consumer where identification is reliable. Investigate failures and unknown traffic before retirement. If a release must be reversed, define which application and data changes can be rolled back and which need forward repair. Share the plan with the people who operate the integration.

Hapy builds business systems and integrations. If your team needs help planning a change across connected systems, bring the consumer record, compatibility policy, and proposed retirement decision to the discussion.

Further questions

Can one application use two API versions at once?

It can if the provider supports both versions and the application can keep their requests and data handling separate. Test each operation and event against the version it uses.

Is semantic versioning required for an API?

No. A provider can use dates, major version numbers, or another documented scheme. The important point is to explain what each label means and which changes require consumer action.


Share with others

Continue reading

More from the journal