软件规格文档中的 10 个常见错误
我评审过的每一个失败项目都有 spec。问题是 spec 里没写真正重要的那些事。下面这十个错误在我评审过的 spec 里反复出现——我自己写的也不例外。它们都不难修,但在截止日期的压力下,每一个都容易被漏掉。
1. 只描述功能,不描述决策
功能描述告诉读者系统会做什么。而决策告诉读者的是:系统会做这个,而不是别的什么。"用户可以通过邮箱重置密码"描述的是功能。"用户通过邮箱重置密码——不用短信、不用安全问题、也不用 magic link——因为邮箱是我们对未认证用户唯一已验证的联系渠道"描述的是决策。
如果你的 spec 只是功能的一遍走马观花,没有任何"因为",那你写的就不是 spec,而是宣传册。决策是歧义真正藏身的地方,要把它们摆到台面上来。
2. 本该写数字的地方写了形容词
"快"、"可靠"、"合理"、"性能可接受"——这些词在不同读者眼里永远意味着不同的东西。只要 spec 里描述系统行为的形容词还没被换成一个带单位的数字,这份 spec 就不该发出去。
- "响应快" → "10KB 负载下 p95 低于 300ms,在负载均衡器处测量"
- "合理重试" → "重试 3 次,退避间隔为 1s、2s、4s"
- "可靠投递" → "60s 内投递成功率 99.9%,以 30 天窗口衡量"
3. 缺失非目标
我见过的每一个延期交付的项目,要么根本没有非目标,要么只有几句不承诺任何东西的套话。非目标能在实现阶段挡住范围蔓延。如果 spec 里只有目标那一节,却没有非目标那一节,那就是最先该补的东西。
每个中等规模的功能,目标是明确列出 3–6 条非目标。每一条都附带一个理由——推迟、否决,或者不在范围内。
4. 不是标准的验收标准
"用户应该能够看到自己的订单历史"是一个功能诉求,不是 AC。QA 没法测"应该能够"。他们需要的是:输入、触发条件、以及具体可观测的输出。
给自己做个测试:把"应该"这个词去掉,这句话还通顺吗?如果通顺,那它多半是一条可测试的主张。如果不通顺,说明你还有活要干。
5. 跳过了失败路径
每一条走得通的 happy path AC,都至少对应三条需要写下来的失败路径:校验失败、授权失败、下游依赖失败。只覆盖 happy path 的 spec,就是你错误处理最后沦为一串生产环境才发现的 500 的根本原因。
对每一条 AC,问一问:这一步失败时用户会看到什么?如果答案是"我不知道",那这份 spec 就还没写完。
6. 范围归属含糊
"团队会决定"不是归属——那是一个被推迟的争论。每一个范围问题都需要一个具名的人,这个人能说 yes 或 no,而不是一个"大家讨论一下"的小组。当功能在实现中途膨胀时(它一定会),你需要知道谁有权力说"不,这个里程碑不做"。
一个人。写在 spec 里。哪怕这个人就是你自己。
7. 把实现细节伪装成需求
"系统使用 Redis 存储会话"是实现细节。除非 Redis 本身是用户可观测的行为(并不是),否则它不该出现在 spec 里。spec 应该描述可观测的行为:会话在刷新后仍然保持、会话在 30 分钟不活动后过期、会话在服务器重启后仍然存在。
至于底层是 Redis、Postgres 还是内存缓存,那是工程团队的判断。把它写进 spec 只会约束团队,却没有给评审者增加任何清晰度。
8. 把发布计划当作事后补充
这种情况我几乎每周都能见到。一份五页的 spec,AC 写得很详尽,然后底下就一行字:"Rollout:feature flag"。凌晨两点出事的时候,这一行字对任何人都没什么用。
一个发布章节至少要有:具名阶段和明确的门禁、数值化的止损阈值、以及按类型描述的回滚机制(flag 翻转 vs. 代码回退 vs. 迁移回滚)。每一项都只要几分钟就能写完,却能在事故时替你省下几小时。
9. 没有并发方案
如果两个用户可能同时操作同一条记录,那无论 spec 承不承认,你都已经有了并发问题。我评审过的大部分 spec 完全跳过了并发。结果就是实现阶段悄悄地挑了一种策略——通常是 last-write-wins 而且不给用户任何提示——然后第一个真正的竞态条件以客户投诉的形式浮出水面。
把并发规则明确写出来。"Last-write-wins,不显示冲突提示"是一个合法的选择。"乐观并发,冲突时返回 409"也是。唯一不合法的,是把这件事留给实现去凭空发明。
10. 当过去不存在的 spec
新功能的 spec 常常把功能描述得好像是在真空里上线。实际上,大多数功能都要和已有流程、已有数据、已有的用户预期打交道。spec 需要一个章节——哪怕只是一段——说清楚这次变更相对于现状改了什么。
- 这个功能依赖哪些既有行为?
- 它会读写哪些既有数据?
- 这次变更可能影响哪些既有流程?
- 是否有任何既有行为被改动了,哪怕只是细微的改动?
那些"只是加一点东西"的功能,仍然要回答这些问题。"只是"两个字,往往就是生产环境意外最爱藏身的地方。
这些错误的共同点
注意这个规律:每一个错误都是 spec 把某个决策留给了以后。不是"决策做错了"——而是"决策没做"。spec-first 这套纪律不是要你一次就把事情想对,而是不要把决策推给实现阶段,那里改起来代价最贵。
我评审自己的 spec 时,就是扫这十件事。不是因为别的错误不会犯,而是因为这十个是我自己反复在犯的。你自己的那一份清单可能不一样。找到属于你自己的那十个,才是真正的功夫。
评审时看什么
这篇文章适合用在清理一份看似完整但藏着缺口的规格时。别从“原则”聊起,直接拿一条真实改动来对照,看看规格里还缺什么。
- 找到只描述功能、不说明取舍的句子。
- 把“合理、快速、优雅”改成状态、数字或示例。
- 给每条 happy path 补验证失败、权限失败和依赖失败。
- 写清谁有权拒绝中途加需求。
这篇文章适合直接当审稿清单用。扫完后应该得到几处具体修改,而不是一句“文档还需要更清楚”。
例子:“管理员可以管理用户”至少藏着四个决策:哪些角色算管理员、哪些用户状态允许编辑、审计日志写什么、目标用户被锁定时怎么处理。评审时先拆这些词。
落地例子
“用户可以邀请队友”这句话要拆开看:谁有邀请权限,访客算不算队友,重复邀请如何处理,链接多久过期,被禁用域名是否拦截,审计日志写什么。它们听起来像细枝末节,但上线后安全例外、客服工单和权限争议大多就出在这里。
所以评审规格时,不要满足于句子顺畅。看到“管理”“支持”“处理”这类词,先问它具体落到哪个状态、哪条记录、哪种错误路径。
一个简单的自查口径
如果一份 spec 读完后,开发、QA 和产品各自能讲出三个不同版本,那它还没写完。我会用下面这张小表做最后检查,特别适合抓住看起来完整但其实很空的文档。
Spec self-check: - Goal: can be explained in one sentence - Non-goals: name specific excluded work - Acceptance criteria: each maps to a test - Failure paths: include timeout, permission, validation, duplicate request - Data contract: fields, states, and events are named - Rollback: owner and trigger are explicit
边界:不要为了通过清单而把 spec 写成长文。清单是找洞的,不是逼你堆段落。
把常见错误变成 review 评论
发现“目标含糊”时,不要只说“写清楚”。直接要求作者补用户、行为、输出和证据。发现“没有失败路径”时,让他补 timeout、权限、重复请求和回滚。评论越具体,spec 修改越快。
如果团队已经有模板,可以把这张自查表变成 PR bot 评论:缺非目标、缺回滚、缺 API 字段、缺测试证据时自动提醒。自动提醒不替代 review,但能减少最无聊的一轮来回。
可复制产物:规格写作片段
当工单看似清楚但还缺验收语言时使用。它要求作者写出角色、触发、结果和证据。
规格写作评审片段:软件规格文档中的 10 个常见错误 本次要做的决策: - 把模糊需求改写成可以由工程和 QA 共同判断的验收条款。 责任人检查: - 产品责任人: - 工程责任人: - QA 或运维评审: 范围边界: - 本次包含: - 本次不包含: - 仍需确认的假设: 验收证据: - 测试或 fixture: - 日志、指标或截图: - 人工复核步骤: 写作边界:避免模糊动词,每条验收标准都要有可见的通过或失败信号。 评审追问: - 没参加需求会的人还会误解哪里? - 哪个证据能证明这次改动足够安全,可以发布?
编辑复核记录
复核日期:2026-04-28。本次补充了可复用产物,按相关主题 Hub 检查了文章定位,并收紧下一步链接,让页面更像可操作参考,而不是孤立长文。
评审改写示例:把一段空话改成工程产物
下面这种改写,能让规格从“会议纪要”变成真正可以评审、联调和验收的工程文档。
修改前: 新的导出流程应该快速、稳定、易用。出问题时要通知用户。 修改后: 50k 行以内的 CSV 导出必须在 2 秒内开始。 超过 50k 行进入异步任务,并展示 job_id。 任务失败时,用户看到 failed 状态、retry 操作和 last_error_code。 QA 未验证 timeout、retry、重复请求、permission denied 前,不允许发布。
第二版写清了数字、状态、错误和发布证据。它仍然容易读,但不再把决策藏进形容词里。
专题阅读路径
这篇文章归入 Spec-First 开发 主题。先读 Hub,再结合下面的清单、模板或工具落到具体项目里。
继续阅读
填写表单,生成完整的功能规格 Markdown——免费使用,无需注册。
编辑说明
最近复核:2026-04-28。编辑部检查了示例、内链和可复制评审片段,确保内容更适合真实项目使用。
本文面向软件交付团队,介绍软件规格文档中的 10 个常见错误。示例均为工程场景说明,不构成法律、税务或投资建议。
- 作者信息:Daniel Marsh
- 编辑政策:文章审阅与更新方式
- 纠错:联系编辑