如何在不进行深度重构的情况下修复遗留应用程序中的错误?

Posted

技术标签:

【中文标题】如何在不进行深度重构的情况下修复遗留应用程序中的错误?【英文标题】:How to fix bugs in a legacy application without profound refactoring first? 【发布时间】:2011-05-17 05:25:06 【问题描述】:

有一个中型遗留应用程序(20K LoC,150 个 java 类)。设计和代码的质量非常低,没有单元测试,没有文档。但是它有效。

您受雇来维护应用程序。有一个非常具体的正确报告的错误列表。很难理解如何修复它们。时间有限,您应该在几周内开始解决这些错误(您根本没有时间先进行深入的重构)。如何做到这一点?

【问题讨论】:

一次一个错误?这似乎更适合程序员...... 【参考方案1】:

从第一个错误开始。也许是最简单的,也许是最高优先级的。任何对您的商店有意义的东西。

不要修复它。

相反,找出它无法正常工作的原因。根据您的描述,它不太可能采用某种简洁、漂亮、可单元测试的方法;它可能在某个地方的一些复杂的逻辑中很深。找到它。

不要修复它。

想办法找出罪魁祸首。也许是Extract Method;也许是Extract Class;也许其他一些技术会有所帮助。但是把它放在那里,独立于其他代码。仍然失败,只是更容易使用。现在,编写一个演示失败的测试。

好的,现在修复它。

你做了什么?您已经隔离了问题,修复了它,让一小部分代码更易于处理,并开始进行测试覆盖。

起泡、冲洗、重复。

【讨论】:

如果一个人无休止地重复这个过程,在我看来,你得到的似乎是围绕已修复的错误组织的代码。这似乎是一个非常奇怪的目标。 一点也不奇怪。代码中的热点、故障和已知弱点都得到修复和清理。 那旁边滴答作响的时钟呢? 那个时钟——那个定时炸弹,如果你愿意的话——滴答作响。当它滴答作响时,你可以 (a) 进行大规模的重构并希望你能及时完成,或者 (b) 采用增量方法,专注于你知道的被破坏的地方,并让你的代码在每一步都变得更好。轻松选择。【参考方案2】:

我认为你的处境并不好。

我首先要“重构”这项工作。让你的老板清楚地知道情况非常困难。你还应该明确表示,既然你没有造成混乱,他现在需要承诺不让你为不成功负责,但你会尽最大努力。如果他不愿意在这种情况下预先做出承诺,那么我会再找一份工作。

现在的问题是如何进行。无论你做什么,你都会发现代码中的问题,并在你进行的过程中更容易地做到这一点。

首先,您必须浏览代码并了解一些结构。无论您认为自己理解了什么,都可以插入评论;如果您不知道并且很重要,请插入带有问题的评论。如果可以,请添加一个断言。如果这些新断言很容易被修复,那么就这样做;如果没有,您可以通过简单地删除它们或将它们变成问题评论来对它们进行分类。这些 cmets、问题和断言至少会记录您(不)理解的内容的低级细节。

我还会得到一个工具,它可以让您rename 可靠地应用重构(强调后者:您不能让这些破坏代码比实际情况更糟)。***而虔诚地重命名将有助于稳定代码和您的词汇表,并摆脱坏名。您可以在浏览/处理代码的同时做到这一点,只需很少的时间成本,并在可读性方面获得巨大回报。

为了找到错误的(潜在)来源,我会使用测试覆盖率工具。这样的工具会告诉您在运行“测试”时执行了哪些代码。创造性地使用这样的工具,您可以运行一个“测试”(手动或自动,视实际情况而定)来测试错误;通过测试覆盖工具点亮的代码必须在某处包含错误。您可以运行其他不显示错误的“测试”;他们执行的与错误跟踪相同的代码可能不包含错误。

一个问题是如何计算执行代码中的这种“差异”?一些测试覆盖率工具将帮助您确定这一点。我的公司(语义设计)提供这些:Test Coverage tools for many languages 正是这样做的。通常测试覆盖向量用于显示您的代码,覆盖显示覆盖。我们的工具将让您处理独立的测试覆盖集:相交、差异、联合等,以了解生成向量的测试之间的关系,并显示叠加在您的代码上的那些结果。通过这种方式,您可以直接看到错误的测试覆盖率向量和非错误的测试覆盖率之间的差异。

一旦您知道错误在哪里,就可以在相关代码中添加更多断言,然后再次运行产生错误的测试。触发的是您接近的指标。

我最初会避免需要认真重构代码才能解决的问题,因为您面临时间压力。 (显然,在您了解问题出在哪里之前,您无法很好地了解这一点;探索是不可避免的成本)。只要你能及早解决一些问题,你就会购买政治积分。您可以使用它们来获得更多时间,从而有机会进行更多重构。

【讨论】:

以上是关于如何在不进行深度重构的情况下修复遗留应用程序中的错误?的主要内容,如果未能解决你的问题,请参考以下文章

Room 无法验证数据完整性。如何在不编写迁移步骤的情况下修复它?

重构 PHP OOP - 如何在不传递参数的情况下获取对象?

如何在不复制太多代码的情况下重构此代码?

如何在不替换 ES6/Javascript 中的整个属性的情况下深度复制对象 [重复]

如何在不重构的情况下发布两个具有相同源代码但不同包名的 android 应用程序

InvalidFormatException for Date - 在不使用 JsonFormat 或修改原始类的情况下进行修复