如何进行大型重构项目?

Ido*_*ang 21 legacy refactoring project-management

我即将开始计划我们的代码库的重大重构,我想得到一些问题的一些意见和答案(我已经看到很多关于类似主题的讨论,例如/sf/ 108141/how-do-do-do-working-very-messy-legacy-code, 用于大规模重构的策略,但我有一些具体的问题(在底部):

我们开发了一个复杂的应用 有大约25名开发人员在使用代码库.到目前为止,该产品的总人数约为150年.目前的代码库是一个用ant构建的单一项目.我正在着手的项目的高级目标是将代码库模块化为其各种基础架构和应用程序组件.目前各种逻辑组件之间没有良好的分离,因此很明显,任何模块化工作都需要包含一些API定义并进行严格的解决以实现分离.质量标准很低 - 几乎没有测试,并且绝对没有测试作为构建过程的一部分运行.

另一个非常重要的一点是,该项目需要与活跃的产品开发和发送给客户的版本并行进行.

项目目标:

  • 允许跨不同项目重用组件
  • 将应用程序与基础架构分开,并允许它们独立发展
  • 提高可测试性(通过创建API)
  • 简化开发人员的开发环境(减少检出和编译的代码)

我的想法和问题:

  1. 您对该项目的目标有何看法?你会改变什么?
  2. 你有这些项目的经验吗?会有什么建议吗?
  3. 我非常关心缺乏测试 - 因此我无法控制,因为我知道重构过程并没有像我一样破坏任何东西.这是一个问题22,因为这个项目的目标之一是使我们的代码更可测试...
  4. Michael Feathers的" 有效使用遗产代码 "非常受我的影响.根据它,自下而上的方法是解决我的问题的方法 - 不要首先跳到代码库并尝试修复它,而是通过在几个月内围绕新代码添加单元测试来开始,并看看如何代码(和团队)变得更好,在某种程度上抽象将出现,API将浮出水面,基本上 - 模块化将自己开始发生.有没有人有这方面的经验?正如关于这个主题的许多其他问题所见 - 这里的主要问题是管理上的怀疑."如何按类别进行测试(并花费大量时间这样做)会让我们进入一个稳定的系统?这是一个很好的理论,它在现实生活中不起作用".出售这个的任何提示?

jon*_*tyc 14

嗯,我想现在比以后更好,但你肯定有一个任务在你前面.我曾经在一个三人小组中负责重构类似大小的产品.这是程序代码,但我将描述一些同样适用的问题.

我们从底层开始,通过选择应该具有高度可重用性但不是可重复使用的功能,开始放松.我们会在现有代码上编写一系列单元测试(根本不存在!),但不久之后,我们遇到了第一个大问题 - 现有代码中存在已经处于休眠状态的错误.

我们修理它们吗?如果我们这样做,那么我们已经超越了重构.因此,我们要记录现有代码的问题,希望获得一个固定且经过新近测试的代码库,但当然管理层认为有更重要的优先事项而不是修复从未出现过的错误.可以理解的.

所以我们认为我们会尝试修复新代码中的错误.然后我们发现原始代码中的这些错误使其他代码工作,所以真的是"概念错误"而不是"功能错误".也许.在原始软件中偶尔出现间歇性痉挛,这些痉挛从未被追踪过.

因此,我们改变了方向,并决定将错误保留到位,正如真正的重构应该做的那样.很容易无意中引入错误,故意这么做更难!

接下来的问题是,代码是混乱的,我们编写的初始单元测试必须大幅改变才能满足重构.换句话说,两个移动目标.不好.只是写测试花了很多年,让我们失去了对项目价值的信念.这真的是你想要离开的东西.

我们最终发现,如果我们要完成这个千年,我们真的不得不降低重构的程度,这意味着我们梦想的代码库将无法实现.我们宣称最可行的解决方案只是清理和修剪代码,至少使概念上更易于理解,以供将来的开发人员修改.

有限重构的优势减少被认为不值得管理层付出努力,并且考虑到在硬件平台(嵌入式项目)中发现了类似的可靠性问题,公司认为这是他们用软件更新整个产品的机会.从头开始,新语言,对象.只有原始产品的广泛系统测试规范意味着这有机会.


Ira*_*ter 6

显然,当您尝试重构代码时,缺少测试会使人们感到紧张。哪里会有人相信您的重构不会破坏应用程序?我认为,您会得到的大多数答案都是“这将非常困难,而且不会很成功”,这主要是因为您面临着巨大的手动任务,并且对答案没有信心。

只有两种出路。

  • 构建一堆测试。不幸的是,这会花费很多时间,而且大多数管理者看不到任何价值;毕竟,到目前为止,你已经在没有他们的情况下相处了。回到信仰问题无济于事;在任何有用的事情发生之前,您仍然需要花费很多时间。如果他们确实允许您构建测试,那么您将在重构时遇到改进测试的问题;它们可能不会改变一点功能,但是当您构建新的 API 时,测试将不得不更改以匹配新的 API。这是重构代码库之外的额外工作。

  • 自动化重构过程。如果您应用值得信赖的自动化转换,您可能会争辩说(通常不成功)重构的代码保留了原始系统功能。打败不成功论点的方法是编写这些测试(参见第一种方法)并将重构过程应用于应用程序测试;随着应用程序改变结构,测试也必须改变。但从自动化机械的角度来看,它们只是应用程序代码。

没有多少人做后者;你从哪里得到可以做这些事情的工具?

事实上,这样的工具是存在的。它们被称为程序转换工具,用于对代码进行大量转换。将这些视为大规模重构的工具;由于规模原因,它们往往不具有交互性。

为手头的任务配置它们确实需要付出努力;您必须编写自定义规则来完成自定义所需的结果。您可能无法在一周内完成此操作,但这比手动修改大型系统要少得多。并且您应该考虑到您在现有软件上投入了 150 人年;花了这么长时间才弄得一团糟。相比之下,“一些”小的努力应该是可以的,这似乎是合理的。

我只知道 3 个这样的工具有机会处理真实代码: TXLStratego/XT和我们的工具DMS 软件再工程工具包。前两个是学术产品(虽然TXL过去曾用于商业活动);DMS 是商业的。

DMS 已被用于各种大规模软件分析和大规模转换任务。一项任务是 B-2 隐形轰炸机的语言之间的自动翻译。另一个更接近您的重构问题的是为组件自动构建基于组件的大规模系统 C++,从具有关于组件组织方式的特殊规则的遗留专有 RTOS,到组件 API 具有的 CORBA/RT从临时结构更改为 CORBA 样式的方面插座接口,并使用 CORBA/RT 服务代替传统的 RTOS 服务。(这些任务都是由非常聪明且精通 DMS 的人用 1-2 人年的实际努力完成的)。

仍然存在测试构建问题(上面的两个示例都已经进行了很好的系统测试)。在这里,我要冒险一试。我相信有希望通过检测运行代码来收集函数输入输出结果,从而使此类工具自动生成测试。我们已经为源代码构建了各种检测(显然您必须在检测后编译它)并且认为我们知道如何做到这一点。天啊。

您所做的事情远没有那么雄心勃勃:通过找出代码中已重用的内容,确定代码的可重用部分。大多数软件系统都包含大量克隆代码(我们的经验是 10-20% [我对另一个答案中 PHP 报告的较小数字感到惊讶;我怀疑他们使用的是弱克隆检测器)。克隆代码暗示了应用软件中缺少抽象。如果您可以找到克隆并查看它们如何变化,您就可以很容易地了解如何将它们抽象为函数(或其他任何东西)以使其明确且可重用。

Salion Inc.进行了克隆检测和提取。这篇论文没有探讨抽象活动;Salion 实际上所做的是对检测到的克隆进行定期审查,并对过分的克隆或那些对(通常是图书馆)方法有意义的克隆进行手动修复。最终结果是代码库的大小实际上缩小了,程序员变得更有效率,因为他们拥有更好(“更可重用”)的库。

他们使用了我们的CloneDR,这是一种通过使用程序语法作为指南来查找克隆的工具。CloneDR 查找精确的克隆和未遂事件(标识符或语句的替换),并提供克隆位置和克隆参数化的特定列表,而不管布局和注释如何。您可以在链接中查看多种语言的克隆报告。(在我的众多帽子中,我是 CloneDR创始人和作者)。

关于另一个答案中讨论的 PHP 项目的“小克隆百分比”:我不知道克隆检测器使用了什么。我所知道的唯一专注于 PHP 的克隆检测器是PHPCPD,恕我直言,这是一个糟糕的克隆检测器;如果我理解声称的实现,它只会找到精确的克隆。请参阅我们网站上的 PHP 示例以进行比较。