如何解决技术债

简介: 本文介绍了技术债的概念及其影响。技术债是指在开发过程中因选择快速解决方案而非最优方法而产生的额外工作量。文章指出,技术债可能导致项目中出现如流水线失败、无用代码、难以理解的代码等问题。还强调了管理技术债的重要性,因为它会影响软件的交付速率和质量。有效的管理包括识别技术债、可视化问题、分析优先级、制定执行计划和持续改进。建议团队通过价值/成本矩阵来确定优先解决的技术债,并通过建立技术规范、服务责任人制度和持续关注技术趋势来预防和解决技术债。此外,应确保持续投入资源进行技术优化,并与团队和客户分享改进成果,以维持软件的高质量和稳定性。

一、什么是技术债

基本定义

关于技术债的定义,维基百科的解释是:由于现在选择简单(有限)解决方案而不是使用需要更长时间的更好方法而导致的额外返工的隐含成本。

二、技术债的影响

在项目中,你是否曾经遇到过这样的问题:

  • 随机失败的流水线
  • 无处引用的文件和方法
  • 令人费解的代码实现
  • 大跌眼镜的无效测试
  • 无处追溯的基础设施配置

在项目中,每一个人或多或少都遇到过以上问题。当开始新功能的交付时,每一个问题都可能成为快速且成功交付的阻碍。

如何尽可能地减少或避免这些问题?如何确保项目的稳定交付?除了遵循良好的实践和设计,统一规范之外,对技术债务的有效管理也是其中不可或缺的一环。

三、技术债产生原因

Martin Fowler 提出了著名的TechnicalDebtQuadrant

鲁莽/有意 - “我们没有时间去设计”

团队虽然意识到这样做会导致技术债的积累,但却不清楚具体欠下债务的后果。为了提醒团队,可以增加相关流程和责任人。

谨慎/有意 - “我们必须现在交付,之后再处理因为追求速度所产生的结果”

当团队面临业务压力时,例如在发布新产品时需要快速上线以占领市场时,快速积累用户的重要性常常超越了更好的实践。在这种情况下,团队往往会选择快速完成产品交付,然后再处理技术债务。团队清楚这样做会带来技术债务,也知道逾期还债的具体后果。

鲁莽/无意 - “什么是分层?”

这个维度技术债务产生的原因通常是由于人员技能的不足。由于缺乏相关技能,开发人员可能不清楚如何编写更优秀和精炼的代码,如何设计良好的架构或者什么是更佳的解决方案。因此,导致技术债的出现和持续积累。这种情况需要加强团队成员的技能培养。

谨慎/无意 - “我们现在知道应该怎么做了”

随着团队成员的能力和对系统的了解的不断提升,使得之前认为是最佳方案的解决方案现在看来并不完全准确。但在当时,团队中没有人知道更好的做法。

以上将技术债务产生的原因分为四类。我们通常认为,健康的技术债是右边的两个维度,不健康的技术债是左边的两个维度。基于此我们可以分析技术债产生的原因并制定相应的改进措施。

四、技术债类型

关于技术债的类型及常见问题主要有:

五、为什么要管理技术债

Martin Fowler 在 Is High Quality Software Worth the Cost? 一文中从添加功能 / 时间两个维度来对比高质量的软件低质量的软件在交付速率上的差别。

我们如何保持高质量的软件状态及交付速率?

技术债的持续累积是导致质量下降的关键原因,但技术债无法避免,因此技术债的有效管理和消除是我们保障高质量软件的必不可少的方式之一。

六、如何管理技术债

关于技术债管理的流程,主要分为六个步骤:

6.1识别

技术的持续改进离不开团队中每个人的努力,因此需要每个成员都积极参与。在日常交付中,团队成员应该持续识别和记录需要改进的问题并将其放入backlog中,以便在技术改进会议中与团队同步。此外,团队还可以定期组织头脑风暴,以收集技术痛点和改进建议。

当然也不仅仅限于团队内部,也要倾听用户的问题,有意识的建设研发效能度量体系,度量技术债和留意产品延迟和成本的上升。

针对上面这几个技术债象限产生问题的原因来分析如何避免和解决技术债。鲁莽的/谨慎的 可以理解在技术债产生时,团队是否做出来细致的分析,其结果可以对应到 无解决方案/有解决方案。而 故意的/无心的 表达的是对于技术债的产生是已知的,还是未知的,可以对应到 已知问题/未知问题。这样就可以将问题的解决也同样划到四个不同的象限,对于第一象限而言,已知问题,也有解决方案,只需要将技术债的解决列入计划实施起来就可以了。而对于其他象限,都无法直接列入计划解决,因此,我们需要通过一些手段让技术债从认知和方案层面向第一象限移动。第三象限的技术债,团队认知水平很低,只能通过提升团队的整体能力和认知水平,让问题逐渐清晰,向第二和第四象限转移。

6.2可视化

当进行技术债务管理时,我们必须始终以价值为出发点。这个价值不仅限于技术层面,最终还会反映在客户或用户所获得的业务价值上。因此,我们建议在评估技术债务和技术改进任务时,同时考虑技术价值和业务价值两个维度。我们可以参考以下“价值映射图”示例,根据架构债务中存在的技术问题以及解决这些技术债务所带来的技术价值和业务价值进行分析。之后为每个改进任务创建技术卡来可视化所有技术改进任务。

6.3分析优先级

我们时常会遇到的问题是,需要改进的地方太多,尤其是对于遗留系统。怎么办?先排优先级。我们可以基于价值/成本矩阵来评估改进任务的价值和成本。

基于以上价值-成本矩阵,我们会:

优先解决高价值+低成本的技术债,;

尝试将高价值+高成本的技术债拆分为高价值+低成本的技术债,“尽早、频繁、小批”地进行PDCA(Plan/Do/Check/Adjust)的迭代解决;

在没有高价值+高/低成本的技术债时,再来考虑低价值+低成本的技术债;

最后如果只剩下低价值+高成本的技术债,还是先拆分,再解决,或可考虑直接移除

6.4计划执行

由于我们难以在一段时间内集中处理技术债务,因此我们认为将消除技术债务作为每个迭代交付目标的一部分是推进技术改进的有效方式。在对技术债务及相关改进任务进行优先级评估后,需要根据优先级将技术改进任务引入到每个迭代中。例如:每个迭代中引入20%的工作量用于技术改进任务,根据不同的问题类型要执行在识别中的不同策略,将技术债的状态进行转移:

6.4.1 明确技术规范,加强管理

对于已知且无解决方案的问题,只是没有深入思考对系统的影响,对于这部分技术债,产生的原因主要有两个:


  • 团队没有技术规范标准,即便发现问题,也没有方案
  • 缺少对架构修改的审查,开发人员自己思考解决方案,依赖个人经验


为了让这些技术债在产生前就避免,或者引导到可以快速解决的方向,首先,需要建立团队的技术规范和标准,让每个决策都有依据。其次,加强流程上的管理,建立架构评审委员会,对架构的修改进行评审,一方面规避问题,另一方面根据问题完善规范和标准。

在Neal Ford、 Rebecca Parsons 的著作《演进式架构》中提出了“架构适应度函数”(Architecture Fitness Function)的概念,当团队有了技术规范和标准,可以通过构建架构适应度函数,来检查和约束架构的变更或代码的修改,帮助我们发现和避免一些新技术债的产生。

6.4.2 技术债解决日常化

对于已知且有解决方案的问题,研发团队对于架构的守护要像产品人员对待产品一样,架构健康和产品质量一样重要。在项目日常开发中,除了产品的业务需求外,应该规划一部分工作用于架构的优化,修复那些已知且有解决方案的技术债,这样才能持续保证软件系统的响应能力和产品质量。对于这部分的技术债有几个原则:

1. 核心领域优于其他子域

识别领域、子域是DDD战略设计的重要步骤,在识别子域之后我们还需要进一步分析哪些是核心域(Core Domain),哪些是支撑子域(Supporting SubDomain)和通用子域(Generic Subdomain)。核心域在业务上至关重要,它提供了区别于行业竞争对手的差异化优势,承载了业务背后最核心的基础理念

2. 可演进性优于可维护性

可演进性问题可能会直接导致开发速度滞后,功能无法按期交付,使项目出现重大的交付风险。而且问题发生的时候往往已经“积重难返”,引入的技术债务没有在合适的时间得到解决,其产生的影响会像“滚雪球”一样越滚越大。和可演进性问题相比,高复杂度、霰弹式修改等代码级别问题也很重要,但是相对来说我们更加关注软件适应变化的能力,通过提升软件系统的适应性减少软件最终交付价值的前置时间,快速收集真实用户的反馈,持续不断迭代产品、完善设计。

3. 明确清晰的责任定义优于松散无序的任务分配

如果我们深入分析一下技术债产生的过程,很容易发现“交付压力”是一个频繁被提及的原因,这也许也是技术债这个隐喻本身存在的一些问题,原本应该体现为内建质量的工作被当做可以取舍、可以之后偿还的债务,导致必要的工作被滞后、被遗忘。有时候浮现式设计(Emergent Design)反而成了一种心理安慰的借口:“我知道这里有问题,但是我觉得这个变更需要通过需求来驱动”。

诚然,在开发阶段彻底消除技术债是需要付出额外成本的,在真实的项目中也很难明确定义出这样的边界: 哪些部分应该是用刻意设计(Intentional Design),哪些应该是用浮现式设计(Emergent Design)?在 Bob大叔的新作《架构整洁之道》 中也提到这种类似的情形:在一开始的设计阶段,如果要划分出完美的架构边界,让两个组件在编译期和部署期相互独立,既要考虑动态的接口抽象、输入(Input)和输出(Output)数据结构定义,又需要做好组件之间的依赖管理,这些都增加了不少额外的工作量,可以采用一些妥协的做法比如共享组件、策略模式、外观模式(Facade)等。

那对应不同的组件、模块或者服务,谁可以来决定采用哪一种设计方法?两个模块之间应该采用完全隔离还是部分隔离?如果采用部分隔离应该采用哪一种方式?团队在针对技术债的治理过程中也经过一段很长时间的混乱期,团队中每个小组并不是一个独立的作战单元,而是一个个特性工厂(Feature Factory), 每个小组没有清晰的业务职责边界,分配功能特性的时候由各个小组根据兴趣、意愿等主观因素选择功能开发;针对于线上的问题分配也比较随机,更多是基于团队当时的忙碌程度和带宽,这些都导致了业务和技术上下文的割裂,原本在开发初期的架构设计原则逐渐退化,刻意选择要在未来偿还的技术债务在各个小组切换功能的过程中也逐渐被遗忘。

理想的情况下每个小组应该是一个价值趋向(Outcome Oridented)的团队,负责一个或者多个业务能力,原则上每个业务能力应该有且仅有一个团队负责。然而这种理想的情况在实际项目中又很难落地,即使业务能力和团队对齐,但客户对于不同业务能力的投资并不是均等固定的,如果客户一开始希望把百分之五十的预算花在某核心业务能力的构建上,而且要在3个月之内交付,之后的投资重心又转向了其他方面,在这种情况下要如何保持这种稳定的结构?不过这种业务能力和团队之间映射关系还是必要的,在多个小组合作开发时,可以由所负责的小组驱动必要的合作对话和相关的技术治理活动,触发关于业务上下文和技术上下文的跨团队分享,制定并推广代码规范、架构设计原则同时监督各个团队实施的效果。

但有时候业务能力和技术模块是无法一一对应的,现存的单体应用可能是横跨了多个上下文、提供了多种业务能力,在分配特性、技术债务或者线上问题的时候,高度抽象的业务能力无法提供有效的指导。所以我们采用了一种比较折中的方案 - 服务责任人制度(Service Owner), 一个小组负责一个或者多个微服务,每个微服务只由一个小组负责,在分配特性功能、技术债务和线上问题时,需要把服务责任人制度作为首要遵循的原则。由于业务知识和技术上下文的相对集中,在解决具体的软件缺陷时不再浮于表面,团队成员可以更加深入地从需求、技术方案、软件架构等方面着手解决根本问题;针对线上问题通过清晰、明确的责任制度,倒逼团队在开发阶段主动关注软件的内建质量,谨慎判断是否引入技术债。

4. 主动预防优于被动响应

这个原则本质上是缩短反馈周期,提前发现潜在问题,除了必要的代码审查流程(Code Review)、提升团队能力之外还可以借助于自动化工具来提前发现问题。

对于代码可维护性方面,很多比较成熟的静态代码扫描工具都可以自动识别这类问题,比如SonarQubecheckstyle 等,但是仅仅在持续集成上(Continuous Integration)运行还不够,需要和团队一起自定义扫描规则,并把检查代码扫描报告作为代码审查的一部分,逐步形成一种正向的反馈机制。

那我们应该如何提前发现不可见的可演进性问题哪 ? 在Neal Ford、 Rebecca Parsons 等合著的《Building Evolutionary Architecture》中提出了“架构适应度函数”(Architecture Fitness Function)的概念,可以给我们发现潜在的架构问题提供一些思路。

“适应度函数”这个概念来源于遗传算法(Genetic Algorithm),用计算机模拟仿自然界生物进化机制,适应度函数用于评价个体的优劣程度,适应度越大个体越好,反之适应度越小则个体越差。在软件系统的不断增量迭代过程中,我们可以基于架构的演进目标,定义出软件架构的适应度函数,来衡量增量的代码是否会导致架构偏离这个目标。

在工具方面使用方面,我们可以借助于 ArchUnitNDepend 帮助我们定义自己项目中的“适应度”规则,这是一个借助于ndepend自动识别组件循环依赖的例子:

6.4.3 持续关注技术发展趋势,提前规划架构的演进方向

对应未知且有或没有解决方案的问题,我们只能提升自己的能力,持续保持对技术发展趋势的关注,探索是否有更优的解决方案,在日常的开发和运维过程中,要做到以下几点:

  • 明确当前团队的技术选型优势和问题,深入理解团队面临的问题
  • 对于所选择的技术框架版本持续关注,定期升级到最新的稳定版本
  • 关注新技术的趋势和动向,探索是否有更优的解决方案,新的技术是否经过成熟的验证,提前规划架构的演进方向

关注新技术不代表一味追求新技术,首先要明确现有的技术选型到底有哪些解决不了的问题,而在选择新技术的时候也要思考它的弊端在哪里,是否有很大的影响。

6.4.4 保持技术优化相关投入

这个建议更多是给团队的高层管理者,在激烈的市场竞争中,产品的开发可谓争分多秒,管理者们一定更愿意花钱在看得见的产品上。一旦技术框架基础已经奠定,会逐渐缩减在技术侧的投入,这其实也是大部分产品的软件系统技术债逐渐增多的一个非常重要的原因,产品的快速演进,进度的压力无疑是技术债产生的最大元凶。

为了保证产品持续的竞争力,上面几点只是方法,需要一定成本上的投入,给技术领导者一定的时间和预算来解决问题并阻止新问题的出现。相信他们能够合适地使用资源。从整个产品团队,都要提升对技术的正确理解,技术的构建并不是一劳永逸的,是需要不断的成本投入来维护的。当然技术战略也要和商业战略协同工作,保证价值最大化。

6.5总结回顾

在技术债务管理中,可视化技术改进成果是不可或缺的一环。总结的目的不仅是梳理改进成果,更是与团队成员持续分享我们所创造的价值。通过可视化技术改进成果,能够增强团队成员的归属感和满足感,这对于提高团队士气非常重要。虽然有人开玩笑说“每一行代码都是改变世界的力量”,我们可能还无法真正改变世界,但我们能够为客户和用户持续创造价值。

6.6展示成果

首先,了解客户需求至关重要,例如客户关心的系统稳定性、成本控制、交付效率等,除了向客户解释技术改进的价值和解决的问题之外,将技术改进的成果分享给客户也是至关重要的。我们需要持续跟进技术改进的成果,并向客户提供反馈。持续增强客户对技术改进的认可,才能真正确保技术改进的可持续性。

以上,就是我们在技术债管理方面的实践。当然,我们还在持续探索学习的路上。

参考整合:1、偿还技术债的六个步骤 - Thoughtworks洞见

2、管理技术债:企业管理者不容忽视的问题 - Thoughtworks洞见


3、如何解决技术债 - Thoughtworks洞见


4、持续改进——优先攻克哪个点? - Thoughtworks洞见


5、技术债治理的四条原则 - Thoughtworks洞见

目录
相关文章
|
NoSQL 网络协议 数据库
为什么 Lettuce 会带来更长的故障时间
本文详述了阿里云数据库 Tair/Redis 将使用长连接客户端在非预期故障宕机切换场景下的恢复时间从最初的 900s 降到 120s 再到 30s的优化过程,涉及产品优化,开源产品问题修复等诸多方面。
70271 11
为什么 Lettuce 会带来更长的故障时间
|
Kubernetes Perl 容器
【kubernetes】修改集群节点中 Pod 数量限制
Kubernetes 默认每个节点只能启动 110 个 Pod,由于业务需要,将每个节点默认限制的 Pod 数量改为 200。
3654 0
|
8月前
|
SQL 存储 缓存
海量数据分页查询效率低?一文解析阿里云AnalyticDB深分页优化方案
本文介绍了AnalyticDB(简称ADB)针对深分页问题的优化方案。深分页是指从海量数据中获取靠后页码的数据,常导致性能下降。ADB通过快照缓存技术解决此问题:首次查询生成结果集快照并缓存,后续分页请求直接读取缓存数据。该方案在数据导出、全量结果分页展示及业务报表并发控制等场景下表现出色。测试结果显示,相比普通分页查询,开启深分页优化后查询RT提升102倍,CPU使用率显著降低,峰值内存减少至原方案的几分之一。实际应用中,某互联网金融客户典型慢查询从30秒优化至0.5秒,性能提升60+倍。
670 1
|
9月前
|
人工智能 自然语言处理 JavaScript
体验 通义灵码 2.5 版
通义灵码是阿里云推出的智能编码辅助工具,支持代码生成、智能问答、多文件修改等功能。2.5版本新增智能体模式,具备自主决策与工程记忆能力,集成MCP广场,支持3000+工具扩展。同时升级Qwen3模型,参数量更少但性能更强,支持复杂问题深度思考。企业版提供多模型配置与私域知识增强功能,优化开发者体验。尽管插件安装需依赖本地环境(如node.js),但整体提升了开发效率与灵活性,推动AI原生研发新范式。
1850 5
|
JSON Cloud Native API
API 规范和设计
今天主要和大家分享的是如何给予 Open API 3.0 标准来设计一套 API 规范。那么整体我们在讲的过程中,大约有以下五方面。 1. 大环境介绍 2. API与服务开放 3. API定义 4. 模型 5. 总结
1264 5
|
数据采集 数据可视化
读软件研发效能度量规范总结
在工作中,作者探索了软件研发效能度量,参考了《软件研发效能度量规范》这一行业标准。该规范旨在帮助企业和团队通过定义指标来衡量和提升研发效率、效果和卓越能力。关键步骤包括理解指标(如效率、质量和成本),选择适用于团队的指标,以及按照适用性、系统性、可靠性和持续性的原则收集和分析数据。通过度量,团队可以识别问题,制定改进策略,并通过可视化工具进行汇报和决策。
881 0
|
Java
《阿里巴巴Java开发规约》插件使用详细指南
阿里巴巴于10月14日在杭州云栖大会上,正式发布众所期待的《阿里巴巴Java开发规约》扫描插件。今天就为大家详细介绍一下IDEA插件与Eclipse插件的安装使用。
18431 2
《阿里巴巴Java开发规约》插件使用详细指南
|
架构师 C++ 开发者
团队管理|如何提高技术Leader的思考技巧?
技术Leader是一个对综合素质要求非常高的岗位,不仅要有解具体技术问题的架构能力,还要具备团队管理的能力,更需要引领方向带领团队/平台穿越迷茫进阶到下一个境界的能力。所以通常来说技术Leader的技能是虚实结合的居多,繁杂的工作偏多。为此我把自己在工作中经常用到的思考技巧也做了一个整理,算是对《关于技术能力的思考和总结》中提及第三阶段的补充。
1703 1144
团队管理|如何提高技术Leader的思考技巧?
|
SQL 关系型数据库 数据库
Schema(模式
【10月更文挑战第11天】
1718 8
|
NoSQL 安全 容灾
阿里云DTS踩坑经验分享系列|Redis迁移、同步
阿里云数据传输服务DTS在帮助用户迁移Redis数据、同步数据时,在某些复杂场景下会出现报错,或者源库与目标库数据不一致的问题,给用户带来困扰。本文介绍了DTS Redis到Redis迁移、同步过程中的典型问题,以帮助用户更好地使用DTS。
1525 2