企业 API 做向后兼容,绕不开四个决策:版本号放 URL 还是 Header、旧版本维护多久、废弃字段怎么通知、破坏性变更怎么灰度。本文给供正在维护存量集成的团队参考。
先说一句可能不好听的话:企业客户不会因为你发了 v2 就去改集成。 老接口会一直活着,因为对客户来说,改代码的成本永远高于继续用老接口的成本。
所以"兼容性"不是"方便",是客户的沉没成本。做不好,客户要么被反复打扰,要么直接流失。
版本号放 URL 还是 Header
放 URL(/api/v1/order):显式、可缓存、可分别限流,运维和客户都看得见"我调的是哪个版本"。代价是污染 URL、也谈不上"纯 REST"(路径承载了版本语义)。对我们这种要按版本治理的场景,省心。
放 Header(Accept: application/vnd.xxx+json;version=1):资源不变、路径干净。但对客户不友好------不好调试、缓存和网关识别成本高,很多客户根本不知道自己在调哪个版本。
选URL 为主。不是因为它更"规范",而是因为企业客户需要"看见"版本。Header 方案更适合内部、版本敏感的消费方。
前提是:URL 只承载大版本(v1/v2),小版本兼容靠字段级演进(只增不改不删)来消化,不进 URL。否则路径会爆炸成 /api/v1.3.2-beta/...,从"显式"变成"灾难"。
旧版本维护多久
没有标准答案,取决于合同和存量。参考策略:
新版本发布后,旧版本至少并存一段固定窗口(比如 6-12 个月)。
窗口内,破坏性变更只在"新大版本"里做,老版本只做安全修复,不做新功能------避免客户"既想升又怕升"。
到期前多轮通知,明确"X 月 X 日强制下线"。强制下线不是赌气,是防止你背着越来越多的老版本,最后没人敢动。
现实是 6-12 个月对企业客户偏乐观,真实约束是采购周期 + 变更审批 + 集成回归测试串起来,轻松超过一年。更务实的做法是把"维护多久"拆成兼容窗口(合同和客户分层决定)和成本窗口(你的技术债承受力决定),两者不重合时用"安全修复继续、功能冻结、逐步降级 SLA"过渡,别搞成日历事件到点拔电。
字段废弃怎么通知
只发 changelog 是没人看的。用"技术强制 + 文案告知"双管齐下:
Deprecation 信号:废弃的字段,在响应/Header 里带上 Deprecation 或 Warning 提示,客户调用时能肉眼看到"这个字段要没了"。
文档改版预告:在 API 文档里把废弃字段标红、写明替代字段和下线时间。
提前 N 个版本预告:废弃的字段先在 v1 里标废、再在 v2 里真删,给足迁移窗口。
坑是:废弃信号如果只在文档里,等于没通知。 必须让它在客户"正在调用"的那个动作上出现,他们才会真的去改。
再补一刀:光有信号不够,还得知道谁在用、用多少。没有字段级用量统计,"提前 N 个版本预告"就是盲发。正确顺序是:先按调用方埋字段级用量 → 标 Deprecation + Header 警告 → 按用量定向通知(大的先谈、给迁移方案)→ 用量降到阈值以下再删,删前留一个"软删除返回空/默认值"的缓冲期。群发 changelog 是告知义务,定向通知才是迁移推动。
破坏性变更怎么灰度
影子流量:新逻辑先在影子环境跑,把结果和线上比对,不一致就不放量。
按调用方灰度:从低价值、内部调用方开始切,观察错误率再放量到外部大客户。灰度期间随时回滚。
双跑期:新旧逻辑同时在线一段,比对正确性,稳定了再切。
兼容性灰度的重点是"客户视角的破坏面"------你不是在灰度一个内部版本,是在灰度所有客户集成行为的总和,出问题要能立刻判定"是哪个调用方受影响"。
补一个工程手段:消费者驱动的契约测试。破坏性变更有种阴险之处------它不报错,只是语义变了(金额精度、时间戳单位、枚举多一个取值),HTTP 200 但业务错账,错误率指标看不出来。契约测试把消费方期望显式化,能在灰度阶段把这类"静默破坏"挡在门外。
总结
企业 API 的兼容性,本质是在"客户的沉没成本"和"你自己的技术债"之间找平衡。版本放哪、维护多久、怎么通知、怎么灰度,每一个选择都有代价,也都会有人反对。
决策清单:
版本号:URL 承载大版本为主,Header 留给内部或版本敏感消费方
维护窗口:兼容窗口与成本窗口分开算,到期前阶梯降级而非硬拔
字段废弃:Deprecation 信号 + 字段级用量 + 定向通知,删前留缓冲期
破坏性变更:影子流量 + 按调用方灰度 + 双跑 + 契约测试
你们的老版本维护窗口是多久?字段废弃是群发 changelog 还是定向通知?欢迎在评论区说说实际做法.