技术债与发布速度 — 测量利息,决定何时偿还
一句话总结
技术债务不是坏事,而是带利息的贷款。早期产品为了尽快学习,会故意去借。问题在于不去衡量利息。从部署记录和工作记录中衡量利息(漏到计划之外的时间)和部署稳定性,再把偿还成本、回收期、被推迟的功能的价值放在同一行里,“现在要不要还”就从意见变成了计算。
为什么需要它
小团队总是在两种声音之间摇摆。“现在不修,就永远修不了”与“先把客户想要的功能做出来,才能活下去”。两句话都对,所以会议永远开不完。结论通常是由声音更大的一方决定的。
Martin Fowler 在技术债务的文章中写道,这个比喻是 Ward Cunningham 在 1992 年的 OOPSLA 经验报告中提出的,并把因为代码缺陷而在每次增加功能时额外付出的工作量,说明为利息,把清除这个缺陷的工作量,说明为本金。直接使用这个框架就行。如果利息很大并且在增加,而本金很小,那么还掉更快;如果利息很小,那么把时间用来做功能更好。
工作原理
部署稳定性——DORA 指标。dora.dev 的指标说明把变更前置时间(提交到版本管理中的变更,到部署到生产环境为止的时间)、部署频率、失败部署恢复时间列为吞吐量指标,把变更失败率(部署之后需要立即介入的部署所占的比例)和部署返工率(因为生产事故而进行的计划外部署所占的比例)列为不稳定性指标。债务累积的模块,往往部署稀少、失败频繁、恢复很长、事故响应部署很多。
看中位数而不是平均数。前置时间里会混入几个搁置了好几天的变更。平均数会被这几个拖着走,从而掩盖典型的变更要花多长时间。本实验把中位数和平均数并排写出来,让人看到差别。
衡量利息。每周按模块记录的“计划外工作时间”(应对缺陷、故障),就是利息。不要只看平均数,要看趋势——把前 4 周与后 4 周比较,如果在增加,那么偿还决策的依据就应当是最近的利息,而不是过去的平均值。
判断要不要还。本实验按下面的顺序计算(所有的值和规则都是示例)。
| 值 | 计算 |
|---|---|
| 节省(小时/周) | 最近的利息 × 重构所降低的比例 |
| 回收期(周) | 重构成本(小时)÷ 节省 |
| 期间净节省(韩元) | (节省 × 判断期间 − 成本) × 每小时成本 |
| 延迟成本(韩元) | (成本 ÷ 团队每周产能) × 功能的每周价值 |
如果团队在重构期间无法开发功能,那么功能就会相应延后,该功能每周本应赚到的价值就消失了——这就是延迟成本(cost of delay)。如果回收期最短的候选项的期间净节省大于延迟成本,就先还;否则先发布功能。“重构会使利息降低 70%”这样的估计可能出错,所以把估计值写下来,并在还完之后与实际利息比较,才算走完一圈。
把债务记录在哪里。要衡量利息,就必须按模块记录计划外工作。不需要什么了不起的工具——只要在问题跟踪器里留下“计划外”标记、模块名称和花费的时间,就能得出每周的合计。没有记录,利息就只作为“大家都很忙”这种感觉而存在,而感觉是赢不过功能请求的数字的。
并不需要还清所有债务。即将丢弃的实验代码、几乎不变的模块,其债务的利息接近于 0。正如 Fowler 所说,利息是在修改那段代码时才产生的。所以,要还的候选项,不是从“脏乱的代码”中挑,而是从“经常变动、并不断制造计划外工作的代码”中挑。变更频率和计划外工作都高的模块,是第一候选。
在现场相遇的样子
- 一碰支付模块,部署就失败,恢复要花半天,每个星期五都在故障应对中结束。利息没有被记录,所以没有人知道它有多大。
- 只有“重构之后会变快”这句话,没有关于快多少、从什么时候开始的数字,所以每次都被功能挤掉。
- 反过来,因为“看着不顺眼”,去改一段几乎没有利息的旧代码,把发布推迟了一个月。
如何发现自己做错了
- 如果前置时间的平均数和中位数相差很大,就意味着存在长尾。要把长尾中的变更单独打开,查看原因——是在等待评审,还是在等待部署窗口。
- 确认变更失败率的分母是不是部署数。如果除以事故数或天数,部署频繁的团队就会吃亏。
- 如果利息的估计用了 12 周平均值,就要同时看趋势。用平均值去判断在增加的利息,就会错过该还的时机。
- 决策之后过几周,重新衡量计划外工作,与估计比较。没有比较过的估计,在下一次决策中还会错同样的幅度。
下一项实验要做什么
你将根据 12 周的部署和工作记录计算各服务的 DORA 指标,并查看前置时间的中位数和平均数为什么不同。衡量各模块的利息及其趋势,计算两个重构候选项的回收期、净节省、延迟成本,并按规则作出决定。评分器还会用打乱过的记录,再次运行你的函数。