上一章我们把四个生成需求文档的 Skill 摆到一起比了比。有读者接着追问一个更基础的问题:PRD、设计文档、SPEC,这三个词到底有什么区别?我平时写代码,一会儿要写 PRD,一会儿又被要求补一份 SPEC,还有人让我先出个"设计文档"——它们不是一回事吗?
《AI 时代的软件工程》电子书: https://se.rpcx.io https://se.rpcx.io
不是。而且这个问题在 AI 时代变得比以前更重要了。
从前这三份文档主要写给人看,人有常识,能自动脑补缺失的部分,名字混一混也不影响干活。现在它们越来越多是写给 Agent 看的,而 Agent 只认你写下来的内容。你把"做什么"和"怎么做"糊在一份文档里,Agent 就会在你还没想清楚要什么的时候,先替你把技术方案定了;你让它照着一份只有需求、没有实现契约的文档去写代码,它就会自作主张地补上一堆你没审过的架构决策。分不清这三份文档,代价从"沟通别扭"升级成了"返工重写"。

所以这一章我们把它们一个一个拆开:PRD 回答"做什么",设计文档回答"为什么这么选、而不是别的",SPEC 回答"具体怎么建"。最后一节再把三者放到一起,看清它们的分工、层次,以及那些容易把人绕晕的术语混乱。
在本书的工具链里,这三份文档恰好对应三个 Skill——/prd、/to-design、/prd-to-spec——它们首尾相接,构成一条从想法到代码的流水线。下面就以它们为标本来讲。
一、PRD:说清"做什么"和"为什么",但绝不说"怎么做"
PRD 是 Product Requirements Document,产品需求文档。它站在用户和产品的角度,回答两个问题:我们要做什么(what),以及为什么值得做(why)。它有一条几乎是纪律性的约束:不规定实现。一份好的 PRD 描述可观察的行为和要达成的结果,而不是替工程师选架构、定技术栈。
这条纪律不是洁癖。一旦 PRD 里锁死了"用某某数据库""走某某框架",它就没法再充当一个中立的验收基准了。因为你已经把"要什么"和"怎么做"搅在了一起,测试的时候分不清到底在验需求还是在验实现。所以业界的共识很一致:把设计决策留给设计文档,PRD 只管需求本身。

这个"what 与 how 分离"的传统,最早被讲透是在 2000 年,Joel Spolsky 写的《Painless Functional Specifications》系列里。他把文档分成两种:functional spec 完全从用户视角描述产品"做什么"——有哪些屏幕、菜单、对话框,压根不管内部怎么实现;technical spec 才描述内部实现——数据结构、数据库模型、语言选型、算法。他还特意点出:设计产品时最要紧的是先把用户体验钉死,"在你决定产品要做什么之前,争论用什么编程语言毫无意义"。二十多年过去,这条"先想清楚做什么,再操心怎么做"的次序,依然是 PRD 存在的全部理由。
本书工具链里的 /prd 就是这条理念的一个具体实现。你丢给它一句功能描述,它先按复杂度提几个选择题澄清需求(简单功能问 2-3 个,复杂的跨系统功能问 6-8 个),然后生成一份固定九段结构的文档:概述、目标、用户故事、功能需求、非目标、设计考量、技术考量、成功指标、待解问题。它明确只产出 PRD,绝不动手写代码。
它有两处约束卡得特别死,正好体现了 PRD 的性格。一是用户故事按 US-001 编号,每条都要带验收标准,而每条验收标准都得过一遍自检——必须是可观察、可测试、或可验证三者之一,"运行正常""体验良好"这类空话一律打回重写。二是功能需求按 FR-N 编号,每条只描述一个行为,不许用 and 把好几件事塞进一条。这两条约束管的都是"把需求说清楚、说到可验收",没有一条在谈技术实现,而这正是 PRD 该有的样子。
PRD 是写给所有人看的需求合约。读者横跨产品、设计、开发、测试;它的作用,是让这些人先就"要解决什么问题、给谁解决、做到什么算完成"达成一致,然后才轮到工程师去操心怎么建。
二、设计文档:论证"为什么是这个方案,而不是别的"
如果说 PRD 回答"做什么",那么设计文档回答的是一个夹在中间、却最容易被跳过的问题:在能达成目标的若干条路里,我们为什么选这一条?
这类文档最成熟的范本来自 Google。在 Google 的工程文化里,工程师在动手写任何有分量的改动之前,通常要先写一份 design doc。它是一份相对非正式的文档,记录高层的实现策略和关键设计决策,重点落在做这些决策时权衡过的取舍上。写它的底层信念是:软件工程师的职责是解决问题,代码只是达成的手段;而在项目早期,一段结构化的文字往往比代码更适合用来解决问题:它更简洁、更好懂,能在比代码更高的层面上把问题和方案讲清楚。
设计文档的价值有好几层:它是规划工具,逼你在动手前把方案想到能写下来的程度,很多行不通的路子在这一步就被拦下,省下了实现之后才发现走不通的巨大浪费;它是对齐平台,团队围着这份文档提问、讨论、达成一致,形成一份能扛过人员变动的"决策存档";它还是历史记录,几年后新人接手,能读到"当初为什么这么定"的完整推理,而不用对着一堆没有上下文的代码干猜。
本书的 /to-design 就是照着这套哲学做的,而且它的文风直接取法 Go 官方的设计提案(proposal)——泛型、错误包装、loopvar、slog、try 这几篇。它产出的文档有固定骨架:Abstract(摘要)、Background(背景与动机)、Design(设计)、Rationale(理由与取舍)、Compatibility(兼容性)、Implementation(实现与过渡)。
这套骨架里,灵魂是 Rationale。/to-design 把它定为最关键的检查项:文档必须主动列出至少一个被放弃的备选方案,以及放弃它的原因——"我们没选 X,因为 Y"。这比单方面论证你选的方案更可信,也让后来人不必把同一场争论再吵一遍。围绕这个灵魂,还有几条讲究:Background 要用真实的痛点代码来讲"痛在哪",而不是堆形容词说"现状很糟";凡是破坏性变更,Compatibility 必须开门见山承认,诚实列出代价,再给出渐进迁移路径;Implementation 要用实测数据和自动化工具来支撑"可落地",而不是空喊"风险可控"。
这份 Skill 背后还藏着一句很硬的信念:一份文档值不值,看它有没有让讨论建立在同一套事实和取舍上;方案最后通没通过,不改变这一点。 一个被否决的设计文档照样是高价值产物,它记录了"这条路为什么走不通",Status 标一个 Rejected 就行。
所以设计文档和另外两者的界线很清楚:它不像 PRD 那样只谈需求、回避实现,也不像 SPEC 那样给出字段级的实现契约。它站在中间,专门处理"how 里面的 which":在多种技术路线之间做选择,并把这个选择的理由和代价永久地记下来。它尤其适合三种场合:改动有真实的取舍、变更有破坏性或难以回退、以及多个人需要在动手前对同一个方向达成共识。

现实样本(一):Rust 的 RFC
设计文档这套理念,Rust 把它做成了一条公开流程,叫 RFC。这个名字借自互联网标准圈的 "Request for Comments",字面就是"征求意见稿"。对语言、标准库、Cargo 这些有分量的改动,Rust 要求先写一份 RFC,走完流程才动手。
流程摊开看不复杂。你把 rust-lang/rfcs 仓库 fork 下来,照着 0000-template.md 写一份,开一个 PR。之后的讨论全在这个 PR 里进行:社区提问、相关团队的人下场辩论,你按反馈改稿。等讨论收敛,负责的团队宣布进入 FCP(Final Comment Period,最后评论期,大约十天),给出处置意见——合并、关闭,或者推迟。合并了,这份 RFC 拿到一个编号,算被接受。
RFC 被接受,不等于功能已经做好,这点容易误会。接受之后还会开一个 tracking issue 追实现进度,从合并到正式进标准库、稳定发布,中间可能隔好几个版本。async/await、非词法生命周期(NLL)、2018 edition,都是这么一步步磨出来的。
值得抄的是 RFC 模板怎么分段。它有九段:Summary、Motivation、Guide-level explanation、Reference-level explanation、Drawbacks、Rationale and alternatives、Prior art、Unresolved questions、Future possibilities。其中两段卡得很死:Drawbacks 逼你写清这个方案有什么坏处,Rationale and alternatives 逼你交代为什么选它、还比较过哪些别的路。另外一段 Prior art,让你去查别的语言是怎么解决同一个问题的。这几段管的,正是前面设计文档那一节说的灵魂——把缺点和被放弃的备选主动摊开,让后来人不用把同一场争论重新吵一遍。
现实样本(二):Go 的 Proposal
前面讲 /to-design 时提过,它的文风取法 Go 的设计提案。这里把 Go 这套 proposal 流程本身说清楚。
Go 的改动走 proposal 流程:在 golang/go 上开一个 issue,打上 Proposal 标签;大一点的改动,还要在 golang/proposal 仓库里配一份正经的设计文档。有一个 proposal review 小组每周碰头,过一遍待决的提案,把会议纪要贴回 issue——接受、拒绝、还是先搁着,都写下来,公开可查。
Go 这套流程的重头戏是兼容性。Go 1 有个出了名的兼容性承诺:老代码在新版本里得照样编得过、跑得对。所以任何改动的设计文档里,Compatibility 都绕不过去,得老老实实交代会不会弄坏现有代码、怎么平滑过渡。loopvar 在 Go 1.22 改循环变量语义那次,光是怎么不悄悄改坏一堆老代码,就琢磨了很久。
流程严,拒绝提案也就成了常事。try 那个简化错误处理的提案,社区讨论完被否了;好几版错误处理的草案也没能进去。但这些被否的提案没白写,它们留下了"这条路为什么走不通"的完整记录,下次有人再提类似想法,翻出来看就行。泛型能落地,靠的也是好几轮设计文档反复推倒重来,磨了好几年。
三、SPEC:给出"怎么建"的实现契约
轮到 SPEC 了。用 /prd-to-spec 里那句话说得最干脆:PRD 说做什么,SPEC 说怎么做。 它是一份写给工程师、或者写给 AI Agent 照着实现的技术契约。
注意这里的"契约"二字。设计文档是让人讨论和对齐的,允许留有余地;SPEC 是让人照着建的,必须精确到能直接施工。/prd-to-spec 甚至定了一条质量红线:文档里不许有 "TBD" 或 "TODO":要么现在就解决,要么明确挪进"待解问题"章节,绝不留一个含糊的坑给实现者。
它的结构比前两者都长、都细,一共十一节:摘要、架构、数据模型、API 设计、业务逻辑、错误处理、安全、性能、测试策略、实施计划、开放问题与风险。数据模型要给出建表 SQL 和实体定义,API 设计要列出每个端点的方法、路径、鉴权、请求响应结构乃至错误码,业务逻辑要写清核心算法和边界情形,测试策略甚至要把每一条 PRD 的用户故事和验收标准,映射到具体的测试用例上。
这份映射关系是 SPEC 的精髓所在。/prd-to-spec 的质量标准要求:每一条用户故事都有对应的 SPEC 章节,每一条功能需求都落到某个 API 端点或业务规则,每一条验收标准都至少对应一个测试用例。 所以 SPEC 是 PRD 的逐条技术兑现:把"要什么"翻译成"怎么建",一条不落。它还有一条反面纪律:不要复述 PRD。SPEC 要添加的是技术深度;把需求换个说法再抄一遍,不算数。

它有个孪生兄弟值得一提:/code-to-spec。/prd-to-spec 是正向的——从需求推导出实现契约;/code-to-spec 是逆向的——从既有代码反推出 SPEC。这在 AI 时代格外有用:面对一座没有文档的屎山,先让 Agent 逆向出一份 SPEC,你就有了一份能读、能对齐、能作为下一步改动基准的实现契约。
最后要点破一处术语的坑。本书说的 SPEC,指的是这种技术实现契约,对应的正是 Joel Spolsky 口中的 technical spec。但请注意,Joel 的 functional spec(从用户视角描述"做什么")反而更接近本书的 PRD。而在第 3 章讲规格驱动开发(Spec-Driven Development)时,那里的"规格"是个更宽的概念——它泛指人与 AI 之间达成一致的"合约",既可能是需求层面的,也可能是实现层面的。同一个 "spec/规格" 的字眼,在不同语境里指的并不是一回事。这正是下一节要处理的麻烦。
四、三者的异同比较
共同点
把 PRD、设计文档、SPEC 摆到一起,会发现它们共享同一套底层信念。
三者都坚持规划先行、不许过早动手。PRD 明说只产出需求、不写代码,设计文档要求在动手前先把方案论证清楚,SPEC 则把"决定怎么建"整个前置到编码之前。它们都是在对抗同一个诱惑——绕过思考、直接开写。
三者都追求可对齐、可留存。PRD 让产品和工程对齐"做什么",设计文档让团队对齐"走哪条路",SPEC 让所有实现者对齐"照什么契约建"。而且它们都落到一份能提交、能复审、能被后人翻出来读的文档上,而不是消散在一场口头讨论里。
三者都内建了防含糊的约束。PRD 的验收标准自检,设计文档必须列出被放弃的备选方案,SPEC 不许留 TBD——都是同一种用心:逼你把话说到可验证、可执行的程度。
差异点
共同点之下,它们的分工泾渭分明。
| 回答的问题 | |||
| 视角 | |||
| 主要读者 | |||
| 内容焦点 | |||
| 对实现的态度 | |||
| 灵魂章节 | |||
| 产物是否可选 | |||
| 本书对应 Skill | /prd | /to-design | /prd-to-spec |
有一个维度值得单独说:什么时候可以跳过。 PRD 几乎总要写——你总得先说清做什么。SPEC 是可选的,简单功能没有多少架构决策,PRD 里的技术考量就够了,硬写一份十一节的 SPEC 反而是负担。设计文档更是"按需触发"——只有当方案存在真实的岔路、或者改动有破坏性、难回退时才值得写;一个纯增量、没有争议的小功能,写设计文档就是仪式主义。三份文档是三种按需取用的工具,不用非得集齐。
关系与层次:一条从想法到代码的流水线
把三者串起来,本书工具链呈现的是一条清晰的流水线:
/prd → /to-design → /prd-to-spec → /goal → /review-it → /ship-it
│ │ │ │
需求(what) 决策与取舍 实现契约(how) 编码
(why/which)/prd 产出 PRD,是整条链的源头;/to-design 接过 PRD,在多种技术路线里做出选择并论证取舍;/prd-to-spec 再把选定的方向落成字段级的实现契约;然后 /to-issues 把 SPEC 拆成一个个带验收标准的 Issue,交给 /goal(见第 8 章 Goal Workflow)逐个实现,最后经 /review-it 审查、/ship-it 发布。
这条链的漂亮之处在于每一环的产出恰好是下一环的输入,而且抽象层级逐级下降:从"要什么"到"走哪条路"再到"怎么建",最后到代码。中间的设计文档这一环是可选的——需求清晰、方案无争议时,可以从 PRD 直接跳到 SPEC;但一旦改动有分量、有取舍,补上这一环,就能避免把一个没论证过的技术决策一路带到代码里。

术语的混乱:同一个词,指的不是一回事
最后必须坦诚一件事:这三个词在不同的组织、不同的作者笔下,用法并不统一。
Joel Spolsky 的 functional spec 指"从用户视角描述做什么",这更接近本书的 PRD;他的 technical spec 才对应本书的 SPEC。有些团队把 functional specification 和 technical requirements document 当同义词,有些团队则把功能行为和技术架构拆成两份文档。本书第 3 章讲的"规格(spec)"又是一个更宽的概念,泛指人与 AI 的合约,未必特指实现契约。
所以名字不用太较真。一份文档到底叫 PRD、SPEC 还是 design doc,各家叫法不同;要一直盯住的是那个不变的内核:它回答的是"做什么"、"为什么选这条路"、还是"怎么建"? 名字会因公司而异,这条 what → which → how 的分界线不会。你团队里管它叫什么不重要,重要的是这三个问题都被人认真回答过,而且没有被搅在同一份含糊的文档里。
我的经验
这三份文档,我不是每次都全写。到底写哪几份,我的判断标准是"这次改动的不确定性落在哪一层"。
- ●需求已经很明确,就重点写 PRD,SPEC 看情况补。 比如我把一个库从 Python 迁移到 Go,要做什么一清二楚——对齐原库的行为就是全部需求。这种时候
/prd帮我把需求梳理成一条条可验收的用户故事,就足够开工了;只有当迁移涉及重新设计接口、数据结构时,我才会再走一遍/prd-to-spec。 - ●方案有真实的岔路、或者改动会破坏现有行为,一定先写设计文档。 这是最容易被省掉、也最不该省的一环。凡是"有两三种做法都说得通"、"这次要改公共 API"、"迁移方案一旦定了很难回退"的场合,我都会先用
/to-design把备选方案和取舍摊开,尤其是把"我们没选 X,因为 Y"写清楚。花在这上面的半小时,换回的是不必在写完两千行代码后才发现方向错了。 - ●要交给 Agent 大规模并行实现时,SPEC 的价值最高。 一个人照着模糊的 PRD 还能凭常识补齐,多个 Agent 并行干活就不行了——它们需要一份字段级的共享契约,才不会各自补出互相打架的实现。这种时候
/prd-to-spec那份"每条需求都映射到 API、每条验收标准都映射到测试"的严格结构,就是把 Agent 拴在同一个方向上的缰绳。
PRD 让你和团队先就"做什么"达成一致,设计文档让你在动手前想清楚"为什么走这条路",SPEC 则把这条路铺成 Agent 能照着走的轨道。想清楚了做什么,才谈得上怎么做;论证过了为什么,才不必回头重来。
参考资料
- ●Joel Spolsky, Painless Functional Specifications – Part 2: What's a Spec?[^1](2000)—— functional spec 与 technical spec 的经典区分
- ●Joel Spolsky, Painless Functional Specifications – Part 1: Why Bother?[^2](2000)
- ●Malte Ubl, Design Docs at Google[^3] —— Google 设计文档文化的权威阐述
- ●The Rust RFC Book[^4] 与 rust-lang/rfcs[^5] —— Rust RFC 的模板与流程
- ●Go Proposal Process[^6] —— Go proposal 流程与设计文档模板
- ●PRD vs Product Spec: Key Differences & When to Use Each[^7](Productboard)
- ●Decoding the Dichotomy: PRD vs TRD[^8](Medium)
- ●Product requirements document[^9](Wikipedia)
- ●本书第 3 章《规格驱动开发:人类与 AI 的合约》、第 8 章《Goal Workflow:目标驱动的研发闭环》、第 30 章《四个流行的需求规划 Skill 的功能对比》 ---
- ●[^1]: Painless Functional Specifications – Part 2: What's a Spec? https://www.joelonsoftware.com/2000/10/03/painless-functional-specifications-part-2-whats-a-spec/ https://www.joelonsoftware.com/2000/10/03/painless-functional-specifications-part-2-whats-a-spec/
- ●[^2]: Painless Functional Specifications – Part 1: Why Bother? https://www.joelonsoftware.com/2000/10/02/painless-functional-specifications-part-1-why-bother/ https://www.joelonsoftware.com/2000/10/02/painless-functional-specifications-part-1-why-bother/
- ●[^3]: Design Docs at Google https://www.industrialempathy.com/posts/design-docs-at-google/ https://www.industrialempathy.com/posts/design-docs-at-google/
- ●[^4]: The Rust RFC Book https://rust-lang.github.io/rfcs/ https://rust-lang.github.io/rfcs/
- ●[^5]: rust-lang/rfcs https://github.com/rust-lang/rfcs https://github.com/rust-lang/rfcs
- ●[^6]: Go Proposal Process https://github.com/golang/proposal https://github.com/golang/proposal
- ●[^7]: PRD vs Product Spec: Key Differences & When to Use Each https://www.productboard.com/glossary/prd-vs-product-spec/ https://www.productboard.com/glossary/prd-vs-product-spec/
- ●[^8]: Decoding the Dichotomy: PRD vs TRD https://medium.com/@kokoproduct/decoding-the-dichotomy-prd-vs-trd-67463a29aa84 https://medium.com/@kokoproduct/decoding-the-dichotomy-prd-vs-trd-67463a29aa84
- ●[^9]: Product requirements document https://en.wikipedia.org/wiki/Product_requirements_document https://en.wikipedia.org/wiki/Product_requirements_document
夜雨聆风