API 设计 — 好的 API 设计有什么特点?

2024年9月16日

💎 加入 E+ 成長計畫 與超過 400+ 位軟體工程師一同在社群中成長,並且獲得更多的軟體工程學習資源

相信不论是前端、后端,或者是全端工程师的读者,对于 API 这个概念都不陌生。对于后端与全端的开发者来说,设计与开发 API 是日常工作中相当重要的环节之一;在接下来几篇文,我们将会着重在 API 设计这个主题,一同来探讨如何设计好 API。

这一篇文章我们会先从:

  • 为什么要有 API?
  • 好的 API 有什么特点谈起

在下一篇文会谈设计 API 时有哪些该特别注意的要点;而下下一篇进一步拉到资深工程师的角度,谈如何优化团队的 API 设计流程、与整体 API 品质。

API 是什么? 为什么要有 API?

由于多数读者已经熟知 API 是什么,我们在这边不重复做基本的介绍(不清楚的可以看下方相关文章)。

不过想回到本质与大家一同思考,API 是什么? 为什么要有 API?

透过 API 避免重复造轮子

让我们先用多数人熟知的汽车为例,来做一个类比。在硬体的世界中,相信多数人都对供应链并不陌生。

以造车为例,今天 Toyota 或 Ford 要造一辆车,不会所有的东西都自己做,而是会跟上游的供应商买部分的零件,在有了来自不同供应商提供的零件后,根据自己的设计来造出一台车。

而 API 就像软体世界的供应链,当有了 API,软体团队不再需用什么东西都自己开发,透过 API 就能够完成某些特定的任务。

举例来说,如果想要存图片,可以呼叫 AWS S3 的 API,免去自己手动去开发复杂的云端存储系统;又或者是想要做生成式 AI 的应用,可以直接呼叫 OpenAI 的 ChatGPT API,这样能免去自己训练 LLM 的时间与精力。

从本质来看,API 是把一个系统模组化的方式。我们可以把系统拆分成不同的模组,而不同的模组由不同的 API 来提供相关功能,S3 的 API 提供存储功能、OpenAI 的 API 提供 AI 功能,而 Stripe 的 API 提供金流功能,系统这样拆分后就变得容易管理与维护,同时也不用什么都自己重新造轮子。

透过 API 整合系统

除了让开发者们免于重新自己造轮子,API 的另一个存在目的,是可以协助整合不同系统。透过 API,我们能把一个大的系统拆解成小的子系统 (或说模组),而不同的模组,彼此交互的介面就是 API。

从前端与后端分离的角度来看,API 也扮演重要的角色。传统来说,可以把所有的操作都写在后端,然后每次使用者有任何与应用程式的互动,都交给后端处理后,传送一个全新的 HTML 给前端;但在加入 API 的概念后,可以把前端与后端视为不同的模组,然后两者透过 API 整合在一起,两者透过 API 互动。

近年来业界也有一种叫 API 先行 (API-first) 的做法,所谓的 API 先行是指在进到任何的实际开发工作前,会先设计好 API。举例来说,前后端之间会先共同讨论,把 API 的规格都设计好后,然后才进到实际的开发工作中。这样做的好处,是可以确保 API 的品质与易用性;这是对比过往可能后端都开发完 API 后才找前端对,结果发现对前端来说不好用,导致需要花额外时间重新设计与修改。

好的 API 设计有什么特点?

推荐平常有在开发 API 的读者,可以用下面这些要点来检视,看看自己开发的 API,是否有符合好的 API 设计该有的要点 (备注:以下的要点,是取自于业界中不同的资深工程师分享,且仅取一再被不同人重复提到的;因此为最核心的特点,而非穷尽所有特点)。

简单直观

API 设计要简单直观,API 的任务是让使用它的开发者能够成功。从这个角度看,如果 API 设计得复杂,让使用该 API 的开发者觉得难用,甚至无法顺利用,那这个 API 将无法达到其存在目的。简言之,让人容易学会怎么用,是 API 设计最重要的原则之一。

先前在社群上有一个激烈的讨论,是知名独立开发者 levelsio 批评 Google 的 AI API 太难用,连用之前的设定都很难(如下图);相比之下 OpenAI 的 API 只需要一个指令就能直接用,两者极大的差别,让人不想用 Google 的 API。该推文获得极大的回响,因为许多开发者都有同感。

levelsio 批评 Google 的 AI API 太难用的推文
levelsio 批评 Google 的 AI API 太难用的推文
圖片來源:https://x.com/levelsio/status/1831836883829322156

从这可以说明,API 够不够简单好用,影响是很巨大的。特别是当开发的产品本身就是 API 时 (例如这些大型语言模型,都是靠 API 来赚钱),这时 API 不够简单好用,对于商业会有很直接的冲击。

进一步说,直观的意思是让人可以不用额外思考。要做到让人不用特别思考,可以透过以下方式做到:

一致性要高

同一个系列的 API 一致性高,就可以让消费 API 的人不用适应不同的风格,当东西用起来足够熟悉,就会让人觉得直观,不用花额外的心力去思考。

举例来说,今天同一个团队出了两个 API,都是要移除资源的 API,然而一个是 remove 开头,另一个是 delete 开头,当要用的开发者看到,就会疑问说为什么会有两种不同的命名? 这是典型的不一致的状况。

从一致性高的角度来看,除非真的有不同含意,不然不要有多个不同的单词来代表同样的意思。以这边来说,removedelete 假如是指同一种操作,挑一个用就好。

要做到一致性高,有另一个简单的方式,是遵循业界常见的规范。举例来说,如果有个 URI 原本都回传 200,但某天突然回传 301,用的人不用特别思考,也可以知道该资源被永久换去其他地方。

延续这个观点,在 API 设计上有一个推荐的方式叫「入境随俗」。因为许多 API 会需要支援多种不同程式语言,在设计时,要入境随俗,依照该程式语言的规范做调整。

举例来说,目前多数的生成式 AI 的 API 都同时支援 Python 与 Node.js,但这两种语言的许多规范不同,在设计时就需要因应来调整,这样让分别用这两种语言的开发者,在串接 API 时,就可以更直观地完成。

总的来说,当一致性高,意味就会少;当 API 的使用者不容易感到意外,就会相对难用错,也会让 API 更好用。

命名可读性

命名上的可读性高,也可以让使用该 API 的开发者更轻松。一个推荐的原则是,命名要能够解释该 API 在做什么。当能做到这样,API 就容易让人一看就懂;或是反过来说,让想用某个 API 的开发者,能够在去查看 API 文件前,就轻易猜到想用的那支 API 会叫什么名字。

谈到可读性,就不能不谈命名的重要。很多时候,开发者在命名时,很常会从技术的角度出发来定义与命名,而不是从使用者的角度来思考命名,这种做法会很容易让 API 让人难以理解。

先前 Stripe 的技术长有分享,他们曾经开发一支处理 Issuer Fraud Record 的 API,在实际上线前的小规模测试时,发现许多使用者不清楚这支 API 在做什么,因为不知道 Issuer Fraud Record 是什么。去做了一些调查与调整后,他们重新命名成 Early Fraud Warning 大家瞬间就懂,原来这支 API 是用来侦测并警告潜在的诈骗。

一支 API 做一件事

在设计 API 时,要尽量避免某支 API 包山包海,而是尽量做到让一支 API 就做一件事,然后把这件事做好。这样才能让使用者容易使用。

换句话说,API 的范围要尽可能窄,不要想要把什么功能、参数都加进来。业界有一句话叫 When in doubt, leave it out,意思是当你有所迟疑,在想要不要把某个东西放进 API 时,这时就应该把该东西排除,不要放进去。通常来说,后续要新增会比较容易,但要移除通常比较困难,所以在一开始就想包山包海,不仅使用者可能觉得难用,也可能让后续维护困难。

有个可以检视的方法,是问自己「能不能轻易为该 API 命名? 以及能不能轻易解释该 API 的功能?」 如果没办法做到这两件事,那很可能是参杂了多个不同的功能在一起。

阅读更多

如果你对好的 API 设计这主题感兴趣,我们在 E+ 有更完整讲解的版本,除了「简单直观」这个要点外,我们还谈了其他的重要原则。有兴趣的读者,欢迎加入 E+ 成长计划。

本文为 E+ 成长计划的深度内容,截取段落开放免费阅读。欢迎加入 E+ 成长计划阅读完整版本 (点此了解 E+ 的详细介绍)


🧵 如果你想收到最即時的內容更新,可以在 FacebookInstagram 上追蹤我們