Introduce Version Identifier

Updated: Published: EuroPLoP 2024

also known as: Apply Semantic API Versioning, Make Version Explicit (in Messages), Provide Compatibility Metadata

Context and Motivation

An API has been deployed to a production environment and is used by clients. The provider is evolving the API with new or improved functionality. Existing clients might have to be adjusted when a new version is released.

As an API provider, I want to indicate versions and their compatibility properties explicitly at design time and runtime so that clients can react accordingly to changes that affect them during API evolution.

Stakeholder Concerns

#maintainability
There are many reasons to change an API (besides quality refactorings [Stocker and Zimmermann 2021]). It should be changed as much as required and as little as possible, and ripple effects be avoided. One of the first steps in related maintenance tasks is determining which system parts should be changed. The impact of the major and minor changes on other parts should be kept at a minimum, but it is worth communicating when such changes occur.
#compatibility
Explicit versions, possibly introducing breaking changes, might appear costly and anti-agile or not RESTful at first glance. However, fixing bugs caused by not knowing about versioning mismatches is often expensive.
#developer-experience
Since Version Identifiers can be placed in protocol headers (in most protocols) or in the message payload, additional learning and decision making is required. Accessing protocol headers differs from accessing payload in terms of code to be written, tool and library support, and portability.

Initial Position Sketch

The entire API or individual parts, such as endpoints, operations, or message parts can be versioned. Here, we primarily target endpoint versioning.

All clients invoke operations exposed by a single, unversioned endpoint. Figure 1 shows this rather simple Initial Position Sketch.

Introduce Version Identifier: Initial Position Sketch. An API provider has deployed an API with a single endpoint that clients use.

Figure 1: Introduce Version Identifier: Initial Position Sketch. An API provider has deployed an API with a single endpoint that clients use.

Design Smells

Tacit semantic changes up to incompatibilities creep in
While the technical API contract remains unchanged, the meaning of the received or returned data might change over time. Such semantic mismatches between older and newer versions should be documented in the API Description and examples and then caught during testing, ideally in an automated fashion. Implicit versioning an applying the Tolerant Reader pattern [Daigneau 2011] might hide such changes and their impact for quite some time.
Resistance to change caused by uncertainty
API providers may hesitate to implement necessary changes due to a lack of clarity in their strategy for evolving the API. Clients might be reluctant to upgrade to new versions because they are unable to assess the imposed changes on their side.

See “Interface Evolution Patterns — Balancing Compatibility and Extensibility across Service Life Cycles” [Lübke et al. 2019] for other smells related to API versioning and countermeasures.

Instructions

The introduction and continued use of explicit an Version Identifier has to be planned, executed, and sustained:

  1. Decide on scope and naming conventions for the Version Identifier, for instance, following the Semantic Versioning specification when assigning and communicating version numbers (currently standing at Version 2.0.0).1
  2. Define an evolution roadmap, selecting one or more lifecycle management strategies to define the lifetime of the version; see the related refactorings Tighten Evolution Strategy and Relax Evolution Strategy.
  3. Decide where to place the Version Identifier for each API element to be versioned. For instance, possible locations are endpoint address, message payload, and protocol header.
  4. Put the version-enhanced endpoint addresses in the API implementation code or update the message construction code, depending on where the Version Identifier has been added.
  5. Update the API documentation with the new Version Identifier(s) and meta-information about the meaning of this version information (for instance, consequences of certain version numbers regarding compatibility).

The pattern description of Version Identifier provides more information about versioning scopes, identifier placement (location), and compatibility considerations [Zimmermann et al. 2022].

Target Solution Sketch (Evolution Outline)

Once a Version Identifier has been introduced, clients can choose which version of an endpoint they want to work with (assuming that multiple versions are supported, as described in the Two in Production pattern). Figure 2 illustrates this new, more flexible setup.

Introduce Version Identifier: Target Solution Sketch. The API provider has introduced a [Version Identifier](https://api-patterns.org/patterns/evolution/VersionIdentifier) to the API, allowing clients to choose which version of an endpoint they want to work with.

Figure 2: Introduce Version Identifier: Target Solution Sketch. The API provider has introduced a Version Identifier to the API, allowing clients to choose which version of an endpoint they want to work with.

Example(s)

Depending on the chosen location of the Version Identifier, clients enact their usage decision in the message payload or a header (see section “Instructions” above). In HTTP, it may be part of the endpoint address (relative URI path):

GET /customers/1234
Accept: text/json+customer; version=1.0
...

or

GET /v2/customers/1234
...

The API Stylebook compiled by “API Handyman” Arnaud Lauret points at many additional examples in its design topic “Updates and Versioning”.

Hints and Pitfalls to Avoid

Before and when applying this refactoring, make sure to:

  • Involve API clients in the decisions about and planning of API evolution (assuming that they are known and willing to participate).
  • Stay backward-compatible whenever possible, but do not hesitate to upgrade the major version when necessary.
  • Provide migration aids such as change logs, code snippets, and mappings of identifiers and parameters from old to new versions.
  • Be aware of design challenges caused by automatic routing. For example, Version Identifiers in encrypted message payloads might not be visible to intermediaries and, therefore, can not be used for routing purposes.

“Interface Evolution Patterns — Balancing Compatibility and Extensibility across Service Life Cycles” provides further hints. For example, “stick to a standardized and consistent versioning strategy, e.g., decide which objects to version consistently (operations, data types, etc.) or which versioning schema to use (e.g., Semantic Versioning)” [Lübke et al. 2019].

Other refactorings dealing with API evolution are Introduce Version Mediator, Tighten Evolution Strategy, and Relax Evolution Strategy.

The evolution patterns in Zimmermann et al. [2022] cover versioning. For instance, there is a pattern called Semantic Versioning. The concept of Service Level Agreement (SLA) is captured in pattern form as well; an SLA pertains to a single or multiple versions and should specify and reference those versions explicitly.

Arnaud Lauret’s book on Web API design covers the topic [Lauret 2019], and the blog post “5 Ways to Version APIs “ also discusses options with pros and cons.

James Higginbotham provides advice regarding “When and How Do You Version Your API?.

References

Daigneau, Robert. 2011. Service Design Patterns: Fundamental Design Solutions for SOAP/WSDL and RESTful Web Services. Addison-Wesley.

Lauret, Arnaud. 2019. The Design of Web APIs. Manning.

Lübke, Daniel, Olaf Zimmermann, Cesare Pautasso, Uwe Zdun, and Mirko Stocker. 2019. “Interface Evolution Patterns: Balancing Compatibility and Extensibility Across Service Life Cycles.” In Proceedings of the 24th European Conference on Pattern Languages of Programs. EuroPLop ’19. New York, NY, USA: Association for Computing Machinery. https://doi.org/10.1145/3361149.3361164.

Stocker, Mirko, and Olaf Zimmermann. 2021. “From Code Refactoring to API Refactoring: Agile Service Design and Evolution.” In Service-Oriented Computing, edited by Johanna Barzen, 174–93. Cham: Springer International Publishing. https://doi.org/10.1007/978-3-030-87568-8_11.

Zimmermann, Olaf, Mirko Stocker, Daniel Lübke, Uwe Zdun, and Cesare Pautasso. 2022. Patterns for API Design: Simplifying Integration with Loosely Coupled Message Exchanges. Addison-Wesley Signature Series (Vernon). Addison-Wesley Professional.

  1. Note that the Semantic Versioning specification has a Version Identifier and applies Semantic Versioning itself.