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:
- Observe --- Run Radar in warning-only mode to understand your current debt.
- Enforce selectively --- Block new violations while providing a path to fix existing ones.
- 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:
- Raise the score threshold from 15 to 50 (so most PRs pass).
- Convert architecture blocks to warnings (violations appear in PR comments but do not block merge).
- 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:
- Create the new folder structure for one module (e.g.,
src/billing/domain/,src/billing/infra/). - Move files into the correct layers.
- Run
radar scan src/billing/to check for violations in the migrated module. - Fix violations.
- Remove exceptions for that module.
- 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:
| Sprint | Action | Gate Threshold |
|---|---|---|
| Sprint 1 | Install relaxed pack, baseline scan | debt_delta_score > 50 |
| Sprint 2 | Add exceptions for all existing violations | debt_delta_score > 30 |
| Sprint 3 | Enable architecture gate for new code | architecture_violations > 0 (with exceptions) |
| Sprint 4 | Fix Tier 1 violations (circulars, critical runtime) | debt_delta_score > 20 |
| Sprint 5 | Fix Tier 2 violations (architecture, reliability) | debt_delta_score > 15 |
| Sprint 6 | Remove all exceptions, full enforcement | Default 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.