Use Cases (更新: 2026/6/2)

用 Claude Code 安全现代化遗留代码

用 Claude Code 为遗留代码补测试、分阶段重构和迁移 TypeScript,包含陷阱、示例和验证结果。

用 Claude Code 安全现代化遗留代码

遗留代码现代化不能从大改开始

遗留代码不只是“年代久远的代码”。更准确地说,它是团队不敢轻易修改的代码:测试覆盖不足,业务规则散落在条件分支里,文档已经过期,原作者也不一定还在。Claude Code 在这种场景很有价值,但它不应该被当成一键重写工具。

安全的顺序是先调查,再用测试固定现有行为,然后做很小的重构。这里的测试更接近 characterization test,也就是先记录“系统现在到底怎么表现”,而不是立刻讨论“理想实现应该是什么”。harness 可以理解为“给代理搭的脚手架”:测试、权限、命令、项目规则和审核清单都属于脚手架。

官方 Claude Code common workflows 把代码探索、重构、测试、PR 和 worktree 并行工作放在同一个工作流里。做遗留代码现代化时,这个思路非常重要:Claude Code 提高执行速度,人类仍然负责风险边界和验收。

三个最常见的实战场景

不要因为某段代码“看起来丑”就优先重构。先从能降低业务风险的地方开始。

场景目标Claude Code 适合做什么人类必须确认什么
订单、账单、支付逻辑避免金额和客户状态错误梳理现有行为、补测试、找边界值税率、折扣、舍入、合规规则
JavaScript 迁移到 TypeScript降低后续修改风险生成类型、逐步减少 any、修复类型错误对外 API 兼容性
回调地狱或巨型函数拆分提高可读性和可维护性拆分职责、建议命名、解释差异错误处理、副作用、重试逻辑

我在 ClaudeCodeLab 的内容脚本、结账流程和旧数据转换代码里也用同样的方法。关键不是让 Claude Code 写得越快越好,而是让每个变更小到可以审核,并且每个风险点都有测试或人工确认记录。

flowchart LR
  A[调查] --> B[用测试固定行为]
  B --> C[小步重构]
  C --> D[增加类型并更新依赖]
  D --> E[人工审核风险]
  E --> B

先做只读审计

第一次提示词要明确禁止编辑。你需要的是系统地图,而不是基于不完整上下文的补丁。

请阅读 @src/legacy 和 @test。
现在不要修改任何文件。

请按下面格式输出审计结果:
1. 主要文件和职责
2. 外部 I/O、数据库、API、文件写入和副作用
3. 必须保持兼容的现有行为
4. 缺失测试和高风险分支
5. 最安全的小步修改顺序

如果规则不清楚,请写“需要人工确认”,不要猜测。

官方 How Claude Code works 说明 Claude Code 可以读取文件、运行命令并编辑代码。能力越强,越需要把第一阶段限制在理解和计划上。尤其是旧系统,一次看似合理的“优化”可能改变外部依赖已习惯的返回值。

可复制运行的最小示例

下面的例子是一个缩小版订单处理器。真实项目可能还会连接数据库、消息队列或外部支付 API,但步骤相同。先创建项目。

mkdir legacy-modernization-demo
cd legacy-modernization-demo
npm init -y
npm install -D vitest typescript @types/node
npm pkg set type="module"
npm pkg set scripts.test="vitest run"
npm pkg set scripts.typecheck="tsc --noEmit"
mkdir -p src/legacy test

旧实现把验证、计算和返回值组装放在同一个文件里。这不是夸张的坏代码,而是很多团队真正遇到的“能跑,但没人想碰”的代码。

// src/legacy/orderProcessor.js
export function processOrder(order) {
  if (!order || !Array.isArray(order.items) || order.items.length === 0) {
    return { status: "error", message: "items is required" };
  }

  const subtotal = order.items.reduce((sum, item) => {
    return sum + item.price * item.qty;
  }, 0);

  const discount = order.customer?.type === "vip" ? subtotal * 0.1 : 0;

  return {
    status: "confirmed",
    total: subtotal - discount,
    items: order.items,
    discount
  };
}

先写测试,锁定现有行为。这里测试的是“不能被破坏的行为”,不是马上设计更完美的业务模型。

// test/orderProcessor.test.ts
import { describe, expect, it } from "vitest";
import { processOrder } from "../src/legacy/orderProcessor.js";

describe("processOrder legacy behavior", () => {
  it("calculates total for a regular customer", () => {
    const result = processOrder({
      items: [
        { id: "A1", qty: 2, price: 1000 },
        { id: "B2", qty: 1, price: 500 }
      ],
      customer: { id: "C1", type: "regular" }
    });

    expect(result).toMatchObject({
      status: "confirmed",
      total: 2500,
      discount: 0
    });
  });

  it("applies a 10 percent VIP discount", () => {
    const result = processOrder({
      items: [{ id: "A1", qty: 1, price: 10000 }],
      customer: { id: "C2", type: "vip" }
    });

    expect(result.status).toBe("confirmed");
    expect(result.total).toBe(9000);
    expect(result.discount).toBe(1000);
  });

  it("returns an error when items are empty", () => {
    const result = processOrder({
      items: [],
      customer: { id: "C3", type: "regular" }
    });

    expect(result.status).toBe("error");
    expect(result.message).toContain("items");
  });
});

运行 npm test。通过后,再让 Claude Code 做改动。

请阅读 @src/legacy/orderProcessor.js 和 @test/orderProcessor.test.ts。
在保持现有测试通过的前提下迁移到 TypeScript。

约束:
- 保留公开函数名 processOrder
- 保持 status、total、discount、message 的兼容行为
- 先加类型定义,再拆分职责
- 修改后运行 npm test 和 npm run typecheck
- 说明每个差异保留了什么兼容性

TypeScript 化后的结构

改造后的结构把类型、验证、计算和编排分开。这样做不是为了抽象而抽象,而是为了让金额相关逻辑更容易审核。

// src/orderTypes.ts
export type CustomerType = "regular" | "vip";

export type OrderItem = {
  id: string;
  qty: number;
  price: number;
};

export type OrderInput = {
  items: OrderItem[];
  customer: {
    id: string;
    type: CustomerType;
  };
};

export type OrderResult =
  | {
      status: "confirmed";
      total: number;
      items: OrderItem[];
      discount: number;
    }
  | {
      status: "error";
      message: string;
    };

// src/validators.ts
import type { OrderInput } from "./orderTypes";

export function validateOrder(order: OrderInput | null | undefined): string | null {
  if (!order || !Array.isArray(order.items) || order.items.length === 0) {
    return "items is required";
  }
  return null;
}

// src/calculators.ts
import type { CustomerType, OrderItem } from "./orderTypes";

export function calculateSubtotal(items: OrderItem[]): number {
  return items.reduce((sum, item) => sum + item.price * item.qty, 0);
}

export function calculateDiscount(subtotal: number, customerType: CustomerType): number {
  return customerType === "vip" ? subtotal * 0.1 : 0;
}

// src/orderProcessor.ts
import { calculateDiscount, calculateSubtotal } from "./calculators";
import type { OrderInput, OrderResult } from "./orderTypes";
import { validateOrder } from "./validators";

export function processOrder(order: OrderInput): OrderResult {
  const validationMessage = validateOrder(order);
  if (validationMessage) {
    return { status: "error", message: validationMessage };
  }

  const subtotal = calculateSubtotal(order.items);
  const discount = calculateDiscount(subtotal, order.customer.type);

  return {
    status: "confirmed",
    total: subtotal - discount,
    items: order.items,
    discount
  };
}

把测试 import 改成 ../src/orderProcessor,再运行 npm testnpm run typecheck。如果差异保持这种规模,审核者还能看清楚发生了什么。若同一个 PR 同时做目录移动、依赖升级、格式化、命名变化和业务规则变化,风险会明显上升。

依赖升级要分开处理

另一个常见失败是把重构和依赖大版本升级混在一起。测试失败时,你无法判断是类型迁移、API 破坏性变更、构建工具变更,还是业务逻辑变了。

可以先让 Claude Code 只做清单。

请阅读 package.json 和 lockfile。
现在不要更新任何依赖。

请输出表格:
- package
- 当前版本
- 推荐目标版本
- 是否是 major upgrade
- 官方迁移指南 URL
- 可能影响的文件
- 更新前应该补的测试

涉及删除文件、迁移脚本、部署命令时,权限要保守。官方 Claude Code permissions 文档值得在团队导入前阅读。AI 执行得快不是问题,问题是它是否绕过了本来需要人工确认的风险点。

具体陷阱

第一个陷阱是没有测试就重构。代码变清爽了,但金额舍入、折扣条件、错误文案变了,就是回归问题。

第二个陷阱是把 Claude Code 的建议当成业务规则。更符合 TypeScript 风格的返回值,不代表旧客户端能接受。

第三个陷阱是巨大 PR。类型迁移、逻辑拆分、依赖更新、文件移动和格式化最好拆开提交。

第四个陷阱是过度改善错误处理。旧系统里奇怪的 null、字符串或 HTTP 状态码,可能已经是外部契约的一部分。

第五个陷阱是不写变更说明。让 Claude Code 在 PR 里写兼容性说明、手动验证步骤和回滚方法,可以减少之后的维护成本。

审核流程和下一步

这篇文章可以和重构自动化指南Claude Code 与 TDD文档自动生成一起使用。团队导入时,也建议把禁止修改区域、测试命令、领域术语和审核规则写进 CLAUDE.md 最佳实践

ClaudeCodeLab 提供 Claude Code 培训、CLAUDE.md 模板和遗留系统现代化咨询。目标不是用 AI 一次性重写,而是建立可重复的工作流,让测试、权限和人工审核一起降低风险。

实际验证结果

我按本文流程创建了 legacy-modernization-demo:先写旧 JavaScript 订单处理器,再用 Vitest 固定 3 个行为,然后迁移到 TypeScript 并重新跑测试和类型检查。最有效的一点是把“只读审计”和“允许编辑”分成两次请求。这样 Claude Code 生成的差异更小,金额计算、VIP 折扣和空订单错误的兼容性也更容易确认。

#Claude Code #遗留代码 #重构 #技术债 #现代化
免费

免费 PDF: Claude Code 速查表

输入邮箱即可获取一页 PDF,整理常用命令、审查习惯和安全工作流。

我们会妥善保护你的信息,不发送垃圾邮件。

把 Claude Code 变成真正能带来结果的工作流

先领取中文说明的免费 PDF,再进入英文商品页选择合适的教材。如果你需要团队落地、流程设计或内容变现支持,也可以直接咨询。

Masa

关于作者

Masa

专注 Claude Code 实务流程、团队导入和内容转化的工程师。