遗产代码梦魇

时间:2009-03-17 16:06:13

标签: refactoring legacy-code

我继承了一个项目,其中类图非常类似于意大利面条上的蜘蛛网。在过去的两个月里,我写了大约300个单元测试,给自己一个覆盖主要可执行文件的安全网。

我在任何特定时刻都可以使用我的敏捷开发书库:

  • 有效使用旧版代码
  • 重构
  • 代码完成
  • C#中的敏捷原则模式和实践

问题是我触摸的一切似乎打破了别的东西。 UI类混合了业务逻辑和数据库代码。许多类之间存在相互依赖关系。每次我改变任何其他课程时,都会有几个神课程。还有一个带有大约一半实例方法和半静态方法的突变单例/实用程序类(虽然具有讽刺意味的是静态方法依赖于实例而实例方法却没有)。

我的前辈甚至认为向后使用所有数据集会很聪明。每个数据库更新都作为存储过程中的参数直接发送到数据库服务器,然后手动刷新数据集,以便UI显示最新的更改。

我有时会想到他们会在处理代码之前使用某种形式的弱混淆来保证工作安全或作为最后的告别。

有没有什么好的资源来解决这个烂摊子?我的书很有帮助,但似乎只涵盖了我遇到的一半场景。

13 个答案:

答案 0 :(得分:24)

听起来你正在以正确的方式解决它。

  • 测试
  • 重构
  • 再次测试

不幸的是,这可能是一个缓慢而乏味的过程。实际上没有什么可以替代挖掘并理解代码试图完成的任务。

我可以推荐的一本书(如果你还没有在“等”下提交的那本书)是Refactoring to Patterns。它面向处于您确切情况的人。

答案 1 :(得分:18)

我在类似的情况下工作。

如果它不是一个小型实用程序,而是一个大型企业项目,则它是:

a)来不及修复它    b)超出单个人的能力尝试a)
   c)只能通过完全重写不可能的东西来修复

在许多情况下,重构可能仅在您的私人时间内尝试,您的个人风险。如果你没有明确要求在日常工作中做到这一点,那么你甚至可能没有得到任何信任。甚至可能会因为“毫无意义地浪费时间在已经完美工作了很长时间的事情上”而受到批评。

继续以之前被黑客入侵的方式继续攻击,收到你的薪水等等。当你完全沮丧或系统达到不可攻击的程度时,找另一份工作。

编辑:每当我试图解决真正的建筑问题并以正确的方式做事时,我通常会直接从负责任的经理那里得到LOL,他们说的是“我不会对好的建筑有所了解“(试图从德语翻译)。我亲自带来了一个非常糟糕的组件到了不可破解的程度,当然还提前几个月发出了预警。然后他们不得不取消一些承诺的功能给客户,因为它不再可行。没人接触它......

答案 2 :(得分:8)

我以前做过这份工作。我花了两年多时间研究一种非常相似的传统野兽。我们花了一年多的时间来稳定一切(它仍然破裂,但它更好)。

首先 - 如果应用程序尚不存在,请将异常登录到应用程序中。我们使用FogBugz,我们花了大约一个月的时间将报告集成到我们的应用中;它不是很完美,但它自动报告错误。在您的所有活动中实施try-catch块通常都非常安全,这将涵盖您的大多数错误。

从那里修复首先出现的错误。然后打小战,特别是那些基于小虫的战斗。如果您修复了一个意外影响其他内容的错误,请重构该阻塞,使其与其余代码分离。

无论多么糟糕,都需要采取一些极端措施来重写一个关键的公司成功应用程序。即使您获得了许可,您也将花费太多时间来支持遗留应用程序,以便在重写方面取得任何进展。如果你做了很多小的重构,最终要么大的重构,要么你的重写基础课真的很好。

有一点可以理解,这是一次很棒的体验。这会很令人沮丧,但你会学到很多东西。

答案 3 :(得分:4)

我曾经(曾经)遇到过如此疯狂纠结的代码,以至于我无法在合理的时间内使用功能重复修复它。这是一种特殊情况,因为它是一个解析器,我不知道有多少客户可能“使用”它的一些错误。渲染数百个“工作”源文件是错误的,不是一个好选择。

大部分时间它都是迫在眉睫的,只是令人生畏。仔细阅读重构书。

我通常会通过稍微移动一些东西来开始修复坏代码(实际上没有实际更改实现代码),这样模块和类至少在某种程度上是连贯的。

完成后,您可以使用更连贯的类并重写其内容以执行完全相同的方式,但这次使用合理的代码。这是管理方面的棘手部分,因为他们通常不喜欢听到您需要花费数周时间来编写代码并调试一些行为完全相同的东西(如果一切顺利的话)。

在此过程中,我保证您会发现大量的错误,以及彻底的设计愚蠢。可以在重新编码时修复琐碎的错误,但在以后留下这些东西。

一旦完成了几个类,你就会开始看到哪些东西可以更好地模块化,设计得更好等等。另外,在不影响不相关的东西的情况下进行这样的更改会更容易,因为代码现在更模块化了,你可能彻底知道了。

答案 4 :(得分:1)

大多数情况下,这听起来很糟糕。但我不明白这一部分:

  

我的前辈们甚至认为会这样   聪明地使用所有数据集   向后。每个数据库更新都是   直接发送到数据库服务器   然后,存储过程中的参数   手动刷新数据集   用户界面将显示最新的   变化。

这听起来非常接近我经常写东西的方式。这有什么问题?什么是正确的方法?

答案 5 :(得分:1)

答案 6 :(得分:1)

没有一本书能够涵盖所有可能的场景。它还取决于您对项目的期望以及是否存在任何外部规范。

  • 如果你只需要做一些偶然的小改动,那就做那些,不要再开始重构了。
  • 如果有规范(或者你可以让别人写一个规范),如果可以通过可预见的项目变更来证明这一点,则考虑完全重写
  • 如果“实施是规范”计划进行了很多更改,那么你就会受到很多关注。写下很多unit tests并开始重构。

实际上,无论你做什么,单元测试都是非常宝贵的(如果你可以将它们写入一个不会因重构或重写而发生太大变化的界面)。

答案 7 :(得分:1)

如果您的重构破坏了代码,特别是代码似乎无关,那么您一次尝试做的太多了。

我建议进行首次通过重构,你所做的就是ExtractMethod:目标只是为代码中的每一步命名,而不需要任何合并尝试。

之后,考虑打破依赖关系,取代单身人士,巩固。

答案 8 :(得分:1)

如果你的重构破坏了,那就意味着你没有足够的单元测试覆盖率 - 因为单元测试应该先破坏。我建议你在异常登录后再获得更好的单元测试覆盖率。

然后我建议你先进行小型重构 - 提取方法将大型方法分解为可理解的部分;引入变量以删除方法中的一些重复;如果您发现调用者和被调用者使用的变量之间存在重复,可能会引入参数。

在每次重构或重构后运行单元测试套件。我会说运行它们 all 直到你对每次需要重新运行哪些测试有信心。

答案 9 :(得分:0)

祝你好运,这是成为开发人员的难点。

我认为您的方法很好,但您需要专注于提供业务价值(单位测试的数量不是衡量业务价值的指标,但如果您处于正常轨道或偏离轨道,它可能会给您一个指示)。重要的是要确定需要改变的行为,确定优先顺序并专注于最重要的行为。

另一条建议是保持谦虚。要意识到如果你在真正的截止日期之前写了如此大的东西而其他人看到了你的代码,他们也可能在理解它时遇到问题。编写干净的代码有一定的技巧,处理其他人的代码有一个更重要的技能。

最后一条建议是尝试利用团队的其他成员。过去的成员可能知道您可以学习的系统的信息。此外,他们可能能够帮助测试行为。我知道理想的是进行自动化测试,但是如果有人可以通过手动验证来帮助他们寻求帮助。

答案 10 :(得分:0)

我特别喜欢 Code Complete 中的图表,其中您只使用遗留代码,一个模糊灰色纹理矩形。然后当你替换其中一些时,你的底部是模糊的灰色,顶部是纯白色,还有一条锯齿状的线代表两者之间的界面。

也就是说,一切都是'令人讨厌的旧东西'或'漂亮的新东西'。这一行或另一行的一面。

该行是锯齿状的,因为您正在以不同的速率迁移系统的不同部分。

当你工作时,锯齿状线逐渐下降,直到你有更多的白色而不是灰色,最后只是灰色。

当然,这并不会使细节变得更容易。但它确实为您提供了一个可用于监控进度的模型。在任何时候你都应该清楚地了解这条线的位置:哪些是新的,哪些是旧的,以及双方如何沟通。

答案 11 :(得分:0)

您可能会发现以下帖子有用: http://refactoringin.net/?p=36

正如帖子中所说,不要轻易丢弃完全覆盖。此外,如果可能的话,尝试使用第三方解决方案(例如ORM)替换整个层或层,以实现持久性或使用新代码。但最重要的是,尝试理解代码背后的逻辑(问题域)。

答案 12 :(得分:0)

您可以提取并重构其中的某些部分,以打破依赖关系并将图层隔离到不同的模块,库,程序集和目录中。然后使用strangler application策略将已清理的部件重新注入应用程序。泡沫,冲洗,重复。