代码质量与规范,那些年你欠下的技术债 (2)

从左向右的方向是把代码质量不断细化分解为更小的单元,直到最小粒度可以直接度量的属性;从右向左的方向是把度量值逐步汇总到根节点,最终得到一个总的代码质量的度量值。表1是SQALE质量模型分解的示例。表中第一列把代码质量细分为可维护性、可测性、可变更性和可靠性几个维度,对于每个维度又有进一步的细节,如可测性又细分为单元测试可测性和集成级可测性这样的子特征,进一步的,子特征还能细化到可直接度量的属性,或者称为要求(表中第三列,即我们通常说的代码扫描规则),例如单元测试可测性再细分为“模块测试路径数量<11”和“模块调用参数数量<6”这样的规则:

表1 SQALE质量模型示例(Java语言,节选)

注:我们使用的SonarQube并没有完全照般SQALE的质量模型,在5.4及之前的版本中还存在与SQALE类似的可测性、易变更性、可理解性和可读性等维度,整个模型只有两级,即第一列和第二列合并了,例如可测性维度下直接对应了“表达式不应该太复杂”,“方法不应该太复杂”,“方法不应该有太多参数”等规则。在5.4之后的版本,即目前使用的版本则进一步简化,代码质量对应的扫描规则直接归属于“坏味道”大类,具体的规则可以打上多种标签来归类,分类和配置更加灵活。

代码质量的度量

那么,这些规则应该怎么量化呢?或者说,如何度量代码违背规则的程度,而且这种度量是可以加总的,毕竟规则间差异很大,上文也解释过,直接按数量汇总肯定是不合理的。

怎么办呢?SQALE方法的分析模型解决了这个问题,由此我们也引出了本文中的第二个重要概念:技术债TechnicalDebts。

“技术债”这一概念最早出现在1992年,其本义是指,开发人员为了加速软件开发,在应该采用最佳方案时进行了妥协,改用了短期内能加速软件开发的方案,从而在未来给自己带来的额外开发负担。这个定义暗示了这种“负债”是一种刻意的、理性的经过权衡的行为,后文中我们进一步探讨技术债务的类型时会指出这一定义仅仅代表了技术债中相对良性的一类,是一个比较“温和”的定义。此处我们关注的重点是使用技术债这一隐喻来帮助大家理解度量代码质量的方法。

既然谈的是“债”,自然就应该和钱有关了。因此,技术债的“本金”就定义为修复代码质量问题所需消耗人力资源估值,例如,针对java语言,修复一个圈复杂度为15的方法需要一个开发人员15分钟的时间(以sonar java分析器缺省设置为例),这个值就是负债的本金。代码扫描工具中对应代码质量的每条扫描规则都对应着一个债务计算方法,有的规则是设定了固定的债务值,有的则根据违规程度有相应的计算公式。引入技术债的概念后,SQALE方法就可以把不同规则对应的代码质量度量统一为人力资源的消耗这一单一指标上。

根据图2质量模型所示由右向左的方向逐级汇总,就可以得到待评价软件的代码质量度量值。我们的其中一个度量难题:如何客观评价代码的质量,由此就得到了解答。

技术债的利息

关于技术债另外还有一个概念值得在这儿强调一下,即负债的利息。我们知道,通常借钱是有利息的,有的负债利息很低(如安居计划利息为0),有的利息较高(如信用卡欠款),有的则高到令人绝望(如高利贷)。同样,技术债也是有利息的,存在利滚利的情况,有的违规项马上修复要10分钟,如果放着不管一段时间后,也许就需要20分钟甚至更多的时间来修复(由于代码细节的知识随时间流逝,以及破窗效应造成代码问题加速恶化等原因)。有的代码扫描工具会针对规则定义本金和利息的计算方法,如Coder Gears的CppDepend,我们目前使用的SonarQube平台上的代码扫描插件不支持计算利息,因此本文就不过多讨论,大家只需要记住,因为利息的存在,技术债务不及时偿还的话,会在未来呈现出非线性增长,造成始料不及的损失。后续文章在讨论技术债的危害时,我们还会时常提及技术债的非线性特征。

不同类型代码的比较

现在我们还剩下一个度量问题:如何知道两段代码的质量差异?现在有了技术债本金这个绝对值,但是不同规模,不同类型的代码应该如何比较呢?SQALE方法中继续借鉴了“负债率”这个术语,计算公式为:偿还债务所需耗费的资源(即本金)除以重写所有代码的预估耗费的资源。在扫描工具的实现中,分母是通过代码量和开发生产力水平计算得出,其中的生产力是一个配置项,如SonarQube上可以配置编写一行代码的平均估计耗时。SQALE进一步使用了术语“债务等级”,定义了从A(非常好)到E(非常差)五个等级,根据负债率数值所在区间对应不同的等级,例如SonarQube中缺省[0, 5%]是A,(5%, 10%]是B,(10%,20%]是C,(20%, 50%]是D,高于50%是E。当负债率达到100%时,即债务开始超过资产,资不抵债,这时就称这种情况为“技术破产”。当然,日常工作中碰到这种情况时,我们不会用这么吓人的术语,通常是打着“重构”的旗号重写一遍。

下图是CppDepend的一个扫描汇总结果的示例,包含了我们讨论的所有概念(使用CppDepend为例是为了展示更全面的信息)。

图3技术债度量示例(CppDepend)

上图中工具扫描的代码行数为19862行,共负债32天,债务的年息是9天2小时,负债率是6.39%,债务等级是B级。

我们日常工作使用的工具平台是SonarQube,如下图所示:

图4技术债度量示例(SonarQube)

图中的项目负债12天,共有923个坏味道(即违规项数量),负债率(图中翻译为“技术债务比率”)为6.3%,债务等级(图中为SQALE评级)为B级。

SQALE给我们提供一套有效合理衡量代码质量的方法和工具,下图中SQALE方法流程清晰的展示了整个方法流程各个环节:

图5 SQALE方法流程

图片来源:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/zypgwf.html