I thought I’d blog my thoughts about service and message versioning which is one of the most over-looked topics in Computer Science.
Part I: Food For Thought
1.1 Why Do We Need Versioning
Applications should be deployed independently rather than coupled as one big application. This will ensure teams can work separately aiming for separate releases matching with their own delivery schedules. It will ensure loose coupling.
Coupling releases leads to the desire of having one big release making the architecture volatile and more open to failures spreading throughout the system without being resolved quickly, efficiently, and in a controlled manner. This also makes rollbacks more chaotic.
Having fewer versions (preferably not more than two) ensure gradual phasing out of the old and smoother transition without creating issues around maintenance overhead.
Not having a versioning policy can be expensive. Architect, Project Managers, Business Analyst, and Developers time maybe spent in formulation and implementing a spike to decide on the best approach to approach versioning at different releases. Not having a consistent and clear policy on versioning may also cause inconsistencies to develop at different versions in the same project over time which may include:
1) Not considering a more simpler solutions.
2) Making assumptions when deciding on versioning.
3) Incorrect or inconsistent method of versioning chosen based on team composition at different times rather than company-wide policy.
4) Not having a versioning strategy can be expensive in terms of hardware (load) and support perspective.
5) Not phasing out in a systematic manner will create greater complexity.
Thus, versioning will have far reaching consequences on the business development and at the speed with which we implement change and deliver requirements.
1.2 What Governance Do We Need
Business functionality may change depending on how existing services operate based on newer requirements. That means that old users of the system must be supported (i.e. System B supports multiple versions of System A to receive FulfillmentMessage and send back FulfillmentResponse to the correct System A version).
Risks should also be reduced and potential bug-fixes and optimisations being minor, independent, internal and quick to fix. Support for logging messages, tracking errors, and monitoring performance should be available based on business requirements and future planning. This helps in planning for resource requirements adequately by being able to isolate expensive behaviours and resolving the bottlenecks.
Factors to Considering in Choosing a Versioning Method
1. Amount of code to write
2. How resilient to change it is.
3. Easy to maintain and phase out in the life-cycle.
4. Minimize the impact on the systems.
Forcing consumers to upgrade to a newer version of the contract is expensive and may have lower priority in the consumers release schedule. Governance should account on how the interface is designed, bindings used, and the versioning strategy adopted to be consistent with an option for an unfortunate rollback and ability to support multiple versions simultaneously unless decided otherwise by the management.
Right level of governance should not be left unchecked, as there is a high risk of turning versioning into a maintenance nightmare (this is an argument for governance rather than one against versioning).
In the case of Global System there is also a risk of having a new version of System C that does not get used because System B does not talk to it because System A does not talk to the new System B because there is only one consumer for each service.
1.3 Type of Change
Changes to schema should be marked as:
a. Minor Change: This allows the schema itself to be backward compatible. Changes of this type involve adding optional fields, changing types. This should for the most part be able to be plugged and used by old services without major changes to the consumer. This will not break the service contract. This should not impact consumer implementation, nor the service. Field can be marked as deprecated for potential major change (which is more of a revision rather than minor change), and deleted as per governance policy. This has the least amount of impact on versioning and should be the first option. However it may lead the code with a lot of if statements depending on what the optional attribute is.
b. Major Change: This involves major changes to the schema which are not backward compatible. Adding additional required fields, removing required fields, modifying optional fields to be required, changing namespace or schema name. Since the schema is not backward compatible it requires further in dept versioning mechanisms to be implemented as discussed below.
1.4 Service Versioning [Versioning Services & Versioning Methods in Services]
1. Versioning the service as a whole [new version runs side-by-side with the older ones].
This approach works well in Object-Oriented Design of not having similar object being used in a similar way for matching functions. However, with coarse-grained services (services which expose interfaces to perform complete business functions rather than fine-grained service) it may seem inappropriate. If the contract changes, we would have to version all the internal services and sub-components of that package which may be almost identical.
oldDeployed.dll
processRequest(ObjectTypeV1 yourObject) {
CallA();
CallB();
CallC();
}
newDeployed.dll
processRequest(ObjectTypeV2 yourObject) {
CallA();
CallB();
CallC();
}
2. Versioning methods inside the services.
Having methods representing different versions inside the services allows deprecating services easily without changing the name of the service itself. It also minimizes the affect of changing services (by providing additional methods), such that consumers of other methods do not get effected. Only methods with new versions will need to be deployed. However, it means that each method will have to expose its own endpoint (so that it can provide different SLA (Service Level Agreement) for different method, but in the same service). This would require an addressing schema such that the customer will have to invoke service, operation and the version number. This may work well as long as there are only a limited number of customers using fewer versions.
Endpoint1 – First.dll
processRequest(ObjectTypeV1 yourObject) {
CallA();
CallB();
CallC();
}
Endpoint2 – Second.dll
processRequest (ObejctTypeV2 yourObject){
CallAV2();
CallB(); // upon analysis does not require versioning
CallCV2();
}
1.5 Semantic Versioning (Accepting Xml as string)
Exposed service provides a method which takes in an xml string – which can accept any version of the schema. Thus, all the changes are contained in the semantics changes. The messages are defined using schemas which can be identified from their contents. This brings the issue of code having xpaths and not being resilient to changing schema as well as the potential to accept any invalid messages causing the code to break.
processRequest (string anyObject)
1.6 Adding Versioning attribute to XML Schema
As discussed earlier, one of the options is to have an attribute which identifies the version. Making this field optional in lieu of required because:
- Custom processing may be required to identify version (aside from other validation and parsing) to ensure correct one is being used.
- XML Validation tools can not validate an instance of XML (checking if it is deserialisable may be possible but seems like an inappropriate approach).
- Since versioning happens inside the XML Document, it is hard to identify without parsing as to which instance it is a schema of (and deserialising into the correct one using interface classes may add code unnecessarily).
- XSD, and XSDObjectGen (System B is planning to change its approach and use custom hand-written classes so Sandcastle can be used) can be used but it generates namespaces based on schema namespace and not schema version. However, using auto-generators may not be always be suitable (consult the David Ezzo link and Notes at bottom, or the architect team about project suitability of auto-generators).
It is possible to denote schema version in the XML namespace with each major release. It will resolve the issue of marshalling (preparing to serialise or transmit) which will then generate code into different packages, and thus enabling support of multiple schema simultaneously.
Another similar technique is to add an element for passing customising how the messages will be dealt with. Open Application Group’s Business Object Document includes this custom attribute as part of the schema to pass custom information to maximize extensibility without changing namespace. However this makes the schema a bit more complicated to deal with, and does not work well with different similar schema (e.g. major changes in the xml schema, and trying to support both via this).
1.7 Base Type Versioning
A colleague mentioned using a based type and derived types for versioning:
“Common service gateway to process different type (multiple versions) of messages
Objective: Process different type (multiple versions) of messages derived from same base message, without changing end point.
Description: In this approach we will have a service gateway, which will accept and process multiple versions of messages as long as they are drive from base message.
Advantages:
- Can process multiple version of messages from single end point
- Fully backward compatible
- Help us to write code in Object Oriented way
- Help us to share common code base
- Loosely couple the different systems
- Help us to model our service contract in object oriented way
- Easily maintainable code base
- We can share only required information in service contract with client.
Disadvantages:
- We will have to expose new endpoint if we make any change in our base message, that we can easily solve by making our base message light weight and by putting only those information which we think is mandatory in each message.
“
This can only work for extending the schema, and possibly deprecating some nodes. However, this will not work when restructuring the schema (and possibly introducing redundancy) which can also raise maintenance issues.
1.8 Deployment and Endpoints Versioning
1. Version Parameter Routing [one endpoint]: This implements content based routing where the incoming message has a version parameter based on which the padding (hosted at that endpoint) decides which version to forward to. This simplifies service addressing (and minimizes the impact) as there is only one endpoint to call to, encoding the version number it requires. However, since multiple service methods have been coupled, collisions between class names, namespace, and database access, and in turn possibly with components (causing tighter coupling between them), making it into a more complex issue than it was initially. (This can be mitigated but not without raising code smells). In essence this is hiding the complexity for the consumer to simply its usage and comes at a cost of maintaining obsolete code and depending on implementation, violating DRY [Don’t repeat yourself principle]. [methods calling the same internal methods vs methods calling different internal methods which have to be versioned in turn vs deploying at different IIS virtual directories – how many would be needed with each release].
An ESB (Enterprise Service Bus) [using intermediaries] is thought to be a resolution for the routing and transformation issue, however it lowers performance, introduces a hop and must support all SLAs bring accessed through it (think of messages going through this router) to dynamically resolve the endpoints. This may be an overkill when there are only a limited number of clients.
2. Multiple Endpoints: Each service having its own endpoint address is directly exposed to a service consumer. There is an assumption being made that consumers are able to resolve (and the return path for asynchronous calls) the correct version they require based on service/method version they require. This allows complete separation of multiple methods in deployment at the cost of maintaining addressing issue (which can be mitigated by passing the return path of the asynchronous reply endpoint i.e. Acknowledgement monitor request). There is one less hop than point 1, and lowers coupling between multiple versions.
Steve Vinoski in “The Social Side of Services” mentions that services have to know each other. Hard-wiring this information in code or config files is not a good solution, and thus, should operate dynamically to avoid high coupling. This should be done via a registry. The emphasis here is that exposing multiple end-points involves the client or the system to have a mechanisms to ensure the correct one gets called. This may not be an issue when 1 or 2 clients are involved.
When exposing different schemas externally through different endpoints, from the implementation-side, they xmls can be converted into a baseline internal structure so that with each new requirement too many changes are not required. This is synonymous to translating into an internal objection. [Marshalling by Reference for different types of XML and inheritance may be possible but nevertheless involved many internal object which raises a code smell as they all are in turn used to do the same thing].
1.9 Considerations for Internal Implementation Changes
Bug fixes, adding additional feature, and modification which do not require changing to the implementation of the service do not modify the service contract and thus should not been to be versioned. However, given that different version of consumers may rely of different components to be worked in a certain way, End-To-End testing and Test-Driven-Development approach should ensure should ensure that service does not become broken for some consumers with such changes. This could involve:
- The time synchronous calls take to process
- Additional validation rejecting the message
- Security detail changing (This was seen as a sporadic issue in Automated Deployment project deleting and creating files over the network).
1.10 Phasing Out Versions
As mentioned earlier – corporate governance and policy play a strong part in ensuring that that older versions are supported for a limited period of time to avoid having extra overhead of maintaining old versions. However, reduced period of versioning constraints development of consumer projects to implement and release an upgrade which may contradict and delay other essential features by the whole system.
1.11 Infrastructure
It is important to note consistent adoption of the bindings being used and that they satisfy security measures as well as network capability for future growth.
1.12 Analysis
To compare, at Standard Chartered, when Murex’s (commercial stock-related application, with functionality extended internally) binary updates are taken, the consumers are forced to update. eBay system (web app used by large sellers) tends to provide the older versions (functionality marked to be deprecated) for 18 months.
In order to make a decision, the main criteria is to consider how many different systems will consume the service, and how the messages will be processed and if it will consume any other services.
The most flexible approach seems to be is to have different namespace with each major release (minor versions are backward compatible, thus requiring no change). However, this strategy document is not to make a decision, but rather to provide various options for a decision based on individual projects, and management direction.
If RJIS is moved as a separate service with multiple consumers (System A, System B) it may have followed SOA principles well. If we release a [major] new version of System C which is not consumed, because a new System B is not consume because System A system does not make calls to the new System B, then we are still following the big bang approach where results won’t be known unless the full cycle of the new system is run, thus risking the ability to deploy new business requirements safely [Q Has the dependency been broken?]. This has been mitigated to a certain extent by having technical releases separated out from other types. For the system to have the ability accept a dummy booking which gets discarded just before critical points (e.g. not putting it in SDCI document or sending it off to be printed) might be a better approach.
The question that should be asked when using a certain approach are:
- Is it simple, does it require padding (translation to a baseline object)?
- Does it effect out existing tests
- Will there be an IIS downtime
- Are there any rehydration issues (using Biztalk)
- Will different teams be working on different versions
- If multiple versions are running side by side, will bug fixes be replicated across versions.
- Will it require additional resources?
- How big of a factor will maintenance be?
- Will the build pipeline, deployment, and installer creation be effected?
- Will the system be able to get good feedback in case of errors.
- Is functionality depreciable?
- How easy will it be to break the code? Will there be redundant code? How easy is it to get rid of older versions?
- How will rollback be done in an unfortunate circumstance?
Depending on the kind of change required, it may be best to always opt for a minor change as a stepping stone for marking it in stone as a major change.
Using a padding may work efficiently as long as there are not drastic internal changes between versions (e.g. v2 adds to SDCI, and LENNON, whereas v1 does not – then would be have to also add an attribute to which version it is meant for and have lots of if-statements in the code), otherwise the versions are incompatible.
1.13 Conclusion
This is a strategy document which provides information on different approaches to versioning. This was commissioned to have consistency while providing provisions for the long term approach. Short-terms solutions should be implemented with the aim of implemented the long terms goals successfully. Versioning should be done consistently with each new release (V2 to V3 should be implemented similar to V1 to V2). Since versioning primarily deals with backward compatibility, forward versioning should be a non-issue (v1 consumer trying to communicate with v2), and should not be considered for major releases.
Given that change is not always easy to implement it is vital to consider factor that help assist in deciding how to implement changes, and what the best approach is.
References:
Surekha Durvasula (May 2008), Why you need a stated “service versioning policy”, http://entarch.blogspot.com/2008/05/why-you-need-stated-service-versioning.html [ Accessed March 27, 2009]
Rosen, M., (2008), Applied SOA: Service-Oriented Architecture and Design Strategies, Rosen, M., Lublinsky, B., Smith, K., Balcer M., Wiley Publishing (p67, 325, 383, 484)
Boris, L. (2007), Versioning in SOA, Microsoft Architect Journal, April 2007 http://msdn.microsoft.com/en-us/library/bb491124.aspx
David Ezzo, BEA [http://mail-archives.apache.org/mod_mbox/openjpa-dev/200707.mbox/%3C46A674A3.8060601@bea.com%3E]
