N:1 Web Service Applications – Getting from V1 to V2

Below I walk through the issues an implementer faces in moving from V1 to V2 of a Web Service application. I am limiting my investigation to a sub-set of Web Service applications, a N:1 Service. That is, a Web Service application with many clients (N) for which there is only one instance of the Web Service application itself (1).

Introduction

The first question to ask when versioning a N:1 service from V1 to V2 is how important is backwards compatibility with V1 clients? If N is both very small and well defined then it is possible to take a "Big Bang" approach where on a specific day at a specific time the N:1 service and all clients are simultaneously upgraded to V2. If a Big Bang is possible then backwards compatibility is not an issue. But for everyone else backwards compatibility with V1 clients is a requirement.

It is at least theoretically possible that backwards compatibility with V1 clients may only be needed for a short period of time. In those cases there are sometimes shortcuts one can take but those shortcuts are of dubious merit and the assumption of a short transition time is itself dubious.

So most folks, I expect, when moving their N:1 service from V1 to V2 will not only have to provide backwards compatibility with V1 clients but will have to provide that compatibility for quite some time. To cover the case of "permanent backwards compatibility" I look at two design paradigms – a full re-write and an intermediary based versioning strategy and explain why I suspect that most folks are probably best off using intermediaries.

Assumptions

For the purposes of this article a V2 means a significant alteration and/or expansion in functionality from V1 that requires externally visible changes to the choreography and/or messages used in V1. It is assumed that any minor changes or enhancements will be made directly to the V1 code base. It is also assumed that the N:1 services discussed here in constitute significant chunks of functionality with a substantial number of messages and a non-trivial choreography.

Big Bang

In an ideal world one would flip a switch and the V1 N:1 service and all of its V1 clients would simultaneously change from V1 to V2. This is probably the smoothest possible way to handle an upgrade. Unfortunately making it work can quickly turn into an unsolvable logistical nightmare.

If a web service is at all useful then use will quickly spread. A motley collection of unofficial users of the "official" clients and completely new unofficial clients will spring up. So even if one successfully upgrades all the "legitimate" users of "legitimate" clients the illegitimate users and clients will still be around and will be extremely unhappy to find that the N:1 service no longer functions properly for them. One can argue until one is blue in the face that illegitimate clients had no business using the N:1 service without proper permission and vetting but I suspect lots of folks will find those arguments falling on deaf ears.

Staged Transitioning

If the Big Bang approach isn't workable then one is forced into some kind of backwards compatibility story. Meaning that one can simultaneously handle V1 and V2 requests to the N:1 service.

One approach is to release the V2 N:1 service in parallel to the V1 N:1 service with some kind of temporary loosely coupled background synchronization. For example, once a day a process could run that would synchronize the V1 and V2 data stores.

While such solutions are touted as temporary, to give people time to switch over from V1 to V2, I wouldn't be surprised to find out that in most cases the temporary solution either doesn't work or becomes permanent. The whole approach is suspicious to me because:

  1. It's really hard to keep two loosely coupled parallel systems in a reasonable level of synch
  2. It seems wasteful to spend a ton of time writing synchronization code that is robust enough to work on a continuing basis but will soon be thrown away.

I suppose if the data is high read/low write and trivially formatted with few users/clients with a reasonably believable story for getting those clients moved to V2 then a staged transitioning story might be reasonable but that"s a whole lot of caveats to depend on.

Permanent Backwards Compatibility

If a temporary backwards compatibility story won"t work then all one is left with is a permanent backwards compatibility story. That is, one has to produce a single N:1 service that can robustly handle V1 and V2 messages.

There are two approaches to this problem that stick out in my mind – rewrites and intermediaries.

Rewrites – We Can Rebuild Him…

In my experience developers have a nearly irresistible urge to rewrite any code they come into contact with. I do not believe that this is irrational on their part. From what I've seen every programming language out there is a lot easier to write than to read. So the effort to go through the existing code, understand all of it and then expand it is enormous. In theory it would be faster to just rewrite everything.

So when faced with the task of writing a system that can handle both V1 and V2 messages my guess is that most devs, rather than trying to expand the existing V1 code into V2, will opt for a full rewrite. Joel Splosky has a great article on why rewrites are usually a bad idea. His points about the mountain of bug fixes embedded in the code is especially pertinent to backwards compatibility scenarios because one is expected to be "bug for bug" backwards compatible. it's not enough to write the V2 system with support for V1 messages to a theoretical spec that no one probably every paid any attention to. One has to write code to the actual clients out there. The testing burden alone probably rules out most rewrites.

V2 as an Intermediary

My guess is that most folks will find that Big Bang, Staged Transitions and Rewrites won"t work for them. I suspect that in most interest cases the V1 code has to be left running, probably for a very long time (defined as – after you have left the project). But Web Services does provide a fairly nice technique to make it possible to leave the V1 code alone but still build a V2 – Intermediaries.

Intermediaries are not a new idea. Or even a newish idea. In fact, this is yet another of many decades old ideas that Web Services is recycling. Anyone who has ever had to use an adapter for an integration project full well understands the concept of "intermediary". The American English translation of intermediary is somewhere along the lines of "Something you stick in front of something else that intercepts all the messages/commands/calls/whatever and does stuff with/to them as they come and go."

An example may help to illustrate the idea. Imagine a customer relationship management system. A V1 client sends in a V1 message to create a new customer. The V2 system will intercept the message, see that it is a new customer creation request and then forward the message to the V1 system. The V1 system will then respond, the response will be intercepted by the V2 system who will see what ID the V1 system assigned to the new customer. The V2 system will then create an entry in its own database with that ID.

Later on a V2 client sends in a V2 message to add information about the previously mentioned customer, specifically a new middle name and the customer"s favorite type of pizza. The V1 system has a field for middle name but it doesn't have any field for favorite type of pizza, that was a V2 addition. When the message arrives at the V2 system the V2 system will pull out the favorite type of pizza and put that in its own database under the customers ID. The V2 system will then create a V1 update message specifying the new middle name and send it on to the V1 system.

The end result is that the V1 and V2 systems are now robustly synchronized. No data can get in or out of the V1 system with the V2 system seeing it and being able to take action. An intermediary solution is not a miracle. It won"t work in all cases especially if complicated data changes have occurred and it can require direct access to the V1 data store in some cases. But it can work in many cases and it is a strategy most likely to work within budget since it doesn't require re-inventing all the knowledge locked in the V1 system or trying to do full compliance testing with every possible V1 client.

Getting to V3, V4, V5, etc.

Generally linear versioning doesn't work; the world is too complicated for that. But if one truly has a N:1 service then linear versioning can be made to work, especially considering the alternatives. Sure, over time, the intermediary chain will get longer but also remember that Moore"s law means that the older parts of the chain will be continually be getting faster as new versions are added. I don"t think that performance will keep people from using intermediaries to move on to V3, V4, V5, etc.

I suspect a much deadlier problem will be that any N:1 service that manages to get through V2 and is on its way to V3 and beyond is sufficiently interesting that most companies will end up wanting multiple versions of the service to do different things for different people. Then one doesn't have a N:1 service, one has a N:N service (following the general rule that in computers there are only three numbers – 0, 1 and infinity). Versioning considerations for N:N services are very different than for N:1 services. But that"s a topic for another day.

Conclusion

For N:1 Web Service Applications, that is, Web Services for which there is one instance of the Web Service application but many clients, going from V1 to V2 typically means having to support V1 clients for an indeterminate period of time. While it is theoretically possible to write a brand new V2 code base that includes support for V1 messages in practice the loss of the knowledge in the V1 code base combined with the testing burden of making sure every V1 client (known and unknown) works with the V2 system"s implementation of the V1 messages is likely to cause most implementers to prefer an intermediary strategy. An intermediary strategy involves putting the V2 system in front of the V1 system so that it can intercept all incoming and outgoing messages in order to manipulate/record/re-route them as necessary.

2 thoughts on “N:1 Web Service Applications – Getting from V1 to V2”

  1. Yaron, that's an interesting walk through. i'd like to reference this in a paper i'm writing on practical versioning if that's OK.

    Test Driven Development is the answer to removing the fear of rewriting – i guess you may have already seen Tim Bray's TDD sermon:
    http://www.tbray.org/ongoing/When/200x/2004/06/13/TDD2004

    i wonder if the term 'facade' or 'proxy' is better than 'intermediary' here since you're describing an agent invisible to the sender.

    as you point out, branching is the killer, and there is no silver bullet here..

  2. Please, feel free to reference it.

    TDD – I think TDD is interesting but I suspect it won't prove enough for backwards compatibility. The issue I point out in the article is that the test matrix for backwards compatibility is enormous. So in a sense I'm assuming that the developers are doing something like TDD but the number of tests needed to capture all the edge case behaviors of the clients as they interact with the service for V1 are just too many to be useful enumerated. In most cases, where dropping V1 clients isn't an option, just leaving the V1 code alone is probably the most productive option.

    Facade? Proxy? Intermediary? – I used the term intermediary because it has a nice web service sound. It really was a rather arbitrary choice.

Leave a Reply

Your email address will not be published. Required fields are marked *