用 SDD + TDD 驯服 AI:让「写得快」也「信得过」
SDD(规格驱动开发)
SDD,即规格驱动开发(Spec-Driven Development),核心思路一句话:规格是首要产物,代码是从规格构建出来的。
传统开发流程里,需求文档写完,开发就开始写代码。
规格如果写的话,往往夹在需求文档的段落里,或者散落在各个接口文档中,不是独立的一级产物。
SDD 把这个顺序倒了过来:在动手写任何实现代码之前,先把"做成什么样算对",写成一份独立、完整、人能审的规格,审过之后,代码从规格生成。
规格阶段有一个重要的设计原则:聚焦"怎么用",不急于讨论"怎么实现"。 好的 spec 讨论的是场景——在什么情况下、解决什么问题、输入和输出是什么。它不讨论用什么框架、数据库怎么设计、服务怎么拆分。
以设计一个待办事项 API 为例:
spec 阶段应该问的是:用户创建待办时要不要填截止日期?标记完成之后还能不能撤销?列表默认按什么排序?——这些是"怎么用"的问题。
后端用 FastAPI 还是 Flask、数据存内存还是 SQLite,这些是实现方案的范畴,在 spec 确认之后再回答。
同样的道理,在设计一个框架的时候,第一件事不是画架构图,是先写出预期的调用代码样例——这段代码读起来是不是顺畅、使用者需要写多少行才能完成一个常见任务。代码样例确认了"易用",内部的实现方案是后续顺水推舟的事情。
这就是 spec 阶段的核心价值:用最轻的方式把"怎么用"聊清楚、定下来,之后的实现方案和测试用例都是自然推导。
这份规格通常包含:数据模型、接口的输入和输出、校验规则、边界条件、错误返回,以及明确不做的事。它的核心特征是人可以快速审完——不是上百页的文档,而是十几条可以逐条对照的约束。
SDD 这个方向在 2025 年前后加速成型,直接原因是 AI 编码工具的普及。当代码的"写"越来越快、越来越便宜,"写得对不对"就成了瓶颈。
GitHub 在 2025 年 9 月开源了 Spec Kit(specify → plan → tasks → implement),AWS 在 2025 年 7 月发布了 Kiro(需求 → 设计 → 任务 → 实现)。它们面向的是同一个问题:AI 生成的代码怎么从凭感觉走向可验证。
SDD 的核心操作:
SDD 的操作可以总结成三步——有人把它叫作"Spec Coding 三铁律"。
"铁律"这个说法偏重,但三步的顺序值得记住:先写规格、规格必须可验证、小步可回滚。
- 先写规格: 在让 AI 写实现之前,先把输入、输出、边界、校验规则、明确不做的范围写成一份人能审完的文档。审规格审的是"对不对",不是"看着像不像对的"。
- 规格必须可验证: 每一条规则都要能变成一条测试或一个类型约束。不可验证的规格等于没有——"性能不能太差"是不可验证的,"P99 延迟不超过 200ms"才是。
- 小步可回滚: 每次只改一件能独立验证的事。一个端点一个红绿循环,不是一次性把整个服务写完。每步的爆炸半径控制在一个端点以内。
也可以看下我之前写的文章 Harness Engineering:从入门到精通。
SPEC.MD文档:

TDD(测试驱动开发)
TDD,即测试驱动开发(Test-Driven Development),是 Kent Beck 在 1990 年代末系统化提出的一套开发方法。核心操作是一个短循环:先写一个会失败的测试(红),再写刚好能让它通过的最少实现(绿),然后清理代码(重构)。 循环一次只走一个小步,每一步都有测试兜底。
TDD 在提出后的二十多年里,认同的人多、坚持的人少。原因很简单:手写测试太慢了。在快速迭代的项目里,测试经常是第一个被砍的。
AI 把这个账重新算了一遍。AI 写测试的速度比人快得多——原来一个下午才能写完的测试,现在十分钟就能生成。
而且测试恰好是 AI 生成代码最缺的东西:一个不带感觉、只判断对错的裁判。 所以在 AI 编码的时代,TDD 从"道理对但太慢"变成了"道理对且现在做得起了"。
这里值得补一个判断。TDD 这套方法本质上是反人性的——它要求人在写实现之前先写一堆会失败的测试,把满足感最强、收益最直接的"先把功能跑起来"往后推。人会嫌麻烦、会偷懒,本能地先做那些能立刻看到成果的事,测试于是一拖再拖。这不是纪律问题,是人性。
但同样这套约束,放到 AI Agent 身上就刚刚好。AI Agent 不会嫌麻烦,也不在乎满足感来得早还是晚,它真正需要的恰恰是 TDD 能给的两样东西:明确的行为指导(规格和测试告诉它要做成什么样),和清晰的检验标准(测试的红绿告诉它做到了没有)。对人来说是负担的东西,对 AI 来说是它最需要的脚手架。从这个角度看,TDD 在 AI 时代的复活,不只是因为写测试变快了,更是因为执行这套方法的主体——从一个会偷懒的人,换成了一个需要明确指令才能干好活的 Agent。
核心:红-绿-重构循环
| 阶段 | 做什么 | 验证 |
|---|---|---|
| RED 红灯 | 写一个最小的失败测试 | 运行测试,看到 FAILED |
| GREEN 绿灯 | 写最少的代码让测试通过 | 运行测试,看到 PASSED |
| REFACTOR 重构 | 在测试保持绿灯的前提下优化代码 | 运行测试,仍然 PASSED |
没有先失败的测试,就没有生产代码:
TDD 的红-绿-重构(Red-Green-Refactor)循环中的硬性顺序:
- Red(红): 先写一个测试,运行它,确保它失败。这一步证明:
测试本身是有效的(不是 永远通过的假测试)
当前确实没有实现这个功能
你清楚知道"完成"的标准是什么(测试代码就是需求规格) - Green(绿): 编写刚好够让测试通过的最少代码,不做多余设计;
- Refactor(重构): 在不破坏测试的前提下优化代码结构。
简而言之,在看到一个失败的测试之前,你不能写任何生产代码。这个失败是扳机,确认你正在解决一个真实存在的问题,而不是臆想。
如何理解先写了代码再补测试?删掉代码,重新开始?
为了防止自欺欺人的心理陷阱:
- 如果先写代码: 你潜意识里会写 刚好能让这段代码通过的测试 => 测试变成了验证代码正确性的工具,而不是定义需求的规格。
- 测试失去防护价值: 当需求变更或重构时,这些后补的测试往往过于宽松,或者测试的是实现细节而非行为,无法保护代码。
删掉重来是为了强制你回到正确的思维轨道:让需求(测试)驱动设计,而不是让设计(实现)扭曲需求。
TDD的错误理解:
- 太简单不需要测试 => 简单代码也会坏。写测试只要 30 秒。
- 我先写代码,之后补测试 => 后补的测试立刻通过,证明不了任何东西。
- TDD 太教条了,我更务实 => TDD 就是务实:先找 bug 比事后 debug 快 10 倍。
- 已经手动测过了 => 手动测试无法重复、无法回归、无法证明覆盖面。
- 删掉 X 小时的代码太浪费 => 沉没成本谬误。留着你无法信任的代码才是浪费。
SDD 与 TDD的配合
两个概念经常被放在一起提,但它们管的是不同的事:
- SDD 回答"什么是对": 规格是人确认的,描述的是"做成什么样算对";
- TDD 把"对"变成机器能判的东西: 规格的每一条规则和边界,变成一条可执行的测试;
规格是源头,测试是规格的可执行形态。两者配合的流程是:
- 先写规格(SDD),人审过;
- 按规格写测试(TDD 的红),此时没有实现,全红;
- 补实现跑到全绿(TDD 的绿),测试没绿就继续修;
这套流程走到"全绿",代码就算是有了第一层保障。具体到 Python 技术栈,验收可以分成三层:
- 第一层:pyright 静态校验。 类型错误、缺少导入、参数不匹配——这些不需要跑测试,静态分析就能拦下来。每次 AI 产出代码之后,先跑 pyright,零 error 再往下走;
- 第二层:pytest 全量用例。 这是 TDD 的主体——spec 的每一条规则和边界都变成一条测试,全绿才是"行为正确";
- 第三层:example 实际案例 + 人工复检。 前两层是机器判的,但有些东西机器判不了——实现方案是不是好用(调用方式是否简洁、错误提示是否清晰)、业务逻辑是不是真的正确(case 覆盖的语义对不对)。这一层要人来看,关注的是易用性和业务正确性;

三层下来,效果是把一个概率系统(LLM)约束到可接受区间:

举例:
小李在一家电商公司做后端。上周他让 AI 写了一段处理订单金额的函数——逻辑看起来通顺,变量命名也对,读着差不多。合进去,上线。
两周后线上出了一笔异常单:一个订单同时命中会员折扣和优惠券,两个优惠叠加了,金额算错。小李回来翻代码,发现在 calculate_discount 分支里,AI 根本没有处理"会员身份同时持有有效优惠券"的冲突逻辑——边界条件被静默吞掉了。
问题出在哪一步?不是 AI 的能力不够。 是小李 review 这段代码的时候,手里没有一个可以逐条对照的标准。他凭的是感觉——“读着像对的”。LLM 的产出是概率分布,同一个 prompt 两次输出不完全一样。如果 review 的标准也是模糊的,那审查就成了碰运气:产出碰巧像对的就放过去,碰巧不像才拦下来。这种没有可对照标准、靠感觉判断对错的写法,常被叫作"感觉式编程"——SDD 要解决的正是它。
SDD 做的事就是给小李一个可以逐条对照的东西。十几条规格——标题不能为空、长度不超过 100、去空白后再判空——审完不需要五分钟。如果当初有这份规格,那条"会员和优惠券不能叠加"的规则在审规格的时候就暴露了,不会等到上线两周后。
TDD(UT/回归测试) 做的事是让这些规则不会被"忘了测"。每条规格对应一条测试——空标题、仅空白、超长、不存在 id、重复标记——八条测试全红的时候,AI 得一条一条修到绿。少一条绿不过,少一条就没测到。
coding agent
理解了 SDD+TDD 这条线之后,有一个很实际的问题:用什么工具来做?下面的演示会在一个 coding agent 上完整跑一遍——我用的是 Cursor 或 Trae。在看它跑之前,先把选型的判断框架讲清楚。

Cursor工具的工作模式:
1)Agent: AI自己来做决策,这也是我们大部分时间使用的模式,一般问题它会用简单的模型,节省token,问题复杂了,它会自己决策,切换复杂的模式进行解决问题。
有个特殊的Agent(Background Agent),这个Agent使用的是云端自己的服务器,比如你有程序在github上,它会自动在github上pull代码、编写、push代码,支持后台并行处理任务,让它在后台工作,你当前电脑可以做其他的事情,不会被任何事务影响到。
在80% - 90%的时候都在使用该模式,Agent模式是AI自己做决策、做判断,但是遇到复杂问题的时候,会有50-60%的概率,不询问用户,不写规划方案,直接进行修改,这样会导致精度下降,可能质量不高,在大工程的时候,前期都会切换Plan模式,甚至一些复杂场景,用户要求高,也会切换到Plan模式下先进行,之后再切换到Agent模式。
2)Plan: 规划,在复杂场景下适用,比如重构项目,代码重构,涉及多个文件,它会把规划内容(方案说明)写成xxxPlan.md文件,让你确认,如果方案OK的话,你可以继续执行Build,把方案进行执行。但是这个模式下消耗的token比较多,这个模式会花更多的时间跟你做核查,并且梳理成方案说明,让你进行人工确认,确认ok的情况下,再进行执行。
大需求 ==> 开荒
Plan模式其实分2个步骤,1.规划=>写方案.md,在某些情景下,需要用户协助,进行提问,用户确定后,才会走下一步;2.修改代码(Build)。
3)Debug: 有问题出现了,可以切换到Debug模式,它可以自动的进行分析bug、定位根因、提供修复方案以及bug补丁提交,彻底解决这个bug。虽然Agent模式也可以做bug定位、修复,但是深入、准确、效率没有Debug模式好,这个模式是以解决bug为目标的。但是Agent只会修改当前问题,如果当前问题跟其他地方有关联,其实还有其他问题,它有可能会偷懒不会去修复,但是Debug模式会全盘梳理把涉及到该问题的bug都修复掉。
4)Ask: 简单来说,就是不想改代码,想了解代码的逻辑、代码审核、学习新技术、架构方案的了解等,就是纯咨询模式,不需要担心我的问题会触动代码的变动。
让 AI 不用每次都从零开始
每开一个新会话,AI 又把项目当陌生人,从头猜。上次踩过的坑——函数命名用 snake_case、测试放在 tests/ 下、不新增没写在 requirements 里的依赖——下次还会再犯。
前面确认的那份 spec,本身就是一个可以一直留着的东西。固定住它,下次 AI 就不用重学——拿出这个模块的规格,AI 就有了"上次做成什么样算对"的全部记忆。
顺着这个思路,有三类东西值得持续沉淀——把它们放在一起,就是常说的 Rules + Spec + Skills 三位一体:
| 沉淀什么 | 管什么事 | 直接效果 |
|---|---|---|
| Rules | 约束类(命名规范、目录约定、禁止项、不可逆操作的审批规则) | 产出贴着团队的工程口味,不需要每个 PR 手动纠风格 |
| Spec | 意图类(模块要解决什么、边界在哪、明确不做什么) | AI 理解的是"为什么",不是照抄上次的代码 |
| Skills | 可复用动作(固化的常用操作流程,如"给新数据表生成 CRUD + 测试"的步骤) | 不用每次重新拼装步骤 |
把散在人脑、PR 评论、群聊里的口头知识固化成 AI 能直接读的文本——新人和 AI 都不用再翻聊天记录做"代码考古"。这个过程越用越省力,沉淀越多、复用越快——有人把这种正反馈叫作"知识飞轮"。落到日常就一句话:每确认一份规格、每踩一次坑、每总结一条规则,都在垫高团队和 AI 下一次的起点。
团队级的 spec 管理体系在后面章节会细讲。这里先确立一个习惯:每次 AI 产出跑偏,不只是在 PR 里改掉就完了,而是把这条约束写进 Rules 或 Spec——让它下次不会再犯。
system rule规则:
1.之前完成正确的功能,尽量不要修改。
比如当前的instruction是完善功能A的,那么只需要专注于功能A,不需要修改其他功能(比如功能B)。
2.生成的注释用中文,并使用 UTF-8 编码。
3.生成的代码有时候会存在中文乱码的情况,所以你在生成中文的时候,需要检查是否有中文乱码,如果有乱码需要修正。
4.如果修改某个函数的实现,先理解之前函数实现的逻辑。然后在原本的基础上,再进行修改(保留之前的函数逻辑,不要移除)。
5.你操作的环境是windows系统。
6.如果用户没用明说,就不需要编写测试脚本,也不需要写专门的项目说明.md文件。
7.写代码,不考虑 fallback。
8.代码与注释中不要有 emoji。
9.修改代码能在已有文件内修改,就不要新增文件。
10.只执行当前的用户描述的功能,你尽量不要去扩展功能。
11.需要下载python插件时,优先使用使用清华园镜像,样例:pip install tushare -i https://pypi.tuna.tsinghua.edu.cn/simple。
如果规则有冲突的话,project rule会覆盖掉system rule,没用冲突的话,当前的规则是project+system rule。
写完以后,谁来担保它是对的
测试全绿不等于可以直接上线。中间还差一步——谁来担保这段代码放进生产环境不会出问题?
第一道关:AI 自我审查(自审)。 换一个会话或让 AI 换审查视角,把产出丢给它,让它在几个具体方向上找问题:边界有没有处理、异常路径上有没有静默吞错、和 spec 有没有偏差、哪些行为缺测试。它能抓到很多"明显错"——没处理的 None、漏掉的异常分支、被测漏的边界。
但它抓不住一类问题:代码完全正确,但理解错了需求。因为 AI 不知道真实的业务意图。
第二道关:人最终决策。 不可逆的操作(删数据、动钱、对外发布、改线上配置)、业务语义对不对——这些没有机器能替人判断,必须在合并之前由人按确认。两关都放在合并前,而不是上线后补救。
整条链路:

这套两道关是个人和小队尺度的做法——一段审查 prompt 加一份合并前 checklist 就能跑起来。到了大型项目,"我审一下"不够了,需要把它系统化成不依赖某个人盯着的质量体系:生成 → 验证 → 修正的闭环、用测试做判据自动把关——这是下一节课要展开的内容。
AI 先放大了个体——一个人借助它能顶过去一个小组的产出,这就是所谓的"超级个体"。但如果每个人的方法、规格、踩坑经验全锁在自己脑子里,一堆超级个体并不等于一个"超级团队"——一个人休假,他的 prompt、他的标准也跟着休假,团队反而更依赖个人。怎么从"超级个体"走到"超级团队",后面两节课会从工程管理和组织分工两个角度展开。
DEMO案例
step1,建立基础的Spec,方便后面的spec都按照这个逻辑来
prompt:我需要在code文件夹内,进行代办事项项目的开发,现在先约定基础的行为规范:
1)我们需要先进行Spec探讨,再进行具体的工作;
2)我们所有的工作产出都需要进行测试用例的积累。
请把这些行为规范和Spec文档都统一落入code/spec文件夹内;

step2,开始做一个demo方案
prompt:我想要做一个代办事项的管理根据,请帮我规划整体的实现思路,落实到spec文档

step3,测试文档统一管理
prompt:所有测试方案都统一集中在code/tests文件夹管理

step4,方案都确认好后,进行代码编写
prompt:进行代码编写

step5,编写测试用例
prompt:帮我编写前端的测试用例,并且是可以进行模拟点击的,请帮我规划整体测试实现思路,落实到spec文档;

step6,编程测试用例代码
prompt:编程测试用例代码

step7,审查下测试用例
prompt:@test_todo_api.py这个测试用例主要测了哪些关键事项?

step8,优化测试行为规划
prompt:优化测试行为规范,除了pytest之外,应该先做一次pyright的静态校验检测,在pytest之前,并请重新跑一遍后端测试

prompt:优化为 Session.get()

step9,运行项目
prompt:运行前后端项目


step10,当前打开的浏览器页面中执行模拟点击测试
prompt:使用写好的测试用例,在当前打开的浏览器页面中执行模拟点击测试

step11,生成API文档、测试报告
prompt:帮我生成一份API文档,放到code/docs文件夹中,生成一份完整的测试报告(包含前端测试的),也放到code/docs文件夹中

行为约束
prompt:本对话后续所有工作任务必须在code/does范围内进行
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)