Fix-Regression 防御 — 修 A 不要引入 B,修 B 不要引入 C¶
核心:修 bug 时没扫所有触发路径 / 假定单一原因 / 没多场景测试 → 新代码引入新 bug = fix-regression 连环。
本项目 v0.10.117 → v0.10.122 出现 2 次连环 fix-regression,必须沉淀成规则强制防御。
反模式(红信号)¶
反模式 1:单点假定修复(最常见)¶
// ❌ 修 ISSUE-0083 时只想到一种原因
if (row.scrape_status === 2) {
return <Tooltip title="日志已被环形 buffer 淘汰">✓</Tooltip>;
// ↑ 假定单一原因 — 实际还有 batchDedupeByUrl / sourceRow 复用 2 种
}
修了表象,没扫所有触发路径 → tooltip 撒谎 → ISSUE-0085 诞生。
反模式 2:副作用未感知¶
// v0.10.117 修嵌套滚动
<Box sx={{ overflow: 'hidden' }}> // 改这个为 hidden
<Box sx={{ height: tableH }}> // 但这个还是固定 height
<DataGrid /> // 当 tableH > 实际可用高度时,分页器被裁
</Box>
</Box>
只看修复点没看父子约束 → 引入 ISSUE-0081 分页器被裁。
反模式 3:阈值往严调(修过严)¶
// ISSUE-0072: cleanupOrphan 误杀新标签页
const SIZE_TOLERANCE = 80; // v0.10.101 设的,太宽
// v0.10.103 修:±80 → ±20
// 但用户新标签页尺寸恰巧 ±20 内的也被误杀 — 又一个 fix-regression
修阈值时没列"宽 vs 严的副作用矩阵"。
反模式 4:连环 fix-regression(最严重)¶
v0.10.117 修 ISSUE-0078 嵌套滚动 → 引入 ISSUE-0081 分页器被裁
v0.10.120 修 ISSUE-0081 + 加兜底 → 引入 ISSUE-0085 tooltip 撒谎
v0.10.122 修 ISSUE-0085 ← 还在收拾
同一文件连续 N 次 commit 都在修上次引入的问题 → 强烈信号:当时根因没分析透。
正模式(绿信号)¶
正模式 1:修前 impact-trace(必做)¶
修任何代码前先 grep,列出所有相关写入/读取点:
# 修 HTTP 列空显示前,先 trace 谁能让 scrape_status = 2
$ grep -rn "scrape_status: 2" src/utils/
src/utils/scraper-executor.ts:115 # 路径 A:社媒短路
src/utils/scraper-executor.ts:157 # 路径 B:查重复用 sourceRow(不写 page-log)
src/utils/scraper-executor.ts:174 # 路径 B 续
src/utils/scraper-executor.ts:291 # 路径 C:tab 抓成功
src/utils/scraper-executor.ts:431 # 路径 D:pipeline success
src/utils/scraper-executor.ts:453 # 路径 E:pipeline skip
src/utils/engine-manager.ts:171 # 路径 F:blacklist 跳过
src/utils/engine-manager.ts:194 # 路径 G:batchDedupeByUrl(不写 page-log)
8 个路径里有 2 个不写 page-log — 这是修复必须考虑的所有场景。
正模式 2:列举触发场景(写进 commit / ISSUE)¶
修复 commit message / ISSUE 文档里写明:
## 触发场景列举(修前调查)
1. 5000 条 buffer 淘汰
2. batchDedupeByUrl 复用(v0.10.114 路径不写 page-log)
3. sourceRow 复用(scraper-executor:152-178 不写 page-log)
## 修复策略
对每个场景都成立的兜底:用启发式区分(hasContact 判断)+ 措辞"可能是 X 或 Y"
正模式 3:兜底文案诚实化¶
兜底分支显示的文案绝不假定单一原因:
// ❌ 反例
<Tooltip title="日志被淘汰">✓</Tooltip>
// ✅ 正例 — 用启发式 + 措辞"可能是 / 或"
const hint = hasContact
? '可能是同 URL 其他商家先采过,通过批量复用获得'
: '可能是:批量复用、缓存命中、或日志已淘汰';
<Tooltip title={hint}>✓</Tooltip>
正模式 4:副作用矩阵(layout / 阈值 调整时)¶
改 overflow:'auto' → 'hidden' 的副作用矩阵:
| 子元素 height < 父 | 子元素 height > 父
|-------------------|-------------------
overflow:auto | OK | 出滚动条(嵌套)
overflow:hidden| OK | 被裁掉(看不到分页器) ← 新引入的问题
→ 修复前必走完矩阵 4 格
fix-regression 检查清单¶
修任何 bug 前必问:
[ ] 这个 bug 的所有触发路径都列了吗?(grep 字段/变量/状态写入点)
[ ] 我的修复方案对每个路径都成立吗?
[ ] 兜底分支文案/逻辑假定了哪些前提?
[ ] 改 layout/阈值时画了副作用矩阵吗?
[ ] 同一文件上次 commit 是修啥?是不是同一类问题?
同文件改动追踪(连环 fix-regression 预警)¶
git log 看同一文件最近 3 commit:
$ git log --oneline src/sections/data/data-view.tsx | head -5
7d7b9b4 v0.10.122 HTTP 列 tooltip 诚实化
0f15566 v0.10.120 UI 3 处修复
41eb0c1 v0.10.117 UI bug 修复
8209b50 v0.10.115 补沉淀
如果连续 3 次都是 fix-regression → 强烈信号停下来重新设计,不要继续打补丁。
本项目历史 fix-regression 案例¶
| 起始 ISSUE | 引入的 regression | 第几次 fix |
|---|---|---|
| ISSUE-0008(v0.10.2 jsstore where:taskId) | ISSUE-0071(v0.10.103 重蹈) | 2 次 |
| ISSUE-0070(v0.10.101 cleanupOrphan) | ISSUE-0072(v0.10.103 误杀新标签) | 2 次 |
| ISSUE-0078(v0.10.117 嵌套滚动) | ISSUE-0081(v0.10.120 分页器被裁) | 2 次 |
| ISSUE-0083(v0.10.120 HTTP 列空) | ISSUE-0085(v0.10.122 tooltip 撒谎) | 2 次 |
| ISSUE-0081 + 0083 + 0085 是同一连环 | 3 次连续 | ⚠️ |
工程实践¶
grep 路径列表(按场景)¶
| 修复主题 | 必 grep |
|---|---|
| 字段写入 / 状态变化 | grep -rn "<field>: <value>" src/ |
| UI 列文案 | grep -rn "<field>" src/sections/ + grep -rn "renderCell" 同文件 |
| layout overflow | grep -rn "overflow:" src/sections/ |
| 阈值调整 | 找上次调阈值的 ISSUE 看场景 |
| 状态机降级 | grep -rn "setState\|outcome ===" src/utils/<state-machine> |
Commit message 模板(修 bug 时)¶
v0.X.Y 修 ISSUE-NNNN — <简短描述>
## 触发场景列举(修前 impact-trace)
1. <场景 A>:xxx 路径
2. <场景 B>:yyy 路径
## 修复对每个场景的处理
- 场景 A: <怎么处理>
- 场景 B: <怎么处理>
## 副作用矩阵 (如改 layout/阈值)
| 配置 | 场景 1 | 场景 2 |
|---|---|---|
| ... | ... | ... |
## 沉淀
- ISSUE-NNNN 文件
- 教训: <下次怎么不犯>
自动化(待做)¶
可加的 pre-commit / commit-msg 检查:
- 连环检测:同一文件最近 3 次 commit 都含 "fix" / "regression" → warn
- scrape_status 写入审计:改 page-log/HTTP 列时 grep scrape_status: 2 必跑
- 待 v0.10.123+ 加 scan 脚本
教训¶
- fix 不是修了就完 — 上线后场景暴露才能算修完
- 「假定单一原因」是 fix-regression 第一根因 — 95% 的连环 fix-regression 都源自此
- 同文件连续 commit 是黄信号 — 第 3 次该 review 而非继续打补丁
- 诚实文案 > 简洁文案 — 兜底分支"可能是 / 或"比"日志被淘汰"安全 10×
- 本项目过去 v0.10.117-122 6 次发版有 3 次是 fix-regression — 不能再来