什么是合适的重构流程
重构是软件开发中不可或缺的环节,但错误的重构方式会让代码库陷入混乱。本文档介绍一种渐进式、可控的重构策略,特别适用于大文件拆分等复杂场景。
核心原则
渐进式重构,而非一次性搬迁。
每次只移动一小部分代码,立即验证,然后继续。这种方法将风险降到最低,并确保每个步骤都是可控的。
大文件拆分的标准流程
第一步:创建专属分支
在开始任何重构工作之前,必须创建一个独立的 git 分支:
git checkout -b refactor/split-large-file为什么需要独立分支?
- 保护主分支的稳定性
- 可以随时回退到重构前的状态
- 便于代码审查和追踪变更
- 如果重构失败,不影响主分支和其他开发工作
第二步:分析拆分目标
不要急于动手,先搞清楚”拆什么”:
- 阅读完整文件:理解文件的整体结构和职责
- 识别功能模块:找出逻辑上独立的代码块
- 绘制依赖关系:标注模块之间的引用关系
- 确定拆分粒度:每个新文件应该职责单一、内聚性高
原始大文件分析示例:
┌─────────────────────────────┐
│ utils-large-file.ts │
├─────────────────────────────┤
│ • 字符串处理函数 (15个) │ ← 可拆分为 string-utils.ts
│ • 日期处理函数 (8个) │ ← 可拆分为 date-utils.ts
│ • 数组工具函数 (12个) │ ← 可拆分为 array-utils.ts
│ • 格式化函数 (10个) │ ← 可拆分为 formatters.ts
└─────────────────────────────┘
第三步:从最简单的模块开始
什么是最简单的模块?
- 没有外部依赖或依赖最少的模块
- 函数数量少、逻辑清晰的模块
- 不涉及复杂类型定义的模块
从简单到复杂的好处:
- 快速建立信心
- 积累拆分经验
- 早期发现潜在问题
- 为后续复杂拆分建立模板
第四步:渐进式迁移
这是最关键的步骤,遵循”拆一个、改引用、删原代码”的循环:
循环执行以下步骤:
1. 将目标模块复制到新文件
↓
2. 更新原文件中对该模块的引用,指向新文件
↓
3. 从原文件中删除已迁移的代码
↓
4. 运行测试/构建,验证没有破坏功能
↓
5. 提交变更(可选,但推荐)
↓
6. 重复以上步骤,直到全部拆分完成
具体示例
假设我们要拆分 utils-large-file.ts:
# 1. 创建新文件 string-utils.ts,包含字符串处理函数
touch src/utils/string-utils.ts// src/utils/string-utils.ts
export function capitalize(str: string): string {
return str.charAt(0).toUpperCase() + str.slice(1)
}
export function camelCase(str: string): string {
// ... 实现
}// 原文件 utils-large-file.ts 中,更新引用
// 修改前:
// import { capitalize, camelCase } from './utils-large-file'
// 修改后:
import { capitalize, camelCase } from './string-utils'// 然后从 utils-large-file.ts 中删除 capitalize 和 camelCase 的函数定义# 验证没有破坏功能
npm run build
npm test❌ 错误的重构方式
反模式:胡乱拆卸 + 集中搬迁
错误做法:
1. 从大文件中随意复制多个函数到新文件
2. 不更新引用,让原文件保持混乱
3. 继续复制粘贴,创建十几个新文件
4. 最后才想起来要统一修改所有引用
5. 发现引用关系错综复杂,根本无法理清
6. 陷入"修复引用 → 引入新bug → 再修复"的死循环
为什么这种方式有问题?
- 工作量爆炸:最后需要一次性修改几十处引用,容易遗漏
- 调试困难:如果出现问题,无法定位是哪一步引入的
- 心理压力:面对一团乱麻的代码,容易产生挫败感
- 代码审查困难:一次性改动太大,reviewer 无法有效审查
✅ 正确重构的关键特征
| 特征 | 说明 |
|---|---|
| 小步前进 | 每次只迁移一个模块 |
| 即时验证 | 每步都运行测试/构建 |
| 引用同步 | 拆完立即修改引用 |
| 原代码清理 | 迁移后删除原文件中的对应部分 |
| 分支保护 | 始终在独立分支进行 |
重构后的收尾工作
- 运行完整的测试套件:确保没有破坏任何功能
- 检查代码风格:运行 linter 和 formatter
- 审查新文件结构:确保拆分后的文件组织合理
- 更新文档:如果文件结构变化影响文档,及时更新
- 提交并合并:
git add .
git commit -m "refactor: 拆分大文件为多个职责单一的模块"
git checkout main
git merge refactor/split-large-file其他重构场景的通用策略
虽然本文档以大文件拆分为例,但同样的原则适用于:
- 函数提取:将长函数拆分为多个小函数
- 模块重组:重新组织目录结构
- 接口优化:简化复杂的 API 或函数签名
- 依赖解耦:减少模块间的耦合
核心思想不变:小步、验证、清理、循环。
总结
合适的重构流程不是”拆了再说”,而是有计划的渐进式改进。建立分支 → 分析目标 → 从简单开始 → 拆一个改一个 → 立即验证,这个循环虽然看起来慢,但整体效率远高于混乱的搬迁式重构。
“重构的两条法则:
- 任何情况下,重构前必须有能运行的代码
- 每次只做一个小改动,然后测试” — Martin Fowler, 《重构:改善既有代码的设计》