by Leon Rosenshein

On Branching

There are lots of different branching strategies. And they have their strengths and weaknesses. And there is no one right strategy. Don't let anyone kid you. There are however strategies that are wrong, especially ones that are wrong in a specific time and place.

Given that there is no consistent best strategy, how do you pick which strategy to use. The answer to that strategy is highly dependent on why you're making the branch. Unless you know that, you can't make a good choice. The other thing you need to keep in mind when deciding how to branch is your exit strategy. How (if ever) are you going to merge the branch back in, and what happens to the branch after that first merge?

Are you optimizing for individual developer velocity? Product velocity? Size of development team? Stability? Each of these will push you towards a different strategy.

Long lived feature branches make it really easy for a developer to get a feature to a stable point for a demo, and that developer will feel really good about their progress. Right up until they need to share their work. Because the longer they've worked, the further their codebase has diverged from the shared view. If they're lucky their work is isolated enough that conflicts, physical and logical, are small, but that's usually not the case. So you pay for that individual velocity at the end when it comes time to bring things back together and someone, usually a build team, has to deal with those conflicts.

The most common way to avoid that divergence is to keep branches short lived, on the order of days or less. After all, how far can two branches diverge in a couple days? The corollary to that is of course, how much progress can you make in a couple of days? So lots of tension there. But there are ways to make it easier. Clear boundaries/interfaces. Feature Flags. Small changes. Lots of automated tests. At the limit this is Continuous Integration/Continuous Deployment.

On the other hand, consider the commercially released product, nominally shipped in a box. Or maybe the firmware inside a physical LIDAR product. It's going to be out in the field for years. And you need to support it. And probably make some fixes/modifications. But the hardware itself won't change, and you want to be able to support new hardware/functionality going forward. So you create a release/support branch for that work. That branch is very static. And it's got a simple merge strategy. Never. Things that happen in that branch never go anywhere else directly. You might make the same logical change, but you don't do it as a merge. That lets you continue to do new cool things while supporting the old. In that case you've got a clearly defined reason for the branch and an end state. Go ahead and branch with confidence.

As an organization we've picked, rightly I believe, to use trunk based development and optimize for overall product velocity. That lets us do lots of good things. Like minimizing the time between getting a change approved and the time it's in production. Like minimizing the time between a change be done and it being available to others to build on. Like alerting us to problems early.

And to take full advantage of it, it requires some discipline on the part of developers. To write and maintain good tests. To have and maintain clear boundaries. To use feature flags. To keep changes small. To reduce side effects of changes. All things that are straightforward to do.

We just need to do them.