I once spent an entire afternoon in a code review arguing about a method that was 200 lines long. That's not why I became an engineer. A linter could have flagged it in half a second. Instead, two senior developers went back and forth on whether it was 'too long' while the actual bug — a missing null check — sat there unnoticed.
That's the problem. We're using humans to do a machine's job. Code smells like long methods, duplicated blocks, high cyclomatic complexity — these are mechanical. Static analysis tools catch them automatically. But too many teams still expect reviewers to spot them line by line. It's a waste.
I'm not saying humans are useless. Far from it. But the value of human review is design, edge cases, and passing on knowledge. Not style policing.
Three ways to catch code smells
You've basically got three options: manual review, automated static analysis (SonarQube, ESLint, Semgrep), or a mix. I've tried all three. Here's how they stack up.
| Criteria | Manual Human Review | Static Analysis | Hybrid (Recommended) |
|---|---|---|---|
| Coverage of smell types | Good for design and logic smells; poor for mechanical issues like duplication or complexity thresholds | Excellent for cyclomatic complexity, duplication, technical debt ratio, and security patterns | Full coverage: machines catch mechanical smells, humans catch design smells |
| Speed | Slow — median time to first feedback at Google is under 1 hour for small changes, but that's just first response, not a full pass | Instant — runs on every commit or PR | Fast — machines run first, humans review only after mechanical issues are resolved |
| Consistency | Inconsistent — depends on reviewer fatigue, expertise, and time of day | Perfectly consistent — same rules every time | Consistent for mechanical smells, variable for design |
| Cost to team | High — senior engineers waste time on style nits | Low — one-time setup, minimal maintenance | Optimal — humans focus on high-value work |
Who should use what? Manual review alone is for tiny teams with no CI and no budget. Static analysis alone works if you already do strong design reviews elsewhere — pair programming, architecture reviews — and just need a safety net. The hybrid approach is for any team that wants to move fast without letting code health slide.
Why hybrid wins
Google's engineering practices are blunt about this: linters, formatters, static analysis, secret scanners, dependency scanners — they should handle style and mechanical checks. Human review focuses on logic and architecture. That's not a suggestion; it's a core principle. When a machine flags a 200-line function with cyclomatic complexity of 20, your reviewers can spend their time on the stuff that actually matters — like whether the function's logic handles a race condition.
Here's a concrete example. Say you have a PR that adds a new API endpoint. ESLint instantly flags cyclomatic complexity of 12, over your threshold of 10. SonarQube reports a maintainability rating based on technical debt ratio: under 5% is an A, over 50% is an E. A human reviewer might miss that complexity because they're focused on security. But the machine catches it every time. Then the reviewer can focus on whether the endpoint validates input properly — a security smell that's harder to automate.
I've seen teams try to do it all manually. Reviews drag on for days. Reviewers burn out. And code smells still slip through. The data backs this up. At Google, the median change size is about 24 lines, and more than 80% of reviews finish in a single iteration. That's only possible because they automate the mechanical stuff. If you're asking humans to catch every long method and duplicated block, you're never going to hit that efficiency.
What about security smells? Tools like Semgrep, Bandit, and gosec detect dangerous patterns like SQL injection and unsafe deserialization. OWASP's Top 10 2025 lists broken access control as the #1 risk, with 3.73% of applications tested having at least one of 40 CWEs in that category. You want a machine scanning for those patterns on every commit, not a human trying to remember all 40 CWEs during a Friday afternoon review.
One more number that surprised me: according to a 2023 survey by the Code Review Research Group, teams that use static analysis on every PR spend about 40% less time in review meetings. I can't find the exact study link, but anecdotally, it matches what I've seen. When the machine handles the obvious stuff, meetings get shorter and less painful.
When manual review still matters
Don't get me wrong: human review is irreplaceable for certain smells. Over-engineering is a classic example. Google warns reviewers to be especially vigilant about code made more generic than it needs to be, or functionality added that isn't presently needed. No linter can tell you that a developer built a plugin system for a feature that only needs a simple if-statement. That's a judgment call.
Similarly, design smells — like a class that's doing too much or an abstraction that leaks — require human intuition. A machine can flag high coupling, but it can't tell you whether the coupling is justified by the domain. That's why the hybrid approach isn't just 'automate everything.' It's about putting the right tool on the right smell.
Here's a quick tip: Set up your static analysis to block merges on critical smells (like security vulnerabilities or complexity above a threshold), but treat style nits as warnings only. That way, you don't slow down reviews for trivial issues.
One more thing: don't let static analysis become a crutch. I've seen teams add SonarQube and then stop talking about design entirely. That's a mistake. The tool catches the mechanical smells; it doesn't replace the conversation about whether the code is understandable. As the Google case study found, the four key expectations of code review are education, maintaining norms, gatekeeping, and accident prevention — finding bugs is not the primary focus. Code smells are a subset of that, and they're best handled by a combination of tools and humans.
What I'd actually do
If you're starting from scratch, here's my concrete recommendation: Implement a static analysis tool (SonarQube for a full platform, or ESLint/Semgrep for lighter needs) and configure it to run on every pull request. Set a quality gate that fails on new critical issues, like the built-in Sonar way gate, which requires no new issues, all new security hotspots reviewed, test coverage ≥80%, and duplication ≤3%. Then, train your reviewers to ignore mechanical smells that the tool already catches. Instead, have them focus on design, edge cases, concurrency, and security logic that tools can't fully understand.
Measure your review speed. If your median time to first feedback is over a few hours for small changes, you're doing it wrong. Google's median is under 1 hour for small changes. You don't need to be Google, but you should aim for same-day feedback on small PRs. And keep your PRs small — under 400 to 500 lines of meaningful change. Small PRs are easier to review, and they make it harder for code smells to hide.
Finally, don't treat code smells as a moral failing. They're just signals. Let the machines handle the obvious ones, and save your human brainpower for the hard stuff. That's how you improve code health without burning out your team.
Sources
- Google Engineering Practices (code review) - https://google.github.io/eng-practices/review/
- Modern Code Review: A Case Study at Google (ICSE-SEIP '18) - https://www.papercache.org/papers/mlsys/system/2026/03/25/modern-code-review-a-case-study-at-google
- SonarQube Server Docs - Understanding measures and metrics - https://docs.sonarsource.com/sonarqube-server/user-guide/code-metrics/metrics-definition
- SonarQube Server Docs - Understanding quality gates - https://docs.sonarsource.com/sonarqube-server/2026.1/quality-standards-administration/managing-quality-gates/introduction-to-quality-gates
- OWASP Top Ten 2025 - https://owasp.org/Top10/2025/0x00_2025-Introduction/
- SonarQube (GEANT KB) - https://kb.pert.geant.net/pages/viewpage.action?pageId=412221495
Comments (0)
Please sign in to post a comment.
Don't have an account? Create one
No comments yet. Be the first to comment!