错误码设计原理
说到错误码,它其实没那么神秘。我刚开始接触SDK时,看到一堆数字也头疼。后来慢慢摸清了规律——错误码就是通信双方约定好的暗号。
错误码的定义
错误码就是一个数值或短字符串,用来表达一个具体的结果状态。比如“成功”对应0,“参数错误”对应1001。你想想看,如果没有这个约定,服务端给你返回一段“出错了”,你根本不知道错在哪。
我个人习惯在设计错误码时,先明确三个要素:
- 唯一性——每个错误必须有且只有一个码
- 可读性——码本身能透露出错误范围
- 扩展性——未来加新错误不用推倒重来
实战小发现:我在项目中遇到过,团队直接拿HTTP状态码当业务错误码用。结果200响应里包着“用户余额不足”,前端统一弹“网络异常”,用户直接骂娘。错误码定义一定要分清楚层。
错误码的分类标准
按照业界通行的做法,错误码通常分成三大类:
| 分类 | 范围 | 典型场景 |
|---|---|---|
| 系统级 | 0~999 | 内存不足、数据库断连、网络超时 |
| 业务级 | 1000~9999 | 订单不存在、密码错误、余额不足 |
| 安全级 | 10000+ | 过期、签名校验失败、IP被限制 |
为什么这样分?说白了就是为了快速定位问题。看到4001,你就知道是业务参数问题;看到10001,肯定是安全认证挂了。我记得有一次线上告警,运维一看码段就知道该找谁,省了很多扯皮时间。
HTTP状态码体系
这是一个大家都绕不开的体系。HTTP状态码是Web通信的基础暗号,SDK接入时你一定会遇到它们。
| 范围 | 含义 | 我踩过的坑 |
|---|---|---|
| 1xx | 信息提示 | 几乎不用管,除非做WebSocket升级 |
| 2xx | 成功 | 200不代表业务成功!我吃过亏——接口返回200,但body里带"error":true |
| 3xx | 重定向 | SDK接302时没处理自动跳转,直接报404 |
| 4xx | 客户端错误 | 429(限流)经常被忽略,其实应该做退避重试 |
| 5xx | 服务端错误 | 502/503直接重试大概率死循环,最好加上指数退避 |
千万不要把HTTP状态码当业务错误码用。200里装业务失败,500里装参数错误——这会让前后端对接变得一团糟。我见过一个团队,所有接口全返回200,然后靠自定义码区分,结果排查问题时日志里全是200,根本看不出谁挂了。
企业内部错误码设计规范
每家公司的规范都不一样,但有几个雷打不动的原则。我参与过三套SDK的错误码体系设计,总结出下面这套方案:
- 前缀+数字:比如
ERR_PARAM_INVALID = 1001,前缀说明模块,数字说明错误序号。 - 层级码:比如
A0101(A=认证,01=登录,01=密码错误),一眼能看出属于哪个层级。 - 统一出口:所有错误码定义放在一个配置文件里,谁都不许在代码里硬编码。
我曾经因为硬编码吃过亏——客户端和服务端各写了一版码表,结果对不上,联调了两天才发现。后来我强制团队用同一个Excel源自动生成枚举文件,问题一次性解决。
给内部规范的三条建议
- 预留间隔(比如1000间隔一段),方便以后插入新错误码
- 避免使用0当作“成功”以外的含义
- 每个错误码配上中英文描述、建议处理动作——这条能让客服少挨骂
错误码设计原则(再唠叨几句)
除了前面提到的唯一性、可读性、扩展性,还有两点我特别想强调:
- 错误码不要重排——一旦发布,永久保留。删掉码会导致旧版SDK炸掉。我习惯用
deprecated标记废弃码,但码位始终占着。 - 错误消息不要硬编码在码里——客户端的语言包和错误码分离,这样国际化时不用改码表。
经典翻车案例:有一回我接手一个SDK,发现他们把所有错误消息都写在码定义注释里,然后在客户端做字符串匹配。结果服务端升级时改了一句消息文案,客户端就全崩了。错误码,码是码,消息是消息,千万别混在一起。
嗯,错误码设计看起来是小事,但烂码表能把联调效率拖到地板上。记得我刚开始带新人时,他们总喜欢把错误码写成-1、-2这种魔数,被我在代码评审里狠狠怼了一顿。从那以后团队规范里加了一条:任何负数错误码不得出现在公开接口中,必须转成有意义的正数枚举。
说一千道一万,错误码设计的核心就是:让人和机器都能快速理解发生了什么,以及下一步该怎么干。多花十分钟设计码表,未来省十小时排查时间,这笔账怎么算都划算。