颠覆外部是反模式吗?


69

Subversion允许您使用外部工具嵌入其他存储库的工作副本,从而可以轻松地在项目中控制第三方库软件的版本。

尽管这些对于重用库和供应商软件的版本控制而言似乎是理想的选择,但它们并非没有批评者的

请不要使用Subversion外部工具(或其他工具中的类似工具),因为它们是反模式,因此不必要

使用外部元件是否存在隐患?请解释为什么将它们视为反模式。


另请参阅以下答案:stackoverflow.com/a/248367/29152
DuckMaestro

由于可能的答案的主观性质,我看到的问题比主持人认为不适合堆栈溢出论坛的问题要好。当在某些情况下不允许后续答案时,主持人在标记问题时出现不一致的情况是很奇怪的。
shawn1874

Answers:


71

我是问题的引文的作者,该引文来自先前的回答

杰森(Jason)对诸如我的简短陈述表示怀疑,并要求作出解释是正确的。当然,如果我完全解释了该答案中的所有内容,则需要写一本书。

迈克(Mike)也正确指出, svn:external类似功能是目标源的更改可能会破坏您自己的源,尤其是如果目标源位于您不拥有的存储库中时。

在进一步解释我的评论时,让我首先说,有使用“svn:external类似”功能的“安全”方法,就像使用任何其他工具或功能一样。但是,我将其称为反模式因为该功能更容易被滥用。根据我的经验,它一直被滥用,我发现自己极不可能以这种安全的方式使用它,也不太可能建议使用。请进一步注意,我的意思是不贬低Subversion团队,尽管我打算搬到Bazaar,但我喜欢Subversion。

此功能的主要问题是鼓励使用,通常用于直接将一个构建(“项目”)的源链接到另一个构建的源,或将该项目链接到二进制文件(DLL,JAR等)。取决于它。这些用法都不是明智的,它们构成了反模式。

正如我在其他答案中所说的那样,我相信软件构建的一项基本原则是,每个项目都完全构建一个二进制或主要可交付成果。可以将关注点分离原理应用于构建过程。对于直接引用另一个项目的一个项目,尤其如此,这也违反了封装原理。这种违规的另一种形式是尝试通过递归调用子构建来创建构建层次结构以构建整个系统或子系统。Maven强烈鼓励/强制这种行为,这是我不建议这样做的众多原因之一。

最后,我发现有许多实际问题使此功能不受欢迎。首先,它svn:external具有一些有趣的行为特征(但是细节暂时使我无所适从)。另外,我总是发现我需要这样的依赖关系对我的项目(构建过程)是显式可见的,而不是作为某些源代码控制元数据掩埋。

那么,使用此功能的“安全”方式是什么?我认为这是只有一个人临时使用它的时候,例如一种“配置”工作环境的方法。我可以看到程序员可以在存储库中创建自己的文件夹的位置(或为每个程序员创建一个文件夹),在那里他们可以配置svn:external指向当前正在使用的存储库其他各个部分的链接。然后,签出该文件夹将创建其所有当前项目的工作副本。添加或完成项目时,svn:external可以调整定义并适当更新工作副本。但是,我更喜欢一种不依赖于特定源代码控制系统的方法,例如使用调用检出的脚本来执行此操作。

记录下来,我最近一次接触此问题是在2008年夏天,该客户是svn:external在一个大规模使用的咨询客户中进行的,“一切”都经过交联生成一个主工作副本。他们基于Ant&Jython(用于WebLogic)的构建脚本是在此主工作副本之上构建的。最终结果是:NOTHING可以独立构建,实际上有数十个子项目,但是没有一个项目可以自行安全地进行结帐/工作。因此,在此系统上进行的任何工作都首先需要检出/更新2 GB以上的文件(它们还将二进制文件也放入存储库中)。做任何事情都是徒劳的,我在尝试了三个月后离开了(还有许多其他的反模式)。

编辑:对递归构建的解释-

多年来(尤其是最近的十年),我为财富500强公司和大型政府机构构建了庞大的系统,其中涉及数十个子项目,这些子项目排列在多个层次的目录层次结构中。我已经使用Microsoft Visual Studio项目/解决方案来组织基于.NET的系统,用于基于Java的系统的Ant或Maven 2,并且已经开始针对基于Python的系统使用distutils和setuptools(easyinstall)。这些系统还包括通常在Oracle或Microsoft SQL Server中使用的大型数据库。

在设计这些庞大的版本以简化易用性和可重复性方面,我取得了巨大的成功。我的设计标准是,新开发人员可以在第一天就出现,可以得到一个新的工作站(也许是直接从Dell获得的,只有典型的OS安装),可以得到一个简单的安装文档(通常只有一页安装说明),并能够在半天或更短的时间内从源头,无人监督,无人协助的情况下完全设置工作站并构建完整的系统。调用构建本身涉及到打开命令外壳,更改到源树的根目录,以及发出单行命令来构建所有内容。

尽管取得了成功,但构建如此庞大的构建系统仍需要格外小心并严格遵循扎实的设计原则,就像构建庞大的业务关键型应用程序/系统一样。我发现至关重要的部分是,每个项目(产生单个工件/可交付成果)必须具有一个构建脚本,该脚本必须具有定义明确的界面(用于调用构建过程各部分的命令),并且它必须能够站立仅与所有其他(子)项目无关。从历史上看,构建整个系统很容易,但是很难/不可能只构建一个。直到最近,我才学会仔细地确保每个项目真正独立。

实际上,这意味着必须至少有两层构建脚本。最低层是产生每个可交付成果/工件的项目构建脚本。每个这样的脚本都位于其项目源代码树的根目录中(实际上,此脚本定义了其项目源代码树),这些脚本对源代码控制一无所知,它们希望从命令行运行,它们引用项目相关的所有内容到构建脚本,它们根据一些可配置的设置(环境变量,配置文件等)引用其外部依赖项(工具或二进制工件,没有其他源项目)。

第二层构建脚本也打算从命令行调用,但是它们知道源代码控制。实际上,第二层通常是使用项目名称和版本调用的单个脚本,然后将命名项目的源检出到新的临时目录(可能在命令行中指定)并调用其构建脚本。

可能需要更多的变化以适应连续集成服务器,多个平台和各种发行方案。

有时需要第三层脚本来调用第二层脚本(后者会调用第一层),以构建整个项目集的特定子集。例如,每个开发人员可能都有自己的脚本来构建他们今天正在处理的项目。可能会有一个脚本来构建所有内容,以便生成主文档或计算指标。

无论如何,我发现尝试将系统视为项目的层次结构会适得其反。它将项目彼此关联,以使它们不能单独自由构建,也不能在任意位置(连续集成服务器上的临时目录)或任意顺序(假定满足依赖性)自由构建。通常,尝试强制执行层次结构会破坏任何可能尝试的IDE集成。

最后,构建庞大的项目层次结构可能太耗费性能。例如,在2007年春季,我尝试使用Ant构建适度的源层次结构(Java加Oracle),但最终失败,因为构建始终因Java OutOfMemoryException而中止。这是在具有3.5 GB交换空间的2 GB RAM工作站上,我对其进行了调整,以使其能够使用所有可用内存。就代码量而言,该应用程序/系统相对来说是微不足道的,但是无论我给它分配了多少内存,递归构建调用最终都会耗尽内存。当然,执行它也要花很多时间(在中止之前,通常需要30-60分钟)。我知道如何很好地进行调优,但最终我只是超出了工具的限制(在这种情况下为Java / Ant)。

因此,请帮自己一个忙,将构建构建为独立项目,然后将它们组合成一个完整的系统。保持轻便灵活。请享用。

编辑:更多关于反模式

严格来说,反模式是一种常见的解决方案,看起来像它可以解决问题,但不能解决问题,这可能是因为它留下了重要的空白,或者是因为它引入了其他问题(通常比原始问题更糟)。解决方案必然涉及一个或多个工具,以及将其应用于当前问题的技术。因此,将工具或工具的特定功能称为反模式是很困难的,似乎人们正在检测并对此做出反应-足够公平。

另一方面,由于在我们行业中通常将重点放在工具而不是技术上,因此引起关注的是工具/功能(对StackOverflow此处的问题进行的随意调查似乎很容易说明)。我的评论以及这个问题本身反映了这种做法。

但是,有时进行这种拉伸似乎特别合理,例如在这种情况下。有些工具似乎将用户“引向”应用它们的特定技术,以至于有些人认为工具会影响思想(略作改写)。我主要svn:external是出于这种精神,我建议这是一种反模式。

为了更严格地说明问题,反模式是设计一种构建解决方案,该解决方案包括在源代码级别将项目捆绑在一起,或者隐式地版本化项目之间的依赖关系,或者允许这种依赖关系被隐式更改,因为每个这些调用都带来很大的负面影响。后果。svn:external类特征的性质使得很难避免那些负面后果。

正确处理项目之间的依赖关系涉及解决这些动态问题以及基本问题,而工具和技术则走了一条不同的道路。应该考虑的一个示例是Ivy,它以类似于Maven的方式提供帮助,但没有很多缺点。我正在研究Ivy和Ant一起作为Java构建问题的短期解决方案。从长远来看,我希望将核心概念和功能整合到一个开源工具中,以促进多平台解决方案的发展。


4
+1; 奇特的书面答复,其中包含一些非常引人注目的想法。特别是,我非常喜欢将SOLID / OOP设计原则应用于构建过程。
马特·坎贝尔,

2
>递归调用子构建您能否详细说明?我已经阅读了“递归使有害”,但我仍然没有看到问题,或者应该采取什么措施避免出现此问题。
KeyserSoze

8
共享库呢?您如何建议优雅地处理存储库中的共享资源?例如,一个资源库中有多个项目,所有项目都使用通用代码。
克林特·帕奇

1
之后我被推迟了Environment variables。我并不是为了避免在存储库中链接而插入此类黑客。您谈论很多有关构建过程,但很少讨论组织存储库。我仍然不相信。
2014年

3
SVN exernals在一家拥有100名员工的公司中表现理想,该公司拥有大约50个小型软件模块,这些模块用于制造大约10种产品(每个模块由几个模块组成)。SVN外部组件在3人创业公司中也表现出色。Starup没有时间发明自己的基于脚本的构建系统。答案虽然不错。
Danijel

66

我认为这根本不是反模式。我在Google上进行了一些快速搜索,但基本上没有得到任何结果……没有人抱怨使用svn:externals是有害的还是有害的。当然,您需要注意一些警告事项……这不是您应该大量投入所有存储库中的事情……但是对于原始报价,这只是他的个人(和主观)观点。他从未真正讨论过svn:externals,只是谴责它们是一种反模式。这种笼统的陈述在没有任何支持或至少没有关于该人如何发表陈述的理由的情况下总是令人怀疑。

也就是说,使用外部组件存在一些问题。就像Mike回答的那样,它们对于指出已发布软件的稳定分支非常有帮助...尤其是您已经控制的软件。我们在实用程序库等许多项目中内部使用它们。我们有一个小组来增强实用程序库并在其上工作,但是该基础代码在许多项目中都是共享的。我们不希望各个团队仅签入公用事业项目代码,也不希望处理一百万个分支,因此对于我们来说svn:externals运作良好。对于某些人来说,它们可能不是答案。但是,我强烈反对“请不要使用...”的说法,并且这些工具代表了反模式。


20
我同意,可接受的答案是垃圾。我读了整本书,没有看到一个具体的理由来支持他的主张。“例如,svn:external具有一些有趣的行为特征(但细节暂时使我无视)”他就近了。
快速乔·史密斯,

19

使用svn:externals的主要风险是,所引用的存储库将以破坏代码或引入安全漏洞的方式进行更改。如果外部存储库也处于您的控制之下,那么这可能是可以接受的。

就个人而言,我仅使用svn:externals指向我拥有的存储库的“稳定”分支。


16
这就是为什么(或者应该)将svn:externals链接指向标记的发行版,甚至是特定的修订版本,而不仅仅是/ trunk @ HEAD引起麻烦的原因。
快速乔·史密斯,

1
好点,快乔。我想我会将其作为我的新政策。
麦克,

2
我不是一个正式的文档专家,但是对于诸如源代码控制之类的复杂和重要的事情,我倾向于在使用它们之前阅读打算使用的功能的文档。Subversion文档建议“您应该认真考虑在所有外部定义中使用显式修订版本号。” 除了解释,他们还给出了解释的原因。
jpierson

2
指向头部修订不一定是不好的。指向带标签的发行版可能会使您的代码陈旧,并且没有错误修复。指向标签或头部修订的决定应视情况而定,这取决于您的项目要求。
live-love

18

一个旧线程,但我想解决的问题是外部的变化可能会破坏您的代码。如前所述,这通常是由于外部属性使用不正确造成的。在几乎所有情况下,外部引用都应指向外部存储库URI中的特定修订号。这样可以确保外部设备永远不会更改,除非您将其更改为指向其他版本号。

对于某些内部库(在最终用户项目中用作外部库),我发现在Major.Minor版本中创建该库的标签非常有用,在该版本中,我们不执行重大更改。通过四点版本控制方案(Major.Minor.BugFix.Build),我们允许标记通过BugFix.Build保持最新(再次,不执行任何重大更改)。这使我们可以使用不带修订号的外部引用。在重大更改或其他重大更改的情况下,将创建一个新标签。

外部本身并不坏,但这并不能阻止人们为它们创建错误的实现。无需进行大量研究,只需阅读一些文档即可了解如何安全有效地使用它们。


保持纪录永远不会太晚。:-)好的答案。
克林特·帕奇

1
“几乎在所有情况下,外部引用都应指向外部存储库URI中的特定修订版号”-并且您会得到另一个缺点-完全有效,并且通过所有测试(对于过时的外部)的代码都被破坏了,当您会在与您的代码链接的顶部发现提交-大多在截止日期发生
-Lazy Badger

9

如果纯外部格式是一种反模式,因为它可能破坏您的存储库,则不应进行显式修订。

svn书摘录:

外部定义是本地目录到版本化资源的URL **(可能是特定版本)的映射。

我认为这完全取决于您使用该功能的目的,它本身并不是反模式。


2
这儿这儿!如果没有真实,理智的示例,我无法相信所有关于外部因素的the昧。
克林特·帕奇

9

Subversion外部组件中确实存在一些缺陷,但是我们似乎可以合理地成功地使用它们来包含当前项目所依赖的库(包括我们自己的库和供应商库)。因此,我不认为它们是“反模式”。对我来说重要的使用要点是:

  • 它们指向另一个项目的特定修订版或标签(从不开头)。
  • 它们被插入到当前项目中,而远离其自身的源代码等(例如,在名为“支持文件”的子目录中)。
  • 它们仅引用其他项目的“接口”文件(例如,include文件夹)和二进制库(即,我们没有获得其他项目的完整源代码)。

我也对这种安排和更好的方法的任何重大风险感兴趣。


2

A是B不会使一个一个b,除非你说为什么会这样。

我在Subversion中通过外部引用看到的主要缺陷是,不能保证更新工作副本时存在该存储库。

可以使用和滥用Subversion外部引用,而功能本身仅是一个功能而已。不能说是模式,也不是反模式

我已经阅读了您引用的人的回答,我必须说我不同意。如果您的项目要求存储库中的文件版本为XYZ,则外部的Subversion参考可以轻松地为您提供该版本。

是的,您可以通过不特别指定所需引用版本来错误地使用它。那会给你麻烦吗?可能!

它是反模式吗?这得看情况。如果您点击文本作者提供的链接,则引用。在这里,然后没有。可以使用某些东西来提供不好的解决方案并不能使整个方法成为反模式。如果那是规则,那么我会说,编程语言大体上都是反模式,因为在每种编程语言中,您都可能做出不好的解决方案


2
反模式是一种产生更多问题的解决方案,通常会解决更多的问题。使用诸如svn:external功能之类的依赖项就是这种情况。我将尽力说明。
罗布·威廉姆斯

4
@RobWilliams-我想看看您对svn:externals的图示,它被描述为一个anit模式。此外,我希望看到您的解决方案,用于在不引入重复的情况下将共享数据合并到版本控制的系统中。
克林特·帕奇
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.