Dmitri Shuralyov的全职开源之旅

文摘   2024-10-16 07:51   上海  

本篇内容是根据2017年7月份Full-time Open Source [1]音频录制内容的整理与翻译

Dmitri Shuralyov 加入节目,谈论作为开源的全职贡献者、开发开发人员工具以及其他有趣的 Go 项目和新闻。

过程中为符合中文惯用表达有适当删改, 版权归原作者所有.



Erik St. Martin: 欢迎大家回来!我知道这两周有点间隔,因为风暴等等事情……首先是我们的工作室所在的休斯顿,然后是我和布莱恩所在的坦帕,再加上工作室的搬迁……不过我们现在已经回到正轨了,今天准备了一个有趣的节目。

今天是第58期节目,参与的有我自己,Erik St. Martin[2]Brian Ketelsen[3]……

Brian Ketelsen: 大家好!

Erik St. Martin: 还有 Carlisia Pinto[4]……

Carlisia Thompson: 加州这边没有风暴……

Erik St. Martin: 对吧?!也许我该重新考虑搬到西海岸的计划。

Carlisia Thompson: 是啊!

Erik St. Martin: 我们今天的特别嘉宾是 Dmitri Shurya[5] ---我知道我会把这名字念错……Dmitri Shuryal---为啥我……看,当我想快点说出来时反而念不出来了。

Dmitri Shuralyov: 对,你之前念对过……

Erik St. Martin: Dmitri Shuralyov。哈哈,试着带着点激情快速说出来时反而就不对了……所以是Dmitri Shuralyov。

Dmitri Shuralyov: 对,大家好!

Erik St. Martin: 大家更熟悉的可能是他的推特用户名,@shurcooL。

Dmitri Shuralyov: 对,我在GitHub、Twitter等地方都叫shurcooL。很高兴终于能来到这里。

Erik St. Martin: 对于那些可能不太熟悉你和你作品的人,你能简要介绍一下你是谁,你做什么,以及你在Go社区的历史吗?

Dmitri Shuralyov: 当然,没问题。就像你说的,大多数人可能更熟悉我的用户名shurcooL,这是我在各个平台上使用的名字。我是一名软件工程师,大概从2013年开始使用Go语言,到现在已经四年了。不是什么秘密,我是Go的超级粉丝,非常喜欢使用它。

这些天我做的事稍微有点不寻常---我全职在做开源的Go代码……也许我可以简单讲一下我的背景,这样能解释我怎么走到今天这一步。

Carlisia Thompson: 是的!

Brian Ketelsen: 请继续。

Dmitri Shuralyov: 好的,我很早就开始编程了,大概在13或14岁左右。基本上,当我发现你不仅可以玩电子游戏,还可以通过写代码来制作游戏时,我就被深深吸引了。我开始对编程非常感兴趣,学习了很多东西,很快从Visual Basic转向其他语言,最终选了C++。对于做游戏开发的人来说,C++是一个很好的工具,成为我很长时间的首选语言。

我一直想做视频游戏,几乎是直到我开始攻读硕士学位。我当时在做一个大型的C++项目,这是我的毕业设计,花了几个月的时间在上面。当时压力很大,截止日期也很紧,我必须很快完成很多事情,那段时间让我感到非常沮丧,因为有太多的事情需要手动完成,而且非常繁琐。

我当时使用的是Visual Studio,C++的重构工具并不理想,我不得不手动做很多事情---比如说,如果我想重命名一个变量或函数,我必须同时在CPP文件和头文件中进行更改。

如果我要移动某些东西,也需要做很多手动操作。这种挫败感让我特别想开发一些工具,能让我的工作变得更轻松,甚至让所有程序员的工作变得更轻松……所以我逐渐对开发工具产生了兴趣。

在那之后不久,我感到非常有动力去开发这些工具,并且脑子里有很多想法想要尝试,所以我开始了一个实验性项目,叫做Conception[6]。我真的想测试一些疯狂的想法,比如---我在想,“为什么源代码是文本文件?这太落后了,它应该是更复杂的东西。”我尝试将这些想法付诸实践,大约一年时间我全心投入这个项目中。那段时间非常有趣,我们尝试了很多实验性的想法,最终我把这个项目提交到了一个会议---2013年的一个竞赛,最后我赢得了一等奖。这真的很酷。

之后,这个项目获得了很多曝光和关注,也帮助我获得了在旧金山的第一份工作,那是我一直梦想去的地方。我来自多伦多,而旧金山---一直是我想去工作的地方,因为我知道那里是技术的圣地。那是所有初创公司和大公司聚集的地方,很多事情都在那里发生,所以去旧金山工作一直是我的梦想。

我最终找到了工作,第一份是在Triggit[7](译者注: 一家互联网广告公司,现已被收购) 工作。那是一家做实时竞价和广告的初创公司;他们刚开始使用Go语言,所以雇佣我的原因之一是因为我擅长C++和Go。我觉得这是个很好的机会,可以同时练习两种语言,并帮助他们将很多项目从多种语言转换为Go。他们在Go上取得了很大的成功,因此开始将Go应用到越来越多的项目中。

在那之后,我去了另一家公司,Sourcegraph[8]。你可能听说过这家公司。我们当时在为开发者构建工具,它在Go社区中很有名,因为我们为GopherCon[9]做了现场直播等等。这是一个非常不同的环境,开源的比重更大。我实际上是在开发者工具上工作,这非常有趣。

在旧金山工作了三年后,我一直在为未来的一个梦想储蓄,那就是全职做开源。这是我一直以来想要尝试的,因为业余时间做开源和全职做开源是完全不同的体验。平时你只能在周末或工作日的几个小时里贡献开源,而全职的话就有更多时间。

由于我非常喜欢Go语言,我想做更多相关的工作。于是,这件事终于实现了,大概是从去年的年底、今年年初开始,我辞去了工作,回到了多伦多,开始全职投入开源Go代码的工作。

Carlisia Thompson: 这太棒了!

Brian Ketelsen: 是啊,真的很了不起。

Carlisia Thompson: 我本来想问你是自己资助,还是有公司资助你……但听起来你是自己资助的,对吗?

Dmitri Shuralyov:  是的,我是自我资助的。基本上是靠我之前储蓄的资金来实现这个目标,这也是我今年的主要资金来源。当然,随着我赚到一些钱并增加储蓄,我可能可以继续做这个项目更长时间,但目前我对明年还没有明确的计划;我们看看会发生什么吧,但今年肯定是靠自己资助的。

Carlisia Thompson: 你有丰富的经验,所以我猜测你现在全职做开源项目并不是为了获得更多的技能。因此我很好奇,你的最终目标是什么?是想给自己一个心理上的休息,做自己喜欢的项目,也许完成一些,或者将它们提升到更高的水平?还是你没有明确的终点,或者是在为下一步做准备?你打算做什么?

Dmitri Shuralyov: 完全正确,我确实有一个目标,不过更像是一个逐步推进的中期计划,而不是一个完全明确的愿景。但基本上可以说,我在开发一些我非常希望存在但目前还不存在的东西。我在满足自己对某些工具的需求,或者在Go生态系统中,我认为有些很棒的东西还没有出现,或者还没做完。所以我就是在做那些我想看到的事情。这是我主要的动力。当然,我也希望保持我的技能,始终学习新的东西并不断进步,但这主要是关于实现我想看到的东西。

Carlisia Thompson: 太棒了。

Brian Ketelsen: 这真的很酷。我觉得我们应该回过头来谈一下Conception,因为那东西真的超前……非常超前!既然这是一个播客,我们看不到实物,告诉我们Conception是什么,以及你当时想达成什么目标。

Dmitri Shuralyov: 当然可以。这个项目最初是用C++开发的,当时我对集成开发环境(IDE)非常感兴趣,希望它们能变得更具视觉化,同时也希望它们能在重构时更能帮上忙。

当时我主要使用C++,我认为有很多机会可以让C++的开发更简单,但随着我开始开发这个项目,我逐渐发现了Go语言。在第一个项目年中的某个时候,我发现了Go语言,我心想“嘿,这个语言挺酷的,试试吧。”当时我要解决的问题是需要启动一些二进制文件,基本上是通过命令行运行一些命令,而在C++中,这个过程非常困难。大概需要写80行非移植性、非常复杂且容易出错的代码,虽然能工作,但你会感觉这代码不太对;如果发生点什么,它可能就会失败,而且代码太长,难以理解。

于是,当我试用了Go语言,我发现了 os/exec包,这让我非常惊讶。很快,Conception就变成了---它本应该是一个用于Go代码开发的IDE,所以逐渐远离了C++。

不过更好的描述方式---最好是观看。我制作了一个五分钟的视频,基本上介绍了我在一年半时间里完成的所有事情(在五分钟内)。你可以把它想象成一个二维画布;一个非常大,甚至是无限大的画布,你可以在上面看到视图。在这个画布上,你有我当时称之为“部件”的东西。这些部件可以是任何东西,但大多数情况下是一些像Go文件之类的东西;另一个部件可以是一个Go运行部件,它可以接受一个文件部件并运行它,然后展示输出结果。但你也可以在此基础上连接其他部件。你可以有一个差异部件,将其连接到程序的输出上,然后将其与预期的输出结果进行比较。

这个想法是,一切都是实时的,当你做出更改---更改代码或更改部件之间的连接时,所有内容都会自动更新;不需要按下按钮来触发这些变化。

因此,你可以持续创建东西并继续工作,每当你对正在处理的问题有疑问,并希望获得一些有关问题的可见性时,希望有一个部件可以帮助你找到答案,如果没有,你可以创建一个。至少当时我是在创建这些部件。

这就是项目的进展情况。作为一个实验性项目,我最大的收获之一是,我能够理解为什么很多事情是以现有的方式完成的,为什么看似糟糕或过时的做法,比如使用文本文件,实际上有其合理性。你可能会想“这可能是因为70年代的计算机不够强大。”但事实并非如此。现有系统在很多方面都非常出色,人们往往不太理解或不太欣赏它们,但当你尝试创建一个超越现有系统的东西时,这是发现现有系统为何如此有效的好方法。

所以,我学到了很多关于我们如何使用编译器、代码编辑器等工具的优点。掌握了这些知识,接下来我可以做的事情显然会更好。

Brian Ketelsen: 当我第一次看到那些演示[10]时,真是让我大开眼界。所以,非常恭喜你能够跳脱框架思考,并探索代码编辑的不同概念和想法。第一次看到时,真的让人惊叹。

Dmitri Shuralyov: 非常感谢你,我很感激。这正是我希望的,尝试创造一些非常酷的东西,当你看到时会觉得非常合理。这并不容易,但那些确实有效的东西,看到它是很棒的。如果我能激励到其他人,那就更棒了。

Brian Ketelsen: 我每次想到你的名字时,都会想到两件事。第一是Conception,它非常棒;第二是“那不是那个把包放进GitHub gists里的人吗?”

Dmitri Shuralyov: [笑] 我知道你会提到这个。看来我永远无法摆脱这个标签了。

Brian Ketelsen: 是的,确实无法逃避。

Dmitri Shuralyov: 是的,我想谈一下这个……提前声明一下,我相信我已经在2015年删除了最后一个gist。

Brian Ketelsen: [笑] 这太棒了!

Dmitri Shuralyov: 从那时起,唯一剩下的gists就是其他人的旧代码;也许他们复制了、fork了、或者引用了它们。所以我的代码已经不再存在于gists里了,但确实,我这么做过……这也是我当时运行的一个实验。当时我确实意识到它的缺点,或者经过一段时间后意识到了更多的缺点……但我当时想尝试一下,因为把一些代码放到gists里确实有一些优势。比如说,你想在两个项目之间共享一个函数,并且希望在一分钟半内完成;你没有时间去完整创建一个新的Go包,并让它变得非常完美……你只是希望快速完成,所以gist在这种情况下非常合适。

让我觉得这种做法有效的原因是,我在Conception里有一个部件,我只需按下一个按钮,任何我粘贴到框中的内容都会自动变成一个gist,它会放到我的GOPATH中……所有事情都可以通过一个按钮完成。所以在当时,这确实是一个高效的方式来创建很多共享代码。

随着时间的推移,我发现这种方式在原型开发阶段很棒,但在维护方面不太好;gists使用数字作为包名也很糟糕……这不是一种好的做法。随着时间推移,我重构了所有需要的代码,让它们拥有真正符合惯用法的、漂亮的包名,变成了人们可以说“是的,我可以在我的项目中实际引入的代码。”

所以,是的,它们曾经存在过,但现在已经不在了。如果你看到它们,请删除它们……

Brian Ketelsen: 我觉得你应该为此感到自豪。为什么不呢……?

Dmitri Shuralyov: 是的,我的确为此感到自豪。

Carlisia Thompson: 我想现在我们可以讨论一下,是读作“djist”还是“gist”?开玩笑的……[笑声]

Erik St. Martin: 然后我们得停止讨论gif是读作“gif”还是“djif”……

Dmitri Shuralyov: 是的,是的,这是同样的问题。 [笑声]

Carlisia Thompson: 但你知道,我说gif读作“gif”,但是gist我读作“djist”,所以这不可能是同样的……

Erik St. Martin: 我也是。

Carlisia Thompson: 对吧?

Dmitri Shuralyov: 是的,这确实说不通。

Carlisia Thompson: 看?我们就别讨论这个了... [笑声] 但我确实想谈谈你正在做的事情---对我来说,你为自己喜欢的开源项目腾出时间工作,确保这些项目能够实现,这真是件大事。你涉及的项目太多了,但我希望你能跟我们聊聊你的前三个项目,从最重要的开始,谈谈你真的想实现的项目,以及为什么。你现在这些项目进展如何?有人帮忙吗?离完成还有多远?完成意味着什么?告诉我们吧。

Dmitri Shuralyov: 当然,当然。我不认为我的项目之间有很明显的区分;很多项目在某种程度上是相互关联的,而且很多项目是那种更高层次和更低层次的“伞式”项目。但我想说的是,我们现在处在一个非常有趣的时间点;Go语言相对来说问世不久... 它已经大约十年了,但相对来说还是很新。就在几年前的后端,大家还可以选择一整套不同的语言,但在前端,JavaScript一直是唯一被浏览器官方支持的语言,而这最近开始有所改变。在我看来,最明显的变化之一就是---首先出现了asm.js[11],然后现在是WebAssembly[12]

在我看来,这是一个非常棒且重要的时间点,因为这意味着很快人们就可以开始看到,现在可以选择在浏览器中使用哪种语言,选择哪种技术来开发前端应用。我真的希望Go在这里能发挥更重要的作用,因为在我看来,Go是一门非常好的语言。我这里说的是普遍意义上的好;我不认为它只是适合做Web服务器,虽然它确实很适合,但它适合做很多事情。

我发现它甚至适合做前端的东西,过去两年我一直在验证这个理论,并且我仍然对它非常满意,所以我会继续朝这个方向努力。

因此,我认为WebAssembly和Go都非常重要,这也是我目前的首要任务之一---在未来几年内,我希望尽可能多地将其变为现实。

Erik St. Martin: 那么,目前有没有什么工具可以将Go编译或转译成WebAssembly?Gopher.js[13]会这样做吗?

Dmitri Shuralyov:  目前的情况是,如果你想用Go写前端代码,你有一个非常好的选择,那就是Gopher.js。但Gopher.js直接从Go代码转换为JavaScript,它与WebAssembly无关。WebAssembly是一种非常高效的低级字节码、虚拟机、栈机器语言,所以你希望用一个强大的编译器来生成它。像Clang、GCC这样的编译器有很大的机会生成WebAssembly代码。你不想完全从头开始手写它,或者类似的东西。Gopher.js之所以能做它所做的事情,完全是围绕着Go和JavaScript展开的。

如果你想给Gopher.js添加WebAssembly支持,基本上就是从头开始,因为它里面没有任何东西能帮助你完成这项工作。所以我认为这件事的实现方式是,也许官方的Go编译器将来某个时候能够支持它,或者可能会有人做一个单独的项目... 但Gopher.js在这方面帮不上忙,因为它主要是针对JavaScript的,没有必要让它去做其他事情。

Erik St. Martin: 所以基本上你做的所有事情都是这些前沿的想法和概念... 因为你也一直在做GraphQL[14]相关的东西,对吧?

Dmitri Shuralyov: 是的,GraphQL是我最近有些涉猎的东西,我大部分工作已经完成了。大概四个月前,GitHub宣布他们的下一版API (第4版) 将使用GraphQL。对于没听过这个技术的人来说,它是Facebook开发的一种技术,他们为自己的API开发的,因为REST已经无法满足他们的需求,他们想推动技术进步... 所以这是构建网站API的一种方式,允许你进行非常高效的查询,只获取你需要的数据,其他的都不拿。

当GitHub宣布他们的下一个API将使用GraphQL时,我看到这确实是一项正在获得关注的技术,支持它会变得更加有用。我注意到在Go语言中,有几个包实现了GraphQL的服务端功能,所以你可以用Go编写GraphQL服务器,但在客户端方面却完全没有东西。这时我看到有机会开发一个客户端。

我做了一些研究,尝试了几种方法,觉得如果我花几周时间,我可以做出一个可用的客户端。我知道这有很多工作要做,但我觉得在那个时间范围内可以完成,我认为值得这么做,这样就会有一个Go语言的GraphQL客户端供大家使用。目前进展就是这样,我已经创建了它[15]

它还有一些待办事项,但大部分已经完成了,而且这些待办事项可以随着时间推移慢慢完成。基本功能已经可以使用,任何人都可以使用它。

Carlisia Thompson: 这真是太酷了。GraphQL真的很棒。

Erik St. Martin: 是的,这是那种看起来很有趣的技术,我总是在寻找适合它的场景...

Dmitri Shuralyov:  对。我比较幸运的是,大约两周前,我终于有机会在一个项目中真正使用它。在此之前,我只是做了一些测试,写了些程序来做各种测试,从来没有真正用它来满足具体的需求。但后来,当我有机会真正使用它时,感觉真的很好。这是一次愉快的体验。

GraphQL的一个特点是它非常类型安全,而我非常喜欢Go的类型安全功能。看到它在GraphQL中也能实现,真是太棒了。所以你会有非常好的、强类型的安全性,自动补全功能也能帮你很多忙,你写的代码可以明确地看到处理了所有情况,没有遗漏的地方。是的,使用它的体验很棒,它帮了我不少忙。

Carlisia Thompson: 我知道至少有一期Changelog节目谈到了GraphQL,我听了那期节目,每当我听到别人谈论它,或者我读到关于它的文章,我就想立刻去使用它... 但之后我又回到了老朋友REST。 [笑]

Dmitri Shuralyov: 我真的认为GraphQL是一项很棒的技术,它有很多优点,但它也有一些缺点,这些需要大家花时间去找到解决方案。没有人会在今年立刻跳过去使用它;我觉得需要很多年,人们会逐渐转向它。如果你在开始一个新项目时考虑使用它,那是最合适的。将一个已经基于REST API构建的现有项目重写或加入GraphQL并不会对你有太大帮助... 但也可能会。所以你不需要立刻跳进去;你等得越久,当你使用它时它就会越成熟,但它确实在某些方面比REST有很好的优势。

Carlisia Thompson: 说到前端的东西,你对这个领域如此了解... 有时候,一些应用程序需要一个与数据库连接的后端,同时还需要一个报告布局来展示数据。有没有Go包可以做到这一点?

Dmitri Shuralyov: 当你说“报告”时,意思是渲染一些前端组件来可视化后端提供的数据吗?

Carlisia Thompson: 是的,类似于一个Excel前端的东西。

Dmitri Shuralyov: 哦,像Excel前端,我明白了。

Carlisia Thompson: 对,不是完全像Excel,但你明白的... 你选择字段,然后得到一个数据表格的输出。

Dmitri Shuralyov: 嗯,我一时想不出有哪个现成的项目正好符合这个需求;如果你说的是数据库编辑或表格编辑组件,或者...

Carlisia Thompson: 不,不是编辑,只是输出。

Dmitri Shuralyov: 哦,只是可视化...

Carlisia Thompson: 对,没错。

Dmitri Shuralyov: 所以你有一些数据库输出,想以这种方式可视化,并希望使用它吗?

Carlisia Thompson: 是的,我可能还想按字段进行筛选。

Dmitri Shuralyov:我明白。我不知道是否有现成的解决方案。我想你也许需要去寻找,或者可能需要自己创建,但目前确实存在的一个项目是 Vecty[16],而且我认为这个项目在之前几期节目中已经被提到过。它有点像 React,但完全是用 Go 写的,并且特别专注于 Go... 尽管之前有一些项目尝试将 React 包装起来,使其可以用于前端,但由于 React 是专门为 JavaScript 设计的,因此在使用时需要进行大量的映射,这会带来很多效率问题。

所以 Vecty 尝试从 Go 的角度来解决问题,你完全可以用 Vecty 创建某种前端应用程序。你仍然需要从个别的 HTML 标签开始构建,以可视化你想要的所有内容... 但它会帮你处理类似 React 的事情,比如在数据改变时重新渲染页面的部分内容。不过是的,你可能需要自己动手开发或处理。

使用 Go 做前端的一个缺点是,除非已经有人创建了你需要的东西,否则你可能必须自己动手开发。有时候,你需要走一条前人未曾走过的路。但这也是一种乐趣,如果你喜欢这种挑战的话,这其实是很棒的。

Carlisia Thompson:是的,因为其他那些更加成熟的语言已经有很多现成的包可以做这些事情了。

Dmitri Shuralyov:是的,实际上我最近看到了一个这样的例子。有一个 npm 包,你可以下载它,基本上它是一个组件,用来可视化差异(diffs)或补丁文件(patch files)。我当时想,“哇,你只要 npm install 它,它已经存在了,而且它有很多配置属性,可以调整显示方式。”这就是他们目前的状态---他们有现成的可视化差异的组件。我觉得看到这个很有趣,而我自己是从头开始构建类似的东西,因为当时还没有现成的解决方案。

Brian Ketelsen:我记得我们以前谈过几次 Vecty,它绝对是我最了解的 Gopher.js 前端框架,也是最能让我理解的框架。我非常喜欢 Vecty。

Carlisia Thompson:Brian,你知道有哪个项目在使用它吗?

Brian Ketelsen:这是个好问题,现在我一时想不起来,但...

Carlisia Thompson:嗯,这在 GitHub 上很容易找到。

Brian Ketelsen:是的,我在 GitHub 上标记了一些...

Erik St. Martin:或者 Sourcegraph...

Carlisia Thompson:没错,Sourcegraph 是最好的。

Dmitri Shuralyov:其实我在我的一个项目中使用了它。这个项目叫做 Go Package Store,它用于显示你 GOPATH 中 Go 包的更新情况。最初,它是一个非常简单的应用程序,基本上从后端流传输 HTML,浏览器在接收到 HTML 的同时显示它。但这种方式有一些局限,导致有些事情我无法实现... 所以我最终重写了它,让所有内容都在前端渲染,我使用了 Vecty,效果非常好。它让我能够实现之前无法完成的功能。

有一个功能请求是添加一个“全部更新”按钮... 我猜大家不想逐个点击更新每个 Go 包,他们只想按一下“全部更新”按钮,然后让它处理剩下的工作。现在这个按钮已经存在了,而 Vecty 让它的实现变得非常简单。

这个包仍在开发中,所以还会有一些改动,他们也总是在寻找改进的方式。它目前大概处于中期阶段,但绝对是可用的;你今天就可以开始使用它,只要你愿意在 API 变化时更新代码。

Brian Ketelsen:我最喜欢的 Vecty 应用示例之一是 Go Play Space[17]。哦,看来刚有人在我们的 Slack 里也发了这个链接。它的代码在 github.com/iafan/goplayspace[18],这是一个带有很多额外酷炫功能的 Go playground,真的很不错。

Dmitri Shuralyov:是的,这是个很棒的例子。

Erik St. Martin:我稍微晚一点开始了计时器,所以我想我们还有时间再讨论几件事情,然后就可以进入项目和新闻环节了。有一件事我想提到---在 GopherCon 上有讨论过这个话题,我今天早上也看到了... 对于那些不知道的人来说,我们录制这个节目的同时,Gotham Go 正在进行中,而我有点“错失恐惧症”(FOMO)。

Brian Ketelsen:非常严重的 FOMO,没错。

Erik St. Martin:我看到了一些帖子,特别是关于 Steve Francia[19]Go 现状演讲[20],大家都在讨论 Go 2 以及类似的话题... 你对此有什么看法?你希望看到什么变化,不希望看到什么变化?

Carlisia Thompson:这是个好问题,Dimitri...

Dmitri Shuralyov:是的,我对使用 Go 的方式有很明确的偏好,至少其中之一是... 我欣赏它的简单性;它提供了非常经过深思熟虑的基础构造块,然后让你自行处理剩下的事情。这种方式非常符合我处理问题的方式。

我最不希望看到的情况是 Go 变成下一个 JavaScript,完全改变它,加入泛型和各种新特性... 我不希望看到太多新特性,我只希望看到稳定性,并且 Go 能继续保持简单。

当然,它可以解决一些问题。基本上,Ross Cox[21]演讲[22] 提到他们计划如何实现 2.0 的内容对我来说非常棒。他表示这不会是一个大的跳跃,而是通过一系列向后兼容的改进来逐步实现。因此,未来几年发布的版本都将是渐进式的变化,最终让 2.0 成为可能,但仍然是 1.x 版本... 比如 1.11、1.12 等等。

Erik St. Martin:是的,我也非常喜欢这种方法... 最后可能会是“1.20 就是 2.0”。

Dmitri Shuralyov: 是的,这并不是一个大而戏剧性的变化,而是一个平滑的过渡。这听起来真的很棒。我记得 Brad Fitzpatrick[23] 做过一次演讲,他提到随着 Go 的成熟,它会逐渐变得越来越“无聊”,但这是件好事。他对此的论证非常有力,因为真正的兴奋点在更高的层次---你构建的项目、你编写的库、你如何使用 Go;这才是令人兴奋的部分。而 Go 本身---它不需要做更多的事情。

当人们在想“为什么不添加这些功能?为什么不做这个或那个?”时,你必须考虑所有的角度,其中一个经常被忽视的角度是这个功能将如何与工具链相互作用;它将如何影响你使用 Go 的方式,或者它可能如何生成代码,或者它会让你能够回答代码库中的哪些问题……

Go 的目标之一是能够创建在复杂性上扩展良好的代码或项目,尤其是当更多开发人员在同一个代码库上工作时,这种扩展性尤为重要。而在这些情况下,你并不一定需要所有最新的炫酷功能。恰恰相反,你只希望它能够很好地完成其任务,并继续以这种方式扩展。我认为这真的很有效。

Erik St. Martin: 这其实非常有趣,因为我刚刚参加完 Velocity 大会[24]Brendan Burns[25] 做了一个演讲---虽然这个演讲主要是关于 Kubernetes 的,但它和 Go 的情况很相似。他谈到了事物的演变过程,比如我们遇到了一个问题,我们发明了一些东西来解决问题,然后需要一段时间来稳定和成熟,模式才会从中演化出来,接着我们在它之上构建了一个抽象层。就像最初有机器码,然后是汇编语言,接着我们在其上构建了 C 和 C++。

他的演讲的要点是,即使使用 Kubernetes,我们构建分布式系统的方式也像是在用汇编语言编写代码。但我认为这个观点与 Go 现在的情况类似---它不需要变得花哨、崭新,或者塞满各种功能,因为那样它就永远不会成熟到我们可以在其上建立一致的模式,进而构建抽象层的程度。而创新总是来自于抽象层之上。我们现在可以用 Go 语言构建出远比汇编时代更强大的系统。

所以,是的,正如你所说,我们希望它保持相对稳定,然后“让我们来找出这些模式”。

Dmitri Shuralyov: 是的,绝对如此。我最喜欢的一句话之一,因为我非常看重简洁性。这句话是由 Frederic Chopin (肖邦) 说的,他是 1800 年代早期的波兰钢琴家。他说过一句话:“_简洁是最终的成就。在演奏了大量的音符之后,正是简洁才成为艺术的顶点。_”

我对此非常有共鸣。因为当你刚开始做一个项目时,你可能还不知道它最终会是什么样子或你将如何实现它,于是你做了很多尝试,试图去了解它。当项目完成后,你才开始真正理解它,只有在获得这些洞察之后,你才有可能简化它,提出最终的解决方案。而这种简化是你起初无法做到的。因为你了解了事物的运作方式,所以简化成为可能。类似地,Go 语言的许多编程语言功能看似是几十年前的,但这是因为它非常仔细地应用了之前语言和发展中的所有洞见。

这真的很有趣,但你无法一开始就达到简洁。

Brian Ketelsen: 最近我阅读了很多使用 泛型 的代码,我知道很多人都说过,Go 被设计成一种易于阅读和理解的语言……在处理这些语言之后---我不会指出具体语言,因为这不是语言之争,但我确实非常感激 Go 没有 _泛型_,因为它确实让理解代码变得更困难。

Dmitri Shuralyov: 是的,是的。

Carlisia Thompson: 我同意这一点。

Dmitri Shuralyov: 我有另一个例子---这是一个功能请求,提交到了问题跟踪器中,我想分享一下添加一个看似让生活更轻松的功能,实际上可能不会带来帮助的情况。

这个功能请求是关于为测试文件添加构建标签的功能,这样你就可以在测试时排除或包含某些东西。目前唯一的方法是用 _test,但这个请求是希望也能使用构建标签。我非常欣赏当前的限制---你不能为任何与测试相关的内容使用构建标签---因为我知道每当我阅读代码时,我非常清楚如果我现在运行实际代码,再运行测试,它们必须是相同的。没有任何额外的代码只会在非测试模式下运行。

有些限制实际上让代码更容易阅读和理解,并且可以对代码做出某些保证。所以它真的很有帮助;可读性确实得到了优先考虑,这在我看来也是 Go 最好的功能之一。

Carlisia Thompson: 是的,我使用过带 泛型 的语言,我理解它使某些事情变得更简单;我不确定是否有些事情只能通过泛型实现,我对此没有那么了解,但我仍然不相信任何通过使用 泛型 变得更简单、更容易或更简洁的东西,会比 Go 的可读性更重要。

Dmitri Shuralyov: 是的。

Carlisia Thompson:  我觉得这可能是个很个人化的观点。

Dmitri Shuralyov: 嗯,我们必须记住,在编程语言和软件工程领域,我们总是需要做出妥协。你不可能拥有一切,所以你必须确保在有与没有之间找到平衡。有时候这意味着要拒绝某些东西,有时候这意味着不做你想做的事,因为你知道更大的好处是值得的。我们必须记住拒绝的积极面。

Brian Ketelsen: 我认为你可以拥有一切。这不是 Scala 吗?[笑声]

Carlisia Thompson: 哦,天哪……

Dmitri Shuralyov: 是啊……实际上,看看现在最新的 C++,C++ 17 和之后的版本---它是一个很好的例子,说明当你拥有一切时会发生什么。当然,你可以做所有那些事情,但我不愿意去审查或维护那样的代码。

Carlisia Thompson: 你并没有拥有一切,因为你没有可读性。拜托!

Dmitri Shuralyov: 是的。

Carlisia Thompson: 去读一些 C++ 代码吧……你必须像这样:“让我们看看头文件,这到底是做什么的。”

Dmitri Shuralyov: 我想量化一下……今天早上,我运行了一个程序---它统计代码行数,我看到我维护的包里有超过 100,000 行的 Go 代码。这些是我每天支持的包---如果有人发现了 bug,或者它无法构建,或者某些东西坏了,我会在 24 小时内回应并跟进---我对此非常有信心。如果这些代码是用其他语言编写的,我绝对无法处理如此多的代码,因为我没有 Go 给我的这些保证和简洁性。

我觉得能够维护这么多 Go 代码---这是 Go 自身提供的能力。否则我无法做到。

Brian Ketelsen: 阿门!

Carlisia Thompson: 我认为 Go 的代码行数有点具有误导性,因为 Go 的代码在垂直空间上非常密集,但如果你读十行 Go 代码,里面的内容非常少。它只是占用了很多垂直空间,但在这些垂直空间中发生的事情并不多。所以 100 行 Go 代码与 100 行其他语言的代码相比---Go 的 100 行代码中发生的事情要少得多。但也许应该更多,我不确定,但肯定要少得多,因为更多的内容发生在垂直空间中。

Brian Ketelsen: 不,而且这使得它更容易阅读。

Dmitri Shuralyov: 是的,你可以看到所有发生的事情,一切都在你面前的代码中;你不需要去跳转到某个宏定义,或者其他的东西,或许某个操作符被重载了……这些都不用担心。你只需要知道它就是你看到的那样。

Carlisia Thompson: 是的。你是 Go 语言的维护者,对吧?

Dmitri Shuralyov: 我是---让我看看,怎么形容更合适……我算是贡献者吧。我对这个项目有过贡献。我确实拥有审查权限,所以我可以审查提交的代码,但当然,他们有一个包拥有者制度---这个人必须做出最终决定,是否将某个更改合并到某个包中。

Erik St. Martin: 我们可能时间不多了……我们要不要开始讨论项目和新闻?我知道有一则特别的新闻我们需要谈到……

Brian Ketelsen: 当然。

Dmitri Shuralyov: 听起来不错!

Erik St. Martin: 好的,我们应该从最重要的事情开始,那就是有安全更新---Go 1.9.1 和 1.8.4[26]。如果你现在运行的是 1.9 或 1.8,请及时更新。我记得这些版本修复了两个不同的安全问题。

Brian Ketelsen:  是的,其中一个是关于 net.smtp 的漏洞,另一个是一个嵌套的 Git 漏洞;如果你尝试将 Go 包检出到另一个 Git 仓库中,可能会导致代码执行漏洞。

Dmitri Shuralyov: 是的,在更新之前,不要 go get 你不信任的包……更新之后可能也不要。

Erik St. Martin: 是的,实际上你也不应该信任那些包。几乎所有语言都有这种问题,因为在执行 go install 之类的操作时,代码会被执行……你无法控制---

Dmitri Shuralyov: 对。实际上,Go 在这方面相对更好一些,因为当你 go get 某个包时,它只是下载和检出,可能还会编译,但实际上直到你 go test 时,才会真正运行其中的代码。

Erik St. Martin: 是的,不过你可能会有生成器之类的东西与构建一起执行

Dmitri Shuralyov: 对,所以除非你 go generate,否则你不会执行他们的代码。

Erik St. Martin: 嗯,这也是事实。

Dmitri Shuralyov: 所以这个漏洞实际上是唯一一个可以让你 go get 某个包并被入侵的方式。据我所知,没有其他方式可以做到这一点。

Erik St. Martin: 好吧,接下来我们聊聊一些有趣的项目……

Brian Ketelsen: Skylark[27] 本周发布了,这个项目看起来很有趣,因为它是 Python 的一个轻量级版本,但它是用 Go 编写的,似乎会被用于 Bazel[28],这是 Google 的构建工具,用来替代原来的脚本工具。我不记得原来的 Python 版本叫什么了,但这个项目看起来真的很有趣,因为它可以嵌入到 Go 中,所以它可以很好地用于构建 DSL(领域特定语言)或配置语言等。原来的 Python 版本也叫 Skylark。

Erik St. Martin: 还有一个新的 Lua 解释器[29] 也是用 Go 写的。

Brian Ketelsen: 确实是。

Erik St. Martin: 我得找到链接……我会确保它出现在节目笔记中,不过几年前我记得有几个类似的项目……是的,我看到有新的出来了。

Dmitri Shuralyov: 这个我认为是针对 Lua 5.3 的,这是较新的版本之一,我认为之前的项目可能没有支持这个版本,所以这点很令人兴奋……因为大多数人实际上都想用更新的版本。

Erik St. Martin: 没有人愿意用旧的语言版本……

Dmitri Shuralyov: 嗯,Lua 的小版本变化很大,几乎就像是一个新的主版本。

Erik St. Martin: 是的,我其实已经很久没有跟进 Lua 了。早期我的职业生涯中我用了很多 Lua,现在就很少用了。

Brian Ketelsen: 那问题是,为什么你会---哦,有人已经在我们 Slack 频道里发布了链接。这些人真是太快了![笑声]

Erik St. Martin: 是的,我也刚准备发布链接……

Brian Ketelsen: 那为什么你当时用了很多 Lua 呢?你当时用 Lua 解决什么问题?对我来说,是在 NGINX 里用 OpenResty 之类的东西做脚本编写……

Erik St. Martin: 是的,有这种情况,也有一些场景是你在应用程序中需要某种 DSL(领域特定语言),你需要创建规则引擎之类的东西,可能你不想自己构建这些东西,所以你就支持 Lua。

我还知道视频游戏也在用 Lua。

Dmitri Shuralyov: 是的,这也是我见过 Lua 最多的应用场景。那是很久以前了,但它确实是其中一个最佳的使用案例,到处都在用脚本。

Erik St. Martin: Lua 可以非常快速地嵌入到某个地方,给人们提供一个接近真实语言的工具,而且有相对简单的方式可以将其沙盒化,以便他们执行的代码无法访问完整的实现。

Brian Ketelsen:  我很想看到一些 Go 应用程序的代码示例,这些应用程序嵌入了 Lua 脚本语言来做一些事情。这是我对互联网的公开挑战。给我发一个这样的示例。我想看看。

Erik St. Martin: 现在我得想出点什么了。我会想出点东西的。

Brian Ketelsen: 你不需要自己编一个,我只是想看到一个真实的应用程序。

Carlisia Thompson: 不是假的,Erik。要真的。

Dmitri Shuralyov: 如果它们真的存在的话。

Erik St. Martin: 好的,还有另一个我看到的有趣工具---我猜它叫 Colly(像那条狗的名字)。它在 github.com/asciimoo/colly[30]。它是一个用 Go 编写的网页抓取工具,你可以访问链接之类的东西。看起来很酷。因为很多人一直在用 Python 来做这个,有一些很好的工具,比如 Scrapy,还有另一个非常常见的用于网页抓取的工具。

我想知道我们是否会看到基于它构建的 Capybara[31] 之类的东西。(译者注: Ruby生态的一个框架)

Brian Ketelsen: 那会很酷。

Erik St. Martin: 除此之外,公开声明,我还没有看完这个视频---我最近一直在旅行---Tyler Treat[32] 在 StrangeLoop 上做了一个演讲[33],标题是“你想跑得快吗?”,其实这个演讲蛮有趣的。我们会把这个视频的链接放在节目笔记里;到目前为止我觉得值得一看。

Brian Ketelsen: 是的,我也是---我看了一部分,感觉很不错。

Carlisia Thompson: 我肯定会去看。

Brian Ketelsen: 我还没看完。

Erik St. Martin: 他非常聪明,所以我猜后面的内容也不错。

Brian Ketelsen: 是的,基于他以前的博客文章和其他演讲,这应该是个很棒的演讲……看了肯定不会有损失。

Erik St. Martin: 太多的旅行---看,我刚想在频道里贴链接,Florin 已经发出来了。

Brian Ketelsen: 是的……

Erik St. Martin: 大家现在还需要我们做什么呢……?

Brian Ketelsen: 该死的 Florin[34]……[笑声]

Erik St. Martin: 好吧,我们进入 #FreeSoftwareFriday 环节。大家有没有想要给某人致谢的?

Brian Ketelsen: 我有。

Erik St. Martin: 好的,来说说吧。

Brian Ketelsen: 我决定终于要学一下 tmux 了。我一直抗拒用 tmux,因为它让我觉得很烦,我已经在 Vim 里要记住一堆快捷键了,现在又多了一层东西,所以我一直在避免它……但我发现了一个东西---我甚至不打算尝试发音;我会拼出来---Byobu[35]Dustin Kirkland[36] 开发的,它是一个漂亮的 tmux 或 Screen 的封装,很不错。只需要记住几条快捷键,而不是那些疯狂的 tmux 组合……我现在非常喜欢它。

Carlisia Thompson: 让我们知道用得怎么样……[笑声]

Erik St. Martin: 什么?

Carlisia Thompson: 让我们知道用得怎么样。

Brian Ketelsen: 我会的。

Carlisia Thompson: 我想对 Go Examples 表示感谢……怎么说呢?Go 中写示例代码的能力。

Erik St. Martin: 是的,那是文档的一部分……

Carlisia Thompson: 是的。

Dmitri Shuralyov: 哦,是的,_godoc_ 功能……

Carlisia Thompson: 是的,你可以写测试,还可以写示例---实际上我几个月前才发现这个功能。这让我心里感到特别温暖……这是其中一个让我超喜欢的功能。今天我终于写了我的第一个示例代码,我觉得我会对它痴迷。

Dmitri Shuralyov: 不错。

Brian Ketelsen: 这太棒了。

Dmitri Shuralyov: 这是个很棒的功能。

Erik St. Martin: 我非常喜欢这个功能,因为它确保了代码始终能够运行,对吧?

Carlisia Thompson: 而且它真的会运行!我太喜欢了!我太喜欢了!

Brian Ketelsen: 而且它们还是测试代码。这是最棒的部分,它们也是测试代码。所以如果你的示例代码不能通过测试,那你的测试也就无法通过。

Dmitri Shuralyov: 没错。

Carlisia Thompson: 是的!

Erik St. Martin: 这样的话,如果你更改了 API,导致示例代码不再匹配,它还会导致构建失败。

Brian Ketelsen: 没错。

Carlisia Thompson: 对于那些不了解你代码库的人来说,这真的非常有帮助。你可以填充一个类型,然后运行代码,输出结果就在那儿;其实你甚至不需要运行,结果就直接在文件中显示出来。太酷了。

Erik St. Martin: 我们应该查一查是谁实现了这个功能,然后好好感谢一下他们。

Carlisia Thompson: 是的。无论你是谁,你太棒了。谢谢你。

Brian Ketelsen: 你知道,可能性很大是 Brad 实现的。Go 团队里那些跳出常规思维的东西都是 Brad 想出来的,坦白说。你可以在推特上引用我的话,没关系。他现在正在休陪产假。

Dmitri Shuralyov: 是的,你能感觉到他不在。这种感觉……他现在不在这里。

Brian Ketelsen: 太安静了……

Dmitri Shuralyov: 至少我能感觉到。

Brian Ketelsen: 他是推动安全更新补丁发布的人。如果他没有强力推动,可能这个补丁不会发布。所以即使在陪产假期间,Brad 依然在处理工作。向 Brad 致敬!向他的小宝宝致敬。我们喜欢小 gopher。

Erik St. Martin: 永远喜欢。

Dmitri Shuralyov: 而且你知道,他的孩子会在一堆 gopher 周围长大。

Brian Ketelsen: 这挺酷的。

Erik St. Martin: 是的,我家里有太多 gopher 的东西了,简直搞笑。别人肯定觉得我们家是最奇怪的家庭之一。

Dmitri Shuralyov: 哈哈,现在我感觉好多了,因为我也有很多 gopher 相关的东西。

Erik St. Martin: 别人会想,“他们对卡通 gopher 的痴迷是怎么回事?我不懂。” [笑声]

Dmitri Shuralyov: 我有个项目想法。

Brian Ketelsen: 太棒了。

Dmitri Shuralyov: 这个项目是 GopherCI[37],由 Bradley Falzon[38] 开发。他做了一个面向 Go 的持续代码质量 CI 系统。它可以运行很多 Go 的静态代码分析工具,比如 staticcheckgovetgosimple,都是社区开发的工具,当然也包括官方工具。这个系统的设计理念是非常易于使用;比方说,比 Travis CI 还要简单。

你只需要为你的 GitHub 用户名启用它,然后你所有的 Go 项目就可以开始从中受益了。当你提交一个 pull request 或别人提交时,如果这些工具发现了任何问题,它会留下评论并通知你。

Bradley Falzon 开发并持续改进这个项目。这真的是一个非常棒的 Go 项目,我很高兴它的存在,也要感谢他为此付出的努力。

Brian Ketelsen: 是的,价格也非常便宜。向所有在南半球的 gopher 致敬……在那边马桶水流方向都相反的地方,谢谢你,Bradley。

Dmitri Shuralyov:  我还特别喜欢他把这个项目开源了。基本上,如果你想自己运行它,可以下载代码,放到自己的服务器上运行。但如果你想方便一点,让别人来托管并运行你的代码,你只需要支付个人版大概 4 美元/月的费用,就不需要自己动手。我觉得这是两全其美的方案。

Erik St. Martin: 是的,而且它进步了很多。我们实际上已经把 Bradley 列入了我们要邀请上节目的人名单中,打算请他来聊聊这个项目。

Brian Ketelsen: 非常酷。

Dmitri Shuralyov: 不错。我关注了他开发新功能的代码库,看到那些进展真的很酷。他在合并代码之前会确保所有代码质量很高,所以能够这样做真的很棒。

Erik St. Martin: 我这次没有什么新东西,大家可能之前已经听说过了,但对我来说是新的……所以我要感谢 VSCode[39]Ramya[40] 以及所有为 Go 插件做出贡献的人。

Brian Ketelsen: 太棒了!

Erik St. Martin: 因为我在 GopherCon 前的时候答应过她,我会认真使用 VS Code。因为我已经用了很多年的 Vim,过去几周我基本上只用 VS Code 来记录日志和写代码,感觉很不错。

我还没决定是否会一直用下去,因为我有点舍不得 Vim,但如果我要离开 Vim,我可能会选择 VS Code。

大家还有什么想要感谢的人吗?还是我们该结束了?

Carlisia Thompson: 我觉得我们差不多了。

Dmitri Shuralyov: 我还可以再提一个……

Carlisia Thompson: 好的!

Dmitri Shuralyov: 这是关于一位开源贡献者的。这次我要感谢 Glenn Lewis[41]。他对 go-GitHub Go 包帮助很大---这是 GitHub API 的 Go 客户端。他基本上确保 PR 能被审查并合并。如果没有他,我不知道这个项目会怎样,所以非常感谢他的所有工作。Glenn Lewis。

Erik St. Martin: 太棒了。

Carlisia Thompson: Dmitri,请加个链接吧。

Dmitri Shuralyov: 哦,对,好主意。

Carlisia Thompson: 谢谢。

Erik St. Martin: 我们会确保把我们提到的所有内容都放到节目笔记里。所以我们估计时间差不多了,现在要结束正式的节目了。像往常一样,我们可能还会留在这里,回答一些现场听众的问题。所以如果你碰巧在周四下午 3 点(东部时间)加入我们在 Slack 频道的讨论,你可以在节目开始前后和我们互动。

好了,非常感谢大家参加节目。特别感谢 Dmitri,感谢你忍受我们所有的延迟、重新安排以及暂时改变录制方式……

Dmitri Shuralyov: 我真的很高兴能来。谢谢你们邀请我!

Brian Ketelsen: 非常感谢你来做客,Dmitri。

Erik St. Martin: 你可以在推特上找到我们 @GoTimeFM,或者在网上访问 GoTime.fm。如果你想上节目或有嘉宾或话题建议,最好的方式是在我们的 GitHub 仓库 ping[42] 提交一个 issue。好了,大家再见。我们下周见。

Brian Ketelsen: 再见!

Carlisia Thompson: 再见!这是一场很棒的节目!

Dmitri Shuralyov: 再见!

参考资料
[1]

Full-time Open Source : https://changelog.com/gotime/58

[2]

Erik St. Martin: https://github.com/erikstmartin

[3]

Brian Ketelsen: https://github.com/bketelsen

[4]

Carlisia Pinto: https://github.com/carlisia

[5]

Dmitri Shurya: https://github.com/dmitshur

[6]

Conception: https://www.youtube.com/watch?v=DNJ7HqlV55k

[7]

Triggit: https://www.businessinsider.com/triggit-acquisition-by-gravity4-and-gurbaksh-chahal-2015-3

[8]

Sourcegraph: https://github.com/sourcegraph

[9]

GopherCon: https://www.gophercon.com/

[10]

演示: https://www.youtube.com/watch?v=DNJ7HqlV55k

[11]

asm.js: https://en.wikipedia.org/wiki/Asm.js

[12]

WebAssembly: http://webassembly.org/

[13]

Gopher.js: https://github.com/gopherjs/gopherjs

[14]

GraphQL: https://graphql.org/

[15]

我已经创建了它: https://github.com/shurcooL/graphql

[16]

Vecty: https://github.com/gopherjs/vecty

[17]

Go Play Space: https://goplay.space/

[18]

github.com/iafan/goplayspace: https://github.com/iafan/goplayspace

[19]

Steve Francia: https://twitter.com/spf13

[20]

Go 现状演讲: https://www.youtube.com/watch?v=l_FkVIPerzE

[21]

Ross Cox: https://twitter.com/_rsc

[22]

演讲: https://www.youtube.com/watch?v=h6Cw9iCDVcU&t=282s

[23]

Brad Fitzpatrick: https://twitter.com/bradfitz

[24]

Velocity 大会: https://velocity-conference.com/

[25]

Brendan Burns: https://conferences.oreilly.com/velocity/vl-ny/public/schedule/speaker/290285.html

[26]

Go 1.9.1 和 1.8.4: https://groups.google.com/forum/#!msg/golang-nuts/sHfMg4gZNps/a-HDgDDDAAAJ

[27]

Skylark: https://github.com/google/skylark

[28]

Bazel: https://bazel.build/

[29]

Lua 解释器: https://github.com/milochristiansen/lua

[30]

github.com/asciimoo/colly: https://github.com/gocolly/colly

[31]

Capybara: https://github.com/teamcapybara/capybara

[32]

Tyler Treat: https://github.com/tylertreat

[33]

演讲: https://www.youtube.com/watch?v=DJ4d_PZ6Gns

[34]

Florin: https://twitter.com/dlsniper

[35]

Byobu: http://byobu.co/

[36]

Dustin Kirkland: https://github.com/dustinkirkland/byobu

[37]

GopherCI: https://gopherci.io/

[38]

Bradley Falzon: https://github.com/bradleyfalzon

[39]

VSCode: https://code.visualstudio.com/

[40]

Ramya: https://twitter.com/ramyanexus

[41]

Glenn Lewis: https://github.com/gmlewis

[42]

ping: https://github.com/GoTimeFM/ping


旅途散记
记录技术提升