处理重构大文件的最佳方法是什么?


41

我目前正在做一个更大的项目,不幸的是其中有些文件的软件质量准则并不总是遵循的。这包括大文件(读取2000-4000行),这些文件显然包含多个不同的功能。

现在,我想将这些大文件重构为多个小文件。问题是,由于它们很大,因此不同分支上的多个人(包括我)正在处理这些文件。因此,我无法真正从开发和重构中分支出来,因为将这些重构与其他人的更改合并将变得很困难。

当然,我们可以要求每个人重新合并以开发,“冻结”文件(即,不再允许任何人对其进行编辑),重构然后“取消冻结”。但这也不是一件好事,因为这将要求每个人在重构完成之前基本上停止对这些文件的工作。

因此,有没有一种方法可以重构,不要求其他人停止工作(很长时间)或合并其功能分支以进行开发?


6
我认为这也取决于所使用的编程语言。
罗伯特·安德泽胡克

8
我喜欢“小增量”签到。除非有人不让其回购副本保持最新状态,否则这种做法将使每个人的合并冲突降至最低。
Matt Raffel

5
您的测试是什么样的?如果您要重构一大段(也许很重要!)代码,请在重构之前确保测试套件处于良好状态。这将使确保在较小的文件中正确显示变得容易得多。
corsiKa

1
您可以采用多种方法,最好的方法取决于您的情况。
史蒂芬

3
我加入了这个项目,其中最大的文件是10k行,其中包含一个类,该类本身是6k行,每个人都不敢碰它。我的意思是你的问题很好。我们甚至开了个玩笑,说这门课是解锁鼠标滚轮的一个很好的理由。
ElmoVanKielmo

Answers:


41

您已经正确地理解,这不仅仅是技术问题,而是社会问题:如果要避免过多的合并冲突,团队需要以避免这些冲突的方式进行协作。

这是Git更大问题的一部分,因为分支非常容易,但是合并仍然需要很多努力。开发团队倾向于创建很多分支,然后感到惊讶的是,合并它们很困难,可能是因为他们在不了解其上下文的情况下试图模仿Git Flow。

快速轻松合并的一般规则是防止累积大差异,尤其是要素分支的生存期应非常短(数小时或数天,而不是数月)。能够快速集成其更改的开发团队将看到较少的合并冲突。如果某些代码尚未投入生产,则可以将其集成,但可以通过功能标记将其停用。一旦将代码集成到您的master分支中,您便可以尝试进行重构。

对于您眼前的问题,这可能太多了。但是可能可行的是,要求同事合并影响该文件的更改,直到一周结束,以便您可以执行重构。如果他们等待更长的时间,他们将不得不自己处理合并冲突。这不是不可能的,只是可以避免的工作。

您可能还想防止破坏大量的相关代码,而仅进行与API兼容的更改。例如,如果要将某些功能提取到单独的模块中:

  1. 将功能提取到单独的模块中。
  2. 更改旧函数以将其调用转发到新API。
  3. 随着时间的流逝,将依赖于代码的代码移植到新的API。
  4. 最后,您可以删除旧功能。
  5. (重复下一组功能)

此多步骤过程可以避免许多合并冲突。特别是,只有当其他人也在更改您提取的功能时,才会出现冲突。这种方法的成本是,它比一次更改所有内容要慢得多,并且您暂时拥有两个重复的API。直到紧急情况中断此重构,重复被遗忘或取消优先级,并且最终导致一大笔技术债务之后,情况才算好。

但是最后,任何解决方案都需要您与团队协调。


1
@Laiv不幸的是,所有这些都是非常笼统的建议,但是敏捷开发之外的一些想法(例如,持续集成)显然有其优点。与仅相互配合的团队相比,一起工作(并经常整合工作)的团队将更容易进行大型跨领域更改。这不一定与整个SDLC有关,而与团队内部的协作有关。有些方法使协同工作更加可行(请考虑开放式/封闭式原理,微服务),但是OP的团队还不存在。
阿蒙

22
我不会说一个功能分支的寿命很短-只是它不应长时间偏离其父分支。在功能分支需要停留更长的时间的情况下,定期将父分支中的更改合并到功能分支中是可行的。不过,保持要素分支的保留时间不要过长是一个好主意。
丹·里昂

1
@Laiv以我的经验,事先与团队讨论后重构设计是有意义的,但是通常一个人对代码进行更改最容易。否则,您会回到必须合并内容的问题。4k行听起来很多,但实际上并不是针对目标重构(例如extract-class)的。(我这么辛苦这里抬价Martin Fowler的重构书,如果我读它。)但是4K线很多只为不相关的重构像“让我们来看看如何提高这个”。
阿蒙

1
@DanLyons原则上您是对的:这可以分散一些合并的精力。实际上,Git的合并在很大程度上取决于合并分支的最新共同祖先提交。合并master→features并不能使我们成为master的新共同祖先,但可以合并feature→master。使用重复的master→feature合并,可能会发生,我们不得不一次又一次地解决相同的冲突(但请参见git rerere使其自动化)。在这里,重新定基严格是优越的,因为掌握者的尖端成为了新的共同祖先,但是历史重写还有其他问题。
阿蒙

1
对我来说,答案是可以的,除了关于git的咆哮使得分支太容易了,因此devs分支太频繁了。我很好地记得SVN甚至CVS的时代,那时分支很难(或者至少很麻烦),人们通常在可能的情况下避免分支以及所有相关问题。在git中,作为一个分布式系统,拥有许多分支实际上与完全拥有许多分离的存储库(即,在每个dev上)没有什么不同。解决方案在别处,易于分支不是问题。(是的,我确实看到这只是一个问题……但仍然如此)。
11:33

30

以较小的步骤进行重构。假设您的大文件名为Foo

  1. 添加一个新的空文件,Bar并将其提交到“ trunk”。

  2. 找到一小部分Foo可以移至的代码Bar。应用移动,从主干更新,构建和测试代码,然后提交到“ trunk”。

  3. 重复第2步,直到FooBar具有相等的大小(或您喜欢的任何大小)

这样,下次您的队友从主干更新分支时,他们会在“小部分”中获得更改,并且可以将它们逐一合并,这比必须一步合并一个完整的分支要容易得多。当在第2步中发生合并冲突时,情况也是如此,因为其他人在两者之间更新了中继。

这不会消除合并冲突或需要手动解决它们,但是会将每个冲突限制在较小的代码区域中,这更易于管理。

当然-在团队中沟通重构。通知您的伴侣您在做什么,这样他们就知道为什么他们必须期望特定文件的合并冲突。


2
这在rerere启用gits 选项时特别有用
D. Ben Knoble

@ D.BenKnoble:感谢您的补充。我必须承认,我不是git专家(但是所描述的问题不是专门针对git的,它适用于任何允许分支的VCS,我的答案应该适合大多数这些系统)。
布朗

我根据术语来计算;实际上,使用git时,这种合并仍然仅执行一次(如果只是拉并合并)。但是,根据开发人员的喜好,您总是可以拉动和选择,或者合并单个提交,或者重新设置基础。这会花费更多时间,但是如果自动合并可能会失败,那肯定是可行的。
D. Ben Knoble

18

您正在考虑将文件拆分为原子操作,但是可以进行一些中间更改。文件随着时间逐渐变大,可能随着时间而逐渐变小。

选择一个不需要很长时间进行更改的零件(git blame可以帮助您完成此工作),然后首先将其拆开。将更改合并到每个人的分支中,然后选择下一个最容易拆分的部分。甚至分割一个部分都太大了,您应该首先在大文件中进行一些重新排列。

如果人们不经常合并以进行开发,则应鼓励他们,然后在合并后利用这个机会将刚更改的部分分开。或要求他们进行拆分,作为拉取请求审核的一部分。

这个想法是要慢慢地朝自己的目标迈进。感觉进度很慢,但是突然之间,您就会意识到自己的代码要好得多。转动远洋客轮需要很长时间。


该文件可能已开始很大。这样大小的文件可以快速创建。我知道人们每天或每周可以写1000份LoC。OP没有提到自动测试,这向我表明它们缺乏。
ChuckCottrill

9

我将针对此问题提出一种不同于通常的解决方案。

将此用作团队代码事件。让每个人都可以签入自己的代码,然后帮助仍在使用该文件的其他人。一旦每个相关人员都将其代码签入,请找到一台配有投影仪的会议室,并一起工作以开始将内容四处移动并移动到新文件中。

您可能需要为此设置一个特定的时间,这样就不会浪费一个星期的时间而没有结束的争论。取而代之的是,这甚至可能是每周一次的1-2小时活动,直到大家都了解它的状态。也许您只需要1-2个小时即可重构文件。您可能直到尝试才知道。

这样做的好处是每个人都可以在同一页面上进行重构,而无需双关语,但是它也可以帮助您避免错误,并在必要时从其他人​​那里获取有关可能的方法分组的输入。

如果您这样做的话,可以认为这样做具有内置的代码审查功能。这样一来,有相应数量的开发人员就可以在签入代码后立即签收代码,并准备对其进行审核。您可能仍然希望他们检查代码中是否缺少任何内容,但是要确保缩短审核过程有很长的路要走。

这可能不适用于所有情况,团队或公司,因为工作分配的方式并不容易做到。也可以(错误地)将其解释为滥用开发时间。该组代码需要管理者的支持以及重构本身。

为了帮助您将此想法卖给您的经理,请提及代码审查位,以及每个人从一开始就知道事情在哪里。避免开发人员浪费时间搜索大量新文件是值得避免的。同样,防止开发人员获得关于事情在哪里结束或“完全丢失”的信息通常是一件好事。(IMO崩溃越少越好。)

一旦以这种方式重构了一个文件,如果成功且有用的话,您便可以更轻松地获得批准以进行更多重构。

但是,您决定进行重构,祝您好运!


这是一个很棒的建议,它捕获了一种实现团队协作的非常好的方法,这对于使其协同工作至关重要。此外,如果某些分支不能合并回master第一层,那么至少您需要每个人在会议室中帮助您处理合并到这些分支中的事务。
Colin Young

为建议代码暴民而+1
乔恩·雷诺

1
恰好解决了问题的社会方面。
ChuckCottrill

4

要解决此问题,需要其他团队的支持,因为您正在尝试更改共享资源(代码本身)。话虽如此,我认为有一种方法可以“迁移”拥有庞大的整体文件而不会打扰人们。

我还建议不要一次将所有大文件作为目标,除非除了单个文件的大小之外,大文件的数量不受控制地增长。

像这样重构大文件经常会导致意外问题。第一步是阻止大文件积累除master或development分支中当前可用的功能以外的其他功能。

我认为最好的方法是使用提交钩子,这些钩子默认情况下会阻止对大文件的某些添加,但是可以用提交消息中的神奇注释(如之类的@bigfileok东西)来否决。能够以无痛但可跟踪的方式推翻该政策非常重要。理想情况下,您应该能够在本地运行提交挂钩,并且应该告诉您如何在错误消息本身中覆盖此特定错误。另外,这只是我的偏爱,但是无法识别的魔术注释或魔术注释抑制了在提交消息中并未真正触发的错误,这应该是一个提交时间警告或错误,因此您不会无意中培训人们抑制钩子,无论他们是否需要。

提交钩子可以检查新类或进行其他静态分析(临时或不临时)。您还可以选择比当前文件大10%的行或字符数,并说大文件不能超过新的限制。您也可以拒绝使大文件增加太多行或太多字符或w / e的单个提交。

大文件停止累积新功能后,您可以一次将其重构(并同时减少提交钩子强制执行的限制,以防止其再次增长)。

最终,大文件将足够小,可以完全删除提交挂钩。


-3

等到回家。拆分文件,提交并合并到母版。

像其他任何更改一样,其他人必须在早上将更改拉入其功能分支。


3
尽管如此,这仍然意味着他们将不得不将我的重构与他们的更改合并...
霍夫


1
好吧,如果他们都在更改这些文件,则实际上他们无论如何都要处理合并。
莱夫

9
这有一个问题,“我吃惊了,我弄坏了所有东西”。OP必须先获得批准并获得批准,并且要在预定的时间(没有其他人正在“处理”文件的情况下这样做)有所帮助。
computercarguy

6
为了爱克苏鲁,不要这样做。这是团队中最糟糕的工作方式。
Monica
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.