大仓库里 Claude Code 变慢、找不到文件:官方 large codebases 页给出的做法
在几百万行的单体仓库或者十几个包的 monorepo 里用 Claude Code,抱怨通常是两件事:它读了一堆跟这次改动无关的文件;你让它改某个包,它却在整棵树里翻。
官方文档专门有一页讲这个,路径是 code.claude.com/docs/en/large-codebases。这一页对问题的描述是它自己的口径:随着代码库变大,为小项目调好的默认行为会把与任务无关的说明和文件读取塞满上下文窗口,消耗 token 并让 Claude 的表现变差。这句是文档自述。本文依据只有官方文档的公开内容,我们没有对文中任何配置做过实测。下面按排查顺序走:先判定、再处置、再验证,最后说清楚什么情况说明不是这个原因。
一、先判定:到底加载了什么
改配置之前先做两个动作,都是文档里写明的。
看加载了哪些 CLAUDE.md:large-codebases 页写明,运行 /context,看 Memory files 下面那个列表。
看加载了哪些设置文件:code.claude.com/docs/en/settings 页写明,运行 /status,菜单 Status 标签下有一行 Setting sources,列出当前会话加载的每一层来源,例如 User settings 或 Project local settings。这一页给了两个限定:这一行只确认哪些来源被读到,不显示某个键最终由哪一层提供;而且一个来源要加载了至少一个设置才会出现,所以 JSON 写坏的文件根本不会出现在这一行里。
设置文件有语法错误或值校验不过时,文档写明交互式会话启动会弹出 Settings Error 对话框;选择继续之后 /status 会列出受影响的文件,claude doctor 能看到每个错误的详情。托管设置走另一套更宽容的流程:不合法条目被剥掉并记录警告,其余策略照常生效,用 /doctor 列出被剥掉的条目及其来源文件和字段。
跑完这两条,你就知道上下文里躺着哪些不该躺的说明文件,以及你以为写好的那份设置到底加载没加载。
二、处置的前提:你从哪个目录启动
文档把「Choose where to start Claude」放在所有配置之前。启动目录决定三件事:不用额外授权就能读写哪些文件、启动时加载哪些 CLAUDE.md、哪份项目设置生效。
文档给的对照是:
- 从仓库根启动:可访问每个文件;启动时只加载根
CLAUDE.md,子目录的文件在 Claude 读到那里时按需加载 - 从子目录启动:只限那棵子树(除非另行授权);启动时加载该目录以及每一层祖先目录的
CLAUDE.md
有个反直觉的地方文档单独强调过:.claude/settings.json 里的项目设置只从启动目录加载,不像 CLAUDE.md 那样从父目录继承。放在仓库根的 .claude/settings.json,只有从根启动时才生效。这条不弄明白,后面配置都可能放错地方。
作用域这边,settings 页列了四个:Managed、User、Project、Local。Windows 上文档里写作 ~/.claude 的路径解析为 %USERPROFILE%\.claude。
三、分层 CLAUDE.md 与 claudeMdExcludes
第一个处置是把说明拆开:根 CLAUDE.md 放全仓通用规则(编码规范、提交约定、仓库结构),每个子目录的 CLAUDE.md 放那块代码自己的约定,提交进仓库让队友继承,通常由各目录负责人维护。
从仓库根启动时,子目录的 CLAUDE.md 会在 Claude 读到那里的文件时加载进来。claudeMdExcludes 按路径或 glob 跳过指定文件让它们永不加载。settings 页对它的描述是:匹配的是绝对文件路径,且只作用于 user、project、local 三类记忆,托管策略下发的 CLAUDE.md 无法被排除。
文档给的示例写在 .claude/settings.local.json 里:
{
"claudeMdExcludes": [
"**/packages/web/**"
]
}
其它模式的语义文档也写了:"**/packages/*/CLAUDE.md" 排除每个包的 CLAUDE.md 但保留根文件;"**/packages/legacy-*/**" 排除名字匹配该 glob 的每个包,连规则文件一起;也可以用一条绝对路径只排除某个具体文件。文档给的绝对路径示例是类 Unix 形式,Windows 下绝对路径怎么写这一页没有给出对应示例,别照着拼。相对写法则要以 **/ 开头才能匹配树里任意位置。
还有一点容易忽略:这份列表是静态的,不是按任务切换的开关——文档明说想今天专注一个包、明天换另一个,正确做法是从那个包的目录启动 Claude。另外手写 .claude/settings.local.json 要自己加进 gitignore,文档写明只有 Claude Code 自己保存设置到这个文件时才会替你加。
四、挡住生成物与 vendored 代码
文档写明 Claude 的内容搜索默认遵守 .gitignore,所以已经列在里面的 node_modules/、dist/、build/ 不用额外配置就不会进搜索结果。要处理的是被签入仓库的那些:vendored SDK、提交进去的生成代码。这类靠 permissions.deny 里的 Read 规则挡:
{
"permissions": {
"deny": [
"Read(./**/dist/**)",
"Read(./**/build/**)",
"Read(./**/*.generated.*)",
"Read(./vendor/**)"
]
}
}
边界文档写得很清楚,别理解成「加了就一定读不到」:deny 规则覆盖 Claude 内置的文件工具,以及能被识别出来的 Bash 文件命令(文档点名 cat、head、grep、find),前提是被拒路径作为参数传进去。它不会把被拒路径从递归搜索的输出里过滤掉,也不覆盖自己去打开文件的任意子进程。
规则放哪份文件决定它管谁:提交到 .claude/settings.json 管所有人(仍受第二节那条限制);只管自己就用仓库根的 .claude/settings.local.json。这里有个坑文档明确写了:像 Read(./vendor/**) 这样的相对模式仍然锚定在你启动 Claude Code 的目录,所以习惯从子目录开会话的话要写成 // 开头的绝对路径形式,例如 Read(//absolute/path/to/repo/vendor/**)。文档另注明在 v2.1.211 之前,.claude/settings.local.json 同样只从启动目录加载。要在每个会话强制生效且不允许用户和项目设置覆盖,文档指向的是托管设置。
五、用 code intelligence 插件替代满树翻找
「某个符号定义在哪」这件事,文档给的处置是接语言服务器:code intelligence 插件让 Claude 跳转定义、查找引用、拿到类型错误,而不是扫整棵树。官方 marketplace 有 TypeScript、Python、Go、Rust 等常见语言的插件。文档给的安装命令,在会话里运行:
/plugin install typescript-lsp@claude-plugins-official
装不上时按报错对号入座:报 Marketplace "claude-plugins-official" not found,先用 /plugin marketplace add anthropics/claude-plugins-official 加上 marketplace 再重试;报插件在 marketplace 里找不到,就是名字写错了。前置条件有两条:每台开发机上要装有该语言的语言服务器二进制;从官方 marketplace 安装需要能访问 GitHub,内网受限时文档给的路子是从内部 Git 主机或本地路径添加 marketplace。想让整仓的人都启用而不是各自安装,用 enabledPlugins 这个项目设置。
六、worktree 只检出你要的目录
--worktree 在新的 git worktree 里开会话,让改动和主检出隔离。文档写明它默认检出整个仓库;worktree.sparsePaths 用 git sparse-checkout,只把列出的目录加根级文件写到磁盘:
{
"worktree": {
"sparsePaths": [
".claude",
"packages/api",
"packages/shared"
],
"symlinkDirectories": [
"node_modules"
]
}
}
容易踩的语义:sparsePaths 里的路径相对仓库根,跟你从哪个子目录启动无关;只能列目录不能列单个文件;根级文件(package.json、锁文件之类)总会一并检出,但根级目录不会——想在 worktree 里用到仓库根的 .claude/settings.json、.claude/rules/、.claude/skills/,就得把 .claude 显式列进去。symlinkDirectories 把主仓库的目录软链进每个 worktree 以免重复占盘,settings 页写明默认不软链任何目录。另外一个会话里所有 worktree 共用同一份 sparsePaths,所以一个 subagent 要 packages/api/、另一个要 packages/web/,两个都得列。
还有个 git 层面的副作用别当成 bug:sparse checkout 需要 git 在仓库共享的 .git/config 里启用 extensions.worktreeConfig。文档写明 Claude Code 会在最后一个 worktree 移除后清掉这个条目,但仅限于是它自己加上的;文档另注明在 v2.1.207 之前条目会残留,基于 go-git 的工具(文档点名 tea)会打不开仓库,直到手动执行 git config --unset extensions.worktreeConfig。
七、跨包访问:additionalDirectories 与 --add-dir 不是一回事
从 packages/api/ 启动后要顺手改 packages/shared/,文档给了两条路,并写明了差别。设置里配:
{
"permissions": {
"additionalDirectories": [
"../shared",
"../web"
]
}
}
启动时临时加:
claude --add-dir ../shared
差别在于配置会不会跟着加载。文档给的对照:用 additionalDirectories 设置加进来的目录,它的 CLAUDE.md、.claude/rules/ 和 skills 都不会加载;用 --add-dir 标志或 /add-dir 命令加进来的,skills 会加载,CLAUDE.md 和 rules 则需要额外设环境变量:
CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1 claude --add-dir ../shared
文档同时写明这个环境变量对 additionalDirectories 设置里列的目录没有作用。以上为按官方文档中的参数语义组合的示例,未经实测,以官方文档与 --help 的实际输出为准。
Windows 侧要提一句:VAR=1 command 这种前缀写法是 POSIX shell 语法,PowerShell 或 CMD 下等价怎么写官方文档没有说明这一点——但 settings 页写明 settings.json 的 env 键里配的环境变量会应用到每个会话以及 Claude Code 从中派生的子进程,这是文档里有依据的替代路子(同一页也注明有几类由宿主环境自己拥有的身份变量在这里设了会被忽略)。文档创建技能目录用的 mkdir -p packages/api/.claude/skills/api-testing 同理,Windows 下的等价命令文档没写。
八、处置后怎么验证
回到第一节那两个判定动作上核:跑 /context 看 Memory files 里排除掉的文件是否消失、该加载的是否都在;跑 /status 看 Setting sources 是否列出了你刚写的那一层,没出现要么没加载要么 JSON 坏了;报错时 claude doctor 看详情,托管设置用 /doctor 看被剥掉的条目。
settings 页还写明 Claude Code 会监视设置文件并在变更时重载,permissions、hooks 等多数键无需重启即可生效;但 model 和 outputStyle 是会话启动时读一次的,改完要 /model 切换或 /clear、重启才算数。
skills 这边有一条值得单记:文档写明 Claude 靠读每个被发现的 skill 的名称和描述来挑,只有被选中的那个才把完整内容装进上下文;skill 数量多时描述会被截短,可能剪掉判断是否适用的关键词,所以文档建议描述写短、把请求里会出现的词放前面。settings 页有个 skillListingBudgetFraction 控制每轮留给 skill 列表的上下文比例,超出预算时最少用到的 skill 只剩名称。
九、什么情况说明不是这个原因
这一节最容易被略过,但它决定你会不会白折腾一天。
从仓库根启动单体大仓时,第七节整段不适用。 文档自己写了:从根启动 Claude 已经能访问每个文件。这种场景下的「读不到文件」,原因在别处。
被读到的是 .gitignore 已覆盖的路径时,不是 deny 规则没配,文档写明内容搜索默认就遵守 .gitignore,先确认那个路径是不是其实被签入了;加了 deny 规则后路径仍出现在递归搜索输出里,也不是规则失效,文档明说 deny 不会过滤递归搜索的输出,也不覆盖自行打开文件的子进程。
要排除的是托管策略下发的 CLAUDE.md 时,claudeMdExcludes 排不掉。 文档写明组织级说明始终生效。
.claude/settings.json 放在仓库根却从子目录启动时,问题不在配置内容而在位置。 worktree 场景有个对应的坑:文档写明 sparsePaths 和 symlinkDirectories 是在创建 worktree 之前从启动目录读取的,创建之后会话工作目录变成 worktree 根,项目设置改从 worktree 根那份检出副本加载。所以希望在 worktree 里也生效的权限规则、hooks 要放进仓库根的 .claude/settings.json——文档「Put it together」一节就是把 deny 规则在包目录和仓库根各放了一份。
symlinkDirectories 没生效时,先确认你不是把示例当默认值了。 settings 页那张表最后一列是 Example 不是 Default,文档写明默认不软链任何目录。
如果上面这些都排掉了,问题可能压根不在配置层:文档对「分目录 CLAUDE.md 多到难以治理」给的方向是把约定挪进按需加载的机制——skills、plugin,或者把组织里已有的代码检索索引暴露成 MCP 工具让 Claude 去查,而不是继续加排除规则。
本文依据 Claude Code 官方文档(code.claude.com/docs)于 2026-08-17 的公开内容整理。
该产品闭源,本文只复述官方文档写明的机制,不推断其内部实现;
我们没有对文中涉及的功能做过实测,因此不涉及界面外观、操作手感与运行速度的任何描述。
该产品迭代频繁,文中涉及的命令、配置项与默认值随版本变动,请以官方文档最新内容为准。
本文不涉及价格、额度与限流的具体数值,相关信息请以官方定价与用量说明页为准。
安全与合规相关做法请结合自身环境评估,本文不构成安全方案建议。