来源:本文档由重组合并生成 ——
13-refactoring-standards.md+ 《AI 编程规范》§6 衔接章节。冲突处以更具体/更新版本为准,双方独有内容均保留。
在保持外部行为不变的前提下改善代码内部结构,提升可读性、可维护性和性能,同时确保重构过程安全可控、不引入新 Bug。
- 代码出现明显的"坏味道"
- 添加新功能前发现现有结构阻碍开发
- 代码审查中发现可改进的结构
- 性能优化需要调整代码组织
- 技术债务偿还计划中的重构项
代码坏味道清单:
| 坏味道 | 表现 | 重构方向 |
|---|---|---|
| 过长函数 | 函数 > 50 行 | 提取子函数 |
| 过大类/文件 | 文件 > 300 行,职责不清 | 拆分类/模块 |
| 重复代码 | 相似逻辑出现 3+ 次 | 提取公共函数/抽象 |
| 过长参数列表 | 参数 > 4 个 | 用对象/配置参数 |
| 散弹式修改 | 改一处要改 N 个文件 | 内联/合并模块 |
| 依恋情结 | 函数大量使用别的类的数据 | 移动函数到数据所在类 |
| 基本类型偏执 | 用 string/number 代替领域概念 | 引入值对象/枚举 |
| 临时字段 | 某些字段只在特定情况有值 | 提取子类/策略模式 |
| 过度注释 | 需要大量注释解释代码 | 重命名/重构使代码自解释 |
| 中间人 | 类的大部分方法只是委托调用 | 移除中间人 |
重构 vs 重写决策:
| 选重构 | 选重写 |
|---|---|
| 核心逻辑正确,结构不好 | 核心逻辑就有根本问题 |
| 有测试覆盖 | 无测试且代码不可理解 |
| 逐步改善即可 | 技术栈需要更换 |
| 风险可控 | 重写的 ROI 明确更高 |
-
确保有测试覆盖
- 重构区域必须有测试(没有就先补)
- 测试覆盖核心行为和边界条件
- 测试全部通过后才开始
-
明确重构目标
- 这次重构解决什么问题?
- 重构后的代码应该是什么样?
- 不改变什么(外部行为不变)
-
创建独立分支
git checkout -b refactor/extract-user-service
-
小步计划
- 列出重构步骤(每步都是可编译可运行的)
- 每步改动尽量小
核心原则:
- 小步前进 — 每次只做一个小改动
- 持续可运行 — 每一步后代码都能编译和通过测试
- 行为不变 — 重构不改外部行为(改行为是另一个提交)
- 频繁提交 — 每完成一个小步骤就提交
- 随时可停 — 任何时刻都可以停下来,代码都是完整可用的
重构步骤模式:
1. 小改动(如:提取一个函数)
2. 运行测试 → 全部通过
3. 提交
4. 下一个小改动
5. 运行测试 → 全部通过
6. 提交
...重复
| 手法 | 适用场景 |
|---|---|
| Extract Function | 一段代码可以独立命名 |
| Inline Function | 函数体比名字更清晰 |
| Rename Variable/Function | 名字不能表达意图 |
| Move Function | 函数更适合放在另一个模块 |
| Replace Conditional with Polymorphism | 复杂的 if/switch |
| Introduce Parameter Object | 参数过多 |
| Replace Magic Number with Constant | 硬编码数字 |
| Extract Class | 一个类做太多事 |
| Introduce Repository/Service | 逻辑散落在各处 |
- 所有现有测试通过
- 手动测试关键路径
- 代码审查(重构也需要 review)
- 性能无退化(必要时跑 benchmark)
- 无新增 lint 警告
- 目标已达成(坏味道消除)
- 测试开始难以维护(过度抽象)
- 时间盒到了(预留时间用完)
- 发现需要改行为(停下来,另开任务)
- 重构区域有测试覆盖
- 重构前所有测试通过
- 在独立分支上进行
- 每步改动小且可运行
- 外部行为未改变
- 频繁提交(每步一个 commit)
- 重构后所有测试通过
- 代码审查通过
- 性能无退化
- 重构目标已达成
| 输出物 | 格式 | 存放位置 |
|---|---|---|
| 重构后的代码 | 提交 | refactor/ 分支 |
| 补充的测试 | 测试代码 | tests/ |
| 重构说明(大重构时) | PR 描述 | GitHub PR |
| 误区 | 正确做法 |
|---|---|
| 没有测试就重构 | 先补测试,再重构 |
| 一次改太多 | 小步前进,每步可运行 |
| 重构时顺手加功能 | 重构只改结构,不改行为 |
| 追求"完美设计" | 够用就好,避免过度工程 |
| 重构完不提交 | 每步都提交,方便回滚 |
| 为了模式而模式 | 简单问题用简单方案 |
本文档的通用重构手法在本项目受以下硬性约束(详见 ai-coding.md 第二部分):
- §1 单文件 ≤300 行:"过大类/文件"坏味道在本项目有明确阈值——>600 行必须硬拆;300-600 行登记豁免清单待专项重构(该清单由批 0 的
0-C1重建为快照表;注意与拆分任务区分:0-C2前端 5 个 /0-C3Rust 10 个拆的是 >600 那 15 个文件,不是 300-600 清单)。行数口径见 AGENTS.md §3.1(全部行数,以node scripts/line-limits.mjs为准) - §6 自底向上重构顺序:拆分遵循 原子层(纯函数/类型)→ 业务层(hook/service)→ 系统层(页面/装配);公共 API 保持零改动(兼容 re-export)
- 已验证拆分模式:React 页面 → 纯展示组件 + 域 hook + 组合层;Python 巨型模块 → 包化 + Mixin + schemas 提取;Go 巨型 handler → 同包多文件职责拆分