下载本文的PDF版本 PDF

技术债务管理

今天节省金钱和时间的捷径可能会在未来付出代价。


埃里克·奥尔曼


1992年,沃德·坎宁安在OOPSLA(面向对象编程、系统、语言和应用)2会议上发表了一份报告,其中他提出了技术债务的概念。他用不成熟的代码来定义它:“首次发布代码就像是背负债务。” 然而,技术债务并不局限于首次发布的代码。有很多方法和理由(并非都不好)来承担技术债务。

技术债务通常是工程“最佳实践”与其他因素(发布日期、工具成本以及可用工程师的技能等)之间紧张关系的结果。粗略地说,当工程师采取不符合最佳实践的捷径时,就会产生技术债务。这包括为了避免“正确地做事”的困难(或不可能)而偷偷绕过抽象,省略或吝啬文档(代码内部和外部文档),使用晦涩或不完整的错误消息,因为它太难创建更具信息性的内容,即使知道生产中需要更好的算法,也使用简单但速度慢的算法来实现代码,当您真的应该创建适当的联合*时却使用void*,使用不太适合手头系统的构建工具,吝啬于良好的安全实践,不编写单元测试等等。承认吧——你们都在职业生涯中的某个时刻(或可能全部)做过这些事情中的一件或多件。(技术债务也可能被有意地作为一种节省时间和金钱的策略来承担;稍后会详细介绍。)

并非所有的债务(无论是技术债务还是财务债务)都是坏的。我们很少有人能负担得起用现金买房,而且为了买房而负债并非不负责任,前提是我们知道如何偿还。相比之下,用信用卡购买奢侈品,明明知道你的工资无法支付,通常是灾难的根源。在原型中使用简单但速度慢的算法可能是完全正确的路径,只要你有一个计划,说明如何在代码发布之前更新代码。这意味着在计划中留出时间,确保问题得到跟踪,以免在混乱中丢失,在实现代码时知道确实存在一个适用于此实例的良好算法,并相信管理层会支持你。

理解、沟通和管理技术债务可以对系统的短期和长期成功产生巨大影响。(请注意,尽管本文重点关注软件工程中的技术债务,但许多这些原则可以应用于其他技术学科。)

与金融债务的比较

承担金融债务通常有三个重要的属性。首先,贷款人希望最终得到偿还。其次,你通常必须支付利息才能偿还——也就是说,你偿还的钱比你最初得到的钱要多。第三,如果事实证明你无法偿还,那将付出非常高的代价,无论是宣布破产、失去房子,还是(如果你是从错误的人那里借钱)穿着水泥鞋从短码头走下去。

技术债务在某些方面相似,但在其他方面则不同。虽然你不需要在任何固定的时间表上偿还债务(有些债务可能永远不需要偿还),但你通常确实需要偿还(即,重写代码或以其他方式解决问题)那些对你或你的客户产生重大影响的部分。“利息”在你或任何其他人(支持台工作人员、未来的程序员、客户等)与你的系统工作时,因错误、性能问题、莫名其妙的错误功能、花费时间研究系统哪里出了问题(而系统本可以给出更明确的错误消息)等原因而延迟时累积。未能解决问题可能会导致系统彻底崩溃——客户放弃并转向其他地方,系统变得如此缓慢和脆弱,以至于必须从头开始重写,或者在极端情况下,公司被迫关门。

也存在一些显著的差异。也许最有害的一个是,承担技术债务的人不一定是偿还债务的人——事实上,大多数时候,承担债务的人可以将成本转移给其他人,这鼓励了承担债务。太多开发人员不维护自己的代码。许多公司都有一个政策,软件从由他们最好的程序员组成的开发模式转移到由二线工程师组成的维护模式(他们的工资较低,但通常比一流团队拥有更困难的工作)。有时甚至不是你组织中的任何人在支付利息:而是用户必须支付。开发人员因实施速度而非长期可维护性而获得更多奖励,并且可能在真正付出代价之前就转移到不同的项目或公司。这使得最初的开发人员几乎没有动力第一次就把工作做好。

与金融债务不同,技术债务几乎永远不需要全部偿还。大多数(可能所有)生产系统都有一些瑕疵,这些瑕疵对最终系统的可用性或长期可维护性没有重大影响。极少有系统在源代码的某个地方没有TODO或FIXME或XXX注释。请注意,偿还技术债务的成本以重写或重构代码或以其他方式解决问题所需的工程时间的形式出现。如果你最终累积的利息低于偿还债务的成本,那么首先就没有必要偿还债务。问题在于,提前知道哪些债务最终成本最高可能很困难。

例如,当加州大学伯克利分校的CalMail系统在2011年11月宕机时,问题追溯到延迟维护——特别是,即使已知系统接近容量,也决定推迟更新系统。5 RAID中的一块磁盘损坏,随后不久又损坏了第二块,重建阵列的成本降低了容量,足以造成危机。在决定接受多少技术债务时,需要考虑墨菲定律。在CalMail案例中,单个硬件故障在基本设计中是预期的,但多次故障发生在历史上使用高峰期,造成了无法快速解决的状况。根据伯克利分校负责信息技术和首席信息官的副校长谢尔顿·瓦格纳的说法,“鉴于我们计划迁移到新技术,我决定不花一百万美元来升级CalMail软件,仅仅为了使用12个月。我们试图在预算紧张的情况下保持谨慎,(但)回顾来看[原文如此],投资存储升级以避免这场危机本应是好的。” 这是一个有意承担技术债务但结果证明是一场糟糕赌博的案例。如果系统在那12个月的窗口期幸存下来,学校很可能会在预算紧缩期间节省100万美元。

有句谚语说,工程中有三个变量:时间、功能和资源——选择两个。事实上,还有第四个变量:债务。在这四个变量中,你可以设置其中任何三个,但你永远无法设置全部四个;总要有所牺牲,而通常债务是方程中的自由变量。债务起初看起来可能是“免费的”,但技术债务往往会自我累积。如果债务的获取涉及利息,以增加维护和扩展系统的精力为形式,那么随着你承担债务,维护和扩展变得更加困难和耗时。这是债务崩溃的一种形式:如果你所有的“收入”(以精力形式)都用于支付利息,而没有剩余任何东西来推动系统前进,那么该系统就会陷入困境。如果生产力以每天产生的代码行数来衡量,这一点尤其明显,这种衡量标准应该被扔进地狱之火。你没有多少选择:增加精力(雇用更多工程师),放弃系统并转移,或者破产。从这个意义上说,技术债务的利息实际上是复利,或者换句话说:如果你不控制债务,那么支付额会随着时间推移而增加。

考虑以下其他有趣的比较。Construx Software的首席执行官兼首席软件工程师史蒂夫·麦康奈尔区分了无意和有意的债务,而有意的债务又分为短期(战术)债务和长期(战略)债务。6 他还指出,当一个系统接近生命周期结束时,承担债务变得更具吸引力,因为当系统退役时,所有债务都会被清偿。他还就如何向非技术人员传达技术债务的概念提出了一些有趣的看法,部分是通过在跟踪系统中维护“债务积压”,并以美元而非更技术性的东西来呈现。

在略有不同的分析中,软件设计师马丁·福勒在两个轴上分解了技术债务:鲁莽/谨慎和有意/无意。3 他将鲁莽-有意的债务描述为“我们没有时间进行设计”,鲁莽-无意为“什么是分层?”,谨慎-有意为“我们必须立即发布并处理后果。” 这揭示了第四类技术债务,它不容易映射到金融模型:谨慎-无意,他将其描述为“现在我们知道我们应该如何做了。”

另一种与某些人产生共鸣的技术债务分析是,管理技术债务是管理技术领域风险的一种方式。软件顾问史蒂夫·弗里曼通过将技术债务与未对冲(或“裸”)看涨期权进行比较来讨论这一点。4 两种情况(风险或裸看涨期权)都允许债务可能永远不需要偿还的可能性;事实上,通过承担适当的风险可以赚大钱。然而,裸看涨期权也可能失去其所有价值——本质上,风险是无限的。这种情况并不经常发生(大多数时候,当你亏损时,你只损失一部分钱,而不是全部),但它可能会发生,就像错误的技术债务选择可能导致灾难一样。

到目前为止,我们一直在谈论技术债务,就好像它是程序员独有的。这远非事实。例如,运营部门也会产生自己的债务。避免磁盘阵列升级是技术债务和财务成本之间的权衡。在向机房添加新的、更热的设备时,未能考虑电力和冷却要求是一种债务。未能自动化一个简单但繁琐的手动流程是一种债务。系统管理员(由于缺乏意愿、灵感或时间)既没有记录他们支持的系统,也没有在休假前培训同事,这是另一个例子。在这些非代码相关的情况下,技术债务与风险管理的比较通常更加鲜明:你是在打赌你不会耗尽磁盘空间或带宽,你不会遇到超级热的一天,你的系统不会变得如此成功以至于手动流程成为瓶颈,或者在你去马丘比丘时不会出现任何问题。

某些人员配置问题可能导致另一种形式的技术债务:系统的一部分只有一个人理解。有时这种情况发生是因为人员配置过于分散,但也可能是由于缺乏安全感的人认为,如果他们让其他人都蒙在鼓里,那么他们将变得不可或缺。当然,问题是每个人最终都会离开。

管理你的债务

技术债务是不可避免的。问题不是消除债务,而是管理债务。当一个项目开始时,团队几乎永远无法完全掌握问题的全部。这是软件开发的瀑布模型失败的根源,该模型假设所有需求都可以在设计开始之前最终确定,而设计又可以在系统实现之前完成,等等。这个论点似乎很好:随着系统的开发,进行更改的成本呈指数级增长,因此最佳路径是先完成早期阶段,然后再继续进行。现实情况是,需求总是会变化(“需求变更”)。拥有一个可工作的原型(即使它不完整或不完美)通常更好,这样你和客户就可以开始获得系统经验。这就是敏捷编程背后的理念,它接受一些技术债务是不可避免的,但也要求采取补救措施(“计划变更”)。

然而,尽管技术债务可能是必要的,但重要的是及时偿还其中的战略部分。随着时间的推移,程序员会跳槽到其他公司,而那些同意各种妥协的人已经转移到其他项目,被不以相同方式看待问题的人所取代。未能为初始原型编写文档(内部和外部文档)可能是一个好的权衡,但时间越长,编写起来就越困难——仅仅是因为人类记忆是短暂的,而且如果你给大多数人看他们一年前编写的代码,他们将不得不研究它才能记住他们当时为什么要那样做。旨在具有有限寿命的代码可能不受这些问题的困扰,但许多短期“原型”最终会被交付给客户。不幸的是,弗雷德·布鲁克斯在《人月神话》中说的“计划扔掉一个;反正你也会扔掉”1,似乎经常被曲解为“让你的原型可交付;反正它也会被交付。” 这两种说法并不矛盾。

同样重要的是,某些形式的技术债务非常昂贵,以至于应尽可能完全避免。安全是采取捷径可能导致灾难的领域。你永远不希望说“我们今天正在明文中使用密码,但我们将来会回来将其更改为质询-响应”,除非在非常早期的原型中,除了你之外没有人会看到。如果它被意外部署,这将是灾难的根源。你还想避免在代码中确立“赌上你的公司”的捷径。如果由于某种原因你别无选择(例如,因为在开发过程中,其他工程师必须编写将与你的代码接口的代码,而你不能让他们等待),请保留一份“发布前必须偿还的债务”日志。如果这些事情没有写下来,很容易忘记,这真是令人惊讶。

发布周期可能会对技术债务的获取和处置速度产生相当大的影响。现代“尽早且频繁发布”的趋势,尤其是在基于Web的服务的背景下,使得承担技术债务变得更加容易,但也使得解决这些债务变得更加容易。如果管理得当,这可能是一种祝福——更早地承担债务可以让你更早地发布更多功能,从而允许来自客户的即时反馈,从而产生更贴近用户需求的产品。然而,如果这些债务没有及时偿还,它也会更快地复合,系统可能会以真正可怕的速度陷入困境。特别是基于Web的服务的另一方面是,正确但效率低下的解决方案实际上可能会让你的公司花费更多的钱——例如,以服务器场租金的形式。幸运的是,这使得债务很容易转化为美元,非技术利益相关者通常发现美元比关于可维护性的断言更容易理解。

并非所有的技术债务都是程序员懒惰的结果。有些是管理层或其他部门强加的,尤其是在他们不理解这种债务可能有多有害时。客户通常购买功能,而不是长期可维护性,因此营销部门经常鼓励工程部门转向下一个伟大的事物,而不是花费必要的时间来巩固、清理和记录现有系统。对他们来说,采取这些步骤是不必要的成本——毕竟,系统今天可以工作,那么工程部门为什么需要花时间锦上添花呢?

技术债务还有另一个方面需要考虑:它以多种方式发生并且是持续的。它当然可能来自设计或实施阶段,但也可能发生在运营阶段。例如,计算机系统可能已经设计和安装了UPS(不间断电源),但延迟维护——以未能测试这些单元和更换电池的形式——可能会使它们失效。磁盘阵列在指定时可能足够,但随着系统的增长,它们必须升级。当试图从资金紧张的管理层那里提取资金来升级在他们看来运行良好的东西时,这可能尤其困难。

管理层常常助长和纵容这个问题。“股东价值”的当前商业口头禅本来很好,如果股东有足够的耐心来奖励长期价值创造的话。相反,趋势是按季度而非十年地思考,这给组织中的每个人带来了巨大的压力,要求他们尽可能快地生产尽可能多的东西,而不考虑长期成本(正如古老的哀叹所表明的那样,“永远没有时间把事情做对,但总是有时间重新做一遍”)。将成本推到未来被认为是一种好的策略。这强烈鼓励承担技术债务。这方面的一个指标是,当工程部门长期处于“高强度工作模式”而不是有节制地使用高强度工作模式时。有多少公司在他们的网站和企业价值观声明中宣传自己是“家庭友好型”,同时鼓励他们的员工每周工作60个小时,惩罚每周工作40个小时然后回家与家人团聚的“懒人”?在这些环境中,几乎不可能避免承担不适当的技术债务。

这并不是说管理层总是错误的。在适当的时候积累债务是合适的。如果我的孩子需要紧急医疗,我不会仅仅因为它意味着承担债务而拒绝,即使偿还债务会很昂贵。同样,管理层对客户、员工和(是的)投资者负有责任,有时可能会施加令人不安的要求。在睁大眼睛并以负责任的方式承担的债务并不是坏事。加州大学伯克利分校的首席信息官做了一个后来证明是错误的赌注,但这可能是一个成功的赌注。他知道自己正在这样做,当屋顶真的塌下来时,他承担了责任。困难在于管理层不理解他们正在承担的债务,或者过于轻易和频繁地承担债务,而没有偿还债务的计划。在过去的工作中,我曾争辩说我们需要更多时间来构建一个系统,但当我们的缺陷率很高时,却被管理层责怪,这直接归因于违背我更好判断而强加的人为缩短的工期。在这种情况下,管理层不理解债务,无视相反的警告,然后在问题显现时没有承担责任。

从不同角度看债务成本

技术债务影响着每个人,但方式不同。这是管理债务问题的一部分——即使你从你的角度理解它,也有其他合理的方式来看待它。

客户。 似乎客户是这起事件中的最终恶棍(和受害者)。毕竟,如果他们更有耐心,如果他们对产品的要求更少,并给公司更多的时间第一次把工作做好,这一切都不会发生(或者可能不会)。诚然,客户有时可能更关注功能(并且,可悲的是,有时更关注营销噱头),而不是长期可维护性、安全性和可靠性,但他们是受伤最严重的人。当移动网络中断时,当他们无法按时提交工作时,当他们的公司因为他们与软件作斗争而失去业务时,他们付出了代价。最终,一切都是关于做客户需要的事情,而客户需要能够工作、他们可以理解、可以维护和扩展、可以支持以及(最终)他们喜欢使用的软件。这离不开在流程的每个级别管理技术债务,但客户很少能控制债务的管理方式。值得注意的是,为定制解决方案付费的客户通常比购买“现成”软件的客户拥有更多的控制权,后者在很大程度上必须使用他们得到的东西。与此同时,当你为特定客户构建软件时,你也许可以协商“债务偿还”版本(可能不使用这个术语)。

帮助台。 在帮助台工作的人值得在天堂——或者偶尔在地狱中占有一席之地。客户很少打电话来说他们有多高兴;他们通常有相当不同的议程。帮助台人员遭受几乎所有方面的技术债务之苦:设计不良的界面、糟糕或不存在的文档、缓慢的算法等。此外,可能看起来不会直接影响他们的事情(例如代码本身的晦涩难懂)会产生间接影响:客户解决问题所需的时间越长,他们就越暴躁。尽管帮助台是客户对内部流程的主要输入,但帮助台通常无法直接联系到可以解决问题的人。

运营。 在面向服务的环境中,运营人员(那些每天24小时携带寻呼机并且必须保持一切正常运行的人员)常常是前线的炮灰;他们可能会花费大量时间为其他人未经咨询他们而做出的决定付出代价。有时他们可以查看代码,有时则不能。无论如何,他们可以查看文档——如果它存在的话。 (最起码的)好消息是,只要他们能提出可接受的权宜之计,他们或许可以将问题推给维护人员。 DevOps运动的兴起——运营人员需要尽早与开发人员合作以确保产品可靠、可维护和易于理解的概念——是一个积极的发展。这是减少长期技术债务的好方法,应该大力鼓励。

工程师。 工程师分为两种角色:编写代码的开发人员和必须修复、扩展或以其他方式维护代码的人员(这些人可能是同一批工程师,但在许多地方他们不是)。乍一看,最初的开发人员似乎是技术债务的主要创造者,他们确实有强烈的动机承担债务,但正如我们所见,债务可能来自许多来源。在早期,技术债务几乎是不可见的,因为利息支付尚未开始到期。进行快速、功能强大的初始实施使程序员看起来不错,但代价是阻碍了后来加入的工程师。在某些情况下,如果程序员维护成熟代码的经验有限,他们甚至可能没有意识到自己正在承担债务。因此,一个速度适中、稳定、经验丰富的程序员,他产生可维护的代码,可能比一个“超级程序员”更好、最终质量更高的工程师,后者可以一步跨越高耸的原型,但从未维护过成熟的代码。

营销。 这些面向客户的人员经常不得不承受客户不满的冲击。他们通常可能是最强烈推动缩短产品开发时间的人,因为他们受到销售和客户的压力,要求尽快提供新功能。然而,当新功能在现场无法正常工作时,他们也是站在风口浪尖的人。此外,快速交付新功能的压力通常意味着以后的功能将需要更长的时间才能生产出来。优秀的营销人员理解这一点,但通常这并不是一个符合营销世界模型的概念。

管理层。 有好的管理层和坏的管理层。好的管理层理解风险管理,并平衡公司所有部门的需求。坏的管理层常常偏袒一个部门,损害其他部门的利益。如果受青睐的部门是营销或销售,管理层将倾向于承担技术债务,而不理解成本。然而,管理层也付出了代价。 “没有坏的宣传”是不正确的,尤其是当你的公司似乎正在走向衰落时。管理层应该毫不费力地接受管理金融债务的概念。管理技术债务也对他们有利。

总结

技术债务可以被描述为所有为了今天节省金钱或加快进度而采取的捷径,但可能会在(通常是不明确的)未来付出金钱或减慢进度的风险。它是不可避免的,甚至可能是一件好事,只要它得到妥善管理,但这可能很棘手:技术债务来自多种原因,通常具有难以预测的影响,并且通常涉及对未来会发生什么的一种赌博。管理技术债务的大部分内容与风险管理相同,并且可以应用类似的技术。如果技术债务没有得到管理,那么它往往会随着时间的推移而累积,直到可能导致危机。

可以从多种角度看待技术债务,并且可能由组织的各个层面引起。只有在所有层面的协助和理解下才能对其进行妥善管理。特别重要的是帮助非技术方理解因技术债务管理不善而可能产生的成本。

参考文献

1. Brooks, F. 1995. 《人月神话》周年纪念版。第11章。Addison-Wesley。

2. Cunningham, W. 1992. WyCash投资组合管理系统。OOPSLA经验报告; http://c2.com/doc/oopsla92.html

3. Fowler, M. 2009. 技术债务象限; https://martinfowler.com.cn/bliki/TechnicalDebtQuadrant.html

4. Freeman, S. 2010. 糟糕的代码不是技术债务,而是一个未对冲的看涨期权。高阶逻辑; http://www.higherorderlogic.com/2010/07/bad-code-isnt-technical-debt-its-an-unhedged-call-option/

5. Grossman, S. 2011. Calmail崩溃持续多天。《每日加州人报》(12月1日); http://www.dailycal.org/2011/12/01/calmail-crashes-last-multiple-days/

6. McConnell, S. 2007. 技术债务。Construx对话:软件最佳实践; http://blogs.construx.com/blogs/stevemcc/archive/2007/11/01/technical-debt-2.aspx

喜欢还是讨厌?请告诉我们

[email protected]

埃里克·奥尔曼 曾担任程序员、维护人员、帮助台幸存者、一线和执行管理人员、顾问、技术作家,偶尔甚至还担任客户。他体会到所有这些角色的困难,有时甚至是愚蠢之处。

© 2012 1542-7730/12/0300 $10.00

acmqueue

最初发表于Queue杂志第10卷,第3期
数字图书馆中评论本文





更多相关文章

凯瑟琳·海耶斯,大卫·马龙 - 质疑评估非密码散列函数的标准
虽然密码散列函数和非密码散列函数无处不在,但在它们的设计方式上似乎存在差距。 存在许多由各种安全要求驱动的密码散列标准,但在非密码方面,存在一定程度的民间传说,尽管散列函数历史悠久,但尚未得到充分探索。 虽然针对现实世界数据集的均匀分布非常有意义,但在面对具有特定模式的数据集时,这可能是一个挑战。


妮可·福斯格伦,埃里尼·卡利亚姆瓦库,艾比·野田,米凯拉·格雷勒,布莱恩·豪克,玛格丽特-安妮·斯托里 - DevEx 在行动
随着领导者寻求在财政紧缩和人工智能等变革性技术的背景下优化软件交付,DevEx(开发者体验)在许多软件组织中越来越受到关注。 技术领导者凭直觉接受,良好的开发者体验能够实现更有效的软件交付和开发者幸福感。 然而,在许多组织中,旨在改进 DevEx 的拟议倡议和投资难以获得支持,因为业务利益相关者质疑改进的价值主张。


若昂·瓦拉霍,安东尼奥·特里戈,米格尔·阿尔梅达 - 低代码开发生产力
本文旨在通过展示使用基于代码、低代码和极限低代码技术进行的实验室实验结果,研究生产力差异,从而为该主题提供新的见解。 低代码技术已明确显示出更高的生产力水平,为低代码在短期/中期内主导软件开发主流提供了强有力的论据。 本文报告了程序和协议、结果、局限性和未来研究的机会。


伊瓦尔·雅各布森,阿利斯泰尔·科克伯恩 - 用例至关重要
虽然软件行业是一个快节奏且令人兴奋的世界,其中不断开发新的工具、技术和技巧来服务于商业和社会,但它也很健忘。 在它匆忙前进的过程中,它容易受到时尚的支配,并且可能会忘记或忽略针对其面临的一些永恒问题的成熟解决方案。 用例于 1986 年首次引入,后来普及开来,就是这些成熟的解决方案之一。





© 版权所有。

© . All rights reserved.