上家公司有个后端同事,因为 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,老代码原地不动,避免历史提交全部错位。
第二步,锁死工具版本。prettier、eslint 版本统一,配置只放在一个共享包里,不要每个人自己搭一套。
第三步,把检查挂到 pre-commit 和 CI 上,比如 npx lint-staged 加 eslint --fix,再配合 CI 的 prettier --check。一旦工具能够拦截,就不要在 code review 里浪费时间说格式问题。
第四步,定几个人工规则就够了。我当时在 code review 模板里只放了一句话:请说明你的异常路径和异步边界。就这一句,比整齐划一的格式更能减少线上事故。
说回那次争吵。后来那个同事去了另一家公司,临走前跟我说,他讨厌的不是加花括号,而是我拿出一套规则就想让别人闭嘴的态度。 我承认他说得对。代码规范真正的意义,不是让某个人的审美统治整个世界,而是把那些不值得反复讨论的东西,提前变成机器检查。 剩下的时间,你可以用来争论更有意思的问题,比如这个模块该不该拆。 如果你最近也因为 if 要不要加括号的事情跟同事闹得不愉快,记得把这篇文章转给他,然后顺手把 pre-commit 钩子修好。就这么简单。