← All Articles 📈 Deep Code Analysis High By Nishant Kumar Verma Updated

SonarQube for AI-Generated Code: Deep Analysis Beyond Linting

ESLint catches style issues. SonarQube catches bugs, security hotspots, and technical debt. Learn what enterprise-grade static analysis reveals.

SEC-002 QUA-006 QUA-008 QUA-010 PERF-003 TST-001

Quick Answer

ESLint enforces code style and catches surface-level errors. SonarQube performs deep static analysis: it finds bugs, security vulnerabilities, security hotspots, code smells, duplicated blocks, and tracks technical debt across your entire codebase. For AI-generated code, which tends to pass ESLint while hiding deeper structural problems, SonarQube provides a second layer of analysis that catches what linting misses.

Why ESLint Is Not Enough for AI-Generated Code

ESLint is a linter. It parses your JavaScript or TypeScript file, checks it against a set of rules, and reports violations. Rules cover things like unused variables, inconsistent quotes, missing semicolons, and some common error patterns. ESLint is excellent at what it does, and tools like Bolt, Cursor, Lovable, and v0 typically generate code that passes ESLint cleanly.

That is part of the problem. AI-generated code is syntactically well-formed. It follows the patterns ESLint knows about. But ESLint does not understand cross-file dataflow, does not track whether a value from an untrusted source reaches a sensitive operation, and does not measure complexity or duplication across the whole project. It is a file-by-file rule engine, not an application-level analyzer.

Static analysis that goes beyond linting, such as data-flow and taint tracking, finds bugs and security issues that a linter is not built to catch. For AI-generated code specifically, that gap is larger because AI tools produce code that is optimized for passing obvious checks while omitting the structural discipline that prevents deeper issues.

What SonarQube Actually Analyzes

SonarQube runs a suite of analysis engines against your codebase. For a Next.js or Node.js app, the key categories are:

SQ-BUG - Bugs: Code that will behave incorrectly at runtime. Examples include reading a property of a value that is null or undefined, and returning inside a finally block (which discards the exception). These are not style issues - they are defects.

SQ-VULN - Vulnerabilities: Code patterns that are directly exploitable. SQL injection via string concatenation, OS command injection via exec(), and using Math.random() for security-sensitive operations all appear here. Sonar's vulnerability rules overlap with OWASP Top 10 coverage.

SQ-SPOT - Security Hotspots: Code that requires human review because it could be a vulnerability depending on context. You decide whether each is exploitable, but SonarQube ensures you see them. Sonar is moving hotspot rules into the vulnerability category: in its current JavaScript and TypeScript analyzer, former hotspot rules such as eval(), SQL query formatting, and weak hashing now raise vulnerabilities.

SQ-SMELL - Code Smells: Maintainability issues that make code harder to understand and change. Functions that are too long, too deeply nested, or have too many parameters; duplicated logic; variable names that shadow outer scope. AI code is especially prone to smells because it generates the most immediately functional structure, not the most maintainable one.

SQ-DEBT - Technical Debt: SonarQube gives each maintainability issue an estimated remediation time (set per rule) and adds them up, giving you a "technical debt ratio" - the time it would take to fix those issues versus an estimate of the time it took to write the code (30 minutes per line by default). For AI-generated apps, this number is often surprisingly high because AI generates a lot of code quickly.

SQ-DUP - Duplications: Blocks of code that are repeated across files. AI tools frequently copy patterns wholesale, creating duplicate logic that must be updated in multiple places. SonarQube reports both the percentage of duplicated lines and the exact file pairs.

SQ-COV - Coverage: Code coverage from your test suite, integrated into the quality gate. If you have tests (or lack them - which is common in vibe-coded apps), SonarQube shows uncovered lines and fails the build when coverage drops below your threshold.

ESLint vs SonarQube: Capability Comparison

Capability ESLint SonarQube
Syntax errors and style Yes Yes
Unused variables / imports Yes Yes
Cross-file dataflow analysis No Yes (Developer+ edition)
Taint tracking (user input → sink) No Yes (Developer+ edition)
Security hotspot detection No Yes
OWASP Top 10 mapping Partial (plugins) Yes (built-in)
Code duplication detection No Yes
Technical debt estimation No Yes
Test coverage integration No Yes
Quality gate (CI pass/fail) Via exit code Yes (configurable thresholds)
Historical trend tracking No Yes
Branch analysis No Yes (Developer+ edition)

Real Examples: What SonarQube Catches That ESLint Misses

Here is a typical pattern from AI-generated Next.js code. ESLint's recommended rules see no problem. It still hides a bug and a code smell.

// ❌ BAD - passes ESLint's recommended rules, hides a bug and a code smell
export async function getUser(req, res) {
  const user = await prisma.user.findFirst({
    where: { email: req.body.email }
  });

  // Bug: user may be null - reading .id will throw
  const posts = await prisma.post.findMany({
    where: { authorId: user.id }
  });

  // Code smell: function does too much, deeply nested logic follows
  if (posts.length > 0) {
    for (const post of posts) {
      if (post.published) {
        if (post.views > 1000) {
          await prisma.post.update({
            where: { id: post.id },
            data: { featured: true }
          });
        }
      }
    }
  }

  res.json({ user, posts });
}
// ✅ GOOD - null guard + extracted helper + early return
export async function getUser(req, res) {
  const user = await prisma.user.findFirst({
    where: { email: req.body.email }
  });

  if (!user) {
    return res.status(404).json({ error: 'User not found' });
  }

  const posts = await prisma.post.findMany({
    where: { authorId: user.id }
  });

  await flagHighPerformancePosts(posts);
  res.json({ user, posts });
}

async function flagHighPerformancePosts(posts) {
  const ids = posts
    .filter(p => p.published && p.views > 1000)
    .map(p => p.id);

  if (ids.length === 0) return;

  await prisma.post.updateMany({
    where: { id: { in: ids } },
    data: { featured: true }
  });
}

Be precise about which tool catches what. Prisma's findFirst() returns null when no row matches, and TypeScript in strict mode reports 'user' is possibly 'null' at user.id. Sonar's own null-dereference rule (S2259) only fires when a value is definitely null or undefined, so it stays quiet here. The nesting is what Sonar's S134 (nesting depth) and S1066 (mergeable if statements) rules flag, but neither is in the default Sonar way profile, so turn them on in your quality profile.

How to Add SonarQube to a Vibe-Coded Project

The fastest path for a solo founder or small team is SonarCloud (now called SonarQube Cloud), the hosted version of SonarQube. It is free for public repositories, and its free plan also covers private projects up to 50,000 lines of code.

  1. Sign up at sonarcloud.io and connect your GitHub, GitLab, or Bitbucket account.
  2. Import your repository. SonarCloud auto-detects JavaScript, TypeScript, and most frontend frameworks including Next.js and React.
  3. Add the analysis to CI. For GitHub Actions, add the SonarSource/sonarqube-scan-action step (it replaces the deprecated sonarcloud-github-action). It takes under five minutes to configure.
  4. Set a quality gate. The default "Sonar way" gate blocks merges when new code introduces bugs or security vulnerabilities. This is the most valuable setting for AI-assisted development, because every AI-generated commit gets checked before it merges.

Running static application security testing (SAST) in your CI/CD pipeline catches security issues before they reach production, instead of after deployment.

How to Interpret SonarQube Results for AI-Generated Code

When you first scan a vibe-coded project, expect a high issue count. This is normal and not a reason to panic. Prioritize in this order:

  1. Vulnerabilities first (SQ-VULN): These are confirmed exploitable patterns. Fix before any public launch.
  2. Security hotspots second (SQ-SPOT): Review each one manually. Most will be false positives or acceptable risk, but some will be real vulnerabilities.
  3. Bugs third (SQ-BUG): These cause runtime failures. Fix before the code goes to production users.
  4. Code smells and debt (SQ-SMELL, SQ-DEBT): Address these iteratively. Do not try to fix all smells in one sprint.
  5. Duplications (SQ-DUP): Track the percentage. If it is above 15%, you have meaningful architectural debt. Address before the codebase grows further.

Tools like VibeDoctor's Vibe Check run OpenGrep SAST rules, language linters and type checkers, Gitleaks, and Trivy on your repository, and report each finding with its file path and line number. Free to sign up.

FAQ

Is SonarQube free to use?

SonarQube Community Build (formerly Community Edition) is free and open source. SonarCloud (the hosted version, now SonarQube Cloud) is free for public repositories, and its free plan also covers private projects up to 50,000 lines of code. Larger private codebases need a paid plan. The Community Build can be self-hosted at no cost.

Can SonarQube replace ESLint?

No, and you should not try to make it. ESLint runs in milliseconds in your editor and gives instant feedback while you type. SonarQube is a CI tool that analyzes the full repository. They are complementary. Run both: ESLint in your editor and pre-commit hook, SonarQube in your CI pipeline on every pull request.

How long does a SonarQube scan take?

For a typical Next.js or Node.js project, SonarCloud analysis runs as one step in your CI pipeline. The first scan takes slightly longer as it builds the baseline. Subsequent scans are incremental and faster.

Does SonarQube work with TypeScript and Next.js?

Yes. SonarQube has first-class support for TypeScript and understands React component patterns. It reads your tsconfig.json and respects your project structure.

What is a quality gate and should I block deploys on it?

A quality gate is a set of conditions that must pass before code is considered releasable. The default Sonar way gate fails on any new vulnerabilities or bugs in new code. Yes, you should block deploys on it - this is the main value of the tool. It forces every AI-generated commit to meet a minimum quality standard before it reaches production.

Diagnose your codebase - free

VibeDoctor checks for SEC-002, QUA-006, QUA-008, QUA-010, PERF-003, TST-001 and 148 other issues across 21 diagnostic areas - security, performance, code quality, and more.

SCAN MY APP →