SDD实战避坑指南:AI生成应用的20种失败模式与纠偏策略
在生成式 AI 辅助编程的发展过程中,逐渐凸显出一个核心矛盾:AI 的编码能力远超其对系统真实意图的理解能力。这种能力错位也使得“Vibe Coding”所带来的问题被开发人员快速发现——代码生成迅速、看似可用,却常常违背架构约束,导致后期维护成本激增。为应对这一挑战,开发者自然转向规范驱动开发(Spec-Driven Development, SDD)。
何时不该用 SDD?
在生成式 AI 辅助编程的发展过程中,逐渐凸显出一个核心矛盾:AI 的编码能力远超其对系统真实意图的理解能力。这种能力错位也使得“Vibe Coding”所带来的问题被开发人员快速发现——代码生成迅速、看似可用,却常常违背架构约束,导致后期维护成本激增。
为应对这一挑战,开发者自然转向规范驱动开发(Spec-Driven Development, SDD)。然而,SDD 并非万能解药。盲目推行反而可能拖慢节奏、徒增负担。以下六类场景中,不建议采用 SDD:
- 探索性原型(Exploratory Prototyping):当你尚不清楚最终目标,仅想快速验证技术可行性时,直接编写代码更为高效。
- 微小任务(Tiny Tasks):例如调整一行 CSS 样式或修正拼写错误。此类操作无需复杂的规范流程。
- 缺乏领域知识:若你无法判断一份规范是否合理,让 AI 先生成规范再生成代码,无异于“盲人引路”,风险极高。
- 基础设施受限:在无法运行 CI/CD 或容器化环境的系统中,AI 生成的代码难以自动验证,SDD 易退化为形式化的文档工作。
- 紧急热修复(Hotfix):生产环境宕机时,应优先恢复服务,而非走完“设计 → 任务拆解”的完整流程。
- 主观性 UI 调整:如“让 Logo 更大一点,感觉更活泼些”这类感性需求,难以被结构化描述,也不适合纳入 SDD 流程。
SDD能做什么?
SDD 的核心理念是:“意图即真理”(Intent is the source of truth)。传统 AI 编程依赖“Prompt → Code”的线性模式,高度依赖开发者的即时记忆与提示技巧,缺乏上下文持久化与系统性约束。
SDD 引入了一个关键中间层——规格(Spec)。它既是人类意图的结构化表达,也是 AI 执行的刚性边界。
SDD 降低不确定性的原理
Martin Fowler 在其分析文章中指出,AI 编程的最大风险在于非确定性(Non-determinism)。SDD 通过三大机制有效缓解这一问题:
- 上下文固化
将易逝的对话转化为持久的 Markdown 文档(如 requirements.md),作为 AI 的“外部记忆”,避免因对话轮次增加而“遗忘”关键约束。
- 思维链显式化
强制将“需求 → 设计 → 实现”的隐式思考拆解为独立的物理文件,使评审可前置至设计阶段,而非等到代码完成才暴露架构缺陷。
- 任务原子化
AI 不再一次性生成完整功能,而是基于设计文档输出一系列微小、可测试的任务单元,显著缩小错误传播范围。
化的 Markdown 文档。Spec 文件(如 requirements.md)充当了 AI 的 “外部存储器”,防止了随着对话轮次增加而出现的 “遗忘” 现象。
主流 SDD 流派对比:Kiro vs. Spec Kit
目前业界主要有两类 SDD 实践路径:
| 阶段属性 | Kiro(三阶段) | GitHub Spec Kit(四阶段) | 核心产出物 | AI 角色 |
|---|---|---|---|---|
| 需求定义 | Requirements | Specify | 用户故事、成功标准 | 产品经理 / 业务分析师 |
| 架构设计 | Design | Plan | 接口、数据流、API 定义 | 软件架构师 |
| 任务拆解 | Tasks | Tasks | 实施步骤、测试计划 | 技术负责人 |
| 代码实现 | Execution(隐式) | Implement | 源码、测试、配置 | 初级开发者 |
值得注意的是,Spec Kit 引入了 Constitution(规约)概念——即项目级全局约束(如“必须使用 TypeScript”),而 Kiro 通常通过系统提示(System Prompt)实现类似效果。
SDD 成熟度模型:从文档优先到规格即源码
根据Birgitta Böckeler 的研究,SDD 实践可分为三个层级:
- Level 1:Spec-First(文档优先)
代码生成前撰写规范,但后续不再维护。易出现“文档腐烂”。 - Level 2:Spec-Anchored(规格锚定)
规范与代码同等重要,任何变更须先更新 Spec。这是推荐的工程目标。 - Level 3:Spec-As-Source(规格即源码)
代码完全由 AI 从 Spec 自动生成,人类不直接修改代码。目前仍属实验阶段。
失败模式全景
我们将 SDD 四阶段中的典型失效模式总结为 20 种(详见原文表格):
本文所指的 “失败(Failure)”不是指 AI 模型本身的幻觉(Hallucination)或提示注入攻击,而是指A工程过程的失效(Process Failure)。
Phase A: Requirements 阶段(需求定义)
此阶段的核心风险在于 “输入模糊” 与 “上下文过载”。如果种子不纯,果实必烂。
| ID | 失败模式 (Name) | 典型表现 (Manifestation) | 根本原因 (Root Cause) | 最早信号 (Signal) |
|---|---|---|---|---|
| R01 | Ambiguity Avalanche (歧义雪崩) | AI 对一句话需求脑补出大量无关功能。 | Spec 缺乏边界约束(Negative Constraints)。 | Requirements 文档字数暴涨,出现用户未提及的术语。 |
| R02 | Scope Creep Injection (范围蔓延) | AI 自动添加 “用户注册”、“通知系统” 等通用模块。 | 模型过度拟合通用 SaaS 模板,缺乏对当前 MVP 的聚焦。 | Design 中出现了 Requirements 里没提到的模块。 |
| R03 | Context Amnesia (上下文失忆) | 新需求不仅不兼容旧需求,还隐式覆盖了旧规则。 | 缺乏全局的 “Constitution” 或 Memory Bank 机制。 | 生成的 User Story 与现有系统逻辑冲突。 |
| R04 | Premature AI (过早追求智能) | [案例] 骨架未通就开始调优 RAG 或 Prompt。 | 混淆了 “构建系统” 与 “构建模型” 的优先级。 | Spec 中出现大量关于 Prompt 策略而非业务逻辑的描述。 |
| R05 | The Golden Hammer (金锤谬误) | 强行用 AI 解决正则就能搞定的问题。 | 对 AI 能力的盲目崇拜,忽视了确定性方案的价值。 | Requirements 中没有定义 “非 AI 兜底方案”。 |
Phase B: Design 阶段(架构设计)
此阶段是 “Vibe Coding” 最容易失控的环节。AI 倾向于为了实现的便利性而牺牲架构的严谨性。
| ID | 失败模式 (Name) | 典型表现 (Manifestation) | 根本原因 (Root Cause) | 最早信号 (Signal) |
|---|---|---|---|---|
| D01 | The Ivory Tower (象牙塔架构) | [案例] 写了大量泛型、抽象类,但只有一种实现。 | AI 模仿了企业级 Java/C# 的过度设计风格。 | Design 文档中类图极其复杂,但数据流很简单。 |
| D02 | The Spaghetti Trap (耦合陷阱) | [案例] UI 组件直接包含 SQL 查询或 API 密钥。 | 缺乏显式的 Layering(分层)指令。 | Design 中缺少 Service/Repository 层的定义。 |
| D03 | Interface Drift (接口漂移) | [案例] 修改了公共函数签名,导致并未修改的调用方崩溃。 | AI 为了当前任务便利,忽视了接口契约的稳定性。 | `git diff` 显示核心 Interface 文件被修改。 |
| D04 | Security Blindspot (安全盲区) | API 设计中完全缺失鉴权(AuthZ)参数。 | Spec 中未强制包含安全视图(Security View)。 | Design 里的 API 定义没有 `token` 或 `user_id` 参数。 |
| D05 | Tech Stack Hallucination (技术栈幻觉) | 设计了一个不存在的库函数或 AWS 服务。 | 训练数据截止日期的滞后性或混淆了不同库的 API。 | Design 引用了 Google 搜不到的方法名。 |
Phase C: Tasks 阶段(任务规划)
任务规划的质量决定了执行的成败。AI 往往不擅长处理任务间的时序依赖。
| ID | 失败模式 (Name) | 典型表现 (Manifestation) | 根本原因 (Root Cause) | 最早信号 (Signal) |
|---|---|---|---|---|
| T01 | Sample Starvation (样例缺失) | [案例] AI 瞎猜代码风格,生成的代码与现存风格迥异。 | Prompt 中缺乏 One-shot Example。 | Task 描述中没有 `Reference: xxx.ts` 的指引。 |
| T02 | Task Atomicity Violation (原子性破坏) | 一个 Task 包含了 “建表、写后端、写前端” 所有工作。 | AI 懒惰,未能进行细粒度的 Work Breakdown。 | 单个 Task 的预估 Token 数超过上下文窗口限制。 |
| T03 | Dependency Deadlock (依赖死锁) | Task B 依赖 Task A 的接口,但 B 被排在 A 前面。 | 缺乏拓扑排序(Topological Sort)逻辑。 | Task 列表中没有明确的前置条件(Prerequisites)。 |
| T04 | The Happy Path Bias (乐观路径偏差) | 所有 Task 都在实现功能,没有一个 Task 处理错误。 | AI 的生成偏好是 “完成功能” 而非 “健壮性”。 | Task 列表中没有包含 `Error Handling` 关键词。 |
| T05 | Validation Vacuum (验证真空) | Task 完成的定义是 “写完代码”,而不是 “测试通过”。 | Definition of Done (DoD) 缺失。 | Task 描述中没有提到具体的 Test Case。 |
Phase D: Implement 阶段(代码实现)
这是代码实际生成的阶段,也是所有前期错误爆发的终点。
| ID | 失败模式 (Name) | 典型表现 (Manifestation) | 根本原因 (Root Cause) | 最早信号 (Signal) |
|---|---|---|---|---|
| I01 | Dependency Hell (依赖地狱) | [案例] 混用 date-fns, moment, dayjs。 | 缺乏白名单机制,AI 随意选择它熟悉的库。 | `package.json` 在单次提交中新增多个功能重叠的库。 |
| I02 | Test Illusion (测试幻觉) | [案例] 生成了测试但从未运行,断言全是 Mock 的假数据。 | 注重 “覆盖率” 指标而忽视了 “真实性”。 | 测试代码中充满了 `expect(true).toBe(true)` 式的逻辑。 |
| I03 | Zombie Code (僵尸代码) | 新功能通过复制粘贴实现,旧逻辑被注释掉但未删除。 | AI 缺乏重构(Refactor)的胆量,倾向于追加(Append)。 | 文件中出现大段被注释的代码块。 |
| I04 | Configuration Drift (配置漂移) | 代码里硬编码了 `.env` 变量名,与生产环境不一致。 | Spec 未包含运维/部署约束。 | 代码中出现 `process.env.NEW_VAR` 但 `.env.example` 未更新。 |
| I05 | Regression Roulette (回归轮盘赌) | 修复了一个 Bug,引发了三个新 Bug。 | 缺乏自动化回归测试集,仅依赖人工验证。 | CI 状态变红,或相关模块的 Snapshot 测试失败。 |
纠偏机制
识别失败模式只是第一步。要构建健壮的 SDD 工作流,还需要在仓库中建立一套三层控制网络(Three-Layer Control Fabric):
- 预防(Prevention):如注入分层架构上下文、维护依赖白名单;
- 检测(Detection):如 CI 自动校验接口变更、扫描非法跨层调用;
- 纠正(Correction):如回滚代码、插入重构任务、提供测试样例。
| 失败模式类型 | 预防 (Prevention) - 减少发生概率 | 检测 (Detection) - 尽早暴露问题 | 纠正 (Correction) - 止损与修复 |
|---|---|---|---|
| 架构崩坏 (Spaghetti Trap, Ivory Tower) |
|
|
|
| 接口/依赖失控 (Interface Drift, Dependency Hell) |
|
|
|
| 质量虚标 (Test Illusion, Validation Vacuum) |
|
|
|
| 上下文失效 (Ambiguity, Context Amnesia) |
|
|
|
仓库约定示例
.
├── .spec/
│ ├── constitution.md # 全局规约:技术栈、白名单、编码规范
│ ├── requirements.md # 当前任务需求
│ ├── design.md # 当前任务设计
│ └── tasks.md # 任务清单与状态
├── examples/ # 核心纠偏资产:Sample Starvation 的解药
│ ├── component-style.tsx # 完美的 UI 组件范例
│ ├── api-handler.ts # 标准的 API 处理范例
│ └── test-case.spec.ts # 必须遵守的测试写法
├── APPROVED_LIBS.md # 依赖白名单
└── .github/
└── workflows/
└── ci-gate.yml # 必须包含测试步骤
分阶段检查清单
| 阶段 | 检查维度 | 具体要求 |
|---|---|---|
| Phase 1: Requirements Gate | 用户故事质量 | 所有 User Story 都有明确的 Acceptance Criteria |
| 表述清晰性 | 无模糊词汇(如 “better”, “fast”, “modern”) | |
| 范围控制 | 明确定义了 “不做的事”(Out of Scope) | |
| 实现中立性 | 未直接包含具体实现细节(如 “用 Redis”),除非是硬约束 | |
| 异常覆盖 | 包含至少 3 个 Negative Test Cases(异常场景) | |
| 术语一致性 | 术语与现有系统一致(需查阅 Domain Dictionary) | |
| Phase 2: Design Gate | 接口规范 | 所有 API 变更都有 Swagger/OpenAPI 定义 |
| 数据模型 | 数据模型变更包含 SQL/Schema 迁移计划 | |
| 依赖管理 | 识别并列出所有新引入的依赖库(需查白名单) | |
| 安全设计 | 包含具体的安全考量(Auth, Input Validation 等) | |
| 架构合规 | 架构分层符合 constitution.md 中定义的原则 |
|
| 验证路径 | 已规划 Tracer Bullet(首个端到端验证任务) | |
| Phase 3: Tasks Gate | 任务粒度 | 单个任务代码行数 < 200(逻辑可独立完成) |
| 依赖结构 | 任务间依赖关系清晰且无环(DAG) | |
| 测试覆盖 | 每个任务都有关联的测试计划(单元/集成/E2E) | |
| 质量内建 | 包含专门的 “Cleanup/Refactor” 任务(非功能优化) | |
| Phase 4: Implement Gate | 代码健康度 | 代码编译通过,无 Linter 报错 |
| 测试有效性 | 新增测试用例通过,且包含有效断言(非空跑) | |
| 自动化状态 | CI 流水线全绿(构建、测试、扫描) | |
| 代码整洁性 | 无注释掉的死代码(Zombie Code) | |
| 安全合规 | 无硬编码的 Secret 或 Magic Number | |
| 文档同步 | Spec 文档(requirements/design)与代码保持同步 |
失败时的应急预案
当检测到失败模式时,不要试图通过 “再 Prompt 一次” 来碰运气。请执行以下预案:
接口漂移 / 架构崩坏时:STOP & REVERT
1. 立即停止当前 Session。
2. `git reset --hard` 回退到最近一个干净的 Commit。
3. 人工介入 `design.md`,明确添加约束(例如:“必须复用 UserInterface,禁止修改签名”)。
4. 重新生成 Tasks 并 Review。
测试不可跑 / 逻辑死循环时:ADD EXAMPLES
1. 不要让 AI 自己修测试(它通常会删断言)。
2. 开发者手动编写一个通过的测试样例,放入 `examples/` 或直接贴入 Context。
3. 要求 AI 参考该样例重写实现。
依赖膨胀 / 幻觉库时:WHITELIST ENFORCEMENT
1. 拒绝该 PR。
2. 在 System Prompt 或 Constitution 中更新:“仅允许使用 approved_libs.md 中的库”。
3. 要求 AI 使用原生代码实现该功能,或请求人工批准引入新库。
结论
SDD 的本质,是在 AI 编程的流变性中重新注入软件工程的严谨性。其失败根源常归结为三点:规格模糊、反馈滞后、人机信任错位。我们提出三大抓手,来应对这三类失败:
- 建立规约:定义
CONSTITUTION.md,作为 AI 行为的底线; - 强制分层:通过仓库结构与设计门禁,防止“面条代码”;
- 自动化验证:CI 是王道。没有自动测试的代码,生成得越快,崩塌得越快。
更多推荐


所有评论(0)