What’s left for me to review
There’s a lot of conversation about code review right now. Agents write code faster than anyone can read it, so review gets called the bottleneck, and some people argue we should stop reading the code at all. I’m not going to settle that argument here. What follows is how I review code today, offered as one example rather than a verdict.
A code review used to take me an hour, and now it usually takes 5 to 20 minutes, depending on the change. I also understand the change better than I did then. Two things did that: a hard cap on change size, and a stack of filters that answer every question a tool can answer. After review, an agent, Product, and I all exercise the change in a live environment before it merges. That costs people’s time, and the people are also why we trust it.
In my experience, review doesn’t have to be the bottleneck, as long as you control the slice size. I’m not opposed to the other ideas, either. Whatever the idea is, though, I want to test it and confirm it before I rely on it.
Some of this is still in progress, and parts of it will get stricter, so this is where I am now. I offer it as a shape for code review that keeps humans in the loop and understanding the system. Tools do the rest: they keep the volume manageable and the code quality high.
These are the steps, in the order a change goes through them:
1. Keep the change small.
2. Let the tools go first.
3. Run the review agents.
4. Review for patterns, and promote them.
5. Make the agent prove it.
6. Test it together in a live environment.
7. Merge to main, and let the business decide when to deploy.
1. Keep the change small
This step happens before any code exists, and if I could keep only one step, I’d keep this one. Every later step removes concerns from my review, but this one is what makes a careful review possible at all.
A small diff fits in my head. I can read every line, hold the whole change at once, and ask whether it does what the card says. A large diff doesn’t. I skim it, trust the parts that look familiar, and approve something I didn’t fully read. Everyone who has reviewed a 2,000-line pull request knows what that approval is worth.
That’s why the limits are hard. Each card, our unit of planned work, produces one pull request. If the work turns out to be several changes, we split it before anyone writes code, not after. The pieces then merge in dependency order. When order alone isn’t enough, we dark-launch the work or put it behind a feature flag until every piece is in. Then the diff itself has a cap. Past 500 lines of code or 15 files, it goes back to be split instead of going to review. Those numbers are choices, not findings, and you can change them to fit your project.
I’ll give you the honest number, though. Across the last 120 merged pull requests, the median was 415 lines changed, and a tenth were over 2,268. Those counts include tests and docs, so they overstate how much logic I read, but they still aren’t small. The limits are recent, and so is splitting work up front.
Speed is the obvious payoff. I care more about comprehension. If you review code an agent wrote, you know it arrives faster than any person writes. At that pace, the bigger risk is a system you slowly stop understanding, because every change was too big to absorb. When I can read a change whole, I still understand it a month later.
2. Let the tools go first
Once the code is written, it passes a set of checks before anyone reviews it. Each check takes a category of question off my plate.
The first tier blocks the change outright: tests, linting, formatting, and type checking. So I never comment on style or check types by eye. If they were wrong, the change wouldn’t be in front of me.
The second tier looks at quality. A complexity check fails a change only if it makes things worse than they were. That way the code can only improve, and nobody has to rewrite old code first. Static analysis then flags the structural problems a linter can’t see.
Mutation testing is the newest check, and it answers the question that’s hardest to answer by reading: do these tests actually test anything? A mutation tool changes the code in small ways, like flipping a comparison or deleting a line, and checks whether a test fails. If no test notices, that test wasn’t protecting the line. The floor is 60%, and for now it reports rather than blocks.
Security checks run at this step too, inside the agent workflow rather than in CI. That’s a gap, and I know it. They could move into CI and haven’t yet.
Three of these safeguards don’t block anything yet. Mutation testing only reports, security runs outside CI, and the human gate in step 6 is social. Each one is in progress, and each will block once it’s ready.
3. Run the review agents
Next, six review agents read the diff in parallel. Each one has a single job: general code quality, over-engineering, risk, deployment, architecture, or design. They can read the code but can’t change it. One pass deduplicates their findings by file, line, and problem, and the result is a list. A person decides which findings to address. Then a single writer makes those fixes, for reasons I’ll get to in step 4.
They catch real problems, but I rarely see the best ones, because those are fixed before I open the change for review. That’s why the agents go first: anything they can raise is something I no longer have to.
4. Review for patterns, and promote them
Now it’s my turn. My read rarely catches a bug anymore, and that’s the process working. Everything a tool or an agent can find was filtered out before I looked, so it costs me no attention. What I read for is comprehension and patterns, the things no single check can express. I ask whether the change matches what the card asked for. I ask whether the seam, where this code meets the rest of the system, is in the right place. And I watch for a drifting convention, where this file now does something three other files do differently.
Most of all, I look for patterns. A mistake once is a comment, but the same mistake twice is a missing rule. So the size of a finding decides where it goes. A small one-line fix gets fixed as part of the change. If it’s a bigger pattern, I promote it into the charter, the standards the agents work from, so the mistake is less likely to come back. If it recurs, I promote it again to either be a deterministic check or moved into the harness.
The single-writer rule came from this. Before it, I kept seeing review agents try to fix the problems they found, each in its own way, in the same change. Parallel writers make conflicting decisions that a merge can’t reconcile, because the merge never saw the intent behind either one. Now only one agent writes at a time: the review agents report, and a single writer resolves every finding.
This step compounds. Each promoted pattern is one less thing every future review has to check. That’s a big part of how an hour became a few minutes.
5. Make the agent prove it
Passing tests and a clean review still don’t prove a feature works. So the change ships to an ephemeral environment: a short-lived copy of the app, with seed data, that exists only until the change merges. An agent then drives the real UI, exercises the feature, and captures screenshots as evidence. If you don’t have ephemeral environments, or your CI/CD doesn’t build them, a browser test against your local dev setup can be enough. What matters is that the environment is as close to production as you can make it.
The proof has to be evidence. If an agent says the feature works but attaches no screenshot, the workflow marks it unproven, and a partial proof always goes to a person.
This step caught a search feature built on the wrong page. The code was fine, the tests passed, and the review read cleanly, but the screenshot showed the search on a page where it didn’t belong.
6. Test it together in a live environment
That same environment is where the humans test. Product and I both do acceptance testing and exploratory testing, and we close different gaps. I close the technical ones: edge cases, error states, and what happens when a request fails halfway. Product closes the product ones, especially how customers actually behave, which I often don’t know.
The split matters most where the product is young, like our new project, which is under heavy development and has workflows that are new to everyone. Product knows how the people using it will move through those workflows in ways I can’t predict from the code.
Nothing technical enforces this step. The gate is social, so a merge would go through if we skipped it. We don’t skip it.
What it buys is a specific kind of trust. Before a change merges, two people have used the feature as it actually is. They’ve confirmed the happy path, and they’ve pushed on it hard enough to find bugs, edge cases, and workflows nobody planned for.
The strongest objection is the manual work, and for a mature product I think the objection is right. People cost time, and a process with people in it doesn’t scale like a script. I can’t put a number on that cost yet, either. The agent workflow tracks its own costs, but nothing else does, so I have no before and after. That’s something we should measure.
A well-established product changes the trade. Its workflows and its customers’ habits are known. Most of its edge cases have already shown up as bugs at some point. So you can script and test almost all of this, maybe all of it, and you should. Each workflow Product and I would explore by hand becomes an end-to-end test. Each edge case we’d push on becomes a regression test. What’s left for people is the part that’s actually new in the change, which on a mature product is small.
A young product is different. Nobody knows all its workflows yet, so there’s nothing complete to script against. Exploration finds what no script knew to look for. As the product matures, the manual share should shrink, one scripted workflow at a time.
That’s also my answer to the wider debate. Maybe one day we won’t read the code, and I’m open to that. If someone shows me a process that works without reading, I’ll test it like anything else. Even then, someone has to verify that a change does what it should. Reading may go away, but verifying isn’t going anywhere.
7. Merge to main, and let the business decide when to deploy
When CI is green and Product and I have signed off, the change can merge. Merging produces a releasable version and deploys it to a staging server, and because we do continuous delivery, a production deploy is a business decision rather than an engineering event. Every merged change is ready to ship, and whether it ships today is someone else’s call.
The pipeline is fast because it rarely jams. Changes are small, the cheapest step that can catch a problem catches it, and anything that reaches staging has already been used by two people. That speed is a side effect, though.
Reliability is what we’re after.
Try it on your next change
Pull up the last five review comments you left. For each one, ask whether a tool could have made it, and if one could, move it into the tool so you never leave that comment again. Then take your next change and cut it in half before you open the pull request.
Don’t adopt all seven steps at once. Add one at a time, and change each one to fit your team and your project. The process should fit you, not the other way around.