新增字段必须用指针+omitempty,禁用值类型;废弃字段改json:"-"保留锚点;类型变更需双字段过渡;版本结构体必须物理隔离,禁止共用或强转。

新增字段必须用指针 + omitempty
旧客户端发请求时不会带新字段,如果用值类型(如 UpdatedAt time.Time),json.Unmarshal 会默默填成零值(1970-01-01),业务逻辑可能直接错乱。指针类型天然支持“未传即 nil”,配合 omitempty 还能避免序列化空值干扰旧客户端解析。
常见错误现象:定义 Status string `json:"status"` 后,v1 客户端收不到该字段就 panic 或误判为 “status = ''”;正确写法是:Status *string `json:"status,omitempty"`。
- 所有新增字段一律走
*T或可空基础类型(如*int,*bool) -
omitempty不可省略——它控制序列化行为,不是反序列化开关 - 禁止在旧结构体上直接加字段,哪怕加了
omitempty;应定义新结构体(如UserV2)并隔离使用
废弃字段不能删,要留锚点 + json:"-"
删字段看似干净,实则切断了协议演进的观测链路:你无法统计还有多少客户端在传这个字段,也无法在中间件里加灰度开关或日志打点。保留字段定义但屏蔽 JSON 输出,才是可控下线的前提。
使用场景:v1 接口曾接收 OldField int,v2 不再需要,但需监控是否还有残留调用。
- 改写为:
OldField int `json:"-"`,并加注释说明// Deprecated: use CreatedAt instead - 若需兼容旧客户端继续传该字段(比如做迁移过渡),就别加
json:"-",改用*int并在 handler 中忽略其值 - 上线后通过日志提取该字段是否存在,确认无流量后再真正删除
字段类型变更必须双字段过渡
把 Count int 改成 Count string 是高危操作——json.Unmarshal 遇到类型不匹配会静默失败或截断,旧客户端发整数,服务端却收到空字符串或零值。
实操建议:不改原字段,而是新增兼容字段,靠代码逻辑桥接。
- 保留原字段:
CountInt int `json:"count"` - 新增目标字段:
CountStr string `json:"count_str,omitempty"` - 在 Unmarshal 后统一处理:
if u.CountStr != "" { u.Count = parseStringToInt(u.CountStr) },或反之 - 禁止用反射自动转换——类型语义丢失风险太高
结构体不能共享,版本间必须物理隔离
很多人以为只要结构体字段兼容,v1/v2 就能共用一个 type User struct。但现实是:gob 编码、数据库 scan、mock 构造、甚至某些 ORM 的字段映射都依赖结构体定义本身。一旦字段增删,这些隐式依赖全崩。
关键不是“JSON 能不能解析”,而是“整个运行时上下文是否还识别这个 struct layout”。
- v1 接口只用
UserV1,v2 接口只用UserV2,两者完全独立定义 - 禁止在 v1 handler 里把
UserV1强转成UserV2或 vice versa - 共用逻辑抽成函数,参数用 interface{} 或显式字段传入,而不是靠 struct 共享
go run 没报错,不代表 encoding/gob 或下游服务没出问题。大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











