目录

如何处理技术债务

技术债务对开发团队来说,既是一大挑战也是疲惫的来源。虽然软件工程师明白技术债务带来的问题,但他们往往需要向产品团队解释,快速且简单的编程解决方案为何充满风险。

这个概念最早是在1992年由沃德·坎宁安在OOPSLA会议上提出的,他用它来比喻软件开发过程。他认为,开发不仅是学习过程,而且这个过程中产生的所谓技术债务,实际上是不可避免的。

技术债务基本上是指那些阻碍软件快速开发的所有问题。马丁·福勒分析了导致技术债务产生的四个主要原因,但实际上原因远不止这些。特别是在那些追求快速增长而忽视质量的初创公司中,这个问题尤为明显。

研究显示,为了追求短期目标,如加入新功能,开发人员往往不得不引入新的技术债务。这种做法不仅影响整个组织的运作,也会影响开发人员的幸福感、满意度和士气。

技术债务的一大难题在于,代码本质上是抽象的,这使得向业务和管理层解释其影响变得困难。因此,公司可能会忽视那些他们看不见或不理解的问题。因此,有必要清晰并明确地向公司和管理层展示技术债务的情况。

在软件系统开发的过程中,我们会发现,随着每个开发周期,能够用于添加新功能的能力在逐渐减少(交付时间越来越长),同时,系统的复杂度却在不断增加。这导致我们花费更多的时间来应对这些复杂性,而非开发新的功能。

打造一个高效的组织能让我们在竞争中占据优势,更快速地响应并且更加创新。要做到这一点,我们必须拥有一个高品质的软件开发流程。

技术债务是不可避免的,但我们需要一个计划,既减少新的债务产生,又能帮我们减轻现有的债务。这样的投资会为公司积累技术财富。

技术债务的类型

技术债务有很多种类,不仅仅是代码写得糟糕那么简单:

代码质量:代码应该更清晰易懂。目前可能没有统一的编码标准,设计欠佳,复杂度高。代码中也许还有大量注释来说明代码的功能。

测试:我们需要合理的测试策略(如测试金字塔),涵盖从单元测试到集成测试再到端到端测试。

耦合:模块之间的相互依赖增加了处理代码的时间。

过时的库或工具:我们可能在使用一些有安全问题或无法更新至新技术平台的旧库或工具。作为开发者,我们总想使用最新的技术和工具。

手动流程:我们的某些交付流程需要自动化,比如没有自动化构建或CI/CD流程。

错误或缺乏架构:我们可能没有合适的架构,或者架构混乱不堪。架构可能并未能反映出我们希望系统实现的目标,或者不易扩展。

缺乏文档:可能完全没有文档,或文档未能及时更新以反映系统的最新状态。

缺乏知识共享:缺少知识共享文化,使得新成员难以快速熟悉工作。我们应该记录下来我们的决策和规范。

还有其他类型的技术债务,比如低效的工作和监控问题等。

但在很多组织中,我们常见的是缺乏有效管理和减少技术债务的流程和策略,这反映出缺乏适当的工程文化。

应对技术债务的策略

那么,减少技术债务的策略有哪些?我们需要优先考虑处理技术债务。首先分析开发流程和代码库,找出问题所在,创建一张技术债务图。有了技术债务图,就需要识别每个问题的原因。

有了问题列表后,可以逐个评估如果什么都不做会发生什么,以及系统的这部分是否现在或将来会被用于新的开发。这样,我们就可以优先处理那些技术债务严重的部分,这将是我们现在和未来工作的基础。

另一种方式是使用努力和修复难度,或是RICE评分模型来建立这样的图表。

我们也可以通过组织工作坊或回顾会来创建一份技术债务图,并且应该定期对所有这些债务类别进行审计。

处理技术债务的推荐策略

推荐的处理技术债务策略包括以下几步:

1、需要对开发过程、存在的瓶颈以及为什么要加速开发保持透明。然后,需要用大家都能理解的方式来总结这些信息,这样工程和管理层的所有利益相关者都能有共同的理解,使用图表等可视化手段会很有帮助。

2、系统需要有明确的负责人,完全负责它们。如果系统中有哪些部分出了问题,而没有人来负责处理,那就糟糕了。

3、根据影响程度来优先处理任务

4、必须授权团队解决问题和在产品开发的自然过程中处理技术债务。我们需要在减少技术债务和添加新功能之间找到健康的平衡,采取实用的方法。将10-20%的开发时间持续投入到这方面通常是有效的。具体做法包括:

5、对于小额债务(几小时内可完成),在接触时就进行重构,让团队成员养成良好习惯。

6、对于中等债务(需要一天到几天时间),可以试试每周五只处理技术债务,或者将技术债务视为一个特性,优先处理它。

7、对于大额债务(几周到几个月),可以固定时间处理技术债务,比如选择一个任务并花两天时间来解决,或者组建一个专门小组来处理。

8、使用指标来帮助决策,如代码问题(例如使用SonarQube)或交付时间。

跟踪技术债务的工具

跟踪技术债务的工具有多种,但推荐使用一些现有的工具,因为引入新工具可能成本很高。技术债务可以通过以下方式追踪:

1、项目管理工具:例如Jira、Asana、GitHub issues、Azure DevOps Board,或者一些更专业的工具,如Stepsize、CodeScene或针对.NET的NDepend。

2、自动化代码分析工具:可以用如SonarQube这样的代码分析工具来追踪和检测代码问题、安全问题等。

3、手动追踪方法:根据个人喜好,比如使用Excel表格或其他有效的方式。也可以结合使用上述方法,如在Miro追踪战略性技术债务,在Jira追踪战术债务,同时利用SonarQube的指标。关键是要保持信息的易获取和更新。

保持较低的技术债务

我们希望以合理的速度、质量和复杂度生产代码,避免快而糙的做法。随着软件不断发展,系统架构日益复杂,维护也越来越难。如果最开始设计和代码质量就不佳,以后就会面临更多问题。

我们的目标目标是打造一个易于维护、能够避免错误、在低成本下进行问题修复和后续改进的系统。为此,需要恰当地设置软件架构和工艺水平,以实现质量、速度和复杂度之间的平衡。

但是,开发新功能的速度越快,保证软件质量就越困难,技术债务也会逐渐积累,导致开发速度减慢,错误增多。因此,选择一开始就慢一点,以后可以加快速度。

问题在于如何从一开始就避免产生技术债务。答案是拥有一个高质量的软件开发过程。可以采取以下措施:

1、根据预期结果来架构系统。一开始就让架构师参与团队建设,定义其结构,避免康威定律导致的架构成为组织沟通结构的映射,而非预期架构。

2、为所有架构决策和技术文档编写文档,并确保这些文档易于查找,比如放置在代码仓库附近。

3、在测试金字塔的各个层级编写测试,争取达到60-80%的代码覆盖率。

4、鼓励代码审查文化,让每个人都参与进来,检查代码的可读性、设计、性能、安全性、可测试性和文档,以此作为知识分享的一个重要方式。

5、实行配对编程,即一名开发者编写代码,另一名提供反馈和建议,这样可以提高代码质量,甚至无需单独的代码审查。

6、允许开发者自由重构,建立一种文化,在这种文化中,修复技术债务无需特别请求许可,特别是对于那些几小时内就能完成的任务。

7、遵守代码库标准,比如设计、命名和架构标准,并通过linters或CI/CD流程等工具自动化这些标准。

8、实施测试驱动开发(TDD),即首先为每项功能编写测试,然后进行实际开发。

9、编写高质量的代码,首先培训开发者深入了解他们的编程语言,以及理解面向对象的概念、设计模式、重构、干净代码原则、架构模式和风格,以及SOLID、YAGNI、KISS和DRY原则。

10、持续交付,使代码经常进行小幅调整,易于团队其他成员验证。通过频繁发布和快速反馈,避免生产中出现大问题。