本节摘要:代码格式化通过统一的缩进、空格、空行、换行与括号规则让代码视觉上一致、结构上一目了然。本节逐项给出这些"圣战议题"的务实推荐(空格缩进、80 到 120 字符行长、括号风格全项目统一),并给出本节最重要的结论:格式决策应当全部交给自动化格式化工具,人的职责是选好工具、提交配置、不再手调格式。
阅读完本节,你应当能够:
两个工程师合作开发同一模块。A 的编辑器把 Tab 显示为四格,B 的显示为八格;A 习惯 Tab 与空格混用。某次合并时,diff 工具显示两百行"变更",但逐行看过去几乎一模一样——那两百行全是 Tab 与空格的不可见差异。更糟的是,真正的逻辑改动被淹没在这些噪声里,一次本可避免的回归缺陷就这样溜进了主干。
这是格式不一致最实际的伤害:它污染 diff。代码评审依赖 diff 展示"真正变了什么",格式抖动让每一行都像是改过,评审人很快视觉疲劳,逻辑问题反而漏检。除此之外,格式不一致还有第二重成本:每次打开风格迥异的文件,读者都要先"校准"一次阅读习惯,这个隐形税费按文件数征收。
所以格式化规范的目标只有一个:让格式差异从代码库里彻底消失,让 diff 只反映逻辑变更。达成这个目标最省力的方式不是说服每个人手写统一格式,而是让机器接管全部格式决策。
缩进表达代码块的嵌套层级,是格式化的地基。规则有两条。
用空格,不用 Tab。理由:Tab 的宽度是阅读器本地的设置,同一份代码在不同人的屏幕上呈现不同缩进甚至错位;空格在任何环境显示一致。Tab 节省的字节数在现代存储面前可以忽略。
统一空格数:通常两格或四格。两格在前端社区常见,省横向空间;四格在 Python、Java 等后端语言主流,层级视觉感更强。选哪个不重要,全项目一个数很重要。
一组没有争议余地的通则:二元运算符两侧加空格(a = b + c 而非 a=b+c);逗号分号后加空格(f(x, y, z));括号内侧不加空格(f(a) 而非 f( a ));函数名与括号之间不加空格(doSomething() 而非 doSomething ());控制流关键字后加一个空格(if (ok) 而非 if(ok))。这些规则的作用是让 token 之间有视觉分界,眼睛可以按块切分。
空行是代码的"段落标记"。函数定义之间空一到两行(Python 社区约定顶层定义空两行、类内方法空一行);函数内部用空行分隔逻辑段——变量声明、校验、主逻辑、收尾各成一段。类内成员变量与方法之间空一行。反面是走极端:连续五个空行或通篇无空行,都破坏可扫读性。
行长限制通常在 80 到 120 字符。这个数字源于分屏对比代码与评审界面没有横向滚动的需求。超长行在逻辑断点换行:运算符前换行(运算符留在下一行行首,让读者知道这行没完)、逗号后换行、函数参数每行一个或每组一行、链式调用每环一行。对照示例(概念代码):
# 超长单行 横向滚动才能看完 result = some_very_long_function_call(param1, param2, param3, param4, param5, param6, param7) # 在逗号后换行 缩进对齐 result = some_very_long_function_call( param1, param2, param3, param4, param5, param6, param7, )
两大门派:K&R 风格左括号与语句同行(if (ok) {),紧凑省纵向空间,Java、JavaScript 等语言主流;Allman 风格左括号独占一行,块边界上下对齐,C# 社区常见。两派各有道理,谁也说服不了谁——所以这个决策的正确形态不是"谁对",而是"全项目统一选一个"。一致带来的可读性收益,远大于任何一派的局部优势。
# K&R 风格 if (condition) { doWork(); } # Allman 风格 if (condition) { doWork(); }
容易被忽略的格式项:导入按"标准库、第三方库、项目内部"三组分组,组间空行,组内按字母排序。这样导入区可预测、diff 稳定,也容易一眼看出依赖里有没有混进奇怪的东西。

各语言生态都有成熟工具:前端与脚本语言的 Prettier(配置极少、强制统一)、Python 的 Black(风格强硬、几乎没有选项)与 autopep8(较宽松)、Go 官方内置的 gofmt(零配置、全社区一致)、Java 的 Google Java Format、C 与 C++ 系的 clang-format(高度可定制)、Rust 的 rustfmt。共同特点:一次配置、处处执行、没有商量余地——这正是格式化工具最宝贵的品质。
配置入库:格式化配置文件提交进版本库,全团队共享同一份,杜绝"我本地格式不一样"。保存即格式化:编辑器设为保存时自动运行格式化,把合规成本降到零。何时引入都不晚,越早越好:新项目第一天就配好;存量项目选一个低风险时点做一次全量格式化提交(单独一个只含格式变更的提交,便于评审时跳过),之后差异只增不减。
⚠️ 常见坑:全量格式化与功能改动混在同一个提交里。格式化提交必须独立成 commit,且信息写明"仅格式化无逻辑变更"——否则未来用 blame 追溯逻辑变更历史时,每行都被格式化污染。
💡 关键直觉:格式化领域的所有争论(Tab 还是空格、括号放哪)的正确结局都是"机器替我们决定"。工程师的时间应该花在命名与结构这些机器管不了的事情上,第 2.2 节与第 3 章才是值得动脑的地方。
| 维度 | 推荐规则 | 执行方式 |
|---|---|---|
| 缩进 | 空格 两格或四格全项目统一 | 格式化工具 |
| 空格 | 运算符两侧 逗号后 括号内不加 | 格式化工具 |
| 空行 | 函数间一到两行 逻辑段间一行 | 格式化工具加约定 |
| 行长 | 80 到 120 字符 断点换行 | 格式化工具配置 |
| 括号 | K 或 R 与 Allman 二选一统一 | 格式化工具配置 |
| 导入 | 三组分隔 组内字母序 | 工具自动排序 |
下一章从"怎么排"进入"怎么讲"与"怎么装":注释的写法与代码的组织结构。