规范驱动开发:让架构变得可执行

  • 2026-01-14
    北京
  • 本文字数:8325 字

    阅读完需:约 27 分钟

第五代抽象

软件工程历史上的每一次重大转变都是由一种一致的力量驱动的:抽象的兴起。最早一代的软件是用原始机器代码编写的,后来汇编语言引入了可读性和控制层。更高级的语言已经跨越多个不同的范式发展,使得像 C、Java 和 Python 这样的通用语言及其衍生品得以发展。

 

这些语言使得抽象得以进步,其中像内存管理和特定平台的怪癖这样的概念在日常工作流程中被掩盖,并且由开发者代表进行处理。这种级别的可访问性允许更广泛的生态系统发展,因为随着每一代语言的出现,相应的支持工具链也在发展。 

图 1:编程语言的世代

 

第五代,即使用自然语言的第五代,长期以来一直是编程语言的目标演变,其中人类用他们的母语交谈,并且以可执行的方式进行解释。这种演变直到最近才真正成为主流。推动这一点的是人工智能(AI)的代际能力,现在已经成熟到可以接收以人为中心的输入,并用你选择的编程语言构建解决方案。几十年的学术研究记录了这一进化过程,博客也非正式地对此进行了讨论。

 

这些转变中的一个共同主题是开发人员角色的演变。每一次抽象提升都允许开发人员更多地关注意图,而不是机制,我们现在进入了另一个拐点。第五代不仅因为广泛使用生成式 AI 而加速,而且与行业主导的采用相吻合。这代表了开发者如何从根本上接近他们手艺的新纪元。

 

随着前几代的成熟,支持它的工具链也出现了。在前几代中,工具是在一段时期稳定后出现的;然而,随着 AI 研究和产出的快速发展,工具链现在有望塑造和定义这一代,而不仅仅是支持它。

 

作为这些进步的一部分,一套关键的工具出现了,集中在规范驱动开发(SDD)上。这种趋势是由 AI 辅助代码生成的接受所驱动的,它允许开发人员提升自己的抽象,并表达系统应该做什么,而智能工具则实现了它的实际完成方式。

 

这种转变重新定义了我们如何接近系统的架构和设计。现在,团队维护的是活的规范,而不是随着时间的推移而偏离原始意图的静态架构图。这些定义了系统契约、边界、不变量和行为。这些规范在设计上是可执行的;它们可以生成代码、文档、SDK、模拟甚至服务基础设施。AI 智能体,通过角色映射的能力来播种它们的上下文,其中角色的专业知识被捕捉为智能体的可消费输入,现在可以作为解释器、验证器和特定于领域的协作者行使权威。

 

在本文中,我们将 SDD 作为一种架构范例进行研究,详细说明规范如何成为系统的可执行支柱,漂移检测和持续验证如何将架构转变为运行时不变量,AI 和代理工具如何重塑生成和管理,以及这种模型如何代表软件抽象长期演变中的下一个主要拐点。

 

SDD 架构

规范驱动开发(SDD)这个名字可能暗示了一种方法论,类似于测试驱动开发。然而,这种框架低估了它的重要性。更准确的理解是,SDD 是一种架构模式,它通过将可执行规范提升到代码本身之上,从而颠倒了传统的事实来源。

 

SDD 代表了软件系统架构、治理和演变方式的根本转变。在技术层面上,它引入了一个声明性的、以契约为中心的控制平面,将规范重新定位为系统的主要可执行工件。相比之下,实现代码成为了次要的、生成的架构意图表示。

 

传统架构依赖于源代码作为规范的控制面。在 SDD 中,控制面向上移动到规范控制面。这个控制面正式允许我们定义诸如:

  • 接口契约(功能、输入/输出、行为保证)

  • 数据模式和不变量(结构、约束、验证规则)

  • 事件拓扑(允许的流、排序、传播语义)

  • 安全边界(身份、信任区域、策略实施)

  • 兼容性规则(包括向后和向前)

  • 版本控制语义(演进、降级、迁移)

  • 资源和性能约束(延迟、吞吐量、成本)

 

这一变化涵盖了跨行为和治理的经典体系结构表面区域的组合,并具有正确性的时间维度。SDD 不是独立地在服务和存储库之间协调这些领域,而是将它们集中到一个单一的权威模型中。这种模式更接近于语言类型系统或编译器:它不执行程序本身,而是定义了什么是可表达的,拒绝什么是无效的,并限制演变以保持随时间的正确性和兼容性。架构不再是咨询性的;它现在变得可实施和可执行。

 

尽管越来越多的工具被冠以 SDD 的标签,但从根本上说,它不是一个产品、框架或正式语言。相反,它是一个架构构造,以惊人的一致性作为一个五层执行模型重新出现。

 

以我们的订单管理服务为例,在规范层中,我们声明什么必须为真,而不是如何实现它。这是一个简单订单的伪规范可能看起来像:

 

图 2:SDD 5 层执行模型

 

这些层次共同构成了一个封闭的、由规范控制的控制系统,其中意图不断塑造执行,而执行不断地验证意图。由此产生的并不是对现有架构的渐进式改进,而是权威、控制和真实性所在位置的根本倒置。

 

现在让我们来看看这五个层次。在整个过程中,我们将遵循一个经典的订单管理服务的简化示例,以展示各层之间的进展以及它们是如何相互加强的。

 

规范层

这是系统行为的权威定义。它捕获了系统的声明性意图,而不是如何实现。这一层通常包含 API 模型、消息传递契约、领域模式和特定于系统、以策略为中心的约束。从抽象的角度来看,它既是人类可读的,也是机器可执行的,同时作为设计工件和操作控制面。

 

以我们的订单管理服务为例,在规范层,我们声明什么必须为真,而不是如何实现它。这是一个简单订单的伪规范可能的样子:

service: Ordersapi:     POST /orders:      request:               Order:                   id: uuid                   quantity: int > 0           responses:               201: OrderAccepted               400: ValidationError  policies:     compatibility:   backward-only     security:         auth: mTLS
复制代码

 

这个规范明确声明了我们的期望:

  • 订单必须是正数

  • API 不得引入破坏性变更

  • 请求必须经过认证

 

这里没有引用任何语言、框架或基础设施。

 

生成层

这一层将声明性系统意图转化为可执行的形式。它作为一个多目标系统编译器,但与发出机器指令的经典编译器不同,这一层发出系统形状和跨语言、框架和平台的可执行运行时界面。在这里,问题空间由规范层声明,工具将其操作形式具体化。典型的输出包括类型模型、契约存根、验证中间件、文档以及一系列集成和一致性测试。

 

以我们的订单示例为例,规范被摄取并发出可执行的系统表面。从概念上讲,这看起来像:

 

spec.yamlType models (Java, TypeScript)   → Request validators      → API stubs     → Contract tests
复制代码

 

工具将声明的意图转化为具体的、可执行的形式。

 

构件层

这一层包含了生成阶段的具体输出:生成的服务、组件、客户端、数据模型和适配器。关键的是,这些工件不被视为主要资产。相反,它们是可再生的、可丢弃的、可替换的,并且可以持续协调。这颠覆了传统软件架构的一个基本假设:代码不再是系统的记录;规范是。随着代码变得无限可复制和按需生成,新出现的术语环境代码恰如其分地抓住了这种范式转变。

 

我们的订单的形状现在可以用生成的一次性代码实现了。这可以看到类似于类型化模型的输出:

 

export interface Order {   id: string;   quantity: number; }With a validator:if (order.quantity <= 0) {    throw new ValidationError("quantity must be greater than zero"); }
复制代码

 

这些构件不是真相的来源。如果规范发生变化,它们将被重新生成。如果它们被删除,什么也不会丢失。

 

验证层

这一层强制执行意图和执行之间的持续一致性。它由契约测试、模式验证、有效载荷检查、向后兼容性分析和架构漂移检测机制组成。它在结构上扮演了编程语言的类型系统和管理程序对虚拟机所扮演的角色:积极防止架构违规传播到运行时。

 

我们的生成层创建的工件最终在这里进行管理,其中验证确保运行时不能偏离意图:

 

✓ Reject requests with quantity <= 0
复制代码

 

违规行为在构建时、部署期间以及我们的持续集成系统中被检测到。架构正确性是持续强制执行的,而不是手动审查的。

 

运行时层

这是操作系统本身,由典型的一系列构件组成,例如:

  • API

  • 消息代理和流处理管道

  • 函数、方法和等效结构

  • 集成服务

 

至关重要的是,运行时的形状完全受到上游规范和验证层的约束。因此,运行时行为在架构上是确定的,而不是涌现性的。

 

如果我们尝试在我们的订单服务使用负数量,如下所示:

 

POST /orders {   "id": "123",   "quantity": -1 }
复制代码

 

我们返回了一个 400 ValidationError,并不是因为运行时拒绝了请求,而是因为在系统执行任何请求之前,该行为在规范层中声明,由生成层具体化,由构件层实例化,并由验证层持续强制执行。

 

架构反转

几十年来,软件架构一直在一个基本上未受挑战的假设下运作,即代码是最终的权威。架构图、设计文档、接口契约和需求规范都是用来指导实现的。然而,运行中的系统总是从最终部署的内容中获得其真相。当出现不匹配时,标准的反应是“更新文档”。

 

SDD 完全颠覆了这种关系。规范成为系统现实的权威定义,实现是持续派生、验证的,并且在必要时重新生成以符合该真实性。这不是一个哲学上的区别;它是软件系统治理的结构性反转。

 

传统的软件交付遵循线性、有损失的管道,如图 3 所示。

图 3:传统的软件交付管道

 

每一步翻译都引入了重新解释、手动适应和隐藏的假设。因此,不能阻止架构漂移;它是在晚期被发现的,通常是通过生产事件、失败的集成、安全审计或合规性违规。当检测到不一致时,它是取证而不是纠正。

 

SDD 从根本上将这种流程重构为一个受控的控制循环:

图 4:SDD 受控的软件交付管道

 

这个控制循环用积极的架构强制执行取代了延迟发现。漂移检测不会修补运行时行为;它纠正规范权威,并触发系统的受控再生。传统架构假设代码随时间的推移成为事实;SDD 通过确保规范保持永久的事实来源,并且运行时持续被迫符合它,从而颠覆了这一点。

 

这种架构反转可以概括如下几点:

 

 

在 SDD 中,代码不再是真相出现的地方,而成为真相仅仅被实现的地方。

 

这种反转在结构上等同于早期的范式转变,即从人的责任中移除整个类别的正确性约束,并使其在机械上可执行:

  • 从手动内存管理到垃圾收集,内存安全成为运行时不变量

  • 从裸机到虚拟机,隔离和资源边界成为平台保证

  • 从物理服务器到声明性基础设施,其中配置漂移和拓扑正确性不断得到协调

  • 从无类型语言到静态类型系统,在编译时强制执行结构正确性

  • 从非正式的接口协议到模式和契约强制执行的 API,交互正确性被机械地验证

 

在每种情况下,正确性都从传统上由人类强制执行转变为由平台结构性强制执行。SDD 将这一原则应用于系统边界、架构和行为本身。

 

漂移检测:使架构自我强制执行

一旦规范成为权威,漂移检测不再是一种测试便利;它成为一种强制性的架构能力。它是将意图转化为不变性的执行机制。在这个模型中,漂移不仅仅是模式不匹配;它是声明的系统意图和观察到的系统行为之间的任何偏差。这种偏差可能是结构性的、行为性的、语义性的、与安全相关的或进化性的。我们在实验中遇到的一些例子包括:

  • 一个 API 返回了规范中未声明的字段

  • 一个服务在重构过程中默默地省略了必需的字段

  • 消息负载在没有协调的模式版本控制的情况下不断演变

  • 错误处理偏离了合同保证

  • 相对于最初的策略意图,安全范围正在退化

 

没有漂移检测,SDD 就会退回到文档驱动的开发。有了它,系统就变成了自我监管。漂移检测形成了一个闭环反馈控制系统。它不断地比较系统声称要做的事情和它实际做的事情。这与古典测试相比,后者只提供周期性的、基于样本的保证,是一种根本不同的操作姿态。

 

在传统架构中,意图的偏差会悄无声息地传播,通常数月之后才会以中断、审计失败或安全漏洞的形式显现出来。在 SDD 系统中,漂移变成了机器默认可以检测到的。规范验证器可以直接嵌入我们的 CI 管道中,运行时执行层:模式验证、有效负载检查、契约验证和规范差异引擎都成为了一等的架构组件。当输出违反规范时,系统会快速失败,并允许进行航向修正。

 

这种强制要求在一个固有的多模型未来中变得更加重要。软件系统将越来越多地受到人类驱动开发和机器驱动生成的影响,通常在同一规范表面上并行操作。系统中不再有单一的线性路径。更改可能来自开发者、AI 代理、自动化重构工具或政策驱动的生成器。这种进化路径的多样性极大地放大了漂移问题:分歧不再是边缘情况;它是必须持续治理的自然状态。

 

总体效果是治理方式的深刻转变。架构不再是设计阶段的产物;它变成了一个持续执行的运行时不变量。规范从被动的参考资料转变为主动的控制表面,漂移检测作为反馈信号,保持系统与意图一致。

 

然而,这并不意味着一个完全自主的系统,机器单方面定义正确性。规范不仅仅是一个机械合同;它是人类对目的、风险容忍度和权衡的表达。漂移检测可以识别系统已经偏离,但它不能单独决定这种偏离是可以接受的、偶然的还是可取的。一些漂移代表缺陷,而其他漂移代表进化。在这个边界上,当自动化执行遇到解释性判断时,人类的角色再次变得至关重要。不是作为失败后被动审查日志的审查者,而是作为治理意义、意图和受控变更的积极参与者。这就是 Human-in-the-Loop(人在循环中)不再只是一个安全网,而是一个一等的设计原则。

 

人在循环中:在自动化架构中保留意图

当我们最初探索这种系统设计模式时,我们以一种天真的“氛围编码”心态来接受生成的变化,最小化阻力,并信任 SDD 工具链为我们处理边缘情况。那个假设很快就失败了。取而代之的是一个更强大的认识:SDD 并没有将人类从循环中移除;它将人类判断重新定位到更高的控制层面。问题不再是人类如何实现系统,而是如何以及在哪里治理系统。

 

SDD 并没有消除人类在软件设计中的参与。它重新分配了人类认知的应用领域。传统上,一旦功能实现,开发人员就会花费大量的精力来解决不匹配、调试集成故障、协调分散的服务以及修复更改的意外副作用。随着时间的推移,这被错误地等同于软件工程本身的手艺。实际上,这是维护大型、长期、面向生产的系统的负担。SDD 将这个负担转移到机器上,同时故意保留人类对意图、策略和意义的权威。

 

这引入了一种新型的人机界面。人类仍然是领域语义、风险容忍度、安全范围和系统进化方向的最终守护者。这种权威也扩展到隐含地塑造工程决策的法律、伦理和道德框架中。这些维度不能仅从执行跟踪或行为观察中推断出来。它们存在于机器无法完全拥有的抽象层次上。

 

相反,人类将这些约束明确编码到规范层中,机器承担起执行、生成和持续一致性的责任。这反映了我们技术的历史演变:就像我们曾经将手动内存管理交给垃圾收集一样,我们现在正在将结构性执行和机械一致性委托给 SDD。取代这种委托的不是盲目的自动化,而是明确的审批边界:

  • 破坏模式更改需要人工批准

  • 策略转变需要人工授权

  • AI 提出的重构需要人工确认

  • 兼容性降级需要人工解释

 

因此,SDD 实现了有限的自主性,而不是完全自动化,并且在这些限制内,长期架构意图可以得以保留。

 

通过强制漂移检测和人工意图监督,SDD 在人和机器之间建立了新的责任分工。执行变得自动化。意义仍然是人类的。这种分离不是哲学上的;它是架构上的,正是这种分工产生了一类新的基础设施能力。一个的规范原生系统现在必须将执行、演化、验证和治理直接编码到其核心原语中。我们接下来探讨这些能力,以及它们为什么在结构上与经典软件架构中的能力不同。

 

规范原生系统的核心能力

SDD 不是由单一工具、框架或平台启用的。它源于一组紧密耦合的架构能力,这些能力共同允许规范变得可执行、可强制和可扩展。当这些能力中的任何一个缺失时,SDD 就会退回到文档驱动开发或临时代码生成。要从理论进入操作范式,系统必须内化五个核心能力:

  1. 规范编写作为一等工程表面

  2. 正式验证和类型强制

  3. 确定性生成和组合

  4. 持续的一致性和漂移执行

  5. 受控演化和兼容性控制

 

我们将这种操作规程称为 SpecOps,即规范操作。从 SpecOps 的角度来看,规范被视为一等的、可执行的系统资产,这些能力并没有定义一个产品类别;它们代表了软件意图的控制平面。在规范原生系统中,规范编写不是在实现之前进行的活动;它就是实现活动。因此,系统必须支持多模型规范,其中结构、行为和策略定义共存于统一的模式空间内。这需要可组合的领域建模,使得分层规范成为一种可行的架构策略,而不是文档便利。

 

随着规范成为主要的系统构件,它们必须像源代码一样被严格地处理:版本控制、同行评审、分支和受控合并策略都要是强制性的。此时,规范不再是描述性的,而是成为系统本身的可编程模型。

 

一旦规范是可执行的,它还必须是机器可验证的,就像编译器前端或类型系统一样严格。这种执行涵盖了结构验证、语义一致性和领域不变性执行。条件约束、引用完整性和跨规范一致性必须是可证明的。效果不仅仅是提高了正确性,而是从可以表示的所有内容的空间中消除了整个类别的系统故障,就像静态类型限制非法程序一样。

 

在这个范式中,生成不是脚手架的一种形式。它是声明的系统真理的具体化。这需要严格确定性行为。一个生产级别的规范原生系统必须保证输入的确定性:相同的规范总是产生相同的构件。它必须是目标无关的,跨语言、平台和运行时环境产生一致的输出。最关键的是,生成必须是逻辑可逆的。系统必须始终能够回答一个简单但基础的问题:哪个规范状态产生了这种行为?这种决策的谱系可追溯性是将生成从生产力辅助提升到架构权威的关键。

 

一旦生成自动化,执行就必然变得连续。运行时系统不能再悄悄地偏离声明的意图。实现不能引入未记录的行为。消费者不能依赖于未定义的属性。因此,架构从设计时断言转变为运行时不变性,由系统本身积极维护。

 

SDD 中最困难的能力不是生成或验证,而是不断裂的更改。规范原生系统必须自动将更改分类为添加性、兼容性、破坏性或模糊性,并执行明确的兼容性策略。这引入了受控演化的正式概念:需要并行版本表面、已知的兼容性窗口、受控的弃用曲线和用于破坏性更改的显式批准门。没有这个,SDD 在架构上就会变得脆弱。有了它,系统可以在不违反自己的正确性保证的情况下进行演化。

 

这五个能力引入的最深刻的转变不是技术上的;它是结构上的。

 

交付的单元不再是服务或代码库。交付的单元变成了规范本身。这将结果与产出重新对齐:声明的是什么,交付的就是什么。这与以氛围驱动、生成性编码方法形成鲜明对比,在这些方法中,偏差是创造力(或幻觉)的涌现属性,而不是设计中受控的行为。

 

结论:存在的工程权衡和挑战

软件工程中每一次重大的抽象飞跃都带来了非凡的生产力提升,同时引入了全新的系统性风险类别。垃圾收集消除了大量内存错误,同时引入了暂停时间行为和新的故障模式。虚拟机简化了部署,同时增加了业务编排的复杂性。云平台消除了基础设施负担,同时引入了深层操作耦合。规范驱动开发也不例外。

 

通过将系统的真实来源提升到规范和生成器中,SDD 并没有消除复杂性;它只是简单地重新定位了复杂性。下面的权衡定义了我们在大规模采用这种范式时所经历的真实工程成本。

 

规范成为主要的复杂性表面

在 SDD 中,规范不再是文档构件,而是成为长期存在的可执行基础设施。因此,它们获得了传统上与源代码相关的属性。它们继承了通常与源代码相关的所有属性:技术债、跨团队耦合、兼容性惯性和架构引力。因此,模式工程成为了与数据建模和分布式系统设计同等重要的一级架构学科。

 

生成器信任成为供应链问题

在 SDD 中,AI 代码生成器不再是开发者的便利工具。它们成为系统可信计算基础的结构组件。确定性、可重复性、可审计性、沙箱执行和可验证的出处不再是可选属性;它们是强制性的。代码生成从工具提升为关键基础设施。

 

运行时执行有实际成本

SDD 将执行从社会过程转移到技术控制。这种转变是强大的,但不是免费的。运行时契约验证引入了实际的计算开销。在小规模上,这个成本是微不足道的。在大规模上,我们需要考虑系统的目的,无论是高频 API、实时流还是对延迟敏感的系统。这成为了一个明确的架构预算项目。正确性成为了计量资源,而不是默认的免费属性。

 

认知转变非同小可

SDD 用契约优先推理取代了实现优先的思维。这要求工程师采用新的心智模型:

  • 用不变量而不是行为来思考

  • 关于兼容性而不是功能的推理

  • 用声明式而不是过程式表达意图

  • 将模式视为可执行程序

 

历史上每一次抽象的转变都扩大了人类的影响力,同时引入了不熟悉的失败模式,需要多年才能掌握。SDD 现在正进入相同的成熟曲线。

 

架构权威的价格

虽然新的范式转变通常令人兴奋,但最终是否采用这一转变归结于平衡所涉及的实际权衡。

 

一方面,SDD 提供了:

  • 架构确定性

  • 持续的正确性执行

  • 系统性减少漂移

  • 多语言平价

  • 可复制的系统边界

 

但它以以下代价:

  • 模式复杂性

  • 生成器信任要求

  • 运行时验证成本

  • 长期兼容性负担

  • 工程角色的认知转变

 

这不是避免 SDD 的理由。这是一个有意识地采用它的理由,要有明确的治理、有纪律的规范实践,以及对其成本的清醒认识。每一次抽象的飞跃都需要新的严谨形式。

 

SDD 只是将这种严谨重新定位到它一直属于的地方:系统真理本身的定义。

 

原文链接:

https://www.infoq.com/articles/spec-driven-development/