跳至正文
杂学书库让好奇心有所归处
简体中文

匆忙代码的利息:技术债务(Technical Debt)

匆忙打包搬家时,有时我们会先把东西胡乱塞进箱子,打算以后再整理。眼下是很快就收拾完了,但到了新家,想找一件东西就得翻好几个箱子,而且整理拖得越久,麻烦就越大。软件开发中也会发生一模一样的事情,用来指代这种情况的词,正是“技术债务(Technical Debt)”。

简单来说,技术债务是指为了眼下的速度,放弃更好的设计而选择省事的做法时,日后为了修改和完善而必须付出的额外成本。这是美国程序员沃德·坎宁安(Ward Cunningham)在1992年的一次学术会议报告中最早使用的比喻,如今不仅是开发人员,连管理层和产品策划人员也常常使用这个说法。

本文将从技术债务的含义与诞生背景讲起,介绍债务累积的原理、债务的多种类型,以及管理和偿还债务的方法,力求通俗易懂。


技术债务的原理与管理

什么是技术债务?

1992年,坎宁安在面向对象编程学术会议OOPSLA上介绍自己开发金融软件的经验时,提出了这个比喻。他说,第一次交付的代码就像是借了债。借一点债可以加快开发速度,但这笔债必须通过重写代码的方式及时偿还。

这个比喻之所以广为流传,是因为即使不是开发人员也能一听就懂。就像借债要付利息一样,草草写成的代码如果放着不管,每在上面多加一个功能,就要多花一点时间和精力。坎宁安警告说,如果未偿还的债务越积越多,整个组织都可能被利息压垮而停滞不前。

这里的本金,就是尚未修改的粗糙代码本身;利息,则是因为这些代码而每次额外付出的时间和精力。偿还本金,意味着把代码重新好好整理一遍;只要本金还在,开发持续多久,利息就会一分不少地累积多久。

债务如何累积,利息又如何产生?

技术债务会通过多种途径累积。为了赶上发布日期而跳过测试、不加修改地复制粘贴相似的代码、不留下任何文档,都是典型的例子。也有一些债务并不是谁做错了什么造成的,比如一开始合适的设计,随着服务规模扩大而变得不再适用。

利息往往以不易察觉的形式产生。哪怕只改一个小功能,也会在意想不到的地方出错;新来的开发人员要花好几周才能看懂纠缠不清的代码。同样的工作所需的时间也在逐年增加,但由于每天的变化很小,谁都不容易察觉。

利息之所以可怕,是因为它会像复利一样膨胀。在草草写成的代码上叠加新代码,新代码也只能勉强迁就那套粗糙的结构。最终,一处捷径会催生出多处绕行,需要修改的范围也会比最初扩大好几倍。

一旦利息超过本金,就会出现这样的局面:用来堵住现有问题的时间,比开发新功能的时间还多。

并非所有债务都是坏的:技术债务的类型

2009年,英国软件开发者马丁·福勒(Martin Fowler)提出了按两个标准划分技术债务的“技术债务象限”。一个标准是明知而借还是不知而借,另一个标准是审慎还是鲁莽。

(1)审慎且有意的债务
这是在清楚后果的情况下选择的债务,例如“现在发布优先,先这样做,以后再改”。它可以成为一种战略选择,帮助企业抓住关键时机。

(2)鲁莽且有意的债务
这是明知不妥却以“没时间做设计”为由敷衍了事的情况,是最危险的一种。

(3)不知而借的债务
这是因经验不足而产生,或者在工作完成之后才意识到“原来应该这样做”的债务。它也是在学习过程中难以避免的债务。

需要注意的是,即使是有意的债务,如果没有偿还计划,也会很快变成鲁莽的债务。说好“以后再改”的代码好几年原封不动,这在开发一线非常常见。

由此可见,技术债务并不是必须一味回避的东西,而是像银行贷款一样,需要权衡何时借、借多少、如何还的管理对象。

管理和偿还技术债务的方法

偿还债务最具代表性的方法是重构(Refactoring)。重构是指在不改变程序功能的前提下,只把代码结构整理得更易读、更易修改。由于表面上看不出变化,成果也不明显,因此最重要的是让开发人员和管理层共同理解债务的成本。

(1)记录债务
把以后要修改的地方写进待办清单,就不会被遗忘,也能一眼看出债务累积了多少。

(2)持续地一点点偿还
预先划出一部分开发时间,每次都整理一点,就能防止利息不断膨胀。

(3)用测试搭建安全网
建立自动化测试后,修改代码时就能立刻确认其他地方有没有被弄坏,从而减轻偿还债务的负担。

(4)债务过大时推倒重建
如果债务积累过多,修改的成本已经超过重新开发的成本,那么分阶段把旧系统替换为新系统也是一种办法。不过,由于成本和风险都很大,必须慎重决定。


技术债务用“债务”这一熟悉的说法,揭示了开发一线必须始终在速度与质量之间寻求平衡的现实。比起借债本身,忘记自己借过债才是更大的问题;而管理得当的债务,反而能成为快速成长的跳板。

记住今天省下的时间会在明天变成利息,这正是健康软件的第一步。