1. Dyvertex
  2. Blog

81 of my last 109 pull requests merged without a second approval. This is what checked them.

AIDXCode review

A friend sent me Remo H. Jansen's article "Do We Still Need Code Reviews in the Age of Coding Agents?" with a few messages under it. Roughly translated:

Pretty much true. I've been working like this for a while. The only downside is that I have no idea what's being coded. I get the general picture, but the edge cases, no clue. But do I have to know? Probably not. Nobody knows how every piece works in a big system.

The article's argument, in short: when an agent writes the code and you review it before opening the pull request, you're already the reviewer. A second person pressing Approve adds friction, and approval counts are a weak proxy for verification. The fix it proposes is layers: constraints that make bad code hard to write, automated checks, adversarial review agents, and humans only for the judgment calls.

I agreed with most of it, then went to check whether it describes what I actually do.

What my repo says

OpenORA is an open source igaming platform I work on with a team of eight. Almost all of it is written by agents, Claude Code and Codex mostly. The dev branch requires one approval from a code owner, dismisses it on every push, and needs the verify and commitlint checks to pass in a merge queue. I'm on the bypass list.

From July to today, three months:

  • 228 merged pull requests, 313,088 added lines
  • median pull request: 597 added lines; 50 of them over 2,000
  • teammates' pull requests: 100 of 118 had an approval
  • mine: 28 of 109 had one

So for my own code the article describes reality already. 81 pull requests went in with me as the only human who read them, and for the bigger ones "read" is generous. I skimmed the parts I cared about and trusted the rest to something else.

I didn't measure whether those 81 broke more often. 61 of the 228 pull requests are fix:, but linking a fix to the change that caused it needs more than a title, and I don't have that data. So this post is about what the something else is. It doesn't prove it works better than a second approver.

Do you have to understand the edge cases?

My friend's question is the real one. I think the answer is no for the edge cases, and yes for the invariants.

Nobody on a big system knows how every function handles an empty string. That was true before agents. What the people who run a system do know is the short list of things that must never happen. In a casino platform: a balance never goes negative, a debit never happens twice for one bet, an admin route checks the role before anything else, money is never a float.

When agents write the code, that list is what you keep in your head. The edge cases go into tests and rules where a machine checks them every time, because you won't.

LLM review is a probability

We do run reviewer agents on every change. There's a /review skill that spawns two of them on Opus: a security-reviewer that owns money, auth and "does this do what the title says", and a quality-reviewer that owns failure branches, races, performance and conventions. Both prompts open with the same stance:

Assume the change is BROKEN until you trace it working. Green gates, comments, and commit messages prove nothing.

They walk every changed route through seven hops (contract, handler, service, query, table, side effects, response), git grep every caller of every changed export, and only report a finding with a file:line, a concrete trigger and the rule it breaks. They do find real bugs. An audit record written after the money transaction commits, so a crash in between leaves a transfer nobody can trace. Sessions revoked after a 2FA swap had already committed, so if the revoke threw, the old sessions stayed alive and the user was never warned.

They also miss things, and not the same things each time. Our own review doc says it plainly: one reviewer working every checklist finds about half of what two lens reviewers find. Useful for designing the review, and it's also why a reviewer that misses half shouldn't decide a merge.

So the LLM review in our repo is never a required check. It leaves comments. The checks that block a merge are verify and commitlint, and both are deterministic: same input, same answer, every run.

Every review comment I write twice becomes a rule

This is the part I'd copy first.

A few of my old review comments, word for word: "Let's always use timestamptz", "Let's use UuidSchema over z.uuid()", money as a decimal string and never a JS number. I wrote those on human pull requests again and again. Agents repeat a mistake faster than people do, so I stopped writing them. Each one is now an oxlint rule:

  • no-naive-timestamp: a timestamp() column without withTimezone
  • no-raw-z-uuid: z.uuid() instead of the shared UuidSchema
  • no-float-money: a real() or doublePrecision() column in a schema file
  • no-unbounded-db-fanout: Promise.all(rows.map(...)) over a query result, one pool connection per row
  • no-adhoc-zod-in-router, no-cross-core-domain, no-engine-to-domain and friends: module boundaries

That's 20 custom rules across two small oxlint plugins, each with its own tests under tools/__tests__, so a rule can't quietly stop matching. On top of that: six dependency-cruiser rules for the import graph, a script that checks every domain still has its contract, plugin and index (agents love to "simplify" by deleting a required file), a hygiene scan that keeps client names out of a public repo, and a drift check that regenerates every generated file and fails if the committed copy differs.

All of it runs from a single script:

1"verify": "turbo run build check:lint check:format check:boundaries check:shape check:hygiene check:refs check:deprecations test:unit test:integration test:tools && pnpm check:drift"
2

check:deprecations is depretec, a small tool I wrote that finds @deprecated APIs used in your code, including the ones declared in node_modules types. Agents call deprecated APIs without a second thought.

The reviewer agents get a list of what not to flag, and the first item is "anything lint/CI already enforces". So every rule I add makes the LLM review shorter and more about logic.

Put the invariant where the agent can't skip it

Some invariants don't fit a lint rule. "A bet is debited once" can't be checked by reading the code, it depends on what happens when the same request arrives twice.

Hop five of our review trace is about this: the invariant has to live in a constraint, index or foreign key, and code alone isn't enough. Money mutations are idempotent through a unique constraint in the same transaction as the write, so a retry hits the database and fails, whatever the handler does. If the agent forgets an if, the unique index still rejects the second debit.

TypeScript does the same job one layer higher. Contracts are Zod schemas and every type is inferred from them, never written by hand. When a contract changes, the build breaks in every consumer, which tells me more than a reviewer's "looks fine".

QA is where the edge cases live now

If I don't read every line, the tests are what I read instead. We have a qa agent, and its pass has two deliverables, both required:

  1. Automated tests: one end-to-end test per touched route, booting the real app against a real Postgres, with the happy path and at least one hostile one (wrong user, repeated call, missing auth). Mocking the database is not allowed, and the agent deletes such a test if it finds one.
  2. A hands-on walkthrough: actually driving the flow and keeping the evidence, request and response traces or screenshots a human can open. It's mandatory on every pass, because a suite that passes doesn't show you the extra call or the 500 sitting in a log.

The repo has 278 test files now, 60 of them end-to-end and 118 integration tests, sharded across three CI jobs with their own Postgres and Redis.

One line I'd steal from our reviewer prompt: "a test in the diff that would still pass with the feature removed or its mock unconfigured proves nothing - flag it". Agents write those all the time, usually as an assertion that accepts any rejection. A green test written by the same agent that wrote the code is a weak signal until something checks that it can fail.

What humans are still for

The review doc rates risk by the paths a change touches, never by its title or size: critical for money, wallet, KYC, responsible gaming, geo and audit; high for schemas, migrations, contracts and routers; down to low for components and copy. That rating sets how hard the agents look. I think it should also set where a human looks.

For me that's the contract, the migration and the tests. If the input and output shapes are right, the table enforces the invariant and the tests cover the hostile path, I can live without reading the handler in between. When I can't tell what a test proves, that's the pull request I read line by line.

And the judgment calls stay human: should this be in the platform or in one operator's code, does this flow match what the business asked for, will we regret this table in a year. No checklist catches those.

What I'd copy

First, count how many of your merged pull requests had an approval, and how many of those came after someone actually read the diff. Then the second time I write the same review comment, it becomes a lint rule with a test. Required checks stay deterministic, since LLM review can leave comments but shouldn't be what lets a merge through.

I tell reviewer agents to assume the change is broken, require a file:line and a trigger, and hand them a list of what they may not flag. I split them by the files they read, because two agents reading the same files pay for them twice. Invariants go in the database (unique constraints, CHECKs, foreign keys). Tests run against a real database, and a test that passes without the feature counts as a bug.

What it isn't

  • One repo, three months, eight people. A headless TypeScript backend with good test infrastructure. A UI-heavy app would need more of the walkthrough and less of the lint.
  • No defect data. I can show how much went in without a second approval. I can't show whether it broke less or more.
  • I'm the one with the bypass. It's easier to argue approvals are friction when you're not the one waiting for them.

My friend asked whether they need to know the edge cases. I don't think they do. They need to know where each edge case is checked, and if the answer is "the agent probably thought of it", that's the one to fix.

Bezpłatna konsultacja · 20 minut

Masz podobny problem we własnej aplikacji?

Umów się na darmową 20-minutową rozmowę audytową i wyślij repo albo błąd. Wyjdziesz z wiedzą, co naprawić najpierw, niezależnie od tego, czy z nami zaczniesz pracę.

Najszybciej odpiszemy na Telegramie: @dyvertex albo mailem [email protected]

Nie wiesz, co właściwie jest nie tak z Twoją aplikacją? To nasze zadanie, żeby to ustalić. Każdą wiadomość czyta człowiek i odpowiada w ciągu jednego dnia roboczego.