API 废弃计划:写进契约里的兼容通知
如何从第一天起就把废弃计划写进 API 契约:Sunset 响应头、废弃时间线、客户沟通节奏,以及真正关键的用量上报步骤。如果一个接口上线时连"怎么死"都没写清楚,那它的设计就还没完成。
Deprecation 是上线日的事,不是下线日的事
我从没见过哪个团队能"顺利"下线一个上线时没规划过下线的接口。那些真正成功下线的接口,原始契约里就写好了 sunset 策略。那些失败的案例,通常是跑了六个月的 Slack 宣传,时间线延了两次,最后下线当天还是被 VP 拉进电话会议——因为某个没人追踪的支付集成挂了。
这是结构性原因。接口一旦上线,每个接入的客户都会默认它的形状永远不变。如果你在签约时没告诉他们 v1 有 sunset 时间线,也没承诺会测量他们的调用量并把具体数字邮件发给他们,那你手里根本不是废弃计划,是一份祈祷书。
你本该已经在发的 RFC 标准信号
有三个机器可读的信号。每个已废弃接口的响应都应该同时带上这三个,而你的契约应该承诺:从宣布废弃那一刻起就开始发。
Deprecation:头(RFC 9745):HTTP 日期,标明接口是什么时候被标为废弃的。不是下线时间,是倒计时开始时间。Sunset:头(RFC 8594):HTTP 日期,标明接口什么时候停止响应。定一次就不要随便改。Link: <https://docs.example.com/migrate/v2-invoices>; rel="deprecation":指向具体的迁移指南,不是文档首页。
如果你的客户端库不把这些响应头作为警告浮现出来,客户就会漏掉。对于那些不解析新头的老 HTTP 客户端,我会额外加一个 Warning: 299 - "Deprecated API",并且在服务端把每一次调用废弃接口的动作都记下来——客户 ID、接口、时间戳。
没有度量就没办法废弃
这是大多数团队会跳过的一步,但它决定了计划是不是真的。在宣布废弃之前,你需要至少 90 天的、按客户、按接口的调用日志。不是聚合指标,不是 Dashboard,而是一条你能跑出来的查询:客户 X 上周调用了 GET /v1/invoices 41,203 次,分布在这 7 个 IP 上,User-Agent 是这个。
没有这个数据,你 T-6mo 发出去的邮件只能写"您可能受到影响";有了它,邮件就能写"您上周调用该接口 41,203 次,我们检测到以下具体调用点"。前一种会被无视,后一种大概率一小时内就收到对方技术负责人的回复。
带数字的沟通节奏
我给公共 API 的默认节奏是 T-12mo、T-6mo、T-3mo、T-1mo、T-2wk、T-1wk、T-24h。每封邮件都包含该客户过去 30 天在废弃接口上的真实调用量。T-3mo 那封最关键,因为这个时间点接入团队还有足够档期安排正式工作,但又还不足以拖延到最后一刻。
内部 API 可以把窗口压到 3-6 个月,因为消费者在你自己手里。伙伴 API 则完全看契约怎么写——这意味着 sunset 条款必须写进合同,而不是发在博客上。如果合同里没提,那你等于欠了伙伴无限期的支持义务。
宣布之前先把迁移文档写完
这听起来是废话,但违反的人很多。迁移指南必须在第一封废弃邮件发出之前就已经存在、完整、并且包含一段能跑通的代码样例。如果指南是在宣布一周后才上线的,那你等于让客户围绕一个还不存在的目标开始做排期,他们会投诉的。
我会把迁移文档作为 v2 的最后一步来写。如果我写不出一段清晰的迁移说明,那就说明 v2 还没做完。这是一个硬性检查点:客户方一个后端工程师应该能读完文档、把旧字段映射到新字段、估出工作量,而不用提工单。
优雅下线 vs 破坏性变更:双向门和单向门
优雅下线是指:在 sunset 日期之前,老接口继续返回合法数据,只是加上废弃相关的响应头。这是一扇双向门——漏掉邮件的客户仍然能用;如果有大客户卡住了,你可以延时间线;通过调用日志还能发现那些晚反应的客户。
破坏性变更是指:到 sunset 当天,接口直接翻成 410 Gone。这是一扇单向门。只有在老接口本身不兼容、泄漏数据或存在安全漏洞时才该这么做。不要因为你自己懒得维护老 handler 就这么干。"懒得维护"不是够格引发故障的理由。
一个具体例子:废弃 GET /v1/invoices
假设 v2 把分页从 offset 改成 cursor,这是对请求契约的破坏性变更。计划是这样:
- 上线 GET /v2/invoices,与 v1 并存。v1 继续正常响应。
- 每个 v1 响应都加上
Deprecation: @1736899200、Sunset: Wed, 15 Apr 2026 00:00:00 GMT,以及指向 /migrate/v2-invoices 的 Link 头。 - 埋点按客户统计 v1 调用量,每天导出到 CRM,让客户团队看得到。
- 在 T-12mo、T-6mo、T-3mo、T-1mo、T-2wk、T-1wk 发计划内邮件,邮件里带上该客户过去 30 天的 v1 调用次数。
- 到 T-0,v1 返回 410 Gone,响应体里指向 /v2/invoices。把 410 响应保留至少 90 天,让迟到者看到一个有用的错误,而不是 404。
验收标准
- Given v1 接口处于废弃窗口期内 When 任意客户调用 GET /v1/invoices Then 响应带有 Deprecation、Sunset 和 Link: rel="deprecation" 三个头 And 这次调用被记录,包含客户 ID、时间戳、User-Agent And 该客户在下一轮计划内废弃邮件中出现,并附带其调用次数 - Given sunset 日期已过 When 任意客户调用 GET /v1/invoices Then 响应为 410 Gone And 响应体包含指向 v2 迁移指南的链接 And 该调用仍被记录,用于 sunset 后的迟到者报表
对废弃本身做测试
合成监控应当验证:所有废弃接口的响应头确实在;Sunset 日期与宣布的日期完全一致;Link 头里的迁移文档返回 200。我见过线上的迁移 URL 直接 404,原因是文档仓库在废弃发布之后被重构过。合成测试一分钟内就能抓到,客户的邮件则要一周之后,而且代价是信任。
什么时候可以破例
安全问题有资格把时间线压短。老接口里的凭证泄漏事件不值得 12 个月的缓冲;一个可以接管账号的漏洞,72 小时内下线是合理的。把这个例外明确写进策略,别让安全团队在事故中还得现场争取。"安全豁免:经工程 VP 批准后 sunset 可短至 24 小时"——这种条款才是让策略真正落地、而不是停留在愿望里的关键。
可复制产物:契约评审包
当工作涉及 API 行为、schema、事件、重试或消费者预期时使用。它会把兼容性和发布证据提前摊开。
API 契约评审包:API 废弃计划:写进契约里的兼容通知 本次要做的决策: - 确认契约变化是否兼容,消费者需要什么迁移动作,发布后如何观察风险。 责任人检查: - 产品责任人: - 工程责任人: - QA 或运维评审: 范围边界: - 本次包含: - 本次不包含: - 仍需确认的假设: 验收证据: - 测试或 fixture: - 日志、指标或截图: - 人工复核步骤: 契约边界:没有兼容性分类、消费者影响、重试行为和回滚说明,不进入发布。 评审追问: - 没参加需求会的人还会误解哪里? - 哪个证据能证明这次改动足够安全,可以发布?
编辑复核记录
复核日期:2026-04-28。本次补充了可复用产物,按相关主题 Hub 检查了文章定位,并收紧下一步链接,让页面更像可操作参考,而不是孤立长文。
弃用发布清单
在宣布移除日期之前,先把契约计划变成一份可以评审的清单。它应该证明消费者能发现变更、按时迁移,并在漏掉依赖时有恢复路径。
下载:api-deprecation-plan-template.md
- 列出现有消费者,并为每个迁移沟通指定负责人。
- 在宣布移除日期前,把 Sunset、Deprecation 和 Link header 加到旧端点。
- 发布一个迁移示例,展示旧请求、新请求、旧响应和新响应。
- 用合成测试确认 header 和迁移文档在通知窗口内一直可用。
- 把安全紧急例外路径与普通弃用窗口分开记录。
需要追踪的证据
真正有用的证据是消费者是否迁走,而不是文档是否完整。持续看旧端点流量、受影响客户的支持工单、迁移文档访问、warning header 上线后的失败请求,以及需要延期的消费者名单。
专题阅读路径
这篇文章归入 API 契约 主题。先读 Hub,再结合下面的清单、模板或工具落到具体项目里。
继续阅读
编辑说明与免责声明
最近复核:2026-04-28。编辑部检查了示例、内链和可复制评审片段,确保内容更适合真实项目使用。
本文用于软件工程教学与实践参考,不构成法律、税务或投资建议。示例场景用于解释规格方法,不对应真实客户数据。
- 作者信息:Spec Coding 编辑部
- 编辑政策:编辑与事实核查政策
- 联系方式:联系页面