New Code, Clean-as-You-Code, Baselines, and Legacy Modernization: Guided Hands-On Workflow
Build a deliberately imperfect legacy project, pin a baseline analysis, introduce well-tested new code, compare new versus overall measures, change the new-code definition once, and restore the governed baseline with evidence.
Learning objectives
- Create a reproducible legacy fixture with intentionally low overall coverage and a stable baseline analysis.
- Set a Specific analysis baseline using a recorded analysis key without exposing administrative credentials.
- Add and fully test a controlled new-code slice large enough to make new-code coverage meaningful.
- Compare overall coverage and new-code coverage under the same source revision and quality policy.
- Change the new-code definition once to demonstrate population drift, then restore the approved baseline.
- Preserve revision, task, measure, gate, baseline, and cleanup evidence as one auditable chain.
1. Lab contract and assumptions
This workflow is deliberately local and synthetic. It uses coverage because new coverage versus overall coverage makes population changes observable without depending on whichever analyzer rules evolve between releases.
-
SonarQube Community Build 26.9.0.129388 at
http://localhost:9000. - SonarScanner CLI 8.1.0.6389.
- Python 3.11+ with pytest/pytest-cov.
- Project key
academy-sq-ch12. - Full local Git repository; no shallow clone.
- A project-analysis token for scans and a separate short-lived project-admin/system-admin lab token for New Code configuration.
- No commercial branch analysis, enterprise identity, cloud, paid CI, or third-party plugin is required.
SONAR_TOKEN and SONAR_ADMIN_TOKEN in a
local secret/environment store. Do not commit them, echo them, place
them in sonar-project.properties, or reuse a production
admin token.
2. Preflight and project setup
git --version
python --version
sonar-scanner --version
curl -fsS http://localhost:9000/api/server/version
export SONAR_HOST_URL="http://localhost:9000"
export PROJECT_KEY="academy-sq-ch12"
In the local SonarQube UI, create the disposable project
academy-sq-ch12 if needed. Generate a
project analysis token for scans. Separately use a
disposable lab administrator with permission to administer this
project when setting the Specific analysis baseline.
3. Create an intentionally imperfect legacy baseline
mkdir -p academy-sq-ch12/src academy-sq-ch12/tests academy-sq-ch12/evidence
cd academy-sq-ch12
git init
git config user.name "Academy Learner"
git config user.email "academy@example.invalid"
cat > .gitignore <<'EOF'
.venv/
.scannerwork/
.coverage
coverage.xml
evidence/
__pycache__/
.pytest_cache/
EOF
python -m venv .venv
# Linux/macOS:
. .venv/bin/activate
# Windows PowerShell equivalent: .\.venv\Scripts\Activate.ps1
python -m pip install --upgrade pip
python -m pip install pytest pytest-cov
cat > src/legacy.py <<'PY'
def add(a, b):
return a + b
def legacy_price(quantity, unit_price, discount, tax, shipping):
subtotal = quantity * unit_price
if discount > 0:
subtotal -= discount
if tax > 0:
subtotal += subtotal * tax
if shipping > 0:
subtotal += shipping
if subtotal < 0:
subtotal = 0
return round(subtotal, 2)
def legacy_classify(score):
if score >= 90:
return "A"
if score >= 80:
return "B"
if score >= 70:
return "C"
if score >= 60:
return "D"
return "F"
def legacy_discount(customer, total):
if customer == "vip" and total > 500:
return 0.20
if customer == "vip":
return 0.10
if total > 1000:
return 0.05
return 0.0
def legacy_shipping(zone, weight):
if zone == "local":
return 0
if zone == "regional":
return 5 + weight * 0.4
if zone == "international":
return 20 + weight * 1.5
raise ValueError("unknown zone")
def legacy_risk(age_days, failed_attempts, amount):
score = 0
if age_days < 3:
score += 3
if failed_attempts > 2:
score += 4
if amount > 5000:
score += 4
return score
PY
cat > tests/test_legacy.py <<'PY'
from src.legacy import add
def test_add():
assert add(2, 3) == 5
PY
cat > sonar-project.properties <<'EOF'
sonar.projectKey=academy-sq-ch12
sonar.projectName=Academy SonarQube Chapter 12
sonar.sources=src
sonar.tests=tests
sonar.python.coverage.reportPaths=coverage.xml
EOF
git add .gitignore src tests sonar-project.properties
git commit -m "legacy baseline"
git rev-parse HEAD > evidence/baseline-revision.txt
pytest --cov=src --cov-report=term-missing --cov-report=xml
cp coverage.xml evidence/baseline-coverage.xml
Predict first: overall coverage should be low because only
add() is tested. That is intentional legacy evidence,
not a target to hide.
4. Run and identify the baseline analysis
sonar-scanner \
-Dsonar.projectVersion="1.0.0" \
-Dsonar.buildString="ch12-baseline"
cp .scannerwork/report-task.txt evidence/baseline-report-task.txt
cp .scannerwork/scanner-report/metadata.pb evidence/baseline-metadata.pb 2>/dev/null || true
curl -fsS -H "Authorization: Bearer $SONAR_ADMIN_TOKEN" --get \
--data-urlencode "project=$PROJECT_KEY" \
--data-urlencode "ps=20" \
"$SONAR_HOST_URL/api/project_analyses/search" \
> evidence/project-analyses.json
Locate the analysis whose buildString is
ch12-baseline. Record its key as
BASELINE_ANALYSIS_KEY. The build string exists
specifically so the analysis can be identified unambiguously later.
export BASELINE_ANALYSIS_KEY="<copy-the-key-for-ch12-baseline>"
# Set the project New Code definition to that exact analysis.
curl -fsS -X POST -H "Authorization: Bearer $SONAR_ADMIN_TOKEN" \
-d "project=$PROJECT_KEY" \
-d "type=SPECIFIC_ANALYSIS" \
-d "value=$BASELINE_ANALYSIS_KEY" \
"$SONAR_HOST_URL/api/new_code_periods/set"
curl -fsS -H "Authorization: Bearer $SONAR_ADMIN_TOKEN" --get \
--data-urlencode "project=$PROJECT_KEY" \
"$SONAR_HOST_URL/api/new_code_periods/show" \
| tee evidence/new-code-specific-baseline.json
Current Community Build documentation makes Specific analysis API-only. If your release’s built-in Web API documents a different endpoint/parameter surface, follow the instance documentation and record that assumption rather than guessing.
5. Add fully tested new code while legacy coverage stays low
The new module intentionally contains more than twenty coverable lines so Chapter 11’s default small-change coverage behavior does not mask the New Code coverage signal.
cat > src/checkout.py <<'PY'
def normalize_email(value):
cleaned = value.strip().lower()
if "@" not in cleaned:
raise ValueError("invalid email")
local, domain = cleaned.split("@", 1)
if not local or not domain or "." not in domain:
raise ValueError("invalid email")
return f"{local}@{domain}"
def calculate_total(lines, tax_rate=0.0):
subtotal = 0.0
for quantity, unit_price in lines:
if quantity < 0 or unit_price < 0:
raise ValueError("negative value")
subtotal += quantity * unit_price
tax = subtotal * tax_rate
return round(subtotal + tax, 2)
def shipping_tier(total, expedited=False):
if expedited:
if total >= 100:
return "expedited-free"
return "expedited-paid"
if total >= 50:
return "standard-free"
return "standard-paid"
def can_checkout(email, lines):
try:
normalize_email(email)
total = calculate_total(lines)
except ValueError:
return False
return total > 0
PY
cat > tests/test_checkout.py <<'PY'
import pytest
from src.checkout import normalize_email, calculate_total, shipping_tier, can_checkout
def test_normalize_email():
assert normalize_email(" User@Example.COM ") == "user@example.com"
@pytest.mark.parametrize("value", ["missing-at", "@example.com", "user@localhost"])
def test_invalid_email(value):
with pytest.raises(ValueError):
normalize_email(value)
def test_calculate_total():
assert calculate_total([(2, 10.0), (1, 5.0)], 0.10) == 27.5
def test_negative_line():
with pytest.raises(ValueError):
calculate_total([(-1, 5.0)])
def test_shipping_tiers():
assert shipping_tier(120, True) == "expedited-free"
assert shipping_tier(80, True) == "expedited-paid"
assert shipping_tier(80, False) == "standard-free"
assert shipping_tier(20, False) == "standard-paid"
def test_can_checkout():
assert can_checkout("a@example.com", [(1, 10)])
assert not can_checkout("bad", [(1, 10)])
assert not can_checkout("a@example.com", [])
PY
git add src/checkout.py tests/test_checkout.py
git commit -m "add clean checkout slice"
git rev-parse HEAD > evidence/change-revision.txt
pytest --cov=src --cov-report=term-missing --cov-report=xml
cp coverage.xml evidence/change-coverage.xml
sonar-scanner \
-Dsonar.projectVersion="1.0.0" \
-Dsonar.buildString="ch12-clean-change"
cp .scannerwork/report-task.txt evidence/change-report-task.txt
Expected shape: overall coverage remains materially low because the legacy functions are untested, while new coverage is high because the new checkout slice is thoroughly exercised. Preserve actual values; do not invent percentages.
6. Compare overall and new-code results after Compute Engine finishes
# First correlate report-task.txt to its CE task and wait until SUCCESS/FAILED.
cat evidence/change-report-task.txt
curl -fsS -H "Authorization: Bearer $SONAR_ADMIN_TOKEN" --get \
--data-urlencode "component=$PROJECT_KEY" \
--data-urlencode "metricKeys=coverage,new_coverage,ncloc" \
"$SONAR_HOST_URL/api/measures/component" \
| tee evidence/measures-specific-baseline.json
curl -fsS -H "Authorization: Bearer $SONAR_ADMIN_TOKEN" --get \
--data-urlencode "projectKey=$PROJECT_KEY" \
"$SONAR_HOST_URL/api/qualitygates/project_status" \
| tee evidence/gate-specific-baseline.json
In the UI, also capture the New Code and Overall Code panels. The important lesson is not a specific number; it is that the two populations answer different questions from the same analyzed revision.
7. Change only the definition: same revision, different population
Run this experiment on the same day as the fixture creation. Because both legacy and new commits are then within one day, switching to a one-day window deliberately expands New Code to include the lab’s “legacy” lines.
git rev-parse HEAD | tee evidence/definition-experiment-revision.txt
curl -fsS -X POST -H "Authorization: Bearer $SONAR_ADMIN_TOKEN" \
-d "project=$PROJECT_KEY" \
-d "type=NUMBER_OF_DAYS" \
-d "value=1" \
"$SONAR_HOST_URL/api/new_code_periods/set"
curl -fsS -H "Authorization: Bearer $SONAR_ADMIN_TOKEN" --get \
--data-urlencode "project=$PROJECT_KEY" \
"$SONAR_HOST_URL/api/new_code_periods/show" \
| tee evidence/new-code-one-day.json
# Reanalyze the same source revision; do not modify code.
sonar-scanner \
-Dsonar.projectVersion="1.0.0" \
-Dsonar.buildString="ch12-one-day-definition"
cp .scannerwork/report-task.txt evidence/one-day-report-task.txt
Predict before scanning: new-code coverage should now move toward the poor overall coverage because the time window includes the same-day legacy commit. If your result differs, inspect commit timestamps, New Code JSON, SCM logs, and actual measured values instead of forcing the expected narrative.
8. Restore the governed baseline and prove rollback
curl -fsS -X POST -H "Authorization: Bearer $SONAR_ADMIN_TOKEN" \
-d "project=$PROJECT_KEY" \
-d "type=SPECIFIC_ANALYSIS" \
-d "value=$BASELINE_ANALYSIS_KEY" \
"$SONAR_HOST_URL/api/new_code_periods/set"
curl -fsS -H "Authorization: Bearer $SONAR_ADMIN_TOKEN" --get \
--data-urlencode "project=$PROJECT_KEY" \
"$SONAR_HOST_URL/api/new_code_periods/show" \
| tee evidence/new-code-restored.json
sonar-scanner \
-Dsonar.projectVersion="1.0.0" \
-Dsonar.buildString="ch12-restored-definition"
cp .scannerwork/report-task.txt evidence/restored-report-task.txt
The final source SHA should match the one-day-definition run. The policy returns to the specific baseline. A correct evidence packet can therefore demonstrate that a measurement change was caused by policy, not source.
9. Cleanup and challenge
-
Preserve the evidence directory outside
.scannerwork. - Revoke the project-analysis token and the temporary administrative lab token.
-
If the project exists only for this lab, delete only
academy-sq-ch12after evidence is preserved. - Do not delete SonarQube database/search state, Docker volumes, caches shared with other projects, or unrelated projects.
sonar.projectVersion. Identify the policy
layer being changed, the evidence you would preserve, and the safer
remediation path.
Knowledge check
Why use a separate project-admin token and analysis token?
Analysis needs only Execute Analysis. Changing the New Code definition is administrative policy. Separating credentials demonstrates least privilege and makes audit scope clearer.
What must remain unchanged in the one-day-definition experiment?
The source revision and intended scanner inputs. Only the New Code policy should change, otherwise the causal comparison is invalid.
Why identify the baseline analysis by buildString and analysis key?
It prevents selecting a similarly dated but different analysis and makes the Specific analysis reference reproducible.
What does high new coverage plus low overall coverage mean?
The current change is well covered under the governed baseline, while inherited code still has a coverage backlog. Both facts should be reported.
If the one-day run does not change new coverage, what should you do first?
Preserve evidence and inspect commit timestamps, SCM/blame state, the actual New Code definition response, task status, and measures. Do not adjust code or policy merely to force the expected outcome.
Official references and version notes
- Quality standards and new code — current New Code concepts, four definition modes, default baseline, and Clean-as-You-Code framing.
- Configuring new code calculation — project overrides, Specific analysis API-only behavior, and Previous version configuration.
- Global New Code baseline — global inheritance and the default Previous version baseline.
- Checked-out code — full Git history requirements for New Code, blame, and issue backdating.
- Issue management solution — issue identity/date/backdating and how current analysis relates findings to new code.
- Understanding quality gates — Sonar way and new-code-focused quality conditions.
- Feature comparison table — current Community Build versus Server/Cloud branch and pull-request boundaries.
- Analysis overview — scanner/report/Compute Engine processing and new/overall result computation.
- Web API — authenticated API usage and release-sensitive endpoint guidance.
- SonarQube downloads — current Community Build release identity.
- SonarScanner CLI 8.1.0.6389 — scanner baseline used by the local lab.
Rechecked 2026-09-07. Mandatory executable examples target SonarQube Community Build 26.9.0.129388 and SonarScanner CLI 8.1.0.6389. Current Community Build documentation lists Previous version, Number of days, Specific analysis, and Reference branch as New Code definition modes; the project-level definition overrides the global baseline, whose default is Previous version. Specific analysis is configured through the Web API. Commercial branch analysis and broader enterprise capabilities are not required by this chapter. Re-check the linked primary documentation and your instance’s built-in Web API before automating against another release.
Keep the academy open
Support free, practical DevOps education.
Every lesson is designed to remain readable in a browser, downloadable from GitHub, and usable without a paid learning platform. Contributions help expand and maintain the curriculum.
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this
address.