【译】我是如何使用 Claude Code 的

0 阅读14分钟

原文地址

我如何使用 Claude Code

我已经将 Claude Code 作为我的主要开发工具使用了大约 9 个月。我摸索出的这套工作流与大多数人使用 AI 编程工具的方式截然不同。

大多数开发者只是输入一个提示词(Prompt),有时会用一下“计划模式”(plan mode),修复错误,然后不断重复。更有甚者,整天在网上折腾什么 Ralph LoopsMCPs(模型上下文协议)、Gas Towns 之类的。在这些情况下,结果往往一团糟,一旦面对非琐碎的任务就会完全崩盘。

我要介绍的这套工作流核心原则只有一个:在审阅并批准一份书面计划之前,绝不让 Claude 编写任何代码。

这种将“规划”与“执行”分离的做法,是我所做的最重要的事情。它能防止浪费精力,让我始终掌控架构决策,并且相比直接跳到编码阶段,它能以最少的 Token 消耗产生显著更好的结果。

调研(Research)
规划(Plan)
标注(Annotate)
待办事项清单(Todo List)
实施(Implement)
反馈与迭代(Feedback & Iterate)


第一阶段:调研

每一个有意义的任务都始于一个“深度阅读”指令。在进行任何操作之前,我要求 Claude 彻底理解代码库的相关部分。而且我始终要求将研究结果写入一个持久的 Markdown 文件,而不仅仅是聊天记录里的口头总结。

Prompt 示例:

  • “深入阅读这个文件夹,深度理解它的工作原理、功能以及所有特性。完成后,在 research.md 中写一份详细的学习与调研报告。”

  • “非常详细地研究通知系统,理解其中的复杂细节,并编写一份详尽的 research.md 文档,记录关于通知系统运作方式的所有已知信息。”

  • “梳理任务调度流程,深度理解并查找潜在的 Bug。系统中肯定存在 Bug,因为有时会运行本该被取消的任务。持续调研该流程直到找到所有 Bug,在找到所有 Bug 之前不要停止。完成后,在 research.md 中写一份详细的调查报告。”

注意我使用的语言:“深入地(deeply)”、“非常详细地(in great details)”、“复杂细节(intricacies)”、“梳理所有细节(go through everything)”。这不只是废话。如果没有这些词,Claude 就会走马观花。它会读一个文件,在函数签名层面看一眼功能,然后就跳过了。你需要发出信号:表面层的阅读是不可接受的。

书面产物(research.md)至关重要。这不仅仅是让 Claude 做功课,它是我的评审界面。我可以阅读它,验证 Claude 是否真的理解了系统,并在任何规划开始前纠正误解。如果调研错了,计划就会错,实施也会跟着错。垃圾进,垃圾出(Garbage in, garbage out)。

这是 AI 辅助编程中最昂贵的失败模式,而且这通常不是因为语法错误或逻辑不通,而是因为实现方案在孤立环境下可行,却破坏了周围的系统。比如:一个忽略了现有缓存层的函数;一个不符合 ORM 约定的迁移脚本;或者一个重复了现有逻辑的 API 端点。调研阶段可以防止这一切发生。


第二阶段:规划

一旦我审阅完调研报告,我会要求在另一个单独的 Markdown 文件中制定详细的实施计划。

Prompt 示例:

  • “我想构建一个新功能 <名称和描述>,扩展系统以实现 <业务结果>。写一份详细的 plan.md 文档,概述如何实现这一点。包含代码片段。”

  • “列表端点应支持基于游标的分页,而不是偏移量分页。写一份详细的 plan.md 说明如何实现。在建议更改之前阅读源文件,计划要基于实际的代码库。”

生成的计划通常包含:对方法的详细解释、显示实际更改的代码片段、将被修改的文件路径,以及相关考量和权衡。

我坚持使用自己的 .md 计划文件,而不是 Claude Code 内置的计划模式。内置的计划模式很鸡肋。我的 Markdown 文件让我拥有完全的控制权:我可以在编辑器里编辑它,添加行内注释,而且它作为项目中的真实产物是可以持久保存的。

我经常使用的一个技巧是:对于一些封装良好的功能,如果我在某个开源仓库里见过优秀的实现,我会把那段代码作为参考资料和计划请求一起发给它。例如,如果我想添加可排序的 ID,我会粘贴一个做得很好的项目的 ID 生成代码,并说:“这是他们实现可排序 ID 的方式,写一份 plan.md 说明我们如何借鉴类似的方法。” 当 Claude 有具体的参考实现而不是从零开始设计时,它的表现会戏剧性地变好。

但计划文档本身并不是最有趣的部分,有趣的是接下来发生的事。


标注循环(The Annotation Cycle)

这是我工作流中最独特的部分,也是我贡献价值最多的环节。

流程图:

  1. Claude 编写 plan.md

  2. 我在编辑器中审阅

  3. 我添加行内注释

  4. 将 Claude 发回文档

  5. Claude 更新计划

  6. 是否满意?(不满意回到第 2 步,满意则进入下一步)

  7. 请求待办事项清单(Todo list)

Claude 写完计划后,我会在编辑器中打开它,直接在文档中添加行内注释。这些注释用于纠正假设、拒绝某种方案、添加约束条件,或者提供 Claude 所不具备的领域知识。

注释的长度差异很大。有时只是两个单词:在 Claude 标记为可选的参数旁边写上“不是可选”;有时则是一个段落,解释业务约束或粘贴一段我期望的数据结构代码片段。

我添加的一些真实注释示例:

  • “使用 drizzle:generate 生成迁移,不要用原始 SQL。” ——(Claude 不知道的领域知识)
  • “不 —— 这应该是 PATCH,而不是 PUT。” ——(纠正错误假设)
  • “完全删除这一节,这里不需要缓存。” ——(拒绝提议的方案)
  • “队列消费者已经处理了重试逻辑,所以这里的重试逻辑是多余的。删掉它,直接让它失败。” ——(解释为什么要更改)
  • “这不对,可见性字段(visibility)应该在列表本身,而不是在单个条目上。当列表公开时,所有条目都公开。据此重构 Schema 部分。” ——(重新导向整个章节的计划)

然后我把 Claude 发回文档:

“我在文档里加了几处注释,请处理所有注释并相应地更新文档。先不要实施。”

这个循环会重复 1 到 6 次。明确的 “先不要实施” 禁令至关重要。如果没有它,Claude 一旦觉得计划足够好,就会立刻跳到代码环节。但在我说好之前,它都不够好。

为什么这行之有效

Markdown 文件充当了我和 Claude 之间的共享可变状态。我可以按自己的节奏思考,精确地标注错误的地方,并在不丢失上下文的情况下重新介入。我不需要在聊天信息里解释一切,我直接指着文档中具体的位置,把修正写在那里。

这与通过聊天信息引导实施有着本质区别。计划是一个结构化的、完整的规范,我可以进行全局审查。而聊天对话则需要翻阅历史记录来重组决策。计划文档在任何时候都更胜一筹。

三轮“我加了注释,更新计划”的操作,可以将一个平庸的实施方案转化为一个完美契合现有系统的方案。Claude 非常擅长理解代码、提议方案和编写实现,但它不了解我的产品优先级、用户的痛点,或者我愿意在工程上做出的权衡。标注循环就是我注入这些判断力的方式。


待办事项清单(The Todo List)

在开始实施之前,我总是要求一个细粒度的任务拆解:

“在计划中添加详细的待办事项清单(todo list),包含完成计划所需的所有阶段和单个任务 —— 先不要实施。”

这会创建一个检查清单,在实施过程中作为进度追踪器。Claude 会随做随记,将已完成的项打勾,这样我随时看一眼计划就能知道进度。在持续数小时的会话中,这非常有用。


第三阶段:实施

当计划就绪后,我会发布实施命令。我将其精炼成了一个在不同会话中重复使用的标准 Prompt:

“全部实施(implement it all)。当你完成一个任务或阶段时,在计划文档中将其标记为已完成。在所有任务和阶段完成之前不要停止。不要添加不必要的注释或 JSDoc,不要使用 anyunknown 类型。持续运行 typecheck 以确保没有引入新问题。”

这一个 Prompt 包含了所有核心要点:

  • “全部实施”:执行计划中的所有内容,不要挑三拣四。
  • “在计划文档中标记为已完成”:计划是进度的唯一事实来源。
  • “在完成前不要停止”:中途不要停下来向我确认。
  • “不要添加不必要的注释”:保持代码整洁。
  • “不要使用 any 类型”:维持严格的类型检查。
  • “持续运行 typecheck”:尽早发现问题,而不是拖到最后。

在几乎每一个实施会话中,我都使用这段话(稍作改动)。到我说出“全部实施”时,所有的决策都已经做完并验证过了。实施过程变成了机械性的,而不是创造性的。 这是故意的。我希望实施阶段是枯燥的。创造性的工作发生在标注循环中。一旦计划正确,执行就应该是水到渠成的。

如果没有规划阶段,通常会发生的情况是:Claude 在早期做出了一个合理但错误的假设,并在此基础上构建了 15 分钟,然后我不得不回溯并撤销一连串的更改。“先不要实施”这道防线彻底消灭了这种可能性。


实施过程中的反馈

一旦 Claude 开始执行计划,我的角色就从架构师转变为监督者。我的 Prompt 会变得非常简短。

反馈流程:

  1. Claude 实施代码

  2. 我审阅 / 测试

  3. 是否正确?(正确则看是否有更多任务,不正确则发出简短纠正)

在规划阶段,一个注释可能是一个段落;而在实施阶段,一个纠正通常只有一句话:

  • “你没实现 deduplicateByTitle 函数。”
  • “你把设置页面写在了主应用里,它应该在管理员应用(admin app)里,把它挪过去。”

由于 Claude 拥有计划和当前会话的完整上下文,这种简短的纠正就足够了。

前端工作是迭代最频繁的部分。我在浏览器中测试并快速发出修正指令:

  • “再宽一点”
  • “还是被裁剪了”
  • “这儿有个 2px 的缝隙”

对于视觉问题,我有时会附上截图。一张对齐错误的表格截图比文字描述更能快速传达问题。

我也经常参考现有代码:

  • “这个表格应该看起来和用户表(users table)完全一样:同样的表头、同样的分页、同样的行间距。”

这比从头描述设计要精确得多。在成熟的代码库中,大多数功能都是现有模式的变体。一个新的设置页面应该看起来像现有的设置页面。指向参考文件可以传达所有隐含的要求,而无需费力解释。Claude 通常会在修正前阅读参考文件。

当事情走向错误的方向时,我不尝试修补。我会通过撤销 Git 更改来重置并重新调整范围:

  • “我撤销了所有内容。现在我只想让列表视图更简约一些 —— 仅此而已。”

在撤销后缩小范围,几乎总是比试图增量修复一个错误的方案效果更好。


保持主导性

尽管我将执行委托给了 Claude,但我从不给它决定“构建什么”的完全自主权。我在 plan.md 文档中完成了绝大部分的主动引导。

这很重要,因为 Claude 有时会提出技术上正确但对项目来说是错误的方案。也许该方法过度设计了,或者它改变了一个其他系统依赖的公共 API 签名,又或者它在有更简单的选择时选了一个复杂的。我拥有关于整个系统、产品方向和工程文化的上下文,而 Claude 没有。

我会从提案中“精挑细选”: 当 Claude 发现多个问题时,我会逐一过一遍:“对于第一个,直接用 Promise.all,别搞太复杂;对于第三个,提取成独立函数以提高可读性;忽略第四和第五个,不值得为此增加复杂度。” 我是基于对当前重点的了解来做出单项决策的。

削减范围: 当计划中包含了一些“有了更好(nice-to-haves)”的功能时,我会主动砍掉它们:“从计划中删除下载功能,我现在不想实现这个。” 这防止了范围蔓延(scope creep)。

保护现有接口: 当我确信某些东西不应改变时,我会设定硬约束:“这三个函数的签名不能变,调用方应该去适配,而不是库去适配。”

覆盖技术选择: 有时我有特定的偏好,而 Claude 无法预知:“使用这个模型而不是那个模型”,或者“使用这个库的内置方法,而不是手写一个自定义方法”。这些是快速、直接的指令覆盖。

Claude 处理机械性的执行,而我负责做判断决策。计划在前期捕捉大决策,而选择性的引导则处理实施过程中出现的细小决策。


单一长会话

我会在一个单一的长会话中完成调研、规划和实施,而不是将它们拆分到不同的会话中。一个会话可能始于深度阅读一个文件夹,经历三轮计划标注,然后运行完整实施,这一切都在一段连续的对话中完成。

我并没有察觉到大家常说的“上下文窗口超过 50% 后性能下降”的问题。实际上,到我说出“全部实施”时,Claude 已经用整个会话建立了深刻的理解:在调研期间阅读文件,在标注循环中完善思维模型,吸收我对领域知识的修正。

当上下文窗口填满时,Claude 的自动压缩功能(auto-compaction)能保留足够的上下文以维持运行。而且计划文档作为持久产物,在压缩过程中能保持完整的保真度。我可以在任何时间点让 Claude 参考它。


一句话总结工作流

深入阅读,编写计划,标注计划直到它完美,然后让 Claude 不停顿地执行全部内容,并沿途检查类型。

就是这样。没有神奇的提示词,没有复杂的系统指令,没有投机取巧的黑科技。只有一个严谨的流水线,将“思考”与“打字”分离开来。

  • 调研:防止了 Claude 做出无知的修改。
  • 计划:防止了它做出错误的修改。
  • 标注循环:注入了我的判断力。
  • 实施命令:则让它在所有决策达成后,无干扰地运行。

试一下我的工作流吧,你会惊讶于在没有一份标注过的计划文档介入的情况下,你以前是怎么敢用编程 Agent 发布代码的。

扫码_搜索联合传播样式-白色版.png