首页 › tutorials › Plan模式造零返工的大重构

Claude Code Plan 模式造零返工的大重构

概述

本教程教你在 Claude Code 里用 Plan 模式(Plan Mode) 完成"先勘探、出方案、再动手"的大重构,把返工率压到接近为零。你将掌握三幕剧式工作流:只读勘探代码库 → 写方案文件并提交审批(ExitPlanMode)→ 审批通过后用 TodoWrite 逐步落地,再配 PostToolUse/Stop 钩子自动跑 lint 和测试。适合做过几个 Claude Code 小项目、想挑战多文件重构的中级读者。

前置条件


一、先搞懂:Plan 模式到底限制了什么

Claude Code 有三种权限模式(permission mode),用 Shift+Tab 在 CLI 里循环切换,或用 --permission-mode 启动参数指定:

模式 读文件 改文件 / 跑命令 典型用途
Default(默认) 自由 每次改动前询问你 日常交互开发
Auto-Accept(自动接受) 自由 自动放行,不再问 你盯着看、信任度高的批量活
Plan(计划) 自由 一律禁止 勘探、分析、出方案

Plan 模式的核心契约是:模型只能读、搜、想,不能写、不能编辑、不能跑会改东西的命令。 它把 Claude 锁进一个"只读沙箱",逼它先把问题想透,把方案落到一段你可以否决的文字里,而不是一堆你得回滚的坏文件。

一句话记住:Plan 模式下,最便宜的 bug 还停留在"一句话"阶段——你点否决就完了,成本为零;出了 Plan 模式再发现的错,就是一堆要手工理清的烂摊子。

进入 Plan 模式有三种姿势:

  1. 手动:在交互式 CLI 里连按 Shift+Tab,直到底部状态栏显示 plan mode on。
  2. 半自动:模型自己判断"这活儿够复杂、得先勘探",调用 EnterPlanMode 工具把自己切进去——尤其当它原本想用 AskUserQuestion 问你方案选型时,切 Plan 模式更合适。
  3. 无头(headless):claude -p "重构思路" --permission-mode plan,让 Claude 在 CI 或脚本里只产出方案文本、不动文件。

二、第一幕:勘探(只读摸底)

操作说明

进入 Plan 模式后,给 Claude 一个目标导向而非步骤导向的指令。差的指令把活儿规定了,好的指令把"要解决什么"说清、把决策权留给模型。

以本教程的实战案例——把项目里散落的 console.log 统一替换为结构化日志(pino)——为例:

进入 plan 模式。目标:把整个 src/ 下所有 console.log / console.error
统一迁移到 pino 结构化日志。先勘探:列出所有调用点,按 debug/info/warn/error
分类,搞清楚哪些是真正要保留的错误日志、哪些是临时调试残留。
不要改任何文件,勘探完给我一份迁移方案。

模型会动用三类只读工具:

预期结果

勘探阶段你应该看到类似这样的工具调用链:

Grep  pattern="console\.(log|error|warn|info)"
       glob="src/**/*.js"  output_mode="content"  -n
→ 命中 47 处,分布在 12 个文件
Glob  pattern="src/**/*.js"
→ 列出 28 个源文件
Read  file_path="src/services/order.js"   (理解 error 分支)
Read  file_path="package.json"            (确认是否已装 pino)

模型最终会产出一张调用点清单,类似:

src/routes/user.js    :12   console.log('login', userId)        → info
src/routes/user.js    :45   console.error('auth fail', err)     → error(保留)
src/services/order.js :88   console.log('debug stock')          → debug(疑似残留)
... 共 47 处

常见问题与解决


三、第二幕:提案(写方案 + ExitPlanMode 审批)

操作说明

勘探完,模型会把方案写进一个计划文件(plan file),然后调用 ExitPlanMode 工具,把方案端到你面前审批。这一步是整个工作流的咽喉——你不点头,它一行代码都改不了。

一份合格的方案文件通常包含:

  1. 目标与范围:改什么、不改什么、边界在哪
  2. 调用点分析结论:勘探发现的分类汇总
  3. 迁移规则:每类调用怎么转(见下表)
  4. 执行顺序:按文件/模块的依赖顺序,先底层后上层
  5. 回滚策略:出问题怎么退(git revert / 备份分支)
  6. 验证方式:跑什么命令确认没改坏(npm test / npm run lint)

迁移规则示例表(方案里应该有):

原调用 目标 规则
console.log('xxx') 普通信息 logger.info(...) 包成结构化对象
console.log('debug ...') 调试残留 删除 标注后删,不留
console.error(...) 真实错误 logger.error({err}) 保留并结构化
console.warn(...) logger.warn(...) 同上

遇到分叉:用 AskUserQuestion 而不是猜

方案里常会冒出需要你拍板的岔路。与其让模型瞎猜,不如在 ExitPlanMode 前先用 AskUserQuestion 问清楚。比如:

AskUserQuestion
  Q: "src/legacy/ 下的 13 处 console.log 是否一并迁移?"
  选项:
    - 一并迁移(推荐):保持全项目一致
    - 跳过 legacy:该目录即将废弃,不值得动
    - 只迁移 error 级别:最小风险

经验法则:只要方案里有一个"二选一"会影响后面十几个文件改动,就先问、再出方案。Plan 模式的 EnterPlanMode 触发条件之一,正是"原本要问选型问题"——把提问和方案合并到一起,最省往返。

ExitPlanMode:把权限清单一起递上去

ExitPlanMode 不只是"请批准",它还顺带声明执行阶段需要的 Bash 权限(allowedPrompts)。这样审批一过,模型跑 npm install pino、npm test 时不必再逐条问你。模型会写成类似:

{
  "allowedPrompts": [
    { "tool": "Bash", "prompt": "install npm dependencies" },
    { "tool": "Bash", "prompt": "run tests" },
    { "tool": "Bash", "prompt": "run linter and formatter" }
  ]
}

预期结果

审批弹窗里你会看到完整方案 + 权限清单。此时你有三种选择:

常见问题与解决


四、第三幕:执行(TodoWrite + Hook 看门)

操作说明

批准后,模型退出 Plan 模式,开始落地。关键纪律:用 TodoWrite 把方案拆成可见的任务清单,每改一个文件勾一项,你和模型都能看到进度。

一个 47 处调用的迁移,任务清单可能是:

☐ 安装 pino(npm i pino)
☐ 新建 src/lib/logger.js(统一出口)
☐ 迁移 src/routes/(3 文件,18 处)
☐ 迁移 src/services/(5 文件,22 处)
☐ 迁移 src/legacy/(4 文件,13 处)— 仅 error 级
☐ 全局 grep 复查残留 console.*
☐ 跑 npm test 验证

配 Hook 做自动看门

执行阶段最容易翻车的是"改完忘了跑测试"。用两个钩子把质量门焊死。在 .claude/settings.json 里加:

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npx prettier --write \"$CLAUDE_FILE_PATHS\" 2>/dev/null; true"
          }
        ]
      }
    ],
    "Stop": [
      {
        "matcher": "",
        "hooks": [
          { "type": "command", "command": "npm test --silent" }
        ]
      }
    ]
  }
}

关于 Stop 钩子的退码语义:返回非零退出码会把输出回灌给模型,等于"逼它继续修";返回 0 则静默通过。想让失败自动触发修复,就让钩子在测试失败时 exit 1。

预期结果

执行过程会看到任务逐个从 ☐ 变 ☑,文件被 Edit 逐个改写,每个改动后 Prettier 自动格式化,阶段性停下时 npm test 自动跑。最终一次 Stop 时测试全绿,任务清单全勾。

Edit src/lib/logger.js      ✅ + PostToolUse → prettier
Edit src/routes/user.js     ✅ + prettier
...
TodoWrite  全部 ☑
Stop → npm test  PASS (23 specs)

常见问题与解决


五、实战案例:console.log → pino 全量迁移(完整可跑)

把三幕串起来。准备一个最小 Node.js 项目:

mkdir plan-refactor-demo && cd plan-refactor-demo
npm init -y
npm i express
mkdir -p src/routes src/services

造两个带"脏日志"的文件:

// src/routes/user.js
import express from 'express'
const router = express.Router()

router.post('/login', (req, res) => {
  const userId = req.body.id
  console.log('login', userId)          // 临时调试残留
  try {
    auth(userId)
  } catch (err) {
    console.error('auth fail', err)      // 真实错误,要保留
    res.status(500).end()
  }
  res.json({ ok: true })
})
export default router
// src/services/order.js
export function placeOrder(items) {
  console.log('debug stock', items)      // 调试残留
  // ...下单逻辑
}

第一幕:Shift+Tab 切到 plan mode,贴上文那段勘探指令。模型 grep 出 3 处,分类:1 个 error(保留)、2 个 debug 残留(删/转)。

第二幕:模型出方案——建 src/lib/logger.js、user.js 的 log 转 logger.info、error 转 logger.error({err})、order.js 的 debug 直接删;执行顺序 routes → services;验证 node -e "require('./src/routes/user.js')"。带 AskUserQuestion 问"order.js 的 debug 是删还是转?",你选"删"。ExitPlanMode 递上 allowedPrompts: [install, run]。你批准。

第三幕:模型 TodoWrite 建清单,逐个 Edit,PostToolUse 自动 Prettier,最后 Stop 钩子跑 node --check 全过。最终 src/lib/logger.js:

import pino from 'pino'
export const logger = pino({
  level: process.env.LOG_LEVEL || 'info',
  transport: { target: 'pino-pretty' }
})

user.js 里 console.log('login', userId) → logger.info({ userId }, 'login');console.error('auth fail', err) → logger.error({ err }, 'auth fail')。order.js 删掉 debug 行。三处全清,测试通过,零返工。


六、Mermaid 流程图:Plan 模式三幕与工具流转

flowchart TD
    A["用户给目标指令"] --> B{"权限模式?"}
    B -->|默认/自动| C["模型判断复杂度"]
    C -->|"够复杂/要问选型"| D["EnterPlanMode"]
    C -->|简单| Z["直接 Default 执行"]
    B -->|手动 Shift+Tab| D
    D --> E["第一幕:勘探只读"]

    subgraph ACT1["第一幕 勘探(只读沙箱)"]
        E --> E1["Glob 找文件"]
        E1 --> E2["Grep 定位调用点"]
        E2 --> E3["Read 理解上下文"]
        E3 --> E4["输出调用点清单"]
    end

    E4 --> F["第二幕:提案"]
    subgraph ACT2["第二幕 提案 + 审批"]
        F --> F1["写 plan 文件<br/>规则/顺序/回滚/验证"]
        F1 --> F2{"有分叉?"}
        F2 -->|是| F3["AskUserQuestion 问选型"]
        F2 -->|否| F4["ExitPlanMode<br/>+ allowedPrompts"]
        F3 --> F4
        F4 --> F5{"用户审批"}
    end

    F5 -->|打回| E
    F5 -->|批准| G["第三幕:执行"]
    subgraph ACT3["第三幕 执行 + Hook 看门"]
        G --> G1["TodoWrite 建任务清单"]
        G1 --> G2["Edit 逐文件改"]
        G2 --> G3["PostToolUse → Prettier"]
        G3 --> G4["Stop → npm test"]
        G4 --> G5{"测试绿?"}
        G5 -->|红| G2
        G5 -->|绿| H["全清单 ☑ 完成"]
    end

七、进阶:无头 Plan 模式接进流水线

Plan 模式不只是交互玩具。用 -p 把它接进 CI/脚本,让 Claude 在不碰文件的前提下产出方案,挂到 PR 评论里供人评审:

# CI 里:针对本次 diff 产出重构方案,只读、零副作用
claude -p "$(cat <<'EOF'
进入 plan 模式。分析本次 PR 的 git diff,针对 src/ 下被改动的文件,
提出进一步的可读性与性能优化方案。不要修改任何文件,只输出方案。
EOF
)" --permission-mode plan > .claude/plan.md

由于 --permission-mode plan 强制只读,即便 prompt 里误写了"请修改",模型也无法落笔——权限护栏在模型之上,是最后一道闸。产出的 plan.md 可由后续步骤贴进 PR、或交给一次 Auto-Accept 的执行轮次去落地。

把"出方案"和"动手"拆成两个独立进程:第一进程 --permission-mode plan 只读产出方案,人审;第二进程拿到批准的方案再用 Default/Auto-Accept 执行。这样既享自动化、又不失人审关卡,是大型重构的稳妥姿势。


八、常见坑与对策汇总

  1. 勘探即动手:忘了切 Plan 模式。养成习惯——复杂任务先连按两次 Shift+Tab,看到 plan mode on 再开口。
  2. 方案过粗:打回时明确要求"每类转换规则 + 文件顺序 + 回滚 + 验证"四件套。
  3. 不问就猜:见到二选一就要求模型先 AskUserQuestion,别让它替你拍板。
  4. 权限清单过宽:拒绝 Bash(*),收紧到 install/test/lint 等具名子动作。
  5. 执行无清单:强制 TodoWrite,没有可见清单的重构等于盲飞。
  6. 改完不验证:Stop 钩子跑测试,用非零退码逼模型自愈。
  7. 无头模式忘了加 plan:CI 里务必带 --permission-mode plan,否则 prompt 笔误就可能真改文件。

延伸阅读


训练营适用性评估

参考来源

  1. Choose a permission mode — Claude Code Docs — 官方对 Default / Auto-Accept / Plan 三种模式与 Shift+Tab 切换的权威说明
  2. Plan Mode & Exit Plan Mode — DeepWiki — 从源码视角解析 Plan 模式只读沙箱与 ExitPlanMode 审批机制
  3. Claude Code Plan Mode: Get a Plan Before It Touches Your Code — Clearly — 实战角度阐释"先出方案再动手"的价值与用法
  4. Claude Code Plan Mode: The Complete Guide (2026) — anyonebuilds — 2026 年视角的 Shift+Tab 进入、何时使用与配套技巧
  5. pi-claude-code/docs/plan-mode.md — GitHub — 只读分析、先规划后执行的社区整理文档

[!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学通信知识星球海报-扫码加入