代码评审:一台双向学习机器
这一章讲三件事: 评审到底在买什么(答案不只是「抓 bug」); 作为被评审者,怎么让你的代码容易被评、被评后怎么接; 作为评审者,怎么评得又快又准又不伤人。 读完你会拿到一台「双向学习机器」的两侧操作手册。
1. 这一章讲什么
你的代码不会直接进主干(主分支)——中间隔着代码评审:团队里的其他人在你的改动合并之前阅读它、提意见、最后批准。多数团队都有这道流程,但流程的质量天差地别:原书开篇的对仗是,高质量的评审文化「有助于所有具有不同经验水平的工程师的成长」,糟糕的评审文化「抑制创新,减慢开发速度,并且导致滋生怨恨情绪」1。
本章在全书链条里的位置:第 05 章的测试是你自己给自己立的栅栏;评审是团队替你立的第二道闸。它同时是第 01 章那两个心理循环(冒充者/邓宁-克鲁格)的高发现场——原书明确说,评审会同时触发焦虑与过度自信2。
2. 顶层全景
一条改动的生命周期
准备 ──→ 提交(创建评审请求) ──→ 评审(讨论/修改) ──→ 批准 ──→ 合并
│ │ │
│ 被评审者侧 评审者侧
│ ├ 小改动+描述+测试 ├ 分流(紧急?复杂?)
│ ├ 草案可提前 ├ 先懂意图再评论
│ ├ 别用评审触发 CI ├ 区分问题/建议/挑剔(Nit)
│ └ 别太在意 ├ 不当橡皮图章,也不当路障
└──共同前提:高度信任的环境 ←┘
图说:左右两侧各有一套手艺,共享同一个前提。主走查在 3.3(UI-1343)。
3. 核心原理
3.1 评审买的是什么:五重价值,一个前提
抓 bug 是肉眼可见的好处,但原书把它排在更长的清单里3:评审是教学工具(评审者会指出你不知道的类库和实践,你也能从资深队友的评审请求里学写生产级代码);是共同理解的保障(确保不止一个人熟悉每一行生产代码——你休假时没人抓瞎,值班工程师能查到「这块代码什么时候、为什么改的」);是实现决策的档案(为什么这么写的理由,白纸黑字留在评审意见里);有时还是安全与合规的硬要求(防止任何一个人恶意修改代码库)4。
但这五重价值有一个共同的开关:只有当所有参与者处在「高度信任」的环境里——评审者有意提供有用的反馈,被评审者也愿意接受——好处才成立5。反过来,执行不力的评审是一种腐蚀剂:轻率的反馈拖慢所 有人,缓慢的周转让改动停滞,没完没了的拉锯扯皮可能毁掉整个团队。原书给评审文化划的两条底线值得贴在墙上:评审不是证明你有多聪明的机会,也不是橡皮图章式的官僚主义障碍6。
3.2 被评审者侧:把你的改动变成好评的对象
准备请求。 一条改动的生命周期是准备、提交、评审、批准、合并7。好的评审请求(请求别人评审你改动的载体,在 GitHub 上就是拉取请求)有几个构造要素,原书给了样例:标题带上工作单号;描述写清改了什么、怎么验证;勾检查列表(是否加了测试、是否动了公开接口);把该来的评审者(个人加团队)都列上8。
草案降险。 很多人靠写代码思考。写得太早提交正式评审是浪费,闷头写完才发现方向错了更浪费——中间态是评审草案:标题挂上 DRAFT 或 WIP 字样的非正式请求,专门用来在错误的道路上还不太远时获得低成本反馈9。
别拿评审当测试跑。 大项目的测试套件又多又难跑,新人的诱惑是「提交评审让持续集成(CI,合并前自动构建和测试的流水线)替我跑一遍」。原书点名这是糟糕做法:你填满了测试队列,堵住真正要合并的评审;队友误以为你的请求需要人看;而 CI 只会跑全套,你明明只需要跑相关的那几个10。正确动作是花时间搭好本地环境——本地调试失败测试,比隔着 CI 环境容易得多11。
大改动先预排。 几千行的改动在网页评审界面里根本看不动。原书的工具是预排会议:当面分享屏幕,从页面加载或程序启动一路走到执行结束,把新抽象的主要概念讲给团队听——注意,预排不是评审,意见留到正式评审环节;预排给的是「为什么要这么改」的心理模型12。
接反馈的姿态。 两条:一是别太在意——保持一点情感距离,评审意见针对代码,不针对你,而且代码很快就不全是「你的」了,整个团队都会拥有它13;二是保持同理心但不容忍粗鲁——有些人的「简短且命中要害」在别人那里就是「粗暴无礼」,遇到越界的评论要明确指出来;聊不清楚就转成面 对面14。真遇到分歧,先问自己「我护着这段代码,是因为我写的,还是因为这样确实更好?」,解释你的观点,仍谈不拢就交给团队的裁决惯例——有的服从提交者,有的服从技术负责人,有的服从小组法定人数15。
保持主动。 评审者会被各种提醒淹没,没动静就去问(但别催);收到意见尽快回——你回得越快,对方回得也越快。批准之后尽快合并:悬而不决的评审会需要变基(把你的分支挪到最新的主干上重放一遍),极端情况会破坏代码逻辑,逼你再来一轮评审16。
3.3 主走查:一条评审意见的两个版本,和一次完整的请求
原书在两处给了「同一件事的差版和好版」,把它们拼成一条走查最能看出评审手艺的分寸。
走查第一段:一条意见。 场景:被评审的代码在处理网络端口。作者收到的意见原文分两截——第一截:「校验端口是否大于或等于 0,如果不是,需要触发 InvalidArgumentException 异常。」紧跟 着一行「端口不可能是负值」17。这条意见好在哪?它同时给了「什么」和「为什么」:动作(校验并抛异常)+ 理由(端口不可能是负数)。原书给评论的总原则就是按「你们坐在一起评审代码时的说话方式」来写——太简短的评论既难懂又容易显得像命令18。对照第 01 章潘卡的坏邮件,结构是一样的:给足背景,说清理由,让人能行动。
走查第二段:一次请求。 把原书的 UI-1343 样例完整走一遍字段19:
评审者: agupta, csmith, jshu, UI/UX 团队 ← 个人+整个相关团队
标题: [UI-1343] 修复了在目录 Header 上缺失链接的问题
← 带工作单号,可自动关联需求
描述:
# 概述
主页目录 Header 上缺失「关于我们」的链接;现状是单击目录按钮
没有反应,通过追加一个正确的链接修正。
追加了一项 Selenium 测试来验证本次修改。 ← 主动交代怎么验证
# 检查列表
- [x] 添加新的测试代码
- [ ] 修改面向公众的 API ← 让评审者一眼看到风险面
- [ ] 把设计文档涵盖在内
评审者拿到这份请求,从分流(小改动、中等风险,可以安排进今天的评审时段)到理解意图(补一个漏掉的链接,修按钮无响应)都不需要追问;检查列表里「未修改公开 API」替他省掉了兼容性盘查。原书补了一个细节:很多代码库有评审模板,照着填就行——模板存在的意义就是把这些字段变成不用想就会填的习惯20。