Skip to main content

Git branching strategies (Git Flow, GitHub Flow)?

Git branching strategies define structured rules for how a team creates, merges, and deletes branches to coordinate development without breaking production.

Theory

TL;DR

  • Git Flow: factory assembly line with dedicated stations for features, testing, and fixes. GitHub Flow: food truck that cooks and serves immediately.
  • Core split: Git Flow keeps a long-lived develop branch as the integration point before production. GitHub Flow has no develop and merges directly to main.
  • Scheduled versioned releases (desktop app, game)? Git Flow. Continuous web deploys? GitHub Flow.
  • Team of 10+ with quarterly releases: Git Flow. Team of 1-10 shipping daily: GitHub Flow.

Quick example

bash
# GitHub Flow: one short cycle, main stays deployable git checkout main git pull origin main # always start from latest git checkout -b feature/user-login # short-lived branch git add . && git commit -m "Add login form" git push origin feature/user-login # open PR, review, merge to main, delete branch git branch -d feature/user-login # main is deployable right now

One branch. One PR. Main receives the change and is ready to deploy the same day.

Key difference

Git Flow keeps a develop branch as the permanent integration point. Features land on develop first. Then a release/* branch stages production-ready code. Only after that does main get a versioned tag like v1.2. GitHub Flow removes develop entirely: branch from main, open a pull request, merge back to main when approved. That is the whole structural difference. Git Flow is heavier but predictable for teams with fixed release schedules. GitHub Flow is faster for teams that deploy after every review.

When to use

  • Mobile or desktop apps with quarterly releases: Git Flow handles versioning cleanly.
  • Web apps or SaaS with daily deploys: GitHub Flow cuts branch overhead.
  • Solo devs or small teams (1-5 people): GitHub Flow, no workflow tax.
  • Enterprise with compliance audits: Git Flow gives a clean separation between production and staging.
  • Monorepos with microservices: GitHub Flow plus tags and selective CI jobs per service.

Comparison table

AspectGit FlowGitHub Flow
Branchesmain, develop, feature/, release/, hotfix/*main, feature/* (short-lived)
Release cycleScheduled (v1.2 via release branch)Continuous (merge to main = deploy)
Team sizeMedium-large (10+ devs)Small-agile (1-10 devs)
Toolinggit-flow extensions recommendedGitHub PRs + CI/CD
Merge frequencyLow (features collect in develop first)High (daily PRs to main)
Best forElectron, games, Chrome extensionsNext.js on Vercel, VS Code open-source

How git handles branches internally

Git stores each branch as a lightweight pointer to a commit (a SHA-1 hash). Creating feature/login is just writing a small file under .git/refs/heads/. No code is copied. Merging finds the common ancestor via git merge-base and applies diffs from that point, either as a fast-forward or a merge commit. Git Flow adds CLI scripts (git flow feature start) that automate creation from develop. GitHub Flow skips those scripts and uses GitHub's pull request engine, which runs a server-side three-way merge.

The first time I ran a hotfix through full Git Flow, the auto-backport to develop saved a fix that would otherwise have been lost in the next release cycle. That said, most web teams since then use GitHub Flow and solve the same edge case with feature flags.

Common mistakes

Merging a feature directly to main in Git Flow

bash
git checkout feature/add-api # branched from develop git checkout main git merge feature/add-api # skips develop, breaks integration

develop exists to catch integration bugs before they touch main. Skipping it breaks the whole model. Fix: use git flow feature finish add-api, which merges to develop automatically.

Keeping feature branches alive too long in GitHub Flow

bash
git checkout -b feature/epic # created three weeks ago git merge main # merge conflict explosion

GitHub Flow requires short branches (1-3 days). If a feature takes weeks, split it into smaller PRs or use feature flags to hide incomplete work. Fix: git rebase main daily to stay current.

Skipping --no-ff on hotfix and release merges in Git Flow

bash
git merge release/v1.0 # fast-forward erases the release step from history

Without --no-ff, history looks linear and you lose the audit trail showing when a release was merged. Fix: git merge --no-ff release/v1.0.

Not deleting merged branches

Dead branches accumulate fast. A repo can hit thousands of stale refs after a year. Fix: enable auto-delete in GitHub PR settings and run git remote prune origin periodically.

Real-world usage

  • React: GitHub Flow. Features branch from main, PRs with CI checks.
  • VS Code: GitHub Flow. Daily main deploys to the Insiders build.
  • Linux kernel: Git Flow variant. mainline acts as develop, stable/* as release branches.
  • Electron: Git Flow for tagged releases like v25.0.0.
  • Kubernetes: trunk-based development (branches live less than a day).

Follow-up questions

Q: Walk through creating a hotfix in Git Flow.
A: Branch from main with git flow hotfix start fix-crash. Commit the fix. Run git flow hotfix finish fix-crash. This auto-merges to both main (tagged v1.2.1) and develop, so the fix is not lost in the next release cycle.

Q: Why not always use GitHub Flow?
A: It lacks a staging buffer for bigger releases. Without develop, a bad merge can break main before CI catches it. Git Flow gives you a place to catch integration bugs before they reach production.

Q: How do you handle versioned releases in GitHub Flow?
A: Merge to main and create a git tag (v1.2) immediately. CI/CD picks up the tag and deploys that commit. Semantic versioning still applies, you just skip the dedicated release branch.

Q: What is GitLab Flow and how does it differ?
A: GitLab Flow adds environment branches like staging and production between feature branches and main. Git Flow uses release branches tied to version numbers instead. Different mental model, similar goal.

Q: (Senior) In a monorepo with 50 services, how does GitHub Flow scale without breaking deploys?
A: Path filters in CI config (e.g., changed-files: ['services/auth/**']) trigger only the relevant jobs. Canary deploys and feature flags let you merge incomplete code without affecting users. Each service gets its own version tag.

Examples

Creating and merging a feature branch (GitHub Flow)

bash
git checkout main git pull origin main git checkout -b feature/use-auth-hook # src/hooks/useAuth.js # export const useAuth = () => { ... checks token in localStorage ... } git add src/hooks/useAuth.js git commit -m "Add useAuth hook with token check" git push origin feature/use-auth-hook # open PR on GitHub, 2 reviewers approve # auto-merge to main, CI runs tests green # Vercel picks up main and deploys git branch -d feature/use-auth-hook

PR opened, reviewed, merged, deployed the same day. Main never had an unstable state.

Git Flow hotfix with backport to develop

bash
# Production crash discovered during release/v1.2 prep git checkout main git checkout -b hotfix/critical-crash # branch from main, not develop # fix the crash git add . && git commit -m "Fix null pointer in auth handler" git checkout main git merge --no-ff hotfix/critical-crash git tag -a v1.2.1 -m "Hotfix: null pointer in auth" git checkout develop git merge --no-ff hotfix/critical-crash # backport so fix lands in next release too git branch -d hotfix/critical-crash

main gets tagged v1.2.1 and deploys. develop also carries the fix, so the next scheduled release does not reintroduce the bug.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?