In this blog I argue that as a developer working in a team, the more frequently you incorporate incoming changes the better — problems are spotted quicker and you can adjust your work to fit sooner, saving greater pain further down the track. Working small is beautiful.
I’ve been a Trunk* Based Development (TBD) advocate for over ten years now, probably from reading The Art of Agile Development early on; but it also is a key part of *really* doing DevOps — in the Accelerate metrics the best performers have changes in production within 30 minutes of a change being made; you can’t really muck around with branches much and achieve that.
*= other names are available, but I’m sticking with the term for this blog, being the advocate I’ve just said I am.
This was reinforced by a demo for a whole two week sprint’s work being sabotaged by someone working on a branch for the two weeks and merging it at the last minute (actually I think it took 2 days to merge it). It was rather embarrassing that the great functionality we’d implemented and had working the night before was completely destroyed. That and the lack of contrition from the errant-merger, which made it all the more infuriating.
Having ended up in the situation of merges-from-hell myself I have actively tried to avoid ending up in there to begin with — it’s the biggest pain point — partly by influencing work-flow design, partly by trying to downsize commits and pushes, and partly by staying up to date.
Another source of my evangelism is an interest in the principles of Lean, coming from reading around Toyota and Theory of Constraints (ToC) from The Goal; this comes down to any changes not on Trunk is Work In Progress (WIP). And Work in Progress is all Work at Risk.
Work at Risk means that this means that until you know that someone is prepared to pay for the work your doing, you’re funding it yourself — this leads nicely on to minimising the size of feedback loops, and goes hand in hand with ensuring you are delivering value in as small as possible increments.
Continuous Integration is an absolute given for modern software development; of course, this is built on top of version control for your code, but having kicked off with TBD, I’ll skip that.
Part of the problem is that, as they say, git is a git; it has made branching far too easy, so people do it. All the time, they’re addicted to it. Damn their eyes.
Having said that, I can see value in branches being used to run the CI build before automatically put on Trunk after passing.
Half the problem
So in my mind half of the problem is that people use branches. Or perhaps that’s a quarter of the problem, and the other quarter is that they use long-running branches; as a rule of thumb, anything over a day.
The other half of the problem is that when they do put their work where it should be, on Trunk, by merging or by rebasing, they haven’t updated their branch with the updates already rushed/committed on Trunk since they created their branch.
I’ve always found rebasing a branch to be surprisingly cumbersome for what should be a ‘normal operation’. Because of <reasons> (you’re rewriting history) you need to use the command “git push --force-with-lease” (this is safer than just ‘force’ which will overwrite *anything*).
git push --force-with-lease
This is even worse because to physically type force-with-lease you will have typed just “--force” at one point, so if you hit <enter> at the wrong time bad-stuff can happen. Oh, and make sure you’re configured to only push the current branch and not all branches.
So, it may seem a safer course to rely on rebasing manually, and in the developer’s ‘own time’. But one of the points of Continuous Integration is to reduce the size of differences at any given point, so any changes introduced on Trunk should be (generally) reflected on branches as soon as possible.
To provide an analogy - and sneak (or otherwise) in a Withnail and I reference — working on a branch is like holding onto a rising balloon; how long before the rebase (or merge) gets unmanageable? A similar dilemma exists with updating dependencies; it’s tempting to stick your head in the sand and not update them, but that makes it worse when you need to, and modern day security concerns make that inevitable.
“If you’re hanging on to a rising balloon, you’re presented with a difficult decision — let go before it’s too late or hang on and keep getting higher, posing the question: how long can you keep a grip on the rope?”
— Danny, Withnail and I, 1986
A lot of the point of Lean and Agile development is to minimise the length of the feedback loops; the sooner a developer knows about changes on Trunk, the sooner they can integrate them, and the sooner they know what they’re dealing with, and less likely to end up with conflicting changes.
Regular rebasing minimises the distance between your changes and what you need to integrate with, the earlier you see the changes you need to incorporate (or indeed overwrite) the sooner you can do something about them and the less time you’ll waste. If nothing else it will tell you you need to go and speak with the other person(s) working on the same code as you. Like a bug, the sooner you find it, the cheaper and easier it is to fix.
Is there an exception?
I remember someone I was working with ten years ago describing Continuous Integration as the quickest way to spread problems round a development team. Sadly I can’t remember who it was so can’t credit them with that.
But that leads nicely to why you might not want to take changes —there is a Golden Rule in CI that you should never commit anything but a fix to a broken build. This works both ways for rebasing, the only time you should hold off rebasing is when either of the two branches involved are not building cleanly, particularly Trunk; this is because you won’t know what broke the build.
So what can you do about it?
In the next blog I’ll describe my explorations in addressing this on my teams codebase.