当一个重构任务涉及 30 个文件、3 个模块、上百处调用点时,直接让 AI 动手往往会收获一场灾难。2026 年 7 月,Claude Code 的 Plan Mode 配合 TodoWrite 工具,给"复杂任务怎么不跑偏"提供了一个工程化答案。本文用一个真实的 Flask 单体拆分案例,把完整流程跑通。
为什么复杂重构任务容易失控
把一个大型重构任务直接丢给 AI 编码工具,最常见的三种翻车场景:
- 范围蔓延:AI 自作主张改了不该改的文件,引入隐性回归
- 中途断片:上下文压缩后,AI 忘了之前拆解的步骤,重复劳动或漏掉关键环节
- 无法验收:改完之后说不清"到底做了哪些事、还剩哪些没做",review 无从下手
根本原因在于缺少两个东西:一个在动手前锁定范围的计划阶段,一个贯穿执行全程的任务清单。Claude Code 的 Plan Mode 和 TodoWrite 正好对应这两件事。
Plan Mode 是什么:先想清楚再动手
Plan Mode 是 Claude Code 的一个工作模式,开启后 Claude 只做调研和方案设计,不会修改任何文件、不执行任何写操作,直到你确认计划并退出 Plan Mode。
进入 Plan Mode 有两种方式:
# 方式一:启动时直接进入
claude --plan
# 方式二:会话中切换(在 Claude Code 提示符中按 Shift+Tab 循环模式)
# Auto -> Plan -> Accept Edits -> Auto
Plan Mode 下的核心交互流程:
你描述任务
-> Claude 只读代码库,分析依赖关系
-> Claude 输出方案(改动清单、风险点、执行顺序)
-> 你确认或要求调整
-> 退出 Plan Mode,进入执行
Plan Mode 的价值不在"AI 多聪明",而在它强制把"想"和"做"分开,避免边想边改导致的连锁错误。
TodoWrite:任务清单才是执行的骨架
TodoWrite 是 Claude Code 内置的任务管理工具,它会维护一个结构化任务列表,每完成一项就标记为已完成,并优先处理列表中尚未完成的事项。
TodoWrite 的核心作用:
- 显式拆解:把大任务拆成可独立验收的小步骤
- 抗压缩:上下文压缩时,TodoWrite 列表会保留,AI 能据此继续推进
- 进度可视:你和 AI 都能清楚看到还剩多少事没做
一个典型的 TodoWrite 调用长这样:
{
"todos": [
{"id": "1", "content": "梳理现有 Flask 单体结构和路由", "status": "completed", "priority": "high"},
{"id": "2", "content": "抽取数据库模型层到 models/", "status": "in_progress", "priority": "high"},
{"id": "3", "content": "拆分用户模块路由到 user/routes.py", "status": "pending", "priority": "high"},
{"id": "4", "content": "拆分订单模块路由到 order/routes.py", "status": "pending", "priority": "high"},
{"id": "5", "content": "运行测试验证拆分后功能", "status": "pending", "priority": "high"}
]
}
关键约束:同一时刻只有一个 in_progress 任务,这强制 AI 串行推进,不会同时开多个战线。
实战:把单体 Flask 应用拆成模块化结构
下面用一个真实场景把 Plan Mode + TodoWrite 的完整流程走一遍。
场景描述
一个跑了两年的 Flask 电商后台,所有代码挤在 app.py 一个 1800 行的文件里,包含用户、订单、商品三块业务。现在要拆成模块化结构,目标目录:
my-shop/
├── app.py # 入口,只负责创建 app 和注册蓝图
├── models/
│ ├── __init__.py
│ ├── user.py
│ ├── order.py
│ └── product.py
├── user/
│ ├── __init__.py
│ └── routes.py
├── order/
│ ├── __init__.py
│ └── routes.py
└── product/
├── __init__.py
└── routes.py
第一步:用 Plan Mode 锁定方案
启动 Claude Code 进入 Plan Mode:
cd my-shop
claude --plan
输入任务描述:
我要把 app.py 这个 1800 行的单体文件拆成模块化结构。
要求:
1. 按用户、订单、商品三个业务域拆分
2. 每个域拆出 models 和 routes 两层
3. 用 Flask Blueprint 组织路由
4. 保持所有现有 URL 不变
5. 不能破坏现有的 12 个单元测试
先分析现状,给我一份拆分计划,不要改任何文件。
Claude 在 Plan Mode 下会读取 app.py,分析路由、模型、依赖关系,然后输出一份方案,大致包含:改动文件清单、每个文件的职责、迁移顺序、风险点(比如循环依赖、数据库会话作用域)。
确认方案没问题后,按 Shift+Tab 切回 Auto 模式,让 Claude 开始执行。
第二步:让 Claude 用 TodoWrite 拆解执行
切回 Auto 模式后,明确要求 Claude 用 TodoWrite 管理任务:
按照刚才的计划执行拆分。请先用 TodoWrite 把任务拆成清单,
每完成一步就更新状态,全部完成后再跑一遍 pytest。
Claude 会自动生成类似这样的任务清单,并逐步执行:
[1] 创建模块目录结构(models/, user/, order/, product/) in_progress
[2] 抽取 User 模型到 models/user.py pending
[3] 抽取 Order、Product 模型 pending
[4] 抽取用户路由到 user/routes.py(Blueprint) pending
[5] 抽取订单路由到 order/routes.py pending
[6] 抽取商品路由到 product/routes.py pending
[7] 重写 app.py 为蓝图注册入口 pending
[8] 运行 pytest 验证 pending
第三步:关键代码示例
拆分后 app.py 从 1800 行精简到 30 行左右:
from flask import Flask
from models import db
from user.routes import user_bp
from order.routes import order_bp
from product.routes import product_bp
def create_app():
app = Flask(__name__)
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///shop.db'
db.init_app(app)
app.register_blueprint(user_bp, url_prefix='/user')
app.register_blueprint(order_bp, url_prefix='/order')
app.register_blueprint(product_bp, url_prefix='/product')
return app
if __name__ == '__main__':
app = create_app()
app.run(debug=True)
user/routes.py 示例,注意 Blueprint 的写法:
from flask import Blueprint, request, jsonify
from models import db
from models.user import User
user_bp = Blueprint('user', __name__)
@user_bp.route('/list', methods=['GET'])
def list_users():
users = User.query.all()
return jsonify([u.to_dict() for u in users])
@user_bp.route('/create', methods=['POST'])
def create_user():
data = request.get_json()
user = User(name=data['name'], email=data['email'])
db.session.add(user)
db.session.commit()
return jsonify(user.to_dict()), 201
第四步:验收
Claude 执行完会自动跑 pytest。如果测试失败,TodoWrite 里"运行 pytest 验证"那项会保持 in_progress,Claude 会根据失败信息继续修,直到测试通过。
进阶:Plan + Subagent 组合处理超大型任务
当任务规模大到单个会话装不下时(比如全仓迁移某个框架),可以把 Plan Mode 产出的方案拆给多个 Subagent 并行执行。
在项目根目录创建 .claude/agents/refactor-worker.md:
---
name: refactor-worker
description: 执行单个模块的拆分工作
tools: Read, Edit, Write, Bash
---
你是一个专门负责模块拆分的工作 agent。
输入:模块名、目标目录、依赖说明。
输出:完成该模块的拆分,并报告改动文件列表。
不要修改其他模块的代码。
主会话里这样编排:
按计划把用户、订单、商品三个模块的拆分
分别交给 3 个 refactor-worker 子 agent 并行处理。
每个子 agent 完成后汇报改动文件,我来统一跑测试。
Subagent 的核心价值在于隔离上下文:每个子 agent 有独立的上下文窗口,不会互相污染,主 agent 只回收简短的汇报结果,避免主上下文被代码细节撑爆。
常见问题 FAQ
Q1:Plan Mode 下 Claude 不改文件,那它怎么分析代码?
Plan Mode 下 Claude 保留只读工具(Read、Glob、Grep),能读代码、搜符号、看依赖,只是不能写。这正是"调研"该有的权限边界。
Q2:TodoWrite 列表会不会被上下文压缩清掉?
不会。TodoWrite 的状态由 Claude Code 框架持久维护,不依赖主上下文。压缩发生时,Claude 会先读取 TodoWrite 当前状态,再决定下一步,这正是它抗压缩的原因。
Q3:重构过程中测试挂了,Claude 会自己修吗?
会。只要 TodoWrite 里有"运行测试验证"这一项且状态还是 in_progress,Claude 会读取 pytest 输出,定位失败用例,修正代码,再跑一遍,直到通过或遇到无法自动解决的问题才会停下来问你。
Q4:Plan Mode 和直接给 Claude 一份详细 prompt 有什么区别?
区别在权限边界。直接给 prompt 时 Claude 边想边改,一旦理解有偏差就已经产生脏改动;Plan Mode 把"理解"和"修改"隔离开,理解阶段零写操作,偏差只停留在方案文本里,确认前没有任何副作用。
Q5:Subagent 并行拆分,怎么保证模块间不冲突?
靠 Plan 阶段划清边界。方案里要明确每个模块负责的文件清单,Subagent 的系统提示里写死"不要修改其他模块的文件"。共享文件(如 app.py 入口、models/__init__.py)由主 agent 统一处理,不交给子 agent。
小结
复杂重构任务不失控的关键,是给 AI 编码工具加上两道约束:动手前先出方案(Plan Mode),执行中始终有清单(TodoWrite)。这两件事本身不依赖模型有多聪明,而是用流程把"想"和"做"分开,把"做到哪了"显式化。当任务大到单会话装不下时,再叠加 Subagent 做上下文隔离。流程对了,AI 才是助手;流程不对,越聪明的 AI 越容易把项目带进沟里。
本文发布于 1630.top,转载请注明出处。
本文发布于 1630.top,转载请注明出处。