主小席

浅议技术债

主小席 · 2016-12-27翻译 · 1458阅读 原文链接

Harry Roberts

作为一名前端架构咨询师,在为客户服务期间,我曾花费大量精力去评估客户的系统。我需要从那些庞大、老旧又难以维护的系统中甄别出优质代码和不良代码,并判断哪些问题属于技术债。

不良代码与技术债的契合程度如此之高,令我一直以来如鲠在喉,不吐不快。就该问题,我要用下面这句话来表达我的观点:

技术债不等同于不良代码,不良代码也不等同于技术债

有时候,不良代码就是不良代码,不要动辄就称技术债。开发人员明明是完成了低质量的工作,却非要冠以技术债的名义,实在是有粉饰能力不足的嫌疑。

技术债是一个经常被误解的表述。错误地将那些老旧、维护困难甚至令人厌恶的代码与技术债混为一谈的话,是在刻意回避代码库的问题,而这些问题往往才是根源所在。

是时候认真探讨一下技术债了。

当我们说到技术债这个词的时候,究竟是在说什么?

根据定义,所谓技术债,区别于工程中的不良代码,它是程序员采用的一种主动性、策略性的方案。债务不是偶然发生的,它是人们在特定的情况下,为了能够立即获得某些东西,采取暂时性亏欠并承诺未来连本带息一起偿还的一种行为。

举例来说

假设你在为一个SaaS产品做前端开发,你们的销售团队正在努力争取一个非常重要的大客户,如果不能把握住的话,将会使公司损失惨重。此时客户要求:只要能使用客户公司的品牌给你们的产品增加主题,他们就可以签署合同。可是你们的CSS架构在设计之初就不具备包含主题的功能,同时眼下也没有什么良好简洁的方式来给代码库快速增加主题功能。

于是,你和公司商务部决定在两天内给产品强行植入一个主题。这种简单粗暴的方式,将会使你们的代码变得混乱且丑陋不堪。可是因为该客户实在太过重要,即使是CSS写得不太妥当,也只能暂且从权。 这就是我所说的技术债。

其后,你们交付包含主题的产品,客户签订合同,双方皆大欢喜。而此时的你(以及商务部)则需要做出决策:

  1. 回去把主题作为一项核心功能,增加到公司的CSS代码架构中。同时将先前临时添加的主题纳入正式的代码框架。

  2. 或者什么都不用管。因为到目前为止一切相安无事,客户已经完成付款,而且双方也已各取所需。看起来已经没有再投入时间和精力来收拾残局的必要了。

选1,偿还技术债;选2,无视之。

选择1:限于当时的情况和条件,你已经尽己所能。你知道如果不是形势所迫,本可以有更好的办法。此刻的你,就是要把更优的方案付诸实施。

选择2:你就是在逃避偿还债务的责任。而且这样做,会使还债的代价随着日积月累而不断增加。之后你还会遇到更多麻烦:

  1. 你的SaaS产品为你的一个客户提供了主题功能;

  2. 之后,另一个潜在客户可能也会要求给他们的产品同样增加主题。

  3. 面对这样的要求,你既无法拒绝,也无法立即满足;

  4. 于是你强行植入另一个主题,从而又增加了一笔技术债;

  5. 后面还有一连串的主题等着你继续添加,债务将会越积越深。

你令软件熵不断攀升,却丝毫没有要解决现有问题的打算。

然后,类似的需求接踵而至,你不得不一而再再而三地植入新的主题。债务越积越深,而你却无力偿还。经历了相当长一段时间之后,代码会变得臃肿不堪且难以维护。最后你不得不把所有东西推倒重来。然而最痛苦的地方在于,此时你所付出的代价,比你在最开始就选择还债所付出的代价还要多。2天时间的应急方案,再加上5天时间做重构,也依然要比之后花好几周时间重写CSS代码耗时更少。你的技术债策略最终变成了一次大败亏输的经历。

其实不良代码原本可以避免。当初摆在你面前有两条路:正途亦或捷径。在明知道可以走正途的情况下,当初选择走捷径的你绝非一时糊涂。偿还债务的过程是一条艰难的自我救赎之路。

优良债vs不良债

迫于形势而欠下的技术债本无可厚非,前提是你要心思偿还。技术债也可以成为解决商业问题的一种有效手段,当然,事后补救的过程是必不可少的。不过这并不意味着所有形式的债务都是可以被等同看待。现实世界的债务也要分三六九等。

优良的债务可以是:

  • 抵押贷
  • 学生贷
  • 商业贷

有好多种形式的债务,它们可以采取有效的手段来确保债务人具备偿还债务的能力。这些债务的选择,是你经过深思熟虑后的结果,因为它能鞭策你去赚更多的钱,是能为你带来切实利益的。

商业贷,它会抵押你的设备和房产,通过借钱使你企业盈利的方式,来确保你有还贷的能力;学生贷,则会确保你未来能够找到工作从而有能力还债。

这些类型的债务都是债务人审时度势、权衡利弊后所做的抉择,通过举债实现短期获益并且需要明确知悉——将来我有偿还的能力。

反过来,不良债务就是类似于:

  • 为了去赌城而向高利贷举债1万美金;
  • 为了买新电视选择小额短期贷(又称“发薪日贷”);

这样的行为。

以上这些债务,它们只能供你挥霍,却无法保证你偿还。最终往往还会导致你入不敷出。你真的要为眼前这一点蝇头小利而弃长远责任于不顾么?

判断债务好坏的最好办法,就是比较眼前获利和长远付出,看谁的代价更高。

前文说到的添加主题的问题,只要能够按时偿还,就属于优良债务(如果不还,任何债务都将成为不良债)。“权宜行事,日后补过”就是优良债务的真实写照。

不良技术债

作为一名前端开发人员,我主要的工作是同CSS打交道。从我的工作经验出发,给诸位举一个关于技术债最简单的例子:

!important

所有前端开发人员都很清楚:!important有风险,使用需谨慎。可是大家依旧用得不亦乐乎。为什么?

不是因为我们的能力有限,而是因为眼前放着捷径可走。!important是一个能够拯救前端程序员于水火的神器,对于某些疑难问题可以说是“一语见效”。我们可以用一天的时间来重构CSS做到釜底抽薪,或者也可以只花几秒钟用!important大法来掩耳盗铃。

我们用这样一种不十分理想的方案来换取眼前的利益。毕竟,重构CSS会花费大量的时间,而且最后跟!important起到的效果还一样。所以,这种捷径式的解决办法,似乎更符合商业的需求。

然而,这是一个不良债务。!important大法只需要几秒钟就能搞定的问题,未来还需要花费数周的时间来做重构。要知道,日后做重构比开始就拒绝偷懒所付出的代价要昂贵得多。 第一个使用!important的程序员成为始作俑者,导致后来者们也不得不纷纷效尤。

所以,很多CSS项目都是因为!important而越变越糟,最后还不得不面对越来越迫在眉睫的重写工作。这仅仅10个字节的!important,也因此成为了让CSS开发者付出代价最高的一个词。

不良代码

在我们清晰地了解了到底什么是技术债之后,再来了解下什么是不良代码。在我跟客户打交道的时候,我总能听到他们说:

我们已经积攒了很多技术债,得抓紧拿出方案来解决这些问题。

说实话,我由衷地钦佩他们对于定位和解决问题的这份渴望之情。可他们所着眼的,往往并不是技术债,而是不良代码。

技术债,是你在知道有更好解决方案的前提下,暂时性地采取权衡手段。而不良代码,则压根不会考虑是否有更好的方案。

还以CSS举例来说吧。很多不良代码往往是由那些非前端开发人员写就的。这些人几乎没经受过什么专业训练,对前端领域更是无敬无慕。这种草率对待代码的行为,绝不应当被看作技术债。因为:

  • 开发者对他们所实施的低水平解决方案心知肚明;

  • 开发者也同样清楚,更优秀的解决方案并不是没有;

  • 而且他们也知道这些方案能够更有效地解决问题。(可是他们依然选择了前者)

总之,不良代码是一个更深层次的文化问题,不是一朝一夕就能解决。所幸的是,不良代码通常都是由少数人写就的,而且评价不良代码的标准非常客观,这样定位和解决这些代码也非难事。书写不良代码的出发点往往不纯,因此也无需为之辩护。

“能力不足是不良代码的通信证,审时度势则是技术债的指明灯。”

额外奉送

是时候重新审视自己,深挖一下技术债背后的根本原因了。要清楚,技术债尚且可优可劣,但代码往往就真的只是质量不高而已。

在明白这些之后,哪些东西需要重构、何时重构,或者你们的团队有哪些技能上的不足等等这些问题,都将豁然开朗。

  • 如果在短期内能有切实收益, 有所取舍也未尝不可

  • 倘若理由充分,同时能够做到不忘初心,技术债也无可厚非

  • 技术债不能同不良代码划等号。因为技术债是在知识和资源有限的情况下,所采取的权宜之计,且将来务须偿还。

  • 不能偿还的技术债才是劣质债,反之亦然。我们接受甚至也鼓励特定情况下的技术债。当然,一旦时间和知识允许,就应当尽早偿还。

  • 不良代码比之技术债尤甚,因为这是知识不足或者团队把控力不足的体现。唯有从根本入手方能解决。

关于作者

Harry, 国际一流前端架构咨询师,曾为包括谷歌联合利华联合国在内的组织机构进行过产品级UI的规划、搭建与维护。

作为谷歌研发专家,他在csswizardry.com上编写关于CSS架构、性能以及扩展方面的文章;负责inuitcss的开发和维护工作;是CSS Guidelines的作者;Tweeter地址是 @csswizardry