Aug 292011

Before I dive into my main arguments, I do believe feature branches make sense for open source development. Generally, developers will work on branches dedicated to given features. This way, when they are done, they can present the branches to the community, get feedback, and make changes. Often times, they will not even have commit rights, so a committer will have to take the branch and merge it in. This is much harder if the work was a collection of commits and not a cohesive feature branch. It is also common in open source for features to get rejected, so it’s helpful if these commits don’t clutter up the git history. Open source projects also tend to have more sporadic development as opposed to lots of developers all working in the code full time.

The problem is that most work done at companies do not have the same flow. For example, at Braintree Payments, we have about six developer pairs all working on different stories, all committing at the same time, and all with commit rights to our project. We trust that if a pair wrote the code, it’s good and is going into the project. In 99% of cases, if we are working on a feature, it is going to get released.

Given this kind of working environment, here is why I do not like feature branches:

Feature branches make for nasty commit history

Feature branching as a practice encourages lots of small commits, often before tests are passing or the code is in a useable state. Since these commits are on a branch, most people think it’s ok as long as the final merge commit back into master passes all of the tests.

The problem with this practice is that the commit history of the project is now full of broken work and non-passing code. I often use git history to get an idea of why a feature was done, or what other code was changed at the same time. I will “git blame” a method name to see when it was added, and what the code looked like at that point. With feature branches, I can no longer trust that commit. It may have been done on a branch, it may have broken half of the tests, and it may have been completely scrapped before the code went into master.

Furthermore, If feature branches are deleted once they are merged into master, where do bug fixes go? On a new feature branch or on master? There will no longer be an easy way to find a list of the commits that went into a feature. If feature branches are not deleted right away, the repository will likely end up with a huge number of branches.

Feature branches generally have no builds

We use Jenkins to continually test our application. We run numerous builds on every commit, including integration builds that call out to external dependencies.

When we do work on feature branches, we no longer have these builds. Developers will run the main suite of tests before they check in, but no one is going to run the full suite of integration tests on every commit. Furthermore, setting up new builds can be annoying, so we’re unlikely to do it unless we are going to maintain a branch for a long time.

Lots of branches become cumbersome

The lack of builds is one aspect of where lots of branches become cumbersome. Another is user testing. It is common to want the product owner or someone else to take a look at the feature before it’s done to get feedback. If you have a testing environment, they might want to deploy your code there to take a look (rather than looking over your shoulder on the workstation). But if you have feature branches, then they have to deploy your branch, which means they can no longer test other features at the same time. If they are in the middle of testing another feature that requires specific data, they may not be able to deploy your branch (e.g., if it has different database migrations that would require the database to be rebuilt).

Feature branches make refactoring harder

Modern version control tools like git make branching and merging much easier, but they are far from perfect. If we do large refactorings (especially refactorings that take numerous commits), it can become really hard to merge in feature branches. For example, if we rename methods, shift code between classes, or even reorder the methods in a class, the feature branch will likely get tons of conflicts on the merge. Then, someone has to go through each conflict and figure out where the code went. The longer the feature branch lives, the worse the merge becomes.

To take a specific example, say we want to switch our tests from one factory pattern to another. With feature branching, we would create a branch, and spend a few days porting over our tests. Then, when we merge back, we are going to run into conflicts with any tests that were changed by other feature branches. Even worse, while we are porting tests, other developers are still writing tests on their feature branches in the old style.

If we decide to make the test changes on master, but still have feature branches, all of the currently running feature branches have to continually merge from master in order to get the new tests. If they forget, and start modifying tests that have not been ported yet, they are still going to get conflicts when they merge back.

In contrast, if everyone is working in master, the people doing the refactoring can pull, make the changes, and commit frequently. As long as other developers also pull frequently (since everyone is making small, frequent commits), they automatically get the changes.

My preferences

Given that I don’t like feature branches, here is what I prefer:

All developers work in the master branch. When we are ready to release code to production, we merge master into a release branch, which gets some extra regression testing. We then tag from release and deploy the tag.

We use descriptive commit messages to signify what feature we are working on. For example, we use messages like “#card – message.” If we need to see all of the commits for a given feature, we can grep the git history for the card number. Likewise, if “git blame” a line of code, you can get back to the original story card and see why the feature was developed.

Commits only get pushed if they pass all of the tests. If a developer wants to make temp commits, that’s fine as long as they get squashed into a decent commit before pushing. Spike branches are fine for playing around, but the real work should be done on master once the approach is finalized.

On my last few projects, we have used a tool called rake_commit to do much of this work for us. It will prompt for a pair, story, and commit message, squash unpushed changes, run “git pull –rebase” (if working in git), run tests, and then push if the tests pass. This tool ensures a clean, linear git history.

For long running features, I prefer Branch by Abstraction and feature switches. This way, the features can be developed in master along side other development with the full suite of builds, but not necessarily impact the end user until the work is complete. If we have to release early for some reason (feature X just has to go out today), then we can release without the new features in use.

  15 Responses to “Why I don’t like feature branches”

  1. git rebase –interactive ???

    • I don’t understand your comment. Can you elaborate?

      • Interactive debased allows you to squash feature branch commits into a single commit. It’s crucial to fixing the issues you describe. You’re basically arguing against the entire git workflow and why its a paradigm shift from standard version control.

        • rebase == debased.

        • git rebase –interactive can fix up the git history, but it does not address my other points. You still have no builds on the feature branch, refactoring causes problems, and user testing requires deployments of different branches.

          • The git workflow generally lends itself to branching–that’s the whole point. Generally, having a staged level of builds is the correct strategy with git. You have your Gold branch which is what’s in production (with CI). A staging branch that has tests run on it also with CI. Finally a master where everyone integrates their work. Feature work is on the developer boxes. Nothing moves off the master branch into staging until it is finished. In SVN/CVS land branching was a nightmare, in git land it should be a tool used often and in small increments to put a commit in the master branch. Branches aren’t necessarily removed, they are generally used to integrate code separately from the main line. You treat the persisted branches as set in stone, not something to be used as a backup or pushed to without great thought and care.

  2. While I agree feature branches have some drawbacks, many of your points that bring you to this conclusion are not right IMO.

    > Feature branches make for nasty commit history

    The exact opposite is true. With feature branches the commits are laid out in order and can be read like a story. Conversely, if all your developers are commiting to master a bit at a time it’s a real mess to figure out the progression of a story.

    Regarding broken/bad commits ending up in feature branches…show your developers commit –amend and rebase –interactive to tidy up history.

    > Lots of branches become cumbersome

    Git makes it very easy to switch branches. If a developer is in the middle of work and wants to test out a coworkers feature branch then have him stash his code and switch.

    > Feature branches make refactoring harder

    When I had a feature branch a live during a big refactor on master I ended up doing an interactive rebase against master and solved the conflicts one commit at a time. It was tedious yes. Gits file rename detection helps greatly here too. To avoid this try not to let your feature branches rot for months before integrating them back into master.


  3. [...] to be controversial. I googled it a bit, but it does not seem to be embraced as a best practice:*…*…   Add [...]

  4. [...] it is difficult to get feedback from the business during the development of a feature because feature branches do not have dedicated execution environments. If a feature needs to be reviewed during its development, this branch has to be deployed to an [...]

  5. Everyone works in master? What happens when I push bad code? Everyone has to deal with it. Reverts are ugly, too. No CI on branches? You have implemented CI. And still — merging that branch makes it trivial to unmerge an entire set of commits. Using rebase when merging feature branches is also very problematic.

    You use rebase on a shared branch? That’s craziness.

    You don’t like frequent commits? Teach your devs to use rebase -i. Frequent commits come from devs that know how to cover their asses from their own mistakes. Frequent commits save peoples time. Rebase -i is much easier than losing code.

    You have great google juice and I think very dangerous advice for newbies looking for it.

    • > You have great google juice and I think very dangerous advice for newbies looking for it.

      This whole DVCS workflow turns to orthodoxy. Follow the guru, never think by yourself !
      I’m really happy that there are people expressing their opinion and sharing their views on this topic.

  6. git merge –squash <feature_branch"
    git commit -am 'Description of your changes'

    This will clean up all those small commits into one big commit. Our git history on master is very clean.

    As for integration builds, you can have jenkins test your feature branches just like your master branch.

  7. “Feature branches generally have no builds”
    Is in itself wins this debate. Lack of functional test feedback loop until too late is an un-deniable issue.

  8. Brilliant post! I’m also an advocate of tree-like structure for version history and master being used as a main development place. Branch-by-abstraction principle perfectly fits this idea.

  9. >“Feature branches generally have no builds”
    > Is in itself wins this debate. Lack of functional test feedback loop until too late is an un-deniable issue.

    Jenkins can create feature branch builds automatically with a single plugin.

 Leave a Reply



You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">