我们面临的挑战是,当我们维护这数百个定制分支并分发给客户时,我们会不时提供新功能并更新主分支,我们希望推动主分支的变更到自定义分支以将其更新到最新版本。
不幸的是,这通常会导致自定义代码中出现许多冲突,并且我们花费大量时间遍历每个分支来解决所有冲突。这是非常低效的,我们发现解决这些冲突时并不少见出错。将减少合并过程中的工作量。
#1 楼
您正在完全滥用分支!您应该通过应用程序中的灵活性而不是版本控制中的灵活性来进行自定义(如您所发现的那样,版本控制不旨在/设计用于这种用途)。例如,make文本字段标签来自文本文件,不会硬编码到您的应用程序中(这是国际化的工作方式)。如果某些客户具有不同的功能,则使您的应用程序模块化,并通过严格且稳定的API来控制严格的内部界限,以便可以根据需要插入功能。核心基础结构以及任何共享功能,那么只需要存储,维护和测试一次。
您应该从一开始就这样做。如果您已经有五百种产品变体(!),则解决此问题将是一项艰巨的工作…但仅能进行持续的维护。
评论
为“您应该从头开始执行此操作” +1。这种技术债务水平可能会破坏一家公司。
–丹妮丝
2015年11月9日在16:05
@Daenyth:坦率地说有500个自定义分支,我很惊讶它还没有。谁让事情变得如此糟糕?大声笑
–轨道轻度竞赛
2015年11月9日在16:06
@FernandoTan我是如此,所以,为你感到抱歉...
– enderland
2015年11月9日在16:36
@FernandoTan:我也是。 :(也许您应该在面试中问更多问题?;)要明确一点,我的回答中的“您”是组织。这是一个抽象。我不是要怪罪个人。
–轨道轻度竞赛
2015年11月9日在16:42
首先获得更多见解:让开发人员在当前版本和自定义分支之间进行区分。所以您至少知道有什么区别。该列表使您可以查看可以最快减少分支的位置。如果有50个具有自定义字段名,则只需关注该字段名,它将为您节省50个分支。然后寻找下一个。您可能还有些不可恢复,但是至少数量会减少,并且当您获得更多客户时,它不会进一步增长。
–吕克·弗兰肯(Luc Franken)
2015年11月9日在17:44
#2 楼
拥有500个客户是一个很好的问题,如果您花了一些时间来避免分支机构出现此问题,那么您可能永远无法保持足够长的交易时间来吸引任何客户。首先,我希望您向客户收取足够的费用,以支付维护其自定义版本的所有费用。我假设客户希望获得新版本,而不必为再次进行自定义付费。那95%是应用程序的稳定部分。因此,例如,不是拥有100个带有不同文本字段标签的文件,而是拥有1个可以覆盖任何文本标签的配置文件。 (这不必一口气完成,只需在客户第一次想要更改文本字段标签时就对其进行配置即可。)
然后使用策略模式(依赖)来处理更棘手的问题注入等。
考虑将json存储在数据库中,而不是为客户自己的字段添加列-如果您不需要使用SQL搜索这些字段,这可能对您有用。
每次您将文件检入一个分支时,都必须将其与main进行比较,并证明每个更改(包括空白)的合理性。不需要进行很多更改,可以在签入之前将其删除。这可能取决于一位开发人员在其编辑器中对代码的格式设置有不同的设置。
您的目标是首先从500个分支中包含许多不同的文件,到大多数分支中仅包含几个不同的文件。在仍然可以赚钱维持生计的同时。
您可能会在很多年后仍然拥有500家分支机构,但是如果它们更容易管理,那么您就赢了。
基于br3w5的评论:
您可以采用客户端之间不同的每个类
创建一个“ xxx_baseclass”,该类定义从类外部调用类中的所有方法
重命名该类xxx被称为xxx_clientName(作为xxx_baseclass的子类)
使用依赖注入,以便为每个客户端使用正确的类版本。
现在有了br3w5的精辟见解!使用静态代码分析工具查找现在已重复的代码,并将其移至基类等中。
仅在获得简单粒度后执行上述操作,然后首先使用几个类进行跟踪。
评论
+1,用于尝试为实际问题提供解决方案
–伊恩
2015年11月10日,11:38
我真的很担心您对自己的回答表示祝贺,直到我意识到您与编写答案的@Ian不同。
– Theron Luhn
15年11月11日在17:48
也许他们应该使用静态代码分析工具来缩小重复代码的哪些部分(在确定所有相同文件之后)
–br3w5
2015年11月11日在19:07
还创建版本化的软件包,以帮助团队跟踪哪个客户端具有哪个版本的代码
–br3w5
15年11月11日在19:08
听起来很费劲地说“只重构代码”
–罗兰·特普(Roland Tepp)
15年11月12日在21:37
#3 楼
将来,在面试中询问Joel测试问题。您更有可能不会走进火车残骸中。该技术债务的“利率”将非常高。它可能无法恢复...这些自定义更改如何与“核心”集成?您可以让他们成为自己的图书馆并拥有一个“核心”,而每个特定客户都有自己的“附加组件”吗?
还是这些都是非常小的配置?
我认为该解决方案是以下各项的组合:
将所有硬编码的更改更改为基于配置的项目。在这种情况下,每个人都具有相同的核心应用程序,但是用户(或您)可以根据需要打开/关闭功能,设置命名等。
将“特定于客户端的”功能/模块移动到单独的项目中,而不是拥有一个“项目”有一个“核心项目”,其中的模块可以轻松添加/删除。另外,您也可以进行这些配置。我希望您所做的更改来分离此更改将是一项非常耗时的任务。
我还怀疑您在轻松地将所有特定于客户的代码进行分类和分类时将遇到重大问题。
如果您所做的大部分更改都是专门针对措辞上的差异,我建议您阅读有关语言本地化的此类问题。无论您是完全使用多种语言还是仅使用部分语言,解决方案都是相同的。这是专门针对PHP和本地化的内容。
评论
此外,由于这将是一项艰巨的任务(至少可以说),因此,甚至说服您的管理人员在此问题上投入大量时间和金钱也是一项重大挑战。 @FernandoTan本网站上可能有疑问和解答,可以帮助解决此特定问题。
–拉杜(Radu Murzea)
15年11月11日在21:17
乔尔测验的哪个问题会告诉您公司正在滥用分支机构?
–SpaceTrucker
15年11月12日在8:01
@SpaceTrucker:好吧,“你每天制作吗?”可能有所帮助。有500个分支机构,他们可能没有,或者可能提到他们仅对某些分支机构这样做。
–sleske
15年11月13日在12:23
#4 楼
这是任何VCS都可能遇到的最糟糕的反模式之一。硬编码在配置文件,数据库或其他位置。您可以启用或禁用整个功能,自定义响应的外观等等。这使您可以将一个主分支与您的生产代码保持在一起。
评论
如果这样做,请帮自己一个忙,并尝试尽可能多地使用策略模式。与仅在整个过程中简单使用if(getFeature(FEATURE_X).isEnabled())相比,这将使维护代码更加容易。
– TMN
2015年11月9日在17:17
#5 楼
分支机构的目的是探索一种可能的发展途径,而又不会冒险破坏主分支机构的稳定性。它们最终应在适当的时候合并回去,如果导致死胡同,则应将其丢弃。您所拥有的不是那么多分支,而是同一项目中的500个fork并试图将重要变更集应用于所有分支,这是一个sisyphean任务。相反,您应该做的是核心代码位于其自己的存储库中,具有必要的入口点,可以通过配置来修改行为并按反向依赖关系允许的行为进行注入。通过某些外部配置的状态(例如数据库)或在必要时作为单独的存储库存在,这些存储库将核心添加为子模块。
评论
您忘记了维护分支,这些分支基本上与您在答案中描述的分支相反。 :)
–轨道轻度竞赛
2015年11月9日在18:38
#6 楼
所有重要的事情都已经在这里得到了很好的答案。我想补充我的5便士作为工艺建议。我建议您在长期或中期范围内解决此问题,并采纳您的政策以及如何开发代码。尝试成为一个灵活的学习团队。如果有人允许拥有500个回购协议而不是使软件可配置,那么是时候问问自己到目前为止的工作方式了,从现在开始您将继续这样做。
意思是:
明确变更管理职责:如果客户需要一些适应,谁在出售,谁允许他们,以及谁决定如何代码会改变吗?如果必须进行某些更改,拧紧的螺丝在哪里?
弄清角色,允许团队中的谁创建新的仓库,而不允许谁。 />澄清您的管理工具:如何快速了解哪些客户采用了哪些代码。我知道,有些“ 500强名单”听起来很烦人,但是如果您愿意,这里有一些“情感经济”。如果您无法快速告知客户更改,您将感到迷失和困惑,就好像您必须开始列出清单一样。然后,使用该列表按照其他人在这里显示的答案的方式对功能进行分组: >按变更分组容易合并而合并变更却很难合并
找到对几个存储库进行相同更改的分组(哦,是的,会有一些)。 /投资者:按昂贵的更改和廉价的更改分组。
这绝不意味着给团队造成不良的压力氛围。我宁愿建议您先为自己弄清楚这些要点,然后在任何感觉到支持的地方,与您的团队一起组织。邀请对桌友好的人,以改善您的所有体验。
然后,尝试建立长期窗口,在此窗口上用小火烹饪。建议:尝试每周合并至少两个存储库,因此删除至少一个。您可能会经常了解到,随着日常工作和监督的进行,您可以合并两个以上的分支。这样,一年内您可以处理最差(最昂贵的)分支,而两年内您可以减少此问题,以拥有明显更好的软件。但是不要期望更多,因为最终没有人会为此“花时间”,但是由于您是软件架构师,您将不再允许这样做。
这是如果我处于你的位置,我将如何处理它。但是我不知道您的团队将如何接受这些东西,软件如何真正实现这一点,如何为您提供支持以及还需要学习什么。您是软件架构师-继续努力吧:-)
评论
解决技术问题背后隐藏的社会/组织问题的要点。这常常被忽视。
–sleske
15年11月13日在12:24
#7 楼
与所有反对者相反,让我们假设真正的业务需求。另外,让我们假设您的公司拥有维护所有分支机构的工具,即人力(假设100名开发人员致力于合并,假设发布延迟5天;或者10个开发人员假定50天的发布延迟是可以的),或如此出色的自动化测试,即对每个分支的核心规格和扩展规格都进行了真正的自动合并测试,因此只有不“干净”合并的更改才需要人工干预。如果您的客户不仅为定制支付费用,还为维护费用支付费用,那么这可能是一种有效的商业模式。每个客户?例如,如果您是一家拥有10,000人的公司,那么情况可能就是这样。或分支,每个客户的配置都是唯一命名的文件或保存在客户分支中。插件可以在运行时加载,也可以在编译时内置。
实际上确实完成了许多项目,基本上仍然存在相同的问题-简单的核心更改很难集成,必须回退冲突更改,或者需要对许多插件进行更改。
有在某些情况下,插件不够好,那就是必须调整内核的太多内部组件,以致插件接口数量变得太大而无法处理。主干是核心代码,分支是各个方面(即如何将额外内容连接到核心的额外代码和说明)
一个简单的示例,您可以指定在核心
foo
之前或之后运行自定义klass.foo
,或者将其替换,或者将其包装,并可以更改输入或输出。但是合并冲突的问题不会消失-干净的合并是由AOP处理的,并且冲突仍然需要人工干预。特定功能X如此普遍,以至于即使不是所有客户都为它付出代价,将其移至核心的成本也更低?#8 楼
您没有通过看症状解决疾病的根本原因。使用“代码管理”方法是有症状的,但不能长期为您解决问题。根本原因是缺乏“管理良好”的产品功能,特性及其扩展和变化。您的“自定义”代码仅代表产品功能和功能的扩展,以及其他地方数据字段的变化。
除了您的编码和版本方式之外,这是产品管理,产品架构和数据架构发挥作用的地方。认真。
因为,归根结底,代码不过是您为客户提供的业务和产品功能/服务。这就是您的公司所要获得的报酬。产品不能成为所有人的一切。既然您已有500个客户的可观收入基础,那么现在就可以按自己的意愿进行产品生产了。井井有条的时尚。
您的产品将发展到什么程度和深度?否则,当您下线时,这将导致“服务质量”问题和“产品稀释和碎片化”。
您现有的扩展需要汇总和协调,这是大型软件专业公司引入并合并从初创公司获得的产品的方式。管理和数据体系结构人员映射以下内容:
主分支,其产品功能和功能基础
自定义扩展功能,类型和变体
'自定义字段'的意义和变体
..在宏大的环境中创建所有这些松散产品线程/分支的同化和统一路线图您的核心应用程序。
PS:与我联系,我认识一个人可以帮助您解决这个问题:)
#9 楼
我可以与此有关。我承担了许多项目。实际上,我们90%的开发工作都在解决这些问题。并不是每个人都是完美的,所以我建议您以正确的方式以及在哪里使用版本控制,如果可能的话,可以执行以下操作。
从现在开始,当客户要求更新时,请将其移至新的分叉存储库中。
如果要合并它们以进行掌握,首先要做的是解决冲突。
然后使用其存储库管理他们的问题和冲刺,并将那些要在master中启动的master和master保留在master中。这可能会给发布周期带来更大的压力,但这会节省您的时间。旧版分支一旦迁移到客户存储库后便可以删除。
我个人已从GitHub导入了一个带有40个分支的存储库到Bitbucket,并创建了40个存储库。只花了四个小时。这是WordPress主题的变体,因此推入和拉出很快。这次”将永远是成功的。
评论
多个存储库如何使维护变得更容易?
–数学
2015年11月10日,下午3:21
在像我们这样的情况下,客户需要访问每个存储库并在其成为定制解决方案时管理他们自己的问题,以便他们拥有自己的存储库,这使得管理起来更加容易,并且正如我所说的,这些都是wordpress主题变体,它运作良好。在许多情况下,它可能不起作用。
– Farrukh Subhani
2015年11月14日在16:27
评论
抱歉,没有给出“您可以使用X工具”的答案,但没有答案。或在构建过程中(可能更常见)。只是..并非完全独立的代码库。
@FernandoTan-您的明显症状可能是代码,但疾病的根本原因是您的产品碎片,治愈需要来自产品重点/产品功能映射,而不是代码清理-最终将发生。我已经在答案中详细介绍了-developers.stackexchange.com/a/302193/78582
这也可能是一个经济问题。您真的从这500个客户中赚钱了吗?如果不是这样,如果客户不支付额外的费用,则您不得不考虑一下定价模型并拒绝更改请求。
这使我的心碎了。幸运的是,其他人已经在喊出正确的答案了-我唯一的附加建议是您将其撰写并提交给TheDailyWTF。