6. ZeroMQ 社区

第 6 章 - ZeroMQ 社区 #

人们有时会问我 ZeroMQ 有什么特别之处。我的标准答案是,ZeroMQ 可以说是在面对“我们如何构建 21 世纪所需的分布式软件?”这一棘手问题时,我们拥有的最佳答案。但更重要的是,ZeroMQ 之所以特别,是因为它的社区。这最终将狼与羊区分开来。

开源有三种主要模式。第一种是大型公司倾倒代码以打破其他公司的市场。这是 Apache 基金会模式。第二种是小型团队或小型公司构建他们的梦想。这是最常见的开源模式,可以在商业上非常成功。最后一种是积极且多元化的社区,它们在问题领域蜂拥而至。这是 Linux 模式,也是我们在 ZeroMQ 中渴望达到的目标。

一个运作良好的开源社区的力量和持久性再怎么强调都不为过。似乎确实没有更好的方法来长期构建软件。社区不仅选择最好的问题来解决,而且以最小化、谨慎的方式解决它们,然后照看这些解决方案,年复一年,几十年,直到它们不再相关,然后悄悄地将它们收起来。

要想真正从 ZeroMQ 中受益,你需要理解这个社区。在未来的某个时候,你可能想提交一个补丁、一个问题或一个附加组件。你可能想向某人寻求帮助。你很可能想把你的部分业务押在 ZeroMQ 上,当我告诉你社区比支持产品的公司重要得多得多时,即使我是那家公司的 CEO,这也应该很有意义。

在本章中,我将从几个角度审视我们的社区,最后详细解释我们的协作契约,我们称之为“C4”。你应该会发现这次讨论对你自己的工作很有帮助。我们也将 ZeroMQ C4 流程成功应用于闭源项目。

我们将涵盖

  • ZeroMQ 作为一系列项目的粗略结构
  • “软件架构”的真正含义
  • 为什么我们使用 LGPL 而不是 BSD 许可证
  • 我们如何设计和发展 ZeroMQ 社区
  • 支持 ZeroMQ 的业务
  • 谁拥有 ZeroMQ 源代码
  • 如何制作和提交 ZeroMQ 补丁
  • 谁控制哪些补丁实际上进入 ZeroMQ
  • 我们如何保证与旧代码的兼容性
  • 为什么我们不使用公共 git 分支
  • 谁决定 ZeroMQ 的路线图
  • 修改的一个示例libzmq

ZeroMQ 社区的架构 #

你知道 ZeroMQ 是一个采用 LGPL 许可的项目。实际上,它是围绕核心库构建的项目集合,libzmq。我将这些项目可视化为一个不断扩展的星系

  • 在核心,libzmq是 ZeroMQ 核心库。它用 C++ 编写,提供了低级 C API。代码很糟糕,主要是因为它经过了高度优化,但也因为它用 C++ 编写,这是一种容易导致微妙和深层问题的语言。Martin Sustrik 编写了大部分代码。如今,有几十人在维护它的不同部分。

  • 围绕着libzmq,大约有 50 个 绑定。这些是为 ZeroMQ 创建更高级 API 或至少将低级 API 映射到其他语言的独立项目。这些绑定的质量从实验性到绝对惊人不等。可能最令人印象深刻的绑定是 PyZMQ,它是 ZeroMQ 之上最早的社区项目之一。如果你是一个绑定作者,你应该好好研究 PyZMQ,并努力使你的代码和社区同样出色。

  • 许多语言都有多个绑定(至少包括 Erlang、Ruby、C#),由不同的人在不同时期编写,或者采取不同的方法。我们不以任何方式监管这些绑定。没有“官方”绑定。你通过使用其中一个、为其贡献或忽略它来投票。

  • 有一系列对libzmq的重新实现,从 JeroMQ 开始,它是该库的完整 Java 翻译,现在是 NetMQ(一个 C# 栈)的基础。这些原生栈提供相似或相同的 API,并与libzmq.

  • 在绑定的基础上,还有许多使用 ZeroMQ 或基于它的项目。请参阅维基上的“Labs”页面,了解以某种方式使用 ZeroMQ 的项目和原型项目的长列表。有框架、像 Mongrel2 这样的 Web 服务器、像 Majordomo 这样的消息代理,以及像 Storm 这样的企业级开源工具。

Libzmq、大多数绑定以及一些外部项目位于 GitHub 上的 ZeroMQ 社区“组织”中。该组织由一组最资深的绑定作者“管理”。几乎没有什么需要管理的,因为它几乎完全是自我管理的,并且现在零冲突。

iMatix,我的公司,在社区中扮演着特定角色。我们拥有商标,并谨慎地执行它们,以确保如果你下载一个自称为“ZeroMQ”的软件包,你可以相信你得到的东西。人们偶尔会试图劫持这个名字,可能认为“自由软件”意味着没有财产风险,也没有人愿意捍卫它。从本章中你会理解的一点是,我们对待软件背后的流程有多么认真(我说的“我们”是指社区,而不是公司)。iMatix 通过对任何自称为“ZeroMQ”或“ZeroMQ”的东西强制执行这一流程来支持社区。我们还会为软件和打包投入资金和时间,原因我稍后解释。

这不是慈善活动。ZeroMQ 是一个营利性项目,而且利润丰厚。利润广泛分配给所有投资于它的人。道理很简单:花时间成为 ZeroMQ 专家,或者在 ZeroMQ 之上构建有用的东西,你会发现你作为个人、团队或公司的价值正在增长。iMatix 享受着社区中其他人同样的利益。这对除了我们的竞争对手以外的所有人来说都是双赢的,竞争对手发现他们面临着无法击败也无法真正逃脱的威胁。ZeroMQ 主宰着大规模分布式软件的未来世界。

我的公司不仅支持社区,我们还建立了社区。这是一项有意识的工作;在 2007 年的 ZeroMQ 原始白皮书中,有两个项目。一个是技术性的,如何构建更好的消息系统。第二个是如何构建一个能够使软件取得主导性成功的社区。软件会消亡,但社区会生存。

如何构建大型架构 #

有人说(至少大声读出这句话的人是这么说的),构建真正大规模软件有两种方法。第一种选择是向由聪明人组成的帝国投入大量金钱和问题,希望出现的结果不是又一个职业杀手。如果你非常幸运,并且基于丰富的经验构建,团队保持稳定,不追求技术上的辉煌,而且极其幸运,那么它就会奏效。

但用数亿别人的钱去冒险并非人人都能做到。对于我们这些想要构建大规模软件的人来说,还有第二种选择,那就是开源,更具体地说,是自由软件。如果你问软件许可证的选择与你构建的软件规模有什么关系,那问得很好。

杰出且富有远见的 Eben Moglen 曾说过,粗略地讲,自由软件许可证是社区赖以建立的契约。大约十年前,当我听到这句话时,一个想法冒了出来——我们能否有意识地发展自由软件社区

十年后,答案是“是的”,而且这几乎是一门科学。我说“几乎”是因为我们还没有足够的证据表明人们正在有意识地采用一种有文档记录、可重复的流程来做这件事。这正是我试图通过 社会架构 来做的事情。ZeroMQ 出现在 Wikidot、数字标准组织 (Digistan) 和 自由信息基础设施基金会(也称为 FFII,一个反对软件专利的非政府组织)之后。这些都发生在许多不太成功的社区项目(如 Xitami 和 Libero)之后。我在漫长的职业生涯中参与了各种可能形式的项目,主要心得是:如果你想构建真正大规模且持久的软件,目标应该是构建一个自由软件社区。

软件架构心理学 #

Dirkjan Ochtman 指出了 维基百科对软件架构的定义,即“思考系统所需的一系列结构,包括软件元素、它们之间的关系以及两者的属性”。对我来说,这种空洞且循环的术语很好地说明了我们对实际构成成功大规模软件架构的理解是多么微不足道。

架构是为人类使用而构建大型人工结构的艺术和科学。如果说我在构建越来越大的软件系统 30 年中学到并成功应用了一件事,那就是:软件是关于人的。大型结构本身没有意义。重要的是它们如何为人类使用而运转。而在软件中,人类使用始于编写软件本身的程序员。

软件架构中的核心问题是由人类心理而非技术驱动的。我们的心理影响工作的方式有很多。我可以指出,团队似乎随着规模的扩大或必须跨越更远的距离工作时变得更笨。这是否意味着团队越小,效率越高?那么像 ZeroMQ 这样的大型全球社区是如何成功运作的呢?

ZeroMQ 社区并非偶然。这是一个有意识的设计,是我在代码刚从布拉迪斯拉发的一个地下室诞生时的贡献。这个设计基于我钟爱的“社会架构”这门科学,维基百科将其定义为“有意识地设计一个环境,鼓励一系列期望的社会行为,从而实现某个目标或一系列目标。”我更具体地将其定义为“规划、设计和发展在线社区的过程和产物。”

社会架构的一个原则是,我们如何组织我们是谁 更重要。同样的群体,组织方式不同,会产生完全不同的结果。我们就像 ZeroMQ 网络中的对等节点,我们的沟通模式对我们的表现有着巨大的影响。普通人,只要连接良好,其表现可以远远超过采用不良模式的专家团队。如果你是一个更大的 ZeroMQ 应用程序的架构师,你将不得不帮助其他人找到正确的协作模式。做对了,你的项目就能成功。做错了,你的项目就会失败。

两个最重要的心理因素是:我们非常不擅长理解复杂性,以及我们非常擅长通过协作来分治解决大型问题。我们是高度社会化的猿类,有点聪明,但仅限于在合适的群体中。

以下是我列出的软件架构的心理元素清单

  • 愚蠢:我们的思维带宽有限,所以我们在某些时候都会犯傻。架构必须简单易懂。这是头号规则:简单性永远胜过功能性。如果你在寒冷阴沉的周一早晨咖啡前无法理解某个架构,那它就太复杂了。

  • 自私:我们只出于自身利益行事,因此架构必须为有利于整体的自私行为创造空间和机会。自私常常是间接且微妙的。例如,我愿意花几个小时帮助别人理解某个东西,因为这可能在以后为我节省几天的时间。

  • 懒惰:我们做了很多假设,其中许多都是错误的。当我们能够以最少的努力获得结果或快速测试假设时,我们最开心,所以架构必须让这成为可能。具体来说,这意味着它必须简单。

  • 嫉妒:我们嫉妒他人,这意味着我们会克服愚蠢和懒惰,来证明别人是错的,并在竞争中击败他们。因此,架构必须为基于任何人都能理解的公平规则的公开竞争创造空间。

  • 恐惧:我们不愿意冒险,特别是如果这会让我们看起来很笨。对失败的恐惧是人们盲从和随大流集体犯傻的一个主要原因。架构应该使无声的实验变得容易且廉价,给人们成功的机会,同时不对失败进行惩罚。

  • 互惠:我们会付出额外的努力,甚至金钱,来惩罚欺骗行为并执行公平规则。架构应该高度依赖规则,告诉人们如何协作,而不是做什么。

  • 从众:出于恐惧和懒惰,我们最乐意从众,这意味着如果模式良好,解释和文档清晰,并且公平执行,我们自然会每次都选择正确的道路。

  • 自豪:我们强烈意识到自己的社会地位,并且会努力避免在公共场合显得愚蠢或无能。架构必须确保我们所做的每一部分都有我们的名字,这样我们就会彻夜难眠,担心别人会如何评价我们的工作。

  • 贪婪:我们本质上是经济动物(参见自私),因此架构必须为我们提供经济激励,促使我们投入精力去实现它。也许这是为了提升我们作为专家的声誉,也许是真的通过某种技能或组件赚钱。是什么并不重要,但必须有经济激励。把架构看作一个市场,而不是工程设计。

这些策略在大规模和小型范围内都有效,无论是在组织内部还是团队中。

契约的重要性 #

让我讨论一个有争议但重要的领域,那就是选择哪种许可证。我将用“BSD”来涵盖 MIT、X11、BSD、Apache 和类似的许可证,用“GPL”来涵盖 GPLv3、LGPLv3 和 AGPLv3。重要的区别在于是否必须分享任何分叉版本,这可以防止任何实体“俘获”软件,从而保持其“自由”。

软件许可证在技术上并不是一份合同,因为你没有签署任何东西。但广义上讲,称之为契约是有用的,因为它规定了各方的义务,并使其在版权法下具有法律效力,可在法庭上强制执行。

你可能会问,为什么我们做开源需要契约呢?当然,这完全是关于体面、善意、人们出于无私动机共同努力。当然,“少即是多”的原则在这里应该适用?难道更多的规则不意味着更少的自由吗?我们真的需要律师来告诉我们如何一起工作吗?将一套限制性规则强加于自由和开源软件的快乐社区,这似乎是愤世嫉俗甚至是适得其反的。

但关于人性的真相并非那么美好。我们不是天使,也不是魔鬼,只是从十亿年不间断的赢家谱系中继承下来的自利赢家。在商业、婚姻和集体工作中,迟早,我们要么不再关心,要么就会争斗和争吵。

换句话说:集体作品有两种极端结果。要么是失败的、无关紧要的、没有价值的,在这种情况下,每个理智的人都会毫不争执地离开。要么是成功的、相关的、有价值的,在这种情况下,我们就开始争夺权力、控制权,通常还有金钱。

一份写得好的契约的作用在于保护那些宝贵的关系免受冲突。明确预先约定离婚条款的婚姻不太可能以离婚告终。双方同意如何解决各种典型冲突(例如一方窃取对方客户或员工)的商业交易不太可能以冲突告终。

同样,一个拥有明确定义分手条款的写得好的契约的软件项目不太可能以分手告终。另一种选择似乎是将项目融入一个更大的组织,该组织可以对团队施加压力以促使他们合作(否则将失去组织的支持和品牌)。例如,Apache 基金会就是这样运作的。根据我的经验,组织建设有其自身的成本,最终有利于更富有的参与者(他们能够承担那些有时巨大的成本)。

在开源或自由软件项目中,分手通常以分叉的形式出现,社区分裂成两个或多个群体,每个群体对未来有不同的愿景。在项目的“蜜月期”(可能持续数年)内,不会有分手的问题。当项目开始变得有价值,或者主要作者开始倦怠时,善意和慷慨往往会枯竭。

因此,在讨论软件许可证时,无论是你编写的代码还是你使用的代码,带点犬儒主义是有益的。不要问自己“哪种许可证会吸引更多贡献者?”,因为这个问题的答案在于项目的使命宣言和贡献流程。要问自己,“如果这个项目发生一场大争吵,分裂成三派,哪种许可证会拯救我们?”或者,“如果整个团队被一家敌对公司收购,该公司想把代码变成专有产品,哪种许可证会拯救我们?”

长期生存意味着要经历糟糕的时期,也要享受美好的时光。

当 BSD 项目分叉时,它们不容易再次合并。事实上,BSD 项目的单向分叉是相当系统性的:每次 BSD 代码出现在商业项目中,都是这种情况。然而,当 GPL 项目分叉时,重新合并是微不足道的。

GPL 的故事在这里很贴切。尽管在 20 世纪 80 年代,程序员社区公开分享代码已经很普遍,但他们倾向于使用最简单的许可证,只要没有真正的金钱牵涉其中就没问题。有一个重要的语言栈叫做 Emacs,最初由 Richard Stallman 用 Lisp 编写。另一位程序员 James Gosling(后来发明了 Java)在许多贡献者的帮助下,假定 Emacs 将是开源的,用 C 重写了它。Stallman 获得了这些代码,并将其作为他自己的 C 版本的构建基础。然后 Gosling 将代码卖给了一家公司,这家公司转头就阻止任何人分发竞争产品。Stallman 认为这种出售共同作品的行为是极不道德的,于是开始开发一种可复用的许可证来保护社区免受侵害。

最终出现的是 GNU 通用公共许可证,它利用传统版权来强制可混合性。这是一个巧妙的技巧,并传播到其他领域,例如摄影和音乐领域的知识共享许可(Creative Commons)。2007 年,我们看到了该许可证的第三版,这是对微软和其他公司迟来的攻击的回应。它已经成为一份冗长而复杂的文件,但企业版权律师已经熟悉它,根据我的经验,只要界限明确,很少有公司介意使用 GPL 软件和库。

因此,一份好的契约——我认为现代 GPL 是最适合软件的——让程序员无需前期协议、组织或基于体面和善意的假设即可协作。它降低了协作成本,并将冲突转化为健康的竞争。GPL 不仅定义了分叉会发生什么,它还积极鼓励将分叉作为实验和学习的工具。对于“更自由”的许可证项目来说,分叉可能导致项目消亡,而 GPL 项目则因分叉而蓬勃发展,因为成功的实验可以根据契约重新融入主流。

是的,有很多蓬勃发展的 BSD 项目,也有很多消亡的 GPL 项目。一概而论总是错误的。一个项目能否蓬勃发展或消亡有很多原因。然而,在竞争激烈的运动中,你需要每一个优势。

BSD 与 GPL 之争的另一个重要部分是我所说的“泄漏”,就像往一个底部有小但真实的孔的锅里倒水一样。

来吃我吧 #

这里有一个故事。它发生在我一个工作同事的朋友的表弟的大舅子身上。他的名字曾经是,现在仍然是,帕特里克。

帕特里克是一位计算机科学家,拥有高级网络拓扑学的博士学位。他花了两年时间和全部积蓄开发一个新产品,并选择了 BSD 许可证,因为他相信这样能获得更多的采用。他在阁楼里工作,付出了巨大的个人代价,并自豪地发布了他的作品。人们鼓掌叫好,因为他的作品确实太棒了,他的邮件列表很快就热闹起来,充满了活动、补丁和愉快的讨论。许多公司告诉他,他们的工作因他的作品而节省了数百万美元。其中一些公司甚至付钱请他提供咨询和培训。他受邀在会议上发表演讲,并开始收集带有他名字的徽章。他创办了一家小公司,雇了一个朋友和他一起工作,梦想着做大。

然后有一天,有人向他指出了一个采用 GPL 许可证的新项目,该项目分叉了他的作品并在其基础上进行改进。他既生气又沮丧,问那些人——竟然还是开源界的同行!——怎么能如此无耻地剽窃他的代码。邮件列表上就将他们的 BSD 代码重新授权为 GPL 代码是否合法的问题进行了长时间的争论。结果证明,那是合法的。他试图忽略这个新项目,但很快他就意识到,来自那个项目的新补丁竟然无法合并回他的作品!

更糟的是,这个 GPL 项目越来越受欢迎,他的一些核心贡献者先是贡献了小的补丁,然后是更大的补丁。同样,他无法使用这些修改,他感到被抛弃了。帕特里克陷入了抑郁,他的女朋友离开了他,去跟一个奇怪地叫做 Patrice 的国际货币交易员在一起,他停止了项目上的所有工作。他感到被背叛了,彻底崩溃。他解雇了他的朋友,朋友很不爽,告诉所有人帕特里克是一个偷偷弹班卓琴的人。最终,帕特里克在一家云公司找到一份项目经理的工作,到了四十岁,他甚至连编程的乐趣都没有了。

可怜的帕特里克。我差点为他感到难过。然后我问他:“你为什么不选择 GPL 呢?”“因为它是一个限制性的病毒式许可证,”他回答道。我告诉他:“你可能有博士学位,你也可能是我的同事朋友的表弟的大舅子,但你是个白痴,莫妮克离开你是明智的。你发布你的作品,邀请人们随意窃取你的代码,只要他们在最终作品中保留‘请窃取我的代码’这句话,”当人们真的这么做时,你却不开心。更糟的是,你是个伪君子,因为当他们偷偷做时你很高兴,但当他们公开做时,你却感到被背叛。”

看到你的辛勤工作被一个更聪明的团队“俘获”然后用来对付你,这是非常痛苦的,那么为什么还要让这种事情发生呢?每一个使用 BSD 代码的专有项目都在“俘获”它。公开的 GPL 分叉也许更丢脸,但这完全是咎由自取。

BSD 就像食物。它字面意义上(我是打个比方)用那种你想象中一块奶酪坐在世界上最好的啤酒(当然是欧维尔啤酒,由一个古老且几乎灭绝的沉默比利时修道院修士团体,名为 Les Gars Labas Qui Fabrique l’Orval 酿造)的空瓶子旁边时可能会发出的小声音,低语着“来吃我吧”。BSD 许可证,就像它的近亲 MIT/X11 一样,是由一个没有盈利动机的大学(伯克利)专门设计用来泄露工作和努力的。这是一种以低于成本价的方式推广补贴技术,倾销廉价代码,希望能打破其他公司的市场。BSD 是一种出色的战略工具,但仅适用于资金雄厚的大型机构,能够负担得起使用第一种选择。Apache 许可证就是穿上西装的 BSD。

对于我们这些像珍惜子弹一样瞄准投资的小企业来说,工作和努力的流失是不可接受的。打破市场很好,但我们承担不起补贴竞争对手的费用。BSD 网络栈最终将 Windows 带上了互联网。我们不能承担与那些本应是盟友的人作战的代价。我们不能承担犯下根本性商业错误的代价,因为最终,这意味着我们不得不解雇员工。

归根结底是行为经济学和博弈论。我们选择的许可证改变了使用我们作品的人们的经济状况。在软件行业,有朋友、敌人和食物。BSD 让大多数人把我们看作午餐。闭源软件让大多数人把我们看作敌人(你喜欢为软件付费吗?)然而,GPL 让大多数人,除了世界上那些帕特里克们之外,成为我们的盟友。ZeroMQ 的任何分叉都与 ZeroMQ 许可证兼容,以至于我们鼓励分叉作为实验的宝贵工具。是的,看到有人试图带着球跑掉可能会觉得奇怪,但秘密在于,我随时都能把它拿回来。

流程 #

如果你目前为止都接受了我的论点,那太好了!现在,我将解释我们实际构建开源社区的粗略过程。这就是我们如何构建、发展或温和地引导 ZeroMQ 社区形成的方式。

作为社区领导者,你的目标是激励人们出去探索;确保他们能够安全地进行,而不打扰他人;在他们取得成功发现时给予奖励;并确保他们与其他人分享他们的知识(不是因为我们要求他们,不是因为他们慷慨,而是因为这是法律)。

这是一个迭代过程。你以自己的成本,但在公众视野中,制作一个小型产品。然后围绕该产品建立一个小型社区。如果你获得了虽小但真实的成功,社区随后会帮助设计和构建下一个版本,并不断壮大。然后那个社区构建下一个版本,以此类推。显然,你仍然是社区的一部分,甚至可能是主要的贡献者,但你越想对物质成果施加控制,人们就越不愿意参与。在有人决定你是你的下一个问题之前,好好规划你的退休。

疯狂、美丽且简单 #

你需要一个疯狂且足够简单的目标,才能让人们早上愿意起床。你的社区必须吸引最优秀的人,这需要一些特别的东西。对于 ZeroMQ,我们曾说我们要打造“史上最快的消息系统”(“the Fastest. Messaging. Ever.”),这算是一个好的激励因素。如果我们说,我们要打造一个“智能传输层,可以在企业内部廉价灵活地连接你的活动部件”,那我们就会失败。

其次,你的作品必须是美丽的,立即可用且有吸引力。你的贡献者是希望在现有基础上稍作探索的用户。把它做得简单、优雅、极其干净。当人们运行或使用你的作品时,应该是一种情感体验。他们应该感觉到一些东西,如果你能准确地解决哪怕只是一个他们之前没有完全意识到的重大问题,你就会获得他们灵魂的一小部分。

它必须易于理解、使用和加入。太多项目存在访问障碍:设身处地为你以外的人着想,看看他们访问你网站时会想到的所有理由,想着“嗯,有趣的项目,但是……”然后离开。你希望他们留下来尝试一下,哪怕只有一次。使用 GitHub,并将问题追踪器直接放在那里。

如果你把这些事情做好,你的社区将会很聪明,但更重要的是,它将在知识和地理上多样化。这真的非常重要。一群志同道合的专家无法很好地探索问题空间。他们往往会犯下大错。任何时候,多样性都胜过教育。

陌生人,认识陌生人 #

两个人需要多少预先的协议才能合作完成某件事?在大多数组织中,需要很多。但你可以将这个成本降至接近零,这样人们就可以在从未见过面、没有电话会议、会议或商务旅行的情况下进行协作,无需讨论角色和职责,也无需喝太多廉价的韩国米酒。

你需要像我这样愤世嫉俗的人设计的、精心编写的规则,这些规则旨在强制陌生人进行互利合作而不是冲突。GPL 是一个好的开始。GitHub 及其 Fork/Merge 策略是一个好的跟进。然后你需要像我们的 C4 规则手册 这样的东西来控制实际工作如何进行。

C4(我现在用于每个新的开源项目)为人们常犯的许多错误提供了详细且经过验证的答案,比如为了“更快”而在角落里离线工作这种罪过。透明度对于获得信任至关重要,而信任对于扩大规模至关重要。通过强制所有更改都经过一个单一的透明流程,你就能在结果中建立真正的信任。

许多开源开发者犯的另一个首要错误是把自己置于他人之上。“我创建了这个项目,因此我的智力优于其他人”。这不仅不谦虚、粗鲁,而且通常不准确,这还是糟糕的商业行为。规则必须平等适用于所有人,无任何区别。你是社区的一部分。作为项目的创始人,你的工作不是将你对产品的愿景强加于人,而是确保规则是良好、诚实且被执行的。

无限财产 #

知识商业中最悲哀的神话之一是,想法是一种合理的财产形式。这是应该随着奴隶制一起被抛弃的中世纪胡说八道,但遗憾的是,它仍然让太多有权势的人赚了太多钱。

想法很廉价。真正可以作为财产的是我们在构建市场过程中付出的辛勤工作。“靠自己争取”是鼓励人们努力工作的正确模式。无论是项目中的道德权威、咨询收入,还是将商标出售给大型富裕公司:如果你创造了它,你就拥有它。但你真正拥有的是“人流量”,即你项目的参与者,这最终决定了你的力量。

要做到这一点需要无限的自由空间。幸运的是,GitHub 为我们解决了这个问题,对此我将感恩地死去(生活中有很多值得感恩的事,我不会在这里列出来,因为我们只剩下大约一百页了,但这其中一件)。

你无法像扩展许多小型项目(每个项目拥有更少的拥有者)那样扩展一个拥有许多拥有者的单一项目。当我们拥抱 Fork 时,一个人只需点击一下就可以成为“拥有者”。现在他们只需要通过展示自己的独特价值来说服他人加入。

因此,在 ZeroMQ 中,我们致力于使在核心库之上编写绑定变得容易,并且我们不再尝试自己编写这些绑定。这为其他人创造了空间来做这些事情,成为它们的拥有者,并获得相应的认可。

照料与喂养 #

我希望社区能百分之百地自我引导,也许有一天这会实现,但今天还不是。ZeroMQ 已经非常接近,但根据我的经验,社区需要四种类型的照料和喂养:

  • 首先,仅仅因为大多数人太好心,我们需要某种象征性的领导或所有者,以便在发生冲突时提供最终权威。通常是社区的创始人。我见过由自选的“长者”团体来做,但老人家喜欢说很多话。我见过社区因为“谁负责?”的问题而分裂,而设立带有董事会等法律实体似乎只会让关于控制权的争论变得更糟,而不是更好。也许是因为似乎有更多可以争夺的东西。自由软件的真正好处之一是它总是可以被重混(remixable),所以与其争夺一个馅饼,不如直接 Fork 馅饼。

  • 其次,社区需要鲜活的规则,因此他们需要一位能够制定和写下这些规则的律师。规则至关重要;如果做得好,它们会减少摩擦。如果做错了,或被忽视,我们就会看到真实的摩擦和争论,这可能会驱赶走那些友好的多数派,留下那些爱争论的核心人物来掌控燃烧的房子。我在 ZeroMQ 和之前的社区中一直尝试做的一件事是创建可重用的规则,这或许意味着我们对律师的需求没那么大。

  • 第三,社区需要某种形式的资金支持。这是击沉大多数船只的锯齿状岩石。如果你让一个社区挨饿,它会变得更有创意,但核心贡献者会筋疲力尽。如果你投入过多资金,你就会吸引那些永远不会说“不”的专业人士,社区就会失去多样性和创造力。如果你创建一个基金供人们分享,他们会为此争吵(激烈地)。对于 ZeroMQ,我们(iMatix)花费时间和金钱在市场营销和打包上(比如这本书),以及基本的照料上,比如 bug 修复、发布和网站。

  • 最后,销售和商业中介也很重要。专家贡献者和客户之间存在天然的市场,但双方在互相交流方面都有些能力不足。客户认为支持是免费或非常便宜的,因为软件是免费的。贡献者羞于为其工作要求合理报酬。这造成了一个困难的市场。我本人和我的公司越来越多的工作和利润来源仅仅是将 ZeroMQ 用户(需要帮助)与社区中的专家(能够提供帮助)连接起来,并确保双方都对结果满意。

我见过一些由才华横溢的人组成的、拥有崇高目标的社区走向衰亡,就因为创始人在这四点中的部分或全部做错了。核心问题在于,你不能指望任何一家公司、任何一个人或任何一个团体始终提供出色的领导。今天有效的方法明天往往会失效,然而随着时间的推移,结构却变得更僵化,而不是更灵活。

我能找到的最好答案是两者的结合。一是 GPL 及其可重混性的保证。无论权力有多糟糕,无论他们如何试图私有化和攫取社区的工作成果,如果它是 GPL 许可的,那份工作成果就可以离开并寻找更好的权力机构。在你未加思索地说“所有开源都提供这个”之前,好好想想。我可以通过雇佣核心贡献者但不发布任何新补丁来扼杀一个 BSD 许可的项目。但即使投入十亿美元,我也不能扼杀一个 GPL 许可的项目。二是哲学的无政府主义者模型中的权威,即我们选择它,它不拥有我们。

ZeroMQ 流程:C4 #

当我们说到 ZeroMQ 时,有时我们指的是libzmq,即核心库。2012 年初,我们综合了libzmq流程,形成了一个正式的协作协议,我们称之为 集体代码构建合同,或 C4。你可以将其视为 GPL 之上的一个层次。这些是我们的规则,我将解释每条规则背后的原因。

C4 是 GitHub Fork + Pull 模型 的演进。你可能会觉得我是 git 和 GitHub 的粉丝。这是准确的:在过去几年里,这两个工具对我们的工作产生了如此积极的影响,尤其是在构建社区方面。

语言 #

本文档中的关键词“必须”(MUST)、“禁止”(MUST NOT)、“需要”(REQUIRED)、“应该”(SHALL)、“不应”(SHALL NOT)、“宜”(SHOULD)、“不宜”(SHOULD NOT)、“推荐”(RECOMMENDED)、“可以”(MAY)和“可选”(OPTIONAL)应按 RFC 2119 中所述进行解释。

通过采用 RFC 2119 语言,C4 文本非常清楚地表明其意图是作为一种协议而非随机写就的一系列建议。协议是各方之间定义权利和义务的合同。这些各方可以是网络中的对等方,也可以是在同一项目中工作的陌生人。

我认为 C4 是首次尝试将社区规则手册编纂成正式且可重用的协议规范。以前,我们的规则分散在几个 wiki 页面上,并且在许多方面都非常特定于libzmq在许多方面。但经验告诉我们,规则越正式、越准确、越可重用,陌生人就越容易预先协作。摩擦越少意味着社区的可伸缩性越强。在制定 C4 时,我们在libzmq项目中对于我们究竟使用的是何种流程也存在一些分歧。并非所有人都觉得自己受同一规则约束。这么说吧,有些人觉得自己有特殊地位,这与社区其他人产生了摩擦。因此,编纂规则使事情变得清晰。

使用 C4 很简单:只需将你的项目托管在 GitHub 上,找一个人加入,然后开放 pull requests。在你的 README 中,放一个指向 C4 的链接,就这么简单。我们在很多项目中都这么做了,而且似乎奏效了。将这些规则应用于我自己的工作,比如 CZMQ,有好几次让我惊喜不已。我们没有人能出色到可以独自工作。

目标 #

C4 旨在为开源软件项目提供一个可重用的、最优的协作模型。

制定 C4 的短期原因是为了结束关于libzmq贡献流程的争论。持不同意见者去了别处。ZeroMQ 社区 如我所预料的那样平稳而轻松地蓬勃发展。大多数人都感到惊讶,但也心满意足。除了它的分支策略之外,C4 没有受到真正的批评,这一点稍后会讨论,因为它值得单独讨论。

我之所以回顾历史,是有原因的:作为社区的创始人,你正在要求人们投资于你的财产、商标和品牌。作为回报,而这正是我们在 ZeroMQ 中所做的,你可以使用该品牌来设定质量标准。当你下载标有“ZeroMQ”字样的产品时,你知道它是按照一定标准生产的。这是质量的基本规则:写下你的流程;否则你就无法改进它。我们的流程并不完美,也永远不可能完美。但其中的任何缺陷都可以修复和测试。

因此,让 C4 可重用非常重要。要了解最佳流程,我们需要从最广泛的项目中获取结果。

它有以下特定目标:通过减少新贡献者的摩擦并创建具有强大积极反馈的扩展参与模型,最大化项目社区的规模;

首要目标是社区的规模和健康——而不是技术质量、利润、性能或市场份额。目标仅仅是为项目做出贡献的人数。这里的科学很简单:社区越大,结果越准确。

通过分离不同的技能集,从而在任何所需领域拥有更大的能力库,减轻对关键个体的依赖;

也许我们面临的最严重问题是libzmq是对能够理解代码、管理 GitHub 分支并进行干净发布的人的依赖——所有这些都需要同时具备。这就像寻找既能跑马拉松、又能冲刺、游泳、还能举重的运动员。我们人类非常擅长专业化。要求我们在两件相互矛盾的事情上都非常擅长,会极大地减少候选人数,这对任何项目来说都是一件坏事。我们在libzmq在 2009 年左右严重存在这个问题,并通过将维护者角色分为两个来解决:一个人负责制作补丁,另一个人负责发布。

通过增加决策过程的多样性,使项目开发得更快、更准确;

这是理论——尚未完全证明,但也未被证伪。社区的多样性以及能够参与讨论的人数(不怕被批评或驳回),决定了软件开发的速度和准确性。这里的速度相当主观。朝着错误的方向飞速前进不仅无用,而且具有积极的破坏性(我们在libzmq在切换到 C4 之前遭受了很多)。

通过允许安全的实验、快速的失败和稳定代码的隔离,支持项目版本从实验阶段到稳定阶段的自然生命周期;

说实话,这个目标似乎正变得无关紧要。这是流程的一个相当有趣的效应:git 的主分支几乎总是完全稳定的。这与更改的大小及其延迟有关,即编写代码到完全使用代码之间的时间。然而,人们仍然期望“稳定”版本,所以我们会暂时保留这个目标。

减少项目仓库的内部复杂性,从而使贡献者更容易参与,并减少出错的范围;

有趣的观察:那些在复杂环境中茁壮成长的人喜欢制造复杂性,因为这能保持他们的高价值。这就是眼镜蛇效应(谷歌一下)。Git 让分支变得容易,却给我们带来了“一旦你理解 git 分支只是一个折叠的五维轻子空间,它有一个独立的、没有中间缓存的历史,那么 git 就很容易了”这种太常见的综合征。开发者不应该因为他们的工具而觉得自己愚蠢。我见过太多顶尖开发者被仓库结构搞糊涂,无法接受关于 git 分支的传统观点。亲爱的读者,我们很快就会回来处理 git 分支问题。

强制推行项目的集体所有权,从而增加贡献者的经济激励,并降低被敌对实体劫持的风险。

最终,我们是经济动物,而“我们拥有这一切,我们的工作永远不会被用来对付我们”的感觉让人们更容易投入到像 ZeroMQ 这样的开源项目中。而且这不能仅仅是一种感觉,它必须是真实的。实现集体所有权有几个方面,我们将随着 C4 的讲解逐一探讨。

前提 #

项目*应该*使用 git 分布式版本控制系统。

Git 有它的缺点。它的命令行 API 极其不一致,而且它有一个复杂、混乱的内部模型,稍微一激怒就会在你面前显露无遗。但尽管 git 竭尽全力让用户觉得自己愚蠢,它在自己的工作上做得非常好。更实际地说,我发现如果你避开某些区域(分支!),人们会很快学会 git,并且不会犯很多错误。这对我很有效。

项目*应该*托管在 github.com 或同等平台上,此处称之为“平台”。

我确信有一天某个大公司会收购 GitHub 并将其破坏,然后另一个平台会取而代之。在此之前,GitHub 提供了一套近乎完美的最小、快速、简单的工具。我让数百人使用它,他们都像苍蝇粘在蜂蜜碟里一样离不开它。

项目*应该*使用平台的问题追踪器。

我们在libzmq中犯了转向 Jira 的错误,因为当时我们还没有学会如何正确使用 GitHub 问题追踪器。Jira 是如何将有用之物变成复杂烂摊子的绝佳例子,因为它依赖于销售更多“功能”。但即使不批评 Jira,将问题追踪器保留在同一平台上意味着要学习的用户界面更少,登录次数更少,并且问题与补丁之间的集成更顺畅。

项目*宜*有清晰的代码风格文档指南。

这只是常识。我花时间整理过其他人的补丁,就因为他们坚持要把

“贡献者”是指希望提供补丁(即一组提交,解决某个明确定义的问题)的人。“维护者”是指将补丁合并到项目中的人。维护者不是开发者;他们的工作是执行流程。

现在我们继续定义各方,并进行角色划分,这使我们摆脱了对稀有个体产生结构性依赖的罪过。这在libzmq中效果很好,但正如你将看到的那样,它取决于流程的其余部分。C4 不是自助餐;你需要整个流程(或非常相似的东西),否则它就无法维系。

贡献者*不应*拥有仓库的提交权限,除非他们同时也是维护者。维护者*应该*拥有仓库的提交权限。

我们想要避免的是人们直接将他们的更改推送到主分支。这在历史上是libzmq中最大的麻烦来源:大量的原始代码,需要几个月甚至几年才能完全稳定下来。我们最终效仿了其他 ZeroMQ 项目,如 PyZMQ,使用 pull requests。我们做得更彻底,规定所有更改都必须遵循相同的路径。对“特殊人物”没有任何例外。

所有人,无论区别或歧视,*应该*有平等的权利根据本合同的条款成为贡献者。

我们必须明确声明这一点。以前,libzmq的维护者会仅仅因为不喜欢补丁而拒绝它们。(尽管libzmq并非由任何一个人所写),但让我们记住我们的目标是创造一个尽可能多的人拥有的作品。说“我不喜欢你的补丁,所以我拒绝它”等同于说“我声称拥有这个,我认为我比你优秀,我不信任你”。对于那些正在考虑成为你的共同投资者的人来说,这些都是有毒的信息。

我认为个人专业知识与集体智能之间的这场斗争也在其他领域上演。它定义了维基百科,并且在它超越小团体专家构建的一切之后,十年过去了仍然如此。对我来说,我们通过缓慢综合最准确的知识来构建软件,就像我们撰写维基百科文章一样。

许可与所有权 #

项目*应该*使用 GPLv3 或其变种(LGPL, AGPL)。

我已经解释了完全的可重混性如何创造更好的规模,以及 GPL 及其变种为何似乎是可重混软件的最佳合同。如果你是一家大型企业,旨在向市场倾倒代码,你不会想要 C4,但那样你也不会真正关心社区。

对项目源代码的所有贡献(“补丁”)*应该*使用与项目相同的许可证。

这消除了对补丁的任何特定许可证或贡献协议的需要。你 Fork GPL 代码,在 GitHub 上发布你的重混版本,然后你或其他人就可以将其作为补丁提交给原始代码。BSD 不允许这样做。任何包含 BSD 代码的作品也可能包含未授权的专有代码,因此在你可以重混它之前,你需要代码作者的明确行动。

所有补丁归其作者所有。*不应*有任何版权转让流程。

这里我们来到了人们信任他们在 ZeroMQ 投资的关键原因:通过购买版权来创建 ZeroMQ 的闭源竞争对手,从物流上讲是不可能的。iMatix 也做不到。发送补丁的人越多,这变得越困难。ZeroMQ 不仅今天免费开源——这条具体规则意味着它将永远如此。请注意,并非所有 GPL 项目都如此,其中许多仍然要求将版权转让给维护者。

项目*应该*由其所有贡献者集体拥有。

这或许有些多余,但值得一提:如果每个人都拥有自己的补丁,那么由此产生的整体也由每个贡献者拥有。没有拥有代码行的法律概念:“作品”至少是一个源文件。

每位贡献者*应该*负责在项目贡献者列表中表明身份。

换句话说,维护者不是业力会计师。任何想要获得认可的人都必须自己去争取。

补丁要求 #

在本节中,我们旨在定义贡献者的义务:具体来说,什么构成“有效”补丁,以便维护者有规则可用来接受或拒绝补丁。

维护者和贡献者*必须*拥有平台账户,并且*宜*使用他们的真实姓名或众所周知的别名。

在最坏的情况下,如果有人提交了有毒代码(已申请专利或属于他人所有),我们需要能够追溯到是谁以及何时提交的,以便我们可以移除代码。要求真实姓名或众所周知的别名是一种理论上的策略,旨在降低提交虚假补丁的风险。我们不知道这是否实际有效,因为我们还没有遇到过这个问题。

补丁*宜*是对一个明确识别和同意的问题的最小且准确的回答。

这实现了我将在本章后面提到的“面向简单性设计”流程。一个清晰的问题,一个最小的解决方案,应用,测试,重复。

如果项目定义了代码风格指南,补丁*必须*遵守这些指南。

这只是常识。我花时间整理过其他人的补丁,就因为他们坚持要把else放在if的旁边,而不是像自然规律要求的那样放在正下方。一致的代码更健康。

补丁*必须*遵守下文定义的“公共契约演进”指南。

啊,痛苦,太痛苦了。我说的不是我八岁时不小心踩到一块有四英寸钉子的木板的经历。那相对还好。我说的是 2010-2011 年,当时 ZeroMQ 有多个并行版本发布,每个版本的 API 或线协议都不兼容。那是一场关于糟糕规则、毫无意义的执行的演习,至今仍让我们痛苦。当时的规则是:“如果你改变 API 或协议,你*应该*创建一个新的主版本”。宁可让我脚上扎钉子;那没那么疼。

我们在 C4 中做的一大改变就是简单地完全禁止这种被认可的破坏行为。令人惊讶的是,这甚至不难。我们只是不允许破坏现有的公共契约,句号,除非所有人同意,那样就没有句号。正如 Linus Torvalds 在 2012 年 12 月 23 日那句名言:“我们不破坏用户空间!”

补丁*不应*包含来自其他项目的非重要代码,除非贡献者是该代码的原始作者。

这条规则有两个效果。首先,它强制人们制作最小化的解决方案,因为他们不能简单地导入大段现有代码。在我看到这种情况发生在项目上的例子中,除非导入的代码非常清晰地分离,否则总是糟糕的。其次,它避免了许可争论。你编写补丁,你被允许将其以 LGPL 发布,然后我们可以将其合并回来。但如果你在网上找到一个 200 行的代码片段,并试图粘贴它,我们会拒绝。

补丁*必须*在至少主要目标平台上干净地编译并通过项目自测。

对于跨平台项目,要求补丁在贡献者使用的开发机器上工作是公平的。

补丁提交信息*宜*包含一行简短(少于 50 个字符)的概述更改的行,可选地后接一个空行,然后再是更详细的描述。

这是一种适合电子邮件(第一行成为主题,其余成为邮件正文)的提交信息格式。

“正确补丁”是指满足上述要求的补丁。

只是为了防止不清楚,我们回到法律术语和定义。

开发流程 #

在本节中,我们旨在逐步描述实际的开发流程。

项目的变更*应该*遵循准确识别问题并应用最小、准确解决方案的模式。

这是三十年软件设计经验的毫不歉意的推行。这是一种极其简单的设计方法:为实际问题创建最小、准确的解决方案,不多不少。在 ZeroMQ 中,我们没有功能需求。将新功能与错误同等对待会让一些新来者感到困惑。但这种流程奏效了,不仅仅在开源领域。在每一次变更中明确我们正在努力解决的问题,是决定该变更是否值得进行的关键。

要发起变更,用户*应该*在项目平台的 issue tracker 上记录一个 issue。

这旨在阻止我们离线工作或独自一人或与他人一起在“贫民窟”工作。尽管我们倾向于接受有明确论证的 pull requests,但这条规则允许我们对混淆不清或过大的补丁说“停止”。

用户*宜*通过描述他们面临或观察到的问题来编写 issue。

“问题:我们需要功能 X。解决方案:实现它”不是一个好的 issue。“问题:用户无法通过复杂的工作区来执行常见任务 A 或 B。解决方案:实现功能 X”是一个不错的解释。因为我合作过的每个人都需要学习这一点,所以似乎值得重申:首先记录实际问题,其次是解决方案。

用户*宜*就其观察的准确性以及解决问题的价值寻求共识。

而且,由于许多表面上的问题是虚幻的,通过明确阐述问题,我们给别人机会纠正我们的逻辑。“你之所以大量使用 A 和 B,是因为函数 C 不可靠。解决方案:让函数 C 正常工作。”

用户*不应*记录功能需求、想法、建议或任何未明确记录和证明的问题的解决方案。

不记录想法、建议或功能需求有几个原因。根据我们的经验,这些只会积聚在 issue tracker 中,直到有人将其删除。但更深层的原因是,当我们把所有变更都视为问题解决方案时,我们就能轻而易举地确定优先级。要么问题真实存在,有人想立即解决它,要么它就不在讨论范围内。因此,愿望清单被排除在外。

因此,项目的发布历史必须是已记录和解决的有意义问题的列表。

我希望 GitHub 问题追踪器能简单地列出每个版本中我们解决的所有问题。如今,我们仍然不得不手工编写。如果在每次提交中包含问题编号,并且使用 GitHub 问题追踪器(可惜 ZeroMQ 还没有这样做),那么发布历史就可以更容易地机械生成。

要处理一个问题,贡献者必须派生项目仓库,然后在他们派生出的仓库中工作。

在此,我们解释 GitHub 的派生 + 拉取请求模式,以便新手只需学习一个流程(C4)即可做出贡献。

要提交补丁,贡献者必须向项目创建一个平台拉取请求。

GitHub 让这一切变得如此简单,以至于我们无需学习 git 命令就能做到,对此我深感感激。有时,我会告诉那些我不太喜欢的人,命令行 git 非常棒,他们只需要在使用它进行实际工作之前详细了解 git 的内部模型。几个月后我再见到他们时,他们看起来……变了。

贡献者不得直接向项目提交更改。

任何提交补丁的人都是贡献者,所有贡献者都遵守相同的规则。原始作者没有特殊权限,否则我们不是在建设社区,而只是在抬高自我。

要讨论补丁,人们可以在平台拉取请求、提交或其它地方发表评论。

如果您是第一次接触,讨论分散在各处可能会令人困惑,但 GitHub 通过向需要关注情况的人发送电子邮件来解决这个问题,惠及所有现有参与者。我们在 Wikidot 中也有过同样的经历和解决方案,它很有效。没有证据表明在不同地方讨论会产生任何负面影响。

要接受或拒绝补丁,维护者必须使用平台界面。

通过 GitHub Web 用户界面工作意味着拉取请求会被记录为问题,带有工作流程和讨论。我确信有更复杂的工作方式。复杂很容易;令人难以置信的困难在于简洁。

维护者不得接受自己的补丁。

许多年前我们在 FFII 中定义了一条规则来防止人们倦怠:任何项目上至少有两个人。单人项目往往以泪水告终,或者至少是痛苦的沉默。我们有很多关于倦怠、倦怠发生原因以及如何预防(甚至治愈)的数据。我将在本章后面探讨这一点,因为如果您从事或参与开源工作,您需要意识到风险。“不合并自己的补丁”规则有两个目标。首先,如果您的项目想要获得 C4 认证,您必须找到至少另一个人的帮助。如果没有人愿意帮助您,也许您需要重新思考您的项目。其次,对每个补丁进行控制会让人更加满意,让我们更专注,并防止我们在匆忙或只是偷懒时违反规则。

维护者不得对正确的补丁进行价值判断。

我们之前已经说过,但这值得重复:维护者的角色不是判断补丁的实质内容,而只是其技术质量。补丁的实质价值只会随着时间显现:人们使用它并喜欢它,或者不喜欢。如果没人使用某个补丁,最终它会惹恼其他人,他们会将其移除,而没有人会抱怨。

维护者必须迅速合并正确的补丁。

有一个我称之为变更延迟的标准,它是从识别问题到测试解决方案的往返时间。越快越好。如果维护者不能像人们期望的那样迅速响应拉取请求,那他们就没有做好本职工作(或者他们需要更多人手)。

贡献者在为问题创建拉取请求后,可以将问题标记为“Ready”。

默认情况下,GitHub 提供各种常见的问题类型,但在 C4 中我们不使用它们。相反,我们只需要两个标签:“Urgent”和“Ready”。想要让其他用户测试某个问题的贡献者可以将其标记为“Ready”。

创建问题的用户在检查补丁成功后,应当关闭问题。

当一个人开启一个问题,而另一个人着手处理时,最好让原始创建者来关闭该问题。这可以作为对问题是否得到妥善解决的双重检查。

维护者应当要求改进不正确的补丁,如果贡献者没有建设性地回应,则应当拒绝不正确的补丁。

最初,我认为无论补丁质量多差都值得合并。这其中带有一点“挑衅”的成分。我认为接受即使是明显虚假的补丁,也能吸引更多贡献者。但人们对此感到不适,所以我们定义了“正确补丁”的规则,以及维护者在检查质量方面的角色。从负面来看,我认为我们没有冒一些有趣的风险,这些风险本可以带来更多参与者。从正面来看,这导致了libzmq主分支(以及所有使用 C4 的项目)几乎在任何时候都达到了实际生产级质量。

任何对正确补丁有价值判断的贡献者,都应当通过他们自己的补丁来表达。

实质上,这里的目标是让用户尝试补丁,而不是花时间争论优缺点。创建补丁很容易,用另一个补丁恢复它也同样容易。你可能认为这会导致“补丁战争”,但这并未发生。我们曾有过少数几个案例,在libzmq其中一个贡献者的补丁被另一个觉得实验方向不对的人推翻了。这比寻求预先共识要容易。

维护者可以直接向项目提交非源代码文档的更改。

这一例外允许维护者在编写发布说明时直接提交,而无需创建问题(这会影响发布说明),从而对时空结构造成压力,并可能导致在第四维度中非自愿地倒退到冰镇啤酒发明之前。想想都发抖。更简单的方法是同意发布说明在技术上并非软件。

创建稳定版本 #

我们希望为生产系统提供一定的稳定性保证。过去,这意味着获取不稳定的代码,然后花费数月时间消除其中的错误和故障,直到可以安全地信任。iMatix 的工作多年来一直是这样对待libzmq,通过只允许错误修复而不引入新代码到“稳定化分支”,将原始代码转化为软件包。令人惊讶的是,这并不像听起来那样吃力不讨好。

现在,自从我们全力推行 C4 以来,我们发现 git 主分支的libzmq大部分时候都几乎是完美的。这解放了我们的时间去做更有趣的事情,例如在libzmq之上构建新的开源层。然而,人们仍然想要那种保证:许多用户只会从“官方”版本安装。因此,今天的稳定版本意味着两件事。首先,是在一段时间内没有新的更改且没有重大未解决错误时,主分支的一个快照。其次,是一种可以微调该快照以修复其中剩余的关键问题的方法。

这就是我们在本节中解释的过程。

项目必须有一个分支(“主分支”)始终包含最新的开发中版本,并且应当始终能够构建。

这有些冗余,因为每个补丁都能构建,但这值得重申。如果主分支不能构建(并且通过其测试),就需要有人赶紧处理。

项目不得出于任何原因使用主题分支(topic branches)。个人派生仓库可以使用主题分支。

我稍后会谈到分支。简而言之(或者网络上常说的“tl;dr”,太长不看),分支会使仓库过于复杂和脆弱,并且需要预先达成一致,所有这些都是昂贵且可以避免的。

要制作稳定版本,某人必须通过复制仓库来派生它,从而成为该仓库的维护者。为了稳定化而派生项目可以单方面进行,无需项目维护者的同意。

它是自由软件。没有人可以垄断它。如果你认为维护者没有正确地制作稳定版本,那就派生仓库自己来做。派生不是失败,它是竞争必不可少的工具。用分支无法做到这一点,这意味着基于分支的发布策略赋予项目维护者垄断地位。这很糟糕,因为他们会比有真正的竞争追赶他们时变得更懒惰、更傲慢。

稳定化项目应当按照与主项目相同的流程来维护。

稳定化项目也像任何项目一样有维护者和贡献者。实践中,我们通常会从主项目中 cherry pick 补丁到稳定化项目,但这只是为了方便。

针对已声明为“稳定”的仓库的补丁,必须附带可复现的测试用例。

警惕一刀切的流程。新代码不需要像人们信任用于生产环境的代码那样偏执。在正常开发流程中,我们没有提及测试用例。这有其原因。尽管我喜欢可测试的补丁,但许多更改并不容易或根本无法测试。然而,要稳定一个代码库,你只想修复严重的错误,并且你想百分之百确定每个更改都是准确的。这意味着每个更改都需要有变更前后的测试。

公共契约(Public Contracts)的演进 #

这里的“公共契约”是指 API 和协议。直到 2011 年底,libzmq的天然良好状态受到了承诺和契约被破坏的损害。我们停止为libzmq完全做出承诺(又称“路线图”),我们目前主要的变更理论是它会随着时间谨慎而准确地演进。在 2012 年芝加哥的一次聚会上,Garrett Smith 和 Chuck Remes 将此称为“醉步走向伟大”,我现在也这么想。

我们停止破坏公共契约,只需禁止这种做法即可。在此之前,只要我们更改主版本号,破坏 API 或协议就被认为是“没问题”的(也就是我们做了,每个人都强烈抱怨,而我们则置之不理)。这听起来不错,直到你同时开发 ZeroMQ v2.0、v3.0 和 v4.0,并且它们之间无法通信。

所有公共契约(API 或协议)都应当有文档。

你可能会认为这对专业软件工程师来说是理所当然的,但不,并非如此。所以,这是一条规则。如果你的项目想要获得 C4 认证,你必须确保你的公共契约有文档。没有“代码里规定了”这样的借口。代码不是契约。(是的,我打算在某个时候创建一个 C4 认证流程,作为开源项目的质量指标。)

所有公共契约必须使用语义化版本控制(Semantic Versioning)。

这条规则主要在这里,是因为有人要求。我对此并无真正的喜爱,因为语义化版本控制正是导致了所谓的“ZeroMQ 为什么不能自洽?!”的惨败。我从未见过它解决了什么问题。大概是关于运行时验证库版本之类的东西。

所有公共契约都应当为可扩展性和实验性留有空间。

现在,真正重要的是公共契约确实会改变。问题不在于是否改变它们。而在于如何安全地改变它们。这意味着要教育(特别是协议)设计者,提前创建好那个空间。

修改稳定公共契约的补丁,不应当破坏现有应用,除非有压倒性的共识认为这样做有价值。

有时补丁是修复一个没人使用的糟糕 API。这是我们需要的自由,但这应当基于共识,而不是某个人的教条。然而,随意地“就因为”而做出更改是不好的。在 ZeroMQ v3.x 中,我们将ZMQ_NOBLOCK重命名为ZMQ_DONTWAIT是否从中获益了?当然,它更接近 POSIX socket 的recv()调用,但这值得破坏成千上万个应用吗?从未有人将其报告为问题。套用 Stallman 的话说:“你创造理想世界的自由止步于我的应用一寸之外。”

为公共契约引入新特性的补丁,应当使用新的名称。

在 ZeroMQ 中,我们曾有过一两次新特性使用旧名称(或者更糟,使用了仍在其他地方使用的名称)的经历。ZeroMQ v3.0 新引入了一个“ROUTER” socket,它与 2.x 中已有的 ROUTER socket 完全不同。天哪,你应该扶额叹息了,为什么?原因:显然,即使是聪明人有时也需要规章制度来阻止他们做蠢事。

旧名称应当以系统化的方式弃用,首先将新名称标记为“实验性(experimental)”,直到它们稳定,然后将旧名称标记为“已弃用(deprecated)”。

这种生命周期标记的巨大好处在于,它能够以一致的方向实际告诉用户发生了什么。“实验性”意味着“我们引入了它,如果可行,打算使其稳定”。它不意味着“我们引入了它,并打算随时根据意愿移除它”。人们会认为在一个以上的补丁周期中保留下来的代码是应该存在的。“已弃用”意味着“我们已经替换了它,并打算移除它”。

当经过足够的时间后,旧的已弃用名称应当被标记为“遗留(legacy)”,并最终移除。

理论上,这给了应用时间无风险地迁移到稳定的新契约。你可以先升级,确保一切正常工作,然后随着时间推移,进行修复以移除对已弃用和遗留 API 和协议的依赖。

旧名称不得被新特性复用。

啊,是的,当 ZeroMQ v3.x 重命名了最常用的 API 函数( zmq_send() zmq_recv())然后将旧名称重新用于完全不兼容的新方法(我怀疑很少有人实际使用这些方法)时的“快乐”。你应该再次困惑地拍打自己的脸颊了,但事实确实如此,我也和任何人一样有责任。毕竟,我们确实改变了版本号!那次经历唯一的好处就是得到了这条规则。

当旧名称被移除时,如果被应用使用,它们的实现必须引发异常(断言)。

我没有测试过这条规则来确定它是否有意义。也许它的意思是“如果因为 API 是动态的而无法引发编译错误,那就引发断言”。

项目管理 #

项目创始人必须担任管理员,负责管理项目维护者团队。

管理员必须通过提拔最有效的维护者来确保其自身的长期继任。

同时,作为项目的创始人,在过度依恋它之前,你确实想要放手。提拔最活跃、最坚持的维护者对每个人都有好处。

做出正确补丁的新贡献者,必须被邀请成为维护者。

我于 2012 年在里昂的 Mix-IT 大会上见到了 Felix Geisendörfer,我在那里展示了社会架构,由此产生的一件事是 Felix 如今著名的 Pull Request Hack。它优雅地融入了 C4,并解决了维护者随时间流失的问题。

管理员可以移除长时间不活跃的维护者,或反复未能准确执行此流程的维护者。

这是 Ian Barber 的建议:我们需要一种方法来淘汰不活跃的维护者。最初,维护者是自荐的,但这使得淘汰麻烦制造者(虽然罕见,但并非没有)变得困难。

C4 并非完美。很少有事物是完美的。修改 C4 的流程(Digistan 的 COSS)现在有点过时了:它依赖于单编辑者工作流,可以派生但不能合并。这似乎可行,但最好还是使用 C4 来处理像 C4 这样的协议。

一个真实案例 #

这封邮件讨论串中,Dan Goes 询问如何制作一个知道何时有新客户端订阅并发送先前匹配消息的发布者。这是一种标准的发布-订阅技术,称为“最后值缓存”。现在,在像 pgm 这样单向传输方式(订阅者实际上不发送任何数据包回发布者)上,这无法实现。但在 TCP 上可以,如果我们使用 XPUB socket,并且该 socket 没有巧妙地过滤掉重复订阅以减少上游流量。

虽然我不是

的专家贡献者,但这似乎是一个有趣的问题。能有多难呢?我首先派生了libzmq仓库到我自己的 GitHub 账号,然后克隆到我的笔记本电脑上,并在那里构建它libzmq将仓库 fork 到我自己的 GitHub 账户,然后克隆到我的笔记本电脑,并在那里构建它

git clone git@github.com:hintjens/libzmq.git
cd libzmq
./autogen.sh
./configure
make

因为libzmq代码整洁且组织良好,找到需要修改的主要文件相当容易(xpub.cppxpub.hpp)。每种 socket 类型都有自己的源文件和类。它们继承自socket_base.cpp,其中包含用于特定于 socket 的选项的钩子

//  First, check whether specific socket type overloads the option.
int rc = xsetsockopt (option_, optval_, optvallen_);
if (rc == 0 || errno != EINVAL)
    return rc;

//  If the socket type doesn't support the option, pass it to
//  the generic option parser.
return options.setsockopt (option_, optval_, optvallen_);

然后我查看 XPUB 套接字在其何处过滤掉重复的订阅,在其然后我查看 XPUB socket 在其xread_activated

bool unique;
if (*data == 0)
    unique = subscriptions.rm (data + 1, size - 1, pipe_);
else
    unique = subscriptions.add (data + 1, size - 1, pipe_);

//  If the subscription is not a duplicate store it so that it can be
//  passed to used on next recv call.
if (unique && options.type != ZMQ_PUB)
    pending.push_back (blob_t (data, size));

方法中过滤掉重复订阅的位置在这个阶段,我不太关心subscriptions.rmsubscriptions.add工作细节。代码看起来很明显,只是“订阅”也包括了取消订阅,这让我困惑了几秒钟。如果在 rm 和 add 方法中有其他奇怪之处,那是一个单独的问题,稍后再解决。现在是时候为这次更改创建一个问题了。我前往zeromq.jira.com

网站,登录,然后创建一个新的条目。

Jira 很好心地给了我传统的“bug”和“新特性”之间的选择,我花了三十秒思考这种事倍功半的历史区别从何而来。大概源于“我们会免费修复 bug,但新特性需要付费”的商业提案,而这个提案又来自“你告诉我们需要什么,我们就会按 $X 的价格完成”的软件开发模式,这种模式通常会导致“我们花了三倍的 $X,结果得到了什么?!”之类的邮件版《狂怒之拳》(Fists of Fury)。

抛开这些想法,我创建了一个问题 #443,并描述了问题和可行的解决方案

问题:XPUB socket 会过滤掉重复的订阅(有意设计)。然而,这使得进行基于订阅的智能处理变得不可能。请参阅 http://lists.zeromq.org/pipermail/zeromq-dev/2012-October/018838.html 获取用例。解决方案:通过一个 socket 选项使这种行为可配置。现在是命名时间了。API 位于include/zmq.h中,所以我在这里添加了选项名称。当你在 API 或任何地方发明一个概念时,请务必花点时间选择一个明确、简短且显而易见的名称。不要依赖那些需要额外上下文才能理解的通用名称。你只有一次机会告诉读者你的概念是什么以及做什么。像ZMQ_SUBSCRIPTION_FORWARDING_FLAG这样的名称就很糟糕。它技术上勉强算指向正确方向,但长得令人沮丧,而且含糊不清。我选择了ZMQ_XPUB_VERBOSE

:简短、明确,而且显然是一个开关,默认设置为“关闭”。于是,是时候给xpubxpub.hpp:

// If true, send all subscription messages upstream, not just
// unique ones
bool verbose;

类定义在router.cpprouter.cpp中添加一个私有属性,然后从router.cpp 中复制一些代码来实现 xsetsockopt然后我查看 XPUB socket 在其方法。最后,修改

//  If the subscription is not a duplicate store it so that it can be
//  passed to used on next recv call.
if (options.type == ZMQ_XPUB && (unique || verbose))
    pending.push_back (blob_t (data, size));

方法来使用这个新选项,顺便也将对 socket 类型的测试做得更明确一些代码第一次构建就成功了。这让我有点怀疑,但由于懒惰和时差,我没有立即编写一个测试用例来实际测试这个改动。流程并不要求这样做,尽管通常我都会这么做,只是为了抓住我们所有人都会犯的那些不可避免的 10% 的错误。不过,我在doc/zmq_setsockopt.txt

手册页上记录了这个新选项。最坏的情况下,我只是增加了一个实际上没用的补丁。但我肯定没有破坏任何东西。我没有实现一个匹配的zmq_getsockopt

因为“最小化”就意味着字面意思。在代码中获取你刚刚设置的选项值的明显用例并不存在。仅仅为了对称性而将补丁大小翻倍并不是一个合理的理由。我确实需要记录这个新选项,因为流程规定:“所有公共契约都应当有文档。”

git commit -a -m "Fixed issue #443"
git push origin master

提交代码后,我将补丁推送到我的派生仓库(即“origin”)libzmq切换到 GitHub Web 界面,我进入我的

派生仓库,然后点击顶部的那个大大的“Pull Request”按钮。GitHub 要求我输入标题,所以我输入了“Added ZMQ_XPUB_VERBOSE option”。我不确定为什么会问这个,因为我写了一个清晰的提交信息,不过好吧,随它去吧。这生成了一个不错的包含两个提交的小型拉取请求;一个是我一个月前为准备 v3.2.1 版本在发布说明上做的提交(大部分时间都在机场度过时,一个月过得真快),另一个是我针对问题 #443 的修复(37 行新代码)。GitHub 允许你在发起拉取请求后继续进行提交。这些提交会排队,然后一次性合并。这很简单,但维护者可能会因为其中一个补丁看起来无效而拒绝整个提交集。

因为 Dan 正在等待(至少在我高度乐观的想象中)这个修复,所以我回到 zeromq-dev 邮件列表告诉他我已经完成了补丁,并附上提交链接。越快获得反馈越好。当我做这个补丁时是韩国时间凌晨 1 点,所以是欧洲的傍晚,美国的早上。与世界各地的人一起工作时,你得学会计算时区。Ian 在参加会议,Mikko 正在登机,Chuck 可能在办公室,但三个小时后,Ian 合并了拉取请求。

在 Ian 合并拉取请求后,我将我的派生仓库与上游的libzmq仓库同步。首先,我添加一个 远程仓库(remote)来告诉 git 这个仓库在哪里(我只在我工作的目录中做这一次)

git remote add upstream git://github.com/zeromq/libzmq.git

然后我从上游主分支拉回更改,并查看 git 日志进行双重检查

git pull --rebase upstream master
git log

对于向libzmq贡献补丁需要学习和使用多少 git,这几乎就是全部了。六个 git 命令加上一些网页点击。对我来说,作为一个天生懒惰、愚笨且容易困惑的开发者,最重要的是我无需学习 git 的内部模型,也永远不必做任何涉及那些我们称之为“git 分支”的结构复杂度的地狱引擎的事情。接下来,尝试刺杀 git 分支。让我们危险地生活吧!

Git 分支的危害 #

Git 最受欢迎的特性之一就是它的分支。几乎所有使用 git 的项目都使用分支,选择“最佳”分支策略就像开源项目的一种成年礼(rite of passage)。Vincent Driessen 的 git-flow 可能是最知名的。它有基础分支(master, develop)、特性分支、发布分支、热修复分支和支持分支。许多团队采用了 git-flow,它甚至还有 git 扩展来支持。我非常相信大众的智慧,但有时你必须认清群体性错觉的本质。

这是 C4 中可能会让你初读时感到震惊的一节

项目不得出于任何原因使用主题分支(topic branches)。个人派生仓库可以使用主题分支。

需要明确的是,我指的是共享仓库中的公共分支。将分支用于私人工作,例如处理不同的问题,似乎也工作得很好,尽管这比我个人喜欢的复杂得多。再次借用 Stallman 的话:“你创造复杂性的自由止步于我们共享工作空间一寸之外。”

与 C4 的其余部分一样,关于分支的规则并非偶然。它们源于我们在开发 ZeroMQ 时的经验,始于我和 Martin Sustrik 重新思考如何制作稳定版本。我们都热爱并欣赏简洁(有些人似乎对复杂性有着非凡的容忍度)。我们聊了一会儿……我问他:“我打算开始制作一个稳定版本。我在你正在工作的 git 仓库里创建一个分支可以吗?” Martin 不喜欢这个想法。“好吧,如果我派生这个仓库,我就可以把你的仓库里的补丁移到那个仓库里。” 这让我们俩都感觉好多了。

许多 ZeroMQ 社区成员的反应是震惊和恐惧。人们觉得我们很懒惰,并且让贡献者更难找到“正确”的仓库。尽管如此,这看起来很简单,而且确实运行顺畅。最好的部分是我们每个人都可以按照自己想要的方式工作。而在此之前,ZeroMQ 仓库感觉异常复杂(而且它甚至不像 git-flow 那样),现在感觉很简单。而且它有效。唯一的缺点是我们失去了一个单一的统一历史记录。现在,也许历史学家会感到被剥夺了,但我老实说,我看不到谁在何时更改了什么,包括每个分支和实验的历史细节,是否值得任何重大的痛苦或摩擦。

人们已经习惯了 ZeroMQ 中的“多个仓库”方法,并且我们在其他项目中也相当成功地开始使用它。我个人的观点是,历史会评判 git 分支和像 git-flow 这样的模式,认为它们是针对从 Subversion 和单体仓库时代继承而来的假想问题的复杂解决方案。

更深层次地说,也许这就是为什么大多数人似乎是“错的”:我认为分支与派生的争论实际上是一个更深层次的设计与演进的争论,关于如何最优地构建软件。我将在下一节中探讨这个更深层次的论点。现在,我将尝试用科学的态度来审视我对分支的非理性憎恨,通过考察一系列标准,并在每个标准中比较分支和派生。

简洁性对比复杂性 #

越简洁越好。

分支本身并不一定比派生更复杂。然而,git-flow 使用五种类型的分支,而 C4 使用两种类型的派生(开发型和稳定型)以及一个分支(主分支)。因此,旁证表明分支比派生导致更多的复杂性。对于新用户来说,我们也在实践中衡量过,学习使用多个仓库并且除了主分支外不使用其他分支,确实更容易。

变更延迟 #

交付越小、越迅速越好。

开发分支似乎与大型、缓慢、高风险的交付密切相关。“抱歉,我们得先合并这个分支才能测试新版本”这表明流程出现了问题。这肯定不是 C4 的工作方式,C4 是通过紧密关注单个问题及其最小化解决方案来工作的。在开发中允许使用分支会增加变更延迟。派生则有不同的结果:派生者需要确保其更改能够干净地合并,并保持其简洁性以免被拒绝。

学习曲线 #

学习曲线越平滑越好。

证据明确表明学习使用 git 分支是复杂的。对有些人来说,这没问题。但对大多数开发者而言,花在学习 git 上的每个周期都是浪费在更具生产力的事情上的周期。我被不同的人多次告知,我不喜欢分支是因为我“从未真正学好 git”。这话没错,但这是对工具的批评,而不是对人的批评。

失败成本 #

失败成本越低越好。

分支要求开发者更加完美,因为错误可能会影响到其他人。这提高了失败的成本。派生使得失败成本极其低廉,因为在派生仓库中发生的任何事情都不会影响到未使用该派生仓库的其他人。

预先协调 #

越少需要预先协调越好。

你可以进行“敌意”派生(hostile fork)。你无法进行“敌意”分支(hostile branch)。分支依赖于预先协调,这既昂贵又脆弱。一个人可以否决整个团体的意愿。例如,在 ZeroMQ 社区,我们花了一年时间未能就 git 分支模型达成一致。我们转而使用派生解决了这个问题。问题就消失了。

可伸缩性 #

项目即可伸缩得越大,就越好。

所有分支策略的一个强烈假设是,仓库就是项目。但是,让多少人同意在一个仓库中一起工作是有限制的。正如我所解释的,前期协调的成本可能会变得致命。一个更现实的项目通过允许任何人启动他们自己的仓库,并确保这些仓库可以一起工作来实现扩展。像 ZeroMQ 这样的项目有几十个仓库。分叉看起来比分支更具可扩展性。

惊喜与预期 #

惊喜越少越好。

人们期望使用分支,并认为分叉不常见,因此会感到困惑。这是分支获胜的一个方面。如果使用分支,一个补丁将具有相同的提交哈希标签,而在不同的分叉中,补丁将具有不同的哈希标签。这确实使得跟踪跨越不同分叉的补丁变得更困难。但说真的,不得不跟踪十六进制哈希标签不是一个特性。这是一个 bug。有时更好的工作方式起初会令人惊讶。

参与的经济学 #

奖励越实在越好。

人们喜欢拥有自己的工作并获得认可。这在分叉中比在分支中容易得多。分叉以健康的方式创造更多竞争,而分支抑制竞争并迫使人们协作和分享功劳。这听起来很积极,但在我的经验中,它会削弱人们的积极性。分支不是你可以“拥有”的产品,而分叉可以是。

冲突中的健壮性 #

模型越能在冲突中生存,就越好。

不管喜欢与否,人们会为自尊、地位、信仰和世界观而争斗。挑战是科学的必要组成部分。如果你的组织模式依赖于一致性,那么你在第一场真正的争斗中就会失败。分支无法在真正的争论和争斗中生存,而分叉即使是敌对的,仍然可以使所有参与方受益。而这正是自由软件的运作方式。

隔离的保障 #

生产代码与实验代码之间的隔离越强,就越好。

人们会犯错。我见过实验性代码被错误地推送到主线生产环境。我见过人们在压力下做出糟糕的恐慌性修改。但真正的错误在于允许两代完全独立的产品存在于同一个受保护的空间中。如果你能推送到 random-branch-x,你就能推送到 master。分支不保证生产关键代码的隔离。分叉则保证。

可见性 #

我们的工作越可见,就越好。

分叉有关注者、议题、README 文件和 wiki。分支没有这些。人们尝试分叉,构建它们,弄坏它们,然后修补它们。分支则在那里静止不动,直到有人记起要去处理它们。分叉有下载和 tarball。分支没有。当我们寻找自组织时,问题越可见、越明确,我们工作的速度和准确性就越高。

结论 #

在本节中,我列出了一系列论点,其中大多数来自我的团队成员。情况似乎是这样的:git 老手坚持认为分支是工作方式,而新手则在被要求处理 git 分支时往往感到畏惧。Git 不是一个容易掌握的工具。我们偶然发现,当你完全停止使用分支时,git 变得微不足道,非常容易使用。它实际上就归结为六个命令(clone, remote, commit, log, push,和pull)。此外,一个无分支流程实际上是可行的,我们已经使用了几年了,除了让老手感到惊讶以及“单一”项目在多个仓库上增长之外,没有明显的缺点。

如果你不能使用分叉,也许是因为你的公司不信任 GitHub 的私有仓库,那么你或许可以使用主题分支,每个议题一个分支。你仍然会承担获得前期共识、低竞争性和人为错误的风险的成本。

创新设计 #

让我们来看看创新,维基百科将其定义为,“通过以增加价值的新方式,提供满足新需求、未明确需求或旧客户和市场需求的解决方案,来发展新的价值。” 这实际上仅仅意味着以更低的成本解决问题。这听起来很简单,但科技巨头衰落的历史证明并非如此。我将尝试解释为什么团队常常会搞错,并提出一种正确进行创新的方法。

两座桥的故事 #

两位老工程师谈论着他们的人生,并夸耀着他们最伟大的项目。其中一位工程师讲述了他如何设计了有史以来最伟大的桥梁之一。

“我们在一个河流峡谷上建造了它,”他告诉朋友。“它又宽又深。我们花了两年时间研究土地,选择设计和材料。我们聘请了最优秀的工程师来设计这座桥,这又花了五年。我们与最大的工程公司签订合同,建造结构、桥塔、收费站以及连接桥梁到主干道的道路。施工期间有几十人死亡。路面下方有火车,还有一条专供骑自行车者使用的特殊小径。那座桥代表了我多年的生命。”

第二个人沉默了一会儿,然后开口说。“一天晚上,我和一个朋友喝伏特加喝醉了,然后我们把一根绳子扔过了峡谷,”他说。“只是一根绳子,绑在两棵树上。峡谷两侧有两个村庄。起初,人们用滑轮和绳子把包裹拉过那根绳子。然后有人扔了第二根绳子,建了一条人行道。这很危险,但孩子们喜欢它。后来一群男人重建了它,把它加固了,女人们开始每天带着她们的农产品过桥。桥的一侧形成了一个市场,慢慢地那里变成了一个大镇,因为有很多地方可以盖房子。绳桥被木桥取代,允许马匹和马车通过。然后镇上建了一座真正的石桥,带有金属梁。后来,他们用钢铁取代了石材部分,今天,就在同一个位置,矗立着一座悬索桥。”

第一位工程师沉默了。他说道:“有件有趣的事,我的桥建成大约十年后就被拆除了。原来它建错了地方,没人想用。有些人早在下游几英里处就将绳子扔过了峡谷,而那里才是所有人都去的地方。”

ZeroMQ 如何失去了它的路线图 #

2012 年初在里昂的 Mix-IT 会议上介绍 ZeroMQ 时,我几次被问及“路线图”。我的回答是:不再有路线图了。我们曾经有,但我们删除了它们。我们没有让少数专家来规划下一步,而是让事情有机地发生。听众不太喜欢我的回答。太不法国了。

然而,ZeroMQ 的历史非常清楚地解释了为什么路线图是成问题的。一开始,我们有一个小团队在开发这个库,贡献者很少,也没有文档化的路线图。随着 ZeroMQ 越来越受欢迎,我们转向了更多贡献者,用户开始要求路线图。于是我们将计划收集起来,并试图将它们组织成发布版本。我们写道,这就是下一版本中会有的内容。

随着我们推出版本,我们遇到了一个问题,即承诺东西非常容易,但按照计划实现起来则相当困难。一方面,很多工作是志愿性的,不清楚如何强迫志愿者遵守路线图。但另一方面,优先级也可能随着时间发生巨大变化。所以我们做出了无法兑现的承诺,而实际交付的内容与路线图不符。

第二个问题是,通过定义路线图,我们实际上是在划定地盘,使其他人更难参与进来。人们确实更喜欢贡献于他们认为是自己想法的改变。写下一系列待办事项列表,会将贡献变成一种苦差事,而不是一个机会。

最后,我们在 ZeroMQ 中看到了一些相当痛苦的改变,尽管进行了大量的讨论并努力“做对”,但路线图对此毫无帮助。例如 API 和协议中的不兼容更改。很明显,我们需要一种不同的方法来定义变更过程。

软件工程师不喜欢这样一种观念:强大、有效的解决方案可以在没有智能设计师主动思考周全的情况下产生。然而,在里昂那个房间里,没有人会质疑进化论。这是一个奇怪的讽刺,也是我想要进一步探索的,因为它支撑着 ZeroMQ 社区自 2012 年初以来所采取的方向。

在主流的创新理论中,杰出的个体对大型问题集进行思考,然后仔细精确地创造出解决方案。有时他们会有“尤里卡”时刻,瞬间领悟到针对整个大型问题集而又极其简单的答案。发明者和发明过程是稀有、宝贵的,可以形成垄断。历史上充满了这样的英雄人物。我们的现代世界归功于他们。

然而,仔细观察,你会发现事实并不相符。历史并没有显示孤立的发明者。它显示的是幸运的人窃取或声称拥有许多人正在研究的想法。它显示的是杰出的人偶然幸运一次,然后花费几十年时间进行徒劳无意义的探索。最著名的大规模发明家,如托马斯·爱迪生,实际上只是非常擅长组织大型团队进行系统性的广泛研究。这就像声称史蒂夫·乔布斯发明了苹果制造的每一件设备一样。这是一个美好的神话,有利于营销,但作为实用科学则完全无用。

近期的历史,有更好的文档记录,也不那么容易被篡改,很好地证明了这一点。互联网无疑是技术领域最具创新性、发展最快的领域之一,也是文档最完善的领域之一。它没有发明者。相反,它拥有一个庞大的经济体,人们仔细而逐步地解决了一系列紧迫的问题,记录了他们的答案,并将其提供给所有人。互联网的创新性不是来自于一小群精选的爱因斯坦式人物。它来自于任何人都可以使用和改进的 RFC(请求评论),这些 RFC 是由成百上千个聪明但并非绝顶聪明的个体创造的。它来自于任何人都可以使用和改进的开源软件。它来自于分享、社区规模以及对好的解决方案的持续积累和对坏解决方案的淘汰。

因此,这里提出另一种创新理论

  1. 存在一个无限的问题/解决方案领域。
  2. 这个领域会根据外部条件随时间变化。
  3. 我们只能准确感知我们接近的问题。
  4. 我们可以使用解决方案市场来衡量问题的成本/收益经济学。
  5. 任何可解决的问题都存在一个最优解。
  6. 我们可以通过启发式和机械化方法逼近这个最优解。
  7. 我们的智能可以加快这个过程,但不能取代它。

由此得出几个推论

  • 个体创造力不如过程重要。更聪明的人可能工作更快,但也可能方向错误。是集体的现实视野让我们保持诚实和相关性。

  • 如果我们有一个好的过程,就不需要路线图。随着解决方案争夺市场份额,功能将随时间出现和演变。

  • 我们与其说是在发明解决方案,不如说是在发现它们。向创造性的灵魂致敬。它只是一个喜欢修饰自己的自尊心并收集业力的信息处理机器。

  • 智能是一种社会效应,尽管感觉是个人拥有的。一个与世隔绝的人最终会停止思考。没有其他人,我们既无法收集问题,也无法衡量解决方案。

  • 社区的规模和多样性是一个关键因素。规模更大、更多样化的社区比小型专家组收集更相关的问题,解决得更准确,速度也更快。

所以,当我们信任孤立的专家时,他们会犯经典错误。他们关注的是想法,而不是问题。他们关注的是错误的问题。他们对解决问题的价值做出错误的判断。他们不使用自己的工作成果。

那么,我们可以将上述理论转化为一个可重用的过程吗?2011 年底,我开始记录 C4 和类似的契约,并在 ZeroMQ 和闭源项目中同时使用它们。其底层过程我称之为“面向简洁的设计”,或 SOD。这是一种开发简洁优雅产品的可复制方式。它将人们组织成灵活的供应链,能够快速且廉价地在一个问题格局中前行。他们通过构建、测试、保留或丢弃最小可行的解决方案,称为“补丁”,来做到这一点。活生生的产品由一系列长串的补丁组成,一个接一个地应用。

首先,SOD 之所以相关,是因为它是我们发展 ZeroMQ 的方式。它也是我们将在 第七章 - 使用 ZeroMQ 的高级架构 中用于开发更大规模 ZeroMQ 应用程序的设计过程的基础。当然,你可以结合 ZeroMQ 使用任何软件架构方法论。

为了最好地理解我们是如何采用 SOD 的,让我们看看其他选择。

面向垃圾的设计 #

大型企业中最流行的设计过程似乎是面向垃圾的设计,或 TOD。TOD 依赖于这样一种信念:我们赚钱所需的一切就是伟大的想法。这是顽固的胡说八道,但对于缺乏想象力的人来说是一个强大的拐杖。理论认为想法是稀有的,所以技巧在于捕捉它们。这就像非音乐人士被吉他手惊艳到,却不知道伟大的才能是如此廉价,以至于字面上可以在街头为硬币演奏。

TOD 的主要产出是昂贵的“构思”:概念、设计文档和直接进入垃圾桶的产品。它的运作方式如下

  • 创意人员会想出长长的“我们可以做 X 和 Y”的列表。我见过无休止地详细列出产品可以做的所有惊人事情的清单。我们都曾犯过这个错误。一旦想法生成的创意工作完成,当然就只是执行的问题了。

  • 因此,经理们和他们的顾问将他们绝妙的想法传给设计师,设计师会创建大量精心打磨的设计文档。设计师们接过经理们提出的几十个想法,将它们转化为数百个改变世界的设计。

  • 这些设计被交给工程师,他们挠头不解,想知道到底是哪个家伙想出了这些胡说八道。他们开始反驳,但设计来自高层,说实话,工程师是没有资格与创意人员和昂贵的顾问争论的。

  • 于是工程师们受辱且受到威胁地爬回他们的隔间,去建造那个巨大却又“优雅”的垃圾堆。这是艰苦卓绝的工作,因为设计完全不考虑实际成本。微不足道的奇思妙想可能需要几周的工时来建造。随着项目延迟,经理们开始强迫工程师们放弃他们的晚上和周末休息时间。

  • 最终,一件勉强称得上工作产品的玩意儿问世了。它吱呀作响,脆弱不堪,复杂又丑陋。设计师咒骂工程师无能,并支付更多顾问费来给这头猪涂口红,产品这才慢慢开始看起来稍微好一些。

  • 此时,经理们已经开始尝试销售产品,他们震惊地发现,没人想要。他们无畏地耗资百万美元建造网站和广告宣传活动,向公众解释为什么他们绝对需要这款产品。他们与其他企业合作,将产品强加给那些懒惰、愚蠢和不识好歹的市场。

  • 经过十二个月的密集营销,产品仍然没有盈利。更糟的是,它遭遇了巨大的失败,并在媒体上被贴上了灾难的标签。公司悄悄地将其束之高阁,解雇了顾问,从一家小型初创公司购买了一个竞争产品,并将其重新包装为自己的 Version 2。数亿美元最终打了水漂。

  • 与此同时,组织里的某个地方,另一位有远见的经理和一些市场人员喝了太多龙舌兰酒,有了一个绝妙的主意。

面向垃圾的设计如果不是如此普遍,那它就是一种漫画。大型公司开发的市场就绪产品中,大约 20 个中有 19 个是失败的(没错,87% 的统计数据都是现场编造的)。剩下的 20 个中的 1 个可能仅仅因为竞争对手太差以及营销过于激进而成功。

TOD 的主要教训非常直白,但难以接受。它们是

  • 想法是廉价的。没有例外。没有绝妙的想法。任何试图以“哇,我们也可以做这个!”来开启讨论的人,都应该以对待旅行传道士的热情来将其压制下去。这就像坐在山脚下的咖啡馆里,喝着热巧克力,告诉其他人:“嘿,我有一个好主意,我们可以爬那座山!并在山顶建一座小木屋!里面有两个桑拿房!还有一个花园!嘿,我们还可以把它做成太阳能供电的!伙计,太棒了!我们应该把它漆成什么颜色?绿色!不,蓝色!好了,你去建造吧,我留在这里做电子表格和图表!”

  • 好的设计过程的起点是收集真实人们面临的真实问题。第二步是评估这些问题,并问一个基本问题:“解决这个问题值多少钱?” 完成之后,我们就可以收集那些值得解决的问题集。

  • 针对真实问题的好解决方案将会作为产品获得成功。它们的成功将取决于解决方案有多好、多便宜,以及问题有多重要(可悲的是,还取决于营销预算有多大)。但它们的成功也将取决于使用它们需要付出多少努力——换句话说,它们有多简单。

现在,在我们屠杀了完全不相关性这条巨龙之后,我们将攻击复杂性这个恶魔。

面向复杂性的设计 #

真正优秀的工程团队和小公司通常可以构建出不错的产品。但是绝大多数产品最终仍然过于复杂,并且不如它们本应取得的成功。这是因为专业团队,即使是最好的,也经常顽固地应用我称之为面向复杂性的设计,或 COD 的过程,其运作方式如下

  • 管理层正确地识别出一些具有经济价值的有趣且困难的问题。这样一来,他们就已经超越了任何 TOD 团队。

  • 团队充满热情地开始构建原型和核心层。这些工作按设计进行,因此受到鼓舞,团队便投入到紧张的设计和架构讨论中,提出了看起来美观且扎实的优雅方案。

  • 管理层回过头来,用更多困难的问题挑战团队。我们倾向于将成本等同于价值,所以在他们看来,解决问题越困难、越昂贵,解决方案就越有价值。

  • 团队,作为工程师,热爱构建东西,于是他们就构建东西。他们不断构建、构建、构建,最终得到了巨大且设计完美的复杂性。

  • 产品投放市场后,市场摸不着头脑,问道:“说真的,这是你们能做到的最好的吗?”人们确实会使用这些产品,尤其是如果他们不必自己花钱来攀爬学习曲线的话。

  • 管理层从其大型客户那里得到了积极反馈,这些客户也有同样的观念,认为高成本(在培训和使用方面)意味着高价值,因此管理层继续推动这个过程。

  • 与此同时,世界某个地方,一支小团队正在使用更好的流程解决同样的问题,一年后,他们将市场搅得粉碎。

COD 的特点是团队以一种集体妄想的形式,痴迷于解决错误的问题。COD 产品往往规模庞大、雄心勃勃、复杂且不受欢迎。许多开源软件是 COD 流程的产物。工程师要停止扩展设计以涵盖更多潜在问题是极其困难的。他们会争辩说,“万一有人想做 X 怎么办?”,但从不问自己,“解决 X 的真正价值是什么?”

蓝牙是一个典型的 COD 实践案例,它是一套复杂、过度设计的协议,用户对此深恶痛绝。它之所以继续存在,仅仅是因为在一个专利密集的行业中没有真正的替代方案。蓝牙完美安全,这对于一个近距离协议来说几乎毫无意义。与此同时,它缺乏面向开发者的标准 API,这意味着在应用程序中使用蓝牙成本很高。

在 #zeromq IRC 频道上,Wintre 曾经写道多年前他感到多么愤怒,当时他“发现 XMMS 2 有一个可用的插件系统,但实际上却无法播放音乐。”

COD 是一种大规模的“钻牛角尖”,设计师和工程师无法与他们工作的技术细节保持距离。他们添加越来越多的功能,完全误读了他们工作的经济性。

COD 的主要教训也很简单,但专家们难以接受。它们是

  • 制作你当下不需要的东西是毫无意义的。无论你多么有才华或多么杰出,如果你只是坐下来制作人们实际上没有要求的东西,你很可能是在浪费时间。

  • 问题并非等同。有些很简单,有些很复杂。具有讽刺意味的是,解决更简单的问题往往比解决真正困难的问题对更多人来说更有价值。所以如果你允许工程师随意工作,他们大多会专注于最有趣但最不值得的事情。

  • 工程师和设计师喜欢制作东西和装饰,这不可避免地导致复杂性。至关重要的是要有一个“停止机制”,一种设置短暂而严格的截止日期的方式,迫使人们仅针对最关键的问题提供更小、更简单的答案。

面向简洁的设计 #

最后,我们来到罕见而宝贵的面向简洁的设计,或 SOD。这个过程始于一个认识:我们不知道要制造什么,直到开始制造之后才知道。想出想法或大型设计不仅是浪费,更是设计真正准确解决方案的直接障碍。真正有价值的问题隐藏得像远方的山谷,除了积极探索之外的任何活动都会制造出隐藏这些远方山谷的迷雾。你需要保持机动,轻装简行,行动迅速。

SOD 的运作方式如下

  • 我们收集一组有趣的问题(通过观察人们如何使用技术或其他产品),然后我们将这些问题从简单到复杂排列起来,寻找并识别使用模式。

  • 我们选取最简单、最突出的问题,并用最小可行解决方案,或称“补丁”来解决它。每个补丁都以极其简洁的方式,准确地解决一个真实且达成共识的问题。

  • 我们对补丁应用一个质量衡量标准,即“在仍然解决既定问题的前提下,还能再简单些吗?”我们可以根据用户为了使用补丁需要学习或猜测的概念和模型来衡量复杂性。越少越好。一个完美的补丁解决了问题,而用户无需学习任何东西。

  • 我们的产品开发始于一个解决“我们需要一个概念验证”问题的补丁,然后通过数百或数千个补丁相互叠加,不间断地演变为一系列成熟的产品。

  • 我们不进行任何不是补丁的任何活动。我们通过正式流程来强制执行这条规则,这些流程要求每一项活动或任务都必须与一个真实且达成共识的问题相关联,并且问题必须被明确阐述和记录。

  • 我们将项目构建成一个供应链,其中每个项目都可以向其“供应商”提供问题,并获得补丁作为回报。供应链创建了“停止机制”,因为当人们焦急地等待答复时,我们必然会缩短工作时间。

  • 个人可以自由地在任何项目上工作,并在他们认为有价值的任何地方提供补丁。除了强制执行正式流程的人员外,没有任何个人“拥有”任何项目。一个单一项目可以有多种变体,每种变体都是不同、竞争的补丁集合。

  • 项目导出正式且有文档记录的接口,以便上游(客户端)项目不会感知到供应商项目中发生的变化。因此,多个供应商项目可以竞争客户端项目,实际上创造了一个自由竞争的市场。

  • 我们将供应链与实际用户和外部客户端联系起来,并通过快速迭代驱动整个过程,以便从外部用户收到的问题可以在几小时内被分析、评估并用补丁解决。

  • 从第一个补丁开始的每一个时刻,我们的产品都是可发布的。这是至关重要的,因为很大一部分补丁会是错误的(10-30%),只有将产品交给用户,我们才能知道哪些补丁已经成为需要解决的问题。

SOD 是一种爬山算法,是在未知环境中寻找最重要问题最优解的可靠方法。你不需要成为天才才能成功使用 SOD,你只需要能够分辨活动产生的迷雾与解决新真实问题的进展之间的区别。

人们指出,爬山算法存在已知的局限性。主要是在局部最优解处停滞。但这正是生命本身的运作方式:在漫长的时间里积累微小的增量改进。没有智能设计师。我们通过在环境中广泛分散来降低局部最优解的风险,但这在某种程度上是无关紧要的。这些局限性不是可选项,它们是物理定律。该理论认为,这就是创新真正运作的方式,所以最好接受并与其合作,而不是基于魔法思维来工作

事实上,一旦你将所有创新都视为或多或少成功的爬山过程,你就会明白为什么有些团队、公司和产品会困在前景日益黯淡的永无之地。他们只是缺乏足够的多样性和集体智慧来寻找更高的山峰攀爬。当诺基亚扼杀他们的开源项目时,他们等于自断生路。

一个真正优秀的设计师和一个优秀的团队可以利用 SOD 快速准确地构建世界级产品。为了最大程度地发挥 SOD 的作用,设计师必须从第一天起就持续使用产品,并培养他/她发现诸如不一致性、意外行为以及其他形式的摩擦等问题的能力。我们自然会忽略许多令人烦恼的小事,但优秀的设计师会注意到这些问题,并思考如何修复它们。设计的本质是消除产品使用中的摩擦。

在开源环境中,我们在公共场合进行这项工作。没有所谓的“让我们开放代码”的时刻。在我看来,这样做(指到了某个时候才开放代码)的项目错失了开源的要点,即让你的用户参与你的探索,并在架构的种子周围建立社区。

职业倦怠 #

ZeroMQ 社区过去和现在都严重依赖于无偿的个人努力。我愿意认为每个人都以某种方式得到了他们贡献的补偿,我相信在 ZeroMQ 中,贡献意味着获得一种极其有价值的技术领域的专业知识,从而带来更好的职业选择。

然而,并非所有项目都会如此幸运,如果你与开源项目合作或参与其中,你应该了解志愿者面临的职业倦怠风险。这适用于所有无偿社区。在本节中,我将解释导致职业倦怠的原因、如何识别它、如何预防它,以及(如果发生)如何尝试治疗它。免责声明:我不是精神科医生,本文基于我过去 20 年在无偿环境中工作的个人经验,包括自由软件项目和像 FFII 这样的非政府组织。

在无偿环境中,我们被期望在没有直接或明显的经济激励下工作。也就是说,我们牺牲家庭生活、职业发展、空闲时间和健康,以实现我们决定实现的一些目标。在任何项目中,我们都需要某种奖励来使我们每天都值得继续下去。在大多数无偿项目中,奖励非常间接,表面上根本没有经济性。大多数时候,我们做事是因为人们说:“嘿,太棒了!”业力是一个强大的动力。

然而,我们是经济生物,迟早,如果一个项目耗费了我们巨大的代价,却没有带来任何形式的经济回报(金钱、名声、新工作),我们就会开始遭受痛苦。在某个阶段,似乎我们的潜意识就会感到厌恶,然后说:“够了!”并拒绝再进一步。如果我们试图强迫自己,我们真的可能会生病。

这就是我所说的“职业倦怠”,尽管这个词也用于其他形式的筋疲力尽。在一个回报过低的项目上投入过多,且时间过长。我们很擅长操控自己和他人,这通常是导致职业倦怠过程的一部分。我们告诉自己这是为了一个好的事业,并且别人都做得很好,所以我们也应该能够做到。

当我因 Xitami 等开源项目而精疲力竭时,我清楚地记得我的感受。我就是停止了工作,拒绝回复任何邮件,并告诉人们忘了这件事吧。你可以看出一个人是否精疲力竭。他们会下线,然后每个人都开始说:“他行为很奇怪……沮丧,或者累了……”

诊断很简单。有人是否在一个没有任何回报的项目上投入了大量工作?她是否做出了非凡的牺牲?他是否为了做这个项目而失去或放弃了工作或学业?如果你的答案是“是”,那就是职业倦怠。

多年来,我摸索出了三种简单的方法来降低我所合作团队中职业倦怠的风险

  • 没有人是不可替代的。独自在一个关键或受欢迎的项目上工作——责任集中在一个无法自我设限的人身上——可能是主要因素。这是一个管理上的真理:如果你的组织里有人是不可替代的,那就开除他/她。

  • 我们需要一份日常工作来维持生计。这可能很困难,但似乎是必要的。从别处获得收入使得维持一个牺牲型的项目变得容易得多。

  • 教导人们关于职业倦怠的知识。这应该成为大专院校的基础课程,因为无偿工作正在成为年轻人进行职业探索的更常见方式。

当有人独自在一个关键项目上工作时,你知道他们迟早会崩溃。这实际上相当可预测:大约 18-36 个月,具体取决于个人以及他们在私人生活中面临的经济压力。我没见过有人在半年后就精疲力竭,也没见过有人在一个没有回报的项目中坚持五年。

职业倦怠有一种简单的疗法,至少在某些情况下有效:为你的工作获得合理的报酬。然而,这几乎会摧毁志愿者所享有的(跨越那无限问题领域)的行动自由。

成功模式 #

我将以一系列软件工程成功模式来结束这一无代码章节。它们旨在捕捉将辉煌成功与悲剧失败区分开来的精髓。同一天里,一位经理将其描述为“宗教狂热教条”,一位同事则称“任何其他做法都是彻头彻尾的疯狂”。对我而言,它们是科学。但请将懒惰完美主义者等模式视为工具来使用、改进,并在出现更好的方法时抛弃它们。

懒惰完美主义者 #

绝不设计任何不是针对我们能够识别并必须解决的问题的精确且最小的答案的东西。

懒惰完美主义者利用闲暇时间观察他人,并识别值得解决的问题。他寻求对这些问题的共识,总是问道:“什么是真正的问题?” 然后他精确而 minimally(最小化地)行动,去构建,或让其他人去构建,一个针对一个问题的可用答案。他使用,或让其他人使用这些解决方案。他重复这个过程,直到没有问题需要解决,或者时间或金钱耗尽。

仁慈的暴君 #

控制大部队的原则与控制少数人是一样的:只是一个划分数量的问题。——孙子

仁慈的暴君将大问题分解成小问题,然后交给不同的团队去解决。她以 API 和我们下一章将读到的“非协议”(unprotocols)的形式,在这些团队之间建立联系。仁慈的暴君构建一条始于问题、终于可用解决方案的供应链。她对供应链的运作方式毫不留情/严格要求,但不会告诉人们应该做什么,也不会告诉他们如何做。

天地 #

理想的团队由两方面组成:一方写代码,另一方提供反馈。

天地作为一个整体紧密协作,但他们通过问题跟踪系统进行正式沟通。“天空”从他人那里以及他们自己使用产品过程中寻找问题,并将这些问题反馈给“大地”。“大地”迅速给出可测试的解决方案。“天地”一天之内可以处理数十个问题。“天空”与其他用户交流,而“大地”与其他开发者交流。“天地”可以是两个人,也可以是两个小团队。

开放之门 #

知识的准确性源于多样性。

开放之门几乎接受任何人的贡献。她不争论质量或方向,而是让其他人去争论,从而让他们更投入。她认为,即使是捣乱者也能带来更多不同的观点。她让团队形成关于什么代码可以进入稳定版本的意见,并在仁慈的暴君的帮助下执行这个意见。

嬉笑小丑 #

完美阻碍参与。

嬉笑小丑,通常以快乐的失败者自居,从不声称自己能力超群。相反,他的滑稽行为和笨拙尝试会激发他人,促使他们来解救他脱离自己的困境。然而,他总能不知怎地识别出需要解决的正确问题。人们忙着证明他是错的,以至于没有意识到他们正在做有价值的工作。

明智的将军 #

不要制定计划。设定目标,发展战略和战术。

明智的将军在未知领域运作,解决那些直到靠近才显现的问题。因此她不做计划,而是寻找机会,然后迅速而准确地加以利用。她在实践中发展战术和战略,并将这些教给她的“士兵”,使他们能够独立行动,并能协同作战。

社会工程师 #

知己知彼,百战不殆。——孙子

社会工程师洞察与他共事以及他为其工作的人的思想和情感。他问所有人:“是什么让这个人生气、不安全、好争论、平静、快乐?”他研究他们的情绪和性情。有了这些知识,他就能鼓励有用的人,劝退无用的人。社会工程师从不凭自己的情绪行事。

恒常园丁 #

上下同欲者胜。——孙子

随着项目参与人数的增加,她像从一粒小种子那样一步步发展出一套流程。每一个改变都有明确的原因,并且得到了所有人的同意。她从不自上而下地强加流程,而是让其他人达成共识,然后她来执行这个共识。通过这种方式,每个人都共同拥有这个流程,并且因为拥有它而对其产生了感情/归属感。

滚石 #

渡河而远之。——孙子

滚石接受他自身的有限性和短暂性。他对过去的工作没有任何留恋。他接受我们所创造的一切都注定要进入垃圾桶,这只是时间问题。通过精确、最低限度的投入,他可以迅速摆脱过去,专注于当下和近期。最重要的是,他没有自我意识,也没有自尊心会被他人的行为伤害。

海盗团 #

代码和所有知识一样,作为集体财产——而非私人财产——时效果最佳。

海盗团围绕问题自由组织。它在权威提供目标和资源时接受权威。海盗团拥有并共享它创造的一切:海盗团中的其他人可以完全自由地重组/改编每一项工作成果。随着新问题的出现,海盗团迅速行动,如果旧方案不再相关,则迅速放弃。任何个人或团体都不能垄断供应链的任何部分。

快闪族 #

水因地而制流。——孙子

快闪族在需要时在时间和空间上聚集在一起,然后尽快分散开。物理上的接近对于高带宽的沟通至关重要。但随着时间的推移,它会产生技术上的隔阂/壁垒,导致“大地”与“天空”分离。快闪族往往会积累很多飞行里程。

金丝雀观察者 #

痛苦通常不是一个好兆头。

金丝雀观察者通过自己的痛苦程度以及观察到的与他共事的人的痛苦程度来衡量一个组织的质量。他将新的参与者带入现有组织,以便他们能表达出“局外人”的真实痛苦。他可能会利用酒精让他人说出他们的痛点。他问他人,也问自己:“你在这个流程中快乐吗?如果不快乐,为什么?”当一个组织让他自己或他人感到痛苦时,他会将此视为一个需要解决的问题。人们应该在工作中感到快乐。

绞刑师 #

绝不要在别人犯错时打断他们。

绞刑师知道我们只能通过犯错来学习,她给予他人充足的“绳索”,让他们去学习。她只在适当的时候轻轻拉一下绳索。轻轻一拉,提醒对方所处的危险境地。允许他人从失败中学习,给了优秀者留下的理由,给了不合格者离开的借口。绞刑师有无穷的耐心,因为学习过程没有捷径。

历史记录者 #

保持公开记录可能很乏味,但这却是阻止串通的唯一方法。

历史记录者强迫讨论公开进行,以防止在工作领域形成垄断式串通。海盗团依赖于充分平等的沟通,这种沟通不取决于是否在场。没有人真正阅读存档,但仅仅这种可能性就足以阻止大多数滥用行为。历史记录者鼓励为工作选择合适的工具:电子邮件用于临时性讨论,IRC 用于闲聊,维基用于知识,问题跟踪系统用于记录机会。

煽动者 #

如果一个人知道自己两周后就要被绞死,这会极大地集中他的思维。——塞缪尔·约翰逊

煽动者制造截止日期、敌人,以及偶尔的不可能性。团队在没有时间瞎搞时工作得最好。截止日期将人们聚集在一起,并集中集体的思维。一个外部敌人能促使被动团队行动起来。煽动者从不把截止日期看得太重。产品*总是*准备好发布。但她轻轻提醒团队利害关系:如果失败,我们都得另找工作。

神秘主义者 #

当人们争吵或抱怨时,只需给他们写一段孙子语录。——米克·科帕宁

神秘主义者从不直接争论。他知道与一个情绪化的人争论只会制造更多情绪。相反,他会绕开讨论。很难对一位中国将军生气,尤其是一位已经去世了2400年的将军。当人们坚持犯错的权利时,神秘主义者会扮演绞刑师的角色。