内燃发动机上的星际飞船。通过技术债务来生存



如何在技术债务的战斗中生存?如果您有严重的遗留遗产怎么办?在本文中,我将以三种情况为例,建议找出如何构建处理技术债务的流程以及为此使用的工程方法。

我叫Denis,我是Wrike的后端团队负责人。我负责团队中的交付以及几个团队中开发人员的成长。碰巧我几乎所有的经验都在金融科技公司工作。我曾在两家大型银行工作,现在我在Wrike工作。

在银行,您将学习如何使用对可靠性和容错性很重要的系统。那里有很多遗产,作为开发人员,我自己经历了这一切,并帮助其他人作为领导团队经历了自己。

Wrike是我们出售给客户的SaaS团队协作解决方案。我们使用Wrike来组织Wrike进行开发。

Wrike发展了30个Scrum团队。该服务24/7全天候可用,因此我们做出的决定必须可靠。如果出现问题,那么这将影响几乎所有团队的工作。

为什么要飞船?


星际飞船是一个比喻,我用它来描述程序员正在使用的工具。该应用程序从几个模块开始。然后它开始增长:它承受了巨大的负担,微服务出现了,它们之间的通信,集成,外部API,客户端。事实证明,这是一个庞大且相互联系的生态系统。应用程序越大和越旧,生态系统之间的联系就越紧密。而且,更重要的是使重要节点保持最新状态并处于可靠状态。当其中最重要的一个不满足今天的要求或失败时,这将变得非常可悲。
如果您的星际飞船在AI-95上运行,它将不会跳入扭曲状态。如果中央计算机是Intel Celeron,则系统将不会在四个星系中导航。

什么是技术债务?


想象一下,您具有不断出现错误的功能。一名测试人员来到您面前说:“这周我们在这里发现了四个新错误。” 是否是技术债务?并且如果此功能的存在阻止了其他需要完成的故事?如果很难使用该解决方案,而您每次都需要重构呢?用户是否抱怨某个功能?如果她不满足当今的要求,或者冒犯了追求卓越的开发人员的感情?

在本文中,由于技术原因,我将了解那些会干扰产品进一步开发的功能,解决方案或方法。上述所有问题都是技术债务的结果。这些是特定的原因:为什么他是他,以及为什么您需要与他一起工作。

技术债务算法


为了有效处理技术债务,您需要提出三个基本问题。

  1. 做什么的?我们为什么承诺与他做某事?为什么我们需要这项工作?为什么要花费公司资金,开发时间,工程师时间?应该清楚地了解通过解决特定问题将获得什么好处。从定义中可以明显看出,技术债务阻碍了产品的开发。使用它,我们将获得新的功能。
  2. 什么?我们必须清楚地了解技术债务的始发地,结束地,技术面貌以及为消除技术债务必须要做的工作。
  3. 怎么样?为了摆脱技术债务,需要采取的行动。

为了回答最后一个问题,有一个简单的迭代四步算法。第一步是突出技术债务并了解其边界。下一步是将其与其他所有内容分离:封装并输入已分配的解决方案与系统其余部分之间的工作合同。之后,您可以在附近创建一个新的解决方案,将其替换,因此,部分技术债务将离开应用程序。重复此迭代多次,您将获得现成的解决方案。



现在,以几种情况为例,看看该算法在现实中是如何工作的。

第一种情况


有一个可以处理来自客户的订单的应用程序-订单管理系统。该应用程序是在很早以前(即2010年)编写的,并基于当时的最新技术构建。该应用程序在过去的9年中已成功在生产中运行,但是今天,企业了解到有必要占领新市场并进一步开发该系统。同时,重要的是保存数据并增加系统中的新功能。



事实证明,有些技术早已不复存在,但也有一些数据无法丢失。并非所有功能都可以使用旧技术在应用程序中实现。因此,说实话,情况看起来像这样:



这里的问题不是在旧框架中,而是在我们所处的情况下:不支持该应用程序,几乎不可能找到十年前框架的开发人员。我们必须对此做些事情。

让我们运行算法。可以区分技术债务的几个部分,并以迭代方式处理此过程。首先,让我们处理前端。我们可以使用旧的后端启动新的前端。我们将能够扩展新的Frontend,使其适应现代技术,它将满足我们的目标。我们既可以完全依靠旧的后端,也可以对其稍加修改以与新的前端一起使用。下一步是封装。通过封装,架构可以在这里为我们提供帮助。在这种情况下,封装点将是与后端的合同。启动新的Frontend之后,我们可以删除Frontend的旧部分。现在,我们的整个应用程序将变得越来越绿色。



下一步是使用后端。在这里,封装点已经是数据库层。事实证明,架构将再次为我们完成这种封装。我们可以在附近创建一个新的解决方案,使用相同的数据,然后将Frontend传输到该解决方案。现在,我们已经完全放弃了旧的解决方案,可以将其丢弃。这使我们可以实现为该项目设定的目标。



第二种情况


采取棘手的情况。有一个应用程序,它具有特定功能,负责将货币对保存在数据库中。例如,卢布-美元,美元-日元等。信息存储在表中的数据库中。为了使它更加有趣,请添加几个依赖项:有一个使用者直接从数据库接收数据,另一个数据提供者可以直接将其提供给数据库。



我们对数据格式和此数据进入数据库的方式不满意。但是您需要仔细修复它,因为有很多依赖项。

为此,选择一个特定的部分-数据。您需要封装它们。为此,请输入中间层。他们的任务是确保消费者和供应商不会注意到任何变化。这就是封装的含义。现在我们可以更改存储结构,因为这不会影响外部依赖关系。之后,我们可以构建一个新的解决方案,以新格式记录数据。最后一步是将旧数据转换为新格式,并从项目中获得我们想要的:新格式的数据,可以从应用程序中删除旧逻辑。


在此过程中,数据使用者不会注意到这些更改,这意味着我们在保持向后兼容性的同时完全安全地进行了更改。然后,如果项目需要,您可以与使用者和数据提供者一起使用,以便他们也使用新格式。

第三种情况


为了扩大规模并了解它在大型项目中的工作原理,可以想象有一个大型项目,大型代码库和某种关键功能可以发芽到应用程序的各个方面。后端的其他部分使用它,它可以访问Public API,也就是说,数据在某处泄漏。前端,外部系统甚至附件中都使用该功能,直接从数据库到分析。为了使示例更有趣,请在此处添加一些Legacy。好吧,一点。



两个有趣的事实:

  1. 绝对可以。
  2. 没有人知道它是如何工作的。这就是传统。

在这种情况下,有很多联系点和许多未知数,使用这种情况时,有必要了解一下:实际上,我们正在使用什么,该解决方案的外观以及其功能是什么。重要的是要了解我们要重做或希望摆脱的解决方案如何与应用程序的其余部分进行交互。我们需要找到所有共同点:了解他们的工作方式,了解他们的合同以便能够提供其他东西。

这里有几种方法可以提供帮助,尤其是在灾难规模足够大的代码量的情况下:

  • . Java , , , . , , , , ;
  • . , , , . , , . , ;
  • -.

如果我们在代码中找到共同点,则可以使用它们,并用封装点包装解决方案。我们为应用程序与我们功能的交互引入了新合同。



这里减少了遗留的数量,因为这时我们已经开始将想要的内容输入到应用程序中。

接下来,您需要迈向新解决方案的第一步-测试。他们将结束我先前提到的有关Legacy No. 2的非常有趣的事实。测试将显示您的解决方案的工作方式。除了检查密钥流之外,您还需要确保它也恰好落在您对他的期望中,以及您对他的期望中。

经常发生的情况是,曾经为业务目的而创建的解决方案被100%使用,而今天与当前目标相交的只有30%,而与当前目标相交的却没有。在当今的现实中,这70%不再重要。您编写的测试将突出显示这30%。运行涂层测试后,您可以了解根本不使用哪些代码,将其删除并降低解决方案的连接性和复杂性。

如果我们编写了测试并理解了它的工作原理后,开始在封装区域引入新的解决方案,那么我们将逐步取代我们不需要的所有内容,删除旧的并用适合我们需求的新解决方案代替。



新的解决方案应该简单易懂,并且应该专门解决​​您的问题,特别是在今天以及针对特定的近期目标。无需进行过度工程,因为我们正在接近今天的最终目标。

这是您应该停下来的地方,暂时挂下来,想一想:“我们为什么要这样做?” 在此阶段,您将获得有关解决方案的工作原理,解决方案的内容和不解决方案的大量信息。您编写了测试,标记了代码,绘制了图表,现在您了解了一个数量级。也许这是值得停止和理解的一点:是否有可能解决更大的问题?这就是曾经为我们的项目节省了开发季度的订单的原因,因为我们为项目的开发选择了正确的方向。

如何组织工作


我们采用一种将新解决方案分解为迭代的方法。这些是其特定的部分,可以在生产中推出,并将对其进行某些更改。

为了了解迭代是什么以及迭代包含什么任务,我们从Scrum借用了“完成定义”的概念。这是必须满足的一组条件,故事才能被视为满足。

在这里,我以稍微不同的形式使用此概念。通过“完成的定义”,我了解当特定迭代投入生产时应用程序将发生什么变化的描述。

让我们记住订单管理系统的第一个示例。在其中,用户可以使用新的UI和旧的后端创建新订单。这是迭代的类型-我们可以将其推广到生产中的功能的特定部分,它将起作用。或者用户可以使用新的访问权限模型登录-这也是质的变化。因此,您可以描述每次迭代在对抗技术债务的斗争中究竟能提供什么。

当您将解决方案分解为迭代时,可能会有很多问题。它们之间将存在依赖关系,我们得到一个完整的图。在本专栏中,将有几种方法来获得最终结果。您可以使用反向计划-一种有助于减少工作时间的工具。我们从项目的最后一点开始,问一个问题:“要实现这个目标需要做什么?”。我们了解,为了实现这一目标,我们必须采取上一步。因此,我们从头到尾移动,在每个中间步骤中,我们都会通过关键路径回答此问题。一旦这种方法为我们节省了开发时间。

称为甘特图的工具非常适合可视化工作。此图显示了任务之间的关系,任务的持续时间以及项目的外观。图中是Wrike的屏幕截图。我们拥有这个工具,我们正在积极地使用它来处理项目。



如果您正在开发一个广泛的解决方案,则可能会遇到这样的情况,即有人更改了您重构,修改,引导您的过程以及您刚刚想到的代码。这些更改会使您难以处理技术债务。

您可以通过以下几种方式保护自己免受此类更改:

  • — . , , - , .
  • , . git hook, git commit git push. unit test, , - , , , : .

您也可以为代码配置外部监视系统。在Wrike,我们使用PMD。系统启动,并在每行新代码中检查是否符合某些规则。该图片是构建日志中的一个示例。在这里违反了“公共方法结果必须是不可变的”规则,PMD讨论了该规则并显示了哪一行-这是“错误方法”方法。以下是您需要对其进行修复的线索。因此,我们始终知道违反规则的地方以及解决方法。



如何度过最后的老板


我们尚未谈论的重要一件事是最后的老板,他在处理技术债务时必须经过审查。企业,产品所有者,客户-每个人的名字都不同。它可能会干扰我们将工程计划拖到生产中。

我们都知道从产品的角度来看产品所有者没有提供最佳解决方案的情况。但是,即使您的产品所有者凝视着钛片,您仍然可以与他谈判。使用技术债务,您会开辟新的机会,产品所有者需要他们开发产品。

您可以约定时间配额。例如,开发人员有10%的时间将致力于处理技术债务。这样的配额将不允许摆脱技术债务,但不会使其膨胀。

显然,如果不清楚谈话的确切含义,就很难谈论技术债务。因此,您的团队应该有一个技术积压,其中的任务由开发人员评估和确定优先级。

但是,以上工具将不允许处理大型项目。在这种情况下,就前面列出的内容(技术债务,跟踪器中的项目,图形中的关键路径等3个问题)而言,重要的是要能够向客户解释项目的好处。而且,故事越详尽,您对故事的理解就越多。企业的目标是可以帮助您实现的。重要的是,您不仅要了解当前正在发生的事情,而且还要了解即将在各个季度实现的计划。因此,与业务部门进行沟通,了解其要做什么以及实现这些目标的方式是什么,然后使用这些知识来组合您的工程目标和业务目标。

目标可能不会立即重合,而是要在四分之一甚至一年内达成。但是,这将使您了解何时可以进行更改。

如果您能够同步目标,那么您将获得所有好处:优先级,资源和所有工作的组织。产品负责人将帮助您做到这一点。主要任务是同意他的意见。


当我们谈论产品关键领域的变化时,一个简单的四步算法变得复杂:增加了



两个步骤。首先是对我们正在使用的东西的理解。第二个-封装后-了解解决方案的工作原理以及如何防止代码更改。

使用此算法和我对组织流程的建议,您可以解决任何技术问题。

这篇文章基于我在会议上的演讲,您可以看到视频报告。

All Articles