转载

直面技术债务

软件和大教堂是类似,都是先构建,然后祈祷”。————Earl Everett

关于技术债务的讨论时而蔓延时而消退,技术债务仿佛是个筐,什么东西都可以往里装,然而当我们企图倒光筐里东西的时候,却发现每人看到的东西都不一样,甚至有时候都数不清里面都有些什么。作为一个半吊子全栈工匠,试图从一个老码农的视角审视一下技术债务。

一个比喻导致的分歧

技术债务是由敏捷先驱 Ward Cunningham(https://en.wikipedia.org/wiki/Ward_Cunningham) 在1992年的一个报告中的一个比喻,大意是做了错误的或不理想的技术决策所导致的债务。由于是个比喻,所以产生了每个人眼中的哈姆雷特。

首先,Steve McConnell将技术债务分为两类:无意的和有意的。1)无意产生的技术债务:由于缺乏经验而编写了低质量的代码。2)有意产生的技术债务:根据当前情况进行设计选型,可能很快就能解决当前的问题,有时会变得拙劣。

作为《重构》一书的作者,Martin Fowler认为技术债务产生的利息是指由于鲁莽的设计决策导致需要在未来的研发中付出更多。面对技术债务,可以持续付利,也可以通过重构一次付清。代码中的坏味道也是技术债务,是一种不计后果的债务,会让问题变得更加严重,进而将技术债务划分为4个象限:

直面技术债务

Bob大叔则认为坏味道并非技术债务。技术债务的评价标准是真实的项目约束,这些约束是风险和好处并存的。坏味道是由懒惰和外行导致的,总是意味着损失。技术债务要求牢记保持代码的整洁,就好像一个人在背负巨大的抵押债务时需要时刻保持警醒一样。

关于技术债务的更多定义讨论参见http://wiki.c2.com/?TechnicalDebt。

技术债务的现象与类型

将一个比喻作为定义是不严谨的,而且很多人对定义的重要性并不是很关注。于是,可以从现象和构成的角度来看待技术债务。

如何认识无意产生的技术债务呢?当出现如下现象的时候,说明技术债务开始累积:1)类似的代码在不同的项目/产品间迁移 2)代码已经稳定但回归测试的陈本在增加 3)每一次交付的成本逐渐增加 4)类似feature 开发的周期开始变长 5)在既有代码上开发,还不如推到了重来

技术债务的类型是由产生的原因和方式来定义的,其中一种分类如下:

  1. 战略债务:为了战略利益(例如首次上市)故意为之,并长期存在。

  2. 战术债务:在知情的情况下为了快速收益而产生,适用于短期。

  3. 疏忽债务:在不知情的情况由于缺乏知识和意识而产生。

  4. 增量债务:定期不慎产生的而导致增量债务。

技术债务包括那些现在选择不做的内部事务,但会影响未来的开发工作,例如延迟重构。不包括推迟的功能实现,除非交付功能对客户来说“足够好”的情况下,但不满足某些标准(例如,UI元素不完全符合某些UI标准)。

技术债务的组成

技术债务是多方面的,涉及技术的多个维度,这也是每人眼中都有着自己的哈姆雷特的一个重要原因。

现罗列自己经历或者看到过的一些方面:

  1. 代码债务:代码重复、违反静态分析工具和代码异味等。

  2. 设计债务:设计异味、违反设计规则等

  3. 架构债务:违反架构规则,对非功能性约束认知不足等。

  4. 测试债务:缺乏测试、测试覆盖面不足和不正确的测试设计等。

  5. 质量债务:缺乏稳定性和健壮性的技术验证,QA的自动化不足等

  6. 配置债务:版本控制的模糊,环境参数的混淆等

  7. 平台债务:平台经验的匮乏,云服务融合与应用不足等

  8. 文档债务:存在重大问题的文档、缺乏文档和文档过期等。

针对每一类技术债务,都有着相应的原则和处理方法,有时间再仔细展开。

技术债务的度量之痛

No measurement,No management。令人遗憾的是,很难有相关工具或者框架来提供量化数据对其进行准确而完整的分析。

技术债务并没有一个普遍接受的范围定义,甚至认为已知的bug也构成了技术债务。当前可用的技术债务量化工具仅仅关注几个维度,比如代码债务和一定程度的设计债务和测试债务。对于同其它维度相关的问题,比如架构债务或文档债务,这类工具并没有提供全面的检测支持。实际上,已支持的维度的全面性也是有问题的。

一般,技术债务的量化工具通常会转换成偿还这些债务所需的工作量,而工作量会随问题的严重性、范围、平台、技能等的变化而不同。这样所产生的估计值与实际情况大不相符,最多只是一种近似,而不是准确的结论。

任何类型的债务都会包含本金和利息,而当前的一些所谓的量化工具都关注与技术债务相关的本金,而忽视了利息部分,从而并不可信。利息部分关系到了理解的难度等因素,越发难以准确地量化。

技术债务很好地充当了沟通拙劣设计结果和持续重构需求的隐喻。但试图测量和量化技术债务时,变得同准确测量软件生产力或者量化软件质量一样困难!

技术债务的现金衡量

当然,技术债务的货币化有助于了解技术债务的严重程度,提供了一种跟踪技术债务偿还进度的方法。不过,需要谨慎对待这些成本和工作量估计。

一旦有了与技术债务直接相关的金钱数目,关于软件的多种复杂而麻烦的问题就可能得到答案。Israel Gat提出:除非对于技术债务有一个量化的账单,否则团队都会忽略其重要性。他提到了以金钱方式计算技术债务的需要,以金钱方式计算技术债务有如下好处:

  1. 能够告诉团队何时停止开发,开始重构。团队进入重构过程,除非债务得以偿还,否则不加入任何功能。

  2. 客户对于软件的风险得以了解。

  3. VC们可以以此判断向某项软件产品中投入资金是否理智。

  4. 有助于判断软件的支付能力,判断在重构和重写这二者之间做出选择

有哪些有效的方式可以用来将技术债务以金钱衡量呢?

Sonar中的技术债务插件是一种方式。在Sonar的站点上,已经有了对于项目的技术债务分析。要计算成本,首先要使用下面的方式找出债务:

债务(人/天)=修复重复部分的成本
+修复违规的成本
+为公共API做注释的成本
+修复未发现的复杂性的成本
+带入低于阈值复杂性的成本
+在包的层面上切断生命周期的成本

对于每个小时的成本有个默认值,例如人工200块每小时。与之类似,就可以做出各种情况的现金分析,并可以计算出技术债务的总和。

因此,以金钱方式计算技术债务能够深入理解与软件相关的潜在成本。对于所有希望监控技术债务成本并将其保持在一定限额内的敏捷团队来说,这很关键。

直面技术债务

面对已知的技术债务,普遍的经验是防止技术债务的积聚,以及有计划地偿还技术债务。

防止技术债务产生的主要方法是了解开发团队存在的技术债务。开发团队必须了解技术债务,它的各种方面和类型,以及债务对他们的项目的影响。他们必须具备完善的设施与代码质量的概念,干净的编码习惯、设计嗅觉、以及如何重构它们。合适的流程可以帮助开发团队避免技术债务积累,例如代码、设计、架构和测试的审查等。然而,这些流程必须是务实的,否则事与愿违。

对于偿还技术债务而言,首先同样是识别并记录现有债务,优先处理异味,在每个迭代中分期偿还债务。如果一个研发团队的以团队交付功能的数量或修复bug的数量为考核标准,那么团队将只专注于增加功能和修复bug。但实际上,做好它和完成它同样重要。同时,留意可能出现的大规模债务偿还,属于不同维度的债务实例相互影响。在某些情况下,即使债务很高,也不值得偿还技术债务。这些情况包括原型、概念实现和即将废弃的产品所产生的技术债务。此外,如果计划为一个传统项目迁移到一个新的技术、平台或架构,不偿还的技术债务是明智的,因为相关的债务可以在迁移过程中解决的。

管理他人的技术债务

由于不提倡重复造轮子,我们所使用的第三方软件和开源软件产生的技术债务同样会对我们造成影响。它们的Bug会成为我们的Bug,安全漏洞也会成为我们的安全漏洞,错误决定会成为了我们的错误决定。

我们所使用其他软件的代码量可能会非常大,由此产生的技术债务也可能大,甚至超过自己所编写的代码量。根据Sonatype的一项调查显示,80%的Java应用都是由开源组件与框架装配起来的,一个大型系统甚至会使用30多个不同的库或组件。

要想了解这种债务问题的严重性,需要审查代码中使用了哪些第三方开源包与依赖。一旦清楚了其他人的软件可能会造成的影响,就需要评估由此所带来的风险和问题了,并需要紧密追踪这些软件的补丁、升级信息及Bug报告等,这确实不太容易。

知道问题的严重性是一方面,修复问题则是另一方面。对最新的发布打补丁并非易事。最好能在外围打补丁,因为补丁并非总是适合于我们所使用软件的方式:Apache、Tomcat、Web Service库、客户端组件等。我们需要更加谨慎地管理这些后端代码。如果每当出现一个Linux补丁时需要立刻打上,那就说明架构可能有些问题。

升级更是一个大问题了,升级到最新的OS、RDBMS、VM等都涉及到很高的代价与风险。虽然可以通过升级的方式获得在可伸缩性、管理性及新特性等方面的一些优势,不过升级项目还需要更加关注一些潜在的问题:功能回归、兼容性问题、操作流程的变化、对其他系统的依赖变化等等。升级之后,大多数软件会变得更大而不是更好,也许会加入很多新特性,不过可能压根就用不上这些特性,这也意味着需要花更多的时间进行安装、配置和测试。我们需要搞清楚变化的地方,重新进行测试和调优。

技术债务的价值

把技术债务和金融债务来类比,贷款就会产生债务,如果定期还款,那么债务是可接受的,不会产生进一步的问题。但是,如果他不还款,就会以利息作为惩罚,并随着不还款次数的增加而增加。如果很长一段时间不能支付任何款项,那么应计利息使得债务更难以偿还。

同样地,当我们采用一个非最优或次优技术决定,就引入了技术债务。在很长一段时间未偿还累计的技术债务的情况下,软件越来越难以改变,在极端情况下,软件产品变得在技术上已经破产,往往会导致项目终止。利息有复合的性质:开发团队越是忽略或推迟它,随着时间的推移,债务变得越大。因此,是利息使得技术债务成为一个显著问题。

我们谈及技术债务的时候往往都清楚它的种种弊端,却缺乏主动使用技术债务的勇气。债务具有着现金价值,在金融领域中,货币杠杆的作用显著。对企业而言,现金流更是关键因素, 所以,没有必要谈债色变,没有债的企业未必都是一流的好企业。

合理使用技术债务,在这个快节奏的时代,我们有责任迅速为客户提供价值。在这种追求中,有各种情况团队必须选择快速或者肮脏的解决方案。需要注意的是,在这种情况下,需要我们以勤奋和务实的态度处理技术债务。

往期推荐

            写给工作5年的年轻程序员的一封信

  • 事务与工程:什么是工程师文化?

  • 架构如何做减法?

  • 罗永浩卖了1.1亿!面子vs里子,理想主义vs现实主义

  • DDD如何讲清楚,做出来?

……

关注本公众号,欢迎订阅。

技术琐话 

以分布式设计、架构、体系思想为基础,兼论研发相关的点点滴滴,不限于代码、质量体系和研发管理。本号由坐馆老司机技术团队维护。

直面技术债务

原文  http://mp.weixin.qq.com/s?__biz=MzIxMzEzMjM5NQ==&mid=2651035977&idx=1&sn=5b5b8fd5015bfa012386d5f75e0d4dbe
正文到此结束
Loading...