Scaling a Git workflow: solo, small, medium, and large teams
The Git workflow that worked at 5 engineers breaks at 30, and the one that works at 30 is overkill at 5. Here is what changes at each team-size boundary, with concrete examples.
A friend runs a small startup. Two years ago they had 5 engineers and shipped twice a day. Their workflow fit on a sticky note: branch, PR, merge, deploy. Last year they grew to 32 engineers. The same workflow started failing. main was red half the day. Two teams kept stepping on each other's changes. People started "saving up" their PRs to merge in batches, which made the conflicts worse.
The workflow did not break because they were doing it wrong. It broke because the right workflow at 5 engineers is not the right workflow at 32. The mechanics change at predictable team-size boundaries.
This post walks through what changes at each boundary: solo (1), small (2–10), medium (10–50), and large (50+). Each section has concrete configuration — branch protection rules, CODEOWNERS examples, merge queue settings — that you can copy.
What doesn't change
Before the differences, the constants. A few things stay true at every team size:
- Small, focused commits beat big sprawling ones. A commit should do one thing and explain why.
- Branches should be short-lived where possible. Long branches mean painful merges. This holds at 1 engineer and at 1000.
mainshould be in a known-good state most of the time. What "known-good" means changes (tested? deployed? released?), but the principle is constant.- Commit messages should explain why, not just what. Future you, six months from now, will read them.
The Linux kernel project's contribution docs — refined over thirty years across thousands of contributors — emphasize all four. They scale from solo work to the largest distributed software project on earth.
With that constant in mind, here is what changes at each boundary.
Solo (1 engineer)
You are the only one touching the repo. There are no merge conflicts with other people. No code review. No "who broke main."
What matters for you:
Commit hygiene. You will read your own commit history six months from now and need to understand what you did. Small commits, real messages.
Tags as savepoints. When you ship a version (even just "the version I gave to my first customer"), tag it.
git tag -a v0.1.0 -m "First customer demo build"
git push --tags
If something breaks two months later, you can check out the tag and confirm what was there.
Branches as work-in-progress. You do not need PRs, but branches still help. Use them when you start something you might abandon.
git checkout -b try-new-search-algo
# ... explore for an hour ...
# Doesn't work? Just don't merge.
git checkout main
A simple backup strategy. Push to a remote (GitHub, GitLab, your own server) regularly. The remote is your backup.
What you do not need: branch protection rules, CODEOWNERS, merge queues, signed commits enforcement. These solve coordination problems. You have no coordination problem.
Small (2–10 engineers)
A second engineer changes everything. Now there is review. Now there are conflicts. But the overhead should stay low.
What to add at this scale:
- Pull requests for every change. No direct pushes to
main. Even one-line fixes go through a PR. - Lightweight review. A single approval is usually enough. Reviews should happen within hours.
- Branch protection on
main. Require PR review. Require CI to pass. That is it. - A shared
CONTRIBUTING.md. One page. What the branch naming convention is, what the commit message convention is, who deploys.
A small-team branch protection config on GitHub looks like:
- Require a pull request before merging
- Require approvals: 1
- Require status checks to pass before merging
- Require branches to be up to date before merging
- Do not allow bypassing the above settings
GitHub's branch protection docs cover the full options. Most are not needed yet.
What you do not need yet:
- CODEOWNERS. With 10 people, you all know who owns what. A file does not add information.
- Merge queue. With 10 people, two PRs merging at the same time is rare. When it happens, fixing it manually costs minutes, not hours.
- Signed commits. Nice to have, not load-bearing yet.
Common pitfall at this scale: the workflow document does not exist or is out of date. Every new hire learns by asking. After 6 months, no two people describe the workflow the same way. Write it down. Update it when it changes.
Medium (10–50 engineers)
Two things break at this scale.
First, ownership becomes ambiguous. With 30 engineers across 6 product areas, the person reviewing a payments PR might know nothing about payments. Reviews become rubber stamps. Bugs ship because the reviewer was not the right reviewer.
Second, PRs collide on main regularly. Two PRs each pass their own CI, get merged minutes apart, and together break main because they touched the same module from different angles. This happens weekly. Sometimes daily. Engineers stop trusting main.
CODEOWNERS
A CODEOWNERS file maps paths in the repo to teams or individuals. When a PR touches a path, the listed owner is automatically requested for review. GitHub, GitLab, and Bitbucket all support the format.
# CODEOWNERS — example for a 30-engineer SaaS
# Default owners — review anything not covered below
* @org/platform-leads
# Payments
/services/payments/ @org/payments-team
/services/billing/ @org/payments-team
# Customer-facing web
/apps/web/ @org/web-team
/apps/web/components/checkout/ @org/payments-team @org/web-team
# Infrastructure
/infra/ @org/infra-team
/.github/workflows/ @org/infra-team
Look at any large open-source project's CODEOWNERS for a concrete model — for example, Kubernetes' OWNERS files (different format, same idea) or Rust's CODEOWNERS.
Pair CODEOWNERS with a branch protection rule: "Require review from Code Owners." Now a payments PR cannot merge without payments-team approval. Bad reviews become hard to give.
Merge queue
GitHub's merge queue (generally available since July 2023) solves the "two PRs collide on main" problem.
How it works:
- PR is approved and tests pass.
- Author clicks "Merge when ready."
- The PR enters a queue. The queue creates a temporary branch that contains the latest
mainplus this PR, and runs CI on that. - If CI passes, the PR merges. If it fails, the PR drops out of the queue and the author is notified.
This guarantees that no PR merges to main without being tested against the actual state of main at merge time. At 30 engineers, this is the difference between main being green most of the time and red most of the time.
GitLab has a similar feature called merge trains.
Why "merge commits clutter history" stops mattering
At small scale, people argue about whether to use merge commits or rebase-and-merge for a tidy history. At medium scale, the argument fades. The history will not be readable by hand at this size anyway — there are too many merges per day. What you actually use to navigate history is tooling (git log --grep, git bisect, search by author, search by file). Whether the graph is "tidy" matters less than whether the commit messages and PR titles describe what each change did.
Pick a convention (most medium teams use squash-and-merge) and stop arguing about it.
Large (50+ engineers)
At 50+, the workflow stops being one team's process and starts being a system. New patterns appear.
Distributed maintainer model
The Linux kernel famously has thousands of contributors but a small number of maintainers per subsystem. Patches flow up through tiered review: contributor → subsystem maintainer → top-level maintainer → main repo. Read the kernel's submitting-patches.rst for the full process — it is the reference document for large-scale collaboration on a single codebase.
Most companies do not need formal maintainer roles, but they end up with informal versions of the same thing:
- A "platform team" reviews changes to shared infrastructure.
- Each product area has 2–3 senior engineers who must approve significant changes in their area.
- A small number of senior engineers can override CODEOWNERS in an emergency.
The structure is not hierarchy for hierarchy's sake. It is recognition that, at 100+ engineers, no one person can review everything competently, and trust has to be encoded in tooling.
Tier-2 reviewers and RFCs
At this scale, big architectural changes need more than a PR. A typical pattern:
- Author writes a short RFC (1–3 pages) describing the change and trade-offs.
- RFC is reviewed by the relevant senior engineers for that area, often async over a week.
- Once accepted, implementation happens in normal PRs that reference the RFC.
Rust's RFC process is a public example. Many large companies have internal versions.
Branch protection at large scale
Beyond what medium-size teams use, add:
- Required signed commits (if you have a verification system set up).
- Required linear history (squash or rebase only — no merge commits) if your tooling depends on it.
- Restrict who can push to release branches to a small set of release managers.
- Block force-pushes everywhere except on personal branches.
Build and CI sharding
At 50+ engineers, CI on every PR can take 30 minutes if you run everything. Most large teams shard:
- Affected-only tests for PRs that touch a small area.
- Full suite runs on
mainafter merge, and nightly. - Pre-merge smoke tests that catch obvious breakage in under 5 minutes.
The exact tooling differs (Bazel, Nx, custom). The principle is the same: CI cost should scale with what changed, not with the size of the codebase.
What doesn't change at scale
The constants from the top of this post still apply. A 200-engineer team writes the same kind of focused commits as a 5-engineer team. The discipline does not get replaced; it gets reinforced by tooling.
What breaks at each boundary
Three boundaries, three failure modes that signal you have crossed.
1 → 2 engineers. The failure mode is "we both edited the same file." Both engineers were used to committing to main directly. Now they conflict. The fix is to introduce branches and PRs, even for tiny changes. New habit: never commit to main without a PR.
10 → 15 engineers. The failure mode is "the wrong person reviewed this PR." A frontend engineer reviewed a payments change because they were available, not because they knew the system. A bug ships. The fix is CODEOWNERS plus required Code Owner approval. New habit: routing is enforced, not informal.
40 → 60 engineers. The failure mode is "main is red every afternoon." Two PRs merged minutes apart and broke each other. Engineers stop trusting main. The fix is a merge queue. New habit: PRs are tested against the actual latest main, not against a snapshot.
80 → 150 engineers. The failure mode is "no one knows the right person to review this." Even with CODEOWNERS, paths overlap and the "right" reviewer is ambiguous. The fix is tiered review — a primary reviewer who can approve, a secondary reviewer who can override in their area. New habit: review tiers are explicit, not informal seniority.
Recognising these failure modes early is the difference between a smooth transition and a multi-month productivity dip.
A real CODEOWNERS file evolves with the team
The CODEOWNERS file is not static. Here is how one looked at three sizes for the same product team.
At 8 engineers — single line:
* @org/everyone
Effectively no routing. With 8 people, the file is paperwork.
At 25 engineers — first split by area:
* @org/eng-leads
/apps/web/ @org/web
/services/api/ @org/backend
/infra/ @org/sre
/.github/ @org/sre
Five lines, three teams. Code Owner approval becomes a required check.
At 80 engineers — fine-grained:
* @org/platform-leads
/services/auth/ @org/auth-team
/services/billing/ @org/billing-team
/services/billing/integrations/stripe/ @org/billing-team @org/payments-platform
/services/search/ @org/search-team
/apps/web/components/checkout/ @org/web @org/billing-team
/libs/shared-types/ @org/platform-leads
/infra/k8s/ @org/sre
/infra/terraform/ @org/sre @org/security
/.github/workflows/ @org/sre
SECURITY.md @org/security
The file grew with the team. Each change to it was driven by a specific incident — "no security review caught this" → add @org/security to the relevant paths. Letting the file evolve from incidents is more reliable than designing it upfront.
A scaling table
| Scale | Branch protection | CODEOWNERS | Merge queue | Review tiers | RFC process |
|---|---|---|---|---|---|
| Solo (1) | Optional | No | No | N/A | No |
| Small (2–10) | Require PR + 1 approval | No | No | Flat | No |
| Medium (10–50) | Above + Code Owners review | Yes | Yes | Flat or 2-tier | Sometimes |
| Large (50+) | Above + signed, linear | Yes, deep | Yes | 2–3 tiers | Yes |
Most teams under-tool when they cross a boundary. They keep the small-team workflow at 25 engineers, hit the friction, and only add tooling after a few near-disasters. If you are within 50% of a boundary, start preparing for the next set of practices now.
Common myths
Myth 1: "CODEOWNERS slows everything down." Wrong, usually. CODEOWNERS routes the right reviewer to each PR. Without it, PRs sit unreviewed or get rubber-stamped by the wrong person. At medium scale, that is slower than well-tuned CODEOWNERS, not faster.
Myth 2: "Merge queues are only for huge teams." Wrong. The breakeven point for a merge queue is roughly when you have one "two PRs broke main together" incident a week. For many teams, that is 15–20 engineers, not 100.
Myth 3: "Large teams need to abandon trunk-based development." Wrong. Google, Meta, and Stripe all run TBD at thousands of engineers. What scales is the supporting tooling (merge queues, CODEOWNERS, sharded CI). The workflow itself stays.
What to read next
- Trunk-based development: when it wins and when it doesn't — the model behind most modern medium-and-large workflows.
- Monorepo Git techniques — what changes when 50+ engineers share one giant repo.
- Multi-repo coordination — the alternative shape.
- Choosing a Git workflow: a decision guide for real teams — the series overview.
For a hands-on warm-up to commit hygiene that scales with you, the Perfect Commit lesson below opens a live terminal in two minutes.