📦 Phantom Limb — 幽灵依赖检测
v1.0.0检测代码库中的幽灵依赖和幽灵引用——指向已不存在事物的代码,包括环境变量、配置键、API 端点、文件路径等,帮助发现导致神秘问题的隐性依赖。
详细分析 ▾
运行时依赖
版本
初始版本 — 检测代码库中的幽灵依赖和幽灵引用
安装命令
点击复制技能文档
"最危险的依赖是曾经存在过的那个。"
它做什么
Phantom Limb 检测幽灵引用——代码试图获取不再存在的东西。不是那种你的 linter 能捕获的导入错误。而是更微妙的那种:没人设置的环境变量、三个 sprint 前被重命名的配置键、已弃用但从未从客户端移除的 API 端点、只存在于原始开发者机器上的目录路径。
每个代码库都会积累幽灵。它们不会导致错误——它们导致神秘感。它们就是某个功能"除了生产环境外 everywhere 都正常"的原因。它们就是入职需要两周而不是两天的原因。
为什么存在
静态分析捕捉错误。Linter 捕捉丑陋。Phantom Limb 捕捉缺失——你的代码和现实之间的负空间。
| 传统工具发现 | Phantom Limb 发现 |
|---|---|
| 破损的导入 | 能解析但引用死代码路径的导入 |
| 语法错误 | 语义有效但指向已删除概念的引用 |
| 未使用的变量 | 引用幽灵状态的已使用变量 |
| 缺失的文件 | 存在但包含先前架构假设的文件 |
| 类型不匹配 | 类型匹配但描述已不存在事物的类型 |
幽灵的六种类型
1. 环境幽灵
引用不再被任何进程设置的环境变量、配置文件或系统状态。// This worked when we ran Redis locally
const cache = process.env.REDIS_URL || 'redis://localhost:6379';
// Redis was replaced with Memcached 8 months ago.
// Nobody removed this. The fallback silently runs. Against nothing.
检测方法: 交叉引用每个 process.env、os.environ、ENV[] 读取与实际的 .env、.env.example、CI/CD 配置和部署清单。
2. 引用幽灵
代码引用被移动、重命名或删除的函数、类或模块——但由于垫片、重导出或回退机制,引用仍然"有效"。# utils.py re-exports calculate_tax for "backwards compatibility"
# Nobody imports calculate_tax from the original location anymore
# But nobody removed the re-export either
# And the original calculate_tax was rewritten. The re-export points to the old version.
from legacy.tax import calculate_tax # pragma: no cover
检测方法: 追踪每个导入链到其终端定义。标记超过 2 跳的链。标记路径中包含 "legacy"、"compat"、"old" 或 "deprecated" 但没有弃用截止日期的任何内容。
3. 时间幽灵
代码依赖于在先前架构下为真但不再保证的时序、排序或顺序。// This worked when auth was synchronous middleware
// After the async rewrite, user might not be populated yet
app.get('/dashboard', (req, res) => {
const name = req.user.displayName; // Sometimes undefined. Sometimes not.
});
检测方法: 映射所有隐式排序假设。标记任何假设先前的中间件/钩子/生命周期事件已完成但没有显式 await/guard 的数据访问。
4. 契约幽灵
代码期望但另一方不再遵守的 API 契约、数据库模式或线格式。# The payments API v2 removed the 'discount_code' field
# Our code still sends it. The API silently ignores it.
# Nobody knows the discount feature has been broken for 3 months.
payload = {
"amount": total,
"discount_code": user.discount, # Phantom. Silently ignored.
}
检测方法: 将每个出站 payload 构建与最新的 API 架构/文档进行比较。将每个数据库查询与当前模式进行比较。标记构造但从未消费的字段。
5. 意图幽灵
描述代码不再展现的行为的注释、TODO 和文档。规范变成了鬼故事。/*
Retries up to 3 times with exponential backoff.
Falls back to cache on failure.
/
// Retry logic was removed in PR #847. Cache fallback was never implemented.
public Response fetchData() {
return client.get(url); // One shot. No retry. No fallback.
}
检测方法: 解析文档注释并将声称的行为与实际实现进行比较。标记文档字符串中提到但方法体中不存在的模式(重试、缓存、回退、队列、批处理)。
6. 身份幽灵
名称描述的内容与实际行为不符的变量、函数或模块。名称是它们原始目的的幽灵。// This was a temporary cache. Three years ago.
func getTempCache() PermanentStore {
return &PermanentStore{ttl: 0} // TTL of zero = lives forever
}
检测方法: 对标识符名称与其实际行为进行语义分析。标记名称语义和实现语义之间的矛盾(例如 temp + 无过期、async + 同步执行、safe + 无错误处理)。
工作原理
Phase 1: EXCAVATION(挖掘) ├── 扫描所有源文件以查找外部引用 ├── 构建引用图(什么指向什么) ├── 映射所有环境读取、配置查找、API 调用 └── 编目所有导入链及其终端定义Phase 2: REALITY CHECK(现实检查) ├── 交叉引用实际环境状态 ├── 将 API 契约与当前模式进行比较 ├── 追踪导入链以检测幽灵重导出 └── 将文档声明与实现进行比较
Phase 3: PHANTOM CLASSIFICATION(幽灵分类) ├── 按类型(上述 1-6)对每个幽灵进行分类 ├── 评分严重程度(静默失败 vs 响亮失败 vs 潜在) ├── 估算爆炸半径(多少代码路径受影响) └── 计算持续时间(幽灵存在了多久)
Phase 4: EXORCISM REPORT(驱魔报告) ├── 按严重程度 × 爆炸半径排序的幽灵列表 ├── 对于每个幽灵:它引用什么、实际存在什么、该怎么办 ├── 每个类别的快速修复建议 └── 依赖现实图(你的代码认为存在什么 vs 实际存在什么)
严重程度评分
| 严重程度 | 描述 | 示例 |
|---|---|---|
| Critical(严重) | 幽灵导致静默数据丢失或损坏 | API 字段被静默忽略,数据从未保存 |
| High(高) | 幽灵导致间歇性故障 | 时间幽灵,与幽灵状态的竞态条件 |
| Medium(中) | 幽灵导致混淆但无运行时错误 | 身份幽灵,误导性名称 |
| Low(低) | 幽灵是惰性的但增加认知负担 | 死重导出,孤立配置 |
| Vestigial(残留) | 幽灵无害但表明架构腐朽 | 2 年多前的 TODO 注释 |
输出格式
╔══════════════════════════════════════════════════════════════╗
║ PHANTOM LIMB SCAN ║
║ 12 phantoms detected ║
╠══════════════════════════════════════════════════════════════╣
║ ║
║ CRITICAL (2) ║
║ ├── [Contractual] POST /api/payments sends 'discount_code'║
║ │ → Field removed in API v2 (2024-11-03) ║
║ │ → 3 months of silent discount failures ║
║ │ → Fix: Remove field from payload builder ║
║ │ ║
║ ├── [Environmental] REDIS_URL referenced in 4 files ║
║ │ → No process sets this variable ║
║ │ → Fallback to localhost:6379 connects to nothing ║
║ │ → Fix: Remove Redis references, use Memcached client ║
║ │ ║
║ HIGH (3) ║
║ ├── [Temporal] req.user accessed before auth middleware ║
║ │ ... ║
╚══════════════════════════════════════════════════════════════╝
集成
在以下情况下调用:
- 开发者入职时(向他们展示幽灵在哪里)
- 主要重构后(找出重构留下了什么)
- 生产部署前(在用户之前捕获幽灵)
- 架构审查期间(映射意图与现实之间的差距)
为什么重要
每个代码库都有一个幽灵架构——它认为的系统,叠加在它实际的系统之上。这两个架构之间的差距是 bug 藏身、入职停滞和技术债务悄然累积的地方。
Phantom Limb 不找 bug。它找让 bug 必然出现的条件*。
零外部依赖。零 API 调用。纯粹的结构分析。