编写良好的规格说明
你很少会从空白页开始编写规格说明。你先用通俗语言描述变更,/opsx:propose 起草需求和场景,然后由你把它们打磨好。本页关注最后这一步——什么样的规格说明才算“好”,以及如何引导 AI 写出这样的内容。
本文与审查变更相辅相成:审查是发现草稿中的薄弱之处,编写则是了解优质规格说明由什么构成。
规格说明描述行为,而非代码
Section titled “规格说明描述行为,而非代码”规格说明应描述系统做什么,并且任何人都能检查,而不是描述系统如何构建。它由需求(对行为的陈述)和场景(证明需求成立的具体示例)组成。
### Requirement: Session TimeoutThe system SHALL expire a session after 30 minutes of inactivity.
#### Scenario: Idle timeout- GIVEN an authenticated session- WHEN 30 minutes pass with no activity- THEN the session is invalidated and the user must re-authenticate将实现方式——队列、库、表结构——放在 design.md 或代码中。如果把行为和实现混在同一条需求里,这条需求就无法测试,并且代码一旦变化,它很快就会过时。
优质需求的特征
Section titled “优质需求的特征”一条优质需求只描述一种行为,并且表述清楚到可以交给别人测试。
-
一句话,一个
SHALL/MUST。 如果一条需求包含三项“还要”内容,那实际是三条需求。应将它们拆开。 -
可观察。 不看代码的人也应该能够判断需求是否满足。“上传内容超过 10 MB 时,系统 SHALL 显示错误横幅”是可观察的;“系统 SHALL 妥善处理大型上传”则不是。
-
严格程度恰当。 OpenSpec 使用 RFC 2119 关键词,各关键词含义不同:
关键词 含义 MUST/SHALL强制要求,不容协商。 SHOULD强烈建议,但有理由时允许例外。 MAY确实可选。 默认使用
MUST/SHALL。只有在确实表示“除非有充分理由不这样做”时才使用SHOULD。
检验一条需求的方法是:一个从未看过代码的测试人员能否判断它是否通过? 如果不能,就需要把需求写得更明确。
优质场景的特征
Section titled “优质场景的特征”场景能体现需求的价值。每个场景都是一个具体的 GIVEN / WHEN / THEN,并且可以转化为自动化测试。
- 场景要验证对应需求。 只换一种说法重述需求的场景毫无测试价值。应描述一种具体情形及其具体结果。
- 覆盖重要情形,而不只是正常流程。 成功登录很容易想到。空输入、过期令牌、再次点击、出错情形——这些才是 bug 常见之处,也是场景最有价值的地方。
- 在标题中说明所测情形。 “Scenario: Rejects an expired token”能让审查者一眼看出测试覆盖范围;“Scenario: Test 2”则不能。
一个实用习惯:批准之前,先问自己最不希望看到哪种情形出错?——并确保有一个场景明确描述了它。
选择正确的差异类型
Section titled “选择正确的差异类型”变更通过三种章节类型描述对规格说明的修改。正确选择可确保归档后的规格说明如实反映实际情况:
## ADDED Requirements——新增之前不存在的行为。## MODIFIED Requirements——修改已有行为。应写出完整的新版本;简要说明变化内容有助于审查者理解。## REMOVED Requirements——删除即将废止的行为,并说明原因。
归档时,ADDED 内容会追加到主规格说明中,MODIFIED 内容会替换旧版本,REMOVED 内容则会从主规格说明中移除。删除某项能力的最后一条需求,就表示要废弃该能力:归档会删除 openspec/specs/<capability>/spec.md,而不是留下空文件。因为这是归档过程中唯一会删除文件的步骤,所以必须明确请求:在变更的 .openspec.yaml 中添加 retire_capabilities: true,并保留该文件已有的 schema: 配置。未设置此项时,归档会中止并说明原因。
废弃操作会删除整个文件;因此,如果规格说明中除了标题、## Purpose 和需求块之外还包含其他内容(例如 ## Notes 章节或需求下方的注释),操作也会被拒绝。中止消息会指出这些行;你可以将其移至 ## Purpose 或某项需求中,也可以手动删除规格说明。如果规格说明位于调用者当前检出的仓库中,归档输出还会说明如何使用 git checkout 恢复已提交的文件;如果是所选存储库,则会提供针对该检出范围的恢复说明。
如果把一项真实变更标为 ADDED,就会留下两条互相竞争的需求;如果把新行为描述为 MODIFIED,则没有旧内容可供替换。拿不准时,先打开当前规格说明,确认该需求是否已经存在。
还有一种章节值得了解。如果差异创建了一个此前不存在的能力,应使用 ## Purpose 开头,并用一两句话说明该能力的用途。归档时,这段内容会成为新建主规格说明的 Purpose;如果省略,则会留下 TBD 占位符,需要手动补充。现有规格说明已经有 Purpose,因此其中差异文件写的 Purpose 会被忽略——如需修改它,请直接编辑 openspec/specs/<capability-path>/spec.md。这里的 <capability-path> 是相对于 specs/ 的目录,例如扁平项目中的 user-auth,或按领域组织的项目中的 identity/user-auth。
合理控制变更规模
Section titled “合理控制变更规模”最常见的编写错误并不是需求措辞不佳,而是试图在一个变更中塞进三项变更。
优质变更只有一个意图,可以用一句话说清楚。 “添加深色模式切换开关。”“对登录端点限流。”“将会话存储从 cookie 迁出。”如果描述变更时需要反复说“还要……”,就说明应该拆分。
以下迹象表明变更过大:
- 提案中的范围像一份互不相关功能的清单。
- 审查要花一个下午,因此没人会认真审查。
- 两个人无法同时处理而不相互冲突。
- 一半任务可以单独发布。
更小的变更更易审查、更容易在一次专注的工作时段内完成,也更便于在六个月后仅剩归档时理解。你可以并行执行多个变更——参阅编辑与迭代和工作流。
反过来也一样:修复一处拼写错误,不需要列三项需求并撰写设计文档。流程的复杂程度应与风险相匹配。
如何引导 AI 写出优质草稿
Section titled “如何引导 AI 写出优质草稿”/opsx:propose 会起草初稿,因此产出的质量取决于你提供的信息。你不必手动编写需求,但需要为 AI 指明正确方向:
- 说明意图和边界。 “添加深色模式切换开关,首次加载时遵循操作系统设置——不要修改现有主题 API。” 范围外内容与范围内内容同样重要。
- 指出你关心的情形。 “确保包含一个用户已手动选择主题的场景。” AI 会重点覆盖你指出的内容。
- 然后进行编辑。 规格说明是纯 Markdown。你可以明确含糊的
SHALL、删除毫无测试价值的场景、补上遗漏的情况,也可以请 AI 帮忙:“超时需求太含糊了,请明确为 30 分钟。”
起草、完善、重复。经过几轮这样的迭代,你就能得到一份值得信赖的规格说明——这正是整个流程的目标。
快速检查清单
Section titled “快速检查清单”- 每条需求都是一种可观察的行为,并包含
SHALL/MUST。 - 需求中没有混入实现细节。
- 每条需求至少有一个真正验证其行为的场景。
- 重要的边界和错误情形都有对应场景,而不仅是正常流程。
- 根据当前规格说明,正确使用 ADDED / MODIFIED / REMOVED 差异类型。
- 整个变更只有一个意图,并且可以用一句话描述。
接下来读什么
Section titled “接下来读什么”HagiCode
HagiCode 是一套智能体编码工作台:结构化工作流、多 Agent 并行执行与 Hero Dungeon 视图,把想法变成真正交付的软件。
让想法更快变成好用的软件,让智能编码更聪明、更高效,也更有趣。

- Smart结构化工作流将意图转化为从想法到交付的可执行路径。
- Efficient多 Agent 工作流让调研、实现与审阅并行推进。
- FunHero Dungeon 让长时间编码协作更直观、更有参与感。
生态站点
快速链接
社区
© 2026 HagiCode