Branching in source control can be a rather controversial topic. We are unlikely to settle the matter today, but we want to share our philosophy, which is based on some basic principles and some hard-learned lessons.
The motivation behind creating a branch is the desire to isolate a copy of the code from changes being made by other developers. In that sense, the most basic branch is an individual developer’s working copy (we certainly don’t want to work on the same set of shared files). In fact, in distributed version control systems, you must create a “proper” branch (by cloning the repository) before you can create a working copy. From this point, we will exclude “working copy” branches from our discussion, as I think everyone agrees that they are a Good Thing.
Developers create branches for two basic reasons:
- Isolating code that is/will be released to production (a release branch)
- Isolating code for the purposes of long-term feature development (a feature branch)
In the context of a small to mid-size enterprise, we believe that development teams should regularly make release branches, and never (or perhaps very rarely) create feature branches. We believe in trunk-based development and the branch-for-release pattern.
Why release branches are good
Release branches ensure that you have easy access to the code that you are currently using in production. They allow you to make isolated bug fixes and rapidly roll out “patches” to production without the risk of a fresh rollout from trunk. They are your most important bulwark against “losing” code that your business depends on. As we have discussed before, we strongly encourage following a regular schedule for deployments to ensure that you are getting your newest code in front of your users, gathering feedback, and keeping the agile cycle turning. “Cutting” a fresh release branch from trunk should be a simple procedure (not necessarily an automated one, though — if you find that the manual process is too cumbersome, consider simplifying the process before automating), and you should make it part of your weekly (or bi-weekly, or daily) release routine.
To make a regular release procedure work, consider adopting the following practices:
- Make sure the entire team knows when the branch will be cut. This is all the more reason to cut on a regular schedule, so that the team internalizes the “rhythm” of the release process.
- Avoid re-using the same release branch and “merging” changes up to it. This is a recipe for disaster if the merge gets messed up. It is much less risky to “cut” a fresh branch every time, because you are guaranteed a branch that looks just like trunk did at the time of the cut.
- Adopt a standard naming convention for release branches. Our preference is to use branches that include the application name and the date in YYYY-MM-DD format. In subversion, you would create a directory like MyApp/releases/2013-05-17. A standard convention will help your team know where to go when they need to find a particular release branch.
- Create a CI build for each new release branch. In the same way that we prefer a fresh branch for each release, a fresh build is a good idea as well. You should keep your old release builds around until software built from them is no longer in production, then retire them.
- Only release software to production if it was built off a release branch. This may seem obvious, but is the linchpin to a successful branch-for-release workflow. Resist the temptation to throw trunk software out there “just this once”, because doing so will make life harder on everyone who needs to maintain that release.
Why feature branches are bad
Feature branches differ from release branches in one very important way — a feature branch is intended to be merged back into trunk at some point, while a release branch will never be merged (it will diverge from trunk until it is no longer needed, at which point it can simply be abandoned or deleted). This means that the basic purpose of a feature branch is to delay integration of the work being done there. This is the fundamental problem with feature branches. They represent a development pattern that is the complete opposite of what we are trying to achieve with continuous integration. Remember, CI is more than just having an automated build, it is the principle that all work should be integrated into a shared mainline as soon as possible.
Let’s review some of the benefits of following CI:
- The smaller each change to the mainline is, the easier it is to merge changes. Ideally, merging is rarely required, and is trivial when it is.
- There is always a single “latest” version of the software that all developers are working on. This version should always work and ideally be releasable to production.
- It encourages incremental change and progress. To continuously integrate, developers must find ways to split large features into small chunks that can be committed to the trunk. This is a good forcing mechanism for the creation of a well-factored code base.
- Code checked in to trunk will trigger a continuous build and a run of the automated test suite, ensuring that the trunk is always compiling and unit-tested.
- If you follow branch-for-release, it ensures that work gets deployed to production quickly and the feedback loop from your users is delayed as little as possible.
Now, consider how feature branching subverts those benefits:
- When the feature branch is complete, there will be a “big merge” at the end, which could lead to a painful merge process and the risk that the merge is not done correctly.
- There are now multiple “latest” versions of the software. Your team is no longer sharing a single code base.
- It discourages incremental change. In fact, it encourages broad, sweeping changes across the whole code base to get a feature done, as opposed to the incremental approach which would require the creation of well-factored interfaces and abstractions.
- You do not get the benefit of the continuous build unless you create a build for every feature branch.
- Since it delays integration and release, it delays your ability to get feedback from your users.
A common objection to the anti-feature-branch stance is that “you just feel that way because branching is hard in subversion — come over here and see the light of git!” I grant that branching and merging are much cheaper and easier under git than they are under subversion. However, just because something is cheap and easy doesn’t make it right. Feature branching fundamentally breaks the continuous integration workflow, no matter how easy the “big merge” is at the end.
We hope you’ll consider our point-of-view on this and think about religiously branching for release and avoiding feature branches like the plague. Care to disagree? Let us know in the comments.