CODE STYLE · 代码质量 / 规范先行

代码规范速查

命名、注释、函数、重构、提交与 Review 六张表,把「代码怎么写才体面」讲清楚,条条可对照执行,适合贴在手边随查随用。

69条规范 11大主题 持续更新

📖 速查表

点击展开各小节

🏷️ 命名规范
场景规范示例
Java 类 / 接口 大驼峰 UpperCamelCase,用名词,见名知义 UserServiceOrderController
Java 方法 / 变量 小驼峰 lowerCamelCase;方法用动词或动词短语开头 getUserById()orderCount
Java 常量 全大写 + 下划线分隔,集中定义,禁止散落的魔法值 MAX_RETRY_COUNTDEFAULT_TIMEOUT
包名 全小写,倒置域名开头,层级清晰,不使用下划线与大写 com.codechuan.service.impl
JS / TS 变量与函数 camelCase;类、组件、类型用 PascalCase;私有约定 _ 前缀 fetchUserDataLoginForm
CSS 类名 kebab-case 中划线小写,避免下划线与驼峰混用 .nav-bar.card-header
数据库表 / 字段 蛇形命名 snake_case,表名单复数风格全库统一,时间字段带后缀 _at user_ordercreated_at
布尔命名 用 isXxx / hasXxx / canXxx 前缀,保持正向语义,避免双重否定 isEnabledhasPermission
isNotError ✗ 难以理解
禁用项 拼音与英文混用、无意义缩写、魔法值直接出现在逻辑里 getUserDataByYh
if (status == 3) ✗ 应定义 ORDER_PAID = 3 红线
💬 注释规范
主题说明示例
Javadoc 要素 公共类与方法注明用途、参数、返回值、抛出异常;对外契约必须完整 @param@return@throws 公共 API 必写
解释为什么 注释的价值在动机与背景,而非复述代码在做什么 「延迟 200ms 等待下游最终一致」✓
「睡 200 毫秒」✗ 无信息量
该写注释 复杂算法、魔法值含义、绕坑说明(兼容性、踩坑记录)、公开接口契约 「JDK 8 的 Arrays.asList 返回定长列表,勿增删」
不该写注释 废话注释、与代码脱节的过期注释、被注释掉的死代码 // 获取用户 + getUser() 应删
死代码交给 Git 历史 可检索
TODO / FIXME 统一格式便于 IDE 全局检索与清理;TODO 表示待实现,FIXME 表示待修复缺陷 // TODO(zhang): 接入网关限流
及时同步 代码改动必须同步更新注释,过期注释比没有注释更有害 Review 时注释与实现不一致应打回 一致性
🧩 函数设计
原则说明示例
单一职责 一个函数只做一件事,能用一句不带「与 / 或」的话描述清楚 sendEmail() 不要顺便写审计日志
函数长度 一屏可读完,经验值不超过 20 行(不含空行注释);超长先按逻辑块提取子函数 80 行函数 → 拆为「校验 / 计算 / 落库」三段 20 行经验值
参数个数 不超过 3 个;过多用对象封装或 Builder,同类型连续参数尤其易传错 createUser(name, age, email, phone)
createUser(UserCreateCmd cmd)
避免布尔参数 boolean 参数在调用处不可读,拆成两个语义明确的函数 render(true)
renderHtml() / renderJson()
卫语句提前返回 先处理空值与边界并尽早 return,消除多层嵌套 if (user == null) return;
优于 if-else 层层包裹 扁平化
返回值一致 同一函数不要既返回对象又返回 null;集合返回空集合,单个对象可用 Optional 无数据返回 Collections.emptyList() 而非 null
无副作用 查询类函数不偷偷改状态、不发请求、不写库,副作用显式命名 getOrder() 内部不应更新库存 隐蔽 Bug 源
🚩 重构信号(坏味道)
坏味道特征重构手法
长函数 超一屏、靠注释分隔的多段逻辑堆在同一个方法里 提取方法(Extract Method)
重复代码 相似逻辑散落多处,改一处漏三处 提取公共函数 / 父类 / 组件 DRY
过深嵌套 if / for 超过 2~3 层,可读性骤降 卫语句提前返回、continue、提取方法
霰弹式修改 一个小需求要同时改 N 个文件 聚合内聚逻辑,按业务域重新划分模块
依恋情结 函数大量访问别的类的数据,本地数据反而不关心 搬移函数(Move Method)到数据所在类
发散式变化 一个类因多种不同原因被反复修改,职责过多 按单一职责拆分类 SRP
📝 提交规范(Conventional Commits)
类型 / 原则说明示例
统一格式 <type>(<scope>): <subject>;subject 用祈使句、一行说清、结尾不加句号 feat(order): 支持优惠券叠加
feat / fix 新功能 / 缺陷修复,最常用两类;破坏性变更需加 ! 或脚注 feat: add export api
fix: npe when cart empty
docs / style / refactor 文档变更 / 格式调整(不影响逻辑)/ 重构(不加功能也不修缺陷) refactor: extract payment strategy
perf / test / chore 性能优化 / 测试相关 / 构建脚本与依赖等杂项 perf: batch load user roles
chore: bump spring-boot to 3.2
scope 用法 标注影响模块,便于生成变更日志与定位责任人,团队内约定固定词表 fix(user): 头像上传超时 可追溯
原子提交 一次提交只做一件事,message 讲清「为什么改」;拒绝混合大提交 fix bug & update docs & misc禁止
单行 subject 说不清时,空一行写 body 讲动机与影响面;破坏性变更必须写在脚注里。
feat(order): 支持优惠券叠加 购物车可叠加多张券,按发放时间顺序核销,与满减互斥; 互斥校验见 OrderPromotionService#checkConflict。 BREAKING CHANGE: /api/coupons/apply 入参 couponId 改为 couponIds 数组
🔍 Code Review 清单
检查项要点提示
正确性 逻辑是否达成需求,边界条件是否处理 空值、空列表、越界、并发场景逐一过
可读性 命名达意、结构清晰,新人能否不问人就看懂 复杂逻辑需注释说明动机 最高性价比
异常与边界 参数校验是否完整,异常是否被吞掉 catch 后至少记录日志,禁止静默失败
并发安全 共享可变状态、锁粒度、容器选型 HashMap 并发写、双重检查锁是惯犯
测试覆盖 关键路径有无单测,回归场景是否补齐 修复 Bug 必须附带复现用例 防复发
性能 循环内 IO、N+1 查询、重复计算、大对象 批量替代逐条、必要时加缓存并评估失效策略
Review 礼仪 对事不对人;PR 保持小步(建议 ≤ 400 行);24 小时内响应 「这里建议改成 X,因为 Y」✓
「你写得太烂」✗ 对事不对人
🔖 版本与分支命名
场景 / 规则说明示例
SemVer 语义化版本 版本号遵循 主版本.次版本.修订号 三段式,变更类型决定递增哪一段 2.4.1(主.次.修订)
主版本 MAJOR 出现不兼容的 API 变更时递增,归零次版本与修订号 1.9.92.0.0
次版本 MINOR 向下兼容的新功能、新接口时递增,修订号归零 2.0.02.1.0
修订号 PATCH 向下兼容的缺陷修复时递增 2.1.02.1.1
先行版本标识 正式发布前的预览版本加后缀,稳定后去掉后缀发布 3.0.0-alpha3.0.0-beta.23.0.0-rc.1
分支命名 小写 + 中划线,按用途加类型前缀;常驻分支仅 main feature/user-exportfix/login-timeouthotfix/order-refund
tag 命名 发布时在 main 打 tag,格式 v + 三段版本号,与发布一一对应 git tag v1.2.0 可追溯
📋 日志与异常规范
主题说明示例
DEBUG 开发调试细节,生产环境关闭,不进告警 打印入参出参、中间状态
INFO 关键流程节点,能凭日志还原操作轨迹 「订单 1001 支付成功」主流量
WARN 可自动恢复或存在潜在风险的场景 重试后成功、降级触发、慢 SQL 预警
ERROR 需要人工介入的故障,带堆栈与业务上下文 下游持续超时、落库失败 需告警
占位符 {} 用占位符替代字符串拼接,级别未开启时零开销 log.info("user {}", id) ✓ log.info("user " + id)
异常三不 不吞异常;不打印后又重抛(双份日志);不用异常做流程控制 catch 后要么记录要么上抛,二选一 红线
全局兜底 @RestControllerAdvice + @ExceptionHandler 统一兜底未捕获异常 返回统一错误结构,避免堆栈直接暴露给前端
业务异常体系 定义 BusinessException + 错误码枚举,与系统异常分离 throw new BusinessException(ErrorCode.ORDER_NOT_FOUND)
⚙️ 配置与常量
主题说明示例
配置外置 配置进 application.yml / 环境变量 / 配置中心,禁止硬编码在代码里 数据库地址、第三方密钥、功能开关
多环境隔离 application-{profile}.yml 按环境拆分,敏感配置不入 Git application-dev.yml / application-prod.yml
魔法值治理 逻辑中禁止裸字面量,提取为有名字的常量或配置 if (status == 3) ✗ → STATUS_PAID
枚举替代常量类 状态 / 类型用枚举内聚语义与行为,携带 code 与描述,便于遍历与校验 OrderStatus.PAID.getCode() 推荐
工具类静态导入 高频工具方法用静态导入,调用处更简洁;仅对高频惯用工具启用 import static java.util.Objects.requireNonNull;
敏感配置 密码、密钥不写死、不入库,用环境变量或配置中心加密下发 ${DB_PASSWORD} 从环境注入 红线
🧼 坏味道 → 整改示范
一段「长函数 + 魔法值 + 吞异常」三连的典型坏代码,对照整改后的样子;整改按「补测试 → 一处小改 → 跑测试 → 提交」节奏推进,每步只改一处
// ❌ 整改前:超长方法 + 魔法值 + 吞异常(三连典型) public void handle(List<Order> orders) { for (Order o : orders) { if (o.getStatus() == 3 && o.getAmount().doubleValue() > 0) { // 3 是什么状态? try { BigDecimal fee = o.getAmount().multiply(new BigDecimal("0.03")); // 费率哪来的? o.setFee(fee); orderMapper.update(o); } catch (Exception e) { // 吞异常:失败无声消失,出了 Bug 无从排查 } } } } // ✅ 整改后:提取方法 + 魔法值具名 + 异常上抛 private static final BigDecimal FEE_RATE = new BigDecimal("0.03"); // 更进一步:收敛为费率配置 private static final int ORDER_PAID = OrderStatus.PAID.getCode(); // 或直接用枚举比较 public void settlePaidOrders(List<Order> orders) { // 方法名说清做什么 orders.stream().filter(this::isPaid).forEach(this::settleFee); } private boolean isPaid(Order order) { // 具名布尔方法替代魔法值判断 return order.getStatus() == ORDER_PAID; } private void settleFee(Order order) { // 单一职责,一屏读完 order.setFee(order.getAmount().multiply(FEE_RATE)); orderMapper.update(order); // 异常交全局处理器记录并告警,绝不静默 }
🚷 命名红线
红线反例 ✗正例 ✓
布尔加 is / has / can 前缀 flagvip、名词化的 deletion isViphasChildrenisDeleted,读起来就是一句判断
集合命名用复数 List<Order> listorderArrdata List<Order> pendingOrders,复数自带「这是一组」的信息
方法用动词开头 order()userState() 分不清查询还是改状态 createOrder()getUserState(),读方法名即知行为
禁拼音与拼音缩写 getYhmById()djzt(订单状态?) getUsernameById()orderStatus;拼音让新人完全无法上手
常量全大写 + 下划线 final int maxRetry = 3;、逻辑里裸写 status == 3 MAX_RETRY_COUNTORDER_STATUS_PAID,全库可检索
类名用名词,拒绝万金油后缀 OrderProcessorDataManager 什么都往里塞 OrderFeeCalculatorRefundOrderHandler,按职责具名,一个类一个变更理由