One of the biggest problems that faces designers of distributed application is making sure their applications are resistant to change (i.e. versioning). Making sure services are designed with forwards and backwards compatibility in mind is especially challenging when one has no control over the various parties that will be invoking the service.
In traditional applications, enumerated types (aka enums) are particularly problematic when it comes to versioning. The problem case being when new values are added to an enumerated type in a later version. The .NET Framework Design Guidelines about adding new values to enumerated types shows how insidious this problem actually can be. The original guidelines stated that it was OK to add values to enumerated types but this was later surrounding with lots of warnings as to why this is a bad idea. The original guideline states
It is acceptable to add values to enums If a caller receives an enum as an out or return value (or as a parameter to a virtual method), and switches over every valid value of the enum and throws unconditionally in the default case, then added values will cause the caller to perform the default case, and throw If a caller receives an enum as an out or return value, and performs some default behavior in the default case, then added values will behave as if they were default values If you receive compatibility data for your application which indicates returning the new values from the existing API will cause issues for callers, consider adding a new API which returns the new (and old) values, and deprecate the old API. This will ensure your existing code remains compatible.
It is acceptable to add values to enums
If a caller receives an enum as an out or return value (or as a parameter to a virtual method), and switches over every valid value of the enum and throws unconditionally in the default case, then added values will cause the caller to perform the default case, and throw
If a caller receives an enum as an out or return value, and performs some default behavior in the default case, then added values will behave as if they were default values
If you receive compatibility data for your application which indicates returning the new values from the existing API will cause issues for callers, consider adding a new API which returns the new (and old) values, and deprecate the old API. This will ensure your existing code remains compatible.
The following addendum was later added
Adding a value to an enum has a very real possibility of breaking a client. Before the addition of the new enum value, a client who was throwing unconditionally in the default case presumably never actually threw the exception, and the corresponding catch path is likely untested. Now that the new enum value can pop up, the client will throw and likely fold. The biggest concern with adding values to enums, is that you don't know whether clients perform an exhaustive switch over an enum or a progressive case analysis across wider-spread code. Even with the FxCop rules above in place and even when it is assumed that client apps pass FxCop without warnings, we still would not know about code that performs things like if (myEnum == someValue) ... in various places. Clients may instead perform point-wise case analyses across their code, resulting in fragility under enum versioning. It is important to provide specific guidelines to developers of enum client code detailing what they need to do to survive the addition of new elements to enums they use. Developing with the suspected future versioning of an enum in mind is the required attitude.
Adding a value to an enum has a very real possibility of breaking a client. Before the addition of the new enum value, a client who was throwing unconditionally in the default case presumably never actually threw the exception, and the corresponding catch path is likely untested. Now that the new enum value can pop up, the client will throw and likely fold.
The biggest concern with adding values to enums, is that you don't know whether clients perform an exhaustive switch over an enum or a progressive case analysis across wider-spread code. Even with the FxCop rules above in place and even when it is assumed that client apps pass FxCop without warnings, we still would not know about code that performs things like if (myEnum == someValue) ... in various places.
Clients may instead perform point-wise case analyses across their code, resulting in fragility under enum versioning. It is important to provide specific guidelines to developers of enum client code detailing what they need to do to survive the addition of new elements to enums they use. Developing with the suspected future versioning of an enum in mind is the required attitude.
There is an additional wrinkle when adding values to an enumerated type in XML Web Services especially if the calling application is built using the .NET Framework. Let's say we have the following enumerated type declaration in the schema for v1 of our service
<xsd:simpleType name="SyndicationFormat"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="RSS10"/> <xsd:enumeration value="RSS20"/> <xsd:enumeration value="CDF"/> </xsd:restriction></xsd:simpleType>
and in a later version modify it in the following way
<xsd:simpleType name="SyndicationFormat"> <xsd:restriction base="xsd:string"> <xsd:enumeration value="RSS10"/> <xsd:enumeration value="RSS20"/> <xsd:enumeration value="CDF"/> <xsd:enumeration value="Atom"/> </xsd:restriction></xsd:simpleType>
Of course, as mentioned in the amended discussion on adding values to enumerated types in the .NET Framework design guidelines, this is a forwards incompatible change because new messages will very likely not be properly processed by old clients. However when the consuming applications are built using the XML Web services capabilities of the .NET Framework we dont even get that far. Instead you will most likely get an exception that looks like the following
Unhandled Exception: System.InvalidOperationException: There is an error in XML document (1, 1022). ---> System.InvalidOperationException: 'Atom' is not a valid value for SyndicationFormat. at Microsoft.Xml.Serialization.GeneratedAssembly.XmlSerializationReaderSyndicationService.Read4_SyndicationFormat(String s)
This is because the client code will have been generated from the v1 version of the WSDL for the service where "Atom" was not a valid value for the SyndicationFormat enumerated type. So adding a value to an enumerated type in an existing XML Web service end point is pretty much guaranteed to break client applications.
I love my day job. ;)
PS: This example is made up but the problem is real.