Version Control – Choosing the Right Branching Strategy for Releases

branchingrelease-managementteam-foundation-serverversion control

Starting with a new dev team on a new project and we have to define our Branching strategy for our source repository (e.g. Microsoft Team Foundation Server 2010). We've run into a sticky discussion over whether or not to…

A. Have one Release branch from which we do production builds and then Label when something is actually released

OR

B. Have a new Release branch for each new Release into production (Ex. Version 1, 2, 3, etc…)

Option A seems pretty straight-forward but we're not sure if this will lead to problems in the long run. Option B seems like it just creates lots of one-time long-lived branches that just pile up over time.

Does anyone have any experience either way that could help us decide? Specifically, I'm looking to hear where the pain-points are for any one choice. Feel free to provide specific experience relative to TFS and/or Release-Management implications.

Best Answer

Option A. Just using mainline and tagging for release

Pros:

  • You avoid merge hell.
  • Keeping to the mainline encourages some best practices like proper release planning, not introducing a lot of WIP, using branching by abstraction to deal with out-of-band long term work, and using the open closed system and configurable features for dealing with managing works in progress that may; or may not; need to be disabled now or in the future in order to release or to avoid a full rollback.

Cons:

  • Dealing with works in progress becomes an issue and adds to potential surface attack area when it comes time to release. However, if your developers are disciplined then new features should be configurable and modular and therefore easily disabled/enabled, or there is no WIP and at each release point all work is either completed or has not yet been started (i.e. Scrum).
  • Large scale/out-of-band changes require more thinking ahead of time to implement (e.g. branching by abstraction).

Personally I prefer this approach. Code coverage and unit tests should identify code that isn't ready to go out the door and people should not be working on code that will not be released during the current iteration. Branching by abstraction or other mechanisms can be used to deal with long term changes and works in progress.

When you don’t do this you start finding yourself dealing with merge issues, stale code, features that never get released, etc.

Option B. Branch by release

Pros:

  • You can begin working on the next iteration while the current iteration finishes its round of acceptance testing.
  • Other stuff im sure.

Cons:

  • Tons of branches.
  • Still need to tag branches at release points.
  • Still need to deal with WIP and merge WIP from previous release branch into next release branch if it's not going to make it and still need to disable or yank it of release branch and re-run acceptance tests
  • Hot fixes need to be applied to more branches (release branch + hotfix + new tag + merge hotfix into vnext branch and possibly vnextnext depending on where the hotfix falls.)

I'm not a huge fan of this solution ^_^.

Generally I would recommend just trying to stick to the mainline. If your developers are having trouble with not writing WIP that can be easily yanked when it fails the cut or that is checked in early for the next release then you can start talking about tagging the code at the point where it should be code complete and branching from there if necessary to address overlooked defects and bugs that your developers unit tests failed to catch.

Ideally though I think you want that to be the exception process, not the rule.

Option C. Crazy Bonus Option

If you want to get fancy you can also consider a per-user-story/per-feature branching model. (A terrible idea in TFS or any non DVCS while at the same time incredibly trivial to implement if using a DVCS like git or mercurial).

In the past I implemented the below for a previous employers maintenance team which worked with a legacy code base that could not easily be ported over to mercurial from svn. A lot of unnecessary work was involved to meet a business requirement of a always releasable mainline rather than just coordinating releases better but . . .

  1. Features were developed by devs in their teams dev branch.
  2. When a feature is ready to be peer reviewed the devs bundle it together into a single merge from the Dev branch into the CR branch and include the feature id/user story in the title. *Enforced by pre-commit hook*
  3. After it passes CR an admin tool is used to Promote the feature into the QA branch. (I wrote a little terminal application that listed the user stories present in the various acceptance stages and allowed the operator to promote or demote it inbetween those acceptance stages)
  4. QA runs automation and manual usability tests. If the feature is good its pushed into the release branch (mainline). If the feature is rejected its demoted/reverted out of the QA branch until devs can address the issues brought up during test and add submit a patch to the CR branch.
  5. If code was reverted from the QA branch and a fix is applied the terminal tool will re-apply the necessary changes to bring the feature back onto the QA branch from the CR branch so that QA may re-review the code and either promote it or demote it again.
  6. At any point in time the release branch should be in a stable releasable state.
  7. After a release new Dev, QA, and CR are spun from mainline.