ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

前端团队Code Review实践指南:open-code-review开源工作流解析

前端团队Code Review实践指南:open-code-review开源工作流解析

Code Review 这件事,前端团队只要超过五个人,就一定会遇到两种声音:一种是“每回合并都要卡我半小时,进度全被拖死”,另一种是“这代码屎山又加了一层,下次谁接手谁倒霉”。我见过太多团队在“审不审”和“怎么审”之间反复横跳,最后要么形式化地点个通过按钮,要么干脆取消 Review 回到单兵作战。今天聊的这份“前端团队 Review 指南(open-code-review 版)”,不是某个大厂内部保密 SOP,而是一套可以自己搭起来的开源工作流,把规则、工具、流程全部落到一个仓库里,让人人可看、可改、可争议。

这套东西解决的核心问题很简单:告诉团队每个人,Review 到底看什么、按什么顺序看、哪些问题必须拦截、哪些问题可以放行。它把“凭经验”变成了“按清单”,把“看心情”变成了“看证据”。适合正在组建前端团队的技术负责人,适合被 Review 折磨得想退群的开发同学,也适合想建立规范但不知道怎么下手的初学者。

1. Review 的现状与前端团队的核心矛盾

1.1 前端 Review 最容易走过的三条弯路

第一个弯路是把 Review 当“找茬”。很多人一看到别人的 PR,下意识就盯着代码风格、缩进、变量命名这种表层问题不放。前端代码的静态检查工具已经能覆盖大半风格问题,人肉盯着这种事纯属浪费。真正值钱的问题是逻辑边界、状态冲突和数据流异常,这些东西机器人暂时还看不明白。

第二个弯路是把 Review 当“过场”。合并按钮点下去之前,连 diff 都没打开过,直接点 Approve。这种情况在团队里最常见,因为大家默认“又不是我写的,出了问题也不是我背锅”。可实际上 Review 最大的价值不是抓 bug,而是让代码在合并之前多一双眼睛,把“我没想到”变成“我们想到了”。

第三个弯路是把 Review 当“考试”。Reviewer 在评论里写了八百字论文,开发得逐条回复,很多问题其实可以线下直接聊清楚,非要在评论区里吵。最后的结果就是团队为了少吵架,干脆不写注释、不拆函数、不问为什么,一切以“能过审”为目标。

这三条弯路走完,Review 就从质量工具变成了内耗工具。open-code-review 的思路恰恰相反,它不追求每一条评论都有回应,而是追求每一类问题都有明确的归属,该机器管的归机器,该人管的归人,该开会的去开会。

1.2 为什么是“open-code-review”,而不是自己定一套规则

很多团队一开始都想自己写一份《前端代码评审规范》,写来写去也就十几条,翻来覆去都是那几句。真正落地的难度不在规则文本,而在执行过程。你自己写的规范没人认,因为缺乏“出处”;你自己定的流程没人守,因为缺少“工具支撑”。

open-code-review 的做法是把仓库作为规则的载体,所有规则、模板、检查脚本全部开源放在团队仓库里。这么做有几个直接好处:任何人改动规则都留痕,规则更新不用开大会通知;新人进来先看规则仓库,对流程的疑问可以提 Issue,而不是私下问东问西;规则本身接受团队的批判,不合理的条目可以被删,而不是一年多没人碰的死文档。

当然,这套方案也不是拿了就能用。它更像一个空壳框架,需要团队按自己的技术栈往里填内容。真正用过一段时间之后,你会发现价值最大的不是那份最终定稿的规范文件,而是团队在讨论规则时达成的共识。这种共识一旦形成,Review 就不再是“审代码”,而是“核对大家都认可的约定”。

2. 打开 open-code-review:它的结构到底怎么搭

2.1 三层结构:规则层、执行层、反馈层

我接触这类方案越多,越觉得一个能存活超过半年的 Review 体系,至少要拆成三层:规则层负责定义“什么是好代码”;执行层负责把规则变成启动 Review 的触发条件;反馈层负责把 Review 中发现的问题回流成规则更新。

规则层是 docs/ 目录下的清单,比如checklist.react.md、checklist.state.md、checklist.a11y.md,每份清单里只有二十条以内的条目,每条都是可检查的动作描述,而不是抽象的“注意代码质量”。执行层是脚本和 CI 配置,比如当一个 PR 触发了 src/ 变更时,自动带上对应规则;当某个目录的代码变更超行数时,自动提醒开发补充自测说明。反馈层是 issue 模板和月度复盘模板,每个月把 Review 中拦截下来的高价值问题整理成案例库。

这套三层结构的好处是,任何一层都不会过度膨胀。规则层不会长成几十页的文档,执行层不会堆积一堆没人用的自动化脚本,反馈层也不会变成无病呻吟的周报。想改哪里就直接动哪里,结构清晰,改起来也不动全局。

2.2 前端专属规则条目:别拿后端模板硬套

后端代码评审常用的规则模板,拿到前端经常水土不服。比如“所有外部输入必须校验”这种规则,在后端是铁律,在前端就要拆成“用户可见输入必须做格式校验”“接口返回的数据必须做类型断言或运行时校验”。同样,前端代码有自己特有的高危区:组件重新渲染的频率、状态管理里是否塞了不可序列化的数据、副作用是否在正确的生命周期清理、样式类名的语义是否清晰。

我在实际搭规则的时候,把它分成了五组:可访问性与语义化、状态管理与数据流、渲染性能与副作用、错误处理与边界情况、组件抽象与可维护性。每组里挑当前阶段团队最容易翻车的条目,宁可少写,不能写好高骛远的废话。比如可访问性这条只盯三个点:所有按钮有 aria-label 或可视文本、所有表单错误提示关联到对应输入框、焦点状态不能被随手取消。这三个点不是最全面的,但却是前端开发里最容易遗漏、用户影响又最大的。

2.3 与现有工具链怎么衔接:CI、IDE、聊天工具

规则定得再好,如果嵌不进日常工作流,迟早被遗忘。open-code-review 一般要求接入三个点:Git 托管平台的 MR 检查接口、开发同学的本地编辑器、团队的通信工具。

CI 侧做的是“硬卡”:比如类型检查没过、构建产物对比异常、覆盖率下降超过阈值,这类问题直接在 Merge 按钮上把入口堵死。IDE 侧做的是“软提示”:把 Review 清单转换成 VS Code 的 snippets 或插件,让开发在写代码时就看到周围有哪些红线。通信工具做的是“仪式感”:MR 被驳回时自动通知,带有模板化的驳回理由;MR 通过之后也自动回执一条简单的总结,慢慢地形成一种正反馈循环。

这里有个经验之谈:CI 硬卡不要一开始就上太多条。规则多了,新人的首个 MR 就是九九八十一难,挫败感太强。我建议第一批只卡三样:TypeScript 严格模式的错误、后端字段类型不一致、明显会扯出线上事故的风险逻辑。其余的问题先放给人来判断,等大家适应了流程,再逐步把高频人工判断的问题交给自动化。

3. 前端 Review 实操:把流程跑起来

3.1 最小的 MR 规范:从提交到合并要过三关

这套方案的日常使用体验,说白了就是每个 MR 走三段路。前 10% 的时间在准备,中 70% 的时间在看代码,后 20% 的时间在处理评论和复盘。前端团队尤其是这样,因为一次改动可能牵涉组件、样式、接口联调多个层面。

第一关是提交前的自检。我不建议用一堆强制钩子,但至少要有npm run lint、npm run type-check、npm run test这三道基础门槛。自检的意义不是保证代码绝对没问题,而是保证 Reviewer 的时间不被低技术含量的错误浪费。想象一下,你兴高采烈打开一个 MR 准备看逻辑,结果第一眼看到 TypeScript 报错,这种体验真的让人瞬间失去看的欲望。

第二关是 MR 描述的质量。 open-code-review 的流程里特别强调描述模板:这次改动解决什么问题、涉及的页面或模块、是否更新接口、是否影响已有用户路径。描述不用写成长篇小说,但要能让人在不开 IDE 的情况下,大致知道这个 MR 的来龙去脉。描述写不清楚的 MR,十有八九改动本身就没想清楚。

第三关才是正式的代码审查。审查者要先快速扫一遍文件变更范围,再看核心逻辑,最后看边缘情况。前端项目里最常见的错误不是主流程写错,而是主流程写对之后,空数据、加载中、错误态、权限不足这几个副状态没覆盖。审查的顺序对了,效率能高一半以上。

3.2 一份可以直接抄作业的前端 Review Checklist

这份清单是我在团队里实际迭代了三个月总结出来的,不一定适合所有项目,但绝大部分 React 或 Vue 技术栈的前端团队可以直接抄:

  • 组件 props 是否做过默认值和必填校验,有没有把仅用于内部的状态暴露给外部
  • 副作用函数的清理逻辑是否正确,防抖/节流/事件监听是否在卸载时取消
  • 状态管理里是否存了非序列化数据,比如 Date 对象、文件句柄、DOM 元素引用
  • 相同的数据是否在两个 store 里各存了一份,导致同步逻辑散落多个文件
  • 列表渲染的 key 是否稳定,有没有用数组下标充当 key
  • 接口请求是否有超时控制和错误分支,loading 状态是否覆盖到所有调用场景
  • 样式是否硬编码了像素值而没有走设计系统变量,深色模式是否有明显断层
  • 可访问性基础项是否落实,图标按钮有无文案或标签,焦点是否可见
  • 是否有不必要的重渲染触发点,比如父组件每次传新对象给子组件且子组件未做 memo

这份清单最大的特点就是“每条都能在五分钟内验证”。如果一条规则要用半小时才能判断对错,它就不适合放在 Checklist 里,更适合放在专项学习里。

3.3 机器判断与人判断的边界划分

前端团队用这套体系最容易踩的坑,是把工具能做的和不能做的混在一起。Lint、类型检查、单测跑完不代表 Review 完成了,这正是机器判断与人判断的边界所在。

机器能有效覆盖的是确定性问题:类型不匹配、构建失败、明显的语法反模式、覆盖率明显下降。机器覆盖不了的是模糊问题:这个组件抽象得合不合理、这个 state 设计会不会在后继需求里变成瓶颈、这次的样式方案改完会不会造成全局影响。这类问题恰恰是前端工程里最值钱的部分,因为它们直接决定了一个项目三个月后牵过来还能不能改得动。

我的建议是,Review 对人和机器的分工要写进规则层。机器通过且无人反对的 MR,允许 10 分钟内直接合并;有人提出模糊问题的 MR,必须至少由另一个熟悉模块的人参与意见。不是每个人都能对每个模块发表有效评论,不要强求。

3.4 一次真实 MR 的完整过会过程

举一个我印象很深的实例:有一次一个同事重构了购物车页面的状态管理,从组件内 state 全量挪到了全局 store。乍看 diff 确实漂亮,逻辑更集中,代码更简单。但我在跑清单的时候发现一个问题:他为了让全局 store 和接口返回结构一致,把后端返回的空数组和真正的用户空车混淆了,导致接口 loading 期间就渲染出了“购物车为空”的空态。

这件事如果在第一遍 Review 里没揪出来,上线后的结果就是用户明明有购物车数据,刷新瞬间会看到一秒钟的“空车”闪烁,严重的会有误判断清空的心理体验。这个 review 的过程用到了清单里的“全局 store 是否依赖接口返回结构”条目,也用了“loading 和空数据是否分离”这条规则。一个问题卡住,我直接给 MR 打了 request changes,理由是:接口返回结构是外部契约,不能被内部状态管理绑定。

从那以后团队对“接口数据必须经过适配层再进入 store”达成了强共识,而这个共识最初就是通过一次 Review 评论建立起来的。Review 的力量从来不只是当下拦住 bug,而是借着一个 bugs 的鲜活案例让团队记住一条规则。

4. 常见问题与排查技巧实录

4.1 时效与质量的矛盾:Review 拖太久,怎么解

Review 最常见的死亡原因不是标准太高,而是太慢。一个 MR 挂两天没人看,开发只能干等,最后大家为了赶进度,一套流程形同虚设。

解决方案是设置 SLA,并且在规则层公开承诺。比如前端团队的约定是:工作日中,8 小时内必须有人 comment 响应,24 小时内必须有明确的 Approve 或 Request Changes。如果超过这个时间没响应,开发可以自行合并,后果自负,同时在周会上复盘这个约定失效的原因。

配合 SLA 的还有“小步快跑”的 MR 拆分策略。把动辄一千行的模块级改动拆成 3-5 个两百行的小 MR,每个小 MR 的上下文减少,审起来自然快。刚推行两周时,大家还是习惯憋大招,根因是有的人觉得拆成多个 MR 协调麻烦,这里需要 Tech Lead 在自己的分支上示范两三次,让别人看到拆分的收益,别用行政命令硬推。

4.2 自动检查出现误报,怎么办

配置了自动化检查之后,最常见的抱怨是“这个插件又在乱报警”。比如前端覆盖了某个 API 字段,但工具不认识业务字典,把正常业务缩略词当作拼写错误;或者某个项目自定义了 ESLint 规则,导致 CI 报错但本地不报。

处理误报的正确姿势不是加 disable 注释一头了之,而是把误报案例沉淀下来。open-code-review 的仓库里专门有一个false-positive.md的文档,里面记录每一次误报的类型、触发条件、处理方式。积累一段时间之后就会发现,大部分误报集中在少数几类模式上,直接改进规则配置,比不断屏蔽检查有效得多。

我自己的经验是:每收到一次误报反馈,第一优先做的事不是修代码,而是确认规则本身是否合理。如果规则对一些合法写法也报警,说明规则本身需要放宽;如果规则是正确的,只是开发没看懂,就把说明文档写清楚,挂在 wiki 里。两种处理方式缺一不可。

4.3 新人能不能快速上手这套流程

新同学入职第一天看 Review 清单,大概率是看完就忘的,这很正常。真正有效的路径是让他先经历一次完整被 Review 的过程,再被要求去 Review 别人的代码。先“被检查”,再“检查他人”,形成闭环。

当新同学第一次作为 Reviewer 时,给他所有规则里的“最强规则”作为兜底选项。比如:如果你判断不了逻辑对不对,至少检查一遍错误处理分支是否齐全。这类规则不需要太多业务上下文,就能发现实质问题。这会给新人建立信心,也会让被 Review 的人觉得“哦,这个新同学还是有水平的”。

这里要注意一个细节:新人的 Review 评论要谨慎给,不要一股脑全抛出来。给两三条最关键的问题就够了,其余问题用线下聊天的方式提。目的是减少评论风暴给新人的压力,让新人维持住“我值得参与 Review”的信心。

4.4 历史代码的包袱太沉,Review 还值得做吗

很多团队说“我们接手的是一个三年前的前端项目,组件滥用、全局样式满天飞,Review 什么呀,先把火灭了再说”。这个说法有一定道理,但正好掉进另一个坑:如果不在新写代码上建立防线,老代码的问题只会继续增殖。

我的建议是,把存量代码和质量防线分开治理。历史代码可以专门列一个“技术债清单”,按影响面排优先级逐步清理;新代码必须走 Review 流程,以此保证垃圾代码只减不增。团队里最忌讳的是“因为老代码差,所以新代码也不需要标准”,这就等于默认项目朝着不可维护的深渊一路狂奔。

5. 值得长期坚持的运营细节

5.1 月度复盘:把零散评论变成团队资产

每个月拿出四十分钟,做一次 Review 复盘。做法是大家把自己印象里最有价值的一次 Review 评论拿出来分享,不用讲代码细节,重点讲这条评论背后的判断思路。往往两个月之后,团队里就能沉淀出一套内部通用的“为什么这么写”的故事集。

这套故事集的力量比任何文档都大。“上次有人因为把后端字段直接复制到 store 导致空态闪烁”已经成为一个代号,它的意义远胜于规范文档里那句“接口数据不能直接进 store”。人在听故事时比看条款学得快,尤其这些故事的主角还坐在同一间办公室。

5.2 规则更新不能一锤子定音

规则要想到会过时。技术栈升级了,团队人员结构变了,再来一个大需求,之前定下的规则可能就不合理。那时候不要让某个人单独背责地改文档,而是借改规则的机会,重新开一次评审共识会。

我在实际操作中总结出一条底线:任何规则的变更都要附带一个案例。没有案例支撑的规则变更,只是在替换喜好,不是解决实际问题。反过来也成立,如果一条规则连续三个月都拦截不到任何问题,就应当考虑删除或改写。Review 是用来拦截现实问题的,不是让你听起来很专业的摆设。

我个人的体会是,这套流程能跑通的核心不是某一个工具,而是团队里每个人都愿意把“我发现了问题”当成一种贡献,而不是一种冒犯。把代码评审从“考核”变成“合作”,事情就成了一半。如果你正准备在团队里启动一次 Review 改革,可以先从一份三页纸的 Checklist 和一条 SLA 开始,跑一个月再谈工具和自动化,别急着一次到位。

RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进