Code Simplification(代码简化)
Overview
降复杂度但保行为精确不变。目标不是行数少,是新人看得更快、改得更稳、调得更顺。每次简化只回答一件事:比原来好懂吗。
When to Use
- 功能已跑通测试已绿,但实现比需要重时
- review 被点可读性/复杂度时
- 深嵌套、长函数、看不懂的名字、不必要的抽象时
- 还技术债、合流后清重复时
When NOT to use:
- 已经干净——不为简化而简化
- 还没看懂——先懂再动
- 性能关键且简化版实测更慢
- 模块马上整体重写——简化 throwaway 即浪费
五原则
- 行为精确不变:输入输出、副作用、错误、边界全同。吃不准即不动。
- 跟本仓约定:简化是向邻近代码看齐,不是强加外部审美。风格/命名/错误处理先看邻居。
- 清晰压倒聪明:要停顿才看懂的紧凑即失败;显式 beaten 紧凑,除非有实测证明热点。
- 防过简:内联掉有名字的概念、合并无关逻辑、为行数删必要抽象——都是把简单变复杂。
- 只动本次范围:默认只简化本次改的代码;顺手扩区即 diff 噪音 + 回归风险,另起任务。
动手前(Chesterton's Fence)
先答:这段的职责?谁调它调谁?边界与错误路?测试定何行为?当初为何这样写(性能/平台/历史)?查提交史看上下文。答不上即没资格动,先读。
机会清单
结构:深嵌套 3+(护卫从句/提函数)/ 长函数 50+(按职责拆)/ 嵌套三元(if/switch/查表)/ 布尔开关参数(选项对象或拆函数)/ 重复条件(提谓词函数)。
命名:data/result/temp/val 类泛名(改内容名)/ 缩写(全词,通用除外)/ 名不副实(get 却改状态即改名)/ 讲 what 的注释(删)/ 讲 why 的注释(留)。
冗余:同 5+ 行多处(提共享函数)/ 死代码(确认真死即删)/ 无价值包装(内联直调)/ 单策略的策略模式(直接写)/ 多余断言(删)。
抽象税:5 参 3 开关的"复用"(调用比原来难懂即拆回)/ 一行顶十行的 clever(无 benchmark 即拆)——宁要 3 份 10 行一眼懂,不要 1 份 30 行查文档。
过程
- 一次一简化,每步跑测试:绿即留/继续,红即回滚重想
- 重构与功能/修 bug 分提交、分 PR;混即两件事,拆
- 超 500 行先上自动化(codemod/脚本/AST),手改必错
- 完工回看:真好懂了吗?引新异构了吗?复杂度是没了还是搬家了?删抽象不如删概念——让分支/模式/层消失的才是简化
Quick Reference
| 场景 | 动作 |
|---|---|
| 通用函数比调用难懂 | 拆回,允许小重复 |
| clever 一行流 | 无实测即拆,注释+benchmark 才留 |
| 测试挂一边界 | 保行为,回滚修绿;改测试即洗 bug,不许 |
| 顺手想扩区 | 另起任务,不混本单 |
| 超 500 行重构 | 先自动化,不手改 |
Common Rationalizations
| Excuse | Reality |
|---|---|
| "复用总比重复好" | 难懂的复用即负债;重复 3 次清晰胜过抽象 1 次糊涂 |
| "一行流优雅高效" | 优雅需读者投票,高效需实测投票;两票皆无即拆 |
| "这边界没人碰,改测试就行" | 改测试放行即把 bug 洗成正确;边界去留是需求决策,不是简化附带 |
| "顺手清一下隔壁" | 顺手即污染回退;清另起单 |
| "行数少了就是简化" | 行数是虚荣指标;好懂才是,复杂度搬家不算 |
Red Flags — STOP
- 测试为简化让路(改测试迁就实现)
- 行为变了还叫简化
- 破本仓约定(风格/命名/错误处理异构)
- 过度抽象上线(多参多开关,调用更难)
- clever 无实测上线
- 重构与功能同提交
- 范围外顺手改动
以上任一出现 → 停手,回滚/拆单后再谈合。
Verification
- 行为精确不变(旧测试零修改全绿)
- 新人可读性提升(调用点一眼懂)
- 无新异构,与邻近一致
- 重构与功能分提交
- 无范围外改动