Engineering

How to review a pull request you did not ask for

Editorial · Reveneau · October 11, 2026

How to review a pull request you did not ask for

Open any active repository and look at the pull request list. A growing share of what is there was not requested by anyone.

Dependency version updates from an update bot. Security patches from a scanning tool. Lint and formatting corrections. Refactors an agent decided were an improvement while working on something related. Suggested fixes from static analysis. They arrive daily, they are usually small, the build usually passes, and they get approved in a few seconds each.

The review habits everybody learned were built for a different situation, and they fail on these in a specific way that is worth naming.

The missing half of a review

When you review a human's change, half of the information is already in front of you. The diff carries what was done. What is missing, reliably, is why: which approaches were tried and abandoned, what the author knew about the calling code, what they checked before deciding a case could not happen.

So reviewers ask. Why this approach. Did you consider the other thing. What happens when this is null. The question is a request for information that exists, in somebody's memory, and can be retrieved.

On an unrequested pull request there is nothing to retrieve. The agent has no retained reasoning. Ask it anyway and you will get a fluent, confident, well-structured answer, reconstructed from the diff in front of it. That answer may be correct. It carries no evidence either way, and it is more dangerous than silence, because it satisfies the reviewer's instinct that a reason was given.

This is the adjustment: the review has to be based on evidence inside the change rather than on the author's intent.

Three questions that work without an author

What did this change that I cannot see?

The diff shows only part of the change. This is most obvious on a dependency update, where the visible edit is one line in a lock file and the actual change is every difference between two versions of somebody else's code.

For a version update, the document to read is the upstream changelog between the two versions, not the diff. You are looking for behavioural changes, deprecations, default value changes, and anything in the "breaking" section that the semantic version number did not warn you about, which happens more often than the convention promises.

For a generated refactor, the invisible part is whether behaviour changed at a boundary. Refactors are defined as behaviour-preserving and generated ones sometimes are not, particularly around null handling, ordering, and error paths that no test covers.

What does this change assume about real data?

Generated changes constantly encode assumptions about data. This value is never null. This field is unused. This collection is small enough to load at once. This loop runs a handful of times.

Each is a claim about your production system, and your production system is not in the pull request. The reviewer's job on these is to notice the claim and then go and check it against something real: a query, a log, a metric. Intuition is a poor check here, because guesses about what the data looks like are wrong in exactly the way that causes incidents.

This is the same failure described in why data changes are riskier than code changes, and it shows up wherever generated code meets data nobody measured.

What happens if this is wrong at 3am?

Two properties matter, and diff size is neither of them: how much breaks if the change is wrong, and whether it can be reversed.

A change that is small and reversible deserves a fast approval, because being wrong costs a rollback. A change that is small and irreversible does not, regardless of how tidy it looks. Dropping a column, altering a permission default, changing a retry policy, modifying something that runs in a scheduled job: all of these can be eleven lines and none of them is cheaply undone.

The reason this question matters most is that approval speed in practice follows diff size, and diff size is nearly unrelated to consequence. That link is what you have to stop.

The queue problem

Behind the individual reviews is a volume problem that changes behaviour whether or not anyone decides it should.

A repository generating fifteen automated pull requests a week, each needing a genuine ten minutes of investigation, has created two and a half hours of weekly work that nobody was assigned and nobody scheduled. What happens next is predictable. The queue grows, somebody feels bad about it, and a habit forms of approving anything that looks routine. At that point the review step exists only in the process diagram, which is worse than not having it, because it is being counted as a control.

Three things help.

Set a policy per category rather than deciding per pull request. Patch updates to development-only dependencies with a suite that would catch a regression: automatic merging is reasonable. Anything touching authentication, payments, or data handling: always a human, always the changelog. Major versions: always a human. Write this down so it is a decision made once rather than fifteen times a week under time pressure.

Batch and schedule. Review these in one session on a fixed day rather than one by one as they arrive. Reviewing low-urgency changes as they arrive is how the fast-approval habit forms.

Name owners. Dependencies, security patches, and generated refactors each get a specific person. Unowned automated changes are reviewed by whoever has a spare moment, which in practice means no review.

What a passing build proves

Every one of these arrives with a passing build, and the temptation is to treat that as the answer.

A passing build is evidence about exactly the cases the test suite covers, and nothing else. On a dependency update it proves your tests still pass against the new version, which says nothing about the upstream behaviour your tests never test. On a refactor it proves the covered paths still work, and refactors go wrong on uncovered paths.

The useful habit, and it takes an afternoon once rather than effort per review, is knowing what your suite actually protects. Break something a check is supposed to catch, on purpose, and confirm the build fails. A check that cannot fail has been contributing false confidence to every review it appeared in. We made the longer argument in how many tests does AI-generated code need.

Where the actual risk sits

The dangerous unrequested pull request is never the large one. A 600-line generated refactor gets attention automatically, because its size triggers the instinct to slow down.

The one that gets merged is small, green, plausible, and appears among fourteen others exactly like it. It bumps a library that handles session tokens across a minor version where the default expiry changed. It removes a null check that a linter called redundant and that was covering a case the linter could not see. It adjusts a retry count in a job that only runs on the first of the month.

None of those are found by reading more carefully. They are found by asking what changed outside the diff, what the change assumes about real data, and what being wrong costs, on every change, including the boring ones. Especially the boring ones, since almost every change in this category is boring.

Sources

Common questions

What is an unrequested pull request?

It is a change opened by automation or an agent rather than assigned to a person: a dependency version update, a security patch, a lint or formatting fix, a generated refactor, or a suggested improvement from a scanning tool. Nobody wrote a ticket for it, so it arrives with no stated intent and no author who can explain the reasoning behind it.

Why are agent-opened pull requests harder to review?

Because normal review depends on recovering the author's intent, usually by asking. An agent has no retained reasoning to recover, and asking it produces a fluent reconstruction rather than a memory, which sounds like an explanation while carrying no information. The review has to be based on evidence inside the change instead.

Should dependency update pull requests be auto-merged?

Patch-level updates to development-only dependencies, with a test suite that would actually catch a regression, are reasonable candidates. Anything that touches production behaviour, changes a major version, or updates a package involved in authentication, payments, or data handling deserves a person reading the upstream changelog, because the diff shows a version string and hides everything that matters.

How do you review a change when the diff looks trivial?

Read what the diff does not show. For a version update that means the upstream release notes between the two versions. For a refactor it means whether any behaviour changed at a boundary. For a lint fix it means whether the rule change altered semantics anywhere. The size of the diff is not related to the size of the change.

What is the risk of approving small automated changes quickly?

Approval speed tends to follow diff size rather than consequence, and on automated changes those two often differ in dangerous ways. A one-line dependency update can bring in thousands of lines of third-party behaviour, and an eleven-line refactor can remove a null check that the code depended on. The habit of fast-approving small diffs is exactly what makes this category dangerous.

Should you ask an agent why it made a change?

Treat the answer as a hypothesis to check rather than as testimony. A model asked to justify a past change produces a plausible reconstruction from the diff, which may be correct and carries no evidence either way. It is useful as a starting point for your own verification and is not a substitute for it.

How should a team handle a large number of automated pull requests?

Batch them by category and risk, review them on a schedule rather than reactively, and set a policy per category so each one is not an individual decision. The usual failure is a queue with no limit that teaches everybody to approve without looking, at which point the review step exists only in the written process.

What should you check on a generated refactor?

Whether any behaviour changed, particularly at error paths, null handling, ordering, and boundary conditions. Refactors are supposed to preserve behaviour, and a generated one will sometimes improve the code while quietly changing what happens in a case nobody tested. If the test suite passes both before and after, that is evidence only about the cases the suite covers.

Does a passing build make an unrequested pull request safe to merge?

Only to the extent the checks can fail. A passing build on a dependency update proves your tests still pass against the new version, which says nothing about the parts of the upstream change your tests never test. Knowing what the suite actually protects is what makes a passing build useful information.

Who should own automated pull requests?

Someone specific, by category, with a named owner for dependencies, for security patches, and for generated refactors. Unowned automated changes accumulate until they are reviewed by whoever has a spare moment, which in practice means no review at all.

Start a project