你让 Agent 实现一个功能:
给订单列表增加批量导出。
几分钟后,按钮、接口调用和下载逻辑都有了。
但继续问,问题就开始出现:
• 没有选择订单时能不能点击? • 用户没有导出权限怎么办? • 一次选择几万条订单怎么办? • 用户连续点击两次会不会创建两个任务? • 网络超时后能不能重试? • 页面刷新后,导出任务还在不在? • 失败时应该回到什么状态?
Agent 不是不会写代码,而是在替你补全那些没有写出来的需求。
这正是 Spec Coding 想解决的问题。
Spec Coding 到底是什么?
先把术语边界说清楚。
“Spec Coding”目前更像一个工程实践中的称呼,并不是一个统一的编程语言、协议或行业标准。相关官方资料更常使用这些词:
• Spec-Driven Development; • Specs; • Intent-driven development; • Structured requirements; • Agent workflow。
GitHub 的 Spec Kit 将这种方式定义为一种以规格驱动 AI coding agent 的开发流程。[1] 官方文档给出的默认链路是:
Spec -> Plan -> Tasks -> Implement当前流程还增加了 Converge,用于检查实现是否重新符合规格、计划和任务。[2]
Kiro 的 Specs 功能也采用了类似思路:用结构化需求、技术设计和实施计划拆解复杂功能,并持续跟踪实施过程。[4][5]
所以,本文所说的 Spec Coding,可以先理解为:
先把模糊意图整理成可评审、可执行、可验证的规格,再让 Agent 按规格推进实现。
它不是“把 Prompt 写长一点”,也不是“再多维护一份文档”。
真正的变化是:规格开始成为开发过程中的中间控制面。
为什么直接让 Agent 写代码容易失控?
需求歧义会被模型自行补全
“支持导出”可能有很多合理解释:
• 导出当前页,还是全部筛选结果? • 导出 CSV,还是 Excel? • 空数据时显示什么? • 权限由前端判断,还是服务端判断? • 失败后允许重试几次? • 相同查询条件是否复用已有任务?
人类开发者通常会通过产品讨论、历史代码和团队约定补充这些信息。
Agent 没有这些隐含上下文时,只能根据概率选择一种实现。
它可能给出一份看起来很完整的代码,但这份代码解决的是“它理解的需求”,不一定是团队真正要的需求。
聊天上下文会逐渐失效
一次短对话里,Agent 还能记住你说过的约束。
但当任务变成:
• 阅读多个模块; • 调整接口; • 修改状态管理; • 增加测试; • 修复构建错误; • 再回头补边界场景;
原始需求很容易被埋在几十轮对话里。
如果需求、技术决策和任务状态没有被写成稳定文件,后续 Agent 只能不断重新猜测。
代码会和需求逐渐漂移
开发过程中经常会出现这种情况:
1. 最初需求说需要失败重试; 2. 技术计划里决定使用异步任务; 3. 实现阶段先做了同步下载; 4. 测试只覆盖了成功路径; 5. 最后大家以为功能已经完成。
代码可以通过构建,甚至可以通过部分测试,但它已经偏离了最初的目标。
GitHub 对 Spec-Driven Development 的描述,核心就是反转“代码是唯一真相”的习惯:规格、计划和实现之间要保持持续关联。[3]
一套完整的 Spec Coding 工作流
第一步:定义项目原则
GitHub Spec Kit 将项目原则作为一个基础阶段,用来定义后续开发必须遵守的规则。[1]
这些规则可以包括:
• TypeScript 必须开启严格模式; • 新功能必须有自动化测试; • 权限只能由服务端决定; • 组件必须支持键盘操作; • 不引入没有维护状态的依赖; • 页面错误不能导致整条路由不可恢复。
这一步不是为了写一篇宏大的工程宣言,而是为了告诉 Agent:
这个项目不仅要实现功能,还要按什么标准实现功能。
如果团队已经有 CONTRIBUTING.md、架构规范或代码检查规则,也可以把它们整理成 Agent 能稳定读取的项目原则。
第二步:Specify,先写需求,不急着选技术
以批量导出为例,Spec 可以这样写:
# 批量导出订单
## 用户故事
作为运营人员,我希望选择多个订单并导出,
以便进行线下对账。
## 验收标准
- 未选择订单时,导出按钮不可提交
- 请求提交或处理中,不允许重复创建任务
- 导出失败后可以重新发起
- 同一批订单的重复点击不能造成重复请求
## 非目标
- 本次不定义文件格式
- 本次不实现服务端权限系统
- 本次不实现后台任务 Worker这里有两个关键点。
第一,先描述用户行为和可观察结果,不要一开始就写:
使用某个 Hook,创建一个名为 useExport 的文件。第二,明确写出非目标。
不属于本次需求的内容也应该被记录下来,否则 Agent 很容易顺手扩大任务范围。
第三步:Plan,把需求翻译成技术决策
Plan 阶段回答的是:
• 数据流怎么走? • 状态如何组织? • 哪些逻辑属于前端? • 哪些逻辑必须交给服务端? • 如何处理重复请求? • 失败后如何恢复? • 哪些方案明确不采用?
例如:
# 实施计划
- 使用 idle、submitting、processing、success、failed 表示导出生命周期
- 在调用 API 前拒绝空选择和进行中的重复提交
- 为每次请求保留 requestKey
- 服务端自行决定幂等存储和权限校验
- 前端状态机不负责生成文件,也不替代服务端授权Plan 的作用不是提前把所有代码写完,而是让技术选择有理由可查。
如果后续有人问“为什么没有在前端直接生成文件”,可以回到计划中找到边界,而不是重新翻聊天记录。
第四步:Tasks,把计划拆成可执行任务
好的任务应该能被独立完成和验证。
- T-1:实现空选择校验
- T-2:实现请求提交中的重复提交保护
- T-3:实现失败状态和重新发起
- T-4:为每条验收标准增加测试不推荐写成:
- 完成批量导出功能后者看似简洁,实际上无法判断进度,也无法知道漏掉了哪些分支。
一个任务至少要能回答三个问题:
1. 要修改什么? 2. 完成后如何验证? 3. 它对应哪条需求?
第五步:Implement,让 Agent 执行任务
到了实现阶段,Agent 不再是“自由发挥的代码生成器”,而是按照规格和任务推进的执行者。
可以给出这样的约束:
请读取 specs/order-export/spec.md、plan.md 和 tasks.md。
只实现当前任务 T-1。
完成后运行对应测试,并报告:
1. 修改了哪些文件;
2. 哪些验收标准已经覆盖;
3. 哪些假设仍未确认;
4. 哪些任务没有执行。这会比一句“帮我完成批量导出”更容易复核。
当然,Spec 不会让 Agent 自动变得正确。它只是把任务边界、验收标准和未确认事项显式化。
前端最应该关注的:状态和验收标准能否对上
规格不是只给产品经理看的。
对于前端工程师,最有价值的地方往往是把用户行为翻译成状态机。
批量导出可以抽象成:
idle
-> submitting
-> processing
-> success
processing
-> failed
-> retrying
-> processing前端状态机至少应该覆盖:
• 空选择; • 提交中; • 处理中; • 成功; • 失败; • 失败重试; • 重复操作。
对应的 TypeScript 类型可以很简单:
type ExportState =
| "idle"
| "submitting"
| "processing"
| "success"
| "failed";
type ExportSession = {
state: ExportState;
selectedOrderIds: readonly string[];
requestKey?: string;
error?: string;
};然后把非法转换挡在状态层:
function startExport(
session: ExportSession,
requestKey: string,
) {
if (session.selectedOrderIds.length === 0) {
return {
accepted: false,
message: "select-at-least-one-order",
session,
};
}
if (
session.state === "submitting" ||
session.state === "processing"
) {
return {
accepted: false,
message: "request-in-flight",
session,
};
}
return {
accepted: true,
session: {
state: "submitting",
selectedOrderIds: session.selectedOrderIds,
requestKey,
},
};
}这里的价值不是代码有多复杂,而是每个分支都可以回到 Spec 中的验收标准。
如果需求里写了“处理中不可重复提交”,测试就应该能直接证明这一点。
Converge:最容易被漏掉的最后一步
很多 AI 编程流程到“代码生成成功”就结束了。
Spec Coding 需要再问一遍:
现在的代码,真的符合原来的 Spec 吗?
收敛检查至少要看五件事。
1. 每条验收标准都有实现吗?
如果 Spec 中有 AC-1、AC-2、AC-3,就应该能找到对应任务和测试。
2. 每个任务都能回到需求吗?
如果出现一个任务没有对应验收标准,可能是:
• 需求漏写; • 任务范围扩大; • Agent 自行增加了功能; • 原需求已经变化但没有更新 Spec。
3. 失败路径真的存在吗?
很多实现只覆盖:
点击 -> 成功但真实功能还需要考虑:
点击 -> 网络失败
点击 -> 超时
点击 -> 重复点击
点击 -> 页面刷新
点击 -> 权限失效4. 技术决策有没有被偷偷改掉?
Plan 里写了“服务端负责权限校验”,但实现却在前端根据一个隐藏按钮决定是否允许请求,这就是边界漂移。
5. 测试验证的是需求,还是只是代码分支?
测试覆盖率高,不代表需求覆盖完整。
真正应该检查的是:
验收标准 -> 任务 -> 实现 -> 测试这是一条追踪链,而不是四份互相独立的文件。
一个可运行的 Spec Coding 实验
我把上面的批量导出场景做成了独立 TypeScript 实验:
https://github.com/xiaogao007/running-404-code-labs/tree/main/spec-coding-workflow
实验目录包含:
spec-coding-workflow/
├── specs/order-export/
│ ├── spec.md
│ ├── plan.md
│ └── tasks.md
├── src/export-workflow.ts
├── test/export-workflow.test.ts
└── README.md它验证了五条路径:
• 空选择不能启动导出; • 请求处理中不能重复提交; • 失败后可以使用新的请求标识重试; • 成功状态是终态; • 验收标准与任务之间缺失链接时可以被发现。
复现命令:
git clone https://github.com/xiaogao007/running-404-code-labs.git
cd running-404-code-labs/spec-coding-workflow
npm ci
npm run verify代码状态:已运行。验证环境为 Node.js 25.8.2、npm 11.9.0,类型检查通过,5 个测试全部通过。
这个实验有意没有调用 AI 模型,也没有安装 GitHub Spec Kit 或 Kiro。它验证的是更基础、也更通用的一层:
规格里的状态和验收标准,能不能真正变成可执行的代码约束?
Spec 应该写到什么粒度?
写得太粗
实现一个好用的导出功能。Agent 需要自行猜测,团队也无法验收。
写得太细
在第 132 行新增 handleExportClick,
调用某个 Hook 的第三个参数。这会过早锁死实现,限制设计空间,也可能让规格很快过时。
更合适的粒度
Spec 优先描述:
• 用户是谁; • 想完成什么; • 什么情况算成功; • 哪些情况必须失败; • 哪些行为不属于本次范围; • 有哪些安全、性能和一致性约束。
Plan 再描述:
• 采用什么技术; • 如何拆模块; • 数据如何流动; • 为什么不选其他方案。
Implement 阶段才进入具体文件、函数和代码结构。
可以用一句话概括:
Spec 写“要观察到什么”,Plan 写“准备怎么实现”,代码写“具体怎么落地”。
Spec Coding 适合所有任务吗?
不适合。
适合使用
• 跨多个模块的业务功能; • 有权限、数据一致性或回滚要求的功能; • Agent 需要连续工作较长时间的任务; • 多人协作和代码评审; • 需要长期维护的系统; • 风险较高的 Bug 修复。
Kiro 也将 Bugfix Specs 单独作为一种规格类型,用于系统诊断、修复和防止回归。[6]
不必强行使用
• 改一个文案; • 调整一个 CSS 间距; • 一次性脚本; • 已有明确测试的小范围重构; • 只想快速验证一个想法的最初几分钟。
如果一个改动只需要一分钟,却要先写四个文件,流程就已经失去比例感了。
它的真实代价
Spec Coding 不是免费质量。
它会带来额外工作:
• 前期要花时间澄清需求; • 规格也需要维护; • 计划可能因为技术限制而变化; • Agent 仍可能误解自然语言; • 代码审查和测试仍然不可省略。
所以,不要把它宣传成“有了 Spec,AI 就不会写错代码”。
更准确的说法是:
Spec Coding 把一部分隐含猜测提前暴露出来,把一部分口头约定变成可以评审和验证的资产。
一份 Spec 是否合格,可以这样检查
提交给 Agent 之前,至少问自己:
• 是否明确了用户和目标? • 是否写出了非目标? • 是否有可验证的验收标准? • 是否覆盖空数据、失败、权限和重复操作? • 是否区分了需求和技术方案? • 是否记录了关键技术取舍? • 是否能拆成独立任务? • 是否每个任务都能映射回某条需求? • 是否每条需求都有测试或验证方式? • 是否定义了完成和废弃条件?
再做一个很实用的测试:
如果把这份 Spec 交给另一个 Agent,它能否在不依赖作者记忆的情况下完成大部分任务?
如果不能,说明规格还依赖隐含上下文。
结尾:代码没有消失,只是责任上移了
Spec Coding 的核心,不是让开发者少写几行代码,也不是把所有开发流程变成模板。
它改变的是开发者的工作重心:
• 从直接写代码,转向定义边界; • 从一次性 Prompt,转向维护稳定上下文; • 从只检查实现,转向检查需求到代码的链路; • 从“代码能跑”,转向“代码是否符合可验证的意图”。
AI 可以帮助我们把代码写得更快,但它不会自动知道什么应该做、什么不应该做,也不会自动承担需求漂移带来的责任。
真正值得沉淀的,不是某一次对话生成了多少代码,而是团队能否留下这样一条清晰链路:
意图
-> 规格
-> 计划
-> 任务
-> 实现
-> 测试
-> 收敛Spec Coding 不是多写一份文档。
它是在 Agent 参与开发之后,给“为什么这样做、做到什么算完成、出了偏差如何发现”建立一套可追踪的回答。
参考资料:
[1] GitHub, Spec Kit README
[2] GitHub, Spec Kit Documentation
[3] GitHub, Specification-Driven Development
[4] Kiro, Specs Documentation
https://kiro.dev/docs/specs/
[5] Kiro, Feature Specs
[6] Kiro, Bugfix Specs
[7] rstacruz, Spec mode prompt
互动问题:
你在使用 AI 编程工具时,最容易遗漏的是需求边界、异常路径,还是验收标准?
✨ END
我是404星球的猫
拒绝当工具人,做 AI 时代的代码架构师。
漫游继续,我们下篇见~
👇 关注我,手握这份 AI 时代的全栈漫游指南
夜雨聆风