技术债务有一个特性:它不会让系统崩溃,只会让每次改动都比上一次稍微慢一点。等你意识到问题的时候,已经积累了几个月甚至几年的负担。
大多数团队对技术债的态度是「知道有,不知道在哪,不敢动」。/dao 要解决的是:让代码复杂度从「模糊的感知」变成「可量化、可定位、可系统性处理」的问题。
三层分类
代码复杂度不是一种问题,是三类问题,处理方式完全不同:
架构层(Architecture)
架构层的问题是最危险的,也是最不能自动处理的。这类问题需要人工决策:
- 不必要的抽象层:一个接口只有一个实现,从未被替换,但为了「以后扩展」加了一层
- 过度模块化:把一个简单功能拆成了 5 个只有 2-3 个函数的微模块
- 过早泛化:实现了一个通用框架,但实际上只有一个使用场景
- 过度设计的事件/配置系统:用发布-订阅模式处理只有两个固定参与方的交互
/dao 会识别这些模式,在面板上展示给你,并给出「保留/简化/重构」的建议,但不会自动执行。因为这类决策需要了解业务背景,AI 无法替代。
模块层(Module)
模块层的问题涉及模块边界的划分,也需要人工确认:
- 单模块功能膨胀:一个模块的功能数量超过了它的语义边界
- 职责不清:「utils」模块里放了认证逻辑、格式化、数据库操作
- 跨模块重复逻辑:相似的功能在三个不同模块里各自实现了一遍
- 耦合过重:模块 A 依赖了模块 B 的 15 个内部函数
同样展示面板,你选择哪些需要处理,/dao 按你的决策执行。
代码层(Code)
代码层的问题可以全自动处理,而且每一步都通过图谱验证:
- 死代码:从未被调用的函数、类、变量。删除前先确认图谱中调用者为零。
- 循环依赖:模块 A 导入模块 B,模块 B 导入模块 A。图谱直接显示循环路径,切断指定边。
- 过碎的函数:一个函数只有 3 行,只被一个地方调用,合并回调用处。
- 桶式重导出:多层
__init__.py或index.ts只是在转发导出,没有实际逻辑。
「图谱验证」是关键:/dao 不会盲目删除。删死代码前查图谱确认零调用者;切断循环依赖前确认切断后调用关系不受影响;合并函数前确认合并后扇入不变。每一步有依据,不会误操作。
工作流程
- 扫描代码库,计算 8 维度健康评分
- 识别问题,按架构/模块/代码三层分类
- 架构层和模块层:展示决策面板,等待人工确认
- 代码层:根据你的决策 + 自动识别的问题,批量修复
- 每步修复后图谱验证,确认无误
- 提交,输出健康度变化报告
真实数据:Manon 自身代码库的清理结果
Manon v1.1.2 版本,我们把 /dao 应用于自身代码库(93 文件,800+ 实体):
| 指标 | 之前 | 之后 | 变化 |
|---|---|---|---|
| 代码健康度评分 | 88/100 | 97/100 | +9 |
| 死代码实体 | 47 | 29 | -38% |
| 测试覆盖率 | 32% | 61% | +29pp(覆盖率因死代码减少而提升) |
具体操作:
- 17 个从未被调用的函数被移除(图谱确认零调用者)
- 4 个单函数模块合并(扇入不变,模块数量减少)
- 多层
__init__.py间接层简化(导入路径缩短) - 2 个循环依赖切断(图谱标出循环路径,手动确认切断点)
测试覆盖率从 32% 到 61%,原因不是新写了测试,而是死代码被删掉了——之前覆盖率低,有一部分是因为测试套件在统计那些永远不会被执行的死函数。清掉死代码后,实际覆盖率的分母变小了,覆盖率自然提升。
什么时候跑 /dao
我们的建议是建立定期节律,而不是等到「感觉很乱了」再清理:
- 每个功能完成后:新增的代码可能引入新的死函数或不必要的抽象,趁热打铁
- 每个里程碑后:大版本发布前做一次完整扫描,保证交付的代码质量有据可查
- 接手新项目时:快速了解代码库的健康状况,作为后续工作的基线
技术债的清理不是一次性的大工程,是持续的小手术。/dao 的设计就是为了让这件事从「半年一次的痛苦重构」变成「两周一次的轻松维护」。