Web Services Versioning – Part 1

by Subbu Allamaraju on December 6, 2004

Managing change and versioning of web services can be hard. Although based on loose coupling and extensibility concepts, when it comes to change, web services are not far from older supposedly not-so-loosely coupled RPC-style technologies.

Most changes have good intentions – either they fix something broken, or try to enhance the service by adding new features. However, except in the most ideal cases, changes are disruptive. As David Bau explains in his "morning
after
" scenario, the need for compatibility can scuttle most plans for change. It is not always easy to foresee changes and future-proof the design, but eventually, we all get tasked to make changes.

Compatibility is one of the painful aspects of changes. Dave Orchard gives an exhaustive overview of extensibility and versioning scenarios for XML schemas in his "Extensibility, XML Vocabularies, and XML Schema", and this post (and the following) are partly motivated by this paper. In this post, I would like to jot down some of my thoughts on versioning of web services. My objective is to set some constraints for the kinds of changes allowed, and then think of how to go about making changes under those
constraints.

Before making changes to any web service, it is important to discuss and understand the impact of a change. This impact can best be discussed in terms of compatibility. Essentially, there are two kinds of compatibility, viz. backwards compatibility, and forwards compatibility. Depending on how service and client instances are deployed, one or both may be important.

Backwards compatibility ensures that older clients need not be upgraded to use an instance of the modified service. That is, when you upgrade your service instance to offer the latest and the greatest service interface, your clients must be able to ignore that fact and continue to use the service as though nothing changed.

On the other hand, forwards compatibility guarantees that new clients need not be downgraded to work with an old service. That is, when you deploy a new client that conforms to the modified web service interface, it should be able to work with old web service instances without having to downgrade.

Whether your goal is to guarantee backwards compatibility, or both backwards and forwards compatibility depends on how many instances of the service are deployed currently. If there are "n" clients interacting with just one instance of the service, backwards compatibility alone may be sufficient. But if there are "n" clients interacting with "m" instances implementing the service, forwards compatibility becomes an issue. This is because, of those "n" clients, some clients may be using instances offering the old service, some others may be using instances offering the new service, while the rest may be using both. 

So, here is our first constraint:

Constraint 1: Changes must guarantee backwards compatibility and possibly forwards compatibility.

Note that I don’t include the possibility of simultaneous upgrades in this discussion. Simultaneous upgrades are not always practical, and can be disruptive when attempted. Moreover, the whole idea of loose coupling goes against simultaneous upgrades. Why should a client/service upgrade just because the service/client at the other end of the wire upgraded? This may be a reasonable requirement when the software is monolithic, but not when it is loosely coupled. Check Christopher Ferris’s "Loose
Coupling and WSDL Versioning
" for some arguments on this point. So, the second constraint is:

Constraint 2: Service  instances and their clients should not be forced to upgrade simultaneously.

Any change that breaks backwards or forwards compatibility is an incompatible change. Incompatible changes are bad, and include:

  1. Removing an operation from a WSDL port. This breaks both backwards and forwards compatibility, and forces simultaneous upgrades of clients with service instances.
  2. Replacing an input or output message type with a different message type for the same operation in a WSDL port. This kind of change also breaks both backwards and forwards compatibility.
  3. Adding new required elements to existing messages.

Compatible changes include

  1. Adding optional elements or attributes to existing messages.
  2. Adding new operations to an existing WSDL port.

There is one additional detail to consider, and this could be more painful to the folks making changes than anyone
else. Assume that you have the following piece of code at the service end prior to making the change:

   public ResponseMessage doSomething(RequestMessage requestMessage)
// Do something
return responseMessage;
}

After the change, the service implementation might end up looking like the following:

   public ResponseMessage doSomething(RequestMessage requestMessage)
// Do something
return responseMessage;
}

public ResponseMessage1 doSomething1(RequestMessage1 requestMessage)
// Do something better
return responseMessage1;
}

You could possibly refactor this to share some common logic, but there are two pieces of code to deal with.

The client may have to follow a similar code pattern as well.

      // if service is old
...
// dispatch the old request
...
// else if the service is the "better" version
...
// dispatch the new request
...

At first, this option may not look so bad, but this kind of code can be very difficult to maintain. After a few
rounds of changes, it will become spaghetti-like. So, the changes should be evolutionary and additive, leading to
the third constraint:

Constraint 3: Changes to the service should allow evolutionary changes to a service/client implementation.

The idea behind these constraints to make the change process manageable and not make clients tightly-coupled to
the services. In the next part, I will discuss some possibilities, and whether those possibilities honor these
constraints.

{ 2 comments… read them below or add one }

1 bala murugan December 20, 2004 at 12:01 am

Whats a need to give a constructor as private

Reply

2 psychonscious September 11, 2009 at 5:13 am

Implementing a singleton pattern is one example of a need to declare a constructor as private (or protected).

Reply

Leave a Comment

Previous post:

Next post: