Git 多个客户需要多次定制时的管理技巧

Git management technique when multiple customers need multiple customization

我正在做一个应用程序项目,将被许多客户使用,他们可能需要单独定制。也就是说,假设我们向 1000 个客户销售我们的应用程序,我们可能需要在单个程序上进行 1000 次自定义(例如处理连接、请求等方面的差异)。我认为我们可以为每个客户分别创建一个 git 分支,但是在一个程序上有 1000 个分支?我认为我们无法妥善处理。那不只是我想要的。我想要 "sustainable" 解决方案。我应该如何管理该源代码? 我知道有一个 'git flow' 东西,但我也不知道它是否适合这种情况。

首先,对于 1k 自定义项,我觉得有些不对劲。对于 1k 个不同版本的代码,您需要多少 开发团队 才能 真正地 及时地 支持它们?

我会首先考虑那些 'customizations':

  • 真的必须要做in-code?
  • 真的必须在同一个仓库中吗?
  • 真的要开发者管理吗?
  • 真的要你管吗?

对其中​​任何一个的 "no" 都会将您的问题改写为更简单的问题。此外,他们经常一起去。例如,对于 "no" 到 "in-code" 似乎定制主要是在配置或数据库中的数据中,这几乎总是意味着对 #3 也不是。

你的目标,作为supporter/mantainer/planner的发展是最后一点。如果自定义可以从代码中扔到配置中,或者在不同的回购中分开 modules/plugins,或者如果它们可以卸载到另一个团队(客户支持团队?)那么你的基本问题就解决了。


现在,考虑到由于某种原因你不能这样做,那么.. 严格 Git-Flow 的突变是你的朋友。特色分店随处可见。大量使用集成和发布分支。真正严格的流程,而不是教程 Git-Flow,而是 'extended' 我想说的..

Git-Flow 其核心主要基于功能分支,但之后是 single-integration、single-release 工作流程。

在进一步阅读之前应该了解 Git-Flow 的工作原理。如果没有,请继续阅读,只有当您觉得自己很了解时才继续阅读。

在'tutorialish'Git-Flow中,每个开发者配置:

  • 目标核心master分支名称:master
  • 目标名称next-release集成分支:开发
  • 功能分支的命名方案:feature/*
  • 发布分支的命名方案:release/*
  • ..

对于“1000 位客户”,此设置存在问题。您可以拥有大量功能分支,用于核心改进、新功能、各种定制等。但是,一旦您 'finish' 它们,它们就会进入 develop 共同开发,你就完蛋了。其他人都会得到它们。 'Finished' 客户 #1 的定制分支最终会通过 develop 回流到 release 并最终流入 master,下个月或下一年所有其他客户也会得到它。你不想要它。

这证明,没有单一的发展,没有单一的大师。

让我们换一种方式。


在 'extended' Git-Flow 中,(Git-Multi-Flow,听起来很吸引人,有人吗?),每个为客户 XYZ 工作的 开发人员配置,示例:

  • 目标核心主分支名称:master-XYZ
  • 目标名称 next-release 集成分支:develop-XYZ
  • 功能分支的命名方案:feature/*
  • 发布分支的命名方案:release/*
  • ..

我计划他们为 master/develop 分支使用不同的名称。这意味着对于每个客户,我都有单独的 next-release 集成分支和不同版本的 current-release 分支。

它有什么作用?任何从事某项功能(无论是补丁、定制等)的开发人员都只使用 git 流程。但是,如果他们在客户 XYZ 的 上下文 中工作,他们将 'finish' 该功能并将集成到专用的 develop-XYZ 分支上。发布后,它将合并到专用的 master-XYZ 分支中。不可能 "leaking" 功能或自定义。

但是,'feature'个分支还是正常的。如果在某个时间点您想要功能 #100(最初用于客户 ABC)和错误修复 #321(最初用于客户 DEF),您仍然可以将它们合并到另一个客户,如 XYZ,前提是它们的相关差异开发-?/大师-?差别不大,但那是另一回事了)

如果听起来不错,那很好,但 不那么好。有了相当数量的客户并为他们单独 devel/master,您很快就会注意到:

  • 如果客户的定制很难,他们会经常develop/master发散,不仅在定制领域,而且在核心代码中也是如此。这将阻碍他们之间未来的功能共享
  • 开发人员 对必须不断更改 git-flow 配置感到脾气暴躁(切换到 master-FOO,切换到 master-BAR,master-XYZ),或者,必须保留几个 repo 副本,配置给不同的客户
  • 有单独的 develop/master 几乎就像有单独的回购协议,但更改通知会向每个人发送垃圾邮件
  • (..)

这是给初学者的。其中一些可以减轻。第二点可以通过注意 git-flow 在 .git/config 文件中设置几行来解决,所以你 不需要 re-config git流动或保留 repo 的多个副本。只需编辑文件即可。或者保留 git-config 的副本并翻转该文件。或者使用一些 $ENV var 来指示当前客户。十分之一的其他解决方法。

接下来是功能。我故意不考虑命名功能分支 feature/XYZ/,而是保持简单 feature/。功能不必绑定到客户。您可以稍后 share/etc 他们。

接下来,发布。我故意没有考虑命名发布分支release/XYZ/。您可能永远不会分享它们。你可以。但是,我想你已经有了一个 更好的 发布分支命名,而不是像 releases/XYZ 那样只在它们前面加上客户名称。您还需要那里的版本号或日期。也许还有一些功能集代号。我不知道。在这里发明一些东西。

接下来,核心掌握和开发。对于客户,开发人员正在努力 develop-XYZ、master-XYZ。但是您仍然可以使用核心 masterdevelop 来改进共享核心代码。那里没有变化。

接下来,我说master-XYZ,develop-XYZ。但这不一定是那样。你知道 feature/...。那么为什么不 masters/XYZ & develops/XYZ 呢?当然可以你甚至可以 XYZ\masterXYZ\developXYZ\feature\ 但是,为什么不像在 GitHub 上那样制作单独的回购协议,在那里你可以 Fork/Merge/PR 一个又一个?

Git-Multi-Flow 是 vanilla Git-Flow 的扩展,使用后一种命名方案(XYZ\master 等),您最终会在一个存储库中得到多个逻辑存储库。有点 multi-tenant Git-Flow..

所以..这是可能的。设置起来并不难。但是仍然有 1000 多个分支 master/develop/etc 集,由同一组团队处理将是一个痛苦。会有错误。开发人员会不时混淆事物,他们只是人。根据我的经验,当 "lots of customizations" 发生时,它几乎只是 URL、端口、密码、图像、样式、文本,有时可能是有限的布局更改。也许在这里或那里增加一个模块。您应该能够在核心代码中处理所有这些,并通过配置将其 on/off/configure 。这样 long-lived 'customization branches'(又名 master-XYZ)的数量将大大减少,代价是为每个客户部署进行更详细的配置。

该配置也应该被跟踪和版本控制(即再次在 Git 中)。我希望你已经这样做了。但它应该位于与代码不同的存储中。这是另一回事。由支持或部署团队管理。提前计划。

为此使用多个存储库。当然,这需要以支持这种方式构建应用程序。因此,使用所有客户通用的东西创建一个核心应用程序。为每个客户创建一个带有定制的特定模块。当然,它们进入单独的存储库。 如果客户请求的功能需要对核心模块进行 customer-specific 更改,请重构您的应用程序,以便可以在核心模块之外完成自定义。如果时间有问题,请使用 client-specific 分支并执行 cherry-picking 或合并。只要是短时间就可以。 我们在我的一位客户身上成功地使用了这种方法,他面临着完全相同的挑战。

简短回答:不要使用 git 存储库来模拟此需求。

相反,不幸的是,我建议使每个客户与另一个客户之间的所有差异都可配置。这样您就可以为每个客户部署相同的版本并且只有一个发布渠道。

我们公司也有类似的情况,这是我日积月累得出的结论。缺点是这些配置可能会变得混乱,使代码变得复杂,并且您的系统不太确定/难以测试。问题是它需要正确完成,关于编码和建模。我认为最重要的是对产品或领域有丰富的经验,这样人们就可以知道应该如何建模哪些设置,或者如果客户想要一些奇怪的东西,甚至可以拒绝。

这是我的可配置性等级列表(从最好到最差):

  1. 使用persistence-layer (DB) 配置建模

    允许用户 self-service 通过 UI/ConfigTools - 但要付出很多努力才能实现

  2. 使用可动态组合的模块/包/服务

    需要复杂的架构和部署 - 通常 very/too 复杂

  3. 使用config-files

    可能会变成 1k 客户的噩梦 - 简单但扩展性不佳(例如,添加新的配置设置时)

  4. 硬编码(例如依赖注入)

    差:每个客户需要 branch/repo,不太可行

从根本上说,每个客户端都有一个共同的核心,每个客户端都有一个增量。问题是你在哪里保存和构建这个三角洲。你如何保存、显示和建模它。这就是为什么 #1 是最好的:配置保存在部署的实例本身中,并随每个版本一起迁移。这是一个纯粹的运行时事物(尽管可以为安全起见进行备份)。 #2 和 #3 在同一个联盟中很不错,两者都不理想,当你有超过 20 个时会变得混乱 settings/differences.