Design weaknesses of GitFlow
I started using GitFlow more or less two years after Vincent Driessen introduced his proposal in 2010. Since then, there has been several occasions when either me or one of my fellow developers have faced some issues with it.
The company I was working for back then had recently moved from Subversion to Git. We were struggling with defining the best approach for collaborative development, since not everyone was familiar Git. More often than not, either me or another experienced Git user had to help another fellow developer to understand what was going on. So the team welcomed the idea of having a consistent way of working with our repositories.
Four years has passed and every company I have worked with seems to have adopted GitFlow as the default approach. But as with any other tool or methodology, one has to carefully examine whether it fits the purpose of a project or the way a team works. I have the impression that little thought has been put into choosing GitFlow as de facto model. I’d like to share with you what I consider two of its design weaknesses.
The master branch at origin should be familiar to every Git user. Parallel to the master branch, another branch exists called develop.
When the source code in the develop branch reaches a stable point and is ready to be released, all of the changes should be merged back into master somehow and then tagged with a release number.
I always wondered if nobody has noticed that the master branch is completely useless in this model. If every release must be tagged, why do we need the separation between master and develop at all? Can’t we just live with a single main branch and go back to any release using its tag?
Not even the need for hotfix branches seems to justify this redundancy. It’s perfectly possible to create a branch from a tag, apply the patch for the existing bug in production and merge back to the main branch.
A polluted commit history
The --no-ff flag causes the merge to always create a new commit object, even if the merge could be performed with a fast-forward. This avoids losing information about the historical existence of a feature branch and groups together all commits that together added the feature.
Reverting a whole feature (i.e. a group of commits), is a true headache (…) whereas it is easily done if the --no-ff flag was used.
By avoiding fast-forward merges the commit history of the repository is polluted with merge commits. This situation is even worse when you realise that both GitHub and Bitbucket merge pull requests using that flag. But to be honest, I haven’t found a situation where those commits have been proven useful. Let me address these apparent benefits.
There are other ways of keeping historical reference of when a feature was merged. If you have an issue-tracking system, you can prefix commit messages with the related feature ID. You can also provide better commit messages, something that I definitely encourage you to do.
In any case, I think that the fact that a branch was merged at some point in time does not tell me anything about the lifetime of a feature.
Reverting a feature
The existence of merge commits are in theory useful to revert a feature. In my opinion, this is a highly unlikely scenario when you work in a team. You usually find developers working together in several features and using multiple branches. If you want to share work with your team (e.g. because you refactored some code along the way) you have to merge changes so that everyone can benefit. Make that a couple of times and there is no easy way of “reverting a whole feature” anymore.
And this is because software development is inherently an organic process. It’s very difficult to be in an scenario where a feature starts and gets merged later without being affected by changes in other branches. If that happens, it’s probably because the change is so small that you can revert it easily with another batch of changes. And that reflects better the reality of the project: we introduced a change and we explicitly reverted it for some reason.
If you really want to revert a feature, you design it in a way that you can do that with the software itself, not by messing with the commit history.
Ready to try something else?
Git is a difficult tool to master and users can do whatever they want with it. Making mistakes is easy and it takes a while to develop good practices and learn how to avoid or solve some pitfalls. So it’s easy to understand why people have embraced a complex workflow of limiting but well-defined rules.
It’s also difficult to change things when almost every developer is familiar with it. Conventions are powerful. And the fact that major repository hosting services don’t offer a way to fast-forward merges doesn’t help either.
I’m certainly not the first one that saw all the issues stated above. Scott Chacon himself wrote about it in 2011 and proposed the GitHub approach as a simpler alternative. If you are willing to escape from GitFlow, I would start there.
Originally published at elitalon.com on March 12, 2016.