.claude/rules/ 目录下的每个 .md 文件,本质上就是一段会被注入 System Prompt 的文本。它和 CLAUDE.md 没有本质区别——都是 Claude 在每轮 API 调用中“看到”的指令。唯一的结构化优势是,它可以按主题拆分成多个文件,还支持条件加载。
.claude/
└── rules/
├── typescript.md # TypeScript 编码规范
├── testing.md # 测试规范(有 paths 条件)
├── api-design.md # API 设计规范(有 paths 条件)
└── security.md # 安全规范(全局生效)
一、两种加载模式
这是 rules 最重要的设计细节,也是最多人搞错的地方。
1、模式一:全局加载(无 paths 字段)
这种 rule 在会话启动时就加载进上下文,行为和写在 CLAUDE.md 里完全一样。拆出来的唯一好处是文件组织更清晰。
# security.md —— 没有 YAML 头部,或有头部但不含 paths
## 安全规范
- 不在代码中硬编码密码或 API Key
- 所有用户输入必须做 sanitize
- SQL 查询使用参数化,不拼接字符串
2、模式二:条件加载(有 paths 字段)
---
paths:
- "src/**/*.test.ts"
- "tests/**/*.ts"
---# 测试规范
## 命名
- 单元测试: `*.test.ts`
- 集成测试: `*.integration.test.ts`## 结构
使用 Arrange-Act-Assert 模式
这种 rule 在会话启动时不加载。只有当 Claude 读取或编辑匹配 paths 模式的文件时,才会被注入上下文。这个设计很精妙——如果你的测试规范有 50 行,而你这次只是在改一个 CSS 样式,那这 50 行规范就不会浪费上下文空间。但有一个关键细节,一旦加载,就不会卸载。paths 控制的是何时加载,不是何时生效。如果你在会话中先编辑了一个测试文件,testing.md 被加载了,然后你去改 CSS,testing.md 仍然在上下文里。
会话开始
→ 加载全局 rules(无 paths 的)
→ testing.md 未加载 ✗用户:帮我改一下 src/utils/format.ts
→ Claude 读取 format.ts
→ paths 不匹配,testing.md 仍未加载 ✗用户:帮我给这个函数写个测试
→ Claude 创建 src/utils/format.test.ts
→ paths 匹配!testing.md 加载 ✓用户:再帮我改一下 CSS
→ Claude 读取 styles.css
→ testing.md 仍在上下文中 ✓(不会卸载)
什么时候该从 CLAUDE.md 拆到 rules?
判断标准很简单,我们可以根据长度决策。
CLAUDE.md 的总长度如何?
│
├── < 200 行 → 不用拆,CLAUDE.md 一把梭
│ 简单就是好,不要为了组织而组织
│
├── 200-500 行 → 考虑拆
│ │
│ └── 有没有"只和特定文件类型相关"的内容?
│ ├── 有 → 拆出来,加 paths
│ │ (如测试规范、前端规范、API 规范)
│ └── 没有 → 拆出来,不加 paths
│ (纯粹为了文件组织清晰)
│
└── > 500 行 → 必须拆
CLAUDE.md 太长会稀释重要信息的权重
把领域规范拆到 rules,CLAUDE.md 只留核心约定
实战:一个全栈项目的 rules 拆分
假设你有一个 React + Express + PostgreSQL 的全栈项目,CLAUDE.md 膨胀到了 600 行
拆分后的 CLAUDE.md(精简到 80 行以内)
# 项目概述
全栈 TypeScript 项目。前端 React 18 + Tailwind,后端 Express + Prisma + PostgreSQL。# 命令
- `pnpm dev` — 启动前后端开发服务器
- `pnpm test` — 运行全部测试
- `pnpm lint` — ESLint + Prettier 检查
- `pnpm db:migrate` — 执行数据库迁移# 核心约定
- 包管理器用 pnpm,不用 npm 或 yarn
- commit message 用 conventional commits 格式
- 所有 API 返回 { success: boolean, data?: T, error?: string }
- 环境变量通过 .env 管理,不硬编码# 详细规范
领域规范见 .claude/rules/ 目录,按文件类型自动加载。
拆分出的 rules 文件
.claude/rules/frontend.md:
---
paths:
- "src/components/**"
- "src/pages/**"
- "src/hooks/**"
---# 前端规范
## 组件
- 函数式组件,不用 class 组件
- Props 用 interface 定义,命名 XxxProps
- 组件文件和样式文件同名同目录## 状态管理
- 局部状态用 useState
- 跨组件状态用 Zustand
- 服务端状态用 TanStack Query## 样式
- Tailwind 优先,复杂样式用 CSS Modules
- 响应式断点:sm(640) md(768) lg(1024) xl(1280)
.claude/rules/backend.md:
---
paths:
- "server/**"
- "src/api/**"
- "prisma/**"
---# 后端规范
## 路由
- RESTful 风格,资源名用复数
- 路由文件放 server/routes/,一个资源一个文件## 数据库
- 所有查询通过 Prisma ORM,不写原生 SQL
- 迁移文件不手动编辑
- 关联查询用 include,不用多次查询## 错误处理
- 业务错误抛 AppError(code, message)
- 统一在 errorHandler 中间件中捕获
.claude/rules/security.md(注意,没有 paths,全局生效):
# 安全规范
- 用户输入在使用前必须 validate + sanitize
- SQL 参数化(Prisma 默认做到了)
- XSS 防护:不使用 dangerouslySetInnerHTML
- CORS 只允许白名单域名
- 敏感信息(API Key、数据库密码)只放 .env
- 认证 token 用 httpOnly cookie,不存 localStorage
拆完之后,CLAUDE.md 从 600 行变成 80 行,但规范一条都没少——只是按需加载了。当 Claude 在改前端组件时,它看到的是 CLAUDE.md + frontend.md + security.md。当它在写测试时,看到的是 CLAUDE.md + testing.md + security.md。精准、高效。