API Versioning for Systems That Outlive Their Contracts
Public sector integrations have unusually long lives. The consumer you cannot contact is the one that decides your versioning strategy, and most teams choose before they understand that.

The Consumer You Cannot Contact
Commercial API strategy assumes you can reach your consumers. You publish a deprecation notice, email the registered developers, watch the traffic drain off the old version, and switch it off.
Public sector integration frequently violates that assumption. An endpoint is consumed by a system at another agency, procured nine years ago, maintained by a contractor whose contract ended, running inside a network you have no visibility into. Nobody at your organization knows it exists until it breaks and someone calls.
This is not an argument for never changing anything. It is an argument for making change legible and slow enough to survive, and for choosing a versioning scheme on the assumption that some consumers will never read anything you publish.
Version the Contract, Not the Release
The most common mistake is tying the API version to the software version, so v2.4.1 of the service produces /v2.4.1/ in the URL. This trains consumers to pin to something that changes constantly and produces a support matrix nobody can maintain.
The API version should change when, and only when, the contract you promised is no longer honored. That is a much rarer event than a deployment. A well-run API can go years without a version bump while shipping continuously.
Which changes break a contract is worth writing down explicitly, because reasonable engineers disagree:
- Breaking: removing a field, renaming a field, narrowing an accepted value range, changing a type, making an optional request parameter required, changing the meaning of an existing value, changing default sort order that consumers rely on.
- Not breaking: adding a new optional request parameter, adding a field to a response, adding a new endpoint, adding a new enum value if you documented that consumers must tolerate unknown values.
That last caveat carries real weight. If you never told consumers to ignore unrecognized enum values, adding one is a breaking change in practice regardless of what your policy says, because somewhere a switch statement will throw.
Put the Version Where It Cannot Be Missed
There is a long-running argument between URI versioning and content negotiation through an Accept header. The header approach is more architecturally pure. The URI approach wins in this environment for reasons that have nothing to do with purity.
A version in the path is visible in a log line, a firewall rule, a browser address bar and a screenshot in a support ticket. It survives being copied into a config file by someone who does not know what a media type is. It can be routed on by infrastructure that cannot inspect headers. When an integration breaks at 2am and the person debugging it has no documentation, a URI version tells them something a header does not.
Either a major number or a date works. Dates carry more information and read well in a deprecation schedule. Whichever you choose, never let the unversioned root be an alias for "latest": every consumer who omits the version gets silently upgraded into a breaking change. Make the bare path an error that explains itself. We cover the rest of the contract surface in API engineering work.
Deprecation Needs a Date
A deprecation without a date is a preference. Announcing that v1 is deprecated and will be removed "in a future release" produces exactly zero migrations.
A workable deprecation has four parts: a named successor, a date, a signal in the response itself, and a way to find out who is still affected.
- The signal belongs in the response, because that is the only channel every consumer is definitely connected to. A
Deprecationheader and aSunsetheader with the removal date reach systems that no mailing list will. - The audience comes from your own logs. Log the version and an identifying credential on every request, and you can answer "who is still on v1" with a query instead of a guess.
- The date should be generous in this context. Twelve months is not unreasonable when the consumer's change has to pass through someone else's procurement cycle.
Brownouts are worth knowing about: deliberately failing the deprecated version for a short window, announced in advance, some weeks before removal. It surfaces the consumers who never read anything, at a moment when the fix is still easy, rather than on the day you switch it off for good.
Keep the Number of Live Versions Small
Every live version is a testing obligation and a security-patching obligation. Two is comfortable. Three is a stretch. Beyond that, the versions stop being maintained in any real sense and become a promise you are not keeping.
The way to keep the number small is not to version less often but to break less often. Most of that comes from decisions made when the first version is designed: return objects rather than bare arrays so you have somewhere to add fields, avoid leaking internal identifiers that you will want to change, and be conservative about what you promise regarding ordering and pagination.
An API you designed carefully once will need fewer versions than one you have to keep correcting, which is the cheapest form of version management available. Much of that comes down to conventions applied consistently from the first endpoint, which we covered from the implementation side in building secure and scalable REST APIs.
If you are about to publish an interface that other organizations will build against, we can review the contract before it becomes permanent.
Somewhere there is a consumer you cannot contact, maintained by nobody, that will keep calling your endpoint for years. Design the deprecation for that one.
Most API guidance is written for products whose consumers are reachable, attentive and commercially motivated to keep up. Public sector integrations violate all three assumptions, and the advice has to change accordingly.
Longevity Is the Requirement
The practical consequence is conservatism at the design stage and generosity at the deprecation stage. Both are cheaper than the alternative, which is an unplanned outage in someone else's service that becomes your incident.
We design and review integration contracts intended to stay stable well past the contract that funded them.