在主分支上维护数百个定制分支


140

当前,我们在共享存储库中为我们的PHP应用程序提供了一个主分支。我们有500多个客户是我们的软件的订户,其中大多数人针对不同的目的进行了一些自定义,每个客户都在一个单独的分支中。定制可以是不同的文本字段名称,全新功能或模块或数据库中的新表/列。

我们面临的挑战是,当我们维护这数百个定制分支并分发给客户时,我们会不时提供新功能并更新我们的主分支,并且我们希望将主分支更改推送到自定义分支以便更新他们到最新版本。

不幸的是,这通常会导致自定义代码中发生许多冲突,并且我们花费大量时间遍历每个分支来解决所有冲突。这是非常低效的,我们发现解决这些冲突时并不少见的错误。

我正在寻找一种更有效的方法来使我们的客户端发行分支与master分支保持最新,这将减少合并过程中的工作量。


11
抱歉,没有给出“您可以使用X工具”的答案,但是没有答案。
Lightness Races in Orbit

3
或在构建过程中(可能更常见)。只是..不是完全分开的代码库。
Lightness Races in Orbit

15
@FernandoTan-您的明显症状可能是代码,但疾病的根本原因是您的产品碎片,治愈需要来自产品重点/产品功能映射,而不是代码清理-最终会发生。我更在我的答案详细- programmers.stackexchange.com/a/302193/78582
亚历小号

8
这也可能是一个经济问题。您真的从这500个客户中赚钱了吗?如果不是这样,如果客户不支付额外的费用,那么您就不得不考虑一下定价模型并拒绝更改请求。
Christian Strempfer,2015年

13
这使我的心碎了。幸运的是,其他人已经在喊出正确的答案了-我唯一的附加建议是您将其编写并提交给TheDailyWTF。
zxq9 2015年

Answers:


314

您正在完全滥用分支机构!您应该通过应用程序中的灵活性而不是版本控制中的灵活性来进行自定义(如您所发现的那样,版本控制并非旨在/设计用于这种用途)。

例如,使textfield标签来自文本文件,而不是硬编码到应用程序中(这是国际化的工作方式)。如果某些客户具有不同的功能,则使您的应用程序模块化,并通过严格且稳定的API来控制严格的内部边界,以便可以根据需要插入功能。

核心基础架构以及所有共享功能都只需存储,维护和测试一次

您应该从一开始就这样做。如果您已经有五百种产品变体(!),则解决此问题将是一项艰巨的工作……但是,仅此而已的便是持续的维护。


142
为“您应该从头开始执行此操作” +1。这种技术债务水平可能会破坏一家公司。
丹妮丝2015年

31
@Daenyth:坦率地说有500个自定义分支,我很惊讶它还没有。谁让事情变得如此糟糕?哈哈
Lightness Races in Orbit

73
@FernandoTan我是如此,所以,为你感到抱歉...
enderland

20
@FernandoTan:我也是。:(也许您应该在面试中问更多问题?;)明确地说,我的回答中的“您”是组织。这是一个抽象。我不是要怪罪个人。
Lightness Races in Orbit

58
首先获得更多见解:让开发人员在当前版本和自定义分支之间进行区分。所以您至少知道有什么区别。该列表使您可以查看可以最快减少分支的位置。如果有50个具有自定义字段名,则只需关注该字段名,它将为您节省50个分支。然后寻找下一个。您可能还有些不可恢复,但是至少数量会减少,并且当您获得更多客户时,它不会进一步增长。
Luc Franken

93

有500个客户是一个很好的问题,如果您花了一些时间来避免分支机构出现此问题,那么您可能永远无法保持足够长的交易时间来吸引任何客户。

首先,我希望您向客户收取足够的费用,以支付维护其自定义版本的所有费用。我假设客户希望获得新版本,而不必为再次进行自定义付费。我首先要在95%的分支中找到所有相同的文件。这95%是您应用程序的稳定部分。

然后,找到所有在分支之间只有几行不同的文件–尝试引入一个配置系统,以便可以消除这些差异。因此,例如,您拥有一个可以覆盖任何文本标签的配置文件,而不是拥有数百个文本字段标签不同的文件。(这不必一go而就,只需在客户第一次想要更改文本字段标签时对其进行配置即可。)

然后使用“策略”模式,依赖项注入等解决更棘手的问题。

考虑将json存储在数据库中,而不是为客户自己的字段添加列-如果您不需要使用SQL搜索这些字段,这可能对您有用。

每次将文件检入分支时,都必须将其与main进行比较,并证明每个更改(包括空白)的合理性。不需要进行很多更改,可以在签入之前将其删除。这可能取决于一位开发人员在其编辑器中对代码的格式设置有不同的设置。

您的目标是首先从500个具有很多不同文件的分支转到大多数只具有几个不同文件的分支。同时仍然有足够的钱生活。

多年来,您仍然可能拥有500个分支机构,但是如果它们更易于管理,那么您就赢了。


根据br3w5的评论:

  • 您可以参加每个客户不同的课程
  • 创建一个“ xxx_baseclass”,以定义从类外部调用的所有方法
  • 重命名该类,以便xxx称为xxx_clientName(作为xxx_baseclass的子类)
  • 使用依赖项注入,以便为每个客户端使用正确的类版本
  • 现在有了br3w5的明智见解!使用静态代码分析工具查找现在重复的代码,并将其移至基类中,依此类推

只有在掌握了简单的方法之后,才进行上述操作,并首先进行几节课。


28
+1为尝试提供解决实际问题的方法
Ian

35
我真的很担心您对自己的回答表示祝贺,直到我意识到您与编写答案的@Ian不同。
Theron Luhn

2
也许他们应该使用静态代码分析工具来缩小重复代码的哪些部分(确定所有相同的文件之后)
br3w5 2015年

1
还创建版本化的软件包以帮助团队跟踪哪个客户端具有哪个版本的代码
br3w5 2015年

1
这听起来像是一种漫长的说“只重构代码”的方式
Roland Tepp 2015年

40

将来,在面试中询问Joel测试问题。您更有可能不会走进沉船事故。


这是一个啊,我们怎么说呢……确实有一个非常糟糕的问题。该技术债务的“利率”将非常高。它可能无法恢复...

这些自定义更改如何与“核心”集成?您可以将它们设为自己的图书馆并拥有一个“核心”,而每个特定客户都有自己的“附加组件”吗?

还是这些都是非常小的配置?

我认为解决方案是以下各项的组合:

  • 将所有硬编码的更改更改为基于配置的项目。在这种情况下,每个人都有相同的核心应用程序,但是用户(或您)可以根据需要打开/关闭功能,设置命名等。
  • 将“特定于客户的”功能/模块移动到单独的项目中,因此可以拥有一个带有模块的“核心项目”,而不必拥有一个“项目”,因此可以轻松地添加/删除。另外,您也可以使这些配置选项。

就像您最终在这里拥有500多个客户一样,这两者都不是小事,您在这方面可能没有真正的区别。我希望您在分开这方面所做的更改将是非常耗时的任务。

我还怀疑您在轻松地分离和分类所有特定于客户的代码方面会遇到重大问题。

如果您所做的大部分更改都是专门针对措辞上的差异,则建议阅读有关语言本地化的此类问题。无论您是完全使用多种语言还是仅使用部分语言,解决方案都是相同的。特别是PHP和本地化。


1
另外,由于这将是一项艰巨的任务(至少可以说),因此,甚至说服您的管理人员在此问题上投入大量时间和金钱也是一项巨大的挑战。@FernandoTan本网站上可能有问题与解答,可以解决特定问题。
Radu Murzea 2015年

10
乔尔测验的哪个问题会告诉您公司正在滥用分支机构?
SpaceTrucker

2
@SpaceTrucker:好吧,“您是否进行日常构建?” 可能有所帮助。有500个分支机构,他们可能没有,或者可能提到他们只对某些分支机构这样做。
sleske 2015年

17

这是任何VCS都可能遇到的最差的反模式之一。

此处正确的方法是将自定义代码转换为受配置驱动的内容,然后每个客户都可以拥有自己的配置,可以将其硬编码在配置文件中,也可以硬编码在数据库或其他位置。您可以启用或禁用整个功能,自定义响应的外观等等。

这使您可以将一个主分支与您的生产代码一起保留。


3
如果这样做,请帮自己一个忙,并尝试尽可能多地使用策略模式。比起简单地遍历if(getFeature(FEATURE_X).isEnabled())整个代码,这将使维护代码容易得多。
TMN 2015年

13

分支机构的目的是探索一种可能的发展途径,而又不会冒险破坏主分支机构的稳定性。它们最终应在适当的时候合并回去,如果导致死胡同,则应将其丢弃。您所拥有的不是那么多分支,而是同一项目的500个分支,并试图将重要变更集应用于所有分支,这是一个艰巨的任务。

相反,您应该做的是将核心代码保存在其自己的存储库中,并具有必要的入口点,以通过配置修改行为并在反向依赖项允许的情况下注入行为。

然后,您为客户端提供的不同设置可以仅通过某些外部配置的状态(例如数据库)来区分彼此,或者在必要时作为单独的存储库使用,从而将核心添加为子模块。


6
您忘记了维护分支,基本上与您在答案中描述的分支相反。:)
轻轨赛将于

7

所有重要的事情都已经在这里得到了很好的答案。我想添加我的5便士作为工艺建议。

我建议您在长期或中期范围内解决此问题,并采用您的政策以及代码开发方式。尝试成为一个灵活的学习团队。如果有人允许拥有500个回购协议而不是使软件可配置,那么该是时候问问自己到目前为止的工作方式了,您将立即开始工作。

意思是:

  1. 明确变更管理的职责:如果客户需要一些修改,那么谁来出售它们,允许他们,以及决定如何更改代码?如果必须进行某些更改,拧紧的螺丝在哪里?
  2. 明确角色,团队中哪些人可以创建新的回购协议,哪些人不可以。
  3. 尝试确保团队中的每个人都看到有必要使软件具有灵活性的模式。
  4. 澄清您的管理工具:您如何快速了解哪些客户采用了哪些代码。我知道,有些“列表500”听起来很烦人,但是如果您愿意,这里有一些“情感经济”。如果您无法快速告知客户更改,您将感到迷失和困惑,就好像您必须开始列出清单一样。然后,使用该列表按照其他人在此处向您显示的方式对功能进行分组:
    • 按次要/主要变化对客户进行分组
    • 按主题分组
    • 按易于合并和难以合并的更改分组
    • 查找对几个存储库进行相同更改的组(哦,是的,会有一些)。
    • 为了与您的经理/投资人交谈,最重要的可能是:按昂贵的更改和廉价的更改分组。

这绝不意味着给团队带来不良的压力氛围。我宁愿建议您先为自己弄清楚这些要点,然后在任何感觉到支持的地方,与您的团队一起组织。邀请对桌友好的人,以改善您的所有体验。

然后,尝试建立一个长期窗口,在小火上烹饪该东西。建议:尝试每周合并至少两个存储库,并删除至少一个。您可能会经常了解到,随着日常工作和监督的进行,您可以合并两个以上的分支。这样,一年内您可以处理最差(最昂贵?)的分支,而两年内您可以减少此问题以拥有明显更好的软件。但是不要期望更多,因为最终没有人会为此“花时间”,但是由于您是软件架构师,因此您将不再允许这样做。

如果我处于您的位置,这就是我将尝试处理的方式。但是我不知道您的团队将如何接受这些东西,软件如何真正实现这一点,如何为您提供支持以及还需要学习什么。您是软件架构师-努力吧:-)


2
解决技术问题背后隐藏的社会/组织问题的要点。这常常被忽视。
sleske 2015年

5

对比所有反对者,让我们假设真正的业务需求。

(例如,可交付成果是源代码,客户来自同一行业,因此彼此竞争,并且您的业务模型保证将其秘密保密)

此外,假设您的公司拥有维护所有分支机构的工具,即人力(假设100名开发人员致力于合并,假设发布延迟为5天;或者10名开发人员,假设发布延迟50天是可以的),或者如此出色的自动化测试使自动合并在每个分支机构中都经过了核心规范扩展规范的真正测试,因此只有不“干净”合并的更改才需要人工干预。如果您的客户不仅为定制支付费用,还为维护费用支付费用,那么这可能是有效的商业模式。

我(和反对者)的问题是,您是否有专职人员负责交付给每个客户?如果您是一家拥有10,000名员工的公司,则可能是这种情况。

在某些情况下,这可以由插件体系结构来处理,例如,您的核心是主干,插件可以保存在主干或分支中,并且每个客户的配置可以是唯一命名的文件,也可以保存在客户分支中。

插件可以在运行时加载,也可以在编译时内置。

实际上,许多项目都是这样完成的,从根本上讲,仍然存在相同的问题-简单的核心更改很难集成,必须回退冲突更改,或者需要对许多插件进行更改。

在某些情况下,插件还不够好,那就是必须调整内核的太多内部组件,以致插件接口数量变得太大而无法处理。

理想情况下,这将由面向方面的编程处理,其中干线是核心代码,分支是方面(即额外的代码和如何将额外内容连接到核心的说明)

一个简单的示例,您可以指定自定义foo在核心之前或之后运行,或者klass.foo替换它,或者包装它并可以更改输入或输出。

有大量的库,但是合并冲突的问题并没有消失-干净的合并由AOP处理,并且冲突仍然需要人工干预。

最后,此类业务确实必须与分支机构维护有关,即,特定于客户的功能X是否如此普遍,以至于即使不是所有客户都为此付费,将其移至核心系统的成本也较低?


3

您没有通过看症状解决疾病的根本原因。使用“代码管理”方法是有症状的,但不会长期为您解决问题。根本原因是缺乏“管理良好”的产品功能,特性及其扩展和变化。

您的“自定义”代码仅代表产品功能的扩展以及其他方面的数据字段更改。

自定义功能的广泛性,差异性,上下文相似性与否对“清理”产品的代码库起着很大的作用。

在这里,您不仅可以进行编码和版本控制,而且可以在其中发挥产品管理,产品架构和数据架构的作用。说真的

因为,归根结底,代码不过是您为客户提供的业务和产品功能/服务。那就是您的公司得到报酬的。

要更好地解决这一问题,必须从“功能”的角度出发,而不是从代码的角度出发。

您,您的公司和产品不能对所有人都适用。现在您拥有500个客户的可观收入基础,是时候根据您的期望进行生产了。

而且,如果您要提供多种功能,则有条理地将产品功能模块化是很有意义的。

您的产品将发展到什么程度和深度?否则,这将导致“服务质量”问题和“产品稀释和碎片化”。

您将成为CRMERP还是订单处理/调度或Microsoft Excel?

您现有的扩展需要卷起和协调,顺便大型软件主要在拉合并从启动收购的产品。

您将需要具有强大的产品管理和数据体系结构人员以下知识:

  • 主分支,其产品功能和功能库
  • 自定义扩展功能,类型和变体
  • “自定义字段”的意义和变化

..在核心应用程序的广泛上下文中创建所有这些松散产品线程/分支的同化和协调路线图。

PS:与我联系,我认识一个可以帮助您解决此问题的人:)


-5

我可以与此有关。我承担了许多项目。实际上,我们90%的开发工作都在解决这些问题。并非每个人都是完美的,所以我建议您以正确的方式以及在哪里使用版本控制,如果可能的话,可以执行以下操作。

  • 从现在开始,当客户要求更新时,将其移动到新的分支存储库中。
  • 如果要合并它们以掌握,则首先要做的是解决冲突。
  • 然后使用其存储库管理他们的问题和冲刺,并将那些您要在master中启动的master和master保留在master中。这可能会对发布周期造成更大的压力,但是随着时间的流逝,您可以节省时间。
  • 为新客户维护主存储库的主分支,并且主存储库应仅包含您为将来工作而正在使用的那些分支。一旦将旧分支迁移到客户存储库,便可以将其删除。

我已经从GitHub导入了一个存储库,其中有40个分支到Bitbucket,并创建了40个存储库。只花了四个小时。这是WordPress主题变体,因此推和拉很快。

“第一次不正确做”有很多原因,我认为那些很快接受它们并继续“这次正确做”的人总是会成功的。


16
多个存储库如何使维护变得更容易?
Mathletics 2015年

在像我们这样的情况下,客户需要访问每个存储库并在其成为定制解决方案时管理自己的问题,以便他们拥有自己的存储库,这使其更易于管理,并且正如我所说的,这些都是wordpress主题变体,因此效果很好。在许多情况下,它可能不起作用。
Farrukh Subhani 2015年
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.