In a previous post, I mentioned that I preferred a strict model of service versioning for the safety and control that it provides. In the strict model, any change results in a new contract. This is in contrast to the flexible model which allows changes that do not break backwards compatibility and the loose model which supports both backwards and forwards compatibility (by eliminating any concept of contract).
The loose model generally comes in two flavors: string in/string out and generic xml. Both share numerous disadvantages:
… sending such arbitrary messages in a SOAP envelope often requires additional processing by the SOAP engine. The wire format of a message might not be very readable once it gets encoded. Moreover, you must write code manually to deal with the payload of a message. Since there is no clear definition of the message in WSDL, the web services tooling cannot generate this code, which can make such a solution more error prone. Validating messages cannot take place. If a message format changes, it might be easier to update the service interface and regenerate binding code than ensuring all consumers and providers properly handle the new format.
In the loose model, a slight advantage in terms of governance (not having to manage multiple endpoints) is far outweighed by the additional complexity and effort required to compensate for its weaknesses.
The flexible model initially seems to be a compromise. Adding an optional message element with a default value arguably allows you to make a backward compatible change without having a new endpoint. But what happens if the default value is not appropriate to all of your original consumers? Blank or null defaults may work, but only if blank or null is otherwise meaningless for the service. Additionally, changes which break backwards compatibility will require a new contract anyway. Lastly, because multiple versions share the same physical artifacts, it will be impossible to determine which versions are still in use by monitoring log files.
The strict model I prefer is essentially an application of Bertrand Meyer’s Open/Closed Principle. This principle states that “software entities (classes, modules, functions, etc.) should be open for extension, but closed for modification”. In other words, new functionality should be implemented via new code (which may build on existing code) rather than by changing the existing code. In the words of Bob Martin:
When a single change to a program results in a cascade of changes to dependent modules, that program exhibits the undesirable attributes that we have come to associate with “bad” design. The program becomes fragile, rigid, unpredictable and unreusable. The open-closed principle attacks this in a very straightforward way. It says that you should design modules that never change. When requirements change, you extend the behavior of such modules by adding new code, not by changing old code that already works.
Applied to services, this means that all changes (with the exception of bug fixes that don’t affect the signature) result in a new service contract: endpoint, messages, entities. Assuming the service is a facade for components of the business layer, this principle can be applied (or not) to those underlying components based on whether the risk of change outweighs the redundancy introduced. This allows the impact to existing consumers of the service to be managed.
Some general rules for governing service versions (from SOA World Magazine, “Design Strategies for Web Services Versioning”):
1. Determine how often versions are to be released. When considering frequency, you should consider how many versions of the Web service you want to support in parallel.
2. Understand the timeframe within which you expect consumers to move to a new version of a service. The Web services management platform may be able to provide guidance on service usage to determine the appropriate time to phase out older versions.
3. Consider releasing a pilot or an early release of a new version. Give consumers an opportunity to test compatibility and determine potential code impacts.
4. Approach Web services versioning the same way software packages might be released. Changes to your service, either as a result of bug fixes, partner requests, or specification upgrades, should follow a specific release cycle.
5. Clearly communicate your Web services versioning strategy to users of your Web service.
It should also be noted that chunkier services designed around business processes will be less likely to change frequently than fine-grained CRUD services. Additionally, such services will generally be more cohesive, making them easier to understand and use.
Like any rule, the Open/Closed Principle has its exceptions. Applying it universally to an application soon leads to a proliferation of duplicate classes and methods, some of which may no longer be used. However, when dealing with code that is directly consumed by external applications (i.e. a service you expose), then the Open/Closed Principle provides a way to avoid the pain you would otherwise incur.