First week of a new project, the kick-off meeting, coffee still hot, and someone asks: “Which git workflow should we use?” The next 40 minutes go to drawing Gitflow diagrams, defending GitHub Flow, someone says “trunk-based,” and the meeting ends before anyone finds solid ground.
The answer isn’t in that meeting — it’s in the product itself.
The mechanics of both, briefly
- Gitflow.
main(prod),develop(integration),feature/*(development),release/*(release prep),hotfix/*(urgent patches). Release-oriented. A feature travelsfeature→develop→release→main. - GitHub Flow. A single
mainwith short-lived branches off it. Every merge is deployable to prod. There’s no concept of a version — there’s one living version, and it’s the one in prod.
Both genuinely work in real projects. The question isn’t which one is “correct,” but which one fits the shape of your product.
Gitflow’s natural habitat
Gitflow was designed for products where the reality is “several versions are alive at once”:
- Mobile apps — because of store review time, 3.4 is reaching phones while 3.5 is in review and 3.6 lives in develop.
- SDKs and libraries — the user’s upgrade pace isn’t in your hands; you support 2.x for a while.
- On-premise / packaged software — the customer says “we’re on 4.2”; shipping a hotfix for that version isn’t a luxury, it’s a contract.
- Software shipped alongside hardware — release windows coordinated with firmware.
In these scenes the release/* branch isn’t bureaucracy, it’s where the parallel timelines live. The hotfix/* lane is the natural answer to the “the customer’s prod is on fire but we’re on develop” problem.
GitHub Flow’s natural habitat
GitHub Flow breathes in products where the reality is a single living version:
- Web apps, SaaS — the version the user is on is always the one in prod.
- Internal tools, dashboards — a single installation, continuously current.
- API services (unversioned or versioned via routes) — backward compatibility lives in the code, not in a branch.
Here the release/* branch isn’t infrastructure, it’s an unnecessary layer. A hotfix isn’t a separate lane either — it’s just a PR that gets in line.
What I actually choose
The answer to three questions picks the branching strategy for me:
- How many versions are live at once? One → GitHub Flow. More than one → Gitflow.
- Does the customer see my deploy frequency? No, I deploy whenever I want → GitHub Flow. Yes, there’s a contract between versions → Gitflow.
- Does a hotfix need to travel through a “release” on its way to prod? No → GitHub Flow. Yes → Gitflow.
If all three point the same way, the decision is made. If they’re mixed, the product’s shape hasn’t settled yet — and then I temporarily pick the simple one, GitHub Flow, because moving from GitHub Flow to Gitflow is easier than the reverse.
Common mismatch mistakes
Cases where I’ve put the two models in the wrong place (or watched someone else do it):
- Gitflow on SaaS. In a web app with a single living version, the
developbranch creates an unnecessary shadow prod; PRs have to be merged twice. The result: neither the isolation gained nor the friction paid is worth it. - GitHub Flow on a packaged product. A team filling
mainwith 4.x while customers are still on 3.x starts hunting for a branch that doesn’t exist the moment a hotfix is requested. At that point, “setting up Gitflow after the fact” is a migration project. - A hybrid model, “let’s invent our own version.” Most of the time what comes out is a patched-together thing where half the team thinks it’s Gitflow and the other half thinks it’s GitHub Flow. Applying one of the two models correctly is always cheaper than inventing a third.
Workflow is the shape of software’s lifecycle
A branching strategy determines how many days after birth a feature reaches prod, which corridors it passes through, on what maintenance line it ages. In other words, it shapes software’s lifecycle — the speed of the decision, not the code.
Take two companies using the same language, the same framework; if one chose Gitflow and the other GitHub Flow, the time to get the same feature to prod triples on that decision alone. That’s why a branching strategy isn’t a tooling question, it’s a product question.
When neither is enough
In some teams the deploy rhythm speeds up so much — 5+ deploys a day — that even GitHub Flow’s short-lived branches start to be a source of friction. Past that threshold, I switch to trunk-based: branches are measured in hours, unfinished work lives in the code behind a feature flag.
All three are tools designed for a different tempo. Which one will keep you comfortable depends on how many hours you want the distance between the code you write and your prod to be.
The decision changes, and that’s correct
Imposing today’s product shape on your team two years from now is one of the most expensive mistakes. When one of the three signals changes — the version count grew, the customer type changed, the deploy frequency jumped to 10 — reopen the branching strategy too. A workflow isn’t chosen once, it’s a decision you re-evaluate as the product changes.
I gathered the daily operational side of a branching strategy — the code review, deploy, and rollback routine — as a whole in Git in Production: A Senior’s Practical Guide. This choice is only the entry door to that picture.
The choice between Gitflow and GitHub Flow isn’t “which is modern,” it’s “which one your product shape makes natural.” The wrong answer is paid two minutes at a time, on every PR, for years.
Pick your git workflow to fit the product; not the product to fit the workflow.