GitHub Flow: Simple Branching for Teams That Ship Constantly
One rule, six steps: how GitHub Flow keeps main deployable, and the CI, branch protection and rollback habits that make it work.
After GitFlow, GitHub Flow feels almost suspiciously simple. There's one rule that holds everything together:
Anything on
mainis deployable.
That's it. No develop, no release branches, no hotfix ceremony. GitHub's own engineers popularised it, and Scott Chacon wrote the canonical description back in 2011. It fits web apps and SaaS products that deploy often, and it's what most small and mid-sized teams end up using anyway, whether they call it that or not.
The six steps
- Branch off
mainwith a descriptive name. - Commit your changes, early and often.
- Open a pull request, even a draft one, to start the conversation.
- Review and discuss. CI runs here too.
- Deploy from the branch (optionally, to staging or a preview) to verify it works.
- Merge to
main, which then goes to production.
What it looks like in git
gitGraph
commit id: "v1"
branch feature/search
checkout feature/search
commit id: "add index"
commit id: "add UI"
checkout main
branch fix/cart-bug
checkout fix/cart-bug
commit id: "fix total"
checkout main
merge fix/cart-bug
checkout feature/search
commit id: "address review"
checkout main
merge feature/search
commit id: "deploy"
Branches are short, merges are frequent, and main keeps moving forward in a straight-ish line.
A realistic day-to-day example
# 1. Start from fresh main
git checkout main
git pull origin main
# 2. Branch
git checkout -b feature/add-order-export
# 3. Work and commit
git add .
git commit -m "Add CSV export for orders"
git push -u origin feature/add-order-export
Then open the PR using the GitHub CLI (or the web UI):
gh pr create --title "Add CSV export for orders" \
--body "Adds export button on the orders page. Closes #214." \
--draft
When ready for review, mark it ready and let CI and teammates do their thing:
gh pr ready
After approval, merge it. I'm a fan of squash merges for this workflow because each PR becomes one clean commit on main:
gh pr merge --squash --delete-branch
The thing that makes GitHub Flow actually work
The workflow itself is trivial. What makes it safe is everything around it:
1. Reliable automated tests
If main must always be deployable, you need CI that you trust. A flaky test suite quietly turns GitHub Flow into "main is probably okay." Fix flaky tests like they're production bugs.
2. Branch protection rules
At minimum:
- Require PR before merging
- Require status checks to pass
- Require at least one approval
- Dismiss stale approvals when new commits are pushed
- Block force-pushes to
main
3. Fast, ideally automatic deploys
When merging to main triggers a deploy, the loop is tight and bugs get found quickly. Example GitHub Actions pipeline:
name: CI/CD
on:
pull_request:
push:
branches: [main]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- run: npm ci
- run: npm test
deploy:
needs: test
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: ./scripts/deploy.sh
4. A rollback plan
You will ship a bad change eventually. Make sure reverting is one command and one redeploy:
git revert <merge-commit-sha>
git push origin main
5. Feature flags for bigger work
Because every merge goes live, half-finished features need to be hidden. Wrap them in a flag so you can merge daily without exposing unfinished UI.
Pros
- Very easy to learn. New joiners get it in ten minutes.
- Fast feedback. Short branches mean smaller diffs and easier reviews.
- Fits CI/CD naturally. Merge equals deploy.
- Pull requests become the unit of collaboration. Discussion, review, and history all live in one place.
- Minimal overhead. No back-merging, no release branch babysitting.
Cons
- No built-in answer for multiple versions. If you need to support v1 and v2 simultaneously, you'll end up inventing release branches.
- Needs strong automation. Weak testing makes
mainunstable. - Not great for scheduled or gated releases. If compliance requires a sign-off phase before production, this model has no slot for it.
- Staging environments are awkward. Most teams solve this with preview deployments per PR, which is great but takes setup.
- Long-running features are painful unless you use flags.
Best practices
- Keep PRs small. Under ~400 lines changed is a good target. Reviewers actually read those.
- Write meaningful PR descriptions. What changed, why, how to test, and screenshots for UI work.
- Use draft PRs early. Getting feedback before the code is polished saves rework.
- Name branches clearly.
feature/,fix/,chore/,docs/prefixes keep the branch list readable. - Delete branches after merging. Auto-delete is a repo setting, so just turn it on.
- Use
CODEOWNERSto route reviews to the right people automatically. - Prefer squash merge for a clean linear
main, unless you really want the individual commits preserved. - Rebase or merge
maininto your branch if it falls behind, before asking for final review.
Is it right for you?
Pick GitHub Flow if you:
- Run a web app or API with a single production version
- Can deploy anytime without a formal release process
- Have decent automated test coverage
- Want the least process that still gives you code review
If you need release trains or parallel version support, look at GitLab Flow or GitFlow. If you want even less ceremony and have a mature CI setup, Trunk-Based Development is the next step.
Found this useful? You can support my writing by buying me a coffee: buymeacoffee.com/vatul16 ☕
Thanks for reading!
About the author
- Hi, I'm Atul! I am a software engineer with a strong focus on DevOps, Cloud Infrastructure, and automation. I spend my time building scalable CI/CD pipelines, mastering tools like Docker, Terraform, and AWS, and breaking down complex cloud concepts into accessible, hands-on tutorials. I believe the best way to learn is to build, break, and document the process.
Comments
Join the discussionThe main-is-always-deployable rule is the part teams usually skip. Short-lived branches and a quick review keep it honest. If a branch lives for days, the merge pain usually outweighs any benefit from the extra work in progress.
Absolutely, Rishabh. Keeping main deployable is the commitment behind the simplicity. Small PRs and timely reviews help, but reliable CI and required checks are what make that commitment enforceable. For bigger features, I’d rather split the work and use feature flags than let a branch drift for days.
In your experience, what tends to keep branches open longer, large changes or review delays?
In my experience, review delays keep branches open longer. A large change can be split or put behind a flag, but a branch waiting for review goes stale and picks up conflicts. A small PR with a clear review window usually merges faster than a perfect one that waits.
Git Branching Strategies: A Practical Guide for Teams
A five-part, hands-on series on Git branching strategies. We go through GitFlow, GitHub Flow, GitLab Flow and Trunk-Based Development, with diagrams, real commands, pipeline examples, pros, cons and best practices. The final post helps you decide which strategy fits your team, release style and level of automation.




