3. 错误码设计原则:错误码分类、HTTP风格错误码、业务错误码、错误码范围规划

错误码是Go服务中错误断言与故障定位的“第一性信息”。设计一套清晰、可扩展的错误码体系,能显著降低跨团队协作时的认知成本,并让断言逻辑在代码层面保持可读性与可维护性。本章从分类、风格、范围三个维度展开。

3.1 错误码分类:按职责分层

错误码不应是扁平的一维列表,而应按照“系统层 → 通信层 → 业务层”进行分层设计。每一层关注不同的故障域,断言时也能按层快速缩小排查范围。

层级 职责 典型错误码前缀 示例
系统层 基础设施、运行时、依赖服务故障 SYS_ SYS_DB_TIMEOUT, SYS_DISK_FULL
通信层 网络协议、序列化、RPC调用异常 NET_ NET_HTTP_500, NET_GRPC_UNAVAILABLE
业务层 业务规则校验、状态冲突、资源不存在 BIZ_ BIZ_ORDER_EXPIRED, BIZ_USER_NOT_FOUND
设计建议: 在Go项目中,建议将错误码定义放在独立的 pkg/errcode 包中,按层级拆分为 sys.go、net.go、biz.go 三个文件,便于维护与代码生成。

3.2 HTTP风格错误码:与RESTful语义对齐

对于对外暴露的HTTP API,错误码应尽量复用HTTP状态码的语义,避免自创一套与标准脱节的编码。这能降低前端与客户端的理解成本,也便于API网关做统一熔断与重试。

  • 4xx 系列: 客户端错误,如参数校验失败(400)、鉴权失败(401)、资源不存在(404)。
  • 5xx 系列: 服务端错误,如内部异常(500)、服务不可用(503)、上游超时(504)。
  • 扩展规则: 当需要更细粒度的业务区分时,可在HTTP状态码后附加业务子码,例如 40001 表示“订单金额校验失败”,40002 表示“库存不足”。
Go代码示例: 定义带子码的错误结构体
// errcode/errcode.go
type ErrorCode struct {
    HTTPStatus int    // 对应HTTP状态码
    SubCode    int    // 业务子码
    Message    string // 用户可读信息
}

var (
    ErrOrderAmount = &ErrorCode{HTTPStatus: 400, SubCode: 40001, Message: "订单金额校验失败"}
    ErrStockShort  = &ErrorCode{HTTPStatus: 400, SubCode: 40002, Message: "库存不足"}
    ErrInternal    = &ErrorCode{HTTPStatus: 500, SubCode: 50000, Message: "内部服务异常"}
)

3.3 业务错误码:领域内聚、语义自明

业务错误码是断言逻辑中最常接触的部分。设计时应遵循“领域内聚”原则:每个业务模块(如订单、支付、用户)拥有独立的错误码区间,且错误码名称应直接反映业务语义,而非模糊的数字编号。

  • 模块前缀: 使用模块缩写作为前缀,如 ORD_(订单)、PAY_(支付)、USR_(用户)。
  • 语义化命名: 避免 ORD_001 这种无意义编号,改用 ORD_AMOUNT_MISMATCH、ORD_STATUS_CONFLICT。
  • 断言友好: 错误码应支持 errors.Is 或 errors.As 断言,而非仅靠字符串匹配。
避坑提示: 不要将错误码定义为纯数字常量(如 1001),这会导致代码中到处出现魔法数字,且无法利用Go的类型安全进行断言。始终使用具名常量或结构体指针。

3.4 错误码范围规划:预留扩展空间

错误码范围规划是长期演进的关键。一个常见的反模式是:所有错误码从1开始连续编号,导致新模块加入时无处安插,最终变成混乱的“大锅烩”。

推荐按模块与层级分配固定数值区间,并预留充足的扩展段:

区间范围 归属模块 说明
10000 – 19999 系统层(SYS) 数据库、缓存、磁盘等基础设施
20000 – 29999 通信层(NET) HTTP、gRPC、MQ等网络通信
30000 – 39999 用户模块(USR) 登录、注册、权限
40000 – 49999 订单模块(ORD) 创建、支付、退款
50000 – 59999 支付模块(PAY) 渠道、对账、风控
90000 – 99999 预留扩展段 未来新模块或临时应急码
实践建议: 在项目初期就定义好区间分配表,并写入 CONTRIBUTING.md 或 errcode/README.md。每次新增错误码时,必须从对应区间内分配,避免跨区借用。

3.5 错误码设计全景图

下图展示了从分层分类到范围规划的整体设计思路,帮助你在实际项目中快速落地。

错误码设计全景图 错误码设计全景图 系统层 (SYS) 通信层 (NET) 业务层 (BIZ) HTTP风格 4xx/5xx 业务子码扩展 语义化命名 范围规划:10000-19999 | 20000-29999 | 30000-99999 Go 错误断言:errors.Is / errors.As 分层 → 风格 → 范围 → 断言,四步构建可扩展的错误码体系

3.6 本章小结

  • 错误码按系统层、通信层、业务层分层,每层独立前缀与断言域。
  • 对外API优先复用HTTP状态码语义,必要时附加业务子码。
  • 业务错误码使用模块前缀 + 语义化命名,避免魔法数字。
  • 提前规划数值区间,为未来模块预留扩展空间。
  • 所有错误码最终通过 errors.Is / errors.As 进行断言,而非字符串匹配。