第 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
→ 找不到匹配 或 找到多个匹配 → 报错,不修改任何内容这个机制有几个重要含义:
- 精确匹配要求:
old_string必须与文件中的内容完全一致,包括缩进、空格、换行 - 唯一性要求:
old_string在文件中只能出现一次。如果出现多次,Edit 会失败(需要使用replace_all: true) - 原子性:要么替换成功,要么完全不做修改——不会出现"改了一半"的情况
为什么是"精确替换"而非"行号替换"? 行号会随着代码的增删而变化。当 Claude 同时修改多个位置时,如果基于行号,前面的修改会导致后续的行号全部偏移。精确字符串匹配天然规避了这个问题。
Claude 什么时候使用 Edit
Claude Code 会自动判断使用 Edit 还是 Write 工具,判断逻辑如下:
| 场景 | 使用工具 | 原因 |
|---|---|---|
| 修改函数内几行代码 | Edit | 文件大部分不变,只改局部 |
| 重命名一个变量 | Edit | 精确替换标识符 |
| 修改函数签名 | Edit | 只改一行声明 |
| 在现有文件中添加一个新函数 | Edit | 在合适位置插入代码块 |
| 修复一个 bug | Edit | 通常只涉及几行改动 |
| 调整配置文件的某个字段 | Edit | 精确替换一个值 |
实战示例
场景 1:重命名变量
假设你有一个文件 src/utils/user.ts:
function getUser(id: number): User {
const user = db.query('SELECT * FROM users WHERE id = ?', id);
return user;
}你可以在对话中说:
把 src/utils/user.ts 里的 getUser 函数改名为 fetchUserByIdClaude Code 会调用 Edit 工具,将:
function getUser(id: number): User {精确替换为:
function fetchUserById(id: number): User {场景 2:修改函数签名
给 calculateTotal 函数加一个 discount 参数,默认值为 0Claude Code 会:
- 读取包含
calculateTotal的文件 - 找到函数定义行
- 用 Edit 工具将
calculateTotal(items: Item[])替换为calculateTotal(items: Item[], discount: number = 0) - 如果函数体内需要用到
discount参数,同步修改函数体逻辑
场景 3:修复 bug
src/api/auth.ts 第 42 行的条件判断写反了,token 为空时应该返回 401 而不是继续执行Claude Code 定位到那一行,用 Edit 精确替换错误的条件判断。
Edit 工具的约束
理解 Edit 的约束能帮你更好地指导 Claude:
约束 1:old_string 必须精确匹配
// 如果文件中是:
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.comClaude 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 会:
- 分析项目中现有组件的写法风格
- 用 Write 工具创建
src/components/Toast.vue - 写入完整的组件代码
场景 2:创建工具函数
帮我在 src/utils/ 下创建一个 formatDate.ts,把 ISO 日期字符串格式化为 "YYYY 年 MM 月 DD 日" 格式Claude Code 用 Write 创建文件:
// 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 的代码上叠加自己的改动
操作方法:
- 在 Diff 视图中,点击右侧(新代码)的任意位置
- 像编辑普通文件一样直接修改
- 修改完成后点击 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+Z | Reject 单文件或 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 会:
- 用 Grep 找到所有 API 调用(
fetch(、axios.等) - 按业务模块分组
- 为每个模块创建 API 文件
- 修改所有页面组件,替换为 API 函数调用
- 使用 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 提交。 这不是可选的建议,而是必须养成的习惯。
# 重构前的标准操作
git add -A
git commit -m "保存当前状态,准备重构用户模块"
# 然后开始重构...
# 如果重构结果不满意:
git reset --hard HEAD # 回到提交时的干净状态如果重构进行到一半,已经提交了几次,但后来发现方向有问题:
# 查看最近的提交
git log --oneline -5
# 回到重构开始前的状态(保留工作区修改)
git reset --soft <重构前的 commit hash>
# 或者完全回到重构开始前(丢弃所有修改)
git reset --hard <重构前的 commit hash>如果 reset --hard 之后又后悔了:
# git reflog 记录了一切 HEAD 移动
git reflog
# 找回"丢失"的提交
git reset --hard HEAD@{2}最佳实践:提交节奏
大规模重构(10+ 文件):
改完一个模块 → 测试通过 → git commit
改完下一个模块 → 测试通过 → git commit
...
小型修改(1-3 文件):
改完 → 审查通过 → 测试通过 → git commit核心原则:Git 提交越频繁,回退的粒度越细,损失的工作越少。使用
git commit --amend和git 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内联编辑 - 多文件、复杂逻辑 → 侧边栏对话
回退体系(从快到慢):
Cmd+Z— 即时撤销- Reject(单文件)— 选择性回退
- Reject All — 方向性回退
- Git reset — 终极安全网
/clear— 清理上下文
七条重构原则:
- 小步快跑——一次一件事
- 先读后改——确认方案再动手
- 变更后立即验证——改完一个模块就跑测试
- 使用 Git——每次成功后提交
- 给 Claude 看反例——告诉它"不要做什么"
- 指定重构范围——画清施工区域
- 复杂任务用 Plan Mode——3+ 文件开启计划模式
下一章:第 7 章:终端命令执行 —— 学习如何让 Claude Code 替你运行构建、测试和脚本,以及如何安全地管理命令权限。