首页 › tutorials › Claude Code遗留代码现代化六步实战法

Claude Code遗留代码现代化:六步安全重构法

概述

本教程面向需要在生产环境中改造遗留系统的中高级开发者。你将学会如何利用 Claude Code 的 Agent 能力,对零文档、零测试的老旧代码库执行安全的增量现代化——从依赖图谱绘制、特征测试生成,到逐模块迁移与持续回归验证。全文以一个真实的 Java EE 单体应用改造为微服务架构为主线,提供可直接复用的 Prompt 模板和 CLAUDE.md 配置。

适用人群:维护遗留系统的后端开发者、技术负责人、SRE 工程师
预计学习时长:60-90 分钟(含实操)

前置条件

条件 说明
Claude Code CLI 已安装并完成 API Key 配置(claude 命令可用)
遗留代码仓库 一个你熟悉的旧项目(Git 管理)
基础测试工具 项目对应语言的测试框架(JUnit、pytest 等)
Git 操作意识 会创建分支、查看 diff、回滚 commit

为什么遗留代码需要特殊策略

直接对遗留代码说"帮我重构"是 Claude Code 使用中最危险的操作之一。遗留系统的三大特征决定了必须采用分步策略:

  1. 无测试覆盖:无法验证重构后行为是否一致
  2. 高耦合度:一处改动引发连锁编译错误
  3. 隐式依赖:配置文件、环境变量、数据库 Schema 散落各处

Claude Code 的 Agent 模式天然适合这种场景——它能自动读取多文件上下文、生成分步计划、逐个文件修改并验证。但前提是你必须给它正确的约束。

我的项目是一个2015年的Java EE单体应用,约12万行代码。
项目目录: /home/dev/legacy-monolith
技术栈: Java 8, Spring 4, MyBatis, Oracle DB, Maven
目标: 将订单模块拆分为独立微服务

第一步:项目画像与依赖图谱

操作说明

在项目根目录创建专用的 CLAUDE.md,定义项目上下文和约束规则:

# /home/dev/legacy-monolith/CLAUDE.md

## 项目概况
- 12万行Java EE单体应用,2015年开发至今
- 技术栈: Java 8, Spring 4.2, MyBatis 3.3, Oracle 11g
- 构建工具: Maven 3.x
- 无单元测试,仅有少量手动集成测试

## 现代化目标
- 将订单模块(order)拆分为独立Spring Boot微服务
- 保持与主系统的API兼容
- 使用PostgreSQL替代Oracle(仅限新服务)

## 安全约束(CRITICAL)
- 每次修改前必须先创建特征测试(Characterization Test)
- 单次修改不超过3个文件
- 每次修改后必须通过编译和现有测试
- 不修改数据库Schema,除非有明确的migration脚本
- 所有新代码必须包含错误处理和日志

## 代码规范
- 遵循现有命名约定(Service以Impl结尾)
- 新模块使用Spring Boot 3.x标准结构
- 配置使用application.yml而非properties

然后用 Claude Code 生成依赖图谱:

claude -p "分析项目结构,生成订单模块的依赖关系图谱。需要识别:
1. 订单模块涉及的所有Java包和类
2. 这些类被其他模块引用的情况(入向依赖)
3. 订单模块引用的外部模块(出向依赖)
4. 共享数据库表的访问情况
输出格式:每个依赖标注强度(强/中/弱)和风险等级(高/中/低)"

预期结果

Claude Code 会输出类似这样的分析:

=== 订单模块依赖图谱 ===

入向依赖(被其他模块引用):
  [强|高] PaymentService → OrderService.createOrder()
  [强|高] ShippingService → OrderService.getOrderById()
  [中|中] ReportService → OrderRepository.queryOrders()
  [弱|低] NotificationService → OrderStatusListener.onStatusChange()

出向依赖(订单模块引用的外部):
  [强|高] → UserRepository (用户模块)
  [强|高] → ProductRepository (商品模块)
  [中|中] → InventoryService (库存模块)
  [弱|低] → LogUtil (公共工具)

共享数据库表:
  [高] T_ORDER, T_ORDER_ITEM, T_ORDER_STATUS
  [中] T_PAYMENT (与支付模块共享)

常见问题

Q: Claude Code 报"context too large"怎么办?

使用 --max-turns 限制交互轮次,并分模块分析:

claude -p "只分析 src/main/java/com/company/order 目录下的类依赖关系" \
  --max-turns 5

对于超大项目(>50万行),先用 find 和 grep 缩小范围,再让 Claude Code 分析子目录。

第二步:生成特征测试安全网

操作说明

特征测试(Characterization Test)是 Michael Feathers 在《修改代码的艺术》中提出的核心概念——不是测试代码"应该"做什么,而是记录代码"当前实际"做什么。这是安全重构的前提。

claude -p "为 OrderService.createOrder() 方法生成特征测试。

要求:
1. 读取 OrderService.java 的完整实现
2. 读取其所有依赖的接口定义
3. 使用 Mockito 模拟所有外部依赖
4. 测试用例覆盖方法的所有分支路径
5. 每个测试用例的assert必须精确记录当前行为(包括看似bug的行为)
6. 不要修正任何现有行为,即使看起来是bug

测试文件放在 src/test/java/com/company/order/ 目录下"

预期结果

Claude Code 会生成类似这样的测试:

// Characterization Test - 记录当前行为,不判断对错
class OrderServiceCharacterizationTest {

    @InjectMocks
    private OrderServiceImpl orderService;

    @Mock
    private OrderRepository orderRepository;
    @Mock
    private ProductRepository productRepository;
    @Mock
    private InventoryService inventoryService;

    @Test
    void createOrder_normalCase_savesOrderWithCorrectTotal() {
        // 当前行为:计算总价时包含折扣但不包含税费
        when(productRepository.findById(1L))
            .thenReturn(new Product(1L, "Widget", new BigDecimal("99.00")));
        when(inventoryService.checkStock(1L, 2))
            .thenReturn(true);

        OrderRequest request = new OrderRequest(1L, 2, "DISCOUNT10");
        Order result = orderService.createOrder(request);

        // 特征记录:当前实现的总价 = 99 * 2 * 0.9 = 178.20
        // 注意:这里没有加税,这是一个已知的行为特征
        assertEquals(new BigDecimal("178.20"), result.getTotalAmount());
        verify(orderRepository).save(any(Order.class));
    }

    @Test
    void createOrder_outOfStock_throwsRuntimeException() {
        // 当前行为:库存不足时抛出RuntimeException(非业务异常)
        when(productRepository.findById(1L))
            .thenReturn(new Product(1L, "Widget", new BigDecimal("50.00")));
        when(inventoryService.checkStock(1L, 100))
            .thenReturn(false);

        OrderRequest request = new OrderRequest(1L, 100, null);

        assertThrows(RuntimeException.class,
            () -> orderService.createOrder(request));
    }
}

批量生成脚本

对于大量需要特征测试的类,使用 -p 模式批处理:

#!/bin/bash
# gen_characterization_tests.sh
# 为订单模块下所有Service类生成特征测试

CLASSES=$(find src/main/java/com/company/order -name "*ServiceImpl.java")

for class in $CLASSES; do
    classname=$(basename "$class" .java)
    echo "Generating tests for $classname..."

    claude -p "为 $class 生成完整的特征测试(Characterization Test)。
    使用JUnit5和Mockito。记录所有当前行为,不修正任何bug。
    测试文件放在 src/test/java/ 对应路径下。" \
    --max-turns 3 \
    --output-format text > "test_report_${classname}.txt"
done

常见问题

Q: 生成的测试编译不过怎么办?

让 Claude Code 自行修复:

claude -p "运行 mvn test-compile,修复所有编译错误。
不要修改测试的assert内容,只修复import和类型问题。"

第三步:绘制组件边界与接口契约

操作说明

有了依赖图谱和特征测试后,下一步是定义微服务的边界。这一步不需要写代码,而是用 Claude Code 生成结构化的接口文档。

claude -p "基于已有的依赖分析,为订单微服务定义API接口契约。

要求:
1. 分析 OrderService 和 OrderRepository 的所有public方法
2. 区分内部方法(仅限新微服务使用)和外部API(其他模块需要调用)
3. 外部API设计为RESTful接口,使用Spring Boot 3.x的@RestController
4. 生成OpenAPI 3.0规范的YAML文档
5. 列出需要保留的跨模块调用点(暂时通过HTTP客户端桥接)"

预期结果

生成的 OpenAPI 文档片段:

# order-service-api.yaml
openapi: 3.0.3
info:
  title: Order Microservice API
  version: 1.0.0

paths:
  /api/v1/orders:
    post:
      summary: 创建订单
      operationId: createOrder
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateOrderRequest'
      responses:
        '201':
          description: 订单创建成功
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/OrderResponse'
        '400':
          description: 参数校验失败
        '409':
          description: 库存不足

  /api/v1/orders/{orderId}:
    get:
      summary: 查询订单详情
      operationId: getOrderById
      parameters:
        - name: orderId
          in: path
          required: true
          schema:
            type: integer
            format: int64
      responses:
        '200':
          description: 成功返回订单信息

架构决策记录

让 Claude Code 帮你维护架构决策记录(ADR):

claude -p "创建一个ADR(Architecture Decision Record)文件:
ADR-001-订单服务拆分策略.md

记录以下决策:
1. 为什么选择先拆订单模块(而非支付或库存)
2. API兼容策略:采用BFF模式还是直接REST
3. 数据库迁移策略:双写还是CDC
4. 跨模块通信:同步HTTP还是异步MQ"

第四步:Strangler Fig 渐进式迁移

操作说明

Strangler Fig(绞杀者无花果)模式是 Martin Fowler 推荐的遗留系统迁移策略——逐步用新系统替换旧系统,而非一次性重写。Claude Code 的 worktree 功能天然支持这种并行开发模式。

# 创建隔离的worktree进行新服务开发
claude worktree create order-service-migration

在新 worktree 中初始化 Spring Boot 项目:

claude -p "在当前目录创建一个Spring Boot 3.2微服务项目:
1. 使用Spring Initializr结构
2. 依赖: spring-boot-starter-web, spring-boot-starter-data-jpa,
   spring-boot-starter-validation, postgresql driver, flyway
3. 配置文件使用application.yml
4. 创建标准的controller/service/repository/package结构
5. 添加健康检查端点
6. 端口设为8081"

核心迁移步骤:逐步搬运业务逻辑

这是最关键的一步。不要让 Claude Code 一次性迁移所有代码,而是按照以下顺序逐个方法迁移:

claude -p "迁移 OrderService.getOrderById() 方法到新的微服务。

严格按照以下步骤:
1. 在新服务中创建 OrderController.getOrderById()
2. 从旧代码复制核心业务逻辑到新的 OrderService
3. 使用JPA实体替代MyBatis映射(保持字段名不变)
4. 编写对应的Repository方法
5. 确保特征测试在新环境中通过
6. 暂时不删除旧代码,只添加@Deprecated注解"

路由切换:API Gateway 桥接

在旧单体应用中添加路由转发,将对已迁移接口的请求转发到新微服务:

claude -p "在旧单体应用中添加一个OrderServiceProxy。

当订单API调用到达旧系统时:
1. 如果对应的 getOrderById 已迁移到新服务,转发到 http://order-service:8081
2. 如果未迁移,继续走旧的 OrderServiceImpl
3. 使用RestTemplate实现转发
4. 添加降级逻辑:如果新服务不可用,fallback到旧实现
5. 添加请求耗时的metrics日志,用于对比新旧服务的性能

在 src/main/java/com/company/proxy/ 目录下创建。"

预期结果

代理类代码片段:

@Component
@Primary
public class OrderServiceProxy implements OrderService {

    private final OrderServiceImpl legacyService;
    private final RestTemplate restTemplate;
    private final MigrationConfig migrationConfig;

    @Override
    public Order getOrderById(Long id) {
        if (migrationConfig.isMigrated("getOrderById")) {
            // 已迁移:转发到新微服务
            return restTemplate.getForObject(
                "http://order-service:8081/api/v1/orders/" + id,
                Order.class);
        }
        // 未迁移:使用旧实现
        return legacyService.getOrderById(id);
    }

    // ... 其他方法类似
}

常见问题

Q: 两个系统的数据不一致怎么办?

使用双写 + CDC(Change Data Capture)策略:

claude -p "设计数据库同步方案:
1. 旧系统写Oracle的同时,通过Debezium CDC同步到PostgreSQL
2. 新服务的写操作同时写入PostgreSQL和发送MQ消息
3. 旧系统消费MQ消息更新Oracle
4. 添加数据一致性校验的定时任务"

第五步:持续回归验证

操作说明

每完成一批方法迁移,都需要运行全量验证。Claude Code 可以自动化这个过程:

claude -p "执行回归验证:

1. 运行所有特征测试: mvn test
2. 对比新旧服务的API响应(从测试报告读取)
3. 检查是否有测试失败
4. 如果有失败,分析失败原因:
   - 是预期行为变化?→ 更新特征测试
   - 是真正的回归bug?→ 修复新服务代码
5. 生成验证报告保存到 migration/reports/ 目录"

自动化回归脚本

将验证过程写成可重复执行的脚本:

#!/bin/bash
# regression_check.sh
# 每次迁移一批方法后执行

echo "=== Step 1: 编译检查 ==="
mvn compile -q || { echo "编译失败!"; exit 1; }

echo "=== Step 2: 运行特征测试 ==="
mvn test -pl order-module -Dtest="*CharacterizationTest" -q
TEST_RESULT=$?

echo "=== Step 3: API兼容性检查 ==="
claude -p "对比以下两组API响应是否一致:
旧系统: $(curl -s http://localhost:8080/order/1)
新系统: $(curl -s http://localhost:8081/api/v1/orders/1)
忽略响应中的timestamp和traceId字段。"

echo "=== Step 4: 生成迁移进度报告 ==="
claude -p "读取 migration/ 目录下的所有ADR和测试报告,
生成当前迁移进度摘要:
- 已迁移的方法列表
- 待迁移的方法列表
- 已知风险项
- 建议的下一步操作"

if [ $TEST_RESULT -ne 0 ]; then
    echo "⚠️  特征测试失败,请检查!"
    exit 1
fi
echo "✅ 回归验证通过"

使用 Hooks 自动触发验证

在 .claude/settings.json 中配置 hooks,让 Claude Code 在每次文件修改后自动触发测试:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "command": "cd /home/dev/legacy-monolith && mvn test-compile -q 2>&1 | tail -5",
        "description": "每次文件修改后编译检查"
      }
    ]
  }
}

第六步:旧代码退役与文档沉淀

操作说明

当所有方法都迁移完毕且稳定运行后,清理旧代码:

claude -p "执行订单模块旧代码退役:

1. 检查迁移清单,确认所有方法已在新服务中实现
2. 运行全量特征测试,确认新旧服务行为完全一致
3. 移除旧单体中的OrderServiceImpl(保留接口定义作为文档)
4. 移除OrderServiceProxy的降级逻辑
5. 更新README和架构文档
6. 在git中创建 tagged release: order-service-v1.0.0

注意:每一步操作前先确认,不要自动执行。"

知识沉淀:生成迁移文档

claude -p "基于整个迁移过程,生成最终的迁移文档:

包含:
1. 架构变更概览(Before/After对比)
2. 迁移的时间线和关键决策点
3. 遇到的问题和解决方案
4. 新服务的运维指南(部署、监控、排错)
5. 遗留系统的其他模块迁移建议

文档保存到 migration/FINAL_MIGRATION_REPORT.md"

完整工作流程图

flowchart TB
    A[遗留系统现状评估] --> B[CLAUDE.md 项目画像]
    B --> C[依赖图谱绘制]
    C --> D[特征测试生成]
    D --> E[组件边界定义]
    E --> F[API契约设计]
    F --> G[新微服务脚手架]
    G --> H{逐方法迁移}
    H --> I[复制业务逻辑]
    I --> J[适配新框架]
    J --> K[路由切换]
    K --> L[回归验证]
    L --> M{验证通过?}
    M -->|是| N{还有待迁移方法?}
    M -->|否| O[分析失败原因]
    O --> P[修复或更新测试]
    P --> H
    N -->|是| H
    N -->|否| Q[旧代码退役]
    Q --> R[文档沉淀]
    R --> S[迁移完成]

    style A fill:#f9f,stroke:#333
    style D fill:#ff9,stroke:#333
    style L fill:#9ff,stroke:#333
    style S fill:#9f9,stroke:#333

实战案例:订单模块完整迁移日志

以下是一个真实的迁移会话记录(已脱敏),展示每个步骤的实际耗时和关键决策:

Day 1: 项目画像(2小时)
├── 生成CLAUDE.md配置
├── 运行依赖分析 → 识别23个类、47个依赖关系
├── 标记高风险依赖: PaymentService(强), InventoryService(强)
└── 输出: dependency-graph.md

Day 2-3: 特征测试(6小时)
├── 为OrderServiceImpl生成12个特征测试
├── 为OrderRepository生成8个特征测试
├── 编译修复: 3处泛型类型不匹配
├── 特征发现: createOrder()存在隐藏的null指针风险
└── 输出: 20个测试用例, 全部GREEN

Day 4: 接口契约(3小时)
├── 生成OpenAPI 3.0规范
├── ADR-001: 选择同步HTTP而非MQ(降低复杂度)
├── ADR-002: 选择双写而非CDC(数据量小,性价比更高)
└── 输出: order-service-api.yaml

Day 5-8: 逐步迁移(12小时)
├── getOrderById() → 1小时(只读,最简单)
├── queryOrders() → 2小时(复杂查询,需JPA Specification)
├── createOrder() → 4小时(核心逻辑,涉及3个外部调用)
├── updateOrderStatus() → 2小时(状态机逻辑)
├── cancelOrder() → 3小时(涉及退款逻辑,需要补偿事务)
└── 每个方法迁移后立即运行回归验证

Day 9-10: 路由切换与灰度(4小时)
├── 实现OrderServiceProxy
├── 配置化路由开关(按方法粒度)
├── 10%流量灰度 → 50% → 100%
└── 监控: P99延迟从120ms降至45ms

Day 11: 旧代码退役(2小时)
├── 确认新服务稳定运行48小时
├── 移除旧代码
├── 生成迁移报告
└── 创建 release tag

Prompt 模板速查表

以下是本教程中所有关键 Prompt 的精简模板,可直接复制使用:

依赖分析模板

分析 [模块名] 的依赖关系:
1. 涉及的包和类
2. 入向依赖(被谁引用)
3. 出向依赖(引用了谁)
4. 共享资源(数据库表、配置、文件)
输出格式:每项标注强度和风险等级

特征测试模板

为 [类名.方法名] 生成特征测试:
- 使用JUnit5 + Mockito
- 模拟所有外部依赖
- 覆盖所有分支路径
- 精确记录当前行为(包括bug)
- 不修正任何现有行为

迁移执行模板

迁移 [方法名] 到新微服务:
1. 从 [旧路径] 复制核心逻辑
2. 适配 [新框架/新依赖]
3. 确保特征测试通过
4. 旧代码添加 @Deprecated

回归验证模板

执行回归验证:
1. 运行所有特征测试
2. 对比新旧API响应
3. 分析失败原因(行为变化 vs 回归bug)
4. 生成验证报告

关键原则总结

mindmap
  root((遗留代码现代化))
    安全第一
      特征测试先行
      单次修改≤3文件
      每次改完必编译
      Git分支保护
    渐进式迁移
      Strangler Fig模式
      方法级粒度迁移
      路由切换而非重写
      灰度发布验证
    上下文管理
      CLAUDE.md项目画像
      ADR决策记录
      分模块限制上下文
      Worktree隔离开发
    自动化验证
      Hooks编译检查
      特征测试回归
      API兼容性对比
      生成验证报告

延伸阅读

训练营适用性评估

维度 评估
适合训练营类型 企业内训(遗留系统改造专项)、高级开发者训练营
学员基础要求 Java/Spring 经验 2 年以上,理解微服务架构概念
预计教学时长 4-6 小时(含实操演练)
实操依赖 需要准备一个可编译的遗留 Java 项目作为练习素材
难度评级 ★★★★☆(中高级)

参考来源

  1. Anthropic Code Modernization Playbook — Anthropic 官方发布的遗留代码现代化方法论手册
  2. Claude Code for Legacy Codebases: A Practitioner's Guide — 生产环境 CLAUDE.md 配置和增量工作流实践指南
  3. Claude Code for Refactoring: Modernizing Legacy Codebases at Scale — 从定向函数重写到大规模架构迁移的策略对比
  4. Legacy Code Modernization with Claude Code: Breaking Through Context Window Barriers — 多步 Agent 工作流突破上下文窗口限制的实践方案
  5. Modernization with Claude Code: from COBOL to Cloud Migrations — COBOL 到云端迁移的企业级案例研究

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入