当仓库很大时,Claude Code 在 monorepo 中容易出现偏离。了解如何设定边界、本地摘要和可重复的工作流程,以保持回答精确。

Claude Code 在 monorepo 中之所以显得不可预测,有一个简单原因:仓库比模型一次能在工作记忆中容纳的内容要大。
“上下文”是指为了完成该任务而向 Claude 展示的一组文件、代码片段、笔记和指令,以及它能从中推断出的信息。当关键细节缺失时,Claude 会用猜测来填补空白。在大型仓库中,这种情况更常见。
有三种常见的失败模式会反复出现:
首先,漏掉文件。看起来在某个文件夹里做的改动是安全的,但实际上依赖于某个共享类型、配置规则或在别处定义的构建步骤。如果该依赖不在上下文中,Claude 可能自信地编辑错误的东西,或者因为看不到真实的事实来源而提前停止。
第二,错误的相似性。Monorepo 常包含多个看起来很像的包:两个认证模块、三个 API 客户端,或几个结构相似的 React 应用。Claude 可能把模式混在一起,更新错包里的辅助函数,或从“几乎正确”的模块名导入。
第三,时间漂移。大型代码库通常同时保留旧的和新的实现方式。如果 Claude 只看到了较旧的文件,它可能会复制已弃用的模式(已废弃的配置选项、遗留 API),即使团队已经迁移到新方式。
一个常见的真实例子:你请求对计费 UI 做一个小改动,Claude 却修改了一个被其他应用共享使用的 payments 组件,因为它从未看到应该改动的应用级包装器。
目标不是把整个 monorepo 都给 Claude。目标是提供更小、经过设计的输入,仍能回答问题:你要改动的包、它的直接依赖,以及一到两个类型和配置的“事实来源”。同时明确指出“不可动”的区域(其他应用、基础设施、生成代码),并确认哪个包拥有该行为。
准确性依赖的不是你粘贴了多少代码,而是你多么清晰地描述了任务。
先说明你想要的结果:一个具体的修复、重构或答案。“关于代码的问题”可以保持高层次;“做出改动”的请求需要边界、输入和验收检查。
在分享任何东西之前,写一句完成句子来回答这个短语:“在你完成后,我应该能够……”。例如:“能够在不失败的情况下运行 package X 的单元测试”或“在端点 Y 的 API 响应中看到新字段”。当仓库庞大时,这句话就是北极星。
对于改动,请共享能证明改动正确的最小工件集合:入口点、相关的类型/接口或模式、一个失败的测试或带有预期结果的复现步骤,以及影响该路径的任何配置(路由、特性开关、构建或 lint 规则)。如果有帮助,可以添加一个简短的包文件夹地图,帮助 Claude 理解每个目录的用途。
明确说明不需要查看的内容。比如写明:“忽略生成文件、vendor 文件夹、构建输出、快照和锁文件,除非我要求。”这能防止在你不打算审查的地方浪费时间和进行修改。
同时为不确定性设定期望。要求 Claude 标注假设和未知项,而不是猜测。例如:“如果你看不到这个函数在哪里被调用,请说明并提出两种定位它的方法。”
在大型 monorepo 中,当模型“好意”地拉入不属于任务的附近代码时,准确性会下降。解决方法很直接:在请求更改前,定义哪些是范围内、哪些是范围外。
从与你仓库组织方式一致的边界开始:一个 package、service、app 或共享库。如果改动是“更新结账 UI”,边界可能是一个 app 包,而不是所有出现“checkout”字样的地方。
能帮助 Claude 保持范围的信号包括文件夹约定(apps/、services/、packages/、libs/)、包清单(exports 和 dependencies)、公共入口点(index 文件、导出的组件、处理器)以及测试(测试通常揭示预期的表面)。文件夹里的 README 常常是最快的边界标记。
边界最有效的情况是你同时点出它们之间的桥接处。说明 Claude 可能会触及的具体接口,并把其他一切视为禁区。典型的桥接包括 HTTP API 协议、事件主题和载荷、共享类型,或少量导出函数。
在改动不应影响它们时,明确标记“不可修改”区域。常见的有基础设施与部署配置、安全与认证逻辑、计费与支付、数据迁移与生产模式,以及被多个团队使用的共享库。
一个有帮助的具体提示示例:
“仅在 packages/cart/ 及其测试中做修改。你可以读取 packages/types/ 中的共享类型,但不要修改它们。不编辑 infra、auth 或 billing。”
当你提供一张小而稳定的地图时,准确性会提升。所谓“本地摘要”就是这张地图:足够短以便快速阅读,足够具体以防猜测。
每个摘要保持在大约 10 到 20 行。把它写成你把代码交给只需要触及该边界的新同事的方式。用简单语言和代码中的真实名字:文件夹、包、导出的函数。
一个有用的本地摘要应回答五个问题:
追加一句“注意事项”。这是避免昂贵错误的地方:隐藏缓存、特性开关、迁移步骤以及任何会悄然出问题的东西。
下面是一个紧凑的模板(可复制并使用):
Local summary: <package/service name>
Purpose: <1 sentence>
Scope: <what to touch> | Not: <what not to change>
Entry points: <files/routes/commands>
Public surface: <exports/endpoints/events>
Data sources: <tables/collections/queues/caches>
Conventions: errors=<how>, logging=<how>, tests=<where/how>
Gotchas: <flags/caching/migrations/edge cases>
示例:如果你在编辑一个计费包,记下创建发票的确切函数、它写入的表名,以及关于可重试错误的规则。这样 Claude 就能专注于该边界,而不是漫游到共享认证、配置或不相关包中。
最好的摘要就是在需要时 Claude 能看到的那份。把它放在描述代码的旁边,这样不容易被忽视且易于更新。例如,在每个 package、service 或 app 目录中保留短小的 SUMMARY.md(或在 README.md 中的一节),而不是把所有内容放在仓库根目录的一个大文档里。
一个简单、可重复的结构有助于维护。保持摘要足够短,以便人们愿意维护:
YYYY-MM-DD - <what changed in one sentence>摘要会因为可预测的原因变旧。把更新摘要当成完成工作的一个步骤,就像更新类型定义一样,而不是一项单独的任务。
在重构更改结构或名称、新模块成为主要方式、API/事件/模式发生改变(即使测试仍然通过)、包之间的边界移动,或依赖被移除或替换时,更新摘要。
一个实用习惯:合并变更时,在 PR 中添加一行“Last updated”说明发生了什么。像 Koder.ai 这样的工具可以帮助你更快地推进代码更改,但摘要才是保持未来变更准确的关键。
准确性常常取决于你如何节奏化对话。让 Claude 以小步子“挣取”上下文,而不是从一个巨大的快照中猜测。
在任何编辑之前,要求 Claude 描述它所看到的内容和它还需要什么。一个好的地图很短:涉及的关键包、流程入口、测试或类型所在位置。
提示示例:
“Create a map of this change: packages involved, main flow, and likely touch points. Do not propose code yet.”
选择一个狭窄的切片:一个功能、一个包、一个用户流程。明确边界(例如:“仅修改 packages/billing-api/。不要触碰 shared-ui 或 infra。”)。
一个能让你保持掌控的工作流:
如果 Claude 缺少某些东西,它应当说明。要求它写出: (1) 它做出的假设,(2) 哪些情况会证明这些假设是错的,(3) 下一个需要确认的文件。
示例:你需要在某个包的 Invoice 响应中添加字段。Claude 请求处理器、DTO/类型定义和一个测试。你只分享这些。如果你在使用基于聊天的构建器(例如 Koder.ai),同样规则适用:先提供最小的一组源文件,然后仅在确实需要更多时再扩展。
防止错误修改的最好办法是在提示里写一个小“契约”:说明可触及的文件、你如何判断成功,以及它必须遵守的规则。
从一个易于遵守且便于验证的边界开始。明确说明允许编辑的地方,并点出“不可触碰”的区域,以避免漫游的诱惑。
契约模板示例:
packages/payments/ 下的文件。\n- 不要编辑 packages/auth/、infra/ 或任何共享配置。\n- 如果需要在范围外修改,先停下并询问。\n- 保持改动最小:修复 bug,避免重构。然后定义验收检查。没有这些,Claude 可能会产生看起来正确但违背仓库真实规则的代码。
样式约束也很重要。告诉 Claude 哪些模式该遵循、哪些应避免,基于你代码库已有的做法。例如: “在此包使用现有的错误处理 helpers;不要引入新依赖;函数名保持 camelCase;不要引入新的架构层。”
最后,在任何编辑前要求提交一个简短的变更计划:
“在编辑前,列出你预期要改动的 3–5 个文件和确切的行为变化。等待批准。”
示例:
“修复发票总额的四舍五入问题。仅编辑 packages/billing/src/ 和 packages/billing/test/ 下的测试。验收:运行 pnpm -C packages/billing test 并通过类型检查。遵循现有的金额工具,不要重写 API 类型。先提供 4 步计划。”
让 Claude 一次看到太多内容是导致错误修改的最快方式。当你粘贴一大堆代码,它常常退回到通用模式,而不是遵循你仓库已有的具体设计。
另一个陷阱是让它猜架构。如果你不展示真实的入口点,它可能会选择看起来可行的第一个文件并在那里接线。实践中,准确性来自一小组“事实来源”的文件(入口模块、路由、服务注册、包边界文档)。如果这些不在上下文中,模型会去填空。
名字也会误导它。Monorepo 常有 ui、ui-kit、shared-ui 之类的包,或在多个地方有重复的辅助函数 date.ts。如果你混合来自两处的片段,Claude 可能会修改一个文件,同时在脑中以另一个文件为依据。举例:你要求更改一个按钮样式,它编辑了 packages/ui/Button.tsx,但应用实际导入的是 packages/ui-kit/Button.tsx。差异看起来没问题,但生产环境没有变化。
配置也是导致静默漂移的源头。行为可能依赖于环境变量、特性开关、构建设置或工作区工具。如果你不提及这些,Claude 可能会删掉一个“奇怪”的检查(该检查只有在某个开关打开时才重要),或加入破坏构建步骤的代码。
一些表明你正在漂移的红旗:
把跨包导入当作一个决策,而不是默认行为。除非你有意扩展范围,否则保持改动本地化。
获得正确改动的最快方法是以限制开始,而不是以大量信息开始。一个好的提示应该有点严格:告诉 Claude 去哪儿看、忽略什么,以及“完成”意味着什么。
在你粘贴代码前,写一段简短的前言,把工作固定到仓库中的一个位置。写明包名、确切文件夹和具体目标。然后附上本地摘要(目的、关键依赖、重要约定)和锚定改动的入口文件。
检查清单:
\u003cpackage\u003e/\u003cpath\u003e 工作。目标:\u003cone sentence\u003e。除非另行说明,否则忽略其他内容。\n- 上下文起点:本地摘要:\u003c5-10 lines\u003e。入口文件:\u003cpath/to/file\u003e。\n- 约束:允许的文件夹:\u003c...\u003e。不得修改:\u003cfolders/files or APIs\u003e。保持行为:\u003cwhat must stay true\u003e。\n- 先要文件请求:“在提出改动前,列出你需要查看的最少文件(最多 5 个)并说明原因。”\n- 输出格式:先回复一个简短计划。确认后,按文件给出补丁式建议。如果 Claude 提议做范围外的改动,把它当作一个信号:要么收紧提示,要么有意扩展范围并重新声明边界。
假设你的 monorepo 有 apps/web-store(一个 React 应用)和 packages/ui-kit(共享按钮、输入和样式)。你想做一个小功能:在购物车页面添加一个“稍后保存”按钮,使用来自 ui-kit 的新 SaveIcon。其他内容都不应改变。
在请求编辑之前,为两个区域创建本地摘要来作为边界。保持简短、具体,并强调重要点。
# apps/web-store/LOCAL_SUMMARY.md
Purpose: Customer shopping UI.
Entry points: src/routes.tsx, src/pages/cart/CartPage.tsx
Cart rules: cart state lives in src/cart/useCart.ts
Do not touch: checkout flow (src/pages/checkout), payments, auth.
Tests: npm test -w apps/web-store
# packages/ui-kit/LOCAL_SUMMARY.md
Purpose: shared UI components.
Exports: src/index.ts
Icons: src/icons/*, add new icons by exporting from index.
Do not touch: theming tokens, build config.
Tests: npm test -w packages/ui-kit
然后把循环保持紧凑:
CartPage 和 ui-kit 图标。不修改 checkout/auth。”\n2. 假设:要求列出假设并等待确认。\n3. 文件请求:只批准它确实需要的文件(CartPage、useCart、ui-kit 图标、ui-kit index)。\n4. 计划:确认符合边界。\n5. 编辑:做最小差异,然后请求更新后的测试。更改完成后,记录下来以便未来上下文保持小:
如果这种方式对一个人有效但对团队不起作用,缺失的通常是可重复性。把“良好的上下文卫生”设为默认,而不是个人习惯。
保存一个提示骨架,供每个人复制并填写。保持简短但严格。包含目标(什么是“完成”)、允许的范围、硬性边界(以及原因)、本地摘要和输出契约(先计划,然后按 diff 风格给出改动和测试)。
跳过没人做的大型月度审查。在变更影响行为、依赖或 API 时,把摘要更新作为同一个 PR 的一部分。
一个简单规则:如果某个同事会问“这东西在哪里?”或“谁依赖它?”,说明摘要已经过时。
如果你偏好以聊天为主的工作流,Koder.ai 能帮助你把这种迭代方式做得更安全。Planning 模式能在编辑前就达成范围共识,快照与回滚功能让你在猜测出现错误时快速恢复。
当 Claude 不能“看到”真实的事实来源时,准确性就会下降。\n\n在大型 monorepo 中,模型常常漏掉某个依赖文件、把两个相似的包混淆,或者复制出上下文里出现的旧做法。
不要试图把整个仓库都贴过来。先从能证明变更正确的最小集合开始。\n\n一个不错的默认集合是:\n\n- 行为的入口点\n- 关键类型/接口/模式\n- 一个失败的测试或清晰的复现步骤与预期结果\n- 影响该路径的任何配置(路由、特性开关、构建/lint 规则)
分享能锚定该行为的文件,而不是按名字相关的所有内容。\n\n一个实用集合是:\n\n- 行为开始的文件(路由/处理器/组件)\n- 定义“契约”的文件(DTO/类型/模式)\n- 应该通过的测试(或最小复现)\n- 一两个会改变运行时行为的配置文件
选一个和仓库组织方式一致的边界:一个 package、app 或 service。\n\n然后明确说明它的范围和不在范围内的内容。示例约束:\n\n- “仅修改 packages/cart/ 及其测试。”\n- “可以读取共享类型,但不可修改。”\n- “非必要不要触及 infra/auth/billing。”
因为 monorepo 中常有长得很像的模块(ui、ui-kit、shared-ui)和重复的帮助函数(多个地方都有 date.ts)。\n\nClaude 可能会把正确的思路应用到错误的包,或从“几乎正确”的模块名中导入。通过命名确切的包和入口文件来避免这一点。
本地摘要是你希望变更的那一小片区域的简短地图,通常 10–20 行。\n\n包含:\n\n- 目的和范围(是什么 / 不是)\n- 入口点\n- 公共表面(导出/端点)\n- 关键依赖\n\n- 约定(错误/日志/测试)\n- 一个防止常见错误的“注意事项”
把摘要放在靠近它所描述的代码旁边,这样更容易找到和更新。\n\n一个简单默认:\n\n- SUMMARY.md 或包的 README.md 中的一小节\n- 每个 package/service/app 各一份(不要放到仓库根目录的一个大文档里)\n- 当结构、API 或边界改变时更新“Last updated”行
在一开始就告诉 Claude 要标注假设和未知项,而不是盲猜。\n\n一个有用的规则:\n\n- 如果它看不到某处的调用或配置,应当说明。\n- 它应提出两种定位缺失事实的方法(例如“给我路由文件”或“给我 package 导出表”)。
用一个强制的紧循环,让上下文逐步被“挣取”:\n\n1. 要求一个短地图(关键触点,先别写代码)。\n2. 选一个切片 + 一个边界。\n3. 要求列出假设和接下来 3–6 个需要的文件。\n4. 批准这些文件后,要求计划,然后按文件给出补丁式改动并运行测试。
在提示中写一个小“合同”,并使其可执行:\n\n- 允许的路径和“禁止触碰”的区域\n- 若需跨域更改则先停下并询问\n- 最小改动策略(修复 bug,避免重构)\n- 验收检查(测试、lint/格式、类型检查/构建)\n\n这会让审查更容易,减少意外的跨包修改。