第三部分 — 信任层的九项原则
原则二 — 契约即宪法
一条流水线是一个由组件组成的社会。组件由不同的人、在不同 时间、用不同的语言、以不同的发布节奏构建。把这样一个社会 凝聚在一起的,不是善意,也不是统一的代码风格。而是一部 宪法:一小组经过版本化的、公开的、被强制执行的约定,规定 什么可以跨越边界。
在软件中,这部宪法由契约构成——契约是模式(schema), 定义了组件之间传递的每一份载荷的形状。一个良好契约制度的 规则不多,但很严格:
- 不可变地版本化。 契约一经发布,永不更改。在同一主版本 内允许增量补充;任何可能破坏消费方的改动,都必须新建一个 版本目录,而不是原地编辑。
- 自我声明。 每一份载荷都声明自己的契约——一个名字加一 个版本号,盖在信封上。组件收到一份引用了未知主版本契约的 载荷时,必须响亮地拒绝它。
- 在边界处验证。 组件内部百无禁忌——任何语言、任何库、 任何内部重构都可以。但在边界上,只有符合契约的载荷才能 通过。验证住在边界上;边界之外,皆是自由。
为什么这是正确的形状
其他形状都会以各自特有的方式失败。共享库制造编译期耦合, 使独立部署成为不可能。"文档"在截止日期出现的那一刻就开始 腐坏。临时凑合的格式悄悄变异,直到某个晚上,上游团队改了 一个字段名,下游流水线便信心满满地产出垃圾。契约不会以 上述任何一种方式失败,因为契约是被执行的,而不是被查阅的: 拒绝一份畸形载荷不是一项政策,而是一种机制。
更深层的价值是经济性的。契约让组件可以互换,而可互换性正是 小团队强大的原因。当制表引擎、ADaM 构建器、图形内核和 QC 比对器都通过稳定的契约对话时,你可以换掉其中任何一个—— 包括用一个开源实现替换自研实现,或用一个更好的实现替换过时 的实现——而不必与系统其余部分重新谈判。宪法保护的是联邦, 而不是某个公民。
立宪纪律
与任何宪法一样,契约只有在护栏既是文化性的又是机械性的 情况下才有效:
- 契约变更是事件,不是编辑。 它们值得一份书面记录——改了 什么、为什么改、谁在消费旧版本、以及迁移路径。在健康的 系统中,这份记录是一条架构决策条目,像代码一样接受评审。
- 模式是唯一事实来源。 任何转述模式的文档,最终都会与 模式相矛盾;把转述当作评注,绝不当成法律。
- 不允许未记录的例外。 一旦某个组件以"兼容"为由接受了一 份违反契约的载荷,宪法就已经被行政命令中止了,此后每一场 争论都会援引这个先例。
还有一个安静而容易被忽略的好处:契约把人与人之间的信任, 转化为系统与系统之间的信任。两个几乎不打交道的团队也能 安全地集成,因为双方都不必信任对方的内部实现——只需信任 那个共享的、版本化的、由机制强制执行的边界。在组织里,这 不是技术上的锦上添花。这是和平。
检验。 取流水线中任意两个组件,问:"它们彼此之间究竟 承诺了什么?这份承诺写在哪里?有什么物理上的东西能阻止 任何一方悄悄违约?" 如果答案涉及某个人的记忆、一页过时 的 wiki、或"应该没问题吧"这种话,那就不存在宪法——只有 外交,而外交在截止日期面前必败。