一句话定义:API 契约(API Contract)是调用方和服务方之间关于“怎么问、怎么答”的明确约定;它不只写在文档里,还是双方都要遵守的承诺。
这份约定具体包含什么
它规定的不只是“请求哪个地址”。一份完整的契约通常包括:请求方法、参数名与类型、哪些必填、返回结果的字段结构与嵌套关系、错误码的含义、认证方式、分页规则、超时与限流策略,以及“哪些改动算破坏性变更”。这些内容合在一起,才构成契约。
打个比方:餐厅菜单
菜单就是顾客与后厨之间的契约。菜单写着“宫保鸡丁,28 元,微辣”,你照此点单,后厨就能接住。如果餐厅把菜名改成“宫保鸡丁(新版)”,或者把价格单位从元改成角,或者悄悄删掉“微辣”选项,你的点单习惯就会失效。
最麻烦的地方在于:菜还是那道菜,但分量从大份换成小份。此时不会报错,你只会吃得不满意。这就是契约里最危险的一类变化——结构没变,含义变了。
为什么一改就崩
调用方代码是根据旧契约提前写好的:它按固定字段名取值,按固定类型解析,按固定枚举值走分支。这类代码只认“旧答案”。
| 变更类型 | 例子 | 调用方的反应 |
|---|---|---|
| 删字段 / 改名 | user_name 改成 username | 解析报错,或取到空值 |
| 类型变更 | id 从数字变成字符串 | 强类型语言直接抛异常,弱类型静默算错 |
| 语义漂移 | 金额单位从“元”变“分” | 不报错,但账全错 |
| 枚举新增值 | 状态多了一个新取值 | 未兜底的分支走进 default,显示成“未知” |
| 收紧校验 | 原本可选的参数变成必填 | 请求被直接拒绝 |
可以看到,真正会显式报错的情况其实只占一部分。静默出错比崩溃更难排查:系统照常运行,数字全错。
和相邻概念的区别
- API 文档:文档是契约的一种表达方式,可能过期;契约指实际承诺的内容。
- API 规范(如 OpenAPI):机器可读的描述格式,是契约的载体,不等于契约本身。
- SLA(Service Level Agreement):承诺的是可用性、延迟这类服务质量,属于契约中“服务水位”的部分,不是结构约定。
- 接口:接口描述“长什么样”,契约还额外规定“你我都不能随便动它”。
对实际工作的意义
对从业者:加字段通常安全,删字段、改类型、改语义是破坏性变更,必须走版本化和废弃期(deprecation);发布前运行契约测试(Consumer-Driven Contract Testing),让调用方的期望成为自动化检查;对外接口保留 v1、v2 并存窗口,具体策略以官方页面为准。
对职场人:下次听到“只是改了个小地方怎么就全线故障”,答案往往在这里——改动的是几行代码,撕掉的是别人依赖的一纸约定。
