AI系列面试16:一个好的spec coding应该是怎么样的?
一个好的 Spec Coding(规格驱动编程) ,核心是把“模糊想法”变成“精确、可验证、可执行的契约”。它不只是写一份文档,而是建立一套人与 AI(或人与人)之间无歧义的沟通语言。下面我会从规格的内容结构、编写原则、与 AI 的协作流程、质量验证四个维度,给出一个好 spec 的样子。
一、规格文档的标准结构(以功能模块为例)
| 章节 | 必填内容 | 示例 |
|---|---|---|
| 1. 目标与范围 | 一句话说明做什么,明确不做什么 | “实现用户注册 API,不包含邮箱验证” |
| 2. 输入/输出契约 | 数据结构、类型、必填/可选字段、示例值 | POST /register 请求体 {email: string, password: string},响应 201 或 400 含错误码 |
| 3. 行为与逻辑 | 业务规则、边界条件、状态转换 | “密码长度 8-20 位,至少包含一个数字;邮箱已存在时返回 409” |
| 4. 错误处理 | 所有可能的异常场景及对应的错误码/消息 | “数据库连接失败 → 返回 503,不暴露堆栈” |
| 5. 非功能性要求 | 性能(响应时间 < 200ms)、安全(参数化查询)、日志、可观测性 | “所有 SQL 必须使用预编译;记录 email 但不记录 password” |
| 6. 测试用例(关键) | 至少 3 个典型输入 + 2 个边界/异常输入,给出期望输出 | 见下表 |
| 7. 依赖与约束 | 使用什么库、版本、环境变量 | “Python 3.10+,FastAPI,环境变量 DB_URL” |
测试用例示例(内嵌在 spec 中)
| 场景 | 输入 | 期望输出 |
|---|---|---|
| 正常注册 | email: a@b.com, pwd: Pass1234 |
201,返回 user_id |
| 密码过短 | pwd: Ab1 |
400,错误码 WEAK_PASSWORD |
| 邮箱已存在 | 同上 email | 409,错误码 EMAIL_EXISTS |
好的 Spec 必须先写测试用例,因为 AI 可以根据它们直接生成单元测试,完成后自动验证。
二、编写 Spec 的核心原则(SMART 变体)
| 原则 | 解释 | 反例 |
|---|---|---|
| 精确(Precise) | 使用具体数字、类型、布尔条件,避免“尽可能”、“通常” | ❌ “密码要足够安全” → ✅ “密码至少 8 位,含大写、小写、数字” |
| 可验证(Verifiable) | 每个要求都能通过自动测试或人工检查判定通过/失败 | ❌ “代码要优雅” → ✅ “函数圈复杂度 ≤ 10,无重复代码块” |
| 无歧义(Unambiguous) | 同一术语在全文中含义一致,必要时给出 glossary | ❌ “如果用户不存在,返回错误” → ✅ “用户不存在 → 返回 404 及 {code: 'USER_NOT_FOUND'}” |
| 完整(Complete) | 覆盖快乐路径、所有异常路径、非功能性需求 | ❌ 只写了成功场景 → ✅ 包含数据库超时、权限不足等 |
| 原子化(Atomic) | 一个 spec 只描述一个可独立交付的功能点(便于 AI 一次完成) | ❌ 用一个 spec 写“整个支付系统” → ✅ 拆成“生成支付单”、“回调验签”、“退款” |
三、与 AI 协作时的 Spec Coding 流程
- 人写 spec(上述结构,尤其写好测试用例和函数签名)。
- 把 spec 一次性喂给 AI(不要对话式追加需求,避免 vibe 污染)。
- AI 输出代码 + 单元测试(AI 必须按照 spec 中的测试用例生成可执行的测试)。
- 运行测试:如果全部通过,进入下一步;如果不通过,修改 spec 或直接修正代码(此时可以进入小循环,但需记录变更)。
- 人工审查:检查是否引入 spec 之外的功能(scope creep),检查安全/性能。
- 固化:将 spec 文档和最终代码一起提交到仓库,作为永久文档。
关键实践:Spec 代码化 —— 使用
spec.md+test_spec.py,其中测试文件直接来自 spec 中的示例,这样后续修改代码时只需运行测试即可验证 spec 是否被破坏。
四、好的 Spec 带来的效果(可作为验收标准)
- 确定性:相同 spec 给不同 AI(或不同人)产出相近的实现。
- 可测试性:写完代码就立刻能自动验证 90% 的正确性。
- 可维护性:半年后任何人看 spec,都能理解当初的设计意图。
- 低沟通成本:团队讨论时只讨论 spec,不讨论具体代码行。
- 安全/质量内置:安全要求(如参数化查询)和边界条件在 spec 中写明,AI 必须遵守。
五、一个好 Spec 的实例(极简版)
# Spec: 用户注册 API
## 范围
- 接收 email, password
- 不发送验证邮件,不检查邮箱真实性
## 契约
POST /register
Content-Type: application/json
Request: { "email": string, "password": string }
Response 201: { "user_id": string }
Response 400: { "code": "INVALID_PASSWORD" | "INVALID_EMAIL" }
Response 409: { "code": "EMAIL_ALREADY_EXISTS" }
## 行为
- email 必须符合 RFC 5322 基本格式(a@b.c)
- password: 长度 8-20,至少包含一个数字和一个大写字母
- 使用 bcrypt 加密存储,盐成本 10
- 如果在存入数据库前发现 email 已存在 → 409
## 测试用例(输入 -> 期望状态码+响应字段)
| 输入 email | password | 期望 |
|------------|----------|------|
| test@x.com | Pass1234 | 201, user_id 存在 |
| test@x.com | pass | 400, INVALID_PASSWORD |
| bad | Pass1234 | 400, INVALID_EMAIL |
| (已存在的邮箱) | Pass1234 | 409, EMAIL_ALREADY_EXISTS |
## 非功能
- SQL 必须使用参数化查询(防注入)
- 日志记录注册来源 IP,不记录密码
- 响应时间 95% 请求 < 100ms (不含 bcrypt)
## 依赖
- Python 3.10+, FastAPI, bcrypt, asyncpg
好的 Spec Coding = 把人的“设计决策”写成机器的“测试用例 + 类型签名 + 行为约束”,让 AI 只负责填充实现,而人始终掌控质量与方向。
评论
暂无已展示的评论。
发表评论(匿名)