Choosing an API versioning system

There are many ways to version an API, each with pros and cons that need to be considered to balance developer convenience, ease of use and upgradability. Read on for how we addressed API versioning at mod.io.

Posted by on


Behind the scenes at mod.io we have been slowly determining the best road forward in regards to versioning our API which we recently concluded and announced. You would think this is a simple decision, but there are multiple ways to do it, each with their own pros and cons and amongst the wider community there doesn't appear to be any clear standout when it comes to how your API handles versioning - so much so that popular security personality Troy Hunt weighed in on the matter conceding all three options had issues.

Our requirements

The key to our eventual versioning decision was a flexible versioning scheme, that would allow us to push breaking changes at no detriment to our API consumers that ticked the boxes for both development convenience, reliability and familiarity.

  • Full backwards compatibility
  • Version is required in every request
  • Clear version progression with changelog
  • Easy to get up running and make API requests and test
  • Ability to deploy breaking changes without impacting API users

First decision: version in URL vs HTTP request header

Option 1: Version in URL Option 2: Version in HTTP request header 'Accept'

Used By

  • Steam, Stripe, Twitter, Facebook, Instagram, Google, Amazon

Pros

  • Convenient
  • Familiar
  • Explicit
  • Copy & paste code snippets for prototyping

Cons

  • Semantically incorrect as URL should represent the entity
  • Doesn't handle minor versions well
  • Get parameters suck

Used By

  • Twitch, Stripe (they offer both)

Pros

  • Consistent with HTTP spec which does provide a way to specify the version with Accept Header
  • Very explicit (forces devs to pay attention to it)

Cons

  • Added friction by adding headers
  • Cannot copy and paste a URL when developers are prototyping (again, convenience) without adding headers
  • Hard to rapidly test and get up and running

Our choice: version in URL

We value developers more than anything, so we wanted the version to be easy to see and test by simply copying and pasting a URL into your web browser. We opted against requiring a version header, because this approach forces API developers to write code or use a tool to get up and running.

Second decision: version format

Option 1: v1, v2, v3

Option 2: MAJOR.MINOR.PATCH

Option 3: YYYY-MM-DD

Option 4: YYYYr{0-9}

Used By

  • Steam, Facebook, Instagram, Amazon, Twitch

Pros

  • Very explicit
  • Show a major change has occurred

Cons

  • Does not show minor changes
  • Hard to follow

Used By

  • Twitter, Salesforce, Google

Pros

  • Industry standard, familiar to devs
  • Covers breaking and non-breaking changes

Cons

  • Too many updates for minor changes

Used By

  • Stripe, Microsoft Azure

Pros

  • Very explicit
  • Shows date changes made
  • Pick the date closest to your development date and know you are compatible

Cons

  • Does not show minor changes
  • Hard to follow at a glance

Used By

  • Unity, Stripe

Pros

  • Very explicit
  • Shows changes made in each year

Cons

  • Does not show minor changes

Our choice: YYYYr{0-9}

We wanted a version format that clearly shows when a change was made, how many changes have been made and only increments when a breaking change is deployed. We felt that including the year solved the first requirement and provides amazing clarity, and including a release number solves the final two requirements, and allows developers to know how many versions they are behind.

Example: 2018r1

YYYY - The year of the version
r - Always hard-coded as 'r', signifying the release number
0-9 - The release number which is incremented per release, relative to the year. Whenever the year is changed, the release number begins at 1.

Demonstration

A game is currently making HTTP requests like so:

GET https://api.mod.io/2018r3/games/5/mods

We release an important update that removes a field expected from objects within this response, thus a breaking change. The game should not be impacted due to our API change, so the developer of the game views the changelog for the version, see's what changed - makes appropriate changes within the game client to accommodate for this release, then simply upgrades their base URL to include the latest version.

GET https://api.mod.io/2018r4/games/5/mods

Conclusion

As a business we want to move quick and continually update and improve our service to give developers the most powerful mod API on the market. Achieving this will require breaking changes from time to time, so minimizing the impact to our API consumers by really thinking through our versioning system was critical.

We believe the solution we have arrived at balances this need well, for the following reasons:

  • Developer convenience as it uses a popular, familiar and convenient versioning scheme.
  • Developer confidence that our breaking changes will never affect their application.
  • The version is explicit, and hard to miss. You always know what version you are using.
  • When making requests with an api_key during the prototyping phase, you can easily copy and paste code snippets that work. No headers to worry about.
  • Enables us the freedom to evolve our API without worrying about affecting existing API consumers.

For more information on how we version the mod.io API, be sure to check out our new changelog section of the documentation. Thanks for reading!

Comments