4. 编码规范与代码风格最佳实践

编码规范这事儿,说白了就是团队里的“交通规则”。你想想看,如果每个人开车都随性变道、不打转向灯,这条马路还走得通吗?我见过不少项目,前期跑得快,后期改个Bug要花三天——大部分时间都在猜上一任写这行代码时脑子在想什么。所以今天咱们聊聊那些真正让我摔过跟头、后来不得不遵守的规范。

4.1 命名规范:名字就是最好的注释

我个人的第一原则:变量、函数、类名必须一眼能看出它是什么、干什么、从哪来到哪去。别怕名字长,怕的是别人看不懂。

元素 推荐风格 示例 反面教材
变量 小驼峰,名词 userCount uc / temp
函数 小驼峰,动词/动宾 fetchUserData() doStuff()
类 大驼峰,名词 OrderService svc / myclass
常量 大写+下划线 MAX_RETRY_COUNT maxRetry
布尔变量 is/has/can 开头 isReady flag
我的小习惯:写函数名时先写动词,比如 createUser、validateEmail。如果你发现一个函数很难用“动词+名词”描述清楚,那很可能它做了太多事——该拆了。

4.2 缩进与格式:机器不挑,但人挑

我记得刚入行时,有一次修一个线上Bug,打开同事的代码文件……整页都是缩进混乱的嵌套块。我花了一整个下午手动对齐括号。后来团队一致同意:统一缩进风格,并且用工具自动格式化。

  • 缩进:要么 2 空格,要么 4 空格,统一就行。我个人偏好 2 空格,列数多了不换行看得也顺眼。
  • 每行最大长度:80~120 字符。超出就换行,否则横着滚动条看代码真的很痛苦。
  • 花括号:各语言约定不同。Java 用 K&R(左括号在行尾),C# 用 Allman(左括号换行)。选一种,项目里统一。
// 不好的例子
if(condition){
    doSomething();
        doAnother();
}

// 好的例子(K&R 风格)
if (condition) {
  doSomething();
  doAnother();
}

// 好的例子(Allman 风格)
if (condition)
{
  doSomething();
  doAnother();
}
我曾经踩过的坑:团队里有两个人用的编辑器缩进设置不一样,一个用Tab一个用空格。结果Git diff里每次都有几百行“看起来没变”的缩进改动。从那以后,我强制项目配置 .editorconfig 并在 CI 上跑格式化检查。

4.3 注释规范:写“为什么”,而不是“是什么”

很多人都犯过这个错——对着显而易见的代码写注释。比如:

// 增加 1
i++;

这注释有用吗?当然没有。注释应该回答“为什么这么写”,而不是“这句代码干了什么”。如果代码本身已经足够清晰(命名好、逻辑直白),注释甚至可以省略。

  • 必须写注释的场景:复杂算法、业务规则、临时修复(TODO)、边界条件。
  • 最佳实践:写注释前先想一想,能不能通过重构代码让它自解释?
// 不好的注释
// 检查用户是否有权限
if (user.role === 'admin') { ... }

// 好的注释
// 只有管理员才能执行批量删除操作——产品需求 v2.3 指定
if (user.role === 'admin') { ... }

核心原则:注释是用来解释“为什么走这条路”的,不是用来给代码配旁白的。

4.4 函数与类设计:小而专,单一职责

我见过一个函数,有 400 行,参数 8 个,里面同时做了数据校验、统计计算、日志记录、发邮件通知。后来要改一个校验逻辑……你猜动了多少地方?

我的建议很简单:一个函数只做一件事,做好它。如果一个函数超过 20~30 行,大概率需要拆。类的单一职责同理——一个类只负责一个维度的功能。

// 不好的:一个函数干三件事
function handleOrder(order) {
  // 校验
  if (!order.items || order.items.length === 0) throw new Error('订单为空');
  // 计算
  let total = 0;
  order.items.forEach(item => total += item.price);
  // 保存
  saveOrder({ ...order, total });
}

// 好的:拆成三个函数
function validateOrder(order) { ... }
function calculateTotal(order) { ... }
function saveOrder(order) { ... }

4.5 错误处理:不要吃掉异常,要明确传递

“吞异常”是我最讨厌的编码习惯之一。你看看这个:

try {
  fetchData();
} catch (e) {
  // 啥也不干
}

程序出了问题,但没人知道。等用户反馈,再去翻日志……太被动了。

好的做法:

  • 捕获你预期能恢复的异常,做降级或重试。
  • 不可恢复的异常,要么向上抛出,要么记录日志 + 返回用户可以理解的错误信息。
  • 不要捕获 Exception 的基类,除非在最外层做全局兜底。
// 推荐:明确处理
try {
  await fetchData();
} catch (e) {
  if (e instanceof NetworkError) {
    retry();
  } else {
    logger.error('拉取数据失败', e);
    throw e; // 或者返回合适的错误对象
  }
}

4.6 代码复用:DRY 原则,但不要过度抽象

“不要重复自己”是常识,但我也经历过“为复用而复用”的灾难。有人把两个只有一行不同的函数强行合并成一个,传了一堆标志位。结果每次改一个逻辑都要担心影响另一个。

复用之前先问自己:这几段代码未来真的会一起变化吗?如果是,抽成公共函数;如果不是,保持独立反而更清晰。

我的经验:先容忍少量的重复,等重复出现第三次时再考虑抽象。大多数情况下,等到第三次你才真正理解应该怎么抽象。

4.7 版本控制规范:提交信息写清楚

这个可能和代码风格没直接关系,但实际影响极大。我看过太多“fix bug”、“update”、“aaa”这样的提交信息。到了回溯问题时,你根本不知道这次提交改了啥。

推荐使用 Conventional Commits 格式:

feat: 添加用户注册功能
fix: 修复登录时  过期未处理的问题
refactor: 将订单校验逻辑抽成单独模块
chore: 升级 lodash 版本

每个提交只包含一个逻辑变更。不要一次提交改 20 个文件,中间有 5 个文件只是格式调整——那样太乱了。

4.8 整个知识体系一览

上面这些规范,其实可以归纳成一个能力图谱。下面我画了张图,把核心维度串起来:

编码规范与代码风格实践体系 编码规范实践 命名规范 格式与缩进 注释规范 函数/类设计 错误处理 代码复用 (DRY) 提交规范 每个维度可独立 用工具自动检查

这张图帮你把规范分成了 7 个可独立实践的方向。你不需要一次性全改完——从最容易见效的“命名”和“格式”开始,用自动化工具(ESLint、Prettier、Checkstyle)卡住,剩下的逐步推进。

4.9 总结一下

写规范不是为了束缚你,是为了让团队里每个人都少踩坑。我这些年最大的感悟就是:规范越细致,后期维护成本越低。下次你改别人代码时觉得“这谁写的啊”——那就是规范没到位。

几句话记在心头:

  • 名字要能读出来,且一眼知道意思。
  • 用工具统一格式,别在代码评审时扯皮缩进。
  • 注释只写“为什么”,不写“是什么”。
  • 函数做一件事,类负责一个职责。
  • 异常要么处理,要么向上抛,别吞掉。
  • 复用要等三次重复再抽象——过早抽象是万恶之源。

好了,这一章就聊到这儿。这些原则你随便查,都是业界通行的。但怎么落地、怎么说服团队、怎么配置工具链——这才是实战。后面我会继续讲具体怎么做。今天就先消化这些基础规范吧。