Branch by Abstraction
Branch by Abstraction is a set-piece technique to effect a ‘longer to complete’ change in the trunk. Say a developer (or a pair of developers), has a change that is going to take five days to complete. There could easily be pressure to create a branch for this - somewhere that can be unstable for a period of time before it completes (and gets merged back somewhere).
There may be some aspect of repetition to the coding activities that makes it longer to complete. No matter, the change was predicted as being time-consuming, complex, destabilizing/disruptive to everyone else in the development team.
- There are also a lot of developers already depending on the code that is subject of the ‘longer to complete’ change, and we do not want them to be slowed down in any way.
- No commit pushed to the shared repository should jeopardize the ability to go live.
For simplicity’s sake, let us say there is code that is ‘to be replaced’, code ‘to be introduced’.
- Introduce an abstraction around the code that is to be replaced, and commit that for all to see. If needed, this can take multiple commits. None of those are allowed to break the build, and all of them could be pushed to the shared repository in order, and as done.
- Write a second implementation of the abstraction for the to-be-introduced code, and commit that, but maybe as ‘turned off’ within the trunk so that other developers are not depending on it yet. If needed, this can take multiple commits as above. The abstraction from #1 may also be occasionally tweaked, but must follow the same rule - do not break the build.
- Flip the software ‘off’ switch to ‘on’ for the rest of the team, and commit/push that.
- Remove the to-be-replaced implementation
- Remove the abstraction
Hopefully, your team uses an IDE that can perform complex refactorings on sets on checkouts, in a way that running the build after each is an uneventful validation of the refactorings.
Let’s talk about a car having its wheels upgraded. We should never forget that software engineering is nothing like conventional construction, and we want to ram that home. At least, it is nothing like conventional construction where we are not talking about a production line.
- Mechanics must be able to simultaneously work on the upholstery, engine, etc.
- The car must be drivable after every change.
All steps are efficiently performed raised up on car jacks/lifts/ramps, before lowering the car to the ground again.
- One wheel is removed, put in a container that looks pretty much like a wheel (rotates around an axis, can bear weight) and replaced on the car. If driven this wheel functions exactly like the other three wheels.
- The wheel-like container gains a second better/desired/alternate wheel, within exactly the same physical space (magically). A switch is added inside the car to allow the choice of wheel to be switched conveniently
- perhaps only before the engine is started, though.
- The same two operations (#1 and #2) are performed on the other three wheels. Or maybe #1 is done four times, followed by #2 four times. The Mechanics experience will guide which is most efficient.
- After determining that the new wheels are better, the old wheels are removed from the wheel-like containers and are sent for recycling.
- The wheel-like containers are also removed from the new wheels, either one by one or all four simultaneously.
At any stage, when lowered from the jacks/lift/ramps, the car could have been driven (a ‘ready to go-live’ metaphor).
We said ‘jacks’ above, because that’s what mechanics use in real life. Software, however, does not follow the rules of gravity, or many of the costs of actual construction. With an IDE for a glove, a single finger could reposition the car in 3D space to allow easy replacement of the wheels.
A documented case is ThoughtWorks' Go CI-daemon. They changed an Object-Relational mapping library (for persistence), while not slowing down teammates development activities (rule 1), and not jeopardizing the ability to go live (rule 2).
Going from “iBatis” to “Hibernate” for a bunch of reasons, was their plan.
- Introduced an abstraction around the classes/components using iBatis directly, and ensured that all classes/components indirectly referring to iBatis were changed to refer to the abstraction instead.
- Wrote a second implementation of the abstraction, introducing Hibernate to the codebase, perhaps tweaking the abstraction where needed.
- Did a tiny commit that turned on Hibernate for all teammates.
- Removed iBatis, then the abstraction and the on/off old/new switch.
As it happens you could leave the abstraction in place, if your unit tests are able to benefit because of the possibility of another seam that can be mocked.
Cheaply pause and resume ‘migrations’
The migration from old to new can be paused and resumed later casually. This is because the build guards the second, incomplete, implementation. It does so merely because of a compile stage that turns the abstraction and somewhere between 1 to 2 implementation into object code. If there are unit tests for the two alternates, then even more so.
If on a real branch, the casual restart of the paused initiative is missing. There’s possibly an exponential cost of restart given the elapsed time since the initiative was paused.
Pause and resume is much more likely in an enterprise development organization that does not have limitless coffers.
Cancellation of a project is still cheap
In the case of abandonment, deleting a real long running feature branch is cheaper, but deletion of a branch by abstraction thing is only incrementally more expensive.
Not a panacea
Branch by Abstraction does not suit all ‘change’ situations.
One is when you have got to support old APIs and previous releases for more than a short period of time. I.e. when your dependent customers (or detached clients apps) can choose their own upgrade moment.
Dedicated website for this procedure
Teams employed Branch by Abstraction many years before it got its name (Stacy Curl named it in 2007), but it is unknown when the first implementation was. Before the adoption of BbA, teams had to make a branch for the big lengthy disruptive change, or do it with an incredible amount of choreography: “hey everyone, take a week of vacation now”.
With the Branch by Abstraction technique, Trunk-Based Development was less likely to be temporarily or permanently abandoned for a multi-branch model.