阅读视图

发现新文章,点击刷新页面。

我们的博客为什么要专门展示其他人的内容

前几天在积薪作者群里,博友@欧雷发出了两个灵魂之问:

  1. 为什么要自己的网站上有个页面专门展示其他人发了内容?
  2. 为什么要有个专门的网站展示很多人发的内容?(这我觉得不同于积薪这种内容分发)

那天晚上本来准备入睡,看到这个问题一下子来了精神,因为这个问题也是最近我一直在思考的一个问题。那就说说我的理解:

友链朋友圈和说说广场不能仅仅看做是一个友情链接,而是代表博主的一种分享内容,这些内容都是博主精心挑选的,一定程度上也代表着博主信息筛选的能力,类似文摘类期刊,比如读者、青年文摘,或者类似微博上一些bot类主题账号。

如果一个博客分享的内容很有意思,能持续挖掘到独立博客的宝藏博主,那就会有人时不时去这个博客逛一逛,看看有没有新的博主内容推荐。

独立博客没有一个上帝视角的全局页面,都是一个个孤岛,很多博主寻找符合自己阅读口味的独立博客也只有到其他博客上才能发现。

这让我回忆起自己最初开通独立博客时,一直在试图寻找一个关注列表,这个列表能够涵盖简体中文博客圈绝大多数还在活跃着的独立博客,比如Github上有一个叫做中文独立博客列表的项目,看似收录了大量的独立博客,事实上,很多站点已经停止更新或者消失了。

等我慢慢开始融入独立博客圈,认识了很多有想法、有趣的博主后,才发现自己并不是依赖于所谓的独立博客列表,而是通过各个独立博客的评论区,以及友链朋友圈、说说这些实时更新的版块。

不断分享的结果就是有越来越多的人友链到这个博客,尤其是那些「大神」还跟这个博主互动——谈笑有鸿儒,往来无白丁——其他路人就会觉得这个博主也比较有趣,我也链接一下。

友链朋友圈、说说广场只是一种展现形式,它的真正作用就是连接人,而不单单是友情链接。

某种意义上说,能在自己的网站上专门展示其他博主的内容,才是独立博客最有价值的地方。

国产开源社群的运营,为何总是画风奇特?

在过去几年的投入和关注下,国产开源社群如雨后春笋一般冒了出来。今天,以 GPT 为首的 AI 新势力接过话题度的接力棒,我们可以在降温周期里回顾一下过去几年间冒出来的国产开源社群都有什么样的成绩,有些什么样共性的问题可以改进。

本文标题是国产开源社群的运营总是画风奇特,这也符合近几年来作为理论上开源社群的主要参与者,即软件开发者们的主流感受。从眼花缭乱的开源营销,到一次性开源、开关式开源的另类创新,再到同质化开源的内卷、打擂台……凡此种种,不仅和开发者熟悉的黑客精神驱动的开源运动有很大的出入,也很难说实现了开源政策想要的达到数字技术创新目标。

支持数字技术开源社区等创新联合体发展,完善开源知识产权和法律体系,鼓励企业开放软件源代码、硬件设计和应用服务。

《中华人民共和国国民经济和社会发展第十四个五年规划和2035年远景目标纲要》

从开源社群的建设和运营角度来看,我想造成这个局面的主要原因分成三个方面:运营人员的问题、核心团队开发人员的问题,以及根本的软件产品力问题。

运营人员的问题

运营人员的问题主要来自用面向消费者 (2C, to customer) 的小白营销思维做面向开发者 (2D, to developer) 的开源社群运营。

应当说,运营的对象从 2C 到 2D 都是面对个人,都可以认为是某种形式的用户运营:消费者是平台或终端产品的用户,而开源开发者主要是开源软件的用户。在高层次的用户运营策略上,这两者都需要围绕用户群体梳理画像、理解需求,利用资源给到用户及时反馈,使得用户投入更多时间在你的社群当中,创造你期待的价值。

不过,驱动 2C 运营的消费者产品通常具有较短的销售周期和较低的门槛,这就导致 2C 运营的最优策略是小白营销,也就是把用户当成小白,只在地域、年龄、性别和职业等属性上作区分,用快速反馈的活动来刺激用户的即时欲望,赚取平台所需的流量或者产品的出售,即冲动消费。

一个典型的例子是大街上送小礼物换关注的地推营销。例如,住宅区内新建了一家配送超市,通过雇佣一批没有任何背景要求的地推员,在人流量大的地方发传单,关注超市公众号或者下载 APP 就能领取几元的代金券或小礼物。目标人群基本也不用太作区分,能走到大街上的人都是超市的潜在用户。

这个画风是不是有点熟悉?

在开源运营概念炙手可热的时候,不少企业为自己发起的开源社群招聘的运营人员就是用同样的方式在做用户运营的。

256 "OceanBase 点赞送好礼"

512 "TDengine 的灭虫计划"

这两个例子都引起开发者的强烈反感。

开发者不乐意见到这类活动,不是因为不能送小礼物,更不是因为运营有“原罪”,而是这些利用企业资源抢占开发者注意力的活动违反了软件开发的常识。

对于 OceanBase 点“赞”送礼的案例,star 在 GitHub 平台上被某些开发者作为选择开源软件库的辅助指标,是因为自然增长的 star 基本来自用户的主动好评和二次传播。star 并不是开发者采用一个软件的关键指标,软件已经被自己或其他人验证过了,有可复制的使用方案才是决定性的优势。star 暗示用户主动好评,是软件已被验证过的一个弱相关的参考。OceanBase 搞点“赞”送礼活动,其实也不会有大范围的影响,只是破坏了自己 star 数原本有的一点价值。

至于把任何形式的 star 增长解释为社群规模的成长,则是无稽之谈。star 背后的行为只是一个简单的点击动作,没有任何门槛。一个接过超市传单的人,不用任何培训都能成为超市的消费者。但是一个凑热闹去给代码仓库点“赞”的人,是没有任何理由做进一步地参与的。其实,由于众所周知的原因和软件开发本来的小众特点,哪怕就看增长 star 的目标,这样的推广活动甚至不会给 OceanBase 带来 1k 以上的新增 star 数。而且,以 OceanBase 的大厂背景、技术实力和生产实践,基本不需要也不太可能用刻意增长 star 的方式达成提高 awareness 的目标。

对于 TDengine 搞灭虫计划,这个问题还微妙一些。因为它看起来是跟写代码相关的,似乎也是给开源软件的发展创造了价值,这样的活动难难道不值得推广吗?OceanBase 的运营人员就觉得可以推广,也搞了一波复刻:

OceanBase 的同质活动

这些活动的问题,@Xuanwo 在《开源运营当论迹不论心》的评论代表了开源开发者的主要观点:

我们要看这个活动创造出了什么价值,而不是活动组织者的动机。

灭虫活动有价值吗?有,但是只有一点点,跟维护者需要花时间去找 typo 的代价相比,这个活动的价值可能是负的。

GSoC 等活动的项目都是实际的需求,项目都需要配备一名导师,贡献者可以获得来自导师的全程指导。通过参加 GSoC 项目,开源项目可以解决一些长期的需求,贡献者可以深入了解一个开源社区并做出自己的贡献。

我在参与 TiDB 社群的时候,曾经也发起过一个类似的低门槛工作 Tracking issue for restructure tests 来迁移现有测试到一个 IDE 集成友好的框架上。在我完成了一些关键的工具函数和接口开发之后,大部分的工作都是比较机械的翻译。

这个 issue 跟上面活动的不同点在于它来自实际的需求,开发方式符合软件工程的常识。我在第一次发现不能在 GoLand 上跑起来的时候就觉得应该优化测试框架的选型。在 @disksing 成功将 PD 的测试框架从 PingCAP fork 的 check 迁移到 testify 之后,同时也是在 TiDB 社群里我看到了两三个有同样想法的新人表达出对 IDE 集成不足的意见之后,我开始推动这项工作。

这个 issue 里 track 多个 subtask 的经验后来被传播到很多其他项目上,也在引导参与者对 TiDB 代码祛魅的过程中激发了其他的代码贡献。不过,由于 TiDB 核心代码的开发门槛依旧不低,最终我记得是没有通过这个 issue 成为 TiDB 核心开发者的例子。

TiDB 也做过 linter 相关的工作,但是开展方式是利用自动化工具,一个一个模块扫过去,而不是像 TDengine 或 OceanBase 那样,人工看到一个记一个 issue 等“外部”开发者来修,生怕解得太高效了影响数字增长。

对于 typo 的问题,Rust 社群有一个 typos 工具可以自动扫描、报告和修复。它在 Databend 的项目群里已被重度采用,基本可以根绝 typo 的问题。

另外,上面提到的这些工作跟前面列举的反面例子还有一个差别,就是开发者是能够认识到这些工作是 chore 即杂务的。除了在 issue 组织方式和工具框架选型开发上还有讨论的价值,开发者很少会对具体的杂务工作做营销,而反面例子当中,营销的对象就是杂务本身。难怪开发者对这样优先级倒转的营销手段很不感冒。

行文所限,还有其他存在的问题和针对这些问题的解法,本文不做展开,但是在这里做一个简单的罗列。

面向开发者的运营,在海外的行业实践相对丰富,他们创造了诸如开发者关系(Developer Relationship, DevRel)、开发者体验(Developer Experience, DX)和社群布道师(Community Evangelist)这样的概念和职位。

这方面值得参考的文字和富文本材料有:

其中,《开发者关系》是最符合运营人员背景的首选读物,它会讨论开发者用户群体的画像、细分和旅程,也会讨论如何在企业中制定和成功落实开发者关系战略。

不同于国内开源运营的招聘需求一锅端,分工完善的开源社群团队大致由以下几类角色组成:

  1. 社群团队的 manager 制定社群战略和增长目标。
  2. 运营专家策划和组织活动或比赛,负责场地和物料协调。
  3. 内容专家在社群策略的基础上制定内容策略并执行。
  4. DevRel 专家负责技术宣讲,并记录演进的参与和反馈情况,跟进高潜力的开发者,挖掘意见领袖。

除此以外,如果还能找到相应的人才,会考虑做课程开发和跟进用户场景,转化潜在商机的工作。

其中值得一提的是内容专家制定内容策略,生产和传播具体技术内容是一项非常有挑战性的工作。可以参考 StreamNative 文档工程师 Sherlock 的《英文技术文档工程师的培养与发展》。进一步地,开发课程和开发者内容的全面覆盖,可以参考 Confluent Developer 网站的设计和实现。

最后,国内开源社群运营的经验,有以下的参考材料:

值得注意的是,在销售软件产品或提供技术服务的企业当中做开源社群运营的从业者,很容易模糊 2B (to business) 和 2D 的区别。这些文章里讨论如何跟用户开发者打交道,如何生产高质量的内容的部分是值得参考的,对于 2B 和 2D 的认识问题,建议加上《开发者关系》第 8 章做对比理解。

开发人员的问题

通常所说的开源社群是围绕一个特定的开源软件形成的社群。软件不是一成不变的,而是响应需求不断迭代的。主导和完成这个迭代过程的成员是开发者。因此,开发者是开源社群的主要参与人群,也是核心生产力。

企业发起的国产开源社群,开发人员导致的运营变形的问题,起因几乎都是缺乏开源战略和理论指导下被迫突然开源的应激反应。

很多企业决定开源某个内部系统,实际上只是一个跟风行为,没有开源战略,也没有开源经验:开放源代码本身就是目的。

这个时候,作为工作在这个系统上的一线研发人员或是基层研发主管,突然被告知自己每天打交道、提交变更的代码仓库要开源了,下意识的自我保护策略就是 Open Source Code Only - 开放源代码就是源代码用开源协议公开发布?点击发布……好,完事啦!至于研发计划和开发流程,这跟开放源代码有什么关系?我不是已经开源好了吗,这些照旧就可以了。

于是,企业开源出来的是一个内部系统的代码仓库镜像。这其实也无可厚非,毕竟源代码只要是完整的,开放给所有人阅读就是一种社会贡献,没有说法是必须让企业完全公开软件研发的流程和设计讨论内容。

但是,企业对外宣传甚至决策者心里实际预期的效果,并不是单纯地开放源代码,他们要的是“共建”。虽然我在《共建的神话》里已经批评过这一说法的模糊性和异想天开,但是我们暂且从字面意思,以开源社群常见的协同方式来理解实现“共建”的要求。

马上,你会发现共享工作流是必须的。如果没有统一的开发流程,至少是主线流程,那么即使是资深的开发者,缺少必要的信息和充分的讨论,也无法更进一步参与。我在《企业如何实践开源协同》开篇就提过这个观点,并以 OceanBase、Apache InLong、TiDB 和 Taichi 四个项目为案例做了详细讨论。

开源社群能够为企业带来的最大杠杆,来自有一定开发能力和使用经验的开发者,基于能够访问源码的前提,将软件在具体场景下遇到的未实现需求、兼容性问题和易用性问题做就地定制,而后将这些定制反馈到上游,以求自己能够在使用上游软件新版本的时候不再需要重复二次开发。如果是自己无法就地定制解决的问题,由于能够访问源码,开发者可以做一些基础调试,报告一个更清楚的问题。基于相同的源码和统一的开发工作流,不同组织的开发者能够在相同的上下文当中交流,共同推进问题的解决。

企业发起的开源社群,初始的开发者必然是企业的雇员。如果企业的雇员没能率先实践这种开放工作流的做法,那么企业之外的开发者就更不可能推动社群往这个方向发展。最终,社群仍然是一个只有镜像代码的死气沉沉的平台,没有任何生机。

衡量一个开源社群的上游有没有开放工作流,一个最基本的指标就是观察它是否有良好的开发环境配置体验。因为所有基于源码的开源协同,至少开发者要能够搭建起一个自己的开发环境,能够跑通基本用例和运行单元测试。否则,任何改动都是文本编辑,脑内调试,这倒不是做不到,只是门槛非常高。而且是无谓的门槛,即使有这个能力的人,也能看出来工作流的不合理,不愿意浪费这个时间去做本不用做的努力。

最后,我们深挖一下这种开发人员自我保护式的 Open Source Code Only 策略,为什么在个人发起的开源项目社群里很少出现。

直接的原因是个人开发者往往没有自己另建一套工作流的动力和精力,这些项目往往一开始就是在公共空间用通用的工具链搭建起来的。本来就没有内外两套工作流,自然是“全过程开源”。

深层次的原因,探究个人开发者为什么更加容易接受开源协同的开发方式,这是因为项目作者往往是实打实的通过开发出高质量的软件和极强的 ownership 招徕参与者的。对于能够帮助自己的开发者,项目作者通常心怀感激。对于其他开发者提交的问题和补丁,由于软件的核心功能设计和实现是自己完成的,通常项目作者能做出准确的判断,而且有足够的 credit 做决定,因此在协同过程中不会感到受制于人。

反观企业开源的情况。能够被企业选择开源出来的软件,多少还是有点实力的。在目前的研发序列晋升体系下,这些软件的第一作者基本已经高升,否则就是依靠这一成就高薪跳槽:他们是不会参与到软件的开源社群当中的。实际会被指派处理开源社群工作的员工,往往没有足够的 credit 做决策,至少在大部分研发团队完全不管开源社群的情况下,他们很难讲清楚一个开源协同的要求为什么需要其他“内部”开发者也知悉和配合改变工作方式。

因此,这些被指派的员工不管从动力上还是权力上,都只能处理最简单的事务。这就导致高水平的“外部”参与者体验到低效的协同流程,基本无法完成任何工作而离开;而“内部”开发者对不时出现的“外部”需求,又不是工单系统派过来的公司的活,却要花时间去处理,感到不胜其烦。

如果某一天企业又想起来“共建”的故事,开始给这个“开源团队”下达社群人数增长的需求,一方面是短时间内低门槛的手段只有上一节提到杂活,另一方面,不止如此,“开源团队”的人还得求着“外部”开发者来参与,以达成自己的业绩。这种既瞧不起来人贡献的内容,又被迫跪舔式服务的情形,我在很早的一篇文章《Two Hats of Developers》当中就有过讨论。

如果企业开源的项目的第一作者仍然在社群当中,那么这些问题出现的几率就和个人发起的开源项目社群一样低。

例如,前文提到的 Apache InLong 项目是腾讯捐赠的。原创作者之一张国成仍然重度参与社群开发和方向计划。他希望通过代码开源和 ASF 的影响力,把自己创作的软件带到更多用户的生产环境。同时,他也需要维护腾讯内部的 InLong 使用实例。出于这样的动机,以及他就是软件的原创作者,他能够把内外的版本发布流程做到主线统一。同样把自己的职业生涯赌在 InLong 项目上的 PMC Chair Charles 是腾讯内部 InLong 项目的主管,在极强的责任心驱使下,他能够主动的对外宣传 InLong 的设计理念和用例,并且积极发展社群的开发者。根据我对 InLong 项目的观察,基本上每隔两三个月,就有几个 qualified 的开发者被提名进 Committer 队伍。

例如,我最近带到 ASF 孵化器的 OpenDAL 项目是 DatafuseLabs 捐赠的。当然你可以认为它源头上近似于 @Xuanwo 个人的开源项目,但是它确实也是 DatafuseLabs 公司立项的项目,@Xuanwo 的主业也包括开发和维护 OpenDAL 项目。@Xuanwo 在 BeyondStorage: why we failed 一文介绍了 OpenDAL 的前世今生。在他的个人博客上,还有一系列 OpenDAL 的技术介绍和运营分享。

这里的关键点在于,开源社群的建设不只是代码开发。虽然好的软件是核心,但是社群想要建设起来,自给自足的运转,近乎需要创业的热情。对于第一作者来说,软件是他本人的作品,软件成功可以带动自己成功,同时从原创开发软件的过程里,他也得到了指明软件发展方向的权威,因此,这样的社群才能在方法论的加持下腾飞。TiDB 的崛起显然也是一个例证,且在创始人淡出核心社群,转由跟项目也没什么感情的职业经理人担任技术主管以后,开始出现各种大厂开源的典型异味。

当然,并不是说不是原创作者就只能被动等待了,只不过后继者想要通过努力得到近似原创作者的声誉更加困难,而且后继者往往也很少有动力去做这样的事情。我记得有这样做还挺成功的案例,应该算阿里巴巴集团的资深工程师们参与和逐渐继承 Flink 社群主导权。

关于开发人员应该如何参与开源社群,如何组织和建设开源社群的相关材料,首推的自然是《大教堂与集市》。其中的第二篇《大教堂与集市》和第三篇《开垦心智层》分别讨论了开源协同的方式和优势,以及开源软件的所有权和声誉问题。

此外,制造开源软件是一份很有价值的参考材料,GitHub 推出的 Open Source Guides 是几个精练的主题小册子,《开放式协作》是一本有趣的 GitHub 平台上的开源社群及其模式的综述书籍。

软件的产品力

应该说,运营人员的执行问题主要来自行业不成熟,经验尚未广为人知。在诸多好的案例和坏的案例的曝光下,我相信运营人员是能够快速找到新定位,建立起面向开发者的运营能力的。这一点其实已有苗头,也有一些开拓性的人才已经出现。

开发人员的立场问题和激励问题,企业一旦认识到开源社群的成功基本是内部创业,那么如果还决定要走开源路线,相关的资源和人才配备就不再缺失。开发者方面,在成功开源项目社群的引领和经验分享的帮助下,以开发者务实的精神,我相信也能理解建设开源社群所需要的方法论。当然,从知道到实际执行,目前看来还是一个很难跨过去的坎。

除此以外,国产开源社群要想发展,要想获得广阔的运营空间,最后最难的问题,是如何提高开源软件的产品力,或者叫开源软件的质量。

SkyWalking 的作者吴晟在《开源没有黑魔法,两年后泡沫将会破灭》的访谈里提到:

开源最核心的是要具备产品思维。在某种角度上,社区 Leader 一定要是一个非常懂技术的人。但从另外一个角度上看,社区 Leader 更重要的还是要把开源项目看作一个产品或者能够售卖的商品。

开源最忌惮的就是比拼快和性能。如果你一味地追求快,这导致的结果是,你的功能出来后,别人也可以通过某种优化,快速地追赶上来,这就会导致你丧失掉生存的空间。只有持续地保持创新,才能不断地走下去。而这个过程中,如果一味地靠 KPI 以及营销带来的数据指标指导开源项目发展,而不是回归开源项目需要解决的问题本身,这将会成为开源项目发展的灾难。

历史上很多红极一时的项目,在热闹过后留不下什么东西,其核心原因就是软件的产品力不行,也就是不能解决实际问题,甚至号称要解决的问题并不存在。

这个讨论要展开涉及到的内容太复杂,我想抛出这个问题已经达到本文的目的。我在《大厂开源之殇》的第一节“用户不买账:我要怎么用起来?”里举了一些具体的例子,可做参考。

知识星球

这篇文章起初的想法我记录在同名知识星球〖夜天之书〗。

上面讨论过程中,例如社群运营在启动阶段是需要技术地推的,这个要怎么做?社群增长进入平台期以后,如何激发存量用户的创造力和主动性?内容传播的渠道如何建设、定位和维护?对于开发者,如何识别和参与国产开源社群?对于维护者,具体的在某个主题上和特定的开发者怎么开展合作?这些话题都没有展开甚至没有出现。一方面是篇幅所限,另一方面也是文章发布需要大量的时间梳理清楚框架和去繁就简。

如果你想就开源参与和社群建设的问题做深入的交流,或者想要订阅的不成文的一线内容输出,欢迎加入我的同名知识星球《夜天之书》。

256 ""

ClickHouse 社群指标模型

数字化时代下绝大多数工作都有关键绩效指标(KPI)指导,任何组织都期望找到一个合理的指标来校准战略方向,衡量工作成果。

然而,并非所有工作都能有 KPI 准确地衡量产出。著名软件工程师,同时也是《重构》《分析模式》等书籍的作者 Martin Fowler 曾经写过一篇博客论证软件工程师的生产力是无法衡量的

开源社群成员的重要组成部分,也是一切价值的核心飞轮就是这样一群软件工程师。这是否意味着开源社群的工作也是无法衡量的吗?

我认为,目前探索所及的一切开源社群指标都是人的活动产生的,并且这里的人都是匿名的网络用户。这样的背景下,如果有人背负着必须提高某个指标的任务,在不论对社群协作造成的次生影响的前提下,几乎所有指标都是可以做出来的。因此,只是考察社群指标的结果数字,甚至为了掩饰自己不愿意理解社群运作的深层逻辑,而要求组织的社群协调员以某种归一化的分数来汇报社群指标,都不能达到撬动社群杠杆完成组织生产力增效的目的。

但是,没有北极星指标引领社群工作方向,难免会导致社群成员对自己所在的社群正处于一种什么状态缺乏清晰的认识。不是将指标作为目标结果,而是以定性和定量的指标来辅助校准社群发展的方向和衡量社群工作的效率,这样的工作是有价值的。

Apache 成熟度模型就是一个定性模型,从代码、版权、发布、质量、社群、共识和独立性七个方面衡量一个软件项目是否符合 Apache 开源之道。绝大部分从 Apache 孵化器毕业的项目在毕业前都会准备一份对应成熟度模型的报告,例如 Apache Pulsar 在毕业之际就专门撰写了一份社群成熟度报告来回应孵化器导师的质询。

近年来,越来越多的企业和组织认识到社群运营的重要性。除了定性的指标以外,这些企业和组织投入的人力和资金也使得探索定量指标得到了更多的支持。这其中值得关注的两个,就是 orbit.love轨道模型和 ClickHouse 社群基于 GitHub Events 全域公开数据进行的社群分析。

相比之下,orbit.love 的轨道模型不只是针对开源社群,而是针对普遍的社群运营行为及其结果的建模,定义更加严谨,理论更加丰富。ClickHouse 社群的指标模型是从使用自家软件分析社群活动出发,目前还停留在比较简单的统计分析的阶段上。

本文以 ClickHouse 的社群分析报告为基础,看看从 GitHub Events 全域数据能够进行哪些数字化社群指标分析。

准备工作

本文的结果均基于 ClickHouse 提供的 GitHub Events 公开数据集查询产生,任何读者都应该可以复现相同的查询。由于数据集每天都会更新,因此不同时间查询的结果可能会有所出入。

要想查询这个公开数据集,最简单的方式是下载 ClickHouse 的命令行工具:

1
curl https://clickhouse.com/ | sh

如果一切顺利,应该可以看到 clickhouse 可执行文件下载成功,通过下面的命令连接到公开数据集:

1
./clickhouse client --secure --host play.clickhouse.com --user explorer

如果上述操作不成功,可以阅读官方的开始文档或者到项目 GitHub Discussions 的提问区提问。

成功连接到数据集后,可以查询全域被 star 过次数最高的仓库来作为本次旅途的 Hello world 步骤:

1
2
3
4
5
6
SELECT repo_name, count() AS stars FROM github_events WHERE event_type = 'WatchEvent' GROUP BY repo_name ORDER BY stars DESC LIMIT 1;
/*
┌─repo_name──────┬──stars─┐
│ 996icu/996.ICU │ 359029 │
└────────────────┴────────┘
*/

关联社群

探索关注到特定项目的参与者也会关注哪些其他项目。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
SELECT
repo_name,
count() AS stars
FROM github_events
WHERE (event_type = 'WatchEvent') AND (actor_login IN
(
SELECT actor_login
FROM github_events
WHERE (event_type = 'WatchEvent') AND (startsWith(repo_name, 'apache/pulsar'))
)) AND (NOT startsWith(repo_name, 'apache/pulsar'))
GROUP BY repo_name
ORDER BY stars DESC
LIMIT 5;
/*
┌─repo_name────────────────────────┬─stars─┐
│ ant-design/ant-design │ 3810 │
│ kubernetes/kubernetes │ 3540 │
│ donnemartin/system-design-primer │ 3416 │
│ pingcap/tidb │ 3226 │
│ golang/go │ 3225 │
└──────────────────────────────────┴───────┘
*/

SELECT
repo_name,
count() AS stars
FROM github_events
WHERE (event_type = 'WatchEvent') AND (actor_login IN
(
SELECT actor_login
FROM github_events
WHERE (event_type = 'WatchEvent') AND (startsWith(repo_name, 'apache/flink'))
)) AND (NOT startsWith(repo_name, 'apache/flink'))
GROUP BY repo_name
ORDER BY stars DESC
LIMIT 5;
/*
┌─repo_name─────────────┬─stars─┐
│ apache/spark │ 6722 │
│ tensorflow/tensorflow │ 6515 │
│ 996icu/996.ICU │ 5680 │
│ ant-design/ant-design │ 5551 │
│ kubernetes/kubernetes │ 5461 │
└───────────────────────┴───────┘
*/

可以看到,这个指标下被筛选出来的项目往往是热门项目,而不是与自己社群相关的其他项目。只有类似 Flink 和 Spark 这样同时都是热门项目的情况,才有可能把关联项目筛选出来。这也侧面提示了前文提到的,指标只能提供侧面辅助作用,最终需要熟悉社群运作和实际运行情况的人来解释。

为了提高关联度,我们筛选项目关注人员里,同时关注我们项目的人占比最高的那些。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
SELECT
repo_name,
uniq(actor_login) AS total_stars,
uniqIf(actor_login, actor_login IN
(
SELECT actor_login
FROM github_events
WHERE (event_type = 'WatchEvent') AND (startsWith(repo_name, 'apache/pulsar'))
)) AS our_stars,
round(our_stars / total_stars, 2) AS ratio
FROM github_events
WHERE (event_type = 'WatchEvent') AND (NOT startsWith(repo_name, 'apache/pulsar'))
GROUP BY repo_name
HAVING total_stars >= 100
ORDER BY ratio DESC
LIMIT 5;
/*
┌─repo_name──────────────────┬─total_stars─┬─our_stars─┬─ratio─┐
│ streamnative/pulsarctl │ 105 │ 81 │ 0.77 │
│ kuangye098/Pulsar-analysis │ 107 │ 75 │ 0.7 │
│ bbonnin/pulsar-express │ 105 │ 71 │ 0.68 │
│ streamnative/pulsar-flink │ 260 │ 165 │ 0.63 │
│ streamnative/kop │ 332 │ 196 │ 0.59 │
└────────────────────────────┴─────────────┴───────────┴───────┘
*/

SELECT
repo_name,
uniq(actor_login) AS total_stars,
uniqIf(actor_login, actor_login IN
(
SELECT actor_login
FROM github_events
WHERE (event_type = 'WatchEvent') AND (startsWith(repo_name, 'apache/flink'))
)) AS our_stars,
round(our_stars / total_stars, 2) AS ratio
FROM github_events
WHERE (event_type = 'WatchEvent') AND (NOT startsWith(repo_name, 'apache/flink'))
GROUP BY repo_name
HAVING total_stars >= 100
ORDER BY ratio DESC
LIMIT 5;
/*
┌─repo_name───────────────────────────┬─total_stars─┬─our_stars─┬─ratio─┐
│ ververica/flink-jdbc-driver │ 122 │ 89 │ 0.73 │
│ docker-flink/docker-flink │ 154 │ 112 │ 0.73 │
│ flink-extended/flink-remote-shuffle │ 163 │ 119 │ 0.73 │
│ nexmark/nexmark │ 180 │ 124 │ 0.69 │
│ ververica/stateful-functions │ 268 │ 182 │ 0.68 │
└─────────────────────────────────────┴─────────────┴───────────┴───────┘
*/

可以看到,项目的相关性大幅增强了。进一步地,我们可以通过增加项目集合的大小或筛选已知项目来逐步探索生态项目。

除了 star 这样动动手指就能做到的动作,如果一个参与者提出实际提出一个问题、提交一个补丁或者参与讨论,这样的行为所需要的动机更加强烈。我们也可以试着从 issue 和 PR 的角度来观察项目之间的相关性。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
SELECT
repo_name,
count() AS prs,
uniq(actor_login) AS authors
FROM github_events
WHERE (event_type = 'PullRequestEvent') AND (action = 'opened') AND (actor_login IN
(
SELECT actor_login
FROM github_events
WHERE (event_type = 'PullRequestEvent') AND (action = 'opened') AND startsWith(repo_name, 'apache/pulsar')
)) AND (NOT startsWith(repo_name, 'apache/pulsar'))
GROUP BY repo_name
ORDER BY authors DESC
LIMIT 5;
/*
┌─repo_name──────────────┬──prs─┬─authors─┐
│ apache/bookkeeper │ 1761 │ 96 │
│ apache/flink │ 726 │ 46 │
│ streamnative/kop │ 529 │ 34 │
│ apache/spark │ 1033 │ 28 │
│ streamnative/pulsarctl │ 227 │ 26 │
└────────────────────────┴──────┴─────────┘
*/

SELECT
repo_name,
count() AS issues,
uniq(actor_login) AS authors
FROM github_events
WHERE (event_type = 'IssuesEvent') AND (action = 'opened') AND (actor_login IN
(
SELECT actor_login
FROM github_events
WHERE (event_type = 'IssuesEvent') AND (action = 'opened') AND startsWith(repo_name, 'apache/pulsar')
)) AND (NOT startsWith(repo_name, 'apache/pulsar'))
GROUP BY repo_name
ORDER BY authors DESC
LIMIT 5;
/*
┌─repo_name─────────────────┬─issues─┬─authors─┐
│ apache/bookkeeper │ 815 │ 105 │
│ streamnative/kop │ 287 │ 45 │
│ streamnative/pulsar-flink │ 163 │ 42 │
│ kubernetes/kubernetes │ 69 │ 41 │
│ golang/go │ 139 │ 40 │
└───────────────────────────┴────────┴─────────┘
*/

可以看到,Pulsar 与部署集群的直接依赖 BookKeeper 项目,以及生态伙伴 Flink 项目和 Spark 项目是紧密联系的。StreamNative 围绕 Pulsar 打造了一系列的开源生态项目。

成员活动

接下来我们把目光放到单个项目群之内,看看社群当中的最活跃的 Code Reviewer 是谁。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
SELECT
actor_login,
count(),
uniq(repo_name) AS repos,
uniq(repo_name, number) AS prs
FROM github_events
WHERE (event_type = 'PullRequestReviewCommentEvent') AND (action = 'created') AND startsWith(repo_name, 'apache/pulsar')
GROUP BY actor_login
ORDER BY count() DESC
LIMIT 5;
/*
┌─actor_login───┬─count()─┬─repos─┬─prs─┐
│ sijie │ 1828 │ 8 │ 531 │
│ codelipenghui │ 1599 │ 2 │ 648 │
│ eolivelli │ 1482 │ 5 │ 592 │
│ Anonymitaet │ 1306 │ 8 │ 375 │
│ merlimat │ 1272 │ 3 │ 579 │
└───────────────┴─────────┴───────┴─────┘
*/

这和绝大部分 Pulsar 参与者的观感是一致的。创始成员 @sijie 和 @merlimat 参与度非常高,目前分别就职于 StreamNative 和 Datastax 的研发领导者 @codelipenghui 和 @eolivelli 位列前三,Pulsar 文档的维护者 @Anonymitaet 也强于积极沟通。

我们可以通过扩大 LIMIT 数量和排除已知的活跃成员,或者将事件按照时间筛选和分区,来发现更多潜在的活跃参与者。

我们还可以聚合成员活动观察社群发展趋势,例如 Pulsar 项目群的 PR 数目在过去两年间的变化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
SELECT
toStartOfMonth(created_at) AS date,
count() AS total_prs,
uniq(actor_login) AS unique_actors,
bar(unique_actors, 0, 200, 100)
FROM github_events
WHERE startsWith(repo_name, 'apache/pulsar') AND (event_type = 'PullRequestEvent') AND (action = 'opened') AND (created_at > '2020-01-01 00:00:00')
GROUP BY date
ORDER BY date ASC;
/*
┌───────date─┬─total_prs─┬─unique_actors─┬─bar(uniq(actor_login), 0, 200, 100)───────────┐
│ 2020-01-01 │ 149 │ 56 │ ████████████████████████████ │
│ 2020-02-01 │ 161 │ 62 │ ███████████████████████████████ │
│ 2020-03-01 │ 142 │ 47 │ ███████████████████████▌ │
│ 2020-04-01 │ 168 │ 58 │ ████████████████████████████▊ │
│ 2020-05-01 │ 191 │ 63 │ ███████████████████████████████▌ │
│ 2020-06-01 │ 208 │ 54 │ ███████████████████████████ │
│ 2020-07-01 │ 229 │ 74 │ █████████████████████████████████████ │
│ 2020-08-01 │ 168 │ 55 │ ███████████████████████████▌ │
│ 2020-09-01 │ 190 │ 75 │ █████████████████████████████████████▌ │
│ 2020-10-01 │ 166 │ 60 │ ██████████████████████████████ │
│ 2020-11-01 │ 251 │ 60 │ ██████████████████████████████ │
│ 2020-12-01 │ 250 │ 62 │ ███████████████████████████████ │
│ 2021-01-01 │ 200 │ 63 │ ███████████████████████████████▌ │
│ 2021-02-01 │ 249 │ 71 │ ███████████████████████████████████▌ │
│ 2021-03-01 │ 245 │ 58 │ ████████████████████████████▊ │
│ 2021-04-01 │ 251 │ 60 │ ██████████████████████████████ │
│ 2021-05-01 │ 221 │ 68 │ ██████████████████████████████████ │
│ 2021-06-01 │ 325 │ 89 │ ████████████████████████████████████████████▌ │
│ 2021-07-01 │ 248 │ 83 │ █████████████████████████████████████████▌ │
│ 2021-08-01 │ 193 │ 70 │ ███████████████████████████████████ │
│ 2021-09-01 │ 289 │ 70 │ ███████████████████████████████████ │
│ 2021-10-01 │ 33 │ 19 │ █████████▌ │
│ 2021-11-01 │ 383 │ 77 │ ██████████████████████████████████████▌ │
│ 2021-12-01 │ 402 │ 90 │ █████████████████████████████████████████████ │
│ 2022-01-01 │ 377 │ 77 │ ██████████████████████████████████████▌ │
│ 2022-02-01 │ 289 │ 85 │ ██████████████████████████████████████████▌ │
│ 2022-03-01 │ 352 │ 78 │ ███████████████████████████████████████ │
│ 2022-04-01 │ 314 │ 77 │ ██████████████████████████████████████▌ │
│ 2022-05-01 │ 345 │ 86 │ ███████████████████████████████████████████ │
│ 2022-06-01 │ 325 │ 88 │ ████████████████████████████████████████████ │
│ 2022-07-01 │ 306 │ 76 │ ██████████████████████████████████████ │
└────────────┴───────────┴───────────────┴───────────────────────────────────────────────┘
*/

除了 2021 年 11 月的数据部分丢失以外,可以看到 Pulsar 社群每月独立贡献者数量从 50 人左右成长到 80 人左右。如果我们观察一个异军突起的社群,这种变化会更明显。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
SELECT
toStartOfMonth(created_at) AS date,
count() AS total_prs,
uniq(actor_login) AS unique_actors,
bar(unique_actors, 0, 200, 100)
FROM github_events
WHERE startsWith(repo_name, 'bytebase') AND (event_type = 'PullRequestEvent') AND (action = 'opened')
GROUP BY date
ORDER BY date ASC;
/*
┌───────date─┬─total_prs─┬─unique_actors─┬─bar(uniq(actor_login), 0, 200, 100)─┐
│ 2021-03-01 │ 2 │ 1 │ ▌ │
│ 2021-07-01 │ 2 │ 2 │ █ │
│ 2021-08-01 │ 2 │ 2 │ █ │
│ 2021-09-01 │ 2 │ 2 │ █ │
│ 2021-11-01 │ 37 │ 7 │ ███▌ │
│ 2021-12-01 │ 226 │ 10 │ █████ │
│ 2022-01-01 │ 216 │ 14 │ ███████ │
│ 2022-02-01 │ 154 │ 16 │ ████████ │
│ 2022-03-01 │ 320 │ 19 │ █████████▌ │
│ 2022-04-01 │ 288 │ 22 │ ███████████ │
│ 2022-05-01 │ 361 │ 19 │ █████████▌ │
│ 2022-06-01 │ 316 │ 19 │ █████████▌ │
│ 2022-07-01 │ 345 │ 21 │ ██████████▌ │
└────────────┴───────────┴───────────────┴─────────────────────────────────────┘
*/

SELECT
toStartOfMonth(created_at) AS date,
count() AS total_prs,
uniq(actor_login) AS unique_actors,
bar(unique_actors, 0, 200, 100)
FROM github_events
WHERE startsWith(repo_name, 'datafuselabs') AND (event_type = 'PullRequestEvent') AND (action = 'opened')
GROUP BY date
ORDER BY date ASC;
/*
┌───────date─┬─total_prs─┬─unique_actors─┬─bar(uniq(actor_login), 0, 200, 100)─┐
│ 2021-02-01 │ 2 │ 1 │ ▌ │
│ 2021-03-01 │ 91 │ 8 │ ████ │
│ 2021-04-01 │ 163 │ 12 │ ██████ │
│ 2021-05-01 │ 135 │ 13 │ ██████▌ │
│ 2021-06-01 │ 155 │ 16 │ ████████ │
│ 2021-07-01 │ 188 │ 17 │ ████████▌ │
│ 2021-08-01 │ 239 │ 28 │ ██████████████ │
│ 2021-09-01 │ 222 │ 25 │ ████████████▌ │
│ 2021-10-01 │ 42 │ 13 │ ██████▌ │
│ 2021-11-01 │ 361 │ 36 │ ██████████████████ │
│ 2021-12-01 │ 326 │ 43 │ █████████████████████▌ │
│ 2022-01-01 │ 274 │ 34 │ █████████████████ │
│ 2022-02-01 │ 207 │ 36 │ ██████████████████ │
│ 2022-03-01 │ 292 │ 34 │ █████████████████ │
│ 2022-04-01 │ 383 │ 37 │ ██████████████████▌ │
│ 2022-05-01 │ 404 │ 47 │ ███████████████████████▌ │
│ 2022-06-01 │ 444 │ 45 │ ██████████████████████▌ │
│ 2022-07-01 │ 300 │ 40 │ ████████████████████ │
└────────────┴───────────┴───────────────┴─────────────────────────────────────┘
*/

随着核心团队获得资本投资,持续招募全职人员并在市场上释放协同信号,Bytebase 和 DatafuseLabs 的开源项目群都实现了从 0 到 1 乃至小有规模的增长。

总结

ClickHouse 社群指标模型的建设应该开始于 ClickHouse 10616 号议案

对于相当部分开源数据处理项目来说,分析自己的开源社群的指标,是一种自然的展示项目价值,并且自己成为自己所制造的软件的用户的好途径。例如,TiDB 在完成 HTAP 能力生产可用建设以后,就发起了 OSSInsight 项目。虽然在指标思考上不如 ClickHouse 做得这么丰富,但是前端展示水平上要惊艳许多。

另外,ClickHouse 社群的工作也得到了其他致力于开源社群指标衡量的工作小组的关注和引用。例如,X-Lab 开放实验室的赵生宇博士就在其分析工具 Open Digger 当中展示了 ClickHouse GitHub Explorer 的样例

其实,本文当中没有涉及 ClickHouse 社群分析报告里大量全域分析的内容。例如,GitHub 上名称最长的仓库是哪个,star 增长最快的是哪个,PR 最多的是哪个。这些查询虽然有趣,但是对于指导具体社群运营的前进方向提供的帮助则比较有限,只是简单的统计工作。

不过,一个公开可得的数据集,如果有数据开发工程师投入时间研究写出高水平的查询,还是未来可期。一份公开可用而且好用的数据集本身就是对开源指标衡量的重要贡献,GHArchive 原本的数据可远远称不上好用。

对于数据建模和质量方面,ClickHouse 的数据两个比较明显的问题。一个是可能数据会缺失,例如上面我们看到的 2021 年 11 月 PR 创建数据丢失。这是 GHArchive 数据集本身的问题,对这些数据的补偿修复工作,CNCF 的 Devstats 项目有一些经验。另一个是 ClickHouse 只记录用户和仓库名而不是唯一标识的 ID 字段,这就使得重命名过的用户、组织和仓库的对应关系需要先验知识补充。当然,即使记录了唯一标识,也有不同账户实际上是同一个自然人的情况,以及项目群聚类时需要合并计算的情况,这都是一般的 ID Mapping 挑战。

此外,ClickHouse 的数据集是历史全量数据,因此适合用于分析历史变化趋势,不同时间段的人群圈选。对于发现目前社群状态下立刻需要解决的问题,例如目前积压 Requested Review 最多的维护者是谁,以提醒或帮助他处理 backlog 这样的需求,直接调用 GitHub REST 或 GraphQL API 查询当前信息会更加合适。

我会在 Neptune 项目里逐渐把这两年来发现的指标以网页的形式总结展示出来,希望在目前简单统计 star 数和 contributor 数量的基础上,为指标指导社群工作分享一些心得。不过由于我对前端知识的欠缺、指标开发固有的时间开销,以及本职工作的时间占用,可能进度不会很快。欢迎有前端展示经验的同学评审代码,也欢迎社群运营同学交流经验和提出需求。

赵生宇博士在今年初的时候写过一篇文章《开放协作的世界里,每一份贡献都值得回报》,议论了开源度量为开源生态发展可能带来的价值。结合 ClickHouse 社群自己探索社群指标模型,OSSInsight 项目的成立和发展,Open Digger 持续的迭代,我们有理由相信,度量指标助力社群运营,帮助更多开源社群定位自己目前的健康状况和潜在的发展改进方向将逐渐成为现实。

开源社群的注意力经济

《大教堂与集市》中有一个著名的 Linus 定律,“只要眼睛多,bug 容易捉”。在这本书里面,作者讨论了开源社群的集市开发模式,以其开放的特征,以及提供源码从而支持参与者基于相同的真实的源码进行高效交流,驯服了大型软件开发的复杂性。诚然,在其论述中,基于源码的,快速发布反馈缺陷报告和补丁修补的开源协同方式,能够制造出 Linux 这样的大型开源软件。然而,这一论述却有一个隐含的前提假设,那就是开源软件得到了足够多的关注。

Linux 无疑得到了全世界开发者的海量关注,并且一直如此。开源运动的启蒙阶段,大量的软件都被开创式的制造出来,彼时少有其他开源竞争者的存在,因此眼球或者叫注意力能够投放到的目标是相对有限的。另一方面,开源社群彼时仍是小众圈子,天然地对进入其中的注意力有一个质量筛选的机制,除非货真价实的黑客,否则很难在当时的开源社群当中生存。

然而,如今的开源软件从覆盖面到数量上已经彻底超出了所有人的预期,在软件渗透到社会生活的方方面面的前提下,形成了几乎任何软件都包含开源组件的“软件吞噬世界,开源吞噬软件”的格局。越来越多的开源参与者,已经观察到“眼球不够用”的情况了。换句话说,整个开源共同体的注意力供给总量,不足以应对所有开源软件高效发展的需求。

在这样的前提下,开源社群的参与者,应该考虑如何分配自己宝贵的注意力,以促进自身利益与开源社群发展需要的双赢;开源社群的维护者或说开源软件的作者,还需要额外考虑如何吸引开源参与者乃至整个社会可能提供的注意力,从而维系开源软件的高质量,并放大高质量开源软件的影响力。本文将从开源共同体具体的案例出发,讨论这两个问题。

勿以善小而不为

讨论如何合理分配参与者的注意力,提高开源协同效率之前,首先要做到的是增加有效的投入到开源社群当中的注意力总量。为此,我经常在宣传开源之道的过程中提到的观点就是“勿以善小而不为”。也就是说,如果你发现了开源社群值得改进的地方,无论是关于软件代码的还是社群建设的,都可以尝试推进落地,而不用因为担心这个改进微不足道,而自我否定了采取行动的理由。

这一点我在《如何向开源项目提交第一个 Pull Request?》视频里也提到过。许多开源新手刚刚接触开源协同,就是从经典的 fix typo 也就是修复拼写错误入手的。从这样一个具体的工作出发,熟悉了开源社群的合作方式,知道如何把自己脑海中的一个改进想法落实到一个具体的补丁,如何与其他社群成员沟通并推进补丁合并到主分支。另一方面,开源软件的迭代与演进,虽然很大程度上是以实现新功能和架构设计作为主路线和里程碑的,但是使得一个软件能够应对严肃复杂的生产环境,稳定、可靠且高效地服务用户需求,占据补丁多数的反而是细微的改进,一个一个缺陷的修复,以及日积月累的代码重构。

我接触的第一个开源社群是 Perl 6 语言社群,参与 Perl 6 语言社群的第一个 PR 就是有关文档翻译的改进。从这个第三方文档的参与开始,我把 Perl 6 的官方文档整个梳理和修改了一遍。在此过程中,我了解到语言设计的背景,目前遇到的问题,以此在知乎上写了若干介绍 Perl 6 设计和使用的文章,改进 Perl 6 的测试套件 Roast中间语言 NQP 的代码。

可以说,没有当时“勿以善小而不为”的冲动,我就错过了一次乃至以后许多次能够推开开源世界大门的机会。因为这样的理念和由此带来的经验,我在得到一份与 Apache Flink 相关的实习工作以后,就轻车熟路地订阅了项目的开发者邮件列表,并且积极地从代码迁移工作出发了解 Java 软件工程的最佳实践,从修复并发问题的工作出发熟悉分布式系统的常见模式和问题,参与讨论向其他经验丰富的工程师学习,最终能够独立撰写功能提案并落地实现,在一年半以后成为了 Flink Committers 之一。

时至今日,在这样的观点的驱动下,我往往是看到软件有可改进空间,就会尝试参与贡献。如果某个社群碰到了我曾经解决过的问题,或者有解决思路正好缺事实检验的困难,我也会评估自己的精力尽力而为提供帮助。GitHub Stats 的个人资料页上显示我曾经参与过一百多个开源项目,其中许多只是我碰巧看到了或大或小的破窗,顺手为之做出修复。

我相信解决开源社群的注意力投入和分配的问题,不是在总量一定的注意力的基础上,舍弃一些应该投入的工作,人为选择“重要”的工作,而是激发开源参与者持续产生有效的投入到开源社群当中的注意力,提高开源协同的效率,减少不必要的损耗,从而使得每一项应该投入的工作都得到相应的关注。

遵守软件工程实践

关于不必要的消耗投入到开源社群当中的注意力的话题,前两天我在发布了一条推文讨论。其中典型的就是为了追求数字,违背软件工程最佳实践,选择低效的方式参与开源协同。可以从两个方向来看这类反面教材。

开源参与者的动机

第一个方向是开源参与者的动机。

去年六月份,Linux Kernel 邮件列表上出现了一封反对刷榜式提交补丁的邮件。

这封邮件首先提到删除死代码和修改拼写错误,对于一个软件来说都是有价值的。但是,如果同一个人或者同一个组织的人,在短时间内把同样的平凡的变更拆分成若干个小的补丁提交,以达到增加被合并的补丁的数量的效果,那么这将会过分浪费项目维护者的时间。试想,一个参与者可以在阅读文档的过程中提出修改拼写的补丁,但是他在阅读一篇文档的过程中发现的三个相同或位置上临近的拼写错误,非要拆成三个补丁提交上来,这就确确实实浪费了评审和合并补丁的时间。

尤其是对于并非首次参与的开发者,成熟的开源社群实质上对其有一定的心理预期,上述邮件的作者 Qu Wenruo 也提到

It’s OK for first-time/student developers to submit such patches, and I really hope such patches would make them become a long term contributor. In fact, I started my kernel contribution exactly by doing such “cleanups”.

But what you guys are doing is really KPI grabbing, I have already see several maintainers arguing with you on such “cleanups”, and you’re always defending yourself to try to get those patches merged.

这并不是说,修复拼写错误和重构代码是新手的专利,实际上,大部分开源项目的核心开发者也经常做着类似的事情。这里的区别就在于你是按照一个符合软件工程实践的心态以合适的节奏和平均质量产出补丁的,还是为了追求数字而刻意反复多次要求项目的维护者立即处理这类平凡的变更。我想对于绝大部分开发者来说,都能够感受到与这两种合作方协同的区别。开源协同应当是一种高效的协同手段,如果开发人员朴素地就能感受到一种行为是违背这一目标的,那么它一定是错的。

当然,对于偶尔看见的平凡变更,我会很乐意顺手合并。如果出现频繁刷榜的人,我的观点与 Qu Wenruo 在后续回复中提到的一致。要么,可以考虑开发一个工具系统的扫描同类问题,例如 rust-clippy / golangci-lint / spotless 等就是系统性处理代码中的风格问题和容易导致错误的片段的工具。要么,至少先表明你或你的团队将要做这样的改动,并且明确时间,与项目维护者达成一致后在确定的时间段内集中提出类似的修改,以一种 BugBash 的方式来处理。这也是我在 Apache ZooKeeper 社群推动采用 Checkstyle 来减少补丁之间无谓的空白符变更的方式。

这种在开源社群当中提前讨论的模式,不只适用于前面提到的平凡变更,对于复杂的补丁,其实更应该提前沟通而不是直接甩一个合并请求。我在两年多前写《如何参与 Apache 开源项目社区》的时候就提过这个观点。

对于大的特性修改,国内开发者特别是一些写多了内部代码想也不想就提交的人,会犯的一个常见的错误是没有修改的背景和抽象设计,直接就 pia 上去一坨代码,英语又差,别人看不懂他也解释不通。其实代码的提交是一个协作的过程,需要达成共识,并不是说甩一脸代码别人就会去看,特别是 Java 之类的很多样板化的修改的代码,diff 贼多信息量贼少。对于任何 non-trivial 的改动,都需要有一定的描述来表明动机;对于大的改动,更需要设计文档来留存记忆。人的记忆不是永久的,总会忘记最初的时候自己为什么做某一件事情,设计文档的沉淀对于社区摆脱人的不确定性演化有至关重要的作用。只有记下来最初是为了做什么事而做出的这个改动,以后移交代码或者教授新人的时候才好援引和解释。

其实,这也是提高开源协同效率的方式。核心参与者的注意力是有限的,层次化地讨论问题,渐进地融入到开源社群当中,能够逐步取得现有团队的信任,并在其他核心成员的帮助下完成需要更多投入的工作。

对于真的毫无意义的补丁,我会说明情况并拒绝合并。典型地,例如增加 Getter/Setter 式的自解释代码的测试,这类测试是冗余的;或者把 do-while 循环改成 while 循环一类的纯风格变更。

《ZeroMQ 权威指南》里提到,如果一个缺陷报告无人理睬,那么就代表着不是个问题,应该即时关闭。Apache SkyWalking 的维护者也表达过类似的观点,如果没人想修复一个问题,那么就说明这不是个问题,至少不是一个紧急或常见的问题。我认为,具体的策略是一回事,清晰地让社群成员及潜在的参与者尽可能了解社群的风格,确实能够有效减少不必要的摩擦和注意力消耗。

关于代码风格,我的观点是每个深入到模块当中的开发者都会逐渐把模块的风格和结构重构成最符合自己认知的模样,从而使得自己能够高效地相应需求做出后续变更。所以,长期参与的开发者,浸淫在某个模块当中的专家,才有权决定一个模块应该以何种风格来写成。这也是我前面在提到参与其他开源社群时,是“评估自己的精力尽力而为提供帮助”隐含的一层意思。如果只是我个人风格偏好不同,或者我有一个想法,但是并不能保证这个想法落地之后遇到衍生问题能不能及时响应,出于基本的负责任的态度,我不会认为项目的维护者理应接受这个看起来合理的提案。

开源维护者的动机

第二个方向是开源维护者的动机。

开源维护者很大程度上决定了开源社群的发展方向,他们的认识、行动与倾向将影响社群招徕何种注意力,以及注意力将会投入到哪些工作当中。如果开源维护者没有仔细考虑这个问题,或者简单地将开源协同所需的注意力投入等同于广告营销式的注意力收割,那么在实施过程当中就会引起开发者的反感和挑战。

@Xuanwo 在上个月的一篇文章《开源运营当论迹不论心》里以 TDengine 运营的“灭虫活动”为案例说明了这种缺乏考虑的弊端。文章当中提到,希望通过物质激励或者传播技巧招徕更多参与者,这本身无可厚非。但是,违反软件工程的实践,从维护者的角度刻意采用一种更低效的方式“改进”代码,利用人工地毯式搜索的低效特点,制造相同需求需要更多参与者的结果,虽然赢得了短期的喧闹,过后却是一地鸡毛。

这篇文章下面的评论当中 TDengine 的创始人提出了这个活动的动机。

@jtao1735 作为 TDengine 的创始人,认真读了这篇博客,很认同一个观点,那就是开源运营需要帮助开发者去解决真正的问题,包括 TDengine 的核心技术问题,而不是一些简单的 typo, 对于有一定经验的程序员而言,意义不大。我们当时发起这个活动时,是因为在几个高校巡讲,想吸引在校大学生加入进来,不少学生连 GitHub 账号都没有,因此把门槛大大降低了一下。

我对这段话也做了回复。

其实运营本身就有一些 marketing 的属性。“帮助”这个词太笼统了,不如说向社群成员和潜在的社群成员展示和分享 TDengine 是什么,TDengine 要解决什么问题,TDengine 是怎么解决这些问题的。从这三个角度出发,哪些地方是合适的切入点,谁是目标受众,就会清楚很多了。

每个层面的 contribution 都值得感谢,原文和我个人都不认为 fix typo 一类的工作不应该做,而是不用这样大张旗鼓的做。

想吸引在校大学生加入进来,不少学生连GitHub账号都没有,因此把门槛大大降低了一下。

这个说法也是不成立的。起码说明你的目的就是吸引在校大学生“进来”,也就是在 contributor graph 上出现,至于进来做什么,无所谓。这就是问题本身,而不能用来回应问题。GitHub 怎么用,首先 GitHub 自己有文档,而 PingCAP 为了解决这个问题,采取的做法是做一个系列视频【GitHub 新手指南】极简教程教你如何给开源仓库提 PR

最后一段也说明我在这一节开头的观点,也就是开源维护者的认识、行动和倾向将影响社群招徕何种注意力。

如果你关注如何将开源软件打造成高水平的软件。那么你所抛出来的问题就有一条明确的主线。例如 Flink 长期讨论如何在 Kubernetes 环境上部署,前后数年均在不断完善这项功能,从将其作为一个单纯的新的虚拟机环境,到利用 Kubernetes 的资源管理能力,再到整合 Operator 应用管理框架,吸引了许多专业人才贡献聪明才智和生产用例。

如果你关注的是如何提高无差别的 star 数或者 contributor 数量,而不是将它们作为主线发展的一个校正自己行为的辅助指标、副产品,那么为了这些数字,会去做一些与软件发展不相干甚至相抵触的事情,也就不奇怪了。

说到底,开源协同是一种上限极高、潜在参与人数极多的软件工程模型,要想在这样开放的环境下协同海量的潜在参与者,并不是一件易事。如果简单地想象成“开源即成功”或者“人越多越成功”,那势必得不到自己想要的结果。至于某些开源维护者把自己都不知道怎么做或者不愿意做的工作,异想天开地“向社群提出一句话需求”,幻想有个盖世英雄能够出面解决,那就是彻头彻尾的巨婴心态了。

《时代周刊》评价 Linus 的时候说,“有些人生来就注定能领导几百万人,有些人生来就注定能写出翻天覆地的软件。但只有 Linus Torvalds 两样都能做到。”这也侧面体现了开源协同所需要的品质之稀缺。同时,这句话也暗含着 Linux 的核心决策尤其是早期的核心决策,虽然已经有众多参与者的贡献,但是 Linus 是决定 Linux 要往何处去的那个人。这就反驳了上文提到的维护者想不清楚,反而希望另一个素未谋面的救星能够机械降神的荒唐。

类似的运营例子其实不少,例如 Alluxio 开源社区贡献积分奖励计划。其实单从市场声量和招徕用户方面,这些小奖品换注册、点击和转发等等,都是行之有效的 To C 营销手段。对于宣传大使(Ambassador)参与演讲和公开站台,如果采用报销行程和周边反馈,可能比起积分货币会是有效的方式。至于开源平台和技术上的评估,就不太适合用数量来简单衡量。

高质量的开源社群在软件开发上要的是精英社群,不能指望依靠一场活动“转换”一批参与者。这个过程是长期主义细水长流的,搞好流程和文档,找到外向型的社群成员,多多曝光就行。哪怕是搞活动或者专项冲刺,也需要注意和项目发展契合。比如 Apache 项目参与 GSOC 大多是社群切实需要的工作,而不是这样明显由脱离实际开发活动搞出来的标准化 issue 或 pr 数量。追求简单的数量容易引来刷榜的投机者,这些投机者投入钻营得越厉害,例如做出前文《开源参与者的动机》里提到的种种怪事,实际上社群整体的开发效率会受到越严重的影响。

如果真的希望投入资金激励社群开发活动,可以参考 The Perl Foundation Grants 的做法,建立一个拨款委员会和一套技术评审流程,把赞助者的钱用于激励完成社群期待完成的工作上。实际上,GSoC 的形式可以认为就是 Google 出钱给开源项目用于推动它们重要不紧急的工作的落地。让开发者运行开发者社群,才能更准确地吸引到匹配的参与者,并且利用好这些参与者投入的精力。

找到共同价值

既然应该让开发者运行开发者社群,那么对于这些承担了运营职责的开发者来说,具体应该怎么找到匹配的参与者并让这些参与者高效地参与呢?我想这里的指导原则是要找到开源社群和潜在参与者的共同价值。

这里需要强调的是,找到共同价值的过程,不仅仅是开源维护者发现潜在参与者的需求,匹配社群当中需要完成的工作,同时也是参与者理解社群的要求,配合其他社群成员共同创造价值的过程。

为什么强调这点呢?我在发布《共同创造价值》之后,有回复提到“共同创造价值,而不是欢迎谁给谁贡献代码,不存在谁给谁的事情”。这就是前半段所强调的,先来的社群成员应该平等地追求合作。我在观察多个开源社群具体的运营过程里也发现,存在“既看不起参与者,又要惯着参与者”的情况。这也是指标导向带来的不良后果。因为要追求数字,在执行过程中就弱化了对参与者的筛选,来者不拒;因为来者不拒,大量招徕的参与者水平不足,而又强行要求甚至需要他们完成复杂的工作。这样僵持之下,就变成了酷似考试请枪手,枪手还是无偿代打的局面。甚至于参与者在环境的误导下,认为维护者有义务解决他个人的问题。

这一点在《对话吴晟:真正伤害开源的是开发者本身》一文里也有提及。

真正伤害开源的,还是开发者本身。最常见的两个问题。一个是开发者,特别是中国的开发者认为,软件作者去帮助他人是天经地义的,因为整个软件是你写的,所以我来问你问题,你就应该有问必答。如果你不答,就认为你这个人摆架子。而不是考虑因为软件作者用了自己的时间提供服务,所以应该表示感谢。

所以我想要在这里特别强调“共同”这个词所隐含的平等的含义。

在这样的指导原则下,与人协作并不是一件难事。例如,我在成为 Apache Kvrocks (Incubating) 项目的导师之后,在构建过程当中发现了 CMake 脚本存在优化空间。我想起来在发布《CMake 是怎么工作的?》之后,@PragmaTwice 分享了他的一些经验,正好跟我想做的改进相符合。因此,我邀请他把他的经验实践在 Kvrocks 项目上,也欢迎他从 C++ 资深开发者的角度整体评审一下 Kvrocks 的工程质量。

很快,@PragmaTwice 就完成了一系列构建系统改造的工作,并且提出了若干个可以进行工程改进的思路。社群当中与他合作的维护者能够看出他在 C++ 方面的技术水平,并且交流起来没有障碍。我想如果他能持续参与到社群当中,并且愿意承担更多责任的话,成为项目维护者只是水到渠成。

其实,这才是开源协同本来就应该有的样子。因为自己想做,恰好有一个开源社群也有类似的需求,双方一拍即合按照软件工程的最佳实践推进开发。我想这就是向潜在的开发者宣传开源协同的魅力的时候应该着重提及的内容。因为如果你只盯着公司业务代码,尤其是在校学生都很难接触到实际的需求,对于技术成长有追求,希望看到自己的工作落地会怎么样的开发者来说,提供一个有现成软件基础的、有用户群体的开源软件,是一个很大的诱惑。

另一方面,这种共同价值并不局限于代码参与,甚至不应该局限于单一项目。我进入开源世界的大门是 Perl 6 语言社群,但是不再参与也有两年多了。我在完成 TiDB 测试迁移的工作的时候,前后协同了不下五十名开发者,其中绝大部分也在一段时间后离开。

但是,在这个过程里,首先我完成了工作工程上本身要达成的目标,提高测试代码编写和运行的方便性,减少不同测试的耦合,从而为软件测试覆盖和能够有自信地常态化重构做准备。此外,我做了一个完整的从提案到实施,从模糊的原则到具体每个场景的最佳实践的案例。此后开发的内存悲观锁等功能开发就可以 follow 这样的案例来进行。这样的软件工程实践能够影响一部分开发者,我已经很开心了。

最后,这些参与者当中有人留了下来,参与到其他开发工作当中;也有人告诉我,他是因为这个契机才开始参与开源社群。我想这跟我从 Perl 6 打开的大门进入到开源世界,最终在其他的开源社群当中发光发热,应该会是类似的。对于参与者来说,他所期待的基本价值之一是个人成长,那么我所维护的开源社群能够在他成长的过程当中跟他一起做出一些贡献,让这个软件更好一些,不也足够了吗?

当然,共同的价值可以是非常多元的。我个人的理想是提升整个软件行业,所以在写作的过程中难免会经常有所偏向。最近看到 Taichi 开源社群有个 Taichi Voxel Challenge 活动,邀请任何人基于 Taichi 创造自己的三维图形作品。这也算是邀请测试的一种形式。创造出来的作品看着非常有趣,“只是为了有趣”也是一个很好的共同价值。

Taichi 生成的庭院

如果开源社群的维护者所追求的“价值”,就是纯粹的数字,以满足自己依托开源软件创办的公司在宣传和融资或其他经营方向上的需要,或许前面提到的种种市场营销手段,就是维护者所追求的价值。这样的维护者与刷榜换取虚假的名声或者直接物质激励的投机者一拍即合,或许也可以找到某种共同价值。

但是,这与我所坚持和相信的“开源社群的目的是制造高质量的开源软件,开源协同是一种高效的软件开发协同手段”背道而驰。

我在写作《如何参与 Apache 开源项目社区》之前,就有许多人问过我“怎么才能成为 Apache Flink 的 Committer 呢”。那篇文章某种意义上是回应这个问题的,但是我并不是功利地说应该怎么去增加贡献量,怎么创造出新功能的需求,而是讨论怎么与其他社群成员协作,在交流和合作开发上有哪些技巧应该掌握。最后落脚点在“兴趣使然”,也就是目的是成为开源社群力量的一部分,齐心协力完成复杂系统的开发和价值交付。在这个过程里收获成长和信任,成为项目的 Committer 或者参与到整个 Apache 社群当中成为基金会正式成员,也就是顺其自然的事情。

同样,如果开发开源软件的最终目的只有商业成功,那么目前看到的可行的终局就是 MongoDB 或者 Elastic 的形式,也就是走向 Source Available 软件的道路,禁止其他参与者商用。实际上,我很推荐这些公司一开始就采用商业保护性质的源码可得软件协议,例如 Business Source License 1.1 或者加上 Commons Clause 等。如果你想清楚了这一点,你仍然能够通过拆分模块的方式生产开源组件,鼓励生态建设,或者简单地只考虑用户社群,用户当中有开发能力的人,本身对它采用的软件有改进的需求,也没有商业上的冲突,是可能会提交有意义的缺陷报告或者补丁的。但是,这是一种商业上的“免费增值”营销策略,与开源运动的精神就相差甚远了。同时,想清楚这一点,在开源的名义下追求参与者数量或者各类行为活动的数字指标,就是完全的无稽之谈。

开源软件的源代码是可以免费获得的,因此销售源代码必然是不可行的商业模式。开源软件的实施是有附加值的,许多企业服务公司实际上正大规模地使用开源软件组装出自己的解决方案。开源吞噬软件的背景下,供应链上的开源软件的维护是必要成本,因此 RedHat 通过订阅模式能够建立起自己的收入来源,SQLite 的作者为支付酬劳的客户实现功能和提供问题相应支持。开源共同体的生态潜在的价值是无穷无尽的,CockroachLabs 通过提供云上托管数据库服务盈利,为了调优,它需要改进 Golang 运行时的性能,这些改进最好是合并到上游,才能保证在 Golang 后续演进的过程里 CockroachDB 的代码依赖的逻辑自然不被破坏,能够平滑升级。因为这样的原因参与到上游开发建设的参与者,不需要掌握上游的某种垄断权利来盈利。

❌