
How to Version an API - goodroot
https://fly.io/articles/pragmatic-api-versioning/
======
smizell
I personally like to think about managing change well rather than versioning
an entire API. In this, API designers provide instructions on how APIs will
evolve over time, how features will be deprecated, and how this process will
be communicated to API consumers. Change management also includes
recommendations for client developers on how to evolve along with the API and
build clients that don't fail when something as small as a JSON property is
absent or added. You don't necessarily need version numbers to do this.

An API version says to me that one day the entire API may introduce a major
change that is separate from the current API. If you plan to never introduce a
major change like this, you may not need a "/v1" in the URL.

Kudos to the author for putting together a nice article :)

~~~
tetraodonpuffer
API consumers might also not be very flexible when it comes to adapting to API
changes: some might have developed fragile/inflexible code that will go up in
flames if you even add a new field to an object (leading to support calls and
escalations), this is why depending on your domain you might need very strict
versioning requirements.

Some APIs will not change a version number even if new objects/fields are
added, others will, it really depends by what space you are in and your
customer needs and it should be taken into account when designing your API.

~~~
smizell
I think you are right here. It takes two to tango, and if the clients are
tightly coupled to the API, the best intentions for evolving slowly will fail.
:)

------
aantix
If you're a Rails dev, take a look at VersionCake.

It allows you to version your json Jbuilder views by adopting the file naming
convention (e.g. show.json.v1.jbuilder, show.json.v2.jbuilder, etc).

There's fallback to most recent, compatible version of the API.

[https://github.com/bwillis/versioncake](https://github.com/bwillis/versioncake)

~~~
pchristensen
I was like, Who's recommending VersionCake? But it's none other than Jim
Jones!

------
armandososa
Offtopic: I was amazed by the quality of the illustrations and was pleasantly
surprised to find that the woman who makes the illustrations
([https://twitter.com/annieruygt](https://twitter.com/annieruygt)) is listed
on the company's about page.

~~~
mrkurt
Annie's amazing!

------
defanor
The title is a bit misleading: the article is about HTTP-based API versioning,
not just any kind of API. A more generic approach that would fit such a title
is "semantic versioning" [1], which covers different questions.

[1] [http://semver.org/](http://semver.org/)

~~~
wst_
Yeah. People should stop saying API for a HTTP API. This is an application
programming interface. Any application.

------
eridius
> _Header versioning in the wild is primarily done via a customized Accept
> HTTP header._

Is this true? I haven't looked at very many web APIs, but I haven't run across
anyone using Accept in this way, and it strikes me as a pretty bad mis-use of
that header.

~~~
Dirlewanger
How is it a bad misuse? It's used to specify the MIME type. Why is it so much
of a stretch to also specify which version of the MIME type to use? I
personally think this is the cleanest and most unobtrusive way to version an
API. I'd rather change that than have to change the URL all the the time. Not
to mention that /api/vX/... is just ugly.

~~~
eridius
The Accept header specifies the acceptable MIME types for the response. It
doesn't specify how the request itself is supposed to be interpreted. Picking
between alternate MIME types with the Accept header is still supposed to give
you the same content, just in different representations.

The proper solution here is to just define a new header, like X-API-Version
(note: the article picked a header API-Version, but since this is a non-
standard vendor-specific header it should be prefixed with X-).

~~~
gnaritas
A different version is just a different representation of the same resource,
the Accept header is the correct way to do it.

~~~
eridius
If the only difference is the data returned, and not anything to do with how
the request is actually interpreted, then I guess it works. But API versioning
covers more than just changing the fields in the JSON response.

~~~
gnaritas
If the client knows the version to send, they can send a different request
format as well and you'll know how to interpret it due to the version header;
that header isn't just used for the response, it's an agreement about what the
specific message format in both directions is. If he's looking for a version 5
response then parse it as a version 5 request. I don't see the problem? Now
your media types and versions of those types can vary and grow without messing
with your resource locations which can remain stable. Beyond that you can now
version each resource independently rather than forcing the app as a whole to
change.

~~~
eridius
Your problem is you're taking a header that's explicitly intended for
selecting among multiple response types, and using it to control how the
_request itself_ is processed. That's simply incorrect. That's not what the
Accept header is meant to do, and using it that way is confusing.

> _that header isn 't just used for the response, it's an agreement about what
> the specific message format in both directions is_

No, it's not. The Accept header is strictly about the response.

~~~
gnaritas
> No, it's not. The Accept header is strictly about the response.

OK, if you want to be that strict about it, then you use both the ContentType
and Accept headers to specify what version of the request and response you're
making/wanting.

~~~
eridius
The Content-Type specifies the MIME type of the body content. So even that's
not good enough for actually interpreting the URL or URL parameters. Content-
Type would only work if all of your APIs go through the same URL and strictly
use the body content for the actual request (which would be a pretty awful
API).

~~~
gnaritas
Fair point; I still say if you're requesting version 5 response then the
server can interpret the request as version 5 as well and act accordingly,
they're necessarily linked regardless of the Accept header being about the
response; it must necessarily imply the request URI version format.

~~~
eridius
I can see why you're saying that, but I still disagree that this is a valid
usage of Accept, and at a minimum it's going to be confusing, because nobody
expects the Accept header to affect interpretation of the request.

I'm also not sure why anyone would even want to use Accept for this, given
that if you can set Accept, you can also set something like X-API-Version.

~~~
gnaritas
It's not only a valid usage of accept, it's simply necessary; you can't
possibly process a request for a version of a response without assuming the
request contains the necessary information said response requires. If you
don't expect the Accept header asking for a specific version to affect the
processing of the request, I think you really haven't thought it through as it
necessarily _must_ since the output response version must be correlated with
the input request. If output version 5 requires a different set of input args,
then your code for processing the output will have to assume those args
present and thus will have to assume the request was formatted correctly for
version 5 otherwise you'll have to throw an error saying the request was
formatted incorrectly.

Yes, you could use a custom header, many do, but then you're stepping outside
of the HTTP standard, the Accept header is supposed to define the response
format, not some custom one you make up, and version is another facet of the
response format and must imply the request format matches the required data of
the requested output format.

~~~
eridius
...what? Your comment literally makes no sense to me.

> _Yes, you could use a custom header, many do, but then you 're stepping
> outside of the HTTP standard_

No you're not. The HTTP standard allows for custom headers, and nothing in the
HTTP standard restricts servers from using customer headers to influence how
they process the request.

> _Accept header is supposed to define the response format_

Yes, like application/json. It's not supposed to define the request _content_
, just the format, and it's definitely not supposed to affect the way the
request is processed.

> _version is another facet of the response format_

No it's not. JSON is a response format. XML is a response format. "API version
5" is not a response format.

------
hdhzy
Roy Fielding himself had an interesting interview [0] where versioning a REST
API was one of the topics (spoilers: version is not in the url nor headers).

[0]: [https://www.infoq.com/articles/roy-fielding-on-
versioning/](https://www.infoq.com/articles/roy-fielding-on-versioning/)

~~~
deathanatos
I'm going to have to disagree with him. If I need to break compatibility on a
single resource, I don't want to mirror every API endpoint (except that one)
to a new hostname. That becomes continually more and more complex and hard to
keep sense of as changes progress.

The hostname thing might work for huge, world-changing re-writes, but for
small, incremental changes, I think something like what GitHub does w/ the
Accept header is far more appropriate.

~~~
prebrov
His argument is for API discoverability. Client should be able to learn about
available endpoints

[edit: ...and resources. And be able to comprehend resource record
conventions]

~~~
deathanatos
I also don't see the point of API discoverability, at least, not
programmatically. Sure, such a thing could be built, but I don't know what
could be done w/ the results. Most API's I've ever worked on required domain
knowledge in order to operate on (e.g., you had to understand the content —
semantically — of the result. I.e., you needed to understand what a
`applicaiton/vnd.bigcorp.widget.v2+json` was.)

I just don't see any generic operations w.r.t. discovery that are … useful.

I'm not trying to say that you shouldn't, e.g., use URLs in your resulting
resources; e.g., if a query for a widget returns:

    
    
      {
        "widget_name": "NextGen Enterprise Widget",
        "suppliers": "http://bigcorp.example.com/widget/123/suppliers"
      }
    

That's fine. But you're still going to need to know how to parse the result of
…/suppliers, or it's just opaque bytes.

------
BillinghamJ
Currently I structure my URLs like this:

    
    
        /v1/2017-06-08/blah
    

The idea is that you write in the date that you wrote the implementation
against the API. This is just like Stripe's API, but explicit rather than
implicit. Then everything under that will continue to work as-is.

There's a few reasons I like to do this, but the main one is that I often
struggle to decide whether a given change is really "worthy" of a major
version change.

The only rule/caveat we have is: within a given client's implementation, you
should only use one date at a time. Mixing them creates a small possibility of
compatibility issues.

The initial /v1/ is just in case I decide to move away from the date thing at
some stage.

~~~
edoceo
I'm similar but just prefix like

    
    
        /v2014
    

I assume I won't make multiple versions in a year. I have a current system
with /v2014, /v2016 and /v2017

I like to smash dates into all my version strings

~~~
cyann
Yes, human readable dates are very good at conveying a sense of obsolescence.

Mine are a bit more granular: /vyymm, e.g. /v1503, /v1610, /v1706. This should
work well for this century ;-)

------
SamUK96
> 3: RESTful. It should speak the language of HTTP verbs: GET, PUT, POST,
> PATCH, UPDATE, DELETE.

I know it's slightly off-topic, but when one of the early points of an article
contains an incorrect piece of information, that really makes me not want to
read on further

Restful API's are a recent "fad" that, even though_do_ have their place in
mainly basic CRUD apps, RPC still reigns very popular. See [1] for a pretty
good explanation of REST-styled vs RPC-styled APIs.

[1] [https://www.linkedin.com/pulse/rest-vs-rpc-soa-showdown-
josh...](https://www.linkedin.com/pulse/rest-vs-rpc-soa-showdown-joshua-
hartman)

~~~
tracker1
I've rarely seen a "pure" API that wasn't also _very_ simplistic... most
composite APIs of any complexity have things that work better as an RPC
request (via HTTP POST) as well as other more restful endpoints.

------
smnscu
Wrt your users, I think regardless of what scheme you use for API versioning
it's a good idea to build libraries to access it – if you can afford it. [Go
:), ]JS, PHP, C#, Java, Python, and Ruby, and you have most of the market
covered. Then you can use semver for the libraries and the user doesn't care
about how the actual endpoints look like – as an added bonus you can use more
efficient encodings.

Example from a past life: [https://github.com/lavab/api-client-
js](https://github.com/lavab/api-client-js)

------
loup-vaillant
Great, I was wondering how to version my library once I reach 1.0.

> _3: RESTful. It should speak the language of HTTP verbs: GET, PUT, POST,
> PATCH, UPDATE, DELETE._

Oh, it was talking about _web_ interfaces. Oh well.

~~~
tracker1
Is there a reason you can't use semver? Or was this just a quippy remark?

~~~
loup-vaillant
This article isn't even about semantic versioning. It is exclusively about web
API design and versioning, and the web doesn't interest me.

API used to mean headers, types, Java interfaces… stuff programmers use to
link code together. And now the web is hijacking the term to a point where
regular APIs can't even be named. I mean, can web developers even _think_
about this kind of API now?

I bet this explains part of the microservice madness we've seen lately. If the
only kind of API you know goes through a web server, _of course_ you'd use
not-so-micro-services to modularise your application.

~~~
tracker1
My point was, that if it came to versioning API library versions, then "just
use semver" would seem to be the most appropriate answer. I understand the
conflation between "web api" and "api" here, I feel the same when "Microsoft
SQL Server" is referred to as simple "SQL Server"

~~~
loup-vaillant
I do intend to "just use semver". I was just hoping this article would give me
something more.

------
pixelcort
How about systems like GraphQL where the client specifies the attributes to be
returned? To deprecate an attribute, just mark it as deprecated and wait until
no client is requesting that field.

~~~
gnaritas
You'll be waiting possible forever, clients tend to not change until forced
to.

~~~
Moru
And why should they? They probably hired some developer to make the
application and this developer might not be available any more. Are you
forcing the user to change or are you liberating the user to move to a new
company with similar services? If you want your customer to stay with you,
make sure it is as frictionless as possible to stay with your service. Of
course, if the change is because of security you have to be more clear about
it but change just for change sake is dangerous.

~~~
gnaritas
Oh I agree, you should support them as long as you reasonably can if their
business justifies the expense and complexity of maintaining multiple
versions. At some point it eventually won't, and that's when you force them to
upgrade or lose access.

------
paulddraper
> RESTful. It should speak the language of HTTP verbs: GET, PUT, POST, PATCH,
> UPDATE, DELETE

What in the heck is UPDATE? It sounds like a non-technical person found out
REST had CRUD.

~~~
itcmcgrath
It's the verb you use when you want to receive a 400 with "BAD Request -
Invalid verb"

------
vincentmarle
> When you construct an API, you receive scalability and load balancing
> benefits by decoupling your API and hosting it as its own backend

I've thought about doing this a couple of times but it seems like a lot of
overhead if you need to have separate production instances running for each
version of your API.

Is there a better approach to organize this in a more scalable manner?

~~~
rockostrich
The two options are have the different versions running on separate instances
or have all versions in a single instance. The former lets you scale the
versions independently. If v1 traffic drops 5x overnight then you can scale it
down by 5x. The latter simplifies the deployment and management though. You
just have to deploy one service and scale one service to a single amount of
traffic.

------
faitswulff
Random thought, but would it make sense to give each client a unique base path
for URL endpoints and let them instantiate new endpoints with different
versions?

Ex:

    
    
        mysite.com/api/UUID_for_api_v1/cars/1

and

    
    
        mysite.com/api/UUID_for_api_v2/automobiles/1

~~~
caffeinewriter
I was just thinking about this. Instead of versioning with an `Accept` header,
or with a URL version, why not version off of an upgradable API key?

~~~
TimWolla
Facebook does this partly: Each “Application” has an associated API version
which is the earliest API version that can be accessed (and IIRC the one used
implicitly when no version is given inside the route).

------
jcrites
What I've learned about API versioning:

1\. Versioning an entire web service or RPC API is relatively expensive in
terms of engineering resources. Wherever possible, avoid having more than one
version of an API, in favor of a single API version that evolves gradually in
backwards-compatible ways. Having multiple API versions is a recourse to
employ when you've realized that the original API isn't ideal, and you need to
break compatibility to get it right. Strive to avoid this.

2\. Establish ground rules with your clients about what kind of changes will
be considered backward-compatible: what kind of changes they are expected to
tolerate. For example, adding new fields to existing structures is something
that clients should tolerate from the service. Pay careful attention to
enumerations, sum types, and exceptions.

3\. Version upgrades are expensive. Upgrading from one API version to another
typically requires engineering time from every single client application. Any
time that clients spend on version upgrades is time taken away from more
useful activities. The total cost of a version upgrade grows in proportion to
the number of clients. Don't gratuitously release new API major versions;
strive to minimize them.

4\. As a service provider, it often seems convenient to just release a new API
version rather than put in the hard work to provide a feature in a backward-
compatible way. (I see this more often in libraries than in services, where
it's difficult to know 100% for sure that your change to the library won't
break anyone.) Resist this temptation. Just because the upgrade costs your
clients pay are hidden from you doesn't mean they don't exist. Version
upgrades are disruptive and frustrating.

5\. It's sometimes possible to implement backwards-incompatible changes
through a series of smaller backwards-compatible or non-disruptive changes.
For example, say that you want to rename an API element -- normally this would
be a backward-incompatible change. However, you can break the change up into
multiple steps: first, add the new element with the desired name and announce
that the old name is deprecated. Next, work with your clients to upgrade them
all to the new name. Once clients have migrated, you can remove the old name
without impact. (How hard this is and how long it takes depends on the
situation; it varies from relatively easy to almost impossible. A key factor
is your clients - the more you can coordinate with and influence your clients,
the easier this is.)

6\. Releasing changes in backward-compatible or non-impacting ways is
especially useful because it reduces the need for a service provider and
client to directly coordinate. The service provider can announce a feature or
change and clients can update to account for it on their own timeline. The
service provider can then implement any backward-incompatible steps once all
clients are ready.

7\. When running any kind of migration, such as renaming an element or moving
clients from one major version to another, it's helpful to have statistics
about what people are using. If your V1 API isn't getting any traffic because
users have moved to V2, or alternatively if it's continuing to receive
traffic, then that's important to know if you're planning to deprecate it.

8\. Versioning can usually be added after-the-fact if it's needed. It's often
not necessary to worry about it too much up-front. I have yet to run into a
situation where the lack of a version field caused a problem (because its
absence can be interpreted as Version=1).

9\. Most of what I said above matters when you have a large number of clients,
or when you have clients that are outside your influence. This is big company,
big-usage thinking. If you're e.g. a small company building services for your
own consumption, don't worry about API versioning until you begin to notice
friction. Instead, focus on making changes in backwards-compatible ways. If
you need to make a backward-incompatible change, then talk to the people whose
software will be affected. There might be an easier way to manage the change
than maintaining two parallel API versions.

~~~
Moru
We had just changed to a new company running our invoices. They were still
developing their API and we wrote code to transfer our invoices to them for
printing and distributing. They were expensive and there was starting to pop
up new competition for them. But we had already working code with them so we
could not justify the cost of rewriting it. Suddenly they deprecated the old
API forcing us to rewrite. Or liberated us to change to a cheaper and better
alternative.

Well, we took the opportunity to change provider and are very happy now :-)

------
and0
Has anyone ever designed an API with subversioning on single calls?

So you'd have: service.com/api/v1/do_thing/v2/param

That way, you wouldn't have to try to snap new features to entire numbered
revisions,and could open new parameters / results up to clients who are
interested in the meantime. Seems like it would be the best of both worlds,
but I've never had to design a serious API.

------
taormina
At my last job, we used
[https://retailmenot.github.io/shield/](https://retailmenot.github.io/shield/)
to both handle versioning and our microservices. This lets you even entirely
swap out the microservice between versions!

------
iask
I go by this - breaking changes require versioning. And once I version, there
is no time spent on backward compatibility. Time is money.

------
cdevs
We do this at my job, what better way to screw over API users than to make
them rewrite their integration every 2 years

------
novaleaf
article doesn't include "module", which I think is a big mistake, as it
assumes a monolithic api for the entire business.

My opinion for a better structure:

domain.tld/api/module/version/

------
m42
graphql

~~~
dsp1234
[https://developers.facebook.com/docs/graph-
api](https://developers.facebook.com/docs/graph-api)

"The Graph API has multiple versions available"

