先说个事儿
你大概经历过这种场面:
code review 的时候
你:"这个不能用 unwrap(),会 panic 的。"
新人:"为啥?文档里没写啊。"
你:"这...这是常识吧?"
新人(一脸无辜):"什么常识?"
修 bug 的时候
同事:"又挂了,Windows 路径的问题。"
你:"第几次了?代码里硬编码了 `/`,Windows 用的是 `\`。"
同事:"那咋办?"
你:"下次注意点..."
同事:"注意啥?"
开技术会的时候
你:"不能加这个 ProjectManager 类,我们的领域模型就四个名词。"
别人:"为啥?"
你:"因为...因为架构要简单。"
别人:"简单和加个类有啥关系?"
你看,问题出来了:
资深工程师脑子里的"为什么"、“怎么做”、“啥时候要小心”,从来没被写下来。
代码里只有"做了什么"。 README 里只有"怎么装"。 注释里零散且不成体系。
结果就是:
- 新人只能试错学习,一遍遍踩同一个坑
- AI 没上下文,按它自己的理解执行,结果常常不对
- 资深工程师一走,这些经验也就跟着走了
这事儿能解决
问题的根源是:经验没显性化。
解决方案是:建一个 .claude/ 目录,把这些"为什么"和"怎么做"写下来。
写下来之后:
- AI 读了,会按规则执行
- 新人读了,知道该怎么干
- 经验不会因为人走而丢
.claude/ 长什么样
一个标准的目录结构:
.claude/
├─ commands/ # 命令规格:"如何执行具体任务"
│ ├─ bump.md # 版本号 bump 流程
│ ├─ release.md # 发布流程
│ └─ component.md # 组件脚手架流程
├─ rules/ # 开发规则:"什么不能做"
│ ├─ architecture.md # 架构约束
│ ├─ security.md # 安全要求
│ └─ paths.md # 路径处理规则
├─ skills/ # 技能模式:"可复用的能力"
└─ settings.json # AI 行为配置
三个文件夹的区别,说人话就是:
| 文件夹 | 啥意思 | 举个栗子 |
|---|---|---|
commands/ |
“怎么做某件事” | 怎么 bump 版本号 |
rules/ |
“啥不能干” | 别用 unwrap() |
skills/ |
“这类事咋做” | 怎么创建组件 |
举个文件的样子
咱们看一个 commands/bump.md 的完整例子:
---
description: Bump version across 5 files
globs: "{Cargo.toml,package.json,...}"
---
## 为什么要有这个命令
五个文件里的版本号必须一模一样。如果不匹配,会出这些问题:
- About 对话框显示的版本号不对
- 安装包没法覆盖旧版本
- README 上写的版本号和实际对不上
## 输入
$ARGUMENTS 必须是:
- `patch` — 最后一位 +1(0.0.2 → 0.0.3)
- `minor` — 中间一位 +1,最后归零(0.0.2 → 0.1.0)
- `major` — 第一位 +1,后面归零(0.1.0 → 1.0.0)
## 流程
### 第一步:读当前版本
从 `Cargo.toml` 里读 `[workspace.package]` 下面的 `version = "..."`。
**如果五个文件版本不一致,停下来报告**。不要继续。
### 第二步:算出新版本
根据 $ARGUMENTS 计算下一个版本号。
### 第三步:写入五个文件
按顺序改这五个文件:
1. `Cargo.toml` 的 `[workspace.package] version`
2. `package.json` 的 `"version"`
3. `src-tauri/tauri.conf.json` 的 `"version"`
4. `README.md` 里的版本号声明
5. `web/src/app/(reader)/app/install/page.mdx` 的版本号声明
**不要提交** — 留着让人 review。
## 验证步骤
写完后,五个文件的版本号必须一模一样。用这个命令检查:
```bash
grep -h "version" Cargo.toml package.json src-tauri/tauri.conf.json
---
## 你看这个文件每个部分的作用
| 部分 | 啥用 | 没它会咋样 |
|------|------|-----------|
| `description` | 让 AI 能找到这个文件 | AI 找不着,不知道有这命令 |
| `globs` | 告诉 AI 这命令会动哪些文件 | AI 可能漏掉某些文件 |
| "为什么要有这个命令" | 让人理解背后的风险 | 人不理解重要性,可能敷衍 |
| "输入" | 明确命令的接口 | 不知道该传入什么参数 |
| "流程" | 一步步怎么做 | 不知道从哪开始,到哪结束 |
| "STOP if" | 啥时候该停下来 | 可能在错误的状态下继续 |
| "验证步骤" | 怎么确认做对了 | 不知道自己有没有做错 |
**重点说说"为什么要有这个命令"这部分**:
如果不写"为什么":
“修改五个文件的版本号” → 执行者不知道为啥要做这件事 → 可能觉得不重要的就省了 → 出问题了
如果写了"为什么":
“修改五个文件的版本号,因为不匹配会导致:
- About 对话框显示错误版本
- 安装包无法覆盖旧版本
- README 撒谎” → 执行者理解了风险 → 认真执行,不偷懒
---
## 怎么把经验"挖"出来
咱们说具体点,四个步骤:
### 第一步:捕获
啥时候该记下来?
**场景 1:code review 的时候**
你:“这个不能用 unwrap(),会 panic。”
新人:“为啥?”
你:“因为如果返回 Err,程序就崩溃了。”
→ 这时候就该记下来:在 rules/ 里写"不要用 unwrap()"
**为啥这时候要记?**
因为这是知识最"鲜活"的时候:
- 你刚发现问题
- 有具体的上下文
- 有真实的代码案例
- 你知道"为什么"和"怎么做"
不记下来,下次新人还会犯同样的错,你还得再说一遍。而且时间久了,你可能都忘了自己说过这话。
---
**场景 2:修 bug 的时候**
同事:“又是因为 Windows 路径的问题。”
你:“为啥老出这问题?”
同事:“代码里硬编码了 /,Windows 用的是 \。”
→ 这时候就该记下来:在 rules/paths.md 里写"不要硬编码路径分隔符"
**为啥这时候要记?**
因为你找到了"问题的根源":
- 知道啥会导致 bug
- 知道咋预防
- 可以写出具体的规则
不记下来,下次还可能出同样的 bug。
---
**场景 3:技术讨论的时候**
别人:“咱们加个 ProjectManager 类吧。”
你:“不行,我们领域模型就四个名词。”
别人:“为啥?”
你:“因为架构要保持简单,不要随便加名词。”
→ 这时候就该记下来:在 rules/architecture.md 里写"不要添加新的领域名词"
**为啥这时候要记?**
因为你做了一个重要的决策:
- 你知道"为什么"做这个决策
- 你有判断依据
- 后来的人需要知道这个依据
不记下来,后来的人可能不理解,想要"改进"实际上破坏了架构。
---
### 第二步:分类
捕获到的东西,该放哪儿?
| 啥东西 | 放哪儿 | 咋判断 |
|--------|--------|-------|
| 命令流程(怎么做某件具体的事) | `commands/` | 是一次性任务,有明确的开始和结束 |
| 架构约束(啥不能做) | `rules/` | 是持续要遵守的规则 |
| 可复用能力(这类事咋做) | `skills/` | 是可以应用到很多地方的模式 |
**举个反例**:
如果把"不要用 unwrap()"写成 `commands/` 的文件:
- 每次都要手动"执行"这个命令
- AI 不会自动遵守
- 没啥意义
应该写成 `rules/` 的文件:
- AI 写代码时会自动遵守
- 不需要手动触发
- 才是正确的用法
---
### 第三步:结构化
怎么写?
**不要一大段话**:
在Windows上路径分隔符是\不是/,所以代码里不要直接写/, 要用Path::join或者MAIN_SEPARATOR常量,还有要注意 有盘符的路径C:\Users...,还有UNC路径\server\share...
这一大段看着就累,找不着重点。
**要分点写**:
永远不要
- ❌ 硬编码
/做分隔符 - ❌ 假设路径以
/开头
应该
- ✅ 用
std::path::MAIN_SEPARATOR - ✅ 用
Path::join
Windows 的四种路径形状
/Users/…— Unix 绝对路径C:\Users\…— Windows 盘符路径\\server\share\…— Windows UNC 路径\\?\C:\…— Windows verbatim 路径
一眼就能看出重点,也容易记住。
---
### 第四步:验证
写完了不是就完了,得验证。
**让 AI 按你的规则跑一遍**,看它对不对:
你:“按 rules/null-safety.md 检查一下这段代码有没有安全问题”
AI 执行完后…
你:“有没有遗漏?”
AI:“第 15 行有个 unwrap() 在生产代码里,不符合规则。”
你:“对,这正是要抓的。”
如果 AI 没抓到问题,说明你的规则写得不够清楚,得改。
**让人按你的说明做一遍**,看他能不能顺利完成:
你:“按 commands/bump.md 的流程 bump 一下版本号”
新人执行完后…
你:“哪儿不清楚?”
新人:“第二步说’算出新版本’,没说咋算。”
你:“好,我补上。”
如果新人卡住了,说明说明写得不够清楚,得改。
---
## 从零开始的四个月计划
咱们按时间来,一步一步来。
---
### 第 1 周:搭架子
要做三件事:
**1. 创建目录结构**
```bash
mkdir -p .claude/commands
mkdir -p .claude/rules
touch .claude/settings.json
2. 写第一个规则文件
选一个"最常犯的错误"作为第一个规则。
为啥从"最常犯的错误"开始?
- 因为这是最大的痛点
- 解决了立竿见影
- 你能看到效果
- 有动力继续往下写
比如你的项目老是因为 null pointer 崩溃,第一个规则就写"不要用 unwrap()"。
3. 配置 settings.json
{
"contextProviders": {
"claude": {
".claude/**": "read"
}
}
}
这步是告诉 AI:去 .claude/ 目录里找规则。
第 1 周结束: 你有了一个骨架,有一个规则,AI 知道去哪儿找规则了。
第 2-4 周:边干边记
这三周,养成一个习惯:每次遇到问题,都问自己一句"能不能写成规则"。
三个时刻要问:
- code review 的时候:“这个判断能不能写成规则?”
- 修 bug 的时候:“怎么避免这种 bug 再次发生?”
- 技术讨论的时候:“这个决策的依据是什么?”
为啥要"每次都问"?
因为不主动问,忙起来就忘了:
- “这次先算了,下次再写”
- 结果"下次"从来没来
- 知识继续丢失
每次都问,才能形成习惯:
- 第 1 周:得提醒自己
- 第 2 周:开始有点感觉
- 第 3-4 周:不自觉地就开始想"这能不能写成规则"
第 2-4 周结束: 你积累了 5-10 个规则,覆盖了项目的主要问题。
第 2-3 月:整理成体系
这时候你有了一堆规则,但可能乱七八糟。
需要按领域整理一下:
rules/
├─ architecture/
│ ├─ domain-model.md
│ └─ boundaries.md
├─ security/
│ ├─ secret-handling.md
│ └─ input-validation.md
├─ performance/
│ └─ n-plus-one-queries.md
└─ style/
├─ naming.md
└─ formatting.md
为啥要整理?
不整理:
- 找不到相关规则
- 不知道该用哪个
- 想要改进时不知道从哪下手
整理之后:
- 相关的规则在一起
- 容易找到
- 容易维护
第 2-3 月结束: 你有一个结构化的规则库,覆盖了各个领域。
第 3 月+:自我进化
规则不是写完就完了,得持续维护。
三件事:
-
加验证机制
- git pre-commit hook 检查代码
- CI 运行 lint 规则
- 自动化检查
-
建立反馈循环
- 收集违反规则的案例
- 更新规则
- 补充遗漏的地方
-
持续迭代
- 定期审查规则
- 删除过时的
- 合并重复的
为啥要验证?
因为写了规则不等于被遵守:
- 可能 AI 不知道规则
- 可能人不知道规则
- 可能规则已经过时了
验证才能确保规则真的在起作用。
常见的坑
给你说说新手最容易犯的三个错误。
错误 1:写得太抽象
❌ 不好:
保持代码简洁和可读。
问题:
- 啥叫"简洁"?
- 啥叫"可读"?
- 没人说得清楚
✅ 好:
# 函数长度规则
函数不应超过 50 行。
原因:
- 超过 50 行难以理解
- 通常意味着做了太多事
- 应该拆分
例外:
- 测试代码可以超过 50 行
- 数据生成代码可以超过 50 行
错误 2:写得太死板
❌ 不好:
永远不要写超过 20 行的函数。
问题:
- 太死了
- 有时候确实需要长函数
- 强行拆分反而更乱
✅ 好:
# 函数长度建议
函数通常不应超过 50 行。
如果超过:
- 考虑能不能拆分
- 如果拆分更复杂,保持现状
- 用注释说明为啥这么长
错误 3:写完就不管了
❌ 不好:
- 写了规则
- 从不检查
- 从不更新
结果:
- 规则可能过时
- 规则可能不再适用
- 规则变成摆设
✅ 好:
- 写了规则
- 定期检查
- 根据反馈更新
- 删除过时的
一个完整的例子
咱们从头到尾走一遍。
第 1 步:发现问题
项目又因为 null pointer 崩溃了
第 2 步:捕获
为啥会崩溃?
因为代码用了 unwrap(),没检查 None/Err
第 3 步:分类
这是啥?是架构约束
放哪儿?放 rules/
第 4 步:结构化
写成 rules/null-safety.md:
---
description: Null safety rules for Rust code
globs: "**/*.rs"
---
## 永远不要
❌ **永远不要使用 `.unwrap()` 在生产代码中**
**原因**:unwrap 会在值是 None/Err 时 panic,导致整个程序崩溃。
## 应该
✅ **使用 `?` 操作符或显式处理**
```rust
// 好:传播错误
let user = get_user(id)?;
// 好:显式处理
let user = match get_user(id) {
Ok(u) => u,
Err(e) => return Err(e),
};
// 好:提供默认值
let user = get_user(id).unwrap_or_default();
唯一例外
只在测试代码中使用 unwrap,且必须在注释中说明为什么不会 panic:
#[test]
fn test_something() {
let user = get_user(1).unwrap();
// 安全:测试数据库中 id=1 总是存在
}
验证
在 code review 时:
- 搜索所有
.unwrap()调用 - 确认每个要么在测试中
- 要么有注释说明为啥不会 panic
**第 5 步:验证**
你:“AI,按 null-safety.md 检查一下这段代码”
AI:“第 23 行有个 unwrap(),不符合规则”
你:“对,这地方得改”
**结束**。一个完整的规则就诞生了。
---
## 你现在可以开始了
别想太多,从最简单的开始:
1. **创建 `.claude/` 目录**
2. **写下"项目里最常犯的错误"**
3. **按模板整理**
4. **让 AI 按规则检查一遍代码**
四个月之后,你会有一套自己的知识系统。
---
## 最后总结一下
`.claude/` 知识系统的核心思想一句话:
**把专家的直觉变成明确的规则,让 AI(和新人)能够像专家一样思考和行动。**
四个步骤:
- **捕获**:从专家脑子中提取知识
- **分类**:让知识易于查找
- **结构化**:让知识易于理解
- **验证**:确保知识正确
缺了哪一步都不行。
**你现在可以从写下"项目中总是犯的错误"开始。**
一件一件来,别着急。