Static Analysis, Clean Code, Technical Debt, and Quality Governance: Guided Hands-On Workflow
Run one small, reproducible Community Build analysis against synthetic code and prove the chain from Git revision and scanner inputs through report processing, findings, measures, New Code context, and quality-gate outcome.
Learning objectives
- Create a loopback-only SonarQube Community Build lab with pinned, recorded versions.
- Analyze a synthetic Git revision without putting a token in project files or the scanner command line.
- Preserve scanner logs and report-task evidence before interpreting the UI result.
- Compare expected versus observed findings without depending on an exact issue count.
- Remediate one finding and explain which source, task, project, and governance states changed.
1. Lab baseline and prerequisites
This workflow deliberately uses the smallest free path needed to
make Chapter 01’s governance model observable. The server is
SonarQube Community Build 26.9.0.129388 using the
official Docker tag sonarqube:26.9.0.129388-community.
The standalone SonarScanner CLI baseline recorded for this lesson is
8.1.0.6389 from SonarSource update-center metadata.
Recheck scanner/JRE prerequisites when executing because launcher
and Java requirements evolve independently of the server release.
You need Docker, Git, a current supported SonarScanner CLI
installation, a text editor, and enough local resources for
Community Build. The server binds to 127.0.0.1:9000; do
not expose the lab to a public interface.
2. Create a synthetic repository and anchor the revision
The source is deliberately tiny. We care about reproducibility and evidence, not about manufacturing a specific issue count.
mkdir sq-ch01-quality-lab
cd sq-ch01-quality-lab
git init
mkdir src evidence
Create src/pricing.js:
export function checkoutTotal(items, coupon) {
const debugPassword = "password-FAKE_DO_NOT_USE";
let total = 0;
for (const item of items) {
if (item.price < 0) return -1;
if (item.price < 0) return -2;
total += item.price * item.quantity;
}
if (coupon === "SAVE10") total = total * 0.9;
if (coupon === "SAVE10") console.log("coupon applied");
console.log(debugPassword);
return total;
}
The duplicate condition and fake secret-like value are teaching fixtures. Analyzer behavior is version/profile/mode dependent; do not assert that a particular number or category of issues must appear. The fake value is not a credential.
git add src/pricing.js
git commit -m "ch01: add synthetic pricing sample"
git rev-parse HEAD > evidence/source-revision.txt
git status --short
Expected state: the worktree is clean and
source-revision.txt identifies the exact source commit.
The evidence file itself is intentionally not part of the source
commit yet.
3. Start a pinned loopback-only Community Build server
docker pull sonarqube:26.9.0.129388-community
docker image inspect sonarqube:26.9.0.129388-community \
--format '{{.Id}}' > evidence/server-image-id.txt
docker run -d \
--name sq-ch01-server \
-p 127.0.0.1:9000:9000 \
sonarqube:26.9.0.129388-community
On Windows PowerShell, put the command on one line or use PowerShell’s backtick continuation instead of the Bash backslash. Follow the container logs until the service reports readiness:
docker logs -f sq-ch01-server
Do not delete or truncate logs if startup fails. The container image identity, server logs, and exact tag are part of first-failure evidence.
4. Create one local project and a project-scoped analysis token
Open http://127.0.0.1:9000, complete the documented
first-start authentication/password-change flow, and create a local
project with project key:
devops-academy-sonarqube-ch01
Create an analysis token with the narrowest scope available for that
project. Copy it once into the current shell environment. Do not put
it in sonar-project.properties, Git, screenshots, or
the evidence folder.
# Linux/macOS - paste interactively without echo if your shell supports it
read -s SONAR_TOKEN
export SONAR_TOKEN
export SONAR_HOST_URL="http://127.0.0.1:9000"
# Windows PowerShell equivalent:
# $env:SONAR_TOKEN = Read-Host "Project analysis token"
# $env:SONAR_HOST_URL = "http://127.0.0.1:9000"
5. Declare stable project analysis inputs
Create sonar-project.properties:
sonar.projectKey=devops-academy-sonarqube-ch01
sonar.projectName=DevOps Academy SonarQube Chapter 01 Lab
sonar.sources=src
sonar.sourceEncoding=UTF-8
Keep this file non-secret. The scanner can obtain
SONAR_HOST_URL and SONAR_TOKEN from the
environment. Later chapters study parameter precedence in depth. For
this lab, the governance rule is simpler:
project identity and source scope belong in versioned
configuration; credentials do not.
sonar-scanner -v | tee evidence/scanner-version.txt
git add sonar-project.properties
git commit -m "ch01: add reproducible analysis configuration"
git rev-parse HEAD | tee evidence/analysis-revision.txt
The analysis revision differs from the first source-only revision because the configuration is now committed. Record the revision you actually scan.
6. Predict the evidence chain before execution
| Layer | Prediction | How to verify |
|---|---|---|
| Source |
The current Git revision contains
src/pricing.js and project configuration.
|
git rev-parse HEAD,
git status --short
|
| Scanner | Only files under the configured source scope should be indexed as source input. | scanner log/index summary |
| Report | A local scanner report is created and uploaded. |
scanner log and .scannerwork/report-task.txt
|
| Server task | The uploaded report receives an asynchronous task identity and terminal status. | background task/UI using recorded task ID |
| Project | The project displays findings/measures for the completed analysis. | project result and analysis history |
| Policy | The active profile/New Code/gate determine interpretation. | record effective profile, New Code definition, gate/status |
7. Run the baseline scan and preserve first-pass evidence
sonar-scanner 2>&1 | tee evidence/scanner-baseline.log
cp .scannerwork/report-task.txt evidence/report-task-baseline.txt
If the command fails, stop here and preserve the log; do not rerun
until you understand whether the failure is local runtime,
authentication/network, indexing/configuration, report upload, or
server processing. If the command uploads successfully,
report-task.txt gives you the server-side task/result
linkage. Do not publish that file blindly because environments can
encode server URLs or identifiers you may consider internal.
Wait for the corresponding background task to reach a terminal state. Only then inspect issues, measures, New Code context, and the quality-gate result. Scanner process completion and server analysis completion are different events.
8. Interpret findings without gaming them
Record what the current analyzer actually reports. For each finding you choose to discuss, capture the rule key/name, affected line, classification/impact, status, and why the rule exists. Then decide whether the code should change, the finding requires contextual review, or the rule/policy deserves a separate governance discussion.
Do not lower a gate, deactivate a rule, mark a finding false-positive, or change mode merely to make this training project green. Those actions mutate policy and can hide the very evidence the lab is meant to teach.
server image ID · scanner version · exact Git revision · non-secret project properties · scanner log · report-task file · background-task status · active profile/mode · New Code definition · issue/measure snapshot · gate/status · interpretation note.
9. Remediate one source problem and compare states
Choose one clear maintainability/reliability problem the current analysis identified—or, if the analyzer did not flag the duplicated condition, fix it anyway as a code-review defect. For example, remove the duplicated impossible branch and avoid logging the fake password-like value. Commit the code change:
git add src/pricing.js
git commit -m "ch01: remove duplicated branch and debug value"
git rev-parse HEAD | tee evidence/remediated-revision.txt
sonar-scanner 2>&1 | tee evidence/scanner-remediated.log
cp .scannerwork/report-task.txt evidence/report-task-remediated.txt
After server processing completes, compare the two analyses. The source revision changed; a new report/task exists; project measures/findings may change; the policy objects normally stay the same. This is the core causal pattern behind a trustworthy quality program.
10. Challenge: identify the owning layer
For each symptom, name the first layer you would inspect and the evidence you need:
- The scanner cannot connect to
127.0.0.1:9000. - The scanner uploads, but the project UI still shows an older analysis.
- The UI shows the expected analysis, but the gate is red.
- A teammate sees a different issue classification after changing product mode.
- The Git commit in the evidence packet does not match the CI checkout.
Do not answer with “rerun the scan.” The point is to isolate source, scanner/network, server task, policy, mode, or CI ownership before changing anything.
11. Preserve evidence, then clean up safely
docker logs sq-ch01-server > evidence/server-final.log 2>&1
unset SONAR_TOKEN SONAR_HOST_URL
# PowerShell: Remove-Item Env:SONAR_TOKEN, Env:SONAR_HOST_URL
docker stop sq-ch01-server
docker rm sq-ch01-server
Delete the local project through the UI only if you created it for this lab and have verified the exact project key. Do not issue broad deletion commands. The container in this minimal lab has no persistent production data; later deployment chapters introduce durable storage deliberately.
Knowledge check
Why record the Git revision again after committing sonar-project.properties?
Because the analysis configuration changed the repository revision. The evidence packet must identify the exact commit that was actually scanned, not an earlier source-only commit.
Why is report-task.txt important after a successful upload?
It links the scanner-side run to server-side asynchronous processing and the eventual project result. Scanner completion alone does not prove the background task succeeded.
Why should SONAR_TOKEN stay out of sonar-project.properties?
Project configuration is versioned/shareable; a token is a credential. Keeping it in a secret-capable environment variable prevents committing it with source or evidence.
The first scan reports zero issues. What may you conclude?
Only that the current analyzers/rules/profile/mode did not report issues in the analyzed scope. You may not conclude that the program is correct or secure.
A gate is red after the analysis completed successfully. Is that a scanner failure?
No. Successful analysis completion and gate policy outcome are separate states. Inspect the gate conditions and measures rather than “fixing” the scanner.
Official references and version notes
- SonarQube downloads — release identities for Community Build and SonarQube Server.
- SonarQube Community Build documentation — current self-managed Community Build product documentation.
- Changing modes — Standard Experience versus Multi-Quality Rule (MQR) Mode semantics.
- Quality gate introduction — gate purpose and policy model.
- About new code — Clean-as-You-Code and new-code definition choices.
- Metric definitions — maintainability, remediation effort, debt ratio, and rating definitions.
- Rules — rules, issue generation, and mode-dependent classification.
- SonarScanner CLI — scanner execution guidance.
- SonarSource scanner update-center metadata — scanner release metadata used to pin the lab launcher.
- Official SonarQube Docker tags — container tag provenance for the disposable lab.
Version-sensitive statements were rechecked against current
SonarSource primary documentation on 2026-09-07. The executable
local path in this chapter pins SonarQube Community Build
26.9.0.129388 with official image
sonarqube:26.9.0.129388-community. Where a standalone
SonarScanner CLI is used, the lab records
8.1.0.6389 from SonarSource update-center
metadata. Current scanner Java/JRE auto-provisioning behavior and
exact bootstrap requirements must be rechecked at execution time.
Commercial SonarQube Server/Data Center and SonarQube Cloud
features are not required for this chapter.
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.