Hidden Technical Debt in AI-Generated Codebases — Why Working Code Gets Expensive to Change

# ai# webdev# technicaldebt# typescript
Hidden Technical Debt in AI-Generated Codebases — Why Working Code Gets Expensive to Changevibecodiq

"It worked fine for six months. Then every change became expensive." Your AI-generated app is live....

"It worked fine for six months. Then every change became expensive."

Your AI-generated app is live. Customers are paying. The dashboard looks fine. But every feature takes 2-3x longer than it should, and nobody can explain why.

This post explains what hidden technical debt looks like in AI-generated codebases, how to detect it, and the structural decomposition that fixes it.

What Makes AI-Generated Debt "Hidden"

Traditional technical debt is visible — you know you're cutting corners. AI-generated technical debt is invisible — the shortcuts happen at generation time and nobody notices.

Here's the mechanism:

Session 1:  User service created (clean, 80 lines)
Session 15: Pricing logic added to user service ("it needs user data")
Session 30: Notification logic added to pricing flow ("notify on purchase")
Session 45: User service is now 600 lines, handles 4 domains
Session 60: "Why does adding a referral system take 3 weeks?"
Enter fullscreen mode Exit fullscreen mode

Each session makes a locally reasonable decision. The AI puts code where the context is closest. Nobody checks whether the global architecture remains coherent.

DOMAIN SCATTERING MAP

  userService.ts (620 lines)
    - user logic         ████████░░  40%
    - pricing logic      ████░░░░░░  20%
    - notifications      ███░░░░░░░  15%
    - audit logging      ███░░░░░░░  15%
    - shared state       ██░░░░░░░░  10%

  AFTER DECOMPOSITION:
    users/userService.ts          (120 lines)
    pricing/priceCalculator.ts    (90 lines)
    notifications/notifier.ts     (70 lines)
    billing/auditLogger.ts        (60 lines)

  1 file x 5 domains --> 4 files x 1 domain each
Enter fullscreen mode Exit fullscreen mode

Detection: Three Measurements

1. File Size Distribution

find src -name "*.ts" -o -name "*.tsx" | xargs wc -l | sort -rn | head -20
Enter fullscreen mode Exit fullscreen mode

What to look for:

  • Files over 500 lines = almost certainly multi-domain
  • Files over 800 lines = critical structural risk
  • If your top 10 files average over 400 lines, the architecture has drifted

2. Domain Scattering

Pick a core business concept (e.g., "pricing"). Count how many files contain it:

grep -rln "price\|pricing\|amount\|cost\|subscription\|billing" \
  --include="*.ts" --include="*.tsx" src/ | wc -l
Enter fullscreen mode Exit fullscreen mode

What the number means:

  • 1-3 files: Clean. Pricing logic is centralized.
  • 4-7 files: Warning. Logic is scattering.
  • 8+ files: Critical. Pricing decisions happen everywhere.

3. Shared Mutable State

# Count module-level mutable variables
grep -rn "^let \|^var \|export let\|export var" \
  --include="*.ts" --include="*.tsx" src/ | wc -l
Enter fullscreen mode Exit fullscreen mode

Each one is a hidden coupling point. A change in one function silently affects every other function that reads or writes the same variable.

4. Feature Estimate Accuracy

Look at your last 10 features or bug fixes. Calculate:

accuracy = (actual_time / estimated_time)
Enter fullscreen mode Exit fullscreen mode

If the average is above 2.0, the architecture is fighting you. Hidden dependencies are making scope unpredictable.

Why "Refactor Later" Doesn't Work

Technical debt has interest rates. AI-generated codebases have higher interest rates because:

  1. Shortcuts are invisible. Nobody chose to put pricing logic in the user service. The AI did it because that's where the context was.

  2. Coupling compounds. Every feature added on top of a coupled module increases the coupling. By month 6, you can't change pricing without touching users, notifications, and payments.

  3. The window closes. Refactoring in month 3 is a focused sprint. Refactoring in month 9 is a rewrite. The complexity grows faster than capacity.

Month 3:  Refactoring cost = 2-3 days
Month 6:  Refactoring cost = 2-3 weeks
Month 9:  Refactoring cost = "we need to rewrite"
Month 12: "We're stuck"
Enter fullscreen mode Exit fullscreen mode

The Structural Fix: Decomposition + Enforcement

Step 1: Domain Audit

Map every file to its primary business domain:

# Create a domain map
for file in $(find src -name "*.ts" -o -name "*.tsx"); do
  echo "$file: $(head -5 $file | grep -o 'import.*from' | head -3)"
done
Enter fullscreen mode Exit fullscreen mode

Identify files that serve multiple domains (these are the decomposition targets).

Step 2: Extract Domain Modules

For each multi-domain file:

  1. Identify the distinct responsibilities
  2. Create separate module files for each domain
  3. Move logic to the appropriate module
  4. Update imports
// BEFORE: src/services/userService.ts (600 lines, 4 domains)
export function getUser() { ... }
export function calculatePrice() { ... }    // pricing domain
export function sendNotification() { ... }  // notification domain
export function generateInvoice() { ... }   // billing domain

// AFTER: 4 focused modules
// src/modules/users/userService.ts
// src/modules/pricing/priceCalculator.ts
// src/modules/notifications/notificationService.ts
// src/modules/billing/invoiceGenerator.ts
Enter fullscreen mode Exit fullscreen mode

Step 3: Eliminate Shared Mutable State

Replace module-level mutable variables with:

  • Function parameters (explicit data flow)
  • Immutable state containers
  • Event-based communication between modules
// BEFORE: shared mutable state
let currentUser: User | null = null;

export function setUser(user: User) { currentUser = user; }
export function getPrice() { return currentUser?.plan.price; } // hidden dependency

// AFTER: explicit data flow
export function getPrice(user: User): number {
  return user.plan.price;
}
Enter fullscreen mode Exit fullscreen mode

Step 4: Enforce Boundaries

Add automated checks that prevent re-coupling:

// package.json script
{
  "scripts": {
    "check:boundaries": "npx madge --circular --extensions ts,tsx src/",
    "check:size": "find src -name '*.ts' | xargs wc -l | awk '$1 > 500 {print \"WARN:\", $0}'",
    "precommit": "npm run check:boundaries && npm run check:size"
  }
}
Enter fullscreen mode Exit fullscreen mode

Step 5: Monitor Trend

Track these metrics weekly:

  • Number of files over 500 lines (should decrease or stay flat)
  • Number of circular dependencies (should be 0)
  • Feature estimate accuracy (should improve toward 1.0-1.5x)

The Result

After structural decomposition, the cost of change becomes predictable. Adding a referral system touches the referral module — not pricing, users, notifications, and billing. Feature estimates become accurate because scope is bounded.

Hidden technical debt doesn't require a rewrite. It requires structural decomposition and ongoing enforcement.


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