一个童话故事
很久以前,有个软件开发团队找到他们的经理。“我们的项目有相当多的技术债务(Technical Debt),我们应该做点什么。”这个团队说。他们展示了一张图(图 1)来说明项目的技术债务。“技术债务关系到项目质量。”他们说。并展示了技术债务各部分的分解,通过静态代码分析,能发现过于复杂的代码、重复的代码和冲突。“我们需要去除技术债务”他们告诉经理。
(点击查看大图)
图 1:SonarQube 技术债务插件的结果报告
但经理困惑了:什么是技术债务?他该额外再增加 $500.000 的预算?为了什么?这关系到质量,但他不知道现在有什么缺陷。客户一直很满意,这些开发人员在说什么?因此经理和团队进行了长时间的讨论。
事实上,技术债务就是在这类讨论中引入的一个比喻。意思是低质量的代码就像是财务负担。债务的总额就是从代码库中把它们清除出去所需要付出的努力。利率是由于低质量代码造成的生产率下降。管理人员通常熟悉财务领域的术语,所以在谈论软件质量时使用这个比喻会更加容易沟通。
这个故事说明,技术债务这个比喻失败了。本以为在和经理沟通技术质量时,它能起到帮助作用,并最终产生回报。然而,现实并不那么容易。这是因为有技术债务并不意味着它必须要偿还。技术债务甚至不一定是件坏事,有时它只是为了按时上市或者达成项目其它目标而妥协的结果。甚至有时候这种情况无法避免,例如类库采用新技术或者进行了升级,导致原来没问题的代码现在产生了技术债务。关于技术债务该如何偿还,并没有简单的定律。事实上,偿还技术债务有多种方式,或者有时候你甚至可以将它作为你的优势 [1]。
技术债务的问题
技术债务的主要问题是它只代表了系统的内部质量。而质量有哪些影响并不明确。特别是,技术债务的经济影响无法简单地用这个比喻来表示。技术债务还很奇怪。如果这些代码不需要修改,那技术债务就完全没关系。但是,一旦要修改这些代码,技术债务就成为代码的所有重要属性。所以,技术债务很可能对项目的成功、外部可见的质量完全没有影响。
但是如果你忽视项目的技术债务,很可能它就在某个地方等着你:如果你需要修改有大量技术债务的代码,成本可能非常高,最终无法执行。开发人员通常知道和害怕这类情形,维护有大量技术债务的代码不仅仅是少了乐趣,而是风险太高,因为 bug 很可能潜入,而评估很容易就被证明是错的。
因此,软件质量可能对软件项目的成功非常重要,而技术债务的比喻是不够的。它能用来表示软件质量,让人们了解软件质量怎么样,但如何处理软件质量才是真正重要的。
质量投资:修复代码的新比喻
也许换一个比喻更能说明对于软件系统的质量,我们该做什么。技术债务只反映修复质量问题所需支付的成本,上述例子中是 $500.000。这个数字本身没有什么用处,它无法说明我们该如何处理这个问题,或者它们如何影响了系统的开发。也许我们该偿还技术债务,也许它完全没有影响。
所以一个更好的比喻也许是质量投资(Quality Investment)。使用质量投资,处理技术债务就有可能获得利润。这样就可以使用财务术语来积极地管理代码质量,可以很容易决定哪些质量问题应该解决,哪些可以暂时接受。
质量投资的想法来源于 SQALE。SQALE 方法 [2][3] 是一种质量模型,它定义了两种类型的成本:修复成本(remediation costs,RC)和非修复成本(non-remediation costs,NRC)。修复成本是指在你的代码库中清除某个质量问题的付出。事实上,修复成本可看作是技术债务。第二类成本更有趣。当这个质量问题未解决时,就会产生非修复成本。例如,开发某个新功能可能需要更长时间。这个额外的付出就是非修复成本。在这个上下文中,非修复成本看起来像是技术债务的利润。但事实上,这些成本应该给予更多考虑,例如不清理低质量代码的额外风险因素。利用 SQALE,你可以选择维持当前的低质量状态,支付非修复成本,或者去提高质量,支付修复成本。
该质量模型的前期实现,例如 SonarQube 的 SQALE 插件,只支持修复成本。然而,这两种成本类型是经济合理地处理质量的关键,也是这个质量模型的创新性所在。
如果你正在提高质量,解决质量问题,那么支付的是修复成本。当问题清除后,团队避免了相应的非修复成本。因此,只有当非修复成本大于修复成本时,才值得去提高质量。因为只有这样,质量投资才能产生利润。这可以简单地用下面的利润公式来描述:
利润 = 非修复成本 – 修复成本
这个公式给出了一个合理的、符合经济原则的指导方针,用于判断质量问题是否需要修复,以及何时修复。
正如之前提到的,代码的质量只有当它将来需要修改时,才会变得重要,因为只有这时候团队才会因为低质量代码而慢下来。所以,正确的投资分析必须评估代码将来修改的可能性,以及这两种类型的成本。因此,质量投资的比喻可以通过三个评估来实现。让我们看一个例子以便更深入地了解这一点。
假设我们有一个系统,它包括三个模块:客户、订单和发票。客户管理是个非常老的模块,已经不再开发了。因此,这个模块不适于质量投资:仅当代码被修改时才会产生修复成本,在这个例子中,修改的可能性为 0%。因此支付任何修复成本都将导致损失。
然而,我们知道在接下来的迭代中,对订单流程进行了大量修改。根据经验,发票管理也必须进行一些修改。像这样的粗略估计通常是足够的,而且也很容易达成一致。经验告诉我们,详细的评估绝大多数都是假装精确。
接下来的步骤是要评估订单模块和发票模块的质量问题。订单管理的测试覆盖率非常低,客户管理的代码非常复杂,也就是说方法和类有大量的代码,并且有很多复杂的循环。面对众多选择,我们评估了修复成本和非修复成本。非修复成本的评估也应考虑模块修改的可能性。在下一个迭代中,如果代码质量相同,订单管理估计要投入 20 天。而如果测试覆盖率能提高,那么估计只需投入 13 天。因此,非修复成本是 7 天。这非常高,因为质量确实太差了,同时也因为有大量代码要修改。
- 订单管理:很低的测试覆盖率,修复成本:5 天,非修复成本:7 天
- 发票模块:很高的复杂度,修复成本:5 天,非修复成本:4 天
这些评估表明订单模块的质量投资产生了 2 天(=7 天 -5 天)的利润。相反,发票模块的质量投资没有利润,甚至亏损。由此可见,投资于当前的订单系统是有价值的,因为根据团队的评估,提高测试覆盖率就有利润。对于发票模块,情况并不明确。就现在来说,其质量投资没有利润。然而,发票模块很可能在未来的几个迭代中也需要修改。这样你就能从发票模块的质量投资中得到利润。
根据给出的评估和计算出的利润,也可以为每个质量投资推算出投资回报(RoI)。投资回报表示相应的成本产生了多少利润。因此,投资回报率等于利润除以修复成本。订单模块质量投资的投资回报率大约是 40%(=2 天 /5 天)。经理们通常会寻找机会去获取比较高的投资回报率。使用这些财务术语,你能与他们沟通代码质量提高后的收益。当然,就像软件开发的几乎所有其它事情一样,这些数字都是估计值。然而,这显示了团队不只是基于自己的原因寻求提高质量,更重要的是由于经济原因。
质量投资这个比喻,让你能与管理者进行各种类型的讨论。除了技术债务的成本,你还可以与经理讨论节省时间和投入。对于经理来说,这意味着双赢局面:节省了预算,开发人员也很高兴,因为他们可以提高代码质量。同样,管理者可以考虑投资是否合适,或者一个快速但质量较低的解决方案也是足够的,甚至是必要的,因为要按时上市。
最后,质量投资是比较了众多评估的结果:什么是最经济的决定?但是,它回避了一个问题,为什么在软件开发中,很少使用投资这种想法。
另一个童话
现在,让我们再重头开始讲这个童话故事,这次采用质量投资的方式。
很久以前,有个软件开发团队找到他们的经理。“我们相信,已经找到一种方法来提升我们的开发。”他们说,“我们进行了一些评估,刚开始时,下一个迭代中订单处理要做的修改非常多。然后,如果我们投资 5 天用于提高订单管理的测试覆盖率,我们估计下一个迭代能节省 7 天,所以我们可以少投入 2 天。”“太棒了!”经理说,“就这样做!这是有价值的投入。”所以这个团队最终提高了大家都关心的模块质量。“另外还有一个,”这个团队说,“如果我们另外再投资 5 天用来提高发票系统的质量,我们觉得我们将能节省 4 天。虽然这有点损失,但我们相信在下一个迭代中我们就能赚回来。”经理回答:“好的,但是,在这个迭代中,我们真的要推出尽可能多的功能,这额外的两天非常有用。客户现在不是很高兴,我要让他们看到印象深刻的东西。否则可能就没有下次迭代了……”所以他们得以继续提高订单管理的代码质量,并开心地过着日子。
这说明了质量投资作为一个比喻为什么这么有效:可以用它来评估涉及质量的每个决定的利润。也很容易判断出某些质量投资可能比其它方面的关注,例如实现新功能,具有更高的优先级。如果没有这个比喻,所有这些都是不可能的。当然,只有当团队有质量很高的评估历史数据时这些才有可能。但即使没有,使用这些术语去思考也有利于得出更经济的决定。
CodeQ Invest
CodeQ Invest[4] 是一款支持质量投资的工具。它是一个 Web 应用,能够自动计算包括单个类到根包在内的各级别的代码修改概率。源代码控制系统用于计算合适的数字。它基于这样一种思想,过去经常修改的代码,将来它们修改的可能性要比那些过去没修改过的代码更大。你可以定义这个计算要涵盖的天数。这些天内的每次提交都将用于计算,计算每个修改的文件,代码修改的比例。此外,你能选择不同的方法来计算过去代码发生的变化:
- 过去 n 天内,所有提交的权重相同
- 过去 n 天内,所有提交的权重逐渐增加(越新的修改,权重越高)
- 过去 n 次提交的权重相同
除了自动计算修改概率,你也可以人工评估某些代码的修改可能性。对于进入维护阶段的系统,也就是说不会增加新功能的系统,概率计算是非常有用的。它同时也给出了代码库中的有趣视角和代码中可能的热点。人工评估非常适合于开发新功能时,特别是一些过去未触及的代码必须要修改的时候。
CodeQ Invest 还有很多其它功能。它计算你代码库中的最佳投资。你提供预算,也就是投资系统质量的小时数,CodeQ Invest 将建议哪些地方的代码应该提升。团队应该为它进行质量配置。该配置从该团队的视角来定义代码质量。不同的团队可能会给出不同的配置。配置包括许多质量需求。每个需求描述代码质量的一个度量角度。CodeQ Invest 当前允许你对不同的指标设置阀值。可以使用 SonarQube(前身是 Sonar)[5] 对它们进行度量。SonarQube 是非常流行的代码质量管理工具,用于分析代码得到指标值。此外,对每一个质量需求,团队必须评估修复成本和非修复成本。团队只在低层级评估成本,例如,将 100 行代码的测试覆盖率提高 10% 需要花费多长时间?基于该测试覆盖率的代码,实现功能有什么影响?该工具为你提供更高层次的抽象与合计。示例如图 2。
CodeQ Invest 只是一个工具,用于进一步帮助你使用新比喻和新方法来处理代码质量。基础部分是各种评估,它可以以传统的方式完成,即评估故事、质量提升以及在特定故事上进行质量投资的影响。或者你可以使用 CodeQ Invest 并进行设置,正如你所看到的,设置某些质量特性影响的数量。CodeQ Invest 将使用这些基本数据,得到投资计划的相关建议。
(点击查看大图)
图2:CodeQ Invest 的项目视图。显示了一个可缩放的树对应你代码库中的投资机会。在右侧你能看到几种预算的投资回报分布(例如16 或者32 小时)和一个基于给定投资生成的质量投资计划。
总结
尽管技术债务能够帮助沟通软件系统的质量,但对于如何处理这个质量,它并没有太大帮助。这将导致很多问题,例如低质量的代码得不到维护。到最后,主要的关注点是应该提升系统哪一部分的质量,哪些地方是没问题的,不需要修改。而这就是质量投资能够帮助的:每一项提升都根据经济产出来判断,也就是说它是否比保持代码不变更便宜,还是应该投资于质量?这给了一个很好的指导方针,哪些代码的质量应该提升,什么情况下代码质量应优先于实现新功能。
链接
[2] SQALE
[3] Technical Debt Evaluation (SQALE) Installation and Usage
[4] CodeQ Invest
[5] SonarQube
关于作者
Eberhard Wolff是一位自由架构师和顾问。他也是 ADESSO AG 公司的技术顾问委员会负责人。他的兴趣包括敏捷技术和现代软件架构。
Felix Müller作为一名 IT 顾问工作在柏林的 codecentric AG 公司。他是一名富有激情的软件开发人员。他在开发领域的主要兴趣包括 Web 开发、自动化测试、持续交付、代码质量管理和敏捷实践。
评论