Packetizer Logo

Web Services Versioning

When designing a traditional HTTP-based web service, one can make a safe assumption that, as long as the remote web server is available, it will provide the given service. Even if the service is enhanced or updated over time, web designers take stock in the fact that version 1 of the web interface is still there. As a web service is enhanced over time, the service provider might offer versions 2, 3, 4, or 5 of a given service. For this reason, many traditional web services expose those interfaces through URIs of the form /v1/sample_service or /v3/sample_service. A client of that service that was written for version 1 will still be able to access the version 1 service.

However, the environment is quite different for communication systems that are not bound to a given, known web server. For example, suppose you have a mobile device with support for version 4 of a particular web-based interface. Suppose you want to establish communication with an enterprise service using that client. What version of the interface is the enterprise service exposing? Perhaps it is running version 3. If your client attempts to access /v4/sample_service, it will fail since the enterprise server does not have this interface. Your client could try each known version in turn, but this creates unacceptable complexity and delay for any system. Just imagine having to implement every single version of the interface on the client and server. It's hard enough to do it on the server, but the client code would be equally large and complex. Nobody wants a bloated client. It would be a development and sustaining nightmare.

The right solution is to design interfaces between such web clients and servers in a similar way that one designs communication protocols. After all, web interfaces are really protocols. Interfaces need to be both forward- and backward-compatible. In so doing, newer systems can properly exchanges messages with older systems and, likewise, older systems are properly able to exchange messages with newer systems.

So how do we do this? The rules are actually very simple:

  1. All mandatory elements must be defined in the first version of the interface
  2. Revised interfaces must only introduce optional elements that may assume default values if absent
  3. Unknown elements in messages must be ignored

It is important to not forget that message exchanges must result in useful and meaningful communication such that the user gets what he expects. When defining an interface, one must consider what elements are mandatory and which are optional at the outset. Admittedly, this can be a challenge, but it is not impossible. If one needs to introduce a new mandatory element, finding it impossible to specify a default value for some element, then one should define a new interface and retain the former interface as it was. In so doing, compatibility is ensured. (Having said that, can you see that there is a way to retain backward-compatibility? What default value would the old interface use? There is one, apparently.)

Designing properly versioned web service interfaces actually reduces code complexity in the client and the server. It removes a lot of conditional statements that would otherwise be present in the code. If designed properly, one would never need to check the version of the client or server.

There are situations where a client may want to know the version of the server. For example, if the client is talking to an older server that will simply discard certain information collected from the user, then why collect it? A simple solution is that the server expose an interface (e.g., /version_info/) that provides a block of XML or JSON that provides minimal version information.

Refer to the XML protocol versioning page for discussion on how to design properly versioned XML-based communication protocols.