DEV Community

Cover image for How to Require Code Review on GitHub (Branch Protection)
Othman Shareef for Pyor

Posted on Originally published at pyor.review on

How to Require Code Review on GitHub (Branch Protection)

If review depends on everyone remembering to ask for it, it will be skipped exactly when it matters most. The fix is mechanical: require code review on GitHub with branch protection, so a pull request physically cannot merge without an approving read. The settings take five minutes; the policy design is the real work. This piece covers what the protections actually do, verified against the GitHub docs, and how to pick the configuration that guarantees a human read without turning every merge into a queue at the post office.

The short answer: Branch protection can require a set number of approving reviews, dismiss approvals as stale when new commits change the diff, require sign-off from code owners, and demand that the latest push be approved by someone other than its pusher. By default, admins and roles with bypass permission are exempt unless you extend enforcement to them. Aim for the minimum friction that guarantees an honest read: usually one approval, stale dismissal on, and code owners where the risk lives.

What it means to require code review on GitHub

The core setting is plain. Per the GitHub docs on protected branches, you can require that all pull requests receive a specific number of approving reviews before anyone merges them into the protected branch, and once required reviews are on, collaborators can only push changes to that branch through a pull request approved by the required number of reviewers with write access. That second half is the part teams forget: the rule does not just gate the merge button, it closes the direct-push side door. What the setting buys you is a guarantee that every change to main passed through a surface where review could happen. What it cannot buy you is the review itself, a gap we will come back to.

Stale dismissal, code owners, and the last-push rule

Three companion settings decide whether the required approval means anything. First, stale dismissal: with it enabled, the docs state that approving reviews are dismissed when commits are pushed that affect the diff, so an approval describes the code that merges, not a version from last Tuesday. Without it, approve-then-rewrite is a one-person bypass. Turning it on is close to free on a team that keeps pull requests small, and expensive exactly where the expense is telling you something. Second, code owner reviews: any pull request that affects code with a code owner must be approved by that owner before merging, which is how you attach mandatory expertise to specific paths instead of raising the approval count everywhere; our CODEOWNERS guide covers keeping that file honest. Third, the quieter one: you can require that the most recent reviewable push be approved by someone other than the person who pushed it, which blocks the author-pushes-then-self-approves loophole on shared branches.

The bypass honesty problem

Here is the clause that decides whether your policy is real: by default, branch protection restrictions do not apply to repository admins or to roles granted the bypass permission. You can extend enforcement to administrators, and for most teams you should, but some bypass capacity usually survives for genuine emergencies. The problem is not that bypass exists; it is that bypass is quiet. A protection rule that senior people silently skip is worse than no rule, because everyone else can see the merged-without-review commits while the policy page still claims reviews are required. The workable norm is to treat every bypass as a public event: announced in the team channel when it happens, with a reason, and followed by a retroactive review. If that feels heavyweight, notice what that means: bypassing was becoming routine.

Design for the minimum friction that guarantees a read

The failure mode when configuring all this is symmetrical. Too loose, and the settings are decoration; too strict, and engineers spend their afternoons collecting approvals like signatures on a permission slip. The design principle we keep coming back to is the one from review by blast radius: match rigor to what breaks if the change is wrong. In branch protection terms, that usually means one required approval with stale dismissal as the repository-wide floor, and code owners layered on the paths where mistakes are expensive (auth, payments, data migrations, public API), effectively requiring a second, specific reviewer only where it pays. Blanket two-approval rules feel rigorous but mostly convert the second reviewer into a formality; Microsoft’s research on modern code review found the value of review lives in understanding and improvement, not in stacking sign-offs.

Protection is a floor, not a culture

Branch protection guarantees a click. It cannot guarantee that anyone read the diff, and a team that rubber-stamps will rubber-stamp straight through a required approval, one keystroke slower than before. The settings also age: paths gain new risk, code owners leave, and a configuration nobody revisits quietly drifts away from the risks it was meant to cover, so put a yearly review of the rules themselves on the calendar. None of that is an argument against the settings; floors matter, and the direct-push door should be closed on any codebase with users. It is an argument for keeping the two layers straight. The settings make skipping review impossible; only norms make review real, and those norms (approvals that say what was read, nits labeled, leaders who queue like everyone else) are built in the open, the way we describe in code review culture. Configure the floor in an afternoon. Expect the culture to take a year. Both are worth it, and neither substitutes for the other.

Frequently asked questions

How many approvals should a protected branch require?

One, for most teams. GitHub lets you require a specific number of approving reviews, but each extra required approval adds latency to every merge while adding little scrutiny beyond the first honest read. Reserve two approvals for genuinely risky surfaces, such as payment paths or auth, and enforce that through code owners rather than a blanket rule.

Does dismissing stale approvals slow teams down?

It adds a re-approval round trip whenever new commits change the diff, and that is the point: the approval should describe the code being merged, not an earlier version of it. Keep the cost low by keeping pull requests small and re-requesting review promptly. Teams that find this unbearable usually have a PR size problem, not a settings problem.

Top comments (0)