06 - 代码与评审:从代码写到合并的规矩
代码是给未来的人读的,不是给现在的机器执行的。所以 Code Review 不是"挑错",是"教未来的人理解这段代码"。
一句话
CI + Code Review + 测试 = 把代码质量门建在合并前,而不是上线后——一次慢的合并 ≈ 一次快的故障排查。
Git 工作流
推荐:Trunk-Based Development(主干开发)
SE@Google Ch16 推荐:
"We find 'trunk-based development' as popularized by DevOps (one repository, no dev branches) to be a particularly scalable policy approach."
核心:
main (trunk)
│
├─ feature/xxx ──┐
│ │
└─ feature/yyy ──┴→ merge 到 main规则:
- 永远从 main 开新分支
- 1-2 天内必须合回 main
- 长期分支 = 反模式
- feature flag 让"已合并但未发布"成为可能
三种主流工作流对比
| 维度 | Git Flow | GitHub Flow | Trunk-Based |
|---|---|---|---|
| 长期分支 | develop + master | main | 无 |
| 短期分支 | feature/* | feature/* | feature/short-lived |
| 发布节奏 | 版本发布 | 持续部署 | 持续部署 |
| 团队规模 | 10+ 人 | 5-30 人 | 任何规模 |
| 学习成本 | 中 | 低 | 低 |
| 推荐 | 传统企业 | 中小型 | 现代化团队 |
小团队 → Trunk-Based:分支短(< 2 天),合并勤(每天多次)。
Commit 规范
Conventional Commits(推荐)
<类型>(<范围>): <简短描述>
[正文]
[脚注]类型:
feat: 新功能fix: 修复 bugdocs: 仅文档style: 格式(不影响代码)refactor: 重构(既不是新功能也不是 bug 修复)test: 测试相关chore: 杂项(构建/工具等)perf: 性能优化
示例:
feat(orders): 添加订单状态机
订单状态转换:待支付 → 已支付 → 已发货 → 已完成
新增 OrderStatus 枚举 + 状态校验
关联 Issue: #1234
关联 ADR: ADR-2026-Q3-001
🤖 Generated with [Claude Code](https://claude.ai/code)为什么规范:
- 自动生成 CHANGELOG
- 自动判断版本号(semver)
- 工具能识别和分类
反模式:垃圾 Commit Message
❌ "fix bug" / "update" / "WIP" / "最终版本" / "再改一下"
✅ "feat(orders): 添加订单状态机"
Pull Request 模板
## 变更说明
[这段 PR 改了什么 + 为什么]
Closes #[Issue 编号]
## 改动类型
- [ ] 新功能
- [ ] Bug 修复
- [ ] 重构
- [ ] 文档
- [ ] 其他
## 测试
- [ ] 单元测试已通过
- [ ] 集成测试已通过
- [ ] 手动测试通过
- [ ] [其他测试]
## 验证步骤
1. [步骤 1]
2. [步骤 2]
3. [预期结果]
## 截图 / 录屏
[如有 UI 改动,附截图]
## 关联文档
- ADR:[链接]
- 设计稿:[Figma 链接]
## Checklist
- [ ] 代码遵循项目代码规范
- [ ] 没有遗留注释代码(`console.log`、`TODO`、`FIXME`)
- [ ] 没有无关改动
- [ ] 大函数已拆分(< 50 行)
- [ ] 复杂逻辑有注释Code Review 规矩
SE@Google Ch9 关键论断
"Code Review is a mechanism for growing the team and the codebase simultaneously."
翻译:Code Review 既提升代码质量,也提升团队能力。
Review 的两个角色
作者:
- PR < 400 行(多了就拆分)
- 自审(提交前自己看一遍 diff)
- 自测(本地跑过测试)
- 描述清晰(截图 + 验证步骤)
Reviewer:
- 24 小时内响应
- 关注:可读性 > 设计 > 细节
- 评论要"建议"而非"命令"
关注优先级
高(必看):
业务逻辑正确性
安全性漏洞(注入 / XSS / CSRF / 鉴权)
数据一致性
性能瓶颈
可读性(命名 / 函数长短 / 职责清晰)
中(建议看):
错误处理完整性
日志是否充分
测试是否够
低(看也行):
代码风格细节
命名细节
注释风格评论范式
❌ 反模式评论:
"这段代码写得不好"
"应该这样写"
"重写吧"
"废代码"
"很丑"✅ 高质量评论:
"这里如果传入 null 会怎样?建议在前面加 null check"
"我建议提取一个独立函数,可以提升可测试性"
"这里是不是用 Promise.all 并发更好?性能能提升 3 倍"
"建议加个测试覆盖边界情况(空数组)"
"这块跟 ADR-XXX 不一致,是否确认走的是新方案?"SE@Google Ch9 重要原则:
"Treat incoming comments as questions, not commands."
翻译:把评论当"问题"而非"命令"——评论者不是权威,作者可选择不采纳并解释。
Code Review 反模式(10 种)
1. LGTM 农场
症状:Reviewer 一句话不说就 Approve。
修复:
- Review 工具统计:每个 Reviewer 平均评论数
- < 0.5 个评论/人 = 警报
- 必须留评论(包括
LGTM, 1 个疑问)
2. 等太久
症状:PR 提交后 1 周没 Review。
修复:
- 24 小时规则:所有 PR 必须 24h 内有人回应
- 跨团队 PR:明确指定 Reviewer
- 设 Reviewer On-call
3. 一次审 1000 行
症状:PR 含 1000+ 行变动,Reviewer 看不动。
修复:
- PR < 400 行(多了就拆)
- 大重构用"分阶段合并"(多次小 PR)
4. 评论变命令
修复:用问句而非命令("能不能这样"而非"必须这样")。
5. Reviewer 跟作者争论
修复:作者解释理由 → 双方如果还不同意 → 团队 Lead / 架构师仲裁。
6. 不做自审
症状:作者直接提交,Reviewer 第一遍看。
修复:
- 作者先看自己 diff
- 自查清单(命名前后一致 / 测试 / 文档 / 无 console.log)
7. 没有自动化门
症状:Linter / 测试 / 构建靠人手运行。
修复:CI 自动跑所有门(见 07 部署章节)。
8. 评论太多(50+)
症状:PR 评论 50+,说明设计有问题。
修复:设计错误必须先合并设计 PR——代码 PR 是设计已确定的实现。
9. 个人风格当真理
症状:争论 tab vs spaces、单引号 vs 双引号。
修复:用 Linter 强制规范——没有争议空间。
10. PR 长期不合并
症状:PR 一直 In Review。
修复:5 天规则——超过 5 天未处理 = Rebase 或 Close(重新开)。
测试金字塔
┌───────────┐
│ E2E │ 10% 慢 + 脆
├───────────┤
│Integration│ 30% 中等
├───────────┤
│ Unit │ 60% 快 + 独立
└───────────┘SE@Google Ch11 测试原则
测试的 5 个特性:
- 覆盖率不是目标——80% 覆盖率可能是无意义断言
- 测试也是代码——要维护、要 Review
- 测试金字塔——多单元 + 少集成 + 极少量 E2E
- 测试要快——单元 < 5 分钟跑完
- 测试要独立——不依赖外部系统(用 Test Double)
测试覆盖度的真正意义
覆盖率是底线,不是目标:
- 单元测试覆盖率 ≥ 70%
- 关键路径 100%
- 不要追求 100%(代价太高)
测什么:
- ✅ 业务逻辑(订单状态机)
- ✅ 边界条件(空数组/null/极值)
- ✅ 异常路径(错误响应/超时)
- ❌ Getter/Setter
- ❌ 三方库代码
Mock / Stub / Test Double
Test Double 类型:
| 类型 | 作用 | 示例 |
|---|---|---|
| Dummy | 填充参数,不调用 | null, new Object() |
| Stub | 返回预设值 | when(mock.foo()).thenReturn(42) |
| Spy | 记录调用 | 验证某方法被调用过 |
| Mock | 验证行为 | 验证调用顺序和参数 |
| Fake | 简化实现 | 内存数据库 |
使用原则:
- 外部依赖必须 Mock(DB / HTTP / 第三方 API)
- 核心业务逻辑不要 Mock(那就是测 Mock 本身)
Lint + 静态分析
Linter 强制规范
让机器判断的事,不要让人判断:
// .eslintrc.js
module.exports = {
rules: {
'no-console': 'error', // 禁止 console.log
'no-debugger': 'error', // 禁止 debugger
'max-lines-per-function': ['warn', { max: 50 }],
'complexity': ['error', 10], // 圈复杂度不超过 10
// ...
}
}Pre-commit Hook
# .pre-commit-config.yaml
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
hooks:
- id: trailing-whitespace
- id: end-of-file-fixer
- id: check-yaml
- repo: https://github.com/alessandrojcm/commitlint-pre-commit-hook
hooks:
- id: commitlint关键:lint 失败不能合并——CI 自动化强制。
单仓(Monorepo)vs 多仓
单仓(推荐 5-30 人)
优点:
- 共享代码改动立即生效
- 跨项目重构容易
- 统一工具链
缺点:
- 大仓慢(解决方法:稀疏 checkout / 部分克隆)
- 需要权限管理
工具:pnpm workspaces / Turborepo / Lerna / Nx
多仓(推荐 30+ 人)
优点:独立部署、独立权限 缺点:跨项目改动困难
依赖管理
SE@Google Ch21 关键原则:
"Be careful about dependencies. They're often the source of security, stability, and compatibility issues."
3 大依赖管理原则
- 少用第三方——每多一个依赖 = 多一个风险源
- 锁版本——
package-lock.json/go.sum入版本控制 - 定期升级——安全更新必须及时(4 周内)
依赖审计
每月运行:
1. npm audit / pip-audit / go list -m all
2. 处理所有 High / Critical 漏洞
3. 删除不再使用的依赖
每个季度:
1. 大版本升级评估
2. 替换不再维护的依赖文档即代码
代码内文档
每个模块顶层要有 README:
# 模块名:订单服务
## 职责
订单的 CRUD + 状态管理
## 边界
- ✅ 包含:订单创建、状态查询、状态转换
- ❌ 不包含:支付(独立服务)、商品(独立服务)
## 关键类
- Order(聚合根)
- OrderItem(实体)
- OrderStatus(值对象)
## 依赖
- payment-service(支付回调)
- inventory-service(库存扣减)
## 测试
- 单元:src/__tests__
- 集成:tests/integration
- E2E:tests/e2e
## 关联文档
- ADR-XXX:选择 ORM
- PRD:订单需求文档
## Owner
- Tech Lead: 张三
- Product: 李四进入下一章
代码 PR 合并后,下一步:
- 07 部署与运营——把代码变成用户可用的服务
作者:基于 SE@Google Ch9 (Code Review) + Ch16 (VCS) + Ch11-14 (Testing) 综合 最后更新:2026-06-28