Monday, April 30, 2007

Branching by Abstraction

Ok Paul, I'll bite.

Some time ago I was working for a client which was experiencing merge hell, come to think of it all the large clients I've worked for routinely experience merge hell, hopefully this isn't correlated with my presence. At the time I mused that when someone creates two implementations of an interface they are effectively branching and so I
voiced that instead of using version control to branch some code perhaps we should Branch by Abstraction. I was working with Paul Hammant at the time and he's apparently ran with this idea and achieved some notable improvements with it.

I don't think BBA it is really that novel, it's just another way of saying 'Hey!, Separate your Concerns', but since a lot of coding-land seems to be engaged in a energetic fight to do the opposite it's probably good to have as many ways of stating the bleedin' obvious as possible.

I'm quite a lazy creature by habit so I've only drafted an article on this on C2, and intend (honest) to polish it up over time, here's a copy of the first draft:

Summary: Don't separate differing concerns by using a VCS, use an abstraction instead.

Long story: Conceive the changes of a project as providing alternate implementations of abstractions already present in the software, or of extracting out an abstraction and making it explicit and them providing an alternate implementation. By doing so the abstraction and the varying implementations provide the separation and branches are need much less.

Consider a possible project that aims to add more data to a business report, say a customer invoice. Suppose that the data being added is the date on which a transaction took place. The current CustomerInvoice might obtain a bunch of LineItems and render them. The new requirement is to add date information to the LineItem and display this on the invoice. At the same time as this project is going on bug fixes are being applied the existing invoice, so we can't just make changes to the invoice.

Step 1) Extract out abstractions needed by the project which are not currently explicit in the code.

1a) Extract the code which provides line items into an interface and implementation: LineItemProvider, NameAndCostLineItemProvider, make the implementation the default the CustomerInvoice uses. Deprecate the implementation with a note to its impending replacement. Test and release to production.

1b) Extract the code which renders line items into an interface and implementation: LineItemRender, NameAndCostLineItemRenderer, make implementation the default the CustomerInvoice uses. Deprecate the implementation with a note to its impending replacement. Test and release to production.

Since the changes in Step 1 should be expressible as a series of refactorings it should be possible to make the test step very short, or arguably unnecessary.

Step 2) Create alternate implementations of the abstractions created in step 1, or already existing before the project began. NameCostAndDateLineItemProvider, NameCostAndDateLineItemRenderer.

Step 3) Release the new implementations in a QA environment configured to use the new implementations.

Step 4) Release the new implementations in production configured to use them.

Optional step) Back out the implementations from production, revert back to the previous implementations at run time.

Whilst most of the project is going on (steps 2 & 3) bug fixes can still be applied to all other aspect of the system, and the dated-invoice project just receives these changes for free, as they are only changing the line item providing / rendering aspect. If a bug fix is needed in the line item providing / rending aspect the bug fix team easily become aware of the new implementation via the deprecation notice, alternately the dated-invoice team can monitor changes to the undated-implementation.

If another project starts up whilst the dated-invoices is chugging along then it will have the same steps, and will involve work on some other abstraction. Should another project need to do work on the same abstractions they too will have the deprecation as a notice to be careful and consider interaction with the ongoing work on dated-invoice, otherwise they can work merrily in ignorant.

So in short the encapsulation that abstractions provide can work for projects just as well as they work for code. When changes occur in other projects then as long as it doesn't involve the abstractions I'm working on in my project then I don't need to care.

Compare this with BranchByVcs. In this one creates a fork of a much larger set of code, probably all of it. Other projects / bug fixes will occur in the same code and later the two changes will be merged together, the merge will take place a series of poorly understood textual changes with an unspecified focus, additionally the merging of changes from some project is usually done by people not on the project.