Spec-Kit 教训:先打草稿再写 spec,先定范围再写代码
第一次用 spec-kit 时,我塞进去一段含糊的需求就点了运行。出来的 spec 技术上成立——但 practically 没用:太宽、太野心、和编码代理一次会话里能执行的东西脱节。
问题不在工具,在我的输入。
在多个项目里跑了几十次 spec-kit 之后,我形成了一套能稳定出更好结果的流程。核心想法有点反直觉:最重要的工作发生在碰 spec-kit 之前,以及代理写完代码之后。
每个阶段先用聊天机器人打草稿
Spec-kit 是结构化流水线:specify → plan → implement,每阶段吃上一阶段的输出。初始 spec 要是模糊的,模糊会一路放大。
解决办法很简单:在拿进 spec-kit 之前,先用 ChatGPT 这类通用聊天机器人把每一阶段打草稿。描述你想做什么,让机器人问澄清问题,把描述磨到紧——具体需求、明确约束、显式 non-goals。
做这个站时,我第一反应是把所有东西塞进一次 spec-kit:「我要个人站:首页、about、writing 区、projects 区、MDX 内容管道 + Zod 校验 frontmatter、build 时拉 GitHub 贡献、联系表单用 Cloudflare Worker、静态导出、Cloudflare Pages 部署。」我就把这句喂给了 /speckit.specify。
要是先和 ChatGPT 聊一轮,它会问我省略掉的问题:writing 和 project 的 content schema 分别长什么样?frontmatter 形状一样吗?GitHub 数据怎么刷新——定时 rebuild 还是手动?联系表单要不要限流、honeypot?服务端和客户端各做哪些校验?静态导出意味着运行时没有 server components——data fetching 策略里有没有考虑?
这些问题会逼我在 spec 存在之前做决定,而不是实现到一半、代理已经做到第八个任务在猜的时候才发现。
规划阶段也一样。Spec-kit 生成 spec 后,把 spec 拿回聊天机器人问:「这个范围一次实现会话现实吗?缺什么?该砍什么?」再把精简版喂给 /speckit.plan。
把聊天机器人当思考伙伴,spec-kit 当执行框架。各司其职,别混在一起。
永远别把「一次发不出去」的功能当成一个 spec
这是我代价最大的错误。我把整站——首页、about、writing、projects、MDX 管道、GitHub 集成、联系表单、部署——当成一次 spec-kit 跑。Spec-kit 老老实实出了计划,二十多个任务。到第十二个任务时,代理的上下文已经陈旧、前面的假设漂移、代码库半成品很难调。内容解析工具和代理六个任务前定的 frontmatter schema 对不上;联系表单的 server action 用法和静态导出的决定矛盾;组件引用的 props 已经不存在了。
能编译。没有东西真正一起工作。
现在的原则:如果一个功能没法在一次专注会话里实现、测试、验证,就是太大了。在写 spec 之前就拆——不是之后。
这里又轮到聊天机器人发挥作用。把你的功能想法丢给它问:「把它拆成尽可能小、且每一步都让代码库处于可工作状态的增量。」
好的增量要:
- 模块化:每块边界清晰。一个读 MDX、用 Zod schema 校验 frontmatter 的内容解析工具是模块化的;「建一个个人网站」不是。
- 可逆:增量不行可以撤掉而不引发连锁破坏。加一个
/writing索引页可逆;在建到一半时重做整个内容模型以支持新页面类型不可逆。 - 递进:每块建立在前一块之上并有可见进展。先脚手架 + 能跑的首页,再内容 schema 和解析层,再 writing 区,再 projects 区。每一步都能部署、能验证。
要是重做这个站,我会拆成多次 spec-kit:(1) Next.js 脚手架 + 静态导出 + layout + 能跑的首页;(2) MDX 内容目录结构 + writing/project 的 Zod schema;(3) 校验并转换 MDX 为类型化数据的解析工具;(4) 消费解析内容的 writing 索引与详情页;(5) projects 索引与详情页及各自 schema/layout;(6) about 页与结构化区块;(7) build 时拉 GitHub 贡献并渲染成静态产物;(8) 联系表单 + Cloudflare Worker edge 函数;(9) Cloudflare Pages 部署流水线。九次聚焦的 run,而不是一次大 spec;每次都能让站处于可部署状态,每次小到代理不会跟丢自己的决定。
用代理做验证,而不只是写代码
多数人跳过的是这一步:代理实现完一批任务就去做下一个功能。但编码代理会漂移,会做听起来合理却 subtly 违反你原始需求或项目约定的决定。
我用 Cursor 反复用两件事验证代码库:需求(实现是否符合 spec?)和宪法(代码是否符合项目规则和模式?)。
实践中就是在每个增量之间向代理提明确的验证问题:「读内容解析层的 spec,再读实现。Zod schema 和 writing 索引页期望的一致吗?有没有 frontmatter 里的字段没人用?有没有组件引用的类型在更早的增量里定义得不一样?」这样能早点发现漂移,而不是变成架构债。
宪法检查类似。每个项目都有约定——命名、文件结构、错误处理风格、组件模式。我把这些写在规则文件里供代理参考。每次实现一轮后问:「按项目规则审查本轮改动的文件,标出不符合的。」
这个循环——写 → 对照 spec 查 → 对照宪法查 → 修正 → 继续——才把单次高产会话变成稳定可靠的流程。
叠加效应
这些做法单独看都不革命:先打草稿再 spec、范围小、持续验证。但合在一起,就把 spec-kit 从「spec 生成器」变成可靠的工程流程。
代理的能力足够实现你描述的几乎任何东西。瓶颈从来不是能力,而是「我让他们建的东西质量如何」以及「我有没有按自己真正在乎的标准去检查他们的产出」。
输入精确,输出才精确。这是 spec-kit 一直在教我的事。