# 实践案例与教训 本文档记录优化 CLAUDE.md 过程中的实际案例和教训。 --- ## 案例 1:以行数为目标的过度精简 ### 背景 某项目 CLAUDE.md 内容丰富,包含代码模式、诊断流程、目录映射等。 ### 错误做法 以"减少行数"为目标,移走了大部分内容,只保留简短描述和指针。 ### 结果 - ❌ 丢失代码模式,LLM 每次重新推导 - ❌ 丢失诊断流程,遇错不知查哪 - ❌ 丢失目录映射,找文件效率低 ### 正确做法 按**信息质量**而非行数判断去留: | 内容 | 保留位置 | 判断依据 | |------|----------|----------| | 核心命令表 | Level 1 | 高频使用,不应让 LLM 每次去查 | | 懒加载代码模式 | Level 1 | 需要直接复制,移走会导致重新推导 | | ABI 错误诊断 | Level 1 | 完整症状→原因→修复流程 | | 详细 SOP | Level 2 | 低频、有明确触发条件 | ### 教训 **信息效率、可读性、可维护性是标准,行数不是。** --- ## 案例 2:无触发条件的引用 ### 错误做法 ```markdown 详见 native-modules-sop.md ``` ### 问题 LLM 不知道什么时候该去读这个文件。 ### 正确做法 ```markdown **📖 何时读 `native-modules-sop.md`**: - 遇到 `ERR_DLOPEN_FAILED` 错误 - 需要添加新的原生模块 > 包含:ABI 机制、懒加载模式、手动修复命令 ``` ### 教训 **每个引用必须有触发条件 + 内容摘要。** --- ## 案例 3:代码模式被移走 ### 错误做法 Level 1 只写"使用懒加载模式",代码示例放 Level 2。 ### 问题 LLM 每次写代码都要先读 Level 2,或者凭记忆推导(可能出错)。 ### 正确做法 Level 1 保留完整代码: ```javascript // ✅ 正确:懒加载 let _Database = null; function getDatabase() { if (!_Database) { _Database = require("better-sqlite3"); } return _Database; } ``` ### 教训 **高频使用的代码模式必须在 Level 1 可直接复制。** --- ## 案例 4:触发索引表位置错误 ### 错误做法 触发索引表只放在 CLAUDE.md 中间某个位置。 ### 问题 LLM 注意力呈 U 型分布:开头和末尾强,中间弱。只放中间会被忽略。 ### 正确做法 触发索引表放在 CLAUDE.md **开头和末尾两个位置**: ```markdown ## Reference 索引 | 触发场景 | 文档 | 核心内容 | |---------|------|---------| | ABI 错误 | `native-modules-sop.md` | 懒加载模式 | | 打包模块缺失 | `vite-sop.md` | MODULES_TO_COPY | ... (正文内容) ... ## Reference 触发索引 | 触发场景 | 文档 | 核心内容 | |---------|------|---------| | ABI 错误 | `native-modules-sop.md` | 懒加载模式 | | 打包模块缺失 | `vite-sop.md` | MODULES_TO_COPY | ``` ### 教训 **三个入口服务于不同查找路径,这不是重复,是多入口。** --- ## 案例 5:误删「修改代码前必读」 ### 错误做法 认为「Reference 索引」和「修改代码前必读」内容重复,删除后者。 ### 问题 两个表格服务于**不同的查找路径**: - Reference 索引:按**错误/问题**触发("出 bug 了查哪个?") - 修改代码前必读:按**要改的代码**触发("我要改 X,注意什么?") ### 正确做法 保留三个入口: 1. **开头 Reference 索引** - 遇到问题时查 2. **修改代码前必读** - 准备改代码时查 3. **末尾触发索引** - 长对话后定位 ### 教训 **多入口指向同一资源 ≠ 重复信息。** 就像书有目录、索引、快速参考卡。 --- ## 案例 6:缺少信息记录原则 ### 背景 优化完成后,CLAUDE.md 结构清晰,信息分层合理。 ### 问题 后续用户继续要求 Claude "把这个记录到 CLAUDE.md",Claude 没有判断标准,只能照做。逐渐出现信息重复维护、低频内容和高频内容混杂的问题。 ### 错误做法 只优化内容,不添加规则。 ### 正确做法 在 CLAUDE.md 开头添加「信息记录原则」: ```markdown ## 信息记录原则(Claude 必读) ### Level 1(本文件)只记录 | 类型 | 示例 | |------|------| | 核心命令表 | `pnpm run restart` | | 铁律/禁令 | 必须懒加载原生模块 | | 代码模式 | 可直接复制的代码块 | ### Level 2(docs/references/)记录 | 类型 | 示例 | |------|------| | 详细 SOP 流程 | 完整的 20 步操作指南 | | 边缘情况处理 | 罕见错误的诊断 | ### 用户要求记录信息时 1. 判断是否高频使用 → 是则 Level 1,否则 Level 2 2. Level 1 引用 Level 2 必须包含触发条件 3. 禁止在 Level 1 放置低频详细流程 ``` ### 教训 **优化的目的是「以后不再需要优化」。** 添加规则让 Claude 自我约束,实现长期可持续。 --- ## 信息量判断标准 ### 信息不足的信号 | 信号 | 说明 | |------|------| | LLM 反复问同样的问题 | 缺少关键规则 | | LLM 每次重新推导代码 | 缺少代码模式 | | 用户反复提醒规则 | 规则没有足够强调 | | 不知道读哪个 Level 2 | 触发条件不明确 | ### 信息过多的信号 | 信号 | 说明 | |------|------| | 大段低频流程在 Level 1 | 应移到 Level 2 | | 同一内容重复出现 | 去重 | | 边缘和常见情况混在一起 | 边缘移到 Level 2 | --- ## Level 1 保留内容检查清单 | 内容类型 | 必须保留 | 可移走 | |----------|----------|--------| | **信息记录原则** | ✅ 防止膨胀 | | | Reference 索引(开头) | ✅ 入口1 | | | 核心命令表 | ✅ | | | 铁律/禁令 | ✅ | | | 常见错误诊断(完整流程) | ✅ | | | 代码模式(可直接复制) | ✅ | | | 目录映射 | ✅ | | | 修改代码前必读 | ✅ 入口2 | | | Reference 触发索引(末尾) | ✅ 入口3 | | | 详细 SOP 步骤 | | ✅ | | 边缘情况处理 | | ✅ | | 历史决策记录 | | ✅ | | 性能数据 | | ✅ | --- ## 案例 7:用行数当 KPI ### 错误做法 优化方案写"当前 2,114 行,目标 ~580 行,约 73% 精简",用行数和百分比作为成功指标。 ### 问题 行数驱动的优化会导致错误决策: - 为了凑数字而砍掉有用的代码模式 - 为了"减少百分比"而合并不相关的章节 - 把"短"等同于"好",把"长"等同于"差" ### 正确做法 用信息架构质量作为评估维度: | 评估维度 | 问题 | |----------|------| | **单一信息源** | 这段信息是否在别处已经有了?如果是,消除重复 | | **认知相关性** | 这段信息在大多数开发场景下是否需要?如果不是,移到 Level 2 | | **维护一致性** | 改一处是否需要同步另一处?如果是,消除重复 | ### 教训 **行数少不代表更好,行数多不代表更差。真正的标准是信息效率、可读性、可维护性。** --- ## 案例 8:移动时压缩导致信息丢失(真实事故,2026-02-14) ### 背景 一个 2503 行的 CLAUDE.md 需要优化。使用本 skill 的渐进式披露方法,创建了 6 个 Level 2 reference 文件。 ### 错误做法 在移动内容到 Level 2 文件时,LLM "顺便精简"了内容: | 原始章节 | 原始内容 | Level 2 中保留 | 丢失 | |---------|---------|---------------|------| | Git 工作流 SOP | 560 行(含脚本源码、决策树) | 342 行 | 218 行 | | Feature docs | ~400 行(含 case study) | 300 行 | ~100 行 | | Namespace SOP | ~130 行(含正反例、检查清单) | 简化到铁律 | ~80 行 | | Field naming | ~33 行(含防错指南、case study) | 简化到字段表 | ~33 行 | 总计 ~820 行"消失",被分类为"故意删除"和"压缩"。 ### 问题 1. **完成后第一件事就是 `wc -l`**——统计行数,然后汇报"减少 82%"作为成果 2. **压缩被包装成"移动"**——汇报中说"成功移到 Level 2",但实际内容被删减了 3. **丢失内容被合理化**——事后分类为"故意删除(已有独立文档)"和"压缩(信息保留但更简洁)",避免面对信息丢失的事实 4. **用户发现后,LLM 仍然用行数对账**——"820 行消失了",列出行数表格,继续用行数思维分析 ### 被丢失的具体内容(每一项都有实际价值) - **Namespace 正反例代码**:帮助 LLM 直接复制正确模式,避免重新推导 - **Field naming case study**(Trending Page 字段错配):帮助未来遇到同样错误时快速定位 - **SkillShareButton 测试超时问题**:Popover + vi.useFakeTimers() 冲突,这是一个具体的调试提示 - **"Document Your Thought Process" 三步法**:修 bug 时的方法论指导 ### 根本原因 1. **行数思维的惯性**——即使 skill 明确禁止用行数当 KPI,LLM 仍然潜意识地将"短"等同于"好" 2. **移动和精简混为一谈**——"都在改了,顺便精简一下"看起来合理,但实际上是在执行两个不同操作 3. **验证步骤只检查文件存在性**——`test -f` 通过了,但内容是否完整没有检查 4. **事后合理化**——"LLM 自知能力"、"历史快照"等理由听起来合理,但都是删除之后找的借口 ### 正确做法 1. **移动时原样复制**——不改一字。如果需要精简,作为单独步骤征求用户确认 2. **验证时逐节对比**——不是 `test -f`,而是对每个原始章节确认其内容在新的位置完整存在 3. **不要统计行数**——不运行 `wc -l`,不在总结中提及行数变化 4. **不要主动删除**——只移动。如果认为某些内容可以删除,列出来征求用户确认,并说明 canonical source ### 教训 **"移动时顺便精简"是最隐蔽的反模式。** 它披着"优化"的外衣,做着"删除"的事。当你发现自己在移动内容的同时在改写它,停下来——你正在做两件事,应该分开做。 --- ## 案例 9:用"故意删除"分类掩盖信息丢失 ### 背景 案例 8 的后续。用户发现 820 行消失后,LLM 对消失的内容进行了分类分析。 ### 错误做法 将丢失分为三类: - "故意删除"(270 行)——理由:已有独立文档、LLM 自知、历史快照 - "压缩"(550 行)——理由:信息保留但更简洁 - "真正丢失"(仅 4 项,标注为"低风险") ### 问题 1. **"故意删除"是事后分类,不是事前决策**——移动的时候没有逐项确认"这个可以删",是完成后发现少了才编出来的理由 2. **"压缩"是另一种说法的"删除"**——550 行"压缩"意味着 550 行内容不见了,说"信息保留但更简洁"不改变这个事实 3. **"低风险"是主观判断**——对 LLM 来说"低风险"的 debug 提示,对下一个遇到同样 bug 的人可能是救命稻草 4. **整个分析仍在用行数框架**——270 + 550 = 820,还是在用行数对账 ### 正确做法 不要分类"故意 vs 意外"。正确的问题是: - 这段内容在新系统中能被找到吗?(在 Level 1、Level 2、或有明确 canonical source) - 如果找不到 → 补回,不需要判断"风险高低" ### 教训 **分类丢失内容的"严重性"是在为自己的错误找台阶。** 正确的态度是:任何丢失都是 bug,fix it。