TDD × SDD × AI Coding:从"测试驱动"到"规范驱动"的智能协作实践
-
- [一、引言:AI Coding 时代的"驱动方式"之争](#一、引言:AI Coding 时代的"驱动方式"之争)
- [二、概念澄清:TDD vs SDD 是什么?](#二、概念澄清:TDD vs SDD 是什么?)
-
- [2.1 TDD(测试驱动开发)](#2.1 TDD(测试驱动开发))
- [2.2 SDD(规范驱动开发)](#2.2 SDD(规范驱动开发))
- [2.3 核心对比](#2.3 核心对比)
- [三、整体架构:TDD + SDD 融合的 AI Coding 体系](#三、整体架构:TDD + SDD 融合的 AI Coding 体系)
- 四、实践流程:五步融合法
-
- [Step 1:需求 → 规范(SDD 起点)](#Step 1:需求 → 规范(SDD 起点))
- [Step 2:规范 → 测试骨架(TDD 前置)](#Step 2:规范 → 测试骨架(TDD 前置))
- [Step 3:AI 实现代码(受双重约束)](#Step 3:AI 实现代码(受双重约束))
- [Step 4:重构与规范更新(双向反馈)](#Step 4:重构与规范更新(双向反馈))
- [Step 5:CI 流水线中的规范守护](#Step 5:CI 流水线中的规范守护)
- 五、完整实践案例:云安全告警管理模块
- [六、进阶:AI Coding 中的规范漂移治理](#六、进阶:AI Coding 中的规范漂移治理)
- 七、工具链推荐
- 八、常见误区与解答
-
- [❌ 误区 1:有了 AI,不需要写测试了](#❌ 误区 1:有了 AI,不需要写测试了)
- [误区 2:规范文档太重了,敏捷开发不需要](#误区 2:规范文档太重了,敏捷开发不需要)
- [误区 3:TDD 和 SDD 选一个就够了](#误区 3:TDD 和 SDD 选一个就够了)
- [误区 4:AI 会自动保持规范更新](#误区 4:AI 会自动保持规范更新)
- 九、总结:融合方法论的核心思想
- 参考资料
一、引言:AI Coding 时代的"驱动方式"之争
AI 辅助编程工具(GitHub Copilot、Cursor、WorkBuddy 等)正在快速改变软件开发的范式。但随之而来的一个核心问题浮出水面:
AI 写代码,谁来保证代码是对的?
传统工程界已有两个成熟答案:
- TDD(Test-Driven Development,测试驱动开发):先写测试,再写实现,让测试成为代码正确性的"守门员"。
- SDD(Specification-Driven Development,规范驱动开发):先写规范(需求文档、接口约定、类型定义),再驱动 AI 生成代码,让规范成为 AI 的"航线图"。
两者并非对立,而是互补。本文将从理念对比、实践流程、工具链搭建到完整案例,带你深入理解如何在 AI Coding 时代将 TDD 与 SDD 融合,构建可信、可维护、可扩展的 AI 辅助研发体系。
二、概念澄清:TDD vs SDD 是什么?
2.1 TDD(测试驱动开发)
TDD 是 Kent Beck 于 1990 年代提出的极限编程实践,核心循环为"红-绿-重构":
Red → 写一个失败的测试
Green → 写最少的代码让测试通过
Refactor → 在绿灯下优化代码质量
核心价值:
- 测试即文档,即规格
- 防止过度设计
- 持续保障代码可回归
在 AI Coding 中的局限:
- AI 不会自发写测试,需要人工驱动
- 测试粒度不统一,AI 生成测试易流于形式(只测 happy path)
- 缺乏全局视角,难以在架构层约束 AI 输出
2.2 SDD(规范驱动开发)
SDD 是 AI Coding 时代兴起的方法论,核心思路是:用结构化、机器可读的规范文档作为 AI 的输入约束,让 AI 在规范边界内生成代码。
规范的形式包括但不限于:
| 规范类型 | 示例 |
|---|---|
| 接口规范 | OpenAPI / gRPC Proto |
| 类型规范 | TypeScript Interface / JSON Schema |
| 行为规范 | Gherkin / BDD 场景文本 |
| 架构规范 | ADR(架构决策记录) |
| 代码规范 | ESLint Rules / Checkstyle |
| AI 任务规范 | SKILL.md / .cursorrules |
核心价值:
- 约束 AI 输出范围,提升生成准确率
- 规范即上下文,减少 AI "幻觉"
- 跨会话保持一致性
在 AI Coding 中的局限:
- 规范写得不好,AI 生成的代码同样错误
- 规范更新滞后,"规范漂移"导致 AI 代码与实际不符
- 缺少运行时验证,规范停留在"纸面"
2.3 核心对比
| 维度 | TDD | SDD |
|---|---|---|
| 核心产物 | 测试用例 | 规范文档 |
| 约束时机 | 运行时(测试执行) | 编码前(上下文输入) |
| AI 协作方式 | AI 实现满足测试的代码 | AI 根据规范生成代码 |
| 质量保障层 | 功能正确性 | 结构一致性 |
| 适用场景 | 业务逻辑、算法层 | 接口层、架构层 |
| 维护成本 | 测试维护成本高 | 规范维护成本高 |
| AI 友好度 | 中(需精准描述测试意图) | 高(结构化文本天然适合 AI 解析) |
三、整体架构:TDD + SDD 融合的 AI Coding 体系
#mermaid-svg-vJPewtoICajQyKUn{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-vJPewtoICajQyKUn .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vJPewtoICajQyKUn .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vJPewtoICajQyKUn .error-icon{fill:#552222;}#mermaid-svg-vJPewtoICajQyKUn .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vJPewtoICajQyKUn .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vJPewtoICajQyKUn .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vJPewtoICajQyKUn .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vJPewtoICajQyKUn .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vJPewtoICajQyKUn .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vJPewtoICajQyKUn .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vJPewtoICajQyKUn .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vJPewtoICajQyKUn .marker.cross{stroke:#333333;}#mermaid-svg-vJPewtoICajQyKUn svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vJPewtoICajQyKUn p{margin:0;}#mermaid-svg-vJPewtoICajQyKUn .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-vJPewtoICajQyKUn .cluster-label text{fill:#333;}#mermaid-svg-vJPewtoICajQyKUn .cluster-label span{color:#333;}#mermaid-svg-vJPewtoICajQyKUn .cluster-label span p{background-color:transparent;}#mermaid-svg-vJPewtoICajQyKUn .label text,#mermaid-svg-vJPewtoICajQyKUn span{fill:#333;color:#333;}#mermaid-svg-vJPewtoICajQyKUn .node rect,#mermaid-svg-vJPewtoICajQyKUn .node circle,#mermaid-svg-vJPewtoICajQyKUn .node ellipse,#mermaid-svg-vJPewtoICajQyKUn .node polygon,#mermaid-svg-vJPewtoICajQyKUn .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vJPewtoICajQyKUn .rough-node .label text,#mermaid-svg-vJPewtoICajQyKUn .node .label text,#mermaid-svg-vJPewtoICajQyKUn .image-shape .label,#mermaid-svg-vJPewtoICajQyKUn .icon-shape .label{text-anchor:middle;}#mermaid-svg-vJPewtoICajQyKUn .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vJPewtoICajQyKUn .rough-node .label,#mermaid-svg-vJPewtoICajQyKUn .node .label,#mermaid-svg-vJPewtoICajQyKUn .image-shape .label,#mermaid-svg-vJPewtoICajQyKUn .icon-shape .label{text-align:center;}#mermaid-svg-vJPewtoICajQyKUn .node.clickable{cursor:pointer;}#mermaid-svg-vJPewtoICajQyKUn .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vJPewtoICajQyKUn .arrowheadPath{fill:#333333;}#mermaid-svg-vJPewtoICajQyKUn .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vJPewtoICajQyKUn .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vJPewtoICajQyKUn .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vJPewtoICajQyKUn .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vJPewtoICajQyKUn .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vJPewtoICajQyKUn .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vJPewtoICajQyKUn .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vJPewtoICajQyKUn .cluster text{fill:#333;}#mermaid-svg-vJPewtoICajQyKUn .cluster span{color:#333;}#mermaid-svg-vJPewtoICajQyKUn div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-vJPewtoICajQyKUn .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vJPewtoICajQyKUn rect.text{fill:none;stroke-width:0;}#mermaid-svg-vJPewtoICajQyKUn .icon-shape,#mermaid-svg-vJPewtoICajQyKUn .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vJPewtoICajQyKUn .icon-shape p,#mermaid-svg-vJPewtoICajQyKUn .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vJPewtoICajQyKUn .icon-shape .label rect,#mermaid-svg-vJPewtoICajQyKUn .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vJPewtoICajQyKUn .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vJPewtoICajQyKUn .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vJPewtoICajQyKUn :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 失败反馈
规范不一致
测试通过
🔄 持续集成层
规范 Lint 校验
测试自动执行
覆盖率检测
规范漂移告警
✅ 验证层 (TDD)
单元测试 Unit Test
集成测试 Integration
契约测试 Contract
E2E 测试
🤖 AI 编码层
AI 解析规范上下文
AI 生成代码骨架
AI 补全实现逻辑
AI 自动生成测试
📋 规范层 (SDD)
需求文档 / PRD
接口规范 OpenAPI
类型定义 TypeScript
架构决策 ADR
AI 任务规范 SKILL.md
🚀 部署
四、实践流程:五步融合法
Step 1:需求 → 规范(SDD 起点)
在拿到需求后,不要急于让 AI 写代码,先将需求转化为结构化规范:
规范文件 AI 助手 开发者 产品经理 规范文件 AI 助手 开发者 产品经理 #mermaid-svg-Pdo7f2UXhVXhs8Pa{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-Pdo7f2UXhVXhs8Pa .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .error-icon{fill:#552222;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .marker{fill:#333333;stroke:#333333;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .marker.cross{stroke:#333333;}#mermaid-svg-Pdo7f2UXhVXhs8Pa svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-Pdo7f2UXhVXhs8Pa p{margin:0;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Pdo7f2UXhVXhs8Pa text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Pdo7f2UXhVXhs8Pa .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-Pdo7f2UXhVXhs8Pa #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .sequenceNumber{fill:white;}#mermaid-svg-Pdo7f2UXhVXhs8Pa #sequencenumber{fill:#333;}#mermaid-svg-Pdo7f2UXhVXhs8Pa #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .messageText{fill:#333;stroke:none;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .labelText,#mermaid-svg-Pdo7f2UXhVXhs8Pa .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .loopText,#mermaid-svg-Pdo7f2UXhVXhs8Pa .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-Pdo7f2UXhVXhs8Pa .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .noteText,#mermaid-svg-Pdo7f2UXhVXhs8Pa .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .actorPopupMenu{position:absolute;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-Pdo7f2UXhVXhs8Pa .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-Pdo7f2UXhVXhs8Pa .actor-man circle,#mermaid-svg-Pdo7f2UXhVXhs8Pa line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-Pdo7f2UXhVXhs8Pa :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} openapi.yaml / types.ts / ADR.md 提交需求文档 (PRD) 请解析需求,生成 OpenAPI 草稿 返回 OpenAPI YAML 草稿 审核 & 提交规范文件 基于规范生成代码骨架
实操示例: 以"用户登录接口"为例
yaml
# openapi.yaml(规范先行)
paths:
/auth/login:
post:
summary: 用户登录
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [username, password]
properties:
username:
type: string
minLength: 3
maxLength: 50
password:
type: string
minLength: 8
responses:
'200':
description: 登录成功
content:
application/json:
schema:
$ref: '#/components/schemas/LoginResponse'
'401':
description: 用户名或密码错误
Step 2:规范 → 测试骨架(TDD 前置)
规范写完后,让 AI 根据规范生成测试骨架(而非直接生成实现代码):
#mermaid-svg-t2O9fBCbd8jbq51T{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-t2O9fBCbd8jbq51T .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-t2O9fBCbd8jbq51T .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-t2O9fBCbd8jbq51T .error-icon{fill:#552222;}#mermaid-svg-t2O9fBCbd8jbq51T .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-t2O9fBCbd8jbq51T .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-t2O9fBCbd8jbq51T .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-t2O9fBCbd8jbq51T .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-t2O9fBCbd8jbq51T .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-t2O9fBCbd8jbq51T .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-t2O9fBCbd8jbq51T .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-t2O9fBCbd8jbq51T .marker{fill:#333333;stroke:#333333;}#mermaid-svg-t2O9fBCbd8jbq51T .marker.cross{stroke:#333333;}#mermaid-svg-t2O9fBCbd8jbq51T svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-t2O9fBCbd8jbq51T p{margin:0;}#mermaid-svg-t2O9fBCbd8jbq51T .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-t2O9fBCbd8jbq51T .cluster-label text{fill:#333;}#mermaid-svg-t2O9fBCbd8jbq51T .cluster-label span{color:#333;}#mermaid-svg-t2O9fBCbd8jbq51T .cluster-label span p{background-color:transparent;}#mermaid-svg-t2O9fBCbd8jbq51T .label text,#mermaid-svg-t2O9fBCbd8jbq51T span{fill:#333;color:#333;}#mermaid-svg-t2O9fBCbd8jbq51T .node rect,#mermaid-svg-t2O9fBCbd8jbq51T .node circle,#mermaid-svg-t2O9fBCbd8jbq51T .node ellipse,#mermaid-svg-t2O9fBCbd8jbq51T .node polygon,#mermaid-svg-t2O9fBCbd8jbq51T .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-t2O9fBCbd8jbq51T .rough-node .label text,#mermaid-svg-t2O9fBCbd8jbq51T .node .label text,#mermaid-svg-t2O9fBCbd8jbq51T .image-shape .label,#mermaid-svg-t2O9fBCbd8jbq51T .icon-shape .label{text-anchor:middle;}#mermaid-svg-t2O9fBCbd8jbq51T .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-t2O9fBCbd8jbq51T .rough-node .label,#mermaid-svg-t2O9fBCbd8jbq51T .node .label,#mermaid-svg-t2O9fBCbd8jbq51T .image-shape .label,#mermaid-svg-t2O9fBCbd8jbq51T .icon-shape .label{text-align:center;}#mermaid-svg-t2O9fBCbd8jbq51T .node.clickable{cursor:pointer;}#mermaid-svg-t2O9fBCbd8jbq51T .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-t2O9fBCbd8jbq51T .arrowheadPath{fill:#333333;}#mermaid-svg-t2O9fBCbd8jbq51T .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-t2O9fBCbd8jbq51T .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-t2O9fBCbd8jbq51T .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t2O9fBCbd8jbq51T .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-t2O9fBCbd8jbq51T .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t2O9fBCbd8jbq51T .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-t2O9fBCbd8jbq51T .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-t2O9fBCbd8jbq51T .cluster text{fill:#333;}#mermaid-svg-t2O9fBCbd8jbq51T .cluster span{color:#333;}#mermaid-svg-t2O9fBCbd8jbq51T div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-t2O9fBCbd8jbq51T .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-t2O9fBCbd8jbq51T rect.text{fill:none;stroke-width:0;}#mermaid-svg-t2O9fBCbd8jbq51T .icon-shape,#mermaid-svg-t2O9fBCbd8jbq51T .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-t2O9fBCbd8jbq51T .icon-shape p,#mermaid-svg-t2O9fBCbd8jbq51T .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-t2O9fBCbd8jbq51T .icon-shape .label rect,#mermaid-svg-t2O9fBCbd8jbq51T .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-t2O9fBCbd8jbq51T .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-t2O9fBCbd8jbq51T .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-t2O9fBCbd8jbq51T :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} AI 解析
📋 OpenAPI 规范
🧪 测试骨架生成
正常路径测试
边界条件测试
异常路径测试
性能基准测试
全部失败
Red 状态
AI 提示词模板:
根据以下 OpenAPI 规范,生成完整的 Jest 单元测试骨架。
要求:
1. 覆盖所有 HTTP 状态码场景
2. 包含边界值测试(minLength/maxLength)
3. 使用 describe/it 结构,测试描述用中文
4. 先只写测试,不写实现
[粘贴 OpenAPI 规范]
生成的测试骨架示例:
typescript
// auth.test.ts
describe('用户登录接口 POST /auth/login', () => {
describe('正常登录', () => {
it('正确账号密码应返回 200 和 token', async () => {
// TODO: 待实现
expect(true).toBe(false); // Red 状态
});
});
describe('参数校验', () => {
it('用户名少于3位应返回 400', async () => {
expect(true).toBe(false);
});
it('密码少于8位应返回 400', async () => {
expect(true).toBe(false);
});
it('缺少 username 字段应返回 400', async () => {
expect(true).toBe(false);
});
});
describe('认证失败', () => {
it('错误密码应返回 401', async () => {
expect(true).toBe(false);
});
it('不存在的用户应返回 401', async () => {
expect(true).toBe(false);
});
});
});
Step 3:AI 实现代码(受双重约束)
此时 AI 的实现代码同时受到规范约束 (SDD)和测试约束(TDD)的双重限制:
#mermaid-svg-fcra9T94oDSUpTq3{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-fcra9T94oDSUpTq3 .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-fcra9T94oDSUpTq3 .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-fcra9T94oDSUpTq3 .error-icon{fill:#552222;}#mermaid-svg-fcra9T94oDSUpTq3 .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-fcra9T94oDSUpTq3 .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-fcra9T94oDSUpTq3 .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-fcra9T94oDSUpTq3 .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-fcra9T94oDSUpTq3 .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-fcra9T94oDSUpTq3 .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-fcra9T94oDSUpTq3 .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-fcra9T94oDSUpTq3 .marker{fill:#333333;stroke:#333333;}#mermaid-svg-fcra9T94oDSUpTq3 .marker.cross{stroke:#333333;}#mermaid-svg-fcra9T94oDSUpTq3 svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-fcra9T94oDSUpTq3 p{margin:0;}#mermaid-svg-fcra9T94oDSUpTq3 .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-fcra9T94oDSUpTq3 .cluster-label text{fill:#333;}#mermaid-svg-fcra9T94oDSUpTq3 .cluster-label span{color:#333;}#mermaid-svg-fcra9T94oDSUpTq3 .cluster-label span p{background-color:transparent;}#mermaid-svg-fcra9T94oDSUpTq3 .label text,#mermaid-svg-fcra9T94oDSUpTq3 span{fill:#333;color:#333;}#mermaid-svg-fcra9T94oDSUpTq3 .node rect,#mermaid-svg-fcra9T94oDSUpTq3 .node circle,#mermaid-svg-fcra9T94oDSUpTq3 .node ellipse,#mermaid-svg-fcra9T94oDSUpTq3 .node polygon,#mermaid-svg-fcra9T94oDSUpTq3 .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-fcra9T94oDSUpTq3 .rough-node .label text,#mermaid-svg-fcra9T94oDSUpTq3 .node .label text,#mermaid-svg-fcra9T94oDSUpTq3 .image-shape .label,#mermaid-svg-fcra9T94oDSUpTq3 .icon-shape .label{text-anchor:middle;}#mermaid-svg-fcra9T94oDSUpTq3 .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-fcra9T94oDSUpTq3 .rough-node .label,#mermaid-svg-fcra9T94oDSUpTq3 .node .label,#mermaid-svg-fcra9T94oDSUpTq3 .image-shape .label,#mermaid-svg-fcra9T94oDSUpTq3 .icon-shape .label{text-align:center;}#mermaid-svg-fcra9T94oDSUpTq3 .node.clickable{cursor:pointer;}#mermaid-svg-fcra9T94oDSUpTq3 .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-fcra9T94oDSUpTq3 .arrowheadPath{fill:#333333;}#mermaid-svg-fcra9T94oDSUpTq3 .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-fcra9T94oDSUpTq3 .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-fcra9T94oDSUpTq3 .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fcra9T94oDSUpTq3 .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-fcra9T94oDSUpTq3 .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fcra9T94oDSUpTq3 .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-fcra9T94oDSUpTq3 .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-fcra9T94oDSUpTq3 .cluster text{fill:#333;}#mermaid-svg-fcra9T94oDSUpTq3 .cluster span{color:#333;}#mermaid-svg-fcra9T94oDSUpTq3 div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-fcra9T94oDSUpTq3 .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-fcra9T94oDSUpTq3 rect.text{fill:none;stroke-width:0;}#mermaid-svg-fcra9T94oDSUpTq3 .icon-shape,#mermaid-svg-fcra9T94oDSUpTq3 .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-fcra9T94oDSUpTq3 .icon-shape p,#mermaid-svg-fcra9T94oDSUpTq3 .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-fcra9T94oDSUpTq3 .icon-shape .label rect,#mermaid-svg-fcra9T94oDSUpTq3 .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-fcra9T94oDSUpTq3 .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-fcra9T94oDSUpTq3 .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-fcra9T94oDSUpTq3 :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 部分失败
实现逻辑错误
规范理解偏差
✅ 全部通过
🤖 AI 开始实现
读取约束
📋 OpenAPI 规范
接口契约约束
🧪 单元测试
行为约束
AI 生成实现代码
运行测试
测试通过?
AI 分析失败原因
失败类型
重新解析规范
Green 状态
AI 执行重构
再次运行测试确认
AI 提示词模板:
现在请实现以下接口,要求:
1. 严格遵循 openapi.yaml 中的接口规范
2. 实现必须让 auth.test.ts 中所有测试通过
3. 使用 Spring Boot 3 + Java 17
4. 包含参数校验(使用 Bean Validation)
5. 不要超出规范范围添加额外字段
规范文件:[openapi.yaml]
测试文件:[auth.test.ts]
Step 4:重构与规范更新(双向反馈)
代码通过测试后,进入重构阶段,同时维护规范的准确性:
#mermaid-svg-oIGdoawoLOHEfmAG{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-oIGdoawoLOHEfmAG .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-oIGdoawoLOHEfmAG .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-oIGdoawoLOHEfmAG .error-icon{fill:#552222;}#mermaid-svg-oIGdoawoLOHEfmAG .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-oIGdoawoLOHEfmAG .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-oIGdoawoLOHEfmAG .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-oIGdoawoLOHEfmAG .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-oIGdoawoLOHEfmAG .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-oIGdoawoLOHEfmAG .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-oIGdoawoLOHEfmAG .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-oIGdoawoLOHEfmAG .marker{fill:#333333;stroke:#333333;}#mermaid-svg-oIGdoawoLOHEfmAG .marker.cross{stroke:#333333;}#mermaid-svg-oIGdoawoLOHEfmAG svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-oIGdoawoLOHEfmAG p{margin:0;}#mermaid-svg-oIGdoawoLOHEfmAG defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-oIGdoawoLOHEfmAG g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-oIGdoawoLOHEfmAG g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-oIGdoawoLOHEfmAG g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-oIGdoawoLOHEfmAG g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-oIGdoawoLOHEfmAG g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-oIGdoawoLOHEfmAG .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-oIGdoawoLOHEfmAG .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-oIGdoawoLOHEfmAG .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-oIGdoawoLOHEfmAG .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-oIGdoawoLOHEfmAG .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-oIGdoawoLOHEfmAG .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-oIGdoawoLOHEfmAG .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-oIGdoawoLOHEfmAG .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-oIGdoawoLOHEfmAG .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-oIGdoawoLOHEfmAG .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-oIGdoawoLOHEfmAG .edgeLabel .label text{fill:#333;}#mermaid-svg-oIGdoawoLOHEfmAG .label div .edgeLabel{color:#333;}#mermaid-svg-oIGdoawoLOHEfmAG .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-oIGdoawoLOHEfmAG .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-oIGdoawoLOHEfmAG .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-oIGdoawoLOHEfmAG .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-oIGdoawoLOHEfmAG .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-oIGdoawoLOHEfmAG .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oIGdoawoLOHEfmAG .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oIGdoawoLOHEfmAG #statediagram-barbEnd{fill:#333333;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-oIGdoawoLOHEfmAG .cluster-label,#mermaid-svg-oIGdoawoLOHEfmAG .nodeLabel{color:#131300;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-oIGdoawoLOHEfmAG .note-edge{stroke-dasharray:5;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-note text{fill:black;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram-note .nodeLabel{color:black;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagram .edgeLabel{color:red;}#mermaid-svg-oIGdoawoLOHEfmAG #dependencyStart,#mermaid-svg-oIGdoawoLOHEfmAG #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-oIGdoawoLOHEfmAG .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-oIGdoawoLOHEfmAG :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} SDD → TDD
测试驱动
红→绿
Refactor
实现反哺规范
发现规范不完整
规范变化→新测试
无需更新
规范与实现一致
确认绿灯
合并主干
规范制定
测试编写
AI实现
测试通过
代码重构
规范检视
规范更新
测试补充
CI集成
Step 5:CI 流水线中的规范守护
将 SDD 和 TDD 的质量门控集成到 CI/CD 流水线:
#mermaid-svg-XkJ3wfY5DR1g1L3r{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XkJ3wfY5DR1g1L3r .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XkJ3wfY5DR1g1L3r .error-icon{fill:#552222;}#mermaid-svg-XkJ3wfY5DR1g1L3r .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XkJ3wfY5DR1g1L3r .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XkJ3wfY5DR1g1L3r .marker.cross{stroke:#333333;}#mermaid-svg-XkJ3wfY5DR1g1L3r svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XkJ3wfY5DR1g1L3r p{margin:0;}#mermaid-svg-XkJ3wfY5DR1g1L3r .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XkJ3wfY5DR1g1L3r .cluster-label text{fill:#333;}#mermaid-svg-XkJ3wfY5DR1g1L3r .cluster-label span{color:#333;}#mermaid-svg-XkJ3wfY5DR1g1L3r .cluster-label span p{background-color:transparent;}#mermaid-svg-XkJ3wfY5DR1g1L3r .label text,#mermaid-svg-XkJ3wfY5DR1g1L3r span{fill:#333;color:#333;}#mermaid-svg-XkJ3wfY5DR1g1L3r .node rect,#mermaid-svg-XkJ3wfY5DR1g1L3r .node circle,#mermaid-svg-XkJ3wfY5DR1g1L3r .node ellipse,#mermaid-svg-XkJ3wfY5DR1g1L3r .node polygon,#mermaid-svg-XkJ3wfY5DR1g1L3r .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XkJ3wfY5DR1g1L3r .rough-node .label text,#mermaid-svg-XkJ3wfY5DR1g1L3r .node .label text,#mermaid-svg-XkJ3wfY5DR1g1L3r .image-shape .label,#mermaid-svg-XkJ3wfY5DR1g1L3r .icon-shape .label{text-anchor:middle;}#mermaid-svg-XkJ3wfY5DR1g1L3r .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XkJ3wfY5DR1g1L3r .rough-node .label,#mermaid-svg-XkJ3wfY5DR1g1L3r .node .label,#mermaid-svg-XkJ3wfY5DR1g1L3r .image-shape .label,#mermaid-svg-XkJ3wfY5DR1g1L3r .icon-shape .label{text-align:center;}#mermaid-svg-XkJ3wfY5DR1g1L3r .node.clickable{cursor:pointer;}#mermaid-svg-XkJ3wfY5DR1g1L3r .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XkJ3wfY5DR1g1L3r .arrowheadPath{fill:#333333;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XkJ3wfY5DR1g1L3r .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XkJ3wfY5DR1g1L3r .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XkJ3wfY5DR1g1L3r .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XkJ3wfY5DR1g1L3r .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XkJ3wfY5DR1g1L3r .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XkJ3wfY5DR1g1L3r .cluster text{fill:#333;}#mermaid-svg-XkJ3wfY5DR1g1L3r .cluster span{color:#333;}#mermaid-svg-XkJ3wfY5DR1g1L3r div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XkJ3wfY5DR1g1L3r .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XkJ3wfY5DR1g1L3r rect.text{fill:none;stroke-width:0;}#mermaid-svg-XkJ3wfY5DR1g1L3r .icon-shape,#mermaid-svg-XkJ3wfY5DR1g1L3r .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XkJ3wfY5DR1g1L3r .icon-shape p,#mermaid-svg-XkJ3wfY5DR1g1L3r .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XkJ3wfY5DR1g1L3r .icon-shape .label rect,#mermaid-svg-XkJ3wfY5DR1g1L3r .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XkJ3wfY5DR1g1L3r .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XkJ3wfY5DR1g1L3r .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XkJ3wfY5DR1g1L3r :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 结果
质量门控
触发器
全部通过
任一失败
任一失败
任一失败
任一失败
任一失败
任一失败
Pull Request
📋 规范校验
openapi-lint
spectral
🔍 类型检查
tsc --noEmit
🧪 单元测试
jest --coverage
📊 覆盖率门槛
>= 80%
🔄 契约测试
Pact Broker
🤖 AI 规范漂移检测
实现 vs 规范 diff
✅ 准入合并
阻断合并
返回详细报告
五、完整实践案例:云安全告警管理模块
以一个真实场景为例:云安全平台的告警规则管理接口,演示完整的 TDD+SDD+AI 协作流程。
5.1 项目结构
alert-rule-service/
├── specs/ # SDD 规范层
│ ├── openapi.yaml # 接口规范
│ ├── alert-rule.schema.json # 数据模型规范
│ └── ADR-001-alert-engine.md # 架构决策记录
├── src/
│ ├── main/java/.../
│ │ ├── controller/ # AI 根据规范生成
│ │ ├── service/ # AI + TDD 驱动实现
│ │ └── model/ # 从 schema 生成
│ └── test/java/.../ # TDD 测试用例
├── .workbuddy/
│ └── skills/
│ └── alert-rule-dev/ # 项目级 SKILL
│ └── SKILL.md # AI 任务规范
└── .spectral.yaml # 规范 Lint 配置
5.2 SKILL.md:AI 任务规范示例
markdown
# SKILL: 告警规则模块开发规范
## 技术栈约束
- Spring Boot 3.2 + Java 17
- MyBatis-Plus ORM
- Jakarta Bean Validation
- JUnit 5 + Mockito
## 接口规范
- 严格遵循 specs/openapi.yaml
- 所有接口返回统一格式:{ code, message, data }
- 分页接口使用 PageResult<T> 包装
## 命名规范
- Controller 方法名与 operationId 一致
- 异常使用 BusinessException(ErrorCode.XXX) 抛出
- 日志使用 @Slf4j,禁止 System.out.println
## 测试要求
- 每个 Service 方法覆盖率 >= 85%
- Controller 层必须有集成测试
- 涉及数据库操作使用 @Transactional + H2 内存库
5.3 告警规则 CRUD 的测试先行示例
java
// AlertRuleServiceTest.java(测试骨架,Red 阶段)
@SpringBootTest
@Transactional
class AlertRuleServiceTest {
@Autowired
AlertRuleService alertRuleService;
@Test
@DisplayName("创建告警规则 - 正常参数应成功持久化")
void createAlertRule_withValidParams_shouldPersist() {
// Given
CreateAlertRuleRequest request = CreateAlertRuleRequest.builder()
.name("CPU使用率告警")
.metric("cpu_usage")
.threshold(85.0)
.severity(Severity.HIGH)
.build();
// When
AlertRuleVO result = alertRuleService.createAlertRule(request);
// Then
assertThat(result.getId()).isNotNull();
assertThat(result.getName()).isEqualTo("CPU使用率告警");
assertThat(result.getStatus()).isEqualTo(RuleStatus.ENABLED);
}
@Test
@DisplayName("创建告警规则 - 阈值超出范围(>100)应抛出业务异常")
void createAlertRule_withThresholdOver100_shouldThrowBusinessException() {
CreateAlertRuleRequest request = CreateAlertRuleRequest.builder()
.name("非法规则")
.metric("cpu_usage")
.threshold(150.0) // 非法值
.severity(Severity.LOW)
.build();
assertThatThrownBy(() -> alertRuleService.createAlertRule(request))
.isInstanceOf(BusinessException.class)
.hasMessageContaining(ErrorCode.INVALID_THRESHOLD.getMessage());
}
@Test
@DisplayName("查询告警规则列表 - 支持按严重等级过滤")
void listAlertRules_filterBySeverity_shouldReturnMatchedRules() {
// Given - 预置不同等级的规则
createTestRule("高危规则1", Severity.HIGH);
createTestRule("高危规则2", Severity.HIGH);
createTestRule("低危规则", Severity.LOW);
// When
PageResult<AlertRuleVO> result = alertRuleService.listAlertRules(
AlertRuleQuery.builder().severity(Severity.HIGH).pageSize(10).build()
);
// Then
assertThat(result.getTotal()).isEqualTo(2);
assertThat(result.getItems())
.allMatch(rule -> rule.getSeverity() == Severity.HIGH);
}
}
5.4 AI 驱动实现(Green 阶段)
AI 收到测试文件 + SKILL.md + OpenAPI 规范后,生成的 Service 实现:
java
// AlertRuleServiceImpl.java(AI 生成,受双重约束)
@Slf4j
@Service
@RequiredArgsConstructor
public class AlertRuleServiceImpl implements AlertRuleService {
private final AlertRuleMapper alertRuleMapper;
private final AlertRuleConverter converter;
@Override
@Transactional
public AlertRuleVO createAlertRule(CreateAlertRuleRequest request) {
// 参数校验(规范约束)
if (request.getThreshold() < 0 || request.getThreshold() > 100) {
throw new BusinessException(ErrorCode.INVALID_THRESHOLD);
}
// 重名检查
if (alertRuleMapper.existsByName(request.getName())) {
throw new BusinessException(ErrorCode.ALERT_RULE_NAME_DUPLICATE);
}
AlertRule entity = converter.toEntity(request);
entity.setStatus(RuleStatus.ENABLED);
entity.setCreateTime(LocalDateTime.now());
alertRuleMapper.insert(entity);
log.info("告警规则创建成功: id={}, name={}", entity.getId(), entity.getName());
return converter.toVO(entity);
}
@Override
public PageResult<AlertRuleVO> listAlertRules(AlertRuleQuery query) {
Page<AlertRule> page = new Page<>(query.getPageNum(), query.getPageSize());
LambdaQueryWrapper<AlertRule> wrapper = Wrappers.lambdaQuery(AlertRule.class)
.eq(query.getSeverity() != null, AlertRule::getSeverity, query.getSeverity())
.eq(query.getStatus() != null, AlertRule::getStatus, query.getStatus())
.orderByDesc(AlertRule::getCreateTime);
alertRuleMapper.selectPage(page, wrapper);
return PageResult.of(page, converter::toVO);
}
}
六、进阶:AI Coding 中的规范漂移治理
随着项目演进,规范与实现之间会出现"漂移",这是 SDD 面临的最大挑战:
#mermaid-svg-78KZB785at5cIlUi{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-78KZB785at5cIlUi .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-78KZB785at5cIlUi .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-78KZB785at5cIlUi .error-icon{fill:#552222;}#mermaid-svg-78KZB785at5cIlUi .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-78KZB785at5cIlUi .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-78KZB785at5cIlUi .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-78KZB785at5cIlUi .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-78KZB785at5cIlUi .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-78KZB785at5cIlUi .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-78KZB785at5cIlUi .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-78KZB785at5cIlUi .marker{fill:#333333;stroke:#333333;}#mermaid-svg-78KZB785at5cIlUi .marker.cross{stroke:#333333;}#mermaid-svg-78KZB785at5cIlUi svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-78KZB785at5cIlUi p{margin:0;}#mermaid-svg-78KZB785at5cIlUi .edge{stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .section--1 rect,#mermaid-svg-78KZB785at5cIlUi .section--1 path,#mermaid-svg-78KZB785at5cIlUi .section--1 circle,#mermaid-svg-78KZB785at5cIlUi .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section--1 text{fill:#ffffff;}#mermaid-svg-78KZB785at5cIlUi .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-78KZB785at5cIlUi .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth--1{stroke-width:17;}#mermaid-svg-78KZB785at5cIlUi .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:#ffffff;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-0 rect,#mermaid-svg-78KZB785at5cIlUi .section-0 path,#mermaid-svg-78KZB785at5cIlUi .section-0 circle,#mermaid-svg-78KZB785at5cIlUi .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-78KZB785at5cIlUi .section-0 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-0{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-0{stroke-width:14;}#mermaid-svg-78KZB785at5cIlUi .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-1 rect,#mermaid-svg-78KZB785at5cIlUi .section-1 path,#mermaid-svg-78KZB785at5cIlUi .section-1 circle,#mermaid-svg-78KZB785at5cIlUi .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-1 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-1{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-1{stroke-width:11;}#mermaid-svg-78KZB785at5cIlUi .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-2 rect,#mermaid-svg-78KZB785at5cIlUi .section-2 path,#mermaid-svg-78KZB785at5cIlUi .section-2 circle,#mermaid-svg-78KZB785at5cIlUi .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-2 text{fill:#ffffff;}#mermaid-svg-78KZB785at5cIlUi .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-78KZB785at5cIlUi .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-2{stroke-width:8;}#mermaid-svg-78KZB785at5cIlUi .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:#ffffff;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-3 rect,#mermaid-svg-78KZB785at5cIlUi .section-3 path,#mermaid-svg-78KZB785at5cIlUi .section-3 circle,#mermaid-svg-78KZB785at5cIlUi .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-3 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-3{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-3{stroke-width:5;}#mermaid-svg-78KZB785at5cIlUi .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-4 rect,#mermaid-svg-78KZB785at5cIlUi .section-4 path,#mermaid-svg-78KZB785at5cIlUi .section-4 circle,#mermaid-svg-78KZB785at5cIlUi .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-4 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-4{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-4{stroke-width:2;}#mermaid-svg-78KZB785at5cIlUi .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-5 rect,#mermaid-svg-78KZB785at5cIlUi .section-5 path,#mermaid-svg-78KZB785at5cIlUi .section-5 circle,#mermaid-svg-78KZB785at5cIlUi .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-5 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-5{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-5{stroke-width:-1;}#mermaid-svg-78KZB785at5cIlUi .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-6 rect,#mermaid-svg-78KZB785at5cIlUi .section-6 path,#mermaid-svg-78KZB785at5cIlUi .section-6 circle,#mermaid-svg-78KZB785at5cIlUi .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-6 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-6{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-6{stroke-width:-4;}#mermaid-svg-78KZB785at5cIlUi .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-7 rect,#mermaid-svg-78KZB785at5cIlUi .section-7 path,#mermaid-svg-78KZB785at5cIlUi .section-7 circle,#mermaid-svg-78KZB785at5cIlUi .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-7 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-7{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-7{stroke-width:-7;}#mermaid-svg-78KZB785at5cIlUi .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-8 rect,#mermaid-svg-78KZB785at5cIlUi .section-8 path,#mermaid-svg-78KZB785at5cIlUi .section-8 circle,#mermaid-svg-78KZB785at5cIlUi .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-8 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-8{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-8{stroke-width:-10;}#mermaid-svg-78KZB785at5cIlUi .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-9 rect,#mermaid-svg-78KZB785at5cIlUi .section-9 path,#mermaid-svg-78KZB785at5cIlUi .section-9 circle,#mermaid-svg-78KZB785at5cIlUi .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-9 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-9{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-9{stroke-width:-13;}#mermaid-svg-78KZB785at5cIlUi .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-10 rect,#mermaid-svg-78KZB785at5cIlUi .section-10 path,#mermaid-svg-78KZB785at5cIlUi .section-10 circle,#mermaid-svg-78KZB785at5cIlUi .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-10 text{fill:black;}#mermaid-svg-78KZB785at5cIlUi .node-icon-10{font-size:40px;color:black;}#mermaid-svg-78KZB785at5cIlUi .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .edge-depth-10{stroke-width:-16;}#mermaid-svg-78KZB785at5cIlUi .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-78KZB785at5cIlUi .lineWrapper line{stroke:black;}#mermaid-svg-78KZB785at5cIlUi .disabled,#mermaid-svg-78KZB785at5cIlUi .disabled circle,#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:lightgray;}#mermaid-svg-78KZB785at5cIlUi .disabled text{fill:#efefef;}#mermaid-svg-78KZB785at5cIlUi .section-root rect,#mermaid-svg-78KZB785at5cIlUi .section-root path,#mermaid-svg-78KZB785at5cIlUi .section-root circle{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-78KZB785at5cIlUi .section-root text{fill:#ffffff;}#mermaid-svg-78KZB785at5cIlUi .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-78KZB785at5cIlUi .edge{fill:none;}#mermaid-svg-78KZB785at5cIlUi .eventWrapper{filter:brightness(120%);}#mermaid-svg-78KZB785at5cIlUi :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Sprint 1 规范与实现完全一致 OpenAPI v1.0 = 实际接口 Sprint 3 快速迭代导致规范更新滞后 实现新增字段未更新规范 Sprint 6 规范成为"装饰" AI 生成代码开始偏离预期 Sprint 8 技术债爆发 新人困惑·测试失败·AI 幻觉增加 规范漂移的典型演进路径
治理方案:三道防线
#mermaid-svg-tsLgRGMq5OuYds8v{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-tsLgRGMq5OuYds8v .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-tsLgRGMq5OuYds8v .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-tsLgRGMq5OuYds8v .error-icon{fill:#552222;}#mermaid-svg-tsLgRGMq5OuYds8v .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-tsLgRGMq5OuYds8v .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-tsLgRGMq5OuYds8v .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-tsLgRGMq5OuYds8v .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-tsLgRGMq5OuYds8v .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-tsLgRGMq5OuYds8v .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-tsLgRGMq5OuYds8v .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-tsLgRGMq5OuYds8v .marker{fill:#333333;stroke:#333333;}#mermaid-svg-tsLgRGMq5OuYds8v .marker.cross{stroke:#333333;}#mermaid-svg-tsLgRGMq5OuYds8v svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-tsLgRGMq5OuYds8v p{margin:0;}#mermaid-svg-tsLgRGMq5OuYds8v .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-tsLgRGMq5OuYds8v .cluster-label text{fill:#333;}#mermaid-svg-tsLgRGMq5OuYds8v .cluster-label span{color:#333;}#mermaid-svg-tsLgRGMq5OuYds8v .cluster-label span p{background-color:transparent;}#mermaid-svg-tsLgRGMq5OuYds8v .label text,#mermaid-svg-tsLgRGMq5OuYds8v span{fill:#333;color:#333;}#mermaid-svg-tsLgRGMq5OuYds8v .node rect,#mermaid-svg-tsLgRGMq5OuYds8v .node circle,#mermaid-svg-tsLgRGMq5OuYds8v .node ellipse,#mermaid-svg-tsLgRGMq5OuYds8v .node polygon,#mermaid-svg-tsLgRGMq5OuYds8v .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-tsLgRGMq5OuYds8v .rough-node .label text,#mermaid-svg-tsLgRGMq5OuYds8v .node .label text,#mermaid-svg-tsLgRGMq5OuYds8v .image-shape .label,#mermaid-svg-tsLgRGMq5OuYds8v .icon-shape .label{text-anchor:middle;}#mermaid-svg-tsLgRGMq5OuYds8v .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-tsLgRGMq5OuYds8v .rough-node .label,#mermaid-svg-tsLgRGMq5OuYds8v .node .label,#mermaid-svg-tsLgRGMq5OuYds8v .image-shape .label,#mermaid-svg-tsLgRGMq5OuYds8v .icon-shape .label{text-align:center;}#mermaid-svg-tsLgRGMq5OuYds8v .node.clickable{cursor:pointer;}#mermaid-svg-tsLgRGMq5OuYds8v .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-tsLgRGMq5OuYds8v .arrowheadPath{fill:#333333;}#mermaid-svg-tsLgRGMq5OuYds8v .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-tsLgRGMq5OuYds8v .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-tsLgRGMq5OuYds8v .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tsLgRGMq5OuYds8v .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-tsLgRGMq5OuYds8v .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tsLgRGMq5OuYds8v .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-tsLgRGMq5OuYds8v .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-tsLgRGMq5OuYds8v .cluster text{fill:#333;}#mermaid-svg-tsLgRGMq5OuYds8v .cluster span{color:#333;}#mermaid-svg-tsLgRGMq5OuYds8v div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-tsLgRGMq5OuYds8v .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-tsLgRGMq5OuYds8v rect.text{fill:none;stroke-width:0;}#mermaid-svg-tsLgRGMq5OuYds8v .icon-shape,#mermaid-svg-tsLgRGMq5OuYds8v .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-tsLgRGMq5OuYds8v .icon-shape p,#mermaid-svg-tsLgRGMq5OuYds8v .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-tsLgRGMq5OuYds8v .icon-shape .label rect,#mermaid-svg-tsLgRGMq5OuYds8v .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-tsLgRGMq5OuYds8v .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-tsLgRGMq5OuYds8v .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-tsLgRGMq5OuYds8v :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 通过
通过
漂移告警
阻断
阻断
阻断
第三道防线:AI 主动感知
PR 提交时 AI diff 分析
实现与规范语义对比
自动生成规范更新 PR
第二道防线:运行时契约
Pact Consumer-Driven Contract
Spring Cloud Contract
自动生成契约测试
第一道防线:静态检测
spectral lint 规范格式检查
openapi-diff 检测接口变更
tsc 类型守门
开发者修复
PR 不可合并
七、工具链推荐
| 场景 | 推荐工具 | 说明 |
|---|---|---|
| 规范编写 | Stoplight Studio / Swagger Editor | 可视化 OpenAPI 编辑 |
| 规范 Lint | Spectral | 自定义规范校验规则 |
| 契约测试 | Pact.io | Consumer-Driven Contract |
| AI 编码 | WorkBuddy / Cursor | 结合 SKILL.md 使用 |
| 测试框架 | JUnit 5 + AssertJ(Java)/ Jest(TS) | 可读性强 |
| 覆盖率 | JaCoCo / Istanbul | CI 集成门槛检查 |
| 规范版本管理 | Git + Conventional Commits | 规范变更可追溯 |
| 接口 Mock | WireMock / MSW | 基于规范自动 Mock |
| 代码生成 | openapi-generator / swagger-codegen | 规范生成代码骨架 |
八、常见误区与解答
❌ 误区 1:有了 AI,不需要写测试了
实际情况: AI 生成的代码不能自证正确,测试是唯一客观标准。没有测试的 AI 代码库,熵增速度是人工代码库的 3 倍。
误区 2:规范文档太重了,敏捷开发不需要
实际情况: SDD 的规范不是传统的 Word 文档,而是机器可读的 YAML/JSON/TS 文件。写好一份 OpenAPI,AI 可以反复生成:测试、Mock、文档、代码------这是杠杆效应。
误区 3:TDD 和 SDD 选一个就够了
实际情况: SDD 管"写什么 ",TDD 管"对不对"。两者是正交关系,缺一不可。规范告诉 AI 边界,测试告诉 AI 预期。
误区 4:AI 会自动保持规范更新
实际情况: AI 不会主动识别规范漂移,需要在 CI 中设置强制检查点,或使用 AI 定期执行规范审计任务。
九、总结:融合方法论的核心思想
#mermaid-svg-ndlEvwPNMFbzlIeJ{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ndlEvwPNMFbzlIeJ .error-icon{fill:#552222;}#mermaid-svg-ndlEvwPNMFbzlIeJ .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ndlEvwPNMFbzlIeJ .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ndlEvwPNMFbzlIeJ .marker.cross{stroke:#333333;}#mermaid-svg-ndlEvwPNMFbzlIeJ svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ndlEvwPNMFbzlIeJ p{margin:0;}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge{stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section--1 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section--1 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section--1 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section--1 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section--1 path{fill:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section--1 text{fill:#ffffff;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon--1{font-size:40px;color:#ffffff;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge--1{stroke:hsl(240, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth--1{stroke-width:17;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section--1 line{stroke:hsl(60, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-0 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-0 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-0 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-0 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-0 path{fill:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-0 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-0{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-0{stroke:hsl(60, 100%, 73.5294117647%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-0{stroke-width:14;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-0 line{stroke:hsl(240, 100%, 83.5294117647%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-1 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-1 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-1 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-1 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-1 path{fill:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-1 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-1{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-1{stroke:hsl(80, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-1{stroke-width:11;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-1 line{stroke:hsl(260, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-2 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-2 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-2 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-2 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-2 path{fill:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-2 text{fill:#ffffff;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-2{font-size:40px;color:#ffffff;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-2{stroke:hsl(270, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-2{stroke-width:8;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-2 line{stroke:hsl(90, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-3 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-3 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-3 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-3 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-3 path{fill:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-3 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-3{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-3{stroke:hsl(300, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-3{stroke-width:5;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-3 line{stroke:hsl(120, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-4 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-4 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-4 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-4 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-4 path{fill:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-4 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-4{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-4{stroke:hsl(330, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-4{stroke-width:2;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-4 line{stroke:hsl(150, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-5 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-5 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-5 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-5 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-5 path{fill:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-5 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-5{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-5{stroke:hsl(0, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-5{stroke-width:-1;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-5 line{stroke:hsl(180, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-6 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-6 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-6 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-6 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-6 path{fill:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-6 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-6{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-6{stroke:hsl(30, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-6{stroke-width:-4;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-6 line{stroke:hsl(210, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-7 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-7 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-7 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-7 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-7 path{fill:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-7 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-7{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-7{stroke:hsl(90, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-7{stroke-width:-7;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-7 line{stroke:hsl(270, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-8 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-8 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-8 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-8 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-8 path{fill:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-8 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-8{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-8{stroke:hsl(150, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-8{stroke-width:-10;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-8 line{stroke:hsl(330, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-9 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-9 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-9 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-9 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-9 path{fill:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-9 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-9{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-9{stroke:hsl(180, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-9{stroke-width:-13;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-9 line{stroke:hsl(0, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-10 rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-10 path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-10 circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-10 polygon,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-10 path{fill:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-10 text{fill:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .node-icon-10{font-size:40px;color:black;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-edge-10{stroke:hsl(210, 100%, 76.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge-depth-10{stroke-width:-16;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-10 line{stroke:hsl(30, 100%, 86.2745098039%);stroke-width:3;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:lightgray;}#mermaid-svg-ndlEvwPNMFbzlIeJ .disabled text{fill:#efefef;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-root rect,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-root path,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-root circle,#mermaid-svg-ndlEvwPNMFbzlIeJ .section-root polygon{fill:hsl(240, 100%, 46.2745098039%);}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-root text{fill:#ffffff;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-root span{color:#ffffff;}#mermaid-svg-ndlEvwPNMFbzlIeJ .section-2 span{color:#ffffff;}#mermaid-svg-ndlEvwPNMFbzlIeJ .icon-container{height:100%;display:flex;justify-content:center;align-items:center;}#mermaid-svg-ndlEvwPNMFbzlIeJ .edge{fill:none;}#mermaid-svg-ndlEvwPNMFbzlIeJ .mindmap-node-label{dy:1em;alignment-baseline:middle;text-anchor:middle;dominant-baseline:middle;text-align:center;}#mermaid-svg-ndlEvwPNMFbzlIeJ :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} TDD + SDD\nAI Coding
SDD 规范层
OpenAPI 接口约定
TypeScript 类型定义
ADR 架构决策
SKILL.md AI任务规范
TDD 验证层
单元测试
集成测试
契约测试
E2E 测试
AI 协作层
规范→代码骨架
测试→驱动实现
AI→补全逻辑
CI→守护质量
工程文化
规范即文档
测试即规格
AI 是工具
人是决策者
三句话总结:
- SDD 是 AI 的"航线图":先定义清楚要去哪里,AI 才能飞得准。
- TDD 是 AI 的"落地检查":飞到了之后,测试告诉你有没有偏航。
- 融合是必然趋势:AI Coding 不是替代工程实践,而是让工程实践的价值被放大。
在 AI 时代,代码的质量取决于规范的质量 和测试的密度,而不仅仅是 AI 模型的能力。工程师的核心竞争力,正在从"能写代码"转变为"能定义规范、能设计测试、能驾驭 AI"。
参考资料
- Test-Driven Development: By Example - Kent Beck
- OpenAPI Specification 3.1
- Specification-Driven Development with AI
- Pact Contract Testing
- Spectral OpenAPI Linter
- 《规范驱动开发·以Skill为核心的智能交付》------ 泰合产品本部内部分享
本文首发于 CSDN,转载请注明出处。如有技术交流,欢迎评论区留言。