Branching Strategies That Work in Real Teams
Most teams inherit a branching strategy from whoever set up the repository first. They follow it without thinking about it, and then one day someone reads a blog post about trunk-based development and a two-hour argument breaks out in Slack.
The argument is usually unproductive because both sides are debating strategy without agreeing on what problem they’re trying to solve. Branching strategies are engineering tradeoffs, not belief systems. Once you understand what each one optimizes for, the decision gets easier.
What branching is actually for
A branching strategy solves a coordination problem. When multiple developers work on the same codebase at the same time, you need answers to three questions:
- Isolation - how do you keep incomplete work from breaking everyone else?
- Integration - when and how does work get combined and verified?
- Release control - can you control which features reach production and when?
Different strategies optimize these differently. No strategy is free on all three.
The three strategies worth knowing
Feature branches + PRs
This is what most people mean when they say “GitHub Flow”: one branch per feature or fix, opened against main, reviewed via pull request, merged when approved. main is always deployable.
It works well because it’s simple. There’s no ceremony, no long-lived parallel branches, and the PR gives you a natural point for review and CI verification. For most web teams shipping frequently to a single production environment, this is the right default.
Where it breaks down is when branches live too long. A feature branch that’s been open for three weeks accumulates divergence. By the time you open the PR, you’re not reviewing a feature - you’re reviewing a feature plus three weeks of drift plus a painful merge. The branching strategy didn’t cause this; the working habits did. But they compound.
Gitflow
Gitflow was described by Vincent Driessen in 2010 and has five branch types: main, develop, feature/*, release/*, and hotfix/*. Features branch from develop, get merged back, then develop gets merged into a release branch for stabilization, then into main for production.
Gitflow was designed for versioned software - libraries, desktop apps, mobile apps - where you ship discrete numbered releases and sometimes maintain multiple versions simultaneously. In that context, it makes sense. The release branch gives you a place to stabilize without blocking new feature development. Hotfix branches let you patch 1.2.3 without shipping half-finished work from develop.
For web applications where you deploy continuously to one production environment, Gitflow is overhead without the benefit. The develop branch is a second main that doesn’t mean much. The release branches solve a problem you don’t have.
The criticism of Gitflow is often framed as “it’s old” or “it’s complex.” The more accurate framing is: it was designed for a release model that most web teams don’t use. If your release model matches what it was designed for, it still works fine.
Trunk-based development
In trunk-based development, everyone commits to main (the “trunk”) frequently - at least once a day. There are no long-lived feature branches. Incomplete features are deployed to production but hidden behind feature flags.
This forces a different kind of discipline. You can’t hide work-in-progress in a branch for two weeks. You have to keep your changes small enough to integrate daily. The CI pipeline has to be fast and reliable, because everyone is breaking it if it fails. Feature flags become infrastructure, not an afterthought.
The benefit is that integration surprises disappear. Merge conflicts don’t accumulate. You know immediately if two changes interact badly, because both changes are in main by Tuesday.
The cost is everything you need in place to make it work: fast CI, a feature flag system, discipline around incremental commits, and a team culture that treats a broken main as a fire to put out immediately.
The misconception worth addressing
Trunk-based development gets pitched as “the modern, correct approach” by people who’ve read about how Google or Netflix operates. This framing is misleading.
Those teams have hundreds of engineers on a single codebase, dedicated infrastructure teams, and mature feature flag platforms built specifically for this purpose. The branching strategy is only one part of a larger system. Adopting trunk-based development without the supporting infrastructure doesn’t give you Google’s velocity - it gives you a chaotic main branch and engineers who can’t tell which half-finished things are currently deployed.
The strategy isn’t good or bad. It’s appropriate or inappropriate for a given context.
How to pick
A few questions that actually narrow it down:
How often do you deploy? If you’re deploying multiple times a day, Gitflow’s release branches are ceremony with no benefit. If you ship versioned releases on a schedule, feature branches + PRs or Gitflow fit naturally. Trunk-based development makes sense at high deploy frequency when you have the infrastructure to back it.
Do you maintain multiple versions in production? If yes, you need something like Gitflow’s release branches - a way to stabilize and hotfix v2.x while v3.x is in development. Feature branches into main won’t give you that.
How long do your branches live? This is less about strategy and more about habit, but it predicts whether any strategy will work. Branches that live for days are manageable. Branches that live for weeks create integration debt that no branching strategy fixes.
What’s your CI/CD maturity? Trunk-based development requires reliable, fast CI as a prerequisite. If your pipeline takes 40 minutes and fails intermittently, committing to main daily will cause more problems than it solves.
What most teams should actually do
For a web team of 3 to 20 engineers deploying to a single production environment: short feature branches with PRs. Keep branches alive for days, not weeks. Merge small and often. Use the PR for review, not as the only quality gate.
If you’re hitting integration pain - big merges, constant conflicts, surprises in CI - the answer is almost always shorter branches, not a different strategy. The length of your branches is more predictive of integration health than which strategy you’re following.
Trunk-based development is worth working toward if your team has the CI and feature flag discipline to support it. It’s a destination, not a starting point. Teams that jump straight to it without that foundation usually end up reverting because main becomes unstable.
The branching strategy conversation in most teams is less about engineering and more about identity - people associate with the strategy they learned first or the one their most respected engineer advocates for. Strip that out and the decision is mostly mechanical: what’s your release model, how often do you ship, and how long do your branches live? Answer those honestly and the right strategy becomes obvious.
Pick one, be consistent, and revisit if it’s creating friction. The strategy that works is the one your team actually follows.