Many years ago, Trelo Android used a fairly simple Git branching strategy – or so we thought at the time.
We were fully evolving main Through withdrawal requests. main Designed to be releaseable at any time, although occasionally we would create a Release / xyz Branch, if we felt that any system of features needed additional testing time.
After being acquired by Atlassian, we encountered some issues in our process.
First, Atlasian is a listed company, so it must meet the requirements Sarbanes-Oxley Law (SOX). For us, this means we had to make the distribution industries SOX compliant, while adhering to additional rules and constraints. The creation of a new SOX compliant branch was highly overhead and its deletion was even more abominable. so, Release branches were now a huge PITA for creation and deletion.
also, Our old strategy had a tendency to create nightmare situations when fixing release buggies. Suppose we were prepared Release / 1.3.0, But it turns out Release / 1.2.0 There was a severe bug. We’ll fix it Hotfix / 1.2.1, Oh, but how do we make sure the bug fix makes it main and Release / 1.3.0, Damn, let’s just pick everything. Let’s hope the hot fix will not require further hot fixes or that things will come really ugly.
(I tried to create a chart that illustrates the above but it was so clustering that I spared you all from it.)
In other words, the “simple” solution, when faced with common circumstances unfortunately, became quite complex and lacked a well-defined strategy. This problem has been added by a growing team; The amount of communication required about where to merge code has increased exponentially with the number of developers.
With all this in mind, we set out to find a better branching strategy.
I do not believe in one solution that suits everyone basically everything and this is especially true for code management strategies. Your context, goals and constraints will accompany you to the right tool for the job.
for example, GitHub flow he is great! The only common branch is main, You merge attributes using a pull request, and if – it is deployed immediately. It’s amazingly simple! But … GitHub Flow relies on continuous deployment and Android apps can not do that.
Thus, for our first step, we have compiled a list of factors to consider.
Here are the problems we tried to fix:
- Creating an SOX compliant branch is expensive.
- We need a well-defined strategy for all situations.
- Our strategy should fit more than a handful of developers.
We then outlined the team’s cultural preferences:
- We check the code before merging.
- We avoid long-term feature branches (instead of using feature flags).
- We want to be able to test / repair a version at his branch before shipping.
Finally, some facts about our environment:
- We release three components regularly: internal, beta and manufacturing.
- We release only one version of the software at a time for production; We should never maintain older editions.
- Android does not support continuous deployment.
We eventually established our branching strategy Three flows. The basic idea of three-flow is that you only have three stable and long-term branches – one for development, release candidates and releases.
Our three branches are main, candidate, And release. main Designed for continuous development. When you want to start a new beta, you merge main into candidate. Once this structure is stable enough, you merge candidate into release.
If a bug is found in candidate, So you’re fixing it candidate And merge it back down main. Similarly, if a severe bug is found in release And you need to make a hot fix, you fix it release, Then merge this patch until candidate and then main.
(Note how there are never any direct mergers from main To release or vice versa; The advantage of always passing candidate It means simplicity and consistency that IMO is worth the extra work.)
Whenever you want to merge code to any of the three branches (main, candidate, release), You create your own attribute branch (e.g. dlew / feature) And opened an application for attraction in the target industry.
We use feature flags to prevent pre-distribution code from being sent to users. It is still possible to blend a semi-baked feature candidate and release, Only in disabled condition.
Three flows fixed our problems:
- Stable branches mean never create or delete a SOX compliant branch.
- Any stable industry can be released to our internal, beta or production tracks.
- We have a feature book for developing features, beta troubleshooting and hotfix deployment.
- This is a simple strategy that makes it easy to know where to merge code (main For development, candidate For beta, release For hot fixes).
It is also in line with our existing preferences:
- We can still use pull requests to test the code.
- It explicitly embraces feature flags and shuns long-term feature branches.
- This gives us time to bake a release candidate before being pushed into production.
As a bonus, three flows also does not add additional material that we do not require. By eliminating support for things like continuous deployment and previous generation editions, it maintains complexity.
The core of three flows is about three stable branches, but how you manage these three branches can be different from team to team. Hence, it is worth noting that we stagnated The original article in two ways:
- Short-term feature branches – We are big fans of code reviews, so opening branch withdrawal requests is a major part of our flow. Remote branches are a prerequisite for public relations people, but the original article addresses any kind of anthem of distant branches. They offer to simply re-establish all commitments to the master – but then how do you do code reviews?
Now, we are No Use long-term trait industries – We agree that these are nightmares that are waiting to happen and that trait flags are preferable.
- Merger between branches The article uses power urgently for release branch. Although it makes your Git trees look more beautiful, we do not like to rewrite Git history (and SOX compatibility also prevents us from rewriting history anyway).
There is a certain elegance to always merge between the three branches; It’s always the same process for moving from one to the other, and it makes the code history completely traceable. Plus, using mergers means you can simply use pull requests to merge branches – without having to memorize more git commands.
We have ruled out other popular Git branching strategies for a variety of reasons:
- Trunk-based development (Also known as Stable central line or cactus) – This strategy was basically what we did before.
- GitFlow – It is much more complex than other branching strategies and requires the creation of many release industries.
- OneFlow – Simpler than GitFlow, but still requires the creation of many release branches.
- GitHub flow – It requires continuous delivery to work.
I do not suggest everyone go out today and use three streams. This is great for us given our constraints, but it may not work for everyone. In particular, there are at least two cases where you definitely need to No Use it:
- If you can perform continuous deployment, use GitHub Flow. It’s simpler!
- If you need to support multiple editions at once (previous generation versions + current version), three streams just will not work.
If not for these cases, I would consider giving three streams a chance. We use it for more than two years without any problems.