技术债正在悄悄拖垮你的团队!

news/2025/2/24 7:53:08/文章来源:https://www.cnblogs.com/JavaEdge/p/18566584

0 前言

软件开发的核心在于应对变化。在软件的生命周期中,目标是能够在合理的时间内实施必要的更改。不管这些更改是技术性的,比如紧急安全升级,还是业务需求所驱动的,比如开发新功能以在目标市场中更具竞争力——能否快速应对变化是成败的关键。

是什么让我们慢下来?通常,这是因为让某个功能能够运行并不等于让它在长期内具备良好的可维护性(参考 Google 软件工程实践)。第一个可运行版本通常是快速而粗糙的,而让其具备可修改性则需要额外的努力。这引出了“技术债务”的隐喻(参考 Ward 的解释)。开发人员选择暂时不投资于代码的可变更性,而是承受技术债务,以便更快完成任务。之后,每次修改代码都需要支付额外的“利息”,直到技术债务彻底清偿。

1 啥是技术债?

技术债务是指当前软件状态与最适合于轻松实现更改的目标状态之间的差距。在某些情况下,积累技术债务可能是值得的——例如,为了满足一个硬性截止日期,否则整个项目可能停滞不前。但从长远来看,采取措施来控制和减少技术债务无疑是明智的(参考 Fowler 的文章)。

对于寿命预计以年为单位的软件来说,是否偿还技术债务并不是问题。问题在于如何识别、衡量和管理它。

技术债务可能有不同的来源。例如,团队可能对技术债务引发的问题缺乏认识;或者,尽管意识到问题存在,但误以为永远没有时间解决。这与工程文化密切相关。随着时间推移,问题只会越来越严重(参考 破窗理论)。另一种情况是,团队在权衡利弊后,有意积累技术债务。第三种情况则是因为我们无法事先掌握所有信息,需求可能变化,而开发过程中会逐步学习。这种债务即使对于最优秀的团队来说也不可避免(参考 Fowler 的技术债务象限)。

技术债务的棘手之处在于,它通过不断做出小的妥协而逐渐积累。为了短期的便利和简单而牺牲长期的结果,被称为“温水煮青蛙隐喻”。换句话说,问题会逐渐积累,直到灾难发生为止。我们如何防止这种情况的发生?

应对技术债务的最佳防御措施是从一开始就使其可见。然后,我们可以通过设立适当的健康指标,并尽早采取纠正措施来主动管理它。

另一方面,如果我们的系统已经因为技术债务濒临崩溃,那就需要采取更激进的“清理”措施——在为时已晚之前。在这种情况下,建议建立多个改进指标,并使用它们来跟踪这些措施的进展。

2 WTFs 每分钟

一个广泛认可的观点是,代码质量的唯一有效衡量指标是每分钟 WTF 次数(参考 Martin 的《代码整洁之道》)。或许可以开发一款设备,专门用来统计 WTF 次数,这或许会成为一个不错的创业点子?

当然,这个指标既主观又依赖于开发者的技术水平及团队的工程文化。根据破窗理论,糟糕的代码越多,就越会鼓励开发人员继续制造技术债务。

3 代码异味的数量

Martin Fowler 和 Kent Beck 引入了“代码异味”这一概念,帮助开发者识别代码中可能存在问题的地方。Fowler 的《重构》一书列举了 24 个代码异味示例。Uncle Bob 的《代码整洁之道》中也包含了许多代码异味和启发式规则(参考《[代码整洁之道》第17章](https://learning.oreilly.com/library/view/clean-code-a/9780136083238/chapter17.xhtml#:-:text=Smells and Heuristics))。一些代码异味,如重复代码和过长函数,可以通过静态分析工具(如 [SonarQube](https://docs.sonarqube.org/9.6/user-guide/concepts/#:~:text=Code smell,errors as they make changes.))轻松检测。然而,许多代码异味无法通过静态工具轻松发现。这也是为什么需要像“每分钟 WTF 次数”这样的另一个指标。

4 自动化测试覆盖率

尽管早在[《Google 软件工程实践》](https://abseil.io/resources/swe-book/html/ch11.html#:~:text=We have a name for,changes across the entire codebase.)、《代码整洁之道》](https://learning.oreilly.com/library/view/clean-coder-the/9780132542913/ch01.xhtml#:-:text=Am I suggesting,be tested. Period.) 等书中提到过自动化测试的重要性,最近的研究(例如《Accelerate》和 DORA 研究)表明,测试自动化与软件生产力之间存在统计上的正相关。这表明,提高自动化测试覆盖率通常可以显著提升团队的生产力。

可以通过许多工具(例如 [JaCoCo](https://www.eclemma.org/jacoco/#:~:text=JaCoCo is a free code,existing libraries for many years.))来追踪这个指标。但如同许多其他指标一样,它也容易被“造假”,比如编写大量实际上并未测试任何内容的测试。因此,结合其他努力来提升团队技能,并阐明编写自测试代码的好处是非常重要的——比如 Google 推行的厕所上的测试。

当测试自动化覆盖率较低成为限制团队进步的因素时,这一指标尤为有效。例如,我的团队曾发现某个遗留组件的测试自动化覆盖率非常低(仅约50%)。因此,我们将提高该覆盖率作为优先事项。通过持续监控指标、在团队回顾会议上讨论进展,我们在一年内将单元测试覆盖率提升至80%,并且不再视其为限制因素。现在,我们将其作为代码库健康的一个重要指标。

5 文档覆盖率

文档不足可能对团队效率产生负面影响。因此,我们可以采用一个与文档覆盖率相关的指标:

文档覆盖率:系统中文档覆盖部分占总系统的百分比。

如何使用这个指标?在文档不足被视为制约因素的团队中,可以优先改善这一问题。我们列出所有组件,并评估每个组件当前的文档覆盖情况。每周更新指标,并监控改进进展。

6 用在弃用组件上的精力

在一些情况下,为了支持新的组件,我们需要弃用旧组件,但在一段时间内仍然不得不保留这些组件。例如,有些客户端需要时间完成迁移。在此期间,我们仍可能需要对这些弃用组件进行工作,比如修复漏洞。由于这些弃用组件最终会被移除,这种工作实际上是一种浪费。问题是,团队往往会“忘记”这些弃用组件,继续对它们提供支持。随着时间推移,这些工作会不断积累,甚至可能成为团队的主要限制因素之一。因此,跟踪弃用组件并尽早废止它们是非常重要的。

可以采用以下指标:

  • 弃用组件工作的比例 = 用在弃用组件上的时间 / 总时间
  • 弃用组件工作的比例 = 与弃用组件相关的任务数 / 总任务数
  • 弃用变更比例 = 弃用组件的变更数 / 总变更数

如何使用这些指标?例如,我的团队负责一个覆盖 200 多个国家的住宿合作伙伴门户中与发票相关的财务内容。去年,我们开发了一个新的发票展示页面,并在几乎所有国家推出。然而,由于一些国家有特定逻辑,我们决定暂时保留旧页面以便后续迁移。这一决定让我们可以更快地获得新页面的反馈。然而,这也导致我们在几个月内需要支持多个版本的页面。尽管旧页面的支持工作量不大,但累积效应可能会显著增加负担。在这种情况下,我们将这一指标作为改进和健康监控的重要工具。

7 用于修复用户发现缺陷的工作量

软件中的缺陷显然会减缓功能开发的进度。因此,这些缺陷可以被视为技术债务的一部分。

我们可以使用以下指标来量化相关工作量:

  • %修复缺陷的工作量 = 修复缺陷所花的时间 / 总时间
  • %修复缺陷的工作量 = 缺陷数量 / 总任务数量

8 漏洞的数量

在 OWASP Top Ten 网络应用安全风险列表中,“漏洞和过时组件”被列为其中之一。这些问题可能导致紧急计划外工作,来修复漏洞和应对后果。因此,漏洞可以被视为技术债务的一部分。

我们可以使用工具 Dependency-Check 来检查项目中的依赖项。将该工具集成到 CI/CD 流水线中是广泛认可的最佳实践。这种方法可以帮助我们尽早发现并修复漏洞或过时组件,从而减少技术债务带来的潜在影响。

9 清偿技术债务所需的估计工作量

有些导致团队效率降低的问题无法通过静态分析工具轻松追踪。例如,共享数据库架构或其他复杂的架构问题,通常难以用工具直接衡量。每个团队都会面临其独特的技术债务挑战,因此解决方法也会有所不同。

最简单的衡量方式可能是估算清偿技术债务所需的工作量。然而,这种估算需要团队具备足够的技能和经验,例如掌握设计模式、重构技巧、自测试代码的编写,甚至是架构最佳实践(如松耦合架构)等。通过结合这些能力,我们可以更准确地评估并应对技术债务。

10 关键总结

  • 技术债务是指当前软件状态与最适合轻松实现更改的目标状态之间的差距。
  • 在几乎所有情况下,保持技术债务处于较低水平是非常重要的。如果忽视它,每次修改代码时都会付出额外的努力。
  • 技术债务的来源包括:(i) 团队对其危害缺乏认识;(ii) 在权衡利弊后有意选择积累技术债务;(iii) 由于信息不完整,随着开发过程中的学习和需求变化不可避免地产生的债务。
  • 技术债务往往通过无数次小的妥协逐渐积累,最终可能导致严重后果。
  • 最好的实践是从一开始就让技术债务变得可见,并通过设置健康指标进行监控,在早期采取纠正措施。
  • 如果发现技术债务已经威胁到系统的正常运行,则需要采取更加激进的清理措施。在这种情况下,可以通过设立改进指标并定期跟踪进展来评估和调整清理策略。
  • 各团队可以使用多种指标来衡量技术债务的健康状况和改进效果,例如 WTFs 每分钟、代码异味数量、漏洞数量、测试覆盖率、文档覆盖率,以及用于弃用组件、计划外工作、修复缺陷的工作量和清偿技术债务的估算工作量。这些指标并非唯一选择,团队可以根据自身需求设计更合适的指标。

关注我,紧跟本系列专栏文章,咱们下篇再续!

作者简介:魔都架构师,多家大厂后端一线研发经验,在分布式系统设计、数据平台架构和AI应用开发等领域都有丰富实践经验。

各大技术社区头部专家博主。具有丰富的引领团队经验,深厚业务架构和解决方案的积累。

负责:

  • 中央/分销预订系统性能优化
  • 活动&券等营销中台建设
  • 交易平台及数据中台等架构和开发设计
  • 车联网核心平台-物联网连接平台、大数据平台架构设计及优化
  • LLM Agent应用开发
  • 区块链应用开发
  • 大数据开发挖掘经验
  • 推荐系统项目

目前主攻市级软件项目设计、构建服务全社会的应用系统。

参考:

  • 编程严选网

本文由博客一文多发平台 OpenWrite 发布!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/840619.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

HCIA-06 IP路由基础

介绍了路由的基本概念,路由条目三种生成方式:直连路由、静态路由(缺省路由)、动态路由,路由器的基本工作原理、路由表的具体内容。路由器选择最佳路由的方法:先比较匹配的掩码长度、(如果掩码长度相同)再比较优先级、(如果优先级相同)再比较度量值。路由转发选择的路…

学习笔记(四十四):自定义组件@LocalBuilder装饰器

概述: 当开发者使用@Builder做引用数据传递时,会考虑组件的父子关系,使用了bind(this)之后,组件的父子关系和状态管理的父子关系并不一致。 为了解决组件的父子关系和状态管理的父子关系保持一致的问题,引入@LocalBuilder装饰器。 @LocalBuilder拥有和局部@Builder相同的功…

推荐5款程序员必备的画图工具,效率提升利器!

ProcessOn ProcessOn是一款专业强大在线作图工具,提供AI生成思维导图流程图,支持思维导图、流程图、组织结构图、网络拓扑图、鱼骨图、UML图等多种图形。同时可实现人与人之间的实时协作和共享,提升团队工作效率。在线访问地址:https://www.processon.com丰富的图形素材库:…

定时器JS深入理解解读

set&clear interval&timeout 相信不论是老油条还是新手小白,你早就听过或者用过setInterval和setTimeout这俩哥们儿和他们的死敌clearInterval和clearTimeout了吧,作为项目里的“老面孔”和面试高频考点,今天GJ504b就带大家深入解读这四大活宝😀setInterval/setT…

not in 和 not exists 比较和用法

尽量不要使用not in(它会调用子查询),而尽量使用not exists(它会调用关联子查询)。查询语句使用了not in,那么对内外表都进行全表扫描,没有用到索引;而not exists的子查询依然能用到表上的索引。所以无论哪个表大,用not exists都比not in 要快。 NOT IN 查询返回空结果:…

Flash动画综合设计并发布、嵌入到网页

【作业要求】 自己选定主题,创意制作Flash动画,并与网页进行集成 【实验环境】 l 所需硬件环境为微机; l 所需软件环境为VS code 【创意内容】 因为我比较爱国,所以我选定的主题都是关于国旗的【关键步骤】 1、选好想要做的内容,找到一些视频; 2、查找关于任如何在浏览器…

洛谷P11290 【MX-S6-T2】「KDOI-11」飞船

Problem本题开启Special Judge,无需考虑精度问题Solve 一开始想到这个用DP写,但是不知道怎么定义 去"提交记录"旁边的神秘按钮得知速度可以作为第二维,且类似于背包 那么我们就可以按照背包列出定义 前i个加油站,花费时间j...... 但是这里是求最小花费,所以我们…

Analogue Pocket 软硬件实验预告

如题,还在测试中,预计会在今年内发布。

.NET云原生应用实践(六):多租户初步

本章目标多租户简介 实现public租户下的用户数据隔离出于开发进度考虑,本章暂不会完全实现多租户的整套体系,而是会实现其中的一小部分:基于默认public租户的数据隔离,并在本章节中会讨论多租户的实现框架结构。在后续的系列文章章节中,我们会完成多租户的实现。多租户(M…

【MX-S7】梦熊 NOIP 2024 模拟赛 3 SMOI Round 2

hdk俩签到俩不可做是吧。Rank【MX-S7-T1】「SMOI-R2」Happy Card 签到题一号,以为撑死评个黄但没想到那么多人不会打扑克。 考虑炸弹也是三带一,出三带一肯定更优秀。 考虑将所有牌变为若干个三张和剩余的,那么三张先带单张,再将对子拆开带。那么现在就有以下几种情况:单张…

我勒湘军杯

今天以体验队参加了湘军杯,感觉和ctf差别挺大 使用sql注入、xss等方式对靶场漏洞进行挖掘,漏洞挖掘感觉需要把知识系统的利用起来, 虽然也是ctf的知识,但是感觉ctf的目标很明确,获取flag就结束,而且可以在网上搜索到类似的步骤解题