Git Branching Strategy: main, develop, and When to Branch
A branching strategy answers three operational questions: which branch matches production, where unfinished work integrates, and how an urgent fix reaches users without disappearing from the next release. If those answers are implicit, teams merge the same change twice or ship from the wrong branch. Review the Git commands cheatsheet first if add, commit, fetch, rebase, and push are unfamiliar.
This guide uses a Git Flow–style model with long-lived main and develop branches and short-lived feature, release, and hotfix branches. Follow one payment-timeout fix through the workflow, including conflict recovery; choose trunk-based development instead when frequent small releases make long-lived integration branches unnecessary. CI/CD with GitHub Actions shows how branch events and required checks enforce the chosen policy.
Main Branches: main and develop
Two branches outlive every sprint. main (sometimes still called master) holds production-ready code. Every commit on main should be deployable — if it is not, it does not belong there. Most teams protect main: no direct pushes, merges only through reviewed pull requests, and CI must pass before merge.
develop is the integration branch where completed features land before a release. Developers merge feature branches into develop, run integration tests, and only cut a release branch when the codebase is stable enough to ship. Develop is not production — it is the staging ground where the next version takes shape.
Quick reference
- main: production code — stable, tagged releases, protected from direct commits.
- develop: integration branch — features merge here first.
- Never deploy develop directly to production in Git Flow.
- Use branch protection rules on main (and often develop) in GitHub/GitLab.
- Tag releases on main (v2.1.0) for traceability and rollbacks.
- Some teams use only main (trunk-based) — this article covers the two-branch model.
Remember this
main is production truth; develop is where features integrate before a release cut.
Supporting Branches: feature, release, hotfix
Supporting branches are short-lived — created for one job, merged, then deleted. feature/ branches (e.g. feature/login) hold new work. Branch from develop, merge back to develop when the pull request is approved. Never merge an unfinished feature directly to main.
release/ branches (e.g. release/v2.0) prepare a version for production. Only bug fixes and release chores go here — no new features. When ready, merge release into both main and develop, tag main, and delete the release branch. hotfix/ branches fix critical production bugs: branch from main, patch, merge to both main and develop so the fix is not lost on the next release.
Quick reference
- feature/* — from develop → merge develop → delete after merge.
- release/* — from develop → merge main + develop → tag and delete.
- hotfix/* — from main → merge main + develop → deploy immediately.
- Keep supporting branches days or weeks, not months.
- One feature per branch — easier review and safer rollback.
- Cherry-pick hotfixes to develop if branches have diverged.
Remember this
Feature branches build, release branches stabilize, hotfix branches patch production — all short-lived.
How Branches Flow Together
The lifecycle follows a predictable path. Developers create feature branches from develop and open pull requests. When features pass review and CI, they merge into develop. Integration tests run on develop continuously.
When it is time to ship, create a release branch from develop. Stabilize it with minor fixes, then merge to main (deploy to production) and back to develop (so fixes are not lost). If production breaks, branch a hotfix from main, patch, merge to main and develop, deploy, and delete the hotfix branch. This loop keeps main always deployable while develop moves forward.
Quick reference
- Multiple feature branches can merge to develop in parallel.
- Only one active release branch per version is typical.
- Merge release → main first, deploy, then merge release → develop.
- Hotfixes skip the normal feature → develop → release path for speed.
- Rebase or merge develop into long-running feature branches weekly.
- Delete merged branches — stale branches confuse everyone.
Remember this
Features flow up through develop; releases and hotfixes are the only paths to main.
Branching Best Practices
A strategy on paper fails without discipline. Create a branch for every feature — no committing directly to develop. Keep commits small and focused so reviewers can understand each change. Always open a pull request before merging; even solo developers benefit from CI running on the PR.
Delete branches after merge — Git hosting platforms offer a button for this. Protect main (and usually develop) with required reviews and status checks. Rebase or merge from develop regularly on long feature branches to avoid painful merge conflicts at the end. Use consistent naming so anyone can tell what a branch does from its name alone.
Quick reference
- One branch per feature or bug — not one branch per developer.
- Small commits with clear messages (imperative mood: "Add auth middleware").
- Pull requests trigger CI — merge only when green.
- Delete merged branches immediately.
- Protect main: require PR, require passing checks, no force-push.
- Rebase or merge develop into feature branches at least weekly.
- Use conventional branch prefixes (feature/, bugfix/, hotfix/).
Remember this
Small branches, reviewed PRs, protected main, and deleted merged branches keep the workflow fast.
Branch Naming Conventions
Consistent names make branches scannable in lists and automation-friendly in CI. Prefix the branch type, then a short kebab-case description. feature/user-auth for new functionality, bugfix/login-error for non-urgent fixes on develop, hotfix/api-timeout for production emergencies, release/v2.1.0 for release preparation.
Use chore/ for tooling and dependency updates that are not user-facing features. Use docs/ for documentation-only changes. Avoid personal names (john-fix) or vague names (fix-stuff) — future you will not remember what they mean.
Quick reference
- feature/ — new functionality (feature/oauth-login).
- bugfix/ — fix on develop, not production-critical.
- hotfix/ — urgent production patch (hotfix/payment-null).
- release/ — version prep (release/v2.1.0).
- chore/ — deps, config, tooling (chore/upgrade-eslint).
- docs/ — documentation only (docs/api-readme).
Remember this
Prefix + kebab-case description — feature/user-auth, not johns-branch or fix2.
Key takeaway
Use the smallest branch model that matches how you release. A protected main plus short feature branches is enough for many teams; add develop, release, and hotfix branches when versioned stabilization work needs separate lanes.
Practice (25 min): Create main and develop in a scratch repo and carry the payment-timeout fix through a feature branch; the expected graph has the fix integrated once with no work left uncommitted. Intentionally make conflicting edits on develop, then recover by rebasing the feature, resolving the conflict, and running a verification command before merge. Pass when git log --graph shows the intended history and you can state the exact merge targets for both a release and a hotfix—or explain why your team should omit those branches.
Related Articles
Explore this topic