Monorepos, Multi-Module Builds, Generated Code, and Complex Repository Layouts: Diagnostics, Failure Modes, and Production Practices
Diagnose complex-layout failures by separating source scope, report resolution, project identity, scanner workspace, Compute Engine, policy, CI concurrency, and commercial aggregation instead of hiding symptoms with exclusions.
Learning objectives
- Use an evidence-first diagnostic sequence for missing, duplicated, stale or cross-owned source/report results.
- Diagnose shared project keys and shared scanner working directories without erasing first-failure artifacts.
- Separate report-path defects from source indexing, Compute Engine, Quality Gate and provider failures.
- Detect exclusions that hide first-party code instead of repairing ownership.
- Recognize when a problem is Community/commercial feature expectation rather than a scanner defect.
- Repair the smallest state surface and rerun the smallest equivalent analysis.
1. Evidence-first diagnostic sequence for complex layouts
-
Preserve the exact Git SHA, CI job identity, scanner
command/effective parameters, scanner log, report files and
report-task.txt. - Confirm server edition/version, scanner/runtime and build-tool/analyzer versions.
- Confirm project key, branch/revision identity and expected module owner.
-
Resolve
projectBaseDir,sonar.sources,sonar.tests, inclusions/exclusions and working directory. - Inspect indexed-file evidence and report-import evidence independently.
-
Inspect
ceTaskIdand Compute Engine terminal state. - Inspect project measures/issues/gate/New Code state only after the analysis task is known.
- Inspect CI concurrency, provider decoration and aggregation only if the underlying project analysis is correct.
- Apply the least destructive correction and rerun the smallest equivalent scope at the same revision when possible.
2. Failure: overlapping analyses double-own the same first-party file
Symptom: shared/util.py appears in
both repo-api and repo-worker, each with
separate issues/metrics. This may be intentional if both projects
truly ship a copy, but in a monorepo it often means both scanners
claimed the same source path.
Preserve both scanners’ indexed-file evidence and project scopes. Then decide ownership. Options include one shared-library project, ownership by one component with consumers treated as dependencies, or deliberate duplicate ownership documented as such. Do not “fix” the dashboard by randomly excluding whichever project looks worse.
3. Failure: report path is relative to the wrong workspace
report exists + source indexed + no imported measure → resolve
report parameter from projectBaseDir → inspect report-internal
paths → repair coordinate only
This is the Chapter 24 deliberately broken case. The scanner may upload a report and the Compute Engine may succeed while a coverage sensor warns that the configured coverage file cannot be found. That is a scanner/build/report-layer defect, not a database or gate defect.
5. Failure: an exclusion makes the metric green by hiding owned code
# Suspicious shortcut
sonar.exclusions=modules/worker/**
If worker is part of the intended project, this does not repair a
path or report defect—it changes the analyzed population. Preserve
before/after file counts and explain the governance impact. Prefer
correcting projectBaseDir, sources or report path. Use
exclusions only when the ownership contract says those files do not
belong.
6. Failure: parallel jobs overwrite scanner work or reports
Symptom: one job intermittently loses
report-task.txt, coverage changes unpredictably, or
scanner logs reference files created by another job.
Check whether jobs share:
- the same checkout directory;
- the same
sonar.working.directory; - the same coverage/report output filename;
- the same project key/branch identity;
- the same artifact upload destination.
Repair isolation first. Distinct module projects can run in parallel with separate bases/workdirs/artifacts. Same-project runs should normally be serialized or superseded deterministically.
7. Failure: generated/vendor treatment changes silently
A generator upgrade may move output from
generated/ into src/generated/. An
exclusion pattern can stop matching, causing a sudden
LOC/duplication/issue increase. Or a new broad pattern can hide
first-party code accidentally.
Preserve generator version, Git diff, scope settings and indexed-file delta. Treat exclusion changes as policy changes. Test patterns against an explicit file inventory before applying them globally.
8. Failure: expecting SonarQube to infer business ownership
Even Enterprise monorepo onboarding requires projects to be created/configured; current documentation explicitly notes SonarQube cannot detect projects inside a monorepo automatically. Directory structure and build graph are technical signals, not authorization or business-ownership truth.
The repair is an ownership manifest and deterministic CI mapping, not a plugin that guesses team boundaries from folder names.
9. Intentionally broken example: interpret the evidence without hiding it
revision=7f00...cafe
projectKey=sq-ch24-worker
projectBaseDir=modules/worker
sources=src
tests=tests
coverageReportPath=modules/worker/coverage.xml
scannerExit=0
ceTaskId=AYx...
ceStatus=SUCCESS
indexedMainFiles=1
coverageImportWarning="report not found at configured path"
projectCoverage=<missing or zero-like result>
The causal diagnosis is not “SonarQube ignored coverage.” It is “the
scanner’s project coordinate is modules/worker, while
the configured report path redundantly includes that prefix.” The
least destructive fix is coverage.xml, followed by a
same-revision analysis and task/measure comparison.
10. Failure-layer map
| Evidence | Likely layer | Next safe action |
|---|---|---|
Directory in sonar.sources does not exist
|
Base/scope configuration |
Resolve path from projectBaseDir; do not change
server state.
|
| File indexed, coverage report missing | Producer/report path | Inspect producer output and report parameter/internal paths. |
| Scanner succeeds, CE fails | Server processing |
Preserve ceTaskId, CE error and server logs.
|
| Two tasks for same key from different modules | Project topology/CI concurrency | Reconcile intended project boundary and serialize/rename by approved ownership. |
| Aggregate view absent in Community Build | Edition capability | Use project-level API summary fallback; do not install unverified plugins. |
| Generated files suddenly appear | Build/scope policy drift | Compare generator/output path and indexed-file inventory. |
11. Production shortcuts to reject
- Do not exclude uncovered modules to improve coverage.
- Do not reuse one project key for unrelated module histories.
-
Do not make parallel jobs share
.scannerworkor mutable report artifacts. - Do not delete scanner logs/report files before preserving first-failure evidence.
- Do not directly edit SonarQube database/search state to remove a bad analysis.
- Do not lower Quality Gate thresholds because a previously omitted module is now visible.
- Do not invent Enterprise monorepo/application/portfolio behavior on Community Build.
- Do not replace a failed project with a different key unless the approved topology itself changed.
Knowledge check
Two modules both analyze shared/util.py. What do
you inspect before excluding it?
The ownership model and both scanners’ indexed scopes. The overlap may be accidental or deliberate; exclusion is a policy decision, not a default fix.
A scan exits 0 and CE succeeds, but coverage is absent. Which layer can still be wrong?
The coverage producer/import path or report-internal file mapping can be wrong even when analysis processing succeeds.
Why is a broad sonar.exclusions change dangerous
during troubleshooting?
It changes the analyzed population and can make metrics improve by hiding owned source rather than repairing the defect.
What is the first concurrency artifact to inspect when two jobs share one checkout?
Working/report directories and task files—especially whether
both scanners use the same
sonar.working.directory or report output.
Why can’t a Portfolio prove that underlying project boundaries are correct?
It aggregates already-produced project results; it does not validate file ownership, scanner scope or report mapping inside those projects.
Official references and version notes
-
Community Build — analysis parameters not settable in UI
—
sonar.projectBaseDir,sonar.sources,sonar.tests,sonar.working.directory, report-task path and token guidance. - Community Build — setting initial scope — source/test roots are simple paths relative to project base directory; no wildcards in initial roots.
- Community Build — analysis-scope introduction — generated/library code, coverage/duplication exclusions and verification workflow.
- Community Build — test coverage parameters — externally generated reports and project-root-relative path rules.
- Community Build — SonarScanner CLI — alternate project base directory behavior and CLI configuration.
- Community Build — Java/multi-module coverage — build-native JaCoCo generation and multi-module aggregation patterns.
- SonarQube Server — managing monorepo projects — Enterprise monorepo feature, multiple SonarQube projects bound to one repository, unique project keys and manual project definition.
- SonarQube Server — Applications — synthetic aggregation for projects sharing a lifecycle; commercial feature.
- SonarQube Server — Portfolios — Enterprise governance aggregation.
- SonarScanner CLI releases — current public scanner release baseline.
- SonarQube downloads — current Community Build/Server/LTA release identities.
Rechecked 2026-09-08. Mandatory examples target
Community Build 26.9.0.129388 and
SonarScanner CLI 8.1.0.6389. For CLI-managed
projects, sonar.projectBaseDir changes the analysis
directory; sonar.sources/sonar.tests are
simple paths relative to that base unless absolute. Most
coverage/external report paths are project-root/base-relative
unless their parameter docs state otherwise.
sonar.working.directory must be unique for each
project and is deleted before each analysis, so parallel jobs must
isolate scanner/report artifacts. Current native monorepo
onboarding/binding is an
Enterprise Edition feature and still requires
explicit projects; SonarQube does not infer monorepo projects
automatically. Applications are a commercial aggregation starting
in Developer Edition; Portfolios start in Enterprise Edition.
Community Build can still model multiple independent projects from
one repository manually with distinct project keys/base
directories and an external governance-summary fallback. Recheck
the exact scanner/build integration and report parameter semantics
before production use.
projectBaseDir,
source/test roots, inclusions/exclusions, generated/vendor policy,
report producer/path/internal filenames, unique scanner working
directory, token owner/type (never value), scanner log,
report-task.txt, ceTaskId, Compute Engine
state, measures/gate, CI concurrency identity and aggregation
edition/definition separately.
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.