Chapter 12Lesson 02~135 minutes

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.

Specific analysisCoverageSCMEvidenceRollback

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.

Dated baseline
  • 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.
Credential boundary: keep 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

  1. Preserve the evidence directory outside .scannerwork.
  2. Revoke the project-analysis token and the temporary administrative lab token.
  3. If the project exists only for this lab, delete only academy-sq-ch12 after evidence is preserved.
  4. Do not delete SonarQube database/search state, Docker volumes, caches shared with other projects, or unrelated projects.
Challenge: a team proposes changing to Previous version immediately after a failing release-candidate analysis, then increments 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?

What must remain unchanged in the one-day-definition experiment?

Why identify the baseline analysis by buildString and analysis key?

What does high new coverage plus low overall coverage mean?

If the one-day run does not change new coverage, what should you do first?

Next lesson

Choose a baseline model that matches delivery reality

Lesson 3 compares all four definitions, project/global precedence, branch and pull-request boundaries, Clean-as-You-Code, and the separate governance needed for legacy risk.

Official references and version notes

Version and compatibility note

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.

Ethereum / ERC-20
0x716c4Ab160C4B66F31a28AE2448BfF68fc3a2ef0Send only Ethereum/ERC-20 compatible assets to this address.