Skip to main content
Code Smells

Code Smells Are Not Bugs: Stop Treating Them Like They Are

You've been told to fix every code smell your linter flags. That's a trap. Here's how to tell real problems from noise, and why reviewing for maintainability beats chasing perfect scores.

You've heard it a hundred times: "fix the code smells before they rot your codebase." Your linter flags a long method, your SonarQube dashboard shows a sea of yellow, and your team treats each one like a bug to squash. Stop it. A code smell is not a bug. It's a hint, a warning light, not a fire alarm. The contrarian truth: most code smells are harmless, and obsessing over them wastes your review time and, worse, distracts you from actual problems. This article is your myth-busting guide to handling code smells like a seasoned reviewer, not a robot.

What's the difference between a code smell and a bug?

Bugs are violations of requirements. They crash, corrupt data, or leak secrets. Code smells are structural characteristics that suggest deeper problems—things like duplicated blocks, long parameter lists, or high cyclomatic complexity. SonarQube, the static analysis heavyweight, explicitly separates these: reliability issues (bugs), security vulnerabilities, and maintainability issues (code smells) are distinct categories. A smell doesn't break anything today; it makes tomorrow's change riskier. That's a crucial distinction because it changes your response. You don't ship a hotfix for a smell; you file a refactor ticket. You don't block a merge over a smell unless it's truly egregious; you note it and move on.

Doesn't SonarQube's quality gate mean I should fix every smell?

No, and this is the biggest myth. SonarQube's built-in "Sonar way" quality gate—the one that's on by default—doesn't demand zero smells. It checks for four things in new code: no new issues, all new security hotspots reviewed, test coverage at least 80%, and duplication under 3%. Notice it doesn't say "zero smells." It says "no new issues," which includes smells, but the gate is about new code, not the whole codebase. And SonarQube itself rates maintainability on a scale from A to E based on technical debt ratio (under 5% is an A, over 50% is an E). An A rating doesn't mean no smells; it means the cost to fix them is less than 5% of the cost to rewrite the code. So a codebase with a few smells can still be an A. The gate is a tripwire for catastrophic regression, not a mandate for perfection. If you treat every smell as a merge blocker, you'll never merge anything.

Should I use automated tools to catch smells instead of human review?

Absolutely yes, but only for the mechanical stuff. Google's own engineering practices say linters, formatters, static analysis, secret scanners, and dependency scanners should handle style and mechanical checks so human reviewers can focus on logic and architecture. That's the division of labor that works. Tools like ESLint (for JavaScript) or Bandit (for Python) or gosec (for Go) are great at catching specific patterns—gosec even does taint analysis for SQL injection and path traversal. But they can't judge whether a class's design is sound or whether a function's complexity is worth the performance gain. That's your job. So let the tools flag the smells; you decide which ones matter. The tool is your assistant, not your boss.

What does a good code review actually look for?

If you're spending your review time counting duplicated lines, you're missing the point. Google's guidance is clear: the most important thing to cover is the overall design of the change. Then functionality, complexity, tests, naming, and comments. That's the order of importance. A smell like high cyclomatic complexity (which SonarQube defines as 1 + number of conditional branches) is a design smell—it hints the function is doing too much. But you should evaluate that in the context of the change: is this a new function that's trying to do three things? Or is it a legacy function that's being touched for the first time in years? The latter might be a known smell you accept. The former is worth discussing. So when you see a smell, ask: "Does this smell indicate a design problem that will hurt us later?" Not: "Does this violate the style guide?"

How do I know when a smell is actually a security problem?

That's the sneaky one. Some smells are just ugly; others are gateways to disaster. For example, a function that does too much might be a maintainability smell, but a function that does too much and handles user input might hide an injection flaw. OWASP's Top 10 for 2025 puts Injection at #5, and Broken Access Control at #1—both are often the result of code that's too tangled to see the vulnerability. The fix: use security-focused analyzers like Semgrep, Bandit, or gosec to catch the known dangerous patterns, and then use your human judgment to spot smells that could harbor them. If a function has a cyclomatic complexity of 20 and it's processing HTTP requests, that's a red flag—not because complexity is bad per se, but because it's hard to reason about security in a complex function. So treat high complexity as a smell that deserves extra security scrutiny, not just a refactor candidate.

So what should my team's policy be?

Here's my blunt recommendation: automate the mechanical checks, and for human review, use a severity label system. Google's own advice is to label comments by severity—nit, suggestion, blocking—and to offer solutions, not just problems. That's how you keep the conversation productive. A nit isn't a blocking issue; it's a nice-to-have. A suggestion is a better approach. A blocking comment is something that must be fixed before merge. Apply that to smells: a duplicated block in a new function might be a suggestion; a security hotspot that's unresolved is blocking. And remember Google's senior review principle: favor approving a change once it's in a state where it definitely improves overall code health, even if it's not perfect. There's no such thing as perfect code, only better code. So don't let a smell that's not affecting functionality or security hold up a good change.

To put it in concrete terms: imagine a pull request that adds a new authentication module. The linter flags a function with a cyclomatic complexity of 15 (it has a bunch of if-else branches for different login methods). The SonarQube quality gate passes because it's new code with 85% test coverage and no duplication. Your reviewer instinct says "this is a smell." But the function is well-tested, the logic is clear, and refactoring it now would delay the feature. What do you do? You approve with a comment: "This complexity is a bit high, but the tests cover it. Let's refactor in a follow-up." That's the LGTM with comments approach—Google explicitly says it's okay to approve when you're confident the developer will address minor comments later. So my policy is: smells are not blockers; they're conversation starters. Fix the ones that threaten security or maintainability, schedule the rest for later, and always keep the goal in mind—improving the overall health of the codebase, not achieving a perfect score.

What's the one thing to remember?

Code smells are not bugs; they're warnings. Treat them as hints to discuss, not errors to eliminate. Your review should focus on design and functionality, not on achieving a perfect static analysis score. Automate the mechanical checks, use severity labels, and approve changes that improve the codebase even if they're not perfect. That's how you keep your reviews fast, your team sane, and your code healthy.

Sources

  • Google Engineering Practices - What to look for in a code review - https://google.github.io/eng-practices/review/reviewer/looking-for.html
  • Google Engineering Practices - Standard of Code Review - https://google.github.io/eng-practices/review/reviewer/standard.html
  • SonarQube Server Docs - Understanding quality gates - https://docs.sonarsource.com/sonarqube-server/2026.1/quality-standards-administration/managing-quality-gates/introduction-to-quality-gates
  • SonarQube Server Docs - Understanding measures and metrics - https://docs.sonarsource.com/sonarqube-server/user-guide/code-metrics/metrics-definition
  • OWASP Top Ten 2025 - https://owasp.org/Top10/2025/0x00_2025-Introduction/

Share this article:

Comments (0)

No comments yet. Be the first to comment!