维护一个大型开源项目是怎样的体验?

(我认为)大型开源项目包含以下任意一个或多个特征: GitHub star 数量 1000 + 每周都可以接收到多个 issues 或 PR 有一些公司或其…
关注者
5083
被浏览
117199

24 个回答

这次籍着Electron 1.0发布的机会,说说我自己维护两个大型开源项目的经历吧,分别是node-webkit(也就是现在的NW.js)和Electron

前者是几年前自己单打独斗,在公司0资源投入的情况下从两百多star一直做到好几千star,具体数字已经忘掉了,只记得当时在C++项目排行里做到了前十,直到自己另起炉灶。而后者,则是披着GitHub的光环,有着公司热心的投入,毫无障碍一路跑到了现在的三万多star。

(使用Star History生成的star数统计图)

一切从node-webkit开始

如果大家有接触过前端或者桌面端的开发,那么很可能有听说过NW.js的大名,node-webkit就是其改名之前的称呼。

但绝大部分人可能并不知道,node-webkit在最初发布的时候(2011年),并不是面向开发桌面应用程序,而是一个Node.js模块,可以创建一个WebKit窗口。神奇的一点是,你可以在这个窗口的页面里调用Node.js的模块。

Node.js代码:
var nwebkit = require('nwebkit')
nwebkit.init({'url' : 'index.html'}, function () {
  nwebkit.context.runScript('')
})

index.html代码:
<html><body>
<p id="output"></p>
<script>
require('fs').readdir('.', function (err, files) {
  var result = ''
  files.forEach(function (filename) { result += filename + '<br/>' } )
  document.getElementById('output').innerHTML = result
});
</script>
</body></html>

大家可以在NW.js的webkitgtk分支里找到node-webkit的原始实现,甚至尝试重新build一遍。不过这个模块一直未能稳定下来,玩具的味道更多一些。

再后来,原作者rogerwang对其进行了改进,从使用WebKit改成了调用Chromium Embedded Framework(简称CEF),一个可以把Chromium嵌入应用程序的库。这个代码一直没有正式公布,但大家可以在node-webkit的cef分支里找到。代码的另一部分是针对Chromium的几十行补丁,将Chromium的message loop替换为libuv,但是NW.js在开发过程中对代码库进行了很多次rebase操作,原始代码已经找不回来了。

找个实习生来开发

node-webkit的原作者rogerwang在Intel开源技术中心工作,虽然Intel在大家心目中可能更多是个卖CPU的,其实在开源方面也非常热心,甚至提供开源方面的实习工作。

在2012年的夏天,一则Intel的实习信息吸引了我的注意,上面说Intel需要一名熟悉Node.js的学生来进行node-webkit的开发。我投了简历,也很幸运地开始了node-webkit的开发。

Content Shell

我最初的工作是对node-webkit的cef分支进行改进,但是很快就发现很难继续进行开发了。CEF提供了自己的一套API来包装Chromium的内部API,而Node.js则是直接调用V8的API,如果想要把CEF和Node.js合并到同一个项目,困难重重。

于是我干脆将node-webkit推倒重写。新的代码基于Content Shell,一个Chromium代码库内的最小化浏览器实现。

重写后的结果非常不错,我得到了一个可以调用Node.js模块的浏览器实现。基于这套代码我发布了node-webkit v0.2.1

把node-webkit进化成桌面开发利器

至此node-webkit已经变成了一个独立的浏览器,而不再是Node.js模块。为什么不把它变成一个使用JavaScript和HTML开发桌面应用的工具呢?后面几个月我开始对这个想法进行尝试。

首先我模仿游戏框架LÖVE framework为node-webkit实现了一个打包系统,可以把应用直接附加到exe上:

(node-webkit的打包系统,来自我自己的PPT

这个打包系统一直沿用到NW.js,但是因为性能方面的种种原因,后来在开发Electron时我并没有使用这套系统。

接着这是各种细节上的完善:比如利用package.json文件来描述应用;给窗口加工具栏;拓展DOM来提供API;取消浏览器的安全系统;等等。当然还有无休无止的bug修复。

其中最有挑战性的一点是支持Node.js的native module,我对Chromium打上各种补丁来暴露V8和OpenSSL的API,给Node.js打补丁好解决OpenSSL和NSS之间的符号冲突,提供自定义的node.lib来支持Windows,最后还要提供适用于node-webkit的编译工具。

完成这些工作以后我发布了node-webkit v0.2.5

提供构建GUI的API

这时我的绝大部分工作都在围着浏览器转,而由于浏览器自身的局限性很多功能我都没法提供。比方说浏览器里就没法创建系统原生的菜单。(当然今天已经有了HTML5 Menu标签,而当时是2012年。)

于是我想到增加一个新的内置模块,用来提供对窗口、菜单等系统API的绑定,也就是:
require('nw.gui')

如果你有使用过NW.js的话,你应该很熟悉这一套API。

经过几个月的完善后,我发布了node-webkit v0.3.6,这也是由我维护的最后一个版本。

node-webkit的推广

虽然node-webkit属于Intel,但其更多属于rogerwang的个人项目,那时候这个项目在GitHub上也还挂在rogerwang的账户下。所以当时除了我自己一个人,没有其他任何来自Intel的资源投入。

而像node-webkit这样的框架类项目,只有拥有用户,才会有生存下去的意义,所以在开发node-webkit的半年时间里,我也一直有在积极推广。

首先是到处发帖子宣传node-webkit的好处,一大阵地是Node.js的邮件列表。每次有新版本我就会过去发布公告,回答问题,与人撕逼。其次则是编写范例应用,让新手快速入门,让其他人相信node-webkit的能力。最后则是去技术会议宣传,让大家知道node-webkit这个东西。

当然最重要的还是认真回答Issues里的问题,努力修复bug增加功能。

我的努力最后也得到了非常好的回报,在我结束node-webkit开发的时候,项目在GitHub上获得了数千个star,排到了C++项目排行的前十。

第一个用户

另外值得一提的是node-webkit的第一个用户,Light Table editor。这个编辑器的作者ibdknox很大胆地使用了node-webkit来进行开发,当时为node-webkit迎来了非常多的关注,也给大家吃了一枚定心丸,为项目推广助力巨大。

几年后Light Table又从node-webkit迁移到了Electron,当时好似好友重逢,感触良多。

工作的转移

在我为node-webkit工作半年之后,2012年底,这个项目开始吸引越来越多的注意力,也开始引起Intel一定程度的注意。rogerwang得到了机会放下其他的工作,开始全职维护node-webkit。

(GitHub的代码贡献表)


大家如果有在比较大的公司工作过,之前可能会奇怪,为什么一个实习生能如此随意地修改代码,发布新版本。其实正是因为这个项目当时在Intel内部无人关注,无人管理,我才得以随心所欲地尝试各种想法。

但之后正当node-webkit冉冉升起之时,我却彻底失去了node-webkit的自治权,开始收到命令去完成指定的开发工作,被禁止随意去增加功能,也不允许去随意修复bug,而发布新版本、和客户交流的工作也被转移给更高级别的工程师手上。正如一个大公司里的螺丝钉。

可能这种开发方式才是大公司的常态,我也不想抱怨任何人,但是抱歉,我只是一点也忍不了。

为GitHub而战

在我进行node-webkit开发的同时,GitHub正在秘密开发Atom编辑器。我了解到GitHub正在寻求一种使用HTMl和Node.js来开发桌面应用的方式,于是联系到Github
的工程师nathansobo,表达了为GitHub工作的意愿。

所幸node-webkit已经为我自己博得了足够的名气,在一面未见的情况下,我们敲定了协议,我需要将Atom迁移到node-webkit之上,并且提供支持工作。

于是2012年底,我结束了在Intel的实习,开始为GitHub作为contractor工作。

一个更好的桌面应用框架

在我加入Atom的开发时,项目还基于CEF。我们尝试了将Atom迁移到node-webkit上面,但是最终效果并不是很好,node-webkit当时并不稳定,而且API有太多的坑。

于是我开始重新考虑node-webkit的API设计,发现如果想要支撑大型应用,我不得不做出很多结构性的改变,而这些改变与其修改node-webkit,不如重写更加实际。在和GitHub讨论之后,我开始编写一个新的桌面应用框架,当时的名称叫做atom-shell。

有兴趣的同学可以去了解一下我们究竟做了哪些改变

左右互搏

后来,经过长时间的开发,atom-shell最终开放了源代码,而与此同时node-webkit 已经吸引了非常多的注意力和用户,我开始了和自己的竞争。

再之后node-webkit改名为NW.js,而atom-shell则改名为Electron。

(Electron 1.0)


维护开源项目的生活

说完了从node-webkit到Electron进化的历程,最后说说维护开源项目的体验吧。

和普通项目不同,开源项目几乎所有的用户都来自公司外部,取决于项目的受欢迎程度,每天都会受到相当大数量的邮件。就我自己而言,来自GitHub的通知邮件每天数量在50封左右,需要花1到3小时左右一一消化回复。很多issue要去仔细重现,一些话题也需要大量的讨论,很费神。甚至会有troll过来破坏所有人的心情。

其中最让人无奈的一个troll,最爱跑到issue下面留言,指摘Electron抄袭NW.js的代码,剽窃NW.js的理念,还到处劝人转用NW.js。简直了。

维护项目的另一项工作,是审核pull request,和维护node-webkit时不同,Electron的用户群要大很多,所以每天都会收到pull request,其中不乏高质量的代码。但多数时候代码都不能直接合并,要么设计不合理,要么代码不合规范,更多地则是引入新bug。所以每个pull request都需要细心查看,还少不了和贡献者大量的讨论。

于是,做完这些工作以后,留给写代码的时候反而少了很多,也是没有办法。但对于一个开源项目而言,这些琐碎的工作其实非常重要,只要让人感觉你的项目在被精心维护,才能不断吸引更多用户。一个维护不善的项目,哪怕初期因为种种原因吸引了很多用户,一旦更好的替代品出现,用户马上会飞速流失。

生活上,因为是全职做开源,影响反而非常小。GitHub的远程工作氛围非常浓厚,我的大部分工作时间也是在家里而不是办公室里,所以多数时候是想写代码了便开始工作,不在状态就把工作放在一边去做做喜欢的事,以此用有限的时间维持非常高的工作效率。

但开源会给自己带来另一个问题,责任感。一个没人用的项目,弃坑不过是一转念的事情,但是当越来越多人开始用你的项目,甚至有创业公司把未来压在你的项目上,责任感会越来越重,你唯一的选择就是将项目继续维护下去,无法弃坑,直到更好的技术出现。

不知道我未来还会维护Electron多久,不过就过去几年来看,还挺开心的,应该还会继续做下去吧。
我来说一个面向最终用户的软件项目 Koreader (koreader/koreader · GitHub)。Koreader是跨平台的电子书阅读软件,支持多种文档格式,可以运行在Kindle、Kobo、PocketBook等电子书,以及Android和Ubuntu Touch设备上。目前Koreader在GitHub上有1400+ star,PR+issues超过1600个,有将近40名开发者为项目贡献了代码,在Transifex上有超过15种语言的本地化翻译。

Koreader最有特色的功能是PDF页面文字回流。和其他支持文字回流的PDF阅读软件不同,Koreader把PDF页面当成图像,按照行间距和词间距把页面分割成以词为单位的图像块,再将这些图像块适应屏幕重新排版。这种基于图像的PDF页面重排不仅支持扫描的PDF页面,还可以让大部分数学公式的结构在页面重排后保持完整,更多重排样例参考如何解决六寸的 Kindle 看扫描版 PDF 的问题? - 黄鑫的回答


Koreader是一个完全由社区驱动的软件项目,采用去中心化的管理方式。用户和开发者把bug和特性需求发到GitHub的issues列表里,由开发者自愿认领。开发者把实现或者修复的补丁提交Pull Request,由另一名或多名开发者评议然后合并。目前Koreader项目有11名开发者有合并PR的权限。这种“同行评议”的PR(peer-reviewed pull request)机制使项目不需要一个中心维护者仍然可以快速迭代

我们通过持续集成工具Travis CI(travis-ci.org/koreader/) 对主分支上的每个Pull Request运行单元测试和代码覆盖测试,一定程度上保证提交的代码不会破坏已有代码的功能。我在服务器上部署了一个每日编译(nightly build)的脚本程序,这个脚本机器人在每天的北京时间6点和18点会从GitHub拉取主分支上最新的代码,从Transifex上拉取最新的本地化翻译,所有测试通过之后会为Koreader支持的每个平台编译最新版本的软件包,然后将软件包自动发布到Koreader的OTA(over-the-air)升级服务器。在所有支持OTA升级的Koreader平台上,用户就可以一键下载增量更新安装最新的版本。所以,经常会有用户上午提交了bug报告,第二天就可以通过OTA得到问题的修复。

除了GitHub上的开发者社区之外,Koreader正在形成一个论坛形式的用户社区。在电子书阅读论坛比如国内的 Hi-PDA E-INK板块 和国外的 MobileRead 上都聚集了一大批Koreader用户。最让我感动的是,一些Koreader热心用户会不厌其烦地为新用户解答使用过程中遇到的问题,会从普通用户的角度为新用户写各种教程和指引。前几天MobileRead上的Koreader粉丝又在向管理员建议为Koreader开设专门的板块,貌似新板块正在建设中。

善用这些自动化工具,让机器尽可能代替人力来维护开源项目然后有更多的时间去实现设想的功能,我想这就是作为开源软件开发者和维护者极好的体验了。
为什么?