"GitFlow is a branching dumpster fire."
-- Developers Everywhere
"Let's use GitFlow"
-- Also Developers
While its intentions were good, GitFlow presents several hurdles for software delivery teams in their journey to find a branching strategy that supports a modern software development lifecycle.
Yes, GitFlow sucks. But why?
Is it as bad as it could be? Probably not. But GitFlow does you no favors in delivering new features quickly or helping maintain your existing code base. But I don't think GitFlow is the problem. It's only a symptom and we should look at the reasons why it exists to understand why it or branching strategies like it have become so prolific.
Where did this whole branching thing come from?
Let's outline a few basics of why branching exists and see if we can make an argument for GitFlow, or any other branching strategy. The technical challenges of using GitFlow have been discussed ad nauseam so I'd like to take a different approach here. I want to focus on the reason branching strategies exist in the first place: enabling software delivery.
Branches help facilitate innovation, testing, parallel development, and collaboration. We see some form of branching in almost every version control system. Git, however, differentiates itself by how lightweight its branches are. It is almost magic how fast branches can be created and how quickly we can move between them during development. Git branches are awesome and creating them is nearly effortless. I know what you're thinking... Maybe GitFlow has an outside chance here? Maybe not. Let's take a minute and think about what GitFlow promotes.
GitFlow champions long-running branches that ignore the benefits and inherent features of Git branching. If you're creating several long-running branches the differences between branches can become complex and difficult to keep in sync. Maintaining the differences, rebasing often and simply knowing that there are divergent paths creates cognitive load as well as merging conflicts.
"But we only have a few branches, we're fine!"
I don't believe it. The more I see teams use GitFlow or similar hybrid branching strategies, the more branches seem to hang around, creating a constant weight of at least potential problems. The more and more branches exist, the more branches become technical debt. On a balance sheet, we treat inventory as an asset. If we look at the inventory of features and even defects that may or may not exist in our long running branches, can we honestly look at them as assets? I imagine we should be looking at each branch as a liability at least in the short term.
The best use of Git branching is to reduce drift and dissonance, not maintain it.
So if branching really is so easy and amazing in Git, why are we so hung up about it? Why can't we just let it go and let teams decide what is best for their team? That's where we get into the real problem with GitFlow. It isn't a solution to a technical problem.
Complex branching strategies are a workaround to enable or protect bad behaviors of project teams that exist often as a result of organizational hierarchy, placing more importance on maintaining inertia of existing process than improving delivery capabilities. (Ok, that was a mouth full...) The development team may have given up or has made compromises to reduce the conflict inherent in pushing back against organizational inertia. It's easier to create another branch (remember how easy and wonderful it is) than it is to get the work we do day to day released to production.
If we were working via small-batch, feature-flagged, iterative delivery, the need to maintain branches is drastically reduced, if not eliminated completely. There are some valid use cases around maintaining forks for open-source projects but the specific use cases exist to prove the rule. We are creating more work for ourselves because we don't want to push back on institutional challenges. Reducing dependencies, breaking down monoliths, modernizing your applications and their delivery processes are consistent goals in improving software delivery. Creating a branching strategy that compensates for organizational complexity might be easier, but it isn't the right thing to do.
I've waited until now to mention trunk based development.
I won't get into the technical details around trunk-based development but I do believe staying as close to trunk as possible is fundamentally "right". (And yes, your team can ACTUALLY do it, too.) I want to call out that trunk, or your "main branch" as it might be called, is not a branch. You wouldn't call the trunk of a tree a branch. We can do so with Git because it is technically accurate. Functionally, we need to think of trunk as our stable backbone of core product features. Can features be released from elsewhere? Certainly. But what other features in Git can be used to allow us flexibility without compromising our core delivery fundamentals? Things like Git tags facilitate versioning and commit history allows us to pick any point in time and rebuild from source. We have plenty of tools and processes at our disposal to ensure GitFlow or other branching strategies don't dictate our delivery approach.
That's the point I wanted to close with here. GitFlow sucks but it's not GitFlow's fault. We can choose to use Git however we want, but we should avoid the temptation to use it to hide other challenges. Don't resort to blaming Git for a bad merge or, even worse, a production issue. The problem is upstream in how we are choosing to work. Branching in Git is easy and awesome but let's keep it that way.