We use AI agents as coding assistants (here Claude Code) in our daily work. Not in a "vibe coding" way where you throw a prompt and hope for the best. We practice spec-driven development: detailed specifications, structured plans, context engineering through CLAUDE.md files, custom instructions, and carefully scoped tasks. The goal is to give the AI enough context to produce good code from the start.
And it mostly works. The code we get is solid. But "mostly" isn't enough when you're shipping to production. Even with good context and clear specs, an AI assistant can still produce code with security issues, excessive complexity, or subtle smells that you'd catch in a thorough review — if you had the time.
So we added SonarQube to the loop, and we let the AI fix what Sonar finds.
The setup
If you want to rely on CI based Sonar scans and reports, you need to have non-free options :
- Sonar cloud subscription
- SonarQube sever, developer edition (the community edition does not help much).
We wanted something that runs locally, requires no manual steps, and integrates directly into the AI agent's workflow.
A local SonarQube instance via Docker Compose:
services:
sonarqube:
image: sonarqube:lts-community
ports:
- "9000:9000"
volumes:
- sonar_data:/opt/sonarqube/data
A scan script (scripts/sonar-scan.sh) that handles the full lifecycle:
- checks that SonarQube is up
- auto-provisions an auth token on first run
- creates the project if needed
- runs the scanner via Docker
- waits for analysis to finish
- fetches all issues
- and writes them to a structured
sonar-report.jsonfile.
Each issue comes with a file path, line number, severity, rule ID, and a description. This structured output is what makes the automation possible — the AI agent can parse it and act on each issue directly.
A Claude Code slash command (.claude/commands/sonar.md) that ties it together. When you run /sonar, the agent:
- Runs
bash scripts/sonar-scan.sh - Reads the report — issues sorted by severity (BLOCKER, CRITICAL, MAJOR...)
- For each issue: reads the file, understands the rule violation, applies a minimal fix
- Skips generated code (API clients, etc.)
- Runs
npm run type-checkto make sure fixes don't break anything - Re-scans. If new issues appeared, fixes those too.
- Reports what it changed and how many iterations it took.
CI enforcement via GitHub Actions on every PR:
- name: SonarQube Scan
uses: SonarSource/sonarqube-scan-action@v5
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
- name: Quality Gate
uses: SonarSource/sonarqube-quality-gate-action@v1
How the loop works in practice
We recently built a feature with several Lit web components, services, and types — version history with diffs, draft management, conflict resolution dialogs. The AI generated it from detailed specs. The code worked, tests passed, TypeScript was happy.
Then we ran /sonar. Sonar found things like:
- Cognitive complexity too high in some diff rendering logic
- Unused imports left over from refactoring iterations
- Duplicated string literals across components
- Missing type narrowing where null dereferences were possible
- Missing ARIA attributes on interactive elements
None of these were show-stoppers. All of them were things you'd want fixed before merging. The agent resolved them in 2-3 scan cycles without intervention.
The point isn't that the AI wrote bad code. The point is that static analysis catches a class of issues that are easy to miss during generation — and the AI is perfectly capable of fixing them when told what's wrong.
Why Sonar and not just a linter
ESLint catches a lot, but SonarQube covers ground that linters don't:
- Security vulnerability detection (injections, hardcoded secrets, insecure patterns)
- Cross-file duplication detection
- Cognitive complexity scoring
- A quality gate — a binary pass/fail, not just a list of warnings
Combined with TypeScript's type checker and your test suite, you get three independent verification layers, each catching different categories of problems.
What this looks like day-to-day
Our workflow before a PR:
- Write specs and plan the feature
- AI implements it with full context (CLAUDE.md, specs, existing code)
- Run
/sonar— the agent scans, fixes, re-scans until clean - Verify that typecheck and tests still pass
- Review the diff and open the PR
The Sonar step typically adds a few minutes. It's not something we run after every edit — just before we consider a feature done. Think of it as an automated pre-review pass.
Closing thoughts
If you use AI assistants for coding, you already have good reasons to invest in context engineering — giving the AI the right specs, the right constraints, the right examples. SonarQube is another form of that same idea: giving the AI structured feedback about what's not right, and letting it iterate.
The scan script, the slash command, and the CI workflow are all straightforward to set up. SonarQube Community Edition is free. The scripts are plain bash. This works with any AI coding tool that can run shell commands and read structured output.
The code is on GitHub if you want to look at the implementation.
Top comments (0)