I like to look at things the other way around and ask the question: Is it upward compatible? Then the answer is: If we don't need to maintain a parallel version.
Upward compatibility is not an easy term to define. I can give you a hundred definitions that might work but don't. Consider these. It is upward compatible (UC) only if:
· It doesn't change the user interface
· It runs on the current hardware
· I don't have to convert or reload the database
· It doesn't de-stabilize the build
· The documentation doesn't have to be changed
· It supports the same message protocol
And so on. There are a lot of seemingly good candidate definitions and they're not bad considerations. In fact, they're very good. The problem comes when I go back to the original question: Do I need to branch this software and maintain a parallel version? Now look at the list above again. A developer takes his answer if it's not upward compatible and proceeds to branch his or her software subsystem 19 times before release 1 is even out the door. Why?
Well a lot of changes are going to occur to the user interface early on. The current hardware is changing rapidly to meet production cost targets. The data schema has not yet been finalized and my change requires some new fields. The build is already unstable. This feature was built slightly differently to provide more flexibility than the requirements and specifications mandated, in order to reduce complexity and increase reliability - so the documentation has to be changed. We needed to add a new message and a new parameter to another one in order to complete this feature properly.
The Customer is Right
All sorts of scenarios exist where any of the above are valid UC changes. We get closer to the answer when we start asking the question: Does it affect the customer? That's a good one. If there are no customers yet, I really am a lot less concerned if the user interface is still changing. I would like to avoid having to recreate my test bed every week because of database re-creation requirements, though.
Maybe the key is to consider the internal customers as well as the external customers. Until, that is, you fail to deliver a change because it was Non-UC (NUC) and your test lead complains that if you don't deliver it soon, he's not going to have time to verify the product. As far as he was concerned, the NUC change was really UC, even though others consider it NUC. We're basically working our way back to a better definition of UC.
I would define a change as upward compatible if, and only if, there is no need to maintain the parallel version. It's not just a condition, it's a definition. And it forces you to understand the business conditions which designate the need.
Some will argue that this isn’t good, as I can't look at my change and know whether it’s UC or NUC. That's right. You can't. In fact, I can take the exact same change at two different points in time and it will be UC at one point but NUC at another. You cannot just look at the change. You have to look at the whole picture.
Sometimes we find this out the hard way. You have to look at the whole picture. So how does this definition of UC help? Well, basically, it begs the question by focusing you on the heart of the matter. You need to understand if a change you are making can go ahead without continuing support for the previous version - then you know whether it is UC or NUC. If it is NUC, then you need to first identify if there's an UC (sounds the same as "a NUC!") way of doing the same thing. You'll be surprised at the ingenuity when someone understands the impact of their change.
NUC to UC Transformation
If you cannot find an UC way, you then need to break it down into a series of steps that make the "NUCness" easier to swallow. First, identify all of the customer-affecting elements (in the broader sense) and how they affect each customer. Then identify the best way to prepare the customer to accept the change most easily. Maybe it's adding a data field to the schema ahead of time. Maybe it's retro-fitting a feature into the current customer release first. Maybe it's throwing a party for them on the day the system is going to be out of service! You want to isolate the incompatibility from the rest of the change as much as possible, and make it less visible to the customer.
Don't bundle UC changes with the NUC changes where you can avoid it. Your job is basically to identify a way to transform the NUC change into an UC one, with as little risk and impact as possible. OK, our network is going to be down for 2 hours. If all the NUC stuff, and only the NUC stuff is done in that 2 hours, it's easier to believe the downtime estimate. It's easier to roll back if something goes wrong. It's easier to understand the risks. Then when the network is back up, add the rest of the changes in - the same day or over the next few weeks. Unless there's an urgent concern, it won't matter much. And the customer knows the network will remain up.
Eventually you'll start to use foresight, such as enabling the previous release before it is shipped, with the proper data structures so that conversion to the next release is painless. If you work at identifying (and I would argue implementing) NUC changes near the beginning of your release development cycle, there's a good chance you'll have time to put some hooks into the previous release before it's widely distributed.