重写旧代码是牙医的旅行-似乎每个人都知道应该去,但他们仍然拖延并试图延迟不可避免的事情,因为他们知道这样做会伤害人。 在我们的情况下,情况甚至更糟:我们不得不重写系统的关键部分,并且由于外部环境,我们无法用新的部分替换旧的代码片段,而只能一次全部删除。 所有这些都是在缺乏时间,资源和文档的情况下进行的,但是由于管理上的要求,由于“运营”,任何客户都不应遭受损失。
在削减之后,我们如何重写产品主要组件的故事已有17年的历史(!)从Scheme到Clojure,所有这些都立即起作用了(嗯,几乎是:))。

17年的“观察”
Solar Dozor是具有悠久历史的DLP系统。 第一个版本出现在2001年,当时它是用于过滤邮件流量的相对较小的服务。 在超过17年的时间里,该产品已发展成为一个大型软件包,可以收集,过滤和分析组织内部运行的异构信息,并保护客户业务免受内部威胁。
在开发第6版Solar Dozor时,我们果断地动摇了产品,从代码中淘汰了旧的拐杖,
并用新的拐杖
代替了它们 ,更新了界面,朝着现代现实的方向修改了功能-总的来说,使产品在结构上和概念上更具整体性。
当时,在经过更新的Solar Dozor的掩盖下,存在着巨大的整体遗留代码层-非常过滤的服务,在这17年中,它已逐渐发展为新功能,既体现了长期解决方案又体现了短期业务任务,但设法保留在原始体系结构内范例。
过滤服务不用说,对这种古老的代码进行任何更改都需要特殊的技巧。 开发人员必须非常小心,以免意外破坏十年前创建的功能。 此外,还迫使相当有趣的新解决方案挤入了在时代初期发明的Procrustean建筑床。
很早以前就已经了解到有必要更新系统。 但是显然缺乏接触庞大而古老的系统服务的精神。
不试图延迟不可避免
具有悠久历史的产品具有有趣的功能。 无论任何功能看起来多么奇特,只要它能够成功存活到今天,就意味着它不是根据开发人员的理论思想而是根据客户的特定需求而创建的。
在这种情况下,无法谈论任何分阶段更换。 削减和重做零件的功能是不可能的,因为所有这些零件都是客户需要的,我们无法“关闭它们以进行重建”。 有必要仔细删除旧服务,并为其提供功能齐全的替代产品。 仅整体而言,仅一次。
改善产品开发过程,进行更改的速度以及提高整体质量是必要但不充分的条件。 管理层想知道变革将为我们的客户带来什么好处。 答案是扩展用于与新拦截系统交互的接口集,这将提供快速反馈并允许拦截器对事件做出更快的响应。
我们还必须努力减少资源消耗,保持(最好是提高)当前的处理速度。
关于填充的一些知识
在整个产品开发路径中,Solar Dozor团队倾向于采用功能性方法。 对于成熟的行业,这导致了相当不标准的编程语言选择。 在系统生命周期的不同阶段,除了传统的C(++)和Java,它们还包括Scheme,OCaml,Scala,Clojure。
主要的筛选服务和其他有助于接收和传输消息的服务均以Scheme语言在各种实现中编写和开发(Racket使用了后者)。 不管有多少人想赞美这种语言的简洁性和优雅性,人们都不得不承认它的发展比工业的满足了更多的学术兴趣。 与主要在Scala和Clojure上开发的其他更现代的Solar Dozor服务相比,此延迟尤其明显。 还决定在Clojure中实施新服务。
Clojure?!
当然,在这里,我必须说几句为什么我们选择Clojure作为主要的实现语言。
首先,我不想失去在Scheme上开发团队的独特经验。 Clojure还是Lisp语言家族的现代成员,从一个Lisp切换到另一个Lisp通常很简单。
其次,由于对功能原理的承诺和许多独特的体系结构解决方案,Clojure提供了前所未有的操作数据流的简便性。 Clojure在JVM平台上运行也很重要,这意味着您可以将联合数据库与Java和Scala中的其他服务一起使用,以及使用大量工具进行性能分析和调试。
第三,Clojure是一种简洁而富有表现力的语言。 这使得读取他人的代码变得容易,并且易于将代码传递给队友。
最后,我们对Clojure的原型开发和所谓的以REPL为中心的开发的易用性表示赞赏。 在几乎任何有疑问的情况下,您都可以简单地创建一个原型,并使用新数据以更实质性的方式继续讨论。 面向REPL的开发可以快速获得回报,因为要检查功能的功能,不仅需要重新编译程序,甚至需要重新启动它(即使该程序是位于远程服务器上的服务)。
展望未来,我可以说:我认为我们没有失去选择的余地。
一点一点地放置功能
当我们谈论功能齐全的替代产品时,出现的第一个问题是有关现有功能的信息的收集。
这已经成为一个非常有趣的任务。 似乎这是一个工作系统,这是该系统的文档,这是一些人-与该系统紧密合作并向他人介绍该系统的专家。 但是要获得整个品种的完整图片,甚至更多,对开发的要求就不是那么简单。
需求的收集不会白费一个单独的工程学科。 矛盾的是,现有的实现是某些“腐败的标准”的作用。 它显示了它是如何工作的以及如何工作,但是与此同时,开发人员希望新版本的结果要好于原始版本。 有必要将实施的必要时刻(通常与外部接口有关)与可以根据用户期望进行改进的时刻分开。
邮件过滤过程说明文件还不够
系统的实际功能是什么? 通过各种描述(例如用户文档,手册和体系结构文档)给出了此问题的答案,这些描述在各个方面反映了服务的结构。 但是当涉及到它时,您会非常了解想法和现实之间的差异,旧代码包含多少细微差别和无法解释的可能性。
我想联系所有开发商。 照顾好您的代码! 这是您最重要的资产。 不要依赖文档。 仅信任源代码。
对我们来说幸运的是,由于为教学编程而创建的语言的本质,Scheme代码即使对于未经培训的人也很容易阅读。 最主要的是要习惯一些带有淡淡的Lisp古老风格的个体形式。
建立一个过程
工作量巨大,团队很小。 因此,这并非没有组织上的困难。 对旧过滤服务的错误和修复请求(和较小的改进)的工作流甚至没有停止。 开发人员通常必须分心于这些任务。
幸运的是,有可能抵制将功能强大的新功能嵌入旧过滤器的请求。 没错,在将这项功能嵌入新服务的承诺中。 但是,发布任务的集合正在缓慢但确实在增长。
增加麻烦的另一个因素是服务的外部依赖性。 作为核心组件,筛选服务使用大量服务来解压缩和分析内容(文本,图像,数字指纹等)。 与他们的合作部分受旧架构决策的指导。 在开发过程中,还必须以现代方式重写某些组件(并以现代语言重写某些组件)。
在这样的条件下,逐步构建了功能测试系统。 我们将服务提高到了一定的状态,并通过主动测试进行了加强,然后继续实施新的服务。
开始开发
首先,实现了服务的主要框架,用于接收消息和解压缩文件的基本机制。 这是开始测试未来服务的速度和正确性所必需的绝对最低要求。
在此必须澄清,解压缩是指从文件中获取零件并从中提取有用信息的递归过程。 因此,例如,Word文档不仅可以包含文本,还可以包含图像,嵌入式Excel文档,OLE对象等等。
拆包机制不区分使用内部库,外部程序还是第三方服务,而是提供用于组织拆包管道的单个接口。
对Clojure的另一个称赞是:我们得到了一个可行的原型,其中我们在最短的时间内概述了未来功能的轮廓。
DSL用于政治
第二步是使用过滤策略添加消息验证。
为了描述策略,创建了一种特殊的DSL-一种简单的语言,没有多余的装饰,这使我们能够以某种或多或少人类可以理解的形式呈现该策略的规则和条件。 它称为MFLang。
使用Clojure代码解释MFLang上“运行中”的脚本,缓存对消息的检查结果,维护详细的工作日志(坦率地说,值得另作文章)。
DSL的使用吸引了测试人员。 深入挖掘数据库或导出格式! 现在可以简单地将生成的规则发送给验证,并且立即清楚检查了哪些条件。 还可以获取详细的消息验证日志,从该日志中可以清楚地看到验证所用的数据以及比较功能返回的结果。
我们可以满怀信心地说,MFLang实际上是调试功能绝对有价值的工具。
全力以赴
在第三阶段,添加了一种机制,以将安全策略定义的操作应用于消息以及服务绑定,以使新组件可以包含在Solar Dozor综合大楼中。 最后,我们能够启动该服务,并观察其各种多样性的工作结果。
当然,主要问题是实现的功能如何与预期的功能相对应以及实现功能的程度。
我注意到,如果很长一段时间以来一直未对单元测试的需求提出质疑(尽管TDD实践本身仍然引起了激烈的争论),则对系统功能的自动测试的引入通常会遇到开放式阻力。
自动测试的开发有助于所有团队成员更好地了解产品的过程,节省回归的精力,对产品的性能充满信心。 但是创建它们的过程充满了许多困难-收集必要的数据,确定感兴趣的指标和测试选项。 程序员不可避免地将自动测试的创建视为可选的附带工作,如果可能的话,最好避免这样做。
但是,如果您设法克服阻力,则会创建一个相当坚实的基础,使您可以对系统的运行状况有所了解。
我们取代
然后一个重要的时刻到了:我们将服务包含在交货包装中。 到目前为止,与旧的一起。 因此,一个团队可以进行版本更改并比较服务的行为。
在这种并行模式下,新的筛选服务持续了一个发行版。 在这段时间内,我们设法收集了有关工作的其他统计数据,概述并实施了必要的改进。
最后,在积累了我们的实力后,我们从产品中删除了旧的过滤服务。 内部验收的最后阶段去了,错误已修复,开发人员开始逐渐转向其他任务。 不知不觉中,在没有大张旗鼓和掌声的情况下,产品发布了一项新服务。
只有当实施团队开始提出问题时,才了解到了-我们已经工作了很长时间的服务已经在现场并且正在工作!
当然,存在错误和较小的改进,但是,在一个月的积极使用之后,客户做出了一个结论:推出具有新版本过滤服务的产品所引起的问题要少于引入先前版本的问题。 y! 看起来我们做到了!
最后
新过滤服务的开发花费了大约一年半的时间。 比原先想的要长,但并不严格,特别是因为工作的实际劳动强度与初步评估相吻合。 更重要的是,我们能够满足管理层和客户的期望,并为将来的产品改进奠定了基础。 尽管产品仍然有足够的优化机会,但在当前状态下,您已经看到资源消耗已大大减少。
我可以添加一些个人印象。
替换历史悠久的核心组件是一口新鲜的空气,以促进发展。 很长时间以来,人们第一次有信心将产品控制权交还给我们。
很难高估适当组织的交流和发展过程的好处。 在这种情况下,重要的是在团队内部建立工作,而不是在众多产品消费者之间建立工作,这些消费者既有长期明确的偏好和对系统的期望,又含糊其词。
对我们来说,这是在Clojure上开发如此大型项目的第一次经验。 最初,人们担心语言的动态特性,速度和容错能力。 幸运的是,他们没有实现。
仍然只希望新组件能够像它的前任一样成功和成功地工作。