What do you do when you need to introduce a breaking change into an existing API?
This is a question nearly every API developer has had to answer at one point or another. Unfortunately, there are a few variables to consider, so actually arriving at an answer isn't quite as straightforward as we'd like. The biggest problem to consider when adding a breaking change to an API is how to handle existing consumers. We can't exactly force all customers to upgrade their API integrations immediately, but at the same time, we can't let them use a legacy API indefinitely either.
Providing both API versions in a production environment -- at least, temporarily -- is unavoidable, but exactly how long that is necessary (and how to do it without going crazy) is the big question. We need to find a sweet spot between the two extremes that gives customers sufficient time to accommodate the change, while also not draining too many internal resources to maintain outdated code.
Before we get into exactly how we transition customers between API versions, though, let's first talk about a few ways to actually manage multiple customer-facing API versions.
The first and, likely, most common method is to separate the major API versions directly within the source code. As a basic example, this could be accomplished as simply as having a version 1 and version 2 directory (although there are much better ways to handle this at the code level, depending on the language and framework). By separating API versions at the file structure level, rather than the server level, you are able to save on infrastructure resources and reduce management overhead. While this solution can feel a little sloppy if it's not properly planned for, it is perfect for short transition times and organizations with limited resources.
The second, more appropriate method of managing multiple production API versions is directly through the version control system. In git, for example, this would be accomplished through branches and tags. Because this does a great job of separating and classifying different code versions, it feels significantly more organized, but it also comes with additional server requirements. This is a much better option for longer transition runways, as it provides a clear line of separation between old and new API versions, and can be comfortably integrated into multiple continuous integration/continuous deployment pipelines.
It is important to mention that, while the specific versioning specification your organization uses is outside the scope of this article, proper care should be taken to establish and enforce one. Managing incompatible API versions can be an incredibly complex task, so using a clearly defined application versioning method will allow you to effectively indicate -- both internally and externally -- what level of support any application has (my personal favorite schema is the Semantic Versioning specification).
Timing and communication
So, once you have multiple API versions running in production, how much time should your customers be given to migrate?
The answer to this question depends largely on how ubiquitous your API is, how much revenue it generates and how dependent your consumers are on it. For example, if you have a private API servicing enterprise customers that amounts to 30% of your revenue, the same runway should be given as if you had a public API with a million active customers. Being aware of the potential impact of any breaking change to your API is crucial to maintaining a good relationship with your user base.
While there is no hard and fast rule about exactly how long to support deprecated API versions, for your own sanity it is strongly recommended to never have more than two major API versions in production at any given time. This allows your team to have a consistent and reliable transition process every time you need to bump the version up, without having to worry about managing a seemingly random number of resources.
Regardless of the amount of runway given, clearly communicating the timeline and changes associated with your API version updates is important to ensuring a smooth migration. The first deprecation notice should be sent to users as soon as possible -- typically when the new API version is production-ready. This message should include everything such as the changes that are being made, how it will affect users, what steps are necessary for consumers to take to avoid any downtime and the exact date when the deprecated functionality will be turned off.
One notification isn't enough, however, so you should be careful to also send follow-up communications to your users as well. Send one at least once every few weeks -- more or less, depending on how long the migration period is. By keeping all of your API stakeholders in the loop, you will ensure that there are no surprises on the day you finally shut down your deprecated version.
One big caveat that can make managing multiple production APIs difficult is database state changes. If moving from version 1 to version 2 of an API removes or modifies the database schema in a way that makes the versions incompatible with each other, it will be impossible to properly run two separate production API versions. The best way to handle this situation is by creating transitional database migrations, allowing both versions to effectively communicate with the database until the deprecated functionality is disabled.
Introducing breaking changes into an API is unavoidable, but doing it successfully relies heavily on how you communicate the changes and the migration process with your users. As with any customer-facing functionality, transparency can go a long way into maintaining positive relationships with your users.
Developing an API management strategy