接口设计的向后兼容
接口一旦有外部调用方,它就不再是你的代码,而是一份契约。向后兼容是契约的底线。
哪些改动是破坏性的
请求侧:
- 删除字段、重命名字段(对调用方等价于删除);
- 新增必填参数;
- 收紧校验(原来接受 1-100,现在只接受 1-50);
- 字段类型变化(int → string)。
响应侧:
- 删除或重命名响应字段(调用方可能正在读);
- 修改枚举值语义(status=2 从「已支付」改成「已取消」);
- 错误码含义变化或结构变化。
语义侧(最隐蔽):
- 同一请求结果变化(把「模糊匹配」改成「精确匹配」);
- 副作用变化(幂等接口变成非幂等)。
平滑演进的常用手法
只加不改:新增字段而不是改字段;新逻辑放新字段,旧字段过渡期返回原值。
// v1:{"name": "张三"} → 保留
// v2:{"name": "张三", "fullName": {"first": "张", "last": "三"}}
容忍输入(Postel 法则):对自己输出的格式要严格,对接受的输入要宽容——未知字段忽略而不是报错,给调用方演进空间。
版本并行:URI 前缀(/v2/orders)最直观,Header 版本(X-Api-Version)URL 干净。旧版本必须有明确的废弃时间表,并提前通知调用方,日志统计旧版本流量归零后再下线。
破坏性变更的落地流程
- 发布变更公告(影响面、时间表、迁移示例);
- 新旧并行,旧版本只读或打废弃标(响应头
Deprecation: true); - 用日志确认旧版本调用方迁移完成;
- 按承诺时间下线,下线后返回明确的 410 + 迁移指引,而不是 404。
内部接口也别大意
内部调用方(其他团队、自己的前端)同样是调用方。区别只在于沟通成本更低——但不检查就改,代价是联调时间。把「改接口前先查调用方」做成习惯,比事后修兼容补丁便宜得多。