在 1992 年 Ward Cunningham 在博客中提出技术债这个概念后,技术债这个比喻因完美地表达了遗留技术问题的影响,被一直沿用至今,且一直是行业内关注的焦点。如今各大企业为了建立持续交付的能力,快速响应市场的变化,也纷纷开始提升研发效能,技术债更是不可忽视的关键因素。
技术债的分类
(图片源自 MartinFowler 的博客:TechnicalDebtQuadrant)
按照技术债产生的原因,Martin Fowler 将技术债按照鲁莽的(Reckless)/谨慎的(Prudent) 以及故意的(Deliberate)/无心的(Inadvertent) 划分到 4 个不同的象限中。我们分别来解释下这几种不同的技术债:
1. 谨慎的且故意的
这种场景很常见,是已知技术债的一种主要来源。为了让产品快速投入市场,获得更大的收益,通常团队会选择更快速的方案,开发成本低,时长短,但解决方案并不是最优,且可能只是临时方案。然而,团队对技术选择做了详尽的评估,了解技术债产生后给产品和架构会带来什么影响,后果是可估量的,甚至已经安排好了未来的改进计划。
2. 鲁莽的且故意的
相比上一个分类而言,团队知道当前的方案不是最优选择,但通常由于时间紧迫,实现当前的业务需求是第一优先级的,未对当前的方案做细致的分析,因此对遗留技术债可能产生的影响是未知的,甚至具体产生了哪些问题也可能是未知的。
3. 鲁莽的且无心的
不计后果而又是无心之举,这往往是因为团队成员的认知还不足以判断当前的选择是不是会带来不良影响。这种技术债在技术债总体的占比很高,甚至可能比上面提到的第一种技术债更多。因为不管怎样的团队,人员的更替都是避免不了的,个人的经验不同,认知不同,在实现相同的功能时选择的方案也是不同的,虽然可以通过一些社交活动来减少不同团队成员的认知差异,如代码评审,但想通过这种方式来避免技术债的产生,效果往往并不是很好。
4. 谨慎的且无心的
这种技术债看上去让人难以理解,既然每次都是深思熟虑,为什么还会有无心之失。然而,这种技术债确实也是无法避免的,甚至会经常遇到,最简单的莫过于当下基于当下的经验甚至业界最优的一些实践选择的技术方案或者技术框架,而随着技术的进步和发展,它们的弊端和问题也会逐渐显现出来,这些过时的技术方案设计和框架也就成了技术债。
技术债的影响
不管是上面的何种原因,技术债一旦产生,都会对软件系统产生或大或小的影响。技术债涉及到的具体内容也非常广泛,一切不合理的设计或妥协都可以称之为技术债,比如:
- 因认知不足或成本原因而产生的不良架构设计或代码设计
- 因能力或认知不足而选择非最优的技术框架或选型
- 因成本或进度原因而没有完成的测试代码
这些技术债短期都不会表现出明显的问题,但在项目长期迭代过程中,随着技术债不断的增加,软件系统的可维护性会直线下降,随之而来的是质量的下滑,这给后续的产品开发带来了非常大的问题。
1. 产品质量下降,生产事故频发
这是最大最直接的影响,当技术债不断增加,软件系统会变得非常脆弱。这种脆弱主要是由不良的架构设计或代码设计导致,不管最初是选择了划分良好的微服务架构,还是单体架构,技术债不断打破设计原则,让原则不复存在,软件系统都将走向大泥球,没有人能理得清系统组件之间的关系和职责。当修改其中的一部分组件时,其他组件莫名奇妙受到影响,而且这种情况通常只有上线之后才会被发现,整个团队将陷入不断救火的循环中。
2. 系统可维护性差,开发效率低下
一个软件产品如果在5-10 年后依然还能很好的响应市场的变化,说明软件的设计也在逐渐演进,还具有很强的生命力。相反,很多软件在经过几年的开发之后,随着技术债的增加,已经变得不可维护,大部分情况下只能被推倒重写。
这种不可维护都可以归功于不断叠加的技术债,技术债的叠加不断增加系统的复杂性。在这些技术债没有解决之前,开发的成本逐渐增高,每次变更都需要解决不良的设计或落后的技术选型所带来的问题。
举个例子,一个简单的业务需求实现在设计有缺陷的软件架构上,修改已存在的复杂逻辑还不如重写来的简单,相信很多人曾有过这种感觉。重写只需要理解新需求就可以了,而修改需要理解旧的实现,找到新旧之间的差异。重写可以直接采用较优化的方案,而修改可能在已有技术债的基础上,要么解决技术债的问题,要么找出一个绕行的方案,实现成本通常是更高的。
如何避免和解决技术债
理解了上面技术债产生的原因,就知道想要彻底避免技术债的产生是不可能的。产品在演进,技术在更新,代码一旦写出来就需要花费额外的成本来维护,再加上团队人员的变化,上下文的丢失,很难做到时时保持系统在 100% 健康的状态。
然而,我们却可以根据技术债产生的原因,来分析如何避免和解决技术债。鲁莽的/谨慎的可以理解在技术债产生时,团队是否做出来细致的分析,其结果可以对应到无解决方案/有解决方案。而故意的/无心的表达的是对于技术债的产生是已知的,还是未知的,可以对应到已知问题/未知问题。这样就可以将问题的解决也同样划到四个不同的象限。
对于第一象限而言,已知问题,也有解决方案,只需要将技术债的解决列入计划实施起来就可以了。而对于其他象限,都无法直接列入计划解决,因此,我们需要通过一些手段让技术债从认知和方案层面向第一象限移动。第三象限的技术债,团队认知水平很低,只能通过提升团队的整体能力和认知水平,让问题逐渐清晰,向第二和第四象限转移。为了尽快解决团队内的技术债,可以尝试以下几点:
1. 技术债解决日常化
研发团队对于架构的守护要像产品人员对待产品一样,架构健康和产品质量一样重要。在项目日常开发中,除了产品的业务需求外,应该规划一部分工作用于架构的优化,修复那些已知且有解决方案的技术债,这样才能持续保证软件系统的响应能力和产品质量。
2. 明确技术规范,加强管理
对于已知且无解决方案的问题,只是没有深入思考对系统的影响,对于这部分技术债,产生的原因主要有两个:
- 团队没有技术规范标准,即便发现问题,也没有方案
- 缺少对架构修改的审查,开发人员自己思考解决方案,依赖个人经验
为了让这些技术债在产生前就避免,或者引导到可以快速解决的方向,首先,需要建立团队的技术规范和标准,让每个决策都有依据。其次,加强流程上的管理,建立架构评审委员会,对架构的修改进行评审,一方面规避问题,另一方面根据问题完善规范和标准。
在 Neal Ford、 Rebecca Parsons 的著作《演进式架构》中提出了“架构适应度函数”(Architecture Fitness Function)的概念,当团队有了技术规范和标准,可以通过构建架构适应度函数,来检查和约束架构的变更或代码的修改,帮助我们发现和避免一些新技术债的产生。
3. 持续关注技术发展趋势,提前规划架构的演进方向
随着技术的不断发展,很多几年前被认为非常先进的技术表现出了一些弊端,也逐渐在被新的技术替代。研发团队需要持续保持对技术发展趋势的关注,探索是否有更优的解决方案,在日常的开发和运维过程中,要做到以下几点:
- 明确当前团队的技术选型优势和问题,深入理解团队面临的问题
- 对于所选择的技术框架版本持续关注,定期升级到最新的稳定版本
- 关注新技术的趋势和动向,探索是否有更优的解决方案,新的技术是否经过成熟的验证,提前规划架构的演进方向
关注新技术不代表一味追求新技术,首先要明确现有的技术选型到底有哪些解决不了的问题,而在选择新技术的时候也要思考它的弊端在哪里,是否有很大的影响。
4. 技术债可视化
通常可视化是一个非常快速能帮助团队发现问题以及强调问题的最直接的办法,任何言语也无法媲美视觉的冲击。但要注意的事,技术债粒度可大可小,优先级也各有不同,并不是所有的技术债都需要立即修复,技术债的产生在某些情况下就是为了快速的追求产品的市场价值而存在的,需要不断的权衡。
团队需要将识别到技术债时及时可视化出来,判断技术债所处的象限,判定优先级,通过内部流程将技术债的状态进行转移,进而可以通过上面的方法进行解决。
5. 保持技术优化相关投入
这个建议更多是给团队的高层管理者,在激烈的市场竞争中,产品的开发可谓争分多秒,管理者们一定更愿意花钱在看得见的产品上。一旦技术框架基础已经奠定,会逐渐缩减在技术侧的投入,这其实也是大部分产品的软件系统技术债逐渐增多的一个非常重要的原因,产品的快速演进,进度的压力无疑是技术债产生的最大元凶。
为了保证产品持续的竞争力,上面几点只是方法,如果没有成本上的投入,只能沦为空谈。从整个产品团队,都要提升对技术的正确理解,技术的构建并不是一劳永逸的,是需要不断的成本投入来维护的。