Switch to light mode

GitHub Branch Protection Rules Setup Guide for Growing Engineering Teams

- 6 min read

GitHub repository settings page showing branch protection rules being configured for the main branch

Setting Up GitHub Branch Protection Rules

At two or three engineers, a broken main branch is a minor annoyance someone notices and fixes within the hour. Past that, a single force-push or an unreviewed merge can take down a deploy pipeline, and the person who caused it often isn’t the one who has to untangle it. Branch protection rules are the cheapest fix available, and most teams set them up reactively, after the first incident, instead of before.

Prerequisites

  • Admin access to the repository
  • Agreement within the team on what “ready to merge” actually means (this matters more than the rules themselves)

Core Rules Worth Turning On

In the repository’s Settings > Branches page, add a protection rule for main (and any other long-lived branch like staging or production):

  1. Require a pull request before merging. No direct pushes to main, even for admins. This alone prevents most accidental breakage.
  2. Require approvals. One approval is usually enough for a small team; requiring the author to not be the sole approver is the actual goal, not a specific number.
  3. Require status checks to pass before merging. Whatever CI runs (tests, linting, type checks) should be a hard gate, not advisory. A green checkmark that doesn’t block merging isn’t protection, it’s decoration.
  4. Require branches to be up to date before merging. Prevents merging a PR that passed CI against an older version of main that no longer reflects reality.
  5. Do not allow bypassing the above settings. Including for admins. The moment “except for admins” is checked, the rule only applies to people who were never going to break it on purpose anyway.

Rules to Consider, Not Default To

  • Require signed commits: worth it for regulated industries or larger teams, often unnecessary friction for a five-person startup.
  • Require linear history: a style preference (rebase vs merge commits), not a safety rule. Decide based on how the team already works, don’t impose it as a security measure.
  • Restrict who can push to matching branches: useful once the team is large enough that not everyone should have merge rights to main directly, premature before that.

Rolling This Out Without Slowing People Down

Branch protection fails as a habit when it’s introduced as a blocker with no explanation. Two things help:

  • Turn on required status checks only once CI is actually reliable. Flaky tests blocking every merge trains people to see protection as friction, not safety.
  • Explain the “why” once, briefly, rather than just enabling settings silently. A team that understands a force-push wiped out a day of someone’s work six months ago at a previous job needs no convincing; a team that’s never seen the failure mode will just experience the rule as red tape.

Common Mistakes

  • Protecting main but not staging/production. If deploys happen from a branch other than main, that branch needs the same protection, or it’s the actual unguarded path.
  • Requiring approvals but not requiring status checks. A human approval doesn’t catch a failing test suite; the two checks cover different failure modes and both matter.
  • Turning on every available rule at once. Start with the five core rules above. Add more only when a specific incident or team size change actually calls for it.

The goal isn’t process for its own sake, it’s making the safe path the only path, so nobody has to remember to be careful.

© 2024 Shawn Mayzes. All rights reserved.