← 返回博客

代码重构实战:从"能跑"到"好改"的工程方法

重构不是"觉得代码烂就重写"——而是有步骤、有验证、有节奏地改善代码结构。本文从重构的时机选择、安全重构的步骤、常见重构手法、以及如何让重构成为日常习惯四个层面展开——适合正在维护中大型项目的后端和前端的开发者。

先说结论:重构不是”重写”,是”改善”

很多开发者看到烂代码的第一反应是”重写”,但重写的风险远高于重构——重写过程中,你可能会丢失对边界情况的处理、引入新 Bug、而且业务方不会等你”重写完了再上线”。

重构的核心原则是:每次只改一点点,改完确保还能跑


1. 重构的时机

值得重构的信号

信号表现解决方案
重复代码同一逻辑出现在多处抽取函数或类
过长函数一个函数超过 50 行拆分为多个小函数
过大类一个类超过 500 行拆分职责
过度耦合改一处要改多处引入接口或依赖注入
命名混乱变量名不能表达意图重命名

不值得重构的场景

  • 代码即将被替换(下个月就要下线)
  • 代码几乎不被修改(稳定运行、没人动)
  • 没有测试覆盖且无法补测试(遗留系统,即将报废)

2. 安全重构的步骤

2.1 补测试

// 特征测试:锁定当前行为
describe('formatPrice', () => {
  it('formats price correctly', () => {
    // 不管当前输出是什么,先记录下来
    expect(formatPrice(1000)).toBe('¥1,000.00');
    expect(formatPrice(2500.5)).toBe('¥2,500.50');
  });
});

2.2 小步重构

每次只做一种重构操作,做完就跑测试:

// 第一步:重命名(rename)
// 改前
function calc(a: number, b: number) { ... }
// 改后
function calculateDiscount(originalPrice: number, level: string) { ... }

// 第二步:提取函数(extract function)
// 改前
const total = items.reduce((sum, i) => sum + i.price * i.qty, 0);
const tax = total * 0.1;
const grandTotal = total + tax;

// 改后
function calculateSubtotal(items: Item[]): number { ... }
function calculateTax(subtotal: number): number { ... }
function calculateTotal(items: Item[]): number { ... }

2.3 小步提交

每个小步骤完成后,提交一次代码。提交信息写清楚”重构了什么、为什么重构”。

git commit -m "refactor: 抽取 calculateDiscount 函数,替换内联计算"

3. 常见重构手法

手法适用场景说明
重命名(Rename)命名不能表达意图IDE 自动重构,安全
提取函数(Extract)函数过长或逻辑重复将一段逻辑提取为独立函数
提取变量(Extract Variable)表达式难以理解用有意义的变量名解释表达式
拆分条件(Decompose)条件表达式过长将条件拆分为多个函数
引入参数(Introduce Parameter)硬编码的值将硬编码值改为参数
移动方法(Move Method)方法放在错误的类中将方法移到更合适的类

4. 让重构成为习惯

童子军规则

“离开时让营地比来时更干净”——每次修改代码时,顺手改善一下你碰到的代码。不需要专门安排重构时间,每次改 Bug 或加功能时顺手做一点。

重构时间预算

场景建议时间
修 Bug 时顺手重构 5-10 分钟
加小功能时先重构受影响的代码 15-30 分钟
加大功能时规划 1-2 天专门重构
代码 Review 时如果发现坏味道,记录下来下次重构

总结

原则说明
先补测试没有测试的重构是赌博
小步前进每次只做一种重构操作
频繁提交每步一个 commit,方便回滚
融入日常不专门安排”重构月”
童子军规则每次修改都让代码好一点点

重构不是”等代码烂到不行了再大修”,而是”每次经过都把它修好一点点”。 一个每天改进 1% 的代码库,半年后和一个每天堆积 1% 技术债务的代码库,差距是巨大的。

需要代码重构或技术咨询?联系我们,说清你的项目规模与痛点,24 小时内回可行性。

相关阅读

常见问题

重构和重写的区别是什么?

重构是在不改变外部行为的前提下改善内部结构——功能不变,代码变好。重写是推倒重来——功能可能变,代码全部重写。重构的每一步都是可验证的(测试通过),风险可控;重写的风险高得多,尤其在缺乏测试覆盖的情况下。建议:除非代码已经完全无法维护,否则优先选择重构。

什么时候应该重构?

三种情况值得重构:① 每次添加新功能之前——先重构受影响的代码,让新功能更容易加入("童子军规则":离开时让营地比来时更干净);② 修复 Bug 时——如果 Bug 是因为代码难以理解导致的,先重构再修;③ 代码出现"坏味道"时——重复代码、过长函数、过大类、过度耦合。不要专门安排"重构月",重构应该融入日常开发。

重构没有测试怎么办?

先补测试,再重构。如果代码完全没有测试,重构就是在赌博。做法:① 先用 Characterization Test(特征测试)锁定当前行为——写一个测试调用函数,断言"当前输出是什么",不管它是对是错;② 然后重构,确保重构后的输出和重构前一样;③ 如果发现 Bug,先修 Bug 再重构。

大重构怎么拆解?

大重构必须拆成小步骤。每个小步骤完成后,代码都是可运行、可测试的。推荐的拆解方式:① 按模块拆分——一次只重构一个模块,完成后验证再继续下一个;② 按层次拆分——先重构测试层,再重构接口层,最后重构实现层;③ 使用"绞杀者模式"——为新功能写新代码,逐步将调用方迁移到新代码,最后删掉旧代码。

本文来自 AI Enable Harness 一线交付实践。需要同类系统或优化服务?

📡 本文同步发布平台: CSDN 知乎

订阅博客更新

新文章发布后第一时间邮件通知。不定期发送,不推销。

订阅 →