我要提问
ARTICLE DETAIL

资讯详情

前沿编程新知与开发实战干货的深度解读。

代码评审流于形式?一套可落地的评审流程重构实践

代码评审流于形式?一套可落地的评审流程重构实践 我先把话说在前头绝大多数团队的代码评审根本不是“评审”是“盖戳”。PR 一上来reviewer 扫一眼看到绿勾就 LGTM看到代码风格不合意就丢两句“这里最好拆一下”“命名再想想”改完合入完事大吉。团队倒是挂了“代码评审覆盖率 100%”的牌代码质量却一年比一年差生产事故里有一半都能追溯到一个“当时没人发现的低级问题”。我在经历了好几次这种打脸之后决定把团队的评审流程彻底重做一遍项目代号就叫open-code-review。它是怎么从一团糟变成可复用机制的这篇就完整拆给你看。1. 传统代码评审的三大痛点为什么绝大多数评审流于形式想做好评审得先搞清楚评审为什么做不好。我当时没有急着上工具、写规范而是拉了团队里六个人一个个聊再加上翻过去三个月的评审记录最后总结出三个最要命的问题。每一个背后都有对应的机制缺陷不解决机制只强调“大家认真点”毫无意义。1.1 “人情评审”LGTM 背后的责任真空第一个问题是人情。代码评审里最泛滥的回复就是一个词LGTMLooks Good To Me我看着没问题。问题出在哪出在“没问题”这三个字的定义不清晰。很多 reviewer 的心理活动其实是“我没仔细看但我不好意思说没看”“这是老同事写的应该靠谱”“上次我给他提了意见这次不好意思再挑刺”。当一个团队里“不好意思”比“负责任”更常出现评审就变成了审人情技术防线形同虚设。有一次一位同事提交了一个改数据库连接池参数的 PR改动只有三行看起来人畜无害。评审的人一分钟不到就 LGTM 了结果参数配错半个服务在高峰期连不上数据库线上故障挂了四十分钟。事后复盘那位 reviewer 说“我只看到是调参想着他自己的模块他最熟就没细看。”——这话能讲出口说明团队根本没有把“评审者要对合入后果负责”这件事建立起来。靠个人自觉撑起的评审防线一定会垮。必须把“必须看懂”变成评审流程本身的要求而不是道德要求。1.2 千行大改动一次性看完工作记忆的物理极限第二个问题是评审单元太大。我翻了旧记录当时平均每个合并请求PR的改动量在 800 到 1500 行之间有些人甚至能把几周的工作攒成一个大 PR 推到仓库里。reviewer 一看 diff一千多行正常人一次性根本看不过来只能“战略性放弃”——从头到尾翻一遍文件名看看每个文件改了哪里遇到看不懂的函数上下文切来切去半小时过去脑仁疼最后草草给个绿灯。这不是态度问题是认知资源有限。人类工作记忆最多只能同时处理四到五个信息块一个上千行的 diff代码块之间相互调用、状态流转贯穿多个文件你让大脑在工作记忆里同时维护这些关系数学上就不可行。所以评审做不好有一半是怪流程没有替评审者“降低认知负载”。只要改动上限不控制换谁来评审结果都一样。1.3 评审意见无人闭环讨论完就丢的“临时批评”第三个问题是评论没有闭环。旧流程里评审意见散落在各个 PR 讨论串里作者改完一部分还有一部分假装没看见reviewer 自己也没回看“我上轮提的那些点是否都解决了”。没有任何机制去跟踪“本轮提出 N 条意见其中 M 条已处理K 条待处理”。更常见的情况是reviewer 在评论里提了一个设计层面的大问题作者回了一句“这个后面再优化一下”然后就没有然后了。三个月后这个“后面”就成了技术债。我们做过一个统计随机抽了上个季度 40 个已合入 PR发现其中 35 个存在“评审提出过但被搁置”的评论比例高得吓人。这些被搁置的意见连回滚记录、TODO 注释都没留下直接就消失在了空气里。所以open-code-review 的第一个目标不是教会大家怎么“品鉴代码”而是建立一套机制让上述三类问题根本失去存活的土壤。2. 评审粒度与流程门禁让“开放”长在地基上在解决“人性问题”之前先要解决“结构问题”。我做的第一件事是重定义两个东西一个 PR 该多大一个 PR 在到什么条件下才允许进入人工评审。这两个是地基中的地基。2.1 小步合入不是风格偏好是认知科学的必然选择我给团队定了一条明文规则单个 PR 改动量默认不超过 400 行理想区间是 150 到 250 行。超过就拆。为什么是 400因为我发现当 diff 超过 400 行reviewer 平均花在每个文件上的注意力会急剧下降出现“看了后面忘了前面”的频率大幅上升。400 是一个经验值但背后对应的是一个下午一个评审者一个完整的上下文刚好能认真处理完的体量。一开始团队非常抵触最典型的声音是“我这次重构顺手改了几个文件的格式这不拆也没事吧”我告诉他们能拆但请按“行为边界”拆不要按“时间先后”拆。比如“引入新传输协议”是一个行为“用新协议替换老协议”是另一个行为。格式调整这种噪音直接混合进去就是逼迫评审者把宝贵的注意力浪费在无意义的 diff 上。我们的仓库里甚至专门加了一条规定纯格式或重命名类改动必须单独提交不许混在功能 PR 里。拆完之后效果立竿见影。第一周大家还骂骂咧咧到第三周一个两百行的 PR 从提交到合入平均只需一天而以前一个千行大 PR 要来回拖一周。评审速度反而快了四倍。2.2 两级评审路径快审与会审各司其职不是所有改动都需要同等强度的评审。我们跑通了全部流程。当前检查项若不匹配禁止合入。自然会拖慢节奏对每一行都战战兢兢也不现实。我当时给团队设计了两条评审路径按风险等级分流。路径 A轻量快审。适用于文案调整、样式微调、非核心路径的小工具类改动。一个 reviewer 即可评审目标是在 24 小时内给出结论。路径 B完整会审。适用于核心链路、数据模型变更、权限校验、基础设施配置、对外 API 变更。至少两个 reviewer其中一个必须是“不熟悉这段代码的人”以旁观者视角检查假设是否合理。路径 C我后来加的紧急修复。先合入、后补审但合入时必须在 PR 描述里打上hotfix标签48 小时内必须补完评审否则自动回滚。这个口子不能开多一个月超过五次就要开会说清楚流程哪里出了问题。分流的关键不在于“区别对待”而在于让团队的评审能量优先花在高风险改动的确定性上。低风险改动走快速通道不是为了偷懒而是为了给高风险改动腾出充分的评审时间。2.3 机器先于人的门禁把低级问题挡在人工评审之前完善评审不等于取消工具检查。工具能做的事情尽量不让评审者重复做否则他们很快就会对机械问题麻木再遇到真问题时嗅觉也灵不起来。我们的门禁分为三条全部在 CI 阶段执行编译与单测失败直接挂起不允许请求人工 review。静态扫描严重级别的问题数量必须为零警告级别不能新增。仓库里放了一份SEVERITY_BASELINE清单老问题可以保留但新问题的指标只增不减。自动化格式检查风格由机器统一评审者不再就“缩进用空格还是 Tab”发表意见。这三层做完人工评审面对的就是一个相对“干净”的 diff讨论的质量明显提高。我也跟团队说得很直白一旦人工评审者发现自己在一个合并请求上还要抓排版类问题说明门禁没设好先别怪大家不认真先回去补自动化。小提醒门禁不是越严越好。我见过一上来挂十几个检查项的项目开发者提交一个 PR 要来回重跑三次构建最后大家学会了一招——赶在下班前检查空档偷偷合入。门禁的粒度要让开发者觉得“够得着”而不是“闯关”。3. 评审清单的建设与维护团队最容易忽略的杠杆资产谈完结构谈清单。这是我体会到性价比最高的一步也是很多人做评审流程时最容易忽略的东西。清单不是几行文字它其实是经验的显性化把团队踩过的坑编译成所有人都能调用的默认检查项。3.1 清单不是抄来的是从失败复盘里长出来的一开始我没经验直接去社区里找了一份“通用代码评审清单”丢到 Wiki。结果可想而知没人看也没人用。原因是太泛什么“确保代码可读”“考虑边界条件”——这些话谁不知道真正有用的清单每条都必须带有你自己的团队记忆。我从团队过去一年的生产事故和严重缺陷里挑出 24 个典型案例逐个反推“如果能早一步在评审中发现应该检查什么”由此提炼出第一版清单。比如我们出现过一次数据导出把权限过滤条件写丢的问题清单里就加了“导出类接口是否带上了下游租户的可见域过滤条件”。出现过一次定时任务重试导致重复扣费清单里就加了“该任务的重复触发保护是否已经验证”。出现过一次缓存 key 没带版本号导致灰度发布后脏数据串读清单里就加了“缓存 key 是否根据业务版本做了隔离”。每一条都对应一次具体的“疼”团队看到清单时第一反应是“哦这条是那次事故”而不是“这说的是啥”。清单天然带有情感记忆这决定了它是否会被执行。3.2 四类核心清单及其组织方式现在仓库根目录下放了一份REVIEW_CHECKLIST.md按四类组织每类十到十五项。这四类分别是类型核心关注点典型条目通用正确性并发、异常、空指针、边界值是否存在共享可变状态未加锁异常被吞后日志链路是否可追溯安全与权限越权访问、数据暴露、敏感信息新接口是否校验了调用的数据可见范围日志里是否打印了敏感字段可测试性单测覆盖、构造难度、测试替身新增分支是否缺少对应单测模块边界是否依赖了可 mock 的接口性能与兼容批量循环、连接数、灰度兼容是否引入了 N1 查询老版本客户端兼容性是否验证每一项后面都跟一栏“如何检查”不是简单一句“注意并发安全”而是具体的动作。比如“并发”那条写的是“查一下对象是否在多线程间共享用synchronized或原子类重跑一遍竞态场景确认无脏读写。”有了动作清单才能被判断为“通过”或“不通过”。3.3 清单的版本化与“反官僚化”维护清单不能一成不变也不能朝令夕改。我把它和 CI 配置一样纳入 Git 管理每次变更都走 PR 评审。但在维护节奏上我们严格控制一个原则清单是减法是活文档加法要克制减法要常态。为什么控制加法因为清单条目一多就会官僚化。评审者面对一份 80 条的清单只会麻木地打勾根本不会逐条思考。控制在每种五到十五条以内让清单保持“扫一眼就能记起大致内容”的长度执行率最高。每次季度复盘会上我们会逐条问两个问题过去三个月这条清单帮忙拦住了多少问题如果连续三个月一条都没触发就直接删掉。留下的都是经过实战检验的清单的价值才能持续。实操建议不要用评审工具里自带的清单插件直接弹窗逼着人打勾。那会让清单变成行政流程。我建议把清单放在仓库里的引导文件里让每个 PR 描述默认带上模板作者在“自检”阶段填好“哪些条目已自查哪些需要 reviewer 特别关注”把清单变成沟通语言而不是审批表。4. 异步评审的评论规范让同事愿意认真读你的代码框架搭好了接下来是每天的实操细节怎么提问、怎么回复、怎么让评论真的推动改动。这一节分享的是把评审从“一场尴尬对话”变成“高效异步沟通”的具体规则。4.1 为什么评审会开不起来打字反而比说话高效有人会问直接拉一个会议室大家围着投影仪过代码不是更直观更快吗我的经验恰恰相反。会议式评审有三个通病一大家会碍于面子不反复追问二讨论发散半小时下来可能只过了两个文件三会后没有任何可检索的记录结论全凭记忆。异步评审在 PR 内留言、分段讨论、各自有空时参与反而是最适合大多数团队的形态。它的优势是每个观点都有文字记录、可以被搜索、可以被引用。哪怕两个人就一个设计问题争论了六轮这些讨论对后来者依然是宝贵的决策日志。但异步评审有个前提评论必须“好读”。为此我在团队里推行了一套评论书写规范核心是三语气原则。4.2 评论三语气提问式、建议式、阻塞式的边界这三类在措辞上不是客套问题而是决策框架。提问式用于作者更了解上下文、评审者缺少信息时的探询。句式通常是“这里如果遇到 X 情况会怎样”、“我没理解为什么要这么做能说说背景吗”。建议式用于有明确改进方向但非原则性问题。句式通常是“更常见的做法是 Y你们权衡一下要不要改”。阻塞式用于确认“不解决就不能合入”的问题通常是正确性、安全性和数据一致性。句式通常是“这个分支下存在越权风险合入前必须处理”。我统计过引入三语气前PR 讨论串里 60% 的评论会被作者忽略或误读。引入后作者扫一眼语气前缀就能判断优先级评审者自己也会在写“阻塞式”时更谨慎——因为这个词意味着他有责任把问题讲清楚而不是随手打个高风险标签吓人。踩坑提示最高频翻车场景是建议式被作者听成了阻塞式或反过来——建议式成了橡皮图章。所以规则里同时要求作者在回复每条阻塞式意见时至少要在评论里写清“处理方式”要么“修改了链接到新 commit”要么“说明了为什么不改的理由”绝不能用一个“done”草草带过。4.3 带着上下文评论路径、行号与方案示例第三评论必须指向具体位置不许泛泛而谈。我遇到过最恼火的评审评论是这样的“这个模块设计得不太合理建议重构。”——文件是哪一个函数是哪一段什么算合理完全没有坐标。作者只能自己猜猜错了又是一轮往返。我们定下来两条硬规则评论必须落在具体的文件路径和行号区间哪怕这条评论是在讨论整体设计也要先指出“看order_service.py168 行过来的数据流这个设计在三层调用里会出现误判”。设计级别的问题必须给出一个“可执行的替代方案示例”可以是一段伪代码也可以是一个链接。就算方案不完美也要给一个讨论起点。只提问题不给方案的评论作者很难动手给了方案的评论作者至少可以直接说“方案 B 哪里哪里不可行”对话瞬间具体了。这里有个真实例子。某次一位同学改了一个支付回调的状态机流转评审者提了七条意见五条都是“这里还有问题”。作者懵了追问哪来的问题。如果每条评论都带例子就不会有这个来回。后来我们要求 PR 作者在描述里画清楚状态流转表评论区关于“状态机是否合理”的争论就再也没发生过。5. 评审数据的度量与复盘把讨论沉淀成团队资产流程、工具、沟通规则都到位后我加了最后一块数据。评审不能只靠感觉评价“好了不少”得有指标、有趋势、有复盘。但怎么度量非常讲究度量错了团队会把力气花在刷指标上而不是解决问题上。5.1 不要盯着“评审时长”做绩效考核我见过有人用“评审响应时间”作为团队绩效指标的结果相当讽刺大家为了显眼挂 1 分钟就点 approve等于变相鼓励走过场。“快”这个变量只有在“质量也达标”时才有意义。如果只把速度快当绩效用不了多久审得最糙的人评分最高认真挑问题的人反而因为“拖慢了发布节奏”挨批评。门禁之外的评审质量短期没有机器能测出来所以度量必须回归“评审行为”而不是“评审速度”。5.2 真正有价值的四类度量指标我设计了四个指标只用于团队自我观察不进个人绩效考核每条评论的采纳率评论量除以被落实或明确回应的评论量。这个数字过低说明评论位置或粒度有问题。阻塞式评论密度每百行 diff 里的阻塞级意见数。它可以反映代码质量和评审严格度是否失衡。二次合入前返工轮次一个 PR 从第一次 push 到合入之间有多少轮 review 往返。轮次过高说明拆分支工作有问题过低说明评审可能流于形式。评审带出的缺陷率合入后一个月内因评审遗漏修复的 bug 占该迭代总 bug 的比例。这个指标是亡羊补牢但能帮我们验证评审是否真的“覆盖了”。我每两周拉一次数据只给团队看趋势不下结论。比如“这一迭代阻塞式评论密度从 0.9 降到 0.4看看是不是我们太重人情了”这样的讨论才是有建设性的。5.3 月度评审复盘怎么开才不像批斗会复盘会我们叫“评审质量回顾”不是秋后算账。要点就三条每个案例的第一个动作是先分析“流程为什么没拦住”而不是“谁没看仔细”。流程缺位的问题通过改流程解决个人失误的问题才是通过提醒解决。每次只挑两到三个典型案例深入讨论一个案例 20 分钟禁止懒婆娘裹脚。选案例的标准很统一要么是评审成功拦下严重问题要么是评审没拦住导致问题漏进了生产。讨论完必须有产出要么更新清单要么更新门禁配置要么写一条新的评审规范。空谈一轮没有行动项的复盘下次没人愿意来。有一个案例我记得很清楚。某次漏了一个权限校验评审没拦住事后查下来是清单里根本没有对应条目——因为那种业务场景半年前才上线。复盘会就是从这个缺口出发给清单加了一条“新上线的导出/下载类能力是否默认要求租户维度隔离”。下个季度就真的靠这条拦下了一次越权风险。这个反馈闭环才是评审数据最有价值的地方。6. 落地过程中的坑与我的收尾心得最后讲几个真实踩过的坑这些如果不讲可能有人照着上面的方案做半年就做出一身内伤。6.1 门禁数量先从三个起我刚开始时兴致极高一次性挂上编译、单测、覆盖率、静态扫描、复杂度上限、变更文件数量告警等七个门禁。结果自然是吐槽声一片。覆盖率 80% 以下就拦合入导致大量测试基建还没跟上的老模块直接“无法交 PR”于是大家学会了在覆盖率配置文件里做局部豁免虚报出来一个达标数字。后来我把门禁阈值重新设计先按住编译、单测、静态扫描三个“绝对底线”其余全部设为“warn”警告级别不拦截合入但与 PR 信息同屏展示。跑三个月水落石出再逐步把确有必要的 warn 升成必须项。渐进式收紧比一步到位温和得多团队的对抗情绪会小很多。6.2 清单沦为打勾游戏之后的纠正措施用了一个月后我注意到部分成员会直接在 PR 描述里复制模板逐条勾上“已自查”其实根本没看。发现这一点源于一次抽查一位同学提交了一个涉及事务边界的改动自检表里勾了“并发与事务安全已确认”但问起来他连事务挂起的两条分支路径都讲不清楚。我的处理方式不是批评他而是把模板的填法改了每一条不只给勾还要填“对应代码位置或说明”。比如“并发与事务安全”这一条模板要求写出“锁对象名 / 事务边界在哪个方法”写不出来就说明没查。改成开放式填写后糊弄成本直线上升自检表的质量就上来了。6.3 AI 辅助评审的正确定位现在评审工具链里自然会加入大语言模型辅助。我的用法是“AI 先查人再看差异”而不是把 AI 结论直接当评审答卷。具体流程是PR 提交时AI 助手先扫一遍给出一个“预审报告”列它认为的高风险区域、潜在 bug、缺少的测试覆盖。它的价值是帮 reviewer 臭毛病——用较低成本快速标记出值得深挖的位置。但对于 AI 给的建议合入与否最终还是由人决定而且我们明确要求AI 报告里的每条线索reviewer 都要自己打开对应代码核实一遍再决定是否转成评论。用过几次就会发现AI 对“模式类问题”特别灵比如循环里调接口、异常被吞、并发修改——这些高频模板它一下子就盯上了。但对业务规则的理解比如“这个字段是否应该按租户隔离”它完全没有判断力。所以别指望 AI 替代评审就当它是个不嫌烦的实习生先把容易踩的坑帮你标出来就好。6.4 文化布道的最后一步流程和工具都到位也不意味着万事大吉。最重要的一步其实是最软的让大家真正相信提严重意见不是找茬而被提意见不是被否定。我们做了一件特别笨但有效的事。每个迭代把“评论区最有价值的讨论”选一条出来放到周会表扬。选的标准很简单这条评论或者推动了设计变化或者拦下了一个潜在问题而且讨论过程礼貌、具体、有建设性。表扬的对象不限于提意见的人也包括好好回应并采纳意见的人。谁给了一个好意见谁坦然接受了一个好意见都会被看见。这套正向反馈跑了半年团队里的评论区氛围完全不同。另外作为项目的初始推动者我给自己定了一条约束自己写的 PR必须主动在描述里写出“我拿不准的三个点请重点 review”先示范什么叫“开放地接受评审”。如果你自己都做到这份上团队没有理由不跟上。最后说一句我一直信奉的话代码评审不是质量部门它是一群人共同理解一套系统如何演化、如何避免踩坑、如何把个人经验变成团队共识的过程。工具和流程都只是手段把这个共识做好比哪个 PR 里多抓出几个低级 bug 重要得多。open-code-review 这套实践走到今天最能称道的其实不是那些指标而是团队从此默认“代码是大家一起写出来的而不是写完了给你看一眼”。
返回列表