What Is the Feature Branch Workflow?
The workflow has three non-negotiable rules:
- main is always deployable — every commit on main could theoretically be deployed to production right now
- No direct commits to main — all changes go through a branch and a pull request
- PRs require code review and CI checks before merging
Everything else — how you name branches, how many reviewers a PR needs, squash vs merge commits — is team convention built on top of these three rules.
Core Rules Explained
When main is always deployable, you can respond to a production outage at any time. You don't need to find a "safe" point in history to roll back to — main IS the safe point. This discipline also catches integration problems early: when your feature branch is merged, any breakage is immediately visible and attributed to that specific merge.
Step-by-Step Workflow
# Step 1: Start from the latest main
git switch main
git pull origin main
# Step 2: Create a descriptive feature branch
git switch -c feature/user-profile-page
# Step 3: Work on the feature — commit early and often
git add src/pages/UserProfile.js
git commit -m "feat(profile): add user profile page skeleton"
git add src/pages/UserProfile.js src/api/users.js
git commit -m "feat(profile): connect profile page to users API"
git add src/pages/UserProfile.css
git commit -m "style(profile): add responsive layout for profile page"
# Step 4: Keep your branch up to date with main to avoid big conflicts
git fetch origin
git rebase origin/main # or: git merge origin/main
# Step 5: Push your branch to the remote
git push -u origin feature/user-profile-page
# -u sets the upstream — future pushes only need: git push
# Step 6: Open a Pull Request on GitHub/GitLab
# - Write a clear PR title and description
# - Link to the related issue (#42)
# - Request reviewers
# CI/CD automatically runs tests on your branch
# Step 7: Address review comments
git add src/pages/UserProfile.js
git commit -m "fix(profile): address code review feedback"
git push
# Step 8: After PR is approved and CI passes — merge!
# (Done via GitHub/GitLab UI — squash merge or regular merge)
# Step 9: Clean up — delete the feature branch
git switch main
git pull origin main
git branch -d feature/user-profile-page
Branch Naming Conventions
Consistent branch naming helps the whole team understand what's being worked on at a glance:
| Prefix | Used for | Example |
|---|---|---|
feature/ | New features | feature/user-authentication |
bugfix/ | Bug fixes in development | bugfix/login-null-pointer |
hotfix/ | Urgent production fixes | hotfix/payment-crash-v2.1 |
chore/ | Maintenance, config, tooling | chore/upgrade-dependencies |
docs/ | Documentation only changes | docs/update-api-readme |
refactor/ | Code refactoring | refactor/extract-auth-service |
Many teams include the issue/ticket number: feature/GH-42-user-profile or bugfix/JIRA-1234-fix-payment. This creates an instant link between the branch and the requirements/bug report, and GitHub automatically links commits to issues when the branch name contains the issue number.
The Pull Request Process
A good pull request makes the reviewer's job easy:
- Title: short, imperative mood — "Add user profile page" (not "Added" or "Adding")
- Description: what changed, why, how to test it, screenshots for UI changes
- Link to issue: "Closes #42" in the description auto-closes the issue on merge
- Small scope: a PR should do one thing — a 500-line PR is hard to review; ten 50-line PRs are much better
- CI/CD must pass: never merge a red PR
Trunk-Based Development — The Even Simpler Version
Trunk-based development is a variation where feature branches are extremely short-lived (less than a day) or developers commit directly to main using feature flags to hide incomplete features from users:
// Feature flag pattern — incomplete feature is in main but hidden
if (featureFlags.isEnabled('new-dashboard')) {
return <NewDashboard />;
}
return <OldDashboard />;
Workflow Comparison
| Aspect | Git Flow | Feature Branch (GitHub Flow) | Trunk-Based |
|---|---|---|---|
| Permanent branches | main + develop | main only | main only |
| Branch lifespan | Days to weeks | Hours to days | Hours or none |
| Release cadence | Versioned releases | Continuous | Continuous (multiple/day) |
| Complexity | High | Medium | Low |
| Best for | Versioned software | Most web teams | Elite DevOps teams |
📋 Summary
- The three non-negotiable rules: main is always deployable, no direct commits to main, PRs require review + CI.
- Workflow: pull main → create branch → work → push → open PR → review → merge → delete branch.
- Use git push -u origin branch-name on first push to set upstream tracking.
- Branch naming: use prefixes like
feature/,bugfix/,hotfix/,chore/,docs/. - Good PRs: clear title, description, linked issue, small scope, passing CI.
- Trunk-based development: same idea but with very short-lived branches + feature flags.
- Feature Branch Workflow is the right default for most teams in 2026.
FAQ
Squash merge: all commits on the feature branch are squashed into one clean commit on main. Best for keeping main history linear and readable — especially when individual commits on the branch are messy WIP commits. Merge commit: preserves the full branch history with a merge commit. Useful when commits are well-crafted and you want to keep the context. Rebase: replays branch commits on top of main with no merge commit — cleanest history but rewrites commit hashes. Most teams default to squash merge for PRs, using the PR title as the commit message.
Two options: merge (git merge origin/main) — adds a merge commit to your branch preserving its history; or rebase (git rebase origin/main) — replays your commits on top of the latest main, resulting in a linear history. Rebase is generally preferred for feature branches to keep history clean, but it rewrites commit hashes which requires a force-push if you've already pushed the branch. For solo branches (only you), rebasing is usually the better choice.
Long-lived branches are a sign that the feature is too large. Consider breaking it into smaller pieces (each merged via a separate PR). If the feature must be long-lived, rebase onto main frequently (daily) to catch conflicts early while they're still small. Some teams use feature flags to merge incomplete features: the code is in main but hidden behind a flag, and the flag is enabled when the feature is done.
A draft PR on GitHub signals "I'm still working on this, not ready for review yet." It's useful for: getting early feedback on your approach, running CI on a work-in-progress, making your work visible to the team without triggering a review request. Convert the draft to a regular PR when it's ready for review by clicking "Ready for review". Draft PRs are a great habit — they increase team visibility and enable collaboration earlier in the development process.