What is a branching strategy?

Under the impression of “Continuous Integration” book

We should start from somewhere, so let me begin from an overview of the most popular branching strategies or models. This will help us define a basic vocabulary in order to draw a context around the opening question.

Stable Trunk

Trunk must be The Stability. No unverified code can get there. Just very done and well baked. Real work usually goes in the version-next branch. When your acceptance criteria have been met, you're able to merge updates into the mainline. Depending on a type of a software that you are working on (web application, desktop, embedded or distributed systems) and your release plans, you may publish or deploy this new version for the world.

Integration Branch

A buffer branch between a bunch of feature branches and the destination branch (e.g. master or rel2.0 version). As name implies, this kind of strategies stimulates any sort of branching because at the end those isolated changes have to be working together. But this model locates furthest from Continuous Integration ideas (and as some of you noticed Continuous Delivery/Deployment too). I don't say that it is not possible to have a good CI solution with many branches. I'm saying that here you just postpone real integration procedure by working on a separate branch and delaying your feedback loop. Integration is hard.

Unstable Trunk or Trunk-based development

Day to day commits on a trunk. When you're ready to release just cut a branch. Simple. Straightforward. Very CI friendly. Homepage.

Promiscuous Integration

This name was proposed by Martin Fowler:

With this approach they only push to the mainline at the end, as before. But they merge frequently with each other, so this avoids the Big Scary Merge. So is this more ad-hoc integration a form of CI or a different animal entirely? I think it is a different animal, again a key point of CI is everyone integrates to the mainline every day. Integrating across feature branches, which I shall call promiscuous integration (PI), doesn’t involve or even need a mainline.

GitFlow

Often people tend to believe that they are using something like GitFlow or A successful Git branching model. But in the real world of extreme situations, they might violate the rules implied by this strategy, and in the end, they could have something ugly like PI. GitFlow has many nuances and asks from a team the Spartan’s discipline. Really complex.

Let’s take one step back

Why do we need branching at all? Karl Bielefeldt says:

Unless you are all working out of the same working tree, you are using branches, whether you call them that or not.

So see in the branches a potential releasable version of your program. While any team works on a project, each member have been working on its own version. And the longer the work goes, the more different versions they all will have. "It works on my machine!"—have you heard that before?

The essence of Continuous Integration is a single version integrating all the changes from all the sources that can be verified for any requirements compliance and be ready for release. One of the key characteristics of a good CI solution is a short feedback loop. You've made a commit, build server makes his routines and viola—you are receiving report about what was done well and where something felled off.

Even if you try to make the world better making a super-refactoring in a feature branch, that brave new world do not stand still and your chances of meeting the Integration Hell are hugely increased.

There are several other reasons why teams may choose to branch their code.

Bit earlier, the same authors of Continuous Delivery book—Jez Humble and David Farley—wrote that only three of above are worth it:

There are three good reasons to branch your code.

Avoid Continuous Disintegration

We have different flavors of continuous integration practices. Starting from CI itself than going through Continuous Delivery and ending by Continuous Deployment.

All of them are united by idea of multi-level feedback loop that provide to a human, in a relatively short time, observations about work that they have just done. TDD (or sometimes Design by Testing) allows you to see how your programming language can express your design ideas. Unit test gives you understanding that your basic algorithm or happy and sad paths are valid. Integration testing brings you ability to see how groups of classes, modules, or components interact with each other, or how well 3rd party services are integrated with your application.

On a higher level, you could check the code quality by using various static code analysis tools. Functional application quality can be verified by going through packs of automatic regression and acceptance tests. Another level—performance, load and stress tests which also provide you good information about different sides and slices of your project.

Put this model simple: You've made a modification of your software (no matter application code, database scheme, environment configuration) and publish your update to the real working directory that automatically insures that all the things are still stable by using CI/CD mechanism.

Each time you create a branch you are making a step away from CI. Moreover, the longer the branch lives, the greater the likelihood of subsequent integration errors. Until of course you know what you're doing. Good questions to think about: Do you need a feature branch? How long is it going to live? What is the cost of merging? Do all the team members know what your branch are created for? Do you have all the tests that reassure your in that all existing functionality still working fine and as expected? Did you and all of yours coworkers really understand yours branching strategy?

Life without branching

Reasonable question: If branching is a potential step back from a continuous stability and integrity, how can one deal with releases when epic-like task or a huge refactoring took place on a road? Continuous Integration book says:

There are four strategies to employ in order to keep your application releasable in the face of change:

The first one is clear: Just use application settings and condition variables in your code or feature toggling frameworks. Second option is about more careful release planning, backlog grooming, or deep understanding of how to split unsplittable. Third, is a pattern proposed by Paul Hammant. And the last one bullet means a good knowledge of your domain, organisation structure, and change rates of individual modules of designed system.