15 KiB
高质量 Spec 编写指南
核心原则:人定义 WHAT,AI 实现 HOW;Spec 是唯一的真实来源。
1. 核心概念
1.1 高质量 Spec 的标准
- 可测试:每个需求都有明确验收标准,可被自动化测试验证。
- 有边界:明确做什么、不做什么、在什么约束下做。
- 独立于实现:描述 WHAT,不限制 HOW(外部约束除外)。
1.2 六要素模型
完整 Spec 须包含以下六个要素:
| 要素 | 回答的问题 |
|---|---|
| Problem Statement | 为什么要做?背景和痛点是什么? |
| Success Metrics | 做到什么程度算成功?可量化的指标。 |
| User Stories | 谁、在什么场景下、获得什么价值? |
| Acceptance Criteria | 具体怎么验证?什么行为算通过? |
| Non-Goals | 明确本期不做什么。 |
| Constraints | 必须遵守的技术、法律、业务约束。 |
1.3 验收标准与原子性原则
验收标准(AC) 定义「怎么做才算对」。模糊 AC(如「用户可以登录系统」)会导致实现不一致。
原子性 AC 要求每条 AC 只验证一个独立业务行为:
- 单一职责:一个场景只测试一个动作的结果。
- 无依赖:可单独执行,不依赖其他 AC 的执行顺序。
- 可独立验证:一条失败时能立刻定位到具体问题。
六要素提供全景结构,原子性 AC 提供可执行粒度,二者融合形成可被 AI 直接消费的精确指令。
2. 六要素编写规范
2.1 Problem Statement(问题陈述)
用简洁语言描述当前系统或业务存在的痛点、机会或待解决问题。
对比:
- ❌ 「用户需要搜索功能。」
- ✅ 「用户在 10,000+ 文档的知识库中查找目标文档平均需 3 分钟,效率低下;需提供搜索功能,将查找时间缩短至 10 秒以内。」
要点:
- 用数据或事实描述现状。
- 说明不解决的后果。
- 不在问题陈述中直接给出解决方案。
2.2 Success Metrics(成功指标)
可量化、可测量的成功标准,用于判断功能是否真正解决问题。
对比:
- ❌ 「搜索应该很快。」
- ✅ 「搜索 API 响应时间 P95 < 200ms;搜索结果 Top-5 相关性准确率 > 85%(基于人工标注测试集)。」
要点:
- 必须包含数字(时间、百分比、数量等)。
- 避免主观词汇(「好用」「快速」「美观」)。
- 每个指标须能在 AC 或后续测试中直接验证。
2.3 User Stories(用户故事)
从用户视角描述谁想做什么、为了什么价值。格式:「作为…,我希望…,以便…」
示例:
- 作为系统管理员,我可以创建自定义角色并分配权限组合,以便为不同部门提供差异化的系统访问能力。
- 作为普通用户,我的权限变更后无需重新登录即可生效,以便工作时不被中断。
要点:
- 每条故事体现独立的用户价值。
- 用户角色要具体,避免笼统使用「用户」。
- 故事不包含实现细节或技术术语。
2.4 Acceptance Criteria(验收标准)
对每条用户故事的具体验收规则,定义什么行为算完成。须采用 Given/When/Then 格式(或等效结构化场景),覆盖正常路径、异常路径和关键边界,且全部可自动化测试。
示例结构:
Scenario: 用正确密码登录
Given 用户 alice@example.com 已注册且密码为 "P@ssw0rd1"
When 她用该邮箱和正确密码登录
Then 返回 200,包含有效 JWT
2.5 Non-Goals(非目标)
明确本期不实现的范围,防止范围蔓延和过度设计。
示例:
- 本期不实现跨组织的权限委托。
- 不实现基于时间段的临时权限。
- 不涉及 UI 层的权限管理界面(由前端团队单独出 Spec)。
要点:
- 与成功指标互补:成功指标说「要什么」,非目标说「不要什么」。
- 将容易产生歧义或易被「顺手」加上的功能提前排除。
- 阻止 AI 自动添加不必要的复杂功能。
2.6 Constraints(约束条件)
技术、法规、业务等方面必须遵守的外部限制,是不可违反的边界。
示例:
- 必须兼容现有 OAuth2.0 认证流程。
- 权限数据存储使用现有 PostgreSQL 实例,不引入新的存储组件。
- 权限模型设计需参考 AWS IAM Policy 语法规范。
- 所有数据库查询必须使用参数化查询,禁止字符串拼接。
要点:
- 约束不是实现方案,而是「必须在这个框里做事」。
- 若某约束严重影响目标,须先修正约束(升级 Spec),而非绕过。
- 项目级约束放在项目级规范文档中,模块级约束放在当前 Spec 中。
3. 原子性验收标准(Atomic AC)设计
3.1 定义
一条原子性 AC 只描述一个独立、完整、可单独验证的业务行为:
- 不依赖其他 AC 的执行顺序。
- 一条失败时原因清晰,不会「一条挂、多条误伤」。
- 能一对一转化为一个自动化测试用例。
3.2 标准格式
推荐使用 Gherkin Scenario 格式:
- Given:前置条件,系统当前状态。
- When:触发动作,用户或系统操作。
- Then:预期结果,可观察的输出或状态变化。
3.3 编写原则
① 单一职责
一个 Scenario 只测试一个业务动作的结果。
❌ 「Given 未登录用户,When 他登录,Then 跳转首页,And 显示待办数量」
✅ 拆成两条:一条测登录成功,一条测首页数据展示。
② 前置状态自给自足
Given 通过数据夹具直接设定所需状态,不依赖「先执行 AC-1 再执行 AC-2」。
❌ 「Given 已通过上一个测试创建了用户」
✅ 「Given 数据库中已存在用户 alice@example.com,密码为…,登录失败次数为 3」
③ Then 允许多个相关断言,但须属于同一结果
✅ 「Then 返回 200,And body 中包含 access_token 字段,And token 有效期 3600 秒」
❌ 「Then 跳转首页,And 弹出欢迎提示,And 导航栏显示头像」(可能来自不同接口)
④ 环境可恢复、隔离
每条 AC 执行后不留影响其他 AC 的副作用;转化为测试时通过 beforeEach/afterEach 保证数据隔离。
3.4 常见反模式与修正
| 反模式 | 问题 | 修正方法 |
|---|---|---|
| 「用户完成整个订单流程」 | 包含浏览、加购、下单、支付多个动作 | 拆为浏览商品、加入购物车、提交订单、支付成功等多个独立 AC |
| Given 描述模糊:「系统正常运行」 | 无具体状态 | 明确系统状态,如「数据库已清空」「缓存已预热」 |
| Then 使用主观词:「页面美观」 | 不可测试 | 改为可观察行为,如「页面包含 CSS 类 primary-button」或视觉回归对比 |
| 异常场景与正常场景混在一起 | 定位困难 | 异常须单独成 AC,如「密码错误」「账户锁定」「参数缺失」 |
4. 编写流程:从六要素到可执行 Spec
步骤一:起草五要素
先完成除 Acceptance Criteria 外的五个要素,不急于写 AC,确保方向正确。
输出示例(权限管理模块):
- Problem Statement:当前系统缺乏细粒度权限控制,无法满足不同部门差异化需求。
- Success Metrics:支持 5 种以上自定义角色;权限校验 P95 < 50ms;变更后 5 秒内生效。
- User Stories:
- 管理员创建角色并分配权限
- 用户权限变更后无需重新登录
- Non-Goals:不做跨组织委托;不实现临时权限。
- Constraints:必须兼容现有 OAuth2.0;使用现有 PostgreSQL。
Acceptance Criteria 暂时留空。
步骤二:为每条 User Story 生成原子性 AC
逐个故事展开,写出覆盖正常、异常、边界的原子性 AC。可用 AI 辅助生成草稿,但须逐条审查和修正。
以「管理员创建角色并分配权限」为例:
### Acceptance Criteria for User Story 1: 管理员创建角色
Scenario: 成功创建角色并分配指定权限
Given 管理员已通过 OAuth2.0 认证,且拥有「角色管理」权限
When 调用 POST /api/v1/roles,传入角色名「财务审核员」和权限列表 ["read:order", "write:order"]
Then 返回 201,角色创建成功
And 查询该角色权限,包含 "read:order" 和 "write:order"
Scenario: 创建已存在的角色名应被拒绝
Given 数据库中已存在角色「财务审核员」
When 管理员尝试创建同名角色
Then 返回 409,错误消息「角色名已存在」
Scenario: 无权限管理员无法创建角色
Given 管理员没有「角色管理」权限
When 调用创建角色接口
Then 返回 403,不产生任何数据
Scenario: 权限列表包含不存在的权限码应被拒绝
Given 系统合法权限码集合中不包含 "delete:everything"
When 创建角色时传入权限列表 ["read:order", "delete:everything"]
Then 返回 400,错误消息指明非法权限码
Scenario: 角色名为空应被拒绝
Given 管理员拥有创建权限
When 请求的角色名为空字符串
Then 返回 400,错误消息「角色名不能为空」
步骤三:反向验证覆盖度
检查所有 AC 是否覆盖 Success Metrics 和 Constraints。例如 Success Metrics 要求「权限变更后 5 秒内生效」,须有一条 AC 直接验证:
Scenario: 新增权限在5秒内生效
Given 用户 Alice 当前权限为 ["read:order"]
When 管理员为 Alice 新增 "write:order" 权限
And 等待不超过 5 秒
Then Alice 调用权限校验接口,返回的权限包含 "write:order"
Scenario: 移除权限在5秒内不再可用
Given Alice 拥有 "write:order"
When 管理员移除该权限,等待不超过 5 秒
Then Alice 调用权限校验,不包含 "write:order"
同时检查 Constraints:所有 AC 是否满足硬性约束(如上例中 OAuth2.0 认证)。
步骤四:迭代打磨(3–5 轮)
基于第一版 AC,生成简要实现计划或测试大纲,检查模糊、遗漏、矛盾之处,反复修订直至六要素与 AC 自洽。一般需 3–5 轮迭代。
步骤五:分配测试锚点(可选)
为每条 AC 分配唯一锚点(如 [AUTH-ROLE-AC-01]),便于在开发任务和测试代码中追溯。AC 可进一步拆解为实现任务,但与 Spec 本身解耦,属于实现计划范畴。
5. 完整示例:用户权限管理模块 Spec
# Feature Spec: 用户权限管理模块
## Problem Statement
当前系统仅区分管理员(全部权限)和普通用户(只读),无法满足业务部门对细粒度权限的需求。需要支持至少 5 种自定义角色,每个角色可配置 20 种以上独立权限。
## Success Metrics
- 支持创建、编辑、删除自定义角色,每个角色可配置 ≥ 20 种独立权限。
- 权限校验 API 响应 P95 < 50ms。
- 权限变更后,无需用户重新登录,5 秒内生效。
- 向后兼容:现有管理员/普通用户的行为不变。
## User Stories
1. 作为系统管理员,我可以创建自定义角色并分配权限组合,以便为不同部门提供差异化访问能力。
2. 作为普通用户,我的权限变更后无需重新登录即可生效,以便工作不被中断。
## Non-Goals
- 本期不实现跨组织的权限委托。
- 不支持基于时间段的临时权限。
- 不涉及 UI 层的权限管理界面(前端团队单独出 Spec)。
## Constraints
- 必须兼容现有 OAuth2.0 认证流程。
- 权限数据使用现有 PostgreSQL 存储,不引入新组件。
- 权限模型设计参考 AWS IAM Policy 语法风格。
- 所有查询必须参数化,敏感数据不出现在日志中。
## Acceptance Criteria
### Story 1: 管理员创建角色并分配权限
Scenario: 成功创建角色并分配权限
Given 管理员已通过 OAuth2.0 认证,拥有 "role:manage" 权限
When 调用 POST /api/v1/roles,body 为 { "name": "财务审核员", "permissions": ["order:read", "order:write"] }
Then HTTP 201,响应体包含角色 ID
And 查询该角色权限列表,包含 "order:read" 和 "order:write"
Scenario: 角色名重复时创建失败
Given 数据库中已存在角色名 "财务审核员"
When 管理员尝试创建同名角色
Then HTTP 409,错误消息 "ROLE_ALREADY_EXISTS"
Scenario: 缺少必填字段
Given 管理员拥有创建权限
When 请求 body 中 name 为空
Then HTTP 400,错误消息 "NAME_REQUIRED"
Scenario: 包含非法权限码
Given 系统合法权限集合中无 "super:destroy"
When 创建角色时 permissions 包含 "super:destroy"
Then HTTP 400,错误消息 "INVALID_PERMISSION_CODE"
Scenario: 无权限管理员无法创建
Given 管理员不具备 "role:manage" 权限
When 调用创建角色接口
Then HTTP 403,无数据变更
### Story 2: 权限变更实时生效
Scenario: 新增权限在5秒内生效
Given 用户 Alice 当前权限为 ["order:read"]
When 管理员为 Alice 添加 "order:write" 权限
And 等待 ≤ 5 秒
Then Alice 调用 GET /api/v1/users/{userId}/permissions,结果包含 "order:write"
Scenario: 移除权限在5秒内生效
Given Alice 权限为 ["order:read", "order:write"]
When 管理员移除 "order:write"
And 等待 ≤ 5 秒
Then Alice 查询权限,结果不包含 "order:write"
6. 质量自检清单
交付前逐项确认:
- Problem Statement 是否量化了痛点或机会?
- Success Metrics 是否包含具体数字,且每个指标可测试?
- User Stories 是否覆盖了所有核心用户角色和场景?
- 每条 AC 是否原子化?能否单独运行?
- AC 是否覆盖正常、异常、边界?(空值、重复、权限不足、超时等)
- Non-Goals 是否明确了本期不做的范围?
- Constraints 是否列出了所有硬性技术/业务约束?
- 能否用另一种技术栈实现,而 Spec 依然有效?(说明没有泄露 HOW)
- 所有 AC 与 Success Metrics、Constraints 之间是否可追溯?
7. 常见陷阱与应对
| 陷阱 | 表现 | 应对 |
|---|---|---|
| 过度规格化 | AC 变成分步操作手册,描述实现细节 | 用「换技术栈检验法」:若 Spec 绑定特定框架或算法,删去实现细节 |
| AC 非原子化 | 一条 AC 包含多个动作,或 Given 依赖其他 AC | 坚持「一个动作,一个结果」,用数据夹具直接创建初始状态 |
| 遗漏边界条件 | 只写 Happy Path,没有异常场景 | 每个 Story 至少包含:1 条正常、1 条权限/认证异常、1 条数据边界 |
| 指标不可测 | 「系统应该快速响应」 | 改为「P95 < 200ms」或「3 秒内返回结果」 |
| Spec 腐烂 | 代码迭代了,Spec 未更新 | 将 Spec 纳入版本控制,与代码同步提交;采用增量 Spec |
| 约束与方案混淆 | 把「使用 Redis 缓存」当成约束 | 若是强制要求(如公司中间件标准)则为约束;否则归入实现计划决策 |