何时将一个项目分成多个子项目


30

我想知道将我正在处理的项目分成两个存储库而不是一个存储库是否有意义。

从我可以说:

  • 前端将用html + js编写
  • .net后端
  • 后端不依赖于前端,前端不依赖于后端
  • 前端将使用后端实现的一个宁静的api。
  • 前端可以托管在任何静态http服务器上。

到目前为止,存储库具有以下结构:

根:

  • 前端/*
  • 后端/ *

我认为将两个项目都放在同一个存储库中是一个错误。由于两个项目之间没有相互依赖关系,因此它们应属于单独的存储库,并且如果需要,则应具有包含子模块的父存储库。

有人告诉我这是没有意义的,这样做不会给我们带来任何好处。

这是我的一些论点:

  • 我们有两个互不依赖的模块。
  • 长期拥有两个项目的源历史记录可能会使事情复杂化(尝试在历史记录中搜索前端中的某些内容,而您有一半的提交与所寻找的bug完全无关)
  • 冲突和合并(这不应该发生,但是让某人推送到后端将迫使其他开发人员提取后端更改以推送前端更改。)
  • 一个开发人员可能只在后端工作,但总是不得不拉扯前端或反过来。
  • 从长远来看,将是时候进行部署。以某种方式,前端可以在拥有一个后端服务器的同时部署到多个静态服务器。在每种情况下,人们都将不得不要么克隆整个后端,要么创建自定义脚本以仅将所有前端推送到所有服务器或删除后端。如果只需要一个,则仅推/拉前端或后端比两个都容易。
  • 反驳论点(一个人可能同时在两个项目上工作),使用子模块创建第三个存储库并进行开发。历史记录保存在各个模块中,您始终可以创建标签,在这些标签中,后端/前端的版本确实可以同步工作。将两个前端/后端放在一个仓库中并不意味着它们可以一起工作。它只是将两个历史合并到一个大型仓库中。
  • 如果要将自由职业者添加到项目中,将前端/后端作为子模块将使事情变得更容易。在某些情况下,您实际上并不想授予对代码库的完全访问权限。如果您想限制“局外人”可以看到/编辑的内容,拥有一个大模块将使工作变得更加困难。
  • 错误介绍和修复错误,我在前端插入了一个新错误。然后有人修复了后端的错误。对于一个存储库,在新错误之前回滚也将回滚后端,这可能使其难以修复。我必须将后端克隆到另一个文件夹中,以使后端工作,同时修复前端中的错误……然后尝试重新合并东西……拥有两个存储库将很轻松,因为移动了一个回购的HEAD不会改变另一个。并且针对不同版本的后端进行测试将是轻而易举的。

有人可以给我更多的论据来说服他们,或者至少告诉我为什么将项目分为两个子模块是没有意义的(更复杂的)。该项目是新项目,代码库已有两天的历史,因此修复还为时不晚。

Answers:


23

在我公司,我们为系统的每个组件使用单独的SVN存储库。我可以告诉你,它变得非常令人沮丧。我们的构建过程具有如此多的抽象层。

我们使用Java进行此操作,因此我们需要进行大量的Javac编译,JibX绑定编译,XML验证等构建过程。

对于您的网站,如果您不是真正地“构建”它(例如香草PHP),那可能不是什么大问题。

将产品拆分为多个存储库的缺点

  1. 生成管理-我不能只签出代码,运行自包含的生成脚本并拥有可运行/可安装/可部署的产品。我需要一个外部构建系统,该系统可以使用多个存储库,运行多个内部构建脚本,然后组装工件。
  2. 更改跟踪-查看谁更改了内容,时间和原因。如果前端的错误修复需要后端更改,那么现在有2条不同的路径供我参考。
  3. 管理-您是否真的想将需要管理的用户帐户,密码策略等数量加倍?
  4. 合并-新功能可能会更改许多代码。通过将项目拆分为多个存储库,您将需要的合并数量成倍增加。
  5. 分支创建-与分支处理相同,要创建分支,现在必须在每个存储库中创建一个分支。
  6. 标记-成功测试代码后,您想要标记要发布的版本。现在您可以创建多个标签,每个存储库中都有一个。
  7. 很难找到东西-也许前端/后端很简单,但它会变得很滑。如果分成足够的模块,则开发人员可能必须调查源代码控制中某些代码的位置。

我的情况有点极端,因为我们的产品分为14个不同的存储库,然后每个存储库分为4-8个模块。如果我还记得的话,我们大约有80个左右的地方或一些“包装”,都需要单独检查然后组装。

仅后端/前端的情况可能不太复杂,但我还是建议不要这样做。

极端的例子可能是支持或反对几乎任何东西的有说服力的论点:)

我用来决定的标准

考虑以下因素后,我将考虑将一个产品分成多个源代码存储库:

  1. 构建-构建每个组件的结果是否合并在一起形成一个产品?就像将一堆组件中的.class文件组合成一系列.jar或.war文件一样。
  2. 部署-您最终获得的组件是作为一个单元一起部署还是将不同的单元一起部署到不同的服务器?例如,数据库脚本转到您的数据库服务器,而JavaScript转到您的Web服务器。
  3. 共同变更-他们倾向于经常变更还是一起变更?就您而言,它们可能会单独更改,但仍会经常更改。
  4. 分支/合并的频率-如果每个人都很少进入主干和分支,那么您也许可以摆脱它。如果您经常分支并合并,这可能会成为一场噩梦。
  5. 敏捷性-如果您需要立即开发,测试,发布和部署更改(可能使用SaaS),您是否可以这样做而无需花费宝贵的时间处理分支机构和存储库?

你的论点

我也不同意您的大多数观点。我不会对此全部提出异议,因为这个冗长的答案将会更长,但是其中一些引人注目:

我们有两个互不依赖的模块。

废话。如果您不使用后端,前端会工作吗?我也那么认为。

长期拥有两个项目的源历史记录可能会使事情复杂化(尝试在历史记录中搜索前端中的某些内容,而您有一半的提交与所寻找的bug完全无关)

如果您的项目根目录分为frontend /和backend /,那么您可以独立查看这些层次结构的历史记录。

冲突和合并(这不应该发生,但是有人推向后端将迫使其他开发人员拉动后端更改以推动前端更改。)一个开发人员可能只在后端工作,但始终必须拉回后端或以其他方式进行操作周围。

将您的项目分成不同的存储库并不能解决这个问题。前端冲突和后端冲突仍然使您面临2个冲突,无论是1个存储库乘以2个冲突,还是2个存储库乘以1个冲突。仍然有人需要解决它们。

如果担心2个回购协议意味着前端开发人员可以合并前端代码,而后端开发人员可以合并后端代码,您仍然可以使用SVN在单个存储库中进行操作。SVN可以在任何级别合并。也许这是一个git或mercurial限制(您同时标记了两者,所以不确定使用的是什么SCM)?

另一方面

综上所述,我已经看到了将项目拆分为多个模块或存储库的情况。我什至曾为我们将Solr集成到我们产品中的特定项目提倡这样做。当然,Solr在单独的服务器上运行,仅在变更集与搜索相关时(我们的产品比搜索更多),具有单独的构建过程,并且没有共享的代码构件或构建构件,才进行更改。


我母亲曾经说过的所有事情都要节制……
William Payne

在撰写本文时,我正在编写没有后端的前端。我用json文件模拟了后端,甚至可能甚至在浏览器中用indexedDB进行了完全模拟。所以是的,后端只是服务json的服务器。只要接收到的数据符合API,它就可以用任何东西代替。两个项目使用不同的构建系统。简而言之,这就像拥有一个网站和一个移动android应用程序。将移动应用程序添加到Web服务器存储库中。
卢瓦克福雷-拉克鲁瓦

另外,如果不清楚,则后端和前端不是用户/管理员界面。但是前端只是ajax接口,后端则提供json。用户和角色的处理方式不同,管理界面位于前端。这样做的目的是使两个部分保持隔离,并防止javascript生成的html加载服务器生成的html。服务器应仅提供json或xml。
卢瓦克福雷-拉克鲁瓦

1
然后,您就不会有构建或部署问题,这样就可以了。但是同样,如果您进行了重大更改,则可能需要更改API,这会影响前端和后端,因此您将分支两次,合并两次,标记两次,等等。但是只要它只保留两次且不会不会变成3 ... 4 ... 12 ... 20,可能不是一个坏主意。
布兰登

即使API发生了更改,如果使用了正确的版本控制,也可以为每个支持API版本的前端创建分支版本。后端应具有一些“向后”兼容性,并使旧API保持尽可能长的时间。
卢瓦克福雷-拉克鲁瓦

3

您的某些论点是有效的,而有些则无效。

我们有两个互不依赖的模块。

实际上,这并非完全正确。为了能够进行通信,前端和后端都需要有一个公共接口(描述)。这使得支持将两者都放在一个公共存储库中成为一个弱点。但是只有一个微弱的论点,因为它并没有太大的不同。

长期拥有两个项目的源历史记录可能会使事情复杂化(尝试在历史记录中搜索前端中的某些内容,而您有一半的提交与所寻找的bug完全无关)

这是一个虚假的论点。如果要查找特定错误的修复方式,请查看错误跟踪器,其提交包含该修复程序。而且,如果您想知道特定代码的演变方式,请查看单个文件的历史记录(或最多几个)。无论哪种情况,在存储库中拥有其他文件(可能来自其他模块)都不应使事情复杂化。

冲突和合并(这不应该发生,但是让某人推送到后端将迫使其他开发人员提取后端更改以推送前端更改。)

这是一个虚假的论点。我不知道在提交/推送更改之前需要同步整个存储库的任何(半个像样的)VCS。最多您需要同步包含已更改文件的文件夹(通常仅同步文件本身)。

一个开发人员可能只在后端工作,但总是不得不拉后端或反之亦然。

这与上一个参数相同。

从长远来看,将是时候进行部署。以某种方式,前端可以在拥有一个后端服务器的同时部署到多个静态服务器。在每种情况下,人们都将不得不要么克隆整个后端,要么创建自定义脚本以仅将所有前端推送到所有服务器或删除后端。如果只需要一个,则仅推/拉前端或后端比两个都容易。

根据人们对部署完成方式的设想,这可能是一个有效的论据。如果部署将通过解压缩服务器上的zip文件/ tarbal来完成,则存储库的组织方式无关紧要。如果部署是通过检出服务器上的某个存储库(的一部分)来完成的,那么最好对单独部署的模块使用单独的存储库。

反驳论点(一个人可能同时在两个项目上工作),使用子模块创建第三个存储库并进行开发。历史记录保存在各个模块中,您始终可以创建标签,在这些标签中,后端/前端的版本确实可以同步工作。将两个前端/后端放在一个仓库中并不意味着它们可以一起工作。它只是将两个历史合并到一个大型仓库中。

这是一个有效的论点,但没有那么强。

如果要将自由职业者添加到项目中,将前端/后端作为子模块将使事情变得更容易。在某些情况下,您实际上并不想授予对代码库的完全访问权限。如果您想限制“局外人”可以看到/编辑的内容,拥有一个大模块将使工作变得更加困难。

这是一个有效的论点。

错误介绍和修复错误,我在前端插入了一个新错误。然后有人修复了后端的错误。对于一个存储库,在新错误之前回滚也将回滚后端,这可能使其难以修复。

这是一个伪造的参数,因为这意味着在对一个模块进行两次错误修复后,您将无法还原第一个模块。任何半透明的VCS都允许您回滚几乎所有旧的提交(尽管这通常意味着您进行新的提交以撤消那些更改,甚至对于HEAD的顶部也是如此)。

我必须将后端克隆到另一个文件夹中,以使后端工作,同时修复前端中的错误……然后尝试重新合并东西……拥有两个存储库将很轻松,因为移动了一个回购的HEAD不会改变另一个。并且针对不同版本的后端进行测试将是轻而易举的。

这实际上是一个很好的论据。具有两个存储库可以更轻松地测试部署的前端和后端可能(略)不同步的方案。


老实说,大多数伪造的论点都可以通过分支来解决。前端分支,后端分支。掌握同步。但是以某种方式,像这样处理分支会使事情变得比拥有两个仓库更为复杂。
卢瓦克福雷-拉克鲁瓦

1
@Sybiam:实际上,它们是虚假的参数,因为它们没有突出显示使用单个存储库可能存在的问题,即使所有更改仅针对主干/ main也是如此。
Bart van Ingen Schenau 2013年

我认为您的批评是正确的。我只是不认为这是问题的重点。
sylvanaar 2013年

2

这篇文章有点老,但我想贡献。虽然您的后端并不真正了解前端,但是前端需要具有与后端API匹配的请求。如果您将后端视为REST API,则可以定义一个接口文件,例如宽大的YAML接口。现在确实有3个项目,您可以根据需要将它们分别划分为不同的存储库:

  • API定义
  • 后端
  • 前端

API定义是其他两个项目中的依赖项,可以说您将maven用作依赖项注入工具。然后,这取决于您要执行版本控制的严格程度。您可以在每次进行更改时提高API定义项目的版本,以确保项目始终处于兼容状态但需要更多开销,或者您可以在maven中使用SNAPSHOTS之类的东西,并且仅在进行版本控制后对开销较小的界面感到满意,但您可能经常会遇到不兼容的情况。但是只要您在前端和后端实施API定义,就可以将项目很好地拆分到不同的存储库中。

这些问题更多是关于管理依赖项。即使项目没有拆分并且位于同一存储库中,也很容易将网站置于前端和后端不同步的状态。真正阻止这种情况的唯一方法是实际定义两者之间的协定,但是您想要以一种不会将前端和后端的实现耦合的方式来实现,就像您将代码编码到接口上一样OO编程中的实现。

另外,为了抢先处理这样的批评,即这样做会增加维护该接口文件的开销,例如swagger甚至可以为不同的编程语言和框架(例如JAX-RS)生成代码存根。因此,您可以使用所选技术生成一个接口,然后实施此接口。它还向您的后端添加了非常好的文档,从而使前端开发人员更轻松地完成工作。

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.