文章摘要
现代软件工程师的工作大多涉及API设计,好的API应在熟悉性和灵活性之间取得平衡,过于复杂的API往往不是好设计。API对开发者来说是复杂的产品,但对使用者而言,它们只是实现目标的工具,因此简洁、易用的API才是优秀的设计。
文章总结
关于优秀API设计的核心观点
现代软件工程师的工作大多涉及API(应用程序接口),API是程序之间通信的公共接口。设计优秀的API需要在熟悉性和灵活性之间找到平衡。好的API应该是“无聊”的,因为开发者使用API的目的是完成其他任务,而不是花时间理解API本身。API一旦发布,修改起来非常困难,因此设计时需要谨慎,确保第一次就做对。
不破坏用户空间
API的维护者有一个神圣的职责:不破坏用户空间。这意味着一旦API发布,任何对接口的修改都可能破坏用户的软件。即使某些设计不够完美,也不能轻易更改,比如HTTP规范中的“referer”拼写错误,至今未改,因为我们不破坏用户空间。
如何在不破坏用户空间的情况下修改API
当API确实需要重大修改时,版本控制是解决方案。通过同时提供新旧版本的API,现有用户可以继续使用旧版本,而新用户可以选择新版本。例如,OpenAI的聊天API使用/v1/chat/completions,未来可以在v2/chat/completions中进行重大修改,同时保持旧版本的运行。尽管版本控制是必要的,但它也带来了维护的复杂性,因此应作为最后的手段。
API的成功取决于产品本身
API本身并不具备价值,它只是用户与产品之间的桥梁。如果产品足够有价值,用户甚至会使用设计糟糕的API。例如,Facebook和Jira的API虽然难用,但由于其产品的强大,用户仍然愿意花时间集成。因此,API的质量在产品选择中通常只是次要因素。
糟糕的产品设计会导致糟糕的API
如果产品设计不佳,API也很难优雅。API设计通常反映产品的基本资源结构,如果这些资源设计不合理,API也会显得笨拙。例如,一个博客系统如果以链表形式存储评论,API设计也会变得复杂,用户不得不理解系统的内部设计。
认证与幂等性
API应支持长期有效的API密钥,尽管OAuth等短期凭证更安全,但API密钥更容易让用户快速上手。许多API用户并非专业工程师,因此应尽量简化使用流程。对于可能重复的操作,API应支持幂等性,确保请求可以安全重试而不会产生重复操作。
安全性与速率限制
API暴露的操作可以被代码快速调用,因此需要谨慎设计。对于高负载的操作,应设置速率限制,并在API响应中包含速率限制的元数据,如X-Limit-Remaining和Retry-After,以帮助用户合理使用API。
分页与可选字段
对于可能包含大量记录的API,应使用基于游标的分页,而不是简单的偏移分页。此外,如果某些字段的获取成本较高,应将其设为可选字段,用户可以通过参数选择是否包含这些字段。GraphQL虽然提供了灵活性,但其复杂性较高,通常只在必要时使用。
内部API
内部API与公共API有所不同,用户通常是专业工程师,且可以安全地进行破坏性修改。然而,内部API仍然需要确保关键操作的幂等性,并避免成为事故的源头。
总结
- API设计需要在灵活性和易用性之间找到平衡。
- API维护者的首要职责是不破坏用户空间。
- 版本控制允许API进行修改,但会带来维护和采用的复杂性。
- 如果产品足够有价值,API的质量并不重要。
- 糟糕的产品设计会导致糟糕的API。
- API应支持简单的API密钥认证,因为许多用户并非专业工程师。
- 对于可能重复的操作,API应支持幂等性。
- API应设置速率限制和紧急关闭机制。
- 对于可能包含大量记录的数据集,应使用基于游标的分页。
- 内部API与公共API有所不同,但仍需谨慎设计。
评论总结
API版本控制:
- 支持从应用一开始就引入版本控制,因为未来可能出现不可控的破坏性变更。
- "I always recommend baking them in from the very start of your application."(pixl97)
- "You cannot predict the future and chances are there will be some breaking change forced upon you by someone or something out of your control."(pixl97)
- 对版本控制持不同意见,认为幂等性(idempotency)是必须的,API应支持指定截止时间和静态稳定性。
- "I definitely disagree about idempotency: it's NOT optional."(cyberax)
- "Your API should allow to specify the deadline after which the request is no longer going to matter."(cyberax)
- 支持从应用一开始就引入版本控制,因为未来可能出现不可控的破坏性变更。
API设计原则:
- 提醒“永远不要破坏用户空间”是重要的,但应明确哪些部分是稳定的。
- "The reminder to 'never break userspace' is gold and often overlooked."(frabonacci)
- "It's the more nuanced 'declare what's stable, and never break those'."(dwattttt)
- 内部用户也应被视为普通用户,API设计应经过充分测试后再开放。
- "Internal users are just users. If at all possible, take your time and dog-food your API before opening it up to others."(runroader)
- 提醒“永远不要破坏用户空间”是重要的,但应明确哪些部分是稳定的。
API资源设计:
- 产品的基础资源设计直接影响API的优雅性,避免不必要的抽象。
- "A technically-poor product can make it nearly impossible to build an elegant API."(claw-el)
- "One issue I have with weird resources are those that feel like unnecessary abstraction."(claw-el)
- 产品的基础资源设计直接影响API的优雅性,避免不必要的抽象。
API的历史与多样性:
- API不仅限于HTTP和JSON,过去还包括本地安装的API。
- "Anyone else old enough to remember when 'API' also meant something that had nothing to do with sending and receiving JSON over HTTP?"(zahlman)
- API不仅限于HTTP和JSON,过去还包括本地安装的API。
API的通用性:
- 版本控制等原则不仅适用于Web API,也适用于低级网络API。
- "Versioning, etc. matter (or don’t) for binary UDP APIs (aka protocols) just as much as for any web API."(mlhpdx)
- 版本控制等原则不仅适用于Web API,也适用于低级网络API。