Merge without code review: where the main branch breaks
The main branch is the only place where the state of the code matches what goes to production.
As long as direct push is open, any commit past code review ends up there without review: the build runs, tests are formally green, and the defect surfaces on live traffic — usually in the worst-case usage scenario.
The cost of such breakage is higher than it seems. A broken build in the main branch blocks not the author of the edit: the pipeline stops, other tasks cannot be merged, and the release shifts as a whole.
Rolling back a release drags along other people's changes — you have to choose between unstable prod and losing completed tasks that were in the same release.
Branch protection turns this from personal discipline into a rule that cannot be bypassed accidentally.
Direct push is closed; merging into the main branch is possible only through a merge request; approval by the relevant reviewer and a successful run of checks on the build agent are mandatory.
We describe exceptions separately: an urgent fix follows the same path, but with a reduced set of checks.
Next, we configure the merge queue and conflict resolution rules so parallel tasks do not break each other's build. Then the main branch is releasable at any moment, and a rollback returns one specific set of changes, not the team's entire work. We break down how this is covered at the repository settings and process level.
What mandatory review before merging into the main branch provides
The main branch protection rule is configured so that a merge request physically cannot be accepted without approval.
The required number of approvers, a mandatory build and test run, dismissal of approvals when new commits appear — then approval applies to the revision actually reviewed, not an earlier one.
In GitLab this is set by protected branch settings and the list of code owners; on other platforms the logic is reproduced with the same rules.
Where the effect is visible in numbers
The share of defects caught before merge rather than in prod grows multiple times. Handling of boundary values, missing conditional branches, incorrect handling of empty data and access rights are visible immediately in change review.
Returns depend on change size: the first two to three hundred lines are reviewed closely, then attention drops — so the rule of small changes yields more than adding reviewers.
The number of rollbacks and urgent fixes after release decreases: the cause of a rollback was almost always already present in the change, but remained undiscussed. Review speed increases — comments arrive before the next batch of edits is layered on top.
Release predictability
Release stops depending on who is free that particular evening. Checks and approvals run in parallel, and the queue of open requests is visible in full — it is clear what exactly is slowing the release and who needs to be brought in.
Merge control options: comparison of approaches
The choice of control mode is determined by the cost of an error in a specific branch and the number of people writing to it. There is no universal option: strict rules in a small team turn into downtime, while loose ones on a release branch let regression into production. The good news: rules live at the repository level and change without code edits.
| Control mode | When it fits | Limitations of the approach |
|---|---|---|
| Full protection of the main branch | Release branches: direct commit prohibited, only via merge request | Urgent fixes follow the same path — incident response time increases |
| Mandatory approval with a number of confirmations | Critical code where an error costs more than waiting | Two confirmations in a low-activity area block merging until a second reviewer appears |
| Selective review by areas of responsibility | Large repository with different module owners | The owner list becomes outdated, and some paths remain without mandatory review |
| Branch protection plus mandatory check run | Teams with automated tests and static analysis | Requires maintaining the checks themselves, otherwise merges stall on an unstable step |
The combination works more predictably than any single mode: main branch protection prevents bypassing the process, the number of confirmations sets the cost of approval, and selective review offloads secondary paths.
Separately, consider dismissal of approvals on new commits and workarounds for documentation edits — otherwise the team will start looking for loopholes around the rules.
Stages of implementing mandatory review in development
Review rules break when they are enabled formally: approval is required, but branches live for weeks, the reviewer skims changes, and some edits go into the main branch directly. We break implementation of mandatory Code Review into stages — from audit to handover of the policy.
-
Process audit. We look at how changes get into the main branch now: how long branches live, who reviews them and when, how many edits come after merge. From history, we calculate the share of changes that passed no review.
-
Branching strategy. We choose a model for the team's rhythm: short-lived branches from main, release branches, or environment branches. Separately, we describe the path for urgent fixes, otherwise the rules will be bypassed.
-
Merge rules. Protection of the main branch from direct pushes, mandatory reviewer approval, dismissal of approvals on new commits, automated checks — build, tests, static analysis — as a merge condition.
-
Pilot on one team. We enable the rules on one service and for several weeks monitor time from first commit to merge, return rate, and size of changes in one request.
-
Validation on real changes. We run regular tasks and incidents through the new rules. For incidents, we keep an expedited path: merge without waiting for approval, but with mandatory post-hoc review.
-
Team training. Policy, change description template, reviewer checklist, and review of typical comments. We embed the rules into the definition of done so review does not depend on participants' memory.
After the pilot, we transfer the settings to the remaining repositories — resulting in a single set of rules instead of manual agreements.
Configurations, policies, and documentation at the output
At the output, the client is left not with access to our engineers but with artifacts in their own repository: branch protection rules, branching strategy, approver matrix, and the policy according to which the team works without us.
All of this lives in version control next to the code — edits go through as a regular change, edit history is visible, and a rule can be rolled back the same way as code.
- Branch protection setting files: prohibition of direct push to the main branch, mandatory merge via merge request, minimum number of approvals, dismissal of approvals on every new commit.
- Branching strategy: which branches exist, how they are named, how long they live, from which to which they merge and under what conditions — with examples on the client's real services.
- Approver matrix: who approves which paths — service owner, infrastructure engineer, security responsible. Implemented by a code owners file by paths (CODEOWNERS) or an equivalent in your hosting.
- Change description template: what we change, why, how it was tested, rollback plan, links to tasks. An empty template does not pass validation on the merge side.
- Set of automated checks tied to merge: build, tests, static analysis, secret scanning — indicating which are blocking and which are informational.
- Policy for new employees: what to read on the first day, how to get permissions, who approves the first change, where to go in a disputed approval situation.
- Maintenance instructions: how to add a new approver, how to loosen a rule for an urgent fix, and how to then restore strict mode.
Case: fewer rollbacks and faster change review
A team of fourteen engineers across four time zones ran a service with releases several times a week. Changes went into the main branch directly, without review: the author considered the edit trivial, while a colleague in another time zone learned about it only after deployment.
Every fifth release ended in a rollback, and reviewing one change took on average about forty minutes — from logs and chats they reconstructed who touched the module and why.
What changed: in GitLab they enabled main branch protection and mandatory approval before merge. For critical modules, two approvals are required; for the rest, one is enough — otherwise the rule starts slowing down small edits.
They added sections "how to test" and "how to roll back" to the description template, and limited change size: a large edit is split into parts that can realistically be reviewed in one pass.
Process metrics were collected after a quarter. The rollback rate fell from one fifth to isolated cases per hundred merges, and change review time dropped from forty minutes to ten to twelve: the context of the edit and its discussion are in one place.
The share of tasks returned for rework, on the contrary, grew — and that is expected, because defects began to be found at the review stage, where a fix takes minutes, rather than in prod, where it turns into a nighttime incident investigation.
For a distributed team, something else is more important: mandatory code review moved discussion into comments on the change, and it remains in history next to the code.
Module knowledge is no longer tied to one person; a new engineer joins the project through reviews of real edits, not oral introductions. The policy lives in repository settings, not in personal agreements — so it does not erode as the team grows.
What to do if code review is implemented but doesn't work
The rule exists, but a merge goes through without approval — meaning the protected branch setting does not match the process.
A typical cause: the minimum number of approvals is set, but dismissal of old approvals is not enabled, and the author adds a commit after review, while the previous approval continues to count.
The second defect: required status checks allow the build if it has not completed, rather than if it failed.
Administrative bypass is a separate cause, and it is organizational, not technical. If half the team has permission to bypass protection, any urgent fix goes past review, and the rule stops being a rule.
This is not cured by persuasion: bypass permission stays with one or two engineers, every such merge goes into the audit log and is reviewed at retrospective. When bypass is visible, it stops being used as a normal path.
Working exceptions are needed, but narrow and described in advance: release branches, schema migrations, deployment configurations.
For critical paths, code owners are assigned — then approval is given by the one responsible for the module, not the first available colleague, and formal approval stops passing.
Process restructuring proceeds without stopping development: rules are enabled in stages. First critical paths in warning mode, then they look at metrics — share of merges without approval, time to first review, diff size — and only then enable blocking.
Nobody reads diffs of hundreds of lines, so limiting change size gives more than another mandatory approver.
The point of restructuring is to return review to its function: the rule should catch a defect, not create a ritual. If after enabling blocking the share of edits based on comments grows, while delivery time does not fall, then the setting remained formal.
Common technical doubts before implementation
Objections before implementing mandatory review usually hinge not on the idea, but on the fear that "the flow will stop." Let's go through typical engineering-level doubts — each has an answer at the process level, not a promise that "it will be fine."
Will delivery timelines increase?
The approval-before-merge rule adds reviewer wait time to the task, not work time on it. We limit work in progress and set a target time to first response so a branch does not hang for a day.
Checks are launched in parallel with code reading, so the test run does not lengthen the cycle but happens within it.
Will large changes have to be split?
Yes, and that is the main gain. The change is split by layers: data model separately, logic separately, interface separately. The reviewer reads blocks of a few hundred lines and retains context, and the share of missed defects falls simply because nothing is skimmed.
What to do about urgent fixes?
For them we keep a separate path: one branch, on-call engineer approval, automated run mandatory. Speed is preserved, but the fix does not slip past history — after service recovery, a review still takes place for it.
Why manual review if there are automated checks?
Automation catches syntax, regressions, and style guide violations, but it does not see errors in the data model, unnecessary calls to external services, and decisions that will become a bottleneck in six months. Checks remove noise; a human reviews logic.
It is convenient to implement this gradually: first critical repositories, then the rest, and the rule with blocking is enabled after the share of passing checks has stabilized.Let's discuss the task: how to set up review in your team
Setting up mandatory review is not a checkbox in branch protection, but an agreement about the process that must survive a crunch and an urgent fix.
The rule "at least one approval is needed" by itself guarantees little: without directory owners, edits to the payment module are approved by a random person, and without a limit on branch age, the merge queue grows faster than the team can review code.
What we will clarify on the call before enabling anything:
- Branching strategy: parallel release branches or short-lived branches from main — this determines the set of protection rules for each.
- Team composition and roles: how many approvals to require, who owns directories via the CODEOWNERS file, how to distribute workload so review does not bottleneck on one person.
- Critical code areas: authentication, payments, data migrations — these have a separate approval threshold and a mandatory owner.
- Release process: how review combines with a freeze, urgent fixes, and rollback to the previous release.
- Implementation plan: first warning mode without blocking merges, then mandatory status — delivery does not stop on the first day.
- Permissions and exceptions: who can bypass blocking and how each such action remains in the audit log.
To discuss the task concretely, describe the team size, branching strategy, and what is already configured. We will come back with an estimate and a phased implementation plan.







