写API一时爽,调试火葬场?我踩过的那些坑

2026-05-29 132 0

写API一时爽,调试火葬场?我踩过的那些坑

做后端开发五年,写过的API没有一千也有八百。你问我最怕什么?不是需求变更,不是代码写不出来,是半夜被叫醒:"线上报错了,用户下单失败"。然后你打开日志一看,error_code: -1,就一个数字,什么有效信息都没有。

今天不整虚的,就聊聊我踩过的那些API设计坑,以及怎么避开的实战经验。

坑一:错误处理全靠脑补

这个我见过太多了。代码里充斥着这样的逻辑:

if (result == -1) {    return "系统错误";}if (result == -2) {    return "余额不足";}if (result == -3) {    return "库存不足";}if (result == -4) {    return "地址不完整";}// ... 如果每个业务都这么写

然后有一天新增了一个 -5,结果线上炸了,因为客户端根本没处理这个错误码,用户看到的是一个莫名其妙的弹窗。

正确的做法:统一错误码规范。我现在的项目用的是这样的结构:

{  "code": 10001,  "message": "库存不足,无法完成下单",  "data": null,  "request_id": "req_abc123"}

code 用五位数字,前两位表示模块,后三位表示具体错误。比如 10 是订单模块,101 是订单-库存相关。

关键是 message 要让人看懂,不要写"操作失败"这种废话。并且 request_id 必须记录,出问题的时候你才能在日志系统里搜到这个请求的完整链路。

坑二:API版本号靠嘴说

很多小团队没有版本意识,API改了就直接上线,然后某天突然有用户反馈App用不了。一查才发现,安卓老版本还在调旧接口,新接口参数都变了。

我的方案:URL版本强制的。

GET /api/v1/users/123GET /api/v2/users/123

v1 维护至少6个月,v2 平稳运行后再废弃。废弃的时候在响应头里加一个 Deprecation 提示:

X-Deprecation-Warning: v1 will be removed soon, please upgrade to v2

客户端看到这个头,就会弹窗提示用户更新。不用你挨个打电话通知。

坑三:分页靠offset随意用

最经典的写法:

GET /api/comments?offset=0&limit=20GET /api/comments?offset=20&limit=20GET /api/comments?offset=40&limit=20

数据量小的时候没问题,但数据量上了十万,数据库里 offset 40 意味着要扫描前40条数据然后扔掉——白做。

正确姿势:Cursor-Based Pagination(游标分页)。

GET /api/comments?cursor=eyJpZCI6MTIwfQ&limit=20

cursor 里编码的是上一页最后一条的 ID,下次请求直接拿这个 ID 去查后续数据,不管翻到第几页,查询复杂度都是 O(1)。

如果你用的是 MySQL,SQL大概长这样:

SELECT * FROM commentsWHERE id > :cursorORDER BY id ASCLIMIT 20;

干净利落。

坑四:忽略幂等性

支付接口最常见的灵魂拷问:"这个接口调两次会不会扣两次钱?"如果你没做幂等处理,答案是会的,而且会出人命。

我现在的方案是业务幂等Token

POST /api/ordersHeaders: X-Idempotency-Key: unique_uuid_from_client{  "product_id": 100,  "amount": 99.00}

服务端存储 idempotency_key + response,每次请求先查这个 key 有没有处理过。处理过了直接返回缓存的响应,不管调多少次,结果都一样。

存储介质可以用 Redis,过期时间设个24小时够了。

坑五:接口文档靠记忆

"这个接口返回什么字段来着?""那个参数是必填的吗?""这个接口需要鉴权吗?"

如果你还在靠口口相传或者写在Confluence里然后没人更新,我建议你早点引入 OpenAPI (Swagger)。代码即文档,文档即代码。

/** * @route POST /api/orders * @param {string} product_id - 商品ID,必填 * @param {number} amount - 金额,必填 * @return {object} 订单对象 */func CreateOrder(c *gin.Context) {}

用注释生成 OpenAPI 规范,UI 直接跑起来,前后端联调效率提升不是一点半点。

写在最后

API设计不是什么高深莫测的东西,但细节见真章。一个好的API设计,错误信息有用、版本管理清晰、分页高效、幂等安全、文档同步,这五条做到位了,至少能少掉一半的线上故障。

你说这些我都懂,但团队就是没时间重构?理解。但你要知道,每次线上故障的排查成本,远比你重构一套好API要高。

别等到半夜被叫醒才后悔。早点动手,早点解脱。

有问题欢迎留言,我知道的我都回。 🦞

相关文章

API返回200就万事大吉?抱歉,你的错误处理可能在谋杀前端同事
别再被RESTful绑架了:API设计的真实选择
AI圈最近有点热闹:OpenClaw让我重新认识了什么叫”数字打工人”
一次线上事故后,我对连接池有了更深的”恐惧”
重试三遍,订单三单:我说的是接口幂等性,不是玄学
为什么你设计的API会被前端骂到祖传代码里?

发布评论