It’s the stuff a techies dreams are made of, you start work on a new project and get to decide what branching strategy to apply! However your dream can quickly become a nightmare if you choose a common or well known branching strategy without really considering the management overhead associated with the approach. I have experienced this pain in the past where significant team effort has been expended just servicing the overhead of branching rather than delivering actual business value. If you often have to perform mental acrobatics to decipher the sequence of actions needed to apply a relatively simple change to production then this article is for you.
I have come to conclusion that if you get your branching right it's empowering and if you get it wrong and it can be debilitating beyond despair.
Recently I was involved in a team that was very much in the ‘dream’ category above and we needed to decide how we managed our codebase. Our team was atypical but there were a few things you should know to provide some context for our requirements before reading further.
- We were 10 or so people spread across the UK, but organised as a single team using SCRUM.
- All of our code repositories are stored in git repositories and most everything in the platform is automated.
- The product was a storage platform that had a number of moving parts — we consider all of these component parts a piece of a holistic release e.g. a release was potentially everything in the platform that has changed. Eventually we want to move towards product based pipelines for individual components but right now the platform is our unit of deployment.
- Releases can be cut at any point during the development cycle but more often than not this was every week (or two!).
- When a release candidate was cut and it was making its way along our pipeline to production, we needed to unblock development so the team wasn’t stagnant.
- We had just deployed the first release of our service into live (!) when we decided to add more rigour to our branching strategy. We had a branching strategy before this but it didn’t account for hotfixing.
- Given live hotfixes were now very much a thing, they might be required at short notice (think security patching) and might require expedited deployment to production.
Our conversations (and there were many) started where you might expect in that we considered using existing branching strategies such as GitFlow/Gitlab Flow/OneFlow but these ended inconclusively.
It seemed the ceremony involved with applying these existing strategies needlessly added complexity relative to our requirements.
So we came up with our own which we’ve dubbed MasterFlow; it in our view was the simplest strategy that met our requirements and revolves around using a single master branch. It supports:
- Parallel development of features.
- Tracking and hotfixing released versions of your software.
- Retaining your sanity by not turning git branching into a cottage industry.
With MasterFlow we use a single master branch and the abstract view of the strategy is shown below (graphviz code available here):
Features are branched of the current head of master and merged back into master once complete (removing source branch of course).
There is no rule about rebasing on feature branches to keep commits as clean and as atomic as possible, but you’ll definitely get bonus points for doing so.
When you believe you have a release candidate, tag your repositories at the current head of master with the major/minor release number e.g r2.1
For the purposes of planned releases we are only really concerned with the major/minor release numbers; the use of the patch version is reserved for hotfixing and is a trigger for automation.
Thus during the normal release tagging process your patch version should always be excluded or set to 0 e.g. r2.1 or r.3.7.0
Assuming you have more than 1 git repository then applying tags to head of master for each release candidate is likely to become painful. In our scenario we have 15+ git repositories so we decided to automate the entire process using the myrepo.
It’s pretty basic really, this tool checks out all 15+ repositories, pulls the latest version of master and adds a user-specified tag at head e.g. r2.1.0
Note: If the tag already exists and it fits with your model, your automation can move the tag it to the current head of master. Think of this in a scenario where you decided to release prematurely but want to include a feature that just entered the pipeline.
You need to consider whether the hotfix can wait until the next planned release or if it needs an expedited deployment to production?
If the hotfix isn’t urgent it could be merged to master and it would be batched up and deployed along with the next release. Treat this with the same process as with feature branches, simple.
If the hotfix needs to be deployed independently of the next release then create a hotfix branch of the release version deployed to production e.g. r2.4.3
git checkout -b hotfix/bugfix-for-ui-ticket353 r2.4.3
Make your changes and commit to the single repository as normal. Once the hotfix is complete push your hotfix branch to origin, tag it with the relevant patch number (in the above example r2.4.4) and submit a merge request to master.
In our scenario hotfix tags are only created on individual repositories as this better suits our release process.
Our release process is codified through our Continuous Integration server as below.
Continuous Integration (CI)
Our CI server looks for push events on our git repositories and takes action when:
- There is a push to master -> We build normally from the head of master and promote into our development pipeline.
- A patch release tag is pushed to origin that isn’t 0 -> We build from this patch release tag and the admins can then choose to expedite patches to particular environments.
With this approach we need not revert our pipeline back to a prior state to apply a hotfix which enables us to patch production at short notice.