前言
在现代软件开发中,技术债务(Technical Debt)已成为一个不可忽视的重要概念。它指的是在软件开发过程中,为了追求短期目标而做出的妥协,导致未来需要付出额外成本来修复这些妥协所带来的问题。技术债务的管理不仅影响项目的质量和可维护性,还直接关系到团队的工作效率和公司的长期发展。

什么是技术债务?
技术债务的定义可以追溯到Ward Cunningham,他将其比喻为“借款”,即在软件开发中选择了一个较为简单的解决方案,而非最佳实践。这种选择虽然在短期内能加快开发进度,但却可能在未来导致更高的维护成本和更复杂的技术问题。

为了提升需求开发速度,我们有时会在应当采纳最佳方案时作出妥协,转而选择那些短期内能迅速推进项目进程的方案。然而,这种做法往往会导致未来的错误问题增多,并给自己带来额外的开发负担。这种技术层面的抉择,犹如背负了一笔债务。

技术债务的分类
同样,技术债务也有其层次之分,通常根据其影响程度和解决的紧迫性,我们可以将其划分为四个象限,即技术债务的四象限模型。

技术债务可以根据不同的标准进行分类:
有意和无意两种:
-
有意技术债务:团队明确意识到当前的解决方案并非最佳选择,且计划在未来进行改进。
-
无意技术债务:团队在缺乏足够信息或经验的情况下做出的选择,往往潜伏较久,难以察觉。
鲁莽型和谨慎型:
-
鲁莽技术债务:由于缺乏规划和规范而产生的债务,通常会导致严重的后果。
-
谨慎技术债务:在项目进度中做出的合理妥协,虽然存在风险,但在可控范围内。

技术债务全景图
根据卡内基-梅龙大学软件工程研究所(SEI)的Robert Nord在《The Future of Managing Technical Debt》中提出的“技术债务全景图”,技术债务可以从多个维度进行分析:

这张全景图清晰地展现了技术债务的多个层面,包括那些通常与架构相关联的债务、因环境变化而产生的技术差距型债务,以及主要由内部代码质量低下引起的小粒度技术债务。
此外,通过这张图,我们还可以洞察到两个重要的方向。
可演进性
本质上,架构的元特征描述的是软件架构在演进过程中趋于目标的能力。这种演进目标并不仅限于支撑功能快速迭代的灵活性,同样可以涵盖其他重要的架构属性,如高可用性和可扩展性。
可维护性
狭义上的代码问题主要涉及代码的易理解性、问题的易修复性,以及在现有基础上的易扩展性。这些因素共同影响着代码的质量和可维护性。

背景
当技术债务已经严重影响了公司的运作、工作的效率时,才会着手去处理这些方面的问题,可要付出的代价就太大了。

随着滴滴国际化业务的快速发展,技术栈的多样化使得技术债务的管理变得愈发复杂。当前,研发所需的语言已不仅局限于平台原生语言,跨平台技术(如Flutter及其Dart语言)逐渐成为主流。这一转变带来了新的技术债务挑战,尤其是在代码质量和架构设计方面。

演绎过程
在项目的初期阶段,我们面临着快速上线和快速迭代的压力,因此问题的积累是不可避免的。然而,随着我们的不断发展,我们意识到如果继续这种状态,将会对项目的质量和成本带来双重风险。同时,随着业务逐渐融入Flutter跨平台能力,我们在学习和研发Flutter的过程中,也遇到了诸多挑战。特别是随着Flutter SDK的不断升级,我们在这个过程中也积累了大量的技术债务,例如空安全适配问题。目前,我们的SDK版本支持2.12.0,这是一个允许空安全和非空安全混合的版本,因此存在很大的隐患。

影响分析
对开发的影响
也许某一天我们接收了一个陌生的模块,也许是自己曾经的代码,发现如同屎山一样,如下图,自己都看不懂了,为了应付快速迭代的需求,只能不停的往这上面堆,这个屎山也会愈发庞大和混乱,如果这样继续下去,知道某一天因为一个小小的Bug,你需要花半天的时间来排查问题出在哪里,最后当你觉得问题终于改好了的时候,却不料碰了不该碰的地方,结果就是 fixing 1 bug will create 10 new bugs,甚至程序的崩溃。

我们需要正确面对、积极面对这个事情,它不是没有技术含量,他能给我们带来更多的技术和业务上的挑战。
对效率的影响
技术债务的治理本质上是提升效率的过程。
治理不当将导致开发周期延长、资源浪费和团队士气下降。因此,及时识别和解决技术债务是确保项目成功的关键。


现状梳理
业务发展至今,通过整理存量问题和结合监控报警沉淀的问题,可以看出目前工程存在的问题方向。(非最全)

代码复杂
在快速迭代过程中,往往忽视了良好的代码组织与模块化设计,导致组件间出现高度耦合的现象。此外,类文件行数过长也是一个需要关注的问题。这些问题可能会影响代码的可读性和可维护性,进而降低开发效率。因此,我们需要重视代码结构的优化和模块化设计,以降低组件间的耦合度,并合理控制类文件行数,从而提高代码质量。
架构混乱
-
业务架构在从初期到后期的迭代过程中,经历了逐步分化和尝试等阶段,尚未形成统一的结构走势。
-
目前存在新旧架构混合使用的情况。
代码风格
在Flutter跨平台代码中,代码结构和规范风格的不统一是一个常见问题。这主要是由于不同IDE的使用,很容易引发代码风格上的冲突,进而带来潜在的风险。为了提高代码的可读性和可维护性,降低风险,我们需要确保在整个代码库中保持一致的代码结构和规范风格。
基建混乱
在业务组件基建和服务基建方面,由于缺乏统一的最佳实践范式,导致使用层的代码出现混乱。这一问题亟待解决,以确保代码的整洁性和可维护性。
工程效率
管理缺乏统一性,功能分散,导致功能可用性低下。
性能债务
-
解决由持续SDK升级触发的报警问题
-
优化内存管理
-
清理未使用的资源和已下架的代码
-
实施lint代码质量检查与治理
-
进行空安全适配(针对Flutter框架)

目标

产物目标
一套简易可视化运营平台
-
结合Lean平台,对Lean平台录入信息,通过脚本产出自定义报告。
-
增加巡检能力,周频次跟进问题状态。

稳定性目标
沉淀问题,整合债务
通过深入剖析问题和细致整合债务,将技术债务治理提升为确保系统稳定性的关键环节。

质量目标
提高代码质量
-
提高代码质量,确保代码符合Lint标准。
-
完成Flutter的空安全适配。

面对挑战

业务挑战
-
稳定性的设计需要针对老业务流程进行二次梳理,如何保证线上稳定性是关键。
-
在需求中不断植入Fix的问题,确保技术债务的及时解决。

技术挑战
-
优化思路和设计需要更全面的考虑。
-
如何通过架构设计更完美地落地修改和重构。
-
对技术的深入理解是实现更好方案的基础。

治理方案

债务整体架构

方向:通过对当前问题的归纳,合理划分技术债务的治理方向
-
业务架构:主要以业务代码实现为主,最佳范式等;
-
基建:对接底层基建能力,使用上不合理不舒服的点。
-
代码:实施代码规范、Lint治理和代码格式化。
-
效能:所有辅助程序运行的脚本能力。
治理运营:一套可持续运行的方案
任何人不管问题的大小,都可以畅快的提出问题(不是谁提出谁修改,你只管提),把痛点问题进行描述,通过Lean平台记录,并会有专门的人(提出人)标记方向(分类),负责的同学或者感兴趣的同学都可以进行认领,并做出合理的方案和同步进度。
沉淀:
通过这些点沉淀能力,不断的下沉能力,解耦业务。
建设:
-
非开源:可以按照产物进行成果分享。
-
开源:是最好的状态,开源我们一些厉害的能力,无论是什么方向的结果。

债务治理机制
为了解决这些问题,我们决定采取一系列措施来优化我们的项目和技术栈,并最终沉淀出一套我们可执行可长期运营的方案。

识别
-
技术的持续改进离不开团队中每个人的努力,因此需要每个成员都积极参与。在日常交付中,团队成员应该持续识别和记录需要改进的问题并将其放入Lean平台中,以便在技术改进会议中与团队同步。
-
此外,团队还可以定期组织头脑风暴,以收集技术痛点和改进建议。
通过一个共享平台,我们可以高效地录入和展示信息,从而更清晰地一览所有问题的分布情况和各类问题的占比。这样的设计不仅提升了工作效率,还使得数据分析更加直观、便捷。
优先级
我们时常会遇到的问题是,需要改进的地方太多,尤其是对于遗留系统。怎么办?先排优先级。我们可以基于价值/成本矩阵来评估改进任务的价值和成本。

基于矩阵:
-
优先解决高价值+低成本的技术债。
-
尝试将高价值+高成本的技术债拆分为高价值+低成本的技术债,“尽早、频繁、小批”地进行PDCA(Plan/Do/Check/Adjust)的迭代解决。
-
在没有高价值+高/低成本的技术债时,再来考虑低价值+低成本的技术债。
-
最后如果只剩下低价值+高成本的技术债,还是先拆分,再解决,或可考虑直接移除。
债务问题,是无法集中清理的,每个人每天都有自己的业务需求,所以我个人认为可以存在两个进度方式:
-
在版本跌在中,引入20%的工作量进行技术任务的改进。
-
在技术架构升级或者重构等方案中,评估债务任务的清理。
总结&公式
-
每周固定时间进行报告产出。
-
存在这个能力,就能更好的整体观测治理的大盘,有助于促进治理的节奏,不会石沉大海。
-
让债务问题成为一个话题榜,成为一个论坛贴。从枯燥变成一种可持续输出知识点的平台。

我认为在技术债务管理中,这一步是很重要的,我们需要分享解决的思路和成果,它是具有价值的,分享的过程是信息同步也是团队认知的对齐。

(自定义报告生成平台)
总结
通过这套精心设计的治理机制,我们能够高效地对问题进行细致分类,并实现有条不紊的治理。结合先进的可视化平台Lean+报告产出平台,我们可以实时追踪问题的处理进度。这一机制不仅帮助我们成功解决了问题,还促进了宝贵知识的积累和共享,形成了丰富的知识库。

稳定性方向沉淀
公司层面一直在推进稳定性,通过事前、事中、事后三个阶段进行预防、监控、复盘总结,形成一个闭环流程。

在技术债务治理方面,我认为它扮演着事前过滤器的重要角色。通过运用技术债务治理的手段,我们可以有效地识别并处理许多潜在隐患,从而为系统的稳定性奠定坚实基础。
在整体稳定性的三个阶段中,事中和事后阶段能够持续揭露技术债务问题的多维面貌,并对其进行系统归纳与整理。这些宝贵的经验教训随后被转化为事前阶段的预防性过滤网,从而助力我们更加精准地预防和应对未来潜在的问题挑战。
总结
在事中、事后阶段,我们要能够及时发现并沉淀归纳债务问题,确保问题留痕、总结到位,并达成共识,以便更出色地完成事前规划的任务。

代码质量治理
lint治理
从此时此刻起:债务问题不再新增,趋势线逐步下降;
严格执行代码规范、CR机制、Dlinter统一规范,提交高质量代码。

工程配置规则、远程代码配置规则开启,自动reject。(Flutter 配置为主)

空安全适配(Flutter)
我们强烈建议你按顺序迁移代码,先迁移依赖关系中的处于最末端的依赖。例如,如果 C 依赖了 B,B 依赖了 A,那么应该按照 A -> B -> C 的顺序进行迁移。

迁移时序层
从底层服务类出发,逐层向上适配。

总结
技术债务是项目进程中难以避免的现象,然而,如何将其控制在可管理范围内,却是我们必须深思的问题。要想有效预防和化解技术债务,离不开优秀开发人员的贡献,而团队的协同合作更是至关重要。借助这套精心设计的治理机制,我们能够对各类问题进行科学分类和有序治理。同时,结合高效的可视化平台,我们可以实时追踪问题进展,不仅及时解决现有问题,还能在此过程中积累并提炼出大量宝贵的共享知识资源。
结论
技术债务在现代软件开发中屡见不鲜,然而,借助高效的治理策略与团队的协同合作,我们完全有能力将其潜在影响压缩至最小。不懈的学习与持续的改进乃是项目取得成功的基石,唯有如此,我们方能在日新月异的技术浪潮中稳立潮头,保持强劲的竞争力。
技术债务是一个普遍存在的问题,它不受平台或编程语言的限制。我们应该共同努力,不断学习和提升自己,以应对这一挑战,共同推动技术进步。
本期话题互动
关于”技术债务“欢迎在评论区分享出你的见解,与我们一同探讨!我们将为高赞TOP10的同学,送上“滴滴工程师人手一个”的双肩背包!
本篇文章来源于微信公众号:滴滴技术
本文来自投稿,不代表TakinTalks稳定性技术交流平台立场,如若转载,请联系原作者。