# How Does an Application Code Review Help Catch Problems Earlier?

A few years back I reviewed a pull request that added a single line to a payment handler. It looked fine. Ten minutes later I asked the author what happens if the webhook fires twice, and the room went quiet. That one question saved us from double-charging customers in production, and it cost us a fifteen-minute conversation instead of a weekend of refunds and apology emails.

That's the whole argument for application code review in one story. Not the ceremony, not the approval button, not the compliance checkbox. The value is that a second brain looks at your change while the change is still cheap to undo.

I want to unpack how that works mechanically, because "reviews catch bugs" is true but lazy. The interesting part is which problems get caught, why they surface at review time instead of later, and what makes some teams get almost nothing out of the practice.

## The cost of a defect climbs fast, and review sits near the bottom of the curve

Barry Boehm popularised the idea decades ago that defects get more expensive the later you find them, and while the exact multipliers people quote (1x in design, 100x in production) are often repeated without much rigour behind them, the direction holds up in any team I've worked with. The reason is simple enough to reason about without statistics.

When I catch a problem in review, the author still has the mental model loaded. They wrote the code yesterday. They remember why they picked that data structure. The fix is a commit.

When the same problem shows up in production six months later, the picture changes. Someone has to reproduce it from a Sentry stack trace, find the original author (who may have left), reconstruct the intent, write a regression test, patch it, and ship a hotfix through whatever release process exists. There's also the part nobody budgets for: the incident channel, the status page, the customer who churns.

So the earliest point at which a human can plausibly spot the issue is where you want the net. Static analysis catches some of it. Tests catch a different slice. Review catches the category that neither one can: intent mismatches.

## What an application code review catches that tooling cannot

Linters find style. Type checkers find shape errors. Tests find the failures you thought to write a test for. All three are enormously useful and all three are blind to the same thing, which is whether the code does what the business actually needed.

Here's a representative example. Express endpoint, looks totally reasonable:

![Code review example: vulnerable Express PATCH endpoint missing an ownership check and open to mass assignment](https://cdn.hashnode.com/uploads/covers/681d8caa35a8fbc31aea8203/f15fd06c-be60-41c1-8dd1-0947bfce578a.png align="center")

Tests pass. Types are fine. The linter is happy. A reviewer reads it and asks two questions:

- What stops me from PATCHing someone else's user ID? requireAuth proves you're logged in, not that you own this record.
- What stops me from sending a request body with "role": "admin" in it?

Both are privilege escalation bugs, and both are invisible to automated tooling because the code is syntactically perfect. The fixed version needs an ownership check and an explicit allow list:

![Code review fix: the same Express endpoint with an ownership check and an allow list of permitted fields](https://cdn.hashnode.com/uploads/covers/681d8caa35a8fbc31aea8203/159fb121-78cc-4c25-843a-fd7339e3b5fb.png align="center")

Mass assignment has been on the OWASP radar for years and it still ships constantly, because the vulnerable version is shorter and reads better. A reviewer with security on their mind catches it in under a minute. A penetration test catches it in month nine, at a price.

## The four categories worth reviewing for

I keep a rough mental checklist, and it maps to four kinds of problem that surface at different speeds.

**Bugs you can see.** Off-by-ones, null handling, missed error branches, retry logic that isn't idempotent. These are the ones people picture when they hear code review, and they're genuinely worth catching, though in a team with decent test coverage they're the smallest category.

**Security gaps.** Missing authorisation checks, string-concatenated SQL, secrets in config, overly permissive CORS, tokens logged in plaintext. Security review works well at the PR level because the question is usually local: who can call this, with what, and what happens if they lie about who they are.

**Maintainability.** Will the next person understand this in a year? Is the abstraction carrying its weight or is it one more layer to trace through at 2am? I've killed more clever code in review than I've killed bugs, and I think that was the higher-value work. Maintainability problems don't page anyone. They just slow every future change by five percent until nobody can estimate anything.

**Coding standards and consistency.** Low-glamour and high-leverage. When every module handles errors the same way, names things the same way, and structures its tests the same way, reviewers can scan for substance instead of burning attention on surface decisions. Push as much of this into automated formatters and linters as you can, then use review for the parts a tool can't express, like naming that matches the domain language rather than the database schema.

## Reviews are where technical debt gets priced

This is the benefit teams underrate most. Technical debt rarely arrives as one bad decision. It accumulates through a hundred small, individually defensible shortcuts that nobody said out loud.

Review is the moment where saying it out loud is cheap. Someone writes "this duplicates the logic in BillingService, fine for now?" and the team makes a conscious choice. That's debt with a note attached. Compare it to the alternative, where the duplication ships silently and is discovered two years later by someone who has no idea which copy is authoritative.

I've watched projects stall out for exactly this reason. Not a dramatic failure, just a gradual slide where velocity drops, estimates stop meaning anything, and every sprint carries more unplanned work than planned work. By the time leadership notices, the fix is no longer a refactor, it's an archaeology project. If you want a concrete sense of what reviewers should be hunting for before things get to that stage, [The 48-Hour Code Audit: What We Look For](https://www.jhavtech.com.au/pdf-resource/free-48hrs-code-audit-pdf) lays out a practical set of checks.

And when a codebase has already crossed that line, review alone won't save it. At that point you're looking at a structured intervention, and there's useful ground to cover on [getting a stalled codebase back on track](https://www.jhavtech.com.au/software-project-rescue-service) before you commit to a rewrite you probably don't need.

## How to run an application code review that catches things early

Most teams that complain reviews don't work are running them in a way that guarantees that outcome. Three things make the difference.

### Keep the diffs small

A 50-line PR gets read. A 2,000-line PR gets approved. Cisco's often-cited internal study on review practice found reviewer effectiveness degrades sharply past a few hundred lines in a sitting, which matches my experience to an uncomfortable degree. If a change can't be reviewed in 30 minutes, split it. Stacked PRs are awkward tooling-wise and still better than the alternative.

### Review early, not just at the end

The highest-value review I do is often on a draft PR with three files and a rough shape. That's when direction is still negotiable. By the time the feature is complete, polished, and demoed to the PM, nobody wants to hear that the data model is wrong, so they don't say it.

### Ask questions, don't issue verdicts

"Why a map here instead of a set?" invites the author to explain, and half the time they're right and I learn something. "Use a set" invites a defensive reply and teaches nobody anything. Tone sounds like a soft concern until you notice teams where reviews have become adversarial. People start routing around them, approving quickly to avoid conflict, and the net catches nothing.

## What early buys you

Shift the discovery of a problem left by a few weeks and three things change: the fix is smaller, the person fixing it is the person who caused it, and no customer was involved. Application code review is one of the few practices that does all three at once, for roughly the cost of an hour a day across the team.

It won't catch everything. It's weak on performance under real load, on integration behaviour, and on anything emergent across services. You still need tests, observability, and staging that resembles production. What review gives you is a consistent, human checkpoint where intent and implementation get compared while both are still fresh, and that's a point in the lifecycle nothing else occupies.

Start with the smallest version that works. Two people, small diffs, questions instead of commands. Build from there.
