Claude Code 十大坑:半年实战总结的避坑指南
前言: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.java(迁移脚本,已废弃但生产依赖)- .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/**/*.java"---# API 规范(只在修改 API 层文件时加载)- 所有接口返回 Result 包装类- 参数校验使用 javax.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.java - 添加 /login/phone 接口2. UserService.java - 添加 phoneLogin() 方法3. SmsService.java - 添加验证码校验逻辑4. UserMapper.java - 添加按手机号查询用户的 SQL5. LoginDTO.java - 添加 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 漫无目的地“找”快得多
常见的误区
“帮我修一下登录的 Bug”——于是 Claude 开始对整个项目进行 grep 搜索,读十几个文件,耗费大量上下文 tokens,才找到问题所在。
为什么会这样
如果不告诉 Claude 去哪里找,它就会做代码库级别的搜索——这非常消耗上下文。每一次 grep、每一次 Read 都在占用 token 配额。在 200K 的上下文里,读 5 个 2000 行的文件就花掉了约 5% 的额度。
正确的做法
用 @ 直接引用你知道相关的文件或目录:
@src/api/UserController.java @src/service/UserService.java登录接口在高并发下出现了 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.java这段代码,重点查看:事务边界是否正确、异常处理是否有漏洞、有没有可能出现重复扣款的场景。技巧九: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 检查和清理是很必要的习惯。