Version the contract, not the code
The versioning schemes that aged well were tied to the request and response shape, not internal refactoring milestones. A v2 designation should mean the shape changed, not that the engineering team cleaned up the codebase.
When an engineering organization undertakes a massive backend rewrite, there is always a strong temptation to increment the API version. A team might spend six months migrating a legacy monolith into a modern microservice architecture. Because the underlying technology is completely new, they deploy the endpoints under a /v2/ routing path to celebrate the milestone. The problem is that the JSON payloads, the authorization headers, and the HTTP methods remain exactly the same.
The Consumer Perspective From the perspective of a downstream consumer, your internal architecture is completely irrelevant. An API version is not a release tag for your deployment pipeline. It is a strict structural contract between two systems.
If you force a client to update their integration to point to a new version, they expect a material change in how they interact with your service. When the request and response shapes are identical, you have forced a migration coordination cycle purely for internal vanity. You burn goodwill with your external partners and internal frontend teams by making them do unnecessary work just to accommodate your infrastructure refactor.
Defining a Breaking Change The only trigger for a major version bump should be a breaking change to the contract itself. This occurs when you change the data type of an existing field, when you drop a property that clients actively read, or when you fundamentally alter the pagination mechanism.
If you want to add new optional fields or introduce a new endpoint, that is an additive change. It belongs in the existing version. If you completely rewrite the database query layer to make the response ten times faster, the version stays exactly the same. The client still sends the same payload and receives the same structure, just with lower latency.
Decoupling Deployment from the Interface To build stable enterprise platforms, you must fiercely decouple your internal engineering milestones from your external interfaces. You can deploy version five of your backend infrastructure while still proudly serving version one of your API contract.
Versioning the contract rather than the code ensures that your system remains predictable. It respects the time of the developers integrating with your platform and guarantees that integration paths only break when it is structurally necessary.