编码规范这件事,越早当回事越好

0 阅读17分钟

做了这些年开发,我越来越觉得编码规范是个被严重低估的东西。

很多团队的态度是"差不多就行"——变量名能看懂就行,缩进统一就行,代码能跑就行。然后等项目做到第三年、团队换了一半人的时候,打开某个核心模块的代码,发现里面同时存在 camelCase、snake_case 和 PascalCase 三种命名风格,有人用 tab 有人用空格,有人一个函数写三百行有人把三行逻辑拆成五个函数。代码审查变成了考古,新人上手变成了解谜。

编码规范解决的不是"代码好不好看"的问题,而是"这个代码库在三年后还能不能被人维护"的问题。Google 的 C++ Style Guide 在开头说得很直白:我们的代码库预计会存在很长时间,花在阅读代码上的时间远多于编写代码的时间,所以我们明确选择为阅读体验优化,而非为编写便利优化。

这句话值得每个技术负责人贴在工位上。

今天这篇文章,我想系统梳理几个我认为最值得参考的编码规范资源,聊聊它们背后的设计哲学,以及怎么把这些东西落地到自己的团队。不是那种"收藏了就等于学了"的资源列表,而是想把每个资源里真正有价值的东西拎出来说说。

Google Style Guides:数万工程师踩出来的路

地址:google.github.io/styleguide/

Google 的编码规范应该是业界影响力最大的一套。不是因为 Google 的光环,而是因为这套规范是被数万名工程师在一个庞大的单体代码仓库(monorepo)里实战验证过的——他们的主代码库有超过 20 亿行代码,几万名工程师在上面同时工作。任何在这种规模下能持续执行的规范,都经过了极其充分的压力测试。

目前 Google Style Guides 覆盖了 17 种语言和格式:

C++、Java、Python、Go、JavaScript、TypeScript、Swift、Objective-C、C#、R、Shell、HTML/CSS、JSON、Markdown、AngularJS、Vim script,还有一个我个人很喜欢的 Common Lisp Style Guide。另外 Dart 的规范(Effective Dart)和 Kotlin 的规范在各自的官方网站上维护,也是 Google 出品。

这个覆盖范围本身就说明了一件事:编码规范不是某一种语言的专属需求,而是每一种你在生产环境中使用的语言都需要的基础设施。

比规则更重要的是规则背后的原则

Google Style Guides 最值得学习的地方,不是具体的规则本身(因为有些规则是 Google 特定上下文下的选择),而是它明确阐述的设计原则。他们在 C++ Style Guide 的开头列出了几条值得每个团队参考的元原则:

一条规则的收益必须大到值得让所有工程师去记住它。 这个原则解释了为什么很多你"觉得应该有"的规则其实没有出现在 Google 的规范里。比如他们没有明确禁止 goto——不是因为 goto 好用,而是因为在现实中几乎没人用 goto,专门写一条规则去禁止一个没人做的事情,就是浪费所有人的认知负荷。好的规范是克制的,只管真正高频出现的问题。

优化读者体验,而非作者便利。 代码写一次,读无数次。任何让写代码更方便但让读代码更费劲的做法,长期来看都是亏的。这个原则最典型的体现是他们的命名规范——Google 的 C++ 命名风格让你从名字本身就能看出这个东西是类、函数、变量还是常量,不需要翻到定义处去确认。写的时候需要多想一秒,读的时候能省很多时间。

一致性是好的平局裁决者,但不是免死金牌。 当两种做法在技术上没有明显优劣时,选一个然后坚持。但一致性不应该被用来为过时的做法辩护——"我们一直都是这样做的"不是拒绝改进的理由。

几个具体的、有争议的选择

Google 的规范里有一些规则在社区里争议很大,但争议本身就很有教育意义——它让你看到"规范设计"这件事涉及的真实权衡。

C++ 禁用异常。 这可能是 Google C++ Style Guide 里最具争议的规则。C++ 异常是一个强大且有用的错误处理机制,禁用它意味着代码里到处都需要手动传递和检查错误返回值。Google 自己也承认"如果从头来过,可能会做不同的选择"。但他们现有的代码库太大了,大量代码不是异常安全的,在这个基础上强行引入异常反而会引发更多问题。这是一个典型的"技术债务决定技术规范"的案例——你的规范不是在真空中设计的,它必须兼容你的历史包袱。

80 列行宽限制。 这个规则可以追溯到 1960 年代大型机的终端宽度。Google 自己也在规范文档里承认"我们知道这个规则有争议",并且列出了正反双方的论点:支持者说这能让大家并排打开多个编辑窗口,反对者说现代宽屏显示器完全可以容纳更长的行。最终 Google 选择坚持 80 列,理由是一致性——太多现有代码已经按这个标准写了,改规则的迁移成本太高。

缩进用 2 个空格。 大多数 C++ 项目用 4 个空格,Google 用 2 个。他们的理由也很实际:在 80 列限制下,2 个空格的缩进能让你在嵌套较深时仍然有足够的空间写代码。这两个选择是互相配合的。

这些具体选择你可以同意也可以不同意,重要的是理解它们背后的决策逻辑。你的团队可能不需要 80 列限制(如果你们没有几十年的历史代码),但你需要一个行宽限制;你的 C++ 项目可能可以用异常(如果你的代码库是异常安全的),但你需要对错误处理有一个统一的策略。

Go Style Guide 值得单独说一句

Google 的 Go Style Guide 在所有语言规范里是比较特殊的。Go 这门语言本身就带有很强的"观点"——gofmt 工具直接硬性统一了代码格式,没得商量。所以 Go Style Guide 讨论的不是"用 tab 还是空格"这种已经被语言级工具解决了的问题,而是更高层次的东西:如何命名、如何组织包结构、如何写注释、如何处理错误。

这给了一个很好的启示:如果你的语言有官方格式化工具(Go 有 gofmt,Rust 有 rustfmt,Python 有 black),那你的团队规范就不需要浪费篇幅在格式问题上,可以把精力放在更有价值的架构和设计层面的约定上。

Airbnb JavaScript Style Guide:前端领域的事实标准

地址:github.com/airbnb/java…

在 JavaScript/前端领域,如果你只看一份编码规范,大多数人会推荐 Airbnb 的。这个项目在 GitHub 上有 148k 星,是 JavaScript 分类下星数最高的仓库之一。

Airbnb 的 JavaScript Style Guide 影响力之所以这么大,有一个很重要的原因:它不只是一份文档,而是配套了一整套可执行的 ESLint 配置(eslint-config-airbnb)。这意味着规范不是靠人肉遵守的——在你保存代码的那一刻,lint 工具就会告诉你哪里不符合规范。甚至很多规则可以自动修复。

这一点太重要了。我见过太多团队花了几个月讨论编码规范,写了一份几十页的文档,然后没人执行。规范如果不能自动化,就是一纸空文。Airbnb 的做法是正确的:规范应该尽可能被工具强制执行,只有工具管不了的部分才靠人工 review。

几个值得关注的具体规则

Airbnb 的规范里有一些规则特别能反映 JavaScript 社区这些年的演进方向:

const 优先于 let,永远不用 var。 这不只是个风格偏好。const 声明意味着这个引用不会被重新赋值,读代码的人看到 const 就知道这个变量在后续代码中不会变,心智负担立刻下降。let 只在确实需要重新赋值时使用。var 因为函数作用域和变量提升的问题,被完全弃用。

箭头函数优先于 function 表达式。 在需要匿名函数的场景下,箭头函数的行为更加可预测(没有自己的 this 绑定),而且更简洁。但 Airbnb 也指出了一个例外:如果你需要具名函数用于调试时的堆栈追踪,普通的函数声明更好。

解构赋值几乎无处不在。 从对象和数组中提取值时,优先使用解构。这让代码的意图更明确——你一眼就能看出这个函数从参数中取了哪几个字段。

模板字符串优先于字符串拼接。 这个规则现在看来已经是常识了,但 Airbnb 是最早把它写进规范的团队之一。

Airbnb 的规范还有一个 React 专项(在仓库的 /react 目录下),覆盖了组件命名、JSX 格式化、Props 定义等 React 特定的约定。如果你的团队在用 React,这份 React 规范几乎是必读的。

为什么是 Airbnb 而不是 Standard

JavaScript 社区还有另一个流行的规范——Standard JS,它的核心卖点是"零配置"和"不用分号"。选 Airbnb 还是 Standard 曾经是前端圈的经典论战。

我个人倾向 Airbnb 的原因是它更"显式"。Standard 追求的是最小化配置和最少的规则,而 Airbnb 追求的是最大化一致性和可读性。在团队协作的场景下,我宁愿规则多一些但每个人写出来的代码看起来更统一,也不愿意规则少一些但大家各写各的。当然这只是个人偏好,两者都是经过大量实战检验的好规范。

Hyperpolyglot:用对比学习掌握多种语言的语法规则

地址:hyperpolyglot.org

这个网站不是编码规范,但我觉得它对理解和制定规范非常有帮助,所以一定要提。

Hyperpolyglot 做的事情很简单:把多种编程语言的语法规则放在一个并排对比的表格里。比如"脚本语言"那一栏,它会把 Node.js、PHP、Python、Ruby 的变量声明、控制流、函数定义、字符串处理等语法点逐一对比。

它的分类组织是有讲究的:

  • 脚本语言:Node.js / PHP / Python / Ruby
  • C 族面向对象语言:C++ / Objective-C / Java / C#
  • 系统编程:Rust / Swift / Scala
  • Lisp 方言:Common Lisp / Racket / Clojure / Emacs Lisp
  • 函数式语言:SML / OCaml / F# / Haskell
  • Shell:Bash / Fish / Ksh / Tcsh / Zsh
  • 数据库:PostgreSQL / MySQL / SQLite

每个分类下都是同族语言的横向对比。

这种对比学习的价值在于:当你从一种语言切换到另一种语言时,你最需要知道的是"这件事在新语言里怎么表达",而不是从头学起。Hyperpolyglot 正好满足这个需求。

同样的,这种对比视角对设计编码规范也有帮助。当你为一个多语言技术栈制定团队规范时,你会发现某些约定是跨语言通用的(比如命名的一致性原则、注释的写法、文件组织的逻辑),而某些约定是语言特有的(比如 Python 的缩进是语法的一部分,Go 的格式由 gofmt 决定)。Hyperpolyglot 帮你快速看清楚这种区别。

对于需要频繁在多种语言之间切换的全栈工程师或架构师来说,Hyperpolyglot 几乎是日常工具级别的存在。

其他值得参考的编码规范

上面三个是我认为最有代表性的,但编码规范这个领域还有很多好资源。列几个我觉得有独特价值的:

Linux 内核编码规范(Linux Kernel Coding Style) ——如果你写 C 或者对系统编程感兴趣,这份规范值得读。Linus Torvalds 的风格非常鲜明:8 个空格缩进(没写错,是 8 个),函数不能超过一屏,if 嵌套超过三层就说明你的逻辑需要重构。他的理由是,如果你需要超过三层嵌套,你的代码就是有问题的,加宽缩进是为了让深嵌套看起来丑到你受不了,从而逼你重构。这个设计思路非常有意思——用格式上的不适感来驱动架构改进。

PEP 8(Python) ——Python 社区的官方编码规范,由 Guido van Rossum 参与制定。跟 Google 的 Python Style Guide 有不少重叠,但也有一些差异。Python 社区有一句名言来自 PEP 20(The Zen of Python):"There should be one-- and preferably only one --obvious way to do it."(做一件事应该有且最好只有一个显而易见的方式。)整个 PEP 8 都在贯彻这个哲学。

Effective Go——Go 官方的编码指南。因为 Go 本身的设计哲学就是"简洁和统一",Effective Go 读起来更像是语言设计者在告诉你"我们设计这门语言的时候就是希望你这样写"。

Microsoft C# Coding Conventions——如果你在 .NET 生态工作,微软官方的 C# 规范是首选参考。它和 Google 的 C# Style Guide 有一些差异(比如大括号的位置),两份对照来看可以帮你理解不同风格选择的权衡。

Alibaba Java 开发手册——国内 Java 领域最有影响力的编码规范之一。它的特点是非常贴合国内大厂的实际业务场景,包含了很多其他规范不会覆盖的实用约定,比如数据库命名规范、异常处理层次、日志规范等。如果你在国内做 Java 后端,这份手册几乎是必读。

怎么在自己的团队里落地

说完了"看什么",最后聊聊"怎么做"。我见过很多团队在编码规范上栽跟头,基本上栽在三个地方。

第一个坑:规范太厚,没人看

有些团队会花几个月时间写一份百页的编码规范文档,覆盖从命名到注释到异常处理到数据库到日志的方方面面。然后这份文档被放进内部 Wiki,再也没人打开过。

务实的做法是:先从最小可用规范开始。 团队里最容易引发争论的是什么?是缩进风格?命名约定?还是代码组织结构?先把前两三个最高频的争议点统一了,形成一份一页纸能写完的基础规范,然后用工具强制执行。其余的以后再加。

Google Style Guide 自己说了一个原则我很认同:一条规则的收益必须大到值得让所有工程师记住它。用这个标准筛一遍,你会发现大多数规则其实不需要在初期就定义。

第二个坑:规范不能自动执行

如果你的规范只写在文档里,靠 Code Review 的时候人肉检查,那注定会执行不下去。Code Review 应该花在讨论逻辑和架构上,不应该浪费在"这里少了个空格"上。

正确的做法是最大化利用工具链:

  • 格式化:Prettier(JS/TS)、gofmt(Go)、black(Python)、clang-format(C/C++)——这些工具解决"代码长什么样"的问题,保存时自动运行,完全不需要人操心
  • Lint:ESLint(JS/TS)、pylint/ruff(Python)、golangci-lint(Go)、checkstyle(Java)——这些工具解决"代码有没有坏味道"的问题,在 CI 里强制检查
  • Pre-commit hooks:在 git commit 之前自动运行格式化和 lint,不符合规范的代码根本提交不上去

工具能覆盖的部分,就让工具管。工具管不了的部分(比如函数职责是否单一、架构是否合理、命名是否准确表达了业务含义),才需要人在 Code Review 中判断。

第三个坑:规范不演进

技术在变,团队在变,规范也应该变。五年前写的 JavaScript 规范可能还在用 var 和 callback,放到今天就完全过时了。

Google 在他们的规范里也提到了这一点:一致性不应该被用来为过时的做法辩护。当有明确的技术理由支持新做法时,规范应该更新,即使这意味着和现有代码不一致。

实操建议是建立一个定期的规范审查机制——比如每半年 review 一次,看看有没有需要更新的规则、有没有新的工具可以替代人工检查、有没有团队成员一直在吐槽某条规则不合理。规范是活的,不是一锤子买卖。

在 AI 编程时代,编码规范反而更重要了

最后聊一个很多人可能没想到的点。

2025-2026 年,越来越多的代码是由 AI 辅助生成的。GitHub Copilot、Cursor、Claude Code 这些工具已经深入到日常开发流程中。有人觉得既然 AI 在写代码,编码规范就不重要了。我的观点恰恰相反——AI 编程时代,编码规范反而更重要了。

原因很简单:AI 生成的代码风格可能每次都不一样。如果你没有一个明确的规范来约束,AI 这次用 camelCase、下次用 snake_case,这次用 async/await、下次用 callback,代码库的一致性会迅速崩坏。

Cursor 的 Rules 系统就是一个很好的例子。你可以在项目根目录定义 .cursor/rules/ 目录,里面放上你的编码规范文件,Cursor 的 AI 在生成代码时会遵守这些规则。这正是编码规范的新用途——它不仅是给人看的,还是给 AI 看的。你的编码规范越清晰、越结构化,AI 生成的代码就越一致、越可维护。

同样的逻辑适用于所有 AI 编程工具。当你使用 Claude Code 时,它支持 CLAUDE.md 文件来定义项目级的编码约定。这些工具都在朝同一个方向演进:编码规范作为人和 AI 之间的共享契约。

所以,如果你的团队还没有一份正式的编码规范,现在开始建是一个好时机——不仅是为了人,也是为了即将(或已经)接管一部分编码工作的 AI。

资源汇总

最后把文章中提到的资源整理一下,方便收藏:

核心推荐

其他值得参考的规范

自动化工具

Git commit 规范(Google Style Guides 也推荐了这个)

各家公司完全可以在这些公开规范的基础上,结合自身业务场景和技术栈,形成自己的编码规范。不需要从零开始造轮子——站在巨人肩膀上,然后做适合自己团队的裁剪和增补。

编码规范这件事,最好的开始时间是项目第一天,其次好的开始时间是现在。