All posts
·14 min read· scaling· team· workflows· codeowners· merge-queue

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:

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:

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:

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:

  1. PR is approved and tests pass.
  2. Author clicks "Merge when ready."
  3. The PR enters a queue. The queue creates a temporary branch that contains the latest main plus this PR, and runs CI on that.
  4. 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:

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:

  1. Author writes a short RFC (1–3 pages) describing the change and trade-offs.
  2. RFC is reviewed by the relevant senior engineers for that area, often async over a week.
  3. 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:

Build and CI sharding

At 50+ engineers, CI on every PR can take 30 minutes if you run everything. Most large teams shard:

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

ScaleBranch protectionCODEOWNERSMerge queueReview tiersRFC process
Solo (1)OptionalNoNoN/ANo
Small (2–10)Require PR + 1 approvalNoNoFlatNo
Medium (10–50)Above + Code Owners reviewYesYesFlat or 2-tierSometimes
Large (50+)Above + signed, linearYes, deepYes2–3 tiersYes

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

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.