Martin Fowler说我们应该在添加新功能之前进行重构(鉴于原始程序结构不完善)。
所以我们都想重构这个肮脏的代码库,这是肯定的。我们还知道,如果没有单元测试代码,很容易引入细微的错误。
但这是一个庞大的代码库。为其添加一整套测试似乎是不切实际的。
在这种情况下你会怎么做?
Martin Fowler说我们应该在添加新功能之前进行重构(鉴于原始程序结构不完善)。
所以我们都想重构这个肮脏的代码库,这是肯定的。我们还知道,如果没有单元测试代码,很容易引入细微的错误。
但这是一个庞大的代码库。为其添加一整套测试似乎是不切实际的。
在这种情况下你会怎么做?
我的建议是你尽可能少地触摸并添加你需要的东西。我发现最好不要管它,尤其是在你的最后期限很紧的时候。
如果您进行了单元测试,那将是另一回事,但是当您更改代码时,就像触摸蜘蛛网一样。改变一件事会影响其他一切。
Let me recommend the book Working effectively with legacy code by Michael Feathers. It contains a lot of realistic examples and shows good techniques for tackling the legacy code beast.
将单元测试添加到要重构的代码中。
请参阅Repose 的 Trickle Theory中的 Rands,了解他遇到的大量错误的类似问题。他的建议:
我的建议是:开始
当单元测试不存在或很难在给定的时间范围内添加适当的覆盖范围时,您将不得不依赖定期测试。从理论上讲,您所说的这个庞大而肮脏的代码库在过去一段时间内被认为适合使用。为了做出决定,要么进行程序员测试,要么进行 QA 测试,要么进行某种由客户进行的测试。您想了解它是如何完成的,做了什么,是否足以涵盖您将进行的更改,然后承诺再次完成该操作以及新代码所需的任何测试,直到产品好足够的。
单元测试对程序员来说是一项很棒的服务,但它们并不是唯一的测试类型。
我发现自己经常处于这种情况,因为这几乎是我过去两年一直坚持的。
正确的方法实际上更多地取决于社会和组织方面,而不是技术方面。您的工作是为您的组织创造价值。如果重构产生的价值大于成本,那么你应该能够出售它。就我而言,关键因素包括:
相关项目的预期所有权。 如果您希望在可预见的未来成为这个特定软件的重要利益相关者,那么这是一个支持对糟糕的代码库 b/c 进行更广泛修改的论据,随着您继续维护它,它将获得更多回报。如果您要添加免下车功能,请采用更加不干涉的方法。
所做更改的复杂性。 如果您正在对代码库进行非常复杂的更改(“脏”代码库中的典型情况,b/c 此类源通常是紧密耦合且不连贯的),则更有可能需要进行一些重构。此类更改也不是成为代码忍者的结果,因为它们对于您进行简单推理是必要的关于你正在做的改变。这也与您正在修改的代码库的“坏处”有关。几乎不可能为紧密耦合、不连贯的混乱创建最简单的单元测试。(我根据经验说话。我几乎无法维护的项目之一是大约 20k 行,不包括生成的代码,包含在 12 个文件中。整个应用程序是一个名为“Form1”的类。这是开发人员滥用部分类特征。)
组织监督。 您的组织监督的强度和严格性在这里发挥作用。如果您的团队确实执行了一些核心最佳实践,例如代码审查,并且不只是口头上说,我更倾向于不进行广泛的重构。价值权衡可能更倾向于尽可能少地接触,因为您有另一双新的眼睛检查以确保您所做的少数更改都不会产生不良的副作用。同样,更严格的监督更有可能对“游击式”代码策略感到不满,即进行更改请求中未严格要求的更改。
你的老板。 如果你的老板站在你这边,你就更有可能对你的代码库进行长期的价值改进,特别是如果你可以在以后的预算小时数方面证明现在增加的成本是合理的。请记住,您的经理比您更了解该软件在大局中的作用。如果它是一个只有十或二十人使用的软件,它就不需要像一个十或两万人使用的软件所要求的那种长期维护改进。
在考虑像这样的任何时间投资时,您需要回答的核心问题是,“价值在哪里?” 然后你需要追逐那个价值。
不要将其视为非此即彼的命题。通过添加单元测试可以增加价值——您不必添加整个套件即可获得添加一些测试的好处。
很大程度上取决于您的语言。如果您使用的是静态类型语言,则可能无需单元测试就可以逃脱。我从事过许多与您所描述的完全一样的工作。
如果工作量很大(比如说,一个人一年或 3 个人 4 个月),那么您可能希望有人拆开代码并首先对其进行分析。
如果它是一种动态语言,那么问题会更大——你需要一定程度的单元测试。也许您可以将单元测试添加到您需要接触的领域。
我只区分静态语言和动态语言,因为在静态类型语言中进行重构要容易得多——它们往往更容易预测。我并不讨厌 Ruby 或其他任何东西——我也在 ROR 上花了一年的时间。我只是认为他们需要不同的方法。
First thing, add tests for the parts you're going to change. Second, refactor the code until it makes sense to a sane person. Third, make the required changes.
Fowler 还建议你永远不要在没有测试安全的情况下进行重构。但是,您如何进行这些测试呢?而且,你能走多远?
之前推荐的书( Michael Feathers 的《有效处理遗留代码》)是该主题的权威著作。
需要快速阅读?查看 Michael 早期的同名文章 (PDF) 。
你必须从某个地方开始。您可以从在可能的地方添加测试开始,并随着您的前进而构建您的测试套件。即使您有一些“愚蠢的”测试,它们什么都不做,只是告诉您您只能测试代码的某些部分告诉您一些事情。
如果添加一套测试是不切实际的,那么代码库就存在严重问题。
添加新代码并希望它可以正常工作是不好的。
编写测试、重构基础并添加新代码。如果你不能测试它,你永远不会知道它是否正确。
尽量少碰。并祈祷。
我同意其他的,尽量少碰。但是,您也可以随时开始为您将要接触的新功能和/或现有功能编写单元测试。没有人说您需要编写单元测试来测试 100% 的现有代码库。