Monday, March 01, 2010

Branching with Design

Code.  Code never changes. 
The end of the world occurred pretty much as we predicted.  Too much duplication.  Not enough manpower to go around.  The details are trivial and pointless.  The reasons, as always, purely human ones. 
(adapted from the beginning of the last real Fallout game)
 I hate branches.  I hate them... and why shouldn't I?  Here is the promise of a branch...

We're going to defer integration for a long time so that it will cost more when we do it but don't worry, until the branch is merged, we're going to essentially maintain two copies of the same code base.  Make that three.  Make that four.  Make that... ah fuck it.
Recently, a pretty decent argument was made in favor of branching...
We have to change X.  Whenever we change X, everyone on the team is broken for months.
Naturally, the first response is "Well, it shouldn't be that way."  To which the person with whom I was having the debate replied...
I know but it is.
Check...
We're trying to fix it but that's the way things are right now.
 ...aaaaaaand mate.

The team understood that it was a huge problem and wanted desperately to fix it but didn't have the resources to do all of the necessary refactoring before they had to change X.  Because of the size of the system, its complexity, and the level of encapsulation around X, there were going to be breaks; no "if"s, "and"s, or "but"s about it.


So what to do, then?  The answer is, as I'm sure you've guessed now, to branch with design.  What, precisely, does that mean?  The steps can be summarized pretty simply as follows:

  1. Find the places where X, is used and its behavior needs to change.
  2. Create a Facade that exposes just the functionality needed by those touch-points.
  3. Promote the Facade to an abstraction with its only variation being the classic usages of X.
  4. Make the old behavior the default variation of your Facade abstraction.
  5. Create another variation into which the new behavior is placed; provide QA with a place to "turn on" the new behavior.
The alternate behavior can be a Proxy to X, an alternate implementation... it can even be incomplete or not work - it's not being used in the normal "production" code path.  Yet, your "branch" is continuously integrated.


Without even touching your continuous integration configuration code, your "branch" will be built with the rest of the code.  If a test fails against it, your build goes red.  If you forget a semicolon, your build goes red.  You will receive continuous feedback as though you didn't branch... because you didn't.  Yet you will be able to insulate people working on other things from pain caused by your changes as if you did.


Now, I'm not claiming this as my own.  It would have to be crazy to do so as it is basically nothing more than a narration of how the Facade pattern plays out when you pursue it aggressively.  My point is not "Hey!  Look at this neat thing I invented," because I didn't invent it.


Instead, my point is that you can always do this and it always works.  I'm sure that, somewhere out in the world, someone has a good reason why they absolutely have to branch.  Disregarding such one-in-a-million scenarios, you should always branch with design rather than with source control.  It's safer, cleaner, and lower cost.