When devising the first workshop for the new Redgate training program, Steve Jones (b|t) and I got into a bit of a debate about branching. Upon reflection, it’s obvious why. People often use the term ‘release branch’ – but they can mean different things. It is important to be clear.
I did some Googling and I couldn’t find consistent terminology to differentiate between the two common approaches. In fact, I’ve seen both strategies documented using the same ‘release branch’ name – which can be confusing.
In this post I’m going to talk about two different types of release branch. It’s possible that there are already names for the following two practices and that I simply haven’t found them yet – but since I can’t find any consistent terminology I’ve made some up.
These branching strategies are specifically intended to solve specific and different problems associated with releasing code. If you think you need to use a release branch you should make it very clear which of the following variants of release branch you are using.
Problem 1: Maintaining multiple production versions
Some of your customers are still on version X, but your newer customers are on version Y. Your primary objective is to develop version Y, but you still need to be able to support version X and provide hot-fixes when required.
We have to solve this problem at Redgate with almost all our products. Most obviously, we are currently developing against SQL Compare v11 but we also have customers using SQL Compare v10 and many prior versions. Normally we would only fix issues (or add new features) on v11, hence, only customers who paid the maintenance fees would get them.
However, should a serious security bug come along we may need to ship a patch fix for version 10 and perhaps earlier versions too. Thankfully this happens very rarely (if ever), but it is only prudent to plan for it just I case.
For some of the people I work with, however, shipping patch releases for old versions is a reasonably common requirement.
Solution 1: Dead-end release branches
When shipping a new version create a new branch.
One branch is a dead end. Hopefully this is the end of the line for this particular branch, however it is still possible to come back in the future and make a patch.
The other branch is your new version, let’s call it main. This is the branch on which you will continue to develop your next version.
While you may merge your hot fix on the release branch into to main, you would not expect to merge regular dev work in main back into the release branch. You would never expect to fully merge these two branches back together.
With dead end release branches you would expect the code bases to become divergent from each other over time, which can make it difficult to return to an old branch and work on a patch release since the developer will need to be able to work against two different versions of the same codebase. That’s hard, especially if you have a complicated legacy codebase and the two branches are very different.
Also, the more versions you support the more complicated this becomes. Trying to support more than two or three just sounds crazy to me.
Problem 2: It’s time to release – but not all the code is ready
This is a common problem. Perhaps you have several teams working on the same codebase? Perhaps you have a regular release cadence? In either case, you want to release some of the code, but not all of it.
We’ve had to solve this problem at Redgate every time we’ve committed to ship an update to some product every week on “Release Wednesday”. I’ve personally been part of two such projects: Deployment Manager and DLM Dashboard (aka SQL Lighthouse).
In this scenario, particularly when working with databases, what people sometimes do is take a subset of the stuff from source control and release it. This may be to some test environment or production but for the purposes of this post that isn’t important. The point is that they just did their release wrong.
They just released a particular subset of their code, without recording in source control what was included and what wasn’t. This particular collection of scripts has not been tested as a single unit. Bad stuff might happen. You might see unexpected behaviour. Since you don’t have a record of the specific code you deployed in source control it can be hard to reproduce and debug.
This problem can be solved by using, what I call, a parallel release branch.
Solution 2: Parallel release branches
Maintain two separate persistent branches in source control, one called trunk, the other called release (or something similar).
Normal development work happens on trunk. When it is time to release the code that is ready to go is merged onto the release branch. Code that is not ready is left on master until it is ready.
You would expect to see the master branch have regular commits, multiple times per day per developer. All new dev work happens here. In contrast, you would expect to only see updates to the release branch only when putting together a new release candidate. You should not see any new dev work happening on the release branch.
In this model, it is important that the release branch is always kept “releaseable”. This way, if you need to ship a hot-fix, you have a version in source control that is ready to go. You can branch from here, add your fix, merge it back in and ship your update and you can worry about merging the fix with trunk when your users have stopped making angry phone calls to your support team.
You should be mindful that code on the release branch still needs to be tested, since when it was on trunk it may not have been tested in this particular state. If a mistake was made during the merge or if the code is dependent on code that lives on master but was not merged to release you could have problems.
When using parallel release branches you should try to keep master and release as close to in sync as possible. The more divergent the code gets between the two branches the harder it will become to merge and the more likely it is that errors will occur. Frankly, if you find that the same piece of work can’t be released for several sprints in a row it may be a sign that the particular piece of work is either too large or that there is some other problem that needs to be addressed.
You should be mindful that this also goes against some of the principles of continuous integration, since the code is only truly integrated in the state you plan to release when you merge with the release branch, which won’t happen every commit.
If you can get away without using a parallel release branch, you shouldn’t use one. Doing all the work on master is likely to be more effective for small teams and is more aligned with the principles of continuous integration. However, parallel release branches are more scalable as more developers start working on the same codebase.
What we use at Redgate
At Redgate we use both strategies fairly regularly. Hence, it is important for us to be very clear about the purpose of any release branch.
In summary, I believe that the term ‘release branch’ is too general. People use branches in different ways to solve different problems and I’ve noticed two very different approaches to release branches to solve very different problems. You should use the correct approach for the problem you are trying to solve.
However, if possible try to keep it simple and solve your problem without branching at all.
If you would like to read more about other issues around Database Lifecycle Management (DLM) my colleagues at Redgate are putting together a patterns and practices library on SimpleTalk: DLM Patterns and Practices