你有没有想过拥有自己的开源项目?我敢打赌,你一定有——因为你正在读这篇文章。

也许你现在正在考虑这事儿。也许你来这里是为了了解应该对开源项目有何期望,你将面临哪些挑战,以及如何应对这些挑战。好吧,你来对地方了。

以下指南基于我个人拥有一个开源项目中的经验。我指的是拥有一个开源项目,而不仅是向一个开源项目做贡献。这两者之间有着巨大的差别,我们将学习为什么。

所以让我们从开源项目负责人终极指南开始吧……

目录

1_now0_4liLR7fJcvvnWWStQ

我是谁

我叫 Jeb,这几年一直在维护几个开源项目。其中最受欢迎的一个是 @angular-builders,它也是我从中学到最多东西的一个开源项目。在写这篇文章的时候,它在 GitHub 上有约九百颗星,月下载量大约一百万。

是的,它与 Angular 或 React 这样的大型项目相差甚远,但是我觉得我有足够的经验和你分享,帮助你避免重蹈覆辙。更重要的是,帮助你了解拥有一个开源项目的成本。

那么什么是开源

首先,让我们建立共同语言,并在关键术语和定义上达成共识。

什么开源(open source)?

这里是一个非常通用的定义,来自著名的开源百科全书(亦称维基百科):

开源是指允许复制或修改的信息向公众开放的概念。

或者,从软件开发模式来看:

开源模式是一种去中心化的软件开发模式,鼓励开放协作

开源软件开发的一个主要原则是对等生产,源代码、蓝图和文档等产品免费向公众开放。

以维基百科为例,我们有编辑文章的人(贡献者),也有批准编辑的人(经验更加丰富的成员、版主)。

如果我们将其投射到软件世界,那些编辑们将形成一个开源项目的核心团队,而做贡献的人就是贡献者。

维基百科是一个非常庞大的开源项目,但这一切都是从一个小东西开始的。维基百科诞生自新百科全书(Nepedia),它的创建是有原因的:

尽管邮件列表里有很多感兴趣的编辑,还有一个全职主编 Larry Sanger(威尔士聘请的哲学研究生)在场,但新百科全书的内容写作还是非常缓慢,第一年只写了 12 篇文章。

所以第一个问题来了……

为什么你应该关注开源

你可以想象,将某些东西开放给更多人的一个主要的原因是 为了招募协作者(collaborator)

Together we’re strong.
(Zarya, 2016)

在写这篇文章的时候,维基百科拥有 37899499 个注册账户,其中有 134022 个账户正在积极编辑。

想想看…… 134022 名活跃的协作者。噢,它还有六百万的内容页

如果新百科全书(维基百科的前身,Nupedia)没有转向开源,这个数字还会是这样吗?我对此表示高度怀疑。

在软件领域也没有什么不同。为了解决某个问题,你必须写代码。为了解决一个大问题,你必须写很多代码。而为了妥善解决这个问题,你必须写出高质量的代码,做出高质量的设计,等等。

所有的这些都需要资源。说实话,你可能还没有这些资源。毕竟,你需要交房租。

为什么要启动一个开源项目

虽然招募协作者是一个合理的动力,但几乎没有人仅仅因为这个原因就启动一个新的开源项目。你的理由可能会有所不同,但是我们来聊聊最流行的理由吧。

#1 你想解决一个没有免费解决方案的问题

你遇到一个问题,但没有任何东西可以为你解决它(或者有,但是要花钱),所以你不得不自己解决它。你设法解决了这个问题,你对自己的工作感到非常兴奋,而且你认为其他人可以从中受益,所以你把这个项目开源了。

#2 你想成为发起人

你想成为一个开源项目的发起人,你想在你的简历上写上那漂亮的一行。你真自以为是(毕竟,我们都是人)。如果这是你的主要理由,那么我向你保证——读完本指南之后,你会重新考虑的,这么做可能很不值得。

#3 你想比别人更好地解决一个问题

你面临一个问题,有一个开源项目实际上为你解决了这个问题,但是它不够好(在你看来)或者它没有你需要的确切功能。

如果你仅仅因为这个就创建一个新的开源项目,那么你 很有可能处于第二种情况(自以为是)。让自己成为一个贡献者,然后为现有的项目创建一个 PR 吧。

如果那个现有的项目有不同的愿景,创建 PR 并不可行,那么你应该考虑通过在你的项目中重用它的功能来扩展它,或者 复刻(fork)它,这可能会让你以后省去很多头疼的事情。

#4 你想通过创建一个开源项目来解决一个问题

你面临一个问题,并且现在没有任何人为你解决过它。所以,你认为从一开始就以开源的方式解决问题是一个非常好的想法。

在我看来,并不是。

解决这个问题,确保它对你有用,然后进入第一种情况。

这是我最常发现人们创建新的开源项目的四个动力。但是在这篇指南中,我们将主要关注第一种情况。

原因很简单——我相信,如果你发起开源项目的主要原因不是热衷于分享或者贡献你所做的东西,那么这就不成立了。

在相当长的一段时间内,你帮助别人这一事实可能是你得到的唯一回报。如果这不是你要找的那种满足感,那么你或许应该就此打住,不要再浪费你的时间了。

另一个相当流行场景值得一提:有些公司将它们的部分代码开放给社区。例如 Angular(由 Google 维护)、React(由 Facebook 维护)、VSCode(由微软维护)等等。

它们的理由可能各有不同,但是赢得协作者和为社区做贡献肯定是少不了的。

虽然我不能否认这种做法的重要性,但是这种情况与其它的情况有很大不同,因为维护这些项目的雇员们会因自己的工作得到报酬

如果你在一家考虑创建开源项目的公司工作,那么这里的大部分内容对你来说还是有意义的,但是动力可能有所不同。

那么你是否应该创建一个开源项目呢?

如果要我用一句话对这部分进行总结的话,那就是:

确保你的意图符合你的期望。

要相信,你想拥有一个开源项目与你实际拥有一个开源项目并不相同,你将会这接下来的几章中看到。

1_6OX0spWqVQZxG3ue5D_EBA

如何发起一个开源项目

所以你现在处于第一种情况——你有某个问题的解决方案,并且你渴望与世界分享。让我们再强调一遍:

  1. 它与你的自负无关
  2. 你不是希望从中获益
  3. 你真的想要帮助有着相同问题的其他人

对于所有这些问题,如果你的答案都是肯定的,那么这里是一份为你准备的快速检查单,让你确保你做的是正确的事情:

  1. 确保开源是正确的形式。如果它只是某个你想与世界分享的小东西,那么一篇博客文章可能就足够了。
  2. 仔细检查是否已经有类似的项目存在。或许你的解决方案对某个已有的开源项目来说是一个绝佳的 PR。

为即将到来的事情做好准备

正如我所提到的,拥有一个开源项目会带来很多挑战。

其中最突出的一点就是,它需要你投入大量时间。你为项目做的一切都需要时间,无论是写代码、管理议题(issues)、更新依赖、与人交谈、回答问题等等。

你每多投入一分钟到你的开源项目中,你本可以投入到你的家庭、爱好、健康和其它的一切中的时间就会少一分钟。

你能做的唯一一件能让这种情况变好的事情就是开始委派任务。当(或者我应该说“如果”)你有足够多的协作者时,你可以将部分责任外包给你信任的人。

代码分离

所以我们开始了,你有一个针对特定问题的解决方案,并且你认为其他人可以从中受益。这个解决方案仍然集成在你的代码库中,你可能并不想让整个代码库开源(除非你真的想)。

首先你需要将这部分代码从代码库中的其它代码分离出来。

idigomnotoya-refactoring

这最终会意味着将要开源的所有代码都会位于一个单独的目录中。

让代码变得通用

确保新目录中的代码是通用的,同时也不局限于你那特定的问题。如果需要的话,写一个抽象层。

举个例子,我发起angular-builders 的时候,有一个非常具体的需求(来自我其它的开源项目之一),就是为 Angular 构建添加一个自定义原生模块加载器。

我本可以创建原生模块构建器,它只用于这个目的。然而,我意识到我可以以相对较低的代价创建一个更加通用的解决方案,来解决相似(但不相同!)问题。

custom-webpack 构建器就是这样诞生的。

保持简单

通用的方案会非常棒,但要注意别过头了。

过早优化(premature optimization)和过度泛化(over-generalization)是软件工程中两个非常著名的问题。你应该找到那个最佳的点,你的解决方案可以解决你的问题之外的问题,但是不能解决世界上所有的问题

如果你建立了一个量表,其中你那特定问题的解决方案是 1 ,而世界上所有问题的解决方案是 100,那么你应该从 2 开始。

你的通用代码应该能比你的特定代码多解决几个问题。

吃自己的狗粮

坚持在你的代码库中使用这部分通用代码——这样可以确保你去除不必要的部分,只留下必需的。它可以确保你要开源的代码能够正常工作。

记住,你是你的开源项目的第一个用户。

别被告了

如果你正从公司的代码库中提取代码,咨询一下你的上级,如果需要的话,也咨询下法律部门。确保他们支持你的提议,并且你打算开源的那部分代码不会违反公司的知识产权。

这也能帮你决定哪个开源协议更加适用于你的项目。

当一切正常,代码已经分离并且足够通用,你也得到了所有的批准(如果需要的话),那么就是时候向世界开放它了。

1_oT-ftfrBJ_BvmaZk2zumpg

当你的开源代码分离完成并且通用化,就可以把它从你的代码库中完全分开了。

公开你的代码

首先,你需要开放你项目的源代码(在一天结束时,这让它成为了一个开源项目!)。

在线托管源代码的选择有很多,但是我们将会采用默认的——GitHub。

  1. 在 GitHub 上创建一个新仓库
  2. 克隆该仓库
  3. 将源代码从你之前创建的目录(暂时不要删除这个目录)移动到这个仓库
  4. 提交并推送(Commit & push)——就这样,它现在是一个开源项目了。

不是吗?

创建一个包

你的项目已公开,但是没有人使用它(包括你,因为你正在自己更大的代码库中使用这份代码的副本)。并且,没有人知道它的存在。

此外,你的项目在网上公开的唯一形式是源代码,而使用它的唯一方式就是将它复制-粘贴进代码库中。这种方式并不是很方便,对不对?

为了恰当地分发你的项目,你需要:

  1. 从源代码创建一个包
  2. 将这个包放到某个公开的程序包注册中心中。(根据你的生态系统进行选择,例如,对 Java 来说可能是 Maven Central Repository,对 JavaScript 来说可能是 Npm Package Registry,等等)。

这时,你要为你的新仓库添加一个构建链,确定项目的名字,等等。

我并不打算对整个过程进行分解,因为它非常依赖于你的生态系统、工具集和你使用的编程语言。

你可能无所不包,对你来说,确定新项目,添加构建链,发布程序包都是小菜一碟。如果是这样的话,那就好办了!

你可能习惯于只写代码,从未面对过所有的这些定义、配置、工件和类似的东西。对你来说,这可能是一个全新的新世界。

如果你是后者,就该学习了。我向你保证,要不了多久就能达成目标。

不论如何

当你完成脑海中所有缺失拼图的拼装,你就学会了有关程序包注册中心的一切。并且你的程序包已经发布了,那时,也只有那时你才能真正地认为你的项目开源了

这时,你可以告诉人们:“嘿,我已经有你那问题的解决方案了,你只需要把这个包添加到你的项目中,然后使用它就行了!”

进行完整性检查

确保你的项目在开始像病毒一样传播之前能够正常工作。

对程序包的完整性检查,实际上就是从你那更大的代码库中删除通用的目录,并改用这个公开的程序包。

毕竟,你是你的开源项目的第一个用户

如何处理你的代码库中的进一步开发问题

当你开始在你的代码库中使用这个程序包时,开发流程可能会发生变化。在之前,开源代码是你代码库的一部分——你可以立即使用所做的更改。

但是现在,它与你代码中使用的任何其它第三方软件包一样,都是外部的软件包。

因此,当你在崭新的开源项目中开发新的功能时,必须先发布它,才能在你那更大的代码库中使用它。然而,如果你不确定它是否可行,你就不能发布它。因为一旦发布,就可能会影响其他用户。

为了避免发布有问题的版本,你可以做以下几件事情:

  1. 用测试覆盖你的代码,包括单元测试和端到端测试。
    我认为我不需要跟你讲测试是多么的重要。
  2. 在本地打包并安装新版本的程序包到你那更大的代码库中。
    验证一切按预期工作之后,你就可以发布它了。
  3. 发布一个 Beta 版本,仅开放给那些明确希望使用该版本的用户,而不是全世界。
    例如,在 npm 的程序包注册中心中,dist tags 就可以用于这个目的。
    默认的 tag 是 default,当你运行 npm install mypackage 时,它实际上会运行 npm install mypackage@latest。当你用另一个 tag(比如 beta) 发布一个新版本时,人们只有显式地从这个 tag 安装才能获取到最新的版本:npm install mypackage@beta

小结

与前一节的纯理论不同,这一节实际上会要求你做一些事情。根据你经验和学习能力,可能需要花费你几天甚至几周的时间来完成这个必需步骤,而我们甚至还没有开始呢。

这就是为什么我有责任再问你一次:

你真的准备好将宝贵的时间奉献给社区了吗?

1_z5sGJuWoz02x3uSBaF4tLg

如何为你的开源项目编写文档

这篇文章的前两部分是针对那些正在考虑创建开源项目的人写的。我想让他们知道应该期望什么,并帮他们在开源世界里开个头。

这一部分,以及后面的部分,也会与那些已经在维护开源项目并希望有所改进的人们有关。

这一部分的基线:

你已经有了一个开源项目,我们可以在 GitHub 上访问它,并且可以很容易地通过某个程序包注册中心使用它。

为什么你需要文档,它应该包含哪些内容?

一个没有文档的开源项目毫无生命可言

之所以说毫无生命,是因为没有人会深入你的代码,去了解应该如何使用它。在此之前,甚至没有人知道你的代码是干嘛的。

所以你的文档应该基本上包含这两部分的内容——它是做什么的以及如何使用它。这两点是文档的奠基石,是文档的必备内容。

如何写项目描述

人们在进入某个 GitHub 仓库时,首先看到的就是项目的描述信息。因此,一个好的描述应该简明扼要地回答“它是做什么的”这个问题。例如:

React:

一个声明式的、高效且灵活的 JavaScript 库,用于构建用户界面。https://reactjs.org

Moment.js:

在 JavaScript 中解析、验证、操作并展示日期。http://momentjs.com

Angular builders (这个项目是我的):

Angular 构建门面扩展(Jest 与自定义 webpack 配置)

你可以在仓库的 About 部分编辑描述信息:

Screen-Shot-2021-03-11-at-10.20.43-1

如何写 README.MD 文件

README.MD 是一个位于你的项目根目录中的文件,用 Markdown 语法编写,它包含别人需要知道的有关你的项目所需要的一切信息。

README 文件应该包含一个详细的描述(在“它是做什么的”这个问题上进行展开),以及关于“如何使用你的开源项目”的详细说明。

说明的内容应该覆盖每一个公共 API,最好是有使用示例。

这里编写良好 API 文档的几个要点:

  • 最简法则——API 和示例越简单,使用者就越容易理解它是做什么的以及如何使用它。
  • 条理清晰——对所有的 API 方法都使用相同的模板和可视化结构。这样,你就可以定义自己的语言,向使用者传达 API。
  • 变身用户——总是从用户的角度编写 API 描述。假设你对项目的内部一无所知,并且这份文档就是你的全部。
  • 保持最新——随着项目的演进,API 可能会发生变化。确保你的 README 文件总是包含最新的 API 和示例。

README 可以(但不是必须的)包含以下内容:

  • 贡献指南的链接
  • 贡献者名单
  • 变更日志的链接
  • 最新版本
  • 协议
  • 构建状态
  • 下载次数
  • 用于快速反馈的聊天链接

这里是一个优秀 README 的示例。

何为徽标

徽标(Badge)是一种很好的方式,可以直观地显示项目的基本信息,例如:构建状态、版本、协议以及项目使用的各种工具。

选择有很多,但是我推荐你使用 shields.io 的徽标。他们的徽标很丰富。

给 README 文件添加徽标真的非常简单:

  1. 前往 shields.io
  2. 选择合适的分类
  3. 点击你想要添加到 README 的徽标
  4. 填写需要的信息(如果需要的话)
  5. 从下拉菜单中选择 Copy Markdown
  6. 将 markdown 粘贴到你的 README 文件中

Screenshot-2021-03-12-141342

徽标通常放在 README 文件的顶部,就在详细描述的前面。它看起来像这样:

1_hgG8kurYMkdAsMXxji4iVg

确保你进行了测试

API 参考很棒,但是没有什么比使用你的公共 API 的真实代码更好了。

完善文档的最佳方式之一就是用描述性测试来覆盖代码。有时,测试比任何文档更能解释代码。

小结

在这一部分,我们只覆盖了文档的基础知识。例如,除了 README 和描述信息,还有很多其它内容。随着项目的发展和问题(issue)的出现,它们将成为文档的组成部分。

然而,对于任何一个像样的开源项目来说,拥有一份覆盖公共 API 的 README 文件只是最低要求。

david-menidrey-16ep3TGZR-0-unsplash

如何推广你的开源项目

我们已经讨论过发起一个项目意味着什么,如何以最佳的方式去做,以及如何为它写好的文档。

现在,我们就来聊聊如何将公众的注意力吸引到你的项目上来,以及如何在吸引与正确管理贡献者上面对项目进行优化。

这一部分的基线是:

你已经有了一个开源项目,人们可以在 GitHub 上访问它,它有良好的文档,并且可以很容易地通过某个程序包注册中心使用它。

如何宣传你的项目

咱们开门见山吧:随着项目的发展,你根本无法独自处理每件事情。所以,如果你想让项目长久生存下去,繁荣昌盛,你需要更多的人参与到项目中来。

为了让更多的人参与到你的项目中,你需要更多的人知道它,更重要的是相信它。

根据我的经验,将你的开源项目暴露给合适的受众的最佳方式是 使用知名的资源渠道,并写一篇关于项目的博客文章。

资源渠道可以是纯面向开发的(比如 dev.to),也可以不是(比如 Medium)。

所有这些资源之间都有一个共同点:它们都有既定的受众,并且是相关的受众。

你也可以在不同在线资源之间交叉发表你的文章,从而覆盖到更到的受众。但是要注意,交叉发表有几个弊端:

  • 每个平台可能都有各自不同的标记语言,你不得不重新调整所有的格式
  • 维护性——如果某部分内容变了(事情 发生变化),你就需要在所有的资源中对你的博客进行更新

如果你选择 Medium,我会高度推荐你将自己的文章提交到某个大型专栏。这将要求你做更多的事情,因为你需要让你的文章满足专栏的要求。但是它也能确保更多的受众接触到你的文章,更重要的是,相关的受众。

你还可以决定选择 metered paywall(你可以从中赚钱!):

属于付费专区的故事也可以通过主题分发给 Medium 读者,这些主题可在我们的主页、主题页、每日摘要和应用程序中被推荐。

我无法告诉你哪种方式更好,但是我个人更喜欢专栏,因为它能确保你的文章被读者看到,而不是像“符合发布条件”一词这么模糊。

如果你的博客广为传播,那么它就可以产生级联效应,为你的开源项目带来更多的人。

例如,如果你的 GitHub 项目在发表文章之后的一天之内收获了几十颗星,就可以进入 GitHub 的趋势页,这本身也是另一个暴露源。

让你的博文更加优秀的几个要点:

  • 以问题陈述作为开始,它甚至可以是博客的标题。
    人们通常是在寻找某个特定问题的解决方案,在他们决定花时间读你的文章之前,他们应该知道你的文章是否是他们正在寻找的东西。这里是我写的一篇文章的示例
    如你所见,它在标题中清晰地阐述了它所解决的问题。
    如果你在谷歌搜索“Customizing Angular build”,它将会出现在排名靠前的几个结果中,并且你可以直接从搜索页面上看到它解决了哪个问题。
  • 描述一下你的项目为什么要解决了这个问题,它是如何解决的。
  • 提供一份详细的逐步指导,从安装开始,以可以正常运行的示例结束。
    有很多开发者都更喜欢可以正常运行的示例,而不是博客文章。
  • 在发表文章之前,先获取一些反馈。
    让你的朋友们仔细阅读你的文章,不要告诉他们你的文章是关于什么的,看它们能够自己说出来。如果他们做不到,那么很有可能是你的文章不够清晰,你需要写得更加详细。

在发表博文之后,在社交媒体上与你的朋友、家人和马路上的陌生人分享。

这将会增加你的项目的曝光度——但是你也要让人们愿意向你的项目做贡献。

项目如何吸引贡献者

最好的办法就是与他人一起发起一个开源项目。通过这种方式,你从一开始就能拥有一个可以一起分担责任的团队。

然而,并不总是如此。

如果你独自发起开源项目,你必须吸引贡献者。根据我的经验,有两种类型的贡献者:

  1. 想要产生影响并在找项目做贡献的人(虽然很少见,但是也有)。
  2. 使用你的程序包并且发现了缺陷或者缺少某些功能的人。

在这两种情况下,只在 GitHub 上分享你的源代码并写一篇关于如何使用它的博文是不够的。以下是一些可以使人们愿意做贡献的事情:

一个待实现清单

它可能包含已知缺陷、规划的功能或者其它的东西。这个清单会让第一类贡献者更容易选择正确的事项并发起 PR。

它可以是一个独立的清单,你也可以(或许是应该)使用 GitHub 上的议题(issues)和标签(labels)。

一份 贡献者指南

基本的贡献者指南应该解释仓库的结构,包含关于构建并运行项目与测试的逐步指导。扩展的指南可以包含架构、设计决策、行为准则等。

Atom 的贡献者指南就是一个很好的例子。千万不到低估它的价值!随着项目的发展,这需要花费大量的时间,我希望我从一开始就创建它,并随着项目的发展逐渐更新。

不幸的是,我没有人指出它的重要性,而我的项目今天都还没有贡献者指南。它一直在我的待办清单上,但总有比它更紧急的事情。

感谢你的贡献者们

在项目的主页列出贡献者们,这会让他们有更多的动力去做贡献。

只添加他们的名字就够了,但是我将会推荐你使用 All Contributors。它不仅能为你的所有贡献者创建带有个人资料图片和徽标的精美部分,还能通过创建 PR 来自动添加新的贡献者,将贡献者添加到这个区域。

小结

我们在这一部分讨论了增加项目曝光度以及赋予人们初始动力去创建 PR 或议题的几件事情。

但是这并不能让他们坚持做贡献者,也不能确保他们完成已开始的工作。

1_tyzBkDXaXjRW4UIEWBikzQ

如何管理议题与拉取请求

既然我们已经探索了共享信息和如何让你的开源项目变得更具吸引力,让我们来讨论一下贡献吧,它是每个开源项目的圣杯。

什么是开源贡献?

对一个开源项目的贡献是指由所有者以外的人所做的任何改变。在实践中,它有两种形式:

议题

这里是 GitHub 关于议题(issues) 的描述

你可以在仓库中使用议题收集用户反馈,报告软件漏洞,并且组织要完成的任务。议题不只是一个报告软件漏洞的地方。

简而言之,议题可以是需要采取某种行动的任何信息。

拉取请求(PR)

这里是 GitHub 关于拉取请求(Pull Request,PR)的描述

拉取请求可让你在 GitHub 上向他人告知你已经推送到仓库中分支的更改。在拉取请求打开后,你可以与协作者讨论并审查潜在更改,在更改合并到基本分支之前添加跟进提交。

简而言之,拉取请求就是对项目的实际修改。

如何使用议题和 PR

那么,你应该如何使用议题和 PR,又该如何处理贡献者创建的议题与 PR 呢?

以身作则

我能给你的最好建议是,结合某个具体的工作方法以身作则。这意味着,当你开发新功能时,你应该为这个功能创建一个 PR,在它满足你所有的要求之后就进行合并。

你应该在发现缺陷或者一些缺失功能时创建议题。

这个方法不仅能组织好你的工作,让你的项目变得井井有条,还能给贡献者们提供一个参考,他们可以从中学习并调整自己的议题与 PR。

此外,如果你的标准很高(即你相信每个 PR 都应该有适当的文档、测试覆盖等等),那么你应该像对待任何其他贡献者一样对待你自己。你不能要求别人做你自己都没有做的事情。

还有就是,有时候你对贡献者应该比对自己更宽容。在你的项目处于初期阶段,没有很多贡献者时,更应该这样。这就涉及到了下面这一点。

感谢一切付出

与他人协作就是要相互尊重。你应该尊重你的贡献者们,耐心地回答他们的问题(即便问题看起来很简单),礼貌地对待建设性批评

记住:对贡献者工作的感谢至关重要。如果某人只是创建了一个议题(即使这个议题没有经过深入研究,甚至没有重现),感谢他们。他们费力地把自己的椅子挪到离桌子近一点的地方,坐直身子,然后打了一点他们认为对你有用的东西,感谢他们。如果需要的话,用礼貌而又尊重的方式向他们询问更多的细节。

如果某人创建的 PR 没有满足你的高标准,感谢他们。感谢他们并礼貌地请求他们修改代码/编写测试/添加文档,等等。给他们一个你的 PR 的链接作为参考,或者给他们一个贡献指南的链接。

建设性地积极对话将会给予那些贡献者们额外的动力,让他们继续工作。

质量 vs 数量

最终,几乎总会有一个折衷(除非你自己拥有一个像 Angular 或 React 这样的大型开源项目)。你可以决定不放低标准,哪怕是一点点也不行,很有可能你最终会自己完成所有的工作。

或者,你可以决定放低对贡献者的标准(但是这可能会让你的标准显得毫无用处,因为它们没有被执行)。

我了解到,每个贡献者都需要使用不同的方法。这真的是由他们个人及其对贡献的兴趣决定的。

你应该考虑议题的紧急性、贡献者的经验、代码的复杂度、所需修复或功能的复杂度、贡献者的动机等因素。

通常,当议题非常重要时,我会礼貌地请求贡献者进行更改,然后等个几天,如果没有任何进展,我就会自己进行更改。至于那些没那么重要的(有了会更好)修复或者功能,我通常会把它们完全留给社区。

随着议题和 PR 数量的增长,跟踪、确定优先级并对它们进行分类就会成为一项艰巨的任务。这意味着标签会变得异常重要。

使用有用的标签

GitHub 的标签是让议题和 PR 保持优先级与组织性的好工具。虽然你可以通过标签进行搜索和过滤,但是我发现最有用的还是它可以帮助可视化项目的整体状态。

这样,你可以进入“议题”页,看到大部分议题都被打上了 bug 标签(这意味着你应该停下来集中精力修复它们,而不是往前推进了)。

或者,你可以看见大部分议题(issue)都被标记为 enhancement 或需要 featurespriority 是另一个有用的标签,可以帮你首先关注到重要的东西。

此外,你的贡献者可以(也会)从你使用的标签中受益。例如,回到吸引贡献者,一些人可以进入议题页(issues),然后直观地地识别出那些需要社区帮忙处理的议题(help-wantedpr-welcome,等等)。

除了职责单一的标签(比如 bugenchancement)外,我推荐你使用标签来限定议题/PR 的范围。例如:

  • priority:lowpriority:high
  • required:investigationrequired:testsrequired:docs
  • 或者在单仓库的情况下: packages:package1packages:package2 等等

这里是一个使用了标签的议题页,它来自我的项目:

Screenshot-2021-03-12-141634

标签让你可以快速地分辨出哪些问题是需要你(或你的贡献者)注意的,这些问题与哪个组件相关以及需要什么才能继续进行。

使用 PR 和议题模板

我强烈建议你花几分钟时间,为议题PR 定义模板。

利用议题和拉取请求模板,可以自定义和标准化你希望贡献者在你的仓库中打开议题和拉取请求时加入的信息。

这将为你节省大量的时间,因为你不需要对每个问题或 PR 进行附加信息或更改的请求。有时候你还是需要这样做(因为有些贡献者根本不关注模板),但是它发生的频率要比不创建模板少得多。

这里是默认议题的一个例子,你可以看到对应的模板是何时在你的仓库中定义的:

Screenshot-2021-03-12-141725

使用 GitHub 的应用程序与操作

有很多的 GitHub 应用程序和操作可以帮你管理 PR 与议题。相关应用程序和操作的数量仍在不断增长,但是我个人发现这些功能最有用:

及时响应

如果我在其它开源项目上打开了一个议题或 PR,并且等了很长时间才收到回复,那么我就会对它失去兴趣。这里是一个例子:

  • 最初的响应非常快,只花了两天
  • 讨论的成果丰富
  • PR 仍然处于打开状态,但却没有关于到底是什么少了/错了的更新

最终,我转向了另一个包。

如果你不及时响应,这也会发生在你的项目上:如果你要花两周的时间响应一个需要你处理的 PR,而不是等待你要求的贡献者更改,那么你就会失去用户(即潜在的贡献者)。

所以帮你自己一个忙——及时响应。不一定要立马解决某人的问题,但是,即便是让用户知道你会在下周研究他们的议题,也给了他们一些确定性和时限。

坏消息是,你应该信守诺言。如果你的诺言有时没有完成,不要担心——我们所有人都有自己的生活,如果你因一些紧急的事情推迟了你在开源上的工作,是可以理解的。

如果发生了这种情况,就给一个简短的更新——又不是什么大事儿,只需要写一两个字,让人们知道他们一直在等待的那个功能被推迟了。

如何确定议题的优先级

有几种方法可以帮你确定最重要议题的优先级。

首先,应该如何识别出最重要的议题呢?我个人认为,最重要的议题就是用户最想要的那些东西,不管它是新功能、缺陷修复,还是其它东西。

有时候,用户会在议题中表现出他们的兴趣,但是他们很有可能不会这么做。因此,我给大家介绍一种了解用户对哪些东西感兴趣的简单方法:

GitHub 上的每个项目都有一个 “Insights” 选项,其中有一部分叫 “Traffic":

Screenshot-2021-03-12-142214

你可以在这部分的底部找到热门内容表(Popular Content table),它可以让你深入了解哪些页面是使用者们访问得最多的:

Screenshot-2021-03-12-142309

这个表中展示的议题是访问频率最高的那些议题,因此对你的用户来说,它们最有可能是最重要的。

甄别出最重要的议题后,你需要在议题页面突出它们。这里是几种方式:

固定议题

每个仓库可以有最多三个固定议题(pinned issue)。固定议题出现在议题页面的顶部,所以几乎不可能忽略他们:

Screenshot-2021-03-12-142429

添加标签

我们已经讨论过使用标签了,并且对 help-wantedpriority:high 标签的应用来说,这是一个绝佳的例子。这些标签会让潜在的贡献者们知道这个议题很重要,并且他们的任何帮助都会被感谢。

持续集成

在将每个拉取请求合并到主干(master,或者 main)之前,先进行构建和测试,这将让你对即将合并到主干分支的代码充满信心(取决于测试的覆盖程度)。

尽管我不能不提到它是 PR 管理过程的一部分,但是它是一项任务的自动化,否则你不得不自己做,这样它就与 PR 管理没有直接关系了。

你仍然可以检出(check out)每个 PR,在本地构建,运行测试,然后在一切都通过之后进行合并(这样的话,持续集成就与 PR 管理没有直接关系)。不过别担心,我们将会在下一部分详细介绍持续集成。

小结

让你的项目保持整洁有序非常重要,因为,我们都知道,整洁是一种美德。它不仅让管理过程更加高效,还能改善项目给人的总体印象。

PR 和议题(以及代码库)是开源项目门面的不可或缺的一部分。不要低估它们的价值。

1_n8_iSirZKBjHRufT6silGw

如何实现流程自动化

管理贡献(即议题和 PR)的一个自然部分是自动化——可能是 OSS 项目管理中最重要的方面之一。

为什么要自动化?

如果说我在拥有一个开源系统的这些年里学到了什么,那就是你要做的例行事项越少,你就有越多的空闲时间用于实际的工作(比如修复缺陷或者开发新功能)。因此,我力求尽可能自动化

这里是我希望我们如何实现这个目标的方式:首先检查两个工作流程(非自动化和全自动化),看你有多少时间实际上是花在例行事项上的。然后,我们将探讨如何实现改进的工作流程,让我们有更多的时间来修复缺陷。

最糟糕的情况——没有自动化

Screenshot-2021-03-12-142749

如你所见,在没有任何自动化的情况下,所有的工作都由你来做。仅仅对于一个缺陷修复来说,你就需要做很多工作,更重要的是,每次修复缺陷或开发新功能时,你都要做这些工作!

现在我们来看看另一种情况。

最好的情况——一切都是自动化的

Screenshot-2021-03-12-142807

在这种情况下,你只需要做必须要做的事情——检查代码和(偶尔)批准拉取请求,其他的一切都是自动完成的。

科幻小说?不,它被称为持续集成(continuous integration)持续部署(continuous deployment)。在这里,我们并不会深入构建脚本和特定系统配置的细节。相反,我们将会查看让它发挥作用所需的工具,我将会让你自己决定具体细节。

什么是持续集成(CI)?

持续集成(CI)是一种自动将代码改动从多个贡献者集成到单个软件项目中的实践。 CI 过程由自动工具组成,这些工具会在集成之前断言新代码的正确性。

一个非常基础的 CI 运行将会包括 构建(build)单元测试(unit tests),但是并不局限于这两种。可能也会包含各种各样的静态代码分析工具、链接器等等。这里的标准由你定。

为什么你应该使用端到端测试

构建和单元测试可以为你提供有关代码变动的快速反馈,所需时间相对较短,并且在出现问题的时候迅速失败。但是端到端(end-to-end,E2E)测试在 CI 中有着特殊的地位。

端到端测试不仅应该覆盖代码的正确性,还应该覆盖到你的部署流程、包的完整性等等。

我自己也意识到了这一点,当我不小心发布了一个不包含代码的新版本包。构建通过了,单元测试和端到端测试也没有问题(这一次是通过链接测试项目的构建输出目录来安装的)。哪里失败了呢?在打包阶段。

这里有一个关键点:端到端测试应该像真实用户使用那样测试你的软件包。

为了达到这个目标,我推荐以下几步:

  1. 在你的 CI 运行期间,启动一个本地的包注册中心。每个语言/生态系统都有几个选择,例如对 Java 或 Scala 项目来说,你可以用 Nexus 仓库,对 JavaScript 来说,可以使用 Verdaccio(我在 @angular-builders中使用它)。
  2. 有一个使用你的软件包的独立项目(它可以位于同一个仓库中)。这个仓库中的测试应该测试你的打包功能。
  3. 配置这个项目使用本地包注册中心。
  4. 构建完你的包之后,将其发布到本地包注册中心(在你的 CI 系统中启动)
  5. 安装改包的最新版本(你刚才构建的那个)到你的测试项目中。
  6. 运行测试。

这不仅可以测试软件包的完整性和可靠性,还可以在进行持续部署时给你省去一些工作。

CI 系统是如何工作的

很多 CI 系统都有针对开源项目的免费计划,其中有 Travis CICircleCIAppVeyorGitHub Actions 等。

它们的功能都比较多,做的事情也基本相同:检出你的代码到虚拟机、运行你定义的脚本(通常运行构建和测试),然后向 GitHub 报告成功或失败。

所有这些系统都有一个用于和 GitHub 集成的 应用程序,它们当中的集成过程也非常类似:

  1. 在平台上注册。
  2. 在你的 GitHub 账户中安装对应的应用程序。
  3. 配置对所选仓库的访问
  4. 创建一个配置文件(比如 travis.yaml),定义构建矩阵、所需构建链和 CI 脚本。
  5. 将它推送到主干。

这会使你的 CI 在每个 PR 上运行,并向 GitHub 报告状态——但是这还不够。你真正想要的是在 PR 通过所有检查之前,阻止合并到主干分支。

这可以通过定义分支保护规则来实现。为了定义这些规则,你需要前往你仓库的 Setting 中的 Branch 部分,然后点击 Add rule 按钮:

Screenshot-2021-03-12-142547

然后选择多选框 Require status checks to pass before merging

Screenshot-2021-03-12-142635

如你所见,相应的 GitHub Apps 多选框也已经出现在了这里,所以剩下的唯一一件事情就是启用它们。

具体的构建脚本由你的生态系统、编写项目的语言、你所使用的框架等决定。因此,我不会在这里进行介绍——你需要自己检查 CI 系统的文档,了解具体细节。然而,你现在对什么是 CI 以及它是如何自动化你的 PR 有了很好的认识,让我们继续前进吧。

持续部署(CD)是如何工作的

持续部署(CD)是一个软件发布过程,它使用自动化测试来验证对代码库的更改是否正确和稳定,以便立即自动部署到生产环境中。

在我们的情况中,生产环境就是程序包在包注册中心中公开可用的时候。这是一个无法返回的阶段,因为一旦发布,就不能取消发布了,因为程序包是公开可用的(因此,它可能正被使用)。

持续部署有多种策略,具体策略取决于项目及其复杂程度。但是在我看来,发行(release)只应该基于主干分支,因为这会让工作流变得非常简单。具体做法如下:

  1. 每个 PR 要么代表一个缺陷修复,要么代表一个新功能。
  2. 代码在进入主干之前经过了测试(包括端到端测试)。
  3. 主干分支是受保护的分支,所以只要你不合并失败的 PR,它就会保持稳定。
  4. 每个合并到主干的 PR 都会触发主干 CI 运行,CI 最终会发布一个新版本。

这将确保所有的发布都是按顺序进行的,并且可以很容易地将某些 PR 与特定的版本联系起来。

为了自动化程序包的发行过程,你需要做几件事情:

  1. 基于提交信息自动升级版本。
  2. 基于提交信息自动更新 CHANGELOG。
  3. 自动发布程序包到公共程序包仓库。
  4. 自动在 GitHub 上发行。

给大家带来一个好消息:语义化版本(semantic-release)已经支持所有这些功能了。坏消息是:你需要花一些时间才能让它发挥作用(但是最终会有所回报)。

Semantic-release 是如何工作的

semantic-release 自动化整个程序包发行工作流程,包括:确定下个版本号、生成发行说明和发布程序包。

这消除了人类感情和版本号之间的直接联系,严格遵循 语义化版本 版本规范。

我们不会在这里介绍整个集成过程,因为它们有良好的文档,也没有理由在这里进行复述。不过,我还是会提几点:

  • 在你开始 Semantic Release 之前,确保你理解 语义化版本声明约定式提交 的格式。
  • 为了使 semantic-release 能够良好地工作,你应该强制执行某些提交消息格式。为此,你可以将 Commitlint 作为一个 husky 预提交钩子运行。当有人创建本地提交时,它将强制执行常规提交,但对于直接从 GitHub Web UI 进行的提交就无能为力了(这通常发生在有人想要快速修复他们的 PR 时)。因此,我建立你通过 commitlint GitHub Action 对其进行备份。

在将语义化发行设置为工作流的一部分之后,你差不多就快完成了,你不再需要在这些常规过程上花时间。尽管你还可以进行另一项优化。

如何保持项目的更新

如果你的项目没有外部依赖,跳过这一部分。然而,大多数项目都依赖于其它程序包,而其它程序包往往会发生变化。

使项目保持最新的依赖关系很重要,但这很耗时。幸运的是,我们有一个解决方案。实际上,有一些,例如 GreenkeeperRenovateDependabot

它们的想法几乎相同,因此我只引用 Dependabot 的 “How it works” 部分:

1. Dependabot checks for updates
Dependabot pulls down your dependency files and looks for any outdated or insecure requirements.

2. Dependabot opens pull requests
If any of your dependencies are out-of-date, Dependabot opens individual pull requests to update each one.

3. You review and merge
You check that your tests pass, scan the included changelog and release notes, then hit merge with confidence.

你可能已经注意到,他只在你有能发挥作用的 CI 时才有意义。

小结

如果你有一个全自动化的 CI/CD 闭环,并且在你的 OSS 仓库中有一个新打开的议题,你可以在几分钟内提供一个缺陷修复。

实际上,你可以在你的手机上进入 GitHub 移动版,修复一两行缺陷代码,然后提交。剩余的事情就自动完成了,你的客户马上就能得到一个新的版本。

我自己就能够快速、轻松地向客户多次提供修复版本。

拥有强大的自动化能力并不是为了腾出一些时间进行休闲娱乐,而是要把时间用在真正重要的事情上,并提高响应能力。

1_6k7J2Dj1iz0c901UExzjWg

版本管理

在这篇指南的最后,我想谈一谈版本管理,对于任何拥有大量用户的 OSS 项目来说,版本管理总是很重要。你将会了解到版本符号、中断性变更、向后移植,等等。

什么是软件版本控制?

咱们来看一下维基百科对软件版本控制(software versioning)的解释吧。

软件升级版本控制(Software upgrade versioning)是将唯一的版本名称或版本号分配给计算机软件的唯一状态的一个过程。

现代计算机软件通常采用两种不同的软件版本控制方案进行版本跟踪——可能在一天内增长很多次的内部版本号,比如修订控制号,还有一个就是发行版本,它变化得通常没这么快,比如语义化版本[1]项目代号

确实,有很多方法可以唯一标识你的软件产品的版本。

最广为人知的方式就是给它起一个名字。

地球上的绝大多数人,甚至包含那些与技术有着间接关系的人,都可能听说过安卓的冰淇淋三明治(Ice Cream Sandwich)和棉花糖(Marshmallow)或 Mac OS 的美洲豹(Leopard),以及它的冷冻表亲雪豹(Snow Leopard),还有 Big Sur。

程序员可能听过 Eclipse 及其天体版本 Luna、Mars 和 Photon。

所有这些都是软件产品的大版本号。

尽管名字非常适合市场营销,但是它们有时候也会让人感到困惑。

实际上,谷歌已经在它们的安卓版本名字中取消了对糖果的使用,因为它们:

多年以来都听到用户在反馈:这些名字总是不能让全球社区中的每个人都有直观的理解。

没错,但也许只是我们还没进化到从动物种类推断出版本号的程度,尽管雪豹(Snow Leapard)比美洲豹(Leopard)要酷很多。

天体和糖果都是比较容易理解的概念,但前提是你必须按照字母的出现顺序命名(像安卓和 Eclipse 这样)。但是有一点可以肯定——没有什么方法能够比数字更好地确定连续的情况。

因此,如果你将你的软件产品的第一个版本命名为“Product 1”,将第二个版本命名为“Product 2”,那么就可以很直观地说第二个版本是最新的,不是吗?

然而,不同于不暴露 API 的独立软件产品,那些被其他软件(比如 OSS 产品主体部分)使用的软件需要更好的版本控制,而不仅仅是一串数字。

例如,如果我们用一个简单的数字序列进行版本控制,用户如何能区分出缺陷修复和中断现有 API 的变更呢?

答案就是……语义化版本。

什么是语义化版本?

语义化版本(又称 SemVer)是一个广泛采用的版本管理方案,它使用格式为 MAJOR.MINOR.PATCH 的三位数字序列。

规则很简单——给定一个版本号 MAJOR.MINOR.PATCH,分别在不同的情况下递增不同的版本:

  • 当你进行了不兼容的 API 变更时,递增 MAJOR 版本
  • 当你以向后兼容的方式添加了一个功能时,递增 MINOR 版本
  • 当你进行了向后兼容的缺陷修复时,递增 PATCH 版本

预发行和构建元数据的其他标签可以作为 MAJOR.MINOR.PATCH 格式的扩展使用。

versioning

它提供了一种简洁明了的方式,来将软件产品中的变更传递给你的用户。

但是最重要的是,它被所有种类的包管理器和构建工具(比如 NPMMaven)广泛使用,这些工具允许用户依赖 某个范围内 的版本,而不是某个特定的版本。

例如,声明版本区间 ^2.2.1 而不是显式的版本号 2.2.1 将会让用户接受任何向后兼容的缺陷修复或将会在 2.2.1 版本之上发布的新功能。

也就是说,构建工具和包管理器依赖用户和包的所有者之间的约定——这个约定是由 SemVer 定义的。

那意味着你要全权负责——你就是那个给中断性变更和小型变更下定义的人。你可以不小心将一个中断性变更作为缺陷修复(补丁版本)发布,并且它将会破坏依赖某个范围的构建。

破坏构建是一件非常恐怖的事情,所以我推荐你使用带有预定义消息格式的 semantic-release 和提交格式的强制工具。

你可以在 Server.org 的官网找到更多有关语义化版本的信息。

既然我们已经学了如何识别中断性变更,我们就来聊聊如何引入它们吧。

如何管理中断性变更

中断性变更(breaking change)是那些对公开 API 的变更,这些变更以不兼容的方式移除、重命名或更改了你与用户之间的约定。

理想情况下,你将会在你的代码中保持向后兼容,并且永远不会引入中断性变更。但是,你会意识到现实的残酷的。

软件在不断演进,你的代码也是。用户的需求会变,你的 API 也会变。你作为一名开发者在成长,你的产品也在成长。

因此,特别是作为一个不拿工资的开源开发者,你就是不能容许你自己维护的项目中存在的所有遗留代码。有时,你需要移除它们。

问题是如何移除?

与往常一样,需要进行权衡。你会更清楚这个或其它的变更对用户的影响。

你不必不惜代价地保持向后兼容,也不必在每个旧版本中实现所有的新功能。但是,这毫无疑问是你应该考虑到的事情。

如果用户的迁移成本比较低,那么进行中断性变更是可以的,在较老的版本中不支持这个功能也很合理。

然而,如果迁移成本很高,绝大多数用户无法承担的话,你或许应该先考虑让这个变更向后兼容,然后发布一个废弃警告。

废弃警告通常和新的 API 一起发布,旧的 API 仍然受到支持。这样一来,用户就有时间进行迁移。在他们完成迁移之后,你就可以再下个大版本中安全地移除废弃警告和旧的 API 了。

无论如何,不管你何时引入中断性变更,都要确保有一份迁移指南,包含迁移的每一步。

此外,出于礼貌,你最好给用户留下为中断性变更做准备的时间,尤其是在没有宽限期的情况下(新旧 API 都支持)。

一个解释中断性变更、其背后的原因以及预期的时间范围的预先通知是非常有用的。这个通知可以是一条推特、一篇博客文章,甚至是任何带有废弃警告的新的小版本。

记住,虽然中断性变更基本上是一个负面体验,但是一个突然的中断性变更却是一个非常负面的体验。

自动迁移

我们可以将中断性变更分为两类——非确定性(non-determinstic)变更与确定性(determinstic)变更。

非确定性变更是指那些你无法预测迁移工作结果的变更,比如将某个 API 的特定部分完全移除。

在这种情况下,由用户自己决定是否要用第三方库替换它、自己实现它。

确定性变更是指那些给定代码 X 和用户输入 I,允许你将其转变为代码 Y 的变更(即通过输入可以明确地知道输出)。比如,改变函数名或者导入语句。

如果你引入了一个确定的中断性变更,你可以编写一个自动化程序,修改用户的代码库,并将其调整为新的 API。

有了这种自动化,你就不必担心向后兼容和详细的迁移指南了。你给用户提供一种不需任何努力就能升级他们代码的方式,这对软件更新至关重要。

然而,这里也存在固有的权衡。写代码需要花时间,就像编写迁移指南一样。当然,编写将复杂代码流迁移到新 API 的代码比编写替换函数名的代码花费更多的时间。

有时,你并不能负担起这个时间。

如果你决定这么做,有一些工具可以帮你实现目标。

其中最广为人知且语言无关的就是 Facebook 的 Codemode

codemod 是一个工具/库,可以帮助你重构大规模代码库,重构可以部分自动化,但仍然需要人工监督和偶尔干预。

还有一些更加复杂的工具,它们使用 抽象语法树(AST),可以用于更加复杂的任务,而不仅仅是查找并替换。

例如,Facebook 的另一个库(只适用于 JS/TS)被称为 JSCodeShift。或者 code-migrate——一个允许你比较容易地编写迁移指南的工具(还是只适用于 JS/TS),为用户提供一个漂亮的基于命令提示符的 CLI 界面。

1_aFlF8Vx0-thA0EutbBgiUA

一些大型 OSS 项目甚至有他们自己的解决方案。其中有一个例子就是 Angular schematics ——一个基于模板的代码生成器,支持复杂逻辑。

自动代码迁移可以作为一个单独的程序包(比如 my-cool-oss-migrate-v4-v5)发布,并作为迁移指南中的一个步骤被提到。

另外,迁移可以是包含中断性变更的大版本的一部分,可以在用户在代码库中安装该版本后执行。具体由你决定。

移植

另一个惯例就是将重要的变更移植到以前的版本中。例如,在某个主要发行版本(此版本包含中断性变更)之后发现了一个严重缺陷,但是这个缺陷也存在于之前的版本中。

这时,你不能指望用户们因为这一个缺陷就去进行繁琐的迁移。相反,检出老的修订版本,在它的上面进行修复,然后以该老版本的小版本发行,可能会很复杂。

解决方案:为每个大版本建立一个受保护的分支。

每当你计划发行一个大版本时,就从主分支创建出一个命名为 c.x.x 的分支,其中 c 就是当前的大版本号。把所有这样的分支设置为受保护的分支(就像主分支一样),这样你就不会在不经意间破坏到它们。然后,在你不得不从一个新的大版本中移植某个功能或缺陷修复的任何时候,你可以在这个分支上重新实现它,也可以(如果可能的话)从主分支挑选(cherry-pick)对应的提交。

此外,有一个策略值得一提:为下一个大版本创建一个单独的分支(而不是只为之前大版本创建分支)。

这通常适用于大规模项目(比如 Webpack 或 Babel),这些项目在每个新的大版本中多有很多的变更。

为即将到来的大版本建立一个单独的分支允许在其上开展工作,并将其发布进行测试,同时仍然将最相关的版本(在其上开展工作)保留在主分支。

新版本发布之后,它的分支就成为主分支,下个大版本的新分支也会被创建。

最后的想法

我希望你喜欢这篇指南,现在已经对拥有一个开源项目意味着什么有了一个更好的理解。

最后,我想和大家分享一件事,在拥有一个开源项目时,你应该使用牢记。

倾听用户的声音

这听起来可能有点违反直觉,但事实就是这样——你并不是唯一一个定义路线图的人,用户也定义了它。实际上,用户定义了路线图的绝大部分。

如果你拥有一个开源项目,你维护它的目的是为了帮助他人,而不是你自己。

准备多个反馈渠道。有些用户只有一个快速的问题,你可以在一秒钟内给出答案。

也有潜在的贡献者想要讨论路线图,但是他们又不想公开讨论,那么给他们一个联系你的途径。提供一个 Slack 或 Discord 的链接,分享你的 Twitter 账户,等等。渠道越多越好。

说到渠道,如果你有任何问题或想法,可以随时在 Twitter 上直接给我发消息。

原文:How to Be a Good Open Source Project Owner – The Ultimate Guide,作者:JeB Barabanov