Go with the (git)flow, or die debating
We all version our codebase using one of the many version control systems (VCS) on the market. Since I started working for wellD, we have changed three of them.
In the beginning, we were using CVS, but given its inability to be atomic–in an all-or-nothing sense–it really wasn’t fun when something got stuck halfway through. Let’s not even mention its performances.
Our next step was then to switch to SVN; we chose not to go for git because it was too complex! As a result, merging two branches would always give us a headache.
Finally, it was git-time, which positively disrupted the way our developers’ team worked.
Unlike the first two VCSs, however, git forces users to define shared methodologies to use the system.
In this respect, one of the first challenges we faced was to agree on a shared branching strategy. To make our decision, we considered the following criteria: easy-to-integrate Continuous Integration (CI), frequent releases, and step-by-step code review.
Branching strategy
Keeping in mind the criteria we just mentioned, here is an overview of thebranching strategies we, directly and indirectly, experimented with.
Anarchy strategy
AKA “no-strategy.” This model empowers each developer to create branches without following any convention whatsoever with respect to a branch’s duration, name, and content.
Ideally, this enables a quick kickoff and an independent development. But alas, a much more tangible result is that it doesn’t take long for the situation to become impossible to manage.
By adopting this strategy, the team will have to spend time trying to identify the active development branch, on which branch they should develop code, on which branch(es) a specific feature is located, or which branch should be pushed to production.
Git Flow
For over a year, we used git-flow, which provided us with an organized, methodical, and structured alternative, and a CLI that allowed us to avoid naming convention issues.
The idea behind git-flow is pretty simple:
- The master branch includes all the tags for all released versions;
- The develop branch is used for ongoing development processes;
- Feature branches are dedicated to the new features that are being developed;
- Release branches are for software versions that are candidates for release;
- Hotfix branches are destined to bug fixes flagged by the production team.
The biggest problem with GitFlow is its complexity. The whole development process doesn’t occur on the master
branch–i.e. the default branch for many tools–rather on develop
, while master
is only used to store releases.
This kind of approach actually prevents developers from code reviewingpending merge requests (MR) on the master branch–which are required since this branch is protected–as these will include content developed in weeks, if not months. It is, however, possible to send MRs from feature
branches to the develop
branch. Yet this only depends on the developer’s commitment, because MRs are not required for the develop branch since it is not protected, or at least it is not so by default.
One of the greatest issues we had was to correct bugs in versions preceding the latest release. Let’s say we released version 2.X
of a software and the client flags us an issue in version 1.Z
. The wisest choice would be to go on the master
branch and create a support
branch for tag 1.Z
to fix the bug. But what should we do after the bug fix? From which branch should our fix be released: the support branch, the master branch, or something else? Into what should we merge our fix? Should it be merged in the master branch or perhaps in the develop branch? Or, rather, should we just leave the support
branch we created forever unmerged
?
The release process in GitFlow can get really tough. Here are the steps involved:
- Use the command
git flow release start <VERSION_NUMBER>
to create the release branch fromdevelop
; - Update your codebase (changelog, project version, readme, etc.;)
- Wait for the CI to green-light the creation of an MR on
master
; - Merge the release branch into
master
with the commandgit flow release finish <VERSION_NUMBER>
. Once this step is done, therelease
branch will be merged intomaster
anddevelop
, and atag
will be created for the release commit. After that, we will be redirected todevelop
; - push
master
,develop
, and thetag
(note:git push --tags
)
P.S.: First, update the project version on develop
for the next iteration, then push.
Sometimes this process can be overkill, and experience has taught me that making a mistake is quite easy.
GitHub Flow
GitHub Flow relies on a clean and simple structure, with only 2 kinds of branches: the master branch and the feature branches. A developer creates a branch from the repository on which the new feature is developed. Once done, an MR (with code review) is sent to master
. To update an MR, you simply need to push content on your branch, and when the MR is ready this will be merged in master
. After that, the feature
branch is removed.
This branching strategy, however, is based on 2 strong assumptions:
- Developed contents can be pushed to production at any time, which is not always true. For many of our clients, we have a set time frame on a certain date to push a new software version to production.
- Clients always install the last version of our app, so there is no need to support and fix bugs for older releases. 🤔
Gitlab Flow 🚀
Starting from GitHub Flow, a new strategy came up, one that provides a branch for each environment. Let’s say we have a staging environment, a Quality Assurance (QA) environment, and a Prod environment. Each of them is updated automatically using a CI script pointing to the relevant branch.
Like this, we can benefit from small and exact MRs to master
, which can be subject to code review. When we’re ready, we can push changes to the QA environment (sending MRs from the master
to the pre-production
branch) and then to the Prod environment (sending MRs from the pre-production
to the production
branch).
Thanks to this workflow, we can rest assured that all commits are tested in all environments.
Sometimes, there is the need to keep developing multiple versions of a software, in which case Release Branches come in handy.
In this case, each branch corresponds to one version of our software (the image above depicts a minor version.) In order to avoid making hotfixes on multiple branches, the creation of the release branch (the stable branch in the picture) is delayed for as long as possible.
When an anomaly occurs in our software, the fix is made on a feature
branch, which is then merged into the master
branch. After that, we cherry-pick the commit on the release branch. This technique, known as Upstream First, is used so that the developer doesn’t forget to merge a hotfix into master, preventing the problem from occurring in future releases.
Conclusion 🤓
One of the principles of Lean that I appreciate the most is continuous improvement, a concept that is perfectly depicted by this anecdote:
There was once the owner of a big American company, who decided to adopt Leanto improve his production line. A couple of months after introducing it, he invited the owner of a Japanese factory–where Lean had been implemented since day one–to visit his factory.
On the day of the meeting, the American gave his Japanese guest a tour of his factory, showing him the improvements that had been made thanks to Lean. The Japanese watched but didn’t make any comments.
At the end of the tour, the host asked his guest: “What do you think of the way we reorganized our factory by following your guidelines? Have we managed to embrace Lean fully?”
The Japanese guest answered: “I do not know that. I was not here yesterday!”
This is just to say that, starting from CVS, in time we have embraced git and Gitlab Flow, which currently seems to suit our development cycle well enough. Nonetheless, we are always open to experiment with and potentially adopt any tool or strategy that improves our developers’ team life.
References 🔗
- https://docs.gitlab.com/ee/workflow/gitlab_flow.html
- https://datasift.github.io/gitflow/IntroducingGitFlow.html
- https://danielkummer.github.io/git-flow-cheatsheet/
- GitLab Flow example: https://github.com/spring-projects/spring-boot
- Upstream first: https://www.chromium.org/chromium-os/chromiumos-design-docs/upstream-first
—
Matteo Codogno, Technical Leader @WellD