前言:200K tokens 还不够?这很常见
在实际使用中会发现——Claude Code 号称拥有 200K tokens 的上下文窗口,但一旦面对大型代码库,这个容量其实很容易达到上限。
粗略估算一下:一个普通的 Java 服务文件通常有 200-500 行,按每行 10 个 token 计算,200K tokens 理论上可以容纳 400-1000 个完整文件。听起来相当充裕,对吧?但一旦进入深入调试或功能开发阶段,Claude 会连带读取依赖文件、测试代码和配置文件——上下文很快就会填满,随后征兆开始显现:它开始“失忆”,反复出现之前已经明确修正过的错误。
我们当时接手了一个运行了 4 年的 Spring Cloud 微服务项目,代码量超过 8 万行,各模块之间的依赖关系错综复杂。起初,Claude Code 只是被当作一个“更聪明”的代码补全工具——改几个文件、写几个测试,能跑就行。但很快发现,同事能用它 20 分钟搞定的事,自己却往往要忙上两个小时。
问题并不在模型,而在于使用方式。
以下这 10 个技巧,是我们踩过不少坑之后才真正理解的。
技巧一:CLAUDE.md 不是“说明书”,超过 200 行反而有害
常见的误区
许多开发者初次使用时,容易将 CLAUDE.md 视为一本“万能手册”——项目架构、API 文档、代码规范、常用命令、禁止事项等所有内容一股脑塞进去,洋洋洒洒写上 500 行。觉得写得越详尽,Claude 的表现就会越好。
实际情况恰恰相反。Claude 经常会忽略文件后半段的内容,明确交代不能修改的文件依然被改,说好要用的框架也被替换成另一个。
为什么会这样
CLAUDE.md 的内容在每个会话开始时都会被直接注入上下文窗口。一份典型的项目 CLAUDE.md 大约消耗 1800 tokens。但如果文件膨胀到 500 行,消耗就会增加到 5000-8000 tokens。
更重要的是,CLAUDE.md 是以“上下文”的形式加载的,而非作为“配置规则”。Claude 读取它之后,并不会像代码里的 if/else 那样严格执行,而是“知道了这些信息,再根据情况自行判断”。文件越长,重要规则被稀释的可能性就越高,最终导致每条规则的权重都差不多。
正确的做法
官方推荐的做法是:将 CLAUDE.md 保持在 200 行以内,只写入那些 Claude 靠自己读不到的信息。
值得写入的部分:
# 项目构建- 运行:mvn clean test -pl user-service(单服务测试)- 全量构建:./scripts/build-all.sh(大约 3 分钟)# 禁止修改的文件- src/legacy/DataMigration.ja va(迁移脚本,已废弃但生产依赖)- .env.production(生产环境变量,只读不改)# 代码规范差异- 此项目使用 MyBatis,不引入 JPA 或 Hibernate- 服务间通信走 OpenFeign,不用 RestTemplate# 架构决策- 每个服务独立数据库,禁止跨服务直接访问 DB不推荐写入的内容: Java 基本语法、标准 REST 规范、以及 Claude 能够通过阅读代码自行推断出的信息。
如果规则确实较多,可以利用 .claude/rules/ 目录进行路径隔离——例如,仅在修改 src/api/**/*.java 时加载 API 规范文件。让规则按需加载,避免每次都占用大量上下文。
// .claude/rules/api-conventions.md 的 frontmatter---paths:- "src/api/**/*.ja va"---# API 规范(只在修改 API 层文件时加载)- 所有接口返回 Result 包装类- 参数校验使用 ja vax.validation 注解,避免手写 if 判断标准很简单: 如果去掉某条规则,Claude 是否会做出不同的决策? 如果“不会”,那就果断删掉它。
图:一个典型的 200K tokens 上下文分配示意——启动自动注入约 7,400 tokens;一次典型调试 Session 消耗约 30,400 tokens(15%);探索陌生大模块不使用 Subagent 可能耗掉 80%+
技巧二:一个 Session 别干太多事
常见的误区
打开 Claude Code 后,让它修一个 Bug,修完顺手加个新功能,加完再写测试,顺便更新文档……几个小时过去,Session 上下文里充斥着各种文件读取记录、试错历史和修改日志,变得混乱不堪。
接着,Claude 开始犯一些奇怪的错误——约定好的变量名会被替换成另一个;之前已经修好的问题又再次出现。
为什么会这样
Claude 的 Session 是有状态的,所有对话内容都会累积在上下文里。读了 10 个文件、尝试了 3 种方案、来回修改了 2 次——这些全都在消耗上下文。当上下文接近上限时,Claude 会触发自动压缩,把之前的对话总结成摘要。
但压缩是有损的。你在对话中临时做出的决定、中途调整的方向、某个特定的约束条件,在压缩后很可能丢失。 而 CLAUDE.md 里的内容压缩后会重新注入,因此写入文件里的规则比口头交代的可靠得多。
正确的做法
养成在任务切换后重置 Session 的习惯。使用 /clear 在无关任务之间清空上下文。
一个实用的工作流程:
# 任务 A:修复登录 Bugclaude# 完成后/clear# 任务 B:新增支付功能claude如果当前任务需要阅读大量文件(比如理解一个陌生模块),可以使用 Subagent——让子 Agent 去读取文件、进行调查,只把结论带回来,避免探索过程污染主 Session 的上下文。
用 subagent 去读一下 payment-service 模块,弄清楚现有的支付流程,告诉我需要在哪个环节新增支付渠道子 Agent 在自己的独立上下文里运行,即使读取 100 个文件,也不会影响主 Session 的正常工作。
图:遇到新任务时的 Session 管理决策树——何时使用 /clear、何时 /compact、何时用 Subagent、何时开 Worktree
技巧三:Plan 模式——应对大型代码库最有效的工具
常见的误区
直接让 Claude 修改代码,特别是在大型代码库里,你往往不清楚它会改动哪些文件。结果改完一看,动了 15 个文件,其中有几处的改法根本不对,甚至不知道从哪开始 revert。
为什么会这样
大型代码库的修改常常会引发连锁反应。改一个接口,可能会涉及 Controller、Service、Mapper、DTO、测试文件、文档等多个层面……Claude 知道要改这些,但执行的顺序和具体的修改方式未必符合你的预期。在代码真正落地之前,你根本没有机会说“等一下,这里不对”。
正确的做法
启动 Plan 模式(快捷键 Shift+Tab 切换,或使用 --permission-mode plan)。在这个模式下,Claude 会先读取文件、分析代码,然后给出完整的变更计划,但不会执行任何实际的修改。
实践表明,这对大型代码库的效率提升最为显著。典型的工作流程:
[Plan 模式下]我想在 user-service 里添加手机号登录功能。请先阅读现有的登录实现,告诉我需要修改哪些文件,以及每个文件具体的改动方向。Claude 会给出类似这样的计划:
计划修改以下文件:1. UserController.ja va - 添加 /login/phone 接口2. UserService.ja va - 添加 phoneLogin() 方法3. SmsService.ja va - 添加验证码校验逻辑4. UserMapper.ja va - 添加按手机号查询用户的 SQL5. LoginDTO.ja va - 添加 PhoneLoginRequest 内部类不需要修改:认证中间件(复用现有 token 逻辑)确认计划没有问题后,再切换回正常模式让 Claude 执行。这样一来,所有改动的代码都是你知情并且同意的,不会出现意外情况。
进阶用法: 看完计划后,可以按 Ctrl+G 在文本编辑器里直接编辑计划,修改其中有问题的部分,再让 Claude 按照修改后的计划来执行。
技巧四:上下文不够用时,多开几个 Session 并行跑
常见的误区
始终在同一个 Session 里工作,遇到上下文快满的提示就用 /compact 压缩一次,然后继续跑。但压缩后 Claude 的表现明显变差,却又找不到原因。
为什么会这样
/compact 会把历史对话压缩成摘要来节省 tokens,但压缩后 Claude 丢失了大量上下文细节——之前读过的文件内容、探索过的路径,在摘要里可能只剩下几行概括性的描述。
对于复杂任务来说,一个 Session 根本不够用。
正确的做法
使用 Worktrees 来开启多个并行 Session,每个 Session 独立处理各自的子任务。
# 创建一个新的 worktree,专门处理认证模块重构claude --worktree auth-refactor# 另一个终端,另一个 worktree,处理支付模块claude --worktree payment-feature每个 Worktree 都是独立的 git 分支和独立的文件系统快照,两个 Session 之间的修改互不干扰。完成任务后合并即可。
对于大型单体仓库(monorepo),还可以通过配置 sparsePaths 进行稀疏检出,让 Claude 只看到它需要的部分:
// .claude/settings.json{"worktree": {"baseRef": "fresh","symlinkDirectories": ["node_modules", ".cache"],"sparsePaths": ["packages/user-service", "shared/common-utils"]}}这样,在一个 monorepo 中,Claude 只会关注 user-service 和共用工具部分,不会受到其他 50 个服务代码的干扰。
技巧五:赋予 Claude 自我验证的能力,效果倍增
常见的误区
让 Claude 改完代码后,自己去跑测试来查看结果,然后再回来告诉 Claude “有个测试失败了”。一来一回,效率损失相当大。
为什么会这样
Claude 的核心能力在于“探索-修改-验证”的循环。如果每次修改后都需要等待你的反馈结果,这个循环就被人为中断了。很多本来可以自动修复的小问题,最终变成了需要手动介入的环节。
更重要的是,当 Claude 能够自己运行测试、看到输出结果时,它的修复精准度会明显提升——因为它直接面对真实的错误信息,而不是你转述的二手信息。
正确的做法
在 prompt 中明确告诉 Claude 验证方法:
修改 UserService 的 findByPhone 方法,让它在手机号格式不对时抛出 InvalidPhoneException。改完之后运行 mvn test -pl user-service -Dtest=UserServiceTest,确保测试通过。若测试失败,根据错误信息继续修改。或者使用 Hooks 实现自动化——每次文件修改后自动运行 lint:
// .claude/settings.json{"hooks": {"PostToolUse": [{"matcher": "Edit|Write","hooks": [{"type": "command","command": "mvn checkstyle:check -pl user-service","timeout": 30}]}]}}这样一来,每次 Claude 改完代码,都会自动运行一次 checkstyle,违规情况会直接反馈给 Claude,它会自己动手修复,无需人工监控。
技巧六:用 @ 精确引用文件,比让 Claude 漫无目的地“找”快得多
常见的误区
“帮我修一下登录的 Bug”——于是 Claude 开始对整个项目进行 grep 搜索,读十几个文件,耗费大量上下文 tokens,才找到问题所在。
为什么会这样
如果不告诉 Claude 去哪里找,它就会进行代码库级别的搜索——这个过程非常消耗上下文。每一次 grep、每一次 Read 都在占用 token 配额。在 200K 的上下文里,读 5 个 2000 行的文件就花掉了约 5% 的额度。
正确的做法
用 @ 直接引用你知道的相关文件或目录:
@src/api/UserController.ja va @src/service/UserService.ja va登录接口在高并发下出现了 token 不一致的问题,请帮我检查可能存在的并发问题。这样,Claude 可以直接获取你指定的上下文,无需到处搜索。这不仅减少了不必要的文件读取,也让 Claude 更聚焦在你认为相关的代码上。
进阶技巧: 为大型项目配置自定义的文件建议命令。这样当 @ 触发自动补全时,将是项目专属的,而不是通用的文件系统搜索:
// ~/.claude/settings.json 或 .claude/settings.json{"fileSuggestion": {"type": "command","command": "~/.claude/scripts/project-file-index.sh"}}这个脚本可以结合项目结构(比如 Maven 的 module 划分)返回更精确的文件建议,而不是列出整个文件树。
技巧七:定期核查 Auto Memory 记录了什么
常见的误区
不知道有 Auto Memory 功能,或者知道但认为它是黑盒,放任不管。
为什么会这样
Claude Code 的 Auto Memory 功能,会把你对 Claude 行为的纠正、项目的特殊规则等信息存入 ~/.claude/projects/<项目>/memory/MEMORY.md,并在下次 Session 时自动加载。这虽然很有用,但存在两个潜在问题:
第一,你可能不知道它“记住”了什么错误的东西。比如曾经在某个测试场景下让它使用了一个临时方案,结果它把临时方案作为长期规则记了下来,之后每次都如此操作,让你莫名其妙。
第二,MEMORY.md 的前 200 行会自动加载进每个 Session,如果记录了大量杂乱的内容,就会无形中消耗宝贵的上下文。
正确的做法
定期审查 Auto Memory:
# 打开当前项目的自动记忆文件~/.claude/projects/<项目hash>/memory/MEMORY.md或者在 Session 里运行 /memory,查看当前加载的所有记忆文件,该删除的就删除,该修正的就修正。
对于明确不想让 Claude 自动记忆的内容,可以在 CLAUDE.md 里显式声明——CLAUDE.md 里的规则优先级更高,并且由你主动维护,比 Auto Memory 更可控。
技巧八:Subagent 不只是“让它自己动脑”,更是上下文隔离的关键
常见的误区
只知道 Subagent 可以并行处理任务,以为它只是一个“加速”工具。
为什么会这样
Subagent 最重要的作用其实不是速度,而是上下文隔离。当你让 Claude 探索一个陌生模块时,它会读取大量文件,这些读取操作全都会进入主 Session 上下文。但如果改用 Subagent,这些读取都发生在子 Agent 的独立上下文里,主 Session 只收到最终的结论。
正确的做法
所有“先了解一件事,再告诉我结果”的任务,都适合使用 Subagent:
用 subagent 去调查一下 order-service 里的库存扣减逻辑,包括:现在的并发处理方式、已有的测试覆盖范围、有没有已知的并发问题。调查完后告诉我结论,不需要给我读文件的过程。更高级的用法: Writer/Reviewer 模式。用一个 Session 写代码,写完之后再用另一个 Session(或 Subagent)来进行 Review。因为 Reviewer 没有参与写代码的过程,不会带入“我觉得这样写是对的”的主观偏见,Review 的质量会更高。
用 subagent review 一下 @src/payment/PaymentProcessor.ja va这段代码,重点查看:事务边界是否正确、异常处理是否有漏洞、有没有可能出现重复扣款的场景。技巧九:Hooks 确保“每次自动执行”,CLAUDE.md 只能“大概会做”
常见的误区
把许多本应由 Hooks 实现的自动化规则写进了 CLAUDE.md,比如“每次改完代码要运行一遍 lint”。
为什么这样不可靠
CLAUDE.md 里的规则对于 Claude 来说只是“建议”,它会尽量遵守,但不能 100% 保证。特别是在上下文接近满的时候,这些规则很可能被“遗忘”。
而 Hooks 是程序级别的触发——在特定事件发生时,无论 Claude 是否愿意,脚本都会执行,结果都会反馈给 Claude。
正确的做法
将“每次必须做”的事情写成 Hooks,将“应该怎么做”的风格规范写进 CLAUDE.md。
几个真正能派上用场的 Hook 场景:
{"hooks": {"PreToolUse": [{"matcher": "Bash","hooks": [{"type": "command","command": "~/.claude/hooks/check-dangerous-commands.sh","timeout": 5}]}]"PostToolUse": [{"matcher": "Edit|Write","hooks": [{"type": "command","command": "mvn checkstyle:check -q 2>&1 | head -20","timeout": 30}]}]"Stop": [{"hooks": [{"type": "command","command": "~/.claude/hooks/session-summary.sh"}]}]}}第一个 Hook 在每次执行 Bash 命令前检查是否有危险操作(如 rm -rf、DROP TABLE);第二个在每次文件修改后自动运行代码风格检查;第三个在 Session 结束时自动记录工作内容,便于下次继续。
这些都是“每次必须做”的事情,用 Hook 来实现远比写进 CLAUDE.md 可靠得多。
图:CLAUDE.md 是“告知型”软性约束,Hooks 是“程序化”确定性执行——二者定位不同,不要混用
技巧十:大型代码库的权限配置不是障碍,而是安全保障
常见的误区
嫌权限确认麻烦,直接开启 --dangerously-skip-permissions 或者把所有权限都设置为 allow。
为什么这是个坑
在小项目里,Claude 改错了大不了撤销。但在 8 万行代码的生产服务中,如果 Claude 在没有权限控制的情况下随意执行命令——比如不小心运行了数据库 migration、删除了正在被其他进程使用的临时文件——代价可能非常高昂。
曾遇到过 Claude 误删本地缓存目录的情况,虽说可以恢复,但恢复过程花了一个小时,而原任务本身才 20 分钟。
正确的做法
配置精细化权限,不搞全开或全关,只放行你真正信任的操作:
// .claude/settings.json{"permissions": {"allow": ["Bash(mvn test *)","Bash(mvn clean compile)","Bash(git status)","Bash(git diff *)","Bash(git add *)","Bash(git commit *)","Read(**)","Edit(src/**)","Write(src/**)"]"deny": ["Bash(rm *)","Bash(DROP *)","Read(.env*)","Read(**/secrets/**)","Write(**/migrations/**)"]}}允许读取所有文件、修改 src 目录下的代码、运行 Maven 命令和 Git 操作;禁止删除文件、禁止读取密钥文件、禁止直接写 migration 脚本。
配置过后,绝大多数正常的开发操作 Claude 可以直接执行,无需反复确认,但真正危险的操作会被准确拦截。权限配置的目标不是让你彻底省掉确认,而是把需要确认的操作缩减到真正值得你留意的那几项。
常见问题
Q: Claude Code 上下文空间用尽后,用 /compact 好还是 /clear 好?
A: 取决于当前任务的完成状态。如果仍然处于同一个任务中,并且正在进行的修改有一定的保留价值,可以使用 /compact——它会对信息进行总结,保留关键上下文。如果当前任务已经结束,准备开始新的无关任务,直接使用 /clear——完全清理的效果优于有损压缩。一个判断标准:如果你能用一两句话清晰说明当前任务的状态,那就 /clear 重开,把那段描述作为新 Session 的开场白,效果会更佳。
Q: CLAUDE.md 里的规则,Claude 不遵守怎么办?
A: 首先检查文件是否过长(通过 /memory 查看当前 Session 加载了哪些文件)。如果 CLAUDE.md 超过 200 行,精简是第一步。其次,确认规则是否足够具体——“写高质量代码”远不如“使用 2 空格缩进,类名使用 PascalCase”来得有效。如果某条规则是“必须执行”的(例如每次提交前要运行测试),建议改用 Hook 来保障执行,而不是依赖 Claude 的自觉性。
Q: 在大型代码库中,如何让 Claude 快速定位相关代码?
A: 有两种方法:第一,使用 @ 直接引用已知的相关文件,省去 Claude 的搜索过程;第二,让 Claude 使用 git log --all -S "关键词" 这类 git 命令进行搜索,这比 grep 全文更精准,还能查看变更历史。如果代码库有特殊的模块划分或目录结构,在 CLAUDE.md 中加以说明:“认证相关代码在 auth-service/src/main”,这样 Claude 搜索时就能有明确的范围。
Q: 多个 Worktree 并行工作后,如何合并结果?
A: 每个 Worktree 都是独立的 git 分支。并行工作完成后,按照标准的 git merge 或 git rebase 流程操作即可。一个更优的做法是:在合并前使用另一个 Session(全新的上下文)来 Review 所有变更,由于 Reviewer 没有参与编写过程,Review 的质量通常会更高。
Q: Auto Memory 记录的内容会影响哪些 Session?
A: Auto Memory 是基于仓库级别的,该仓库的所有 Worktrees 共享同一份 memory。也就是说,在 main 分支上教给 Claude 的内容,在 feature 分支的 Session 里同样会生效。这通常是好事,但偶尔会出现“在某个场景下学到的临时习惯,影响到另一个完全不同场景”的问题——因此,定期使用 /memory 进行检查和清理是非常必要的习惯。
