3178 字
约 10 分钟
16
S07-A10 · 契约变更的协同:谁先改、谁等着

两个团队同时改接口,联调时才发现各改了一半。复盘通常落在"沟通不够"上,但沟通只是表象:真正的问题是顺序——没人说过谁先动、谁在过渡期承担什么、什么时候算完。契约变更的协同不是文档工作,是一次带时序的工程协调,顺序错了,再多沟通也只是把返工推到联调那天。


1. 协同问题本质上是顺序问题

一个契约两侧各有一份实现,变更意味着两份实现要先后落到不同状态。任意时刻只可能出现三种组合,其中两种是"半改"。

状态 提供方 消费方 结果
旧×旧 旧契约 旧契约 正常
新×旧 或 旧×新 一侧已改 一侧未改 只在兼容时正常
新×新 新契约 新契约 正常

协同要做的就是安全地穿过中间状态抵达第三种。问题不是"要不要改",是"中间状态怎么活"。

2. 先改提供方,还是先改消费方

两条路径都成立,选择依据是"哪一侧的中间状态更安全",而不是哪边更闲。

顺序 适用条件 中间状态 风险
先改提供方 提供方能同时输出新旧两种形态 新×旧:旧消费方读旧字段 提供方要维护双写
先改消费方 消费方能同时兼容新旧两种形态 旧×新:消费方容忍缺失或未知 消费方要维护双读
不可双向兼容 语义变化,两侧必须同刻切换 中间态不安全 只能靠原子切换或停机窗口

口诀:能容忍未知的一侧先改。消费方若已实现宽松解析,可先在旧契约上适配新形态,等提供方切换时不再改动。

3. 四种变更模式与各自的失败方式

模式没有优劣,失败方式各异;选模式等于选要防哪种事故。

模式 做法 失败方式
兼容扩展 只加可选字段,两侧都不改代码 被误判为安全,实际改了语义
双版本并行 新旧路径并存,按版本分流 旧版本永久存活,无人收尾
消费方先行适配 消费方先兼容两种形态 适配代码长期留着,变成第二套契约
同时切换 同刻上线,原子生效 无回滚路径,切换失败即全断

最容易出事的是"同时切换":它看起来最省事,实际上是唯一要求"零误差"的模式。

4. 过渡期的三种状态与必须存在的观测点

过渡期必须能被看见,否则没人知道进行到哪一步,也没人敢宣布结束。三个观测点缺一不可。

状态 观测点 判断依据
旧契约 旧字段/旧路径的调用量 该数归零才能进下一步
双契约 新形态调用量占比 占比爬升曲线是否健康
新契约 旧形态残留调用方列表 列表为空或只剩已同意豁免者
// 三状态观测的最小查询(语法按所用平台核对)
// 1) 旧形态是否还有人调
// sum(rate(api_calls_total{shape="legacy"}[5m]))
// 2) 新形态占比
// sum(rate(api_calls_total{shape="new"}[5m])) / sum(rate(api_calls_total[5m]))
// 3) 残留调用方
// sum by (client_id) (rate(api_calls_total{shape="legacy"}[1h]))

观测点要在变更之前就位——事后补埋点,往往正好错过双契约期最需要判断的那几周。

5. "同时切换"唯一可行的前提

同时切换要求两侧在同一时刻生效,只有两种工程手段能做到:同一发布单元,或可原子回滚。

前提 检查方式 不成立时
同一发布单元 两侧代码同仓、同流水线、同一次发布 改用双版本并行
可原子回滚 回滚脚本已演练,回滚耗时已知 先建回滚路径再谈切换
状态可共存 数据层能同时容纳新旧语义 先做数据迁移与双写
无跨版本缓存 缓存键含语义版本 清缓存或加版本前缀
// 同时切换的最小回滚契约:切换前必须写好并演练
switch:
  at: "CUTOVER_TS"
  steps: [ "停旧写入", "双写校验", "切读", "观察 OBSERVE_WINDOW", "停旧读" ]
rollback:
  trigger: "错误率 > ERROR_THRESHOLD 或 关键接口 P99 > LATENCY_THRESHOLD"
  action: "回切读路径 + 保留双写"
  tested_at: "REHEARSAL_DATE"

6. 谁承担过渡期成本

这条不谈清楚,协同必然演变成推诿。过渡期有三类成本,必须逐项指定承担方。

成本 具体形态 常见默认(危险)
双写/双读实现 一方或双方多维护一条路径 "谁提需求谁做" → 提出方往往只提不实现
观测与告警 新形态的看板与阈值 "观测是平台的事" → 没人认领
清理 删除临时兼容代码 "以后再说" → 永久保留

可执行的规则:变更提出方承担过渡期的观测与清理,被变更方只承担自己一侧的适配。把清理责任写进同一张工单,而不是留在口头。

7. 公告该写什么

公告不是礼貌,是接口。四件事缺一件,对方就做不到独立完成迁移。

要素 内容 缺失后果
改什么 字段/路径/语义的具体差异 对方按猜测改,改错方向
何时 生效日与旧形态停止日 无限期拖延
影响谁 按调用方列出的受影响清单 长尾调用方最后才发现
怎么验证 一条可执行的验证命令或对照用例 迁移完成的判定变成主观
// 公告模板:四要素 + 一个可执行的验证方式
// 变更:POST /orders 的 amount 单位由「元」改为「分」,字段名与类型不变
// 生效:EFFECTIVE_DATE 起双读;SUNSET_DATE 起仅返回分
// 影响:调用方 CLIENT_A、CLIENT_B(其余调用方未使用该字段)
// 验证:curl -s APP_URL/orders/SAMPLE_ID | jq '.amount' 应与下单金额×100 一致

8. 变更窗口与发布窗口的协调

两侧各有自己的发布节奏,协同失败常常不是技术问题,而是窗口错位:一方周五发,另一方下周二才发,中间跨了一个周末。

情形 处理
两侧发布窗口不重叠 先做兼容态发布(一侧能容忍两种形态),再各自按窗口上
一侧有变更冻结期 冻结期前完成兼容态,冻结期只做切换
跨时区团队 以"最后一个窗口关闭"为对齐点,而不是以第一个为准
紧急修复需求 允许绕过窗口,但必须走同一套兼容态要求

一句话原则:窗口不一致时,先让系统进入兼容态,再各自发。兼容态是唯一能在窗口之间安全停留的状态。

9. 验收判据

"改完了"不是判据。可验收的判据只有三条,都能用数回答。

判据 阈值形式 采集方式
旧调用量归零 旧形态请求数持续为 0 达 STABLE_WINDOW 按 shape 标签统计
新调用成功率达标 成功率不低于变更前的同口径基线 分组对比
错误率无变化 变更前后错误率差在噪声范围内 同窗口对照
// 验收用一条命令跑完,不靠人肉对看板
// 1) 旧形态残留
curl -s PROM_URL/api/v1/query --data-urlencode 'query=sum(rate(api_calls_total{shape="legacy"}[1h]))'
// 2) 新形态成功率与基线差
curl -s PROM_URL/api/v1/query --data-urlencode 'query=sum(rate(api_ok_total{shape="new"}[1h]))/sum(rate(api_calls_total{shape="new"}[1h]))'
─── 三个数都达标才算完成,缺一个就只能算"已切换,未验收"

10. 临时兼容代码的清理责任与截止日

过渡期代码是技术债的定时器。没有截止日,它就变成永久契约的一部分,且没人记得为什么存在。

项 要求 反模式
责任 具名到人,不是团队 "后端组会清"
截止日 与旧形态下线日同一张工单 "迁移完成后"
标记 代码里带 TODO(CONTRACT-xxx) 与日期 无标记,靠记忆
复核 到期未清则在例会上被点名 到期无人提,静默转永久
// 临时兼容代码必须自带截止日与工单号,否则不会被自动追缴
// TODO(CONTRACT-1234, deadline=EXPIRE_DATE) 旧字段兜底,旧形态下线后删除
if (request.getLegacyAmount() != null) {
    amount = request.getLegacyAmount();   // 单位:元,仅旧调用方使用
}

11. 用什么判断协同效率

度量 定义 健康信号
契约变更周期 公告日到验收完成日 有上限且稳定
过渡期长度 进入双契约到旧形态归零 逐次缩短
跨团队返工次数 因顺序错误导致的重复改动 趋零
遗留兼容代码数 带 CONTRACT 标记未清的处数 到期即递减

返工次数是最诚实的指标:它直接反映顺序是否被讲清楚,而不受个人沟通能力影响。

12. 排错:协同失败的常见形态

现象 原因 处理
联调才发现各改一半 没有约定顺序,双方各自开工 先定兼容态,再定谁先发
双契约期拖了一个季度 旧形态没人观测,不知道还剩谁 变更前就位观测,按调用方点名
切换当天全断 走"同时切换"但无回滚路径 补回滚演练,或改双版本并行
兼容代码越堆越多 无截止日与责任 加标记、工单与到期复核
公告发了但没人动 公告缺验证方式,对方不知从何开始 补一条可执行的验证命令

13. 边界:什么时候协同流程该压到一句话

情形 判断依据 做法
同一团队内部 同一批人、同一发布节奏 口头对齐 + 一条群消息
调用方可同步升级 同一发布单元内 一起发,不做兼容态
仅新增可选字段 旧调用方不改也不出错 一句通知即可
内部实验接口 生命周期以周计,无外部依赖 直接改,不公告

反过来,一旦调用方跨团队、跨公司、或难以协调发布窗口,上面这套顺序与责任就必须写下来——省掉的每一条,都会在联调那天以返工的形式出现。

14. 自测

  1. 契约变更的协同问题,本质上是哪一类问题?为什么"多沟通"解决不了它?
  2. "先改提供方"与"先改消费方"的选择依据是什么?有没有一个更简的判断口诀?
  3. 四种变更模式里,哪一种要求"零误差"?它可行的两个工程前提是什么?
  4. 过渡期三个观测点分别是什么?为什么必须在变更之前就位?
  5. 过渡期成本该由谁承担?请给出一个可执行的分配规则。
  6. 验收判据有哪三条?缺一条时最可能的后果是什么?
S07-A10 · 契约变更的协同:谁先改、谁等着
https://www.clxhxhhr.top/posts/6291/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。