3233 字
约 10 分钟
15
S16-A07 · 缺陷预防:从复盘中提取可执行规则
2024-12-31
2026-11-09

同一类空指针三个季度里被修了三次,三份复盘都写得很长,三份都写了"加强测试"。失效点不在篇幅,在产出物:复盘产出的是叙述,不是规则。叙述只能在会议室里让人点头,规则才能在别人下一次提交代码时说话。

要回答的不是"这次为什么错",而是"要改什么,才能让这个形状的错不可能再出现一次"。判据只有一条:这条结论能不能写成检查清单的一行,或者自动化里的一个断言。


1. 两种产出物的差别

维度 叙述型 规则型
产出物 时间线 + 根因描述 + "加强 XX" 可判定的检查项 + 自动化断言
载体 人的记忆、wiki 深处 仓库文件、CI 配置、IDE 设置
执行者 当时开会的人 后来的人、机器
失效方式 人一换就消失 过期不清理变成噪声
验收 文档写完 同类缺陷复发率降下来

"加强测试"不是措施,是愿望:谁加强、加到哪个类、什么输入下必须失败,一个字都没说。检验办法是能不能写出反例——说不出"下次出现这个形状的代码时,哪一步会拦住它",结论就还停在叙述层。

2. 四个维度与对应的预防手段

分类不是为了归档,是为了让手段可以直接推出来。一个缺陷至少要在三个维度上有坐标,否则选出来的预防手段是猜的。

维度 取值 推得的预防手段 代价
发现阶段 编码 / 单测 / 集成 / 预发 / 生产 越靠后,手段越贵,且只能补网 生产发现一次的成本远高于编码期
逃逸路径 没写用例 / 用例没跑 / 跑了没断言 / 没评审到 补哪一层,取决于路径而不是阶段 补错层等于白做
根因类型 需求理解 / 设计缺口 / 编码疏忽 / 环境差异 需求类只能改需求侧动作 编码侧手段对需求类缺陷无效
修复成本 分钟 / 小时 / 天 / 事故 决定要不要固化成规则 只有高成本或高频才值得

这张表的用法是先定根因类型,再看阶段:根因是"需求理解偏差"的缺陷,无论发生在预发还是生产,加强代码评审都不会有效——要改的是需求确认的产出物(验收标准写没写、反例问没问)。

3. 抽象阶梯:个案 → 模式 → 规则 → 门禁

层 形态 判据 常见错误
个案 "3 月 12 日订单接口空指针" 有没有具体输入 停在这一层,视为已复盘
模式 "外部回调的字段可能为空" 能否举出第二个同类样例 只有一个样例就宣布是模式
规则 "回调入口的参数必须显式校验" 能否被机器判定 写成"注意判空"这种不可判定句
门禁 CI 里跑、IDE 里提示、运行时兜底 违反时是否真的失败 只提示不阻断,规则变建议

上跳的关键动作是找第二个样例:只出现一次的大概率是个案,为它建规则是过度反应;出现两次以上、分布在不同人手上的,才是模式。

# rules/DEF-021-callback-null.yaml
id: DEF-021
from_defect: INC-2213
pattern: 外部回调 / 消息消费入口的可选字段被直接解引用
rule: 入口方法首个参数必须经 validate(...) 或显式 null 分支
enforced_at: [ci, ide]
exceptions:
  - 内部服务间调用且契约已标注 required
owner: order-team
review_by: 2026-06-30   # 到期必须复核,否则自动失效

4. 逃逸缺陷必查三问

逃逸到生产的缺陷,价值高于被拦住的缺陷——它暴露的是流程的孔。三问必须都有答案,缺一个就等于没查:

问题 要的答案形态 不合格的答案
为什么测试没抓到? 缺用例 / 用例没在这条路径上 / 断言太弱 "用例覆盖不够"
为什么评审没看到? 改动太大 / 缺少上下文 / 检查项里没这条 "评审不够仔细"
为什么上线没拦住? 灰度没覆盖该流量 / 缺少对账 / 告警阈值太宽 "下次注意"

第二问最常被跳过,因为它指向组织问题。但评审漏看的真实原因通常很具体:单次 diff 超过几百行,或改动散在十几个文件——该改的是分批规则。

5. 规则落到门禁的三层

层 手段 反馈延迟 适用规则 失效风险
IDE 检查插件、模板、snippet、保存时格式化 秒级 命名、结构、常见空值、禁用的 API 可被本地关闭
CI 静态检查、架构测试、必跑用例、依赖审查 分钟级 分层依赖、越界调用、契约兼容 可以被 --no-verify 绕过
运行时 断言、校验、降级、对账任务 小时级 数据不变量、外部输入、跨系统一致性 发现问题但已影响用户

三层不是备份关系,是成本递增的漏斗:能在 IDE 拦的不要留到 CI,能在 CI 拦的不要留到运行时。反过来的错误更常见——需要上下文判断的规则塞进 IDE 提示,全员点"忽略"后规则就废了。

# PR 检查清单片段:只保留"人看才有效"的项
[ ] 新增外部入口是否走统一校验层(规则 DEF-021)
[ ] 数据变更是否带对账/回滚脚本
// 架构规则写成测试,而不是写成文档:违反即构建失败
@ArchTest
static final ArchRule callbacks_must_validate =
    methods().that().haveNameMatching("onCallback|onMessage")
        .and().areDeclaredInClassesThat().resideInAPackage("..adapter..")
        .should().beAnnotatedWith(Validated.class);

6. 缺陷密度的偏差:密度低可能是测得少

指标 常见口径 偏差来源 决策含义
缺陷密度 缺陷数 / KLOC 分母是存量代码,新增代码的问题被摊薄 重构期会"变好",纯属算术
逃逸率 生产缺陷 / 总缺陷 测得少则分子分母同时缩 分母小的时候不可比
复发率 命中既有规则的新缺陷 / 规则覆盖的缺陷 分类粒度决定一切 唯一适合衡量预防的指标

密度的方向性错误很典型:砍掉一半单元测试后密度跟着下降,被解释为"质量提升"。密度低有三种解释——代码真干净、测得少、缺陷记到了别处。它只能做同口径环比。

真实分布上,多数组织的缺陷根因集中在需求理解与设计缺口,而不是编码疏忽:把预算全押在静态检查上,等于给非主要矛盾加杠杆。

7. 规则的生命周期与规则库膨胀

阶段 触发条件 动作 不做的后果
新增 同类缺陷第 2 次出现 写成规则 + 指定执行层 第三次还会来
加固 规则被绕过(有例外但没登记) 补例外清单或收紧判定 规则名存实亡
失效 承载它的代码/框架已下线 标记 deprecated,一个版本后删除 噪声掩盖真信号
清理 连续两个季度零命中 删除或降级为文档说明 规则库变成没人读的清单

规则库膨胀是必然的,需要配额:规则总数与误报率同时设上限。误报率超阈值时正确的动作是删规则,不是加解释——加解释等于把成本转给每一个看到告警的人。

8. 同人反复 vs 系统性复发

现象 判据 对策 错误对策
同一人反复犯 集中在 1–2 人,其他人零命中 一对一复盘具体判断过程、结对、缩小其负责面 全员培训
不同人系统性犯 分布均匀,新人老人都有 改工具、改模板、改门禁 逐个谈话
老手也犯 集中在某类接口/某段时间 查上下文缺失、查并行任务切换 归因为"责任心"

区分办法很简单:把缺陷按人聚合一次。集中度高是个体问题,分布平坦是系统问题。用错对策的代价是双向的——对系统问题做个体问责,会让人开始隐藏缺陷。

9. 复盘会的组织:只谈事实,产出物明确

时长 45 分钟,硬性时间盒
0–5   :只读事实(时间线、日志、影响面),不允许评价
5–15  :逃逸路径三问,必须有具体答案
15–30 :根因定位,停在"可改动的环节",不做人品归因
30–40 :产出规则候选,每条带执行层与判定方式
40–45 :认领与到期日;无人认领的规则直接丢弃

会前不发材料、会上现读,是最大的低效来源:参与者第一次看到时间线,四十分钟全花在理解事实上。

10. 度量:只看复发率

-- 复发率:新缺陷中落在既有规则覆盖范围内的比例
SELECT date_trunc('month', d.created_at) AS month,
       count(*) FILTER (WHERE r.id IS NOT NULL) * 1.0 / count(*) AS recurrence_rate
FROM defects d LEFT JOIN defect_rules r ON d.rule_id = r.id
WHERE d.source = 'production'
GROUP BY 1 ORDER BY 1;

总缺陷数不是预防指标——它同时受需求数量、发布频率、测试强度影响。复发率只在预防动作改变时动。

11. 排错:预防体系自身的失效

现象 原因 处理
规则写了但没人违反过,缺陷照旧 规则判定的不是真实触发路径 拿最近三个缺陷回放,看规则是否命中
CI 全绿但线上仍出同类问题 执行层选错(该运行时兜底却只在 CI) 按"数据在运行时才能判定"重新分层
团队开始绕过检查 误报率高,或反馈太慢 删低信噪比规则,别加免责说明
老规则频繁误报 承载代码已演进,规则没跟 触发 review_by 到期清理

12. 边界:什么不值得建规则

场景 做法 理由
一次性脚本、导入导出工具 不建规则,只留注释 生命周期短于规则维护成本
外部依赖故障(第三方超时、证书过期) 建监控与降级,不建编码规则 规则管不到别人的行为
偶发环境差异(只在某台机器复现) 固化环境,不写检查项 修环境比修规则便宜
单人项目、发布频率极低 用清单代替门禁 门禁的固定成本摊不开
规则只能靠人判断(命名好坏、抽象是否合理) 留在评审,不进 CI 不可判定的规则会训练出点"忽略"的习惯

13. 自测

  1. 一条复盘结论要满足什么条件,才算进了"预防"层?
  2. 同类缺陷出现第几次才值得建规则?为什么是这个次数?
  3. 逃逸缺陷的三个必查问题里,哪一个最常被跳过,为什么?
  4. 缺陷密度下降可能对应哪三种解释?
  5. 为什么"同一个人反复犯"与"不同人系统性犯"的对策完全不同?
  6. 哪类规则不该放进 CI,放进去的代价是什么?
S16-A07 · 缺陷预防:从复盘中提取可执行规则
https://www.clxhxhhr.top/posts/6184/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。