In the previous article I showed how to add an optional element and make this a compatible change over two releases, so existing receivers aren’t broken by unknown elements. It is done by following two simple principles:
- have one or more intermediate releases;
- use different schema’s in intermediate releases for senders and receivers.
This strategy works well in environments where ‘Big Bang’ upgrades are undesirable and there is control over the number of different releases ‘in the field’. This applies to the Dutch national EHR infrastructure, for which I work, and probably for most exchanges between companies and/or governments.The same can be done for any kind of change. Consider making an optional element required: we have an existing
Order V1:
- customer-id 1..1
- name 0..1
- order-line 1..n
with an optional ‘name’ element, and we want:
Order V2:
- customer-id 1..1
- name 1..1
- order-line 1..n
Upgrading some receiving nodes to V2 would break them once they receive orders without names. If we use an intermediate release, there are no problems:
Sender 2 cannot break receiver 1. Once all parties are at V2 at least, receivers can start to upgrade to V3. If the level of control is such that there are no more than two versions allowed simultaneously, this is sufficient – else we’ll just have to have some more intermediate releases.
There are more use cases. Consider code lists: in V1, we allow the Netherlands and the USA as countries. In the next release, we want to trade with Ireland as well:
If we upgrade in two steps, every subsequent pair of releases is fully compatible. No sender can break a receiver. The same if we remove old codes:
It’s as easy to increase maximum cardinality:
Some changes take more than one intermediate release, such as adding a required element where there was none:
Sometimes we change senders first, other times receivers. It turns out there is a simple rule behind this: if the change from V1 to the final version is – by itself – backward compatible, we change receivers first. If the change is forward compatible, we change senders first.
Backward compatible changes (where new receivers can handle content from old senders) are:
- making required elements optional;
- introducing new optional elements;
- increasing maximum cardinality (from 1 to n, or 0 to n, or 1 to 2);
- adding code values;
and in general all changes which allow more document instances.
Forward compatible changes (where old receivers can handle content from new senders) are:
- making optional elements required;
- removing optional elements;
- decreasing maximum cardinality (from 1 to n, or 0 to n, or 1 to 2);
- removing code values;
and in general all changes which allow less document instances.
All patterns are reversible – if one way they describe a backward compatible change over multiple versions, then the other way they describe a forward they describe a forward compatible change and vice versa. In a next post I’ll look at some advantages and disadvantages of this approach and other approaches, such as using <xs:any> schema elements.