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();
}
.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 整个知识体系一览
上面这些规范,其实可以归纳成一个能力图谱。下面我画了张图,把核心维度串起来:
这张图帮你把规范分成了 7 个可独立实践的方向。你不需要一次性全改完——从最容易见效的“命名”和“格式”开始,用自动化工具(ESLint、Prettier、Checkstyle)卡住,剩下的逐步推进。
4.9 总结一下
写规范不是为了束缚你,是为了让团队里每个人都少踩坑。我这些年最大的感悟就是:规范越细致,后期维护成本越低。下次你改别人代码时觉得“这谁写的啊”——那就是规范没到位。
几句话记在心头:
- 名字要能读出来,且一眼知道意思。
- 用工具统一格式,别在代码评审时扯皮缩进。
- 注释只写“为什么”,不写“是什么”。
- 函数做一件事,类负责一个职责。
- 异常要么处理,要么向上抛,别吞掉。
- 复用要等三次重复再抽象——过早抽象是万恶之源。
好了,这一章就聊到这儿。这些原则你随便查,都是业界通行的。但怎么落地、怎么说服团队、怎么配置工具链——这才是实战。后面我会继续讲具体怎么做。今天就先消化这些基础规范吧。