我把 Claude Code 当作主力开发工具已经大概 9 个月了,而我最终形成的工作流,和大多数人使用 AI 编码工具的方式完全不一样。大部分开发者会敲一段提示,偶尔用一下“规划模式”,然后修修报错,再重复这一套。更“网瘾”一点的,会拼各种 ralph loop、MCP、gas town(还记得这些吗?)之类的东西。两种路数的结果都一样:只要一稍微不那么简单,整个东西就会直接散架。
我要讲的这个工作流只有一个核心原则:在你审阅并通过一份书面的实现计划之前,绝对不要让 Claude 写任何代码。
把“规划”和“执行”彻底分开,是我做的最重要的一件事。它能避免浪费精力,让架构决策始终掌握在我手里,而且相比直接上来就写代码,这种方式在 token 使用更少的前提下,能稳定产出更好的结果。

第一阶段:研究
只要是有意义的任务,我一开始都会先下一个“深度阅读”指令。我会让 Claude 在做任何事之前,先把相关代码区域“啃”一遍。而且我**一定会要求**它把研究结果写进一个持久存在的 markdown 文件里,而不是只在聊天里随便总结两句。
比如:
- 深入阅读这个目录,深刻理解它是如何工作的、在做什么、以及所有具体细节。完成之后,把你的所有理解和发现写进 research.md 里。
- 把通知系统彻底、详细地研究一遍,搞清楚其中所有错综复杂的细节,然后写一份详尽的 research.md 文档,把通知机制的方方面面都记录下来。
- 沿着任务调度流程从头到尾过一遍,深入理解它的运行方式,并且查找所有潜在的 bug。系统里肯定有 bug,因为它有时会执行本该被取消的任务。持续研究这个流程,直到你把所有 bug 都找出来,没找全不要停。完成后,把你的发现写进 research.md。
注意上面的措辞:“深刻理解 (deeply)”、“非常详细( in great details)”、“复杂之处 (intricacies)”、“全部过一遍 (go through everything)”。这些词不是废话。如果不加这些强调词,Claude 就会“扫一眼就过”。它可能只看一下函数签名层面的含义,就往下翻了。你必须用语言明确表达出:浅层阅读是不可接受的。
那个写好的 research.md 文档是关键。这不是在给 Claude 布置“作业”,而是我自己的“审阅界面”。我可以读这份文档,验证 Claude 真的理解了系统,如果哪里理解错了,可以在任何规划开始之前先纠正它。如果研究错了,后面的计划就会错,实现也会错——垃圾进,垃圾出。
在 AI 辅助编码里,最昂贵的失败模式其实不是语法错误或者逻辑错误,而是:实现本身看似“自洽”,却悄悄把周边系统搞坏了。比如,一个函数完全忽略了现有的缓存层;一个迁移没有考虑 ORM 的约定;一个 API endpoint 复制了系统里已经存在的逻辑。研究阶段就是用来尽量避免这些问题的。
第二阶段:规划
等我审阅完 research 之后,我会让它在另一个 markdown 文件里写出详细的实现计划。
例如:
- 我要构建一个新功能:<功能名称和描述>,它会扩展系统来实现 <业务结果>。请写一份详细的 plan.md,说明如何实现这个功能。包含代码片段。
- 现在列表接口需要改成 cursor-based pagination(游标分页),而不是 offset(偏移量)分页。请写一份详细的 plan.md,说明如何实现。先阅读相关源码,再提出修改方案,计划必须基于真实代码来写。
生成出来的计划里,通常会包括:完整的实现思路说明、展示实际变更的代码片段、需要改动的文件路径、以及各种注意事项和权衡点。
我用的是自己项目里的 .md 计划文件,而不是 Claude Code 内置的“Plan 模式”。内置的那个计划模式很拉。用 markdown 文件就完全在我掌控之中:我可以在编辑器里随便改、加行内批注,而且它作为项目的一部分是持久存在的。
我非常常用的一个小技巧
如果要做的是一个边界很清晰的功能,而我知道在某个开源仓库里有一个很好的实现示例,我会在提需求的时候直接把那段代码贴给 Claude 当“参考实现”。比如我要加可排序的 ID,我会把一个项目里做得不错的 ID 生成代码贴进去,然后说:“这是他们实现可排序 ID 的方式,请写一份 plan.md,说明我们如何采用类似的方案。”
有一份具体的参考实现可以对照时,Claude 的表现要比从零开始“凭空设计”好得多。
不过,计划文档本身并不是最有意思的部分。真正有意思的是后面要发生的事。
注解循环:Annotation Cycle
这是整个工作流里最有特色的一环,也是我自己“注入价值”最多的地方。

在 Claude 写完计划之后,我会把 plan.md 在编辑器里打开,然后**直接在文档里加行内批注**。这些批注用来纠正假设、否掉某些做法、补充约束条件,或者告诉它一些 Claude 不可能知道的业务背景。
批注的长度变化很大。有时候只是一句两个词:在一个被它标成可选的参数旁边写“不能可选 not optional”。有时候则是一整段,用来解释业务约束,或者贴上一个我更期待的数据结构示例。
一些我实际会写的批注例子:
- “迁移要用 drizzle:generate,不要写裸 SQL。”—— 这是 Claude 不知道的域知识;
- “不,HTTP 方法应该用 PATCH,不是 PUT。”—— 指出错误的假设;
- “这一节整个删掉,这里不需要做缓存。”—— 否掉某个子方案;
- “队列消费者已经负责重试了,这里再写一套重试逻辑是多余的。删掉,让它直接抛错就行。”—— 解释为什么需要改;
- “这里不对,visibility 字段应该挂在列表本身上,而不是每个 item 上。列表公开时,所有 item 都是公开的。重新设计一下这一节的 schema。”—— 直接把整个方案部分“改路”。
然后我再把 Claude 召回到这份文档上:
1我在文档里加了一些批注,请逐条处理这些批注并更新文档。先不要开始实现。
这个循环会重复 1~6 次。那句明确的“先不要实现 (don’t implement yet)”是一个非常重要的护栏。如果没有这句,Claude 一旦觉得计划“差不多了”,就会抢先开始写代码。而在我看来,只有当我说“可以了”的时候,计划才真的足够好。
为什么注解循环这么有效
这个 markdown 文件本质上是我和 Claude 之间的一块共享可变状态。我可以用自己的节奏思考,在“问题的那一行”旁边精准地写上修改意见,然后随时让 Claude 再次介入。我不需要在聊天里长篇大论解释一大堆上下文——我只是直接指向文档中某个具体位置,在那里写好我想要的更改。
这和在聊天里试图“遥控”实现过程,完全不是一个东西。计划是一个结构化、完整的规格说明,我可以整体性地审视它;聊天记录则是一坨需要来回滚动才能重新拼起上下文的东西。两者相比,计划永远更胜一筹。
经过三轮“我加了批注,请更新计划”的往返之后,一个普通的、模板化的实现计划,就会被打磨成一个**精准贴合现有系统**的方案。Claude 在理解代码、提出解决方案、以及写出实现方面非常强,但它不知道我的产品优先级、用户的真实痛点、或者我愿意做出的工程权衡。注解循环就是我把这些判断力注入进去的方式。
Todo List:实现清单
在真正开始实现之前,我会再要求它把计划拆解成一份细粒度的任务清单:
1在计划里加上一份详细的 todo list,把完成这个计划需要的所有阶段和具体任务都列出来——先不要实现。
这样就有了一份 checklist,可以在实现过程中作为进度追踪器使用。Claude 在完成每一个任务时都会在计划里标记为“已完成”,这样我随时只要瞄一眼 plan.md,就能知道现在进展到哪一步了。这个在持续几个小时的长会话里尤其有价值。
第三阶段:实现
等计划打磨到位,我就会发出“实现指令”。我已经把这个指令提炼成一个几乎在每个会话里都会复用的标准提示词:
1把计划里的内容全部实现。每完成一个任务或阶段,就在计划文档中标记为完成。不要停下,直到所有任务和阶段全部完成。不要添加不必要的注释或 JSDoc,不要使用 any 或 unknown 类型。持续运行 typecheck,确保没有引入新的问题。
这一句提示里,包含了我关心的所有东西:
- “把计划里的内容全部实现”:按计划做完所有事情,不要挑着做;
- “在计划文档中标记为完成”:进度的唯一真相来源就是这份计划;
- “不要停下,直到全部完成”:中途不要频繁停下来等我确认;
- “不要添加不必要的注释或 JSDoc”:保持代码干净;
- “不要使用 any / unknown 类型”:保持严格的类型体系;
- “持续跑 typecheck”:尽早发现问题,而不是堆到最后一口气修。
我几乎在每次实现阶段都会用非常相似的措辞。等我说“实现全部内容 (implement it all)”的时候,所有关键决策都已经做完并且经过验证了。此时的实现阶段应该是机械性的,而不是创造性的。这是刻意设计的。**我希望实现阶段无聊一点**。真正有创意的工作已经在注解循环里完成了;计划一旦正确,执行就应该是“按图施工”。
如果省略掉规划阶段,常见的情况是:Claude 在一开始做出一个看似合理、实际上却错误的假设,接着用 15 分钟的时间在这个错误假设上不断“加盖子”,最后我需要一点一点拆掉它之前做的所有修改。有了“先不要实现”的护栏之后,这种情况几乎不会发生。
实现过程中的反馈
一旦 Claude 开始执行计划,我的角色就从“架构师”切换成“监工”。我的提示会变得非常短。
在规划阶段,一条批注可能是一整段话;到了实现阶段,一个纠正往往只需要一句话:
- “你还没实现 `deduplicateByTitle` 函数。”
- “你把设置页做在主应用里了,它应该放在 admin 应用里,挪过去。”
因为 Claude 拥有完整的计划上下文和整个会话的历史,这样短句式的纠正就足够了。
前端工作是迭代最快的部分。我会在浏览器里测试,然后连续发出一连串超短的命令:
- “再宽一点”
- “还是被裁切了”
- “这里有 2px 的缝”
遇到视觉问题时,我有时会附上一张截图。一张“错位的表格”的截图,比我用文字去描绘要快得多。
我也会不停地引用现有代码作为参照:
- “这个表格应该和用户列表的表格一模一样:相同的表头、分页、行间距。”
这比从零开始描述一套设计要精确得多。在一个成熟的代码库里,大部分功能其实都是既有模式的变体。新的设置页应该长得像已有的设置页。指向参照物,就能把所有隐含要求一起传达过去。Claude 通常会先读一下这些参照文件,再做修改。
当某个方向明显走偏时,我不会试图修修补补,而是直接回滚并重新收紧范围(比如用 git 把改动全部丢掉):
- “我把所有改动都回滚了。现在我只想让列表视图看起来更简洁——只做这一件事。”
在回滚之后缩小 scope,几乎总是比在错误方向上“硬改”要好得多。
保持主导权:Staying in the Driver’s Seat
虽然我把执行这件事交给了 Claude,但我从来不会把“做什么”这件事完全放手给它。整个开发过程中,绝大部分“主动驾驶”都是我在 plan.md 中完成的。
这点很重要,因为 Claude 有时会提出一些在技术上“没毛病”,但对项目而言完全不合适的方案。可能是架构过度复杂,可能是改动了某个被广泛依赖的公共接口,或者它在两个方案里倾向了一个更复杂的选项,而实际上一个更简单的办法就足够用了。我掌握着关于更大系统、产品方向以及工程文化的上下文,而 Claude 没有。
在计划阶段和实现阶段,我会做一些“逐项裁剪”的事:
从提案里按项挑选
当 Claude 罗列出一堆需要改动的点时,我会逐条过一遍:
1“第一条,用 `Promise.all` 就够了,别搞太复杂;第三条,把它提取成一个单独函数以提高可读性;第四和第五条忽略,这两点不值得为了它们增加复杂度。”
我在每一项上做选择,是基于我当前知道“什么对于项目是重要的”。
刻意减小范围
当计划里包含了一些看着“很不错”、但不是当下必须要做的东西时,我会主动砍掉它们。
1“把下载功能从计划里删掉,这次我不想实现它。”
这样可以防止 scope 一路膨胀。
保护现有接口
在我知道某些东西绝对不能变的时候,我会给出硬约束:
1“这三个函数的签名不能改,应该是调用方去适配,而不是库本身去迁就。”
覆盖技术选型
有时,我会针对一些 Claude 不会知道的偏好,直接更改:
1“用这个模型,而不是那个。”2“用这个库自带的方法,不要自己写。”
Claude 负责“机械执行”,而我负责“价值判断”。大的决策通过计划提前定好,小的决策则在实现过程中根据情况微调。
单一长会话:Single Long Sessions
我倾向于在一个长会话里完成研究、规划和实现,而不是把它们拆成多个独立会话。一个会话可能从深度阅读某个目录开始,然后经历三轮计划注解循环,最后再跑完全部实现,整个过程都在同一条聊天历史里完成。
大家常说在上下文使用超过 50% 之后模型性能会下降,但我并没有真的看到这种明显的劣化。实际上,当我说“开始实现”的时候,Claude 已经在整个会话里花了大量时间去构建对系统的理解:研究阶段读过很多文件,在注解循环中不断修正自己的心智模型,也吸收了我通过批注传递给它的域知识纠正。
当上下文窗口被填满时,Claude 的自动压缩会保留足够的关键信息,让它能继续正常工作。而计划文档作为一个持久的外部实体,则完全不受压缩影响。我可以在任何时候把 Claude 再次指向那份文件。
一句话总结这个工作流
先深度阅读,再写计划,用注解把计划打磨到位,然后让 Claude 按计划一次性把所有实现做完,全程盯住类型检查。
就这样。没有什么神奇提示词,没有复杂的系统指令,没有花里胡哨的小技巧。只是一个有纪律的流水线,把“思考”和“敲代码”彻底分离开来。
研究阶段防止 Claude 在完全不了解的前提下胡乱改代码;计划阶段防止它做出偏离系统真实需求的实现;注解循环把我的判断力注入进去;而最终的实现指令,则是在所有决策已经明晰之后,让它无阻碍地把剩下的“体力活”干完。
试试这套工作流,你大概率会惊讶:原来没有一份带注解的计划文档垫在你和代码之间的时候,你之前那些“AI 代理写代码”的体验有多糟。
原文:How I Use Claude Code, Boris Tane


