Skip to content
compiler.dev

CODEOWNERS and Branch Protection Best Practices

CODEOWNERS says who must review which files. Branch protection (or rulesets) says what must be true before code reaches main. Together they are the cheapest quality and security controls you have, if they are set up so that people do not route around them. This guide covers the syntax and the settings that matter.

How CODEOWNERS works

A CODEOWNERS file maps path patterns to users or teams. GitHub looks for it in .github/, the repository root or docs/, in that order, and uses the first one it finds. When a PR changes matching files, GitHub automatically requests review from the listed owners. If branch protection requires code owner review, the PR cannot merge without approval from an owner.

# Default owners for everything
*                       @acme/platform

# Frontend
/web/                   @acme/frontend
*.css                   @acme/frontend @acme/design

# Backend and database
/services/api/          @acme/backend
/db/migrations/         @acme/backend @acme/dba

# CI and security-sensitive files
/.github/               @acme/platform @acme/security
/infra/                 @acme/platform
Dockerfile              @acme/platform

The rules, from GitHub's docs:

  • The last matching pattern wins. Order from general to specific. A file matching two lines takes the owners of the later line only, not both.
  • Patterns follow .gitignore rules, with exceptions: you cannot use ! negation, and [ ] character ranges are not supported.
  • Owners are @username, @org/team-name or an email address linked to an account.
  • Owners must have write access to the repository, and a team must be visible and have write access. Otherwise the line is ignored.
  • A file over 3 MB is not read.
  • Invalid lines are flagged in the file view's error list; check it after edits.

Generate a starting file with the CODEOWNERS generator, then read through it as a reviewer would.

Use teams, not individuals

Individuals leave, go on holiday and become bottlenecks. Assign teams, and let GitHub's team review assignment pick one or more members (round robin or load balance) in the team's settings. For very large teams, enable "Auto assignment" so notifications do not go to everyone.

Keep ownership broad enough that a PR always has someone available, and narrow enough to mean something. A file where every PR needs five approvals teaches people to rubber-stamp.

Protect the paths attackers want

Give a trusted team ownership of:

  • /.github/ and especially /.github/workflows/. A workflow change can access secrets and deploy; see securing GitHub Actions.
  • CODEOWNERS itself, so the rules cannot be changed by someone who owns nothing.
  • Dependency manifests and lockfiles, if supply-chain review matters to you.
  • Infrastructure code, auth and billing logic, and migrations.

Branch protection or rulesets?

Rulesets are the newer mechanism. They can layer, apply to many branches by pattern, work across an organization, and have bypass lists that are audited. Branch protection rules are older but still supported. Either way, set these on main and release branches:

SettingRecommendationWhy
Require a pull request before mergingOnNo direct pushes
Required approvals1, or 2 for sensitive reposReview for every change
Dismiss stale approvals on new commitsOnAn approval should cover the code that merges
Require review from Code OwnersOnMakes CODEOWNERS binding
Require approval of the most recent pushOnThe author cannot add commits after approval and self-merge
Require status checksOn, with named required checksCI must pass
Require branches to be up to dateOn, or use a merge queueAvoids semantic conflicts
Require conversation resolutionOnComments get answered
Require signed commitsOptionalNeeded in some compliance regimes
Require linear historyOptionalMatches squash or rebase merging
Block force pushes and deletionsOnProtect history
Do not allow bypassingOn for everyone, admins included"Admins can override" tends to be used at the worst time

Required checks should be few and stable: tests, lint, build and a security scan. Name them through a single summary job so that renaming matrix cells does not break the rule; see matrix builds done right. A required check that is skipped by a paths filter stays pending forever, so handle it in the workflow.

Avoid the common failure modes

  • Code owner review on a repository with one maintainer. The author cannot approve their own PR, so a lone owner is stuck. Add a second owner or use a team of at least two.
  • Owner not in the repository's write list. The line is silently ignored.
  • ** catch-all with a huge team.* Everyone is requested on every PR, so nobody feels responsible.
  • Bots. Dependabot and Renovate PRs need owners too, so they are not stuck. Route them to a rotating dependency-owner team and use auto-merge for low-risk updates. See Dependabot setup.
  • Required checks that are flaky. People start using admin bypass. Fix the flakes; see dealing with flaky tests.
  • Bypass lists that grow. Review them each quarter.

Review hygiene that goes with it

  • Keep PRs small. Reviewers approve large diffs without reading them.
  • Add a PR template with a checklist for the risky paths.
  • Track review latency; slow reviews stretch lead time, one of the DORA metrics.
  • Use required reviews from owners for risky paths and a lighter process for docs. CODEOWNERS can express that: assign docs to a broad team.

Test it

After changing rules, open a throwaway PR that touches each protected area and confirm the right people are requested and the merge button is blocked. In the CODEOWNERS file view, GitHub shows syntax errors; fix them first.

FAQ

Where should the CODEOWNERS file live?

.github/CODEOWNERS is the common choice. GitHub also reads the repository root and docs/.

Can one file have several owners?

Yes. List several owners on one line. Any one of them can satisfy the code owner requirement (not all of them), unless you create separate requirements with rulesets.

Do code owners apply to the PR author?

An owner who authors the PR cannot approve it themselves, so another owner has to.

Is the main branch the only one that needs this?

Protect any branch that deploys, such as release/* or production. A ruleset with a branch pattern does this in one place.

Pair this with the Dependabot guide so update PRs get an owner. Faster required checks also help people stay inside the process: compiler.dev's comparison mode shows how much faster your required checks run on faster runners.

Made by compiler.dev. Free tools · Pricing