All articles
169 articles · updated weekly See our Tools
All articles
Comparisons

Branching strategies: Git Flow, trunk-based and GitHub Flow

Branching strategies compared: Git Flow, trunk-based and GitHub Flow, plus the common mistakes to avoid when picking a workflow for your team.

Branching strategies: Git Flow, trunk-based and GitHub Flow
COVER · Comparisons

The team that drowned in branches

I once joined a project with 47 open branches at the same time. develop, release/2.3, release/2.4, hotfix/login, dozens of feature branches stale for weeks, and nobody knew for sure what was actually in production. Every merge was a round of Russian roulette: you pulled develop, said a prayer, and lost half an afternoon untangling a conflict from code someone touched two weeks ago. The problem was never Git. It was the wrong branching strategy for that team's context.

Here I compare the three workflows that dominate the discussion — Git Flow, GitHub Flow and trunk-based — and what each one demands from your team. If your current headache is the result of this (huge merges that turn into a conflict minefield), the way out starts with resolving merge conflicts without panic first, and then adopting a flow that doesn't recreate them every week.

Git Flow: heavy, and almost never what you want

Git Flow is the strategy most people learn first and abandon later. The model has main (production), develop (integration), feature branches off develop, release branches to stabilize a version, and hotfix branches off main. It's designed for versioned software with discrete releases: a desktop app, a distributed library, firmware, anything where "version 2.4" is a real entity living in parallel with 2.3 still under support.

The cost is high. You keep two long-lived branches (main and develop) that diverge constantly, and every release becomes a merge ceremony. On a web product with continuous deploy, that's pure overhead: you don't have "version 2.4", you have whatever is in production right now. The develop branch becomes a limbo where code piles up for days before reaching users, and the longer a branch lives away from main, the worse the conflict when it finally comes back.

My blunt opinion: if you're building SaaS or a web app and you use Git Flow, you're probably paying a complexity tax you don't need to pay.

GitHub Flow: one main and short-lived branches

GitHub Flow is the opposite of bureaucracy. There's a single long-lived branch, main, that's always deployable. For any work you cut a short branch off main, make your commits, open a Pull Request, go through review and CI, and merge back. The branch is deleted right after. That's it.

git switch -c fix/timezone-in-report
# commits...
git push -u origin fix/timezone-in-report
# open PR, review + green CI, merge, delete branch

The secret is branch size. Short-lived means hours to a few days — not weeks. When a branch lives briefly, it diverges little from main, and small divergence means small conflicts. The PR also becomes the natural unit of review and the trigger for your pipeline: tests run on the PR, and merging to main fires the deploy. For most web teams this is the sweet spot — low ceremony, frequent deploy, readable history.

The classic trap is letting a "big feature" branch live for three weeks. Now you've reinvented the worst of Git Flow without its benefit. If the feature is big, break it into smaller PRs that land on main behind a feature flag.

Trunk-based: commit straight to main, with discipline

Trunk-based development pushes the idea to its limit: everyone integrates into main (the "trunk") several times a day, with branches that live hours at most — or no branch at all. Incomplete code ships to production turned off behind a feature flag and gets switched on when it's ready.

if feature_flags.enabled("new_checkout", user):
    return new_checkout(request)
return legacy_checkout(request)

This eliminates the entire category of "merge hell" by construction: there's no long-lived branch to diverge. But it has a non-negotiable prerequisite: CI maturity. You need a fast, reliable test suite running on every commit, because main is always one push away from production. Without that, trunk-based is an elegant way to break production three times a day.

Feature flags charge their own price too — they're conditional code that needs cleaning up later, or you accumulate dead-flag debt. But for teams with serious CI and a testing culture, trunk-based gives the fastest loop between writing code and it reaching the user.

How to actually choose

Forget which flow is "modern". Choose by the nature of what you ship and the maturity of the team:

  • You version discrete releases (lib, desktop, firmware, supporting multiple versions in parallel): Git Flow still makes sense. The overhead exists because the problem is genuinely complex.
  • You build web/SaaS with continuous deploy and want simplicity: GitHub Flow. Short branch, PR, merge. Works for 90% of teams.
  • You have fast, reliable CI, a testing culture, and want the shortest possible loop: trunk-based with feature flags.

The axis that matters more than the flow's name is branch lifetime. The shorter the branches, the fewer conflicts, the more frequent the deploys, the easier the reviews. Every good branching strategy is, at heart, an excuse to keep branches short. The bad ones are those that encourage code to sit idle away from main.

Frequently asked questions

Is Git Flow dead?

No, but its scope has shrunk. For versioned software with multiple versions in simultaneous support, the model still solves a real problem. For a web app with continuous deploy, it became overhead — which is why most teams migrated to GitHub Flow or trunk-based.

Isn't trunk-based too risky?

The risk comes from the lack of CI, not from the strategy. With a fast, reliable test suite and feature flags, committing to main several times a day is safer than merging a giant three-week branch. The real danger is adopting trunk-based without the testing maturity it demands.

What's the practical difference between GitHub Flow and trunk-based?

GitHub Flow still uses short branches with a PR before reaching main. Trunk-based reduces that to a minimum: branches of hours or direct commits, with continuous integration into main and incomplete code hidden behind flags. It's a matter of degree — trunk-based is GitHub Flow taken to the extreme of continuous integration.

Can I mix strategies?

You can, and many teams do without noticing. A team might run GitHub Flow day to day and open a release branch only when it needs to stabilize something critical. The problem is mixing without criteria and ending up with develop, release and PRs coexisting with no clear rule — that's when you get the worst of both worlds.

What to take to your team

The right question isn't "which is the best branching strategy", it's "which flow keeps our branches short given what we ship". Git Flow for versioned releases, GitHub Flow for the common web-with-continuous-deploy case, trunk-based for mature teams that want the shortest loop. In all of them, the enemy is the long-lived branch that diverges from main and turns every merge into a minefield. Pick the simplest flow your context supports, keep the branches short, and most of the merge pain disappears on its own.

RD
Author
Rafael Duarte
Backend developer with a background in fintech and B2B SaaS — worked on teams that scaled APIs from zero to millions of requests. Carries enough production scars to hold strong opinions on tools, patterns and architecture decisions. Not an academic: he read the UUID RFC when he had to choose between v4 and v7 for a write-heavy table.
View profile