One branching strategy for many teams Jump to heading

In separate repositories, each team picks its own branching model and nobody else notices. In a monorepo, everyone shares one main, one set of branch protections and one merge queue, so a choice that suits one team is imposed on all of them. A team that deploys hourly cannot share a GitFlow develop branch with a team that releases quarterly; a team that wants linear history collides with one that relies on merge commits. The way through is to separate what must be shared from what can vary. The trunk, the merge rules and the quality bar must be common. Release cadence, release branches and deployment can vary by team, attached to paths rather than to the whole repository. This page lays out that split, within monorepo branch topology.

When to use this approach Jump to heading

  • Several teams share a monorepo and disagree about the branching model.
  • Some teams deploy continuously and others release on a schedule.
  • Repository-wide rules — branch protections, merge methods — were chosen for one team and frustrate others.
  • You are consolidating repositories into a monorepo, as in merging two repositories while keeping history.

Step 1 — Decide what must be shared Jump to heading

Some things can only exist once in a repository. List them and agree them across teams first, because every other decision builds on them.

Shared foundations, team-specific layersThe trunk, the merge method and the merge queue exist once and are agreed by all teams. Required checks and protection on main form a shared quality bar. Ownership, release branches, tags and deployment attach to each team's paths and can differ between teams.agree the bottom, let teams own the topTeam-specificrelease branches, tags, deploy cadenceTeam-owned pathsCODEOWNERS, affected CIShared quality barrequired checks on mainShared trunkmain, merge method, merge queuedisagreements about the bottom two layers must be settled; the top two need not be
# Shared (decided once, for everyone)
- One trunk: `main`. All work merges there through pull requests.
- One merge method: squash. One merge queue on `main`.
- Required on main: build and tests of affected projects, lint, signature check.
- Short-lived branches; long-lived branches only with an owner and a sync plan.

Step 2 — Give teams ownership of paths, not branches Jump to heading

In a monorepo, ownership attaches naturally to directories. Code owners for each team’s paths decide reviews; affected-project CI decides which checks run. Teams then control their own area without needing their own branches.

# CODEOWNERS
/services/billing/    @acme/billing
/services/export/     @acme/data-platform
/apps/web/            @acme/web
/libs/money/          @acme/billing @acme/payments-platform
/.github/             @acme/platform

Ownership in monorepos is covered in path-based CODEOWNERS in a monorepo, and per-project CI in detecting affected projects from git diff.

Step 3 — Let release cadence vary by team Jump to heading

Teams that deploy continuously deploy their projects from main after each merge. Teams that release on a schedule cut per-product release branches from main. Both work against the same trunk.

Two teams, one trunk, different cadencesThe web team deploys its app from main after every merge that affects it. The billing team cuts a release branch for its service every two weeks and backports fixes. Both merge their work to the same main through the same queue and rules; only what happens after main differs.Web teamBilling teamwork merges tomainmainrelease uniteach affecting mergerelease/billing-api/X.Ydeploy triggermerge to maintag on release branchfixes after releasenext mergecherry-pick to branchthe trunk is shared; everything after the trunk is the team's choice
# Web: deploy on merge when web is affected
on: { push: { branches: [main], paths: ["apps/web/**", "libs/ui-kit/**"] } }

# Billing: deploy on its own tags only
on: { push: { tags: ["billing-api@*"] } }

Per-product release branches are described in release branches in a monorepo.

Step 4 — Protect shared code with stricter rules Jump to heading

Shared libraries are where one team’s change can break another’s product. Require approval from every consuming team’s owners for library changes, and run all dependents’ tests. Make breaking changes visible early, as in catching cross-package breaking changes.

# Who must approve a change to libs/money? Every owner listed for it.
gh api "repos/$OWNER/$REPO/contents/.github/CODEOWNERS" --jq .content | base64 -d | grep '^/libs/money/'

Step 5 — Write the strategy down in one place Jump to heading

A monorepo strategy document covers the shared rules once and lets each team add a short section for its own cadence. New teams joining the repository read one page instead of discovering rules by breaking them.

# Branching in this repository
## Shared rules (all teams)
…
## Team sections
### Web — continuous deployment from main
### Billing — release/billing-api/X.Y every two weeks; fixes via labelled backports
### Data platform — continuous; export-api canaried before full rollout

Step 6 — Revisit as teams and products change Jump to heading

A strategy chosen when the monorepo had three teams may not fit ten. Review it yearly, or whenever a new team joins, with the question: is any shared rule now blocking a team without protecting anyone?

Is a proposed rule shared or team-specific?If the rule affects main or the merge process, it must be shared and agreed by all teams. If it affects only one team's paths, the team owns it. If it affects shared libraries, it needs agreement from every consuming team.What does the rule govern?main, merges, queueSharedall teams agreeone team's pathsTeam-specificteam decidesshared librariesConsumers agreeall ownersmost disagreements dissolve once a rule is placed in the right category

Validation checklist Jump to heading

Frequently Asked Questions Jump to heading

Can one team keep using GitFlow in a monorepo? Jump to heading

Not with its own develop branch, which would be a second trunk. It can get the same effect with per-product release branches cut from main, which gives a stabilisation phase without a separate integration branch.

How do we handle a team that needs a different merge method? Jump to heading

Merge method is a repository setting, so it must be shared. Usually a team’s real need — preserved per-commit history, say — can be met another way, such as better squash messages or keeping detailed history in the pull request.

Who owns the shared rules? Jump to heading

A small platform or developer-experience group, with changes proposed by pull request and reviewed by representatives of each team.