返回文章列表
从零开始:用 AI 重构遗留代码的完整指南
学习如何安全地使用 AI 工具重构老旧代码,包括评估、规划、执行和验证的完整流程。
AIProgHub
2024年12月04日
7 min read
重构遗留代码AI编程最佳实践
遗留代码的痛点
每个程序员都遇到过这种代码:
// 2018 年写的,没人敢动
function processData(d, f, t, opts) {
var r = [];
for (var i = 0; i < d.length; i++) {
if (d[i].type == t || t == null) {
var x = d[i];
if (opts && opts.filter) {
if (opts.filter(x)) {
if (f) {
r.push(f(x));
} else {
r.push(x);
}
}
} else {
if (f) {
r.push(f(x));
} else {
r.push(x);
}
}
}
}
return r;
}
看不懂?别担心,AI 可以帮你。
重构前的准备
1. 评估代码覆盖率
# 运行测试并生成覆盖率报告
npm test -- --coverage
如果覆盖率低于 60%,先补测试。
2. 让 AI 解释代码
Prompt:
分析以下代码的功能:
1. 解释每个参数的作用
2. 描述函数的主要逻辑
3. 列出潜在的问题和风险
代码:
[粘贴代码]
AI 分析结果:
这个函数的功能是对数据数组进行过滤和转换:
参数说明:
- d: 数据数组
- f: 可选的转换函数
- t: 可选的类型过滤器
- opts: 配置对象,包含额外的过滤函数
问题:
1. 参数命名太简略,难以理解
2. 嵌套 if 太深(4 层),认知负担重
3. 使用 var 而非 let/const
4. 重复代码(push 逻辑出现两次)
5. 缺少类型检查和错误处理
3. 确定重构目标
根据 AI 分析,设定明确目标:
- [ ] 改善命名
- [ ] 减少嵌套层级
- [ ] 使用现代 JavaScript 语法
- [ ] 添加类型定义(TypeScript)
- [ ] 消除重复代码
分步重构策略
第一步:补充测试
Prompt:
为这个函数生成全面的单元测试,覆盖:
1. 正常使用场景
2. 边界条件
3. 空值/null 处理
4. 各种参数组合
AI 生成的测试:
describe('processData', () => {
const sampleData = [
{ id: 1, type: 'A', value: 10 },
{ id: 2, type: 'B', value: 20 },
{ id: 3, type: 'A', value: 30 },
];
describe('基本过滤', () => {
it('按类型过滤数据', () => {
const result = processData(sampleData, null, 'A');
expect(result).toHaveLength(2);
expect(result.every(item => item.type === 'A')).toBe(true);
});
it('type 为 null 时返回所有数据', () => {
const result = processData(sampleData, null, null);
expect(result).toHaveLength(3);
});
});
describe('转换功能', () => {
it('应用转换函数', () => {
const transform = item => item.value * 2;
const result = processData(sampleData, transform, null);
expect(result).toEqual([20, 40, 60]);
});
});
describe('opts.filter', () => {
it('应用额外过滤器', () => {
const opts = { filter: item => item.value > 15 };
const result = processData(sampleData, null, null, opts);
expect(result).toHaveLength(2);
});
});
describe('边界条件', () => {
it('空数组返回空数组', () => {
expect(processData([], null, null)).toEqual([]);
});
it('undefined 数据抛出错误', () => {
expect(() => processData(undefined)).toThrow();
});
});
});
运行测试确保全部通过。
第二步:小步重构
原则:每次只改一件事
第一轮:改善命名
function processData(data, transform, typeFilter, options) {
var results = [];
for (var i = 0; i < data.length; i++) {
if (data[i].type == typeFilter || typeFilter == null) {
var item = data[i];
// ... 其余逻辑不变
}
}
return results;
}
运行测试 → 全部通过 → 提交
第二轮:现代化语法
function processData(data, transform, typeFilter, options) {
const results = [];
for (const item of data) {
if (item.type === typeFilter || typeFilter === null) {
// ... 其余逻辑不变
}
}
return results;
}
运行测试 → 全部通过 → 提交
第三轮:减少嵌套
function processData(data, transform, typeFilter, options) {
const results = [];
for (const item of data) {
// 使用 early continue 减少嵌套
if (typeFilter !== null && item.type !== typeFilter) {
continue;
}
if (options?.filter && !options.filter(item)) {
continue;
}
const result = transform ? transform(item) : item;
results.push(result);
}
return results;
}
运行测试 → 全部通过 → 提交
第三步:AI 辅助最终优化
Prompt:
将以下代码重构为现代 TypeScript,要求:
1. 添加完整类型定义
2. 使用函数式编程风格
3. 添加 JSDoc 文档
4. 保持功能完全相同
代码:
[粘贴第三轮结果]
最终版本:
interface DataItem {
type: string;
[key: string]: unknown;
}
interface ProcessOptions<T extends DataItem> {
filter?: (item: T) => boolean;
}
/**
* 过滤和转换数据数组
*
* @param data - 待处理的数据数组
* @param transform - 可选的转换函数
* @param typeFilter - 可选的类型过滤器,null 表示不过滤
* @param options - 额外配置选项
* @returns 处理后的数据数组
*
* @example
* const result = processData(
* users,
* user => user.name,
* 'admin',
* { filter: user => user.active }
* );
*/
export function processData<T extends DataItem, R = T>(
data: T[],
transform?: ((item: T) => R) | null,
typeFilter?: string | null,
options?: ProcessOptions<T>
): R[] {
return data
.filter(item => typeFilter === null || item.type === typeFilter)
.filter(item => !options?.filter || options.filter(item))
.map(item => (transform ? transform(item) : item) as R);
}
重构检查清单
- [ ] 所有测试通过
- [ ] 覆盖率没有下降
- [ ] 性能没有明显退化
- [ ] 代码可读性提升
- [ ] 团队成员 review 通过
常见陷阱
1. 一次改太多
❌ 错误:一口气重构整个文件 ✅ 正确:每次只改一件事,频繁提交
2. 忽略测试
❌ 错误:先重构,后补测试 ✅ 正确:先补测试,再重构
3. 盲目相信 AI
❌ 错误:AI 生成什么就用什么 ✅ 正确:每次改动都运行测试验证
总结
AI 让遗留代码重构变得可控:
- 理解:让 AI 解释复杂代码
- 测试:让 AI 生成测试用例
- 重构:让 AI 建议改进方案
- 验证:始终运行测试确保正确
记住:AI 是助手,不是替代品。最终的决策权在你手中。
延伸阅读:AI 编程技巧 | Prompt 工程指南