Synaptic Pruning
"A brain that never prunes becomes a brain that can't think. A codebase that never prunes becomes a codebase that can't change."
What It Does
Your linter finds unused imports. Your compiler finds unreachable code. Synaptic Pruning finds vestigial organs — code that is technically reachable, technically used, technically valid — but serves no living purpose.
In neuroscience, synaptic pruning eliminates neural connections the brain no longer needs. It's not destruction — it's maturation. A child's brain has more synapses than an adult's. The adult brain is more capable because it has fewer.
Your codebase needs the same process.
The Seven Vestigial Classes
1. Zombie Features
Features that are fully implemented, fully compiled, fully deployed — and fully unused. No user path reaches them. No button triggers them. They exist because nobody was confident enough to delete them.
CODEBLOCK0
2. Fossil Configurations
Config keys, feature flags, and environment settings that are read by code but never influence behavior. The
if branch they gate is always true (or always false). They're the appendix of your architecture.
CODEBLOCK1
Detection: Evaluate every config-gated branch. If the gate has been in the same state across all environments for > N days, the gate is a fossil.
3. Orphaned Tests
Tests that pass, appear in coverage reports, and validate... nothing that matters. They test functions that were refactored away, mock interfaces that no longer exist, or assert behavior that was intentionally changed (and the test was updated to match the new behavior, making it a tautology).
CODEBLOCK2
Detection: Identify tests where every assertion is trivially true, where mocks replace 100% of real behavior, or where the tested function's actual callsites have all been removed.
4. Compatibility Shims
Adapters, wrappers, and translation layers that were added for a migration that completed. The old system is gone. The shim remains, adding a layer of indirection that obscures the actual architecture.
CODEBLOCK3
Detection: Find wrapper/adapter functions where input === output, translation layers where source and target are the same format, and abstraction layers with exactly one implementation.
5. Defensive Fossils
Error handling, validation, and guard clauses that protect against conditions that the current architecture makes impossible. They were necessary under a previous design. Now they're scar tissue.
CODEBLOCK4
Detection: Analyze guard clauses against current control flow. If a predecessor guarantees the condition can never be true, the guard is a fossil.
6. Documentation Ghosts
README sections, API docs, and inline comments that describe systems, processes, or architectures that no longer exist. They don't cause bugs — they cause
wrong mental models, which is worse.
CODEBLOCK5
Detection: Cross-reference documentation commands, paths, and process descriptions against actual project tooling, CI/CD configs, and infrastructure definitions.
7. Evolutionary Dead Ends
Entire modules or subsystems that represent an architectural direction the team tried and abandoned — but the code was never fully removed. Partial implementations, experimental branches merged to main, or V2 rewrites that were started but never finished.
CODEBLOCK6
Detection: Find directories/modules with high internal cohesion but zero external references. Flag modules where >50% of exports are unused outside the module.
The Pruning Process
CODEBLOCK7
Vitality Scoring
| Score | State | Action |
|---|
| 100 | Fully alive | No action |
| 75-99 |
Alive but calcifying | Monitor for drift |
|
50-74 | Dormant | Review for removal |
|
25-49 | Effectively dead | Schedule removal |
|
1-24 | Dead weight | Remove immediately |
|
0 | Never lived | Delete with prejudice |
Output Format
CODEBLOCK8
When to Invoke
- - After a major version release (prune the migration artifacts)
- Before onboarding new team members (reduce noise)
- Quarterly codebase health reviews
- After any "why does this exist?" question in code review
Why It Matters
Dead code doesn't just waste disk space. It wastes attention. Every vestigial function a developer reads, every fossil config they try to understand, every zombie feature they accidentally modify — that's cognitive load stolen from productive work.
The leanest codebases aren't the ones that added the least. They're the ones that pruned the most.
Zero external dependencies. Zero API calls. Pure evolutionary analysis.
突触修剪
从不修剪的大脑会变成无法思考的大脑。从不修剪的代码库会变成无法改变的代码库。
功能概述
你的代码检查器能发现未使用的导入。你的编译器能发现不可达的代码。而突触修剪能发现退化器官——那些技术上可达、技术上被使用、技术上有效,但已无实际用途的代码。
在神经科学中,突触修剪会消除大脑不再需要的神经连接。这不是破坏——而是成熟。儿童大脑的突触比成人更多。成人大脑能力更强,恰恰因为突触更少。
你的代码库需要同样的过程。
七类退化代码
1. 僵尸功能
那些已完全实现、完全编译、完全部署——却完全无人使用的功能。没有用户路径能到达它们。没有按钮能触发它们。它们存在,只是因为没人有足够信心删除它们。
检测:追踪每个UI元素和API端点到用户可达路径。
标记在最近N个部署周期内零调用的功能。
2. 化石配置
那些被代码读取但从不影响行为的配置键、功能开关和环境设置。它们控制的if分支总是为真(或总是为假)。它们是架构中的阑尾。
yaml
这个标志在所有环境中已为true两年
feature_flags:
enable
newcheckout: true # 新结账已是唯一结账方式
use
v2api: true # v1已在18个月前退役
experimental_search: true # 去年三月已向100%用户发布
检测: 评估每个配置控制的分支。如果该开关在所有环境中保持相同状态超过N天,则该开关就是化石。
3. 孤儿测试
那些通过测试、出现在覆盖率报告中、但验证了...毫无意义之事的测试。它们测试已被重构掉的函数,模拟已不存在的接口,或断言已被有意更改的行为(并且测试已更新以匹配新行为,使其成为同义反复)。
python
def testcalculatediscount():
# 此测试在折扣被移除时已更新。
# 现在它测试函数始终返回0。
# 它永远不会失败。它验证不了任何东西。
assert calculatediscount(anyinput) == 0
检测: 识别每个断言都平凡为真的测试,模拟完全替代真实行为的测试,或被测函数的实际调用点已被全部移除的测试。
4. 兼容性垫片
为已完成迁移而添加的适配器、包装器和转换层。旧系统已不复存在。垫片却保留下来,增加了掩盖实际架构的间接层。
javascript
// 在Angular→React迁移期间添加(2023年)
// Angular已于2024年完全移除
// 此包装器仍毫无理由地包装每个React组件
export function withAngularCompat(Component) {
return Component; // 字面意义上原样返回其输入
}
检测: 寻找输入===输出的包装器/适配器函数,源和目标格式相同的转换层,以及只有一个实现的抽象层。
5. 防御性化石
针对当前架构已使不可能的条件而设的错误处理、验证和保护子句。它们在之前的设计中是必要的。现在它们只是伤疤组织。
go
// 当getUser()可能返回nil时,此nil检查是必要的
// 在认证重写后,getUser()始终返回有效用户或触发panic
// 此分支不可达但看起来很重要
if user == nil {
return ErrUserNotFound // 这一行在生产环境中从未执行过
}
检测: 根据当前控制流分析保护子句。如果前置条件保证该条件永远不可能为真,则该保护子句就是化石。
6. 文档幽灵
描述已不存在的系统、流程或架构的README章节、API文档和内联注释。它们不会导致错误——它们会导致
错误的心智模型,这更糟糕。
markdown
部署流程
- 1. SSH到staging服务器 ← 我们现在使用Kubernetes
- 运行部署脚本 ← 已被GitHub Actions替代
- 验证健康检查端点 ← 端点已重命名
- 更新负载均衡器配置 ← 由Istio自动处理
检测: 将文档中的命令、路径和流程描述与实际项目工具、CI/CD配置和基础设施定义进行交叉引用。
7. 进化死胡同
代表团队尝试过但放弃的架构方向的整个模块或子系统——但代码从未被完全移除。部分实现、合并到主分支的实验性分支、或已开始但未完成的V2重写。
src/
├── search/ ← 当前搜索(Elasticsearch)
├── search-v2/ ← 开始迁移到Meilisearch。已停止。
│ ├── index.ts ← 实现了40%
│ ├── client.ts ← 可用但未使用
│ └── README.md ← TODO:完成迁移
检测: 寻找内部高内聚但外部零引用的目录/模块。标记超过50%导出在模块外未使用的模块。
修剪流程
阶段1:普查
├── 编录每个函数、类、配置、测试和文档章节
├── 从面向用户的入口点构建完整可达性图
├── 映射每个功能开关及其历史状态
└── 时间戳:每个单元上次有意义的修改时间?
阶段2:活力检查
├── 对每个单元,确定:是活跃、休眠还是死亡?
│ ├── 活跃:可达、已执行、行为有意义
│ ├── 休眠:可达但行为恒定/平凡
│ └── 死亡:不可达、未触发或同义反复
├── 评分置信度(分类的确定性程度)
└── 标记边界情况供人工审查
阶段3:修剪计划
├── 按类别(上述1-7)分组退化代码
├── 计算移除安全性(可能破坏什么)
├── 估算认知负荷减少量(行数×复杂度×阅读频率)
├── 生成有序移除计划(最安全优先)
└── 生成前后复杂度指标
阶段4:成熟度报告
├── 总退化负担(行数、文件数、认知重量)
├── 推荐修剪顺序及安全评分
├── 估算入职时间改善
└── 代码库年龄分布(活跃vs.化石)
活力评分
活跃但钙化中 | 监控漂移 |
|
50-74 | 休眠 | 审查是否移除 |
|
25-49 | 实际上已死亡 | 安排移除 |
|
1-24 | 死重 | 立即移除 |
|
0 | 从未活过 | 直接删除 |
输出格式
╔══════════════════════════════════════════════════════════════╗
║ 突触修剪报告 ║
║ 代码库成熟度:67%(成长中) ║
╠══════════════════════════════════════════════════════════════╣
║ ║
║ 退化负担:38个文件中4,217行 ║
║ 认知重量:约占代码库总复杂度的12% ║
║ 预计入职时间减少:1.5天 ║
║ ║
║ 按类别: ║
║ ├── 僵尸功能 ......... 2个功能,890行 ║
║ ├── 化石配置 ......... 14个标志,3个始终为真 ║
║ ├── 孤儿测试 ......... 7个测试,340行 ║
║ ├── 兼容性垫片 ....... 4个包装器,恒等函数 ║
║ ├── 防御性化石 ....... 23个不可达保护子句 ║
║ ├── 文档幽灵 ......... 3个章节,2个README ║
║ └── 进化死胡同 ....... 1个模块(search-v2/) ║
║ ║
║ 可立即安全修剪:2,841行(零风险) ║
║ 需审查后修剪:1,376行(低风险) ║
╚══════════════════════════════════════════════════════════════╝
何时调用
- - 主要版本发布后(修剪迁移遗留物)
- 新团队成员入职前(减少噪音)
- 季度代码库健康审查
- 代码审查中出现任何为什么这个还存在?的问题后
为何重要
死代码不仅浪费磁盘空间。它浪费注意力。开发者阅读的每个退化函数,试图理解的每个化石配置,意外修改的每个僵尸功能——这些都是