A Git workflow that scales from 1 to 50 engineers
The branching strategy and review rituals we've used to ship 15,000+ projects across distributed teams.
Git workflows are like coffee orders — everyone has one, and most of them are wrong.
Over 11 years and 15,000+ projects, we've converged on a workflow that works whether the team is two engineers in the same room or fifty across three continents. Here's the whole thing.
The branching model
We use a modified trunk-based approach. There are exactly three kinds of branches:
main— always deployable. Protected. Nothing lands here without review.feature/*— short-lived. Created from main, merged back within 2–5 days max.hotfix/*— for emergencies. Branched from main, merged with priority.
That's it. No develop, no release, no long-lived branches that drift for weeks and become merge nightmares.
Pull request rules
Every PR must:
- Touch fewer than 400 lines (ideal: 100–200).
- Have a clear title describing the what and why.
- Include a screenshot if there's any UI change.
- Pass CI before review is requested.
The 400-line rule is non-negotiable. Anything larger gets split. A 1,200-line PR is unreviewable — reviewers either rubber-stamp it or burn out trying.
The review ritual
Two reviewers. Always.
One reviewer for correctness, one for design.
The first reviewer checks: does it work? Does it have tests? Is the logic sound?
The second reviewer checks: does it fit the rest of the codebase? Will the next engineer who reads this understand it?
This sounds slow. It isn't. Most PRs ship in under 4 hours total review time. The structure prevents the "lone reviewer becomes bottleneck" problem.
Commit messages
We use conventional commits. They're searchable, they're parseable, they auto-generate changelogs.
feat(auth): add OAuth login with Google
fix(cart): prevent duplicate items on rapid clicks
docs(api): update rate-limit thresholds
chore: bump axios to 1.7.0
It takes 5 seconds longer per commit and saves hours later when bisecting a regression.
The deploy cadence
main → staging on every push. Staging → production on a manual gate.
The manual gate is important. Continuous deployment is great until it isn't — production needs an adult in the room to roll back when needed.
What we don't do
We don't squash everything. We squash feature branches on merge to main (one commit per feature), but inside the branch, granular commits are encouraged. They tell the story of how the feature came together.
We don't force-push to shared branches. Ever. Even on hotfixes.
We don't merge our own PRs. Ever. Even for typo fixes. The two-reviewer rule has no exceptions because the moment it has exceptions, it has many exceptions.
TL;DR
- Three branch types: main, feature, hotfix.
- PRs under 400 lines.
- Two reviewers always.
- Conventional commits.
- Manual production gate.
Boring. Predictable. Ships.