"The architecture changed. Nobody decided it should."
Architecture drift is the #1 root cause of structural failure in AI-generated codebases. It happens silently, one prompt at a time, and by the time it's visible, the cost of fixing it has compounded significantly.
The Mechanism
Each AI session optimizes for the immediate task without maintaining awareness of the broader architecture.
BOUNDARY EROSION TIMELINE
Month 1: API --> Business --> Data
(clean layers, clear direction)
Month 4: API <-> Business <-> Data
(boundaries crossed, bidirectional)
Month 8: API <-> Business <-> Data
\ | X | /
\---> UI <---/
(boundaries dissolved, spaghetti)
Session 1: "Create a user service" — clean module with clear boundaries.
Session 15: "Fix the checkout bug" — fix goes in the route handler because that's where the symptom is.
Session 30: "Add email on purchase" — notification logic added inline in the payment handler.
Session 50: Business logic lives in 5 layers. Nobody can draw the architecture from memory.
Detection
1. Layer Violation Check
# Database queries in UI components (should be 0)
grep -rn "prisma\|supabase\|\.from(" --include="*.tsx" src/components/ src/app/ | wc -l
# Business logic in route handlers (should be minimal)
grep -rn "if.*price\|calculate\|validate" --include="*.ts" src/app/api/ | wc -l
2. Import Direction Analysis
# Files with the most imports (potential boundary violators)
grep -rn "import.*from" --include="*.ts" --include="*.tsx" src/ | \
awk -F: '{print $1}' | sort | uniq -c | sort -rn | head -10
In a clean architecture, import counts should be low and directional (API imports Business, Business imports Data, never the reverse).
3. Business Logic Scattering
# Where does "payment" logic live?
grep -rln "payment\|stripe\|checkout\|price" \
--include="*.ts" --include="*.tsx" src/ | \
awk -F/ '{print $1"/"$2}' | sort | uniq -c | sort -rn
If a single business concept appears in 4+ directories, the architecture has drifted.
4. Convention Drift
# Check for inconsistent patterns
grep -rn "export default function\|export function\|export const" \
--include="*.ts" --include="*.tsx" src/ | \
awk -F: '{print $3}' | sort | uniq -c | sort -rn
Multiple export patterns suggest the AI changed conventions mid-project.
The Compounding Cost
Feature delivery cost over time:
Month 1: 2 days (clean architecture)
Month 4: 4 days (50% archaeology)
Month 8: 8 days (75% archaeology)
Month 12: 12+ days (mostly archaeology)
For a 3-person team at $150/hour, the velocity difference between month 1 and month 8 represents ~$36,000/month in wasted capacity.
Why Sprints Don't Fix This
Architecture drift is a compound structural condition. Sprints are time-boxed delivery cycles. They operate at different scales:
- Sprint: "Fix this module" — fixes surface symptoms
- Structural intervention: "Re-establish all boundaries" — fixes root cause
Without enforcement, the next sprint re-introduces the same violations.
The Structural Fix
Step 1: Map Current State
# Generate dependency graph
npx madge --image graph.svg --extensions ts,tsx src/
# List all circular dependencies
npx madge --circular --extensions ts,tsx src/
Step 2: Define Target Architecture
Document the intended layer structure:
src/
app/ # Routing only (Next.js pages/API routes)
modules/ # Business logic (one module per domain)
auth/
payments/
users/
notifications/
shared/ # Cross-cutting utilities
types/
utils/
lib/
Rule: app/ imports modules/. modules/ imports shared/. Never the reverse.
Step 3: Migrate Incrementally
For each layer violation:
- Identify the misplaced logic
- Create the correct target module
- Move the logic
- Update imports
- Verify no regressions
Step 4: Enforce Boundaries
// eslint.config.js — import boundary enforcement
export default [
{
rules: {
'import/no-restricted-paths': ['error', {
zones: [
// modules/ cannot import from app/
{ target: './src/modules', from: './src/app' },
// shared/ cannot import from modules/ or app/
{ target: './src/shared', from: './src/modules' },
{ target: './src/shared', from: './src/app' },
]
}]
}
}
];
Step 5: CI/CD Enforcement
# .github/workflows/architecture.yml
name: Architecture Check
on: [pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci
- name: Circular dependency check
run: npx madge --circular --extensions ts,tsx src/ && echo "PASS" || (echo "FAIL: circular deps" && exit 1)
- name: Lint (includes boundary rules)
run: npm run lint
The Result
After structural intervention + enforcement:
- Layer violations: blocked at CI
- Circular dependencies: prevented
- Business logic: centralized in domain modules
- Feature delivery: predictable again (back to ~2 days for standard features)
Architecture drift is the root cause. Boundary enforcement is the structural fix.
This is part of the AI Chaos series — a structural analysis of failure patterns in AI-generated codebases. Based on ASA (Atomic Slice Architecture) — an open architecture standard for AI-generated software.
Resources
- ASA Standard — the open specification
- GitHub — source, examples, documentation
- Vibecodiq — structural diagnostics for AI-built apps
Top comments (0)