可维护性审查
标准只有一个:下一个人(无论是人还是代理)在修改这段代码时需要打开多少个地方? 不处理正确性bug。那是code-review或pr-review的职责。
0. 模式
| 模式 | 目标 | 比较基准 | 阻塞目标 |
|---|---|---|---|
| 自检 | 提交前的本地diff | 整个仓库 | 直接修改(不报告,立即修改) |
| 变更审查 | PR diff | 整个仓库 | 仅限本次变更产生的问题 |
| 检查 | 整个模块或目录 | 整个仓库 | 全部。按影响大小排序 |
在变更审查中,原先存在的问题不应作为阻塞项提出。应将其单独记录为“既有债务”。
1. 读取项目标准
- 查找
docs/review/architecture.md。如果没有,则使用CLAUDE.md或AGENTS.md所指向的结构文档。 - 项目标准优先于本技能的通用标准。特别是从标准文档中获取以下内容:
- 模块映射和依赖方向
- 应重用的抽象层列表(HTTP客户端、结果类型、测试辅助工具等)
- 已知债务列表。此列表中的模式不应成为新代码效仿的先例。
- 如果没有标准文档,则直接根据目录结构和每个模块的
index导出绘制模块映射,并在报告第一行注明“无标准文档”。
2. 审查内容
按优先级从高到低审查。越靠前的项,未来付出的代价越大,且代理经常忽略。
2-1. 是否绕过了现有抽象层
代理只关注单个问题,可能不知道之前问题创建的模块,从而再次创建相同的东西。这是最常见且代价最高的失败。
为新增的函数、类型、错误类、常量、外部调用,在仓库中查找执行相同任务的代码。
git grep -nE "class \\w+Error extends" # 错误类
git grep -nE "https?://[a-z.]+\\.(com|io|dev)" # 外部API地址和头信息
git grep -nE "^(export )?type \\w+ = \\{" # 类似名称的类型(例如:XxxEntry)
git grep -nF "<新代码的特征性字符串·正则表达式>"
- 如果调用同一个外部系统的封装器有两个以上,调用者就需要用多个类来区分错误。同时查找此类痕迹(
a instanceof X || b instanceof Y)。 - 如果相同的列表常量(扩展名、状态值)出现在两个地方,则只有一方会被更改。
2-2. 依赖方向
- 是否违反了标准文档中的层级规则(例如,领域模块导入了UI或外部API)。
- 子模块是否导入了父模块,或者两个模块是否互相导入。
2-3. 单元
按变更原因而非代码长度划分。
- 如果一个文件中包含两个或以上互不调用的函数组,则表明存在多个变更原因(例如:表单映射 + 嵌套键访问 + 日期格式化)。
- 如果多个屏幕文件重复相同的准备流程(认证 → 加载配置 → 错误分支),则该流程应被封装为一个命名的函数。
2-4. 抽象层的质量
- 缺失的API:如果两个或以上的调用者重复相同的前置/后置处理,则说明该模块缺乏一个包含此处理的函数。例如:所有调用者在调用
doc.items.find((s) => s.kind === "x")之后都调用parseX(...),则说明缺少readX(doc)。 - 过度抽象:只有一个使用点的接口、工厂、选项、泛型。这些是当前不需要的扩展点。
- 边界的类型:失败是否以结果类型(判别联合)的形式暴露给调用者处理?调用者是否通过比较消息字符串来进行分支?
2-5. 模式
- 是否遵循了仓库已有的模式(结果类型、依赖注入、错误处理、模块导出方式)。
- 新模式只有在存在无法使用现有模式的理由时才被接受。如果理由未在PR或注释中说明,则指出。
2-6. 注释与命名
- 注释是否挂载到了错误的目标上?修改提交可能在函数之间插入新函数,导致JSDoc遗留在其他函数上,这是常见的事故。
- 名称是否与其功能匹配?如果仅凭名称会导致期望不同的行为,则指出。
3. 判定
阻塞(不合并)
- 已存在相同职责的代码,但却重新创建了新的。如果PR或注释中说明了无法使用现有代码的原因,则除外。
- 违反了标准文档中的层级规则。
- 新API导致两个以上的调用者重复相同的处理。
- 注释挂载到了不正确的目标上,误导读者。
建议(不阻止合并,作为后续任务记录)
- 文件拆分、命名改进、既有重复。
不记录的内容:个人偏好(函数式/命令式、行长度)、由linter和formatter处理的内容、以及问题范围之外的新库引入。
4. 验证
每项发现都必须通过代码进行确认。错误的发现会浪费实现代理的时间。
- 如果是“重复”,则打开两个位置,确认它们确实做了相同的事情。即使外观相似,如果遵守的规范不同,则排除。
- 如果是“N个地方”,则使用grep进行计数。
- 如果是“本次变更产生的”,则检查
git diff <base>...HEAD中是否存在该行代码。
5. 报告
第一行:审查范围(模式、目标路径、标准文档是否存在)和计数(阻塞 N、建议 N、既有债务 N)。
每个条目:
- 标题:以结果形式写出代价是什么。(“GitHub 401错误被调用者用3个错误类检查” ○ / “错误处理改进” ×)
- 位置:所有相关位置以
文件:行号格式列出。如果是重复,则列出双方位置。 - 代价:下一次更改时需修改多少个地方。(“更改API版本头信息时需要修改3个文件”)
- 方向:用一两句话说明应合并到哪一方。
- 等级:阻塞 / 建议 / 既有债务。
如果没有发现任何问题,则写明“无”。不要为了凑数而强行指出问题。
禁止事项
- 仅根据代码行数或文件大小建议拆分。
- 提出无方向的建议(“最好能更抽象一些”)。
- 将仅外观相似的代码判断为重复。
- 将原先存在的问题作为本次PR的阻塞项提出。