第 07 章 Plan Mode:计划优先
Boris Cherny(Claude Code 作者)多次在公开场合说,Plan Mode 是 Claude Code 里最被低估的功能。这一章解释为什么,以及怎么用对。
7.1 结论:Plan Mode 是一次性成功率的杠杆
Plan Mode 让 Claude 在只读状态下探索代码库,输出一份结构化方案,经你确认后再执行。它的价值不是「看起来更严谨」,而是把错误从昂贵的代码阶段挪到廉价的方案阶段:在 plan 里改一行字,比在代码里回滚一次提交便宜两个数量级。
与第 06 章的可执行闭环对照,Plan Mode 就是闭环步骤 2「建立事实模型」的官方实现。闭环七步里,步骤 1(限定范围)由你用 @文件 与权限规则完成,步骤 2(建立事实模型)由 Plan Mode 完成,步骤 3-7 才进入修改与验证。跳过步骤 2 直接改代码,等于在没有事实模型的前提下让 Claude 自信地写代码——这是绝大多数「时灵时不灵」的根因。
7.2 为什么 Plan First
LLM 有一个致命特性:在错误前提下,它依然能自信地生成看起来合理的代码。一旦 Claude 对项目入口、数据流、权限边界的理解是错的,它生成的代码会自洽地符合那个错误理解,测试都可能通过(因为测试也建立在错误前提上)。等你发现时,已经是一堆需要回滚的提交。
Plan First 解决的是这个问题。它强制 Claude 在动手前先把「我以为这个系统长这样」写下来,让你核对。核对的成本是读一份 markdown;不核对的成本是回滚 N 个文件、重跑 CI、修被带歪的测试。
成本对比:
| 阶段 | 发现理解错误 | 改正成本 |
|---|---|---|
| Plan 阶段 | 你读 plan 时 | 改 plan 里几行字,秒级 |
| 代码阶段 | code review 或测试失败时 | 回滚 + 重写,小时级 |
| 上线后 | 生产报警 | 不可估 |
结论:任何超过 3 个文件的改动、任何涉及不熟悉模块的任务、任何架构变更,都该先 plan。
7.3 进入与退出
三种进入方式,对应三种使用场景。
方式一:Shift+Tab 两次(交互中最快)
在交互会话里,按两次 Shift+Tab 切换到 Plan Mode。状态栏会显示当前模式。适合在对话中途意识到「这个任务该先 plan 一下」时切换。
方式二:/plan(v2.1.0+)
直接输入 /plan 进入 Plan Mode。与 Shift+Tab 等价,但更适合习惯命令式操作的用户。版本敏感:2026-07-08 核对 v2.1.202+,该命令稳定可用。
方式三:--permission-mode plan(启动时指定)
claude --permission-mode plan从启动就进入只读模式。适合做代码考古、影响分析、给陌生仓库做架构梳理这类纯探索任务。配合 -p 可以在 CI 或脚本里跑只读分析:
claude --permission-mode plan -p \
"分析 src/auth 目录的鉴权流程,输出入口、数据流、高风险点" \
--output-format json注意:--permission-mode plan 与交互里的 Plan Mode 行为一致——禁止任何文件改动与有副作用命令,Claude 只能 Read/Grep/Glob/Bash(只读)。
退出:ExitPlanMode
Plan Mode 下,Claude 完成探索后会调用 ExitPlanMode 工具,把方案呈现给你。你看到的是一份结构化 plan,带「确认执行 / 拒绝 / 修改」的选项。确认后,Claude 退出 Plan Mode,进入可写状态开始执行。如果你不同意,可以拒绝并继续对话调整 plan,直到方案准确再确认。
plan 文件
Plan 会写入 ~/.claude/plans/ 下的 markdown 文件。你可以手动编辑这个文件再让 Claude 继续——这是 Plan Mode 一个被忽视的特性:plan 不是一次性的对话产物,而是可版本化、可协作的中间制品。团队可以把 plan 文件纳入 review 流程,甚至把历史 plan 作为决策记录归档。
ls ~/.claude/plans/
# 2026-07-08-auth-refactor.md
# 2026-07-08-payment-timeout.md手动编辑 plan 文件的典型场景:你在 review 时发现某个步骤的验收命令写错了,直接改 plan 文件,再让 Claude 按 plan 继续执行——比重开对话成本低得多。
7.4 约束式 Prompt:给约束,不给开放问题
Plan Mode 的效果取决于你给 Claude 什么 prompt。最大的反模式是开放问题:「看一下这个项目,给我个重构方案」。这会让 Claude 输出一份泛泛而谈的方案,看起来很全面,实则没有可执行性。
正确的做法是约束式 prompt:告诉 Claude 你要什么、约束是什么、让它输出哪几件事。Plan 阶段你要让 Claude 输出的五件事是固定的:
- 系统入口:请求从哪里进来,经过哪些层
- 模块边界:哪些模块会被触碰,它们的职责
- 数据流:数据怎么流动,在哪里持久化,在哪里被消费
- 权限边界:哪些操作需要鉴权,哪些资源受保护
- 高风险区:容易出错的地方——并发、事务、外部副作用、向后兼容
约束式 prompt 骨架:
任务:<可观察的结果,而非实现方式>
范围:只读 <目录/文件>
约束:
- <兼容性约束,如保持 API 签名不变>
- <安全约束,如不读取 .env>
- <风格约束,如遵循现有错误处理模式>
输出:
1. 系统入口(带文件路径与行号)
2. 模块边界(列出会被触碰的模块及职责)
3. 数据流(从输入到持久化到输出)
4. 权限边界(鉴权点、受保护资源)
5. 高风险区(并发、事务、外部副作用、向后兼容)
6. 实施步骤(每步控制在 1-3 个文件粒度,含验收命令)
不要修改任何文件。对比:
# 差(开放问题)
> 看一下支付模块,给我个重构方案
# 好(约束式)
> 任务:把支付超时从硬编码 30s 改为可配置,支持按渠道覆盖
> 范围:只读 src/payment/ 与 src/config/
> 约束:
> - 保持 PaymentService 公开 API 不变
> - 不引入新依赖
> - 配置缺失时回退到当前默认值
> 输出:系统入口、模块边界、数据流、权限边界、高风险区、实施步骤
> 不要修改文件。约束式 prompt 的本质是:你把验收标准前置到 plan 阶段。Claude 不是在猜你要什么,而是在你给的框里填细节。
7.5 确认时检查:文件路径与行号
Plan 呈现给你时,最关键的验证不是「方案听起来对不对」,而是plan 里引用的文件路径与行号是否真实存在。这是 Plan Mode 给你的最大价值:在 Claude 动手前,你就能发现它对代码库的理解是否准确。
如果 plan 里写「修改 src/auth/middleware.ts:45 的鉴权逻辑」,你打开这个文件看一眼第 45 行,确认那里确实是鉴权逻辑。如果文件不存在、行号对不上、或者那里是无关代码,说明 Claude 的事实模型是错的——这时候执行必然翻车,直接拒绝 plan 让它重新探索。
确认 checklist:
- [ ] plan 引用的每个文件路径都存在
- [ ] 行号引用与实际代码对得上
- [ ] 模块职责描述与代码一致
- [ ] 数据流方向正确
- [ ] 高风险区没有遗漏(尤其是事务、并发、外部副作用)
模拟终端:
● Plan: payment-timeout-config
实施步骤:
1. 在 src/config/payment.ts:12 添加 channelTimeout 配置项
2. 修改 src/payment/service.ts:87 读取配置替换硬编码
3. 在 src/payment/service.ts:142 添加渠道级覆盖逻辑
4. 添加测试 test/payment/timeout.test.ts
[确认执行] [拒绝] [修改]你打开 src/config/payment.ts:12 确认那里确实是配置对象,打开 src/payment/service.ts:87 确认那里确实是硬编码 30s 的位置。都对得上,才确认。这一步花 30 秒,能拦掉绝大多数基于错误理解的执行。
7.6 TaskCreate 拆解:1-3 文件粒度
确认 plan 后,Claude 退出 Plan Mode 开始执行。对于复杂任务,plan 里应该已经把实施步骤拆成了 1-3 文件粒度的 task。Claude 会用 TaskCreate 工具把 plan 里的步骤落地为可追踪的任务列表,每完成一个就标记完成。
这不是装饰——它让你在任何时刻都能看到「现在做到第几步、哪一步卡住了」。当执行偏离 plan 时,任务列表就是纠偏的依据。
每个 task 应该满足:
- 独立可验证(有明确的完成标准与验收命令)
- 粒度可控(1-3 个文件变更)
- 顺序合理(依赖关系明确,避免循环依赖)
如果 plan 里的某个 task 粒度过大(超过 3 个文件),在确认前就要求拆分。一个 10 文件的 task 等于没有拆解,执行时必然出现上下文衰减和一致性错误——这正是第 06 章闭环步骤 3「任务粒度控制在 1-3 个文件变更」的落地。
7.7 实战示例:一个真实 plan prompt
场景:把一个 Express 服务的支付超时从硬编码改为可配置。
命令(Prompt)
任务:把支付超时从硬编码 30s 改为可配置,支持按渠道覆盖
范围:只读 src/payment/ 与 src/config/
约束:
- 保持 PaymentService 公开 API 不变
- 不引入新依赖
- 配置缺失时回退到当前默认值 30s
- 新增配置必须通过环境变量注入,不写入代码
输出:
1. 系统入口(带文件路径与行号)
2. 模块边界
3. 数据流
4. 权限边界
5. 高风险区
6. 实施步骤(每步 1-3 文件粒度,含验收命令)
不要修改文件。预期输出
## 系统入口
- src/routes/payment.ts:23 POST /api/payment/charge 处理器
- src/routes/payment.ts:67 POST /api/payment/refund 退款处理器
## 模块边界
- src/payment/service.ts PaymentService.charge() 主流程,触发超时
- src/payment/gateway.ts 渠道适配层,各渠道超时不同
- src/config/payment.ts 配置加载,当前无超时配置项
## 数据流
请求 → route → PaymentService.charge → gateway.callChannel → 外部渠道
超时当前在 service.ts:87 硬编码 30s,gateway 层无覆盖能力
## 权限边界
- /api/payment 需鉴权,中间件在 src/auth/middleware.ts:15
- 退款需 admin 角色,检查在 src/auth/roles.ts:34
## 高风险区
- 渠道级覆盖逻辑:配置缺失时必须回退默认值,否则渠道调用会无限等待
- 向后兼容:现有测试硬编码了 30s,需同步更新
- 环境变量注入:配置加载顺序早于服务初始化,不能有循环依赖
## 实施步骤
1. 在 src/config/payment.ts 添加 channelTimeout 配置项,从 env 读取
验收:npm run typecheck
2. 修改 src/payment/service.ts:87 读取配置替换硬编码
验收:npm test -- payment/service
3. 在 src/payment/gateway.ts 添加渠道级覆盖
验收:npm test -- payment/gateway
4. 更新 test/payment/timeout.test.ts 覆盖默认值与覆盖值
验收:npm test -- payment/timeout验证
打开 src/routes/payment.ts:23 确认是 charge 处理器;打开 src/payment/service.ts:87 确认是硬编码 30s;打开 src/auth/middleware.ts:15 确认是鉴权中间件。都对得上,确认执行。
失败边界
- 如果 plan 里引用的行号全对不上,说明 Claude 没真正读代码,是在猜——拒绝,重新探索
- 如果高风险区漏了「配置加载顺序」这一条,说明 Claude 没意识到循环依赖风险——要求补充
- 如果某个 task 粒度超过 3 文件,要求拆分后再执行
- 如果 plan 里出现了范围外的文件(比如要改
src/auth/),说明范围漂移——拒绝或重新限定
7.8 与第 06 章可执行闭环的关系
Plan Mode 不是独立功能,它是可执行闭环(第 6.2 节)步骤 2「建立事实模型」的官方实现:
| 闭环步骤 | 对应机制 |
|---|---|
| 1. 限定范围 | @文件、权限规则、Plan Mode 的只读约束 |
| 2. 建立事实模型 | Plan Mode(本章) |
| 3. 修改代码 | 确认 plan 后退出 Plan Mode,进入 acceptEdits 或 default |
| 4. 运行验证 | plan 里写明的验收命令 |
| 5. 读取失败 | 失败时回到 Plan Mode 重新建立事实模型 |
| 6. 自我修正 | 基于 plan 调整,而非凭对话记忆 |
| 7. 人类审查 | plan 确认 + 最终 git diff 审查 |
关键洞察:Plan Mode 不只是任务开始时用一次。当执行中遇到失败、需要回退到步骤 1 重新限定范围时(第 6.2 节闭环箭头「不通过则回退」),也应该重新进入 Plan Mode 调整事实模型。很多用户把 Plan Mode 当成「一次性入口」,用完就忘,这是浪费——它是闭环里可以反复触发的步骤,每次事实模型失准都该回到 plan。
7.9 失败边界与反模式
反模式一:plan 过大
一个 plan 试图覆盖 20 个文件的改动。这种 plan 必然在某个细节上失真,因为 Claude 的上下文也是有限的。拆成多个小 plan,每个覆盖 3-5 个文件,顺序执行。大重构的正确做法见第 08 章。
反模式二:未确认就执行
Claude 呈现 plan 后,你不看就点确认。这等于跳过了 Plan Mode 的全部价值——你既没有核对文件路径,也没有核对行号,也没有审查高风险区。Plan Mode 变成了仪式感,不是控制面。确认前至少抽查 3 个路径与行号引用。
反模式三:把 plan 当聊天
在 Plan Mode 里和 Claude 讨论开放问题,不提约束,不要求五件事输出。Claude 会生成一份散文式方案,看起来全面,实则不可执行。Plan Mode 的 prompt 必须是约束式的(7.4 节)。
反模式四:plan 与执行脱节
确认 plan 后,执行时 Claude 偏离了 plan(改了 plan 没提的文件)。这通常是因为执行中发现新情况。正确做法是:让 Claude 在偏离前先说明原因,必要时回到 Plan Mode 更新事实模型。直接偏离等于丢了 plan 这个中间制品,后续审查会失去对照基准。
反模式五:只 plan 不验证
Plan 里的验收命令必须在执行阶段真的跑。Plan 写得再漂亮,不跑验证就等于回到「完成只是 Claude 一句话」的老路(第 6.3 节支点三)。验收命令是闭环步骤 4 的输入,不是 plan 的装饰。
7.10 小结
Plan Mode 是 Claude Code 里性价比最高的纪律:用一次只读探索 + 一份 markdown,把错误从代码阶段挪到方案阶段。它的正确用法是:约束式 prompt → 五件事输出 → 路径行号核对 → 确认执行 → 验收命令跑通。与第 06 章的可执行闭环配合,Plan Mode 是步骤 2 的实现,也是整个闭环里最容易被跳过、最不该被跳过的一步。
记住一句:plan 阶段省的每一分钟,执行阶段都会以小时偿还。
上一章:核心心智模型与命令真实性
下一章:重构与大规模变更