For most of software history, writing code was the expensive part.
A developer might spend hours or days implementing a feature, while review was a relatively small step at the end. AI coding tools have quietly flipped that equation. A model can now draft a function in seconds and produce an entire feature in minutes. In other words, producing code become cheap. Way too cheap. But the review (hopefully with human in the loop) is still expensive.
The bottleneck hasn’t disappeared. It has moved.
Today, the scarce resource is increasingly the work that comes after code generation: reading the code, understanding its behavior, testing it, identifying what is wrong, and deciding whether it is safe to ship.
This isn’t simply a matter of perception. Research on AI-assisted development has found that delivery stability can decline as teams adopt more AI, while developer trust in AI-generated code remains far from universal. In one controlled study of experienced open-source developers, AI assistance actually made participants about 19% slower on real-world tasks—even though they expected to be faster and believed afterward that they had been.
The extra time went into prompting, reviewing generated code, debugging it, and fixing things that didn’t quite work.
The lesson isn’t that AI coding tools are bad.
Quite the opposite: they are extremely good at making code cheap.
The problem is that everything downstream of code generation—understanding it, validating it, and trusting it—hasn’t become cheap at the same rate.
That changes where engineering teams need to invest.
Verification Is a Stack of Filters, Not a Single Gate
Code verification isn’t one activity.
It’s a stack of increasingly expensive filters, each designed to catch problems the cheaper layers missed:
- Type checkers and linters — fast and inexpensive, catching mechanical mistakes and violations of known rules before code runs.
- Automated tests — validate behavior that static checks cannot. A function can be perfectly typed and still return the wrong answer.
- Static analysis and security scanning — look for deeper structural, reliability, and security problems that ordinary linters and tests may miss.
- Human review — evaluates things machines struggle to judge reliably:
Is this the right design?
Does it fit the architecture?
Does it solve the actual problem?
Will someone be able to maintain it six months from now? - Production monitoring — the final safety net, detecting problems that survived everything before it.
These filters fall broadly into two categories.
Static analysis examines code without executing it.
It is fast and can cover enormous amounts of code, but it has limited visibility into what actually happens at runtime.
Dynamic analysis executes the software and observes its behavior.
Tests can tell you what the system actually does—but only for the paths and scenarios you test.
Neither is sufficient on its own.
That’s why mature engineering organizations don’t ask, “Which verification tool should we use?”
They ask, “Which combination of verification layers gives us enough confidence for this particular change?”
The Tradeoff Nobody Escapes: False Positives vs. False Negatives
Psst… if you ever wonder:
* False Positive: This means flagging something as a problem when the code is actually fine.
* False Negative: This means staying quiet while a real bug slips through.
Every verification system has to make a tradeoff.
Tune a tool aggressively and it catches more potential problems—but developers get buried in false positives. Tune it conservatively and the signal becomes cleaner—but real problems inevitably slip through.
The false-positive problem is particularly dangerous because it changes human behavior.
A tool that cries wolf often enough teaches developers to ignore it. Eventually, even the warnings that matter become background noise.
That’s how a security scanner or static analyzer can end up providing almost no practical protection—not because the technology stopped working, but because developers stopped trusting its output.
The goal, therefore, isn’t maximum detection.
The goal is actionable detection.
A useful finding is one that is sufficiently accurate, understandable, and relevant that a developer will actually investigate and fix it.
This creates a three-way tension between speed, accuracy, and coverage. You can optimize heavily for any two, but pushing all three to their theoretical maximum is difficult.
The practical objective isn’t a perfect verification system.
It’s a system that produces high-confidence signals at the speed developers work.
What AI Changes About the Verification Problem
AI-assisted development doesn’t simply produce more code.
It changes the shape of the review problem in two important ways.
1. More code, larger diffs (Psst… the key is to keep them as short as you can)
AI agents can generate significantly more code in a single interaction than a developer would typically write manually in the same amount of time.
That creates a predictable problem: review capacity doesn’t scale with generation capacity.
A developer can generate a 5,000-line pull request in minutes. They cannot meaningfully understand 5,000 lines in minutes.
As diffs get larger, review quality inevitably declines. Reviewers skim more, focus on obvious changes, and become increasingly dependent on automated checks.
The worst-case outcome is familiar:
A reviewer sees a huge AI-generated PR, verifies that it looks plausible, clicks “Approve,” and implicitly hopes production will catch whatever was missed.
That’s not verification. It’s rubber-stamping with better tooling.
2. Functional correctness is improving faster than security
AI models have become dramatically better at generating code that compiles, follows familiar patterns, and passes straightforward tests.
Security is a different story.
Studies evaluating AI-generated code across models have found substantial rates of known security weaknesses in generated output. In some benchmarks, a surprisingly large fraction of generated code contained vulnerabilities that had already been well documented.
The important point isn’t any single benchmark number. It’s the underlying pattern:
A model can become much better at writing code without becoming equally better at writing secure code.
Functional correctness and security are different dimensions of quality.
Improving one doesn’t automatically improve the other.
That distinction becomes increasingly important as AI moves from autocomplete to autonomous agents that can modify entire codebases.
The Trap of Letting AI Review AI
The obvious response to more AI-generated code is more AI-driven review.
And it works—to a point.
An AI reviewer can scan a change almost instantly, identify many bugs and security issues, check consistency, and apply the same review process to every change.
But there is a fundamental limitation.
When the model reviewing the code shares similar training data, patterns, assumptions, and failure modes with the model that generated it, the second opinion isn’t necessarily independent.
The reviewer may recognize that the code:
- compiles
- follows a familiar pattern
- resembles code it has seen before
- satisfies the literal requirements of the prompt.
None of those things proves that the implementation is correct.
The harder question is:
Does this code actually do what the system needs it to do?
Two similar models can end up producing one opinion twice.
That’s why AI review should be treated as another verification layer, not the final authority.
Independent signals matter.
Deterministic tests, static analysis, security tooling, runtime checks, and humans with domain context provide forms of verification that are meaningfully different from simply asking another model to look at the same code.
What Developers Should Do Differently
The answer isn’t to retreat from AI.
It’s to change the engineering process around it.
1. Keep AI-generated changes small
Don’t let an agent produce a 5,000-line change simply because it can. Ask for incremental changes that can be understood, tested, and reviewed independently.
A useful rule is:
If a human cannot reasonably review the diff, the diff is too large.
Smaller changes also make failures easier to localize and rollbacks safer.
2. Treat “LGTM” as a warning sign when you didn’t really review the code
If you find yourself approving an AI-generated PR because it looks plausible, you’ve quietly outsourced a judgment you’re still accountable for.
Review AI-generated code at least as critically as you would review code from a junior engineer.
In some respects, you should be more critical: an AI agent doesn’t understand your business context, your historical architectural decisions, or the consequences of subtle mistakes.
3. Don’t let AI be the only reviewer of AI
Use AI review. But pair it with deterministic verification.
That means:
- type checking
- linting
- unit and integration tests
- static analysis
- dependency scanning
- security analysis
- secret detection
- runtime monitoring
The important property is independence of signal.
A second model producing a second opinion is useful. A deterministic test producing a different kind of evidence is even more valuable.
4. Scale verification to risk
Not every change deserves the same verification process.
A copy change on a marketing page shouldn’t require the same scrutiny as a modification to payment processing, authentication, authorization, encryption, or data deletion.
Define risk categories and explicitly decide what verification each category requires.
For example:
- Low risk: automated checks + lightweight review.
- Medium risk: automated checks + AI review + human review.
- High risk: comprehensive automated testing + security analysis + human review by someone with relevant domain expertise.
The important part is making this a team policy rather than leaving it to individual judgment on every pull request.
5. Move security checks as far left as possible
The earlier you detect a problem, the cheaper it is to fix.
For secrets in particular, prevention matters more than cleanup. Secret detection should run before commits reach the repository, while controls such as DLP or AI gateways can help prevent sensitive credentials from being sent into AI systems in the first place.
Once a secret has been committed, logged, or transmitted to an external system, the problem is no longer simply “remove it from the code.”
You may need to assume it is compromised and rotate it.
6. Give agents real context
An AI agent working against a blank codebase has to infer your architecture, conventions, security assumptions, and design constraints.
It will make different assumptions on different runs.
Give it the information a good engineer would have before starting:
- architecture documentation
- coding conventions
- API contracts
- security requirements
- design constraints
- examples of preferred patterns
- explicit instructions about what it must not change
Better context produces better code—and, just as importantly, more predictable code that is easier to review.
7. Watch for code entropy, not just correctness
AI-generated code can be perfectly functional and still make a codebase worse.
Watch for: unnecessary abstraction, duplication, excessive complexity, inconsistent patterns, dead code, oversized functions, unnecessary dependencies and architecture that exists only because the model took the easiest local path.
These problems may not break today’s build.
They increase the cost of every future change.
And that cost compounds because future developers—and future AI agents—will have to understand the same mess before modifying it.
Don’t optimize only for code generated per hour.
Optimize for the long-term cost of changing the code.
8. Spend the time AI saves on judgment
This may be the biggest shift of all.
When writing code becomes dramatically cheaper, writing more code isn’t necessarily the highest-value use of engineering time.
The scarce skills become:
- knowing what should be built
- identifying ambiguous requirements
- understanding system-level tradeoffs
- recognizing dangerous edge cases
- deciding how much verification a change deserves
- determining when an implementation is technically correct but strategically wrong
AI can generate an implementation.
It is much harder for AI to determine whether that implementation should exist in the first place.
That’s where the engineering advantage increasingly moves.
The Bottom Line
The center of gravity in software development has shifted.
Writing code is becoming cheap. Trusting code is becoming expensive.
That doesn’t mean developers matter less. It means their highest-value work is moving up the stack.
The winning engineering teams won’t necessarily be the ones that generate the most code. They’ll be the ones that can generate code quickly without allowing verification to become the bottleneck that kills quality, security, or velocity.
That requires a deliberate verification stack: deterministic checks for deterministic problems, AI for the problems it is good at finding, humans for questions that require context and judgment, and production monitoring for everything that inevitably slips through.
The goal isn’t to eliminate human review.
The goal is to make human judgment scarce, focused, and valuable.
AI has made code generation abundant.
Now we need to make verification scale with it. Hopefully.
Based on reporting and an interview with Sonar CTO Andrea Malagoli, originally published by ByteByteGo.
Discover more from Ido Green
Subscribe to get the latest posts sent to your email.