在DevOps组织中,利用速度的测量来确定技术债务的成本和位置。

技术债务漫画

技术债务的定义是(通过维基百科):。

由于现在选择一个简单的解决方案,而不是使用一个更好的方法,需要更长的时间,所造成的额外返工的隐含成本。

许多人认为这种债务只存在于代码中,并测量循环复杂度、测试覆盖率和/或使用linter来确定有多少债务和它的位置。这些测量方法很有效--但它们并没有突出哪种类型的债务(复杂性、缺乏测试、不良文档)导致了最大的问题或最好从哪里开始。它们也无法衡量那些更难发现的债务类型,例如:。

特征重复
整个构件是重复的,但写法不同,也许是用不同的语言写的。
没有为调试而设计
由于日志记录、错误报告或发布/调试的差异,当故障发生在距离较远的地方(客户现场、QA部门)时,调试就很难进行。
缓慢的测试套件
测试套件需要很长的时间来运行,导致更高的周转率
...

这些可能会把技术性债务的标准定义拉开一些,所以在Cristie,我们使用以下定义。

由于现在选择一个简单的解决方案,而不是使用一个更好的方法,需要更长的时间,所造成的额外工作成本。

这给了我们一个方法来解决这个问题。

  1. 如果我们假设更频繁地投入是可取的
    (......而且我们说它是,并鼓励人们这样做......)
  2. 那么,更频繁地投入的唯一原因是你不能
  3. 而你不能的原因我们定义为我们的技术债务,因为
  4. 如果我们重构、架构或以其他方式改进,那么你可以。

因此,我们可以将技术债务衡量为

超过α标准差的提交所花费的时间,其中包含一个与平时相差α标准差的文件。

也就是说,在包含文件的提交上的额外时间,这些文件通常会出现在需要更长时间的提交中。

弄清我们的提交历史

我们写了一个处理subversion和git日志的python脚本,以产生。

  • 提交者
  • 自作者上次在任何repo 中提交以来的业务时间
  • 该提交中的文件

然后,我们对每个作者的这些提交进行标准化处理,并制作了这个图表。

归一化的承诺历史

一些试验和错误(有 绝对 一个更好的方法......但统计知识是在实验过程中学习的)确定,应用 lambda x: np.log(x) ** 2 作为两个并行的Box-Cox变换,给出了一个 非常 接近正态分布,并将此反向应用,得到上图中的最佳拟合线。

转化的提交历史

技术债务

我们现在可以通过计算每个文件的z分数(衡量一个应该正常的群体有多 "正常")来计算出技术债务,即。

def zscore(commits):
"Z-Score per-file for all files in commits"
perpath = collections.defaultdict(list)
for commit in commits:
for path in commit.paths:
perpath[path].append(commit.interval)
return {path : sum(perpath[path]) / math.sqrt(len(perpath[path]))}

这意味着我们可以计算出我们在技术债务上支付的利息。

def debt(commits, deviation=1):
"Total debt for this series of commits"
scores = zscore(commits)
debt = 0
for commit in commits:
for path in commits.paths:
if scores[path] > deviation:
debt += commit.duration -
inverse(0) -
inverse(deviation)
return debt * 100.0 / sum(c.duration for c in commits)

通过使用滑动窗口,我们可以制作一个技术债务随时间变化的图表,并找到造成这种情况的文件。下图显示了与正常情况相比,高关注度文件的提交量。

相比之下,高利息文件

结果

2018年的技术债务在20%左右徘徊。

债务的利息,2018年

更重要的是,这给我们提供了一个我们需要处理的文件清单(为保护无辜而编辑)。

Z-Score承诺的百分比乘法器文件名
3.181.66 %2.44x设备的Docker文件
0.824.76 %2.14x新产品的许可代码
2.940.98 %2.15x远程Python REST API

在上述案例中,我们整个团队一致认为这些都是有问题的领域。我们的诊断依次是

设备的Docker文件
设备的尺寸太大,所以通过网络从Nexus拉下所需的包,导致往返的延迟很大。
为了解决这个问题,我们大大减少了设备的尺寸。
新产品的许可代码
这个代码是买来的,有很多问题。

  • 32/64位和大/小恩典架构的不匹配的编译方式
  • 重复的,但名称相似的函数,例如:。 CheckLicence vs check_licence
  • 误导性的名称(dsUint32_t 实际上只是一个 int...)

我们对这段代码进行了广泛的重构,并增加了测试来清除这个问题。

远程Python REST API
这有很高的循环复杂性和很低的文档,但最重要的是,有很高的集成测试周转时间。
通过删除每个测试所执行的集成测试中的一些阶段,这一点得到了改善。

反对这种方法的论点

那些不在版本控制中的东西怎么办?

首先......所有的东西都应该在版本控制中!简单。当然,总有一些行动不是这样的--但这些行动要么是独立于当前的提交(所以在大量采取时不会影响数据),要么是影响当前的提交--在这种情况下,它们是技术债务

如果有些人比其他人更谨慎呢?

我们在每个作者的基础上进行规范化,试图使我们的数据关于代码和过程,而不是个人。

只有当代码当前处于活动状态时才支付利息,所以利息的下降可能只是意味着当前活动的代码更好。

这是非常正确的......而且绝对是一个问题。高技术债务的不活跃的代码代表着风险,但实际上并没有阻碍事情的发展......还没有。这就是二级指标(复杂性、覆盖率和提示性)可以帮助的地方--特别是如果可以找到与债务的经验性衡量标准的联系。...关注这个空间。