Stopping the Leak: Using SonarQube Quality Gates to Manage Technical Debt
SonarQube Quality Gates decide whether a pipeline passes. Configure the New Code baseline, poll gate status from CI, and choose which conditions are worth blocking on.
15 Apr 2026, 07:11 UTC

The dilemma of the legacy codebase
Most teams hit the same wall: a large existing codebase with thousands of smells, vulnerabilities, and low test coverage. Set a global requirement such as 80% test coverage and the build fails on day one, so the team spends weeks fixing old code instead of shipping. Disable the checks and new debt leaks in unnoticed.
The practical answer is a Quality Gate evaluated against New Code. Rather than boiling the ocean, the gate acts as a circuit breaker in the CI/CD pipeline: legacy code stays imperfect, but new regressions stop at the merge request.
Defining the New Code baseline
A Quality Gate is a set of conditions a project must meet to be marked PASSED. The most consequential setting is what counts as New Code, sometimes called the leak period. In SonarQube 9.9 LTS through 10.x you can choose:
- Previous version: anything changed since the last version bump counts as new.
- Number of days: a rolling window, for example 30 days. Useful when the project has no formal versioning.
- Specific analysis: a fixed analysis used as the baseline.
- SCM reference branch: compares the current branch against a target branch such as
main. This fits pull-request workflows.
Changing this definition mid-project resets the baseline. Switch from previous version to 30 days and historical trends stop being comparable, while issues previously counted as old can reappear as new. Record the change and its rationale in the project README so an audit does not have to reconstruct it.
Making the pipeline wait for the gate
Running the scanner only uploads data. The server evaluates the gate afterwards, so a pipeline that exits right after the scanner can report success while the gate is still computing. Poll the status endpoint instead.
Example: polling gate status from a CI job
Run this after sonar-scanner completes in the same job. It needs an API token that can browse the project and read analysis status; inject the token from the CI secret store rather than committing it.
PROJECT_KEY="my-app-service"
SONAR_URL="https://sonarqube.example.com"
TOKEN="$SONAR_TOKEN"
deadline=$((SECONDS + 300))
while true; do
STATUS=$(curl -s -u "$TOKEN:" \
"$SONAR_URL/api/qualitygates/project_status?projectKey=$PROJECT_KEY" \
| jq -r '.projectStatus.status')
if [ "$STATUS" != "IN_PROGRESS" ] && [ -n "$STATUS" ]; then
break
fi
if [ "$SECONDS" -ge "$deadline" ]; then
echo "Timed out waiting for the Quality Gate"
exit 2
fi
sleep 10
done
if [ "$STATUS" = "FAILED" ]; then
echo "Quality Gate failed; see the dashboard for failing conditions"
exit 1
fi
echo "Quality Gate status: $STATUS"
Expected check: once evaluation finishes the endpoint returns PASSED, WARN, or FAILED. If a developer adds a blocker issue or new-code coverage falls below the threshold, the job exits non-zero and the deploy step never runs. The timeout matters: without it, a SonarQube outage leaves the job hanging until the runner's own limit kills it.
Trade-offs: what to block on and what to warn on
The built-in Sonar way gate is read-only. To change thresholds you copy it to a custom gate, then assign that gate to projects or set it as the organization default. Two consequences are easy to miss: changing the organization default affects new projects only, and existing project associations are not migrated automatically; and issues imported through the generic issue format are not evaluated by gate conditions unless they map to SonarQube rule types with a severity.
| Condition | Blocking posture | Warning posture |
|---|---|---|
| New security rating | Must be A | B or better |
| New coverage | Above 80% | Above 50% |
| New reliability | No blocker or critical issues | Blockers only |
A common compromise is to block on security and reliability ratings while only warning on coverage, so an urgent hotfix is not held hostage to a coverage chase. The cost is that coverage debt accumulates quietly; watch the warning trend rather than ignoring it.
Verifying the setup
Create a test project, assign a custom gate with a single condition such as new-code coverage below 50%, and run the scanner on a branch with known low coverage. Query /api/qualitygates/project_status?projectKey=YOUR_KEY and confirm the status matches what the dashboard shows. Then enable pull-request decoration for your SCM host and open a pull request that introduces a blocker issue; the decoration comment should name the failing condition and its actual value.
To undo a threshold change, edit the custom gate in the SonarQube UI or reassign the project to Sonar way. Both are server-side configuration changes, so note the previous values before editing if you may need to restore them.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.