技术债务
2019-07-16
软件系统容易产生内部缺陷(Cruft),这些缺陷将使得软件在演进过程中难以维护及扩展,Ward Cunningham 将这类缺陷比喻为技术债务(Technical Debt)。技术债务形象地将软件新增功能时因为内部缺陷所带来的额外投入比喻为金融中的利息,这些利息完全是由于技术债务的存在所带来的。
想象一下,假如基线代码中存在一个令人费解的模块,对于需要在这个模块开发一个新功能时,如果其结构清晰,那么可能四天就可以完成,可由于其存在内部缺陷,实际却花费了六天才完成,这两天额外的投入,就是债务利息。
关于债务的比喻,最为贴切的是让我们明白应该如何去看待及处理这些问题。如果投入五天时间可以理清模块结构,完成其内部缺陷修复,那么这五天时间就像是在偿还本金,此时新功能开发的完整时间将是九天,而不是先前的六天。此后,如果这个模块又新增两个类似功能需要开发,那么此时这两个功能开发将比先前要快得多。
这听起来像是个很简单的数字问题,任何一个会使用 Excel 的项目经理都能够很快地做出一个明智的选择。但不幸的是,软件的生产率效率是个难以度量的事情,因此这些投入的成本也就难以客观度量。我们是可以估算完成一个功能需要多长时间,同时也可以估算在修复内部缺陷的投入,然而在实际情况下,这些估算是相当不准确的。
鉴于此,通常最好的办法是参考金融财务债务,采取逐步偿还本金的方式进行。在开发第一个功能时,额外投入几天时间来处理掉一些内部缺陷,这能够将后续再次变更软件时所要付出债务”利息”减少到一天。虽然债务并没有完全消除,但在再次变更代码增加功能,此时要付出的”利息”将显著减少。采取这种渐进式改进的最大好处在于,我们投入改进的软件模块必定是我们高频变更的模块,这部份才是真正急需修复的内部缺陷。
通过利息与本金的比喻,可以帮助我们识别出需要修复的内部缺陷。对于一个修复起来像是个噩梦的软件代码,如果这段代码无需进行修复,那么它也不是个问题。对于存在内部缺陷代码,仅当我们需要修订它时,才需要为其支付 “利息”(这点与财务利息不同,财务利息是随时间推移进行支付的)。因此对于有内部缺陷但基本已稳定的代码,无需修复,而对于高频活跃且有内部缺陷的模块代码,应该采取零容忍的态度,及时修复,因此这部分的模块的”利息”非常的高昂。这一点,特别重要,因此如果软件开发人员在不关注内部缺陷的情况下去修改代码,这将累积更高额的”债务”,变化越多,所增加内部质量的风险就越大。
用债务来比喻忽视软件内部质量所带来的问题,其核心的观点在于:软件质量缺陷的偿还与财务债务一样,需要消耗大量的时间与精力。因此一旦遇到紧迫的功能要开发而无法周全内部质量,不得以选择在内部质量上妥协,在欠下技术债务的同时,也必须认可并接受这些债务必定要在未来进行偿还。
注意,在选择 “负债” 时存在风险,通常情况下这个选择分析过程都做不好。内部质量缺陷其增长飞快,可能导致需要快速开发的功能被其极大地减缓,此时不得不将所有成员的 “信用卡” 都掏空,这种情况下可能比团队先投入全力改善内部质量,之后再进行功能开发还要来得更慢。在这种情况下,“债务” 的比喻可能会让人误入岐途,因为技术债务的变化状态与实际金融贷款并不完全相同。只有当技术债务保持在 设计持久性假设(DesignStaminaHypothesis) 下之,才可能通过 “负债” 的方式加速软件功能开发。
对于各式各样的软件内部缺陷问题是否是应作为 “技术债务” 来考虑,这是个经常产生争议的内容。通常的判断依据是:这些债务是有意而为之的,还是无知而为之的。具体的分类可参看 技术债务象限(TechnicalDebtQuadrant) 一文。
注:cruft 文中译为”内部缺陷”,原意为 “badly designed, unnecessarily complicated, or unwanted code or software:” ,是指软件中那些缺乏有效设计、实现复杂、诡异的代码。