Branching strategies for teams
In this series (8 parts)
Branching mechanics are universal. Branching strategies are opinions. The previous article covered how merges and rebases work. This one covers when to use them and how teams organize their branches.
There is no single best strategy. The right choice depends on release cadence, team size, and deployment model.
GitFlow
GitFlow was introduced by Vincent Driessen in 2010. It defines a strict branching model designed around scheduled releases.
Branch structure
- main: Always reflects production. Tagged with version numbers.
- develop: Integration branch for the next release.
- feature/*: Branch off develop, merge back into develop.
- release/*: Branch off develop when preparing a release. Merge into both main and develop.
- hotfix/*: Branch off main for urgent fixes. Merge into both main and develop.
gitGraph commit id: "v1.0" tag: "v1.0" branch develop commit id: "d1" branch feature/auth commit id: "f1" commit id: "f2" checkout develop merge feature/auth id: "d2" branch release/1.1 commit id: "r1" tag: "RC" checkout main merge release/1.1 id: "v1.1" tag: "v1.1" checkout develop merge release/1.1 id: "d3"
GitFlow in action. Features merge into develop. A release branch stabilizes the code before merging into main.
When GitFlow works
- Versioned software with scheduled releases (desktop apps, libraries, mobile apps).
- Teams that need to support multiple versions simultaneously.
- Organizations with dedicated QA phases.
When it hurts
- Continuous deployment environments. The ceremony of release branches adds friction.
- Small teams. The overhead of maintaining main, develop, release, and hotfix branches is not justified when two people push code daily.
GitHub Flow
GitHub Flow strips branching down to the essentials.
Branch structure
- main: Always deployable.
- feature/*: Branch off main. Open a pull request. Merge back into main after review.
That is it. No develop branch. No release branches.
gitGraph commit id: "A" commit id: "B" branch feature/search commit id: "C" commit id: "D" checkout main merge feature/search id: "E" commit id: "F" branch feature/cache commit id: "G" checkout main merge feature/cache id: "H"
GitHub Flow. Short-lived feature branches merge directly into main.
The rules
- Anything on main is deployable.
- Branch off main to work on something.
- Open a pull request early for discussion.
- Merge after review and CI passes.
- Deploy immediately after merging.
When GitHub Flow works
- Web applications with continuous deployment.
- Teams that deploy multiple times per day.
- Projects where “the latest commit is the release.”
When it hurts
- When you need to maintain multiple versions. There is no mechanism for parallel release tracks.
- When deployments require a stabilization phase.
GitLab Flow
GitLab Flow sits between GitFlow and GitHub Flow. It adds environment branches to GitHub Flow’s simplicity.
Branch structure
- main: The default development branch.
- feature/*: Branch off main. Merge back via merge request.
- pre-production: Mirrors what is deployed to staging.
- production: Mirrors what is deployed to production.
Changes flow one direction: feature to main to pre-production to production.
gitGraph commit id: "A" branch pre-production checkout main commit id: "B" branch feature/api commit id: "C" checkout main merge feature/api id: "D" checkout pre-production merge main id: "E" branch production merge pre-production id: "F"
GitLab Flow. Changes promote from main through environment branches.
When GitLab Flow works
- Teams that deploy to multiple environments in sequence.
- Organizations that need an audit trail of what is deployed where.
- Projects that want simplicity without giving up staged deployments.
Trunk-based development
Trunk-based development (TBD) pushes the philosophy further. Everyone commits to a single branch. For more depth, see the trunk-based development article.
How it works
- Developers commit directly to
main(the trunk) or use very short-lived branches (hours, not days). - Feature flags hide incomplete work.
- CI runs on every commit.
- The trunk is always releasable.
gitGraph commit id: "A" commit id: "B" branch short-lived commit id: "C" checkout main merge short-lived id: "D" commit id: "E" commit id: "F" branch fix commit id: "G" checkout main merge fix id: "H"
Trunk-based development. Branches last hours, not weeks. Most work lands on main directly.
Key practices
- Feature flags: Decouple deployment from release. Ship code behind a flag, enable it when ready.
- Small commits: Each commit should be independently safe.
- Fast CI: If CI takes 30 minutes, developers batch changes and the trunk gets stale.
- Code review: Pre-commit review (pair programming) or short-lived PR branches.
When TBD works
- High-performing teams with strong CI/CD pipelines.
- Continuous deployment environments.
- Teams that invest in feature flags and automated testing.
When it hurts
- Teams without CI. Committing directly to trunk without tests is reckless.
- Projects where feature flags are impractical (some embedded systems, some mobile apps).
Choosing a strategy
The decision comes down to a few factors.
Decision guide
Do you deploy continuously? Use GitHub Flow or trunk-based development.
Do you need staged environments? GitLab Flow adds environment promotion.
Do you ship versioned releases? GitFlow handles parallel version maintenance.
Is your team small and fast? GitHub Flow or trunk-based. Skip the ceremony.
Is your team large with separate QA? GitFlow or GitLab Flow give structure.
Mixing strategies
Real teams often blend strategies. A common pattern:
- Use trunk-based development for the core product.
- Use GitFlow for a versioned public API or library.
- Use GitHub Flow for internal tools.
The strategy serves the team. If it creates friction without value, change it.
Branch naming conventions
Regardless of strategy, consistent naming helps.
feature/JIRA-123-user-auth
bugfix/JIRA-456-null-pointer
hotfix/JIRA-789-security-patch
release/2.1.0
Prefixes signal intent. Ticket numbers enable traceability. Descriptive suffixes help humans.
Protected branches
Most Git hosting platforms support branch protection rules.
- Require pull request reviews before merging.
- Require status checks (CI must pass).
- Restrict force pushes to prevent history rewriting.
- Require signed commits for compliance.
Protection rules enforce the strategy. Without them, conventions rely on discipline alone.
What comes next
With branching strategies in place, the next article covers rebase and history rewriting in depth. Interactive rebase, squashing, and fixup give you precise control over your commit history before merging.