Coding Convention
解决什么问题:让 50 个人写出像 1 个人写的代码——可读性优先于个人偏好,约定替代争议。
它是什么
Coding Convention(编码约定) 是团队对代码外观的共同约束——命名、格式、文件组织、注释风格。约定不改变程序行为(行为由逻辑决定),但直接决定团队协作成本:
- 命名一致 → 不用每次猜"这个变量是啥"
- 格式一致 → 代码评审只看逻辑,不争论空格
- 组织一致 → 任何人都能快速定位文件
SE@G Ch 3:"Style rules... make code easier to read and reduce friction in reviews."
V2 视角(V2.7 B4 抽象):约定是"从代码具体形态抽取的命名规律"——把团队每天的命名决策从"个人偏好" 提升到"统一规律",等价于控制论把物理变量抽取为状态方程的标准形式。
关键属性
| 属性 | 值/含义 |
|---|---|
| 覆盖范围 | 命名 / 格式 / 文件结构 / 注释 / 错误处理 |
| 强制方式 | linter(自动) + code review(人工) |
| 优先级 | 一致性 > 个人偏好("这里是团队代码") |
| 反模式 | 一边倒的细节强迫症(行宽 80 vs 120 没本质区别) |
| 核心约束 | 工具强制 > 文档描述 |
| 判错标准 | 评审争论"风格"超过 5 分钟 = 约定失效 |
| 演化 | 约定随团队规模演化(小团队宽松、大团队严格) |
怎么用
场景 1:建立约定(项目启动)
- 起步用社区主流约定(Google Style / PEP 8 / Effective Go)——不要从零造
- 落到 linter 配置(ESLint / Checkstyle / golint)——机器执行 > 文档
- CI 强制——lint 不通过 = build failed
- 少数例外允许——在文件头明确
# noqa: E501之类标注
场景 2:老项目补救
问题:老项目没约定,命名混乱(user/User/USER 混用)
- 盘点现状——选出现最常用的 1-2 种命名
- 用 linter 逐步收敛——开启 rule,逐步 fix
- 不要"全改"——V2 B5 视角:代码评审时按现有约定对齐 = SE = 编程 × 时间
- 记录到 CONTRIBUTING.md——新成员 onboarding 看一份文档即可
场景 3:约定冲突
问题:A 喜欢 getUser(),B 喜欢 fetchUser()
处理:
- 不投票——A 风格不损害 B,B 风格不损害 A
- 选多数——全团队历史代码哪种多就用哪种
- 写进 linter——下次再有分歧,工具裁定
常见误区
- "约定越细越好"——错。约定是降低争议,不是制造争论。命名/格式核心 20 条足够
- "约定 = 文档"——错。文档没人看,约定 =
eslintrc.json。没工具强制 = 没约定 - "约定可以个性化"——错。SE@G Ch 3:"consistency over preference"。团队代码不是个人作品
- "等出问题再补约定"——错。新人 onboarding 第一天就会问,约定缺失 = 团队损耗
- "约定 = 限制创造力"——错。约定管外观,逻辑自由发挥。外观一致才看得出逻辑差异
关键洞察
约定是 SE@G 的"工程纪律基础"——它让团队从"个人英雄" 变成"可复制生产力"。
控制论视角:约定 = 状态变量的标准形式。没有标准形式,控制器(团队协作)无法工作。 IDDD Ch 9 视角:约定 = 模块的命名语言。一个项目的模块命名风格统一 = Ubiquitous Language 在代码层的固化。 EDM 视角:约定 = 事件 schema 的命名规律。Producer / Consumer 用同一套命名 = 减少集成摩擦。
相关链接
- Package Structure——约定的"载体"
- Code Review Purpose——约定的执行机制
- [V3 涌现作为] · 抽象——约定 = 从代码抽取命名规律
- [V3 涌现作为] · 权衡——约定细粒度的取舍
- 源典:SE@G Ch 3——Style Guides
- 源典:IDDD Ch 9——Modules + 命名语言