“Don’t use branches”. Three words that are guaranteed to trigger reactions when I utter them during my continuous deployment talks. Three words that make for interesting discussions. This post contains some of the arguments I use during those discussions.
Continuous deployment and the master branch
A lot of teams are used to having some combination of feature, release and development branches, possibly using tools such as git-flow. At any given moment, depending on the size of the team, tens to hundreds of branches may be active in a single repository.
Full disclosure: I’m a continuous deployment advocate. And I feel that if your goal is to achieve true continuous deployment (or even continuous delivery), that’s a model that’s not going to work. Or at least, it’ll be a barrier to progress. I’ll explain why having only a master branch is the way to go.
The cons of using (feature) branches
Simply put, branches delay integration and break continuous integration. This can lead to many issues: merge conflicts, incompatible code and functionality, build failures, etc. Until everything has been merged to the master branch, it’s impossible to know how the combination of all the changes that a team is working on fit together. This issue is further compounded when it takes a while for branches to be merged.
For a nice description of the pitfalls of using (feature) branches when developing a large scale, complicated product (SQL Server), please read this post by Remus Rusanu.
Eric Ries explains in one of his older posts, “Why Continuous Deployment?”, the merge issues a team can run into when using branches. He calls that code bouncing:
… In a code bounce, someone tries to check in a huge batch. First they have integration conflicts, which require talking to various people on the team to know how to resolve them properly. Of course, while they are resolving, new changes are being checked in. So new conflicts appear. This cycle repeats for a while, until the team either catches up to all the conflicts or just asks the rest of the team for a general check-in freeze. Then the fun part begins. Getting a large batch through the continuous integration server, incremental deploy system, and real-time monitoring system almost never works on the first try. Thus the large batch gets reverted. While the problems are being fixed, more changes are being checked in. Unless we freeze the work of the whole team, this can go on for days. But if we do engage in a general check-in freeze, then we’re driving up the batch size of everyone else – which will lead to future episodes of code bouncing. In my experience, just one or two episodes are enough to cure anyone of their desire to work in large batches.
But, I hear you say, what if we always keep our feature branches small and short-lived, doesn’t that mitigate these issues? Unfortunately, no:
- Not all features can be completed in a day or two;
- Product may not want a certain feature live yet, or not in combination with other features;
- Even a branch that lives for a day, or two, may contain a large number of commits.
Not to mention develop and release branches, which by definition are long-lived.
Or, put in other words (Dan Bodart):
Feature Branching is a poor man’s modular architecture, instead of building systems with the ability to easy swap in and out features at runtime/deploytime they couple themselves to the source control providing this mechanism through manual merging.
Feature toggles, switches, flags
Enter feature toggles. Essentially if statements, they provide that ability, an elegant way of turning certain features on or off, without having to change and redeploy the code. An application can determine the setting of a feature toggle using cookies, based on IP ranges, configuration settings, any number of sources. There are many libraries that implement (some form of) feature toggles and even companies offering them as a service.
Feature toggles allow us to make full use of continuous integration and deployment, spreading out a feature over multiple small commits. Once a feature is complete, the toggle can be switched on. Feature toggles enable continuous experimentation, iteration and improvement. Be conscious of their number though.
Pull requests and code reviews
A lot of teams use pull requests to do code reviews. One developer writes some code, submits a pull request and another developer reviews that code. Simultaneously, automated checks can run tests and verify whether the pull request can be merged with the main line. Useful functionality, but pull requests are essentially branches, so those are out too!
Code reviews are a very useful tool to maintain standards and quality. But where do they fit in the workflow if we can’t use pull requests and all our commits go the master branch (which is automatically deployed)?
The answer: pair programming!
Pair programming enables continuous, real-time code review. Mix experienced team members with less experienced team members and you get transfer of knowledge as well. Make sure pair partners regularly switch roles (driver, navigator).
Any good rule has a few exceptions. With master-only development, I can think of the following:
- Work in progress
You & your pair programming partner started work on a new task right before the end of the day and the work is not yet ready to commit. For example, you have a red test but haven’t finished the implementing code yet. It’s probably wise to push that work on a “wip” branch so that the work, should you or your partner call in sick the next day, or your machine crashes, can be continued without delay. The work should of course be pushed to master as soon as possible, and then the branch should be deleted.
- Throw-away prototyping
If you’re trying out a new library or tool and other people in your team need to see that work, then a prototype branch is acceptable. After the test is done, the branch should be removed. Incremental/evolutionary prototyping should of course happen on the master branch.
I hope this post clarifies my views on (feature) branching, especially when viewed in the context of continuous deployment. Naturally, I’m interested in your feedback, so let me know what you think about using just the master branch!