说实话,我见过太多“能用但谈不上好用”的接口了。你问一个后端工程师接口写得怎么样,他会说“没问题啊,都能通”;但如果你去问对接的前端、第三方开发者,甚至两个月后的他自己,答案往往就变成“有点乱”“有些坑”“说不清楚”。我做过后端、写过 SDK、也带过团队,对 RESTful API 设计这件事最大的感悟就是:“优雅”不是一个审美问题,而是一个成本和工程问题。 接口设计得优雅,不只是看着舒服,而是每次联调少返工、每个新人对接少踩坑、每个接口上线后少几个“紧急修复”。这篇文章我想把这些年设计 RESTful 接口的完整思路、踩过的坑、沉淀下来的规范一次性讲透,适合初级后端、对接口规范有追求的工程师、以及前端开发者在设计联调时参考。
1. 先认清:接口的“优雅”到底在解决什么问题
1.1 一个接口给团队带来的隐性成本
很多人设计接口时只考虑“怎么让这次需求跑通”,没有想过一个接口的完整生命周期里,有多少人要跟它打交道。我粗略算过一笔账:一个典型的业务接口,从定义到联调再到上线后的维护,至少要经过后端开发、前端开发(甚至多端)、测试、运维这几双手。如果接口设计得模棱两可,光联调阶段的沟通成本就会翻倍。
举个最直观的例子。我们团队有一个老接口,URL 长这样:/api/updateUserInfo。当时写这个接口的同事已经离职了,新来的前端同学要对接,问了我三个问题:“这个接口是全量更新还是部分更新?”“如果用户不存在,是返回报错还是自动创建?”“password 字段传不传?传空字符串会怎么样?”我答不上来,只能去翻代码。翻完代码又发现一个问题:这个接口内部对“字段不存在”和“字段传空串”的处理完全不一样。就这么一个小接口,前后花了四个人、两个小时的沟通时间才确认清楚。
这种成本是很隐蔽的,它不会出现在需求文档里,也不会被排期你头上,但它真实消耗着每个团队的产能。而好的 API 设计,本质上就是把这些“藏起来的沟通成本”提前用约定消灭掉——让调用方看一眼接口就能猜个八九不离十,不用一次一次找人确认。
1.2 优雅接口的三个可量化特征
我这些年指导团队做接口设计,从来不谈“美观”“风格”之类的虚词,而是把目标收敛成三个可验证的特征:可预测性、自解释性、可演化性。
可预测性,意思是调用方基于对资源和 HTTP 协议的基础认知,就能猜出这个接口大概的行为。比如看到 DELETE /api/orders/123,任何人都能猜到这是“删除订单123”,而不是“把订单移到回收站”或者“软删除但返回200”。自解释性,意思是接口的入参、出参、错误信息合在一起,能让调用方在尽量少查文档的情况下完成对接。可演化性,意思是 v1 上线之后,加字段、加功能不会破坏已有调用方,不需要动不动就推倒重来。
这三条就是我后面所有设计决策的判断标准。一句话说不清楚该不该这样设计时,就拿这三条去卡,基本不会出错。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 资源命名:URL 是接口给调用方的第一印象
2.1 名词复数与层级表达:别在 URL 里写“动作”
REST 的核心思想是“资源”。URL 只应该回答一个问题:我操作的是哪一类东西? 至于“操作”是什么,交给 HTTP 方法去表达。
很多从 MVC 框架入门的朋友,最容易犯的一个习惯就是把接口写成 RPC 风格,在 URL 里面写动词。我随便列几个看过无数次的反面教材:
text复制/api/createOrder
/api/getUserInfo
/api/order/delete?id=123
/api/checkStock
这种写法不是不能用,而是在“可预测性”上非常差。第一,动词的选取没有标准,同一个团队有人写 delete,有人写 remove,有人写 del,调用方根本没法预测。第二,动作和资源耦合在一起,导致资源的生命周期管理变得混乱。比如 deleteOrder 接口一开始是物理删除,后来业务要求改成只做标记,前端还在调旧接口,后端只能偷偷改逻辑,结果接口名变成了“撒谎”。
正确的做法是让 URL 只描述资源,动词完全交给 HTTP 方法:
text复制POST /api/orders 创建订单
GET /api/orders 查询订单列表
GET /api/orders/123 查询订单详情
PUT /api/orders/123 全量替换订单
PATCH /api/orders/123 局部更新订单
DELETE /api/orders/123 删除订单
同一个资源,五种行为,URL 完全没有重复信息。调用方只需要记住“资源长什么样”,剩下的事情全是 HTTP 协议已经规定好的常识。
2.2 层级与集合:嵌套几层才算合理
资源之间会有从属关系,这就要谈到 URL 的层级设计。我见过最多的问题是“两个极端”:一种是所有接口全部平铺,靠查询参数关联;另一种是一层层嵌套,比如 /api/companies/{companyId}/departments/{departmentId}/employees/{employeeId}/salary,看到第五层人都晕了。
我的判断标准其实很简单:子资源有没有独立的生命周期? 有,就把它独立出来;没有,就作为嵌套子资源。
拿“公司-员工”举例。如果员工必须属于某个公司,且脱离了公司没有任何独立存在的意义,那 /api/companies/{companyId}/employees 这种嵌套就很合理。但如果员工这个实体本身就很重要,会出现在其他业务场景里(比如跨公司搜索),那就应该做成 /api/employees?companyId=xxx,把公司的从属关系降为过滤条件。
嵌套本身不是坏事,但要克制。我个人的经验是:超过两层的嵌套,就应该停下来想一想是不是该扁平化了。三层以上的 URL,写出来你自己可能分得清,但调用方真的记不住,而且 URL 越长,出错的概率越大。
2.3 URL 里哪些东西不该放:小写、连字符与版本号的位置
关于 URL 的风格,业界讨论已经很多了,我只讲几条实战中真正影响体验的硬规则。
第一,全小写。 千万不要在 URL 里混用大小写,/api/users/123 和 /api/Users/123 在部分服务器配置下可能指向同一个资源,但这不是什么值得利用的特性,只会让调用方多一个出错维度。
第二,用连字符 - 而不是下划线 _。 原因很实在:在大多数浏览器和终端里,双击下划线会被识别为单词的一部分,不方便复制;而且连字符在视觉上更接近自然语言的分词。/api/user-profiles 一眼能看出两个单词,/api/user_profiles 看起来就是一个奇怪的词。
第三,版本号放路径里。 我承认 Header 版本(Accept: application/vnd.myapp.v2+json)在理论上更“纯 REST”,但这些年实测下来,绝大多数团队在这种方案上都会栽跟头:Header 版本对调用方不可见,调试的时候浏览器直接访问 URL 根本看不出版本,出了问题排查效率极低。URL 路径里带版本号虽然朴素,但有一个不可替代的好处——它可以被收藏、分享、直接写在文档里。一个新人对接接口,看到 /api/v2/orders 就知道自己用的是哪个版本,一眼就明白。后面章节我会专门讲版本策略,这里先记住结论:版本号放路径。
3. 方法与状态码:让 HTTP 协议替你说话
3.1 方法语义的边界:GET 必须无副作用,POST 不是万能的
HTTP 方法本身就带语义,设计接口时最偷懒也最正确的方式,就是老老实实按语义来。
GET 负责查询,约束是最严格的:必须无副作用。也就是说,GET 请求不能修改任何数据。这个约束不只是规范,更是安全底线——搜索引擎的爬虫、浏览器的预加载、代理服务器的缓存,都会自动发起 GET 请求,如果你的 GET 接口会改数据,那这些“看不见的调用方”就可能帮你执行一堆莫名其妙的操作。我见过一个极端案例:某同事为了省事,用 GET 接口处理用户的注销操作,结果被公司内部的监控系统自动探测了一遍,当天晚上生产环境一堆账号被“注销”了。
POST 和 PUT 的差别在面试里是老生常谈,但在真实项目里却经常被人绕开。核心区别是幂等性:PUT 是幂等的,同一个请求发多少次,结果都一样;POST 不幂等,发两次就可能创建两条数据。基于这个差异,设计规则就很清晰了:
- 创建新资源,客户端不确定资源的最终 URL 时,用
POST /api/orders。 - 更新整个资源、且客户端知道资源的完整内容时,用
PUT /api/orders/123,payload 里是这个订单被替换后的完整状态。 - 只更新部分字段时,用
PATCH /api/orders/123,payload 里只放要改的字段。
很多项目把更新一律写成 POST /api/order/update,表面上是省事了,实际上是把“完整更新”“部分更新”“幂等与否”这些信息全部揉成一团,调用方根本不知道该传什么、能不能重试。与其把决策成本转嫁给调用方,不如在方法选择上把语义定死。
3.2 状态码的精细使用:别再“永远返回200”
“永远返回200,然后在 body 里放一个 code 字段表示成功失败”——这种设计在早期国内互联网项目里极其常见,我甚至在一些不小的公司里见过。它最大的问题不是不能用,而是:它把 HTTP 协议已经定义好的语义白白扔掉了,所有错误都退化成了一种格式,调用方必须逐层解析 body 才能知道发生了什么。
更合理的做法是让状态码承担“这通请求大体上成没成”的职责,body 承担“如果没成,是因为什么”的职责。
我来说说状态码的几个关键边界,这是我在指导团队时最容易纠正的认知盲区:
| 状态码 | 使用场景 | 容易混淆的错误用法 |
|---|---|---|
| 200 OK | 查询、更新的通用成功 | 删除接口也返回 200 |
| 201 Created | 创建成功,资源已生成 | 创建成功后只返回 200 空 body |
| 202 Accepted | 请求已接收,但异步处理中 | 同步接口误用,客户端不知道什么时候处理完 |
| 204 No Content | 删除成功/无需响应体 | 为了省事返回 200 + 空对象 |
| 400 Bad Request | 参数语法错误、缺失必填字段 | 把“业务不允许”也甩给 400 |
| 404 Not Found | 资源不存在 | 把“订单不存在”返回 400 |
| 409 Conflict | 资源状态冲突(如重复创建、版本过期) | 一律归为 500 |
| 422 Unprocessable Entity | 参数语法对,但业务规则不通过 | 用 400 替代一切参数问题 |
| 500 Internal Server Error | 未捕获的服务端异常 | 把 400 和 500 混用 |
这里重点解释一下 400、422、409 这三个看似“都是参数/业务错误”的状态码,因为实际项目里它们最容易被搞混。
400 的本质是“你发过来的请求本身就不合法”——字段缺失、JSON 格式错了、邮箱格式不对,这个问题客户端改一下请求就能解决,不用找后端。422 的本质是“你请求的语法没问题,但在当前业务规则下过不了”——比如订单金额必须大于0,你传了0;优惠券已经过期,你还在使用。409 的本质是“资源当前的状态和你的操作冲突”——比如你想创建一个已经存在的用户;你想用旧版本的数据去覆盖新版本的数据,这时返回 409 并带上最新的资源信息,调用方就知道要先刷新了。
还有一个非常容易犯的低级错误:删除一个不存在的资源,到底应该返回 404 还是 200? 我在代码评审中经常看到有人返回 200,理由还很充分:“删除操作本身成功了,因为本来就没这个东西了。”这个理由是典型的“实现者思维”,而不是“调用者思维”——对调用方来说,他以为这个订单存在才去删,结果删完发现订单从来没存在过,这说明他手头的订单 ID 已经过期了,这是一个值得让他知道的情况。所以应该返回 404,让调用方去刷新自己的数据状态。接口设计的一个原则就是:别替调用方做“这没关系”的判断,把真实情况告诉他。
3.3 幂等设计:为什么说它是分布式时代的接口标配
幂等性这个词这几年被炒得越来越热,但很多人只记住了“POST 不幂等、PUT 幂等”这个结论,没有真正理解它要解决什么问题。
在真实网络环境里,请求超时是常态。客户端发起一个支付请求,服务端其实已经处理成功了,但响应在网络上丢了,客户端那边看到的是“超时”。怎么办?绝大多数客户端都会重试。如果支付接口不幂等,重试就会导致用户被扣两次款——这是所有支付系统的噩梦。
所以幂等设计的核心思路是:让服务端能识别出“这个请求是刚才那个请求的重试”。
在 REST API 领域最通用的方案是 Idempotency-Key Header。客户端在创建一个可能重复操作的请求时,生成一个唯一的 key(通常用 UUID),放在请求头里:
http复制POST /api/payments
Idempotency-Key: 3b3a7f2e-1c43-4a5b-8e0f-9a8b7c6d5e4f
Content-Type: application/json
{
"orderId": "20190101",
"amount": 99.00,
"currency": "CNY"
}
服务端收到这个请求后,先查一下这个 Idempotency-Key 有没有处理过。如果没处理过,正常处理并存储结果;如果处理过,直接返回第一次处理的结果,不再重复扣款。这套机制在 Stripe 的 API 里已经用了很多年,实测下来非常可靠。
落到实现层面,幂等键最重要的是和业务动作绑定存储。我见过一个半吊子实现:做了幂等键,但因为数据库表没有唯一的约束,高并发下两个相同的请求同时进来,照样插了两条数据。“去重表 + 唯一约束”是底线,去重表里要存请求的原始内容、处理结果和状态码,排查问题时这些信息远远不够,但已经是最低要求。
4. 错误信息设计:接口最暴露功力的地方
4.1 烂错误长什么样
我拿实际项目里见过的错误响应给你感受一下:
json复制{
"message": "保存失败"
}
这种错误响应,调用方能得到什么信息?只知道“失败”了。为什么失败?哪里失败?是参数错了还是服务端炸了?要不要重试?前端拿到这个错误,只能原样给用户弹一个“保存失败”的提示框,用户也一脸懵。更麻烦的是排查问题的时候,前端把报错截图给后端,后端也看不出来具体原因,只能去翻日志,而日志里如果没有把请求体打出来,两个人就只能干瞪眼。
再对比一个我认为可以当范本的错误响应:
json复制{
"code": "ORDER_NOT_FOUND",
"message": "订单不存在或已被删除",
"details": {
"orderId": "12345"
},
"traceId": "8f2a1c7e-9d34-4b6f-ae91-23c5e6d70988"
}
这四个字段缺一不可:code 是机器可读的业务错误码;message 是人可读的错误描述(也是可以直接展示给用户看的);details 是额外的上下文信息,比如是哪个订单不存在、哪个字段校验没通过;traceId 是排查问题的关键,能够在日志里把所有链路串起来。
4.2 统一错误结构:code 不能是数字
错误结构里最容易被忽视的是 code 字段的设计。很多团队会把 code 写成数字,比如 40001、50002,这样其实和 HTTP 状态码的语义又重复了,而且数字本身没有可读性,看到 40001 和 40002,想不起来分别代表什么。
我的建议是:code 用业务语义的字符串。比如 ORDER_NOT_FOUND、INVALID_PARAMETER、USER_FORBIDDEN。这样做的好处很直接——code 可以作为一种“协议”和文档对照,“你去查一下 USER_FORBIDDEN 是什么意思”,比“你去查一下 40001 是什么意思”要友好得多。
另外,错误结构必须是全局统一的。有的接口返回 {error: "xxx"},有的返回 {message: "xxx"},有的返回 {errors: [...]},调用方每接一个新接口都要写一种错误处理逻辑,这是接口设计中最容易忽略却最影响体验的细节。定了结构,全项目所有接口,不管成功的还是失败的,都严格按这个结构返回。
4.3 从 400 错误出发:参数校验到底该怎么报错
最近不少人在搜索“api error: 400 invalid schema”相关的问题。这类错误通常不来自业务代码,而是来自 API 网关或参数校验框架的自动拦截——当请求的 JSON 结构不满足接口定义的 Schema 时,网关直接返回一个笼统的 400。这种响应最大的痛点是:它不告诉你具体是哪个字段不合法,客户端只能自己猜。
我在团队里定的规矩是:框架层的 Schema 校验只能兜底,业务层的参数校验必须自己写,并给出结构化的错误详情。比如处理一个注册接口,遇到以下错误时,应该一次性把问题全部返回给调用方,而不是一次只报第一个错误:
json复制{
"code": "VALIDATION_FAILED",
"message": "请求参数校验失败",
"details": {
"errors": [
{
"field": "email",
"message": "邮箱格式不正确"
},
{
"field": "age",
"message": "年龄不能小于 18"
}
]
},
"traceId": "9c1a2b3d-4e5f-6a7b-8c9d-0e1f2a3b4c5d"
}
为什么强调“一次性全返回”?因为在实际对接中,前端拿到参数错误后通常要改表单。如果你一次只报一个错误,前端改完第一处,提交后才发现第二处也有错,来回交互相当于把联调时间乘以了“错误个数”。一次性返回所有字段错误,一次就能改完,这个细节对开发体验的提升是非常明显的。
4.4 错误信息里的安全红线
错误信息写得太细不行,太粗也不行,这两者之间的度是我这些年反复琢磨的点。
太细的典型问题:直接把 JVM 的堆栈异常打在 response 里,或者把 SQL 语句、数据库连接串、内部 IP 地址暴露给调用方。这不是“信息丰富”,这是给攻击者递刀。别人看到你的 SQL 结构,就能推理出你的表结构,进而构造注入攻击。
太粗的典型问题:所有异常统一返回“系统繁忙,请稍后再试”,连“参数格式错误”和“服务器内部错误”都不区分。调用方遇到前者可能改一下就通过了,遇到后者改多少遍都没用,但不区分就导致客户端不知道该怎么办。
我的折中方案是分两层:对调用方一层,对日志一层。response 里只暴露业务可理解的错误信息,不暴露堆栈和内部细节;但每个 response 都必须带上 traceId,这个 traceId 必须在日志里能查到完整的调用链、异常堆栈和入参出参。调用方拿着 traceId 来找我,我 30 秒就能定位问题;没有 traceId,就只能靠时间戳和灵感的配合了——这体验可太糟了。
5. 分页、过滤、版本化:高频接口的现场经验
5.1 分页到底该用页码还是游标
分页这个问题,几乎每个接口都躲不掉。很多人的第一反应是“不就是 page 和 pageSize 吗”,但实际上这两个方案在真实业务里差别很大。
页码分页(page/pageSize) 的优点是直观、好跳转:GET /api/orders?page=3&pageSize=20 一看就懂,而且用户可以直接从第 1 页跳到第 10 页,这在后台管理系统里非常常用。它的缺点是数据量大以后性能会下降,因为数据库执行 LIMIT 60, 20 时会跳过前 60 条再取 20 条,跳过的部分还得扫描,页数越深越慢。更重要的是,在数据实时变化的场景下,页码分页的数据会发生重复或丢失——比如你在第 1 页看到一条数据,翻到第 2 页时,前面插入了一条新数据,原来第 20 条就跑到了第 21 条,你会漏掉一条。
游标分页(cursor-based) 是应对前两种问题的更优方案。它的做法是在第一页请求时返回一个 nextCursor 字段,客户端翻下一页时把这个 cursor 原样传回来:
http复制GET /api/orders?cursor=eyJpZCI6MTAwfQ&limit=20
json复制{
"data": [...],
"nextCursor": "eyJpZCI6MTIwfQ",
"hasMore": true
}
服务端拿到 cursor 之后,直接定位到上一次返回的最后一条记录,然后往后取。它避免了 OFFSET 的性能问题,也不会因为插入新数据而重复或漏掉。缺点是不支持随意跳页。
我的选型经验是:后台管理类的接口,数据量不大、需要自由翻页,用页码分页没问题;C 端对外接口,尤其是信息流、订单列表这类数据量大且实时变化的场景,优先用游标分页。另外,无论用哪种方案,都一定要给分页加上限,比如 pageSize 最大 100,防止调用方一次拉取全表数据把数据库拖垮。
5.2 过滤、排序、字段选择的参数约定
查询接口不止是“列表 + 分页”,还有过滤、排序、字段裁剪这些常见的配套需求。这里最容易犯的错有两种:把所有过滤条件全放在路径里(/api/orders/status/1/date/2024-01-01),或者发明一套复杂的 RQL 表达式让调用方去学。
我的推荐是分两档处理。
一档:简单过滤条件直接用 Query 参数。 ?status=paid&channel=app,肉眼可见、语义清晰、实现简单,应对 80% 的场景完全够用。二档:多条件 OR、嵌套条件这种复杂查询,就别硬塞进 URL 了,考虑用 POST + body 传查询表达式,这种需求真正出现的时候已经是报表类接口了,值得一个更完整的设计。
排序方面,我习惯用 sort 参数加上方向前缀,比如 sort=-created_at 表示按创建时间倒序。这里有个细节:公开接口里排序字段名要不要直接用数据库字段? 我的建议是定义一套对外暴露的字段名,内部映射到数据库字段。比如对外是 createdAt,内部是 create_time,这样以后内部改了表结构、改了字段名,对外接口不用变。
字段裁剪(fields 参数)是很多团队忽略的能力。GET /api/orders?fields=id,status,totalAmount 可以让调用方只取需要的字段,在数据量大、流量高的场景下能明显节省带宽。但别一开始就上这个能力,等有明确的性能需求再引入,否则只是多了一层没人用的复杂度。
5.3 接口演化与版本管理的实际选择
任何一个活着的产品,接口都会经历无数次演化。我见过最混乱的状态:一套接口文档里堆着十几个版本的老接口,谁都不敢删,谁也不敢动,生怕影响不知道哪个“远古调用方”。
我的版本管理策略非常简单明确:
第一,优先做兼容性演进,而不是急着生版本。 绝大多数业务需求,其实都能用“新增可选字段 + 新增可选参数 + 新增子资源”的方式覆盖,不需要升级版本。比如用户信息接口,要加一个 vipLevel 字段,直接在现有响应里加就行,老的调用方不解析这个字段,完全不受影响。这里的关键是:新加的字段必须是可选的,不能强制要求老的调用方必须传,否则就不是兼容性提升了。
第二,必须破坏性变更时,才生新版本。 比如要删掉一个老字段、改变一个字段的数据类型、或者改掉整个认证流程,这种变更没法兼容,就应该升版本。版本号放 URL 路径里,/api/v2/...。老接口继续保留,但可以加上弃用头:
http复制HTTP/1.1 200 OK
Deprecation: true
Sunset: Sun, 31 Dec 2025 23:59:59 GMT
第三,版本周期要留足缓冲。 我见过最不靠谱的做法是今天通知弃用老接口,下周就把老接口下线。真实世界里的第三方调用方,迭代周期根本跟不上你的节奏。我的经验是:给调用方至少 3~6 个月的缓冲期,并且在弃用期间把弃用信息写进日志、定期统计老接口的调用量,等到调用量降到可以忽略的水平,再安全下线。
6. 写在最后:一些不那么“技术”但很要命的经验
6.1 先用契约,后写代码
模块划分再清晰,如果在开发之前双方对接口没有达成一致,后面联调阶段一定会反复扯皮。我的团队现在执行一个很简单的规矩:开发之前必须先产出 OpenAPI 契约文档(也就是 Swagger),评审通过之后,前后端再并行开工。
这样做的好处在于,接口设计的几乎所有问题——路径、方法、参数、响应结构、错误码——都必须在开发前用文字定死,这就避开了“写完代码才发现接口设计有问题”的最贵返工。后端可以在 Swagger 的基础上做接口 Mock,前端可以按文档并行开发,等到联调的时候,两边其实已经用 Mock 数据走通了大半流程,真正联调只是校验一下真实行为是否符合契约。
6.2 文档工具选择的个人建议
说到契约文档,工具链我也一并说了。我这些年试过不少方案:手写 Markdown 文档、Postman 集合、Swagger UI、Apifox、老牌的 ReadMe 等等。
我的最终建议是:对外接口把 OpenAPI 规范的 YAML/JSON 作为唯一事实来源。不管团队用什么工具,核心在于先有一份机器可读的 API 描述文件,把它提交进代码库,每次接口变更都走代码评审;然后在 CI 里加一步校验,确保实现和契约一致。至于展示层是用 Swagger UI 还是其他工具,都是锦上添花的事。API 描述文件的版本化、可评审性、可 diff 特性,才是最高价值的东西。
6.3 上线前拿这几个问题问自己
最后,根据我踩坑多年的经验,总结出一份接口上线前的“自问清单”,你可以在代码评审时逐一比对:
- URL 上有没有动词?有没有大写?有没有下划线?
- 创建操作用的是 POST 吗?更新操作的语义是 PUT 还是 PATCH 分清楚了吗?
- 删除一个不存在的资源,能正确返回 404 吗?
- 所有响应是否严格遵循同一个错误结构?code 是否是业务语义字符串?
- 参数校验遇到多个字段错误时,是一次性全返回还是只返回一个?如果只返回一个,赶紧改。
- 所有错误响应是否带上了 traceId?日志里能通过 traceId 串联到完整调用链吗?
- 客户端的重试请求,会被幂等机制挡住吗?去重表有没有唯一约束?
- 新增字段时,是否保证了老调用方不受影响?
- 分页上限设置了吗?深度分页会打爆数据库吗?
- 文档里调用方最关心的错误码有哪些,是否写得足够清楚、给到了具体的处理建议?
这些问题的答案如果都是“没问题”,那这个接口基本可以放心上线了。别小看这些问题——我职业生涯里真正让团队吃苦头的接口事故,十有八九都能在这张清单里找到影子。接口规范和优雅,从来不是给评审老师看的表面功夫,而是让团队在无数个平淡日子里少掉头发的实在事。
