From cortexloop
Analyzes dependency graphs to detect circular dependencies, God Objects, and tight coupling, then guides interactive refactoring decisions.
How this skill is triggered — by the user, by Claude, or both
Slash command
/cortexloop:architecture-analysisThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
Detect architectural coupling, identify refactoring opportunities, and guide interactive refactoring decisions.
Detect architectural coupling, identify refactoring opportunities, and guide interactive refactoring decisions.
Use this skill when:
/cortexloop-architectureDo NOT use this skill when:
correctness-review)security-and-hardening)code-simplification)Architecture Analysis (this skill) and Pass 6 Simplicity have overlapping goals but different scopes:
| Dimension | Architecture Analysis | Pass 6 Simplicity |
|---|---|---|
| Scope | Module/package level | Function/class level |
| Focus | Dependency structure | Code clarity |
| Changes | Refactor architecture | Simplify implementation |
| Frequency | Monthly/quarterly | Every PR |
| Examples | Break circular deps, split God Objects | Remove unnecessary abstraction, inline small functions |
When to use Architecture Analysis:
When to use Simplicity (pass 6):
They complement each other:
Example:
Problem: Dashboard.tsx (800 lines) directly imports 8 services
Architecture Analysis says:
→ Introduce DashboardFacade to hide service complexity (module-level refactor)
After that, Simplicity (pass 6) says:
→ DashboardFacade.getUserData() has nested callbacks, flatten with async/await (code-level simplification)
Definition: Module A depends on Module B, and Module B depends on Module A (directly or transitively).
Detection method:
Types:
Direct cycle (2 modules):
A → B → A
Example: users.ts imports auth.ts, auth.ts imports users.ts
Transitive cycle (3+ modules):
A → B → C → A
Example: api.ts → service.ts → repository.ts → api.ts
Symptoms:
Refactoring patterns:
Definition: A module that knows too much or does too much.
Detection method:
Symptoms:
Refactoring patterns:
Definition: A module that uses methods/data from another module more than its own.
Detection method:
Symptoms:
Refactoring patterns:
Definition: Two modules are too tightly coupled, accessing each other's internals.
Detection method:
Symptoms:
Refactoring patterns:
Definition: A single change requires modifications across many modules.
Detection method:
Symptoms:
Refactoring patterns:
Number of modules that depend on this module.
Interpretation:
Number of modules this module depends on.
Interpretation:
Total number of other classes/modules this class/module is coupled to.
Formula: CBO = fan-in + fan-out
Interpretation:
Steps:
Parse imports/requires (language-specific)
JavaScript/TypeScript:
// Match patterns:
import { X } from './module' // ES6 import
import X from './module'
const X = require('./module') // CommonJS
import('./module') // dynamic import
// Extract: './module' → resolve to absolute path
Python:
# Match patterns:
import module # absolute import
from module import X # from import
from . import module # relative import
from .. import module # parent relative
# Extract: 'module' → resolve to file path
Java:
// Match patterns:
import com.example.Module; // class import
import com.example.*; // wildcard import
// Extract: package path → resolve to file
Go:
// Match patterns:
import "github.com/user/module" // external package
import "./module" // relative import
// Extract: import path → resolve to directory
General approach:
@babel/parser for JS, ast for Python)Build adjacency list
{
"src/api/users.ts": ["src/services/auth.ts", "src/db/user-repo.ts"],
"src/services/auth.ts": ["src/api/users.ts", "src/db/user-repo.ts"],
...
}
Important: Use absolute paths or project-relative paths to avoid ambiguity.
Detect cycles
Calculate metrics
Ask for project structure or use file system scan.
If project is small (<50 files):
If project is medium/large (>50 files):
Run cycle detection:
def detect_cycles(graph):
visited = set()
rec_stack = set()
cycles = []
def dfs(node, path):
visited.add(node)
rec_stack.add(node)
for neighbor in graph.get(node, []):
if neighbor not in visited:
dfs(neighbor, path + [node])
elif neighbor in rec_stack:
# Found cycle: reconstruct from path
cycle_start = path.index(neighbor) if neighbor in path else 0
cycle = path[cycle_start:] + [node, neighbor]
cycles.append(cycle)
rec_stack.remove(node)
for node in graph:
if node not in visited:
dfs(node, [])
return cycles
For each module:
def calculate_metrics(graph, module):
fan_out = len(graph.get(module, []))
fan_in = sum(1 for m in graph if module in graph.get(m, []))
cbo = fan_in + fan_out
return {"fan_in": fan_in, "fan_out": fan_out, "cbo": cbo}
def detect_god_objects(modules):
god_objects = []
for module in modules:
metrics = calculate_metrics(graph, module)
lines = count_lines(module)
if metrics["fan_out"] > 10 and lines > 500:
god_objects.append({
"module": module,
"fan_out": metrics["fan_out"],
"lines": lines,
"severity": "high" if lines > 1000 else "medium"
})
return god_objects
Priority scoring:
def priority_score(coupling):
score = 0
# Type weight
if coupling["type"] == "circular-dependency":
score += 3
elif coupling["type"] == "god-object":
score += 2
elif coupling["type"] == "feature-envy":
score += 2
elif coupling["type"] == "inappropriate-intimacy":
score += 2
elif coupling["type"] == "shotgun-surgery":
score += 1
# Impact weight
if coupling["impact"]["files"] > 10:
score += 2
elif coupling["impact"]["files"] > 5:
score += 1
# CBO weight
if coupling["cbo"] > 15:
score += 2
elif coupling["cbo"] > 10:
score += 1
return score
Priority levels:
Examples:
Circular dependency between 2 core modules, CBO=15, impacts 8 files:
Circular dependency between 2 utility modules, CBO=4, impacts 2 files:
God Object with 20 imports, impacts 15 files:
Rule of thumb for circular dependencies:
Each coupling detection should include a confidence level:
Examples:
Report only High/Medium confidence findings. Discard Low confidence findings to reduce false positives.
For each coupling point, generate 2-3 refactoring solutions.
{
"id": "solution-1a",
"label": "Extract UserAuthContext to independent module",
"recommended": true,
"confidence": "high",
"pattern": "extract-interface",
"steps": [
"Create src/auth/UserAuthContext.ts",
"Move auth-related logic from users.ts",
"Update auth.ts references",
"Update all callers"
],
"pros": [
"Breaks circular dependency",
"Clearer responsibility"
],
"cons": [
"Requires new module",
"Need to update 7 files"
],
"impact": {
"newFiles": 1,
"modifiedFiles": 7,
"testsRequired": true
},
"cost": "medium",
"risk": "low"
}
When to use: Circular dependency, tight coupling
Steps:
Example:
Before:
users.ts → auth.ts
auth.ts → users.ts
After:
users.ts → UserAuthContext.ts
auth.ts → UserAuthContext.ts
When to use: Circular dependency, high coupling
Steps:
Example:
// Before
class UserService {
constructor() {
this.auth = new AuthService(); // tight coupling
}
}
// After
interface IAuthService { ... }
class UserService {
constructor(private auth: IAuthService) {} // depend on abstraction
}
When to use: God Object, Feature Envy
Steps:
Example:
// Before: Dashboard directly calls 5 services
import { UserService } from './user-service';
import { OrderService } from './order-service';
import { PaymentService } from './payment-service';
...
// After: Dashboard calls Facade
import { DashboardFacade } from './dashboard-facade';
When to use: God Object
Steps:
Example:
Before:
utils.ts (1000 lines)
- string utilities
- date utilities
- validation utilities
- http utilities
After:
string-utils.ts
date-utils.ts
validation-utils.ts
http-utils.ts
When to use: Feature Envy
Steps:
Present coupling point and ask:
要执行哪个解耦?
A. 执行第 1 个(users + auth 循环依赖)
B. 执行第 2 个(Dashboard 耦合)
C. 执行第 3 个(utils 拆分)
D. 依次执行全部
E. 停止
执行解耦:src/api/users.ts ↔ src/services/auth.ts
推荐方案:提取 UserAuthContext 到独立模块
详细步骤:
1. 创建 src/auth/UserAuthContext.ts
2. 将 users.ts 中的 auth 相关逻辑迁移
3. 更新 auth.ts 引用
4. 更新所有调用方
预计影响:
- 新增文件:1
- 修改文件:7
- 需要更新测试:是
优点:
- 解除循环依赖
- 职责更清晰
缺点:
- 需要新增一个模块
- 需要更新 7 个文件
成本估算:Medium
风险等级:Low
确认执行?
A. 是
B. 否,跳过这个
C. 看详细 diff 再决定
If user chooses A:
After refactoring:
# Run relevant tests
npm test -- users auth
# If tests pass
✓ tests/api/users.test.ts 通过
✓ tests/services/auth.test.ts 通过
# Run full suite
npm test
# If full suite passes
✓ 全量测试通过
If tests fail:
✗ tests/api/users.test.ts 失败
- UserController.login 返回 undefined
检测到测试失败。
选项:
A. 回滚这次重构,保留报告
B. 尝试修复测试(可能需要你手动介入)
C. 忽略失败,继续下一个解耦(不推荐)
If user chooses A (rollback):
git checkout -- src/api/users.ts src/services/auth.ts src/auth/
If user chooses B (fix):
docs/cortexloop/architecture-analysis.md
# 架构解耦分析
**分析时间:** 2026-07-08 10:30:00
**分析范围:** 整个项目
**发现耦合点:** 3 处
## 1. 循环依赖:src/api/users.ts ↔ src/services/auth.ts
**优先级:** High
**CBO:** 15
**问题描述:** users.ts 调用 auth.ts 的 validateToken,auth.ts 调用 users.ts 的 getUserById,形成循环依赖。
**影响:**
- 核心文件:2
- 间接引用:5
- 测试文件:4
### 推荐方案 A(推荐)
提取 UserAuthContext 到独立模块 `src/auth/UserAuthContext.ts`
**模式:** Extract Interface
**步骤:**
1. 创建 src/auth/UserAuthContext.ts
2. 迁移 users.ts 逻辑
3. 更新 auth.ts 引用
4. 更新调用方
**优点:**
- 解除循环依赖
- 职责更清晰
**缺点:**
- 需要新增一个模块
- 需要更新 7 个文件
**成本估算:** Medium
**风险等级:** Low
### 推荐方案 B
将 auth 逻辑下沉到 users 内部
**模式:** Merge Modules
(类似格式...)
---
## 2. God Object:src/lib/utils.ts
(类似格式...)
---
## 3. Feature Envy:src/ui/Dashboard.tsx
(类似格式...)
.cortexloop/architecture-cache.json
{
"analyzedAt": "2026-07-08T10:30:00Z",
"scope": "whole-project",
"couplingPoints": [
{
"id": "coupling-1",
"type": "circular-dependency",
"priority": "high",
"priorityScore": 6,
"confidence": "high",
"files": ["src/api/users.ts", "src/services/auth.ts"],
"cyclePath": ["src/api/users.ts", "src/services/auth.ts", "src/api/users.ts"],
"metrics": {
"cbo": 15,
"fanIn": 7,
"fanOut": 8
},
"description": "users.ts 调用 auth.ts 的 validateToken,auth.ts 调用 users.ts 的 getUserById",
"impact": {
"coreFiles": 2,
"indirectReferences": 5,
"testFiles": 4
},
"solutions": [
{
"id": "solution-1a",
"label": "提取 UserAuthContext 到独立模块",
"pattern": "extract-interface",
"recommended": true,
"confidence": "high",
"steps": [
"Create src/auth/UserAuthContext.ts",
"Move auth-related logic from users.ts",
"Update auth.ts references",
"Update all callers"
],
"pros": [
"Breaks circular dependency",
"Clearer responsibility"
],
"cons": [
"Requires new module",
"Need to update 7 files"
],
"impact": {
"newFiles": 1,
"modifiedFiles": 7,
"testsRequired": true
},
"cost": "medium",
"risk": "low"
},
{
"id": "solution-1b",
"label": "Merge auth logic into users module",
"pattern": "merge-modules",
"recommended": false,
"confidence": "medium",
"steps": [
"Move auth.ts logic into users.ts",
"Update all auth.ts imports to users.ts"
],
"pros": [
"No new module needed",
"Simpler structure"
],
"cons": [
"users module becomes larger",
"Mixing concerns"
],
"impact": {
"newFiles": 0,
"modifiedFiles": 6,
"testsRequired": true
},
"cost": "low",
"risk": "medium"
}
]
}
]
}
| Coupling Type | Typical files affected | Typical cost | Typical risk |
|---|---|---|---|
| Circular dependency | 2-10 | Medium | Low-Medium |
| God Object | 1 core + 10-30 callers | High | Medium |
| Feature Envy | 2-5 | Low | Low |
| Inappropriate Intimacy | 2-8 | Medium | Low-Medium |
| Shotgun Surgery | 10-20 | High | High |
Before:
// src/api/users.ts
import { validateToken } from './auth';
export function getUser(id) {
if (!validateToken()) throw new Error('Unauthorized');
return db.users.get(id);
}
// src/services/auth.ts
import { getUser } from './api/users';
export function validateToken() {
const user = getUser(currentUserId);
return user && user.isActive;
}
After (Extract Interface):
// src/auth/UserAuthContext.ts
export interface UserAuthContext {
userId: string;
isActive: boolean;
}
export function validateToken(context: UserAuthContext) {
return context && context.isActive;
}
// src/api/users.ts
import { validateToken, UserAuthContext } from './auth/UserAuthContext';
export function getUser(id, context: UserAuthContext) {
if (!validateToken(context)) throw new Error('Unauthorized');
return db.users.get(id);
}
// src/services/auth.ts
import { UserAuthContext } from './auth/UserAuthContext';
// No longer imports from users.ts
Before:
// src/lib/utils.ts (1000 lines)
export function formatDate(date) { ... }
export function parseDate(str) { ... }
export function isValidEmail(email) { ... }
export function isValidPhone(phone) { ... }
export function fetchData(url) { ... }
export function postData(url, data) { ... }
// ... 50 more unrelated functions
After (Split by Responsibility):
// src/lib/date-utils.ts
export function formatDate(date) { ... }
export function parseDate(str) { ... }
// src/lib/validation-utils.ts
export function isValidEmail(email) { ... }
export function isValidPhone(phone) { ... }
// src/lib/http-utils.ts
export function fetchData(url) { ... }
export function postData(url, data) { ... }
Before:
// src/api/orders.ts
import { calculatePrice } from '../services/pricing';
export function createOrder(data) {
const price = calculatePrice(data.items);
return db.orders.create({ ...data, price });
}
// src/services/pricing.ts
import { getInventory } from '../repositories/inventory';
export function calculatePrice(items) {
const inventory = getInventory(items);
return items.reduce((sum, item) => sum + inventory[item.id].price, 0);
}
// src/repositories/inventory.ts
import { getOrders } from '../api/orders';
export function getInventory(items) {
// Uses order history to adjust inventory
const orders = getOrders(); // Circular dependency!
// ...
}
Cycle path: api/orders.ts → services/pricing.ts → repositories/inventory.ts → api/orders.ts
After (Extract Interface):
// src/models/OrderData.ts (new shared module)
export interface OrderData {
id: string;
items: Item[];
price: number;
}
// src/api/orders.ts
import { calculatePrice } from '../services/pricing';
import { OrderData } from '../models/OrderData';
export function createOrder(data): OrderData {
const price = calculatePrice(data.items);
return db.orders.create({ ...data, price });
}
// src/services/pricing.ts
import { getInventory } from '../repositories/inventory';
export function calculatePrice(items) {
const inventory = getInventory(items);
return items.reduce((sum, item) => sum + inventory[item.id].price, 0);
}
// src/repositories/inventory.ts
import { OrderData } from '../models/OrderData';
// No longer imports from api/orders
export function getInventory(items) {
// Uses OrderData interface instead of calling API
const orders: OrderData[] = db.orders.getAll();
// ...
}
Key change: Break the cycle by extracting the shared data structure (OrderData) into a separate module that all three layers can depend on, without creating a cycle.
Before finishing refactoring:
docs/cortexloop/architecture-analysis.md.cortexloop/architecture-cache.jsonnpx claudepluginhub whitequeen306/code-cortex-loop --plugin cortexloopAnalyzes codebases for modularity imbalances using Balanced Coupling model to review coupling issues, architecture quality, and distributed monolith risks.
Discovers architectural friction and proposes structural refactors: deepens shallow modules, extracts packages, breaks up god files, reduces duplication, and improves testability.
Scans codebase for architectural friction: shallow modules, tight coupling, untestable seams. Produces an HTML report with before/after diagrams and refactoring candidates.