一键导入
dependency-mapping
Map dependencies and coupling in legacy codebase to understand what breaks when you change something and identify refactoring risks
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
菜单
Map dependencies and coupling in legacy codebase to understand what breaks when you change something and identify refactoring risks
用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
基于 SOC 职业分类
Transform markdown notes into engaging technical blog posts for both programmers and non-technical readers
Systematic techniques for reading and understanding unfamiliar legacy code without documentation
Create detailed implementation plans with bite-sized tasks for engineers with zero codebase context
Creating documentation from legacy code when none exists, focusing on what future maintainers need to know
Maintain skills wiki health - check links, naming, cross-references, and coverage
TDD for process documentation - test with subagents before writing, iterate until bulletproof
| name | Dependency Mapping |
| description | Map dependencies and coupling in legacy codebase to understand what breaks when you change something and identify refactoring risks |
| when_to_use | when you need to understand dependencies and coupling before making changes, or identifying what will break if you modify a component |
| version | 1.0.0 |
| languages | all |
Before changing code, know what depends on it. Dependency mapping reveals the blast radius of your changes.
Core principle: Understand coupling before changing. High coupling = high risk. Map first, change second.
Map dependencies when:
What: Code explicitly imports/requires another module
import { UserService } from './services/user'; // Direct dependency
class OrderController {
constructor(private userService: UserService) {}
// OrderController depends on UserService
}
Tools:
# Find what imports this file (JavaScript/TypeScript)
grep -r "import.*UserService" --include="*.ts" --include="*.js"
# Find what imports this file (C#)
grep -r "using.*UserService" --include="*.cs"
# Find what this file imports
grep "^import" src/services/user.ts # JS/TS
grep "^using" src/Services/UserService.cs # C#
What: Components share data structures or database tables
UserService → users table ← AuthService
Both depend on users table structure. Schema change affects both.
How to find:
# Find all SQL queries against table
grep -r "FROM users" --include="*.ts"
grep -r "users\." --include="*.sql"
What: Component A must run before Component B
// Migrations must run in order
001_create_users.sql
002_add_email_to_users.sql // Depends on 001 existing
Red flag: Race conditions if order not guaranteed
What: Code depends on external service being available
class PaymentService {
async charge() {
await this.stripe.charge(); // Runtime dependency on Stripe API
}
}
Impact: Service down = feature down
Tool: dependency-cruiser (JavaScript/TypeScript)
npm install -g dependency-cruiser
depcruise --include-only "^src" --output-type dot src | dot -T svg > deps.svg
Tool: madge (JavaScript)
npm install -g madge
madge --image deps.png src/
Tool: NDepend (.NET)
# Commercial tool for .NET dependency analysis
# Generates dependency graphs and metrics
# https://www.ndepend.com/
Tool: grep (any language)
# What depends on UserService? (JavaScript/TypeScript)
grep -r "UserService" --include="*.ts" --include="*.js" src/
# What depends on UserService? (C#)
grep -r "UserService" --include="*.cs" src/
# What does UserService depend on?
grep "^import" src/services/UserService.ts # JS/TS
grep "^using" src/Services/UserService.cs # C#
Technique: Add logging
class UserService {
findById(id: string) {
console.log(`UserService.findById called from ${new Error().stack}`);
// ...
}
}
Run application, see who calls what.
Find shared tables:
# Which files query users table?
grep -r "FROM users" --include="*.ts" --include="*.sql"
# Result shows coupling through data
Technique: Files that change together
# Files changed together with UserService
git log --format="" --name-only -- src/services/UserService.ts | \
sort | uniq -c | sort -rn | head -20
Files that change together often = coupled.
OrderController
→ UserService
→ Database (users table)
→ CacheService (Redis)
→ PaymentService
→ Stripe API
→ Database (payments table)
| UserService | PaymentService | EmailService | |
|---|---|---|---|
| OrderController | ✓ | ✓ | ✓ |
| UserController | ✓ | ✓ | |
| AdminController | ✓ | ✓ |
Row depends on columns.
┌─────────────────────────────┐
│ Controllers (HTTP Layer) │
├─────────────────────────────┤
│ Services (Business Logic) │
├─────────────────────────────┤
│ Repositories (Data Access) │
├─────────────────────────────┤
│ Database │
└─────────────────────────────┘
Violations: Controller → Repository directly (skips Service layer)
UserService is depended on by:
- OrderController
- AuthController
- AdminController
Ca = 3 (high coupling - many depend on it)
High Ca = Risky to change (many things break)
OrderController depends on:
- UserService
- PaymentService
- EmailService
- NotificationService
Ce = 4 (high coupling - depends on many)
High Ce = Hard to test (many dependencies to mock)
UserService: Ca=5, Ce=2
I = 2/(5+2) = 0.28 (stable - more depended on than depending)
UtilityFunction: Ca=0, Ce=10
I = 10/(0+10) = 1.0 (unstable - all dependencies, no dependents)
I near 0: Stable (hard to change safely) I near 1: Unstable (easy to change, low impact)
UserService depended on by 50 files
Problem: Change breaks everything Solution: Split into smaller services
A → B → C → A (cycle!)
Problem: Can't change one without others Solution: Break cycle with interface/event
Change "user email format" requires editing:
- UserService.ts
- UserController.ts
- UserValidator.ts
- EmailService.ts
- NotificationService.ts
...20 more files
Problem: Single concept scattered across many files Solution: Extract into single place
Controller → Database directly (skipping Service layer)
Problem: Violates architecture, hard to test Solution: Enforce layer boundaries
JavaScript/TypeScript:
# Count dependencies (imports) per file
find src -name "*.ts" -exec sh -c \
'echo "$(grep -c ^import "$1") $1"' _ {} \; | sort -rn
# Find files with >20 imports (high coupling)
find src -name "*.ts" -exec sh -c \
'count=$(grep -c ^import "$1"); [ $count -gt 20 ] && echo "$count $1"' _ {} \;
# Find most-imported files (high Ca)
grep -rh "^import.*from" --include="*.ts" src/ | \
sed "s/.*from ['\"]//;s/['\"].*//" | \
sort | uniq -c | sort -rn | head -20
C#/.NET:
# Count dependencies (usings) per file
find . -name "*.cs" -exec sh -c \
'echo "$(grep -c "^using " "$1") $1"' _ {} \; | sort -rn
# Find files with >20 usings (high coupling)
find . -name "*.cs" -exec sh -c \
'count=$(grep -c "^using " "$1"); [ $count -gt 20 ] && echo "$count $1"' _ {} \;
# Find most-used namespaces (high Ca)
grep -rh "^using " --include="*.cs" . | \
sed "s/using //;s/;.*//" | \
sort | uniq -c | sort -rn | head -20
Bad: "This looks simple" → Change → 10 things break Good: Map dependencies → Understand impact → Change safely
Bad: "No code imports it, safe to change" Good: "Check who queries this table first"
Data dependencies are invisible in import statements.
Step 1: Map dependencies
# Who imports UserService?
grep -r "UserService" --include="*.ts" src/
# Result: 35 files depend on it (Ca = 35)
# What does UserService import?
grep "^import" src/services/UserService.ts
# Result: Depends on DB, Cache, Logger (Ce = 3)
# Instability: I = 3/(35+3) = 0.08 (very stable = risky to change!)
Step 2: Analyze dependents
Controllers: 12 files
Services: 15 files
Background jobs: 5 files
Tests: 3 files
Step 3: Plan safe refactor
Option A: Big-bang (risky - 35 files break)
Option B: Add interface, change implementation (safer - 0 files break)
Option C: Strangler fig pattern (safest - gradual migration)
Decision: Option C (strangler fig) due to high coupling.