Spec Coding 很早之前就有群友提到说建议写一下。确实还蛮重要的工作中能用到面试也开始问了。这里以我想的一个真实工作场景的例子作为开场。上周和同事聊天他问我还在折腾 Spec Coding 干嘛。原话大概是“Claude Code 都能自己写代码了你花时间写规范不是多此一举”我当时没忍住怼了一句“AI 写出来的屎山代码你来维护”他愣了一下。说实话我理解他的想法。AI 写代码确实快扔一句需求过去几秒钟一个函数就出来了跑起来还挺像那么回事。Demo、脚本、一次性页面这么搞没毛病。但问题是——你把这套玩法搬到一个多人协作、要长期维护的项目里过两周再回来看那段代码你大概率不想碰也不敢碰。因为需求里没写清的部分AI 自己脑补了边界条件你没提它按“常见写法”猜了一个你们团队的错误码格式、权限校验约定AI 一个都不知道。它只是按照训练数据里出现频率最高的方案给你拼了一段“看起来能跑”的代码。这篇文章聊 Spec Coding 的核心思路内容不少建议收藏。通过本文你将搞懂Vibe Coding 和 Spec Coding 的实际差别以及什么时候该用哪个完整的 Spec Coding 落地流程从写需求到让 AI 按规矩执行Spec 在主流 AI IDECursor、Claude Code、Copilot 等里怎么配、怎么管、怎么防止 AI 越界Vibe Coding 不是不能用Vibe Coding氛围编程凭感觉走。给 AI 一句模糊的意图它就直接开始输出代码。Karpathy 最早提这个词时说的也是那种把需求丢给 AI、顺着感觉不断调整、甚至暂时不太管代码细节的写法。Vibe Coding 不是原罪。下面这些场景用它反而很合适验证一个想法先写个 Demo 看看效果写一次性脚本跑完就扔做内部小工具影响面很小AI 写完后有完整测试兜底而且不直接暴露给外部用户这些情况下硬写一大堆 Spec 反而是浪费时间。真正的问题是很多人验证完想法之后顺手就把 Vibe 出来的代码推上了生产。这就不一样了。Demo 阶段你可以靠感觉走因为错了就改坏了就删。生产代码不行它后面会接数据库、接支付、接用户数据、接别人的维护成本。你今天省下来的半小时可能会变成后面几天的排查时间。我的判断标准就一条这段代码要活多久两天就扔掉的脚本Vibe 够了。写 Spec 反而拖效率。3-5 天这种中间地带可以写轻量 Spec。不用展开完整设计只写关键约束和验收标准半小时差不多能搞定。超过一周的代码只要需要别人维护、涉及数据持久化、接入外部接口就别裸 Vibe。至少要把约束、边界和验收标准写清楚。而且很多时候搭配轻量级 Spec 也没问题不需要太死板。轻量 Spec 可以简单到这种程度## 任务目标 实现一个订单导出接口支持按时间范围导出 CSV。 ## 关键约束 - 单次导出最多 5000 条 - 时间范围不能超过 31 天 - 必须校验用户权限只能导出当前租户的数据 - 查询必须命中 order_tenant_time_idx 联合索引 - 导出失败要记录失败原因不能只返回 unknown error ## 验收标准 - 正常导出 CSV字段顺序符合产品约定 - 超过 5000 条时返回明确错误 - 越权租户数据不能被导出 - 单元测试覆盖空时间、越界时间、无权限、无数据四种场景Spec Coding 到底是什么Spec Coding直译过来叫规范驱动编程。简单来说就是先把规范写清楚再让 AI 干活。平时让 AI 写代码很多人会直接丢一句帮我做一个用户系统。AI 当然能写而且看起来还挺像那么回事。但问题也在这里你没告诉它用户系统到底长什么样它就只能自己猜。用户怎么注册邮箱能不能重复密码怎么存接口失败时返回什么格式哪些功能这期不做管理员有没有禁用用户的能力这些东西如果一开始没写清楚AI 不会停下来反问你它大概率会先补一套自己觉得合理的方案。Spec Coding 做的事情就是把这些规则提前写下来。这里的 Spec 不是随便写两句需求而是一份 AI 能照着执行的技术约定。接口、数据结构、错误码、边界条件、安全要求、技术栈限制甚至哪些操作不允许碰都要写在里面。它和 Vibe Coding 的差别也就在这。Vibe Coding 更像是你给 AI 一个大方向然后让它自由发挥。代码生成出来以后你再去验收、改 bug、补细节。短平快的小脚本这么干没啥问题甚至很爽。但项目稍微复杂一点就容易出事。等你发现 AI 理解错了代码已经写了一堆。你回头查也很难说清楚到底是需求没讲明白还是 AI 自己乱发挥。简单总结下 Spec Coding 和 Vibe Coding 的差别AI 的行为是由你定义的还是由它猜的四步落地一般会把 Spec Coding 拆成四步Specify、Plan、Tasks、Implement。阶段干什么产出关键动作Specify产品定义requirements.md明确功能、用户、痛点定“做什么”Plan技术规划design.md定技术栈、架构、契约定“怎么做”Tasks任务拆解tasks.md拆成原子任务写验收标准ImplementAI 执行-AI 按 Spec 干活人验收理解起来其实很简单核心就是先写清楚要做什么再写清楚怎么做然后拆任务最后交给 AI 执行。Spec Coding 规范驱动编程流水线Specify先搞清楚做什么第一步是Specify产出一般是requirements.md或者叫spec.md。这一步有点像写 PRD但面向的使用者是 AI。所以它不能只写方向得把边界也写出来。比如你写一句做一个用户系统。人看着没问题AI 看了就开始猜了用户怎么注册邮箱能不能重复密码有啥要求第三方登录做不做管理员能不能禁人被禁了数据怎么办你不写它就自己定。更稳一点的写法是支持邮箱注册和登录邮箱必须唯一密码长度至少 8 位暂不支持第三方登录管理员可以禁用用户用户被禁用后不能登录但历史数据保留。这句话让 AI 知道哪些能做哪些不能做哪些边界不能碰。Plan敲定技术方案第二步是Plan一般会落到design.md或plan.md里。这一步很多人会跳过觉得反正 AI 会写代码让它自己发挥就行。然后问题就来了。你没说用哪个 Java 版本它可能给你写 Java 8 的代码你没说 Spring Boot 版本它可能按旧写法来你没说错误码格式它就每个接口返回一套你没说分层方式它可能 Controller 里直接写业务逻辑你没说表字段怎么命名它也会按自己的习惯来。所以design.md不用写得特别重但几个关键约束得先定下来。比如先写成这样就够用## 技术栈 - 语言: Java 21 (LTS) - 框架: Spring Boot 3.2.x - 数据库: PostgreSQL 16 - 缓存: Redis 7.x ## 架构设计 - 分层: Controller → Service → Repository - 通信: REST API gRPC内部服务 - 部署: Docker Kubernetes ## 接口约定 - API 规范: OpenAPI 3.0 - 错误码: 统一格式 {code: USER_NOT_FOUND, message: ...} - 日志格式: JSON必须包含 trace_id你可能会想这不就是设计文档吗确实有点像。但区别在于传统设计文档主要是给人看的。人看完知道大方向剩下很多细节可以靠团队习惯补上。比如密码不能明文存、错误码要统一、日志里要带 trace_id这些东西在成熟团队里通常不用反复强调。AI 不一样。你没写它就猜。猜对了还好猜错了就得你回来返工。拿密码存储举个例子。你只写一句“登录要安全”对人来说可能够了但对 AI 来说太宽了。它也许知道不能明文存密码也可能给你整一个看着像安全、实际不该用的方案。更稳的做法是把规则写死密码使用 bcrypt 哈希存储。 bcrypt cost 默认设置为 12可根据服务器性能在 10-14 之间调整。 bcrypt 自带随机盐数据库只保存哈希值不保存明文密码。这段看着有点细但它把“安全”这个大词拆成了 AI 能执行的几条具体规则。错误处理也一样。别写“接口失败时返回友好提示”这句话基本没约束力。AI 可能这个接口返回error那个接口返回message还有的地方直接抛异常。直接写清楚{ code: USER_NOT_FOUND, message: 用户不存在, trace_id: xxx }再补一段状态码约定参数错误返回 400。 未登录返回 401。 无权限返回 403。 资源不存在返回 404。 邮箱重复、用户名重复这类冲突返回 409。这样 AI 至少知道该往哪个方向写。说到底design.md主要是为了减少 AI 自己补设定。你把技术栈、接口格式、错误码、日志、并发、安全这些规则提前写好后面让 AI 写代码时它就不太容易跑偏。Tasks任务要小到能验收第三步是Tasks一般会写到tasks.md里。这里不要一上来就让 AI “完成用户模块”。这个范围太大了。注册、登录、查询、禁用、权限、参数校验、异常处理、单元测试全都塞在一个任务里AI 很容易写着写着漏东西。最后你看代码时还得一项一项往回补。但也别拆得太碎。创建 UserDTO、添加 email 字段、写一个空的 Service 方法——这种任务看起来很细实际会把人折腾死。你维护任务列表的时间可能比让 AI 写代码还长。我比较喜欢的粒度是一个 Task 对应一个 API、一张表的核心操作或者一个能独立验收的小功能。比如用户注册接口可以这么写### Task-001: 用户注册接口 描述实现用户注册包含参数校验、密码加密和用户入库。 验收标准 - [ ] POST /api/v1/users 成功时返回 201 - [ ] 密码使用 bcrypt 加密后存储 - [ ] 邮箱唯一重复注册返回 409 - [ ] 返回体必须包含 user_id、email、created_at - [ ] 分支覆盖率branch coverage不低于 80% 预估工时2h这里真正值钱的是验收标准。“保证安全”“代码优雅”“性能要好”——这种话写了跟没写差不多AI 不知道你心里的安全到底指什么优雅要优雅到什么程度。但密码用 bcrypt重复邮箱返回409返回体里有user_id、email、created_at分支覆盖率不低于80%——这些东西都能跑测试验证不用靠感觉。覆盖率阈值别机械套。纯逻辑模块做到 80% 以上通常合理如果涉及大量外部依赖、异步流程和复杂 mock可以放宽到 60-70%把重点放在关键分支组合有没有覆盖。Implement让 AI 干活提示词不用搞得很玄学直接把相关 Spec 塞进去就行请根据以下 Spec 实现 Task-001。 需求说明 [粘贴 requirements.md 相关段落] 技术约束 [粘贴 design.md 相关段落] 任务验收标准 [粘贴 tasks.md 里的 Task-001]这里有个坑不要把所有 Spec 一股脑塞进上下文。单次会话里我会优先放三类内容全局约束比如代码风格、错误码格式、日志规范当前任务的需求说明当前任务的验收标准。其他内容按需补不要为了“完整”把所有文档都贴进去。一般来说单次输入控制在 3000-8000 tokens 会更稳一点大致相当于 1-2 份 Spec 文档再加 1-2 个相关代码文件。超过这个范围就拆会话。别指望模型在一个特别长的上下文里什么都顾得上。上下文越长关键信息越可能被淹在中间最后反而漏掉最重要的约束。我自己会遵守三条原则第一约定写进文档不要只写在聊天里。聊天记录下次很可能接不上文档才是可以复用的上下文。第二验收标准能量化就量化。“高性能”没法验收QPS 1000、P95 200 ms、branch coverage 80%才能验收。第三Spec 要进 Git跟代码一起走。代码变了Spec 也要改。不然后面继续让 AI 开发它拿到的就是一份过期说明。这一步走通后AI 不会突然变聪明但乱猜的空间会小很多。接下来还有个很现实的问题这些 Spec 到底放哪里怎么让工具每次都读到Spec 在 AI IDE 里怎么落地写完 Spec 之后有个问题经常被忽略这些文件到底放哪里怎么让 AI 自动读到主流工具都有自己的规范文件机制工具规范文件位置作用域加载方式Cursor.cursor/rules/*.mdc新版或 .cursorrules旧版项目级 / 全局新版支持 frontmatter可设 Always apply 或按文件 glob 自动附加Claude CodeCLAUDE.md根目录和子目录均可项目级 / 目录级进入目录自动加载GitHub Copilot.github/copilot-instructions.md仓库级自动注入每次请求Windsurf.windsurfrules项目级自动加载AiderCONVENTIONS.md仓库根目录项目级通过 --read CONVENTIONS.md或在 .aider.conf.yml 里用 read: 自动加载到这里另一个问题也会冒出来Cursor、Claude Code、Copilot 这些是日常写代码的入口那 Superpowers、Spec-Kit、Open Spec、Kiro、BMAD-METHOD 这些专门围绕 Spec Coding 的工具到底该怎么选这个问题展开会比较长我准备放到下一篇单独聊。这里先把 Spec 怎么写、怎么放、怎么管住 AI 说清楚。知道放哪之后还有一个问题哪些 Spec 每次都注入哪些按需带上实际操作中我一般分成两层。几乎每个会话都要带上的必须注入技术栈版本和关键库写明比如 Go 1.21 Gin GORM PostgreSQL 14。别让 AI 自己猜版本号。代码风格贴一段 150-200 行的示例代码展示命名、错误处理、注释、返回格式。别只写抽象原则一段参考实现比十条规则管用。边界条件用三色标签后面会说划清楚什么能做、什么要问、什么绝不能碰。这些放工具的 always-on 规则文件里每次会话自动注入。当前任务相关时才带的按需注入项目愿景一两句话说清为啥做这个项目比如“把用户服务从单体拆出来用 Go 重写API 兼容”。新任务开始时带一次就行。命令清单列出 build、test、run 命令比如make build、go test ./...。有执行任务时带上。目录结构树状图说清代码、测试、文档分别放哪。涉及新增文件时才需要。Git 规范分支名、commit message、PR 要求。涉及 Git 操作时带上。这么分的原因很直接全局约束几乎每次都要遵守值得常驻。其他的按任务加避免上下文里堆太多不相关的内容。Spec 塞越多AI 反而越容易漏掉真正重要的那几条。三色标签AI 能干什么、不能干什么AI 遇到拿不准的操作时到底该自己决定还是停下来问你三种颜色三种权限。三色标签AI 决策权限的风险控制机制✅Always自动执行代码检查、测试、格式化这些AI 自己拍板就行。比如提交前自动跑make lint。⚠️Ask first需确认可能影响其他模块的变更AI 出方案等你审。改数据库索引、改 API 路由这种就属于这类。Never绝对禁止直连生产库、提交密钥、删线上数据。AI 碰到就必须停报错。落地的时候有几件事容易忽略。刚开始宁严勿松。Ask First 多放点跑一周后看哪些操作 AI 每次都做对了再放到 Always。规则必须写具体。“重要变更需确认”这句话 AI 没法执行它不知道什么算“重要”。得写成“修改已有 API 的 URL 路径需确认”。“小心操作数据库”也不行要写“ALTER TABLE 操作需确认”。Never 规则不能只靠 AI 自觉。只在文档里写“禁止直连生产库”并不能真的拦住它。AI 不会主动检查自己的输出是否违规。Never 规则需要多层防线Spec 声明影响 AI 生成倾向但拦不住配置模板.env.example里不放真实密钥AI 就没东西可复制Pre-commit hook正则扫密钥硬编码、生产环境连接串提交时自动拦截AI IDE 配置.cursorignore阻止 Cursor 读取.env.production之类的文件越重要的 Never 规则越要推进到 CI 层做硬性检查。停在“文档里有写”这一步迟早出事。每周回头看一次。AI 是不是动不动就停下来问那 Ask First 里有些操作可以放行了。AI 有没有偷偷干不该干的事有就补 Never。项目里有没有冒出新的敏感操作加进去。项目大了Spec 怎么管小项目 Spec 少手动往上下文里丢就行。模块多了之后全塞上下文就废了AI 看着一堆和当前任务无关的约束反而更容易跑偏。按规模选策略。Spec 管理策略分层过滤 精准召回10 个模块以内分文件存储按领域拆就行specs/ ├── global/ # 全局约束 │ ├── conventions.md # 代码规范 │ └── architecture.md # 架构概览 ├── backend/ # 后端规格 │ ├── api/ │ ├── service/ │ └── persistence/ ├── frontend/ # 前端规格 └── shared/ # 共享契约 └── dto.md每次只把当前任务相关的两三个文件丢进去别贪多。10-30 个模块摘要索引手动挑文件开始累了就让 AI 先生成一份目录加关键词索引## Spec 索引 - [数据库设计](specs/db/schema.md) - 关键词: PostgreSQL, 索引优化 - [用户 API](specs/backend/api/user.md) - 关键词: REST, JWT, 鉴权 - [订单服务](specs/backend/service/order.md) - 关键词: 事务, 幂等需要细节时让 AI 主动来要不用全量灌进去。30 个模块以上RAG 向量检索手动选文件不现实了得上 RAG。Embedding 模型选 text-embedding-3-small/large向量库看规模Chroma 适合本地Pinecone 适合云端Milvus 适合企业级。Chunk 策略按语义单元切一个 Task 或一个 API 定义为一个 chunk默认控制在 512-1024 tokens 之间。Top-K 召回 3-5 条加相似度阈值 0.7。但十个模块的项目搞向量库纯属给自己找事。什么时候人工选上下文开始痛苦了什么时候再上。不分规模都管用的一条单会话单任务Session 1: 数据库设计 ├── 输入: global/conventions.md backend/db/ ├── 输出: 完成实体设计 └── 关闭会话 Session 2: API 实现 ├── 输入: Session 1 产出 backend/api/ ├── 输出: 完成 Controller └── 关闭会话上下文干净AI 就不会被前面任务的边角料带跑。这条比什么花哨的检索策略都管用。领域知识为什么这么重要AI 训练数据再多也不知道你项目里那些特定的规则你得主动告诉它。举个例子你做了一个商城项目其中有一个规则是优惠券和秒杀不能叠加。这个规则你不写进 SpecAI 很可能就把两个折扣都算上了。代码能跑测试也可能过但业务直接错了。这类知识一般可以分成几种业务规则优惠券和秒杀不能叠加同一用户每天只能领取一次奖励技术约束订单分页必须走指定联合索引深分页 100 页改用游标禁止全表扫描历史债务第三方上传接口只支持 5 MB超过就会报错所以代码里要提前校验性能基线单表查询控制在 50 ms 内关键接口超过 200 ms 要考虑降级或兜底这些东西是 AI 写代码时的边界。现在很多 Spec-Driven Development 的思路就是把 Spec 从“写给人看的文档”变成“约束 AI 生成代码的规则”。不要认为 Spec 只是前期用用后续实现、校验和维护时都需要。不过只把规则写进去还不够最好再加一段自检清单。因为 AI 很容易写完功能就结束不会主动回头确认这些隐含约束。完成自检清单任务写完之后不要让 AI 直接说一句“已完成”。至少让它按清单自己过一遍。比如完成Task-001后必须逐项确认[ ] 所有 API 错误返回都符合统一格式[ ] 数据库查询命中了指定联合索引[ ] 优惠券和秒杀的互斥逻辑已正确实现[ ] 单元测试覆盖了空值、越界、并发等边界场景[ ] 分支覆盖率branch coverage 80%[ ] 圈复杂度 10如果有哪一项没法确认不能糊弄过去要把原因写出来。AI 很容易把代码写完当成任务完成。可真实项目里功能能跑只是第一步错误格式、索引命中、边界测试、复杂度控制这些才是后面少背锅的地方。多代理协作的坑有人会问一个 AI 不够用多搞几个行不行可以但坑比你想的多。Multi-Agent 三代理协作流水线三代理协作的思路是代码、测试、审查各管一段流水线推进。代码代理接到 Task 写功能写完交给测试代理出用例跑测试通过后再交给审查代理看代码质量最后人类终审合并。有个坑必须提前说清测试代理在自己的分支上写测试但被测代码在代码代理的分支上。这两个分支是平行的测试代理要么先 merge 代码分支要么根本跑不起来。两种能跑通的模式串行同分支推荐起步。三个代理在同一个 feature 分支上按顺序 commit用 commit message 前缀区分角色。简单没有合并冲突适合大多数项目。git commit -m [code] implement user registration API git commit -m [test] add unit tests for user registration git commit -m [review] fix null check in email validation链式继承代理能力已验证后。测试代理从代码分支 checkout审查代理从测试分支 checkout最后从审查分支 merge 回主线。分支之间是继承关系而不是平行关系每个代理都能看到前一个代理的产出。多代理翻车的场景不少死锁A 等 B、B 等 A设计时确保依赖是 DAG、无限循环代理自我迭代停不下来设最大轮次 Max 3、输出格式错误JSON 解析失败加校验和重试最多 3 次。提前设好这些兜底能避开大部分问题。老实说多代理这块我自己也还在摸索目前的经验是串行同分支模式能覆盖八成场景复杂编排除非团队有人专门维护否则翻车概率不低。Spec 不是写完就扔的跑了几个项目后有几个习惯固定下来了。渐进细化。别想着一口气写出完美 Spec。先写高层大纲让 AI 把骨架跑起来再一个模块一个模块补细节。模块化组织。API、数据库、样式规范、错误码、权限规则各一个文件。每次只给 AI 当前任务用得到的上下文。持续迭代。每次 Code Review 发现问题或者 AI 又把同一个坑踩了一遍回去改 Spec。只改代码不改规范下次照样犯。这里有个高频翻车场景值得特别说一下Task-001 完成时 Spec 规定错误格式是{code: USER_NOT_FOUND, message: ...}两周后 Spec 更新加了trace_id字段但 Task-001 的代码已经没人管了。规范和实现就这么悄悄跑偏了。应对办法Spec 变更时做影响范围评估。可以在每个 Spec 文件里维护一个“依赖此文件的模块”列表Spec 更新时主动触发受影响模块的回归测试。CI 流水线里加一条判断Spec 文件有变动自动跑相关模块的测试。分享几套 Spec 模板我常用的就这三种按场景选一个就行。模板一OpenAPI 风格适合 API 开发## APIPOST /api/v1/users ### 基本信息 - **端点**/api/v1/users - **方法**POST ### 请求参数 | 字段 | 类型 | 必填 | 约束 | 示例 | | -------- | ------ | ---- | --------------------------- | ---------------- | | email | string | 是 | 邮箱格式 | userexample.com | | password | string | 是 | 8-32 字符包含大小写和数字 | - | ### 响应格式 - **201 Created**用户创建成功 json {id: uuid, email: userexample.com, created_at: ...} - **409 Conflict**邮箱已存在 json {code: EMAIL_ALREADY_EXISTS, message: Email already exists} ### 验收标准 - [ ] 密码用 bcryptcost12 - [ ] 邮箱唯一性由数据库唯一索引保证 - [ ] 分支覆盖率branch coverage 80%模板二Gherkin 风格适合 BDDFeature: 用户登录 Scenario: 使用有效凭据登录 Given 用户已注册邮箱 testexample.com 和密码 Password123 When 用户提交登录请求 Then 返回 200 状态码和 JWT token And token 有效期 24 小时 Scenario: 使用无效密码登录 Given 用户已注册邮箱 testexample.com When 用户用错误密码提交登录 Then 返回 401 And 错误信息为 Invalid credentials And 不暴露具体是邮箱还是密码错模板三Checklist 风格适合代码审查## Code Review Checklist ### 功能性 - [ ] 实现符合 Spec 描述 - [ ] 边界条件已处理空值、越界、并发 - [ ] 错误处理完善 ### 质量 - [ ] 函数长度 50 行 - [ ] 圈复杂度 10 - [ ] 无重复代码DRY ### 安全 - [ ] 无敏感信息硬编码 - [ ] 输入已验证/转义 - [ ] 权限检查已加踩过的坑说几个我自己踩过的。约束写太死了AI 连正常的灵活性都没有。比如你把 Service 层每个方法签名都定好AI 连个参数名都不敢改。Spec 定的是边界不是逐行伪代码。反过来约束写少了更常见。关键边界没定义AI 就自己猜。猜对了算运气猜错了算日常。我有一个项目AI 用了 MD5 存密码就是因为 Spec 里没写用什么加密算法。Spec 改了没同步这个最隐蔽。代码和文档慢慢就跑偏了AI 下次拿到的还是旧版规范写出来的代码自然也对不上。还有一个只写不验。Spec 写了一大堆但没接到 CI 里最后变成形式主义。写完没人检查跟没写差不多。最重要的是一定一定一定要上 Git多多小步提交懂得都懂
Spec Coding 规范驱动编程实战:从 Vibe Coding 到 AI 代码规范
Spec Coding 很早之前就有群友提到说建议写一下。确实还蛮重要的工作中能用到面试也开始问了。这里以我想的一个真实工作场景的例子作为开场。上周和同事聊天他问我还在折腾 Spec Coding 干嘛。原话大概是“Claude Code 都能自己写代码了你花时间写规范不是多此一举”我当时没忍住怼了一句“AI 写出来的屎山代码你来维护”他愣了一下。说实话我理解他的想法。AI 写代码确实快扔一句需求过去几秒钟一个函数就出来了跑起来还挺像那么回事。Demo、脚本、一次性页面这么搞没毛病。但问题是——你把这套玩法搬到一个多人协作、要长期维护的项目里过两周再回来看那段代码你大概率不想碰也不敢碰。因为需求里没写清的部分AI 自己脑补了边界条件你没提它按“常见写法”猜了一个你们团队的错误码格式、权限校验约定AI 一个都不知道。它只是按照训练数据里出现频率最高的方案给你拼了一段“看起来能跑”的代码。这篇文章聊 Spec Coding 的核心思路内容不少建议收藏。通过本文你将搞懂Vibe Coding 和 Spec Coding 的实际差别以及什么时候该用哪个完整的 Spec Coding 落地流程从写需求到让 AI 按规矩执行Spec 在主流 AI IDECursor、Claude Code、Copilot 等里怎么配、怎么管、怎么防止 AI 越界Vibe Coding 不是不能用Vibe Coding氛围编程凭感觉走。给 AI 一句模糊的意图它就直接开始输出代码。Karpathy 最早提这个词时说的也是那种把需求丢给 AI、顺着感觉不断调整、甚至暂时不太管代码细节的写法。Vibe Coding 不是原罪。下面这些场景用它反而很合适验证一个想法先写个 Demo 看看效果写一次性脚本跑完就扔做内部小工具影响面很小AI 写完后有完整测试兜底而且不直接暴露给外部用户这些情况下硬写一大堆 Spec 反而是浪费时间。真正的问题是很多人验证完想法之后顺手就把 Vibe 出来的代码推上了生产。这就不一样了。Demo 阶段你可以靠感觉走因为错了就改坏了就删。生产代码不行它后面会接数据库、接支付、接用户数据、接别人的维护成本。你今天省下来的半小时可能会变成后面几天的排查时间。我的判断标准就一条这段代码要活多久两天就扔掉的脚本Vibe 够了。写 Spec 反而拖效率。3-5 天这种中间地带可以写轻量 Spec。不用展开完整设计只写关键约束和验收标准半小时差不多能搞定。超过一周的代码只要需要别人维护、涉及数据持久化、接入外部接口就别裸 Vibe。至少要把约束、边界和验收标准写清楚。而且很多时候搭配轻量级 Spec 也没问题不需要太死板。轻量 Spec 可以简单到这种程度## 任务目标 实现一个订单导出接口支持按时间范围导出 CSV。 ## 关键约束 - 单次导出最多 5000 条 - 时间范围不能超过 31 天 - 必须校验用户权限只能导出当前租户的数据 - 查询必须命中 order_tenant_time_idx 联合索引 - 导出失败要记录失败原因不能只返回 unknown error ## 验收标准 - 正常导出 CSV字段顺序符合产品约定 - 超过 5000 条时返回明确错误 - 越权租户数据不能被导出 - 单元测试覆盖空时间、越界时间、无权限、无数据四种场景Spec Coding 到底是什么Spec Coding直译过来叫规范驱动编程。简单来说就是先把规范写清楚再让 AI 干活。平时让 AI 写代码很多人会直接丢一句帮我做一个用户系统。AI 当然能写而且看起来还挺像那么回事。但问题也在这里你没告诉它用户系统到底长什么样它就只能自己猜。用户怎么注册邮箱能不能重复密码怎么存接口失败时返回什么格式哪些功能这期不做管理员有没有禁用用户的能力这些东西如果一开始没写清楚AI 不会停下来反问你它大概率会先补一套自己觉得合理的方案。Spec Coding 做的事情就是把这些规则提前写下来。这里的 Spec 不是随便写两句需求而是一份 AI 能照着执行的技术约定。接口、数据结构、错误码、边界条件、安全要求、技术栈限制甚至哪些操作不允许碰都要写在里面。它和 Vibe Coding 的差别也就在这。Vibe Coding 更像是你给 AI 一个大方向然后让它自由发挥。代码生成出来以后你再去验收、改 bug、补细节。短平快的小脚本这么干没啥问题甚至很爽。但项目稍微复杂一点就容易出事。等你发现 AI 理解错了代码已经写了一堆。你回头查也很难说清楚到底是需求没讲明白还是 AI 自己乱发挥。简单总结下 Spec Coding 和 Vibe Coding 的差别AI 的行为是由你定义的还是由它猜的四步落地一般会把 Spec Coding 拆成四步Specify、Plan、Tasks、Implement。阶段干什么产出关键动作Specify产品定义requirements.md明确功能、用户、痛点定“做什么”Plan技术规划design.md定技术栈、架构、契约定“怎么做”Tasks任务拆解tasks.md拆成原子任务写验收标准ImplementAI 执行-AI 按 Spec 干活人验收理解起来其实很简单核心就是先写清楚要做什么再写清楚怎么做然后拆任务最后交给 AI 执行。Spec Coding 规范驱动编程流水线Specify先搞清楚做什么第一步是Specify产出一般是requirements.md或者叫spec.md。这一步有点像写 PRD但面向的使用者是 AI。所以它不能只写方向得把边界也写出来。比如你写一句做一个用户系统。人看着没问题AI 看了就开始猜了用户怎么注册邮箱能不能重复密码有啥要求第三方登录做不做管理员能不能禁人被禁了数据怎么办你不写它就自己定。更稳一点的写法是支持邮箱注册和登录邮箱必须唯一密码长度至少 8 位暂不支持第三方登录管理员可以禁用用户用户被禁用后不能登录但历史数据保留。这句话让 AI 知道哪些能做哪些不能做哪些边界不能碰。Plan敲定技术方案第二步是Plan一般会落到design.md或plan.md里。这一步很多人会跳过觉得反正 AI 会写代码让它自己发挥就行。然后问题就来了。你没说用哪个 Java 版本它可能给你写 Java 8 的代码你没说 Spring Boot 版本它可能按旧写法来你没说错误码格式它就每个接口返回一套你没说分层方式它可能 Controller 里直接写业务逻辑你没说表字段怎么命名它也会按自己的习惯来。所以design.md不用写得特别重但几个关键约束得先定下来。比如先写成这样就够用## 技术栈 - 语言: Java 21 (LTS) - 框架: Spring Boot 3.2.x - 数据库: PostgreSQL 16 - 缓存: Redis 7.x ## 架构设计 - 分层: Controller → Service → Repository - 通信: REST API gRPC内部服务 - 部署: Docker Kubernetes ## 接口约定 - API 规范: OpenAPI 3.0 - 错误码: 统一格式 {code: USER_NOT_FOUND, message: ...} - 日志格式: JSON必须包含 trace_id你可能会想这不就是设计文档吗确实有点像。但区别在于传统设计文档主要是给人看的。人看完知道大方向剩下很多细节可以靠团队习惯补上。比如密码不能明文存、错误码要统一、日志里要带 trace_id这些东西在成熟团队里通常不用反复强调。AI 不一样。你没写它就猜。猜对了还好猜错了就得你回来返工。拿密码存储举个例子。你只写一句“登录要安全”对人来说可能够了但对 AI 来说太宽了。它也许知道不能明文存密码也可能给你整一个看着像安全、实际不该用的方案。更稳的做法是把规则写死密码使用 bcrypt 哈希存储。 bcrypt cost 默认设置为 12可根据服务器性能在 10-14 之间调整。 bcrypt 自带随机盐数据库只保存哈希值不保存明文密码。这段看着有点细但它把“安全”这个大词拆成了 AI 能执行的几条具体规则。错误处理也一样。别写“接口失败时返回友好提示”这句话基本没约束力。AI 可能这个接口返回error那个接口返回message还有的地方直接抛异常。直接写清楚{ code: USER_NOT_FOUND, message: 用户不存在, trace_id: xxx }再补一段状态码约定参数错误返回 400。 未登录返回 401。 无权限返回 403。 资源不存在返回 404。 邮箱重复、用户名重复这类冲突返回 409。这样 AI 至少知道该往哪个方向写。说到底design.md主要是为了减少 AI 自己补设定。你把技术栈、接口格式、错误码、日志、并发、安全这些规则提前写好后面让 AI 写代码时它就不太容易跑偏。Tasks任务要小到能验收第三步是Tasks一般会写到tasks.md里。这里不要一上来就让 AI “完成用户模块”。这个范围太大了。注册、登录、查询、禁用、权限、参数校验、异常处理、单元测试全都塞在一个任务里AI 很容易写着写着漏东西。最后你看代码时还得一项一项往回补。但也别拆得太碎。创建 UserDTO、添加 email 字段、写一个空的 Service 方法——这种任务看起来很细实际会把人折腾死。你维护任务列表的时间可能比让 AI 写代码还长。我比较喜欢的粒度是一个 Task 对应一个 API、一张表的核心操作或者一个能独立验收的小功能。比如用户注册接口可以这么写### Task-001: 用户注册接口 描述实现用户注册包含参数校验、密码加密和用户入库。 验收标准 - [ ] POST /api/v1/users 成功时返回 201 - [ ] 密码使用 bcrypt 加密后存储 - [ ] 邮箱唯一重复注册返回 409 - [ ] 返回体必须包含 user_id、email、created_at - [ ] 分支覆盖率branch coverage不低于 80% 预估工时2h这里真正值钱的是验收标准。“保证安全”“代码优雅”“性能要好”——这种话写了跟没写差不多AI 不知道你心里的安全到底指什么优雅要优雅到什么程度。但密码用 bcrypt重复邮箱返回409返回体里有user_id、email、created_at分支覆盖率不低于80%——这些东西都能跑测试验证不用靠感觉。覆盖率阈值别机械套。纯逻辑模块做到 80% 以上通常合理如果涉及大量外部依赖、异步流程和复杂 mock可以放宽到 60-70%把重点放在关键分支组合有没有覆盖。Implement让 AI 干活提示词不用搞得很玄学直接把相关 Spec 塞进去就行请根据以下 Spec 实现 Task-001。 需求说明 [粘贴 requirements.md 相关段落] 技术约束 [粘贴 design.md 相关段落] 任务验收标准 [粘贴 tasks.md 里的 Task-001]这里有个坑不要把所有 Spec 一股脑塞进上下文。单次会话里我会优先放三类内容全局约束比如代码风格、错误码格式、日志规范当前任务的需求说明当前任务的验收标准。其他内容按需补不要为了“完整”把所有文档都贴进去。一般来说单次输入控制在 3000-8000 tokens 会更稳一点大致相当于 1-2 份 Spec 文档再加 1-2 个相关代码文件。超过这个范围就拆会话。别指望模型在一个特别长的上下文里什么都顾得上。上下文越长关键信息越可能被淹在中间最后反而漏掉最重要的约束。我自己会遵守三条原则第一约定写进文档不要只写在聊天里。聊天记录下次很可能接不上文档才是可以复用的上下文。第二验收标准能量化就量化。“高性能”没法验收QPS 1000、P95 200 ms、branch coverage 80%才能验收。第三Spec 要进 Git跟代码一起走。代码变了Spec 也要改。不然后面继续让 AI 开发它拿到的就是一份过期说明。这一步走通后AI 不会突然变聪明但乱猜的空间会小很多。接下来还有个很现实的问题这些 Spec 到底放哪里怎么让工具每次都读到Spec 在 AI IDE 里怎么落地写完 Spec 之后有个问题经常被忽略这些文件到底放哪里怎么让 AI 自动读到主流工具都有自己的规范文件机制工具规范文件位置作用域加载方式Cursor.cursor/rules/*.mdc新版或 .cursorrules旧版项目级 / 全局新版支持 frontmatter可设 Always apply 或按文件 glob 自动附加Claude CodeCLAUDE.md根目录和子目录均可项目级 / 目录级进入目录自动加载GitHub Copilot.github/copilot-instructions.md仓库级自动注入每次请求Windsurf.windsurfrules项目级自动加载AiderCONVENTIONS.md仓库根目录项目级通过 --read CONVENTIONS.md或在 .aider.conf.yml 里用 read: 自动加载到这里另一个问题也会冒出来Cursor、Claude Code、Copilot 这些是日常写代码的入口那 Superpowers、Spec-Kit、Open Spec、Kiro、BMAD-METHOD 这些专门围绕 Spec Coding 的工具到底该怎么选这个问题展开会比较长我准备放到下一篇单独聊。这里先把 Spec 怎么写、怎么放、怎么管住 AI 说清楚。知道放哪之后还有一个问题哪些 Spec 每次都注入哪些按需带上实际操作中我一般分成两层。几乎每个会话都要带上的必须注入技术栈版本和关键库写明比如 Go 1.21 Gin GORM PostgreSQL 14。别让 AI 自己猜版本号。代码风格贴一段 150-200 行的示例代码展示命名、错误处理、注释、返回格式。别只写抽象原则一段参考实现比十条规则管用。边界条件用三色标签后面会说划清楚什么能做、什么要问、什么绝不能碰。这些放工具的 always-on 规则文件里每次会话自动注入。当前任务相关时才带的按需注入项目愿景一两句话说清为啥做这个项目比如“把用户服务从单体拆出来用 Go 重写API 兼容”。新任务开始时带一次就行。命令清单列出 build、test、run 命令比如make build、go test ./...。有执行任务时带上。目录结构树状图说清代码、测试、文档分别放哪。涉及新增文件时才需要。Git 规范分支名、commit message、PR 要求。涉及 Git 操作时带上。这么分的原因很直接全局约束几乎每次都要遵守值得常驻。其他的按任务加避免上下文里堆太多不相关的内容。Spec 塞越多AI 反而越容易漏掉真正重要的那几条。三色标签AI 能干什么、不能干什么AI 遇到拿不准的操作时到底该自己决定还是停下来问你三种颜色三种权限。三色标签AI 决策权限的风险控制机制✅Always自动执行代码检查、测试、格式化这些AI 自己拍板就行。比如提交前自动跑make lint。⚠️Ask first需确认可能影响其他模块的变更AI 出方案等你审。改数据库索引、改 API 路由这种就属于这类。Never绝对禁止直连生产库、提交密钥、删线上数据。AI 碰到就必须停报错。落地的时候有几件事容易忽略。刚开始宁严勿松。Ask First 多放点跑一周后看哪些操作 AI 每次都做对了再放到 Always。规则必须写具体。“重要变更需确认”这句话 AI 没法执行它不知道什么算“重要”。得写成“修改已有 API 的 URL 路径需确认”。“小心操作数据库”也不行要写“ALTER TABLE 操作需确认”。Never 规则不能只靠 AI 自觉。只在文档里写“禁止直连生产库”并不能真的拦住它。AI 不会主动检查自己的输出是否违规。Never 规则需要多层防线Spec 声明影响 AI 生成倾向但拦不住配置模板.env.example里不放真实密钥AI 就没东西可复制Pre-commit hook正则扫密钥硬编码、生产环境连接串提交时自动拦截AI IDE 配置.cursorignore阻止 Cursor 读取.env.production之类的文件越重要的 Never 规则越要推进到 CI 层做硬性检查。停在“文档里有写”这一步迟早出事。每周回头看一次。AI 是不是动不动就停下来问那 Ask First 里有些操作可以放行了。AI 有没有偷偷干不该干的事有就补 Never。项目里有没有冒出新的敏感操作加进去。项目大了Spec 怎么管小项目 Spec 少手动往上下文里丢就行。模块多了之后全塞上下文就废了AI 看着一堆和当前任务无关的约束反而更容易跑偏。按规模选策略。Spec 管理策略分层过滤 精准召回10 个模块以内分文件存储按领域拆就行specs/ ├── global/ # 全局约束 │ ├── conventions.md # 代码规范 │ └── architecture.md # 架构概览 ├── backend/ # 后端规格 │ ├── api/ │ ├── service/ │ └── persistence/ ├── frontend/ # 前端规格 └── shared/ # 共享契约 └── dto.md每次只把当前任务相关的两三个文件丢进去别贪多。10-30 个模块摘要索引手动挑文件开始累了就让 AI 先生成一份目录加关键词索引## Spec 索引 - [数据库设计](specs/db/schema.md) - 关键词: PostgreSQL, 索引优化 - [用户 API](specs/backend/api/user.md) - 关键词: REST, JWT, 鉴权 - [订单服务](specs/backend/service/order.md) - 关键词: 事务, 幂等需要细节时让 AI 主动来要不用全量灌进去。30 个模块以上RAG 向量检索手动选文件不现实了得上 RAG。Embedding 模型选 text-embedding-3-small/large向量库看规模Chroma 适合本地Pinecone 适合云端Milvus 适合企业级。Chunk 策略按语义单元切一个 Task 或一个 API 定义为一个 chunk默认控制在 512-1024 tokens 之间。Top-K 召回 3-5 条加相似度阈值 0.7。但十个模块的项目搞向量库纯属给自己找事。什么时候人工选上下文开始痛苦了什么时候再上。不分规模都管用的一条单会话单任务Session 1: 数据库设计 ├── 输入: global/conventions.md backend/db/ ├── 输出: 完成实体设计 └── 关闭会话 Session 2: API 实现 ├── 输入: Session 1 产出 backend/api/ ├── 输出: 完成 Controller └── 关闭会话上下文干净AI 就不会被前面任务的边角料带跑。这条比什么花哨的检索策略都管用。领域知识为什么这么重要AI 训练数据再多也不知道你项目里那些特定的规则你得主动告诉它。举个例子你做了一个商城项目其中有一个规则是优惠券和秒杀不能叠加。这个规则你不写进 SpecAI 很可能就把两个折扣都算上了。代码能跑测试也可能过但业务直接错了。这类知识一般可以分成几种业务规则优惠券和秒杀不能叠加同一用户每天只能领取一次奖励技术约束订单分页必须走指定联合索引深分页 100 页改用游标禁止全表扫描历史债务第三方上传接口只支持 5 MB超过就会报错所以代码里要提前校验性能基线单表查询控制在 50 ms 内关键接口超过 200 ms 要考虑降级或兜底这些东西是 AI 写代码时的边界。现在很多 Spec-Driven Development 的思路就是把 Spec 从“写给人看的文档”变成“约束 AI 生成代码的规则”。不要认为 Spec 只是前期用用后续实现、校验和维护时都需要。不过只把规则写进去还不够最好再加一段自检清单。因为 AI 很容易写完功能就结束不会主动回头确认这些隐含约束。完成自检清单任务写完之后不要让 AI 直接说一句“已完成”。至少让它按清单自己过一遍。比如完成Task-001后必须逐项确认[ ] 所有 API 错误返回都符合统一格式[ ] 数据库查询命中了指定联合索引[ ] 优惠券和秒杀的互斥逻辑已正确实现[ ] 单元测试覆盖了空值、越界、并发等边界场景[ ] 分支覆盖率branch coverage 80%[ ] 圈复杂度 10如果有哪一项没法确认不能糊弄过去要把原因写出来。AI 很容易把代码写完当成任务完成。可真实项目里功能能跑只是第一步错误格式、索引命中、边界测试、复杂度控制这些才是后面少背锅的地方。多代理协作的坑有人会问一个 AI 不够用多搞几个行不行可以但坑比你想的多。Multi-Agent 三代理协作流水线三代理协作的思路是代码、测试、审查各管一段流水线推进。代码代理接到 Task 写功能写完交给测试代理出用例跑测试通过后再交给审查代理看代码质量最后人类终审合并。有个坑必须提前说清测试代理在自己的分支上写测试但被测代码在代码代理的分支上。这两个分支是平行的测试代理要么先 merge 代码分支要么根本跑不起来。两种能跑通的模式串行同分支推荐起步。三个代理在同一个 feature 分支上按顺序 commit用 commit message 前缀区分角色。简单没有合并冲突适合大多数项目。git commit -m [code] implement user registration API git commit -m [test] add unit tests for user registration git commit -m [review] fix null check in email validation链式继承代理能力已验证后。测试代理从代码分支 checkout审查代理从测试分支 checkout最后从审查分支 merge 回主线。分支之间是继承关系而不是平行关系每个代理都能看到前一个代理的产出。多代理翻车的场景不少死锁A 等 B、B 等 A设计时确保依赖是 DAG、无限循环代理自我迭代停不下来设最大轮次 Max 3、输出格式错误JSON 解析失败加校验和重试最多 3 次。提前设好这些兜底能避开大部分问题。老实说多代理这块我自己也还在摸索目前的经验是串行同分支模式能覆盖八成场景复杂编排除非团队有人专门维护否则翻车概率不低。Spec 不是写完就扔的跑了几个项目后有几个习惯固定下来了。渐进细化。别想着一口气写出完美 Spec。先写高层大纲让 AI 把骨架跑起来再一个模块一个模块补细节。模块化组织。API、数据库、样式规范、错误码、权限规则各一个文件。每次只给 AI 当前任务用得到的上下文。持续迭代。每次 Code Review 发现问题或者 AI 又把同一个坑踩了一遍回去改 Spec。只改代码不改规范下次照样犯。这里有个高频翻车场景值得特别说一下Task-001 完成时 Spec 规定错误格式是{code: USER_NOT_FOUND, message: ...}两周后 Spec 更新加了trace_id字段但 Task-001 的代码已经没人管了。规范和实现就这么悄悄跑偏了。应对办法Spec 变更时做影响范围评估。可以在每个 Spec 文件里维护一个“依赖此文件的模块”列表Spec 更新时主动触发受影响模块的回归测试。CI 流水线里加一条判断Spec 文件有变动自动跑相关模块的测试。分享几套 Spec 模板我常用的就这三种按场景选一个就行。模板一OpenAPI 风格适合 API 开发## APIPOST /api/v1/users ### 基本信息 - **端点**/api/v1/users - **方法**POST ### 请求参数 | 字段 | 类型 | 必填 | 约束 | 示例 | | -------- | ------ | ---- | --------------------------- | ---------------- | | email | string | 是 | 邮箱格式 | userexample.com | | password | string | 是 | 8-32 字符包含大小写和数字 | - | ### 响应格式 - **201 Created**用户创建成功 json {id: uuid, email: userexample.com, created_at: ...} - **409 Conflict**邮箱已存在 json {code: EMAIL_ALREADY_EXISTS, message: Email already exists} ### 验收标准 - [ ] 密码用 bcryptcost12 - [ ] 邮箱唯一性由数据库唯一索引保证 - [ ] 分支覆盖率branch coverage 80%模板二Gherkin 风格适合 BDDFeature: 用户登录 Scenario: 使用有效凭据登录 Given 用户已注册邮箱 testexample.com 和密码 Password123 When 用户提交登录请求 Then 返回 200 状态码和 JWT token And token 有效期 24 小时 Scenario: 使用无效密码登录 Given 用户已注册邮箱 testexample.com When 用户用错误密码提交登录 Then 返回 401 And 错误信息为 Invalid credentials And 不暴露具体是邮箱还是密码错模板三Checklist 风格适合代码审查## Code Review Checklist ### 功能性 - [ ] 实现符合 Spec 描述 - [ ] 边界条件已处理空值、越界、并发 - [ ] 错误处理完善 ### 质量 - [ ] 函数长度 50 行 - [ ] 圈复杂度 10 - [ ] 无重复代码DRY ### 安全 - [ ] 无敏感信息硬编码 - [ ] 输入已验证/转义 - [ ] 权限检查已加踩过的坑说几个我自己踩过的。约束写太死了AI 连正常的灵活性都没有。比如你把 Service 层每个方法签名都定好AI 连个参数名都不敢改。Spec 定的是边界不是逐行伪代码。反过来约束写少了更常见。关键边界没定义AI 就自己猜。猜对了算运气猜错了算日常。我有一个项目AI 用了 MD5 存密码就是因为 Spec 里没写用什么加密算法。Spec 改了没同步这个最隐蔽。代码和文档慢慢就跑偏了AI 下次拿到的还是旧版规范写出来的代码自然也对不上。还有一个只写不验。Spec 写了一大堆但没接到 CI 里最后变成形式主义。写完没人检查跟没写差不多。最重要的是一定一定一定要上 Git多多小步提交懂得都懂