Compatibility and Breaking Changes

I posted a comment to ScottW's weblog about sealing classes and it got me thinking about breaking changes.

 

A breaking change is a typically defined as a change made to an API, or an API's behavior that would cause code compiled on one version of the .NET Framework to somehow fail when run on a different version.

 

This brings up the .NET Framework's breaking change policy.  A typical knee-jerk reaction is, "Well, just don't make any breaking changes.  Then apps will always run, right?"

 

The problem comes when you try and define the term "fail".  What is a failure?  Surely, an exception where none was thrown before.  How about a different order of events?  What about a UI fit and finish breaking changes?  On the far end of the spectrum, any bug fix you make is breaking change.  This is obviously unacceptable.  So where is the line drawn?

 

This is a question we debate about internally at MS to no end.  Dozens of the brightest folks I know can sit in a room for an hour and get no closer to a resolution on even a single change.

 

It gets even more interesting when you consider the other types of breaking changes.  The scenario described above, v1 app running on v1.1 is known as a backwards breaking change.  A change that affects a v1.1 app running on v1 is known as a forward breaking change.  A slightly better description of what this means can be found on gotdotnet.

 

When all is said and done, and bits are released, we do end up making some breaking changes.  Reasons for a breaking change getting checked in include a security fix or a change with a large customer benefit and small chance of breaking apps.  And as I mentioned earlier, any breaking change which gets into a build is debated at length.

 

And then we publish them.  Both backwards and forwards.

 

One of the new compatibility rules for the next version of the Framework we are struggling with is the no enum additions rule.  We asked the Windows compatibility team what their top reasons for an app to break are and they told us additions to an enumeration. 

 

Imagine this:

 

There is the FoofyEnum with members ValueA, ValueB, and Other.  You write a switch statement with a case for ValueA, ValueB and then the default case.  In the next verison of the Framework, we add the ValueC enum.  Your code would never be expecting the ValueC and would fall through to the default processing when it was encountered.

 

Another mitigating factor to consider is the .NET Frameworks side by side policy.  SxS lets you run multiple versions of the Framework at the same time on the same machine.  But, as a general rule, you can't rely on SxS.  There are situations where an app's preferred runtime version won't be present and it will default to a version that has breaking changes. 

 

What all this means is that we have to come to terms with the fact that we are a platform.  We need to learn what we can from the Windows compatibility lab and do our best to make smart decisions about which bugs we can fix and which ones will cause developers endless headaches.

 

Oh yeah, and we're stuck with API names for eternity.  Make sure and thank Brian Pepin the next time you use the DesignerSerializationVisibilityAttribute.

 

I'd be interested in hearing your comments on what is an acceptable level of breaking change to make.