TechEarl

Git Workflows for Teams: GitHub Flow vs Git Flow vs Trunk-Based

A Git branching strategy is the rule for how your team makes and merges branches. GitHub Flow, Git Flow, and trunk-based development compared, and how to pick one for a small team.

Ishan Karunaratne⏱️ 8 min readUpdated
Share thisCopied
Git branching strategy compared: GitHub Flow vs Git Flow vs trunk-based development, and how a small team should choose one

A Git branching strategy is just the agreement your team makes about how branches get created, reviewed, and merged. There are three you will run into by name: GitHub Flow, Git Flow, and trunk-based development. For a small team shipping a web app or a service, GitHub Flow is almost always the right starting point: branch off main, open a pull request, merge when it passes review and CI, deploy. Git Flow adds long-lived develop and release branches and only earns its complexity when you ship versioned releases. Trunk-based development pushes the other way, toward tiny short-lived branches and merging into main many times a day.

That is the whole decision in one paragraph. The rest of this page explains what each one actually is, what it optimizes for, and how to choose without overthinking it.

What a branching strategy actually decides

Strip away the names and every workflow is answering the same handful of questions:

  • Where does new work start: off main, off a shared develop branch, or directly on main?
  • How long does a branch live: hours, days, or weeks?
  • What has to happen before code lands: a pull request and review, CI passing, both, neither?
  • What does main mean: always deployable, or a staging area that gets stabilized before release?

The strategies below are just three common sets of answers. If you are new to the moving parts here, the Git branching basics for beginners page covers what a branch is and how to create one before you commit to a team-wide convention.

GitHub Flow: one main branch, short feature branches

GitHub Flow is the simplest workflow that still gives you code review. There is exactly one long-lived branch, main, and it is always deployable. Everything else is a short-lived branch off main that exists only long enough to finish one piece of work.

The loop looks like this (the git pull at the top refreshes main before you branch, which is also a good moment to understand the difference between git pull and git fetch):

bash
# start from an up-to-date main
git switch main
git pull

# branch for one piece of work
git switch -c add-search-filter

# ... commit as you go ...
git add .
git commit -m "Add search filter to results page"

# push and open a pull request
git push -u origin add-search-filter

You open a pull request from that branch, a teammate reviews it, CI runs, and once it is green you merge into main and delete the branch. Deploy happens from main, either automatically on merge or on a button.

What it optimizes for: speed and simplicity. There is one source of truth, branches stay small, and review is built into the merge. It fits web apps and services where "the latest main" and "what is in production" are meant to be the same thing.

The trade-off: because main is always shippable, you need a way to keep half-finished work from breaking it. That usually means feature flags for anything large, plus a protected main branch that blocks direct pushes and requires the PR checks to pass. Pair it with a continuous integration pipeline so nothing merges without the tests running.

Git Flow: develop, release, and hotfix branches

Git Flow, the model Vincent Driessen described in 2010, is the heavyweight option. Instead of one long-lived branch it defines several:

  • main holds only released, tagged versions. Every commit on it is a production release.
  • develop is the integration branch where finished features accumulate between releases.
  • feature/* branches come off develop and merge back into it.
  • release/* branches fork from develop to stabilize a version, then merge into both main and develop.
  • hotfix/* branches come off main to patch production, then merge back into both.

What it optimizes for: managing multiple versions in flight. If you ship installable software, support several released versions at once, or have a formal QA-then-release cadence, the release and hotfix branches give you somewhere to stabilize and patch without freezing day-to-day feature work.

The trade-off: it is a lot of branches and a lot of merging for a team that just deploys a web app continuously. The develop-to-release-to-main dance only pays off when you genuinely have releases to manage. For most small teams it is ceremony with no payoff, and it makes merge conflicts more frequent because branches live longer and drift further from each other. Even the original author later noted it was designed for versioned-release software, not continuously delivered web apps.

Trunk-based development: everyone commits to main, fast

Trunk-based development goes the opposite direction from Git Flow. There is one trunk (main), and the rule is that branches must be tiny and short-lived: a branch lives for hours, not days, and merges back into main the same day. Many teams running this push to main several times per developer per day.

Because changes are so small, integration pain mostly disappears. You are never reconciling a two-week-old branch against a trunk that moved on without it. Unfinished features hide behind feature flags rather than long-lived branches, so the trunk stays releasable even with work in progress.

What it optimizes for: continuous integration in the literal sense, and it is the workflow most associated with high-performing teams in the DevOps research. It demands strong automated testing and a real CI pipeline, because there is no develop buffer catching problems before they reach the trunk. With weak tests, "commit to main many times a day" just means breaking main many times a day.

The trade-off: discipline. Small commits, feature flags, fast CI, and a clean commit history are not optional here.

The three strategies side by side

GitHub FlowGit FlowTrunk-Based
Long-lived branchesmain onlymain + developmain (trunk) only
Branch lifetimeHours to daysDays to weeksHours
Optimizes forSimplicity, fast deliveryVersioned releases, multiple versions in flightContinuous integration, high deploy frequency
Code reviewPull request per branchPull request into developPR or pair, kept very small
Release modelDeploy from mainStabilize on release, tag on mainDeploy from trunk, flags hide WIP
CI requirementRecommendedRecommendedMandatory, strong tests
Best forMost small web/service teamsInstallable/versioned softwareMature teams with solid automation

How to pick one for a small team

Default to GitHub Flow. For a team of two to ten people shipping a web app or a service, it gives you review and a deployable main without the branch bookkeeping, and you can adopt it in an afternoon. If you are standing up the repo from scratch, set up Git for a brand-new project first, then layer the workflow on top. While you are at it, settle the small mechanical things the whole team shares, like deciding between SSH and HTTPS remotes for the team, so everyone pushes the same way.

Reach for Git Flow only if you actually ship versions: desktop or mobile apps, libraries, on-premise software, anything where "version 2.3 is in production and 2.4 is in QA and someone needs a hotfix on 2.3" is a real sentence in your week. If you cannot say that sentence truthfully, you do not need develop and release branches.

Move toward trunk-based development as the team matures and your test suite gets strong enough to trust. It is less a starting point than a destination: GitHub Flow with the branches getting shorter and shorter until merging daily into main is just how you work. Do not start here with a weak CI pipeline.

Whatever you pick, write it down and protect main. The strategy only works if everyone follows it, and a branch protection rule on GitHub is what turns a written convention into an enforced one. New to the whole picture? Start with Git for beginners and come back once the basics feel natural.

A note on how branches integrate

The branching strategy decides when branches merge; it does not decide how. Whether you keep merge commits or rebase for a linear history is a separate choice, covered in git merge vs git rebase. A common pairing is GitHub Flow plus a squash-merge on the PR, so each feature lands as one tidy commit on main. If you go that route, squashing with interactive rebase is worth knowing for cleaning up a branch before review.

Sources

Authoritative references this article was fact-checked against.

Tagsgit branching strategyGitVersion ControlGitHub FlowGit Flowtrunk-based development

Found this useful? Pass it on.

Copied

Ishan Karunaratne

Tech Architect · Software Engineer · AI/DevOps

Tech architect and software engineer with 20+ years building software, Linux systems, and DevOps infrastructure, and lately working AI into the stack. Currently Chief Technology Officer at a healthcare tech startup, which is where most of these field notes come from.

Keep reading

Related posts