当 AI 可以一秒生成百行代码,我们真正需要的不再是"写得快",而是"写得对"。三种经典的开发方法论——SDD、BDD、TDD——在 AI 编程时代迎来了真正的黄金期。

引言

2025 年,软件开发的世界发生了一个微妙的转变:瓶颈不再是写代码的速度,而是定义"什么是对的代码"

当 Cursor、Claude Code、GitHub Copilot 可以在几秒钟内生成完整的函数实现时,开发者面临的挑战从"怎么写"变成了"怎么确保 AI 写的对"。AI 生成的代码看起来很专业——变量命名规范、缩进整齐、逻辑似乎合理——但它可能调用了不存在的 API,引入了不必要的复杂度,或者偏离了你真正的业务意图。

GitHub 自己的研究承认,Copilot 的引入对代码质量产生了"下行压力"。开发者推送了更多的错误、更多的冗余代码,同时感觉自己更"高效"了。这就是 AI 编程的核心矛盾:速度上去了,质量控制没跟上

解决这个问题的答案,其实已经存在了几十年。Spec-Driven Development(SDD)、Behavior-Driven Development(BDD)和 Test-Driven Development(TDD)——这三种方法论在 AI 时代不仅没有过时,反而找到了它们真正的用武之地。它们提供的正是 AI 编程最缺乏的东西:明确的意图定义、可验证的行为规范、以及自动化的质量守护

本文将深入分析这三种方法论在 AI 编程中的实践价值、具体工作流和组合策略。


一、三种方法论的核心理念

TDD:用测试定义"完成"

TDD 由 Kent Beck 在 90 年代末作为极限编程(XP)的一部分提出,其核心循环简洁有力:

Red → Green → Refactor

  1. Red:先写一个失败的测试,精确描述你期望的行为
  2. Green:写最少的代码让测试通过
  3. Refactor:在测试保护下优化代码结构

TDD 的本质不是"测试",而是用可执行的测试来驱动设计。测试是你对"正确"的定义,是你和 AI 之间的契约。

BDD:用自然语言桥接业务与技术

BDD 由 Dan North 在 2006 年提出,是对 TDD 的自然延伸。它用结构化的自然语言(最著名的是 Gherkin 语法)来描述软件行为:

Feature: 用户登录
  Scenario: 使用有效凭证登录
    Given 用户在登录页面
    When 输入正确的用户名和密码
    Then 应该收到 JWT token
    And 跳转到首页

  Scenario: 使用无效凭证登录
    Given 用户在登录页面
    When 输入错误的密码
    Then 应该收到 401 错误
    And 显示"用户名或密码错误"

BDD 的核心理念是通过具体的例子(Examples)来建立共同理解。传统的"三个朋友"(Three Amigos)实践要求产品、开发和测试坐在一起,用 Given/When/Then 来讨论和定义需求。

SDD:规格说明即真理之源

SDD 是 AI 时代的新方法论,它的核心理念是:在写任何代码之前,先写一份结构化的规格说明(Spec),这份 Spec 成为人和 AI 共同的真理之源

GitHub 的 Spec Kit 文档说得好:“在这个新世界里,维护软件意味着演化规格说明。开发的主要语言上升到了更高的层次,代码只是最后一公里。”

SDD 有三个递进的实践层次:

层次 名称 含义
1 Spec-first 先写 Spec,再用 AI 辅助开发
2 Spec-anchored Spec 在开发完成后继续维护,作为演化和维护的锚点
3 Spec-as-source Spec 是主要源文件,人只编辑 Spec,代码由 AI 生成且标记"请勿编辑"

Microsoft 在其 SDD 实践指南中指出:“Spec 质量 = 输出质量。” 这句话精炼地概括了 SDD 的核心前提。


二、TDD 在 AI 编程中的实践

为什么 AI 让 TDD 变得不可或缺

AI 编程引入了一个根本性挑战:非确定性。相同的 Prompt 可能产生截然不同的代码。这种变异性虽然允许探索不同方案,但在没有测试基础设施的情况下,会导致质量控制的灾难。

TDD 提供了确定性的退出标准。不再依赖 AI 的"判断"来决定代码是否完成,而是用测试来强制约束——所有测试通过,才算完成。

Simon Willison 精准地总结了这一点:“测试给了我们可靠的退出标准。我们不依赖 AI 的心血来潮,而是强制它迭代直到先前失败的测试通过。”

AI 时代的 TDD 新工作流

传统 TDD 是 Red-Green-Refactor,在 AI 时代,这个循环进化了:

Step 1:定义行为(Specify)

不再从空白文件开始,而是描述你想要的行为。用具体的输入和输出来定义:

需求:验证用户凭证并返回 JWT token
- 输入有效凭证 → 返回 JWT
- 输入无效凭证 → 返回 401
- 输入空密码 → 返回 400 验证错误

Step 2:AI 生成测试(Red Phase)

让 AI 生成测试代码。Prompt 示例:

“为一个用户认证函数写单元测试。函数接收用户名和密码,验证成功返回 JWT token,失败抛出 401 异常。”

AI 生成的测试需要你仔细审查——它们现在就是你的规格说明。删除不需要的测试,补充 AI 遗漏的边界情况。

Step 3:运行测试,确认全部失败

$ pytest test_auth.py
FAILED test_valid_credentials
FAILED test_invalid_credentials  
FAILED test_empty_password

Step 4:AI 生成实现(Green Phase)

现在让 AI 实现:

“写代码让这些测试通过。不要添加测试之外的功能。”

关键约束是"不要添加测试以外的功能"——这直接对抗 AI 过度工程的倾向。

Step 5:迭代修复

如果测试没通过,把失败信息喂给 AI:

“test_valid_credentials 失败了,错误信息是 ImportError: No module named ‘jwt’。修复这个问题。”

Step 6:重构

测试全通过后,可以让 AI 优化代码结构,测试作为安全网确保不引入回归。

实践案例:Python 用户认证模块

# Step 2: AI 生成的测试 (test_auth.py)
import pytest
from auth import authenticate_user

def test_valid_credentials_returns_jwt():
    token = authenticate_user("alice", "secure_password_123")
    assert token is not None
    assert isinstance(token, str)
    assert len(token) > 0

def test_invalid_credentials_raises_error():
    with pytest.raises(PermissionError) as exc_info:
        authenticate_user("alice", "wrong_password")
    assert "401" in str(exc_info.value)

def test_empty_password_raises_value_error():
    with pytest.raises(ValueError):
        authenticate_user("alice", "")

# Step 4: AI 生成的实现 (auth.py)
import jwt
import time
from datetime import datetime, timedelta

SECRET_KEY = "your-secret-key"

def authenticate_user(username: str, password: str) -> str:
    if not password:
        raise ValueError("Password cannot be empty")
    
    # 模拟用户数据库查找
    user_db = {"alice": "secure_password_123"}
    
    if username not in user_db or user_db[username] != password:
        raise PermissionError("401: Invalid credentials")
    
    payload = {
        "sub": username,
        "exp": datetime.utcnow() + timedelta(hours=24)
    }
    return jwt.encode(payload, SECRET_KEY, algorithm="HS256")

TDD 抵消 AI 的三大顽疾

AI 的问题 TDD 的对策
正确性:AI 幻觉出不存在的 API,逻辑看似合理实则有误 测试是即时验证,AI 无法蒙混过关
膨胀性:AI 习惯过度工程,添加不需要的字段和函数 TDD 的最小主义哲学——只写让测试通过的代码
架构碎片化:AI 倾向于粘贴新代码而非复用已有工具 测试强制你思考组件间的交互方式

Kent Beck 本人在 AI 时代重新强调了 TDD 的价值:“当机器写代码比人快时,我们的价值转移到了正确定义问题和验证解决方案上。”


三、BDD 在 AI 编程中的实践

BDD 的困境与 AI 的解药

BDD 一直是一个"几乎太好了"的理念。它承诺用自然语言统一产品、开发和测试三方的理解,但在实践中,它经常退化成 QA 工程师的独角戏:

  • 产品经理用自己的格式写需求
  • 开发按自己的理解写代码
  • QA 工程师事后补写 BDD 场景来测试已完成的代码
  • 没人再读这些场景

维护负担也令人头疼:每个 Gherkin 步骤需要对应的"步骤定义"(Step Definition)代码。随着场景增长,步骤定义爆炸式增长,UI 改一个按钮就破坏几十个测试。

AI 改变了一切。那些友好的 Given/When/Then 语句,对 LLM 来说就像量身定做的输入格式。AI 可以:

  1. 消除胶水代码:不再需要手写步骤定义,AI 直接理解自然语言意图并执行
  2. 自动生成场景:从需求文档自动生成 Gherkin 场景
  3. 自愈测试:当 UI 变化时,AI 通过语义理解定位元素,而非依赖脆弱的 CSS 选择器
  4. 桥接技术鸿沟:产品经理写自然语言场景,AI 负责技术实现

AI + BDD 的实践工作流

Step 1:三方协作定义行为

产品经理(或开发者代劳)用 Given/When/Then 描述业务场景。不需要担心技术可行性——这是沟通工具,不是代码。

Step 2:AI 生成完整测试套件

将 Gherkin 场景喂给 AI,让它生成可执行的测试代码:

Prompt: "根据以下 BDD 场景,使用 Playwright 生成 E2E 测试代码:

Given 用户已注册并验证邮箱
When 用户在登录页输入有效凭证并点击登录
Then 用户应该看到首页仪表盘
And 页面应显示欢迎消息"

Step 3:AI 维护和演化

当需求变化时,更新 Gherkin 场景,AI 自动更新对应的测试代码。无需手动维护步骤定义。

为什么 Gherkin 在 LLM 时代焕发新生

Hung Doan 在他的博客中分享了一个深刻洞察:LLM 天然适合处理结构化的 Gherkin 语法。Gherkin 的清晰规则和结构化格式,为 LLM 提供了完美的约束条件——而约束正是让 AI 输出可靠的关键。

在 LLM 出现之前,BDD + Gherkin 最大的痛点是步骤定义的设计和维护成本。创建一个步骤定义字典需要精细的平衡:太具体则无法复用,太抽象则难以理解。而 LLM 可以:

  • 自动为步骤定义保持一致的命名
  • 将需求翻译成基于已有步骤字典的 Gherkin 测试
  • 在你写完草稿后,自动精炼并匹配已有步骤定义

这意味着 BDD 社区长期面临的最大工程障碍,被 AI 自然地消解了。


四、SDD 在 AI 编程中的实践

SDD 的核心逻辑

SDD 是对 AI 编程工作流的重构。传统流程是"先写 Prompt,再对齐",SDD 则是"先对齐,再让 AI 执行"。

Microsoft 的实践总结道:“Spec-First 工作流改变了动态。团队不再让 AI 从散落的 Prompt 中推断意图,而是明确定义意图,然后让 AI 执行。”

SDD 工作流(以 GitHub Spec Kit 为例)

GitHub Spec Kit 的 SDD 生命周期:

graph LR
    A[Constitution<br/>宪法] --> B[Specify<br/>规格化]
    B --> C[Clarify<br/>澄清]
    C --> D[Plan<br/>规划]
    D --> E[Tasks<br/>任务分解]
    E --> F[Implement<br/>实现]
    F --> G[Validate<br/>验证]
  1. Constitution(宪法):定义项目的不可变原则——架构规范、编码标准、安全约束
  2. Specify(规格化):捕获需求、场景和验收标准
  3. Clarify(澄清):解决歧义、依赖和边界情况
  4. Plan(规划):将意图转化为架构、流程和约束
  5. Tasks(任务分解):拆分为可实现的工作单元
  6. Implement(实现):AI 生成和迭代代码
  7. Validate(验证):验证输出与 Spec 一致

实践案例:使用 SDD 构建任务管理 API

Constitution 文件 (spec-kit/constitution.md):

# Project Constitution

## Architecture
- Python 3.12+ with FastAPI
- PostgreSQL database with SQLAlchemy ORM
- All endpoints return JSON

## Standards
- RESTful API conventions
- All public functions must have type hints
- Authentication via JWT Bearer tokens
- Error responses follow RFC 7807 format

Spec 文件 (specs/task-api/spec.md):

# Task Management API Spec

## Feature: Task CRUD Operations

### Requirements
- REQ-001: Users can create tasks with title, description, and due date
- REQ-002: Users can list all tasks, filtered by status
- REQ-003: Users can update task status (todo, in_progress, done)
- REQ-004: Users can delete tasks they own

### Acceptance Criteria

#### REQ-001: Create Task
- GIVEN an authenticated user
- WHEN they POST to /tasks with valid task data
- THEN a new task is created with status "todo"
- AND the response includes the created task with its assigned ID

#### Edge Cases
- Title is required, max 200 characters
- Due date must be in the future
- Description is optional, max 2000 characters

Task 文件 (specs/task-api/tasks.md):

# Implementation Tasks

- [ ] T001: Create Task model and migration [REQ-001]
- [ ] T002: Implement POST /tasks endpoint [REQ-001]
- [ ] T003: Implement GET /tasks with filtering [REQ-002]
- [ ] T004: Implement PATCH /tasks/:id for status update [REQ-003]
- [ ] T005: Implement DELETE /tasks/:id [REQ-004]
- [ ] T006: Write integration tests for all endpoints

然后,你逐个将 Task 喂给 Claude Code 或 Cursor,AI 在 Constitution 约束下,按照 Spec 的定义来实现每个任务。

SDD 工具生态

当前 SDD 领域有三个代表性工具:

工具 来源 特点 SDD 层次
Kiro AWS 轻量级,Requirements → Design → Tasks 三步流程 Spec-first
Spec Kit GitHub/Microsoft 开源 CLI,支持多种 Coding Agent,Constitution 机制强大 Spec-first
Tessl Tessl 追求 Spec-as-source,代码标记"由 Spec 生成,请勿编辑" Spec-as-source

Martin Fowler 的分析指出,这三个工具虽然都自称 SDD,但差异显著。选择哪个取决于你的 SDD 成熟度:从 Spec-first 起步,逐步走向 Spec-anchored。


五、三者对比与适用场景

核心差异

维度 TDD BDD SDD
关注点 代码正确性 业务行为 系统意图
核心产物 测试代码 Gherkin 场景 结构化 Spec 文档
抽象层次 函数/模块级 功能/场景级 系统/功能级
主要驱动者 开发者 产品+开发+QA 架构师+产品+开发
在 AI 工作流中的角色 验证机制 沟通桥梁 真理之源

各自的 AI 编程最佳场景

TDD 最适合:

  • 算法和核心逻辑实现(排序、搜索、数据处理)
  • API 端点开发(输入输出明确,适合测试驱动)
  • Bug 修复(先写复现测试,再让 AI 修复)
  • 重构现有代码(测试作为安全网)

BDD 最适合:

  • 用户交互密集的功能(登录注册、表单提交、工作流)
  • 需要跨角色对齐的复杂业务场景
  • E2E 测试和验收测试
  • 产品需求到测试用例的自动转化

SDD 最适合:

  • 新项目(Greenfield)的整体架构设计
  • 复杂功能的端到端实现(多服务协作、跨团队协调)
  • 合规敏感行业(医疗、航空、汽车——DO-178C、IEC 62304、ISO 26262 要求可追溯性)
  • 团队规模化使用 AI 编程工具时的标准化

六、混合模式:三者如何协同

在实际的 AI 编程实践中,这三种方法论不是互斥的——最强的实践是三者的融合

混合工作流

graph TD
    A["SDD (Spec)<br/>定义'建什么'"] --> B["BDD (Scenarios)<br/>定义'用户怎么用'"]
    B --> C["TDD (Tests)<br/>定义'怎么验证'"]
    C --> D["AI Implementation<br/>AI 实现"]
    D --> E["Validation<br/>全链路验证"]

阶段 1:用 SDD 定义系统意图

写一份结构化 Spec,涵盖业务需求、约束条件、验收标准。这一步可能产出:

  • constitution.md:架构原则和约束
  • feature-spec.md:功能规格说明
  • tasks.md:任务分解

阶段 2:用 BDD 描述关键行为

从 Spec 中提取关键场景,用 Given/When/Then 写成 BDD 场景。这些场景既是文档,也是后续 AI 生成测试的输入:

# 从 SDD Spec 中派生的 BDD 场景
Feature: Task Management
  Scenario: 创建新任务
    Given 一个已认证的用户
    When 发送 POST /tasks 请求,包含标题"完成季度报告"
    Then 返回 201 状态码
    And 响应体包含任务 ID
    And 任务状态为 "todo"

阶段 3:用 TDD 实现和验证

将 BDD 场景转化为单元测试和集成测试,然后让 AI 在测试约束下实现代码。

阶段 4:AI 全链路实现

将 Constitution + Spec + Tests 一并提供给 AI Agent,让它在完整的上下文中实现。

具体的 Prompt 示例

你是一个专业的 Python 开发者。请按照以下约束实现任务管理 API。

## 项目宪法(不可违反)
- FastAPI + PostgreSQL + SQLAlchemy
- 所有函数必须有类型注解
- 错误响应遵循 RFC 7807

## 功能 Spec(需求来源)
[粘贴 feature-spec.md]

## BDD 场景(验收标准)
[粘贴 Gherkin 场景]

## 单元测试(必须通过)
[粘贴 pytest 测试代码]

请实现 tasks.py,确保所有测试通过。

这个 Prompt 的强大之处在于:它利用了 SDD 的上下文(Constitution + Spec)、BDD 的行为定义(场景)、TDD 的验证机制(测试),三者形成了一个完整的约束体系。AI 在这样的约束下,生成可靠代码的概率大大提升。


七、实践建议与避坑指南

1. 从轻量开始,逐步加码

不要一上来就全面采用 SDD + BDD + TDD。建议的演进路径:

  1. 第一步:在 AI 编程中加入 TDD 实践——先写测试,再让 AI 实现
  2. 第二步:对复杂功能写 BDD 场景,帮助理清业务逻辑
  3. 第三步:引入 SDD 工具(如 Spec Kit),对项目级的工作进行结构化管理

2. 规格说明的质量决定一切

Microsoft 的实践报告反复强调:Spec 质量 = 输出质量。一份模糊的 Spec 会产生模糊的代码。好的 Spec 应该:

  • 用具体的例子,不用抽象描述
  • 明确说"不做什么",而非只说"做什么"
  • 包含边界情况和异常路径
  • 每个需求都有明确的验收标准

3. 警惕 AI 生成测试的陷阱

AI 生成的测试可能:

  • 测试实现而非行为:测试内部细节而非外部可观察的行为
  • 过度乐观:遗漏关键的边界情况
  • 互相依赖:测试之间没有正确隔离

始终让人审查 AI 生成的测试。测试是你的规格说明,如果规格错了,一切都错了。

4. 不要追求 Spec-as-Source

对于大多数团队,Spec-first 或 Spec-anchored 是更现实的选择。Spec-as-source(只编辑 Spec,不碰代码)虽然在概念上很优雅,但在实践中面临非确定性挑战——同一个 Spec 多次生成可能产生不同的代码。

5. 右 sizing 你的流程

不是每个改动都需要完整的 SDD 生命周期。Microsoft 的建议是:“采用应该量体裁衣。” 一个 bug fix 可能只需要 TDD,一个新功能可能需要 BDD + TDD,一个新系统才需要完整的 SDD。


八、未来展望

从 Code-First 到 Spec-First 的范式转移

我们正在经历软件开发的一次根本性范式转移:代码不再是主要产物,规格说明才是。当 AI 可以可靠地从 Spec 生成代码时,开发者的核心技能从"写代码"转向"写 Spec"和"验证正确性"。

Martin Fowler 在分析 SDD 时指出,Spec-as-source 模式类似于编程语言中的编译过程——你写高级语言(Spec),“编译器”(AI)生成低级语言(代码)。不同的是,这个"编译器"是非确定性的,所以你需要更强的验证机制。

AI 工具的深度融合

未来的 AI 编程工具将原生支持 SDD/BDD/TDD 工作流:

  • IDE 集成:在编辑器中直接显示 Spec 与代码的对应关系
  • 自动追溯:每个代码变更自动关联到 Spec 中的需求
  • 测试即需求:AI 自动从 Spec 生成并维护测试套件
  • Spec 演化:当代码变更时,AI 提示更新 Spec,反之亦然

测试技能的复兴

在 AI 时代,最值钱的开发者技能不是写代码,而是定义问题和验证答案。TDD 的核心洞察——先定义成功标准再开始——从未如此重要。正如一位实践者所说:“我们正在从代码工人变成质量架构师。”


总结

方法论 在 AI 时代的角色 核心价值
SDD AI 工作流的骨架 确保构建"正确的东西"
BDD 人与 AI 的沟通协议 确保理解"什么是正确的"
TDD AI 输出的质量守护 确保"东西构建得正确"

三种方法论在 AI 编程中形成了完美的互补:SDD 定义系统意图,BDD 描述行为细节,TDD 验证实现正确性。它们共同解决了 AI 编程的核心挑战——如何确保 AI 生成的是对的,而不仅仅是快的

DHH 在 2014 年宣告"TDD 已死",但在 2025 年,我们发现 TDD 不是死了,而是等到了它真正的时代。当 AI 可以一秒生成百行代码,我们比以往任何时候都更需要明确的质量标准。SDD、BDD、TDD 提供的正是这个标准。

在 AI 编程时代,写代码是最容易的部分。定义"什么是正确的代码",才是真正的专业能力。


参考资料: