欢迎光临
我们一直在努力

重构软件开发的权力格局:从TDD、DDD到AI时代的SDD,哪种才是你的工程哲学?

重构软件开发的权力格局:从TDD、DDD到AI时代的SDD,哪种才是你的工程哲学?

十年前,TDD让我们重写测试如同推翻巴别塔;三年前,DDD试图用领域语言统一产品与技术的分歧;今天,SDD直接把“规范”变成可执行的源代码,AI成为这个新世界的翻译官。

上周团队来了个新人,对着我们基于DDD设计的微服务架构一脸困惑:“这服务划分的依据是什么?这个聚合根的边界怎么定的?”我让他看领域文档,他翻了几页:“这些和代码对不上啊,上次重构后就没更新了吧?”

这一瞬间,我突然意识到,我们可能站在又一个范式革命的临界点。


01 演化史:从测试优先到规范即代码

2000年左右,Kent Beck提出TDD时,它的核心很激进:先写测试,再写实现。这在当时绝对是个异类——哪有还没写代码就先想怎么测试的?

可问题恰恰在这里:传统开发中,测试往往是最后一步,经常被挤压甚至忽略。

TDD的诞生,本质上是对“测试是二等公民”这一现状的反抗。它不只是个技术实践,更是一种开发哲学:通过可执行的测试用例来明确需求。

#mermaid-svg-vJuH7GTsXzTrDsru{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-vJuH7GTsXzTrDsru .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-vJuH7GTsXzTrDsru .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-vJuH7GTsXzTrDsru .error-icon{fill:#552222;}#mermaid-svg-vJuH7GTsXzTrDsru .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-vJuH7GTsXzTrDsru .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-vJuH7GTsXzTrDsru .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-vJuH7GTsXzTrDsru .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-vJuH7GTsXzTrDsru .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-vJuH7GTsXzTrDsru .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-vJuH7GTsXzTrDsru .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-vJuH7GTsXzTrDsru .marker{fill:#333333;stroke:#333333;}#mermaid-svg-vJuH7GTsXzTrDsru .marker.cross{stroke:#333333;}#mermaid-svg-vJuH7GTsXzTrDsru svg{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-vJuH7GTsXzTrDsru p{margin:0;}#mermaid-svg-vJuH7GTsXzTrDsru .label{font-family:\”trebuchet ms\”,verdana,arial,sans-serif;color:#333;}#mermaid-svg-vJuH7GTsXzTrDsru .cluster-label text{fill:#333;}#mermaid-svg-vJuH7GTsXzTrDsru .cluster-label span{color:#333;}#mermaid-svg-vJuH7GTsXzTrDsru .cluster-label span p{background-color:transparent;}#mermaid-svg-vJuH7GTsXzTrDsru .label text,#mermaid-svg-vJuH7GTsXzTrDsru span{fill:#333;color:#333;}#mermaid-svg-vJuH7GTsXzTrDsru .node rect,#mermaid-svg-vJuH7GTsXzTrDsru .node circle,#mermaid-svg-vJuH7GTsXzTrDsru .node ellipse,#mermaid-svg-vJuH7GTsXzTrDsru .node polygon,#mermaid-svg-vJuH7GTsXzTrDsru .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-vJuH7GTsXzTrDsru .rough-node .label text,#mermaid-svg-vJuH7GTsXzTrDsru .node .label text,#mermaid-svg-vJuH7GTsXzTrDsru .image-shape .label,#mermaid-svg-vJuH7GTsXzTrDsru .icon-shape .label{text-anchor:middle;}#mermaid-svg-vJuH7GTsXzTrDsru .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-vJuH7GTsXzTrDsru .rough-node .label,#mermaid-svg-vJuH7GTsXzTrDsru .node .label,#mermaid-svg-vJuH7GTsXzTrDsru .image-shape .label,#mermaid-svg-vJuH7GTsXzTrDsru .icon-shape .label{text-align:center;}#mermaid-svg-vJuH7GTsXzTrDsru .node.clickable{cursor:pointer;}#mermaid-svg-vJuH7GTsXzTrDsru .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-vJuH7GTsXzTrDsru .arrowheadPath{fill:#333333;}#mermaid-svg-vJuH7GTsXzTrDsru .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-vJuH7GTsXzTrDsru .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-vJuH7GTsXzTrDsru .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vJuH7GTsXzTrDsru .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-vJuH7GTsXzTrDsru .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vJuH7GTsXzTrDsru .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-vJuH7GTsXzTrDsru .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-vJuH7GTsXzTrDsru .cluster text{fill:#333;}#mermaid-svg-vJuH7GTsXzTrDsru .cluster span{color:#333;}#mermaid-svg-vJuH7GTsXzTrDsru 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-vJuH7GTsXzTrDsru .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-vJuH7GTsXzTrDsru rect.text{fill:none;stroke-width:0;}#mermaid-svg-vJuH7GTsXzTrDsru .icon-shape,#mermaid-svg-vJuH7GTsXzTrDsru .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-vJuH7GTsXzTrDsru .icon-shape p,#mermaid-svg-vJuH7GTsXzTrDsru .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-vJuH7GTsXzTrDsru .icon-shape rect,#mermaid-svg-vJuH7GTsXzTrDsru .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-vJuH7GTsXzTrDsru .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-vJuH7GTsXzTrDsru .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-vJuH7GTsXzTrDsru :root{–mermaid-font-family:\”trebuchet ms\”,verdana,arial,sans-serif;}

2000 左右:TDD测试驱动开发(先写测试,再写实现)

2003:BDD行为驱动开发(Given-When-Then)

2003:DDD领域驱动设计(限界上下文、通用语言)

2010s:敏捷 & 微服务落地与TDD/DDD深度结合

2020+:SDD & AI 时代规范即代码,AI生成实现

大约2003年,Dan North在TDD基础上提出了BDD。他发现许多开发者和业务人员对“测试”这个词有心理抵触,于是把焦点从“测试”转移到“行为”上。

BDD的核心是用业务人员和开发者都能理解的自然语言描述系统行为。那时候流行的工具是Cucumber,那些“Given-When-Then”的语句,我第一次见到时觉得“这玩意儿太形式主义了吧”。

有趣的是,DDD在同一时期也在兴起。Eric Evans在2003年出版的《领域驱动设计》中,直击了当时企业软件开发的核心痛点:业务逻辑与技术实现之间的巨大鸿沟。

回忆一下:你们团队的产品经理有没有说过“这个需求很简单,就加个按钮”?技术实现时才发现背后涉及三个服务的数据一致性?

DDD的核心理念是用“通用语言” 在业务和技术团队间建立沟通桥梁,通过战略设计和战术设计,让软件结构反映业务本质。

02 SDD:AI时代的新范式

到了AI编程时代,问题变得更加尖锐。SDD正是在这样的背景下崛起的。

SDD的核心是规范即真理,代码只是规范在特定语言和框架中的表达。

这不只是个技术上的小调整,而是权力关系的根本反转。

几十年来,代码一直是软件开发的核心资产。PRD、设计文档这些“规范”类的东西,往往是写出来就没人看,代码改了一轮又一轮,文档还停留在最初版本。

SDD打破了这种局面:规范成为单一事实来源。

更重要的是,现在的AI已经能理解并实现复杂的规范。规范不再只是给人看的文档,而是可以直接“喂”给AI生成代码的“可执行蓝图”。

03 理念差异:四种方法的核心对比

方法核心关注点核心产出适合场景典型工具/框架
TDD 代码正确性 测试用例 + 实现代码 函数/模块级别开发 JUnit, pytest, Mocha
BDD 系统行为 行为描述 + 可执行规格 端到端功能验收 Cucumber, Behave, SpecFlow
DDD 业务领域建模 领域模型 + 限界上下文 复杂业务系统设计 无特定工具,是一套方法论
SDD 规范完整性 可执行规范 + 生成代码 AI辅助的现代开发 OpenSpec, Spec Kit, BMad

TDD像是在拼图:你先确定每一块的边缘形状(测试),再去找能匹配的图案(实现代码)。

它的局限性也很明显:测试用例毕竟只是“点”的验证,难以确保整体业务逻辑的连贯性。

BDD前进了一步,它关注的是“用户能看到什么,系统应该有什么行为”。

但我得实话实说:在不少项目中,那些精心编写的.feature文件最后变成了另一种形式的“过时文档”,因为维护成本太高。

DDD试图解决更深层的问题:如何让软件架构反映业务本质?它提出了限界上下文、实体、值对象、聚合、领域服务等一系列概念,试图在业务复杂度和技术实现之间建立映射关系。

实际项目中,DDD最难的不是概念理解,而是如何保持领域模型与代码实现的一致性。

模型讨论会上画了一堆精美的图,两周后代码就偏离了最初的设计——这场景熟悉吗?

SDD采取了截然不同的路径。它不试图让代码去“反映”某个模型,而是让规范成为事实来源,代码只是规范的“快照式实现”。

说得更直白点:在SDD中,如果你要改功能,不是直接改代码,而是改规范,然后重新生成代码。

04 实战场景:何时用哪种方法?

适用TDD的场景

我一般会在这些情况下用TDD:

  • 算法实现:比如写个复杂的排序算法或者数据处理逻辑
  • 工具库开发:确保每个函数在各种边界条件下都正确
  • 遗留代码重构:加测试保护已有的行为

# 典型的TDD流程示例:实现一个字符串计算器
# 1. 先写测试
def test_empty_string_returns_zero():
assert add("") == 0

def test_single_number_returns_itself():
assert add("1") == 1

def test_two_numbers_comma_delimited():
assert add("1,2") == 3

# 2. 实现最简单的可通过代码
def add(numbers: str) > int:
if not numbers:
return 0
if "," in numbers:
nums = numbers.split(",")
return int(nums[0]) + int(nums[1])
return int(numbers)

适用BDD的场景

BDD更适合这些场景:

  • 团队有业务分析师参与:需要共同理解需求
  • 新功能验收:确保开发的功能符合业务期望
  • 端到端测试:用户关键路径的验证

适用DDD的场景

DDD的用武之地:

  • 业务复杂度高的系统:电商、金融、供应链等领域
  • 长期演进的项目:需要清晰的架构应对变化
  • 多团队协作:需要明确的上下文边界

适用SDD的场景

根据参考材料,SDD特别适合:

  • AI辅助开发项目:让AI在清晰的规范约束下工作
  • 增量开发场景:在现有系统上添加功能,需要AI理解项目现状
  • 需要高度一致性的项目:规范作为单一事实来源,确保文档与代码同步

一个典型的SDD工作流,就像材料中描述的OpenSpec流程:

  • 创建变更提案:明确为什么改、改什么、影响范围
  • 编写规范变更:在specs/目录下定义新的能力需求
  • 生成实现计划:AI根据规范创建技术设计
  • 实现并归档:完成代码后,规范自动更新
  • 05 范式融合:实际项目中的混合策略

    在真实项目中,我很少只用一种方法。更常见的是根据项目的不同阶段和不同部分,选择合适的方法组合。

    以我们现在做的一个电商平台为例:

    • 支付领域:用DDD建模,因为业务规则复杂,有清晰的限界上下文(订单上下文、支付上下文、库存上下文)
    • 支付接口:用TDD确保各种异常情况处理正确
    • 用户下单流程:用BDD描述关键路径
    • 新的推荐算法模块:尝试用SDD,因为可以让AI在明确的性能规范和算法约束下生成代码

    更关键的是,这些方法之间并不互斥。完全可以在SDD的规范中,包含TDD的测试要求,描述DDD的领域概念。

    06 SDD的挑战:理想与现实的差距

    别把SDD想得太美好,实际落地有不少坑:

    规范维护成本:写得好的规范需要时间精力,不比手写代码轻松。写得不好的规范,生成的代码质量可想而知。

    AI的局限性:现在的AI还是可能生成有安全隐患的代码、性能不佳的实现,或者完全误解你的意图。你还是得是个有经验的开发者,能看出问题在哪。

    团队适应成本:让团队接受“不改代码改规范”这种思维转变,不是一两天的事。很多人第一反应是:“我直接改代码不是更快?”

    不过有意思的是,SDD某种程度上也让DDD的理念更容易落地。

    想想看:如果你的领域模型就是规范的一部分,那么AI生成的代码自然会遵循这个模型。这比手动确保几十个开发人员都正确理解领域模型,要可靠得多。


    我们正处在一个转折点。SDD的出现,与其说是对TDD、BDD、DDD的否定,不如说是在AI时代对这些思想的延续和重新诠释。

    它用一种更彻底的方式,试图解决软件工程中那个永恒的问题:如何确保我们的意图被准确地实现。

    当你在代码仓库的specs/目录下,看到那份用精确语言描述的系统规范,而旁边的代码文件只是这份规范的“衍生品”时,也许会感受到一丝震撼。

    技术演进的本质,是对抽象层次的不断向上推移。从机器码到汇编,到高级语言,到面向对象,到领域驱动,现在到规范驱动——每一步,我们都把更多“意图”直接表达出来,让机器去处理实现的细节。

    这个趋势不会停止。也许有一天,我们会用更接近人类思考的方式“编写”软件,而今天这些DDD、SDD的讨论,也会成为技术史中一段有趣的注脚。

    赞(0)
    未经允许不得转载:171主机测评 » 重构软件开发的权力格局:从TDD、DDD到AI时代的SDD,哪种才是你的工程哲学?
    分享到: 更多 (0)

    评论 抢沙发

    • 昵称 (必填)
    • 邮箱 (必填)
    • 网址