Sonar’s 2026 State of Code Developer Survey put a hard figure on a feeling every engineer already had: 96% of developers don’t fully trust that AI-generated code is functionally correct. That’s the stat that made headlines. But the more revealing one sits right next to it: only 48% always verify AI code before committing.
Sit with that gap. Nearly everyone distrusts the output, and barely half consistently check it. Sonar calls the space between those numbers the verification gap — and AWS CTO Werner Vogels gave the accumulating consequence a name at re:Invent in December 2025: verification debt.
The pressure behind it is only growing. AI already writes about 42% of committed code, a figure developers expect to hit 65% by 2027. Generation has become nearly free. Trust has not. This series is about the discipline that closes the gap — engineering code review so it scales with the flood of machine-written code instead of drowning in it.
What “AI slop” actually is
“AI slop” is the catch-all term for output that looks like good code without being good code. It’s worth being precise, because each variety fails review differently:
- Looks-correct-but-isn’t. The signature failure. 61% of developers in Sonar’s survey agreed AI often produces code that looks correct but isn’t reliable. It compiles, it reads cleanly, it passes the happy-path test — and it’s subtly wrong.
- “Almost right, but not quite.” Stack Overflow’s 2025 survey of ~49,000 developers found this is the #1 daily frustration, named by 66%. It’s the most expensive failure mode precisely because it survives a casual review — the diff looks reasonable, so it gets approved, and the defect ships.
- Hallucinated APIs and packages. The model invents a function that doesn’t exist, or imports a package that isn’t real — which has spawned slopsquatting, where attackers register the plausible-sounding names models hallucinate, turning a hallucination into a supply-chain attack.
- Edge-case blindness. The happy path is handled; the timeout, the empty list, the concurrent write, the malformed input are not.
- Security vulnerabilities. Multiple 2026 analyses put the share of AI-generated code containing vulnerabilities in the 40–48% range, with a large majority undefended against common classes like cross-site scripting.
- Duplicative and unnecessary code. 40% of developers cite AI generating redundant code; it re-implements what your codebase already has because it never saw your codebase.

The unifying property is the dangerous one: slop passes a casual read. It’s confident, idiomatic, and well-formatted. That’s exactly what makes it harder to review than a human’s bug, which usually looks like a mistake.
Why slop breaks code review specifically
Here’s the counterintuitive finding that reframes the whole problem. You’d expect AI to lighten the review load. The data says the opposite: 38% of developers report that reviewing AI-generated code takes more effort than reviewing code written by their human colleagues.
Why would reviewing be harder? Three reasons:
- No author intent to interrogate. When a colleague writes code, you can ask “why did you do it this way?” and their answer reveals their mental model — and its gaps. AI code arrives with no intent behind it. The reviewer has to reconstruct the reasoning from scratch.
- A false sense of security. AI output compiles cleanly and passes basic tests, which creates an illusion of readiness. Human bugs often announce themselves with rough edges; slop is camouflaged as competence.
- Volume. A human writes a few hundred lines a day and you review at that pace. An agent generates thousands of lines in seconds. The review surface explodes while the number of qualified reviewers stays fixed.
The market data confirms the bottleneck is real and measurable. LinearB’s 2026 benchmark of 8.1 million pull requests across 4,800 teams found that AI-generated PRs have a 32.7% acceptance rate versus 84.4% for human-authored ones, and wait 4.6× longer for review. Two-thirds of AI PRs need significant rework or get rejected — after a reviewer has already spent time on them.
The toil swap
The promise was that AI would eliminate grunt work. What actually happened is what Sonar’s data calls a toil swap: the effort saved during code creation reappeared during code review. Developers still spend roughly 24% of their work week on toil — checking, fixing, and validating output — and that number is essentially unchanged whether they use AI heavily or not. The work didn’t disappear. It moved downstream, from your fingers to your judgement.

This is the shape of the problem. Generation scales with compute; manual review scales with headcount. Point an agent fleet at a codebase and the gap between the two curves — the verification debt — grows without bound unless you change how review works.
The reframe: trust is the new bottleneck
Sonar’s CEO framed the shift bluntly: value in software is no longer defined by the speed of writing code, but by the confidence in deploying it. The same survey found the single most important skill developers now name for the AI era isn’t prompting — it’s “reviewing and validating AI-generated code for quality and security” (47%, the top answer).
That’s the thesis for the rest of this series. When generation is commoditised, verification is the discipline that creates value. And there’s evidence it pays: teams pairing AI generation with systematic automated verification report markedly better outcomes — in Sonar’s data, teams using a dedicated verification platform were 44% less likely to suffer AI-caused outages and reported 24% lower vulnerability rates than teams relying on ad-hoc checks.
The teams that win the agentic era won’t be the ones generating the most code. They’ll be the ones who can trust the most code — because they engineered review to keep pace.
Building the Review Pipeline
The instinct when faced with untrustworthy AI code is “review it more carefully.” That doesn’t scale, and we previously showed why: review effort is already the bottleneck. The answer isn’t more human review — it’s a pipeline where each layer catches the slop it’s best at, so that by the time a human looks, only the things requiring human judgement remain.
There are three layers, and the most important design decision is knowing which kind of problem each one owns.

Layer 1 — Deterministic gates (these block)
The first layer is rule-based static analysis, and its defining property is that it’s deterministic: the same code always produces the same finding. That makes it the only layer you can use as a hard gate — something that mechanically blocks a merge — because it never flakes and never hallucinates a violation.
This layer owns the slop categories that are mechanically detectable: security patterns, complexity, duplication, style, dependency risk. Tools like SonarQube, Semgrep, and the linter family (ESLint, Pylint, RuboCop) live here.
GATES = [ Gate("format", ["ruff", "format", "--check", "."]), Gate("lint", ["ruff", "check", "."]), Gate("types", ["mypy", "."]), Gate("sast", ["semgrep", "--config", "auto", "--error"]), # security patterns Gate("deps", ["pip-audit"]), # known-CVE deps Gate("dup", ["sonar-scanner"]), # duplication, complexity]
But generic gates miss the slop that’s specific to AI. Two checks earn their place in any AI-era pipeline:
Hallucinated-package detection. Slop invents imports. Before anything else, verify every new dependency actually exists — and is the one you meant, not a slopsquatted look-alike registered to catch exactly this mistake:
def check_new_dependencies(new_deps: list[str]) -> list[Finding]: findings = [] for dep in new_deps: meta = registry_lookup(dep) # query npm / PyPI if meta is None: findings.append(Finding("error", f"package '{dep}' does not exist — " "likely hallucinated (possible slopsquatting target)")) elif meta.age_days < 30 or meta.weekly_downloads < 50: findings.append(Finding("warn", f"'{dep}' is new/obscure — confirm it's the " "intended package, not a typosquat")) return findings
Contract and schema checks. “Looks right but isn’t” often means the code violates an interface the rest of the system depends on. If you have an OpenAPI spec, a DB schema, or typed contracts, validate the diff against them — a deterministic way to catch a whole class of subtle wrongness.
The rule for this layer: if a check is deterministic and the failure is unambiguous, it blocks. No human time is spent on a missing return type or a hallucinated import.
Layer 2 — Semantic AI review (this triages)
Deterministic gates can’t read intent. They’ll never catch “this function silently swallows the timeout error that two callers depend on” — that requires understanding the code’s meaning and its neighbours. This is the job of an LLM-backed reviewer (CodeRabbit, Greptile, Qodo, Claude Code Review, Copilot’s reviewer), and it catches the context-dependent slop the first layer can’t express as a rule: logic errors, architectural drift, missed edge cases, misread requirements.
The critical caveat: semantic review is variable, so it advises — it does not block. An LLM reviewer that hard-gates merges will eventually block on a hallucinated objection, and your team will route around it. Use it to triage and surface, not to enforce.
def ai_review(diff: str, repo_context: str) -> list[Finding]:
system = (
"You are a senior reviewer. Review this diff for LOGIC, ARCHITECTURE, and "
"EDGE-CASE issues a linter would miss. For each, give file:line, severity "
"(error/warn/note), and a one-line rationale. Do NOT report style or "
"formatting — gates already cover those. Return JSON only."
)
user = f"REPO CONTEXT:\n{repo_context}\n\nDIFF:\n{diff}"
return parse_findings(call_model(system, user))
Two things make this layer pay off rather than annoy:
Give it repo context. A reviewer that sees only the diff catches generic issues. One that sees the surrounding code, conventions, and call graph catches your issues — the violated invariant, the bypassed auth layer. (This is exactly the context engineering from the companion series: the reviewer is only as good as the context you feed it.)
Manage signal-to-noise ruthlessly. Review tools trade precision against recall, and the trade matters. In 2026 benchmarks, CodeRabbit favours precision (few false positives, so fewer misses get dismissed) while Greptile favours recall (catches more bugs at the cost of more noise). Pick deliberately: a noisy reviewer trains your team to ignore all its comments, including the true ones. Tune severity thresholds, scope it to the diff, and suppress the categories Layer 1 already owns.
The “AI-reviews-AI” pattern
Notice that the author of the code and the reviewer of the code should not be the same model instance with the same context. Letting an agent grade its own output is a conflict of interest — it’s confident about exactly the things it got wrong. The robust pattern is a separate reviewer model, with its own fresh context, critiquing the author’s output before any human sees it:
def review_pipeline(pr) -> ReviewResult:
findings = []
findings += run_deterministic_gates(pr) # Layer 1 — may block
if blocking(findings):
return ReviewResult(lane="RED", findings=findings) # don't waste later layers
findings += check_new_dependencies(pr.new_deps)
findings += ai_review(pr.diff, pr.repo_context) # Layer 2 — independent reviewer
return triage(findings, pr) # route to a human
This is the same independence principle that makes human peer review work, applied at machine speed: the grader is structurally separate from the author. Anthropic reported its own agentic reviewer marked under 1% of findings as incorrect and — the number worth showing a manager — raised the share of PRs receiving a substantive review from 16% to 54%. The point isn’t that AI review replaces humans; it’s that it clears the mechanical backlog so humans review more of what matters, not less.
Layer 3 — Human review (this judges)
The final layer is a human, and the pipeline’s whole purpose is to make sure they spend their attention only where human judgement is irreplaceable. The earlier layers handle correctness, security patterns, and context-dependent bugs. What’s left is what tools fundamentally can’t assess:
- Business logic and intent. AI reviewers can’t read your Jira, your Slack threads, or your product docs. They review code without knowing why it exists or what problem it’s meant to solve. A human confirms the code does the right thing, not just a correct thing.
- Architecture fit. Does this belong here? Does it set a precedent the team wants? Is there a simpler approach the agent didn’t consider?
- Acceptable-risk calls. Whether a known trade-off is worth shipping is a judgement, not a finding.
The reviewer should never receive a raw diff. They should receive a structured packet: what the gates found, what the AI reviewer flagged (and dismissed), what changed, and the blast radius. That packet — and how to route it by risk so humans aren’t reviewing everything — is next.
Routing, Trust & Governance
The previous pipeline catches slop, but it doesn’t by itself solve the scale problem from above. When an agent fleet opens thousands of pull requests, even a great pipeline ends with “route to a human” — and you don’t have thousands of humans. The missing piece is deciding which PRs need a human at all, and how much of one. That decision is risk-based routing, and it’s what makes review tractable at agent scale.
Stop reviewing everything equally
The reflex of treating every PR the same is what breaks under volume. A one-line copy fix and a change to the payments authorisation path get the same review queue, so either the trivial change waits behind deep reviews or the dangerous change gets the same skim as the trivial one. Both outcomes are bad.
The fix is to route by risk, not by volume. A practical and widely-adopted scheme is three lanes — Green, Yellow, Red — assigned by what a change touches, not how big it is:

- Green — low risk. Passed every deterministic gate, touches only low-blast-radius areas (docs, tests, internal tooling, copy). Auto-merge or a light human acknowledgement. Spending senior-reviewer time here is the waste you’re trying to eliminate.
- Yellow — medium risk. Typical feature code in non-critical paths. Gets the full AI review from before plus a targeted human spot-check guided by the structured findings — the reviewer looks where the pipeline pointed, not at the whole diff.
- Red — high risk. Touches authentication, authorisation, payments, data migrations, cryptography, or other security-sensitive surfaces. Mandatory deep human review, every time, no matter how clean it looks — because this is exactly where “looks right but isn’t” is catastrophic.
The risk map is just code, versioned in the repo — a CODEOWNERS-style file that classifies paths:
# review-risk.yml — risk lane by path (most-specific wins)
"**": yellow # default
"docs/**": green
"**/*.test.ts": green
"src/marketing/**": green
"src/auth/**": red
"src/payments/**": red
"**/migrations/**": red
"infra/**": red
def lane_for(pr, gate_findings) -> str:
if blocking(gate_findings):
return "RED" # gate failure is always deep-review
risk = max(risk_of_path(f) for f in pr.files_changed) # worst path wins
return risk # green / yellow / red
Note the “worst path wins” rule: a PR that touches docs and src/auth/ is Red. Slop hides in the one risky file inside an otherwise boring diff.
Make human reviewers effective, not just present
Routing decides who reviews. The other half of scaling is making each human review fast and high-signal. A reviewer handed a raw 600-line agent diff will skim and approve — the failure mode behind the previous verification gap. A reviewer handed a structured packet reviews with precision.
The packet for every Yellow/Red PR should contain, in priority order: what the deterministic gates found, what the AI reviewer flagged and what it explicitly dismissed, the blast radius (which risky paths and call sites are affected), and a tight diff summary — not the raw diff first. Pair it with a short, enforceable checklist the reviewer can actually complete:
AI-PR review checklist (reviewer answers YES to merge):
[ ] I understand WHY this change exists (linked issue / intent is clear)
[ ] Every new dependency is real, maintained, and the intended package
[ ] Error paths and edge cases are handled, not just the happy path
[ ] No auth / validation / data-access pattern was bypassed
[ ] It reuses existing utilities rather than re-implementing them
[ ] Tests exercise the actual behaviour, not just that it runs
[ ] Business logic matches intent (not merely "a correct thing")
The checklist encodes the slop taxonomy from above as questions. It turns “review this” — vague and skippable — into a finite, answerable task.
Measure trust, not speed
You manage what you measure, and measuring the wrong thing is how teams convince themselves AI is working while quality erodes. The trap is real: 2026 benchmark data found senior developers were actually 19% slower with AI on some tasks while feeling faster — a perception gap that hides the cost.
So retire raw velocity as the headline metric and track trust instead:
- Trusted-merge rate — share of PRs that pass the pipeline and merge without later revert or hotfix.
- Escaped-defect rate — slop that reached production, traced back to lane and cause. This is the number that tells you if your risk map is right.
- Substantive-review rate — share of PRs that got a real review, not a rubber stamp. (Anthropic moved this from 16% to 54% with agentic review; it’s a strong health signal.)
- AI-PR acceptance vs human baseline — LinearB’s benchmark put AI PRs at 32.7% acceptance against 84.4% for humans; watching your own ratio tells you whether your generation and review are improving.
The reframe from above, made operational: value isn’t lines shipped, it’s code you can deploy with confidence.
Govern it — including the AI you can’t see
Two governance realities bite at scale. First, shadow AI: 35% of developers in Sonar’s survey access AI tools through personal accounts, outside any sanctioned workflow — code entering your repos from tools your security team can’t see. Risk-based routing helps here, because it gates on what the code touches regardless of how it was produced. Second, policy has to be enforceable, not aspirational. Version your review policy and risk map in the repo; align your gates to a recognized standard (the OWASP LLM Top 10, NIST SP 800-218A); and make the pipeline — not a wiki page — the thing that enforces it. The payoff is measurable: teams with systematic verification reported 44% fewer AI-caused outages than those relying on ad-hoc checks.
Close the loop
The final move turns review from a cost into a compounding asset. Every piece of slop a reviewer catches is a lesson the system can keep. When a human rejects a PR for bypassing the auth layer, that shouldn’t just fix one PR — it should become a deterministic gate (a Semgrep rule), a line in the agent’s context file (AGENTS.md: “all handlers go through withAuth()“), or an entry in the risk map. The slop caught today becomes the slop prevented tomorrow.

That feedback loop is what makes review scale sub-linearly with generation. Without it, every new agent adds review burden forever. With it, the burden per PR falls as the system learns your patterns — the gates get smarter, the context files get sharper, and more PRs land safely in the Green lane.
The bottom line
Generating code is no longer the hard part, and it’s no longer where value lives. The 96% who don’t trust AI output are right to be cautious — but caution without infrastructure is just the verification gap. The teams that turn AI speed into shipped value are the ones that engineer review to scale: deterministic gates that block, semantic review that triages, humans routed by risk to where judgement matters, trust measured instead of speed, and a loop that turns every caught defect into a prevented one. Slop is inevitable at agent volume. Shipping it is a choice — and a solvable one.





