3094 字
约 10 分钟
15
S03-A12 · 技术债的利息怎么还:从记账到偿还顺序

技术债清单列了八十条,一年下来一条都没还。上一轮已经谈过"定价"——怎么给一条债报出价。这篇只谈它之后的两个动作:怎么记,以及按什么顺序还。

因为清单不可执行的真正原因往往不在价格,而在账本:条目混着"重写鉴权"和"删个无用常量",没有年龄、没有影响面、没有偿还成本估算,于是排序只能靠嗓门。账本一改,偿还顺序就能算出来。


1. 八十条一条没还:账本的四个缺陷

缺陷 表现 后果
粒度不一 大重构与一行清理并列 无法比较,只能挑小的
无年龄 不知道谁先欠的、欠了多久 老债永远排在后面
无影响面 不知道哪些改动会碰到它 无法判断阻塞关系
无偿还成本 不知道要几天 排期时必然输给需求

一张只有标题与描述的清单,本质上是一份抱怨记录,不是待办队列。

2. 记账:一条债的最小字段

// 账本条目:字段缺失即视为不可排序
id: DEBT-0142
title: 订单状态机散落在三个服务中
type: coupling            // coupling | correctness | operability | knowledge
scope: [order-service, payment-service]   // 影响面:哪些模块的改动会碰到
interest_signals:                          // 利息的可观测代理指标
  change_lead_time: order-service          // 该模块变更时长
  defect_density: payment-service
age_since: 2025-03
repay_cost_days: 8        // 粗估即可,量级对就够
blocks: [DEBT-0088]       // 阻塞关系
last_touched: 2026-06     // 最近一次被改动触及

字段里最容易被省略、又最决定排序的是 scope 与 last_touched:前者决定它是否阻塞别人,后者决定它是否还活着。

3. 利息不是抽象概念,是四类具体成本

利息形态 具体表现 观测口径
变更变慢 同一需求要改多处、评审轮次变多 该模块从提交到上线的时长趋势
缺陷变多 改动区域反复出回归问题 该模块缺陷密度与复发率
新人上手慢 需要口头传授才能动这块代码 新人首次独立提交的时间
故障恢复慢 定位要跨多个服务与仓库 同类故障的平均定位时长

四类里只有"缺陷变多"通常已有数据;其余三类要专门取数(见第 9 节)。不取数就只能凭感觉说"这块很烂",而感觉无法排期。

4. 偿还顺序:三个判据相乘,而不是相加

判据 含义 取值方向
利息大小 不还会持续损失多少 越大越优先
偿还成本 要几个人天 越小越优先
是否阻塞 是否卡住其他已知工作 阻塞即插队
// 排序分只用于拉出候选,不用来做最终决策
score = interest * block_factor / max(repay_cost_days, 1)
// interest 取四类成本的归一化值;block_factor:阻塞=2,其余=1

顺序上有一条硬规则优先于算分:被它阻塞的活已经排进本迭代,就先还它。反之,一条利息很高但谁也不碰的债,排进去只是自我感动。

5. 三类偿还方式与各自适用条件

方式 做法 适用条件 主要风险
顺手还 在本来就要改的区域附近一并整理 改动半径小、可当场验证 扩大变更范围,评审失控
专项还 单独立项、独立验收 利息可量化、影响面跨模块 立项后指标说不清
绕过去 加隔离层或新路径,不动旧代码 旧模块调用量在降、即将废弃 双路径长期并存
拆出去 把债务模块整体替换为托管服务 该能力非核心、维护成本高于采购 迁移期与依赖成本

三者不是优劣关系,而是成本结构不同:顺手还摊薄在需求里,专项还占用排期,绕过去把成本推给未来。

6. "顺手还"的边界:只在改动半径内

顺手还失控的典型现场:改一个字段校验,顺手把整个 service 的分层重排,PR 从 80 行变成 1200 行,评审放弃,回归问题三周后才暴露。

判据 允许顺手还 应该另开一项
改动文件数 与本需求重叠 出现无关文件
是否改变对外行为 不改变 改变返回值、错误码
能否被现有测试覆盖 已覆盖 需要新增测试体系
回滚粒度 可与需求一起回滚 需要独立回滚
// PR 提交前检查:顺手还的半径是否越界
git diff --stat main | tail -1                    // 行数与文件数
git diff --name-only main | grep -v PATH_PREFIX   // 出现无关模块即停

判据一句话:顺手还只做"读者看不出这是两件事"的改动;读者能看出,就该拆成两项。

7. 专项偿还的立项要求:必须能说清哪个指标会变

立项要素 不合格写法 合格写法
目标 "重构订单模块" "订单模块变更时长从当前量级降到一半"
范围 "顺便优化" 明确的模块与调用方清单
验收 "代码更清晰" 同一需求的改动文件数与评审轮次
回滚 未提 灰度范围与回退开关
结束条件 未定义 指标达成或到期即止

说不清指标变化的重构,最后只能靠"感觉好多了"结项,下一轮排期时它没有说服力。

// 立项卡:五行,缺任何一行都不该占用排期
goal: 订单模块变更时长 p50 减半
scope: [order-service, payment-service]
verify: pr_metrics(module=order-service).p50    // 与基线同口径对比
exit: 连续两个迭代达标或到期即止
rollback: 按服务灰度,开关 ORDER_SVC_NEW_PATH

没有 verify 这一行,偿还就退化成一次"代码重排",无法证明它降低了利息。

8. 不还也是合法决策,但要写理由

理由 判定依据 记录方式
代码即将废弃 有明确下线日与替代路径 账本标 sunset: DATE
影响面小于成本 调用量已归零或极低 附调用量数据
改动频率极低 近一年 last_touched 为空 降级为观察项
已被新路径替代 双路径中旧路径流量占比趋零 标 bypassed_by

"不还"必须留下判据,否则半年后同样的问题会被重新提一遍,再花一次讨论成本。

9. 利息的观测:按模块取趋势,不取绝对值

观测项 取数方式 判读
变更时长 该模块 PR 从首提到合并的中位时长,按月 连续三个月上升即为利息
缺陷密度 该模块每千行改动引发的缺陷数 高于全局均值两倍以上
上手时间 新人首次在该模块独立提交所需天数 逐版本变长
恢复时长 该模块相关故障的平均定位时间 与故障总数脱钩上升
// 变更时长按月趋势:只看趋势,不看单点
SELECT module, date_trunc('month', created_at) AS m,
       percentile_cont(0.5) WITHIN GROUP (ORDER BY lead_hours) AS p50
FROM pr_metrics WHERE module IN (SELECT unnest(:scope))
GROUP BY 1, 2 ORDER BY 1, 2;

绝对值没有意义(不同模块基线不同),同一模块的时间序列才有意义。

10. 还债的人往往不是欠债的人

情形 组织后果 缓解
原作者已转岗/离职 无人认领,只能外行改 交接时把判据写进账本
债务由架构决策产生 团队背成本,决策者不受影响 决策记录关联债务条目
还债不产出可见功能 绩效上不可见 用指标变化作为交付物
债务跨团队 谁都不愿先动 明确 owner 与共同验收

这是组织问题,不是技术问题:不解决它,任何排序算法都会被现实绕开。

11. "重写"与"还债"的分界

维度 还债 重写
边界 沿用现有对外契约 常顺带改契约
风险 局部、可回滚 全局、切换点集中
新债 一般不产生 几乎必然产生(新结构、新缺文档)
验收 指标变化 需要并行运行与数据比对
适合时机 利息可观测且持续 结构已无法承载新需求

重写不是还债的加强版:它同时产生新债,且新债在头半年利息最高。把它当还债写进账本,会让偿还率指标失真。

12. 度量:年龄、可观测比例、偿还率、复发率

指标 口径 异常信号
条目年龄 从 age_since 到今天的时长 中位数单调上升
利息可观测比例 带 interest_signals 的条目占比 低,说明账本仍是抱怨记录
偿还率 本季度关闭条目 / 到期应还条目 长期为零
复发率 同一 scope 半年内重复建条目 高,说明只治了症状

其中"利息可观测比例"最能预测后续:不可观测的债无法被排期保护。

13. 排错:五种"还了但没变好"

现象 原因 处理
还完指标没动 立项时未定义指标 补验收口径,再判断是否真还完
PR 越还越大 顺手还半径失控 按文件数与行为变更拆分
同一处反复建条目 只改了症状 追到契约或数据模型层
老债永远排不上 无阻塞关系、无年龄权重 补 scope 与 blocks 字段
重写后半年更慢 新结构未稳定、双路径并存 把切换与下线写进验收

14. 边界:什么时候偿还应让位于其他工作

情形 处置 理由
系统有明确下线日 只做维持性修复 投入无法回收
模块调用量已归零 直接删除或归档 利息为零
代码长期不被修改 降级为观察项 改动频率低,利息不累积
团队正在救火期 只做阻塞型偿还 注意力是最稀缺资源
债务位于第三方依赖内部 用隔离层绕开 无法直接偿还

15. 自测

  1. 一张只有标题与描述的债务清单,缺哪四个字段导致它无法排序?
  2. 四类利息里,哪一类通常已有现成数据,哪三类需要专门取数?
  3. 排序时哪一条硬规则优先于算分结果?
  4. "顺手还"越界的判据是什么?
  5. 专项偿立项时,为什么必须说清"哪个指标会变"?
  6. 重写为什么会同时产生新债,这对偿还率指标有什么影响?
S03-A12 · 技术债的利息怎么还:从记账到偿还顺序
https://www.clxhxhhr.top/posts/6295/
作者
clxstart
发布于
2024-12-31
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。