safety-eval-service/docs/spec指导手册.md

15 KiB
Raw Blame History

高质量 Spec 编写指南

核心原则:人定义 WHATAI 实现 HOWSpec 是唯一的真实来源。


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 返回 200And 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
    1. 管理员创建角色并分配权限
    2. 用户权限变更后无需重新登录
  • 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 认证)。

步骤四迭代打磨35 轮)

基于第一版 AC生成简要实现计划或测试大纲检查模糊、遗漏、矛盾之处反复修订直至六要素与 AC 自洽。一般需 35 轮迭代。

步骤五:分配测试锚点(可选)

为每条 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/rolesbody  { "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 缓存」当成约束 若是强制要求(如公司中间件标准)则为约束;否则归入实现计划决策