Skip to content
Published at:

第 6 章:代码编辑与重构

如果说对话和代码理解是 Claude Code 的"眼睛",那么代码编辑与重构就是它的"双手"。这是 Claude Code 区别于 Copilot 等补全工具的核心分水岭——它不是一个在你打字时帮你补全下一行的助手,而是一个能够理解你的需求、定位相关文件、直接修改代码的 Agent

本章深入讲解 Claude Code 的代码编辑机制:两个核心编辑工具(Edit 和 Write)的工作原理、Diff 审查工作流、内联编辑、批量重构,以及保证代码质量的回退策略和最佳实践。

学习完本章后,你将能够:

  • 理解 Edit 和 Write 工具的区别与适用场景
  • 熟练使用 Diff 审查工作流逐文件审核变更
  • 掌握内联编辑的快捷操作
  • 自信地执行批量重命名和跨文件重构
  • 建立完善的回退和验证体系

6.1 Edit 工具:精确替换

工作原理

Claude Code 的 Edit 工具遵循一个非常朴素但强大的原则:在文件中找到一段精确匹配的文本,将它替换为新文本

Edit(file_path, old_string, new_string)
  → 在 file_path 中搜索 old_string
  → 找到唯一匹配 → 替换为 new_string
  → 找不到匹配 或 找到多个匹配 → 报错,不修改任何内容

这个机制有几个重要含义:

  1. 精确匹配要求old_string 必须与文件中的内容完全一致,包括缩进、空格、换行
  2. 唯一性要求old_string 在文件中只能出现一次。如果出现多次,Edit 会失败(需要使用 replace_all: true
  3. 原子性:要么替换成功,要么完全不做修改——不会出现"改了一半"的情况

为什么是"精确替换"而非"行号替换"? 行号会随着代码的增删而变化。当 Claude 同时修改多个位置时,如果基于行号,前面的修改会导致后续的行号全部偏移。精确字符串匹配天然规避了这个问题。

Claude 什么时候使用 Edit

Claude Code 会自动判断使用 Edit 还是 Write 工具,判断逻辑如下:

场景使用工具原因
修改函数内几行代码Edit文件大部分不变,只改局部
重命名一个变量Edit精确替换标识符
修改函数签名Edit只改一行声明
在现有文件中添加一个新函数Edit在合适位置插入代码块
修复一个 bugEdit通常只涉及几行改动
调整配置文件的某个字段Edit精确替换一个值

实战示例

场景 1:重命名变量

假设你有一个文件 src/utils/user.ts

typescript
function getUser(id: number): User {
  const user = db.query('SELECT * FROM users WHERE id = ?', id);
  return user;
}

你可以在对话中说:

把 src/utils/user.ts 里的 getUser 函数改名为 fetchUserById

Claude Code 会调用 Edit 工具,将:

function getUser(id: number): User {

精确替换为:

function fetchUserById(id: number): User {

场景 2:修改函数签名

给 calculateTotal 函数加一个 discount 参数,默认值为 0

Claude Code 会:

  1. 读取包含 calculateTotal 的文件
  2. 找到函数定义行
  3. 用 Edit 工具将 calculateTotal(items: Item[]) 替换为 calculateTotal(items: Item[], discount: number = 0)
  4. 如果函数体内需要用到 discount 参数,同步修改函数体逻辑

场景 3:修复 bug

src/api/auth.ts 第 42 行的条件判断写反了,token 为空时应该返回 401 而不是继续执行

Claude Code 定位到那一行,用 Edit 精确替换错误的条件判断。

Edit 工具的约束

理解 Edit 的约束能帮你更好地指导 Claude:

约束 1:old_string 必须精确匹配

typescript
// 如果文件中是:
  const name = 'hello';  // 前面有两个空格缩进

// old_string 必须是:
'  const name = \'hello\';'  // ✅ 包含两个空格

// 而不是:
'const name = \'hello\';'    // ❌ 缺少缩进,匹配失败

实际使用中你不需要关心缩进

Claude Code 会先 Read 文件,获取精确的原始文本,然后构造 old_string。你只需要用自然语言描述"改哪里、改成什么"即可。

约束 2:默认只替换一处

如果你的文件中有 5 个地方都定义了 const API_URL =,而你只说"把 API_URL 改成 https://new-api.example.com",Edit 会报错,因为它找到了 5 个匹配。

解决方法:

把 src/config/ 目录下所有文件中的 API_URL 都改成 https://new-api.example.com

Claude Code 会使用 replace_all: true 参数,或在每个文件中分别调用 Edit。

约束 3:old_string 必须足够长以保证唯一性

如果 old_string 太短(如只匹配一个变量名),可能在文件中出现多次。Claude Code 会自动选择足够长的上下文来保证唯一性——例如不是只匹配 price,而是匹配 const price = item.price * quantity; 整行。


6.2 Write 工具:创建新文件

工作原理

Write 工具用于创建新文件或完全覆盖现有文件。与 Edit 不同,Write 不关心文件原来的内容——它会用你指定的内容替换整个文件。

Write(file_path, content)
  → 如果 file_path 不存在 → 创建新文件,写入 content
  → 如果 file_path 已存在 → 用 content 完全覆盖旧内容

Claude 什么时候使用 Write

场景使用工具原因
创建一个全新的组件文件Write文件不存在,直接创建
创建工具函数文件Write从零开始
创建配置文件Write新文件
完全重写一个文件Write内容变化太大,用 Edit 反而低效
新建类型定义文件Write新文件
创建测试文件Write新文件

实战示例

场景 1:创建新组件

在 src/components/ 下创建一个 Toast.vue 组件,支持 success / error / info 三种类型,3 秒后自动消失

Claude Code 会:

  1. 分析项目中现有组件的写法风格
  2. 用 Write 工具创建 src/components/Toast.vue
  3. 写入完整的组件代码

场景 2:创建工具函数

帮我在 src/utils/ 下创建一个 formatDate.ts,把 ISO 日期字符串格式化为 "YYYY 年 MM 月 DD 日" 格式

Claude Code 用 Write 创建文件:

typescript
// src/utils/formatDate.ts
export function formatDate(isoString: string): string {
  const date = new Date(isoString);
  const year = date.getFullYear();
  const month = String(date.getMonth() + 1).padStart(2, '0');
  const day = String(date.getDate()).padStart(2, '0');
  return `${year} 年 ${month} 月 ${day} 日`;
}

场景 3:创建配置文件

创建一个 .env.example 文件,列出项目需要的所有环境变量

Write vs Edit:Claude 自动选择

你不需要手动指定用哪个工具。Claude Code 会根据任务自动判断:

# 这些指令 → Claude 使用 Edit
"把第 15 行的 port 改成 8080"
"给 handleSubmit 函数加 try-catch"
"把 getUser 重命名为 fetchUser"

# 这些指令 → Claude 使用 Write
"创建一个 useDebounce 的自定义 hook"
"在 src/types/ 下新建 api.ts,定义 API 响应的类型"
"重写 src/legacy/helper.ts,按照新架构来"

了解工具机制的好处:当你理解了 Edit 的精确匹配约束后,如果 Claude 报告"找不到匹配的字符串",你不会困惑——你会知道可能是缩进问题,或者匹配的字符串已经被之前的修改改变了。同理,理解了 Write 是全量覆盖后,你会谨慎对待"重写整个文件"的指令。


6.3 Diff 审查工作流

这是本章最重要的章节。 Diff 审查是你与 Claude Code 协作的核心环节。没有经过审查的 AI 代码,就像没有经过 Code Review 的 PR——你不能假设它总是正确的。

Diff 的呈现方式

当 Claude Code 修改文件后,VSCode 侧边栏会展示每个被修改文件的 Diff 视图

  • 左右双栏对比:左边是修改前(红色标记删除),右边是修改后(绿色标记新增)
  • 顶部工具栏Accept All / Reject All 按钮,以及逐文件的接受/拒绝操作
  • 文件列表:所有被修改的文件以列表形式排列,可逐个点击查看
┌──────────────────────────────────────────────────┐
│  Diff 审查区                         [Accept All] │
│                        [Reject All]               │
├──────────────────────────────────────────────────┤
│  📄 src/utils/formatDate.ts      ✅ Accept  ✗ Reject │
│  📄 src/components/Header.vue   ✅ Accept  ✗ Reject │
│  📄 src/api/auth.ts             ✅ Accept  ✗ Reject │
├──────────────────────────────────────────────────┤
│                                                    │
│   // 左栏(原始)  │  // 右栏(修改后)              │
│   - import { getUser } │ + import { fetchUserById } │
│   - const u = getUser(id) │ + const u = fetchUserById(id) │
│                                                    │
└──────────────────────────────────────────────────┘

逐文件审查:永远不要一次性全部接受

核心原则:逐文件审查,逐文件接受。

一次性 Accept All 是所有 Claude Code 新手的通病。当你按下 Accept All 时,你等于跳过了 Code Review——而 AI 生成的代码有 5%~15% 的概率存在问题(逻辑错误、不符合项目规范、遗漏边界情况等)。

推荐的审查流程:

第 1 步:浏览文件列表,判断变更范围是否合理

第 2 步:选择变更最多的文件,优先审查

第 3 步:逐行检查 Diff:
         - 删除的代码是否真的不再需要?
         - 新增的代码逻辑是否正确?
         - 变量名、函数名是否符合项目规范?
         - 是否有遗漏的边界情况?

第 4 步:审查通过 → 点击 Accept
         有问题 → 在 Diff 中手动编辑,或 Reject 后让 Claude 重来

第 5 步:重复 2-4,直到所有文件审查完毕

在 Diff 中手动编辑

这是一个被很多人忽略的强大功能:你可以在 Diff 视图的右侧(修改后的代码)中直接编辑

适用场景:

  • Claude 的修改方向正确,但细节需要微调——比如变量命名、注释措辞
  • Claude 把 90% 的事情做对了,剩下 10% 自己改比让 Claude 重来更快
  • 你想在 Claude 的代码上叠加自己的改动

操作方法:

  1. 在 Diff 视图中,点击右侧(新代码)的任意位置
  2. 像编辑普通文件一样直接修改
  3. 修改完成后点击 Accept

这体现了人机协作的核心哲学:Claude 负责 90% 的体力活,你负责 10% 的判断力和微调。 在 Diff 中手动编辑比"Reject → 重新描述需求 → 等待 Claude 再次生成 → 再次审查"高效得多。

Accept 和 Reject 的选择策略

操作什么时候用注意事项
Accept(单文件)该文件的修改完全符合预期接受后文件立即被写入磁盘
Reject(单文件)该文件的修改方向不对或有严重错误可以重新描述需求让 Claude 再来一次
Accept All所有修改都很简单且明显正确(如格式化、批量重命名)不推荐用于逻辑修改
Reject All整个修改方向完全错误,需要重新思考方案比逐个 Reject 快,但会丢失部分正确的修改

Cmd+Z 撤销已接受的变更

如果你接受了某个文件的修改,但随后发现有问题——VSCode 的 Cmd+Z 可以撤销

Claude Code 的文件修改与 VSCode 的编辑历史是打通的。接受一个 Diff 等于在 VSCode 中执行了一次编辑操作,因此可以像撤销任何手工编辑一样撤销它。

接受修改 → 发现有问题 → Cmd+Z → 文件恢复到修改前的状态

注意Cmd+Z 的撤销范围是有限的(VSCode 的编辑历史上限)。如果接受了大量修改后又做了很多其他操作,可能无法简单地撤销回 Diff 前的状态。这就是为什么 Git 是更可靠的安全网——见 6.7 撤销与回退策略

先读后改:让 Claude 先展示计划

对于复杂的修改,在 Claude 动手改代码之前,先让它告诉你它打算改什么

# ❌ 直接让它改——你只能事后审查
"把 src/api/ 下所有 API 函数都加上错误重试逻辑"

# ✅ 先让它展示计划——你在动手前就能纠正方向
"我打算给 src/api/ 下所有 API 函数加上错误重试逻辑。
先不要修改代码,告诉我你的方案:你会改哪些文件、每个文件怎么改。"

Claude Code 会输出详细的修改计划:

我计划修改以下文件:

1. src/api/client.ts — 新增 retryRequest 工具函数,封装 fetch 调用
2. src/api/auth.ts — login/register/logout 三个函数用 retryRequest 替换 fetch
3. src/api/users.ts — getUser/updateUser/deleteUser 同上
4. src/types/api.ts — 新增 RetryConfig 类型定义

每个函数的变化模式:
  - fetch(url, options) → retryRequest(url, { ...options, retries: 3 })

预计修改 4 个文件,涉及 8 处调用替换。

确认方案无误后再说"开始吧",Claude 才会执行修改。


6.4 内联编辑(Cmd+Shift+I)

什么是内联编辑

内联编辑是 Claude Code for VSCode 提供的轻量级代码修改方式。与侧边栏对话不同,内联编辑直接在编辑器光标位置生效,不需要切换面板、不需要审查 Diff。

操作流程

1. 在编辑器中,将光标放在你要修改/添加代码的位置
2. 按 Cmd+Shift+I(Windows: Ctrl+Shift+I)
3. 输入你的指令,例如"给这个函数加 try-catch"
4. Claude 在光标位置生成代码,以灰色预览形式显示
5. Tab → 接受,Escape → 拒绝

灰色预览模式

内联编辑的代码先以灰色预览形式出现在编辑器中:

// 原始代码:
function handleSubmit(data: FormData) {
  api.submit(data);
}

// 按 Cmd+Shift+I,输入"加 try-catch"
// 灰色预览出现:
function handleSubmit(data: FormData) {
  try {                           // ← 灰色,未确认
    api.submit(data);             // ← 灰色,未确认
  } catch (error) {              // ← 灰色,未确认
    console.error('提交失败', error); // ← 灰色,未确认
  }
}

// Tab → 灰色变正常,确认接受
// Escape → 灰色消失,恢复原样

内联编辑 vs 侧边栏对话:什么时候用哪个

维度内联编辑(Cmd+Shift+I)侧边栏对话
启动方式编辑器内 Cmd+Shift+I侧边栏 Cmd+Shift+L
适用规模单函数、单块的快速修改多文件、复杂逻辑、需要多轮讨论
审查方式灰色预览,Tab/Escape 二选一逐文件 Diff 审查,可手动编辑
上下文仅当前文件 + 光标位置整个项目 + 对话历史
撤销Escape 拒绝,或 Cmd+ZReject 单文件或 Reject All
最佳场景修改变量名、包裹 try-catch、调整一行逻辑提取公共函数、多文件重构、新增功能

选择指南:

问自己:这个修改涉及几个文件?

  1 个文件 + 几行代码 → 内联编辑(Cmd+Shift+I)
  1 个文件 + 整个函数 → 内联编辑 或 侧边栏对话
  2+ 个文件             → 侧边栏对话
  需要讨论方案          → 侧边栏对话

实战示例:

# 适合内联编辑的场景:
光标放在一个函数上 → Cmd+Shift+I → "把这个函数改成 async"
选中一段代码 → Cmd+Shift+I → "给这段代码加注释"
选中一个 if 条件 → Cmd+Shift+I → "把条件提取成一个有意义的变量"

# 适合侧边栏对话的场景:
"把 src/components/ 里三个组件的重复逻辑提取成公共 hook"
"重构 UserService,把业务逻辑从 controller 移到 service 层"
"给所有 API 调用加统一的错误处理中间件"

6.5 批量重命名与跨文件重构

场景 1:批量重命名

这是 Claude Code 最实用的能力之一。传统 IDE 的重命名功能通常只能处理当前项目内的引用,而 Claude Code 可以理解语义,处理注释、文档、配置文件中的相关引用。

示例:

把项目中所有用到 getUser 的地方改成 fetchUser,包括函数定义、调用、导入语句、注释和文档。

Claude Code 的处理流程:

1. Grep 搜索整个项目,找到所有包含 "getUser" 的文件
2. 逐个文件读取,区分哪些是真正的引用(排除不相关的字符串)
3. 对每个文件使用 Edit 工具,精确替换:
   - import { getUser } → import { fetchUser }
   - const user = getUser(id) → const user = fetchUser(id)
   - function getUser() → function fetchUser()
   - // 调用 getUser 获取用户信息 → // 调用 fetchUser 获取用户信息
4. 在 Diff 审查区展示所有修改,等待你逐文件确认

场景 2:提取公共逻辑

示例:

src/components/ 里有三个组件都写了相似的日期格式化逻辑,请帮我提取成一个公共工具函数。

Claude Code 的处理流程:

1. 读取 src/components/ 下的所有文件
2. 识别出三个组件中重复的日期格式化逻辑:
   - UserProfile.vue 第 45-48 行
   - ArticleCard.vue 第 32-35 行
   - CommentItem.vue 第 67-70 行
3. 创建 src/utils/formatDate.ts,统一实现
4. 修改三个组件文件:
   - 删除内联的格式化代码
   - 添加 import { formatDate } from '@/utils/formatDate'
   - 将调用处替换为 formatDate(...)
5. 展示所有修改,等待审查

场景 3:架构级重构

示例:

把 src/pages/ 下所有组件中的 API 调用提取到 src/api/ 目录下,按模块分文件:user.ts、article.ts、comment.ts

这种重构涉及 10+ 个文件,Claude Code 会:

  1. 用 Grep 找到所有 API 调用(fetch(axios. 等)
  2. 按业务模块分组
  3. 为每个模块创建 API 文件
  4. 修改所有页面组件,替换为 API 函数调用
  5. 使用 TodoWrite 追踪进度,确保不遗漏

批量操作的成功技巧

技巧 1:先搜索、再修改

# ❌ 不清楚影响范围就直接改
"把所有 const 改成 let"

# ✅ 先让 Claude 搜索,确认范围
"先搜索一下项目中所有 const 声明,告诉我大概有多少处,分布在哪些文件"
# 确认范围合理后
"好,把 src/utils/ 下的 const 改成 let"

技巧 2:分批次、模块化

# ❌ 一口气改 30 个文件
"把所有组件都改成 Composition API"

# ✅ 一次改一个目录
"先把 src/components/common/ 下的组件改成 Composition API"
# 审查、测试、提交后
"好,接下来改 src/components/layout/"

技巧 3:给 Claude 提供示例

把 src/api/ 下所有 API 函数的错误处理改成这种模式:

# 参考 src/api/auth.ts 的 handleApiError 用法:
try {
  const res = await fetch(url, options);
  if (!res.ok) throw new ApiError(res.status, await res.text());
  return await res.json();
} catch (error) {
  return handleApiError(error);
}

技巧 4:指定重构范围

# ❌ 范围模糊
"帮我重构一下错误处理"

# ✅ 范围精确
"只重构 src/api/ 目录下的错误处理,不要动 src/components/ 里的"

6.6 右键菜单功能

Claude Code for VSCode 在编辑器的右键菜单中集成了四个快捷功能。选中代码后右键即可使用,无需打开侧边栏对话。

菜单项触发方式功能适用场景
Ask Claude右键 → Ask Claude以选中代码为上下文,向 Claude 提问"这段代码为什么这样写?""这个算法有优化空间吗?"
Explain This右键 → Explain This解释选中代码的逻辑、流程和设计意图接手陌生代码时快速理解;Code Review 前自我检查
Fix This右键 → Fix This自动诊断选中代码的问题,并直接修复看到红色波浪线但不知道怎么修;逻辑明显有问题
Add to Context右键 → Add to Context将选中代码加入当前对话上下文,但不发送新消息积累多段代码后统一提问;构建精准的上下文组合

Ask Claude

选中一段代码,右键选择"Ask Claude",Claude Code 会将这段代码作为上下文,打开对话面板等待你输入问题。

// 选中这段代码:
function processData(items: Item[]) {
  return items.filter(i => i.active).map(i => i.value * 2);
}

// 右键 → Ask Claude → 输入:
"这个函数在大数组上的性能怎么样?有没有更高效的写法?"

Explain This

选中代码,右键选择"Explain This",Claude Code 直接解释这段代码。解释结果在对话面板中展示。

// 选中一段复杂的正则:
const pattern = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;

// 右键 → Explain This
// Claude 返回:
// 这是一个密码强度验证正则,要求密码必须包含:
// - 至少一个小写字母 (?=.*[a-z])
// - 至少一个大写字母 (?=.*[A-Z])
// - 至少一个数字 (?=.*\d)
// - 至少一个特殊字符 (?=.*[@$!%*?&])
// - 总长度至少 8 个字符 {8,}

Fix This

选中有问题的代码,右键选择"Fix This",Claude Code 自动诊断并修复。这是四个功能中唯一会直接修改代码的功能。

// 选中:
for (var i = 0; i < items.length; i++) {
  setTimeout(() => console.log(items[i].name), 100);
}

// 右键 → Fix This
// Claude 诊断出两个问题:
// 1. var 导致 i 没有块级作用域,闭包中 i 都是 items.length
// 2. 没有处理空数组的边界情况
// 自动修复为:
items.forEach((item) => {
  setTimeout(() => console.log(item.name), 100);
});

注意:Fix This 会直接生成 Diff。如果修复方向不对,可以使用 Reject 拒绝修改。

Add to Context

这可能是四个功能中最被低估的一个。它的作用是将代码加入上下文,但不发送消息

使用模式:

1. 打开侧边栏对话
2. 在编辑器中找到第一段相关代码 → 右键 → Add to Context
3. 找到第二段相关代码 → 右键 → Add to Context
4. 找到第三段相关代码 → 右键 → Add to Context
5. 回到对话面板,三段代码已在上下文中
6. 输入你的问题,Claude 会基于这三段代码来回答

这比"复制粘贴代码到对话框"精确得多——因为 Add to Context 会附带文件路径和行号信息,Claude Code 知道这些代码来自哪里。


6.7 撤销与回退策略

AI 辅助编程中,犯错是常态而非例外。一个成熟的 Claude Code 用户不是不犯错,而是知道如何快速回退。

四级回退体系

┌─────────────────────────────────────────┐
│  第 4 级:Git reset / reflog            │  ← 终极安全网
│  第 3 级:Reject All + 重新开始          │  ← 方向性回退
│  第 2 级:逐文件 Reject                  │  ← 选择性回退
│  第 1 级:Cmd+Z(撤销最近接受的修改)     │  ← 即时回退
└─────────────────────────────────────────┘

第 1 级:Cmd+Z 即时撤销

适用场景:刚刚接受了 1-3 个文件的修改,发现有问题。

接受修改 → 发现变量命名不对 → Cmd+Z → 回到修改前
  • 速度:最快,不到 1 秒
  • 范围:最近几次编辑操作
  • 限制:VSCode 编辑历史有上限(默认约 1000 步),如果接受了大量修改后又做了很多操作,可能超出撤销范围

第 2 级:逐文件 Reject

适用场景:审查 Diff 时发现某个文件的修改完全不对。

审查第 3 个文件的 Diff → 发现整个改偏了 → 点击 Reject → 该文件不改
  • 速度:即时
  • 范围:单个文件
  • 后续操作:Reject 后可以重新描述需求,让 Claude 只修改这个文件

第 3 级:Reject All + 重新开始

适用场景:审查 Diff 时发现整个修改方向都错了。

审查 Diff → 发现 Claude 完全理解错了需求 → Reject All → 重新描述
  • 速度:取决于重新描述和生成的时间
  • 注意:如果 Claude 反复理解错误,可能是你的描述不够清晰,尝试:
    • 提供具体的"改前 vs 改后"示例
    • 用 Plan Mode 先让它出方案
    • /clear 清空上下文后重新开始

第 4 级:Git —— 终极安全网

在进行任何大规模重构之前,先做一次 Git 提交。 这不是可选的建议,而是必须养成的习惯。

bash
# 重构前的标准操作
git add -A
git commit -m "保存当前状态,准备重构用户模块"

# 然后开始重构...
# 如果重构结果不满意:
git reset --hard HEAD    # 回到提交时的干净状态

如果重构进行到一半,已经提交了几次,但后来发现方向有问题:

bash
# 查看最近的提交
git log --oneline -5

# 回到重构开始前的状态(保留工作区修改)
git reset --soft <重构前的 commit hash>

# 或者完全回到重构开始前(丢弃所有修改)
git reset --hard <重构前的 commit hash>

如果 reset --hard 之后又后悔了:

bash
# git reflog 记录了一切 HEAD 移动
git reflog

# 找回"丢失"的提交
git reset --hard HEAD@{2}

最佳实践:提交节奏

大规模重构(10+ 文件):
  改完一个模块 → 测试通过 → git commit
  改完下一个模块 → 测试通过 → git commit
  ...

小型修改(1-3 文件):
  改完 → 审查通过 → 测试通过 → git commit

核心原则:Git 提交越频繁,回退的粒度越细,损失的工作越少。使用 git commit --amendgit rebase -i 可以在推送到远程之前整理提交历史——所以不必担心"提交太碎"。

/clear + 重新开始

有时候问题不在代码,而在上下文污染。长对话中 Claude 可能被早期的错误信息或过时的文件状态影响判断。

# 上下文已经很长,Claude 开始做出奇怪的决定
/clear

# 在新对话中重新描述需求(更清晰、更精确)
"我在重构 src/api/ 目录,当前状态是:
 - auth.ts 已经改好了
 - users.ts 还需要加上错误重试
请只修改 users.ts"

6.8 重构最佳实践

本节将从实战经验中提炼出一套可复用的重构方法论

原则 1:小步快跑

一次只做一件事。一个指令只包含一个目标。

# ❌ 一个指令包含太多变更
"把 user 模块重构成 class、把 API 调用改成 axios、给所有函数加类型注解、把日志换成 pino"

# ✅ 拆成四个独立指令
"把 user 模块从普通函数改成 class"
# 审查、测试、提交
"把 user 模块的 API 调用从 fetch 改成 axios"
# 审查、测试、提交
"给 user 模块的所有函数加类型注解"
# 审查、测试、提交
"把 console.log 替换成 pino 日志"

每一步都独立可验证。如果第三步出错,你只需要回退第三步,前两步的工作不受影响。

原则 2:先读后改

让 Claude 先告诉你它打算改什么,确认后再动手。

这是本手册反复强调的原则,因为它太重要也太容易被忽略

# 标准流程
你:  "我打算重构 src/api/ 的错误处理,先不要改代码,说说你的方案"
Claude: "我计划修改以下文件……"(展示方案)
你:  "方向对了,但不要改 auth.ts,它的错误处理逻辑不一样"
Claude: "明白,排除 auth.ts,修改其他文件……"(开始改)

这个流程把"发现问题"的时机从改完之后提前到动手之前,大幅减少返工。

原则 3:变更后立即验证

改完一个模块,立刻运行测试。不要等到改完所有文件。

修改 auth.ts → pnpm test -- auth → ✅ 通过 → 继续
修改 users.ts → pnpm test -- users → ❌ 失败 → 定位修复
修改 articles.ts → 等 users 的问题修好再说

如果一股脑改了 20 个文件再跑测试,测试失败时你很难判断是哪个修改引入的问题。

原则 4:使用 Git 作为安全网

已经在 6.7 节 中详细讨论。这里只强调:

大规模重构前 → git commit(保存出发点)
每完成一个模块 → git commit(保存检查点)
重构完成 → git rebase -i(整理提交历史)
推送到远程 → git push

原则 5:给 Claude 看反例

告诉它"这样不对",比只告诉它"我要这样"更有效。

# ❌ 只给正例
"把 getUser 改成 fetchUser"

# ✅ 给出正例和反例
"把 getUser 改成 fetchUser,但注意:
 - 不要改 getUserName,那是另一个函数
 - 不要改注释中的 'getUser 函数' 这种描述性文字
 - 测试文件中的 mock 函数名不要改"

指定"不要做什么"能大幅减少 Claude 的误操作。

原则 6:指定重构范围

给 Claude 画一个明确的"施工区域"。

# ❌ 范围模糊
"帮我重构一下错误处理"

# ✅ 范围明确
"只重构 src/api/ 目录下的错误处理,按 src/api/auth.ts 的模式统一"
"只重构组件层(src/components/),不要动业务逻辑层(src/services/)"
"只重构类型定义,不要修改任何运行时代码"

原则 7:复杂任务使用 Plan Mode

当任务涉及 3+ 个文件架构级决策时,开启 Plan Mode。

按 Cmd+Shift+P → Claude Code: Toggle Plan Mode

# 在 Plan Mode 中:
"我想把项目中的状态管理从 Vuex 迁移到 Pinia"

Claude 会:
1. 分析项目中使用 Vuex 的所有位置
2. 提出迁移方案(分几个阶段、每个阶段改什么)
3. 等待你确认后,才开始逐阶段执行

Plan Mode 的具体使用方式将在第 9 章"模式切换"中详细讲解。


6.9 本章小结

本章覆盖了 Claude Code 代码编辑与重构的完整工作流。以下是核心要点:

两个编辑工具:

工具原理适用场景
Edit精确查找字符串 → 替换为新字符串修改现有文件的局部内容
Write创建新文件或完全覆盖旧文件新建文件、完全重写

Diff 审查是核心习惯:

  • 永远逐文件审查,不要盲点 Accept All
  • 在 Diff 视图中可以直接编辑 Claude 的输出
  • Cmd+Z 撤销已接受的修改
  • "先读后改"模式:让 Claude 先展示方案,确认后再动手

内联编辑 vs 侧边栏对话:

  • 单文件、几行代码 → Cmd+Shift+I 内联编辑
  • 多文件、复杂逻辑 → 侧边栏对话

回退体系(从快到慢):

  1. Cmd+Z — 即时撤销
  2. Reject(单文件)— 选择性回退
  3. Reject All — 方向性回退
  4. Git reset — 终极安全网
  5. /clear — 清理上下文

七条重构原则:

  1. 小步快跑——一次一件事
  2. 先读后改——确认方案再动手
  3. 变更后立即验证——改完一个模块就跑测试
  4. 使用 Git——每次成功后提交
  5. 给 Claude 看反例——告诉它"不要做什么"
  6. 指定重构范围——画清施工区域
  7. 复杂任务用 Plan Mode——3+ 文件开启计划模式

下一章第 7 章:终端命令执行 —— 学习如何让 Claude Code 替你运行构建、测试和脚本,以及如何安全地管理命令权限。