当结构足够好,你不需要 CLAUDE.md
一次内部复盘引发的思考:为什么 OpenSpec 驱动的工程不需要那 70 行行为约束?
起因
最近网上流行一份叫 CLAUDE.md 的文件,大约 65-70 行,专门用来"驯服"AI 编程助手。内容大致是这类规则:
# CLAUDE.md — Behavioral guidelines to reduce common AI coding mistakes
- Do NOT delete or rewrite existing code unless explicitly asked
- Do NOT add dependencies without asking
- Do NOT modify files outside the scope of the current task
- Always run tests after making changes
- Keep changes minimal — do one thing per commit
- Preserve existing code style and patterns
- Do NOT refactor code that isn't related to the task
- When unsure, ask — don't guess
- Always check if a function/class already exists before creating a new one
- Do NOT change public API signatures without discussion
...
这些规则解决的是一个真实痛点:AI 在大型工程中容易"乱改一通"——删掉不该删的代码、引入不需要的依赖、偏离既有风格、一次改太多文件导致难以 review。
我在 H项目 中用 AI 协作开发了几个月,突然意识到:我的工程从来没出过这些问题。 是 OpenSpec 帮我解决了吗?我专门去查了一下——OpenSpec 里并没有任何类似 CLAUDE.md 的"行为约束"条款。
那问题到底是怎么被解决的?
CLAUDE.md 在解决什么
把那 65 行规则归类,本质上在防三件事:
| 问题类别 | 典型症状 | CLAUDE.md 的应对 |
|---|---|---|
| 作用域失控 | AI 改了不该改的文件,重构了不相关的代码 | "Do NOT modify files outside scope" |
| 风格漂移 | 新代码和老代码风格不一致,命名混乱 | "Preserve existing code style" |
| 破坏性变更 | 删除已有功能、改变公共接口、引入未经审批的依赖 | "Do NOT delete code unless asked" |
这三类问题的共同根源是:AI 缺乏对工程边界的认知。 它不知道哪些能动、哪些不能动、动了之后影响范围有多大。
CLAUDE.md 的策略是用自然语言告诉 AI "别乱来"。有效,但本质上是补丁式的行为约束——你得预判 AI 可能犯的每一种错,然后逐条写规则去堵。
OpenSpec 为什么不需要这些规则
H项目 使用的 OpenSpec 是一套 spec-driven 的工程管理体系。它的目录结构如下:
openspec/
├── specs/ # 主规格文档(真理源)
│ ├── arch/ # 架构约束(按端点)
│ │ ├── global/spec.md # 全局:错误处理、日志、命名、安全、幂等
│ │ ├── iosapp/spec.md # iOS:Swift 5.9+, Clean Arch, singleton
│ │ ├── watchapp/spec.md # Watch:资源限制, ExtendedRuntime
│ │ ├── vault/spec.md # Vault:Python, filesystem-first
│ │ ├── router/spec.md # Router:FastAPI, DDD
│ │ └── worker/spec.md # Worker:处理流水线
│ ├── local/ # 单端功能规格(按端点/层级/能力)
│ │ ├── iosapp/
│ │ │ ├── ui_conversation/spec.md
│ │ │ ├── app_task-queue/spec.md
│ │ │ └── inf_local-first/spec.md
│ │ ├── vault/
│ │ │ ├── inf_storage/spec.md
│ │ │ └── app_ingest/spec.md
│ │ └── ...
│ └── domain/ # 跨端协作规格
│ ├── transcription/
│ ├── auth/
│ └── sync/
├── changes/ # 增量变更(每个 change 是一个完整的迭代单元)
│ ├── mcp-data-catalog/
│ │ ├── proposal.md # 为什么做
│ │ ├── design.md # 怎么做(含决策记录)
│ │ ├── tasks.md # 拆解的任务清单
│ │ └── specs/ # Delta Spec(变更的规格增量)
│ ├── recall-full-coverage/
│ ├── graph-recall-pipeline/
│ └── ... (45+ changes)
└── config.yaml # 目录结构规则定义
关键在于,这个结构天然解决了 CLAUDE.md 试图用规则堵住的三个问题:
1. 作用域失控 → Spec 天然划定边界
每个 change 目录就是一次迭代的完整边界。以 mcp-data-catalog 为例:
proposal.md明确写了 Goals 和 Non-Goals("不做维度分类、不引入新存储层、不做分页")design.md列出了 Impact 范围("MCP tools.py 新增 get_catalog,修改 get_conversation_content")specs/里的 Delta Spec 精确到哪个端点的哪个能力被修改
AI 读到这些信息后,它知道这次改动只涉及 vault 的 MCP 工具层,不会去碰 iOS 端的 UI 代码或 Worker 的转写流水线。不是因为有人告诉它"别改别的文件",而是它根本没有理由去改别的文件——spec 没有要求。
2. 风格漂移 → Arch Spec 定义了每个端点的技术契约
spec.arch.vault 明确规定了:
- Python 3.11+, FastAPI, Pydantic 2.x
- async def 优先,MUST NOT 使用同步函数
- 文件 I/O 通过 StorageBackend 接口,MUST NOT 直接使用 pathlib
- 子域依赖方向:
ingest → core_data → shared,MUST NOT 反向依赖 - 会话目录格式:
YYYYMMDD_HHmmss(无 conv_ 前缀)
spec.arch.iosapp 明确规定了:
- Swift 5.9+, SwiftUI, async/await
- Service 用
static let shared单例 - ViewModel 放
Presentation/ViewModels/,Service 放Data/Services/
spec.arch.global 统一了跨端约定:
- API 端点 kebab-case,JSON 字段 snake_case
- 日志格式 ISO 8601:
<timestamp> [<level>] <service>: <message> - 敏感数据脱敏,凭证不硬编码
这些不是"建议",是用 MUST/SHALL/MUST NOT 写的强约束,每条都带 Scenario 验证场景。AI 写出来的代码如果不符合这些约束,在 review 阶段就会被 spec 打回。
CLAUDE.md 说"preserve existing code style"是一句模糊的祈使句。Arch Spec 说的是"你的 ViewModel MUST 使用 ObservableObject + @Published,MUST 放在 Presentation/ViewModels/ 目录"——这是可验证的规格。
3. 破坏性变更 → 分层 Spec + Delta Spec 机制
OpenSpec 的三层 spec 结构(Arch / Local / Domain)形成了一个天然的变更影响分析框架:
- Arch Spec 是不轻易动的架构约束。改 Arch 意味着全端影响,门槛极高。
- Local Spec 是单端内的功能规格。改一个 Local Spec 只影响一个端点的一个能力。
- Domain Spec 是跨端协作协议。改 Domain Spec 需要协调多个端点。
每次迭代通过 change 目录提交 Delta Spec——不是直接改主 spec,而是先写增量,review 通过后再 sync 到主 spec。这个机制天然防止了"AI 偷偷改了公共接口"的问题,因为:
- 如果 AI 要改一个接口,它必须在 Delta Spec 里声明这个变更
- Delta Spec 的路径规则严格执行(config.yaml 定义了合法路径模式,禁止了离散目录)
- 变更的 Impact 在 proposal.md 里必须列出
CLAUDE.md 说"don't change public API signatures"。OpenSpec 的做法是:你要改可以,但你得先写 proposal 说明为什么改、影响哪些端点、有没有 breaking change,然后在 Delta Spec 里精确描述变更内容。
本质区别:规则约束 vs 结构约束
| 维度 | CLAUDE.md(规则约束) | OpenSpec(结构约束) |
|---|---|---|
| 约束方式 | 自然语言禁令 | 目录结构 + 规格文档 |
| 作用域控制 | "别改别的文件" | change 目录天然划定边界 |
| 风格一致性 | "保持现有风格" | Arch Spec 用 MUST/SHALL 定义技术契约 |
| 变更安全 | "别删代码、别改接口" | Delta Spec 机制要求声明变更 |
| 可验证性 | 不可验证(靠 AI 自觉) | 每条约束带 Scenario,可机器检查 |
| 可扩展性 | 逐条添加规则,容易遗漏 | 新端点/新能力自动继承 Arch 约束 |
| 适用阶段 | 项目任何阶段都能加 | 需要前期投入建立 spec 体系 |
CLAUDE.md 是防御性的——它假设 AI 会犯错,然后用规则去拦截。
OpenSpec 是建设性的——它通过结构让 AI 天然知道该做什么、怎么做、做到什么程度。
打个比方:CLAUDE.md 像是在高速公路上竖警告牌("限速 120"、"禁止变道"),OpenSpec 像是把路本身修好了——有护栏、有车道线、有匝道,你自然就按规矩开了。
那 CLAUDE.md 没用吗?
不是。CLAUDE.md 在新工程的冷启动阶段非常有价值。
Jun在会议里说得很清楚:
"如果一个新工程它的那几行 cloud 点 md 是可以帮助它避免这些问题的,所以这中间就是说在做这个新工程一定是要么就结构好了,要么就是你准备很好的很多一线的这种实践的一些工程的 prompt。"
一个新工程在 spec 体系建立之前,AI 确实需要一些基本的行为约束来避免低级错误。CLAUDE.md 的 65 行规则就是这个阶段的"脚手架"。
但随着工程结构逐渐成熟——Arch Spec 定义了技术栈和代码组织,Local Spec 覆盖了各个功能模块,change 机制规范了迭代流程——这些行为约束就自然被结构吸收了。你不再需要告诉 AI "别乱改",因为 spec 已经告诉它"该改什么"。
结论
| 工程阶段 | 推荐策略 |
|---|---|
| 冷启动期(0-1 个月) | CLAUDE.md 行为约束 + 基础 Arch Spec |
| 结构形成期(1-3 个月) | Arch Spec + Local Spec 逐步覆盖,CLAUDE.md 中的规则被 spec 吸收 |
| 稳定迭代期(3 个月+) | 完整的 OpenSpec 体系(Arch/Local/Domain + change 机制),CLAUDE.md 可以退役 |
与其花精力维护一份越来越长的行为禁令清单,不如把精力投入到工程结构本身。 当你的 spec 体系足够完善,AI 不是因为被告知"不能做什么"而表现良好,而是因为它清楚地知道"应该做什么"。
这可能是 AI 协作开发中一个被低估的认知:约束 AI 最好的方式不是写更多规则,而是建更好的结构。