13.10 技术债与演进:IM 协议每改一次为何总要补同一个洞
返回第十三卷 · 模块边界:13.03 · 设计评审:13.07 · 迁移兼容:13.08 · 复盘改进:11.11
13.10 技术债与演进:IM 协议每改一次为何总要补同一个洞
DeepTutor 八节初稿经技术与教学审阅后的静态课程。变更次数、工时、风险和改进收益均为虚构纸上假设,没有运行 Go、IM、数据库、测试、部署或站点。当前 S2
/v1正文非空且最多 6 UTF-8 B、原始 HTTP 请求正文最多 4096 B、同 ID 重复 409、非成员隐藏 404、成功200 accepted_in_memory只表示本进程内存受理。未来 S3/v2的stored_in_teaching_db是提议且仍为 6 B;R9 的 6→9 B 待审。文中的“未来适配器/重复工作”是假设,不代表仓库现有代码存在该债。09.02 当前合同
一、技术债是“以后碰到这处会多付出什么”的隐喻
Martin Fowler 用技术债解释内部质量不足怎样让之后的变更比本应需要的更费力:还掉“本金”是调整设计/流程的投入,“利息”是以后实际需要修改该处时多花的时间、风险或事故代价。它不像银行贷款按日自动计息;若某处很丑却长期无需改,眼前利息可能低。反过来热门 IM 协议边界几乎每次版本演进都要碰,少量重复规则可能累积很高的修改摩擦。Fowler:Technical Debt
例如未来若同时维护 /v1、获批后的 /v2 与多个客户端版本,正文长度、重复 ID、非成员隐藏和确认文案若被复制到多个散落位置,每次改动都需要人手找齐、复审和回归;这才是一个待取证的债务候选。目前 /v2 还没获批,因此不能把“未来也许会出现的重复适配”写成已发生事实。13.03 信息隐藏
| 词 | 本例应记录 | 不能冒充 |
|---|---|---|
| 本金 | 为减少重复/泄漏而做的小范围重构、契约验证与迁移工时 | 一次大重写的愿望 |
| 利息 | 以后触碰该处时额外的开发、评审、排障/值班损耗 | 仅代码行数多、文件名不美观 |
| 触发条件 | R9 若批准、S3 若推进、旧端演进或重复故障 | 时间流逝本身必然产生同样成本 |
| 收益 | 同类变更更少触碰点、合同错误/人工处置更少 | “代码看上去更整洁”就自动有业务价值 |
二、先分缺陷、范围限制、未决需求和债,处置顺序不同
当前 S2 只到内存是已明确的教学能力边界,本身不因未来 B 想离线 25h 就自动变成技术债。若当前 /v1 错把 7 B 接纳、同 ID 重复给 200、非成员泄露会话或把 200 描述成 DB 已存,则是违反现行合同的缺陷,应先止损/纠正,不能放进“有空再还的债”清单。未来 S3 的历史可见规则未定是产品/安全决定阻断;outbox 尚未实现是未来方案的待验证机制,尚不能据此判当前实现欠债。09.02 当前合同 · 13.07 未决项
| 纸上现象 | 首先归类 | 下一步与责任 |
|---|---|---|
/v1 7 B 被接纳或非成员返回 200 | 当前合同缺陷 | Go/安全立即按现行 6 B/404 合同修正和核影响 |
| S2 200 仅内存受理 | 当前明确范围 | 客户端文案准确;B25h 作为未来目标另审 |
| 退群后 B 能看多少旧历史无人决定 | 业务/安全未决 | 产品/安全给规则;History 不能猜为全可见 |
| 未来 S3 方案尚无事件恢复设计 | 上线前的设计风险/门 | Go/数据/值班比较机制并给故障证据 |
| 若将来每改协议都得同时修四份重复映射 | 待证的技术债 | 用实际变更与故障记录估利息,再决定偿还 |
| 用户要求新的群搜索能力 | 新功能需求 | 与债务/缺陷分开评估用户价值和成本 |
Fowler 的“审慎/鲁莽、刻意/无意”象限可以帮助回顾当初为何接受某个设计折中,却不能替现行合同违规开脱:越权、已确认消息丢失、错误回执属于必须处理的用户风险,不因贴上“审慎债务”标签而可等待。Fowler:Technical Debt Quadrant
三、用变更和故障证据估“利息”,别凭感觉排大重写
做一份纯纸上条件账:假设未来团队预计有 6 次相似的协议修改,每次因为四处重复映射要多花 3 人时,则条件利息为 6×3=18 人时。若一次局部整治估需 12 人时,粗算 18−12=6 人时 的可能净节省。它依赖六次真的发生、每次损耗确为 3 人时、整治不引入回归,并且尚未计算故障影响与持续维护;不能写成已验证 ROI。Fowler:Technical Debt
更好的债务卡可保留原始证据:过去同类变更触碰了多少包/接口、评审等待多久、合同反例哪次漏掉、同一值班手工补偿重复多少次、哪一类用户实际受影响。对尚无 /v2 运行的本课程,卡中这些字段应写“待观察”,不填伪造样本。若半年内没有需要触碰该映射的变更,预估六次可能落空;若一次越权事故风险极高,单看人时又会严重低估处理优先级。10.12 维护性评估
| 取证维度 | 借它回答的问题 | 避免的误判 |
|---|---|---|
| 变更频率与触碰面 | 未来同类修改有多常见、哪些包一起变? | 看文件行数推利息 |
| 交付等待/返工 | 多少时间花在重新理解确认点、旧端回归与跨组等待? | 把所有工期都归咎于代码 |
| 故障与值班 | 是否有同类 409/404/缺序/积压误判反复出现? | 一次偶然事故无限外推 |
| 偿还投入/风险 | 小范围改造成本、迁移兼容和回退怎样? | 只算修复编码时间 |
| 用户结果 | A 的旧合同/B 的未来目标是否变好? | “技术债下降”却用户退步 |
四、把硬门放第一位,再比较债务的偿还时机
排序先处理现行合同缺陷和安全/数据完整性风险,再看未来 S3 的权威、成员历史规则和回退是否仍是上线阻断;只有在这些边界明确后,才比较可维护债务与新功能的机会成本。可用变更频率、单次额外工时、事故严重性与发生可能性、修复成本、能否验证/回退等维度建立理由透明的排序,无需发明一个小数点后两位的“债务总分”。Google SRE:复盘行动项 · Fowler:技术债隐喻
| 假设项目 | 优先级理由 | 不该怎样处理 |
|---|---|---|
/v1 非成员不再隐藏为 404 | 现有越权/合同风险,立即阻断放量 | 排成“下季度偿债” |
未来 /v2 权威写后 E9 漏发恢复未设计 | 若要承诺 stored,是上线前的正确性门 | 先扩量再把它记技术债 |
| 重复版本映射若造成每次协议修改返工 | 频繁变化可能持续付利息 | 没有证据就直接重写整个 IM |
| 某个低变更、低风险工具包不够优雅 | 利息可能很低 | 只因风格不喜欢就抢用户功能资源 |
若业务有意识暂接受一个可控设计折中,应写明获益、受影响范围、到期/触发条件、owner、护栏、偿还路径和谁能重新评审。安全违规或对用户已确认消息的破坏不能靠“我们选择负债”豁免。Google SRE 的复盘资料强调行动项有明确负责人、可衡量终点和追踪,避免“改善重试”这样的无边界口号。Google SRE:Postmortem Culture
五、架构漂移与运行配置漂移不是同一张差异表
13.07 的 ADR-P1 仍是 proposed:实现没有照它做,先问决定是否获批,不能对一个未接受方案直接宣称“偏离了已决定架构”。若未来某设计被接受,后续却未经评审形成同一 m-9 的两个权威写入者、让旧 /v1 偷偷接纳 7 B,或 D2 回 D1 后让已确认 /v2 数据不可读,这可能是架构/合同漂移或直接缺陷,需按现行承诺止损并更新决定链。Nygard:ADR 状态 · 13.08 新权威回退
12.11 讲的运行配置漂移是声明期望与 API/Pod 生效值的差异,例如 Git 期望 K2 而某旧 Pod 仍用 K1。两种漂移会互相影响,但排查证据不同:前者看被接受的决策、接口合同、数据权威及变更历史,后者看渲染清单、字段所有权、运行状态与生效配置。一个有意的新架构决定应写新 ADR 并把旧 ADR 标为被替代,而非悄悄修改旧结论或把每次合法演进都报成异常。12.11 配置与审计
| 观察 | 先问什么 | 需要什么证据 |
|---|---|---|
/v1 7 B 被接纳 | 是否违反现合同? | 09.02 合同、请求/响应反例、影响范围 |
/v2 DB 与旧内存都自称权威 | 是否有批准的单一权威切换? | ADR、写入路由、对账/回退记录 |
| Git K2、某进程仍 K1 | 是否只是 Pod 未替换或配置未生效? | G/M/API/Pod/进程版本链 |
| 新设计主动改变权威 | 谁决定、为何、何时旧读路径可收缩? | 新/旧 ADR 关系及迁移验收 |
六、偿还方案用小步、可回退的边界改动表达
若真实证据表明协议映射反复付利息,一个纸上候选是先把 /v1 和未来 /v2 的输入与确认语义放到明确的版本映射边界,给 Go 用例传稳定消息意图和权限结果;分别保留旧 6 B/409/404/accepted_in_memory 合同测试。之后才讨论未来 /v2 的 DB 权威、E9 恢复与旧端读路径。这样每一步可按当前用户行为验证,R9 仍独立待审。13.03 模块接口 · 13.08 分阶段迁移
| 小步改动 | 可验收终点 | 停止/回退门 |
|---|---|---|
明确当前 /v1 规则位置 | 6 B/409/404/200 正反例在同一纸上契约可追 | 任一旧端行为被改即停 |
| 收敛重复映射(若未来真的存在) | 同类变更触碰面减少、确认点不变 | 新抽象使错误/权限不透明则撤回 |
| 未来权威与 E9 恢复单独评审 | 同 ID 唯一、DB stored 证据、事件重投不造双消息 | 未获权威/权限/回退证据不扩量 |
| 将手工对账变为可复核流程 | 覆盖/缺口/不可比样本与 owner 透明 | 影读或补偿若可能污染权威即停 |
不要用“大重写”代替债务论证:大范围替换可能同时改协议、存储、成员权限和运行路径,扩大不可回退面,且旧 S2 没有可凭空迁移的权威历史。每项行动卡应写提出原因、owner、前置依赖、验收、停止门与后续复测窗口;本章只形成课程设计,不表示仓库已有这些实施任务。Google SRE:行动项可验证
七、用同类变更和用户结果追踪收益,没收益就调整
“偿还了 12 人时”只记录投入,不证明少付利息。未来如真的完成局部整治,应用同类协议变更比较修改触点、等待/返工、客户端和提供方契约差异、值班人工对账与用户合同错误;同时保留变化量和工作复杂度,不能拿一个 3 B R9 变更和一次完整数据迁移直接比人时。若没有足够样本,就如实报告“不确定”。10.12 维护性证据
Fowler 的比喻也提醒:若不再频繁碰某块代码,原先估的利息可能变小;若反而新增抽象导致每次改动需跨更多团队,偿还手段可能比债更贵。按预先写的触发/停止条件复盘,减少无效抽象或重新分界;若发现现行 409/404 错误仍复发,应回到缺陷处置,不继续在账本上优化预测。不能用“完成若干技术债工单”代替 A/B 用户结果和长期支持成本。Fowler:Technical Debt
固定 openimsdk/open-im-server 提交 f6411a8a1a31d3df36f4c2b3ad28481a94141e1f 的 send.go 选定路径调用 MsgToMQ 后返回;另一 Mongo 消费路径调用 BatchInsertChat2DB。两处只支持所读异步边界,不能据此宣称 OpenIM 实际有哪项技术债、事故、组织等待或偿还收益。OpenIM 阅读地图
八、22 道分层练习:给一张 IM 技术债清单分类
先判当前合同与问题性质,再算条件利息,最后设计可验证偿还卡。答案均基于本章虚构设定。
基础 1–8:分类与合同
1. 技术债“利息”主要在何时付出?
以后触碰有问题的设计时,多花变更、评审、排障等代价;不自动等于每日固定费用。
2. 当前 S2 只承诺进程内存受理,本身必是技术债吗?
不必然。它是已明示范围,未来更强需求要另审。
3. `/v1` 接纳 7 B 应先记为何类?
违反当前 6 UTF-8 B 合同的缺陷,优先按影响止损/修复。
4. 当前非成员发送返回 200,可排下季度还债吗?
不行。违反 404/权限合同,应立即调查影响并修正。
5. 退群后历史可见未定,首先是谁的事?
产品/安全的业务规则决定阻断,不只是 Go 代码清理。
6. 未来 S3 的 outbox 尚未实现,就表示当前有技术债吗?
不能直接推断;它是未来候选方案的待评估恢复机制。
7. R9 当前是什么状态?
正文 6→9 B 的待审提议;旧 /v1 仍为 6 B。
8. ADR-P1 proposed 可当现行架构验收依据吗?
不能。未接受的候选不能被当作已实施或已验证事实。
推演 9–16:利息与漂移
9. 未来 6 次同类变更各多 3 人时,条件利息多少?
6×3=18 人时,只是纸上预测,不是真实观察。
10. 若整治估 12 人时,账面净节省多少?
18−12=6 人时,仍须考虑变更是否发生、回归和事故风险。
11. 某块丑代码多年不改,利息一定很高吗?
不一定;债务隐喻的利息通常随触碰/变更出现。
12. 同一个 `m-9` 被两个写入者都称权威,先查什么?
查获批准的数据归属/迁移阶段与实际写入证据;若破坏唯一权威须止损。
13. Git 期望 K2、旧 Pod 进程仍 K1,属于哪类差异?
首先是配置交付/生效漂移,要查声明、API、Pod 和进程链。
14. 有意改掉已接受的架构决定,怎样避免“悄悄漂移”?
写新决定及理由、验证和迁移门,将旧 ADR 标为被替代并保留历史。
15. D2 回 D1 后看不到 `/v2` 已确认消息,可记普通债务吗?
不能。已确认事实不可见是用户正确性/回退缺陷,先保全读取和权威数据。
16. 若预计六次变更结果只发生一次,原 18 人时预测怎样?
需按真实变更频率重估;不能坚持原预测当收益证据。
决策 17–22:偿还与验证
17. 越权风险可因“审慎负债”而等待吗?
不能。象限帮助回顾折中,不豁免现行安全/合同要求。
18. 技术债卡至少需要哪些输入?
用户/变更影响、触发频率、额外成本/事故证据、偿还投入、owner、验收与停止门。
19. 小步整治版本映射,首先守哪组行为?
当前 /v1 6 B/409/404/accepted_in_memory,R9 与未来 /v2 另行评审。
20. 只看修复后代码行数减少,能证明偿还收益吗?
不能。要看同类变更成本、故障/人工处置、用户合同和新增复杂度。
21. 两处固定 OpenIM 源码可证明其真实技术债清单吗?
不能。只支持所读发送到 MQ 与另一 Mongo 消费路径异步边界。
22. 债务偿还后若新增抽象使变更更慢,应怎样?
按预设反馈与停止门调整或撤回方案,重新取证,不为完成工单而继续扩大。
本章完成标准与后续路径
能把当前合同缺陷、S2 已知范围、S3 未决规则、未来技术债候选与新功能分开,用 6×3 对 12 的纸上账解释利息预测的限制,并为一项小步偿还写出 owner、验收和收益复测,才算完成第一轮。下一章13.11 复杂案例答辩将检验条件变化时前面所有决定是否仍成立。