上周跟后端组的老周在新街口吃活珠子,他攥着烤串的手都在抖。前一天晚上他们给用户中心加了个新的实名校验字段,没打招呼直接改了原有返回结构,结果五个下游调用方同时炸掉,凌晨两点半整个组在公司会议室蹲到天亮,连外卖的黄焖鸡都凉透了。
说实话,这种事我见得太多了。
很多团队一提微服务接口版本兼容,第一反应就是“加个v1路径不就完事了”,真到线上出问题,才发现之前的操作全是给自己埋雷。
别把版本号当改bug的遮羞布
我见过最离谱的操作,是把版本号当bug修复的标记用。
上周帮朋友排查接口问题,发现他们同一个接口路径下,三个月改了七次返回结构,版本号标了v1.1、v1.2、v1.3,最后下游对接的人都疯了,不知道哪个版本对应哪个逻辑。
更绝的是,他们把v1.0的旧返回结构直接覆盖了,说“反正没人用旧版本了”。结果第二天就有个三年前的合作方找上门,说他们的对接脚本直接报空指针,业务停了俩小时。

别信什么“我写的接口我知道没人用”的鬼话。
只要你的接口曾经对外开放过,哪怕是内部的,也总会有你想不到的人在调用。可能是离职同事写的没人维护的脚本,可能是运营用来导数据的临时工具,你觉得没用的东西,在别人那可能是吃饭的家伙。
字段级兼容才是真的省力气
很多人觉得版本兼容就是要做独立的v2接口,其实根本没必要。
绝大多数的接口变更,根本到不了需要新开版本的程度。只要你守好几个基本规则,就能避免90%的兼容问题。
所有新增的返回字段必须给可空默认值,绝对不要修改原有字段的类型和语义。
比如原来的user_status是int类型,1代表正常,0代表注销,你哪怕要加新的状态值,也不能改成string的”normal”、”cancel”,不然下游写的if(user_status == 1)的判断直接全废。
还有,别随便删旧字段。哪怕你觉得这个字段已经没人用了,先在接口文档里标上@Deprecated,连续监控三个月的调用日志,确认没有任何请求读取这个字段,再考虑删掉。
上次我们删了个三年前的旧字段avatar_url,结果触发了个离职同事写的用户头像同步脚本,把全平台的头像都爬成了空白图,找了半天才找到根源。

真的,这些小事做起来花不了十分钟,真出了问题,熬通宵排查的痛苦没人能替你受。
版本号到底放哪才不会乱

关于版本号放哪的问题,我们团队吵了快半年。
有人说放URL路径里最直观,debug的时候一眼就能看到;有人说放请求头里更优雅,不用改路径;还有人说放请求参数里最方便。
其实没有绝对的对错,只有适不适合你的团队。
如果是小团队内部用的接口,直接放URL路径里最省事,比如/api/v2/user,不用翻请求头,新人对接的时候一眼就能懂。如果是对外的开放平台,不想让合作方随便改接口路径的,就放请求头里,比如X-API-Version: v2,合作方只需要改请求头就行,不用动现有调用逻辑。
但有一条红线绝对不能碰:绝对不要把版本号藏在非必填的请求参数里。
之前有个电商的朋友,把版本号放在query参数里,结果CDN缓存的时候把参数给丢了,大促的时候一半用户看到的是旧的商品详情页,库存都对不上,亏了十几万。
真要遇到必须修改旧字段语义的极端情况怎么办?比如原来的user_status=1是“正常”,现在要改成“已注销”,总不能直接改吧?
那就给同个接口加版本判断。请求头带v1的,就返回旧的语义;带v2的,返回新的逻辑。别觉得这是冗余,线上的稳定性比代码的优雅重要一百倍。
哦对,还有个小细节,接口变更不管大小,一定要在对接群里@所有下游的对接人,哪怕你觉得是个无关紧要的小改动。上次老周他们就是改了字段没发通知,下游的对接人还在改别的需求,根本不知道接口变了,出了问题都不知道找谁。
别等线上炸了才想起补坑。