happy-nil-empty-error:代码Review的四条路径
Review 最常见的失败,是把“看过了”误认为“审过了”。
看过 diff,不等于审过行为。总结改动,不等于发现风险。提了几个风格建议,不等于覆盖边界。说“整体没问题”,不等于系统真的安全。
好的 Review 不是礼貌地复述改动,而是主动寻找行为错误。
我现在最常用的最低基线,是四条路径:
1 | happy |
这四个词很朴素,但能抓住大量真实问题。
一、Happy:正常世界是否成立
Happy path 检查正常输入、正常权限、正常依赖下,主流程是否成立。
它要问:
- 正常用户能不能完成操作?
- 主流程是否返回正确结果?
- 新旧调用方是否兼容?
- 用户可见行为是否符合需求?
- 成功路径是否有测试或人工验证?
Happy path 是最容易被测到的路径,也是最容易让人过早放心的路径。
一个导出功能正常生成文件,一个设置页正常保存选项,一个列表正常展示数据,这都只是说明系统在正常世界里能工作。
但真实世界不总是正常。
所以 happy path 是起点,不是终点。
二、Nil:缺失时会不会炸
Nil path 检查缺失。
缺用户、缺 token、缺配置、缺依赖、缺字段、缺缓存、缺 navigator、缺文件。很多线上问题,不是因为逻辑多复杂,而是因为你以为一定存在的东西不存在。
Nil path 要问:
- nullable 字段是否处理?
- 缺用户、缺 token、缺配置时会怎样?
- 是否存在直接解包、空指针、未判空访问?
- 缺依赖时是 fail fast,还是悄悄产生半成功状态?
- nil 和 empty 有没有混用?
举个例子。
一个 iOS 设置页在正常情况下能保存 preset。但如果 navigator 没绑定,点击设置会不会崩?如果 UserDefaults 里没有这个 key,是回到默认值,还是显示一个错误高亮?
这就是 nil path。
它经常抓到“本地能跑、线上炸”的问题。因为本地开发环境通常配置齐全、用户状态完整、fixture 数据漂亮。
三、Empty:空不是缺,也不一定是错
Empty path 检查“存在但没有内容”。
空字符串、空数组、空结果集、零条记录、空响应。
很多 bug 来自 nil 和 empty 被混在一起。
空列表不是缺列表。空字符串不是缺字段。零条记录不是查询失败。接口返回空数组,可能是一次成功的空结果,而不是异常。
Empty path 要问:
- 空列表是否显示空态,而不是报错?
- 空搜索结果是否是正常结果?
- 空输入是否需要拒绝?
- 空内容是否允许保存?
- 空响应是否会触发无限 loading 或无限重试?
举个例子。
搜索页面请求成功,返回空数组。正确行为应该是显示“没有找到结果”,并给用户一个修改关键词的入口。错误行为是显示“加载失败”,或者一直转圈。
这类问题看起来小,但非常影响用户信任。
Empty path 的关键是语义。空不一定错,要看需求怎么定义。
四、Error:世界不配合时会怎样
Error path 检查失败。
网络超时、数据库失败、权限不足、磁盘满、第三方接口 500、JSON 解析失败、任务取消、并发冲突。
Error path 要问:
- 错误是否被捕获?
- 错误语义是否正确?
- 用户是否看到合理反馈?
- 日志是否可定位?
- 指标是否可观察?
- 是否需要重试?
- 重试是否幂等?
- 失败后状态是否可恢复?
很多系统不是在成功时分出高下,而是在失败时分出高下。
比如导出任务:文件上传成功了,但数据库状态写回失败。这不是简单的失败。它是半成功。如果系统从头重试,就可能重复上传文件。如果系统直接标记 failed,用户会看到失败,但实际上文件已经生成。
这种问题,happy path 永远抓不到。
你必须专门问 error path。
五、Review 不是建议越多越好
坏 Review 经常堆满细节建议:
- 变量名可以更好。
- 这里可以抽函数。
- 这里可以换一种写法。
- 这个注释不够优雅。
这些建议不一定错,但如果它们淹没了真正的行为风险,Review 就失败了。
Review 的优先级应该是:
- 阻断性行为错误。
- 可能导致数据、权限、稳定性问题的风险。
- 测试缺口。
- 契约和发布风险。
- 可维护性建议。
风格建议应该让路给行为问题。
六、Review 要贴着任务 Mode
同一段 diff,在不同 Mode 下 Review 重点不同。
如果任务是 Expansion,要问:
- 新能力是否有完整状态流?
- 错误态是否设计?
- 发布和回滚是否考虑?
- 新接口是否有契约测试?
如果任务是 Hold,要问:
- 是否保持既有契约?
- 是否只修当前问题?
- 是否避免不必要重构?
- 是否补了回归测试?
如果任务是 Reduction,要问:
- 旧入口是否真正收口?
- 删除是否有证据支持?
- 兼容入口是否有清退说明?
- 回滚是否可行?
没有 Mode 的 Review,很容易用错尺子。
七、一个好 Finding 应该长什么样
不要写:
1 | 建议增强错误处理。 |
这句话太软,不能行动。
改成:
1 | [P1] 重试路径可能重复上传文件 |
这才是有用的 Review。
它说明了路径、触发条件、风险和需要补的验证。
八、让 AI 做 Review 时要禁止它总结
AI 做 Review 时,最容易输出这种东西:
1 | 本次修改优化了导出逻辑,增加了错误处理,并补充了测试。整体结构清晰。 |
这不是 Review,这是摘要。
你应该明确要求:
1 | 请以代码审查姿态输出,优先寻找 bug、行为回归、边界遗漏和测试缺口。 |
AI 很适合做边界扫描,但前提是你要求它扫描边界,而不是让它“看看”。
九、四路径的真正价值
happy、nil、empty、error 并不是完整的软件质量模型。
但它们有一个巨大优点:简单、稳定、容易记。
每次 Review 前扫一遍:
1 | 正常路径成立吗? |
很多问题就会浮出来。
这四条路径的价值,不在于它们高级,而在于它们能把 Review 从“我看了一遍”变成“我攻击了四个方向”。