One Branch to Rule them all! - The story of Git, part three

When I was young, some of the things that amazed me the most was looking at the sky through tree branches. Following the sun, seeing the leaves mixing with the blue sky... That scene always left me speechless for a while, even today, when going hiking, I end up staring up at those magnificent treetops with all their branches spread like webs...

First thought when I hear something about branches is the above one. It is a really nice, one I would say. I just made sure to connect those thoughts when I was first learning about Git and its branches. However, the first version control system that I used was SVN, and the thoughts on branching there were a bit frustrating. Luckily, I soon switched projects and started learning Git, so the branching nightmare was over. Or did it just started?

Jokes aside, this story is going to be about branching strategies in Git, actually, one in particular - Trunk-Based Development. I'll also mention other strategies or flows as you may know them, but I will not go deep into each and every one.

So what is actually a branching strategy? The answer to that is rather simple - it is a way to organize branches within your version control system. Simple as that. Some of the common, newer, "chic" branching strategies include - Git flow, GitHub flow, GitLab flow... As you can see, new way of calling branching strategies is flow. Okay, no problem, but what about Trunk-Based Development? Well, first of all, it's not a new thing. It was here for more than 30 years, and the reason it is not so popular, and "chic", in my opinion is, well - it works! And here's how...

First off - what is Trunk-Based Development? It is a source-control branching model, where developers collaborate on code in a single branch called trunk, resist any pressure to create other long-lived development branches by employing documented techniques. They therefore avoid merge hell, do not break the build, and live happily ever after.[1]

There are two ways of using Trunk-Based Development strategy:

  1. If you are a smaller team - each committer (preferably pair programming duo) should stream small commits straight into the trunk with a check step that runs build before integrating with trunk.
  2. If you are a bigger team - each committer (one person) creates a short lived topic or feature branch (alive for maximum of couple of days) and going through Pull-Request style of code-review & build automation before merging changes into the trunk.

With this strategy you satisfy the core requirement of Continuous Integration - all team members should commit to trunk at least once every 24 hours. And this setup also ensures that Continuous Delivery becomes a reality with having a codebase releasable on demand.

When does the team stop being small and becomes a bigger one? Depends on the number of people and number of commits. It is a subject to debate. However, the one thing that needs to happen is pre-integration build, before committing/pushing for others to see. The pre-integration build can have stages like - compile, unit tests, integration tests. Ideally, those steps should be done on developer's workstation.

There are several things to have in mind when considering Trunk-Based Development:

  • Feature branches need to be short-lived, small, and used only for code-review and (CI). There shouldn't be any artifacts created or published by them. The whole artifact creation and publication needs to happen after integrating to trunk. In case of smaller teams, team members can commit directly to trunk.
  • Depending on when you choose to release, you may opt for creating release branches from trunk, just before the release, and they should be deleted after some time. This creation of release branch from trunk shouldn't be a team activity. Different strategy is to release from trunk and opt for fix-forward in case of bug fixes.
  • If there is some change that would take longer to complete use branch by abstraction technique, and feature flags in order to allow releases to be independent from one another.
  • In case of smaller teams working directly on trunk, it is of importance to have a hook on a build server which will ensure that their commits do not break the trunk. And in case you are using short-lived feature branches, there should also be a hook there to ensure that the merge back to trunk will not break it.
  • Development teams can increase and decrease in size without having any impact on quality of the code. Trunk-based development is one of a set of capabilities that drive higher software delivery and organizational performance. These capabilities were discovered by the DORA State of DevOps research program, an independent, academically rigorous investigation into the practices and capabilities that drive high performance.[2] [3]

What is its relation to other strategies, or flows? Some may say that Trunk-Based Development strategy is similar to GitHub flow - they have the same idea, however, feature branches in GitHub flow are not as short lived as in Trunk-Based Development, pull requests are actually tested in an environment before merging them to main branch[4], and the GitHub flow it is more suitable for open-source projects, where contributors are not working on them full time.[5]

And when it comes to Git flow, well, the difference is a bit obvious - the Trunk-Based Development is the simpler one, which is the way we want to go.

To summarize (this was a short one), I hope the things I mentioned in this blog post will shift your mind towards Trunk-Based Development strategy. If not, well, that is not a problem - you should use the strategy which suits you the most.

Footnotes


  1. https://trunkbaseddevelopment.com/#one-line-summary ↩︎

  2. https://cloud.google.com/architecture/devops/devops-tech-trunk-based-development (page 27) ↩︎

  3. https://services.google.com/fh/files/misc/state-of-devops-2021.pdf ↩︎

  4. https://docs.microsoft.com/en-us/devops/develop/how-microsoft-develops-devops ↩︎

  5. https://jan.schnasse.org/blog/2021/04/trunk-based-development/ ↩︎