作者:来自 vivo 互联网服务器团队- Zhang Mengtao
在项目研发过程中,由于时间、能力等因素往往会出现设计方案没有做到最好或最优、编码质量不够好等问题,技术债的出现是不可避免的,并且随着时间的推移,技术债对系统的影响会越来越大,同时使得对代码和架构设计的更改越来越困难,想要进一步提升效能必须要对技术债进行管理,本文通过在活动中台系统的技术债实践经验,介绍技术债的含义、分类和管理。
一、技术债的含义
1.1 技术债的含义
关于技术债的概念可以追溯到1992年,沃德·坎宁安(Ward Cunningham)首次提出,第一次发布代码,就好比借了一笔钱。只要通过不断重写来偿还债务,小额负债可以加速开发。但久未偿还债务会引发危险。复用马马虎虎的代码,类似于负债的利息。整个部门有可能因为松散的实现,不完全的面向对象的设计或其他诸如此类的负债而陷入窘境[1]。
《维基百科》中提出,技术负债(Technical debt),又称技术债,也称为设计负债(design debt)、代码负债(code debt),是程序设计及软件工程中的一个比喻。指开发人员为了加速软件开发,在应该采用最佳方案时进行了妥协,改用了短期内能加速软件开发的方案,从而在未来给自己带来的额外开发负担。这种技术上的选择,就像一笔债务一样,虽然眼前看起来可以得到好处,但必须在未来偿还。软件工程师必须付出额外的时间和精力持续修复之前的妥协所造成的问题及副作用[3]。
如下图所示,技术债在研发人员的日常工作付出中占据了一定的比例。
1.2 技术债的危害
我们可以从效率、质量、体验三个方面来看:
1.2.1 效率
这是最直接的影响,当技术债不断增加,软件系统会变得非常脆弱。这种脆弱主要是由不良的架构设计或代码设计导致,不管最初是选择了划分良好的微服务架构,还是单体架构,技术债不断打破设计原则,让原则不复存在,以至于很难理清系统组件之间的关系和职责。当修改其中的一部分组件时,其他组件也会牵连,可能会陷入恶性循环。
1.2.2 质量
以上图为例,从研发持续交付角度来分析的话,在项目版本迭代前期,业务功能较少的情况下,高质量要求的项目可能会比低质量要求的项目迭代速度慢一点,但是随着项目逐渐发展,高质量要求的项目对比低质量要求的项目迭代速度显著提升,技术债的持续累积是导致质量下降的关键原因,技术债是无法避免的,因此技术债的有效管理和消除是我们保障高质量软件的必不可少的方式之一。
1.2.3 体验
软件产品需要不断演进才能在长时间后依然还能适应市场,才能具有较强的生命力。相反,有的软件在经过几年的开发之后,随着技术债的增加,已经变的很难维护,很多时候只能被推倒重写,主要是不断叠加的技术债导致。技术债的叠加不断增加系统的复杂性,从而开发的成本逐渐增高,每次迭代都需要解决设计不足或技术所带来的问题。
二、技术债是怎么产生的?
技术债的出现是不可避免的,但是不同的场景下会产生不同的技术债,带来的影响也是不一样的,如果按照健康角度分类的话,对于研发人员允许出现的技术债可以划分为健康的一类,对于研发人员尽量避免的技术债则划分为不健康的一类。我们可以从以下四个维度进行分析。
2.1 冲动/有意 - "没有做更好的设计"
研发团队虽然识别到这样做会导致技术债的积累,但是不清楚带来的后果,没有去做更好的设计方案。比如项目在线上运行时,突然出现了一个线上问题,如果不尽快修复上线,就会造成很大的损失,这种情况下,已经无法再针对问题作详尽的设计方案,现在需要以最快的方式修复上线,这种情况下往往不会考虑更好的设计方案,而是以最快捷的方式解决问题。对于当下的临时方案在未来会带来什么技术债,研发人员并没有关注。
2.2 谨慎/有意 - "必须尽快交付"
当研发团队面临业务压力时,例如在发布新产品时需要快速上线以占领市场时,快速解决问题的重要性常常超越了更好的实践。在这种情况下,团队往往会选择快速完成产品交付,然后再处理技术债务。团队清楚这样做会带来技术债务,也知道逾期还债的具体后果,以至已经安排好了未来的改进计划。这种场景很常见,是已知技术债的一种主要来源。
2.3 冲动/无意 - "不知道怎样设计更好"
这个维度技术债务产生的原因通常是由于人员技能的不足。在实际研发中,不可能保证所有人的水平都是一样的,由于缺乏相关技能,研发人员可能不清楚如何编写更优秀和精炼的代码,不知道如何设计更好的架构或者给出更佳的解决方案,这种情况下,研发人员按照自己的理解和设计方案进行工作,可能会带来一部分技术债。不管怎样的团队,人员的更替都是避免不了的,可能对项目不够熟悉,对某一块功能不够熟悉,短时间内快速理解并给出设计方案,可能会有很大难度,个人的经验不同,认知不同,在实现相同的功能时选择的方案也是不同的。
2.4 谨慎/无意 - "现在有更好的方案"
随着团队成员的能力提升或者行业技术上的演进,对于之前认为的最佳方案现在看来并不是最好的解决方案。但在当时,可能并不知道有更好的做法。这种技术债确实也是无法避免的,甚至会经常遇到,最简单的是基于当下的经验甚至业界最优的一些实践选择技术方案或者技术框架。可能在之前做这块功能的时候,团队成员已经对当时的方案达成了一致,认为是最优的方案,但是现在突然发现有更优的方案,那么为了项目的长远发展、稳定迭代,同样需要对项目作最优方案的替换。
以上将技术债务分为四类。我们通常认为,健康的技术债是右边的两个维度,不健康的技术债是左边的两个维度。基于此我们可以分析技术债产生的原因并制定相应的改进措施。
-
对于冲动/有意类型的技术债,我们可以在日常的研发过程中拟制严格的规范,加强流程化的研发管理,让我们的研发人员对最优设计达成习惯和规范;
-
对于谨慎/有意类型的技术债,我们可以和产品达成一致,每次面临这种紧急需求,或者需要快速上线而没有采取最优方案的情况,可以直接记录,后续尽快优化;
-
对于冲动/无意类型的技术债,我们可以对所有的项目成员进行能力和认知的提升,尽可能让研发人员在熟悉功能和项目的情况下进行研发,另外可以增加设计方案评审流程和代码提交评审流程,有效减少这种类型的技术债;
-
对于谨慎/无意类型的技术债,我们需要在识别到的时候第一时间记录下技术债,并且根据研发排期合理的安排时间进行修复。
为了保证产品持续的竞争力,上面几点只是方法,如果没有成本上的投入,只能沦为空谈。从整个产品团队,都要提升对技术的正确理解,技术的构建并不是一劳永逸的,是需要不断的成本投入来维护的。
三、技术债管理实践
3.1 一个技术债真实案例
下面以一个项目中遇到的真实技术债案例来介绍,活动中台系统是一个面向用户的中台项目,可能每天都会产生大量的活动数据,即使进行了大量的分表处理,但是动辄千万级的数据仍然给数据库操作带来了一定的负担,所以我们目前沉淀了一套通用的数据库数据清理方案,根据不同类型的表配置不同的清理策略,基本参考维度是数据的产生时间和活动状态,如果一个已经结束的活动且数据产生时间大于半年则直接删除。这种方案虽然通用,但是在线上发现了严重的慢SQL问题,即使是通过离线库操作,仍然会让系统存在一定的风险,显然这个问题需要关注。
3.2 原来的做法
在原本的做法中,研发团队对于项目产生的技术债采取的方案是随机修复,也就是发现后会简单的记录下,如果是紧急问题则会同步项目组尽快上线,如果是非紧急问题,则会在下次版本迭代涉及该模块时进行修复,或者在版本gap期间随机进行修复,缺乏系统性的管理,往往可能会导致问题的遗漏,并且对于技术债的修复缺乏系统的分析和判断,虽然在有意识的修复技术债,但是效益容易被忽略,往往看不到真正的价值。
对于这个数据清理带来的慢SQL问题,虽然会产生慢SQL,但是对线上业务影响较小,所以优先级不高,暂时搁置,待到项版本有空闲人力再去优化,这个问题由于是技术侧单纯的技术优化,所以没有纳入需求列表,单纯的依赖开发人员人工记忆,等到人力空闲时再想是否有问题需要优化,这个时候才想到尘封已久的问题。方案的变动需要测试的介入来回归功能,这个时候开发人员想要优化,可能测试人力紧张,没能及时开发和测试,功能的上线又会搁置。
3.3 新的做法
按照现有方案,我们已经形成了一套稳定的技术债机制,相应的按照以下步骤进行处理:
3.3.1 识别技术债
想要管理技术债,首先就是要识别技术债,技术的持续改进离不开团队中每个人的努力,因此需要每个成员都积极参与。通常我们在识别技术债的时候可以从以下几个类别去筛查。
当我们发现这个数据清理问题时,可以直接记录在技术债跟踪列表中,记录技术债的所属项目、问题描述、创建人、处理人、创建时间、修复时间、技术债状态、规划版本、备注等属性,便于技术债的跟踪和审视。这种跟踪表只是一种方式,我们还有各种看板、空间可以用来记录,项目内达成一致即可。
3.3.2 分析技术债
记录技术债之后,时常会遇到的问题是,需要改进的地方太多,尤其是对于遗留系统。怎么办?分析优先级。我们可以基于价值/成本矩阵来评估改进任务的价值和成本。基于下图的价值-成本矩阵,我们会:
-
优先解决高价值+低成本的技术债;
-
尝试将高价值+高成本的技术债拆分为高价值+低成本的技术债,逐步解决;
-
在没有高价值+高/低成本的技术债时,再来考虑低价值+低成本的技术债;
-
最后如果只剩下低价值+高成本的技术债,还是先拆分,再解决,或可考虑直接移除。
3.3.3 解决技术债
分析完技术债的价值、成本、优先级、方案,我们可以在版本的gap期间跟随版本修复技术债,如果某部分功能刚好规划在版本中,那我们技术债的修复刚好由测试一起回归,这样可以做到工作量最小化,如果是高优的技术债,我们就需要尽早安排修复,紧急线上问题更是需要迅速迭代小版本上线。当然,修复完一定要及时更新技术债的状态。对于数据清理这条技术债,我们分析后得出结论:对于系统稳定方面有影响,需要一次性彻底优化,价值较高,优先级较高但是无需紧急修复上线,所以在下一个版本人力空闲期间就可以伺机修复上线。
这里要注意的是,对于同一条技术债,记录的人、修复的人可以不是同一人,这里需要技术债记录详细,便于方案的执行。数据清理这条技术债我们在随后的一段时间内就整改上线,完成了这条技术债的修复。
3.3.4 阶段审视
在实行技术债机制的管理过程中,建议进行阶段审视,查漏补缺。我们已经详细记录了技术债的所属项目、问题描述、创建人、处理人、创建时间、修复时间、技术债状态、规划版本、备注等属性,便于技术债的跟踪和审视。可以从多方面审视,一个是统计数据,如下表,可以看出每个阶段的修复数量、新增数量、上线总数、正在修复的数量和待开始的数量。这也是衡量项目质量的影响指标之一。
另外一种是趋势图,我们可以从下图这样的折线图明显看出不同状态的技术债的趋势,包括不同阶段的技术债新增数量、修复数量、修复中数量和技术债总数趋势变化。
除此之外我们可以观察团队成员在修复技术债方面的工作量体现,比如统计一年不同季度、不同团队成员每个季度对于技术债的修复数量,都是一些阶段审视的方式。
3.4. 技术债管理机制
3.4.1 明确管理机制和责任分配
团队在针对技术债的治理过程中一定要确定主要责任人,虽然解决团队技术债问题是所有团队成员的责任,但是为了管理流程化、合理化、最优化,往往需要指定一个负责人专门跟踪技术债的管理。除此之外,技术债的管理机制要在团队内部达成高度一致,整个团队对于技术债问题的认知、修复、管理都是经过正式裁决的。
3.4.2 主动预防原则
通常来说,开发人员能直观感受到技术债的坏处,大都愿意去偿还技术债,所以技术债累积的主要原因是,没有认识到技术债累积给业务发展带来的巨大坏处。这也就意味着,解决技术债的第一步就是,要意识到偿还技术债的重要性,从而愿意投入资源去解决。对主动引入的技术债,要尽量让管理层和产品团队了解技术上的捷径将会带来的长期危害,尽量减少技术债的引入。
3.4.3 高价值优先原则
需要遵循高价值优先的原则来优先修复技术债,上面我们已经分析过技术债问题的价值/成本矩阵,对于分析结果,我们在版本中解决技术债问题的时候就要严格按照高价值优先的原则去修复,而不是根据技术债发现的时间,或者是成本低的技术债优先修复,切忌随机修复技术债。
四、经验总结
4.1 技术债是不是越少越好?
当然不是!提到技术债,我们想到的往往是它的坏处,比如难以维护、难以增加新功能等,但实际上它也有好处。关于技术债的好处,我们可以对应着金融领域的经济债务来理解。我们都知道,经济债务最明显的好处在于,可以帮助我们完成很多本来不可能完成的任务,比如贷款买房。相应的,技术债可以在短期内帮我们快速完成业务开发,满足用户需求,就类似房贷的作用。当研发团队面临业务压力时,例如在发布新产品时需要快速上线以占领市场时,快速解决问题的重要性常常超越了更好的实践。在这种情况下,团队往往会选择快速完成产品交付,然后再处理技术债务。团队清楚这样做会带来技术债务,也知道逾期还债的具体后果,只要有计划的进行优化即可。
4.2 持续管理技术债带来的益处有哪些?
-
提升系统稳定性:不断规范化的解决更多的技术债有利于系统更加稳定;
-
形成稳定机制:活动中台系统经过长时间的实践,逐渐形成稳定的技术债管理机制,面对项目中的技术债不再头疼如何跟踪;
-
提升项目质量:随着技术债机制的实行,项目成员在迭代时就会更多的考虑技术债方面的问题,久而久之项目质量也会有所提升。
4.3 技术债是否可以作为项目管理的重要指标之一?
技术债已经成为很多项目管理的一个重要指标,用来衡量项目整体的研发效能,虽然本文没有过度涉及技术债在研发效能工具方面的体现,但是在这个过程中工具很重要。
参考文档:
[1] Ward Cunningham,《WyCash 投资组合管理系统》,ACM SIGPLAN OOPS Messenger4, no.2 (1992)
[2] https://www.productplan.com/glossary/technical-debt/
[3] https://zh.wikipedia.org/wiki/技术负债#cite_note-oopsla92-1
[4] Philippe Kruchten, Rod Nord, and Ipek Ozkaya, 《管理技术债务:减少软件开发中的摩擦》(Addison-Wesley, 2019)