Using SonarQube's New-Code Quality Gate to Enforce Clean as You Code
Learn how to configure SonarQube's Quality Gate to evaluate only New Code, enabling the Clean as You Code strategy in a typical CI pipeline with Maven and GitHub Actions.
30 May 2026, 01:39 UTC

Problem: Legacy issues drown out real quality improvements
Many teams inherit a large backlog of existing code issues. When a Quality Gate is configured to fail on any issue, the gate blocks every change, even trivial fixes, because the overall issue count never drops. This creates frustration and discourages frequent commits.
Thesis: Gate only on New Code so quality improves incrementally
SonarQube can evaluate a Quality Gate separately for New Code (code added or changed since a reference point) and for Overall Code. By defining New Code as the changes since the main branch and requiring only New-Code metrics to pass, teams adopt the Clean as You Code strategy: legacy debt remains visible but does not block new work.
How the New Code definition works
SonarQube offers four ways to decide what counts as New Code:
- Reference branch – compares against a branch (usually
mainormaster). - Previous version – uses the last analyzed version.
- Number of days – treats anything newer than N days as new.
- Specific version – a fixed tag or release.
For trunk-based development, the reference-branch mode is the most predictable: every pull request is measured against the tip of main.
Worked example: configuring a New-Code gate in a CI pipeline
Assume a SonarQube Developer Edition server (required for pull-request analysis) at https://sonar.example.com. The project uses Maven and runs in a GitHub Actions workflow.
1. Set the New Code definition
In the SonarQube UI: Administration → Quality Gates → [your gate] → New Code definition, set Reference branch = main.
2. Define gate conditions on New Code
- Coverage on New Code >= 80%
- New Blocker issues = 0
- New Critical issues = 0
- Maintainability rating on New Code = A
Leave Overall Code conditions blank or set them to non-blocking values (e.g., only informational).
3. Analyze and wait for the gate
In the GitHub Actions workflow:
name: CI
on:
pull_request:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0 # needed for SCM blame
- name: Set up JDK
uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- name: Build and test
run: mvn -B verify
- name: SonarQube Scan
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: https://sonar.example.com
run: |
mvn -B sonar:sonar \
-Dsonar.projectKey=my-app
- name: Wait for Quality Gate
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: https://sonar.example.com
run: |
# The scanner writes report-task.txt with the ceTaskId
CE_TASK_ID=$(grep 'ceTaskId' .scannerwork/report-task.txt | cut -d= -f2)
STATUS="PENDING"
while [[ "$STATUS" == "PENDING" || "$STATUS" == "IN_PROGRESS" ]]; do
RESPONSE=$(curl -s -u "$SONAR_TOKEN:" "$SONAR_HOST_URL/api/ce/task?id=$CE_TASK_ID")
STATUS=$(echo "$RESPONSE" | jq -r '.task.status')
sleep 10
done
ANALYSIS_ID=$(echo "$RESPONSE" | jq -r '.task.analysisId')
GATE=$(curl -s -u "$SONAR_TOKEN:" \
"$SONAR_HOST_URL/api/qualitygates/project_status?analysisId=$ANALYSIS_ID" \
| jq -r '.projectStatus.status')
if [[ "$GATE" != "OK" ]]; then
echo "Quality Gate failed: $GATE"
exit 1
fi
echo "Quality Gate passed"
Where to run: The mvn sonar:sonar step runs on the build agent; the polling step runs on the same agent after analysis upload. Permissions: The SONAR_TOKEN must be a user token with permission to execute analysis and view the project. Expected check: the loop exits only when the compute-engine task reaches SUCCESS, then the gate status is read from projectStatus.status. Risks: If the coverage report path is wrong, SonarQube may see 0% coverage on new lines, causing the gate to fail unexpectedly; verify by checking the Measures → Coverage tab for the analyzed revision. The exact report-task.txt path differs between scanners (Maven places it under target/sonar/), so confirm the location for your setup.
Trade-off and limitation
The main trade-off is that legacy issues remain in the project and can accumulate. Teams must periodically allocate dedicated effort to reduce the existing debt, otherwise the overall maintainability rating may degrade despite a passing New-Code gate. A practical way to monitor this is to add a second, non-blocking condition on Overall Code (e.g., "Maintainability rating >= C") and track its trend over time.
Another limitation: choosing "Number of days" as the New Code definition can cause old issues to be re-classified as new after a quiet period, leading to surprise gate failures. Always verify the definition under Administration → Configuration → General Settings → New Code and prefer the reference-branch mode for trunk-based workflows. Also note that branch and pull-request analysis require Developer Edition or higher; Community Edition historically analyzes only the main branch, so confirm your edition before relying on PR gating.
Actionable closing
- In SonarQube, create or edit a Quality Gate and set its New Code definition to "Reference branch = main".
- Add the New-Code conditions shown above (coverage, blocker/critical issues, maintainability).
- In your CI pipeline, run the scanner as usual, then poll the
ceTaskId(or use a webhook) to fail the build only when the server-side gate reports an error. - Confirm success by deliberately introducing a blocker issue on a feature branch and observing the pipeline stop; then remove the issue and see the gate pass.
- Periodically review the Overall Code maintainability metric and schedule debt-reduction work.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.