成为会带团队的技术人 - 前阿里本地生活研发总监 - 拉勾教育
在工作中,我们除了做好团队管理的工作外,还要解决技术与业务发展过程中产生的种种矛盾,而技术债务就是这种矛盾最常见的产物。所以今天,我想结合自己的经验,和你聊一聊应该怎么管理技术债务(Technical Debt)。
我对技术债务的理解
技术债务在研发领域类似于 “金融债务” 的概念,大部分情况下是说因为人为妥协,系统的设计和实现没有遵循最佳实践,所以虽然在短期做到了快速交付,但也制约了系统未来的可扩展性,并且埋下了稳定性的风险隐患。就好比你信用卡分期消费,虽然可以立刻满足自己的购买意愿(得到眼前的好处),但同时也会背上一定的负担,在未来必须得偿还。
其实,包括我在内,很多同学最早接触 “技术债务” 这个概念是读了《重构》这本书。
在这本书里,Martin Fowler 曾发表过他对技术债务的定义,即 “技术债务象限”。他根据债务产生时的原因,将技术债务从两个维度分成四个象限:即有意(Deliberate)的还是无心(Inadvertent)的,谨慎(Prudent)的还是草率(Reckless)的。
简单来讲,就是开发人员是否清楚接下来的设计与实现会造成未来的技术债务,以及在做决策时是否经过慎重的考虑。从我过往的经验来看,技术债务 “是否已知” 是比较关键的,因为往往最难的不是解决问题,是根本不知道这里有问题,而下面两种情况在实际工作中最为普遍:
- 因为能力不足根本没有意识到债务的产生与积累;
- 因为交付压力进而在技术方案与实现上妥协形成已知的技术债务。
什么是能力不足呢? 比如开发者编写了低质量或者有潜在风险的代码;对系统的实现和运行不了解,重复代码被大量构造,缺少抽象与沉淀;缺少完善的开发机制和流程把控,比如测试、文档等方面做得不到位……
而交付压力(技术妥协) 则被很多 Leader 认为是产生技术债务最关键的原因,因为项目很复杂或排期压力,不得不在系统的架构设计与代码实现上作出妥协,选择最容易的方式而非最好的方式。甚至会跳过方案的详细设计,直接开始 Coding,不深究代码风格、标准、最佳实践,更进一步则会压缩测试方面的时间与投入,只为了尽早上线。
而当技术债务产生后,因为系统和项目的惯性,债务会积累并使系统散发出一些 “坏味道”,这些坏味道有很多个方面,我列了三种最常见的情形,如果你负责的系统中出现类似的情况,那你需要警惕了。
技术债务的坏味道
为什么你要重视技术债务?
1. 影响系统扩展和需求交付
结合技术债务产生时的情况,我们不难发现,随着技术债务的累加,系统的负担也不断增加,糟糕的设计与实现导致系统变更时需要处理的代码、考虑的问题越来越多,影响最大的是系统扩展和需求交付。
其中对系统可扩展性影响最大的,大部分和架构以及系统链路的设计有关(比如对业务代码缺少抽象,模块间过度耦合、服务间职能边界不清晰等)。当新业务需求出现时,这些问题的改造和修复成本很高,并且大部分问题积重难返,这就使系统迭代非常困难,最终影响项目进展使系统无法按期交付。这也是技术债务对研发而言,最大、最主要的影响,需要你有足够的认识。
2. 恶性循环导致人员流失
除了影响系统扩展和需求交付之外,技术债务还会导致人员流失。我接触的大部分研发同学最喜欢的就是搭建从 0 到 1 的新系统,因为没有 “历史包袱” 可以轻装上阵,最大程度在设计和实现上完善它。
接手一个旧系统就不一样了,这样的系统往往运行了很久,出问题影响大,又因为人员变化多、文档缺失严重,谁也说不清楚系统的架构,最终只能一行行读代码来结合业务场景进行理解。并且很多看上去不合理的实现,也许会存在特定的原因,往往想改又不敢改。等你梳理清楚所有的关联与影响后,又发现工作量太大改不动,只能像打补丁一样维持着,等到某些外界因素(组织分工变化、重大事故等)发生变化导致问题不能再拖延时,再通过一次大的重构(实为重做)来解决。
不过,与其说研发同学苦恼的是旧系统,不如说是其中沉重的技术债务,它会导致系统未来的迭代愈发困难,迭代困难又会导致交付压力增大,所以只能再次做技术妥协以实现业务优先。
这样就会进入恶性循环,研发同学就好似修补一座破屋,每次修好一点,下一次迭代时就被破坏更多,随着时间持续和系统腐化的加剧,研发同学开发的难度越来越大、风险隐患越来越多、自身能力的成长与提高也很有限,自然就容易导致人员流失。
所以你一定要清楚,技术债务的恶性循环会影响开发团队的生产力,并降低团队的士气和成员的驱动力,而低生产率导致团队只能优先交付功能,这就推迟了技术债务的解决,从而进一步增加技术债务。
既然技术债务如此令人头疼,我们要怎么从这样的困境中突围而出呢?我根据经验总结了 4 个步骤,供你参考。
如何从循环的债务困境中突围而出?
1. 债务的 Owner 是技术 Leader
前面我提到,大部分技术 Leader 认为是业务节奏导致了技术债务的产生,因为研发资源永远不会 100% 充足。所以一些技术 Leader 觉得委屈,明明业务压迫技术导致的问题,最终还要自己来承担责任。
要想解决清楚一个问题,就必须先定义清楚这个问题。 所以我们要先定义清楚技术债务与技术 Leader 的关联,针对 “交付压力 - 技术妥协 - Leader 责任” 这个技术债务形成的关系链,我想说几句个人的理解,供你参考。
我们会下意识地觉得交付压力导致的技术债务和自己无关,因为我们并不是技术债务的 “始作俑者”。但是在我看来,任何一家成长中的公司,都会存在技术资源与业务发展的矛盾,如果矛盾消失,说明公司业务增长放缓,甚至陷入瓶颈,甚至导致技术资源过剩要裁人。这肯定不是我们追求的结果,大部分情况下,我们追求的是技术与业务之间的 Balance,将它们控制在一个动态适配的状态。
当 “交付压力产生技术债务” 变成一个普遍的现象,而非某种特例后,我们应该认识到这就是发展中的一部分,而解决这类问题,是你的责任和本职工作之一。 换一个角度来说,你是整个团队中最理解技术债务影响、最懂系统架构与迭代能力的人,你不解决这类 “技术问题”,难道要靠产品、销售、运营或者管理层来解决吗?
所以我非常不建议你一谈到技术债务,就下意识地路由到 “交付压力太大、排期太紧、产品设计太复杂” 等理由上,这样的 “甩锅” 只会将你从解决问题的决策者变为服从安排的执行者,不但对解决问题没有任何帮助,还从另一个方面证明了自己失职。
那么要想解决技术债务,你需要找到技术与业务的平衡点,我的经验是 “内外双修”:
- 内:加强团队的战斗力,减少债务产生的机会,增强债务处理的能力。
- 外:要深刻地理解业务,并且做好与其他协作方(尤其是产品、业务)的沟通。这样你才能理解协作方想解决什么问题,他们以为要么 A、要么 B 才能解决的问题,既懂技术又懂业务的你能否找到方案 C?
我建议你,面对选择题时不要只看到可选项,要永远寻找第三条路。 如果实在没有其他选择,在技术妥协的同时,做好沟通,让协作方明白方案的临时性以及对未来的影响,争取到承诺在未来给你足够的空间解决这些问题。
2. 通过 CheckList 识别债务
除了明确债务的 Owner 是自己之外,技术债务的度量一直是个难题,因为没有很好的量化方式,所以债务的识别以及收益 ROI 的计算都没有什么标准。所以,我们在处理技术债务的第一阶段就是要识别出技术债务,将其从看不到的未知隐患转变为可视的已知问题。
我习惯根据系统的情况,建立一个债务 Review 的 CheckList ,并且不断完善。技术债务从表象上可以做一些细分(我整理了一张图):
债务细分脑图
通过现象我们就可以反推出一些导致现象的原因,将这些原因结合系统的架构进行分类,就会形成一个个具体的关注点。这些关注点往往是结合我们之前踩过的坑、发生过的问题,以及编码、架构上广为遵守的一些最佳实践所形成的,这样你就可以制定出一个较为详细的 CheckList 用以具体的债务识别(下图供你参考)。
债务识别 Demo
3. 有计划地分级偿债
通过 CheckList 将技术债务作出识别后,往往要解决的问题非常多,但是我们又几乎无法停止需求迭代只做还债这一件事,所以此时要对技术债务做一个 “轻重缓急” 的区分,以确定需要处理的优先级。我结合之前的经验总结了一些分级原则供你参考。
- 关键链路优先: 并非所有糟糕的设计与实现都能产生严重后果,即使能,它们发生的概率也不一样,而关键链路意味着业务影响最大,同时日常的改动频率和事故风险也较高,优先解决它的收益是最大的。
- 历史事故命中优先: 一些设计与实现在过往导致过线上真实问题的发生,不管是否发生在本系统还是当前团队,都相当于已经被证实过的这类债务的严重性,所以应该尽早修复它们,避免类似问题反复发生。
- 可扩展性优先: 在 CheckList 以及债务现象中我们可以发现,有些问题影响了系统未来的演进,增加了迭代成本,有些问题影响系统的维护,比如代码风格没有统一、缺少文档,在处理时应该优先处理影响可扩展性的问题,后续逐步处理影响可维护性的问题。
- 权责清晰优先: 一些问题在处理时受到历史架构、组织分工(康威定律)的影响,会导致系统的权责不清晰,这类系统的推进和改造往往需要花费更多的时间精力,并且从顶层设计出发去重新考量,所以权责清晰的部分可以优先处理。
总的来说,通过对技术债务进行分级,实质上也是一个问题分治的过程,将大问题切分成一个个小问题,这样就可以将它们加入日常的迭代中,形成一个分期偿还技术债务的计划,逐步减少技术债务,减轻负担让团队与系统可以轻装上阵。
4. 正视债务做好预防
除此之外,预防永远胜于治疗,技术债务汇总预防的关键点在于那些 “原本未知” 的技术债务要逐渐减少,大家对于实现质量的追求不能止步于“测试没有明显 Bug”,写出能运行的代码是不够的,还要易维护易扩展。而你可以从几个方面着手:
- 提升团队认识,通过项目复盘、系统重构、事故 Review 等各种机会,通过实际的案例让研发同学清楚技术债务对团队产生的负担,以及对个人能力提升的影响。
- 建立机制流程,比如在方案设计阶段向下深挖一下实现的要点,更多资深的开发参与到架构评审,或者促进团队形成 code review 的习惯并且达成一个共识标准以提升系统质量。
- 确保资源投入,在通过债务识别和分级后,将还债的投入提前计算到每次迭代中,确保有一定的资源投入其中。
5. 一些常见的误区
通过 CheckList 做债务识别,然后定期诊断、水平扫描、债务定级、分期偿还来做技术债务的处理,最终在团队认识、机制氛围、资源保障上下功夫做预防,这就是技术债务管理的核心思路。
而这个过程中,有一些问题是日常你很容易走入误区的,我简单总结了一下几个注意点:
- 存在即合理,动态变化才是王道。 不要总想着毕其功于一役,也几乎不太可能有完美的实现或系统,接受技术债务一定会存在的事实,重点在于控制债务积压的程度,欠债本身不可怕,欠债不知且不还才可怕。
- 不积跬步无以至千里。我们往往过度轻视日常微小积累,又过度重视 “大事件” 产生的影响。日常这里凑合一下,那里妥协一点,没人关注小问题发生的原因。而一旦发生重大的影响,则恨不得把之前的系统全盘推翻重做一遍。
- 机制流程外还要讲策略和方法。很多技术 Leader 觉得这件事很重要,讲的同时设计了很多流程和机制,不遵守就要承担怎样怎样的后果,这样往往事半功倍。机制流程不是越多越好,也不能光有惩罚而没有激励,同时最重要的是你不能只追杀要结果,要给帮助、给方法、给支持。
小结
技术债务和金融债务有很多类似之处,金融借债往往是为了解决当前的资金压力,从而在商业上赢得先机,着眼于未来的长远收益。技术债务往往也出于同样的目的,通过当前适当的技术妥协换取业务更早的交付上线,尽可能与业务的发展节奏匹配,在业务的发展与变化过程中不断完善而非一开始就追求完美。
所以,我们并不能将技术债务单纯看作是 “不好的事务”,与金融借债在金融领域中起到货币杠杆的作用类似,适当的技术债务对加速业务发展、推动系统演进是有积极作用的。所以完全没必要谈“债” 色变,放眼现实企业中,不能说没有债务的一定是三流企业,但是一流企业基本都有合理的负债。
但是对于技术债务而言,怎样的债务才是 “适当” 的,就非常考验你的能力了。金融借债会产生利息,如果定期还款则不会产生太大影响,而如果不偿还的话,不仅有利息还会产生高额的违约金,并且随着不还款的次数增加,还款将会变得越来越困难,最终可能导致企业破产。
同样类比到技术领域的系统项目中,大量的技术债务产生的利息就是系统变得难以扩展和维护,而这些问题没有被及时处理就相当于利息和本金没有按期偿还,那么以事故和交付延期为代表的违约金就不期而至了。
所以我们谈及技术债务时,既要清楚它的种种弊端,也要具备正视技术债务的勇气。一方面不能过于追求完美导致因噎废食,另一方面也不能忽略或者无限期推迟技术债务的处理,因为随着时间的推移,技术债务会逐渐积累,最终量变引发质变只能通过休克疗法来解决(暂停一切业务进展,系统推倒重来)。
以务实的态度来对待技术债务,团队在技术战略上高度重视、在日常研发节奏和项目中寻求战术上的平衡,这是我给你的建议,希望能对你有所帮助。
留个作业: 过往让你印象最深刻的技术债务是如何产生的,它导致了怎样的后果,你最终是如何如处理的?如果现在重新 Review,你会改变当初的处理方式吗,为什么?
最后,感谢你的阅读,如果这节课让你有收获,欢迎你将它分享给其他的朋友,我们下一讲见。