软件质量成本
Post

软件质量成本

翻译自软件领域神级人物 Martin Fowler 的文章。

软件开发项目中,一个常见的争论是花时间提高软件质量还是专注于发布更有价值的功能。通常,功能的交付压力会主导着讨论,导致许多开发人员抱怨他们没有时间提升架构和代码质量。

“Betteridge’s Law of headlines” 这句谚语说的是任何以问号结尾的文章标题都可以用“否”来概括。了解我的人知道我会颠覆这样的规律,但是这篇文章讨论的更进一步:它颠覆了问题本身。这个问题假定了质量和成本之间的权衡,通过本文,我将解释这种权衡并不适用于软件,高质量的软件实际上更便宜。

虽然我大部分的文章都是针对专业软件开发人员的,但本文并不需要具有软件开发的专业知识。我希望这篇文章对任何关注软件工作的人都有价值,特别是那些软件开发团队客户的商业领袖。

我们习惯于在质量和成本之间进行权衡

正如本文开篇所讲,我们习惯于在质量和成本之间进行权衡。当我更换智能手机时,可以选处理器更快、屏幕更好、内存更大但同时也更昂贵的机型,或者可以放弃一些质量换取更低的价格。但事无绝对,有时候我们也可以花更少的钱买到高质量的东西。我们往往对质量有不同的定义:有些人并没有真正注意到一个屏幕比另一个更好。但多数情况下,更高的质量意味着更多的花费。

软件质量意味着很多事

在谈软件质量之前,需要先来解释下什么是软件质量。这是第一个复杂问题,很多东西可以算作软件的质量。从用户界面的角度来看:它是否能便捷的引导我完成某项任务,使我更有效率且不会遇到阻碍?从可靠性的角度来看:它是否包含导致错误和崩溃的缺陷?从架构的角度来看:源代码是否分为明确的模块,以便程序员可以轻松找到并理解本周需要处理的代码?

这三个质量的例子并不是一个详尽的列表,但它们足以说明一个重要的观点。如果我是软件的客户或用户,我并不理解我们称之为“质量”的一些东西。用户可以判断用户界面是否良好,一位管理者可以判断该软件是否使他的员工工作更有效率,用户和客户会注意到缺陷,特别是损坏数据或使系统暂时无法运行的缺陷,但是客户和用户无法理解软件的架构。

因此,我将软件质量属性划分为外部(例如 UI 和缺陷)和内部(架构)。区别在于,用户和客户可以理解什么样的软件产品具有高外部质量,但无法区分内部质量的高低。

乍一看,内部质量和客户没有关系

既然客户或用户看不到内部质量,那么它重要吗?想象一下我和 Rebecca 各自写了一个跟踪和预测航班延误的应用程序。我们的应用程序核心功能相同,都有同样优雅的用户界面,并且几乎没有任何缺陷。唯一的区别是她的内部源代码整洁有序,而我的却是混乱的。另外一个区别是:我的产品售价 6 美元,她的产品售价 10 美元。

由于客户不会看到源代码,并且它不会影响应用程序的运行,为什么有人会为 Rebecca 的软件额外支付 4 美元?通俗的讲,没有必要为更高的内部质量支付更多的钱。

换句话说,为外部质量买单是有意义的,但为内部质量买单是没意义的。用户可以判断他是否要为更好的用户界面支付更多的费用,因为他们能够评估用户界面的好坏。但是用户无法看到软件内部模块化的结构,更不用说判断它的好坏了。既然如此,为什么软件开发者要花时间和精力来提高软件的内部质量呢?

提升内部质量使软件改进更加便捷

为什么软件开发人员会在内部质量上大做文章呢?程序员大部分时间都在修改代码。即使在新系统中,几乎所有编程都是在现有代码库上完成的。当我想为软件添加新功能时,第一个任务就是弄清楚这个功能如何适应现有应用程序的流程,然后我需要更改该流程以使适应我的功能。我经常需要使用已经在应用程序中的数据,因此我需要了解这些数据代表什么,它与周围数据的关系以及需要为新功能添加哪些数据。

所有这些都是有关理解现有代码的。但是软件很难理解。逻辑可能变得纠结,数据可能难以理解,某个命名可能六个月前对 Tony 有意义,但对我来说,这和他离开公司的理由一样神秘。凡此种种,开发人员称之为 cruft(1),即当前代码与理想情况之间的差异。

内部质量的一个主要特点是让我更容易弄清楚应用程序的工作原理,这样就可以知道如何添加内容。如果将软件很好地划分为独立的模块,就不必阅读全部 500,000 行代码,我可以在几个模块中快速找到几百行。如果我们将精力放在明确的命名上,我可以快速了解代码的各个部分,而不必阅读细节。如果数据合理地遵循基础业务的语言和结构,我可以很容易地理解它与客户服务端的请求之间的关系。技术债使我需要花费更多的时间理解如何做出更改,也提升了犯错误的概率。如果发现问题,则需要花费更多的时间去定位和解决问题。如果没有发现这些问题,它们就会成为线上问题,以后会花更多的时间来修理。

我的改动也会影响未来。我可能会找到一种快速添加这个功能的方式,但这会违背程序模块化结构,增加了技术债。如果我这样做了,虽然今天可以更快的完成,但是在未来几周和几个月里,其他所有必须处理此代码的人都只能放慢速度。一旦团队中的其他成员也决定用这种快捷的方法来完成功能,一个易于修改的应用程序会变得任何一个微小的改动都要耗费数周的时间。

客户确实会关心新功能的快速引入

在这方面内部质量对用户和客户至关重要。更好的内部质量使得添加新功能更容易、更快、更便宜。可能现在我和 Rebecca 的应用程序是相同的,但在接下来的几个月里,由于 Rebecca 的高内部质量,她可以每周都添加新功能,而我却一直努力试图在这些技术债中增加新功能。我的生产速率在降低,很快她的软件就比我的软件有更多的功能了。然后,即便她提升价格,客户也都删除了我的应用程序,用了 Rebecca 的。

内部质量影响的可视化

内部质量的基本作用是降低未来变化的成本,但是编写好的软件需要额外的努力,这在短期内会产生一些成本。 一种可视化的方法是使用以下伪图(pseduo-graph),纵坐标为软件累积的功能,横坐标为实现它的时间(成本)。对于大多数软件工作,曲线看起来像这样。

内部质量较差时如上图所示。最初进展很快,但随着时间的推移,添加新功能变得更加困难。即使很小的变化也需要程序员理解大量晦涩难懂的代码。修改代码时,会产生意外的破坏,导致需要长时间来测试,且有很多缺陷要修复。

专注于高内部质量就是减少生产力的下降。事实上,有些产品会产生相反的效果,开发人员可以利用先前的工作轻松构建新功能。这种情况是一种罕见的情况,因为它需要一支技术精湛,训练有素的团队来实现这一目标。但我们偶尔会遇到。

这里的微妙之处在于,在一段时间内,低内部质量比高内部质量的生产力更高。在此期间,在质量和成本之间存在某种权衡。问题是:两条线交叉前的这段时间有多长? 此时我们可以明白为什么这是一个伪图。因为无法量化软件团队所交付的功能。由于无法量化产出,从而无法衡量生产率,因此无法对低内部质量的后果进行可靠的量化。无法衡量产出在专业工作中非常普遍,比如我们如何衡量律师或医生的生产力?

我通过收集我所知道的熟练开发人员的意见来评估两条线交叉点。答案让很多人感到惊讶。开发人员发现质量差的代码会在几周内显着降低开发速度。因此,内部质量和成本之间的权衡取舍并不多。即使很小的软件工作也会受益于对良好软件实践的关注,当然我可以从我的经验中证明这一点。

即使最好的团队也会产生技术债

许多非开发人员倾向于认为只有当开发团队粗心大意或犯错时才会发生这种事情,但即使是最优秀的团队也会在工作时不可避免地产生一些技术债。

我想用一个和我们最好的技术团队负责人聊天的故事来说明这一点。他刚刚完成了一个被广泛认为是非常成功的项目。无论是在功能,时间和成本方面,客户都对交付的系统感到满意。同事们对在此项目的工作经验给出了非常积极的评价。技术负责人非常高兴,但承认系统的架构并不那么好。我的反应是“怎么可能,你是我们最好的架构师之一?”他的答复是任何一位经验丰富的软件架构师都熟悉的答案:“我们做出了很好的决策,但现在才明白应该如何构建它”。

许多人,包括软件行业的一些人,将构建软件比作建造教堂或摩天大楼,这也是为什么我们称资深程序员为“架构师”。但构建软件相比于物理世界不同,它是存在于未知的不确定世界中。软件的客户只是粗略地了解他们在产品中需要哪些功能,并在构建软件时了解更多信息(特别是一旦早期版本发布给用户后)。软件开发的构建模块(语言,库和平台)每隔几年就会发生重大变化。映射到物理世界中就是,当建筑物被建造和使用后,客户要添加新楼层并改变楼层平面图,混凝土的基本属性也每隔一年就会发生变化。

鉴于这种程度的变化,软件项目总是推陈出新。我们几乎不会去主动了解那些已经被轻易解决的问题。当我们构建解决方案时,自然会了解它,所以我常常听到,团队只有在花了一年左右的时间构建它之后,才能真正理解软件的架构。即使是最好的团队在他们的软件中也会有技术债。

不同的是,最好的团队其技术债较少,同时也会偿还技术债,以便继续快速添加功能。他们花时间完成自动化测试,以便能够快速解决问题并减少时间的浪费。他们经常进行重构,以便持续的偿还技术债。当团队成员工作目标相互冲突时,持续集成可以最大限度地减少技术债。一个常见的比喻就是清理厨房的工作台面和厨具。你做饭时不得不弄脏东西,但是如果不快速清理东西,淤泥干涸,更难去除,所有肮脏的东西会妨碍烹饪下一道菜。

高质量的软件生产成本更低

总结起来有如下几点:

  • 忽视内部质量会导致技术债快速累积
  • 技术债降低了功能的开发速度
  • 即使最优秀的团队也会产生技术债,但是通过保持高内部质量可以使其变得可控
  • 高内部质量可以最小化技术债,使得添加新功能的工作量、时间和成本都更少

可悲的是,软件开发人员通常不会很好地解释这种情况。我多次和开发团队谈过,他们说“他们(管理层)不会让我们写出高质量的代码,因为这需要太长时间”。开发人员通常需要适当的专业性来关注质量。但是,这种道德主义的论证意味着高质量是有代价的,这使他们的论点失败了。令人讨厌的是,低质量的代码既使得开发人员的生活更加艰难,又让客户付出了更多成本。在考虑内部质量时,我强调我们应该使用经济论证的方法。高内部质量降低了未来功能的成本,这意味着花时间编写好的代码实际上降低了成本。

这就是为什么文章开头提出的问题忽略了这一点。为构建软件高内部质量所带来的“损耗”实际上是在降低损耗。我们在生活中做大多数决策的时候,习惯于在成本和质量之间进行权衡,但这对于软件的内部质量没有意义。(它适用于外部质量,例如精心设计的用户体验。)因为成本和内部质量之间的关系是一种不同寻常和反直觉的关系,所以通常很难理解。但了解它对于以最高效率开发软件至关重要。

Jenkins Pipeline 一点通

微服务是什么鬼