利率增加了 157%!
不,我不是在谈论美联储的最新决定,而是在谈论 Fictional Inc. 在发布其平台 3.0 版后面临的放缓。
对他们来说幸运的是,产品发布非常成功,他们开始看到收入增长迅速回升,但他们现在需要考虑如何处理作为发布一部分引入的技术债务。
作为新版本的一部分引入的新技术债务可以被认为是提高利率并增加团队未来面临的放缓。
(我假设你在这里相当熟悉技术债务的概念,但如果你需要复习以加快速度,这里有一个快速
好的,您可能不会听到工程经理这样谈论他们的技术债务。
但为什么不呢?
能够衡量和量化您的持续影响
当我们考虑技术债务时,利息是当前和未来开发到现有技术债务水平所损失的时间量。
这意味着在考虑未来偿还本金的决定(重写、重构或修复负责债务的代码的成本)时,这是要考虑的债务的关键部分——因为我们只会在利息时考虑它足够高。
技术债务显然会减缓新的发展——但这本身并不意味着我们应该在所有地方修复技术债务。回过头来重写或重构现有代码的成本可能会很高——因此,如果我们的利息(它减慢我们前进的速度)超过我们的本金,我们只想偿还我们的技术债务本金。
一个明显的问题立即出现——本金是一个固定的时间单位(修复/重写的小时数),但利息是每次损失的小时数。为了解决这个问题,我们需要引入影响间隔的概念——我们关心未来技术债务放缓是否超过重写成本的时间。您关心的影响间隔在很大程度上取决于您的公司、您的典型规划流程及其阶段或代码库的生命周期——但我个人通常会查看 3 个月的影响间隔。在我们作为一家公司的早期阶段,以一年以上的时间尺度来看待任何事情都过于宽泛,但任何短于 2-3 个月的事情都会严重低估技术债务的影响,我们稍后会看到。
这意味着如果出现以下情况,我们的技术债务水平不值得解决:
例如:如果我们有一个小项目,我们知道有技术债务让我们每周放慢 2 小时的速度,那么我们需要 4 天的时间来重构这些债务,并关心 3 个月的影响间隔,那么我们就不会花时间还清那笔债呢。
现在,这实际上并没有回答什么是健康的技术债务水平——因为我认为我们都同意,面对团队的巨大放缓是不健康的。相反,我们现在有一种快速的方法来确定我们何时应该或不应该专注于重写和重构。我们将在本文稍后部分探讨健康的债务水平到底是多少。
确定我们的技术债务利率需要我们弄清楚我们做出的不同决定在多大程度上减慢了我们的速度。不幸的是,没有一种明显或简单的方法来跟踪您的开发速度减慢了多少——但是您可以采用三种不同的方法来获得良好的近似值。
将减速与我们代码库的不同部分联系起来的最直接方法是查看速度在项目的不同部分如何随时间变化。查看代码库的不同区域,您可以开始识别不同部分的变化(例如,涉及此分析部分的任何开发所需的时间是其他任何部分的 3 倍)。查看一个区域随时间的变化也可以让您了解新开发如何影响未来开发的速度,并表明您的团队正在处理的兴趣水平。
例如:如果我们有一个相对简单的项目,包含 4 个不同的领域,那么我们可以查看速度如何随时间变化(这里我们以故事点数/开发人员月为单位跟踪速度)。
基于速度的技术债务影响测量
从这里我们可以看到,对于类似复杂的任务,D 总是花费大约 3 倍于其他任何区域的工作时间。这意味着它的兴趣是代码库所有其他部分的 3 倍。 B 过去与 A 和 C 相当,但从第 4 个月开始,它突然猛增,花费了 2 倍的时间。这表明我们在这里引入了一些债务,与 B 之前的利率相比,我们的利率翻了一番。
需要指出的一件事是,我们不是在谈论整个代码库的利率,而是单个组件/区域的利率——这是因为技术债务积累带来的放缓通常不是问题影响我们所做的一切,而不是将它们本地化到代码库的一部分。
当涉及到基于速度的测量时,有一些重要的注意事项需要考虑。
速度可能是不稳定的,并且可能取决于独立于技术债务的因素,例如新的(或现有的)错误、不一致的估计、技术障碍或外部项目延迟。
估计具有一定程度的内在不确定性,一开始可能是不准确的衡量标准。
收集和分析这些数据可能非常棘手且耗时。
基于速度的测量的一个快速代理是要求您的工程团队估计在代码库的每个主要区域完成项目/任务所需的相对时间。就一个广为人知/经常使用的区域达成一些既定基线,然后让每个人都估计其他区域作为该基线的百分比或倍数。虽然不像基于速度的完整衡量方法那么严格,但它可以根据您团队的洞察力和直觉快速了解您的相关技术债务利息。
一种不同的方法是确定项目中技术债务的具体实例,并估计它们各自可能使您放慢速度的程度。其中一部分可以通过使用自动化工具(例如静态分析工具)来完成,以查找可能影响项目的可读性、可扩展性或可维护性的代码质量方面的常见问题。对于每种类型的问题,您可以根据您的团队处理这些类型问题的经验为其分配利息成本(例如 5 分钟/周或 1%)。
但是,这只会捕获导致问题的技术债务的一个子集——其他问题会更微妙或更适合您的代码库,并且只会在您的团队积极处理该代码区域时才会被观察到。在这种情况下,您需要记录具体问题(与代码库的某个区域相关联)以及它对减慢开发速度的估计影响。为了跟踪这些问题,我们建议使用某种问题跟踪器——在 GitHub、Jira 等的问题积压中,或者使用专门构建的技术债务问题跟踪器,例如
这种方法的一些缺点是:
您可以使用多种代码质量指标来全面了解代码库的状态,进而估算您当前有多少技术债务会影响每个领域的未来发展。在 Sourcery,我们倾向于关注
与基于问题的方法类似,您可以将不同分数(或总体质量或健康分数)的相对影响分配给由于技术债务而导致的持续和未来的开发放缓。研究表明,复杂性和速度以及代码质量和错误风险、维护负载等之间存在很强的负相关关系。
看一个例子——让我们重新审视我们在基于速度的测量部分中看到的简单的 4 部分代码库。
我们可以很容易地在表中看到这个项目的问题部分(以红色突出显示)并且计算兴趣估计相对简单 - 简单地总结不同组件的兴趣影响。
这种方法的一些缺点是:
这种基于质量的衡量方法是我们研究过的三种方法中最不精确的一种——但它在随着时间的推移对项目的不同领域进行整体观察方面非常有效。您可以将此方法与我们刚刚讨论的基于问题的方法结合起来,以平衡跟踪特定问题与跟踪项目每个部分的一般质量和健康问题。
对于所有这三种方法,我们需要一种方法来映射我们代码库不同部分的影响与我们实际接触代码库该部分的频率。如果我们项目的某个部分是一场噩梦,但没有人再触及它,那么它实际上并没有严重影响我们持续的技术债务。相反,每天贡献的代码库的一部分小而持续的减速可能会很快导致大量时间损失。
为了说明这一点,我们需要查看我们项目的每个区域的贡献频率。我们可以在这里采取几种不同的方法——查看 Git 历史以了解哪个区域最常被广泛接触,使用更有针对性的工具,如
无论我们如何获取数据——我们都可以得到我们将花费多少时间与代码库的每个部分进行交互的细目分类。将其与我们已经确定的兴趣贡献相结合,我们现在可以准确地看到在处理我们的代码库的每个部分时我们期望减慢多少。
继续我们之前的示例 - 如果我们采取前瞻性的观点并重新审视我们将在接下来的 3 个月内开展的所有项目,并且可以自信地估计这是在代码库的每个区域花费的时间量(请记住这是一个非常简单的项目):
现在,我们可以将其与我们从基于速度的方法和基于质量指标的方法中获得的兴趣估计联系起来,并清楚地了解我们在哪里最慢。
在这里,我们使用我们在 B 和 C 部分的工作中看到的速度放缓,这是我们之前查看基于速度的测量时所看到的,并使用它来计算我们预计在接下来的三个月中由于技术债务而损失多少时间。总的来说,我们希望看到超过 28 个开发者月的额外努力将花费在债务上。从这种方法中需要考虑的一件重要事情是,所有这些都在考虑相对速度——因此基准项目被视为实际上没有债务,这通常是不准确的。这种方法的另一个问题是它没有考虑未来债务水平的变化——这很可能会发生。但它们很难预测,因此更容易忽略它们。
在这里,我们根据每个部分的代码质量采用了典型的预计延迟,并将其预计在接下来的三个月内进行。总体而言,我们发现债务影响预测明显低于我们使用速度方法时的预测。这是因为基于技术债务的放缓估计低于我们在速度情况下看到的——在这种情况下我们可能需要做更多的校准!就像基于速度的指标一样,这没有考虑技术债务的未来变化——但是这两种估计都可以帮助我们确定我们应该如何优先重写和重构项目的不同部分。
我们已经研究了几种不同的方法来说明我们的技术债务对我们的持续影响有多大,但我们还没有完全回答什么是健康的技术债务水平的问题。
不幸的是,并没有真正准确的数字。在短期内,接受一些债务可能是务实的。但是,从长远来看,我们希望将我们的债务保持在接近于零的水平。因为挥之不去的兴趣将被证明是非常昂贵的,而且技术债务会不断累积。但是,我们不想把所有的时间都花在重构和重写那些只给我们带来边际收益的问题上。
正如我们之前讨论的那样,我们通常不想花时间处理代码库中的以下区域:
但是,在这种极端情况下,我们可能会遇到这样一种情况,即我们因修复成本极高的高额债务而大幅放缓——这也不是一个好情况。
最好的方法是落在中间的某个地方。在您正在进行的计划中留出时间来解决技术债务问题并重构现有代码——按当前对您来说成本最高的优先级排序。并继续推动它,直到你看到减少债务的回报显着递减。