Reach

Grow

Manage

Automate

Reach

Grow

Manage

Automate

RESTful API 版本控制最佳实践:为什么 v1 是第一

电子邮件

1 min read

RESTful API 版本控制最佳实践:为什么 v1 是第一

电子邮件

1 min read

那么,给一个API版本化有多难呢?真相是这并不难,但困难在于保持某种理智,而不是不必要地陷入数量繁多、相互不兼容的版本和子版本中,这些版本和子版本应用于数十个API端点。

重大更改糟糕!API版本管理好!

任何构建或经常使用API的人迟早会意识到,重大变更是非常糟糕的,并且可能对一个原本有用的API造成非常严重的伤害。重大变更是指API行为的改变,它可能会破坏用户的集成,导致大量的挫折以及API提供者和用户之间信任的丧失。重大变更要求用户提前收到通知(以及随附的道歉),而不是像令人愉悦的新功能那样突然出现。避免这种挫折的方法是对API进行版本控制,并从API所有者那里获得保证,在任何单个版本中不会引入令人惊讶的更改。


那么,对API进行版本控制会有多难呢?事实上并不难,但难的是保持一些理智,不要无谓地演变到几十个API端点上应用头晕目眩的多个版本和子版本,并且兼容性不明确。


我们在三年前引入了API的v1版本,并没有意识到它会一直走到今天。那么我们如何在两年多的时间里持续提供最佳的电子邮件传递API,但仍维持同一个API版本呢?虽然对如何对REST API进行版本控制有很多不同的意见,但我希望我们这个不起眼却强大的v1版本的故事,能在你通往API版本控制启迪的道路上为你指引方向。

REST Is Best

SparkPost API 源自我们还是 Message Systems 的时候,在我们涉足云之前。那时,我们正在为 Momentum 4 的测试版发布做最后的准备。这是 3.x 版本的重大升级,我们市场领先的本地 MTA。Momentum 4 包含全新的 UI、实时分析,最重要的是一个用于消息注入和生成、管理模板和获取电子邮件指标的新 Web API。我们的愿景是实现 API 优先的架构——即使 UI 也会与 API 端点交互。


我们做出的最早和最好的决策之一是采用 RESTful 风格。自 2000 年代后期以来,表示状态传递(REST)基础的 Web API 是云 API 的事实标准。使用 HTTP 和 JSON 可以让开发人员更轻松地集成我们的 API,无论他们使用哪种编程语言——PHP、Ruby 和 Java ——而不必了解或关心我们底层的技术。


选择使用 RESTful 架构很容易。选择版本控制约定就不那么容易了。最初,我们通过完全不对测试版进行版本控制来回避这个问题。然而,不到几个月,测试版到了少数客户手中,我们开始构建我们的云服务。是时候进行版本控制了。我们评估了两种版本控制方案。第一个是将版本直接放在 URI 中,第二种是使用 Accept 头。第一个选项更明确,也更简单,这对开发人员来说更容易。由于我们爱开发人员,这是合乎逻辑的选择。

API 管理

选择了版本控制惯例后,我们遇到了更多问题。我们什么时候会提升版本?什么是重大变更?我们会重新版本化整个API还是仅某些端点?在SparkPost,我们有多个团队在处理API的不同部分。在这些团队中,人们在不同的时间处理不同的端点。因此,我们的API在使用惯例时保持一致性非常重要。这比版本控制更重要。


我们成立了一个治理小组,包括代表每个团队的工程师、产品管理团队的一名成员和我们的CTO。该小组负责在所有团队中建立、记录并执行我们的API惯例。一个API治理Slack频道在关于该主题的热烈讨论中也派上了用场。


治理小组确定了若干可以引入API的方式,这些更改对用户有利且不构成重大变更。这些包括:


  • 新资源或API端点

  • 新的可选参数

  • 非公开API端点的更改

  • JSON POST主体中的新可选键

  • JSON响应体中返回的新键


相反,重大变更包括任何可能损坏用户集成的内容,例如:


  • 新的必需参数

  • POST主体中的新的必需键

  • 删除现有端点

  • 删除现有端点请求方法

  • API调用的内部行为实质性不同,例如更改默认行为。

The Big 1.0

在我们记录和讨论这些约定时,我们也得出结论,为了大家(包括我们自己!)的最佳利益,应该避免对API进行重大更改,因为管理多个版本会增加不少工作量。我们决定在承诺“v1”之前,有一些事情需要在我们的API中修复。


发送一封简单的电子邮件所需的努力太大了。为了“保持简单的事情简单”,我们更新了POST正文以确保简单和复杂的用例都得到满足。新的格式在未来也更加可靠。其次,我们解决了Metrics端点的问题。此端点使用了一个“group_by”参数,该参数会更改GET响应正文的格式,使得第一个键将是group_by参数的值。这看起来并不符合REST的风格,所以我们将每个group_by分成了独立的端点。最后,我们审核了每个端点,并在各处做了一些小的修改以确保它们符合标准。

准确的Documentation

拥有准确且可用的API文档对于避免故意或无意的重大更改非常重要。我们决定使用一种简单的API文档方法,利用一种称为API Blueprint的Markdown语言,并在Github上管理我们的文档。我们的社区对这些开源文档进行贡献和改进。我们还在Github中维护了一组非公开的文档,用于内部API和端点。


最初,我们将文档发布到Apiary,这是一个用于原型设计和发布API文档的出色工具。然而,将Apiary嵌入我们的网站在移动设备上不起作用,因此我们现在使用Jekyll来生成静态文档。我们的最新SparkPost API文档现在加载迅速,并且在移动设备上表现良好,这对不总是坐在计算机前的开发人员来说很重要。

分离 Deployment 和 Release

我们很早就学到了一个宝贵的技巧,即将部署与发布分开。这样一来,当变更准备好后,可以通过持续交付和部署频繁地进行部署,但我们不总是同时公开宣布或记录这些变更。对于我们来说,在UI中或使用内部工具时,先部署一个新的API端点或对现有API端点的增强,而在公开记录和支持之前使用它,这并不罕见。这样,我们可以对其进行一些调整以提高可用性或符合标准,而不必担心造成令人害怕的重大变更。一旦我们对更改感到满意,我们就会将其添加到我们的公共文档中。

哎呀!

承认我们有时未能坚持“无破坏性更改”理念是公平的,这些都是值得学习的经验。曾有一次,我们认为如果某个属性默认设置为true而不是false,对用户会更好。当我们部署更改后,收到用户的多次投诉,因为行为发生了意外的变化。 因此我们撤销了更改,并添加了一个账户级别的设置——这显然是更用户友好的方法。


有时我们很想因为修复错误而引入破坏性更改。然而,我们决定保留这些特例,而不是为了保持一致性而冒着破坏客户集成的风险。


也有少数情况下,我们做出了重大的决策来进行破坏性更改——例如弃用一个API资源或方法——以便于更广大用户群体的利益,并且仅在确认对用户没有或几乎没有影响之后。例如,我们故意选择更改了Suppression API的响应行为,但仅是在仔细权衡了对社区的好处和影响并与用户充分沟通之后。然而,我们绝不会引入任何有可能直接影响用户生产邮件发送的更改。

加入我们的Newsletter。

通过每周更新到您的收件箱,随时了解 Bird 的最新动态。

See Bird's Privacy Statement for details on data processing.

加入我们的Newsletter。

通过每周更新到您的收件箱,随时了解 Bird 的最新动态。

See Bird's Privacy Statement for details on data processing.

加入我们的Newsletter。

通过每周更新到您的收件箱,随时了解 Bird 的最新动态。

See Bird's Privacy Statement for details on data processing.

Reach

Grow

Manage

Automate

资源

公司

Newsletter

通过每周更新到您的收件箱,随时了解 Bird 的最新动态。

See Bird's Privacy Statement for details on data processing.