我更改了一种方法签名,现在有25,000多个错误。现在怎么办?


166

我最近开始了新工作,正在处理一个非常大的应用程序(15M loc)。在我之前的工作中,我们有一个类似的大型应用程序,但是(不管是好是坏)我们使用OSGi,这意味着该应用程序被分解为许多微服务,这些微服务可以独立地更改,编译和部署。新的应用程序只是一个大型代码库,可能包含几个.dll。

所以我需要更改此类的界面,因为这是老板要求我执行的操作。最初,他们在编写时就采用了一些假设,但并不能很好地概括其概论,并且有一段时间以来,他们一直在避免重构问题,因为它是如此紧密地耦合在一起。我更改了界面,现在有25000多个错误。有些错误是在具有重要发音的类(如“ XYZPriceCalculator”)中重新产生的不应该打破。但是在解决所有错误之前,我无法启动应用程序来检查其是否正常运行。而且许多单元测试要么直接引用该接口,要么耦合到引用该接口的基类,因此,仅对其进行修复本身就是一项巨大的任务。另外,我真的不知道所有这些部分是如何组合在一起的,因此即使我可以开始学习,但我真的不知道如果事情破裂了会是什么样。

在上一份工作中,我从未真正遇到过这样的问题。我该怎么办?


7
您将需要向我们提供有关错误类型以及“此类”是什么的更多信息,我们不介意读者。
whatsisname

137
“超过25000个错误”在VS中显示的数字通常是错误的。有一些错误,但是由于经常使用的dll的构建被破坏,其他构建也确实失败了,从而增加了天文错误数。我总是从构建输出而不是错误列表中找到的第一条错误消息开始修复。
伯恩哈德·希勒

13
我希望看到您所更改方法的前后签名。顺便说一句-25k的错误听起来确实不胜枚举。乏味的,令人生畏的,是的,难以管理,不。
jmoreno,2016年

46
公共接口是合同。不要打破这样的合同-创建新的合同。
马修(Matthew)

6
25000个错误!?休斯顿,我们遇到了问题。一定要撤消更改并与您的经理交谈,并向他解释您可能必须完全创建一个新界面。
窃窃私语者

Answers:


349

25000个错误基本上意味着“别碰那个”。改回来。创建具有所需接口的新类,然后将类的使用者慢慢移至新类。根据语言的不同,您可以将旧类标记为已弃用,这可能会引起各种编译器警告,但实际上不会破坏您的构建。

不幸的是,这些事情发生在较旧的代码库中。除了慢慢使事情变得更好以外,您无能为力。创建新类时,请确保正确测试它们并使用SOLID原理创建它们,以便将来更容易更改它们。


79
它不一定必须是新。根据语言和情况下,它可能是一个功能,界面,特性等
扬邬达克

33
如果旧接口可以由新接口周围的兼容性包装程序替代,则也有帮助。
Jan Hudec

79
有时候,实际上有25000个错误意味着我们从不敢碰这个问题,但是既然新来者已经到来,就让他完成清理Augean-stables的任务。
mouviciel 2016年

13
@theDmi:我同意,1个更改和25,000个错误最多最多意味着2.5k个更改,并且发现它大约有250个,我不会感到惊讶。无论哪种方式,很多工作都是可行的,但可行的。
jmoreno,2016年

33
一次更改即可修复所有25000个错误。如果我破坏了一个巨大的继承体系的基类,那么每个派生类都会产生关于无效基类的错误,这些类的每次使用都会产生有关该类不存在的错误,等等。假设它是“ getName”,我添加了一个争论。现在,“ HasAName”实现类中的覆盖无效(错误),并且从它继承的所有内容现在都将产生错误(所有1000个类),以及每次我创建其中任何一个的实例时(每个类24x)平均)。解决方法是……一行。
Yakk

80

用重构分而治之

通常,将您需要进行的更改分解为更小的步骤可能会有所帮助,因为您可以用一种完全不会破坏软件的方式执行大多数较小的步骤。重构工具对此类任务有很大帮助。

划分

首先,确定总结出您想要实现的变更的最小可能变更(就逻辑变更而言,而非就LoC而言)。尤其要尝试隔离纯重构的步骤,并且可以通过工具执行这些步骤

征服

对于像您这样的复杂情况,一次执行一个小的重构然后解决问题可能是有意义的,以便所有连续集成系统都可以验证更改,并且测试团队也可以看一下。这将验证您执行的步骤。

要执行特定的重构,如果您要更改的方法有25,000个调用站点,则绝对需要工具支持。也许搜索替换也可以,但是对于这样的紧急情况,我会感到害怕。

在C#中,例如,它可以使用ReSharper的更改签名的方法。如果更改足够简单(例如添加新参数),则可以指定应该在调用站点使用哪个值,否则将出现编译错误。

然后,您立即处于没有错误的代码基础,并且可以运行所有将通过的单元测试,因为这仅是重构。

一旦方法的签名看起来不错,您就可以替换Resharper作为新引入的参数的参数添加的值。这不再是重构,但是您具有无错误的代码库,并且可以在更改的每一行之后运行测试。

有时那行不通

对于像您这样拥有大量呼叫站点的情况,这是一种非常有用的方法。而且您现在确实拥有可以运行的软件,因此可以进行一些小的重构步骤来稍微更改签名,然后再做一个。

不幸的是,如果签名更改太复杂并且无法分解成较小的更改,它将无法正常工作。但是,这种情况很少见。将问题分解为较小的问题通常表明这可能的。


12
这个。如果可以,请重构(安全),否则需要适当的迁移。
sleske

3
对于喜欢的东西,请使用您的IDE内置的重构工具,而不是仅在本地更改(例如)方法签名,然后“全面解决”由此产生的错误。
KlaymenDK '16

36

与老板明确您的任务,以帮助他理解问题和作为专业软件开发人员的需求。

如果您是团队的一员,请寻找首席开发人员并向他寻求建议。

祝好运。


15
这是我要采取的第一步-“我是大三,我的老板告诉我做某事,现在我收到了非常大的错误消息,但我的老板没有告诉我。” 步骤1:“嘿,老板,我做了这件事,但是现在我收到25,000个错误。这应该发生吗,还是...”
Pimgd

28

不要碰它 不要做任何事情。

而是坐在椅子上大喊“ Heeeeelp !!!!!” 尽可能大声。

好吧,不完全是这样,但请向您的任何高级同事征求意见。如果您有25,000个错误,则不修正错误,而是要修正导致错误的原因。而且一位高级同事应该能够为您提供建议,如何进行老板想要的更改,而不会涉及25,000个错误。有多种方法可以执行此操作,但是什么是好方法取决于您的具体情况。

可能是老板告诉您的高级同事做出相同的更改,然后他们说“不”。因为他们知道会发生什么。这就是为什么给你这份工作。


2
这绝对是要记住的重要事情。如果新员工在很大程度上具有雄心壮志,那么他们可能会勤奋(易变),以完成真正的大任务,最终只能获得5字节的内存优势,或者在以后重新编码和维护时更具逻辑性。
大鸭

@TheGreatDuck:告诉我,您从未见过在任何地方都使用过且在任何地方都出错的接口。我肯定有
约书亚

@Joshua甚至和我刚才说的都不相关。有时,修复一个小错误并非总是最聪明的主意,它意味着重写10,000多行代码。不幸的是,新员工可能很容易被骗,使10位通宵工作的人整夜工作,重写代码以打动老板并完成工作。当然,这是一件很棒的事,但有时足够了。您只需要接受该错误的存在即可。
大鸭

1
@Joshua顺便说一句,我之所以加入这个社区是因为我的热门问题提要中出现了这个问题。我没有像这样的大规模设计经验。我只是同意这个人的观点。
大鸭

22

根深蒂固的API不能简单地更改。如果您确实需要更改它们,请对它们作废并/或记录为已弃用(通过语言允许的任何方式),并记录应该使用哪个API。然后,可以缓慢淘汰旧的API ...可能非常缓慢,具体取决于您用于重构的时间预算。


10

评估

评估是否需要进行此更改,或者是否可以添加新方法并弃用另一个方法。

向前

如果需要更改;那么必须制定迁移计划。

第一步是介绍新方法,并让旧方法按摩其参数,以便可以调用新方法。这可能需要对一些事情进行硬编码。没关系。

这是一个提交点:检查所有测试是否通过,提交,推送。

迁移

繁忙的工作正在将旧方法的所有调用者迁移到新方法。值得庆幸的是,由于货运代理,它可以逐步完成。

所以请继续; 毫不犹豫地使用工具来协助(sed最基本的,还有其他)。

将旧方法标记为已弃用(提示切换到新方法);它会帮助您发现您是否忘记了任何内容,并且如果同事在您进行此操作时向旧方法引入了调用,也会有所帮助。

这是一个提交点(或可能是几个提交点):检查所有测试是否通过,提交,推送。

去掉

经过一段时间(可能只有一天)后,只需删除旧方法即可。


4
sed可能是个坏主意……实际上“理解”语言并且不会进行意想不到的剧烈改变的东西会更好。
wizzwizz4 2016年

1
@ wizzwizz4:不幸的是,我发现很少有工具能够很好地理解 C ++语言。大多数工具似乎都可以重命名,仅此而已。当然,重命名确切的方法调用(而不重命名任何重载或无关的但类似命名的方法调用)已经令人印象深刻,但是对于任何更复杂的操作来说都是不够的。至少,您将需要具备以下能力:(1)随机播放参数,(2)将转换应用于给定参数(.c_str()例如调用)并引入新的参数。sed还可以,编译器随后会发现问题。
Matthieu M.

1
sed(或ed可以是足够的这样的事情-只要你正确地审查DIFF你提交之前。
Toby Speight 2016年

这是html的TCRR,是吗?:)
丹尼尔·斯普林格

8

如果对方法签名的更改仅是名称更改,则简单的解决方案是使用工具来自动对25,000个引用了该方法的类进行更改。

我假设您只是手动编辑了代码,这引起了所有错误。我还假设您熟悉Java(请参阅对OSGi的参考),因此例如在Eclipse(我不知道使用哪个编程环境,但是其他环境具有类似的重构工具)中,则可以使用“重构->重命名”更新对方法的所有引用,这应该不会给您带来任何错误。

如果您要对方法签名进行其他更改,而不仅仅是重命名(更改参数的数量或类型),则可以使用“重构->更改方法签名”。但是,正如其他答案所暗示的那样,您可能必须更加小心。同样,不管更改的类型如何,将所有这些更改提交到繁忙的代码库中仍然是一项艰巨的任务。


2
OP特别指出OSGi是他们先前的工作,因此在这里并没有什么实际意义。
CVn

3
@MichaelKjörling如果OP以前的工作是Java程序员,那么他们也是从事这项工作的Java程序员的机会要多得多。
Rich

@MichaelKjörling我想给出具体的建议,因为OP熟悉Java,所以使用Eclipse进行重构。我想,OP实际上是否在当前项目中使用Java并不那么重要,但我应该澄清一下答案。谢谢。
MikkelRJ 2016年

6

这是我的贡献。

我最近开始了一项新工作,当时我正在处理一个非常大的应用程序(1500万行代码)。

您可能不熟悉该项目及其“功能”。在键入一行代码之前,熟悉该项目很重要。因此,请回滚您的更改并从分析代码开始。(至少受影响的一个)

了解现有解决方案可以使您更好地了解要进入的领域。根据具体情况解决方案及其重要性。

正如@Greg指出的那样,您应该能够测试现有代码以具有有效的参考进行比较(回归测试)。您的解决方案应该能够产生与现有解决方案完全相同的结果。在此阶段,您不必担心结果是否正确。第一个目标是重构,而不是修复错误。如果现有解决方案显示为“ 2 + 2 = 42”,那么您的解决方案也应该如此。如果它没有引发异常,那么您也不应抛出异常。如果它返回空值,那么您的也应该返回空值。等等。否则,您将损害25k行代码。

这是 为了实现追溯兼容性。

为什么?因为现在,这是成功重构的唯一保证。

许多单元测试要么直接引用该接口,要么耦合到引用该接口的基类。

您迫切需要一种确保逆向兼容性的方法,因此这是您的第一个挑战。隔离组件以进行单元测试。

请记住,那25k行代码是假设现有代码可能会产生的结果。如果您不违反合同的这部分内容,那么最终解决方案还只是一半。如果您这样做,那么好吧:愿力量与您同在

设计并实施新的“合同”后,请替换旧的。弃用或取出。

我建议不要将错误放在一旁,因为重构和修复错误是不同的任务。如果您尝试将它们一起推进,则可能两者都失败。您可能认为您发现了错误,但是它们可能是“功能”。因此,让他们一个人呆一分钟。

在我看来,25k行代码足以让我专注于一项任务。

一旦您的第一个任务完成。将这些错误/功能暴露给老板。

最后,正如@Stephen所说:

除了慢慢使事情变得更好以外,您无能为力。创建新类时,请确保已正确测试它们并使用SOLID原理创建它们,以便将来更容易更改它们


5

测试一下。

其他所有人都建议如何重构,因此影响不大。但是有这么多错误,即使您仅用10行代码就可以成功进行重构(您可能也可以),即使您不需要重写它们,也影响了25,000个代码流

因此,接下来要做的就是确保您的回归测试套件通过了飞扬的测试。如果您没有,那就做一个。在您的整体项目中添加全面的回归测试套件听起来很无聊,但这是一种增加对候选发布版本的信心的好方法,并且如果套件自动化良好,则可以更快地发布它们。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.