Guides

Migrating Architectures

How to switch architecture presets in Technical Debt Radar, use relaxed enforcement during migrations, and gradually tighten rules as your codebase improves.

Migrating Architectures

Adopting Technical Debt Radar on an existing codebase --- or switching from one architecture pattern to another --- requires a migration strategy. You cannot enforce strict DDD boundaries on a codebase that has never had them without a plan.

This guide covers how to use relaxed rule packs, gradual enforcement, and time-bounded exceptions to migrate without blocking your team's velocity.


The Problem with Immediate Enforcement

If you install Radar on an existing 200-file codebase and enable strict DDD rules, your first scan might report 85 architecture violations, 12 performance issues, and 6 runtime risks. Every PR would be blocked until all 103 violations are resolved. That is not practical.

Instead, use a three-phase approach:

  1. Observe --- Run Radar in warning-only mode to understand your current debt.
  2. Enforce selectively --- Block new violations while providing a path to fix existing ones.
  3. Enforce strictly --- Enable full gate enforcement once the backlog is cleared.

Phase 1: Observe with Relaxed Mode

Install a relaxed rule pack that reports violations without blocking merges:

# For NestJS + Prisma + DDD
radar pack install nestjs-prisma-ddd-relaxed

# For Express + Sequelize + Layered
radar pack install express-layered-relaxed

# For any stack — generic relaxed mode
radar pack install relaxed

Relaxed packs make three changes:

  1. Raise the score threshold from 15 to 50 (so most PRs pass).
  2. Convert architecture blocks to warnings (violations appear in PR comments but do not block merge).
  3. Disable category-specific gates (like architecture_violations > 0).

Your rules.yml after installing a relaxed pack:

# Auto-generated by: radar pack install nestjs-prisma-ddd-relaxed
architecture_rules:
  - deny: domain -> infrastructure     # Still detected, but warns only
  - deny: domain -> api
  - deny: domain -> __db__
  - deny: application -> api
  - deny: api -> infrastructure
  - deny: cross-module direct imports
  - allow: cross-module through "src/**/contracts/**"

gates:
  block_merge:
    # architecture_violations gate removed — violations warn only
    - circular_dependencies_introduced > 0   # Still block circulars
    - runtime_risk_critical > 0              # Still block runtime risks
    - debt_delta_score > 50                  # Relaxed from 15 to 50
  warn:
    - architecture_violations > 0            # Moved to warn
    - debt_delta_score > 15
    - complexity_increase > 10

Tip: Run radar scan . after installing the relaxed pack. The report shows every existing violation, giving your team a baseline to plan the migration.


Phase 2: Enforce Selectively with Exceptions

Once your team understands the violation landscape, add exceptions for known violations in existing files while enforcing strict rules on new code.

Add Exceptions for Existing Violations

Generate exceptions for all current violations:

radar scan . --output-exceptions > existing-exceptions.yml

This produces a list of exception entries that you can merge into your radar.yml:

exceptions:
  - rule: "domain -> infrastructure"
    file: "src/billing/domain/billing.service.ts"
    expires: "2026-09-01"
    reason: "Pre-existing violation — migration sprint Q3 2026"

  - rule: "domain -> infrastructure"
    file: "src/orders/domain/order-calculator.ts"
    expires: "2026-09-01"
    reason: "Pre-existing violation — migration sprint Q3 2026"

  - rule: "controller -> repository"
    file: "src/controllers/legacy-report.controller.ts"
    expires: "2026-07-01"
    reason: "Legacy report endpoint — refactor in JIRA-2345"

Tighten the Gate

Now switch back to standard enforcement:

radar pack install nestjs-prisma-ddd

With exceptions covering existing violations, the strict rules apply only to new code. Existing violations are suppressed until their expiration dates.

gates:
  block_merge:
    - architecture_violations > 0      # Strict — but exceptions suppress old violations
    - circular_dependencies_introduced > 0
    - runtime_risk_critical > 0
    - reliability_critical > 0
    - critical_performance_risk > 0
    - debt_delta_score > 15            # Back to default

How It Works

When Radar scans a file covered by an active exception, that specific rule is suppressed for that file. The violation still appears in the dashboard (marked as "excepted") but does not count toward the gate score.

When the exception expires, the violation starts counting again. If the file has not been fixed by then, the next PR touching that file will be blocked.


Phase 3: Enforce Strictly

As your team fixes excepted violations, the exception list shrinks. Once all exceptions have been resolved or expired, you are running full enforcement.

Monitor progress:

# Show remaining exceptions and their expiry dates
radar validate --show-exceptions

# Output:
# Active exceptions: 12
# Expiring within 30 days: 4
# Expiring within 60 days: 7
# Expired (now enforced): 1

Switching Architecture Presets

If you are changing your architecture pattern entirely (e.g., from layered to DDD), the process involves updating your folder structure and configuration together.

Step 1: Update radar.yml Layers

Change the architecture field and update the layers section to match your new structure:

# Before (layered)
architecture: layered
layers:
  - name: routes
    path: "src/routes/**"
  - name: controllers
    path: "src/controllers/**"
  - name: services
    path: "src/services/**"
  - name: repositories
    path: "src/repositories/**"

# After (DDD)
architecture: ddd
layers:
  - name: api
    path: "src/**/controllers/**"
  - name: application
    path: "src/**/use-cases/**"
  - name: domain
    path: "src/**/domain/**"
  - name: infrastructure
    path: "src/**/infra/**"

Step 2: Update Modules

DDD organizes by bounded context rather than technical layer:

# Before (layered — single module)
modules:
  - name: core
    path: "src/**"

# After (DDD — bounded contexts)
modules:
  - name: billing
    path: "src/billing/**"
  - name: orders
    path: "src/orders/**"
  - name: identity
    path: "src/identity/**"

Step 3: Install the Target Pack in Relaxed Mode

radar pack install nestjs-prisma-ddd-relaxed

Step 4: Migrate Module by Module

Do not refactor the entire codebase at once. Migrate one bounded context at a time:

  1. Create the new folder structure for one module (e.g., src/billing/domain/, src/billing/infra/).
  2. Move files into the correct layers.
  3. Run radar scan src/billing/ to check for violations in the migrated module.
  4. Fix violations.
  5. Remove exceptions for that module.
  6. Repeat for the next module.

Step 5: Enable Strict Enforcement

Once all modules are migrated, switch to the strict pack:

radar pack install nestjs-prisma-ddd

Gradual Enforcement Strategy

For large teams, use a sprint-based approach to gradually tighten rules:

SprintActionGate Threshold
Sprint 1Install relaxed pack, baseline scandebt_delta_score > 50
Sprint 2Add exceptions for all existing violationsdebt_delta_score > 30
Sprint 3Enable architecture gate for new codearchitecture_violations > 0 (with exceptions)
Sprint 4Fix Tier 1 violations (circulars, critical runtime)debt_delta_score > 20
Sprint 5Fix Tier 2 violations (architecture, reliability)debt_delta_score > 15
Sprint 6Remove all exceptions, full enforcementDefault gates

Adjust the timeline based on your violation count and team capacity.


Tracking Migration Progress

The Technical Debt Radar dashboard provides dedicated views for tracking migration progress.

Trend Chart

The trend chart plots your total debt score over time. During a migration, you should see a consistent downward trend as exceptions expire and violations are fixed.

Exception Burndown

The exception burndown chart shows how many active exceptions remain and projects when they will all expire based on current velocity.

Category Breakdown

The category breakdown shows which types of violations are being fixed fastest. Architecture violations typically clear first (they are high-priority and often straightforward), while maintainability issues clear last.

Module Health

The module health view shows debt scores per module, making it easy to track which bounded contexts have been migrated and which still need work.

Technical Debt Radar Documentation