From a design perspective, there are a few ways to go about this depending on the extent to which you expect the service to change over the course of its lifecycle. First, you need to decide which level to apply the versioning. It could be done at the operation level, or it could be done deeper down at the schema type level.
On the extreme end, you could maintain a version attribute or namespace for every complex type within the service, which would provide the ultimate flexibility in versioning since you can modify any type independent of the rest. It allows you to do more frequent changes, and it keeps the changes contained in scope, so there is no need to introduce new namespaces or complete new operations. However, this would add complexity to the code right from the start, and the complexity grows as new type versions are introduced. As an alternative approach, you could version at the service level or the operation level, where you bind new versions of the operations to new or existing messages as needed. The complexity here is slightly reduced, but small changes at a low level creep up, requiring new versions of the entire operation or service.
Both of these approaches result in mixing the business logic with versioning logic, and that's not a good thing. I would not recommend either of them. The best approach is probably to introduce a new layer, or intermediary which delegates the messages that come from one version to the right instance of the service. Such an intermediary can apply descriptive approach to the versioning logic, such as using XSL to transform the messages from one version to another. The benefit of this approach is that it offloads versioning away from the business logic into a separate layer, and its complexity grows with the intensity of changes introduced between versions rather than having complexity from the start - possibly unnecessarily, and the implementation is fully contained within the separate intermediary, thus leaving the actual services alone.
Aside from the design approach, the ultimate and most important best practice to keep in mind is to create and maintain a regression test suite for each version. You never know if messages from one version or another continue to be processed correctly unless there is a regression baseline available. In fact, you should never move ahead with new versions before having such a regression suite. Regression test suites can serve as the functional contract for correctness of the service so you know you did not break anything when introducing the new version.
This was first published in April 2007