In my previous post, I talked about the idea of having a simple branching strategy and why I prefer one where everyone works off the same branch.
In this post I will show you how to create what I believe is the most simple and effective branching strategy.
Take a look at this diagram of a sample project’s code lines:
Walking through it
The idea here is very simple. Let's walk through a development cycle together:
- Development starts. Everyone works off of trunk. Code is frequently checked into trunk, many developers checking in code 3-4 times a day, as they complete small quality sections of development.
- The continuous build server is continuously building and checking the quality of the code every single time code is checked in. Any integration problems are immediately fixed.
- Enough features are done to create a release. Trunk is tagged for release and a release 1 branch is created representing the currently release production code.
- Developers continue to work on trunk not being interrupted by the release.
- A customer finds a high priority issue in Release 1.
- A Rel 1 Hot Fix branch is created, branched off of Release 1 to fix the high priority issue. It turns out that a good fix will take some time. Team decides the best course of action is to apply a temporary fix for now.
- Rel 1 Hot Fix is done and merged back into Release 1 branch. Release 1 is re-deployed to production.
- In the meantime another emergency problem shows up that must be fixed before the next release. Rel 1 Hot Fix 2 branch is created.
- The bug fix for Rel 1 Hot Fix 2 is a good fix which we want in all future releases. Rel 1 Hot Fix 2 branch is merged back to Release 1 branch, and merged back to trunk. Release 1 is redeployed.
- In the meantime work has been going on on trunk, team is ready for Release 2.
- Release 2 branch is created…
Breaking it down
I gave a pretty detailed walk-through for a very simple set of actual steps. But, I hope you can see how simple this process really is.
The basic idea here is that we are trying to decouple releases from development as much as possible. The team is always going to keep chugging along, building new features and enhancing the code base. When we decide we have enough features for a release, we simply branch off of trunk and create the release branch.
We can even do some testing on the release branch before we go to production if we need to without impacting future development.
The release branch code-lines never come back to trunk. They don’t need to, they only exist so that we can have the exact production code and make modifications to it as hot-fixes if we need to.
We branch hot-fixes off of the release branch so that we can work on them independently, because not all hot-fixes go back to the main code-line. We can make a hot-fix just for the current release, or we can merge it back to trunk to make it a permanent fix.
That is all there is to it. This kind of branching strategy almost completely eliminates merges. The only merge you ever do is small merges for hot-fixes.
Your branching strategy does not have to be complicated. A simple strategy like this can fit almost any software development shop.
Frequently disputed points
Almost immediately when I introduce this simple system someone says:
What about half-completed features? I don’t want to release half-completed features. Using this strategy with everyone working off trunk, you will always have half-completed features.
So what? How many times does a half-completed feature cause a potential problem in the system? If the code is quality and incrementally developed, it should not impact the rest of the system. If you are adding a new feature, usually the last thing you do is actually hook-up the UI to it. It won’t hurt anything to have its back-end code released without any way to get to it.
Continuous integration, (especially running automated functional tests), trains you to always keep the system releasable with every commit of new code. It really isn’t hard to do this, you just have to think about it a little bit.
If worse comes to worst and you have a half-finished feature that makes the code releasable, you can always pull out that code on the release branch. (Although I would highly recommend that you try and find a way to build the feature incrementally instead.)
If you know you're going to do something that will disrupt everything, like redesigning the UI, or drastically changing the architecture, then go ahead and create a separate branch for that work. That should be a rare event though.
I need to be able to develop the features in isolation. If everyone is working off of trunk, I can’t tell if what I did broke something or if it is someone else’s code. I am impacted by someone else breaking things.
Good, that is some pain you should feel. It hurts a lot less when you're continuously integrating vs. working on something for a week, merging your feature and finding that everything is broken.
It is like eating a meal. All the food is going to end up in the same place anyway. Don’t worry about mixing your mashed potatoes with your applesauce.
If something someone else is doing is going to break your stuff, better to fail fast, then to fail later. Let’s integrate as soon as possible and fix the issue rather than waiting until we both think we are done.
Besides that, it is good to learn to always check in clean code. When you break other people and they shoot you with Nerf guns and make you wear a chicken head, you are taught to test your code locally before you check it in.
How to be successful
How can you be successful at this simple strategy?
- Make sure you have a continuous integration server up and running and doing everything it should be doing.
- When you work on code, find ways to break it up into small incremental steps of development which never break the system. Hook up the UI last.
- Always think that every time you check in code, it should be code you are comfortable to release.
- Check in code at least once a day, preferably as soon as you make any incremental progress.
- Test, test, test. Test locally, unit test, test driven development, automated functional tests. Have ways to be confident the system never moves backward in functionality.
- So important I’ll say it twice. Automated functional tests. If you don’t know how to do this, read this.
- Release frequently instead of hot-fixing. If you never hot-fix you will never have to merge. If you never have to merge, you will live a longer, less-stressed life.
- Don’t go back and clean up code later. Write it right the first time. Check it in right the first time.
Hopefully that helps you to simplify your branching process. Feel free to email me or post here if you have any questions, or are skeptical that this could work.