Skip to content

Code Quality Analysis Research — SonarQube CE vs SonarCloud

Status: Research Document Created: March 2026 Scope: Forma 3D Connect — Static Code Analysis & Quality Gates

Table of Contents

  1. Executive Summary
  2. Current State
  3. Codebase Size Analysis
  4. SonarQube Community Build Overview
  5. SonarQube CE — Container Deployment on Build Agent
  6. SonarQube CE — Resource Constraints
  7. SonarQube CE — Azure DevOps Pipeline Integration
  8. SonarQube CE — Backup to DigitalOcean Spaces
  9. SonarCloud — Cloud Alternative
  10. SonarCloud — Azure DevOps Pipeline Integration
  11. Lightweight Alternatives
  12. Nx Monorepo Scanner Integration
  13. Quality Gates & Quality Profiles
  14. Head-to-Head Comparison
  15. Cost Analysis
  16. Risks and Mitigations
  17. Recommendation

1. Executive Summary

This document evaluates options for continuous static code analysis for the Forma 3D Connect monorepo. Two primary options are compared: SonarQube Community Build (self-hosted on the DigitalOcean build agent) and SonarCloud Team (SaaS). Lightweight alternatives are also assessed.

Key Findings

Aspect SonarQube CE (self-hosted) SonarCloud Team (SaaS)
Monthly cost ~$49 (droplet upgrade) ~$32 (LoC-based)
Branch / PR analysis No Yes
PR decoration (inline comments) No Yes
Infrastructure work Significant None
Maintenance ~1 hr/month Zero
Data sovereignty Yes No
Cost scales with LoC No (flat) Yes

Recommendation

SonarCloud Team at $32/month is the pragmatic choice. It costs less than the self-hosted option, requires zero infrastructure, includes branch/PR analysis, and can be integrated into the existing Azure DevOps pipeline within an hour. Self-hosted only becomes cost-effective at 200k+ LoC.


2. Current State

2.1 Code Quality Tooling

Tool Purpose Scope
ESLint Linting (style + some code smells) Per-project via Nx
TypeScript strict mode Type safety All projects
Syft + Grype Container SBOM generation and CVE scanning CI pipeline
Vitest / Jest Unit test coverage Frontend / Backend
Playwright + BDD Acceptance tests End-to-end

2.2 What's Missing

  • Cross-cutting code smell detection (duplicated code, cognitive complexity, dead code)
  • Technical debt quantification in hours/days
  • Unified quality dashboard with historical trends
  • Quality gates that block merges based on measurable thresholds
  • Security hotspot tracking beyond what Grype covers

2.3 Current Build Agent

Spec Value
vCPUs 4
RAM 8 GB
Disk 160 GB SSD
IP 159.223.11.111
Pool DO-Build-Agents
Current role Docker image packaging (2 Azure DevOps agent instances)

3. Codebase Size Analysis

SonarCloud bills on ncloc (non-comment, non-blank lines) from source files. Test files marked via sonar.tests do not count toward billing.

3.1 Measured Lines of Code

Area Raw LoC (wc -l) ncloc (billable)
apps/web (React) 17,403 ~17,000
apps/gateway 3,168 ~3,100
apps/order-service 27,021 ~26,500
apps/print-service 12,038 ~11,800
apps/shipping-service 8,893 ~8,700
apps/gridflock-service 6,063 ~5,900
libs (shared) 8,734 ~8,500
Production total ~82k ~80k
Test files (excluded) ~57k

3.2 SonarCloud Free Tier Fit

Metric Our Codebase Free Tier Limit Verdict
Production ncloc ~80k 50k Exceeds by 60%
Users <5 5 max Fits

The Free tier is not viable. At ~80k ncloc, the codebase exceeds the 50k limit. The Team plan ($32/month, up to 100k LoC) is required. The codebase is at ~80% of that 100k ceiling and will cross it as it grows.


4. SonarQube Community Build Overview

4.1 Language Support for Our Stack

Technology Supported Notes
TypeScript Yes Through TS 5.6, 200+ rules
JavaScript Yes ECMAScript 3 through 2022
React (JSX/TSX) Yes Framework-specific rules
NestJS Yes Analyzed via TypeScript rules
CSS / SCSS Yes Included
HTML Yes Included
Prisma / SQL No No .prisma file support

4.2 Community Build vs Paid Editions

Feature Community Build Developer ($) Enterprise ($$)
Main branch analysis Yes Yes Yes
Multi-branch analysis No Yes Yes
Pull request analysis No Yes Yes
PR decoration (comments) No Yes Yes
Quality Gates Yes Yes Yes
Webhooks & API Yes Yes Yes
Security reports (OWASP/CWE) No No Yes
SCA (dependency scanning) No Yes Yes

4.3 Elasticsearch — Mandatory and Non-Negotiable

SonarQube embeds Elasticsearch as a core architectural component. It cannot be disabled, replaced, or swapped for a lighter alternative.

Why it's mandatory: Elasticsearch powers the entire UI — issue browsing, project navigation, rule search, component tree, and measures dashboard. The database stores raw data; Elasticsearch indexes it for querying. Without it, SonarQube can ingest analysis results but cannot display them.

Architecture:

Scanner → Compute Engine → PostgreSQL → Elasticsearch → Web UI

Every component is required. When ES data is lost, SonarQube rebuilds it from PostgreSQL on startup — but ES must always be present at runtime.

Implications: This forces a hard minimum of ~4 GB RAM for SonarQube alone (ES heap + Web + Compute Engine + JVM overhead). There is no file-based mode, no SQLite option, and no plugin to swap ES.


5. SonarQube CE — Container Deployment on Build Agent

5.1 Target Architecture

uml diagram

5.2 Docker Compose Configuration

services:
  sonarqube:
    image: sonarqube:2026.1-community
    container_name: sonarqube
    restart: unless-stopped
    depends_on:
      sonarqube-db:
        condition: service_healthy
    environment:
      SONAR_JDBC_URL: jdbc:postgresql://sonarqube-db:5432/sonarqube
      SONAR_JDBC_USERNAME: sonarqube
      SONAR_JDBC_PASSWORD: ${SONAR_DB_PASSWORD}
    ports:
      - "127.0.0.1:9000:9000"
    volumes:
      - sonarqube_data:/opt/sonarqube/data
      - sonarqube_logs:/opt/sonarqube/logs
      - sonarqube_extensions:/opt/sonarqube/extensions
    deploy:
      resources:
        limits:
          memory: 4G
          cpus: '1.0'
        reservations:
          memory: 2G
    healthcheck:
      test: ["CMD-SHELL", "curl -sf http://localhost:9000/api/system/status | grep -q UP"]
      interval: 30s
      timeout: 10s
      retries: 5
      start_period: 120s

  sonarqube-db:
    image: postgres:16-alpine
    container_name: sonarqube-db
    restart: unless-stopped
    environment:
      POSTGRES_USER: sonarqube
      POSTGRES_PASSWORD: ${SONAR_DB_PASSWORD}
      POSTGRES_DB: sonarqube
    volumes:
      - sonarqube_postgresql:/var/lib/postgresql/data
    deploy:
      resources:
        limits:
          memory: 512M
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U sonarqube"]
      interval: 10s
      timeout: 5s
      retries: 5

volumes:
  sonarqube_data:
  sonarqube_logs:
  sonarqube_extensions:
  sonarqube_postgresql:

5.3 Kernel Tuning (Required)

vm.max_map_count=524288
fs.file-max=131072

5.4 Security

Port 9000 binds to 127.0.0.1 only — not exposed to the public internet. Azure DevOps agents access SonarQube at http://localhost:9000.


6. SonarQube CE — Resource Constraints

6.1 Why the Current Droplet Won't Work

SonarQube runs 3 JVM processes with individually tunable heaps:

Process Default Heap Hard Minimum Role
Web server ~512 MB ~256 MB UI + API
Elasticsearch ~512 MB ~512 MB Indexing (hard floor)
Compute Engine ~512 MB ~256 MB Analysis processing
JVM overhead + OS ~1.5 GB ~1 GB Metaspace, native memory
SonarQube total ~3 GB ~2 GB Absolute minimum

Current droplet memory budget:

uml diagram

6.2 Can Memory Be Reduced?

Theoretically, SonarQube can be squeezed to ~2 GB with aggressive JVM tuning:

sonar.web.javaOpts=-Xmx256m -Xms128m
sonar.search.javaOpts=-Xmx512m -Xms512m
sonar.ce.javaOpts=-Xmx256m -Xms128m

This is not recommended for an 82k LoC codebase — the Compute Engine may OOM during analysis, and the UI becomes sluggish. The official minimum for "up to 1M LoC" is 4 GB.

6.3 Verdict: Upgrade Required

Spec Current Required
RAM 8 GB 16 GB
vCPUs 4 4 (sufficient)
Monthly cost ~$48 ~\(96 (+\)48)

7. SonarQube CE — Azure DevOps Pipeline Integration

7.1 Pipeline Flow

uml diagram

7.2 Pipeline Changes Required

A new CodeQuality stage in azure-pipelines.yml:

- stage: CodeQuality
  displayName: 'Code Quality Analysis'
  dependsOn: ValidateAndTest
  condition: and(succeeded(), eq(variables.isMain, true))
  pool:
    name: 'DO-Build-Agents'
  jobs:
    - job: SonarQubeAnalysis
      displayName: 'SonarQube Analysis'
      steps:
        - template: .azuredevops/pipelines/templates/install-dependencies.yml

        - script: |
            npx sonar-scanner \
              -Dsonar.host.url=http://localhost:9000 \
              -Dsonar.token=$(SONAR_TOKEN) \
              -Dsonar.projectKey=forma3d-connect \
              -Dsonar.sources=apps,libs \
              -Dsonar.tests=apps,libs \
              -Dsonar.test.inclusions=**/*.spec.ts,**/*.spec.tsx,**/*.test.ts \
              -Dsonar.exclusions=**/node_modules/**,**/dist/**,**/coverage/** \
              -Dsonar.typescript.lcov.reportPaths=coverage/**/lcov.info
          displayName: 'Run SonarQube Scanner'

        - script: |
            RESULT=$(curl -sf -u $(SONAR_TOKEN): \
              "http://localhost:9000/api/qualitygates/project_status?projectKey=forma3d-connect" \
              | python3 -c "import sys,json; print(json.load(sys.stdin)['projectStatus']['status'])")
            echo "Quality Gate: $RESULT"
            if [ "$RESULT" != "OK" ]; then
              echo "##vso[task.logissue type=error]Quality Gate FAILED"
              exit 1
            fi
          displayName: 'Check Quality Gate'

Additional requirements: - Add SONAR_TOKEN as a secret pipeline variable in Azure DevOps - Add sonar-project.properties to the repository root - Deploy Docker Compose stack on build agent - Upgrade droplet to 16 GB - Apply kernel tuning

Only works on main branch — Community Build cannot analyze feature branches or PRs.


8. SonarQube CE — Backup to DigitalOcean Spaces

8.1 What Needs Backup

Component Needs Backup Notes
PostgreSQL database Yes All projects, issues, metrics, settings (~50–200 MB)
Elasticsearch data No Rebuilt from PostgreSQL on startup
Plugins (extensions/) Optional Can be reinstalled

8.2 Backup Flow

uml diagram

8.3 s3cmd Configuration

The build agent already uses DO Spaces credentials (DO_SPACES_KEY, DO_SPACES_SECRET, DO_SPACES_BUCKET, DO_SPACES_REGION=ams3). The existing .s3cfg can be reused.

8.4 Restore Procedure

  1. Stop SonarQube
  2. Download and decompress dump from DO Spaces
  3. pg_restore into the sonarqube-db container
  4. Clear Elasticsearch data directory
  5. Start SonarQube — ES reindexes automatically (~5–10 minutes)

9. SonarCloud — Cloud Alternative

9.1 Why Consider SonarCloud

SonarCloud is the SaaS version of SonarQube, hosted by Sonar. It eliminates all infrastructure concerns while adding features that Community Build lacks.

9.2 Plan Fit

SonarCloud Plan LoC Limit Users Branch/PR Monthly Cost Our Fit
Free 50k 5 Yes $0 Too small — we're at ~80k
Team 100k Unlimited Yes $32 Fits now — 80% of ceiling
Enterprise Custom Unlimited Yes Custom Overkill for current scale

9.3 What Team Tier Includes Over Free

  • Unlimited users
  • AI CodeFix (automated vulnerability remediation)
  • Improved secrets detection
  • Unlimited public project scanning
  • 30+ languages and frameworks
  • Main branch and pull request analysis
  • DevOps platform integration (Azure DevOps, GitHub, etc.)
  • Commercial support available

9.4 Growth Projection

The codebase is at ~80k ncloc and growing. When it crosses 100k, the next LoC tier kicks in (pricing not publicly listed, estimated ~$50–60/month). The self-hosted option stays flat regardless of codebase size.


10. SonarCloud — Azure DevOps Pipeline Integration

10.1 Integration Architecture

uml diagram

10.2 One-Time Setup

Step 1 — Install the Azure DevOps Extension

Install the SonarCloud extension from the Visual Studio Marketplace into the Azure DevOps organization.

Step 2 — Create SonarCloud Organization

  1. Sign up at sonarcloud.io with the Azure DevOps account
  2. Import the Azure DevOps organization
  3. Create the forma3d-connect project
  4. Select the Team plan ($32/month)

Step 3 — Create Service Connection

In Azure DevOps: Project Settings > Service connections > New > SonarCloud

  • Generate a token in SonarCloud (User > My Account > Security)
  • Name the service connection SonarCloud

10.3 Pipeline Changes Required

Add a new job to the existing ValidateAndTest stage in azure-pipelines.yml:

# Inside the ValidateAndTest stage, alongside Lint, TypeCheck, UnitTests
- job: CodeQuality
  displayName: 'Code Quality (SonarCloud)'
  cancelTimeoutInMinutes: 0
  steps:
    - template: .azuredevops/pipelines/templates/install-dependencies.yml

    - task: SonarCloudPrepare@4
      displayName: 'Prepare SonarCloud Analysis'
      inputs:
        SonarCloud: 'SonarCloud'
        organization: 'forma3d'
        scannerMode: 'cli'
        configMode: 'manual'
        cliProjectKey: 'forma3d_forma3d-connect'
        cliProjectName: 'Forma 3D Connect'
        cliSources: 'apps/web/src,apps/gateway/src,apps/order-service/src,apps/print-service/src,apps/shipping-service/src,apps/gridflock-service/src,libs'
        extraProperties: |
          sonar.tests=apps,libs
          sonar.test.inclusions=**/*.spec.ts,**/*.spec.tsx,**/*.test.ts
          sonar.exclusions=**/node_modules/**,**/dist/**,**/coverage/**,**/*.spec.ts,**/*.spec.tsx,**/*.test.ts
          sonar.typescript.lcov.reportPaths=coverage/**/lcov.info
          sonar.javascript.lcov.reportPaths=coverage/**/lcov.info

    - task: SonarCloudAnalyze@4
      displayName: 'Run SonarCloud Analysis'

    - task: SonarCloudPublish@4
      displayName: 'Publish Quality Gate Result'
      inputs:
        pollingTimeoutSec: '300'

10.4 What Changes vs Current Pipeline

Change Scope Effort
Install SonarCloud extension in Azure DevOps One-time, org-level 2 minutes
Create SonarCloud service connection One-time, project-level 5 minutes
Add CodeQuality job to ValidateAndTest stage azure-pipelines.yml ~15 lines of YAML
No infrastructure changes None
No droplet upgrade None
No kernel tuning None
No Docker Compose stack None
No backup script None

Total setup time: ~30 minutes

10.5 PR Decoration

With SonarCloud Team, PRs targeting main (or develop) automatically receive: - Quality gate status as a PR check - Inline comments on new issues directly in the Azure DevOps PR view - Summary of new bugs, vulnerabilities, code smells, and coverage

This works out of the box after enabling the Azure DevOps integration in SonarCloud project settings.

10.6 Coverage Reports

For SonarCloud to display coverage data, the test job needs to run before the SonarCloud analysis. Two approaches:

Option A — Depend on UnitTests job (recommended):

- job: CodeQuality
  displayName: 'Code Quality (SonarCloud)'
  dependsOn: UnitTests
  steps:
    # Download coverage artifacts from UnitTests job
    - task: DownloadPipelineArtifact@2
      inputs:
        source: 'current'
        artifact: 'coverage'
        path: '$(System.DefaultWorkingDirectory)/coverage'
    # ... SonarCloud tasks ...

Option B — Run tests inside the CodeQuality job (simpler but slower):

- job: CodeQuality
  displayName: 'Code Quality (SonarCloud)'
  steps:
    - template: .azuredevops/pipelines/templates/install-dependencies.yml
    - script: pnpm nx run-many --target=test --all --parallel=2 -- --coverage
      displayName: 'Run Tests with Coverage'
    # ... SonarCloud tasks ...

11. Lightweight Alternatives

11.1 Alternatives Evaluated

No lightweight tool matches SonarQube/SonarCloud's combination of persistent dashboard, historical trends, technical debt quantification, and quality gates. Here's what exists:

Tool Type Self-Hosted RAM TS/JS Dashboard PR Analysis Cost
Semgrep CE CLI Partial ~200 MB Yes Cloud only (paid) Yes Free (CE)
MegaLinter CI-only Yes ~500 MB Yes No Via CI Free
FTA CLI Yes ~0 TS only No No Free
jscpd CLI Yes ~0 Yes No No Free
Qodana (JetBrains) Server Yes 16 GB Paid only Yes Yes $$$
CodeAnt AI SaaS No 0 Yes Yes Yes Paid

11.2 Why They Fall Short

Semgrep CE — The most interesting lightweight option. Runs as a single container (~200 MB), 3000+ rules, supports TypeScript. But the free version is CLI-only with JSON/SARIF output — no persistent dashboard, no historical trends, no quality gates with memory. The web dashboard requires the paid Semgrep AppSec Platform.

MegaLinter — Wraps 100+ linters (ESLint, jscpd, Semgrep) into a single Docker run. Zero server infrastructure. But produces a one-time report per pipeline run — no trend tracking, no unified dashboard.

FTA (Fast TypeScript Analyzer) — Rust-based, runs via npx fta-cli, measures complexity and maintainability. But TS-only, no dashboard, no duplication detection, no quality gates.

Qodana (JetBrains) — Full-featured but requires 16 GB RAM minimum and the TypeScript/JavaScript analyzer is only available in the paid tier. Heavier than SonarQube.

11.3 Composable "DIY" Stack

It's possible to combine ESLint + jscpd + FTA + Semgrep CE for zero-cost analysis in CI. The catch: no unified dashboard, no technical debt tracking, no historical trends, and you maintain the glue code. This gives "better than nothing" without the value of a real code quality platform.

11.4 Complementary Tooling

Since neither SonarQube CE nor SonarCloud includes full security scanning, the existing Syft + Grype integration remains essential:

Capability SonarQube/Cloud Syft + Grype Combined
Code smells / complexity Yes No Yes
SAST Basic No Basic
CVE scanning (containers) No Yes (Grype) Yes
SBOM generation No Yes (Syft) Yes

12. Nx Monorepo Scanner Integration

A sonar-project.properties file at the repository root, used by both SonarQube CE and SonarCloud:

sonar.projectKey=forma3d-connect
sonar.projectName=Forma 3D Connect

# Sources
sonar.sources=apps/web/src,apps/gateway/src,apps/order-service/src,apps/print-service/src,apps/shipping-service/src,apps/gridflock-service/src,libs
sonar.tests=apps,libs
sonar.test.inclusions=**/*.spec.ts,**/*.spec.tsx,**/*.test.ts

# Exclusions
sonar.exclusions=**/node_modules/**,**/dist/**,**/coverage/**,**/*.spec.ts,**/*.spec.tsx,**/*.test.ts,**/test/**,**/__mocks__/**

# Coverage
sonar.typescript.lcov.reportPaths=coverage/**/lcov.info

# Encoding
sonar.sourceEncoding=UTF-8

12.2 Per-Project Scan (Alternative)

The @koliveira15/nx-sonarqube plugin adds a sonar target per Nx project, leveraging the dependency graph:

pnpm add -D @koliveira15/nx-sonarqube
pnpm nx g @koliveira15/nx-sonarqube:config gateway
pnpm nx sonar gateway
Approach Pros Cons
Root-level scan Single analysis, simple setup No per-project isolation
Nx plugin per-project Granular metrics per service More config, multiple SonarQube/Cloud projects

Recommendation: Start with root-level for simplicity.

12.3 Coverage Flow

uml diagram


13. Quality Gates & Quality Profiles

13.1 Default Quality Gate ("Sonar Way")

Metric Condition
New code coverage ≥ 80%
New duplicated lines ≤ 3%
New maintainability rating A
New reliability rating A
New security rating A
New security hotspots reviewed 100%

13.2 Proposed Custom Gate

Metric Threshold Rationale
New code coverage ≥ 60% Achievable starting point; raise over time
New duplicated lines ≤ 5% Slightly relaxed for initial adoption
New bugs 0 Zero tolerance
New vulnerabilities 0 Zero tolerance
New code smells ≤ 10 Allow some while ramping up
New security hotspots reviewed 100% All hotspots must be triaged

13.3 SonarLint Integration

SonarLint (VS Code / Cursor extension) can connect to both SonarCloud and self-hosted SonarQube in "Connected Mode", syncing quality profiles and rules for real-time IDE feedback.


14. Head-to-Head Comparison

Criterion SonarQube CE (self-hosted) SonarCloud Team (SaaS)
Monthly cost ~$49 (droplet upgrade) $32 (at 100k LoC)
Setup effort ~1 day ~30 minutes
Infrastructure Docker Compose + PostgreSQL + kernel tuning None
Maintenance ~1 hr/month (updates, backups, monitoring) Zero
Branch analysis No (main only) Yes (all branches)
PR decoration No Yes (inline comments)
PR quality gate No Yes (blocks merge)
AI CodeFix No Yes
Dashboard Self-hosted (localhost:9000) sonarcloud.io
Data sovereignty Yes (DO droplet + Spaces) No (Sonar's cloud)
Cost at 200k LoC Still $49 ~$50–60
Cost at 500k LoC Still $49 Higher
Backup needed Yes (pg_dump + s3cmd) No
SonarLint sync Yes Yes
Nx monorepo support Yes Yes

15. Cost Analysis

15.1 Monthly Cost Comparison

Item SonarQube CE SonarCloud Team
License / subscription $0 $32
Droplet upgrade (8→16 GB) $48 $0
DO Spaces backup storage ~$1 $0
Maintenance effort (~1 hr) ~$50 (time value) $0
Total ~$99/month $32/month

15.2 Break-Even Analysis

SonarCloud costs scale with LoC. Self-hosted costs are fixed. At current growth rates:

Codebase Size SonarCloud (est.) SonarQube CE Cheaper Option
80k (now) $32/mo $99/mo SonarCloud
100k ~$32/mo $99/mo SonarCloud
200k ~$50–60/mo $99/mo SonarCloud
500k ~$100+/mo $99/mo Break-even
1M+ $150+/mo $99/mo SonarQube CE

Self-hosted only becomes cost-effective well past 500k LoC — likely years away at current growth pace.


16. Risks and Mitigations

16.1 SonarCloud Risks

Risk Likelihood Impact Mitigation
Cost increases as codebase grows High Low Budget for next tier; switch to self-hosted if needed
SonarCloud outage blocks CI Low Medium Set quality gate to advisory mode initially
Data leaves the security perimeter Ongoing Low Source snippets only; no secrets in code
Vendor lock-in Low Low sonar-project.properties is portable to self-hosted

16.2 SonarQube CE Risks

Risk Likelihood Impact Mitigation
Build agent OOM Medium High Upgrade droplet to 16 GB
Update breaks compatibility Low Medium Pin to LTA releases
Backup fails silently Medium Medium Log + Uptime Kuma health check
No PR feedback loop Ongoing Medium SonarLint Connected Mode for IDE feedback
Disk fills up Low Medium Docker log rotation + SonarQube housekeeping

17. Recommendation

17.1 Verdict: SonarCloud Team

uml diagram

Go with SonarCloud Team at $32/month because:

  1. Cheaper\(32 vs ~\)99/month total cost of self-hosted (including time)
  2. More features — Branch analysis, PR decoration, AI CodeFix
  3. Zero infrastructure — No droplet upgrade, no Docker Compose, no kernel tuning, no backups
  4. 30-minute setup — Install extension, create service connection, add ~15 lines to azure-pipelines.yml
  5. Portable — If the codebase outgrows the pricing, migrate to self-hosted SonarQube using the same sonar-project.properties

17.2 Implementation Steps

  1. Sign up at sonarcloud.io with the Azure DevOps organization
  2. Create the forma3d-connect project, select Team plan
  3. Install the SonarCloud extension in Azure DevOps
  4. Create a SonarCloud service connection in Azure DevOps project settings
  5. Add sonar-project.properties to the repository root
  6. Add the CodeQuality job to the ValidateAndTest stage in azure-pipelines.yml
  7. Run the pipeline, review the initial dashboard
  8. Configure a custom quality gate (start with advisory mode)
  9. Set up SonarLint Connected Mode for the team
  10. Formalize as an ADR

17.3 Decision Needed

  1. Approve SonarCloud Team subscription — $32/month
  2. Advisory vs blocking — Should the quality gate block PRs from the start, or run in advisory mode first?
  3. Coverage dependency — Should the CodeQuality job wait for UnitTests to finish (accurate coverage) or run independently (faster)?

This document should be formalized as an ADR once a decision is made.