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

用 Claude Code 安全自动化重构的实战流程

用 Claude Code 做安全 refactoring:前后对比、测试、git diff、评审清单、常见风险和可复制 prompt。

用 Claude Code 安全自动化重构的实战流程

先定义安全边界,而不是直接让它改全部代码

用 Claude Code 自动化 refactoring 时,最危险的开场不是复杂任务,而是过于简单的一句话:把这个项目整理一下。这个请求看起来高效,但通常会得到一个很大的 diff,里面混合命名、拆函数、格式化、错误处理甚至行为变化。重构的定义是:改善内部结构,但不改变用户、API、测试和下游系统依赖的外部行为。

这篇文章把 Claude Code 当成实务伙伴,而不是魔法按钮:先调查,再给小计划,只改一个主题,运行验证命令,最后解释 git diff。官方的 common workflows 很适合做参考。命令权限和项目配置可以看 settings

如果团队还在建立使用规则,可以先阅读 Claude Code 权限设置Claude Code 上下文管理。本文更关注每天怎么做:先问什么,哪些任务适合初学者,怎样测试,怎样通过 diff 评审。

Masa 的验证笔记:小规模试用时,Claude Code 在变量重命名、抽取纯函数、补 TypeScript 类型、增加回归测试方面表现稳定。反过来,像“把这个 service 现代化”这种宽泛 prompt,很容易让 diff 变大。真正能长期使用的 workflow 往往很朴素:范围小、测试明确、diff 可读。

安全 workflow:调查、计划、一个 diff、验证

在团队形成信任之前,固定使用这个顺序。

步骤Claude Code 做什么人要检查什么
1. 调查阅读目标文件、依赖和测试范围是否过大
2. 计划给出不超过三步的小计划是否隐藏行为变化
3. 编辑只改一个主题diff 是否能读完
4. 验证运行 test、typecheck、lint失败是否解释清楚
5. 评审总结 git diff 和风险before/after 行为是否一致

第一条 prompt 不允许编辑。

请检查这个仓库,找出适合安全 refactoring 的候选项。
现在不要编辑任何文件。

条件:
- 不改变外部行为
- 每个 diff 最多 3 个文件
- 优先选择已有测试覆盖的区域
- 用表格输出候选项、原因、验证命令、风险

“现在不要编辑任何文件”很关键。Claude Code 读完代码后可能直接行动。把调查和实现分开,可以显著降低事故概率。

开始前先建分支并记录 baseline。

git status --short
git checkout -b refactor/safe-extract-order-total
npm test
npm run typecheck
npm run lint

项目命令可能不同,请以 package.json 为准。如果测试在修改前已经失败,要先记录。否则后面无法判断失败是原本存在,还是重构引入。

Use case 1: 从重命名和抽取纯函数开始

最安全的第一步是改善命名和抽取纯函数。纯函数指同样输入总是得到同样输出,并且不更新数据库、不发邮件、不调用外部 API、不修改全局状态。Claude Code 在这类任务上很适合,因为成功条件容易测试。

// before: src/domain/order.ts
export function calc(o: { items: { p: number; q: number }[]; d?: number }) {
  let t = 0;
  for (const i of o.items) {
    t += i.p * i.q;
  }
  if (o.d) {
    t = t - o.d;
  }
  return Math.max(t, 0);
}

这段代码很短,但 pqd 没有表达业务含义。让 Claude Code 先补测试,再改善名称。

请安全重构 src/domain/order.ts 中的 calc 函数。

要求:
- 先添加单元测试,固定当前行为,再修改实现
- 本次 diff 保留导出的函数名 calc
- 改善变量名和类型名
- 保留 total 不会小于 0 的规则
- 修改后运行 npm test -- order

理想的 after 应该很小。

// after: src/domain/order.ts
type OrderLine = {
  price: number;
  quantity: number;
};

type OrderInput = {
  items: OrderLine[];
  discount?: number;
};

export function calc(order: OrderInput): number {
  const subtotal = order.items.reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  );

  return Math.max(subtotal - (order.discount ?? 0), 0);
}

可复制的测试:

// src/domain/order.test.ts
import { describe, expect, it } from "vitest";
import { calc } from "./order";

describe("calc", () => {
  it("multiplies price and quantity", () => {
    expect(calc({ items: [{ price: 1200, quantity: 2 }] })).toBe(2400);
  });

  it("applies discount without returning a negative total", () => {
    expect(calc({ items: [{ price: 500, quantity: 1 }], discount: 800 })).toBe(0);
  });
});

评审只看被改动的文件。

git diff -- src/domain/order.ts src/domain/order.test.ts
npm test -- order
npm run typecheck

这里的评审问题不是“代码看起来是否聪明”,而是“相同输入是否仍然代表相同业务含义”。检查计算结果、导出函数名和测试描述。

Use case 2: 删除 any 时先固定边界类型

减少 any 很有价值,但一次性全项目清理通常是 mistake。应该从边界开始:API 响应、表单输入、配置文件、webhook、导入数据。这些地方是未知数据进入系统的入口。

// before: src/lib/user-api.ts
export async function fetchUser(id: string): Promise<any> {
  const response = await fetch(`/api/users/${id}`);
  return response.json();
}

export function getDisplayName(user: any): string {
  return user.profile.displayName || user.name;
}

prompt 要给出窄目标,并说明缺失数据的处理方式。

请减少 src/lib/user-api.ts 中的 any。

要求:
- 为 API 响应添加类型
- 不改变 fetch URL 和返回值含义
- profile 缺失时 getDisplayName 不能崩溃
- 为当前 display name 行为添加测试
- 运行 npm test -- user-api 和 npm run typecheck

一个可接受的第一版 diff:

// after: src/lib/user-api.ts
export type UserResponse = {
  id: string;
  name: string;
  profile?: {
    displayName?: string;
  };
};

export async function fetchUser(id: string): Promise<UserResponse> {
  const response = await fetch(`/api/users/${id}`);
  return response.json() as Promise<UserResponse>;
}

export function getDisplayName(user: UserResponse): string {
  return user.profile?.displayName ?? user.name;
}

注意,这个 cast 并不会验证运行时数据。如果项目需要真正的输入安全,可以在第二个 diff 中加入 zod 或项目已有 parser。不要把“删除 any”和“引入验证库”混在第一个初学者 diff 里。

// src/lib/user-api.test.ts
import { describe, expect, it } from "vitest";
import { getDisplayName, type UserResponse } from "./user-api";

describe("getDisplayName", () => {
  it("uses profile displayName when present", () => {
    const user: UserResponse = {
      id: "u1",
      name: "Masa",
      profile: { displayName: "Masa I." },
    };

    expect(getDisplayName(user)).toBe("Masa I.");
  });

  it("falls back to name when profile is missing", () => {
    expect(getDisplayName({ id: "u2", name: "Guest" })).toBe("Guest");
  });
});

评审时要找危险捷径:as any、吞掉错误、用空字符串当 fallback、改变 optional field 的语义。类型更安全的 diff 仍然可能破坏行为。

Use case 3: 大函数先建立 test harness 再拆

大 service 函数很适合优化,但风险也高。订单、计费、权限、通知、导入任务常常混合验证、计算、持久化和副作用。让 Claude Code 先抽取一个纯计算片段,不要一次改完整流程。

// before: src/services/order-service.ts
export async function createOrder(input: CreateOrderInput) {
  if (input.items.length === 0) {
    throw new Error("items required");
  }

  const subtotal = input.items.reduce((sum, item) => sum + item.price * item.quantity, 0);
  const shippingFee = subtotal >= 10000 ? 0 : 800;
  const total = subtotal + shippingFee;

  const order = await db.order.create({
    data: { userId: input.userId, subtotal, shippingFee, total },
  });

  await mailer.sendOrderCreated(order.id);
  return order;
}

prompt 要明确排除项。

请让 src/services/order-service.ts 中的 createOrder 变小。

本次 diff 只做:
- 把运费和总额计算抽成纯函数
- 函数名为 calculateOrderTotals
- 为 calculateOrderTotals 添加单元测试
- 保持数据库写入和邮件发送顺序不变

本次 diff 不做:
- 修改数据库 schema
- 修改错误文案
- 修改 API 响应结构
- 移动无关函数
- 格式化整个文件

after 可以保持朴素。

// after: src/services/order-service.ts
export function calculateOrderTotals(items: OrderItem[]) {
  const subtotal = items.reduce(
    (sum, item) => sum + item.price * item.quantity,
    0
  );
  const shippingFee = subtotal >= 10000 ? 0 : 800;

  return {
    subtotal,
    shippingFee,
    total: subtotal + shippingFee,
  };
}

评审命令:

git diff --stat
git diff -- src/services/order-service.ts
git diff -- src/services/order-service.test.ts
npm test -- order-service

如果 Claude Code 顺手格式化了无关代码,就缩小 diff。

这个 diff 太大。
请撤回纯格式化修改,只保留 calculateOrderTotals 的抽取和测试。
不要改变外部行为、错误文案、数据库写入、邮件发送顺序。

用 git diff 评审,不要凭感觉

Claude Code 的说明有帮助,但 diff 才是事实。

git diff --check
git diff --stat
git diff --name-only
git diff --word-diff -- src/domain/order.ts
评审点需要检查
行为输入、输出、异常、HTTP status、持久化顺序是否不变
diff 大小是否能在一次人工 review 中读完
测试原有行为是否被新旧测试保护
类型是否新增 as any、危险 cast、忽略错误
副作用API、邮件、计费、删除、权限顺序是否不变
摘要Claude Code 的总结是否与实际 diff 一致

可复用 review prompt:

请 review 这个 git diff。

检查:
- 是否超出 refactoring 范围
- 哪些行为没有测试保护
- 是否有危险 cast 或吞掉错误
- 哪些文件需要人工重点检查

按三类输出:
- 看起来安全
- 需要人工确认
- 必须修改
并给出文件名和原因。

Pitfall: 常见 failure 和 risk

第一个 failure 是 prompt 太宽。

把这个 service layer 变干净。

这会把抽函数、命名、错误设计、文件移动、格式化混在一起。更安全的写法是:

只把 createOrder 中的运费计算抽成纯函数。
不要改变处理顺序、错误文案、返回值。

第二个 risk 是没有测试就接受“看起来很干净”的 diff。可读性变好,不代表边界条件没变。折扣、免运费、权限拒绝、重试逻辑、null 处理都要先测试。第三个 mistake 是把格式化和结构重构放在一起。Prettier 改了几百行后,真正的行为变化会被隐藏。第四个 risk 是一开始就开放过多命令权限。先从读取、test、typecheck、lint 开始,稳定后再扩大。

适合初学者的推荐顺序

如果你第一次把这套方法放进真实项目,不要从最复杂的 service 开始。推荐顺序是:第一天只做变量名和类型名整理,第二天做纯函数抽取,第三天减少一个边界文件里的 any,第四天再尝试拆分大函数。每次都保留独立 branch,并且只在测试通过、diff 可读、review checklist 全部满足时合并。

团队使用时,还可以把成功案例写进 CLAUDE.md。例如“订单金额计算只能先加测试再改实现”“邮件发送顺序不能改变”“权限判定相关代码必须人工二次确认”。这些规则越具体,Claude Code 越容易沿着安全路线工作。相反,如果只写“保持代码质量”,模型不知道哪些风险最重要。

更实际的做法是为每类 refactoring 准备模板 prompt。命名改善、类型收紧、函数抽取、测试补强分别用不同模板。这样新人也不会一上来让 Claude Code 大范围重写。自动化的目标不是让人完全不看代码,而是把重复劳动缩小到可验证、可复用、可审查的步骤里。

Checklist 与 CTA

## Refactoring checklist

- [ ] 本次修改只有一个目的
- [ ] 编辑前已运行 baseline tests
- [ ] before/after 行为等价
- [ ] 新测试或旧测试保护了行为
- [ ] git diff --stat 足够小
- [ ] git diff --check 通过
- [ ] 没有新增 any、危险 cast、吞掉错误
- [ ] DB、邮件、计费、删除、权限顺序不变

最终 prompt:

执行一个安全 refactoring diff。

目标:
- src/services/order-service.ts
- src/services/order-service.test.ts

成功条件:
- 外部行为不变
- 抽取 calculateOrderTotals
- 旧测试和新测试通过
- 报告 git diff --stat 和执行过的命令

禁止:
- DB schema 修改
- API response 修改
- 错误文案修改
- 编辑无关文件

我的验证结果是,最有效的两个习惯是:先要求“只计划不编辑”,以及每次实现后强制输出 git diff 总结。你也可以把这个 workflow 和 Claude Code review checklist 以及 CLAUDE.md best practices 结合起来,让团队重复使用。

如果团队想把 Claude Code 安全落地,Claude Code training 可以一起设计权限、prompt、review 规则和 workflow。重构自动化真正有价值的时候,不是 diff 看起来很炫,而是维护风险稳定下降。

#Claude Code #refactoring #code quality #automation #TypeScript
免费

免费 PDF: Claude Code 速查表

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

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

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

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

Masa

关于作者

Masa

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