1. 从“代码评审”这件事说起:为什么我们需要一个开放式的评审框架
代码评审这件事,做过几年开发的人都有体会。它表面上是一个技术流程,实际上是一个协作流程、一个知识传递流程,甚至是一个团队文化的缩影。我待过几个不同类型的团队,有那种评审极其严格、每个PR都要两三个人点头才能合并的,也有那种基本靠自觉、评审就是走个形式的。这两种极端我都经历过,说实话,都不太健康。
严格的那种,问题在于瓶颈。一个改动可能要等两天才能拿到所有评审意见,开发者的上下文早就切走了,回来再改的时候还得重新回忆当时为什么这么写。松散的那种,问题更隐蔽——代码质量在不知不觉中滑坡,等到某天线上出了事故,回头一看,那个埋雷的改动当初根本没人仔细看过。
所以当我看到“open-code-review”这个标题的时候,第一反应是:终于有人把这件事当成一个正经的工程问题来对待了。它不是某个具体工具的使用教程,而是一个更底层的问题——如何构建一个开放的、可定制的、不绑定特定平台的代码评审体系。
这篇文章我想聊的,就是围绕这个核心命题展开的完整思考和实践。从评审流程的设计、评审清单的制定、自动化工具的接入,到评审文化的建设、常见反模式的识别,再到如何度量评审效果并持续改进。适合那些正在搭建或优化团队代码评审流程的工程师、技术负责人,也适合那些觉得自己团队的评审“差点意思”但说不清差在哪里的朋友。
我会尽量把每个决策背后的逻辑讲清楚,不只是告诉你“怎么做”,更告诉你“为什么这么做”以及“不这么做会怎样”。这些都是我在实际项目中踩过坑、交过学费之后沉淀下来的东西。
2. 评审流程的骨架设计:从提交到合并的完整链路
2.1 为什么“提交即评审”是个陷阱
很多团队的做法是:开发者写完代码,提一个合并请求,然后@几个人来看。这个流程看起来没问题,但它隐含了一个假设——代码在提交的那一刻就已经是“可评审”的状态了。实际上,大部分时候并不是。
我见过太多这样的情况:一个合并请求里混了三个不相关的改动,一个是修bug,一个是重构,还有一个是顺手改了个配置。评审者打开diff一看,几百行变更散落在十几个文件里,根本不知道从哪里看起。这种情况下,评审要么流于形式(点个approve了事),要么变成一场漫长的拉锯战。
所以开放式评审框架的第一个设计原则是:评审的起点不是提交,而是提交之前的自检。开发者需要在提交之前完成一轮自我评审,确保改动是内聚的、可理解的、有明确边界的。具体来说,我通常要求团队遵循这几个规则:
- 单个合并请求的变更行数控制在400行以内(不含自动生成的代码和测试快照)。超过这个数,评审质量会断崖式下降。有研究数据支撑这个结论,但更重要的是我自己作为评审者的体感——超过400行之后,我的注意力就开始漂移了。
- 一个合并请求只做一件事。修bug就只修bug,不要顺手重构。重构就只重构,不要夹带功能变更。如果确实有关联,拆成多个请求,用依赖关系串起来。
- 提交信息里必须写清楚“为什么改”而不只是“改了什么”。diff本身会告诉你改了什么,但不会告诉你为什么。
这些规则听起来简单,执行起来需要纪律。我的经验是,一开始不要贪多,先把“一个请求只做一件事”这条落实到位,效果立竿见影。
2.2 评审者的选择逻辑:不是谁都可以评
选谁来做评审者,这件事比大多数人想象的更重要。常见的做法是“谁有空谁看”或者“按模块owner来分”,但这两种都有问题。
“谁有空谁看”的问题在于,评审质量完全不可控。一个不熟悉这块代码的人,只能看到表面问题——命名规范、格式、明显的逻辑错误。而那些真正危险的改动,比如边界条件处理、并发安全性、错误恢复路径,他根本看不出来。
“按模块owner来分”的问题在于瓶颈。如果某个模块只有一个人熟悉,那所有相关改动都要等他,他一休假整个团队的节奏就卡住了。
我比较推荐的做法是双评审者机制:每个合并请求指定两个评审者,一个是“领域评审者”,负责判断改动的正确性和完整性;另一个是“交叉评审者”,负责从不同角度审视,比如可测试性、可维护性、对下游的影响。交叉评审者不需要精通这块代码,但需要有足够的工程判断力。
这个机制的好处是,它同时解决了质量问题和知识扩散问题。交叉评审者在评审过程中会逐渐熟悉这块代码,慢慢成长为新的领域评审者。团队的知识分布会越来越健康,而不是越来越集中在少数人身上。
2.3 评审意见的分级处理
评审意见不是平等的。有些是“必须改”,有些是“建议改”,有些只是“提个想法”。如果不做区分,开发者会陷入一种困境:不知道哪些意见是阻塞性的,哪些是可以讨论的。
我在团队里推行的是三级分类:
| 级别 | 标记 | 含义 | 处理方式 |
|---|---|---|---|
| 阻塞 | [B] | 必须修改才能合并 | 开发者必须处理或明确反驳 |
| 建议 | [S] | 推荐修改但不阻塞 | 开发者可以选择接受或说明理由 |
| 讨论 | [D] | 提出一个想法供参考 | 不要求处理,可以留到后续 |
这个分类看起来简单,但它极大地减少了评审中的摩擦。开发者一眼就能看出哪些是硬性要求,哪些是软性建议。评审者也更清楚自己表达的是什么级别的意见,不会因为一个“建议”被忽略而感到不快。
注意:阻塞级意见必须给出具体的修改方向,不能只说“这里有问题”。比如“这个循环在空数组时会抛异常,建议加一个长度检查”就比“这里边界条件没处理”要好得多。
3. 评审清单的制定:把隐性知识变成显性规则
3.1 通用清单和领域清单的分层
评审清单这个东西,很多团队都有,但大部分都停留在“检查命名规范”“检查是否有测试”这种层面。不是说这些不重要,而是它们太基础了,基础到应该由自动化工具来检查,而不是浪费人的时间。
我建议把评审清单分成两层:通用层和领域层。
通用层是所有代码都应该满足的基本要求,比如:
- 改动是否有对应的测试?测试是否覆盖了正常路径和异常路径?
- 是否有明显的性能问题?比如在循环里做数据库查询、在热路径上做字符串拼接。
- 错误处理是否完整?异常是否被吞掉?错误信息是否有助于排查问题?
- 是否有安全风险?比如用户输入未做校验、敏感信息被日志打印。
领域层是针对特定模块或特定类型改动的检查项。比如:
- 涉及数据库schema变更的,是否有回滚方案?是否考虑了数据迁移的兼容性?
- 涉及API接口变更的,是否更新了文档?是否考虑了客户端的兼容性?
- 涉及并发逻辑的,是否有竞态条件的分析?锁的粒度是否合理?
领域层的清单需要团队自己积累。我的做法是,每次线上事故或者严重的评审遗漏之后,都问一句:“这个问题的检查项应该加到清单里吗?”如果是,就加进去。这样清单会越来越贴合团队的实际需要,而不是从网上抄一份通用模板。
3.2 清单的执行方式:嵌入流程而不是挂在墙上
清单最大的问题是容易被忽略。你写了一份很漂亮的清单,放在wiki里,但评审的时候没人看。这不是态度问题,是流程问题——人在忙碌的时候会依赖习惯,而不是依赖文档。
所以清单必须嵌入到流程里。具体来说,有几种做法:
第一种是模板化。在合并请求的描述模板里直接嵌入清单,评审者逐项勾选。这种方式的好处是强制性的,不勾选就提交不了。坏处是容易变成机械操作,勾选的时候不过脑子。
第二种是自动化检查。能自动化的检查项就不要放在人工清单里。比如代码格式、静态分析、测试覆盖率,这些都可以用CI流水线来卡。人工清单只保留那些机器判断不了的项。
第三种是评审引导。在评审界面上,针对不同类型的改动展示不同的检查提示。比如检测到这次改动涉及数据库操作,就自动提示“请确认是否有回滚方案”。这种方式最理想,但需要一定的工具支持。
我自己的实践是第一种和第二种结合:模板里保留少量必须人工确认的项,其余全部交给自动化。清单项控制在10条以内,超过10条就没人认真看了。
3.3 清单的迭代:定期回顾和清理
清单不是一成不变的。我见过一些团队的清单,三年没更新过,里面有些检查项早就过时了,有些已经被自动化工具覆盖了,但还在那里占着位置。
我的建议是每个季度做一次清单回顾。回顾的时候问三个问题:
- 过去一个季度,有哪些问题是在评审中发现的?这些问题对应的检查项在清单里吗?如果不在,加进去。
- 有哪些检查项在过去一个季度里从来没有被触发过?如果一直没有触发,可能是这个检查项不适用了,考虑移除。
- 有哪些检查项已经被自动化工具覆盖了?如果已经覆盖,从人工清单里移除。
这个回顾不需要很长时间,一个小时足够了。但它能保证清单始终是活的、有用的,而不是一份摆设。
4. 自动化工具在评审中的角色:做什么和不做什么
4.1 自动化能解决的问题
自动化工具在代码评审中的价值,我觉得被很多人低估了,也被很多人误解了。低估的人觉得“工具只能查查格式”,误解的人觉得“上了工具就不需要人工评审了”。
实际上,自动化工具最擅长的是消除噪音。什么是噪音?就是那些不需要人类判断力就能发现的问题——格式不一致、明显的空指针风险、未使用的变量、拼写错误、测试覆盖率下降。这些问题如果让人类评审者来发现,是极大的浪费。人的注意力是稀缺资源,应该用在机器判断不了的地方。
我在团队里配置的自动化检查包括:
- 静态分析:用lint工具检查代码风格和常见错误模式。这个应该在提交之前就由开发者的编辑器或pre-commit钩子完成,不要等到评审阶段。
- 类型检查:如果语言支持静态类型,类型检查必须通过。类型错误是最容易发现也最容易修复的问题,不应该流入评审阶段。
- 测试执行:所有测试必须通过,包括单元测试、集成测试。测试覆盖率如果下降,需要评审者确认是否合理。
- 安全扫描:依赖库的已知漏洞扫描、敏感信息泄露检查。这些用现成的工具就能做,不需要人工。
- 构建验证:确保改动能在干净环境中构建成功。这个能避免很多“在我机器上是好的”问题。
这些检查全部通过之后,才进入人工评审阶段。这样评审者打开diff的时候,看到的是一个“基本干净”的改动,可以把精力集中在逻辑正确性、设计合理性、边界条件这些真正需要人类判断的地方。
4.2 自动化的边界:哪些事机器做不了
自动化工具再强大,也有它做不了的事。明确这些边界,才能合理分配人工评审的注意力。
机器判断不了的,首先是意图。代码能告诉你“做了什么”,但很难告诉你“为什么这么做”。一个看起来奇怪的实现,可能是因为有一个不明显的业务约束,可能是因为要兼容某个历史遗留系统,可能是因为性能测试的结果要求必须这样写。这些只有人类评审者通过和作者的沟通才能理解。
其次是设计质量。机器可以检查代码是否符合某种模式,但很难判断这个设计是否合理。比如一个类承担了太多职责,机器可能检测到它的方法数量超过阈值,但“太多”的标准是什么?在这个场景下是否真的有问题?这些需要人类的判断。
第三是可维护性。代码是写给人看的,但“好读”这件事很难量化。变量命名是否达意、函数职责是否单一、注释是否解释了关键决策,这些都需要人类评审者的主观判断。
第四是业务逻辑的正确性。机器可以检查代码是否按照写的逻辑执行,但无法判断这个逻辑是否符合业务需求。一个折扣计算函数,机器能验证它没有除以零,但无法验证折扣规则是否正确。
所以我的观点是:自动化工具负责“让代码达到可评审的状态”,人工评审负责“判断代码是否应该被合并”。两者是接力关系,不是替代关系。
4.3 工具链的集成方式
工具链的集成方式直接影响评审体验。我见过一些团队,自动化检查的结果散落在各个地方——lint结果在一个页面,测试结果在另一个页面,安全扫描在第三个页面。评审者要打开好几个标签页才能看全,体验很差。
比较理想的做法是统一汇总到合并请求的评论里。CI流水线跑完之后,把关键结果以评论的形式贴到请求上。评审者在一个地方就能看到所有检查的状态。
具体实现上,大部分代码托管平台都支持这种集成。关键是要配置好哪些结果需要展示、哪些可以折叠、哪些失败需要阻塞合并。我的配置原则是:
- 阻塞性检查失败时,在评论里用醒目的方式展示失败原因和修复建议。
- 非阻塞性检查失败时,折叠展示,评审者需要时再展开。
- 所有检查通过时,只显示一个简洁的通过标记,不要刷屏。
这样评审者打开请求的时候,第一眼就能看到“有没有问题”,而不是被一堆信息淹没。
5. 评审文化的建设:比流程和工具更难的部分
5.1 评审语言的选择
代码评审中的语言,是一个被严重低估的问题。同样一个意思,不同的表达方式会产生完全不同的效果。
我见过这样的评审意见:“你这里写错了。”这句话的问题不在于它指出了错误,而在于它把人和事混在了一起。“你写错了”听起来像是对人的评价,而不是对代码的评价。作者的本能反应是防御,而不是思考。
换一种说法:“这个条件在输入为空的时候会走到else分支,可能不是预期的行为。”这句话只描述事实,不评价人。作者看到之后,第一反应是去验证这个事实,而不是为自己辩护。
我在团队里推行的原则是:评审意见只描述观察和影响,不做价值判断。具体来说:
- 不说“这样写不好”,说“这样写在X情况下会导致Y结果”。
- 不说“你应该用Z方案”,说“如果用Z方案,可以解决X问题,你觉得呢?”
- 不说“这里看不懂”,说“我没理解这部分的设计意图,能解释一下吗?”
这些表达方式的改变看起来很小,但累积起来会显著改变团队的评审氛围。评审从“挑毛病”变成“一起看问题”,从对抗变成协作。
5.2 如何处理评审中的分歧
评审中出现分歧是正常的,甚至是健康的。如果所有评审意见都被无条件接受,那说明评审没有起到应有的作用。问题在于如何处理分歧。
我的经验是,分歧通常来自三种情况:
第一种是信息不对称。评审者不知道某个约束条件,作者不知道某个潜在影响。这种情况下,解决方式是补充信息,而不是争论谁对谁错。作者解释为什么这样写,评审者解释为什么担心,双方的信息一交换,往往就能达成共识。
第二种是优先级不同。评审者认为可维护性更重要,作者认为交付速度更重要。这种情况下,需要把问题上升到更高层面——这个改动的长期影响是什么?如果现在不改,以后改的成本有多大?把这些摆出来,决策就清晰了。
第三种是审美偏好。这种分歧最难处理,因为没有客观标准。我的做法是,如果分歧只涉及风格问题,不影响正确性和可维护性,就尊重作者的選擇。评审者可以表达偏好,但不应该阻塞合并。
提示:如果一场评审讨论超过五轮还没有结论,就应该从文字沟通切换到面对面(或视频)沟通。文字沟通的效率在复杂问题上会急剧下降,而且容易产生误解。
5.3 评审的及时性
评审的及时性是一个容易被忽视但影响巨大的因素。一个合并请求如果等了三天才拿到评审意见,作者可能已经切换到别的任务了,回来处理评审意见需要重新建立上下文,效率很低。更糟糕的是,如果评审意见涉及到设计层面的修改,作者可能已经基于当前实现做了后续工作,改起来成本更高。
我的建议是,团队应该约定一个评审响应时间的目标,比如“工作日内的请求,评审者应该在4小时内给出第一轮反馈”。这个目标不需要100%达成,但有了目标之后,大家会有意识地去关注评审队列。
实现这个目标的一个实用技巧是评审轮值。每天指定一个人作为“首席评审者”,他的首要任务是处理评审队列,其他工作可以暂时往后排。这样能保证评审不会因为“大家都很忙”而被无限期推迟。
另一个技巧是控制请求的粒度。小请求评审起来快,大请求评审起来慢。如果一个请求需要一个小时才能看完,评审者会本能地拖延。把请求拆小,每个请求15分钟能看完,评审者就更愿意立刻处理。
6. 评审效果的度量与持续改进
6.1 应该度量什么
度量评审效果这件事,很容易走偏。我见过一些团队用“评审意见数量”来考核评审者,结果就是评审者开始吹毛求疵,提一堆无关紧要的意见来凑数。也见过用“评审通过率”来考核开发者,结果就是开发者开始讨好评审者,而不是关注代码质量。
我认为应该度量的不是“评审的量”,而是“评审的效”。具体来说,有几个指标值得关注:
- 评审周期时间:从请求提交到合并的平均时间。这个指标反映的是流程效率。如果这个时间很长,说明评审环节有瓶颈。
- 评审覆盖率:有多少比例的改动经过了至少一个非作者的评审。这个指标反映的是流程执行情况。理想情况下应该是100%。
- 评审后缺陷率:合并之后发现的缺陷中,有多少是评审时应该发现但没发现的。这个指标反映的是评审质量。如果这个比例很高,说明评审清单或评审者能力需要提升。
- 评审意见采纳率:评审意见被采纳的比例。这个指标反映的是评审意见的质量。如果采纳率很低,说明评审者可能提了太多无关紧要的意见。
这些指标不需要精确到小数点,看趋势就够了。如果评审周期时间在变长,就需要找原因。如果评审后缺陷率在上升,就需要检查评审流程。
6.2 定期回顾评审记录
度量指标能告诉你“有没有问题”,但不能告诉你“问题在哪里”。要找到具体问题,需要定期回顾评审记录。
我的做法是每个月抽一个小时,随机抽取10个已合并的请求,从头到尾看一遍评审过程。看什么呢?
- 评审意见是否具体、可操作?有没有“这里不太好”这种模糊的意见?
- 作者是否理解了评审意见?有没有出现“改了但改错了”的情况?
- 有没有评审者提出了很好的意见,但作者没有采纳,后来证明评审者是对的?
- 有没有评审者漏掉了明显的问题?
这个回顾不需要公开,自己看看就行。但坚持做几个月之后,你会对团队的评审质量有一个非常具体的感知,知道哪些地方需要改进。
6.3 从评审中提取学习材料
评审记录是一个被浪费的学习资源。大部分团队的评审记录在合并之后就没人看了,但实际上里面有很多值得沉淀的东西。
我习惯把评审中发现的典型问题整理成案例,在团队内部做分享。比如“这次评审发现了一个并发问题,原因是……”“这次评审发现了一个边界条件遗漏,场景是……”。这些案例比抽象的原则更有说服力,也更容易被记住。
另一个做法是把评审中反复出现的问题提炼成新的检查项,加到评审清单里。这样清单就变成了一个活的文档,随着团队的经验积累而不断丰富。
7. 常见反模式与应对策略
7.1 “橡皮图章”式评审
这是最常见也最危险的反模式。评审者打开请求,扫一眼,点个approve,整个过程不超过30秒。这种评审不仅没有价值,还会给人虚假的安全感——“我们的代码都是经过评审的”。
造成橡皮图章的原因通常有几个:评审者太忙、请求太大、评审者不熟悉代码、团队文化不重视评审。针对不同的原因,应对策略也不同。
如果是太忙,需要调整工作分配,给评审留出专门的时间。如果是请求太大,需要推动小粒度提交。如果是不熟悉代码,需要调整评审者分配,让熟悉的人来评。如果是文化问题,需要从团队负责人开始重视,以身作则。
7.2 “吹毛求疵”式评审
和橡皮图章相反,有些评审者会在无关紧要的细节上纠缠。比如变量命名不符合他的个人偏好、代码格式和他习惯的不一样、注释的写法他不喜欢。这种评审会让作者感到沮丧,也会拖慢整个流程。
应对这种反模式的关键是区分“必须”和“偏好”。评审者可以表达偏好,但不应该把偏好当作阻塞理由。团队应该有一套明确的代码规范,规范之内的事情不需要在评审中讨论。规范之外的事情,如果只是风格问题,尊重作者的选择。
7.3 “延迟评审”式评审
请求提交之后,等了很久才有人看。等评审意见出来的时候,作者已经去做别的事了,回来处理需要重新建立上下文。更糟糕的是,如果改动涉及到和其他分支的交互,延迟可能导致合并冲突,增加额外的工作量。
应对延迟评审,除了前面提到的评审轮值机制,还有一个技巧是设置提醒。大部分代码托管平台都支持在请求长时间未评审时自动提醒评审者。把这个功能打开,能减少很多“忘了看”的情况。
7.4 “单向评审”式评审
评审变成了单向的“检查”,评审者只负责挑毛病,作者只负责改。这种模式下,评审的知识传递是单向的——从评审者到作者。但实际上,作者对这块代码的理解可能比评审者更深,评审者也能从作者那里学到东西。
要打破单向评审,可以在评审中鼓励提问和讨论。评审者可以问“为什么选择这个方案而不是另一个”,作者可以解释背后的权衡。这种讨论不仅能让评审者更好地理解改动,也能帮助作者发现自己没想到的问题。
8. 从零开始搭建评审体系的实操路径
8.1 第一周:建立最小可行流程
不要一开始就追求完美。第一周的目标是让评审这件事“跑起来”。
具体来说,做这几件事:
- 确定评审工具。大部分代码托管平台都自带评审功能,直接用就行,不需要额外搭建。
- 约定评审规则。最简单的规则:所有改动必须经过至少一个非作者的评审才能合并。
- 建立评审清单的初版。不要超过10条,只包含最关键的检查项。
- 配置自动化检查。至少把测试执行和静态分析接进来。
这一周的关键是“简单可执行”。规则太多、清单太长、工具太复杂,都会导致执行不下去。
8.2 第一个月:收集反馈,调整流程
流程跑起来之后,会暴露出各种问题。有的规则不合理,有的清单项没人看,有的自动化检查太慢。这个阶段的任务是收集反馈,快速调整。
我建议每周花15分钟做一个简单的回顾:这周评审中遇到了什么问题?哪些规则需要调整?哪些检查项需要增删?
这个阶段的调整可能会比较频繁,没关系。流程是为人服务的,不是人为流程服务。找到适合团队节奏的方式最重要。
8.3 第三个月:沉淀经验,形成规范
经过两个月的磨合,流程应该比较稳定了。这时候可以把实践中证明有效的做法沉淀成团队规范。
规范的内容包括:评审流程、评审清单、评审语言规范、分歧处理机制、度量指标。这些规范不需要很详细,但需要明确,让新加入的成员能快速上手。
规范形成之后,也不是一成不变的。每个季度回顾一次,根据团队的变化和经验的积累做调整。
8.4 持续改进的日常习惯
评审体系的持续改进,最终要落到日常习惯上。我自己的习惯是:
- 每次评审时,如果发现一个清单里没有的检查项,就记下来,周末统一整理。
- 每个月抽时间看一遍评审记录,找找有没有可以改进的地方。
- 每次线上事故之后,问一句“评审时能发现这个问题吗”,如果能,就把检查项加到清单里。
这些习惯看起来很小,但坚持下来,评审体系会越来越贴合团队的实际需要,而不是一个从网上抄来的模板。
9. 一些实操中的小技巧和踩坑记录
9.1 评审描述模板的设计
合并请求的描述模板,直接影响评审者的第一印象。一个好的模板应该包含:
- 这个改动解决了什么问题(不是“改了什么”,而是“为什么改”)
- 改动的核心思路是什么
- 有哪些地方是评审者需要特别关注的
- 有没有已知的遗留问题或后续计划
我见过很多请求的描述只有一行“fix bug”,评审者打开之后完全不知道背景。这种情况下,评审者要么花大量时间自己搞清楚背景,要么就草草看一眼。两种结果都不好。
9.2 大改动的拆分策略
有时候一个改动确实很大,比如重构一个核心模块。这种情况下,硬拆成小请求可能不现实。我的做法是:
- 如果可能,先做不改变行为的重构,再做功能变更。这样评审者可以分别评审“重构是否保持了行为一致”和“功能变更是否正确”。
- 如果重构和功能变更必须一起做,在描述里详细说明改动的结构和评审顺序。比如“建议先看A文件,这是核心逻辑;再看B文件,这是适配层;最后看测试”。
- 对于特别大的改动,可以考虑先做一个“设计评审”,用文档或注释的形式描述方案,评审通过之后再写代码。
9.3 评审中的沟通技巧
评审中的沟通,有几个技巧很实用:
- 先肯定再建议。比如“这个错误处理的思路很好,不过如果加上重试逻辑会更健壮”。先肯定能让作者更愿意接受后续的建议。
- 用问句代替祈使句。“这里加个空值检查会不会更好?”比“这里必须加空值检查”更容易被接受。
- 给出具体的修改建议。如果只是指出问题而不给方向,作者可能需要花很多时间自己摸索。如果能给出具体的修改建议,作者可以直接评估和采纳。
- 区分“这次必须改”和“下次注意”。不是所有问题都需要在这次改动中解决。有些问题可以记录下来,留到后续处理。
9.4 评审者的自我修养
作为评审者,有几个习惯能显著提升评审质量:
- 先看描述再看代码。了解背景之后再读代码,更容易理解作者的意图。
- 先看测试再看实现。测试能告诉你这个改动“应该做什么”,实现告诉你“怎么做”。先看测试能帮你更快地理解改动的意图。
- 不要一次看太多。连续评审超过一个小时,注意力会显著下降。建议每45分钟休息一下。
- 不确定就问。如果看不懂某个地方,不要猜,直接问作者。猜错了比问出来更尴尬。
9.5 一个真实的踩坑记录
最后分享一个我自己的踩坑经历。有一次,一个同事提交了一个改动,涉及到一个核心的计费逻辑。我看了diff,逻辑看起来没问题,测试也通过了,就approve了。结果上线之后发现,在一个特定的边界条件下,计费会多算一次。
回头复盘的时候发现,问题出在我评审时只看了diff,没有看这个函数被调用的上下文。这个函数在某个路径下会被调用两次,而改动引入了一个副作用,导致第二次调用时状态不对。diff里看不到这个调用路径,只有看完整的文件才能发现。
从那以后,我评审时养成了一个习惯:对于核心逻辑的改动,不只看diff,还要看改动所在的完整函数和它的调用方。这个习惯让我后来避免了好几个类似的问题。
评审这件事,说到底是一个需要持续投入的事情。没有一劳永逸的方案,也没有放之四海而皆准的模板。每个团队都需要根据自己的情况,找到适合自己的节奏和方式。但有一点是共通的:评审的质量,最终取决于团队对代码质量的重视程度。如果团队真正在乎代码质量,评审就不会流于形式;如果不在乎,再好的流程和工具也救不了。