每个工程团队都有技术债务。大多数团队都带着永久的愧疚感和模糊的意图来处理它,想要“进行一次技术债务冲刺”,但这种意图永远不会完全实现。结果是:五个冲刺前就应该偿还的债务仍然存在,实际上并不重要的债务占用了数小时的重构时间,而且没有人能分清哪个是哪个。
这是实用版本。科技债务到底是什么?需要不同处理的三个类别。决定一笔债务是否值得偿还的粗略计算。浪费技术债务预算的五种反模式。冲刺中应该有多少是债务工作;真实的,有数字的。
科技债务到底是什么
这个词已经超载了。三种不同的东西被称为技术债务:
- 深思熟虑,故意采取更快的交付速度。 “我们需要在周五之前将其投入生产;我们将跳过迁移工具并在之后进行清理。”这是最初的比喻:一笔在支付之前复利的贷款。
- 偶然,源于漂移。 “我们没有注意到这部分代码库在一次又一次的冲刺中变得越来越混乱,直到变成一片沼泽。”没有采取特定的捷径;系统已经腐烂了。
- 知识债;伪装成代码债务。 “建造这个的人已经不在团队中了。”代码可能没问题;缺少的是理解为什么会这样的人。
每种类型都需要不同的处理。不要把它们混为一谈。本文的其余部分主要讨论 (1) 和 (2):知识债务是一个单独的问题,需要单独的修复(更好的文档、配对轮换、ADR)。
粗略计算一下:什么时候付款值得?
对于任何特定的债务,三个问题:
- 每个冲刺现在对团队的速度有多大影响? (估计为速率损失的百分比;5%、10%、30%)
- 需要多长时间才能还清? (以点或人日为单位)
- 半衰期是多少?这段代码还要多久才会消失? (3个月?2年?无限期?)
如果 cost-of-paying < (slowdown-per-sprint × number-of-sprints-until-it-goes-away),付钱吧。
工作示例:
每个冲刺中,缓慢的数据库迁移都会消耗大约 15% 的团队速率。修复迁移框架大约需要 20 分(大部分是一名工程师,一次冲刺)。该团队计划至少在明年保留该代码库;说 24 个冲刺。
如果不受影响,总减速:15% × 24 次冲刺 = ~3.6 冲刺周的速率损失。
修复成本:20 分 = ~1 容量冲刺。付钱吧。数学是压倒性的。
反例:
一些旧版管理视图使用已弃用的表单生成器。它们的维护速率很慢(对于从事它们工作的工程师来说,速率阻力约为 5%)。修复它们大约需要 15 分。产品经理表示,整个管理部分将在第三季度(8 个冲刺后)重写。
如果不受影响,总减速:5% × 8 次冲刺 = 0.4 冲刺周的速率损失。
修复成本:15 分 = ~0.75 冲刺周。不付钱。重写免费消除了债务。
计算不应该是精确的。它应该使决定可见。当事情势不可挡时,你就会采取行动。当事情处于临界点时,无论哪种方式,辩论的成本都会超过节省的成本。选择一些容易恢复并继续前进的东西。
需要不同处理的三类
计算完毕后,对每项债务进行排序:
复利债务;积极支付
得到的债务 更贵 当团队围绕它工作时。症状:该区域的每一个新功能都需要触摸腐烂的部分。测试运行时的增长速度快于代码库。 CI 不稳定,每次冲刺都会消耗更多时间。
复合债务是一种危险的债务。成本计算具有误导性,因为经济放缓会随着时间的推移而加剧。团队通常晚六个月才意识到这一点。
处理: 明确的预算。即使一名工程师一次冲刺也常常足以破坏该化合物。不要等待“科技债务冲刺”;那是永远不会发生的版本。
静态债务;机会主义地付出代价
债务很烦人,但并没有变得更糟。旧代码可以正常工作,只是现在没人会这样写。落后三个小版本的依赖项。
处理: 侦察兵统治它。当您已经在文件中进行功能工作时,请使其稍微干净一些。不要专门针对静态债务展开报道,除非计算表明值得重点解决。
冻结债务;别管它
团队很少接触并且稳定的代码中的债务。 2018 年管理脚本,每晚运行,没有任何投诉。一个部分中的旧版 CSS 不再具有样式。计划删除的代码。
处理: 忽略。接触冻结的代码会带来收益微不足道的风险。无法抗拒重构冻结代码的工程师通常是想要提高工作效率的工程师;相反,他们会转向复利债务。
大多数团队都会犯的错误:将所有债务视为复利。他们在静态和冻结债务上消耗产能,而复利债务则在角落里积累。
浪费技术债务预算的五种反模式
1.“科技债务冲刺”解决不了任何问题
纯粹的技术债务工作的整个冲刺。听起来很有成效;通常不是。如果没有范围纪律,团队就会选择容易的债务(低风险、低回报的静态债务)并避免可怕的债务(没人想碰的遗留授权流程)。冲刺结束:大量的小提交,没有获得实际的影响力。
解决办法:将技术债务预算为每个冲刺的百分比(10-20%),而不是单独的冲刺。连续而不是分批偿还债务。
2. 重写策略
“让我们从头开始重写整个模块。”有时是正确的,但通常是灾难。重写花费的时间是估计的 3-5 倍。重写中期,业务优先级发生变化。半重写的状态比旧的或新的状态更糟糕。
修复方法:在两个版本都运行的情况下进行增量迁移。绞杀者图案。在删除旧的之前,达到新的可证明处理 X% 流量的状态。是的,这意味着复杂性会重叠数月。另一种选择是永远不会完成的半重写。
3.没有目标的重构
“我要清理这个文件。”为了什么?无需重构 使用 头脑中倾向于优化工程师的美观而不是团队的速率。文件变得更漂亮;没有什么比这更好的了。
修复:重构以服务下一个功能。如果您要向函数添加功能,请重构 该功能 使添加更干净。不要重构您不打算扩展的功能。
4. 扼杀估计的完美主义者
一名工程师将每个故事变成一个技术债务任务。 3 点的工单变成了 13 点的重构,因为“我注意到这个文件需要修改。”冲刺出现失误;最初的功能发布较晚。
解决办法:在专题故事中重构就可以了 如果它不会改变估计超过 30%。除此之外,这是一个独立的故事,团队应该可以看到。
5.实际上不是债务的债务;这是“我会以不同的方式写这个”
令人惊讶的是,“技术债务”的很大一部分是一个工程师的偏好与另一个工程师的选择。 ORM 与原始 SQL。基于类的 React 与 hooks。 YAML 中的制表符与空格。这些都不是 债务 从成本意义上来说;它们是风格上的分歧。
解决办法:如果一段代码降低了团队速率,那么它只是债务 现在 或将复合。个人喜好不符合条件。团队的 linter 和架构决策处理风格分歧;不要将它们视为债务。
冲刺应该有多少是债务工作?
诚实的范围:10-25%,取决于代码库的年龄和团队最近的运输压力。
- 5%: 团队处于主动救火模式(事件响应、紧急功能压力)。债务不断积累,但团队有意识地接受它。
- 10-15%: 具有活跃产品开发的 2-3 年代码库的健康稳定状态。
- 20-25%: 维护模式,或者团队继承并正在控制的代码库。
- >30%: 出问题了。要么代码库太糟糕,以至于不可能进行纯功能工作,要么团队正在使用“技术债务”作为不交付的掩护。
向利益相关者传达百分比。 “我们将每次冲刺的 15% 都花在基础设施上,从现在起六个月后我们就会感觉到”是一个有道理的答案。 “我们正在根据需要偿还债务”则不然。
债务管理不善的信号
需要注意的三种模式:
“我们需要科技债务冲刺”
说是一年一次,执行的次数可能有一半。如果团队需要技术债务冲刺,债务就会被允许积累,远远超过其应偿还的水平。修复分配,但不修复症状。
“如果不先进行重构,我们就无法发布此功能”
翻译为:我们需要接触的文件已经被搁置太久了,以至于无法安全地接受新的工作。这是未被捕获的复利债务。现在你支付重构费用 和 该功能,系列。
“我们的重构变成了另一个重构”
“我会很快清理掉这个”的模式会像滚雪球一样发展成为期 5 天的重构探险。要么积极地设定时间范围(一天,然后提交或恢复),要么将其转换为具有真实估计的真实故事。
当技术债务不是问题时
一个常见的困惑:将实际问题归咎于科技债务 过程 或 工装.
- CI 慢?这可能是技术债务(测试膨胀),也可能是工具(CI 运行程序大小错误,并行性未设置)。重构之前进行诊断。
- 冲刺中期范围蔓延?不是债务。那是 范围变更处理.
- 速率下降了?可能是债务,可能是人员变化,可能是故事点的重新调整。 (参见 速率下降;该怎么办.)
- “代码很难理解”?可能是债务、可能缺少文档、可能缺少入职培训。
付款前先诊断。科技债务是最方便的答案;这通常不是正确的。
底线
科技债务并不是永久性的道德败坏;这是一个应该产生冲刺级决策的计算。
排序:复利(积极支付)、静态(机会主义)、冻结(不去管)。进行粗略计算:支付成本 vs 减速 × 代码消失所需的时间。将每个冲刺的 10-25% 连续分配给债务,而不是分批冲刺。
没有目标就不要重构。不要重写整个事情。不要将个人偏好视为债务。不要让完美主义者吃掉冲刺。
做对这一点的团队能够多年保持稳定交付。做不到的团队则在赶功能和补技术债之间反复摇摆,始终难以取得进展。
选择一个号码。捍卫它。每季度调整一次。