普通视图

发现新文章,点击刷新页面。
昨天以前首页

Maintainer 的标准

作者 tison
2022年9月12日 08:00

开源社群存在的目的,主要是制造高质量的开源软件,并促进该软件的使用。为了达到这两个目标,开源社群需要调动参与者的积极性,并且协同背景多样的参与者的贡献,共同修复软件缺陷、改善软件体验、增加软件功能、组织社群活动和发展软件生态。大多数开源社群的环境里,实际进行组织协调工作的成员,就是社群的维护者(Maintainer)。

不同开源社群对角色的定位和命名有着各自的风格。Vim 社群生态丰富,但是 Bram Moolenaar 是唯一的“仁慈的独裁者”Kubernetes 制作了一套基于 SIG 划分的从 Reviewer 到 Appover 再到 Owner 的体系。Apache 基金会的每个项目都使用 Committer + Project Management Committee 的治理结构。Rust 和如今的 Linux 采用分模块的 Team Maintainer 模式。PostgreSQL 则由整个项目级别的 Core Team + Committer 来治理。

对于刚起步的开源项目而言,这些眼花缭乱的标准背后,其实是一个大致相同的对项目维护者的标准。对于想要深入参与开源社群的人来说,理解了项目维护者的标准,也就明白该做些什么以成为一名维护者了。本文主要对这个标准的不同层面进行讨论,顺带对比上面这些经过演变的不同版本。

做出承诺的人

开源社群是围绕开源软件建立起来的,开发软件是开源社群主要的生活内容。虽然开源软件在开源协议的许可下允许任何人用于任何用途,但是想要参与软件开发,向上游版本提交补丁,在合并之前则通常需要维护者的评审。这也是绝大多数开发者对社群维护者的第一印象:维护者是少部分具有提交代码到主分支的权限的人。

由于 Git 的 commit 命令跟代码提交相关联,且许多开源社群的维护者都有 Committer 头衔,于是开发者就简单的把维护者等同于有提交代码权限的人。其实,commit 的本意有做出承诺的意思,把 committer 解释成做出承诺的人会贴切一些。社群成员承诺在代码、文档、设计或组织活动等方面做出贡献,并且已经坚持一段时间如此,当前的维护者群体跟这名成员合作愉快,于是吸纳为团队的一员。

这种语言体系能够囊括不同类型的贡献参与,同时强调了为社群做出贡献的衡量标准,而不只是写了一段好的代码。

亲力亲为赢得权威

开源社群的维护者都是对社群做出承诺的人,这种承诺不是口头说说,也不是赌咒发誓将来会做,而是实实在在的已经做出相应的贡献。

The Apache Way 的第一条就是 Earned Authority 赢得权威,或者说,权威是参与者亲自赢得的。开源社群对项目维护者大致相同的标准,也就是能够亲力亲为赢得权威这一点。

比如,我从 Flink 使用 Curator 的切入点进入到 Curator 的社群以后,积极地参与 Curator 补丁的评审。在阅读 Curator 代码的过程中,我按照遗留代码的 TODO 注释完成了重构,又出于自己的需求实现了新的重试策略。这些表现让 Curator 的维护者团队(PMC)认为我是一个不错的维护者候选人,于是在某天早上,我收到了邀约邮件并欣然接受。

比如,@PragmaTwice 通过改进 CMake 构建逻辑的契机进入 Kvrocks 社群之后,持续发起并主导了一系列的改进,包括对 TLS 的支持。同时,他也积极评审其他参与者提交的补丁,并且对自己完成的工作不尽完善的地方能够及时响应追加补丁。他在项目需要的技术方向表现的能力和对社群发展的贡献赢得了当前维护者的一致认同,就在上周,Kvrocks PMC 通过决议邀请他成为 PMC 的一员。

比如,我在腾讯同期的朋友早年参与 Kubernetes Dashboard 项目,边改逻辑边做文档的中文翻译。成为部分模块的 Reviewer 之后,由于种种原因不再活跃,也就没有进一步成为 Kubernetes 体系下的 Approver 角色。

亲力亲为赢得权威这条看似简单唯一的标准,其实包含了几个层面的高要求。

第一点,必须是本人的参与。Earned Authority 可以衍生成 Earn Authority by Contributions, not by the Position. 这就是说,赢得权威的贡献都是你自己做成的,而不是因为你在公司的下属是某个项目的维护者,你是他们的老板,因此你就继承了他们的权威。

这一点在公司主导的项目体现得尤为明显。无论贡献属于个人怎么被强调,社群当中 $dayjob 就跟开发这个软件相关的人,无论如何都会受到老板的影响。这本来是一种公司利益借由雇员表达的正常手段,但是如果想要把公司里的 credit 直接平移到社群的 credit 上,就会导致一些非公平的结果。例如,当 PingCAP 想要为 TiDB 引入关键配置 double check 的 config reviewer 的时候,下意识的就把各个团队的 leader 直接列上,其中甚至有刚加入公司不久的员工。我好歹用“领导也不希望处理这些日常事务”给应对过去了,但是其实还有一点没有提及,那就是今天是因为他是团队 leader 给予了 config reviewer 的权限,明天转岗或者离职了怎么办呢?难道角色也跟着平移,开源社群只是说说而已,实则是公司的一个部门吗?

第二点,必须有的放矢地创造价值。这实际上是对参与者能力的考量。你不会仅仅因为修了一千个拼写错误就成为项目的维护者。虽然开源社群认可代码、文档、设计或组织活动等等多样的贡献,但是作为维护者有决策项目接受什么变更,举办什么活动,以及未来的发展方向。为了保证项目尽可能不受外行领导内行,拍脑门的决策的影响,吸纳新的维护者的时候,对候选人在对应领域已有的贡献和能力会有一个隐形的评估。

这一点也意味着参与开源社群的时候,不能一味地求多,还应该求精。上面提到的修正一千个拼写错误大部分人能够理解,但是为了刷一个项目层面的重要贡献,以自己非专业的能力强行做专业的事情,也往往会引起维护者的负面评价。

比如自己从来没有写过一行代码,却一上来就要在社群里推行新的代码风格或者工作流程。由于没有亲身参与过软件开发的过程,对这个社群的风格及其主要成员的关注点的认识有偏差,想象出来的问题和解决方案往往也是不切实际的。

我在 PingCAP 推行过 Pull Request 必须对应 issue 的策略,现在回想起来就是一个不合适的做法。虽然当时我也知道“必须”是过了,总会在推行过程里引入各种各样的折衷,但是一方面我没有把这件事情彻底做完,另一方面其实这一个打点并不犀利。实际问题是开发者提交补丁和记录问题的时候缺少上下文和交叉引用,以至于大家都在写代码,但是却往往只关注自己当前就在写的这一个补丁,这在复杂项目当中是行不通的。

当然,这不是说没有亲自写过代码就不能提出建议,毕竟参与是多样性的,对于软件工程的老手来说,有些问题只一看也是明显的。不过在实际提案和实行的过程中,以商量的态度跟实际会受建议影响的人竭诚沟通,实地考察和验证提案的影响,最终在当前维护者团队的支持下实施。换句话说,你只能发现和解决已经存在的、社群成员承认的问题,而不能因为要解决某个问题是 OKR 的一部分,例如提高代码测试覆盖率、降低代码圈复杂度,而强推新的标准或流程。

对于代码开发来说也是一样。Twice 在 Kvrocks 提交补丁的时候就遇到过一个新成员主动 review 他的代码,但是所提的问题却非常初级。一个利用新的 C++ 特性改进代码质量的补丁,这位成员不了解 C++ 的标准细节,就提出一些想象出来的边界情况。虽然我赞同不懂就问,但是提问请教的措辞和 request changes 的措辞给到维护者的印象是不一样的。另外,社群成员都不熟悉的领域和都应该熟悉的领域里的措辞也是不一样的。

我在 Kvrocks 也拒绝过两个参与者。一个是说着要帮助改进测试,但是却来了一句“你能告诉我具体要怎么做吗,我不知道要干嘛”的。我差点回我不是你的保姆,你能干就干,不懂可以问,不知道问什么可以不做。另一个是要改进 Kvrocks 传参逻辑的,这种重大的用户界面变更没有任何设计直接就怼了一个用脚本缝合的 monkey patch 来,我只能建议先想清楚要做成的使用方式是什么样的再来。

反转的例子也有。我在 Curator 提出的修复分布式选主模块的活锁问题的时候,一位 Flink Committer 老哥上来指点江山。一开始,我看他都没注意 PR 内容是啥就泛泛而谈 PR 拆分,以前也没见过这人,就不客气的回了一句“你告诉我怎么拆分”。后来他也先道了歉,然后仔细看了补丁内容,给出了不错的评审意见和测试用例,我也认同他是一个有能力的开发者。如果后续有持续的参与,有可能会提他做维护者。

这就引出第三点,必须持续参与以赢得权威。开源社群作为由参与者组成的社群,是一个有机体。这就意味着它的发展是连贯的,而不是由一锤子买卖形成的。

Twice 将 Kvrocks 的构建系统从 Makefile 迁移到 CMake 之后,并不是做完就算了,而是后续还有其他成员包括他自己踩出来的问题,还有新功能加入是跟 CMake 整合的需要和进一步带来的新问题。如果 Twice 没有为他的补丁负责而是做完就算,如果社群当中也没人接过维护的职责,那么这份 credit 也就自然消散了,说不定还需要其他人回滚这部分变更。

比如 BookKeeper 社群想要把构建系统从 Maven 换成 Gradle 以利用增量编译的好处,以及想要把网站迁移到新的架构上面去,这些工作都半途而废了,最终社群维护者需要付出额外的努力来回滚,最初做出这些变更的人自然也算不上赢得权威。又比如,TiKV 的 VerKV 方案没有经过仔细的评审,因为一些外部原因强行推进,但是从来没人正式使用也没人响应问题,到头来维护者清理相关代码也花费了不少时间。

不同项目在不同阶段对参与时间的要求自然是不一样的。新生的项目一片勃勃生机、万物竞发的气象,亟需扩大维护者的队伍来响应参与者提交的补丁和反馈建议,往往在一个水平不错的参与者持续贡献一个月或三个月后就开始考虑邀请成为新的维护者。成熟的项目内容复杂,哪怕掌握一个模块及其相关知识也要花费相当的时间。为了避免在不够了解项目和模块背景的情况下新的维护者做出冒进的决策,往往会延长到半年、一年乃至数年的考察周期。

比如 PostgreSQL 在 EDB 的收购案之前,Core Team 的五名成员加上二十几名 Committer 基本能够响应 PG 社群内所有的事情,直到收购案导致 Core Team 成员多样性不足,才推动引入了来自其他公司的两名新成员。这两名新成员都是 PG 社群十年以上的长期参与者。

反过来看另一个极端,Raku 语言还叫 Perl 6 的时候,它的成员吸纳策略就是任何对项目有兴趣的人,只要提交若干个补丁并被合并,哪怕只是文档的修正,也会给予这个人整个项目除了解释器以外的维护者权限。这样的策略让它在短时间内建成了两百余人的维护者团队,并且其中十几个非常热情的参与者领导完成了测试、文档和工具库的开发。这种策略有一个前提,那就是项目必须事实上有一个仁慈的独裁者或者精英团队,才能在低门槛吸纳的新成员做出主观恶意的行为或者水平有限却热衷于提出关键改动的时候予以制止甚至踢出。如果社群没有这样众望所归的人,那么一旦有新成员的理念与项目原本的主旨背道而驰,就容易反客为主,吞噬原本的社群。

参与社群事务

如果说 Apache 区分 Committer 和 PMC Member 有什么标准的话,那就是看社群成员多大程度上会去参与社群事务了。

相当部分的社群成员来到社群,只是为了提问和得到解答。其中有能力解决自己问题的成员,如果能够持续的提交高质量的补丁或者撰写文档,则有可能被维护者授予对应仓库的权限以简化他参与的流程。但是,尤其是出于工作需要而参与到一个开源社群的成员,往往并不关心其他人在干什么,也不关心项目的未来、生态的发展和软件的影响力。如果某个开源社群对维护者团队设置具体的角色,那么这样的成员就不可能成为整个社群层面的维护者或者所谓核心团队(Core Team)的成员。

对社群事务的关注和参与,包括但不限于:

  1. 回答用户问题,为软件站台。这点非常重要,一下子可以把只是 $dayjob 跟本软件相关的人筛选出大半。许多打工人只是工作需要写点代码,恰巧这些代码需要写在开源软件上。他们不会关注软件社群里其他用户的问题,因为“这跟我有什么关系呢”。而社群的维护者是对社群生产的软件有极高的认同感和责任心,并且自觉有义务推广它的使用的人,自然会关注使用软件的人碰到的问题,解决问题促进使用,总结问题看看软件哪些方面还有不足。Flink 的参与指南就明确把 Support Flink Users (User Mailing List, StackOverflow, Jira, etc.) 和 Spread the Word About Flink 作为社群贡献的一部分。
  2. 参与软件版本发布。越是复杂的软件,发布流程和发布前的测试、校验就越是复杂。Flink 社群把参与发版作为邀请成为 PMC Member 的重要加分项。虽然一般来说只有当前的维护者群体才有最终发布软件的权限,但是作为社群成员,帮助测试和校验,提供反馈,或者和当前的发布团队(Release Manager)沟通参与构件的准备和 blocking issue 的处理,都是为交付软件做出的重要贡献。对于大部分用户而言,不稳定的主分支上的代码是不能在生产环境使用的,只有经过 PMC 认证投票通过发布的版本才是可信赖的版本。
  3. 建设社群基础设施。有些开源社群,例如 Rust 和 Kubernetes 会有专门的团队负责建设和维护基础设施,包括仓库权限和各项功能的设置,CI 流水线的架设和资源使用的监控,网站构建和部署的自动化,等等。对于没有那么复杂生态的社群,这些工作仍然需要有人做,做好了对社群开发、迭代和宣传的效率有明显的提升,而大部分只关心写核心代码的人很少会关注到基础设施的建设和维护。因此充分理解社群的目标和参与到社群日常生活的基础上,愿意承担基础设施的建设和维护工作的成员,在维护者团队考虑吸纳新成员的时候会有相对的优势。
  4. 关注艰苦和乏味的工作,又能领导重要的功能扩展的设计实现。这两点稍微有些冲突,出自《大教堂与集市》 3.13 节“什么才是好礼物”。一方面,社群维护者对项目的责任心促使他能够去做艰苦和乏味的工作,比如极难调试的软件缺陷、修复不稳定的测试、编写技术文档和优化开发构建流程,等等。另一方面,高质量的项目之所以是高质量的项目,不是因为流程良好随便栓条狗都能自动写出来,高质量的项目根本上是由极具才能的工程师写出来的好代码造就的。Flink 的流计算 API 的作者,Pulsar Functions 的作者,都毫无疑问的是对应项目的维护者。
  5. 指导新人,传递知识。开源社群合作开发的一大优势就在于能够最大程度的避免知识单点故障、知识孤岛和知识断层的现象。为了做到这一点,社群维护者积极指导新人,传递社群工作方式和软件专有知识是必不可少的。即使社群就软件使用、设计和社群运转的规则特供了详尽的文档,经过人的连接解释和畅聊一遍,给到新人的体验和理解也很不一样。基本上,每个新的维护者都有自己的领路人,而此后他也会成为其他人的领路人。

关于第四点和“社群事务”之间的关联,我再多做一些讨论。

艰苦和乏味的工作,实则是一个维护者对开源社群当中开发者体验设身处地的考察。如果发现和修复不稳定测试有一套相对可靠稳定的方法论,那么这项工作未必是艰苦和乏味的。能够耐下性子来做大家不愿意做但是对项目的易用性或开发者体验有帮助的工作,很难不说体现出一种对社群的责任心。也只有亲自体验过这些工作为什么艰苦和乏味,才有资格推动流程优化。否则只是捧着软件工程书本上的说辞,甚至拍脑袋想出来的点子,越积极推动,对社群的负面影响反而越大。

重要的功能扩展的设计实现,进一步说是软件的未来要往何处走。比如 Flink 的流计算 API 显然从根本上影响了 Flink 项目未来的走向,Flink Application Mode 部署模式的推出和对 Kubernetes 的支持则显著扩张了 Flink 应用在生产环境的使用场景,也让 StreamPark 项目的作者下定决心做流计算应用开发、部署和监控平台。一个深度参与到项目未来发展的成员,不是维护者也说不过去。

很多人在我今年成为 Apache 软件基金会的正式成员以后,问我成为 ASF Member 的诀窍是什么,这跟这里提到的参与社群事务是一致的。因为我相信 The Apache Way 的理念,在中文世界里大力推广 Apache 的开源理念;不止参与一个 Apache 项目,而是积极推动不同项目之间的联动,例如 Pulsar 踩出了 Maven Shade Plugin 的问题,就在上游修复;参与到孵化器项目的讨论。如此反复,当前的 ASF Member 认同我在基金会层面的协同的开源理念传播的贡献,再由姜宁老师提名,成为 ASF Member 也就水到渠成。

我参与 Flink 社群并成为 Committer 花了一年左右的时间,期间完成了对 Runtime 代码的重构并修复了数十个 Runtime 的并发问题,也主导完成了 FLIP 的设计、发起和实现。但是我对 Flink 的发布和代码以外的工作不是特别关心,后来因为工作内容的变化,也没有太多参与到社群事务当中去,于是成为 Committer 三年以来也就没有被邀请成为 PMC 的成员。反过来,我在成为 Curator PMC Member 以后,把 Curator 积压的 Jira 问题和 PR 处理一空,并作为 Release Manager 发布了 5.3.0 版本,参与完成了对 ZooKeeper 新版本新功能的接入兼容。

这就引出在讨论 Maintainer 的标准后的最后一个点:邀请谁作为 Maintainer 要基于对他已有工作的评估,而不能基于假设;但是 Maintainer 的标准对于大部分社群来说最好也不要太高,很多人在成为 Maintainer 之后会爆发出超人的热情。

前者出自我在 PingCAP 的时候回答当时 TiKV Team Leader 问为什么不能直接给员工写权限的问题。现在看来,其实公司项目上这么做也不是不行。但是就相对中立的开源项目来说,你给予员工写权限,只是因为你基于劳动合同和面试流程,认为他会在未来做出足以匹配项目维护者的贡献和赢得权威。这对于其他背景的社群成员来说只是个说法而不是已经实现的参与贡献。对于全职员工来说,每天大部分工作都与开源软件有关,其实已经是非常大的优势了,只要能够按照社群理念积极参与合作,数个月内成为维护者不是什么大问题。相反,如果基于我当时从事 Flink 工作,持续以当时的强度参与一年也能达到 PMC Member 的标准就邀请我,那么后来我工作转变投入减少,这种提前兑现的权威就很容易造成对其他成员的不公平。某些开源项目的维护者基于对社群成员未来的期待授予权限,但是社群成员随后因为种种原因,比如就是想混个头衔进简历,马上就神隐,这个时候邀请新人的维护者就破防了,这就是基于对未来的期待会导致的问题。

后者,对于真心想要打造优秀社群的参与者,他们是天生的技术领袖、社群领袖,如果能够准确的评估他们当前贡献表现出来的责任心和能力,而不是拘泥于僵化的贡献数量指标,这些人在成为 Maintainer 之后可能会爆发出超人的热情。比如 Twice 一开始可能只是听我提起 Kvrocks 需要帮助,因为他刚刚已经在 OneFlow 做过类似的事情了,正想要推广自己的实现。但是社群在他能把这件事情做好并承担后续维护责任后果断邀请成为 Committer 一员,我想对他的激励也是明显的。再加上社群当中其他维护者以身作则回答用户问题和协同参与者的贡献,他在不是 PMC Member 的时候也承担了 PMC Member 的职责,我们就认为应该让他成为 PMC Member 以帮助他做出更大的贡献。

对于维护者的门槛,Apache Incubator PMC Chair Justin Mclean 还有这样一段话:

IMO setting the bar to be a) signficant feature contributions or b) long-term participation in community building is too high. You want to be again to give committership to people after a short time to encourage people to make further contributions, not to get a goal that only some people will reach. Remember, not everyone will be able to work full time on your project due to their time zone, day job, family commitment, or other factors. While each project is free to set the bar, please don’t make the mistake of setting the bar too high.

开源社群的协同模式像是《合作的进化》当中提到的志同道合愿意合作的小群体,在复杂世界当中生存下来并不断通过合作产生 1+1>2 的效果扩张群体范围。虽然对参与者的热情和能力的评估是有必要的,但是开源协同更多的是基于善意的假设。就像《合作的进化》当中提到的一样,如果倾向于背叛的人太多,那么最优策略就是你也选择背叛,但是如果开源社群一开始就有一个选择合作的领导核心和运行理念,那么即使吸纳进来选择背叛的新成员,他也会很快因为得不到收益而离开。

最后罗列一些关于 Maintainer 的标准、职责和权利的参考资料。

共同创造价值

作者 tison
2022年2月10日 08:00

如何吸引开源开发人员参与项目?如何让他们留下来,成为项目共同体的一部分?这是两个做开源运营必须回答的问题。

我对这两个问题的回答,简而言之是和开源参与者共同创造价值,使得开源项目和开源共同体能够回答潜在参与者的两个事关去留的灵魂提问。

  1. 我能为你做什么?
  2. 我应该怎么做到?

从共同创造价值的角度出发,通过开源运营回答参与者可以做什么的问题,只有可做的事情是令人兴奋的价值创造,才有可能触发潜在的参与者的兴趣。进一步,只有潜在的参与者能够在文档材料与其他成员的帮助下共同完成价值创造,这样的正向激励才能让参与者留下来,成为项目共同体的一部分。

本文内容部分整理自我在开源社举办的 COSCon’21 活动上的主题分享《Why Contributors Stay and Grow》的第二部分。

我能为你做什么?

考虑一个开源开发人员接触项目的典型旅程。他首先要知道这个项目的定位是什么,以决定是否进一步了解它。

如果项目的定位看起来很有趣,或者像是正在解决他希望解决的问题,那么进一步激发他参与贡献的诱因,就是他发现可以为这个项目做点什么。

如果这个项目复杂到无从下手,没有任何引导回答“我能为你做什么”这个问题的入口,那么大部分潜在的参与者都会选择直接放弃理解,也就无法参与。如果这个项目简单到不需要添加什么功能,例如 LineNoiseatoi-rs 等等,或者稳定到没遇到任何迫切的新需求,例如 ZooKeeper 等等,那么大家已经用得特别开心,也就少有参与贡献的动机了。当然,后者或许是件好事。

我们考虑一个蓬勃发展当中的项目,它足够复杂以不断产生新需求,它又很有活力以致力于把问题解决得足够好。在这样的项目里,潜在的参与者通常能够为项目做什么呢?从这个角度出发,也就能够知道哪些是可能的动机触发点,以拓展共同创造价值的故事。

代码之内的参与

开源项目最终生产的是开源软件,很容易想到围绕软件代码参与贡献。典型的代码贡献可以分成这四类。

  • 评审代码
  • 修复缺陷
  • 改进实现
  • 增加功能

首先要讲的是评审代码。

这是一种经常被忽略的参与动机。因为相当部分开源共同体,总是考核并引导参与者自己写代码和提交补丁,只把写代码当成贡献,而将评审代码视作一种不得已而为之的成本,甚至当做是 committer 和 maintainer 的特权。

这当然都是不对的。

实际上,code review 是开源开发人员之间交流技术和建立信任的主要手段。大部分软件的生产过程中,解决问题的办法往往不止一种。每个项目会在众多可能性当中选择自己认可的技术实践,形成自己的调性。通过代码和文档,以及生产代码和文档的过程,这种调性以共识的形式从核心成员同步到每一个参与者的认知当中。这里所说的生产代码和文档的过程,code review 就是其中重要的一环。

对于潜在的参与者来说,参与代码评审并提出问题,门槛较之提交补丁整体要低一些。当然,评审代码并留下意见建议,很多时候要求 reviewer 对软件的理解比补丁作者本身要更高。然而,这里所说的参与代码评审并不局限于针对补丁的实现提出形如补丁的补丁的意见建议,而是强调围绕补丁代码观察和讨论本身。

例如,PostgreSQL 共同体的 CommitFest 就允许任何参与者以 reviewer 的角色评审待合并的补丁。合并代码的动作自然只能由经验丰富的 committer 来执行,但是任何对这个补丁感兴趣的人,都可以参与评审。你可以针对实现提出问题,可以针对文档提出建议,可以本地测试补丁并回报结果,可以就自己不理解的地方,遵守提问的礼仪提出问题。当然,如果某个补丁解决了你的问题,或者实现得非常精彩,不要吝啬感谢、赞扬和鼓励。

TiDB 开发者指南当中有专门的一个章节,告诉潜在的参与者他可以通过评审代码的方式为开源共同体做出贡献。其中关于撰写 review comments 的建议值得在这里分享。

  • Be respectful to pull request authors and other reviewers. Code review is a part of your community activities. You should follow the community requirements.
  • Asking questions instead of making statements. The wording of the review comments is very important. To provide review comments that are constructive rather than critical, you can try asking questions rather than making statements.
  • Offer sincere praise. Good reviewers focus not only on what is wrong with the code but also on good practices in the code. As a reviewer, you are recommended to offer your encouragement and appreciation to the authors for their good practices in the code. In terms of mentoring, telling the authors what they did is right is even more valuable than telling them what they did is wrong.
  • Provide additional details and context of your review process. Instead of simply “approving” the pull request. If your test the pull request, report the result and your test environment details. If you request changes, try to suggest how.

另外,代码评审并不局限于补丁合并之前,也不是必须发表评论。

如果一个已合并的补丁激起了你的好奇心,或者你发现了其中存在的问题,完全可以 review after commit 即合并后评审。这对于高速发展的项目来说尤其重要,因为它们往往会在较短的时间窗口内合并补丁,乃至直接向主分支推送代码。代码评审不是形式主义,不像某些极力推行某种流程的人所宣称的必须在合并前收集到两个赞同才能合并,务实的评审和合并策略应该是持续发生、交错进行的。

不是必须发表评论,意思是所有的参与者在提交代码之前,都应该了解他所要参与的这个开源共同体的惯例,也就是常说的“入乡随俗”。这个过程一般也是通过浏览现有补丁来完成的,其实也算是评审代码的一类。我在撰写提交信息和实际提交补丁之前,往往都会先观察共同体当中的其他人是怎么做的,避免不必要的摩擦,提高协同的效率。如果当前流程有明显的缺陷,也是一个潜在的参与点。

修复缺陷、改进实现和增加功能,这三种都是代码贡献。

理想情况下,潜在的参与者具备代码贡献所需的技术背景,在使用软件或阅读代码的过程中,自己发现可能的改进项,仿照现有的 pull request 风格提交补丁。

这种情况也不算少见。常见的案例是参与者掌握了一项新技能,例如一种新的代码风格或设计模式,一种项目管理或持续集成实践,或者一种具有一定普遍性的功能,从而把已经完成的工作,带到其他缺乏这个改进项的项目当中。具体的例子,比如我在看到我的工作里引用的 atoi-rs 项目,没有按照 Rust 项目的惯例配置支持的 Rust 版本,同时功能开发趋于稳定但却没有发布 1.0 版本导致我不是特别敢用,就通过 issue 和 pull request 的方式直接把我掌握的技能复刻到该项目当中。

上面的例子里有一点值得强调,就是当潜在的参与者不确定能够为开源项目做什么的时候,可以直接询问。当然,这种询问应该是有的放矢的,询问之前应该对项目做基本的了解,而不是居高临下的要求维护者准备好甚至生造出待完成的工作并交给自己。

反过来,从项目维护者的角度出发,回答代码贡献层面潜在的参与者可以做什么的问题,能够采取的方式包括以下几种。

第一种,也是 GitHub 原生支持和倡导的方式,就是为 issue 标注 good first issue 或 help wanted 标签。我在夜天之书 #7 一文里讨论过这两种标签的使用方式。绝大部分 GitHub 的用户的心智模型能够适配这两个标签,并且首先会尝试寻找 good first issue 或 help wanted 标签标注的 issue 作为参与的切入点。

第二种,以 tracking issue 的形式组织起待办事项。通过将零散的 issue 和 pull request 按照主题和模块组织起来,以类似于重构和组织代码的形式管理 issue 和 pull request 来降低潜在参与者的理解成本。

这种做法全面推行,就会在顶层形成一个项目的路线图。例如 Flink 的路线图就把项目的模块分得比较清楚。每个模块现在是稳定状态,快速开发状态,还是原型状态也会标注出来。进一步的,列出每个模块当前正在进行的 Flink Improvement Proposal 和背景,开发人员就可以从这个入口了解相关工作,再从 proposal 关联的 tracking issue 参与到开发工作里面来。当然,这个过程里还有许多可以做代码之外的参与的切入点,这里不做展开。

路线图的更新频率比较慢,日常开发工作里接触得多的是 tracking issue 和对应的改进提案。成熟项目基本都会有一个完整的提案流程,从“我能为项目做什么”的角度出发,这个流程里包含的信息应该可以教会潜在的参与者分辨不同阶段的提案,并且理解当前阶段提案发起人需要得到哪些帮助。另一方面,关于 tracking 的组织,我做过一个视频《如何在开源社区做项目管理?》具体讨论的实践手法。

这些手段的最终目的是一致的。因为开发活动过于琐碎,单个开发活动的价值很难吸引潜在的参与者付出时间和注意力自己补全所需的细节。这种情况下,作为项目维护者和开发活动的发起人,应该尽可能地从多个层面以人能理解的方式把这些具体的开发活动归纳总结,以提供不同详略程度的内容呈现。

如同前文提到的,这个过程类似于重构和组织代码。你不能指望开发人员一上来就盯着每一行代码看,这样低效率的理解项目的定位、设计和发展方向。同样,你也不能指望潜在的参与者从大量琐碎的开发活动里归纳出项目共同体正在做什么,接下来要干嘛。只有把开源项目的开发活动模块化和主题化,才有可能把理解和参与的门槛降低到当前时代下潜在参与者所愿意付出的精力的范围之内。

这其实不止是开源项目的问题,而是一个普遍的项目管理的问题。把潜在的参与者换成项目团队的萌新成员,推理过程和结论一样成立。项目维护者采取这样的做法,也不是全然无私奉献,而主要是控制软件工程由于复杂度增加带来的熵。这对于项目本身的健康快速发展也是有利的。

代码之外的参与

开源共同体虽然是围绕开源软件形成的,但是可能的参与形式远不止于代码贡献。The Apache Way 经常被人引用的一点就是“共同体高于代码”。

Community Over Code: the maxim “Community Over Code” is frequently reinforced throughout the Apache community, as the ASF asserts that a healthy community is a higher priority than good code. Strong communities can always rectify problems with their code, whereas an unhealthy community will likely struggle to maintain a codebase in a sustainable manner.

其实,这里的 community 是包括 code 的,两者并非互斥关系,只是强调决策的基点是共同体的利益,而不只是关注代码。不过,我们首先看到开源共同体当中代码之外的参与形式。

第一类是使用。

开发软件的最终目的是投入使用,因此,使用软件并报告反馈,本身就是参与贡献。我见过不少数据库开发人员,并不了解数据分析师和业务开发人员等具体用户到底是怎么使用数据库的,生产环境里最常见的用法,最常被使用的 SQL 特性,沉浸在内核开发当中的工程师未必能够非常准确的把握。通过测试软件在生产环境下的表现,使用真实业务负载验证软件最终交付的价值,对开源软件的打磨价值都是不可忽视的。

TiDB 共同体非常重视用户使用。“我们已经用起来了”,是他们最喜欢听到的话。TiDB 的 AskTUG 论坛上每天都有海量的问题。这些问题大部分都比较初级,但是初级问题反复出现,意味着开源项目的文档建设和知识库建设不够充分,缺少一个快速上手的文档和常见问题的列表(FAQ)。除去这部分问题,进阶的问题能够描述清楚一个具体的使用场景和遇到的障碍。这类问题往往可以使用某种技巧绕过,这些技巧集合起来,就是使用软件的最佳实践。

前面提过,开发软件的最终目的是投入使用,而软件最终投入生产是一个复杂的过程,任何一个环节无法满足需求,没有绕过方案,都有可能导致用户放弃使用。而任何软件刚刚发布的时候,都是存在这样那样的问题的。通过使用软件并报告反馈,其实就是常说的“踩坑”过程,逐步把软件使用的“坑”给填平,开源软件触达更多用户也就成为可能。为什么 Java 生态比 C# 生态好这么多?为什么 Python 生态比 Elixir 生态好这么多?相当一部分原因就是在漫长的用户使用过程当中,许多未来用户可能遇到的问题,都被先行者给解决了。

对于具备技术能力的开发者来说,使用上游软件并发现问题,还有可能是深入参与的契机。例如我在改进 Apache Flink 的高可用模块的时候,深入理解了 Apache ZooKeeper 和 Apache Curator 的设计实现,并以此为契机为两个项目做出贡献,并成为 Apache Curator PMC 的一员。

另一方面,上面提到的旅程当中,除了使用者以外,还有一个角色就是答疑者。只有少部分提出问题的人,能够自己解决问题。大部分提出问题的人,都需要有另一个经验更丰富的共同体成员协助解决问题。

AskTUG 论坛有版主机制,赋予积极答疑并愿意承担一定协调责任的成员版主的头衔和权限。通过开放合作,AskTUG 论坛聚集起近十位版主,为解答 TiDB 系列产品用户遇到的问题发挥了中流砥柱的作用。可以从《TiDB 社区版主,一群平凡又伟大的 TiDBer》一文当中窥见一二。

Apache 基金会治下的项目共同体,往往也强调承担答疑职责的重要性和共同体对这种行为的认可。例如,Apache Flink 项目对 committer 候选人的第一个期待,就是能够积极回答用户问题

Community contributions include helping to answer user questions on the mailing list, …

第二类是发布。

发布一个软件并非易事,《Perlbrew 中文简介》一文中介绍了 Perl 5.8.8 到 5.10.0 版本之间的发布工作燃尽了两位顶级黑客的精力和热情的故事。

这个故事以发布流程的改进为结局,后来的 Perl 版本发布短到一个月内就可完成。不过,发布这一活动作为开源软件生命周期的重要一环这个事实,却从来没有改变过。PostgreSQL 共同体每次发布都会有一个两到三人组成的临时发布团队,并且整个发布流程都被记录在 Wiki 文档里。我在夜天之书 #20 The PostgreSQL Community 里有一整段详细讨论。Engula 项目第一份开发者文档,就是发布指南Apache 项目成熟度模型里,也对发布的质量详细区分了五个等级。

软件的发布是持续交付或叫持续部署的一环,涉及的专业知识不比开发软件核心功能少。一个项目有可靠的持续交付,下游用户才能基于它构建起繁荣的生态。

举个例子,我在分析 TiDB 软件工程上的问题的时候,就提出过发布过程和产物不够清晰,版本支持策略未定义等问题。现在的 TiDB 不像 Apache 项目有明确的文件服务器和稳定获取发布产物的地址,而是依靠工具来完成部署。耦合发布和部署不是一个好选择,尤其是在上一个部署工具生命不过三年新工具生命不过两年的的情况下。Bytebase 支持 TiDB 的旅程当中,虽然不乏对部署工具的赞许,但也指出了新工具只支持新版本的缺点。由于耦合,部署工具不支持,发布产物也就难以获取。另外,文中也直接明了地提出了版本策略不清晰带给下游的问题。

随着开源吞噬软件,融合不同组件的解决方案在交付时如何进行合规审计和安全审计也是一个日渐复杂的问题,这些问题都归属于发布这一开发活动当中。如果你是一个掌握软件持续交付经验的人才,从这个角度切入参与开源共同体将是非常犀利的,这也是当前不少开源项目求之不得的人才。

第三类是内容。

围绕开源软件开展内容创作,是几何级数乃至指数级数壮大开源共同体的秘诀。内容创作可以大体可以分为四个类型,即文档、博客、演讲和演示。

文档是最贴近开源软件的内容创作。实际上,不少开源软件把用户文档作为版本交付的一部分。TiDB 的用户文档经常受到赞扬,Kubernetes 的共同体文档也被其他开源共同体用作治理和运营的参考材料。

类似的例子数不胜数。一位经验丰富的技术写作者在交流过程当中曾经提到,好的内容创作能够简明扼要的抓住潜在参与者的注意力,在这个信息爆炸的社会当中为开源共同体赢得劳动时间的投入。

好的用户文档获取用户的注意力,提高软件普及程度,扩大开源共同体生存的基本面。好的开发者文档获取参与者的注意力,减少参与贡献过程当中的摩擦损耗,提高协作效率。好的愿景和价值观,能够聚拢起志同道合的参与者长期为项目发展持续做出贡献。

高质量的文档,必定基于对核心开源软件的理解,对受众的共情,以及文字写作的功底。Apache Pulsar 的 PMC 成员 Jennifer Huang 正是出于为 Pulsar 共同体创作技术文档等内容而收获认同的。

博客可以认为是文档的延伸。它是不那么正式的,具备个人风格的内容。同样,许多知名的开源项目,在文档之外都会维护一个博客列表。开源共同体当中也会传阅列表之外的优质博客。例如,我一下子就能想起来 Flink 相关的两篇让我获益匪浅的博客。

类似的例子也比比皆是,我在撰写 This Week in TiDB 期间也多次引用了共同体成员发布的高质量博客。博客的下一步就是书籍,例如 Eric S. Raymond 为 Linux 共同体做宣传的《大教堂与集市》。很明显,这些内容的传播效率较之软件代码、改进提案和用户文档本身,具有更好的传播效果。这一点还体现在中文社群当中多有对知名开源项目提案的解读上。

演讲的传播效果较之博客又要更进一步,因为它不需要受众阅读并理解文字的含义,而是通过视听体验直接获得感觉。Linus 的经典演讲相当之多,他在这些演讲当中表达的观点为 Linux 项目聚拢起志同道合的核心成员发挥了重要作用。

Flink Forward 大会,KubeCon 大会,COSCon 中国开源年会,越来越多开源共同体举办的峰会集中输出开源文化和开源软件的价值,以影响整个软件行业对开源软件的认识和使用。作为开源世界的一员,无论是参与演讲还是运营支持,以至于发起峰会,都是对开源共同体壮大的重要贡献。

最后要讲的是演示。演示可以融入到上面提到的三种方式当中。例如,文档可以加入快速开始的演示项目,上面提到的《基于 Flink SQL 构建流式应用》博文本身就是一个示例应用的讲解,演讲当中也经常插入演示环节增强表现效果。

单独谈论演示的原因,自然是因为它非常重要。在信息爆炸、注意力稀缺的今天,短视频以其能够快速抓住眼球,从而大量占用用户时间成为应用新贵。演示起到的就是一个类似的效果。例如,Perl 的代码诗在相当一段时间内吸引了众多开发者的兴趣和尝试。例如,TiDB 的热力图活动既强调了 TiDB 重视可观测性的调性,又足够有趣以吸引潜在用户试用。例如,太极图形的太极开物宣传视频就将它在计算机图形学领域的先进性和应用价值生动的传达给每一个观众,相当惊艳。

在软件开发越来越趋于复杂的今天,能够通过演示为开源共同体赢得更多的关注乃至劳动时间的投入,就是为开源共同体做出的重大贡献。

再谈代码贡献

如同我在《Why Contributors Stay and Grow》演讲里最后要回过来强调的一样,虽然比起代码贡献,代码之外的参与种类繁多,并且能够达到的传播效果远超代码贡献本身,但是开源共同体的价值,始终是建立在优秀的开源软件之上的,而开源软件,说到底还是代码支撑起核心功能,编译产物是交付物的主要内容。

我们在强调 Community Over Code 的时候,需要避免过犹不及。Community Over Code 不应该忽略核心成员的能力和影响力,开源共同体的价值核心,还是依靠这些核心成员尤其是写出最初的代码,一开始规范软件开发流程的人来定义的。用户使用和反馈也好,内容创作也好,前提是有一个好用的软件。虽然上游软件应该适应下游需求来创造,但是最终形成的开源共同体,还是上游定义的软件及围绕开发软件的共同体的价值观推向下游的。

这一点很好理解。创作 GNU 系列项目的人,通过软件及共同体传播自由软件的理念。Linux 选择了 GPLv2 协议,进而影响了整个 Linux 发行版的生态的调性。试图抛开软件,抛开具体的核心成员,定义一套任何人都可以采用的方案建设成功的开源共同体,是不切实际的。这就像是没有一个确保成功的创业法则一样。

只有深入参与到共同体的发展历程当中,通过长时间的投入赢得声誉,建立影响力,并且对软件的定位和共同体的价值取向有相当的理解,对参与共同体发展的核心成员乃至所有成员都有良好的联系,才能够结合具体的情况从共同体层面为现在该干什么,下一步该怎么走做出正确决策。

我应该怎么做到?

前面讨论“我能为你做什么”的问题的时候,针对每一种可能的参与手段,其实已经或多或少回答了“应该怎么做”这个问题。因此,本段不再对前文已经提过的内容做复述,而是从回答“我应该怎么做到”这个角度出发,给出一个结构化的答案。

我认为,要想解决潜在的参与者提出的“我应该怎么做到”参与贡献的问题,项目维护者应该从这三个方面入手。

  • 内容
  • 论坛
  • 聊天室

内容

前文已经花了不少篇幅讨论为开源共同体创造内容是重要的贡献。这种贡献一部分就体现在能够回答“我应该怎么做到”这个问题上。

不同于论坛和聊天室的形式,内容是一种较为单向的信息传递方式,即潜在的参与者单方面的接收内容(主要是文档)传递的信息。因此,内容应该着重考虑受众的体验,解答最为常见,答案最为明确甚至固定的问题。例如,共同体的角色划分和治理体系,共同体认可的价值观,报告缺陷、评审代码、提交补丁等活动的一般性流程等等。

以 TiDB 开发者指南为例,其第二章 Contribute to TiDB 就是我和张翔专门为了回答“我应该怎么做到”撰写的文档,从它的结构就可以看出我们是如何系统性地回答这些问题的。

  • Community Guideline
  • Report an Issue
  • Issue Triage
  • Contribute Code
  • Cherry-pick a Pull Request
  • Review a Pull Request
  • Make a Proposal
  • Code Style and Quality Guide
  • Write Document
  • Committer Guide
  • Miscellaneous Topics

其他成熟的开源共同体也有类似的材料。

更不用说称得上“卷帙浩繁”的 Apache 基金会的文档和 Kubernetes 共同体的文档。

这些文档,加上共同体当中热衷于布道和培养新人的成员在博客和演讲上的内容分享,是潜在参与者了解如何具体参与一次贡献的直接材料。你正在阅读的这篇文章本身,也作为内容有着回答应该怎么参与贡献的作用。

论坛 + 聊天室

上面提到,文档和博客等形式,潜在的参与者作为被动接收方往往只能单向地获取信息。然而,真实世界当中参与开源共同体的活动,往往每个人都会遇到对于自己独一无二的问题。这些问题不够普遍,所以并不能在解决一般性问题的文档上找到答案。为了解决参与贡献旅程上“最后一公里”的问题,我们需要一个对等的沟通工具。

我在夜天之书 #16 Open Discussion 一文里介绍过 TiDB 共同体解决这个问题的心路历程,这个经验应用到建设 Engula 开源共同体当中,就是 GitHub Discussions + Zulip 即论坛 + 聊天室的模式。

其中论坛是唯一信源,也就是说,类似于 Apache 共同体强调的一切都发生在邮件列表上,所有共同体成员只需要关注论坛这一个渠道,就应该能够接收所有必要的信息。同时,在论坛上提出的问题,应该具有最高的响应优先级。

通过定义唯一信源,信息交互才能在当下这个沟通工具琳琅满目的环境下以一种尽可能低消耗的方式进行。否则,每个人都有他喜欢的沟通方式,甚至是非持久化或非公开的方式,例如不录屏的视频会议,或者微信聊天等等。如果在这些地方的讨论也可以作为信源,那么共同体当中发生的事情,将有可能只是一小部分人未经记录的部落共识。这种共同体当中具有排他性的小团体,对信息对等的流通是极其有害的。

回到解决“我应该怎么做到”这个问题上来,信源的分裂也将提高潜在的参与者理解到底应该在哪里解决他“最后一公里”问题的难度。也就是说,解决“最后一公里”问题的路径,本身成为了“最后一公里”问题的一部分。

选择论坛而不是邮件列表,体现了用户喜好的差异。其实对我来说,论坛和邮件列表的价值是一样的,无非是共同体成员更喜欢哪一个用户界面罢了。类似的,聊天室之于论坛,是用户沟通习惯上的差异。IRC 和 Zulip 在我看来也是一样的,无非是共同体成员更喜欢哪一个罢了。只要指定一个,就可以减少工具数量爆炸带来的信息熵。

不过,聊天室作为即时沟通工具,较之论坛或者相对更为正式的邮件,在即时响应问题上有独特的优势。一两句话能解决的问题,即时通信工具更加轻量级。模糊概念的讨论,即时通信工具能够更好地完成实时的头脑风暴。前者的典型例如 Akka 的 Gitter 聊天室,后者的典型例如 Rust 的 Zulip 聊天室

但是,聊天室核心的问题就在于它的信息密度太低,一旦超过一百人,昨天的消息基本上就已经完全归入历史,要想重提就得从头再来了。虽然现代聊天室大多支持主题化讨论的功能,但是较之论坛的设计理念,还是比较薄弱。因此我在设计沟通工具方案的时候,总是以论坛作为唯一信源。同时在选择聊天室方案的时候,总是选择公开的聊天室,选择支持主题化讨论的聊天室,选择支持引用内容链接的聊天室。支持引用内容链接,就可以在论坛当中直接链接到聊天室的特定讨论串当中,从而打通两边内容的交叉引用。这样避免了大量内容存在于聊天室当中,削弱论坛定位的情况,既能维护论坛唯一信源的定位,又能利用即时通信的优势。

❌
❌