m@ksim.pro
Back to all posts
IT 3 min read

API versioning: why it matters and how to keep it manageable

A practical explanation for product owners and engineering managers: what API versioning is, why getting it wrong is expensive, and what a workable strategy looks like.

Every product that has external integrations, a mobile app, or partner access eventually runs into the same question: how do you change something in the API without breaking the clients who already depend on it? Most teams answer it ad hoc, and the accumulated cost of those ad-hoc decisions is what makes modernisation painful years later.

This is not a deeply technical topic. It is a product and governance topic that engineering managers and product owners need to own.

Why versioning exists

An API is a contract. Once someone - a mobile app, a partner system, an internal service - builds against it, they have an expectation about what fields come back, what the status codes mean, and in what order operations happen.

When you change that contract without coordination, you break those clients. The breakage may be immediate and obvious, or it may be subtle: a field disappears, a new required parameter is added, a status code changes meaning. The client continues working until something depends on the thing that changed.

Versioning is a way to keep the old contract alive while you evolve the new one.

The main approaches

There are three common patterns, each with real trade-offs.

URL versioning - the version is in the path: /v1/orders, /v2/orders. Simple to understand, easy to route, easy to document. The downside is that you end up maintaining parallel implementations and clients have to explicitly migrate.

Header versioning - the client sends a version in a request header. Keeps the URL clean but makes discoverability harder: you cannot just look at a URL and know what version it is.

Date-based versioning - used by Stripe and a few others. Every change is attached to a date, and each client pins to a date. Requires solid infrastructure to maintain but gives very fine-grained control.

For most product companies, URL versioning with a clear deprecation policy is the right starting point. It is boring and it works.

What a deprecation policy needs to contain

Versioning without deprecation policy is half the work. A policy should answer:

  • How long will a version stay supported after a new one is released?
  • What signals will clients get before a version is retired (headers, emails, documentation)?
  • Who is responsible for tracking which clients use which version?
  • What happens if a client does not migrate - is there a hard cutoff or a negotiated extension?

Without written answers to these questions, deprecation becomes a support crisis every time.

The engineering mistake that makes this worse

The most common mistake I see is versioning the whole API when you only need to version specific resources or operations. If /orders has breaking changes but /users does not, there is no reason to bump the whole API to v2. Granular versioning keeps the blast radius small.

The opposite mistake is never retiring old versions. If v1 still runs five years after v3 launched, you have a maintenance burden and a security exposure. Neither is neutral.

A practical starting point

If your product has an API that is used by anyone outside your immediate team, write down the following:

  1. Which version(s) are currently active.
  2. What the support window is for each.
  3. Who in the team owns the deprecation process.
  4. Whether there is a changelog clients can subscribe to.

That is not a full strategy, but it is the minimum that prevents the version debt from accumulating silently.

Back to all posts
Contact

If this resonated, write to me. I reply personally.

WhatsApp