DevOps落地笔记-08|技术债务:勤借勤还,再借不难

上一讲主要介绍了如何有效管理第三方组件的实际案例,目的是让你意识到依赖组件的质量也会影响到软件的质量。前面几个课时谈论的主要内容都是跟软件质量相关,通过各种方式方法提高软件交付的质量。这时就会遇到一个问题,软件质量固然重要,但工作中我们不可能把软件质量做到完美无缺才上线。软件质量不是免费的,更好的质量需要付出更多的成本和时间。那么如何平衡软件的开发速度和软件质量的关系,这就是今天要介绍的内容——技术债务。在软件开发的过程中,技术债务是不可避免的,有技术债务也是正常的。那么,如何合理、有效的管理技术债务,让软件实现健康地发展呢?

什么是技术债务

相信很多软件开发人员都阅读或修改过他人写的代码,特别是企业里遗留下来的老代码。如果要修改其中的一个 Bug,或者在这些系统之上增加新的功能,那将非常痛苦。我听到很多次:“改他人的代码,还不如我自己写一个。”有时候并不是说大家喜欢重复造轮子,而是因为目前这个“轮子”年久失修,已经不具备重复使用的可能性。

目前大多数企业由于交付期限的压力,都存在这样的问题:

& 文档没有写或者根本与当前版本不同步;

& 架构设计也只是满足当时的需求;

& 代码中留下了很多待优化的代码片段;

& 遗留代码缺乏文档和单元测试,无人能改,无人敢改。

上面提到的这些问题你应该都能理解,这些问题大多是技术债务没有有效处理导致的结果。那么,什么是技术债务?技术债务的概念是由敏捷先驱 Ward Cunningham(沃德·柯宁汉)在 1992 年提出的,是指那些现在选择不做,但如果一直不做,就会影响未来软件发展的事情。 下面两个例子可以帮助我们理解。

& 过时的架构设计:支撑 1 万人的架构和支撑 1 千万人的架构肯定是不一样的。在最初用户量不大时,采用单体架构满足业务需求是没问题的,但随着用户量的增长,系统架构不做升级,就会影响到业务的发展。

& 需要重构的代码:在单体架构时,为了加快数据的加载速度,会在内存中将预先加载的数据缓存起来。但当采用分布式系统架构后,这段代码逻辑就要进行相应的重构,采用像 Redis 这种分布式缓存方案,否则就会出现数据不一致的情况。

要注意的是,未实现的功能需求不属于技术债务。

因此,技术债务是在短期内不会产生影响,但从长期来看,经常背负技术债务的团队会面临潜在的严重问题,甚至会危及软件的可持续性。每个技术债务,无论多么小,偿还的成本都会随着时间的推移而不断增加,还会导致额外的技术债务。

技术债务是如何产生的

生命周期

在介绍技术债务产生的原因之前,我们先了解一下技术债务的生命周期,掌握技术债务从产生到消亡的整个过程。如下图所示。技术债务经历了四个阶段:

1.产生阶段, 由于各种原因产生了技术债务,此时并未意识到技术债务的存在。

2.意识阶段, 开发人员意识到技术债务的存在,但并未影响到软件的正常发展。在此之前一直享受技术债务带来的红利。

3.临界点阶段, 技术债务已经阻碍软件的正常发展了,出现负债的情况。

4.补救阶段, 采取措施,偿还技术债务,恢复到正常开发流程。
在这里插入图片描述
产生的原因

从上图可以看出,技术债务的产生是源头。要想有效管理技术债务,首先就要了解技术债务产生的原因。下面介绍一下在许多团队中技术债务产生的常见原因,有利于团队能够清楚地认识技术债务,并选择正确的规避方法。这些原因主要与业务、上下文改变、开发流程和团队有关,如下图所示。
在这里插入图片描述
第一个原因与业务有关。

在企业里,业务目标、需求、具备的资源以及能够承担的风险都会影响软件产品。业务问题导致技术问题,进而产生技术债务。

& 时间和成本压力

开发团队通常因为项目交付的时间、项目预算问题,而优先处理用户需要的功能。业务人员和客户也只关心如何更快的向用户交付新功能,抢占市场先机。因此,开发团队没有太多时间设计具有低耦合、高内聚、可扩展的服务层,而需要继续在现有的系统中添加这些功能。虽然短期内能暂时满足项目需求,但此时技术债务已经产生。

& 业务目标不匹配

开发团体的设计方案和技术选型不是为了实现业务目标,而是其他方面的原因。比如,企业需要快速开发一个 App。但技术人员考虑到性能因素,采用了原生的 Android 和 iOS 语言开发。由于对语言的掌握有限,导致系统设计差,用户体验不好,最终导致大量返工。

& 需求不足

当需求不明确,就会产生技术债务,特别是对软件的非功能性需求,没有明确的要求(如安全性、性能、可扩展性等)情况下。

第二个原因与上下文改变有关

技术债务是一个与时间有关的概念。即便是在当初做决策时未产生任何技术债务的设计,随着时间的推移也会过时,需要重新设计。这种重新设计是由业务、技术的变化,或者自然进化造成的技术债务的结果。

& 业务上下文变化

系统的所有决策在当初都是合理的,可突如其来的外部事件改变了现有的业务目标。比如企业被收购,收购后系统需要进行融合等。

& 技术变化

软件、硬件和中间件技术的更新迭代,导致现有系统债务重重。比如:十年前使用 JDK5 开发的应用系统,在今天的技术形态下只能推翻重来了。

& 自然进化

系统在演化的过程中会发生变化,如修复问题、增加新功能。这种变化本身也会削弱一个系统,这是自然进化的结果。就跟人类身体的零部件随时年龄慢慢老化一样,如果做过手术,更会元气大伤。

第三个原因与开发流程有关

我们通常将没有按照软件工程实践和开发规范完成的事项也归类为技术债务。这类技术债务要分析哪些是没有遵循流程规范,哪些是流程规范本身不合理导致的。再根据分析结果进行相应的处理。

& 无效的文档

文档,一直都是软件开发过程中的痛。开发人员对编写文档的抵触一直都是存在的。事实上,关键性文档的缺失或过时增加了系统产生技术债务的风险,比如架构设计文档、测试用例文档等。最初的设计人员在交付压力下,并未有效纪录架构设计的细节、约束和遗留的问题,后续的开发人员会犹豫是否要更改他们不确定的代码。

& 测试自动化不足

随着系统功能不断增加,开发人员会着重关注当前开发的新功能是否可用,而对已有功能的关注就相应减少了。这时就需要有一套自动化测试工具,能够对已有的功能进行测试。这样就可以校验当前的变更是否对已有功能产生影响。

& 流程不匹配

为了保障研发过程顺利开展,所有的软件研发团队都有自己的研发流程。团队成员如果偏离该流程,就有可能导致技术债务,比如代码评审,分支策略,导致版本分支混乱。

第四个原因与团队相关。

影响系统发展的一个关键的、经常被忽略的因素是人。需求是人提出的,架构是人设计的,代码是人编写的,系统是人使用的。有很多现实的例子表明,团队成员的业务和技术水平对于技术债务的产生至关重要。

& 经验不足

一般情况下,团队中经验较少的队员在编码时更容易产生技术债务。比如缺乏模块化设计、代码复杂度的理解。由于企业内部对员工级别的限制和团队梯队的合理性,每个团队中都会有一些初级开发人员,因此,对初级开发人员的工作结果审核和技能培训是至关重要的。

& 分布式团队

沟通问题可能会导致对设计决策理解不一致。分布式团队经常会面临着沟通协调的问题。比如广州的团队进行架构设计,北京的团队负责代码实现,在进行任务沟通时,将会是一个很大的挑战。

& 非专属团队

团队成员横跨多个项目,特别是经验丰富的开发人员。多任务并行不仅需要在多个任务间进行切换,而且往往团队和个人会将注意力集中在最紧迫的项目上,而忽略对其他项目的关注。不能高效、专注地完成任务,不可避免的会产生技术债务。

管理技术债务的原则和实践

介绍完技术债务产生的原因,下面就要介绍如何有效地管理技术债务。**管理技术债务不是一项一次性活动,它是软件开发过程中的一部分。**下面按照避免产生、提前发现和尽快修复的顺序和原则介绍如何有效管理技术债务。

避免产生技术债务

团队成员的综合能力和丰富经验可以识别技术债务以及避免产生技术债务。因此,从团队成员来说,避免产生技术债务的方法是:

& 让具有丰富经验和能力的人参加到项目中;

& 让具有丰富经验和能力的人把控项目的进度和质量;

& 建立系统化的业务知识和技能的培训,提升团队的整体实力。

提前发现技术债务

大多数情况下,技术债务都是无意产生的。如果要想尽早发现这些技术债务并不是一件容易的事。目前有大量的免费、开源的工具可以帮助识别代码中的技术债务。

& 单元测试框架

单元测试是开发人员自己编写测试,以验证代码中单个代码单元的正确性。如果单元测试失败,团队成员能够立即发现并修复导致失败的代码。单元测试还可以检验当前变更是否对已有功能有影响。目前,每一种编程语言都有对应的单元测试框架 xUnit。比如 Java 语言的 JUnit,Python 语言的 PyUnit 和 C++ 语言的 CppUnit 等等。

& 静态代码分析

静态代码分析工具可以识别代码的编码规范、漏洞和缺陷,以及重复代码等问题。这些工具可以方便地与流水线进行集成,自动化的执行扫描和生成检查报告。下图是 SonarQube 执行代码检查的结果,可以非常清晰的了解当前代码的内部质量和技术债务情况,点击上面的数字,能够看到产生该问题的具体代码片段,对于解决问题非常有帮助。
在这里插入图片描述
& 持续集成

传统软件开发中,一般要到每个功能完成后才会进行集成,此时会有各种各样的问题,如果该问题影响范围较大,甚至将设计方案推翻重来,那付出的代价将会是巨大的。持续集成是一种不同于传统开发的方法,团队每天至少集成一次或者每次提交到代码库时,自动执行集成和验证的过程。通过频繁的集成尽早发现集成中遇到的问题,降低最终集成时的风险。

尽快偿还技术债务

当我们通过代码扫描工具或者在开发过程中发现技术债务后,就要考虑尽快偿还技术债务。根据技术债务的难易、大小、缓急采取不同的偿还策略。

& 立即偿还技术债务

对于能当时处理的就立即偿还掉。这种方法可能是最有效的方法,但也是实施起来难度最大的方法。团队习惯于对当前正在处理的功能进行编码,而是把技术债务留到以后去改进,即便是“以后”永远也不会到来…无论如何,尽快偿还是解决技术债务和使软件更具有可维护性的最有效方法。

& 标记技术债务

当发现技术债务的代码时,如果不能立即处理,就先记录一下,然后在后面的某个时间点进行处理。之前的做法是添加 TODO 或 FIXME 这样的注释作为临时占位符,标记这里是存在技术债务的。但随着时间的推移,这些注释不但效果不好,也不会阻止有问题的代码提交到代码库。更有效的做法是在发现技术债务的代码中添加运行时异常,这样就可以使代码运行时处于中断的状态,阻止有问题的代码提交到代码库。

可以看下面的例子,在代码中灵活配置门限的阈值。代码片段如下:

try {File thresholdConfig = new File("c:/threshold.config");FileInputStream fis = new FileInputStream(thresholdConfig);Properties infoProperties = new Properties();infoProperties.load(fis);String thresholdVal = infoProperties.getProperty("threshold");System.out.println(thresholdVal);} catch (IOException e) {e.printStackTrace();}```这段代码的问题是以硬编码的方式从本地加载配置文件。开发人员也了解这样写是不对的,因为他只是想调试一下代码逻辑的正确性,并且不希望分散目前的注意力。那么可以将运行时异常添加到验证成功代码的后面,这样既能验证代码逻辑的正确性又不会遗忘此处的技术债务。如下所示:

try {
File thresholdConfig = new File(“c:/threshold.config”);
FileInputStream fis = new FileInputStream(thresholdConfig);
Properties infoProperties = new Properties();
infoProperties.load(fis);
String thresholdVal = infoProperties.getProperty(“threshold”);
System.out.println(thresholdVal);
throw new RuntimeException(“不要以硬编码的方式加载配置文件”);
} catch (IOException e) {
e.printStackTrace();
}


运行的结果如下图所示:
![在这里插入图片描述](https://img-blog.csdnimg.cn/direct/cb6cb49fad4d4644adae2ed09972a85a.png)
&  **对技术债务进行排期**当发现技术债务较大,并不能在短时间内解决时,就需要将技术债务添加到产品列表(Product Backlog)中,产品经理将技术债务和需求一起排列优先级,共享团队的资源和时间,并和需求一样进行跟踪和验收,保证技术债务确实被偿还。**总结**本课时开篇以如何平衡软件开发进度和软件质量之间的关系引出技术债务。并提出在软件开发过程中,技术债务是不可避免的,需要有效的管理和控制技术债务的蔓延,才能保证软件的健康发展。接下来系统化地介绍了什么是技术债务,什么不是技术债务,分析了技术债务产生的原因。根据技术债务的生命周期曲线图,技术债务产生后,在一段时间内会对当前业务有积极作用,只是随着时间的推移,慢慢会成为阻碍企业发展的绊脚石。因此,在管理技术债务部分,先是在产生之前能避免则避免,不能避免的可以借助工具提前发现,对于发现的技术债务尽可能早的偿还掉。有句俗话:“勤借勤还,再借不难”,言简意赅地表明了管理技术债务的策略。针对如何管理技术债务,你是否有其他的方法,欢迎在评论区分享自己的方法。

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

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

相关文章

2024年第4届IEEE软件工程与人工智能国际会议(SEAI 2024)

2024年第4届IEEE软件工程与人工智能国际会议(SEAI 2024)将于2024年6月21-23日在中国厦门举办。 SEAI旨在为软件工程与人工智能领域搭建高端前沿的交流平台,推动产业发展。本次会议将汇聚海内外的知名专家、学者和产业界优秀人才,共同围绕国际热点话题、核…

PostGIS空间数据库之空间数据融合实践

目录 前言 一、ST_Union()简介 1、方法说明 2、参数介绍 二、ST_Collect()简介 1、方法说明 2、参数介绍 3、两者区别 三、实际案例实践 1、不重叠融合 2、空间重叠融合 总结 前言 众所周知,熟悉GIS桌面软件的同学一定都知道,想要对空…

使用goland IDE编写go windows ui

最近突发奇想,想实现一款工作节奏的提示安排小闹钟。那首先解决的就是UI。本人擅长go语言。那go在windows ui的探索肯定有人做过了吧。一查还真有,通过知乎,csdn等查到目前支持最好的就是walk库了。那走起试试。 一、拷贝go代码 将官网例子…

WiFi 7 的核心要点

目录 WiFi 7 是什么? WiFi 7 的主要feature功能: 320Mhz channel 4K QAM Multi-Link Operation (MLO),多链路操作 512 block ACK OFDMA:multiple RUs to single STA. 总结:性能是第一优先级,WiFi 7&#xf…

Multi ElasticSearch Head插件基本操作

Multi ElasticSearch Head插件安装好之后我们可以进行一些基本的操作。 1、复合查询 因为ES提供了一些Restful风格的接口,可以让任何语言去调用,因此我们可以将之前的请求地址粘贴到Multi ElasticSearch Head插件里面,选择GET请求方式&#x…

RIP——路由信息协议

目录 1 内部网关协议 RIP 1.1 协议 RIP 的工作原理 1.2 RIP“距离”的定义 1.3 RIP 协议的三个特点 1.4 RIP 协议的优缺点 1.5 路由表的建立 路由表主要信息和更新规则 2 距离向量算法 3 RIP2 报文 4 坏消息传播得慢 5 启动RIP 启动RIP: router rip 命令 启用和检…

Elasticsearch:Geoshape query

Geoshape 查询可以用于过滤使用 geo_shape 或 geo_point 类型索引的文档。 geo_shape 查询使用与 geo_shape 或 geo_point 映射相同的索引来查找具有与查询形状相关的形状的文档,并使用指定的空间关系:相交(intersect)、包含(con…

Git介绍与常用命令总结

Git介绍与其常用命令总结 1、Git介绍2、Git的使用3、Git常用命令3.1 初始化仓库3.2 克隆仓库3.3 配置用户信息3.4 提交代码(Commit)3.5 推送代码(Push)3.6 拉取代码(Pull)3.7 分支(Branch)3.8 远程仓库(Remote)3.9 撤销回退本地改动3.10 更新本地仓库与远程仓库 1、Git介绍 Gi…

AI Prompt工程师 学习整理

前言 如果说Al大语言模型(LLM,Large Language Model)是宝藏我,那么Prompt提示词就是打开宝藏的钥匙。 最新一代的Al大语言模型具备出色的创作能力,能够生成富有人类感情、严谨逻辑、多场景应用的内容,而如何获得高质量的回答,正确学习使用Prompt提示词是关键。 &#x1f4a5…

【2024美国大学生数学建模竞赛】2024美赛C题网球运动中的势头,网球教练4.0没人比我更懂这个题了!!!

【2023美国大学生数学建模竞赛】2024美赛C题 问题分析、数学模型、实现代码、完整论文 引言 题目将于2024年2月2日6:00发布。我们团队将会在8点前准时更新问题分析,逐步更新数学模型和实现代码,最后发布完整的论文。 更新进展: &#xff08…

轻型民用无人机驾驶航空器安全操控——理论考试多旋翼部分笔记

今天已经可以在线考取轻型民用无人机驾驶航空器执照了,所以我也在在线观看完视频之后整理了如下的知识点,所有知识点全部来自UOM平台。 目录 航空器知识 (1)多旋翼民用无人驾驶航空器螺旋桨的作用 (2&#x…

Servlet简述

Servlet是动态web资源开发技术,其实就是一个接口,将来定义Servlet实现类时,都必须实现该接口,并让web服务器运行Servlet 1.快速入门 使用注释配置访问路径在Servlet3.0之后应用,在此之前都是使用xml配置文件来配置的。…

WPS WORD 宏导出高亮文本

WPS手机版可以直接导出高亮文本,但只能导出手机编辑的部分,如果同时在电脑上编辑过,电脑上高亮的无法导出,因为作者不一样。 但WPS电脑版没有这个功能,只能通过宏编程实现。 这里利用了审阅模式,在文字高亮…

springBoot+Vue汽车销售源码

源码描述: 汽车销售管理系统源码基于spring boot以及Vue开发。 针对汽车销售提供客户信息、车辆信息、订单信息、销售人员管理、 财务报表等功能,提供经理和销售两种角色进行管理。 技术架构: idea(推荐)、jdk1.8、mysql5.X(不能为8驱动不匹配)、ma…

重温《深入理解Java虚拟机:JVM高级特性与最佳实践(第二版)》 –– 学习笔记(二)

第二部分:自动内存管理机制 第2章:Java内存区域与内存溢出异常 2.1 概述 Java 与 C 之间有一堵由内存动态分配和垃圾收集技术围成的高墙。 Java 程序员在 虚拟机自动内存管理机制 的帮助下,无需为每一个 new 操作去写配对的 delete/free …

Java知识点总结

数据类型强转:byte short int long float double ; 数组定义 [ ]数组名 clone-复制数组equals-比较存储地址 toString sort-排序 length-长度 arraycopy([]a,s,[]b,ss,n)-数组复制 运算符及语句 instanceof双目运算符 –左对象右类 判断是否是该类创建…

2024年美国大学生数学建模C题思路分析 - 网球的动量

# 1 赛题 问题C:网球的动量 在2023年温布尔登绅士队的决赛中,20岁的西班牙新星卡洛斯阿尔卡拉兹击败了36岁的诺瓦克德约科维奇。这是德约科维奇自2013年以来首次在温布尔登公开赛失利,并结束了他在大满贯赛事中历史上最伟大的球员之一的非凡…

JeecgBoot jmreport/loadTableData RCE漏洞复现(CVE-2023-41544)

免责声明:文章来源互联网收集整理,请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该…

three.js CSS2DRenderer、CSS2DObject渲染HTML标签

有空的老铁关注一下我的抖音&#xff1a; 效果&#xff1a; <template><div><el-container><el-main><div class"box-card-left"><div id"threejs" style"border: 1px solid red;position: relative;"><…

OSPF 协议

OSPF 开放式最短路径优先协议 形容&#xff1a; 无类别链路状态协议&#xff1a; 无类别 ---- 存在掩码 IGP 内部网关协议 动态路由协议 一、链路状态协议(LS) OSPF 协议 隶属于 链路状态协议。 区别&#xff1a; 距离矢量协议(DV)&#xff1a;运行距离矢量协议的路由器…