真实案例:用 AI 重构一段难维护的旧代码

接手旧项目时,最麻烦的通常不是代码不能运行,而是:状态值没有含义、判断层层嵌套,改一处又担心影响其他页面。

AI 很适合帮助我们读懂和整理旧代码,但不能让它直接把一大段代码改掉。

这篇文章用一个订单状态展示函数,演示如何安全地完成一次小步重构。核心原则只有一句:

先确认旧代码做了什么,再决定如何改。

一、先看一段难维护的旧代码

下面的函数根据订单状态,决定页面显示什么文字,以及用户可以执行哪些操作:

javascript 复制代码
function getOrderDisplay(order) {
  let text = '';
  let canPay = false;
  let canCancel = false;

  if (order) {
    if (order.deleted === 1) {
      text = '订单已删除';
    } else if (order.status === 0) {
      text = '待支付';
      canPay = true;
      canCancel = true;
    } else if (order.status === 1) {
      if (order.shippedAt) {
        text = '运输中';
      } else {
        text = '待发货';
      }
      canCancel = true;
    } else if (order.status === 2) {
      text = '已完成';
    } else if (order.status === 3) {
      text = '已取消';
    } else {
      text = '状态异常';
    }
  }

  return {
    text,
    canPay,
    canCancel
  };
}

代码不算很长,但已经有明显的维护问题:

  • 0123 是魔法数字。
  • 状态文字和操作权限混在同一个函数里。
  • status === 1 又依赖 shippedAt,规则不直观。
  • 传入空订单时返回空文本,调用方不容易发现问题。
  • 后续增加"退款中""已退款"等状态时,条件会继续膨胀。

这时不要先说"帮我优化这段代码"。先把业务规则找出来。

二、重构前,先锁定旧行为

重构的目标不是让代码看起来更漂亮,而是在不改变正确业务行为的前提下,让代码更容易修改。

先根据旧代码整理出当前行为:

条件 展示文字 可支付 可取消
deleted === 1 订单已删除
status === 0 待支付
status === 1 且没有 shippedAt 待发货
status === 1 且有 shippedAt 运输中
status === 2 已完成
status === 3 已取消
其他状态 状态异常

这张表非常重要,它不是 AI 猜出来的,而是从现有代码中整理出来的。

如果你不知道某个规则是否正确,例如"运输中的订单为什么还能取消",不要擅自改掉。先向产品、业务同学或旧代码负责人确认。

三、先让 AI 做分析,不直接改代码

可以把代码和已知背景交给 AI:

text 复制代码
请分析下面的 JavaScript 函数,暂时不要修改代码。

函数用途:根据订单数据返回页面展示文字、是否可以支付、是否可以取消。

请输出:
1. 该函数当前的所有行为,整理成条件表。
2. 代码中难维护的地方。
3. 可能存在歧义、需要人工确认的业务规则。
4. 一个保持现有行为的最小重构方案。
5. 重构前应该补充哪些测试。

要求:
- 不要自行改变订单状态含义。
- 不要增加退款、支付超时等未提供的功能。
- 明确区分"代码事实"和"你的推测"。

代码:
[粘贴旧代码]

这类 Prompt 的关键是:限制 AI 不要擅自补业务。

AI 可能会指出一个值得确认的问题:status === 1 时,订单已经发货却仍然可以取消,是否符合业务规则?

这个问题不能由 AI 决定。假设我们和业务确认后,当前规则确实如此,那么重构就必须保留它。

四、先补测试,再动旧代码

旧代码如果没有测试,直接重构风险很高。

先把上面的行为表变成测试。这里以 Vitest 为例:

javascript 复制代码
import { describe, expect, it } from 'vitest';
import { getOrderDisplay } from './order-display.js';

describe('getOrderDisplay', () => {
  it('未支付订单可以支付和取消', () => {
    expect(getOrderDisplay({ deleted: 0, status: 0 })).toEqual({
      text: '待支付',
      canPay: true,
      canCancel: true
    });
  });

  it('已发货订单显示运输中,仍保留当前的取消规则', () => {
    expect(getOrderDisplay({
      deleted: 0,
      status: 1,
      shippedAt: '2026-09-01T10:00:00Z'
    })).toEqual({
      text: '运输中',
      canPay: false,
      canCancel: true
    });
  });

  it('未知状态显示状态异常', () => {
    expect(getOrderDisplay({ deleted: 0, status: 99 })).toEqual({
      text: '状态异常',
      canPay: false,
      canCancel: false
    });
  });
});

真实项目中,应覆盖行为表里的每一种情况。这样重构后若意外改变已有行为,测试会尽早提醒我们。

五、让 AI 提出最小重构方案

这一步的重点是"最小"。

不要让 AI 一次把状态管理改成复杂的状态机,也不要为了一个函数引入新的库。

可以这样提问:

text 复制代码
下面是一段已经有测试保护的订单展示逻辑。

请给出一个最小重构方案,目标是:
1. 消除状态魔法数字。
2. 让每个状态的规则更容易阅读。
3. 保持现有测试行为不变。
4. 不引入新依赖。
5. 不修改调用方接口。

请先解释修改点和风险,再给出完整代码。

一个合适的方案通常是:

  • 用常量命名订单状态。
  • switch 明确分支。
  • 为每个状态直接返回结果。
  • 保留原函数名称和返回结构。

六、重构后的代码

javascript 复制代码
const ORDER_STATUS = {
  PENDING_PAYMENT: 0,
  PENDING_SHIPMENT: 1,
  COMPLETED: 2,
  CANCELED: 3
};

function getOrderDisplay(order) {
  const defaultResult = {
    text: '状态异常',
    canPay: false,
    canCancel: false
  };

  if (!order) {
    return defaultResult;
  }

  if (order.deleted === 1) {
    return {
      text: '订单已删除',
      canPay: false,
      canCancel: false
    };
  }

  switch (order.status) {
    case ORDER_STATUS.PENDING_PAYMENT:
      return {
        text: '待支付',
        canPay: true,
        canCancel: true
      };

    case ORDER_STATUS.PENDING_SHIPMENT:
      return {
        text: order.shippedAt ? '运输中' : '待发货',
        canPay: false,
        canCancel: true
      };

    case ORDER_STATUS.COMPLETED:
      return {
        text: '已完成',
        canPay: false,
        canCancel: false
      };

    case ORDER_STATUS.CANCELED:
      return {
        text: '已取消',
        canPay: false,
        canCancel: false
      };

    default:
      return defaultResult;
  }
}

这次重构没有改变外部接口,调用方仍然使用:

javascript 复制代码
const display = getOrderDisplay(order);

它主要改善了三点:

  • 状态值有了业务名称。
  • 每个分支都在一个清晰的位置返回结果。
  • 新增状态时,不需要在多层 if 中寻找插入点。

七、一个容易忽略的行为变化

旧代码传入 null 时,会返回:

javascript 复制代码
{
  text: '',
  canPay: false,
  canCancel: false
}

重构后传入 null 时,会返回"状态异常"。

这看起来是更合理的结果,但它已经改变了旧行为。

此时必须停下来做决定:

  • 如果调用方依赖空文本,就保持旧行为,并写测试锁定它。
  • 如果"状态异常"才是正确产品表现,就把它作为明确的需求修改,补充验收和测试。

这正是 AI 重构最容易踩的坑:它可能顺手"修复"一些看似不合理的逻辑,但这些逻辑可能是旧系统的一部分。

为了完全保持旧行为,可以把空订单单独处理:

javascript 复制代码
if (!order) {
  return {
    text: '',
    canPay: false,
    canCancel: false
  };
}

重构不是改得越多越好,而是每个行为变化都要有明确依据。

八、重构后怎么验证

完成代码后,至少做四件事:

  1. 运行已有单元测试,确认旧行为没有被意外改变。
  2. 手动验证订单列表和订单详情页,确认展示一致。
  3. 检查本次 diff,确认没有改到无关文件。
  4. 让 AI 再做一次审查,重点检查行为变化和遗漏测试。

可以使用下面的审查 Prompt:

text 复制代码
请审查下面这次旧代码重构的 diff。

重构目标:
- 用具名常量替代订单状态魔法数字。
- 保持原函数名称、返回结构和业务行为。
- 不引入新依赖。

请重点检查:
1. 是否有任何行为变化。
2. 是否遗漏订单状态分支。
3. 空订单和未知状态是否有明确处理。
4. 现有测试是否覆盖了本次改动。
5. 是否存在不必要的复杂化。

请按"问题位置、严重程度、影响、建议、验证方式"输出。
不要直接重写代码。

如果 AI 说"逻辑更简洁了",这不是验收结论。真正要看的,是它是否能指出具体分支、具体影响和验证方法。

九、什么时候不适合直接重构

下面几种情况,不建议直接让 AI 修改:

  • 不知道函数被哪些页面或服务调用。
  • 没有测试,也无法快速补测试。
  • 订单、支付、权限等核心规则尚未确认。
  • 旧代码涉及数据库迁移或批量数据修改。
  • AI 需要读取真实密钥、用户数据或未经授权的公司代码才能理解问题。

这时 AI 仍然可以帮你做"阅读代码、列问题、设计测试"的工作,但修改动作应该更谨慎,并进行人工评审。

十、旧代码重构清单

  • 已经确认函数的用途和调用位置。
  • 已经整理旧代码的行为表。
  • 已经区分代码事实和业务猜测。
  • 已经补充正常、边界和异常测试。
  • 已经让 AI 先分析,再提出最小方案。
  • 已经一次只修改一个清晰问题。
  • 已经确认所有行为变化都有需求依据。
  • 已经运行测试并检查 diff。
  • 没有将敏感数据直接提交给 AI。

总结

AI 可以大幅降低阅读和整理旧代码的成本,尤其适合:

  • 提取复杂条件中的业务规则。
  • 找出重复代码和魔法数字。
  • 提出多种小步重构方案。
  • 补充可能遗漏的测试场景。
  • 在提交前审查改动范围。

但 AI 不知道你的历史包袱和真实业务规则。

面对难维护的旧代码,正确顺序应该是:

text 复制代码
读懂旧代码
  ↓
整理行为表
  ↓
补测试锁定行为
  ↓
AI 提出最小重构方案
  ↓
小步修改并运行验证
  ↓
审查 diff 后再提交

让 AI 帮你看清旧代码,不要让它在不了解业务时替你决定旧代码该怎么改。

下一篇文章将介绍:

《真实案例:用 AI 快速定位一次代码问题》


✍坚持原创,求关注,点赞,收藏

相关推荐
还有多久拿退休金3 小时前
不调多模态,纯文本大模型如何给系统操作配上截图
前端·llm·aigc
anyup5 小时前
DeepSeek Harness 从零上手:从认识到写出第一个插件
人工智能·openai·deepseek
Web3_Basketball5 小时前
1200个Agent如何串通作弊?三道护栏让它无处遁形
ai编程
leeyi7 小时前
把软件装进不能上网的机房——一套建好了、还没上过战场的交付工程(第101篇)
docker·aigc·agent
Nturmoils8 小时前
我用 Qwen3.8-Max 搭了一个电商商品资料包体检助手,6 份资料和 1 张商品图一次查出 27 个问题
aigc
夏天要喝冰可乐9 小时前
从 Idea 到开源插件:我用 Vibe Coding 做了「文章摆渡」
前端·ai编程·vibecoding
plainGeekDev9 小时前
Agent代码审查与批量修复流水线
agent·ai编程·claude
桃西西呀9 小时前
上下文窗口都卷到 100 万了,大模型为什么还在为"位置"发愁?
人工智能·llm·ai编程
程序员天天困10 小时前
向量检索不准怎么办:混合检索与 Rerank 重排序召回优化实战
后端·python·ai编程