如何制定代码规范?我把20页文档精简成3条硬规则,团队再也没吵过架

🔑 关键词:代码规范,代码评审,代码格式化,pre-commit,团队协作

📖 摘要:从一场 if 括号之争说起,聊聊代码规范的本质:不要靠人记规则,而要把它交给 formatter 和 CI。涵盖硬规则示例、格式化工具选择以及实际操作步骤。

上家公司有个后端同事,因为 if 后面要不要加花括号的问题,差点跟我吵起来。他写的是 if (user == null) return;,我让他改成带 block 的样子。 他回我:不是有 prettier 吗?格式化一下是不是一样?我气得不行。 但晚上回家想想,发现我俩争的根本不是代码,而是面子。 他后来在 IDE 里装了个保存时自动格式化的插件,把整个文件缩进换成自己的 tab,凡是他动过的文件,git diff 里一半是空白变化。 这种感觉就像逛超市,出来发现购物车里被塞了别人挑剩的打折菜,不贵,但恶心。

图片

后来换到一家做门店系统的公司,接手一个老项目,那才是真正的文明博物馆。同一个文件里,有人用 4 空格缩进,有人用 2 空格,还有人直接 tab;变量命名一会儿 user_name,一会儿 userName。 更绝的是,有一段代码本来没问题,上一个人为了对齐注释,按了一下格式化,结果把另一个人的分支逻辑搞乱了,线上出过事故。 我一边修 bug 一边想,代码规范到底是给谁看的?后来想通了,规范不是让你变得更有品位,它是用来降低别人阅读代码时的认知负荷。 你以为自己是在写代码,其实是在维护团队所有人的短期记忆。

图片

后来我也观察了几种语言社区,发现一个挺有意思的规律:关于代码风格的吵架严重程度,基本取决于该语言有没有一个“霸道”的默认 formatter。 Go 有 gofmt,Rust 有 rustfmt,所以很少有人因为缩进问题互相拉黑。 Python 社区以前天天谈 PEP8,等 Black 出来后,吵声响了一半。前端更明显,Prettier 的默认值甚至不支持自定义,就是为了防止你把时间花在双引号还是单引号上。 所以我的看法可能有点极端:如果一条规范需要靠人记,那它根本不是好规范。真正有效的规范,要么能自动格式化,要么能被 linter 自动检查,否则就是摆设。

图片

我自己后来在一支小团队里做前端基础设施,写过一个不到 200 行的 ESLint flat config,没有开一大堆 recommended,只留了三条真正会炸的规则:

  • 不可以在 finally 里写 return,它会吞掉 try 块里抛出的异常;
  • 不可以在 await 一个可能 reject 的 Promise 对象时不写 catch,除非你明确处理过拒绝分支;
  • 不可以在 forEach 回调里直接 await 后以为它是并发执行的。

图片

这些规则不关心你用单引号还是双引号,也不管缩进是两格还是四格,因为那些已经让 Prettier 接管了。真正值得人工 review 的,只有逻辑边界、异步流程、异常追踪、数据结构不可变这一类事。

图片

如果你现在也在推代码规范,别急着写文档,更别把 wiki 当成罚单本。可以先做这几步: 第一步,统计一下现有代码库跑格式化之后会影响多少个文件。如果影响范围太大,就只对新代码开 prettier --check,老代码原地不动,避免历史提交全部错位。 第二步,锁死工具版本。prettiereslint 版本统一,配置只放在一个共享包里,不要每个人自己搭一套。 第三步,把检查挂到 pre-commit 和 CI 上,比如 npx lint-stagedeslint --fix,再配合 CI 的 prettier --check。一旦工具能够拦截,就不要在 code review 里浪费时间说格式问题。 第四步,定几个人工规则就够了。我当时在 code review 模板里只放了一句话:请说明你的异常路径和异步边界。就这一句,比整齐划一的格式更能减少线上事故。

图片

说回那次争吵。后来那个同事去了另一家公司,临走前跟我说,他讨厌的不是加花括号,而是我拿出一套规则就想让别人闭嘴的态度。 我承认他说得对。代码规范真正的意义,不是让某个人的审美统治整个世界,而是把那些不值得反复讨论的东西,提前变成机器检查。 剩下的时间,你可以用来争论更有意思的问题,比如这个模块该不该拆。 如果你最近也因为 if 要不要加括号的事情跟同事闹得不愉快,记得把这篇文章转给他,然后顺手把 pre-commit 钩子修好。就这么简单。

🏷️ 标签: