代码评审别只看Bug:12个可维护性检查点
本期《工程夜航》邀请到资深后端工程师、开源项目维护者林澈,围绕“代码评审别只看 Bug”聊一个更长期的话题:可维护性。很多团队把评审当找错现场,但真正决定系统能否持续演进的,是命名、边界、契约、测试意图和反馈方式。以下为对谈整理。
问:为什么代码评审容易变成“Bug 搜索会”? 答:因为 Bug 有即时反馈,可维护性的成本却延迟发生。评审者看到空指针、边界遗漏、并发竞争,会很有成就感;但一个命名混乱、职责粘连、契约模糊的改动,可能三个月后才让团队付出代价。只盯 Bug,评审就会退化成补丁验收。更成熟的视角是:这次改动是否让下一个人更容易理解、修改、验证和回滚。Bug 当然要抓,但可维护性是系统长期正确性的基础设施。
问:可维护性听起来抽象,评审时怎么落地? 答:把它拆成可提问的检查点。检查点不是打分表,而是让作者解释取舍。下面 12 个点,适合在评审里按风险选用,不必每次全量执行。
问:第 1 个检查点为什么是命名? 答:命名是阅读入口。看变量、函数、类是否表达业务意图、单位、边界和副作用。比如 processData 不如 settleOrderAfterPayment 清楚;flag 不如 isEligibleForRefund 明确。红灯信号是万能名、过度缩写、名实不符。评审时可以问:“不看实现,能否从名字猜到输入、输出和副作用?”命名不是审美问题,而是降低认知成本。
问:第 2 个检查点看职责边界,具体看什么? 答:看一个函数或模块是否只有一个变化理由。常见问题是控制器里塞业务规则,业务层里拼 SQL,工具函数偷偷发消息。评审时问:这段代码属于哪一层?改动时会不会牵连无关模块?如果作者需要解释“这里顺便处理一下”,通常就是边界松动。建议把副作用显式化,把编排与计算分开,让依赖方向稳定。
问:第 3 个检查点为什么关注控制流和复杂度? 答:复杂度不是行数,而是理解分支所需的心智负担。深层嵌套、布尔表达式堆叠、提前返回与异常混用,都会让后来者难以推演。评审时看:主路径是否一眼可见?异常路径是否被吞掉?能否用卫语句、策略表或小函数降低分支密度?不要为了“看起来短”过度合并;可读的控制流比聪明的一行式更可维护。
问:第 4 个检查点如何处理重复与抽象时机? 答:重复要区分“知识重复”和“形似重复”。两段代码长得像,但变更原因不同,过早抽象会制造错误耦合。评审时问:它们是否因同一个业务规则一起变化?抽象后是否减少了理解成本?如果只是为了少写几行,先保留重复更安全。真正该抽的是稳定概念,并用命名把规则说清楚。
问:第 5 个检查点看错误处理,重点是什么? 答:重点不是“有没有 try-catch”,而是错误是否被分类、传播和解释。评审时看:失败时调用方能否做出决策?是否区分可重试与不可重试?是否保留上下文而不是只打印“操作失败”?吞异常、过度兜底、用日志代替返回错误,都是维护性债务。错误处理应当和接口契约一起设计,让失败路径也属于产品行为。
问:第 6 个检查点为什么强调接口契约与兼容性? 答:因为接口是团队之间的承诺。评审时看参数含义、空值语义、错误码、幂等性、版本策略和向后兼容。新增字段是否可选?删除字段是否影响旧客户端?默认值会不会改变行为?好的评审会要求把契约写进类型、文档或测试,而不是藏在实现细节里。契约清晰,修改才不会靠猜。
问:第 7 个检查点看数据结构与状态管理,常见问题有哪些? 答:常见问题是状态散落、多个布尔互相矛盾、可变对象被共享。评审时问:这个状态能否用更明确的类型表达?是否存在非法组合?状态变更是否集中?比如用枚举替代多个布尔,用不可变数据减少意外修改。数据结构选错,后续代码会用大量判断来弥补;状态边界清楚,复杂度会显著下降。
问:第 8 个检查点看测试,应该看覆盖率吗? 答:覆盖率只是线索,不是目标。更该看测试是否表达意图:它保护的是哪个业务规则?失败时能否指出原因?是否只测实现细节?评审时看测试命名、断言质量、边界案例和可读性。一个断言模糊、依赖大量 mock 的测试,可能比没有测试更昂贵。好的测试是活文档,能让后来者安全重构。
问:第 9 个检查点看配置与依赖,评审时问什么? 答:问默认值是否安全、配置是否可发现、依赖是否必要。新增库要评估维护状态、体积、许可证和替换成本;新增配置要说明作用域、失败模式和回滚方式。最危险的是“隐式默认值”和“环境差异”。评审时应要求把关键配置写进示例与校验,让部署行为可预测。
问:第 10 个检查点看可观测性,是否只有运维才关心? 答:不是。可观测性是代码可维护性的一部分。评审时看关键路径是否有日志、指标、追踪和调试线索;日志是否结构化、是否包含关联 ID、是否避免泄露敏感信息。问题在于“出事后能否定位”,而不是“日志多不多”。如果一段复杂逻辑没有任何可观察信号,后续维护只能靠猜。
问:第 11 个检查点看文档与决策记录,会不会太重? 答:不必写长篇文档,但要留下决策痕迹。评审时看:为什么选这个方案?有哪些取舍?迁移和回滚怎么做?复杂模块的注释应解释“为什么”,而不是复述代码。对于影响面大的改动,一段简短的决策记录比口头共识可靠。文档的目标是减少重复解释,让新成员能独立判断。
问:第 12 个检查点为什么落到评审反馈与团队学习? 答:因为评审方式本身也会塑造代码。若评论只有“这里不对”,作者只会被动修补;若评论能指出风险、给出选项、区分阻塞与非阻塞,团队才会积累判断力。评审时尽量用问题代替命令,用具体场景说明影响,并控制范围。每次评审都应留下可复用的规则,而不是一次性争论。
问:在 2026 年的交付节奏里,团队怎样避免评审变味? 答:把评审目标说清楚:先保证正确性与安全,再保证可维护性,最后才是风格偏好。小步提交、明确变更说明、自动检查承接机械问题,人只讨论取舍。评审者不必全知,但要能指出“这里未来会贵在哪里”。作者也要主动标注风险点和希望被重点看的部分。评审不是审判,而是共同降低系统熵增。
关键收获:第一,Bug 是当下风险,可维护性是未来风险,二者都要看。第二,评审要用可回答问题替代模糊感觉,例如命名是否表达意图、边界是否稳定、契约是否兼容、失败是否可诊断。第三,抽象要等待稳定变化,测试要表达业务意图,文档要记录决策而非复述代码。第四,最好的评审反馈既指出风险,也给出可执行的下一步,并沉淀为团队规则。代码评审的终点不是“这次没发现问题”,而是“下一个人还能安全地改”。