Calendar-Based API Versioning
Tim Rogers, a Product Manager at GitHub, recently announced that they are moving from incremental API versioning to something they’re calling “calendar-based.” The announcement explains what calendar-based versioning is and why GitHub is adopting it now. Calendar-based versioning allows GitHub to introduce breaking changes regularly without upsetting API consumers. Consumers won’t get mad because they can pin their requests to a particular API version, so they’re sure it won’t contain any breaking changes.
According to GitHub, a breaking change is a change to the API that “can potentially break an integration.” Examples of breaking changes include removing an operation, a parameter, or a response field, adding a new required parameter, and changing the API authentication or authorization. Breaking changes are essential to identify because they can cause integrations to stop working or, in some situations, to behave unexpectedly. So, whenever GitHub introduces a breaking change to its REST API, it will create a new calendar-based version and make it available. API consumers can then opt-in to use the latest version or continue with the one they have been consuming. Changing the version requires a simple change of the value of the
X-GitHub-Api-Version HTTP header. GitHub guarantees that all previous versions will keep working as before.
It’s interesting to analyze what might happen behind the scenes to make this calendar-based API versioning possible. Let’s break it down into different areas. Starting with the announcement, notice that the author is a Product Manager, not an Architect, an Engineer, or someone else from an Engineering part of the organization. Even more, the author is the Product Manager for something called “migration tools.” Migration tools can be described as “tools to make it easy and stress-free for customers to move between GitHub products and from competitors to GitHub.” These include migration of code but also migration of users, organizations, projects, and anything you can use inside GitHub. What is interesting is that migration tools are the driver for the introduction of calendar-based API versioning. It shows that the business priority is making it as easy and safe as possible to move information into GitHub.
From a technical perspective, what does it take to have something like calendar-based API versioning working? While the versioning itself is simple to implement, you can’t say the same about the ability to keep any previous version running, especially considering all the internal changes that lead to breaking changes at the interface level. Imagine that one internal service introduces a breaking change by adding a new required parameter. The public API layer creates a new calendar-based version with the breaking change published. So far, so good. But what about the previous version of the public API? It will have to connect to an internal service that doesn’t have the new required parameter. Managing this type of dependency can be tricky and error-prone. The internal services must also adhere to the new calendar-based API versioning and keep any previous version running. In that case, it will come down to defining dependencies between services. For example, the public API depends on version
2022-12-01 of the internal service
ExampleService, and, in turn,
2022-12-01 depends on version
AnotherExampleService. Whenever a breaking change is introduced on
AnotherExampleService, all the dependents can choose to keep using the same version they were using or to introduce a breaking change and inform their dependents.
The last piece of the puzzle is the specific usage of a date instead of another type of versioning. To achieve the ability to maintain any version running while being able to introduce breaking changes, you wouldn’t need to use dates as your versions. Not only is GitHub using dates as their REST API versions, but they’re also explicitly utilizing the ISO 8601 date extended format. This format is also adopted by the W3C and is used in many other technologies when manipulating dates is needed. So, what can this way of versioning an API provide that a simple numeric incremental versioning can’t? What about semantic versioning? Why wasn’t it adopted instead? Since all versions will contain breaking changes, semantic versioning would require an increment of its major attribute. This would make semantic versioning the same as numeric incremental versioning. Using dates in a standardized format that is easy to parse by humans and machines has advantages over simple numeric incremental versioning. Firstly, you can understand when a specific version was released just by looking at its version. This is something that you can’t do with both semantic and numeric incremental versioning. Secondly, you can have dependency rules based on time because you have a relationship between a version and a date. First, you can quickly identify any service that depends on services that are younger than itself. Since all versions introduce breaking changes, it only makes sense to change a dependency to something more recent if you update your version accordingly. You could also, for example, create a rule that says that all dependencies of your API can’t be more than one month older.
In summary, GitHub’s calendar-based API versioning looks like an exciting approach to managing a good relationship between an API producer and its consumers. While it feels simple to use on the surface, digging deeper reveals that the breaking changes policy needs to bubble up to all the upstream services that the API depends on. From a business perspective, there is a potential tradeoff between the cost of maintaining the whole dependency tree in sync and the gains of having GitHub’s migration tools and integrations running on pace with what the market demands.
Update (2022-12-02): Another company that has been following a similar strategy is Stripe. They have been following calendar-based API versioning for a while. However, they call it “dated versions.” You can specify which version to use by setting the value of the
Stripe-Version HTTP header.