大型团队中的常见库

时间:2009-01-10 14:20:16

标签: unit-testing

假设您有五种产品,并且所有产品都使用一个或多个公司内部库,由各个开发人员编写。

这听起来很简单,但在实践中,我发现非常难以维护。

您如何处理以下情况:

  1. 开发人员无意中引入了一个错误并破坏了生产中的所有内容。

  2. 每个库都必须成熟,这意味着API需要不断发展,那么如果每个开发人员都需要在其他项目中忙于更新/测试代码时,如何将更新后的版本部署到生产中?这是资源和时间问题吗?

  3. 版本控制,部署和使用。您是将它存储在一个全球位置还是强制每个项目使用,例如 svn:externals 来“绑定”一个库?

  4. 我发现制定一个好的策略是非常困难的。我自己的宠物理论是:

    1. 每个公共图书馆都必须有一套超级全面的测试,否则永远不会是常见的,即使这意味着其他人重复这些努力。重复的未经测试的代码优于常见的未经测试的代码(只打破一个项目)。

    2. 每个公共库都必须有一个专门的维护者(可以通过一个小团队中非常好的测试套件来抵消)。

    3. 每个项目都应该检查已知可以使用它的库的版本。这意味着,随着公共代码的更新,开发人员不必为了更新API使用而被撤下。它会是什么。每一段重要的代码都会在几个月甚至几年内发展。

    4. 感谢您对此的看法!

11 个答案:

答案 0 :(得分:13)

这里有一系列竞争目标。首先,可重用组件库必须足够开放,以便其他项目的人员可以轻松添加(或向其提交组件)。如果他们这样做太难了,他们将构建自己的库,忽略常见的库,导致大量重复代码和浪费精力。另一方面,您希望足够控制库的开发,以确保其质量。

我一直在这个位置。没有简单的答案。但是,有一些启发式方法可以提供帮助。

  • 将库视为内部项目。定期发布。确保它具有明确定义的释放程序,完成单元测试和质量保证。而且,最重要的是,经常发布,以便经常向产品库中提交新的提交内容。
  • 鼓励人们为图书馆做出贡献,而不仅仅是建立自己的内部图书馆。
  • 让人们可以轻松地为图书馆做出贡献,并使标准清晰明确(例如,新课程必须附带单元测试和文档)。
  • 让一个或两个开发人员负责图书馆,并且(重要!)为他们分配时间来处理它。一个被视为事后想法的图书馆将很快成为事后的想法。

简而言之,在成功的开源库项目之后,对内部库的开发和维护进行建模。

答案 1 :(得分:5)

我不同意这一点:

  

重复的未经测试的代码优于   常见的未经测试的代码(你只会打破   一个项目)。

如果您同样有可能通过实现相同的方法来创建错误,那么您将不得不在“重复”库的每个实例中修复潜在的不同的错误。

一般来说,编写一次库会更快/更便宜,而不是让其他多个团队编写相同的东西,而是将一些资源分配给测试。


现在解决您的实际问题:我模仿我们对真正的第三方库所做的事情。我们使用特定版本,直到我们准备好或被迫升级。我不会因为我可以升级所有东西 - 必须有原因。

一旦我看到了这个原因(错误修复,新功能等),那么我升级的风险是新库可能会出现新的错误或发生变化。

因此,您的图书馆项目将在必要时继续开发,而不会影响各个团队,直到他们准备“升级”。

您可以发布版本或peg / branches / tag svn来帮助解决所有这些问题。

如果所有团队都可以访问错误跟踪器,他们也可以在升级之前轻松查看升级候选者中存在哪些已知问题。或者,您可以自己维护该列表。

@Brian Clapper为如何在his answer中将项目作为项目运行提供了一些出色的指导。

答案 2 :(得分:2)

我曾经和你所描述的情况类似,只有我公司有几十种软件产品。我参与了负责维护和升级其他人使用的核心库的团队。

我们按如下方式处理了这些情景:

  1. 测试核心库。维护重复代码是一场噩梦。你不只是维护核心和一个副本。在公司的源代码控制中的某个地方,存在相同代码的几个副本。我们有几十种产品,所以这意味着几十份。追捕他们并杀死他们。

  2. 我们有一个由10-12名开发人员组成的小团队,致力于维护核心库及其测试套件。我们还负责召集公司其他1100名开发人员关于如何使用核心库的电话,所以你可以想象,我们非常忙。

  3. 每个其他项目都需要使用已知可以使用的核心库版本。您可以使用版本控制分支来测试旧产品的核心库的新版本,以确保您不会破坏有效的代码。如果核心团队完成了全面的测试工作,那么这应该非常顺利。这对我们来说真正复杂的唯一一次是核心API改变了,或者当我们搞砸了什么东西时。即使您对核心测试非常有信心,也可以使用分支机构来测试单个产品。

答案 3 :(得分:2)

我同意 - 这很难。在我们的小团队中(咨询......而不是产品公司 - 这使得它变得更加困难),我们有一个与众不同的共同组成部分。在这种情况下,成功的秘诀是:

  • 好的开发人员负责开发组件
  • 好的开发人员成为维护组件的网守
  • 确保所有升级(有几个)向后兼容
  • 确保有一些基本文档(或简单的参考应用程序)解释如何使用该组件
  • 确保所有开发人员都知道该组件存在(!)以及他们可以找到它的位置(以及代码,如果他们希望查看它)

让开发人员能够审查代码并建议更好的实现或重构,但最终的mod需要经验丰富的网守。升级组件后,旧版应用程序 无法升级。如果我们做了一个新版本,我们评估是否要升级,如果我们做了,我们需要做的就是交换库 - 不需要更改代码,除非我们想要通过升级使用一些新功能。阻力是不可避免的,但有时它是一种良好的阻力,因为它来自对新一代或重构组件有更好想法的优秀开发人员。

答案 4 :(得分:1)

像任何其他产品一样对待库的开发。每个库都有自己的存储库,自己的版本和版本号。已编译和正式测试的库版本也保存在存储库中。记录版本之间的功能和变化。

然后像使用第三方库一样使用库。您的产品仅使用已编译库的固定版本。当您真正需要时切换到新版本并意识到这涉及更多测试。将您使用的版本添加到版本控制中。

当您在库中发现错误或需要新功能时,会创建新版本或子版本。使用像svn这样的版本控制系统可以轻松实现。当您需要用于调试目的的源代码时,将其导出并将其包含在项目中,但不要在那里进行更改,而是修复库存储库中的问题。

这样,每个团队都可以为图书馆做出贡献而不会危及其他团队的工作。切换版本是故意完成的,而不是偶然的。

答案 5 :(得分:1)

为现有库创建一个反腐败(DDD)层...这只是一个外观..然后为这个反腐败层写单元测试...现在即使有人升级/更新通过运行单元测试,你会知道某些东西是否被打破...

这些测试也可以作为合同的文档......并且如果他们使用相同的功能,并不是每个需要使用该库的项目都必须编写这个反腐败层。

答案 6 :(得分:1)

Duplication is the root of all evil

听起来像你需要的那样:

  • Ivy这样的工件库,因此您可以共享和版本化库,区分API稳定版本和“成熟”版本
  • 测试库
  • 使用它们测试项目
  • 持续集成系统,以便在引入不兼容性或错误时通知项目和原始库开发人员

答案 7 :(得分:0)

我认为一个共享库优于3个重复库(并且1个测试肯定优于3个未经测试)。这是因为当您发现并解决问题时,这会使整个应用领域更加稳固(并且开发和维护更高效)。

顺便说一下,这不是其中一个原因(除了回馈社群之外)为什么我们的公司将我们.NET shared libraries作为开源公开给公众。

另外,编写的代码更少。您可以指定一个开发人员对库及其用法实施良好的开发实践(即通过在库消费者中的单元测试中强制执行的代码合同)。 提高了质量并降低了维护成本

我们将共享库作为二进制文件存储在解决方案中。这来自逻辑要求,任何解决方案都必须是原子的和独立的(这排除了svn:externals链接)。

API兼容性根本不是问题。只需让您的集成服务器重建并重新测试整个产品堆栈(同时更新所有内部引用并传播更改),您将始终确保所有内部API都是可靠的。无论是谁破坏API都必须修复它或更新用法。

答案 8 :(得分:0)

  

复制是万恶之源

我认为,未经检查的政府是所有邪恶的根源:)

我甚至建议复制应该是一个选项。我理解为什么,但让我复杂一点。

假设您有一个相当大的库,实际上并没有特别做任何事情 - 它只是一组实用程序。这个库没有测试 - 根本没有。你只需要一个功能。比如,解析文件扩展名的东西。

流行测验:你只是在你自己的项目中写一些小的东西,或者你咬紧牙关并使用免费的未经测试的一组实用工具,如果有人破坏了这个功能会破坏你的应用程序吗? / p>

另外,想象一下你在编写测试的环境中不是文化的一部分,因为大多数项目非常紧张,开发时间很短。

复制大型系统 - 例如客户注册 - 当然是愚蠢的。但是,如果备选方案不够安全(没有用于维护公共代码的系统),那么在项目中复制相当小的东西会更安全(

)。

以这种方式思考 - 这种情况一直发生 - 多个承包商为同一家公司从事不同的项目。他们甚至不了解彼此。

我的论点是:

如果一个团队不能专注于维护一个可靠的公共代码库,或者如果环境没有给他们足够的时间,那么最好让他们作为单独的“承包商”工作。

您仍然需要使用无法复制的大型现有系统。

答案 9 :(得分:0)

  

复制大型系统 - 例如   客户注册 - 会很愚蠢   不相信,

这就是那些系统发布外部接口的原因。

如果您将库定义为项目之间的共享代码:根据我的经验,这几乎总是很糟糕。项目应该是独立的,一个项目的更新不应该影响其他项目。

即使你从图书馆开始,你最终还是会复制代码。想要修补项目1吗?它与库1.34一起发布,所以为了使修补程序尽可能小,你将返回到库1.34并修复它。但是嘿 - 现在你确实感觉图书馆应该避免 - 你复制了代码。

每位开发人员都使用Google查找代码并将其复制到自己的应用程序中。这可能就是他们首先发现Stack Overflow的原因。想象一下,如果Stackoverflow发布了库而不是代码片段会发生什么,并且您将了解困扰许多有意义的库创建者的问题。

图书馆往往是特定问题的通用解决方案。通常,通用解决方案比两个特定解决方案的总和更复杂。这意味着你需要一个优秀的程序员来解决一个本可以由两个蠢货解决的问题。对我来说听起来很糟糕:D

答案 10 :(得分:0)

我想在上面提到的解决方案中指出一个问题:将库作为具有自己的版本控制方案的内部项目。

问题

如果您的公司有多个产品(比如两个团队 - 两个产品:A,B),那么每个产品都有自己的发布时间表。让我们举个例子:A队正在研究产品A v1.6。他们的发布时间表是两周后(假设10月30日)。 B队正在研究产品B v2.4。他们的发布时间是从现在起11个月到现在的1.5个月。假设两者都在使用acme-commons-1.2-SNAPSHOT。两者都在为acme-commons添加更改,因为他们需要它。在10月30日之前的几天,B队引入了一个有缺陷的变化,对于acme-commons-1.2-SNAPSHOT。 A团队进入压力模式,因为他们在代码冻结前1天发现了错误。

此方案显示将公共库视为第三方库几乎是不可能的。琐碎但有问题的解决方案是让每个团队拥有他们自己要更改的版本的副本。例如,产品A v1.2将为名为“1.2-A-1.6”的acme-commons创建一个分支(和版本)。 B队还将在acme-commons中创建一个名为“1.2-B-2.4”的分支。他们的发展永远不会发生碰撞,一旦他们测试了他们的产品,他们将无压力。

当然,有人必须将他们的更改合并回原始分支(比如master或1.2)。

我在这个解决方案中遇到的问题是:

  1. 分支膨胀 - 树结构将非常浮肿,并且将更难理解变化/合并的流程。
  2. 合并回1.2可能永远不会发生 - 除非团队/开发人员专注于此库,团队A或团队B将他们的代码合并回1.2分支的可能性很小。他们将始终专注于他们的任务,从而创建和使用他们自己的分支空间。开发人员/团队的分配是昂贵的,因此并不总是可行的解决方案。
  3. 我仍然试图弄清楚这一点,所以欢迎任何关于此问题的想法