在一个初创公司成长的过程中,作为工程师的你也许常常会遇到下面这样的情况。
有一天,你看到一个段代码或一个算法,觉得这些代码不大经得起推敲;于是你用 git blame 命令去寻找代码的主人;结果发现,原来作者是如今早就不写代码的 CTO 或 VP。
之后,在一个偶然的机会里,你和他讲起这件事,他会自豪地告诉你:“哦,那时候我们必须在一天之内做出这个产品特性。当时也就我一个程序员吧,一天的时间,这是当时能做出最好的方案了。” 说完,他便陷入了对美好时光的怀念里。
你也可能听说过这样的故事。
有一天你的 CTO 突发奇想,行云流水地提交了一段代码;大家一看很激动啊,很多人跑去观摩大神的代码,结果觉得问题多多,于是在PR( Pull Request )上提了一堆评论。
CTO 一看有点傻眼了:“几十条评论……现在代码要这么写啊,好麻烦。”于是他就和一位工程师说:“你把评论里的问题解决下,合并(Merge)到主分支吧”, 然后就开开心心地该干嘛干嘛去了。
这两个小故事是想说明一个道理:一个公司早期的代码会因为各种历史原因不是那么完美,但是,在特定的时间点,这就是当时最优的方案。
随着公司的发展,成品功能不断叠加,代码架构不断优化,系统会经历一些从简到繁,然后再由繁到简的迭代过程,代码的改动也会相当巨大,也许有一天,你会几乎不认识自己当初的作品了。
API 的设计和实现更是如此。在我们的工作中,很少能见到 API 的设计和实现从最开始就完美无瑕疵。一套成熟的 API,很多时候都是需要通过不断演化迭代出来的。今天我就和你聊聊 API 的设计和实现。
首先第一点,我们先从 API 的签名(Signature)说起。
## API 的签名(Signature)
API 的签名,或者叫协议,就是指 API 请求(Request) 和响应( Response )支持哪些格式和什么样的参数。
首先,做过 API 的人都知道,一个上线使用的 API 再想改它的签名,会因为兼容性的问题痛苦不堪。因此,API 签名的设计初期,一定要经过反复推敲,尽量避免上线后的改动。
除了一些基本的 RESTful 原则外,签名的定义很多时候是对业务逻辑的抽象过程。一个系统的业务逻辑可能错综复杂,因此 API 设计的时候,就应该做到用最简洁直观的格式去支持所有的需求。
这往往是 API 设计中相对立的两面,我们需要找到平衡。有时候为了支持某一个功能,似乎不得不增加一个很违反设计的接口;而有时候我们为了保证 API 绝对规范,又不得不放弃对某一些功能的直接支持,这些功能就只能通过迭代调用或客户端预处理的方式来实现。
这种设计上的取舍,通常会列出所有可行的方案,从简单的设计到繁杂的设计;然后通过分析各种使用实例的频率和使用某种设计时的复杂度,从实际的系统需求入手,尽可能让常用的功能得到最简单直接的支持;还要一定程度上 “牺牲” 一些极少用到的功能,反复考虑系统使用场景,尽可能获得一个合理的折衷方案。
## API 设计原则
在这个折衷的过程中,我们需要始终保证满足这些基本原则。
保证 API 100% RESTful。RESTful 的核心是: everything is a “resource”,所有的行为( Action )和接口,都应该是相应 Resource 上的增删改查(CRUD)操作。如果脱离这种设计模式,一定要再三考虑是不是必要?有没有其他方案可以避免破坏 RESTful 风格。