The Rise of the Humans
In the book Liquid Software: How to Achieve Trusted Continuous Updates in the DevOps World, there’s a great deal of discussion about the rise of the machines, and how the need for speed in today’s (and tomorrow’s) software development environment demands that we automate as many processes as is practical and sensible. These automations allow us to design and refine more and better software. They create business efficiencies, including cost savings and savings on resources – including human. By freeing software development personnel of numerous tedious and repetitive tasks, liquid software lets them focus their attentions on software innovations, improving software delivery and upgrade systems, and certain detail-oriented operations that humans are generally best to handle.
That brings us to REST APIs, which are how we interact with software services. REST APIs, typically comprised of modules, functions, and input/output parameters, are usually a collection of entry points. For each entry point, there are different commands that have different input and output data. Entry points are root pathways to access descriptions about a particular REST API, its functionality, and the URL by which it can be accessed. When we’re talking about REST API entry points, we’re talking about its REST API function name.
For maximum versatility, REST APIs should be both forward and backward compatible. From the client perspective, forward compatibility means an older client can still work with a more recent REST API version; backward compatibility means that a more recent client can still work with an older REST API version. This said, when upgrading a REST API, there are cases when breaking its forward compatibility is desirable as a means of forcing customers to upgrade their clients.
It’s also significant to recognize that when an entry point is changed, a function name is being changed as well. If an entry point is removed, then backward compatibility is broken. The thing that most influences backward and forward compatibility is REST API entry point management. For example, if one were to change the structure of input/output data, it would be very bad practice to use the same REST API entry point that existed before this structural change. In this instance, good REST API housekeeping would necessitate the creation of a new entry point. And in so doing, it’s important to assign the entry point a new name (i.e., a new name for a new function) or, at minimum, place the REST API version number in front of an existing name.
SemVer and REST APIs
One of the best ways to create a software release is to use semantic versioning (SemVer), which features a major.minor.patch versioning number scheme. In most instances, there should be a direct interrelationship between a given piece of software’s SemVer version and REST API versioning. Being mindful that software will change far more often than REST APIs, there are some best practices worth highlighting.
These practices are predicated on an understanding of the factors that impact this relationship (e.g. the types of changes that are being made to a particular REST API). We would certainly expect there to be a software version change every time there is a REST API version change. Conversely, the global version of a REST API is usually the global version of the service to which it is exposed. Accordingly, a first, best practice is to make sure that the global version of both are identical (i.e., if Service “A” is v2.1.1, then the REST API version is exposing v2.1.1).
With REST APIs, there are two levels of versioning:
- Overall
For example, in v1 of a given REST API, all calls would be prefixed with https://…/api/v1/…; in v2, all calls would be prefixed with https://…/api/v2/… - Internal for each endpoint within a REST API
Modifying an existing endpoint (e.g., adding or removing parameters) or adding a new one does not necessarily imply that a change is required for an overall REST API version.
REST API Versioning Sins
When examining the question of REST API versioning, be on the lookout for a number of issues:
- No versioning at all
It’s quite uncommon for REST API entry points to include any versioning. This results in problems, including clients not using new parameters – since they don’t know they exist – and clients communicating parameters which are no longer in use (perhaps being supplanted by default values), since they have been removed. - Inappropriate Version Changes
Even the smallest change to a REST API should generate, at least, a minor version increase (a patch increase is not acceptable). For example, if data fields are added to an existing command, or a new command is added to an existing endpoint, these are typically considered to be minor REST API changes. The application version should, at minimum, be upgraded from x.y.z to x.y+1.z (once again, upgrading to x.y.z+1 is insufficient).Note, however, that when a minor SemVer or service implementation change occurs, it may or may not generate a change in a given REST API, and any changes that occur in that REST API won’t require a full, new entry point. It will only need a new path or new data input/output reference to the existing REST API.A big change, such as exposing a new design architecture for a REST API, should always be considered a major SemVer change. Still, let’s consider whether, when a decision is made to designate a given REST API change as a major SemVer change, the same version number should be used for both the REST API and the service implementation.JFrog’s Artifactory opts against having REST API entry point versions. Instead, every time there is a major change, a new command is added, along with a new path to the REST API. The old path is then gradually deprecated. With this method, there is no versioning for the API path (i.e., there is no version number in the REST API’s URL). This practice presupposes that insistence on entry point names always containing the major version of a service is an artificial and inefficient constraint. On the other hand, this approach can be problematic when there is a need to execute major structural, command and service implementation changes because that requires restructuring one’s REST API in the process.Another option is to create a new version for a given REST API and restructure the command under them. This major version increase in the REST API doesn’t have to be related to the SemVer version. Consequently, REST API v1 can work with v1.0, v2.0, and v3.0, while REST API v2 will pertain to v4.0 and above. - Handshaking
If a proper handshake between clients and REST APIs is not established, it becomes difficult, if not impossible, for each side to confirm the other’s version and establish compatibility between the two.When it is known that certain changes will result in failed connections to a given service, the problem can be avoided/resolved by providing clients with the new code to execute necessary handshakes. Alternatively, developers can create REST API documentation with clear definitions and a compatibility matrix for the different versions of existing services and the several REST APIs that pertain to them. - Maintaining Multiple REST API Versions
When upgrading an overall REST API version, there’s usually a need to sustain and support (at least for a limited period of time) the old version. Problems can be avoided by making sure that endpoints in the old version are clearly mapped to corresponding endpoints in the new version, and that a compatibility matrix is maintained for each endpoint (although this can become quite complex as the REST API grows). Additionally, new features should only be added to the new REST API version. This will gradually encourage customers to upgrade.
The proper design of REST APIs is one of the most important things a software engineer can do in relation to each and every exposed service. And making sure that REST APIs are versioned correctly is a distinctly human endeavor. As indicated, the most critical concern relates to how one precisely links the manual functions of designing, and versioning REST APIs to a SemVer mechanism associated with software generation.
While automation has done much to increase the productivity of software development and reduce the number of hours necessary to attend to non-innovative chores, there’s just no way around it – REST API management is our responsibility. The work demands care, dedication, and focused attention. When we do it right, we reap the praise of industry bosses – and users, alike.