GitHub Flow: Simple Branching for Continuous Delivery

Learn GitHub Flow — the lightweight branching strategy built for continuous deployment. Covers feature branches, pull requests, and production deployment on every merge.

published: reading time: 21 min read author: Geek Workbench updated: March 31, 2026

GitHub Flow: Simple Branching for Continuous Delivery

If you’ve ever looked at Git Flow with its five different branch types, release schedules, and complicated lifecycle diagrams and thought “there has to be a simpler way” — you’re not alone. GitHub Flow was born exactly because Git Flow solves problems that most teams don’t actually have. GitHub Flow strips away the complexity: there’s one rule that governs everything — every line of code starts from main and every line of code merges back to main.

This works because GitHub built this model around a single assumption: if your tests pass and your code is reviewed, you’re ready to deploy. No staging branches, no version tags, no release candidates. When your feature branch merges to main, it goes to production. Some of the fastest-moving teams in the industry run on this philosophy, and this post explains why it works, when it breaks down, and how to make it work for your team.

Introduction

There are a lot of ”## when to use / when not to use” sections in this file — they read like filler content from an AI generator. The core idea behind GitHub Flow is simple: if your tests pass and your code is reviewed, you’re ready to deploy. No staging branches, no version tags, no release candidates. When your feature branch merges to main, it goes to production. Some of the fastest-moving teams in the industry run on this philosophy, and it works — provided you have the CI/CD infrastructure and feature flag culture to back it up. This guide covers when this model shines, where it breaks down, and how to implement it without shooting yourself in the foot.

When to Use / When Not to Use

Use GitHub Flow When

  • Continuous deployment — You deploy to production multiple times per day or week
  • Web applications and SaaS — No packaging, signing, or app store approval process
  • Small to medium teams — Where communication overhead of complex branching outweighs benefits
  • Feature flag culture — You control feature rollout with flags, not branch timing
  • Automated testing — Your CI pipeline catches regressions before they reach main

Do Not Use GitHub Flow When

  • Scheduled releases — You ship on a calendar cadence with QA gates
  • Multiple supported versions — You need to patch v1.x while developing v2.x
  • Regulated environments — You need formal change management and release documentation
  • No CI/CD pipeline — Without automated testing, every merge to main is a gamble
  • Mobile or desktop apps — Where each release requires a build and distribution process

Core Concepts

GitHub Flow has exactly three concepts:

ConceptDescription
main branchAlways deployable. The single source of truth.
Feature branchesBranch from main, merge back to main via pull request
Pull requestsCode review, discussion, and CI validation before merge

That’s it. No release branches. No develop branch. No version branches. The simplicity is the entire point.


graph LR
    A[main - Always Deployable] --> B[feature/new-ui]
    A --> C[feature/api-v2]
    A --> D[feature/bugfix]
    B -->|PR + CI| A
    C -->|PR + CI| A
    D -->|PR + CI| A
    A --> E[Deploy to Production]

Architecture and Flow Diagram

The complete GitHub Flow lifecycle from branch creation through production deployment:


graph TD
    A[main - Production] -->|create branch| B[feature/branch]
    B -->|commit| B
    B -->|push| C[Remote Feature Branch]
    C -->|open PR| D[Pull Request]
    D -->|code review| E[Review Feedback]
    E -->|update| C
    D -->|CI passes| F[Approved]
    F -->|merge| A
    A -->|auto deploy| G[Production]

Step-by-Step Guide

1. Create a Feature Branch

Always branch from the latest main:


# Ensure you have the latest main
git checkout main
git pull origin main

# Create and switch to your feature branch
git checkout -b feature/add-user-profile

Branch naming conventions matter for traceability:


# Good: descriptive with ticket reference
feature/PROJ-123-add-user-profile
feature/add-dark-mode
bugfix/fix-login-timeout

# Bad: vague or personal
feature/work-in-progress
johns-branch
temp-fix

2. Commit and Push Frequently

Small, focused commits make code review easier and bisect more effective:


# Make changes
git add src/components/UserProfile.tsx
git commit -m "feat: add user profile component skeleton"

git add src/styles/profile.css
git commit -m "feat: style user profile card"

# Push to remote
git push -u origin feature/add-user-profile

3. Open a Pull Request

The pull request is the heart of GitHub Flow. It’s where code review, automated testing, and team discussion converge:


# Using GitHub CLI
gh pr create \
  --base main \
  --head feature/add-user-profile \
  --title "feat: add user profile page" \
  --body "## Summary
Adds user profile page with avatar, bio, and settings link.

## Testing

- Manual: /profile endpoint renders correctly
- Unit: UserProfile component tests pass
- E2E: Profile navigation flow verified

## Screenshots

[Attach screenshots if UI changes]"

4. Review and Iterate

Code review is mandatory in GitHub Flow — it’s the only quality gate before main:

  • At least one approved review required
  • All CI checks must pass (tests, linting, type checking)
  • Address review comments with additional commits to the same branch
  • The PR updates automatically with each push

5. Merge and Deploy

Once approved and green:


# Merge via CLI or UI
gh pr merge feature/add-user-profile --squash --delete-branch

# The merge triggers deployment
# CI/CD pipeline detects main branch update and deploys

Most teams use squash merges to keep main history clean, but some prefer merge commits for topology preservation.

Production Failure Scenarios

ScenarioWhat HappensMitigation
Bad merge reaches productionA bug slips through review and CIFeature flags allow instant rollback without code changes; revert the merge commit
CI is green but production breaksTests don’t cover the failure modeAdd canary deployments; monitor error rates post-deploy; implement automated rollback
Conflicting PRsTwo PRs modify the same file, only one can merge firstCommunicate in PR comments; rebase the second PR on updated main before merging
Long-running feature branchBranch diverges from main, merge conflicts pile upRebase on main daily; break large features into smaller, mergeable chunks
Deploy pipeline failureMerge succeeds but deployment failsDeployment should be atomic; failed deploys auto-rollback; never leave main in a broken state

Deployment Verification Checklist

After each merge to main, verify the deployment succeeded:

  • CI pipeline completed with all green checks
  • Deployment logs show successful rollout with correct commit SHA
  • Health endpoint responds with 200 OK
  • Error rate in monitoring is within normal baseline (< 1% increase)
  • Key user journeys pass smoke tests (login, core feature, checkout)
  • No new Sentry/alerting errors in the last 5 minutes
  • Database migrations (if any) completed successfully
  • Feature flags for the new feature are in the intended state
  • Rollback procedure is documented and tested if this is a high-risk deploy

Trade-off Analysis

AspectAdvantageDisadvantage
SimplicityOne permanent branch, easy to understandNo structure for complex release management
SpeedMerge and deploy in minutesRequires excellent automated testing
Code reviewEvery change gets reviewed before mergeCan become a bottleneck for large teams
HistoryClean linear history with squash mergesLoses feature branch topology information
RiskSmall, frequent changes are low-riskEvery merge to main is a production change
Learning curveNew developers can start contributing immediatelyRequires discipline around CI and feature flags

Implementation Snippets

Branch Protection Rules

# GitHub repository settings (via API or UI):
# Required pull request reviews: 1
# Dismiss stale reviews: enabled
# Require status checks: build, test, lint
# Require branches to be up to date: enabled
# Include administrators: enabled
# Restrict pushes that create matching files: enabled

GitHub Actions — CI Pipeline

# .github/workflows/ci.yml
name: CI
on:
  pull_request:
    branches: [main]
  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 run lint
      - run: npm run typecheck
      - run: npm test -- --coverage

  deploy:
    needs: test
    if: github.ref == 'refs/heads/main'
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Deploy to production
        run: ./scripts/deploy.sh
        env:
          DEPLOY_TOKEN: ${{ secrets.DEPLOY_TOKEN }}

Feature Flag Integration


// src/features/userProfile.ts
import { isFeatureEnabled } from '@/utils/featureFlags';

export function UserProfilePage() {
  if (!isFeatureEnabled('user-profile')) {
    return null; // Or redirect to a fallback
  }

  return <UserProfile />;
}

// Feature flag evaluation
// src/utils/featureFlags.ts
export function isFeatureEnabled(flag: string): boolean {
  const flags = process.env.FEATURE_FLAGS || '{}';
  return JSON.parse(flags)[flag] === true;
}

Quick Revert Script


#!/bin/bash
# scripts/revert-last-merge.sh
# Revert the last merge commit on main

git checkout main
git pull origin main

# Find the last merge commit
LAST_MERGE=$(git log --oneline --merges -1 --format="%H")

if [ -z "$LAST_MERGE" ]; then
  echo "No merge commits found on main"
  exit 1
fi

echo "Reverting merge: $LAST_MERGE"
git revert -m 1 "$LAST_MERGE"
git push origin main

echo "Revert pushed. CI will redeploy the previous version."

Observability Checklist

  • Logs: Log every PR merge event with author, PR number, and commit SHA
  • Metrics: Track PR cycle time (open to merge), deployment frequency, and mean time to recovery
  • Traces: Correlate each deployment with the PR that triggered it and any subsequent incidents
  • Alerts: Alert on deployment failure rate > 5%, PRs open longer than 3 days, or main branch CI failures
  • Dashboards: Display deployment frequency, lead time for changes, change failure rate, and MTTR (DORA metrics)

GitHub Flow vs Trunk-Based Development

GitHub Flow and trunk-based development are close relatives — same core idea, different tolerances for how long branches can live. GitHub Flow works with branches that stick around for days or weeks. Trunk-based development expects branches to be opened and closed within hours, with feature flags hiding incomplete work.

The practical difference comes down to infrastructure. GitHub Flow tolerates longer-lived branches because most teams don’t have the feature flag systems needed to merge incomplete code continuously. Trunk-based development requires that infrastructure as a baseline.

If you’re moving from GitHub Flow to trunk-based, you need:

  • Feature flag system (LaunchDarkly, Unleash, Flagsmith, etc.)
  • Canary deployment support in CI/CD
  • A culture of merging incomplete features behind flags
  • Rollback procedures that don’t require new deployments

Security and Compliance Notes

  • Branch protection: Required reviews and status checks prevent unauthorized merges to main
  • Signed commits: Require GPG-signed commits for cryptographic authorship verification
  • CODEOWNERS: Use CODEOWNERS file to require specific team reviews for sensitive directories
  • Secrets scanning: Enable GitHub secret scanning to prevent credential leaks in PRs
  • Audit log: GitHub’s audit log tracks all repository events including branch protection changes
  • Compliance gap: GitHub Flow lacks formal release artifacts — supplement with deployment logs and change tickets if required by your compliance framework

Common Pitfalls / Anti-Patterns

  1. The Staging Branch — Adding a staging branch turns GitHub Flow into a half-baked Git Flow. Either commit to continuous deployment or use a different model.
  2. Skipping Code Review — Merging your own PRs without review defeats the only quality gate. Enforce branch protection rules.
  3. Massive PRs — Pull requests with 50+ files are impossible to review effectively. Break features into smaller, reviewable chunks.
  4. Ignoring CI Failures — Allowing merges with failing checks means main is not always deployable. Make CI mandatory.
  5. No Feature Flags — Without feature flags, every merged feature is immediately visible to users. This couples deployment to release.
  6. Long-Lived Branches — Feature branches that exist for weeks defeat the purpose. Merge small, merge often.
  7. Direct Commits to main — Bypassing PRs for “emergency fixes” creates inconsistency. Use the same process for everything.

Quick Recap Checklist

  • main branch is always deployable and represents production
  • Every change starts as a feature branch from main
  • Pull requests require at least one review before merge
  • CI pipeline runs tests, linting, and type checks on every PR
  • Merged PRs trigger automatic deployment to production
  • Branch protection rules prevent direct pushes to main
  • Feature flags control feature visibility independent of deployment
  • PRs are kept small and focused for effective code review
  • Failed deployments trigger automatic rollback
  • DORA metrics are tracked and reviewed regularly

Interview Questions

1. What is the key difference between GitHub Flow and Git Flow?

GitHub Flow has one permanent branch (main) while Git Flow has two (main and develop) plus temporary release and hotfix branches. GitHub Flow assumes every merge to main is deployable, making it ideal for continuous deployment. Git Flow assumes releases are scheduled events requiring a stabilization period on a release branch.

In practice, GitHub Flow is simpler and faster but requires stronger automated testing and feature flag infrastructure to manage risk.

2. How do you handle a production bug in GitHub Flow?

Create a hotfix branch from main, fix the bug, open a PR, get it reviewed and merged — the same process as any other change. The speed comes from the small scope of the fix, not from bypassing the process.

If the fix needs to go out immediately, use a feature flag to disable the broken feature while the fix goes through the normal pipeline. This is faster than waiting for a merge and deployment cycle.

3. Why are feature flags important in GitHub Flow?

Feature flags decouple deployment from release. You can merge code to main and deploy it to production without exposing the feature to users. This allows you to deploy frequently while controlling when features become visible.

They also enable progressive rollout — enabling a feature for 10% of users, then 50%, then 100% — and instant rollback by toggling the flag off without deploying new code.

4. What happens when two feature branches modify the same file in GitHub Flow?

The first PR to merge wins. The second PR gets merge conflicts that need resolving before it can go in.

Fix it by rebasing the second PR on the latest main, resolving conflicts locally, then pushing. CI reruns on the rebased branch. If the conflict is messy, talk to whoever has the first PR — sometimes coordinate to let one merge first.

Prevent this by using CODEOWNERS for contested directories, announcing big changes in team channels, and splitting features into smaller PRs that touch different files.

5. How does GitHub Flow handle long-running feature branches?

It doesn't — not well, anyway. Branches older than a week or two start collecting merge conflicts and drifting from main. This is an anti-pattern.

What helps:

  • Break large features into smaller chunks behind feature flags
  • Rebase on main every day
  • Use stacked PRs — split a feature into dependent PRs that merge one after another
  • Set team norms around branch age and flag stale branches after a few days
6. What is the minimum CI/CD setup for GitHub Flow to work safely?

A GitHub Flow CI pipeline needs:

  • Automated tests — unit, integration, or E2E that run on every PR
  • Linting and type checking — catch syntax errors and type mismatches before merge
  • Build verification — code compiles and packages correctly
  • Required status checks — configured on the main branch so PRs can't bypass them

Without these, every merge to main is a roll of the dice. Teams without automated testing should stick with Git Flow and longer release cycles.

7. How do you rollback a bad deployment in GitHub Flow?

Three options, in order of preference:

  • Feature flag rollback — Toggle off the flag for the broken feature. Instant. No redeployment. Requires feature flag infrastructure to be in place first.
  • Revert the merge commit — Run git revert -m 1 <merge-commit>, push, and let CI/CD redeploy the previous version.
  • Point-in-time restore — Pull a known-good container image from your registry. Last resort since it may undo several unrelated changes at once.

Whatever approach you use, document it and test it before you need it.

8. What is the difference between squash merge and merge commit in GitHub Flow?

Squash merge: All the commits in your PR collapse into one commit on main. The PR title becomes the commit message. Clean, linear history — easy to read and bisect.

Merge commit: Every commit from your feature branch gets preserved as a separate commit on main. Messier log, but you can see the full history of how the feature developed.

Rebase and merge: Your feature branch commits replay on top of main. Clean history too, but you lose the PR as an atomic unit.

Most teams use squash merge. Pick one and enforce it in repository settings.

9. When would you add a staging environment to GitHub Flow?

Almost never. Adding a staging branch means you've basically built a half-baked Git Flow, and that defeats the entire point of GitHub Flow.

There are a few edge cases where it might make sense:

  • Regulatory compliance — Change management requirements that mandate a pre-production approval step
  • Manual QA gates — Testing that genuinely cannot be automated, like accessibility audits or penetration tests
  • Blue-green setups — Where "staging" is actually your green environment and both are production-equivalent

Most teams should skip the staging branch and invest in better automated testing and feature flags instead.

10. How does GitHub Flow support zero-downtime deployments?

Several mechanisms work together:

  • Feature flags — Deploy new code but hide the feature until you're ready
  • Blue-green deployments — Route a small percentage of traffic to the new version (canary) before switching over fully
  • Rolling updates — Kubernetes-style restarts with health checks between batches
  • Backward-compatible migrations — Add columns, never remove them; the old code keeps working while the new code deploys

The underlying rule: main must always be in a deployable state. If a migration needs downtime, design it to be backward-compatible during the transition.

11. What team structure works best with GitHub Flow?

GitHub Flow fits best with:

  • Small to medium teams — 5 to 20 developers; communication overhead stays manageable
  • Cross-functional teams — Each team can take something from design to deployment without handoffs
  • Flat hierarchies — No approval layers between developer and production merge
  • Strong code review culture — PR reviews are the only quality gate, so they actually have to happen

Once you get above 30 developers, code review turns into a bottleneck. You can work around it with CODEOWNERS and specialized review teams, or just move to trunk-based development with feature flags.

12. What is the purpose of CODEOWNERS file in GitHub Flow, and how do you set it up?

The CODEOWNERS file defines who owns specific directories or file patterns in the repository. When a PR modifies those files, the designated owners are automatically requested for review. This ensures domain experts always review changes to their areas.

Example CODEOWNERS:

# Teams
/src/api/ @api-team
/src/ui/ @frontend-team
/docs/ @docs-team

Individuals

*.sql @senior-dba /security/** @security-team

Place the file at .github/CODEOWNERS in the repository root.

13. How do you handle stale branches in GitHub Flow?

Stale branches (older than 1-2 weeks) indicate work that isn't progressing. Handle them by: setting up branch protection rules to alert on old branches, using automation to close branches with no activity for X days after notification, deleting merged branches automatically via settings or scripts, and encouraging developers to push to remote daily so branches stay current with main.

14. What happens when main is broken and needs immediate fix in GitHub Flow?

The key principle: main should never be broken. If it happens despite best practices: immediately disable the broken feature via feature flag (if infrastructure exists), revert the merge commit with git revert -m 1 <sha>, investigate why CI didn't catch the issue, fix the root cause, test thoroughly, and merge a proper fix. Never commit directly to main to "fix it fast" — this bypasses CI and can make things worse.

15. How does GitHub Flow integrate with GitHub Actions for CI/CD?

GitHub Actions runs on every push and PR. A typical workflow triggers on push: branches: [main] and pull_request: branches: [main]. Jobs run tests, linting, and build verification. On push to main, a deployment job runs after tests pass. Branch protection settings require all status checks to pass before merging.

16. What is the difference between GitHub Flow and trunk-based development at scale?

GitHub Flow allows feature branches to live for days or weeks; trunk-based development restricts them to hours. At scale (50+ developers), GitHub Flow's longer branch lifetimes cause merge conflict accumulation and integration pain. Trunk-based development requires feature flag infrastructure and very fast CI, but eliminates integration debt entirely. The transition requires cultural changes: developers must be comfortable merging incomplete features behind flags.

17. How do you manage environment-specific configurations in GitHub Flow?

Environment configs should be injected at runtime, not stored in code. Use: environment variables set in CI/CD platform settings, config maps/secrets in Kubernetes, or feature flag services for feature-toggled behavior. Never commit API keys or environment-specific URLs to the repository. The same commit should be deployable to any environment with the right configuration injected.

18. What security considerations are specific to GitHub Flow?

Key security measures: require branch protection with required status checks and PR reviews, enable secret scanning to prevent credential leaks, use CODEOWNERS to ensure security-relevant code is reviewed by experts, require signed commits for production branches, enable dependency review to catch vulnerable dependencies before merge, and use GitHub's audit log to track who merged what and when.

19. How do you handle rollback when feature flags are not available?

Without feature flags, rollback options are limited: revert the merge commit (creates a new commit that undoes changes), point-in-time restore from a previous container image (may lose other legitimate changes), or push a fix forward (may take longer). The best mitigation is investing in feature flag infrastructure before you need it. Short branch lifetimes also reduce rollback blast radius.

20. What happens when CI passes but production breaks — a common scenario in GitHub Flow?

Tests don't cover everything. When this happens: immediately toggle off any related feature flags, assess the blast radius (what users are affected), decide between revert or forward fix, add missing test cases to prevent recurrence, conduct blameless postmortem, and consider canary deployments that expose new code to limited users first. The goal is to catch issues before full production exposure through better testing and staged rollouts.

Further Reading

Conclusion

GitHub Flow strips branching to its essence — main branch, short-lived feature branches, and PRs. Its simplicity is its strength: every merge deploys, every deploy is fast, and there’s no ceremony between code and production.

Category

Related Posts

Choosing a Git Team Workflow: Decision Framework

Decision framework for selecting the right Git branching strategy based on team size, release cadence, and project type.

#git #version-control #branching-strategy

Git Flow: The Original Branching Strategy Explained

Master the Git Flow branching model with master, develop, feature, release, and hotfix branches. Learn when to use it, common pitfalls, and production best practices.

#git #version-control #branching-strategy

Git Release Branching and Hotfixes: Managing Versions in Production

Master release branching and hotfix strategies in Git. Learn version branches, emergency fixes, backporting, and how to manage multiple production versions safely.

#git #version-control #release-management