第六章:文档编写规范

为什么文档质量很重要

OpenSpec 的核心是规范文档。文档质量直接决定 AI 实现的质量。

模糊的规范 → AI 猜测 → 实现偏差 → 反复修改
清晰的规范 → AI 理解 → 精准实现 → 一次到位

proposal.md 编写指南

必须包含

字段 说明 示例
背景 为什么要做 "当前没有用户认证,存在安全风险"
目标 要达成什么 "实现安全的用户登录功能"
范围 做什么/不做什么 "包含手机号登录,不包含邮箱登录"
成功标准 怎么算完成 "用户可以成功登录并访问受保护页面"

好的 vs 差的

❌ 差的提案

# 提案:改进登录

需要改进登录功能,让它更好用。

✅ 好的提案

# 提案:添加手机号验证码登录

## 背景
当前系统使用用户名+密码登录,用户反馈记不住密码,
导致每周有 200+ 次"忘记密码"请求,占客服工作量的 30%。

## 目标
- 添加手机号 + 验证码登录方式
- 减少密码相关的客服请求

## 范围
**包含**:
- 手机号 + 短信验证码登录
- 与现有账号绑定(通过手机号匹配)

**不包含**:
- 替换现有用户名+密码登录(保留)
- 微信登录(下一期)

## 成功标准
- 用户可以用手机号 + 验证码成功登录
- 登录成功率 > 99%
- 验证码发送延迟 < 5 秒

specs/ 编写指南

需求要可测试

每个需求都应该能被测试:

❌ 不可测试

- 系统要快
- 界面要好看
- 用户体验要好

✅ 可测试

- API 响应时间 < 200ms(P99)
- 登录页面在 375px 宽度下正常显示
- 验证码输入框自动聚焦

场景要完整

不只写正常流程,还要写异常流程:

## 场景

### 正常场景
1. 用户输入正确验证码
2. 登录成功,跳转首页

### 异常场景 1:验证码错误
1. 用户输入错误验证码
2. 提示"验证码错误,还有 2 次机会"

### 异常场景 2:验证码过期
1. 用户输入已过期验证码
2. 提示"验证码已过期,请重新获取"

### 异常场景 3:网络错误
1. 发送验证码时网络断开
2. 提示"网络错误,请检查网络连接"
3. 按钮恢复可点击状态

### 边界场景:频繁发送
1. 用户 1 分钟内点击 2 次"获取验证码"
2. 第 2 次点击时提示"请 XX 秒后再试"

使用具体数字

❌ 模糊

- 验证码有一定的有效期
- 支持多次重试
- 密码要足够复杂

✅ 具体

- 验证码有效期 5 分钟
- 最多重试 3 次,超过后锁定 60 秒
- 密码至少 8 位,包含大小写字母和数字

design.md 编写指南

API 设计要完整

每个接口都要包含:

### POST /api/auth/login

**描述**:用户登录

**请求头**:

Content-Type: application/json


**请求体**:
```json
{
  "phone": "13800138000",    // 手机号,必填
  "code": "123456"           // 验证码,必填
}

成功响应 (200):

{
  "success": true,
  "data": {
    "token": "eyJhbGciOiJIUzI1NiIs...",
    "expires_in": 604800,
    "user": {
      "id": 1,
      "phone": "138****8000"
    }
  }
}

错误响应 (400):

{
  "success": false,
  "error": {
    "code": "INVALID_CODE",
    "message": "验证码错误"
  }
}

错误码

错误码 说明
INVALID_CODE 验证码错误
CODE_EXPIRED 验证码已过期
TOO_MANY_ATTEMPTS 尝试次数过多

### 数据模型要清晰

```markdown
## 数据模型

### User
```typescript
interface User {
  id: number;
  phone: string;           // 手机号,唯一
  wechat_openid?: string;  // 微信 OpenID,可选
  created_at: Date;
  last_login_at: Date;
}

VerificationCode

interface VerificationCode {
  id: number;
  phone: string;
  code: string;            // 6 位数字
  expires_at: Date;        // 5 分钟后过期
  used: boolean;           // 是否已使用
  created_at: Date;
}

---

## tasks.md 编写指南

### 任务要原子化

每个任务应该是**独立可完成**的最小单元:

**❌ 太大**:
```markdown
- [ ] 1.1 实现登录功能

✅ 原子化

- [ ] 1.1 创建 users 数据库表
- [ ] 1.2 创建 verification_codes 数据库表
- [ ] 1.3 实现 POST /api/auth/send-code 接口
- [ ] 1.4 实现 POST /api/auth/login 接口
- [ ] 1.5 为 send-code 接口添加频率限制
- [ ] 1.6 编写 send-code 接口单元测试
- [ ] 1.7 编写 login 接口单元测试

任务要有顺序

按依赖关系排序:

## 1. 基础设施(先做)
- [ ] 1.1 创建数据库表
- [ ] 1.2 配置 Redis 连接

## 2. 后端 API(依赖 1)
- [ ] 2.1 实现发送验证码接口
- [ ] 2.2 实现登录接口

## 3. 前端页面(依赖 2)
- [ ] 3.1 创建登录页面
- [ ] 3.2 集成 API

## 4. 测试(最后)
- [ ] 4.1 单元测试
- [ ] 4.2 集成测试

包含测试任务

## 4. 测试
- [ ] 4.1 单元测试
  - [ ] 4.1.1 测试验证码生成逻辑
  - [ ] 4.1.2 测试 Token 生成和验证
  - [ ] 4.1.3 测试频率限制逻辑

- [ ] 4.2 集成测试
  - [ ] 4.2.1 测试完整登录流程
  - [ ] 4.2.2 测试异常场景

- [ ] 4.3 手动测试清单
  - [ ] 4.3.1 在 iOS Safari 测试
  - [ ] 4.3.2 在 Android Chrome 测试
  - [ ] 4.3.3 测试网络断开场景

常见错误

1. 规范太模糊

# ❌
用户可以登录系统

# ✅
用户可以通过手机号 + 6 位数字验证码登录系统,
验证码有效期 5 分钟,错误 3 次后锁定 60 秒

2. 遗漏异常场景

# ❌ 只写了正常流程
用户输入验证码,点击登录,跳转首页

# ✅ 包含异常场景
正常:用户输入正确验证码,登录成功,跳转首页
异常 1:验证码错误,提示剩余次数
异常 2:验证码过期,提示重新获取
异常 3:网络错误,提示检查网络

3. 技术方案不明确

# ❌
使用某种方式存储 Token

# ✅
Token 存储在 localStorage,key 为 "auth_token",
格式为 JWT,有效期 7 天,过期后自动清除并跳转登录页

下一步

第七章:实战案例