API 体验日志: 该公开什么内容,又是谁在读它
阅读约 1 分钟 更新于
API 体验日志是对调用方可能注意到的每一个变更所做的、带有日期的记录,它是写给那些正在与这个 API 进行集成的人的,而不是写给发布它的团队的。正是这样的读者群体,让它成为一份和产品体验日志完全不同的文档:读者正在判断自己的代码下个月是否还能正常运行。大多数 API 体验日志都以同样的方式失败了,它们成了内部发布信息流的一份经过过滤的副本,于是一个被删除的字段和一处文字修正被摆在了同样的权重上,而这两者最终都没有人真正去读。
API 体验日志到底是什么
它是一份公开的、带有日期的日志,记录着一个别人已经为之编写过代码的接口发生了哪些变更。判断某样东西是否应该被收录进去的一个有用的测试,和这个变更在内部到底有多大完全没有关系。这个测试问的是:一个去年就已经写好、此后从未被改动过的正确的调用方,会不会因为这个变更而表现出不同的行为。这个测试会接受一些非常微小的变更,也会排除一些非常巨大的变更。
以下所有内容,默认前提是调用方身处公司之外,而且除了这份文档之外,实际上根本没法被联系上。当调用方是同一家公司里的另一个团队时,这笔账会变得完全不同,值得单独拿出来讲一讲;对内 API 体验日志 讲解了这群读者到底需要的是什么。
| 文档 | 读者 | 回答的问题 |
|---|---|---|
| API 体验日志 | 调用 API 的开发者 | 我的集成还能正常工作吗? |
| 发布说明 | 产品的用户 | 我现在能做什么以前做不到的事? |
| 停用通知 | 调用某个具体事物的人 | 这个东西什么时候会停止工作? |
| 状态页面 | 当前正受到影响的任何人 | 现在是不是宕机了? |
| 迁移指南 | 正在升级的调用方 | 我该怎样从 A 迁移到 B? |
怎样写一份真正管用的 API 迁移指南 完整讲解了这最后一份文档;简单来说,这正是一条不兼容变更记录理应链接过去、而不是试图去替代的东西。
这五者是各自拥有独立生命周期的独立文档。停用通知是一个带有日期的承诺,它也应该被收录进体验日志,但体验日志的一条记录只会被写一次,而一个停用计划则会被一直追踪到它的服务下线为止。把两者混为一谈,正是服务下线日期常常被错过的原因。
一条记录里应该包含什么
六件事,而前三件恰恰是通常缺失的那些。这个变更本身,用请求或响应的角度来表述,而不是用内部组件的角度。它是否会破坏一个正确的调用方。调用方需要做什么,包括”什么都不需要做”。它生效的日期。受影响的版本或版本范围。如果存在迁移指南,还需要附上链接。
一条写着”改进了 accounts 端点”的记录,在这六项上全部失败了。一条写着”accounts.type 字段现在返回 individual,而它以前返回的是 personal;对于 9 月 2 日之前创建的账户,现有的值不会发生变化;除非你在比较这个字符串本身,否则不需要采取任何行动”的记录,用一句话就回答了全部六项。
请按后果而不是按部门来对记录进行分类。三个标签几乎承载了全部价值:breaking、additive 和 fixed。Semantic Versioning 已经精确定义了前两者,借用它的定义而不是自己另行发明,意味着一个了解 semver 的读者也能理解你的标签。如果你愿意,Keep a Changelog 提供了一套更长的标签体系,而它的核心原则在这里比在任何其他地方都更加成立:日志是写给人看的,一堆提交标题的堆砌绝不是。
API 体验日志和发布说明到底有什么不同
发布说明描述的是产品现在能做什么。API 体验日志描述的是契约现在是什么样子。同一份已经发布的工作,往往会在两边都产生一条记录,但表述方式各不相同,因为不同的读者需要的东西也不一样:一种新的导出格式,对用户来说是一项功能,但对于依赖那个字段来分支处理的调用方来说,则是一个新的枚举值。
由此带来的实际后果是,这两者不能是同一条信息流,只是换了个样式而已。一个订阅了你发布的所有内容的调用方,最终会取消订阅,然后就会错过那个破坏性变更。如果你只发布一条信息流,请对它做过滤;如果你发布两条,请让 API 那条更窄,并且永远不要让市场营销类的记录混进去。我们在体验日志与发布说明的对比一文中并排比较了这两种形式。
API 体验日志应该放在哪里
放在参考文档旁边,使用一个稳定的 URL,让每一条记录都能通过一个片段或者一个独立的路径被单独寻址。调用方会在事故复盘和内部工单中引用这些记录,而一条无法被链接的记录,最终只会被人当作截图贴出来。
除了做成一个页面之外,也请把它作为一份机器可读的输出来发布。一条遵循 JSON Feed 规范 的 JSON feed,或者一条 RSS feed,一旦记录变成了结构化的数据,就几乎不需要额外成本,而这正是让客户能够把你的变更纳入他们自己发布流程的关键。这也决定了是否会有人在此基础上进行构建。GitHub 出于同样的原因,把它的 REST API 版本 文档放在参考文档的正旁边:版本策略本身就是接口的一部分。
一条好的记录在实践中是什么样子
同一周内的三条记录,采用上面描述的格式:
2026-09-02 Breaking v2
`POST /invoices` 现在会拒绝与客户账户货币不一致的 `currency`,
返回 422 而不是默默地进行转换。依赖过转换行为的调用方,
必须改为发送账户所使用的货币。只影响 v2;v1 在 2027-01-15
的服务下线日期之前不会发生变化。
2026-09-02 Additive v1, v2
`Invoice` 新增了一个 `settled_at` 时间戳字段,在发票结清之前
该字段为 null。不需要采取任何行动。拒绝未知字段的客户端应该
进行更新。
2026-08-31 Fixed v2
`GET /invoices?status=` 之前对未知状态返回的是一个空页面,
而不是 400。现在会返回 400,并附上被接受的合法取值。之前
拼写出错的调用方本来看到的是零条结果,现在会看到一个错误。
第三条是最常被省略的一种,因为在内部看来它只是一次 bug 修复。但对于已经围绕那个空页面构建了重试逻辑的调用方来说,这其实是一次行为变更,而这条记录正是能阻止支持工单产生的东西。标签写着 fixed,正文说明的是调用方可能会注意到什么,正是这种区分,让整份日志在不把每一次修复都夸大成破坏性变更的前提下,保持了诚实。
调用方应该如何订阅它
给他们不止一个渠道,因为他们的任务各不相同。给想要一切信息的开发者提供一条信息流。给只想要破坏性变更的人提供邮件。给代码本身提供响应头,它是唯一一个永远不会忘记检查的订阅者:RFC 8594 中定义的 Sunset 头 会把服务下线日期放进响应里,客户端库可以据此把它记录下来。
大多数团队会遗漏的渠道,是直接联系。如果某个调用方上周刚好用过你正要改动的那个字段,你其实知道那是谁,而给这些账户发一封邮件,价值要远远高于任何一次广播式的通知。这和闭合客户反馈循环遵循的是同一种纪律,只不过应用在了一个没有人主动要求过的变更上:受影响的人会被单独通知,其余所有人则会收到信息流。Webhook 是第四条渠道,有它自己的失败方式,值得在依赖它之前先弄清楚:webhook 体验日志讲的是为什么那边的 payload 改动会悄悄坏掉,根本没有调用方能拒绝那个新形态。
该如何为一个破坏性变更编写记录
请从这个变更本身的破坏性说起,而不是从原因说起。一个正在浏览十条记录的调用方,必须在第一句话里就知道这一条会不会给他带来额外的工作。然后才是日期、受影响的版本、迁移方式,以及旧行为如果是要消失而不是变化,那对应的最后期限是什么。
请把同样的内容,以一致的措辞,分别放进停用通知、响应头和直接邮件之中,并且给这四者设定同一个日期。它们之间的不一致,正是把一次本该有计划的变更变成一场事故的失误,因为一个只读到其中一份内容的调用方,会依据错误的日期采取行动。什么是破坏性变更一文讨论了这个决定本身,而如何停用一个 API则讨论了随之而来的时间安排。
在 changeloop 中,当一个 pull request 被合并、有人编辑并批准了草稿之后,一次 API 变更就会成为一条记录,而这条记录会发布到信息流和小组件上。同一时刻,如果某位调用方的小组件反馈变成了 GitHub issue,而这个 pull request 关闭了该 issue,那位调用方就会在那个 issue 上收到通知。真正重要的是审核这一步:API 体验日志是一份具有契约性质的文档,任何草稿都不应该在没有人真正读过之前,就到达调用方手中。
FAQ
每一次 API 变更都需要一条体验日志记录吗? 只要是一个正确的调用方可能会注意到的变更,就需要,哪怕你自己认为它是内部性质的。对请求或响应没有可观察影响的变更则不需要,把这类变更也加进去,只会训练读者养成一目十行的习惯。
API 体验日志应该放在文档里,还是放在市场营销网站上? 放在文档里,就在参考文档旁边。读者通常本来就已经在那里了,而市场营销网站上的体验日志,往往会吸引到一批它原本并不是为之而写的读者。
它应该往回追溯到多久以前? 无限期。这些记录会在多年之后依然被人在事故复盘中引用,而一份被截断的日志会把这些链接全部弄断。请用分页,而不是删减。
我需要为每一个 API 版本都维护一份单独的体验日志吗? 不需要。一份日志里每条记录带一个版本字段,会更容易阅读,也更容易搜索。按版本过滤是页面本身应该具备的功能,而不是把文档拆开的理由。
本文的技术内容未经独立审核。如有错误,请告诉我们,我们会更正。