Universal versioning
This is a proposal for expanding the discussion of versioning to include a description of "universal versioning" and encourage its adoption.
⚠️ This is the first time I've transcribed this concept, so hopefully it makes some semblance of sense. I plan to revise this proposal based on comments received for clarity and completeness, and eventually close this issue and create a new one with what I would consider to be a more "final draft" type of proposal.
Changes in API versions can be problematic for existing applications. In particular, small businesses who use external contractors to develop applications that interact with web services can be left without a critical piece of functionality after elimination of a deprecated API, even if the service provider continues to offer the underlying functionality under a different name. To avoid unnecessary problems in this space, service authors are encourages to follow a universal versioning practice, which minimizes or eliminates the need to change API versions over time.
Universal versioning involves defining the behavior of new API calls for all preceding versions of a service, even in cases where early versions of a service did not support the call.
Basic characteristics
APIs using universal versioning use a single (fixed) API version for an extended period of time.
The primary impact to consumers of a universal API is existing applications which strictly utilize API behaviors which were documented at the time the application was created will continue to work for an indefinite period of time.
Expanding an API
Expanding an API after the initial release of a service involves two primary steps.
- Define the behavior of the new API in the new version¹ of the service.
- Define the behavior of the new API when invoked in any prior version of the service. This typically involves documenting a specific failure (e.g. 501 as described below), as the distinguishing characteristic(s)² of the new API are not understood by the old version of the service.
By providing both of these definitions, the definition of the new API is constructed in a manner that all versions of the service implement the API in a supported form.
¹ In this context, the "new version" does not actually result in an increase of the major or minor version number used in the URI.
² Should we define distinguishing characteristics in the context of an API? If so, we could define it such that distinguishing characteristics should lie within the reserved API space (next section) to make it easier to define the behavior of new APIs.
Reserved API space
Supporting expansions to an API under a universal versioning approach requires the first version of a universal API to reserve portions of the API space for future expansion. The API space includes several specific items:
- URIs (path and query parameters)
- Request bodies
- HTTP method and headers
Reserved URI for future resources
For any resource resource-name which is not defined by the public API, all HTTP requests to a path starting with the following must return a 501. In addition, a 501 must be returned if the trailing slash is omitted but the resource name is otherwise unchanged.
https://api.contoso.com/v1.0/resource-name/
Unexpected query parameters
The behavior of an HTTP API in the presence of an unrecognized but reserved query parameter, HTTP header, or property of the request body is implementation defined. If the API rejects the call due to the inclusion of an unrecognized reserved element, the return code should be 501.
Deprecation
One of the primary concerns regarding universal versioning is the intentional lack of support for deprecating and eventually removing a supported API call. In general, this design principle increases consumer confidence when developing against an API. However, the identification of certain fundamental performance or security concerns baked into an API may require developers limit or prevent the use of the call at any time.
Deprecation via rate limiting
In cases where performance or security concerns require eliminating support for a prior API, the existing behavior used for reporting failures due to rate limiting may be used to report the API is no longer in service.
Suggested upgrades
The return value used for rate limiting in a universal API should include a provision for notifying callers in cases where an alternate API may be available. When rate limits are reduced (potentially to 0) as a result of a performance or security concern, this provision should be utilized to inform the caller of the change.