颠覆外部是反模式?

通过Subversion,您可以使用外部工具来embedded其他存储库的工作副本,从而可以轻松地对项目中的第三方库软件进行版本控制。

虽然这些对于图书馆的重用和供应商软件的版本控制来说似乎是理想的,但他们并不是没有批评的人 :

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

使用外部存在隐藏的风险吗? 请解释他们为什么会被视为反模式。

我是这个问题中引用的作者,这个问题来自以前的回答 。

杰森对我的简短陈述有疑问,并要求解释。 当然,如果我完全解释了答案中的所有内容,我就需要写一本书。

Mike还指出, svn:external like特性的一个问题是,目标源中的更改可能会破坏自己的源代码,特别是当目标源位于不属于您的存储库时。

在进一步解释我的意见时,首先让我们说,像使用其他工具或function一样,使用svn:external like特性有“安全”的方法。 但是,我将其称为反模式,因为该function更可能被滥用。 根据我的经验,它一直被滥用,我发现自己不太可能以安全的方式使用它,也不会推荐使用它。 请进一步注意,我的意思是不要贬低Subversion团队 – 我喜欢Subversion,尽pipe我打算前往Bazaar。

这个特性的主要问题是鼓励它,它通常用于直接将一个构build的源(“project”)连接到另一个的源,或者将项目链接到二进制(DLL,JAR等)依赖于它。 这些用途都不是明智的,它们构成反模式。

正如我在其他答案中所说的那样,我认为软件构build的一个基本原则是每个项目只能构build一个二进制或主要的可交付成果。 这可以被认为是将关注点分离到构build过程的原则的应用。 对于一个直接参考另一个项目来源的项目来说,这也是违反封装原则的。 这种违规的另一种forms是试图创build一个构build层次结构,通过recursion调用子构build来构build整个系统或子系统。 Maven强烈鼓励/执行这种行为,这是我不推荐的原因之一。

最后,我发现有不同的实际问题,使这个function是不可取的。 首先, svn:external具有一些有趣的行为特征(但是现在的细节让我不知所措)。 另一方面,我总是发现我需要这样的依赖关系对我的项目(构build过程)是可见的,而不是作为一些源控制元数据被埋没。

那么,使用此function的“安全”方式是什么? 我会认为这只是一个人临时使用时,比如“configuration”一个工作环境的方法。 我可以看到程序员可以在存储库中创build自己的文件夹(或者每个程序员都可以在其中创build一个文件夹),在那里他们将svn:external链接configuration到他们目前正在使用的存储库的其他各个部分。 然后,签出一个文件夹将创build所有当前项目的工作副本。 当一个项目被添加或完成时, svn:external定义可以被调整,并且工作副本被适当地更新。 不过,我更喜欢一种不绑定到特定源代码pipe理系统的方法,比如用一个调用checkout的脚本来做这件事。

为了logging,我最近接触到这个问题是在2008年夏天在一个大规模使用svn:external的咨询客户端发生的 – 一切都是交叉链接的,以产生一个主工作副本。 他们的基于Ant和Jython(用于WebLogic)构build脚本build立在这个主工作副本之上。 最终的结果是:任何东西都可以单独build造,实际上有几十个子项目,但没有一个是自己安全的结账/工作。 因此,在这个系统上的任何工作首先需要超过2 GB的文件(他们把二进制文件也放在库中)的检出/更新。 做任何事情都是徒劳的,我试了三个月后就离开了(还有很多其他反模式)。

编辑:阐释recursion构build –

多年来(特别是最近十年),我为“财富”500强企业和大型政府机构build立了庞大的系统,涉及许多子目录,这些子项目安排在多层次的目录层次结构中。 我已经使用Microsoft Visual Studio项目/解决scheme来组织基于.NET的系统,Ant或Maven 2用于基于Java的系统,并且我已经开始为基于Python的系统使用distutils和setuptools(easyinstall)。 这些系统还包括通常在Oracle或Microsoft SQL Server中的大型数据库。

为了易用性和可重复性,我devise了这些庞大的构buildscheme,取得了巨大的成功。 我的devise标准是,新开发人员可以在第一天出现,给予一个新的工作站(可能直接从戴尔只有一个典型的操作系统安装),给一个简单的设置文件(通常只有一页的安装说明),并能够完全设置工作站,并从源,无监督,无辅助,并在半天或更短的时间内build立完整的系统。 调用构build本身涉及到打开一个命令shell,切换到源代码树的根目录,并发出一行命令来构build所有东西。

尽pipe取得了这样的成功,但构build这样一个庞大的构build系统需要非常小心谨慎,坚持坚实的devise原则,就像构build一个大规模的关键业务应用程序/系统一样。 我发现一个关键的部分是每个项目(产生一个工件/可交付成果)必须有一个构build脚本,它必须有一个定义良好的界面(调用构build过程的一部分的命令),它必须单独从所有其他(子)项目。 从历史上看,build立整个系统是很容易的,但是很难/不可能只build立一个整体。 直到最近我才学会了认真确保每个项目都是独立的。

实际上,这意味着必须至less有两层构build脚本。 最底层是产生每个交付物/工件的项目构build脚本。 每个这样的脚本都驻留在它的项目源代码树的根目录下(的确,这个脚本定义了它的项目源代码树),这些脚本对源代码控制一无所知,他们期望从命令行运行,到构build脚本,并且它们基于几个可configuration的设置(环境variables,configuration文件等)引用它们的外部依赖关系(工具或二进制工件,没有其他源项目)。

构build脚本的第二层也打算从命令行调用,但是这些知道源代码控制。 事实上,这个第二层往往是一个单独的脚本,用一个项目名称和一个版本来调用,然后将命名项目的源检出到一个新的临时目录(可能在命令行中指定)并调用其构build脚本。

可能需要更多的变体来适应持续集成服务器,多平台和各种发布scheme。

有时需要第三层脚本来调用第二层脚本(调用第一层)来构build整个项目集的特定子集。 例如,每个开发人员可能都有自己的脚本来构build他们今天正在进行的项目。 可能有一个脚本来构build一切,以便生成主文档或计算度量标准。

无论如何,我发现试图把这个系统看作是一个项目层次结构是适得其反的。 它将项目彼此联系起来,使得它们不能单独自由地build立,或者在任意位置(持续集成服务器上的临时目录),或者以任意顺序(假定依赖关系被满足)自由地build立。 通常,试图强制一个层次结构会破坏所有可能尝试的IDE集成。

最后,构build一个庞大的项目层次结构可能太简单,性能密集。 例如,在2007年的spring,我尝试使用Ant构build的一个适度的源代码层次结构(Java + Oracle),最终由于构build始终以Java OutOfMemoryExceptionexception终止而失败。 这是在一个2 GB的RAM工作站与3.5 GB的交换空间,我已经调整了JVM能够使用所有可用的内存。 应用程序/系统在代码量方面相对较小,但recursion构build调用最终耗尽了内存,无论我给了多less内存。 当然,这也需要永远执行(30-60分钟是普遍的,在中止之前)。 我知道如何调整,但最终我只是超越了工具的限制(在这种情况下是Java / Ant)。

所以帮你一个忙,把你的构build作为独立的项目,然后把它们组合成一个完整的系统。 保持轻便灵活。 请享用。

编辑:更多关于反模式

严格地说,反模式是一种常见的解决scheme,看起来像是解决了问题,但不是,因为它留下了重要的差距,或者因为引入了额外的问题(通常比原始问题更糟)。 一个解决scheme必然涉及一个或多个工具加上应用他们到手边的问题的技术。 因此,将工具或工具的特定function称为反模式是一种延伸,而且似乎人们正在检测这种伸缩,并对此作出反应 – 这是相当公平的。

另一方面,由于在我们这个行业中,注重工具而不是技术似乎是一种常见的做法,所以它是引起注意的工具/特征(这里对StackOverflow的问题进行了一个偶然的调查,似乎很容易说明)。 我的意见和这个问题本身反映了这种做法。

但是,有时这样做似乎特别合理,比如在这种情况下。 有些工具似乎“引导”用户使用特定的技术,直到有些人认为工具塑造思想 (稍微改写一下)。 主要是这个精神,我build议svn:external是一个反模式。

为了更严格地说明问题,反模式是devise一个构build解决scheme,包括在源代码级别绑定项目,或隐式地版本化项目之间的依赖关系,或者允许这样的依赖项隐式地改变,因为每个调用都是非常负面的后果。 svn:external的性质svn:external的特征使得避免这些负面的后果非常困难。

妥善处理项目之间的依赖关系涉及解决这些dynamic问题以及基本问题,这些工具和技术导致不同的途径。 应该考虑的一个例子是常春藤 ,这有助于以类似于Maven的方式,但没有许多缺点。 我正在研究常春藤,再加上Ant,作为我的Java构build问题的短期解决scheme。 长期来看,我期待将核心概念和function整合到一个开源工具中,以促进多平台解决scheme。

我不认为这是一个反模式。 我在谷歌上做了一些快速search,基本上没有任何东西……没有人抱怨说使用svn:externals是坏的或有害的。 当然,有一些注意事项需要注意…并不是你应该把所有的知识储存在你的存储库中……但是对于原始的引用,这只是他个人的(和主观的)意见。 他从来没有真正讨论svn:externals,除了谴责他们作为反模式。 这种粗暴的陈述,没有任何支持,或至less推理人如何来发表声明,总是怀疑。

这就是说,使用外部存在一些问题。 就像Mike回答的那样,他们可以非常有用地指向已发布软件的稳定分支,特别是已经控制的软件。 我们在公用事业图书馆等多个项目内部使用它们。 我们有一个小组,可以增强和使用实用程序库基础,但是这个基本代码在许多项目中共享。 我们不希望各种团队只检查公用事业项目代码,我们不想处理100万个分支机构,所以对我们来说svn:externals工作得很好。 对一些人来说,他们可能不是答案。 但是,我坚决不同意“请不要使用…”的说法,这些工具代表了反模式。

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

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

一个古老的线程,但我想解决的关注,一个不断变化的外部可能会打破你的代码。 如前所述,这通常是由于外部属性的不正确使用所致。 外部引用在几乎所有情况下都应指向外部存储库URI中的特定修订号。 这确保了外部永远不会改变,除非您将其更改为指向不同的修订版本号。

对于我们在最终用户项目中用作外部数据库的一些内部库,我发现在Major.Minor版本中创build库的标签是非常有用的,在这个版本中我们不会执行任何重大更改。 使用四点版本控制scheme(Major.Minor.BugFix.Build),我们允许标记保持与BugFix.Build的变化(再次强制执行没有重大变化)最新。 这使我们可以使用外部参考标签而无需修订版本号。 在主要或其他重大更改的情况下,会创build一个新的标签。

外部本身并不坏,但是这并不能阻止人们制造错误的实现。 它不需要太多的研究,只需通过一些文档的一点点阅读,学习如何安全和有效地使用它们。

如果纯粹的外部是一个反模式,因为它可以打破你的存储库,那么明确的修订应该不会。

摘自svn书 :

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

我认为这一切都取决于你使用该function的目的,它本身不是一个反模式。

颠覆外部存在着明显的缺陷,但是我们似乎合理地成功地使用它们来包含当前项目所依赖的图书馆(包括我们自己的和供应商)。 所以我不认为他们是“反模式”。 对我来说重要的用法是:

  • 他们指向另一个项目的特定版本或标签(从来不是头)。
  • 它们被插入到当前项目中,远离它自己的源代码等(例如,在一个名为“支持文件”的子目录中)。
  • 他们只提到其他项目的“接口”文件(如包括文件夹)和二进制库(即我们没有得到其他项目的完整源)。

我也会对这种安排的任何重大风险感兴趣,并且有更好的方法。

a是b ,除非你说为什么这么做,否则不会成为b

我在subversion中看到的带有外部引用的主要缺陷是,当您更新工作副本时,不能保证存储库存在。

Subversion的外部引用可以被使用和滥用,而这个特性本身就不过是一个特性而已。 它不能说是一个模式 ,也不是一个反模式

我已经读过你引用的人的答案,我必须说我不同意。 如果您的项目需要从版本库获取文件版本XYZ,则外部子版本引用可以轻松地为您提供该版本。

是的,您可以通过不明确指定您需要的引用版本来使用它。 这会给你一些问题吗? 有可能!

这是一个反模式? 那么,这取决于。 如果你按照你引用的文本作者的链接,即。 在这里 ,然后没有。 有些东西可以用来提供一个不好的解决scheme,但并不能使整个方法成为一个反模式 。 如果这是规则,那么我会说,编程语言大体上是反模式,因为在每种编程语言中,你都可以做出不好的解决scheme