古老的工程格言:不触摸它,它起作用。很糟糕。不听。在小规模上可能还可以,但是随着时间的流逝,位腐烂会通过您的代码扩散,并污染所有内容。您系统的大部分变成了无人群的土地。当您开发一个新系统时,您必须始终触摸它,并确保我们雇用害怕这样做的工程师。
是的,我明白了。我过去经常说那句话。我了解动机。管理层不关心我们未来的腐烂。他们关心现在和现在。您为什么浪费时间在此功能上?
它有效。您的盘子上没有受够了吗?
您是编码的玛丽·康多(Marie Kondo)吗?此代码不引发欢乐吗?
它更像是一个桶中的坏苹果。不良的代码和禁区往往会增长和转移。现有团队需要完全访问一个生活项目。它可以继续工作,但这使得将来的每个步骤都很痛苦。
当我们拥有一个具有相对较小且熟悉的代码基础的灵活团队时,触摸一切都没有挑战。在这种情况下很容易。
传统项目
困难的部分是触摸旧版项目中的代码。作为顾问,我必须经常这样做。您如何使用一百万行代码进入项目并开始重构?
好事是我们都一样。建立该项目的工程师接受了类似的书籍和类似的思维过程培训。一旦您了解了他们的逻辑,您就可以理解他们为什么做某事。但是,困难的很大一部分是工具。 20年前建造的项目使用了不再可用的工具。该代码可能不再在现代IDE上编译。我们的直接反射是尝试使用旧的IDE和旧工具。
这可能是一个错误。
旧工具保持陈旧的位腐烂。这是一个机会。重新审视项目并更新工具。几年前,我为较旧的C ++代码库做了一些工作。我不了解代码库,但是原始开发人员在旧版本的Visual Studio中构建了它。使用LLVM和VS代码在我的Mac上使用它有助于我更清楚地想象移动的作品。一旦我启动调试器并运行,解决错误和怪异的问题就变得琐碎了。我可以说我完全理解了该代码库。但是移植和更新工具的过程使我面临许多细微差别和问题。
当你可以
另一面是现有的遗产系统是客户要求的。我必须与外部黑匣子的旧系统实施集成。我们不需要触摸他们的代码,但是我们需要与这些系统进行交互并依靠它们的行为。这是一个非常具有挑战性的情况。
在这种情况下,我们的解决方案是为系统创建模拟,以便我们可以模拟和测试各种情况。在这种情况下,我们编写了一个应用程序,该应用程序从这样的黑匣子中发送了请求并保存了响应,以创建一个简单的记录器。然后,我们将录音用作实施中测试的基础。这可能不是一个选择,因为有时,黑匣子直接连接到生产(一种情况下,直接与股票市场)。
我处理这样一个黑匣子的规则是:
-
一个孤立的模块可以处理所有连接 - 这样我们就可以为故障构建统一的解决方法。我们可以使用物理隔离的微服务,这是这种特定情况的理想选择。
-
使用异步调用揭示结果 - 这可以防止僵局并超载旧系统。我们可以使用队列映射故障和错误处理的原因更简单,因为故障刚刚调用结果回调。
-
我们需要在防御性上进行编码。使用断路器,记录和一般可观察性工具。期望每个角落都失败,因为这将是项目中最有争议的部分。
一旦我们包装了该遗产,就需要触发故障的警报。有些故障可能不会泡出到用户界面,并且可能会触发成功的重试。这可能是一个严重的问题。例如。如果股票市场购买命令失败了,交易者可能会提出重试,这将发出新的成功命令。但是原始命令可能会在旧系统中隐含地重试,我们最终可以进行两次购买。
这样的错误可能非常昂贵,并源自那个黑匣子。如果不完全审查旧版代码并理解它,我们就无法保证。我们可以做的是迅速,准确地应对这种类型的失败。在这种情况下,辩论性很重要,因此在这样的黑匣子中可观察性和孤立的重要性。
通过可观察性信心
过去,每当我们推出新版本时,我们都会观看服务器日志。等待用户投诉涌入。由于可观察性,我们是第一个了解生产中问题的信息。可观察性翻转脚本。
不幸的是,了解问题并理解它,解决问题并注意到它之间有很大的鸿沟。如果我们查看可观察性控制台,我们可能会注意到一个异常,它突出了问题,但即使发生回归,它也可能不会触发警报。一个很好的例子是错误估计。对应用程序逻辑的更改可以报告错误的结果,这在可观察性数据中不太可能显示。
从理论上讲,测试应该发现这个问题,但是测试非常擅长验证我们预测的事情没有发生。他们没有检查意外的错误。例如。我们可能会为财务计算分配一个现场规模,它对美国开发人员的效果很好。但是,日本在日元工作的客户可能会有更大的数量,并且由于限制而经历回归。
我们可以与开发人员可观察性工具一起调试此类问题,但是当我们深入整合旧系统时,我们必须深入应用失败速度原则,这样可观察性层将知道该问题。我们需要主张期望并检查未在测试中而是在生产代码中检查条件。在这里,实际错误将比隐身错误更好。
语言中已经将很多重点放在语言的非零功能上。但是,通过合同埃菲尔(Eiffel)等语言开创的概念已经过时了。这是可以理解的,编写这种代码很难且尴尬。检查的例外通常是Java语言中最讨厌的功能。想象一下必须为每个输入写所有您期望的约束。
更不用说对环境状态的依赖性了。这是不可替代的,并且执行此入住运行时会更加昂贵。但是,这是我们可以有意识地在模块或微服务的入口处做的事情。由于结果的不可预测性质,与传统系统集成时,失败的原则至关重要。
概括
在90年代,我曾经乘公共汽车去工作。每天,我走到办公室时,我都会通过一台银行机器,每次重新启动时,我都会接近。这可能是他们自行车政策的一部分,银行具有按计划重新启动机器以避免潜在问题的文化。
一个早晨,我去了机器,它没有重新启动。我做了每个好程序员/黑客都会做的事情;我掏出卡,试图使用它。它立即重新启动,不会拿我的卡,但是我的直觉是尝试的事实。即使这是世界上最聪明的事情,我们也需要保持代码易于访问和新鲜。遗产代码不是一个鬼屋,我们不应该害怕。