Chapter 24Lesson 03~145 minutes

Monorepos, Multi-Module Builds, Generated Code, and Complex Repository Layouts: Configuration, Design Patterns, and Trade-Offs

Choose one versus multiple SonarQube projects, build-native modules versus explicit scanner base directories, generated-code policies, concurrency controls, and edition-aware aggregation using observable ownership contracts.

MonoreposModulesAnalysis scopeReport pathsGovernance

Learning objectives

  • Choose one SonarQube project or multiple projects using ownership, release lifecycle and policy—not repository aesthetics.
  • Prefer Maven/Gradle/.NET build-native scanners when the build graph already contains the module truth.
  • Use explicit projectBaseDir, source/test scope and report mapping when build-native structure is unavailable.
  • Choose a generated-code treatment that preserves the right security, maintainability, coverage and duplication semantics.
  • Design serial/parallel CI analysis with unique workspaces and deterministic ownership.
  • Separate Community Build project topology from Developer applications and Enterprise monorepo/portfolio capabilities.

1. One project versus multiple projects

The correct boundary is usually the smallest unit whose quality policy and lifecycle can be governed coherently. A monorepo with ten packages may still be one product; a repository with two independently deployed services may need two SonarQube projects.

Signal Favor one project Favor multiple projects
Release lifecycle Modules always ship together. Modules deploy/version independently.
Ownership One team owns the whole codebase. Different teams need distinct permissions/accountability.
Quality Gate/New Code One gate and baseline are meaningful. Different policies/baselines are needed.
Technology/build One build graph naturally covers everything. Independent build pipelines already exist.
Failure isolation Whole-system releasability is the decision. One service should not overwrite another project’s analysis history.

2. Build-native modules versus manual scanner slicing

For Maven, Gradle and .NET, prefer the scanner integrated with the build system when it understands the actual module graph, compiled outputs and test layout. This avoids manually reconstructing a model the build already knows.

Do not resurrect obsolete module tricks. Avoid copying old tutorials that rely on legacy sonar.modules style configuration. Use the current build scanner’s module model or explicit independent project scans.

Manual sonar.projectBaseDir slicing is appropriate for CLI-managed repositories, language ecosystems without a build-integrated scanner, or genuinely independent project boundaries. Record the base and every relative path as part of the configuration contract.

3. Base directory versus changing the shell working directory

These two commands can target the same files but have different operational evidence:

# A: explicit coordinate from repository root
sonar-scanner -Dsonar.projectBaseDir=modules/api -Dsonar.sources=src

# B: shell coordinate change
(cd modules/api && sonar-scanner -Dsonar.sources=src)

Option A makes the module base explicit in scanner parameters. Option B can simplify tool/report paths because the test producer and scanner share a working directory. Choose one convention and standardize it; mixing both styles casually is a common source of doubled prefixes.

4. Generated-code treatment: choose the narrowest exclusion

Need Technique State changed
File is not owned/analyzable first-party source Exclude from source analysis with justified sonar.exclusions or keep it outside sonar.sources. Issues, metrics, duplication and coverage population can all change.
Static analysis matters, coverage responsibility does not sonar.coverage.exclusions. Coverage denominator only; file can remain analyzed.
Duplicated generated templates should not distort CPD sonar.cpd.exclusions. Duplication calculation, not general analysis.
Generated annotation marks issue-only noise Advanced issue exclusion by file content/rule where justified. Issue reporting only; do not assume other metrics disappear.

Broad source exclusion is the most destructive option. Use it only when the file truly sits outside the project’s code-quality ownership contract.

5. Single versus merged reports

A one-project topology can import multiple language-specific coverage reports when the parameter supports a list/wildcards, or an intentionally aggregated report produced by the test tool. A multi-project topology should normally give each project only the report that corresponds to files in its indexed scope.

  • Do not concatenate XML formats unless the producing tool documents that operation.
  • Do not feed one module’s report to another project merely because the filename exists.
  • Do not assume a report-relative source path means the same thing after changing projectBaseDir.
  • Verify the report’s internal filenames before scanner invocation.

6. Serial versus parallel analyses

Parallelism is safe only when the state surfaces are isolated. For distinct projects, use separate checkouts or module directories, unique sonar.working.directory values, unique report destinations, project-scoped tokens and distinct artifact names. Preserve each report-task.txt separately.

For the same SonarQube project/branch, serialization is the safer default. Even if the scanner processes can run concurrently, two reports for the same project identity can race semantically: the build that finishes last may not correspond to the revision you intended to treat as authoritative. CI concurrency controls should cancel/stale-suppress superseded runs or serialize by project/branch.

7. Aggregation is a view over correct projects, not a substitute for topology

Capability Current boundary Use
Explicit multiple Community Build projects Free/local mandatory path Independent project histories from one repository, modeled manually.
Application Developer Edition+ Aggregate projects that share a release lifecycle into a synthetic releasability view.
Native monorepo onboarding/binding Enterprise Edition Bind multiple SonarQube projects to the same DevOps repository and distinguish monorepo project PR reporting.
Portfolio Enterprise Edition+ Executive/high-level governance across projects/applications.

Never create an application or portfolio first and hope it will expose incorrect project scopes. Validate each component project independently before aggregating it.

8. Worked scenarios

Scenario Recommended topology Evidence
Single Java product with Maven reactor modules, one release One project using SonarScanner for Maven/build-native module knowledge. Reactor/build graph, module scanner log, unified release/gate.
Python monorepo with independently deployed API and worker Two project keys, module bases and tokens; optional commercial aggregation if lifecycle warrants. Ownership table, distinct task IDs, independent gates.
Generated API client committed to Git but never edited Decide whether full exclusion or narrower coverage/CPD exclusion matches ownership. Generator/version, regeneration proof, exclusion rationale.
Two CI jobs share repository root and .scannerwork Repair workspace isolation before adding concurrency. Unique work/report dirs and independent task files.

9. Topology manifest as policy as code

revision: "<git-sha>"
projects:
  - key: sq-ch24-api
    owner: team-api
    base_dir: modules/api
    sources: [src]
    tests: [tests]
    reports: [coverage.xml]
    generated_policy: "exclude none inside module"
    scanner_workdir: .scannerwork-ch24-api
    concurrency_key: sq-ch24-api-main
  - key: sq-ch24-worker
    owner: team-worker
    base_dir: modules/worker
    sources: [src]
    tests: [tests]
    reports: [coverage.xml]
    scanner_workdir: .scannerwork-ch24-worker
aggregation:
  community_fallback: "timestamped markdown/API summary"
  optional_application: "Developer+ only if shared lifecycle"
  optional_portfolio: "Enterprise+ governance view"

Treat this manifest as a reviewable contract. A CI implementation can derive commands from it, but changes to ownership/scope should require the same scrutiny as a Quality Gate or permission change.

Knowledge check

When should build-native scanner integration be preferred over manual base-directory slicing?

What is the narrowest choice when generated code should stay analyzed but not reduce coverage?

Why serialize analyses for the same project/branch by default?

Can an Enterprise Portfolio fix double-counted source between two projects?

What should be version-controlled for a multi-project monorepo?

Next lesson

Diagnose layout failures without hiding source

Lesson 4 engineers overlap, missing-report, shared-key, exclusion, concurrency and ownership-inference failures.

Official references and version notes

Version and compatibility note

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.

Complex-layout evidence rule. Preserve repository root, exact revision, project key/owner, 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.

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