破坏性变更处理
识别、分类并吸收上游发布的破坏性变更,无需救火。
为什么
破坏性变更只在触及你实际运行的代码时才破坏;多数团队过度反应,重写 changelog 提到的所有东西。有纪律的做法是把每条破坏性变更映射到代码库中的爆炸半径 — 哪些入口、哪些测试、哪些功能 — 只吸收那些。这把吓人的"200 条破坏性变更"changelog 变成可处理的"其中 7 条触及我们代码"清单。分类还让你排序:纯重命 名可 codemod 且便宜,行为变更需测试,移除的 API 需设计替代。跳过分类是升级花数周而非数天的原因。
何时使用
任何大版本升级期间,或依赖在小版本中发破坏性变更(pre-1.0 包中不幸常见)。
如何做
1. 从 changelog 把每条破坏性变更提取到表格:ID | 描述 | 类型 | 爆炸半径 2. 分类类型:rename(codemod)| removed-API(替代)| behavior-change(测试)| config-required(配置) 3. grep 代码库中每个移除/重命名的符号以测爆炸半径;零命中的行删掉 4. 按风险排序:先重命名(机械),再配置,再移除 API,最后行为变更 5. 每条先写一个失败的测试演示破坏,再修到绿(用 TDD 做升级) 6. 每条破坏性变更单独提交,使 git bisect 能把未来回归隔离到具体变更
示例
# 破坏性变更跟踪表(升级 PR 中的 markdown 表)
| ID | 类型 | 符号 | 命中 | 动作 |
|----|---------------|-------------------|------|---------------------|
| 1 | rename | ReactDOM.render | 3 | codemod -> createRoot |
| 2 | removed-API | componentWillMount | 0 | 跳过(零命中) |
| 3 | behavior | useEffect 跑两次 | 12 | 加 StrictMode 测试 |
| 4 | config | concurrent mode | 1 | 在 root 设置 |
# Codemod 重命名
npx @codemod/cli --plugin react-18 src/
# TDD 一条行为变更:StrictMode 双调用 effect
it("effect 在 StrictMode 下幂等", () => {
// ... 修复前失败,修复后通过
});常见问题
- ⚠把所有破坏性变更同等对待 — 重写 changelog 提到但你实际不调用的代码,浪费数天。
- ⚠无爆炸半径分析 — 在生产运行时才发现移除的 API,因为从没 grep。
- ⚠一个 mega-commit 修所有破坏性变更 — git bisect 无用,回归无法隔离。
最佳实践
- ✓修前先 grep — 零命中的破坏性变更可免费跳过,多数 changelog 80% 可跳。
- ✓每条 破坏性变更单独提交,使 bisect 能把未来回归钉到确切的上游变更。
- ✓每条行为变更先写失败测试 — 测试记录破坏并证明修复。