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.

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.

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.


 

Wednesday, 31 August 2005 15:44:49 (GMT Daylight Time, UTC+01:00)
This page was just reported as "Suspicious Website" by IE7 B1... Wonder what criterion triggered that.

But to your point: I think making the general statement to avoid enumerated types is a bit strong. There are many, many scenarios where the observed behaviour is just great. Especially the ones that are described as problematic in the guidelines that you quote. There the problem is that you introduce a new member to an enum and can't be sure that all code pieces that switch on that enum will handle the new value correctly. The way the .Net endpoints are constructed make the boundaries explicit: You can be sure that you won't get messages with values for enums that you haven't tested or designed your service for. There are MANY scenarios where that is great. Maybe not the one you are working on right now.
David
Wednesday, 31 August 2005 16:10:58 (GMT Daylight Time, UTC+01:00)
Also this is only a problem if you're sending the enum back to the client. If you have the "server" and it is the one receiving the enum, things would be just fine.

But, its definitely something to keep in mind when versioning!
Wednesday, 31 August 2005 16:12:34 (GMT Daylight Time, UTC+01:00)
David and Dan,
As you both point out this is primarily a problem when services return enumerated types not when they accept them as input.
Thursday, 01 September 2005 07:05:41 (GMT Daylight Time, UTC+01:00)
Just something to keep in mind?! It seems like so many developers would not know a fundamental architectural flaw if it hit them with a cat 4 hurricane. This is a case in point why all the effort that goes into strong typing and tight coupling is a monumental waste, and yet here you are dutifully writing a page and a half about watch out for this and that, caveat this and that... oh, and things will be fine if the server is the one receiving the enum... can't see the forest for the trees I guess.
Ben Bryant
Thursday, 01 September 2005 09:16:26 (GMT Daylight Time, UTC+01:00)
This isn't only a problem with strongly typed enums. Any time a response from a server is extended clients which consume it may break. Returning an INT instead of an enum has the same effect, for instance, in that extending the range of return values can cause clients to trip into untested code.

This MSDN article on SOAP versioning discusses the problem as well http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnbda/html/SOADesignVer.asp.

Don Sprague
Comments are closed.