从文档债到文档驱动:AI时代软件开发的Spec-First工程实践
1. 项目概述从“文档债”到“文档驱动”的工程实践如果你和我一样经历过一个项目从零到一再到功能膨胀、团队扩张最后被“文档债”压得喘不过气那你一定能理解那种痛苦。代码库在迭代但文档要么是几年前的“历史遗迹”要么散落在各个角落的README-v2-final-revised.md里没人知道哪个是“真相”。更糟的是当你想引入 AI 助手比如 Claude Code、Cursor来提升开发效率时会发现它们和你一样迷茫——没有准确、实时、结构化的上下文AI 给出的代码建议往往南辕北辙修复错误代码比从头写更费劲。这正是doc-first-dev这个项目要解决的核心问题。它不是一个新奇的文档工具而是一套可安装的Agent Skills技能和配套的工作流模板目标是把“文档先行”从一个美好的口号变成团队和 AI Agent 都能下意识遵循的工程习惯。它的核心理念非常直接每个功能模块只有一份“活”的规格说明书Spec它代表“现在应该是什么样子”所有的设计决策和变更理由则按时间顺序记录在独立的决策日志里。这样一来无论是新人 onboarding、老手回顾还是 AI Agent 介入开发都能基于唯一、准确的“现状”文档开展工作极大降低认知偏差和沟通成本。这套技能包主要包含三个核心技能spec-first用于单个模块的全生命周期开发spec-multi用于协调跨多个服务或项目的变更whylog-record则在任务完成后自动或手动记录“我们为什么这么选”的决策过程。它们共同构成了一个闭环确保“做什么”Spec和“为什么做”决策日志既分离又关联。我花了些时间深入研究并将其应用到自己的几个项目中发现它确实能改变团队与文档、以及与 AI 协作的“肌肉记忆”。下面我就结合自己的实操经验为你彻底拆解这套方法论的设计精髓、落地步骤以及那些只有踩过坑才知道的注意事项。2. 核心设计哲学为什么传统的文档方法会失败在深入使用doc-first-dev的技能之前我们必须先理解它背后针对的“顽疾”。传统的文档管理无论是完全缺失还是过度管理通常都会陷入以下几个陷阱而doc-first-dev的每一条设计原则都是对这些陷阱的精准反击。2.1 模块目录化对抗文档的“膨胀死亡”常见陷阱很多项目喜欢把所有模块的说明都塞进一个巨大的SPEC.md或DESIGN.md里。初期看起来整洁但随着模块增加这个文件会迅速膨胀到几千行。任何微小的改动都可能引发巨大的合并冲突搜索一个特定功能变得异常困难。最终没人敢动这个文件它变成了一个过时且无法维护的“纪念碑”。doc-first-dev的解法强制采用模块化的文档结构。每个业务或技术模块在docs/plans/module-name/目录下拥有自己独立的 spec 文件例如user-auth-spec.md。一个顶层的PROJECT.md文件仅作为索引列出所有模块及其简要描述和路径。your-project/ └── docs/ ├── plans/ │ ├── PROJECT.md # 索引仅包含模块名、简介、链接 │ ├── user-auth/ │ │ └── user-auth-spec.md # 独立、完整的认证模块 spec │ ├── order-service/ │ │ └── order-spec.md │ └── payment/ │ └── payment-spec.md └── decisions/ └── log.md实操心得这种结构带来的最大好处是“关注点分离”和“独立演化”。前端团队可以放心更新frontend-components-spec.md而不用担心影响后端的 API 文档。当某个模块被废弃时直接删除整个docs/plans/old-module/目录即可干净利落不会留下僵尸文本污染其他内容。对于 AI Agent 来说当它处理一个与“用户认证”相关的需求时它能被精确地引导到user-auth-spec.md这个文件获取最相关、最干净的上下文而不是在一锅粥的大文档里费力搜寻。2.2 单一活文档终结版本混乱常见陷阱我们常看到api-spec-v1.md,api-spec-v2-draft.md,api-spec-v2-final.md,api-spec-latest.md并存。哪个是当前线上运行的哪个是正在开发的这种混乱使得无论是人还是 AI都需要花费额外的心智去判断“真相”错误率极高。doc-first-dev的解法一个模块一份 Spec永远代表当前线上或主分支应有的状态。任何变更无论是新增功能、修改接口还是修复 Bug都直接在这份唯一的文件上修改、提交。历史版本由 Git 来完美管理。你需要查看历史时用git log docs/plans/user-auth/user-auth-spec.md即可。理念剖析这强迫团队形成一种纪律——更新代码和更新文档是同一个原子操作。如果你修改了登录接口的响应格式那么必须在同一个 Pull Request 里更新user-auth-spec.md。这样Spec 文件就和代码库一样成为“源代码”的一部分。AI Agent 在分析时读取到的永远是最新的、权威的现状描述从根本上避免了基于过时信息进行开发的风险。注意“当前状态”指的是在特性分支上开发时Spec 描述的是该特性完成后的预期状态。因此在特性分支合并前其 Spec 可能与其他分支的 Spec 存在临时的不一致这需要通过 Code Review 来确保合并时的最终一致性。这比维护多份“未来”文档要简单得多。2.3 Spec与决策分治让文档各司其职常见陷阱在 Spec 文件里夹杂大量的设计讨论比如“我们曾考虑过 OAuth 2.0 但因为复杂度放弃了最终选择了 JWT”。这些内容对于理解“现状”是噪音但对于理解“决策背景”又至关重要。混在一起的结果是Spec 变得冗长难读而真正重要的决策逻辑却随着时间流逝被遗忘。doc-first-dev的解法严格区分“是什么”Spec和“为什么”Decision Log。docs/plans/存放描述现状的 Spec格式要求清晰、简洁、结构化。读写模式是随机访问根据模块名快速定位。docs/decisions/log.md存放决策日志按时间顺序追加记录每次重要选择的原因、考虑的备选方案以及最终拍板的理由。读写模式是顺序追加像日记一样只增不改。操作示例在user-auth-spec.md中你只写## 认证方式 - 主要方式基于 JWT 的 Bearer Token。 - Token 有效期访问令牌 15 分钟刷新令牌 7 天。而在docs/decisions/log.md中你会追加一条记录## 2024-05-27: 选择 JWT 而非 OAuth 2.0 作为主要认证方案 **上下文** 设计用户认证模块。 **备选方案** 1. OAuth 2.0行业标准第三方集成友好但客户端/服务器端实现较复杂对我们初期简单的内部系统而言过重。 2. Session-Cookie传统有状态不利于横向扩展。 3. JWT无状态易于扩展适合 RESTful API。但需妥善处理令牌注销问题。 **决策** 采用 JWT。理由我们初期是纯 API 服务无第三方登录需求且需要无状态扩展。令牌注销问题可通过短有效期访问令牌和将刷新令牌加入服务器端黑名单小型来解决复杂度可控。 **记录人** yourname经验之谈这个分治策略在项目复盘、新人培训和技术债务评估时价值连城。当有人问“为什么我们不用 OAuth”时直接指向决策日志即可。AI Agent 在执行whylog-record技能时会自动按照这个格式帮你归档决策让“记录为什么”变得毫不费力。2.4 确认门把人放在决策回路上而非事后救火常见陷阱AI Agent 能力强大有时在理解需求后会急于展示其能力直接开始生成代码。如果它对需求的理解存在细微偏差或者需求本身模糊那么生成的代码可能就是“精致的错误”后期修改成本反而更高。doc-first-dev的解法在spec-first和spec-multi的工作流中明确设置了确认门Confirmation Gate。具体来说在 Phase A分析阶段结束时AI Agent 会生成一份更新后的 Spec 草案并停止行动等待开发者你的明确确认。流程示意你输入/spec-first 为用户个人资料增加一个“头像裁剪”功能支持矩形和圆形裁剪并生成不同尺寸的缩略图。AI 进入 Phase A分析现有user-profile-spec.md理解需求起草新的 Spec 变更如新增avatar_processing字段描述裁剪选项和输出格式。AI 输出“Phase A 完成。我已更新docs/plans/user-profile/user-profile-spec.md草案。主要变更为……。请确认此 Spec 变更是否符合预期。确认后我将进入开发阶段Phase B。”你需要仔细阅读这份草案确认理解无误。你可以说“确认”或者说“等等我还需要支持自定义裁剪比例”AI 会基于你的反馈重新回到分析阶段。为什么这至关重要这个强制暂停的机制确保了人类开发者始终是需求的最终仲裁者和设计审查者。它把 AI 定位为“高效的执行者”而非“自主的决策者”。这避免了在错误方向上浪费大量时间也让你在代码编写前就对系统变更有了清晰的蓝图。在我自己的使用中这个“确认门”拦截了至少30%的潜在需求理解偏差其价值远超那一次额外的点击确认。3. 三大核心技能深度解析与实操指南理解了设计哲学我们来看这三个技能具体怎么用。它们不是三个独立的工具而是一个协同工作的体系。3.1spec-first单模块开发的自动驾驶仪这是最常用、最基础的技能。它处理的是单个模块如“用户认证”、“订单处理”、“支付回调”内的需求变更或功能新增。典型工作流拆解当你触发/spec-first 需求描述后它会遵循一个严谨的状态机流程Step 0文档匹配与初始化检查索引首先检查docs/plans/PROJECT.md是否存在。如果不存在会运行init.md逻辑引导你创建一个基本的模块索引文件。模块选择器AI 会分析你的需求然后扫描PROJECT.md尝试将需求匹配到已有的模块。它会给你一个列表让你选择例如“1. user-auth, 2. payment, 3. (Other/新建)”。选择“新建”如果你选择新建AI 会引导你在docs/plans/下创建新的模块目录和 Spec 文件。我强烈建议你直接使用项目assets/目录下的tech-spec-blank.md模板它已经预设好了结构概述、接口、数据模型、业务流程、非功能需求等能帮你保持文档规范。Phase A分析与 Spec 更新AI 会读取对应模块的现有 Spec结合你的新需求进行影响分析。然后它会生成一份Spec 变更草案。这个草案会明确标出新增、修改、删除的部分。关键动作确认门。草案生成后流程暂停等待你的确认。这是你纠正 AI 理解、补充细节的最后也是最佳时机。Phase B开发与实现一旦你确认了 SpecAI 就进入了“开发模式”。它会基于确认后的 Spec 来生成或修改代码。一个隐藏的依赖构建命令。在 Phase B.4构建验证阶段AI 需要运行项目的构建命令如npm run build,go build,mvn compile来验证代码是否可编译。这个命令从哪里来它来自于你项目根目录的CLAUDE.md文件中的一个特定片段。这是初期 setup 的一个关键点后面会详细说。Phase C验收与交付代码写完后AI 会尝试启动服务命令同样来自CLAUDE.md并进行一些基础的验收测试比如调用新接口。最后进入 Phase D进行一致性检查确保代码和 Spec 对齐并生成一个简单的交付简报。实操配置要点要让spec-first顺畅工作项目需要一点前置配置主要是CLAUDE.md文件。你需要将spec-first/assets/claude-md-snippet.md中的内容合并到你项目的CLAUDE.md中并填写关键信息。!-- 这是你项目 CLAUDE.md 中需要包含的部分根据 snippet 模板调整 -- ## 构建与开发 - **项目构建命令** npm run build # 用于 Phase B.4 构建验证 - **本地开发启动命令** npm run dev # 用于 Phase C.1 服务启动与验收 - **测试命令** npm test ## 认证与环境如果适用 - **API 密钥/令牌位置** 位于 ~/.config/myapp/token - **服务访问地址** http://localhost:3000 - **数据库连接字符串** postgresql://localhost:5432/mydb (仅示例敏感信息勿直接写死可用环境变量占位符)没有这个配置AI 在需要构建或运行项目时会“不知所措”导致流程中断。3.2spec-multi跨服务协同的交通指挥当你的需求涉及前端、后端、数据服务等多个独立代码库或模块时spec-first就力有不逮了。这时需要spec-multi登场。它像一个项目经理负责协调跨边界的工作。核心机制解析服务注册表SERVICE.md这是spec-multi的“地图”。它位于docs/plans/SERVICES.md在 monorepo 的根目录或 workspace 的docs/plans/下。里面记录了所有参与协同服务的元信息。# 服务注册表 | 服务名 | 代码路径 | 本地端口 | 描述 | 依赖服务 | |--------|----------|----------|------|----------| | frontend | ./frontend | 3000 | 用户界面 | backend | | backend | ./backend | 8080 | 核心API服务 |># 在你的项目根目录下执行 npx skills add zzusp/doc-first-dev这条命令会启动一个交互式 CLI。它会检测你系统里安装了哪些 Agent如 Claude Code然后询问你是否要为每个 Agent 安装这些技能。通常直接回车确认即可。手动安装备选如果你用的工具不在 CLI 自动检测列表或者你想全局安装可以手动复制文件。以 Claude Code 为例macOS/Linux# 克隆仓库 git clone https://github.com/zzusp/doc-first-dev.git # 复制技能到 Claude Code 的全局技能目录 cp -r doc-first-dev/skills/* ~/.claude/skills/安装成功后在你的 AI 开发工具中输入/应该能看到spec-first等命令提示。4.2 项目初始化与目录结构搭建假设我的新项目叫user-platform-backend。创建基础目录mkdir -p docs/plans docs/decisions即使目录为空也要先创建好。这是技能的约定。初始化CLAUDE.md文件在项目根目录创建CLAUDE.md并将spec-first技能包里的assets/claude-md-snippet.md内容复制进去然后根据你的项目实际情况修改。# user-platform-backend - 开发上下文 ## 项目概述 这是一个用户管理平台的后端 API 服务基于 Node.js Express PostgreSQL 构建。 ## 构建与开发 - **安装依赖** npm install - **项目构建命令** npm run build (对应 package.json 中的 build 脚本) - **本地开发启动命令** npm run dev (对应 package.json 中的 dev 脚本使用 nodemon) - **测试命令** npm test ## 服务与认证 - **服务访问地址** http://localhost:3000 - **数据库** PostgreSQL连接字符串通过环境变量 DATABASE_URL 配置。 - **认证** 使用 JWT密钥通过环境变量 JWT_SECRET 配置。 ## 代码风格与约定 - 使用 ES6 语法。 - 路由定义在 src/routes/ 目录。 - 控制器逻辑在 src/controllers/ 目录。 - 数据模型在 src/models/ 目录。关键点构建命令和启动命令必须准确这是 AI 能自动化验证和测试的基石。4.3 首次使用创建一个用户认证模块现在我们来触发第一个spec-first流程创建最核心的认证模块。在 AI 聊天框中输入/spec-first 初始化用户认证模块。需要支持用户邮箱和密码注册、登录返回JWT令牌、以及令牌刷新接口。跟随 AI 引导Step 0:AI 会发现没有PROJECT.md它会主动运行初始化生成一个简单的docs/plans/PROJECT.md索引文件内容可能如下# 项目模块索引 | 模块名 | 路径 | 简介 | |--------|------|------| | (暂无模块) | | |模块选择AI 会提示“未找到匹配的现有模块是否新建”选择“新建”。命名与创建AI 会建议模块名比如user-auth。确认后它会在docs/plans/user-auth/下创建user-auth-spec.md文件。这里有个技巧你可以引导 AI 使用模板。在它询问时你可以说“请使用tech-spec-blank.md模板的结构来创建。” 这样生成的 Spec 会非常规范。Phase A - 分析与确认AI 会基于你的需求生成一份详细的 Spec 草案。内容会包括概述认证模块的目标和范围。接口定义POST /api/auth/register,POST /api/auth/login,POST /api/auth/refresh的详细请求/响应格式、状态码。数据模型User表的字段设计id, email, password_hash, refresh_token等。业务流程注册、登录、刷新的步骤和校验逻辑。非功能需求密码加密bcrypt、JWT 配置有效期、密钥。此时务必仔细审阅这份草案这是“确认门”。检查接口设计是否合理字段是否齐全安全考虑是否到位。你可以提出修改意见例如“登录接口的响应里除了 access_token 和 refresh_token还需要加上 token 的类型Bearer和过期时间。” AI 会据此更新草案直到你确认。Phase B C - 开发与验收确认 Spec 后AI 会开始“自动驾驶”创建src/routes/auth.js和src/controllers/authController.js。创建src/models/User.js可能使用 Sequelize 或 Prisma 等 ORM。安装必要的 npm 包bcryptjs,jsonwebtoken。更新app.js或主路由文件挂载认证路由。关键一步在 Phase B.4AI 会尝试运行npm run build。如果你的package.json里还没有build脚本这一步会失败。因此务必提前在package.json中配置好。对于 Node.js 项目一个简单的构建脚本可以是build: echo No build step required for Node.js或者build: npm run lint如果你有 lint 检查。在 Phase C.1AI 会尝试运行npm run dev来启动服务并可能调用刚创建的注册接口进行验收测试。Phase D - 交付完成后AI 会给出一个简报说明创建了哪些文件修改了哪些配置并提醒你检查决策日志。触发决策记录此时你可以主动说“记录本次决策到日志。” 或者等待 AI 提示。whylog-record技能会被触发引导你记录为什么选择 JWT、为什么密码加密用 bcrypt 等决策。至此一个完整的、文档先行的模块开发流程就走通了。你会发现docs/plans/user-auth/user-auth-spec.md已经是一份非常完善、与代码完全同步的权威文档。PROJECT.md也被自动更新加入了user-auth模块的条目。4.4 后续迭代为认证模块添加“密码重置”功能现在模块已经存在后续迭代会更加流畅。输入/spec-first 为用户认证模块添加密码重置功能。需要包含“发送重置邮件”和“通过令牌重置密码”两个接口。AI 在 Step 0 会通过PROJECT.md自动匹配到user-auth模块。它直接读取现有的user-auth-spec.md进入 Phase A分析现有接口和模型并起草新增功能的 Spec 变更例如新增POST /api/auth/forgot-password和POST /api/auth/reset-password。你确认变更后AI 会进入开发阶段在已有的authController.js和路由文件中添加新逻辑并可能需要更新User模型添加reset_password_token和reset_token_expires字段。整个过程中Spec 文件始终是唯一的真相来源决策日志也追加了关于邮件服务选型如 Nodemailer和令牌过期策略的决策。5. 常见问题、避坑指南与进阶技巧在实际使用中我遇到了一些典型问题也总结出一些让流程更顺滑的技巧。5.1 问题排查速查表问题现象可能原因解决方案触发/spec-first无反应或报错1. 技能未正确安装。2. 当前 AI 工具不支持或未启用 Skills 功能。1. 运行npx skills list检查是否安装。使用npx skills add zzusp/doc-first-dev重装。2. 确认你使用的是 Claude Code、CursorAgent模式等支持 Skills 的工具并检查其设置。AI 提示“未找到构建命令”或构建失败1. 项目CLAUDE.md中未配置构建命令。2.package.json中对应的脚本不存在或本身有错误。1. 检查并完善CLAUDE.md中的## 构建与开发部分。2. 确保npm run build等命令在本地可以独立运行成功。对于无需构建的脚本语言如 Python、PHP可以配置为build: echo Skipping build for Python project或运行 lint/格式化命令。AI 无法启动服务进行验收Phase C失败1.CLAUDE.md中本地开发启动命令配置错误。2. 端口冲突或依赖服务如数据库未启动。3. 新代码本身存在启动错误。1. 核对启动命令确保是npm run dev而非npm start生产模式。2. 确保数据库等基础设施已就绪。可以在CLAUDE.md中添加启动前置条件说明。3. 检查 AI 生成的代码看是否有语法错误或逻辑问题导致服务崩溃。spec-multi找不到服务或依赖混乱1.docs/plans/SERVICES.md文件不存在或格式错误。2. 服务间的依赖关系声明有循环或错误。1. 首次使用/spec-multi时AI 会引导创建SERVICES.md。务必按模板填写清楚每个服务的路径、端口和依赖。2. 仔细检查依赖关系图确保其为一个有向无环图DAG。例如frontend依赖backendbackend不能反过来依赖frontend。决策日志log.md变得冗长难读决策日志是顺序追加的缺乏分类。定期如每月对log.md进行归档。可以手动将旧的条目移动到log-2024-04.md并在当前log.md开头添加链接。doc-first-dev的设计也支持在条目超过一定数量如150条后自动轮转但需要确认具体技能的配置。5.2 进阶技巧与最佳实践Spec 文件的标准化模板虽然技能提供了基础模板但你可以为团队定制更详细的 Spec 模板。例如强制要求每个接口必须包含成功响应示例、错误响应示例、边界条件和性能要求。把这套模板放在团队的知识库让 AI 在创建新 Spec 时引用能极大提升文档质量。利用CLAUDE.md提供更丰富的上下文CLAUDE.md不仅是命令清单更是项目的“AI 助手说明书”。除了构建命令你还可以加上架构图链接- 系统架构图参见 docs/architecture.png核心业务规则- 重要用户状态流转规则为未激活 - 已激活 - 已禁用不可逆。代码生成偏好- 生成控制器代码时请使用 async/await 风格错误处理统一使用next(error)传递给全局错误中间件。这些信息能帮助 AI 生成更符合你项目惯例的代码。Monorepo 下的使用策略如果你使用 monorepo如 pnpm workspace, Turborepo建议在每个子包package的根目录下独立维护docs/plans/和CLAUDE.md。这样当处理某个特定包的需求时spec-first的上下文就限定在该包内不会误操作其他包。spec-multi则可以用于协调跨包的变更。与现有文档流程结合如果你团队已有 PR 模板、代码审查清单可以将“Spec 是否已更新”、“决策是否已记录”作为强制检查项加入。这样就把doc-first-dev的理念固化到了研发流程中。处理模糊或探索性需求有时需求非常模糊如“优化用户体验”。不要直接使用/spec-first。可以先和 AI 进行自由讨论明确具体要改什么例如“优化登录页面的加载速度”。将明确后的、具体的需求描述再交给spec-first处理。记住AI 需要明确、可执行的指令。这套doc-first-dev方法论和工具链其价值并非在于替代人类的思考和设计而是通过一种轻量、可落地的约束将良好的工程实践文档即代码、决策可追溯、变更前确认变成团队和 AI 协作的默认行为。它减少了沟通中的歧义降低了返工的成本让项目知识得以持续、有机地生长。开始可能会觉得多了一些步骤但习惯之后你会发现它带来的清晰度和长期可维护性远超过那一点初始的时间投入。尤其是在 AI 辅助开发日益普及的今天一份准确、实时的“活文档”是让 AI 从“聪明的代码生成器”升级为“可靠的开发伙伴”的关键桥梁。