This commit is contained in:
louzefeng
2024-07-11 05:50:32 +00:00
parent bf99793fd0
commit d3828a7aee
6071 changed files with 0 additions and 0 deletions

Binary file not shown.

View File

@@ -0,0 +1,125 @@
<audio id="audio" title="21 | 高效工作Facebook的10x程序员效率心法" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ae/69/ae007b6781bc727c2e4e6d26b85bac69.mp3"></audio>
你好,我是葛俊。从今天这篇文章开始,我们就正式进入个人效能模块了。今天,我要和你分享的主题是,程序员如何高效地进行开发工作。
最近比较流行的一个说法是10x程序员也就是10倍程序员意思是一个好的程序员工作效率可以达到普通程序员的10倍。要做到这一点并不容易我们需要在编程技术、工作方式、工具使用等方面全面提高。
今天这篇文章我将聚焦于如何提高自己的编程技术给出在实践中被证明有效的3条原则包括抽象和分而治之、快速迭代以及DRYDont Repeat Yourself并针对每条原则给出几个高效实践。而关于工作方式、工具使用等方面的内容我会在后面几篇文章中与你详细讨论。
## 第一条原则:抽象和分而治之
虽然我们面对的世界非常复杂,但大脑只能同时处理有限的信息,那怎么平衡这个有限和复杂之间的矛盾呢?
**最好的办法是**,把一个系统拆分为几个有限的子系统,每个子系统涵盖某一方面的内容,并将其复杂性隐藏起来,只对外暴露关键信息。
这样,我们在研究这个系统的时候,就无需考虑其子系统的细节,从而对整个系统进行有效的思考。如果我们需要深入了解某一个子系统,再打开这个子系统来看即可。
以此类推,如果这个子系统还是很复杂,我们可以再对其进行拆分。这样一来,在任何时候,我们思考时面对的元素都是有限的,复杂度也下降到了大脑的能力范围之内,从而完成对一个复杂系统的理解和处理。
**这个拆分处理的过程,就是我们常说的分而治之;而用子系统来隐藏一个领域的内部细节,就是抽象。**抽象和分而治之,是我们理解世界的基础。
比如我们在了解一张简单的桌子时首先想到的是它由1个桌面和4条桌腿组成。那么桌面和桌腿就是子系统桌面就是一个抽象代表实现摆放物品功能的一个平面桌腿也是一个抽象代表支撑桌面的结构。
如果我们需要进一步了解桌面或者桌腿这两个子系统,可以再进一步去看它们的细节,比如两者都有形状、重量、材料、颜色等。但如果一上来就考虑这些细节的话,我们对桌子的理解就会陷入无尽的细节当中,无法快速形成对整个桌子的认知。
软件开发也是这个道理,我们必须做好抽象和分而治之,才能做出好的程序。
所以,拿到一个任务之后,我们要做的**首先就是进行模块的定义,也就是抽象,然后对其分而治之**。
为方便理解我再和你分享一个在Facebook时几个前后端开发者同时开发一个功能的案例吧。
这个功能由一个前端开发者和两个后端开发者完成整个研发过程至少涉及3个抽象和分而治之的操作
- 第一步前后端模块进行自然的拆分。这时前后端开发者一定会一块儿认真讨论明确前后端代码运行时的流程后端需要提供的API以及交付这些API的时间。
- 第二步,两个后端开发者对后端工作进行拆分,确定各自的工作任务和边界。
- 第三步,每个开发者对自己负责的部分再进行抽象和拆分。
在这个过程中一定要明确模块之间的依赖关系尽快确定接口规格和可调用性。比如在前后端的拆分中常常会采用这几个步骤处理API
1. 前后端开发者一起讨论明确需要的API。
1. 后端人员会先实现API的Mock返回符合格式规范的数据。在这个过程中后端开发者会尽快发出代码审查的要求给另一个后端和前端开发者以确保格式正确。
1. Mock实现之后尽快推到主仓的master上(也就是origin/master),并尽快将其部署到内部测试环境,让前端开发者可以使用内部测试环境进行开发和调试。
1. 这些API还不能面对用户通常会先使用功能开关让它只对公司开发人员可见。这样的话即使API的代码在origin/master上部署到了生产环境也不会对用户产生影响。
通过这样的操作,前后端的任务拆分就顺利完成了。
提高抽象和分而治之效率的一个技巧是,在设计代码架构时注意**寻找合适的设计模式**。
设计模式指的是,设计过程中可以反复使用的、可以解决特定问题的设计方法,最经典的莫过于[《设计模式:可复用面向对象软件的基础》](https://book.douban.com/subject/1052241/)中列举的23个设计模式以及针对企业软件架构的[《企业应用架构模式》](https://book.douban.com/subject/1230559/)。同时,我们还要注意公司内部具体的常用模式。这些模式都是经实践检验有效的,且传播较广容易理解,都可以作为你进行模块拆分的参照。
具体实现功能的过程中,也会处处体现分而治之的思想。最主要的一个表现是,每个开发者都会把自己的**代码尽量做到原子性**。代码的原子性指的是,一个提交包含一个不可分割的特性、修复或者优化。
在实际工作中,功能往往比较大。如果只用一个提交完成一个功能,那这个提交往往会比较大,所以我们需要把这个功能再拆分为子功能。
比如某个后端API的实现我们很可能会把它拆分成数据模型和API业务两部分但如果这样的提交还是太大的话可以进一步将其拆小把API业务再分为重构和添加新业务两部分。
总之我们的目的是让每个提交都做成能够独立完成一些任务但是又不太大。一般来说一个提交通常不超过800行代码。
## 第二条原则:快速迭代
通过前面的文章,我们已经明确了快速迭代对提高研发效能的重要意义。接下来,我们就看看在具体的编程中,快速迭代的一些实践吧。
**第一,不要追求完美,不要过度计划,而是要尽快实现功能,通过不断迭代来完善。**优秀的架构往往不是设计出来的,而是在实现过程中逐步发展、完善起来的。
Facebook有一条常见的海报标语叫作“Done is better than perfect”意思就是完成比完美要重要。要实现快速迭代我们在设计和实现功能时都要注意简单化。
有些开发者过于追求技术,投入了大量时间去设计精美、复杂的系统。这样做没有问题,但一定要有一个度,切忌杀鸡用牛刀。因为复杂的系统虽然精美,但往往不容易理解,维护成本也比较高,修改起来更是不容易。
所以我们在Facebook进行开发的时候尽量使用简单实用的设计然后快速进行版本迭代。
**第二,在设计的实现中,尽量让自己的代码能够尽快运行起来,从而尽快地验证结果。**我们常常会先实现一个可以运行起来的脚手架,然后再持续地往里面添加内容。
在工作中,因为往往是在一个比较大的系统里工作,不能很容易地运行新代码。这时,我们可以编写脚本或者单元测试用例来触发新写的代码。通常情况下,我们更倾向于使用后者,因为这些测试用例,在功能开发完成上线之后,还可以继续用于保证代码质量。
在我看来,在开发过程中,能触发新写的代码帮助我开发,是单元测试的一个重要功能。
**第三,为了能够快速进行验证,一个重要实践是设置好本地的代码检验**包括静态扫描、相关单元测试的方便运行以及IDE能够进行的实时检查等。
**第四,代码写好之后,尽快提交到主代码仓并保证不会阻塞其他开发人员**
实际上这是代码提交原子性的另外一个重要特点即代码提交的原子性可以保证主代码仓在理论上能够随时基于master分支上的任何提交构建出可以运行的、直接面对用户的产品。在这种方式下每个开发者在任何时候都可以基于origin/master进行开发从而确保Facebook几千人共主干开发时分而治之能够顺利进行。
关于实现代码提交的原子性我还有一个小技巧就是如果当前编写的代码提交实在不方便马上推送到origin/master分支上我们也可以频繁地fetch origin/master的代码到本地并在本地对orgin/master进行rebase来解决冲突。这样就可以确保我们开发的代码是基于最新的主仓代码从而降低代码完成之后push时冲突的可能性。
## 第三条原则DRY
DRY也就是不要重复你自己是很多开发模式的基础也是我们非常熟悉的一条开发原则了。比如我们把一段经常使用的代码封装到一个函数里在使用它的地方直接调用这个函数就是一个最基本的DRY。
**代码逻辑的重复**,不仅仅是工作量的浪费,还会大大降低代码的质量和可维护性。所以,我们在开发时,需要留意重复的代码逻辑,并进行适当的处理。
具体来说首先是寻找重复的逻辑和代码。在动手实现功能之前我们会花一些时间在内部代码仓和知识库中进行查找寻找是否有类似的功能实现以及一些底层可以复用的库过程中也可以直接联系类似功能的实现者进行讨论和寻求帮助。另外有一些IDE比如Intellij IDEA可以在编码的过程中自动探测项目中可能的代码重复。
找到重复的逻辑和代码之后,主要的处理方式是,把共同的部分抽象出来,封装到一个模块、类或者函数等结构中去。
如果在开发新功能时发现有需要重构的地方,一个常见的有效办法是,先用几个提交完成重构,然后再基于重构用几个提交实现新功能。
在编程工作中,除了代码的重复外,比较常见的还有**流程的重复**。比如测试中,我们常常需要重复地产生一些测试数据,运行完测试之后再把这些数据删除。
这些重复的流程也需要DRY最主要的办法是自动化。以重复的测试数据产生、删除流程为例一般的做法是编写脚本进行自动化当然有些时候也需要写一些复杂的可执行程序来生成数据。
流程重复还有一个特点是,它常常和团队相关,也就是说很多成员可能都会重复某些操作,这样的操作更值得自动化。比如,团队的很多成员常常都需要产生测试数据,这时我推荐你主动对其进行自动化、通用化,并提交到代码仓的工具文件夹中供团队使用。
## 小结
今天我针对如何使自己成长为10x程序员首先给出了在编程技术方面的3个原则分别是抽象和分而治之、快速迭代以及DRY。然后针对每一条原则我给出了Facebook高效开发者的一些常用实践。
其实我们还可以从这3条原则中延伸出其他很多有效的实践。
比如,好的代码注释。对子系统设计进行合理的注解,可以方便其他开发者在不同的抽象层面对软件结构有更直观的了解。而且如果系统拆分得当的话,需要注释的地方就会比较少。又比如,代码的设计时审查,就是帮助我们及早进行架构讨论,从而实现快速迭代。
为方便你理解并运用到自己的开发工作中,我将这些实践总结到了一张表格中,如下所示。
<img src="https://static001.geekbang.org/resource/image/cc/d1/ccb6ec868814aa9d86a3558739cd8dd1.jpg" alt="">
另外关于编程技术的高效实践也是不断演化和发展的。以设计模式为例最近几年又出现了针对Kubernetes开发场景的模式你可以参考[《Kubernetes Patterns》](https://www.amazon.com/Kubernetes-Patterns-Designing-Cloud-Native-Applications-ebook/dp/B07QH3JCC6/ref=sr_1_1?crid=1M8SGJX67RR5J&amp;keywords=kubernetes+patterns&amp;qid=1570073661&amp;sprefix=kubernetes%2Caps%2C335&amp;sr=8-1)这本书针对云原生Cloud Native开发也有了业界比较认可的[12-factor原则](https://12factor.net/zh_cn/)等。将来必定还会有其他新的设计模式产生。比如伴随着AI的逐渐成熟针对AI的设计模式必定会出现。
所以,作为一名软件开发者,我们必须要持续学习。我之前在一家创业公司时,有一个刚大学毕业两年的同事,他有一个非常好的习惯,就是每天早上比其他同事早半个小时到办公室,专门来学习和提高自己。正是因为他的持续学习,使得他虽然工作时间不长,但在整个团队里一直处于技术领先的位置。你也可以借鉴这个方法,或者采用其他适合自己的方法来持续地提升自己。
## 思考题
1. 我今天提到的关于分而治之的实践,哪一条对你触动最大呢?同时,也和我分享一下你在工作实践中的感受吧。
1. 你还知道哪些编程技术方面的高效原则和实践吗?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,159 @@
<audio id="audio" title="22 | 深度工作:聚焦最有价值的事儿" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/af/b7/af35045310370097905652ec726d1fb7.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊深度工作这个话题。
我在上一篇文章中提到要想成为一个10x程序员我们需要在编程技术、工作方式、工具使用等方面全面提高自己。今天我们就从工作方式的角度来看看如何提高个人效能吧。
毋庸置疑,身边充斥的各种信息,无时无刻不在吸引着我们的注意力,导致我们的可用时间越来越少,需要处理的信息、事情却越来越多。这中间的关系一旦处理不好,很容易就会出现问题。比如:
- 工作从早忙到晚,但一直被业务拖着跑,绩效一般,个人也得不到成长。
- 碎片时间很努力地学习相关技术,似乎学了不少,但不成系统,学完也就完了,没什么效果。
- 工作太忙,没有时间锻炼、放松,效率越来越低,可能自己还察觉不到。
- 工作总是被打断,无法静下心来工作和学习。
所以今天,我就和你聊一个看似简单的问题:在任务多、干扰多的现状下,如何最高效地利用时间,去做最重要的事儿,同时有更多的时间来放松和享受生活。
在我看来,这个问题的答案就是深度工作,聚焦最有价值的事儿。[深度工作](https://book.douban.com/subject/27056409/)这个概念,是由卡尔 · 纽波特Cal Newport提出的指的是在无干扰的状态下才能专注地进行的专业活动。这样的活动能够使个人的认知能力发挥到极限从而让我们创造出最大价值成为一个不可替代的人。
就我个人而言我也一直在想办法去聚焦最有价值的事儿也读了不少GTDGeting Things Done尽管去做的书尝试了不少方法积累了不少经验也踩了不少坑。总结来看实现深度工作的办法说起来很简单可以概括为以下三步
1. 以终为始,寻找并聚焦最重要的任务;
1. 追根究底,寻找最高效的解决方案;
1. 安排时间和精力,高效执行解决方案。
这些办法真正实行起来就比较有挑战了所以接下来我会从这3个方面与你分享些实用的方法和技巧。
## 1. 以终为始,寻找并聚焦最重要的任务
我们都清楚,事情做得快非常重要,但做真正有效的事情更重要。这也就意味着,我们首先要找到目前最最重要的任务。
在找最重要的任务时,我建议你按照以下三步来操作。
1. 自己定义任务;
1. 聚焦目标,以终为始;
1. 无情的筛选。
下面我们分别看看这三步具体如何实施吧。
### 第一步,自己定义任务
GTD的创始人大卫 · 艾伦David Allen提出日常任务可以分为3种
- 预先计划的任务Pre-defined Work比如迭代之初就计划好了的功能开发任务
- 临时产生的任务Work As It Appears比如Bug、邮件、临时会议等
- 自己定义的任务Defining Work即根据当前状况自己决定需要做的任务。
在我看来我们要把更多的时间和精力放到自己定义的任务上。你可能会觉得前两种任务已经够多了也非常重要。但其实我们容易忽略的第3种任务可以帮我们减少前两种任务中不必要的浪费。
因为,预先计划好的任务的优先级常常会随着情况的改变而改变,而临时产生的任务很可能当时觉得很紧急,但实际上是可以推迟或者甚至不做的。我们根据当前情况决定要做的任务,就可以避免把时间花在低优先级的任务上。
我的建议是,在每天的工作间隙,花一些时间考虑自己去定义任务,问自己这几个问题:
- 预先定义的任务是否还需要做?
- 有没有什么更重要的任务要替代现在手上的任务?
- 临时产生的干扰任务怎么处理才好?需要我来处理吗?
- ……
### 第二步,聚焦目标,以终为始
聚焦目标,以终为始,其实就是在自己定义任务。因为时间有限,为目标服务的任务才最重要。
作为高效开发者常见的目标包括业务成功、帮助团队以及个人成长这3个。如果能找到三者重合的任务就最好不过了。
我在Facebook工作的时候有个朋友发现日常工作中自己和团队常常要用到面板而现有的面板系统对他们的场景不够友好。于是他自己花时间实现了一个基于Python的DSL的面板系统。他的工作中就会大量使用Python所以这个项目对他的个人技术成长很有好处。
工具做好第一版之后,团队成员非常喜欢这个工具,并很快就扩展到其他的很多团队,比如我也用它给我们团队做了一个面板。这个工具非常好用,半年后在公司立项,并由这个朋友主导,逐渐发展为公司最受欢迎的面板系统。
这个面板工具任务,就是一个帮助业务成功、帮助团队和个人成长三者相结合的案例。
日常工作中更常见的情况是,三者不能兼得。这时,我们首先应该关注业务成功,因为它是我们工作的最基本目标,是基础。在此之上,我推荐先考虑帮助团队成长。因为帮助团队的同时,往往会给自己带来一些直接或间接的成长机会。
### 第三步,无情的筛选,少即是多
生命有限,而工作无限,所以我们必须要无情地排优先级。
很多人都有一个倾向就是贪多认为越多越好我曾经也这样。在自己的书单里添加了几百本书书签页中添加了几百篇要读的技术文章Todo List里添加的任务也越来越多还计划学习这个语言、那个框架。但实际做起来却因为时间有限不但只能浅尝而止还让自己很疲倦。痛定思痛我下决心去做减法。
在我看来,**“数字3原则”很有效也就是强制把要做的事、要达到的目标都限制在3个以内。**
我曾参加过一个增进对自己了解的工作坊,期间有一个练习,帮助我们明确自己最关注的道德品质。
首先我们要从一个有50项品质的列表上勾选出25项自认为最重要的然后在这25项品质中再选出10项最重要的最后从这10项里再筛选出3个。
第一轮筛选很轻松第二轮筛选时就有一点儿困难了到了第3轮10选3时真的非常痛苦。它强迫我仔细思考到底哪3个品质对我才是最重要的。作出决定之后你就会发现对自己的了解更深了。
这对我帮助非常大所以我在后续的工作计划中也强迫自己使用数字3原则无论是三年规划、半年规划、本周规划还是当天工作都做一个无情的筛选找出最最重要的3件事儿。我推荐你也尝试一下。
经过这三步,我们已经明确了最重要的任务,接下来我们再看看如何寻找高效的解决方案吧。
## 2. 追根究底,寻找最高效的解决方案
回想一下你和产品经理打交道的过程,是不是下面这样呢?产品经理决定如何在业务上满足用户需求,而开发人员就是,从产品经理手中接过任务,马上开始技术上的设计和实现。
这种工作方法我们很熟悉,但其实有很大的局限性。因为开发人员对技术实现最熟悉,如果我们能结合业务进行思考,追根究底,常常能对业务的解决方案做出一些改进,甚至是重新设计出更好的方案。而这样的改进,对个人研发效能的提高帮助非常大。
我在做Phabricator项目时曾收到一个任务要解决用户进行inline讨论时模态弹窗modal挡住弹窗下代码的问题。你可以先思考下自己收到这样的任务会如何做然后再对比看看我的经历收获会更大。
这个任务的描述很简单就是去改进Phabricator使用的JavaScript库让它支持用鼠标拖动模态弹窗。但是当我了解这个JavaScript库后发现工作量非常大需要三四天才能完成。正当我考虑如何下手时一个经验丰富的同事提醒我先别着急去做这样的大改动可以想想看还有没有其他方法能够更简单地解决这个问题。
我觉得这个建议非常有道理,于是仔细思考了用户需求,并与产品经理进行沟通和讨论。结论是,要解决代码被遮挡的问题,并不一定非要拖动模态弹窗,还有一个比较简单的办法,就是放弃使用模态弹窗做讨论功能,而是直接在被讨论的代码和下一行代码之间插入一个文本框供用户进行讨论。
这样一来,不但解决了代码被遮挡的问题,还因为无需改动底层库,工作量直接降为了一天,同时给用户提供了预期之外的方便。因为没有模态弹窗,用户写注释时还可以上下滚动代码窗口,来查看更大范围内的代码,体验更好了。
此后,我在接到任何任务时,都会先考虑它到底要解决什么问题,有没有更好的解决方案,花些时间去深入思考,往往都能帮我节省后面的很多时间。
所以**我认为,开发人员也要对业务有一定的了解**。面对任务的时候多问几个Why并与产品经理和团队成员充分沟通了解它到底要解决什么问题只有这样我们才能以解决问题为出发点找到最高效的解决方案。
接下来,我们再看看如何管理时间和精力,去具体实现这些方案吧。
## 3. 安排时间和精力,高效执行解决方案
信息爆炸、时间碎片化给我们的时间管理和精力管理带来了很大挑战。关于这个话题也有很多理论和实践了我也尝试过很多方法。在这里我给你推荐3种我认为最有效的方法包括用番茄工作法来记录深度工作时间、用拥抱无聊来控制对手机的依赖以及用反向行事日历Reverse Calendar来确保休息和高效。
接下来我们分别看看这3种方法吧。
### 用番茄工作法来记录深度工作时间
简单地说番茄工作法就是把时间划分为固定时长的工作时间和休息时间。一个番茄时间包含两部分25分钟的工作学习和5分钟的休息时间。
番茄工作法的精髓是,在每一个工作时间段,避免干扰、全神贯注。因为只有精力高度集中,减少上下文切换,才能进入深度工作状态,进而最大程度地发挥我们的心智能力,提高个人效能。
根据我的精力以及工作性质我对番茄时钟做了一点调整把每一个工作时段调整成为40分钟。在这个时间段内我尽量避免实时聊天工具和电话的干扰集中精力去做当前最重要的事情。条件允许的话我会把手机调成静音并关闭电脑上的提醒功能甚至干脆离开办公区去走走边散步边进行专注的思考。
### 用拥抱无聊来控制手机依赖
手机依赖症越来越普遍,我也深受其害,有段时间甚至去卫生间都一定要带着手机。
后来,我发现了一个“拥抱无聊”的方法。具体来说就是,在一些非常碎片化的时间,不要因为无聊就马上抄起手机,去找一些有意思的东西放松或者学习,而是尝试适应无聊。
其实,无聊也是一种不错的状态,试着去享受它,不要让自己的大脑一直处于活跃状态。这样可以让我们得到休息,而且更重要的是,能够比较好地避免自己打扰自己的倾向。所谓“自己打扰自己”,就是会不自觉地去想、去做一些与手上工作不相干的事儿,自己干扰自己。
不知道你有没有这样的感受,当习惯一有空闲就使用手机之后,在工作中稍有一点空闲,或者是遇到比较难以处理的问题时,就会不自觉地想停下来看一看手机。我认为,这就是因为我们习惯了用手机来缓解紧张和压力而形成的条件反射,就是对手机的一种依赖。
### 用反向行事日历来确保休息和高效工作
反向行事日历主要用来处理因为工作太多导致的忽略休息、影响健康和效率低下的问题。
我们在安排日程时,通常会把工作任务放到日历中,而不会把运动、休息、吃饭等非工作任务放入其中。反向日历正好相反,首先就是把这些非工作任务作为最高优先级放到日历中,然后再在剩余的时间中安排工作任务。
在我看来,这个方法有两个非常明显的好处:
- 第一,可以强制我们去休息和锻炼;
- 第二,可以让我们更有紧迫感,提高工作效率。当我们在日历上把运动、休息和吃饭的时间标注出来之后,就会清楚看到剩下可以用来工作的时间很有限,必须要提高工作效率才能完成任务。
## 小结
我在[开篇词](https://time.geekbang.org/column/article/120801)中指出团队研发效能的定义包括有效性准确、效率快速和可持续性3个方面。实际上个人研发效能也包括这3个方面。其中有效性就是要做对的事儿效率就是要高效地执行而可持续性就是要安排好时间和精力有张有弛才能持续发展。
针对这3点我今天就与你分享了聚焦最重要的事儿的3个步骤第一以终为始寻找并聚焦最重要的任务第二追根究底寻找最高效的解决方案第三安排时间和精力高效执行解决方案。
可以看到这三步就是从个人效能的3个方面入手的聚焦最重要的任务是帮助我们提高有效性寻找高效的解决方案并高效执行是帮助我们快速完成开发任务而合理安排时间和精力就是让我们持续地进行高效开发。
知易行难,同样适用于这些原则。所以,我非常希望你能实践并坚持这些原则和方法,当然了你也可以去寻找更适合自己的方法,让自己这块好钢能用到刀刃上。
## 思考题
1. 你有其他克服手机依赖症的好方法吗?
1. 你觉得碎片时间,更适合碎片化学习/娱乐,还是拥抱无聊呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,198 @@
<audio id="audio" title="23 | 效率工具:选对用对才能事半功倍" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/58/07/58108212d4d1f40cffb792f82dffd807.mp3"></audio>
你好,我是葛俊。今天,我来和你聊一聊效率工具。
在前两篇文章中我分别从编程技术和工作方式两个方面与你介绍了如何提高个人研发效能成长为10x程序员。今天我再与你分享些工具使用方面的技巧和方法来帮助你全方位地提高自己的工作效率。
谈到研发效能,一定离不开工具。工具使用得当,可以大幅提升研发效率,甚至可以因此引发业务发展、开发行为的质变。比如,我在[第11篇文章](https://time.geekbang.org/column/article/136070)中与你分享的截屏工具链带来的效率提升就使得其被广泛用在了Commit Message中大幅提高了整个过程的效率。
我一直对各种工具很感兴趣从业这些年来在这上面花了不少时间也踩了一些坑。比如我曾经在Emacs上面花费了大量的时间去自动化和优化很多细枝末节的操作。结果是花在优化和自动化上的时间比节省下的时间还要多得不偿失。
所以,我总结得出,**对于工具一定要选对和用对**。
选对工具指的是,我们要针对不同的任务,找到合适的工具来提高效率。而用对工具指的是,我们要分配适量的时间和精力来选择工具,要时刻注意投入产出比。
关于用对工具,我推荐一个比较好的方式是,**留意工作中经常重复和繁琐的工作步骤,隔一段时间就做些工具方面的调研,看看有没有更合适的工具或者使用方法来优化这些流程。**对于一些非常繁琐、执行频率又非常高的操作,如果没有现成的工具,甚至可以考虑自己开发一些工具和脚本进行优化。频次越高的操作,越值得优化。
接下来,我就按照功能和你分享一些有助于提高工作效率的工具。
因为我最近的工作电脑是macOS系统所以我今天推荐的适用于苹果操作系统和生态的工具会偏多一些。但这些优化的方向和思路是一致的在Windows操作系统上你也能找到类似的工具。另外安卓系统更加灵活有比苹果生态更多的工具。
## 第一个任务类别是,操作系统上的通用操作
操作系统上的通用操作,主要有窗口切换、程序启动、窗口管理、剪贴板管理等。这些操作普遍且频繁,比较值得优化。
**关于窗口切换**各操作系统都有一些自带的支持。比如macOS系统自带的方式是cmd+tabWindows系统自带的是Alt+Tab或者Win+Tab。**关于程序启动**macOS系统有SpotlightWindows系统可以使用Win键或者Win+R启动任务。
但这两个操作实在是频繁,且操作系统自带的功能只是基础功能。所以。我推荐你进行进一步优化。
在macOS系统上Alfred、Quicksilver这两个工具就很不错。Windows系统的话我推荐[Wox](http://www.wox.one)。
这些工具的用法也很简单使用一个快捷键启动然后输入过滤条件查找需要运行或者切换的程序回车即可。使用Quicksilver启动/切换程序的方法,如下所示。
<img src="https://static001.geekbang.org/resource/image/ba/80/ba2319c749fed30712d51c189b7b4d80.gif" alt="">
**关于窗口管理**,有一个常用操作,是把一个窗口自动缩放,并摆放到屏幕的某一个位置(比如屏幕的左上角)。
在macOS系统中我一般使用[BetterTouchTool](https://folivora.ai)这个工具实现,也推荐你使用。通过它,我可以非常方便地实现窗口缩放和拖动。有两个操作,我觉得特别方便:
- 一是按住alt键无论光标在窗口的任何位置都可以通过挪动鼠标拖动窗口
- 二是按住cmd+alt键无论光标在窗口的任何位置都可以通过移动鼠标来改变窗口大小。
BetterTouchTool的配置和使用如下面两幅图所示。
<img src="https://static001.geekbang.org/resource/image/d5/26/d5ba52955fc684e464555d3edd7d1b26.png" alt="">
<img src="https://static001.geekbang.org/resource/image/24/25/2438f319fb1d0f3ed131676400baf125.gif" alt="">
最后是,**剪贴板管理**。目前,操作系统自带的剪贴板只能保存一条记录,但如果我们使用剪贴板历史管理工具的话,可以非常方便地保存和使用多条历史拷贝内容。
在macOS系统上我用的工具是Quicksilver。Windows上类似的工具有很多比如[Ditto](https://sspai.com/post/43700)。
## 第二个任务类别是输入
关于输入我主要和你推荐4类工具分别是语音输入、文字快速输入、重新定义按键和具体的键盘。
**我们先看看第一类工具,语音输入**。我在电脑上常用的是[讯飞语音输入](https://apps.apple.com/cn/app/%E8%AE%AF%E9%A3%9E%E8%AF%AD%E9%9F%B3%E8%BE%93%E5%85%A5-%E6%99%BA%E8%83%BD%E8%AF%86%E5%88%AB-%E8%BD%BB%E6%9D%BE%E5%86%99%E4%BD%9C/id1243368435?mt=12),手机上常用的是[讯飞输入法](https://apps.apple.com/cn/app/%E8%AE%AF%E9%A3%9E%E8%BE%93%E5%85%A5%E6%B3%95-%E6%99%BA%E8%83%BD%E8%AF%AD%E9%9F%B3%E8%BE%93%E5%85%A5%E6%B3%95/id917236063)。在电脑上使用语音输入有一个技巧是,把手机作为麦克风。这样输入的效果非常好,尤其是旁边有人说话的时候,手机麦克风的过滤功能就非常棒了。
在手机上,我经常使用讯飞输入法的语音便签功能,保存录音的同时还可以直接把语音转为文字,对于记录灵感来说特别方便。
**第2类工具是文字快速输入**。Mac上最流行的是[TextExpander](https://textexpander.com/),它可以方便地生成预先定义的字符串、当天日期,也可以很方便地指定文字生成之后的光标所在位置等。
比如,经过下面的配置之后,只要输入缩写“;mj”就可以生成日记模板并把光标放到第一个任务处。
```
## %Y-%m-%d:
今天关键任务
%|
-
成功之处,不足之处
-
```
<img src="https://static001.geekbang.org/resource/image/1d/f2/1d887ea891669d78b878e95aecf23ff2.gif" alt="">
在Windows系统上开源免费的[AutoHotkey](https://www.autohotkey.com)非常强大。它不但可以进行文本扩展还可以运行程序。我来和你举一个运行程序的例子吧。比如你可以使用Win+V直接切换到VIM。如果VIM先前没启动的话则会直接其启动。
```
;;; switch to vim
#v::
if WinExist( &quot;ahk_class Vim&quot; )
{
WinActivate
}
Return
```
**第三类工具是,重新定义按键**。在我看来对程序员最实用的一个功能是把大写锁定键caps lock转换成ctrl键。因为caps lock的位置很方便使用频率却很低而编程中常会用到ctrl键所以这个重新定义对程序员非常有用。
至于重新定义按键的工具在Mac上我使用的是[Karabiner-Elements](https://github.com/tekezo/Karabiner-Elements)在Windows上使用AutoHotkey就可以。
另外我对caps lock键的定义不是简单地重新定义为ctrl而是如果和其他键共同使用的话就是ctrl键单独使用的话就是esc键。因为esc键的使用频率也很高尤其是在VIM中。
在Karabiner-Elements中的具体设置方法是
```
&quot;rules&quot;: [
{
&quot;manipulators&quot;: [
{
&quot;description&quot;: &quot;Change ctrl to esc if pressed alone.&quot;,
&quot;from&quot;: {
&quot;key_code&quot;: &quot;caps_lock&quot;,
&quot;modifiers&quot;: {
&quot;optional&quot;: [
&quot;any&quot;
]
}
},
&quot;to&quot;: [
{
&quot;key_code&quot;: &quot;left_control&quot;
}
],
&quot;to_if_alone&quot;: [
{
&quot;key_code&quot;: &quot;escape&quot;
}
],
&quot;type&quot;: &quot;basic&quot;
}
]
}
]
```
**第四类工具是,具体的键盘**。开发者要经常输入,选择一款顺手的键盘就很重要了。键盘的选用,我看重的是手感和键盘布局。手感的话,机械键盘的确好一些,你可以根据自己的体验选择一款合适的。而布局的话,左右手按键分离较远的一般来说会好一些。
根据这些原则,我平时使用[Kinesis Pro2键盘](https://kinesis-ergo.com/keyboards/advantage2-keyboard/)。因为,它的中间是凹进去的,所以手指与每一行按键的距离都差不多,输入时很轻松。另外,特殊键由大拇指控制,这个特点我也非常喜欢。
<img src="https://static001.geekbang.org/resource/image/0a/d7/0a67b27cf7a965869d30417bd46e10d7.png" alt="">
## 第三个任务类别是,知识管理
对学习和知识管理我主要与你分享6类工具。
第一类工具是**云盘**,用来确保存储的内容不会因为本地电脑的意外情况而丢失。目前,市面上的各种云盘都可以满足这个需求。这里我有一个小建议,使用云盘时最好能够使用本地的自动同步工具,自动地将本地文件夹里的内容同步到云盘。
第2类工具是**笔记**。我挑选笔记工具的原则包括支持云同步支持电脑和手机端同时访问。印象笔记、Mac自带的Notes、石墨文档等都不错。
第3类工具是**写文档的工具**。相较于笔记工具来说写文档的工具还有一个挑选原则是支持Markdown。我现在是组合使用iCloud、1Writer和Typora使用iCloud进行同步在手机使用1Writer在电脑上使用Typora。
第4类工具是**思维导图**。对思维导图,比较重要的特性包括跨平台、方便使用快捷键、方便导入\导出其他格式。我以前一直用的是[FreeMind](http://freemind.sourceforge.net/wiki/index.php/Main_Page)它基于Java实现在Windows、macOS和Linux系统上都可以使用快捷键配置也很强大但它的一大缺陷是在手机上不能使用。所以最近两年我转移到了[XMind Zen](https://www.xmind.net/zen/)上面。它可以在电脑和手机上同时使用并自动同步,显示也比较美观。
第5类工具是**截屏、录屏**。我对截屏、录屏工具的选用原则是标注功能要好另外要可以把录屏保存成GIF格式。我现在最常用的是对这两方面都支持不错的[MonoSnap](https://monosnap.com)。另外,录屏的时候,我们常常希望把当前的键盘输入显示到屏幕上,我推荐使用[KeyCastr](https://github.com/keycastr/keycastr)编辑GIF文件的话我使用的是[GIF Brewery 3](https://apps.apple.com/cn/app/gif-brewery-3-by-gfycat/id1081413713?mt=12)。
这个专栏中,你见到的录屏和截屏图片,我就是使用这三个工具完成的。另外,还有一个对命令行终端录制比较方便的工具[asciinema](https://asciinema.org/about)。它可以把你在终端里面的操作和输出用JSON形式保存下来也可以上传到asciinema的网站上还可以方便地在你自己的网站上引用显示。但这个工具的缺点是无法显示键盘的按键操作。
第6类是**处理PDF文件的工具**。这样的工具我主要关注基本的标注功能,以及在平板电脑上可以使用电子笔操作。在电脑上,我一般使用[Foxit Reader](https://download.cnet.com/Foxit-Reader/3000-18497_4-10313206.html)和macOS自带的Preview在手机上我主要使用的是[PDF Expert](https://apps.apple.com/cn/app/pdf-expert-7-pdf-editor/id743974925)。
在我看来一个比较高效的方式是把PDF文件放到笔记软件中然后在笔记中直接对其进行操作。印象笔记和苹果自带的Notes都支持这个功能。
## 第四个任务类别是,浏览网页
访问互联网是非常高频的操作所以我把它单独列成一类。对开发人员我推荐使用Chrome和Firefox因为两者都有很强的扩展功能对开发活动比较友好。
因为最近几年我主要在使用Chrome所以接下来我与你介绍一些我常用的Chrome插件和技巧。
第一个插件是[Octotree](https://www.octotree.io)。它会在你访问GitHub查看项目时在窗口左侧以树形结构清晰明了地显示代码仓结构。
<img src="https://static001.geekbang.org/resource/image/d0/f7/d02f3b1258264c337a5e2af7d59ff3f7.png" alt="">
第二个插件是[Pocket for Chrome](https://getpocket.com/chrome/)。它需要与[Pocket](https://app.getpocket.com)配合使用,实际上就是一个在线的网页书签保存服务,但它的推荐功能很优秀,并且对手机端的支持也很好。
第三个插件是[smartUp Gestures](https://chrome.google.com/webstore/detail/smartup-gestures/bgjfekefhjemchdeigphccilhncnjldn?hl=en)。通过它你可以按住鼠标右键不放在屏幕上画一些形状也就是手势Gesture来进行一些操作。
我最常使用的功能有:切换到左边(或右边)一个标签页、向下(或向上)翻页至网页末尾(或网页开头)、返回网页访问历史的上一页(或下一页),以及关闭当前标签页。
在文稿里我有一个录屏供你参考。
<img src="https://static001.geekbang.org/resource/image/4c/df/4c9dd735eb9d141fe51a9ebfb1cd9cdf.gif" alt="">
## 第5个任务类别是开发
和开发工作相关的工具比较多包括编辑器比如VS Code、VIM、IDE比如Visual Studio和JetBrains系列、代码仓管理工具比如Git和HG、API测试工具比如Postman以及命令行工具等。
关于开发工具的使用,我会在后面的文章中与你详细介绍其中的使用技巧,帮你提高编码效率。
## 小结
无论“磨刀不误砍柴工”“工欲善其事必先利其器”还是乔布斯曾说过的“You cannot mandate productivity, you must provide the tools to let people become their best.(你不能强制要求大家提高生产力,你必须提供工具,让大家发挥他们的最大能力)” ,强调的都是工具的重要性。
所以作为开发人员我们一定要选对工具、用对工具才能提高效能。在今天这篇文章中我从操作系统上的通用操作、输入、知识管理、网页浏览和开发5个方面给你推荐了一些工具及使用方法。当然了关于开发工具这个重头戏我还会在后面的文章中与你详细分享。
其实,使用哪款工具,是一个仁者见仁,智者见智的问题。所以,通过这篇文章,我更希望达到的目的是,能帮助你对日常工作中最常见的操作进行思考,寻找值得优化和可以使用工具优化的地方,从而提高个人的研发效能。
但,我还要强调的是,工具只是辅助,编程工作更重要的还是思考。所以,我建议你不要花费过多的时间在工具研究上,要时刻留意投入产出比。
## 思考题
除了今天提到的操作系统上的通用操作、输入、知识管理、网页浏览和开发5个方面外你觉得还有哪些方面的操作比较频繁吗有什么值得优化的地方吗又有什么值得推荐的工具吗
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,334 @@
<audio id="audio" title="24 | VIM如何高性价比地学习VIM的实用技巧" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/1d/cf/1daf168653a172ae2e7bf014242d06cf.mp3"></audio>
你好我是葛俊。今天我来和你聊聊VIM的使用技巧。
在“[特别放送 | 每个开发人员都应该学一些VIM](https://time.geekbang.org/column/article/144470)”这篇文章中我和你详细介绍了VIM提高研发效能背后的原因。我推荐每个开发者都应该学一些VIM的原因主要有两个
- 独特的命令模式,可以大量减少按键次数,使得编辑更高效;
- 支持跨平台同时可以在很多其他IDE、编辑器中作为插件使用真正做到一次学习处处使用。
VIM确实可以帮助我们提高效率但面对这样一个学习曲线长而且陡的编辑器我们很容易因为上手太难而放弃。所以如何性价比高地学习VIM的使用技巧非常重要。
我推荐你按照以下三步来高效地学习如何使用VIM
1. 学习VIM的命令模式和命令组合方式
1. 学习VIM最常用的命令
1. 在自己的工作环境中使用VIM比如与命令行环境的集成使用。
接下来,我们分别看看这三步吧。
## VIM的模式机制
VIM的基本模式是命令模式在命令模式中敲击主体键的效果不是直接插入字符而是执行命令实现对文本的修改。
### 使用VIM的最佳工作流
在我看来,在命令模式下工作,效率高、按键少,所以我推荐你**尽量让VIM处于命令模式使用各种命令进行工作。进入编辑模式完成编辑工作之后也立即返回命令模式。**
事实上我们从命令模式进入编辑模式修改文件之后再返回命令模式的全过程就是一个编辑命令。它跟其他的命令比如使用dd删除一行并没有本质区别。接下来我们一起看个具体的例子吧。
比如,我要在一行文字后面加上一个大括号,后面再写一行代码。开始编辑时,光标处于这一行的开头。
```
config =
^
```
修改的目标是这样:
```
config = {
timeout: 1000ms,
}
```
在VIM中我的操作是首先敲击大写字母A将光标移到这一行的末尾并进入编辑模式
```
config =
^
```
然后,输入“{ timeout: 1000ms,回车}”在文件中插入内容。最后敲击Esc键回到命令模式。编辑完成。
```
config = {
timeout: 1000ms,
}
^
```
实际上整个过程就是执行了一条“在本行末尾插入文字”的命令。整条命令的输入是“A{ timeout: 1000ms,回车}Esc”。虽然比较长但仍然是一条文本编辑命令。
所以实际上,**我们在VIM中的工作正是在命令模式里执行一条条的命令完成的**。理解了这一点,你就可以有意识地学习、设计命令来高效地完成工作了。
为了让命令更加高效VIM还提供了强大的命令组合功能使得命令的功能效果呈指数级增长。
### 命令的组合方式
在VIM中有相当一部分命令可以扩展为3部分
- 开头的部分是一个数字,代表重复次数;
- 中间的部分是命令;
- 最后的部分,代表命令的对象。
比如命令3de中3表示执行3次d是删除命令e表示从当前位置到单词的末尾。整条命令的意思就是从当前位置向后删除3个单词。类似的命令3ce表示从当前位置向后删除三个单词然后进入编辑模式。
可以看到,命令组合的前两个部分比较简单,但第三个部分也就是命令对象,技巧就比较多了。所以接下来,我就与你详细介绍下到底有**哪些命令对象可以使用。**
其实,对命令对象并没有一个完整的分类。但我根据经验,将其总结为光标移动命令和文本对象两种。
**第一种是光标移动命令**。比如,`$`命令是移动光标到本行末尾,那么`d$`就表示删除到本行末尾;再比如,`4}`表示向下移动4个由空行隔开的段落那么`d4}`就是删除这4个段落。
这一类命令功能很强大,我们也很熟悉了,但它有一个缺陷,就是选择的出发点始终是当前光标所在位置。而我们在处理文本的过程中,尤其是在编写程序的时候,光标所在位置常常是处于需要修改内容的中间,而不是开头。比如,我们常常在写一个注释字符串的时候,需要修改整个字符串:
```
comment: 'this is standalone mode',
^
```
如果使用上面的光标移动作为命令对象的话我们需要执行bbbct命令也就是向左移动三个单词之后再向右删除到第一个单引号的地方操作起来很麻烦。
针对这种情况VIM提供了第二种命令对象**文本对象**。具体来说就是用字符代表一定的文字单位。比如i"代表两个双引号之间的字符串aw表示当前的单词。使用这种文本对象很方便比如上面的编辑例子我们只需要命令ci"就可以实现比bbbct命令方便了很多。
具体来说,文本对象命令由两部分组成:
- 第一部分只能是字符i或者a表示是否包含对象边界。比如i"表示不包括两边的引号而a"就包括引号;
- 第二部分选择比较多,表示各种不同的文本对象。比如
<img src="https://static001.geekbang.org/resource/image/17/22/17ef1f7dba10731966b0048dac716c22.jpg" alt="">
如果你要查看完整的文本对象,可以使用:help text-objects。
这一组文本对象选择功能很强大而且是VIM自带功能不需要安装任何插件。神奇的是不知道为什么很多使用VIM很久的人都不知道这个功能。我个人把它叫作vip功能如果你以前没有用过可以在文件中输入命令vip看看效果。相信不会让你失望。
可见VIM的命令操作及其组合功能非常强大要高效使用VIM我们就必须使用命令模式以及命令组合。我看到有些开发者使用VIM时一上来就使用i命令进入编辑模式然后在编辑模式中工作。但是编辑模式的功能很有限完全发挥不出VIM提供的高效文本编辑功能。
接下来我们进入第二步也就是学习VIM最常用的命令。
## 命令模式中的基础命令
VIM有大量的命令可供我们使用这里我主要与你介绍针对单个文件编辑的命令因为这是编辑工作最基础的部分也是各种跨平台场景尤其在其他IDE中通过插件使用VIM的方式的共性部分。
至于更多的命令和细节,你可以参考我的“[命令模式中的基础命令](https://jungejason.github.io/vim-commands/)”这篇博客,或者自行搜索查询。
接下来,我会按照以下编辑文件的逻辑顺序,来与你介绍关键知识点和技巧:
1. 打开文件,以及进行设置操作;
1. 移动光标;
1. 编辑文本;
1. 查找、替换。
### 第一组常用命令是,打开文件以及对文件的设置
这些命令包括打开文件、退出、保存、设置等。关于设置,如果在远端的服务器上,我常常运行一条命令进行基本设置:
```
:set ic hls is hid nu
```
其中ic表示搜索时忽略大小写hls表示高亮显示搜索结果is表示增量搜索也就是在搜索输入的过程中在按回车键之前就实时显示当前的匹配结果hid表示让VIM支持多文件操作nu表示显示行号。
如果要关闭其中某一个选项的话在前面添加一个no即可。比如关闭显示行号使用
```
:set nonu
```
### 第二组常用命令是,移动光标
VIM提供了非常细粒度的光标移动命令包括水平移动、上下移动、文字段落的移动。这些命令之间的差别很细微。比如w和e都是向右边移动一个单词只不过w是把光标放到下一个单词开头而e是把光标放到这一个单词结尾。
虽然差别很小但正是这样的细粒度才使得VIM能够让我们使用最少的按键次数去完成编辑任务。
### 第三组常用命令是,编辑命令
编辑工作中我们应该大量使用命令组合来提高效率。关于组合命令的威力,我建议你看下“[命令模式中的基础命令](https://jungejason.github.io/vim-commands/)”那篇博客我与你详细解释了一个ct"的例子。
这里,**我重点与你分享一个设计命令的技巧**。VIM的取消命令u、重复命令.,都是针对上一个完整的编辑命令而言。所以,我们可以设计一个完整的编辑命令,从而可以使用重复命令.来提高效率。比如你要把一段文本中的几个时间都修改成20ms这段文本原来是
```
timeout: 1000000ms,
waiting: 300000ms,
starting: 40000ms,
```
希望改变成
```
timeout: 20ms,
waiting: 20ms,
starting: 20ms,
```
在VIM中有多种实现方法。一种还不错的方法是使用修改命令c和移动命令l的组合在每一行用一个命令搞定。具体操作是将光标挪到1000000ms里数字1的地方用c7l20命令对第一行进行修改然后在第二行、第三行的相同位置分别使用c6l20和c5l20进行修改。
这个命令可以完成工作,但每条命令都不一样,不能重复。实际上,还有一种更好的方法。
你可以设计一条命令在每一行重复使用。在这个例子里这个命令是ctm20<esc>意思是从当前位置删除到下一个字母m的位置进入编辑模式插入20然后返回命令模式。</esc>
使用这条命令对第一行进行修改之后,你可以把光标挪到第二、三行使用重复命令.来完成编辑任务,如下所示。
<img src="https://static001.geekbang.org/resource/image/92/5e/92124bb53e6bfa3c5fb0c1f3d5273a5e.gif" alt="">
当然了,你还可以使用正则表达式进行查找替换,但是远比上面这个重复命令要复杂。这个设计命令并重复使用的方法非常好用,建议你上手实践下。
关于重复命令VIM另外还有一个录制命令q和重复命令@,功能更强大,但也比较重。如果你想深入了解的话,[可以参考这篇文章](https://xu3352.github.io/linux/2018/11/04/practical-vim-skills-chapter-11)。
### 第四组常用命令是,查找、替换
这组命令主要有/、*和s等很常用网络上也有很多描述这里我就不再详细介绍了。比如我在“[命令模式中的基础命令](https://jungejason.github.io/vim-commands/)”这篇博客中详细介绍了s命令可供你参考。
除了上述4种常见命令外VIM中还有一种很强大的可视模式Visual Mode可以提高编辑体验。接下来我们再一起看看。
### 可视模式
可视模式一共有3种包括基于字符的可视模式、基于行的可视模式和基于列的可视模式详见“[命令模式中的基础命令](https://jungejason.github.io/vim-commands/)”这篇博客中的介绍。
接下来,我以基于列的可视模式为例,带你看看可视模式是如何提高编辑体验的吧。
我们先来看看第一个例子。有一段文本每一行都用var声明变量比如var gameMode现在我希望改成使用const来声明。我们来看看具体的实现方式。
<img src="https://static001.geekbang.org/resource/image/ca/63/ca00ba7234b33295581aa3ff04469963.png" alt="">
第一步,把光标挪到第一行开头的 v 字符处使用组合键Ctrl-v进入列可视模式然后使用7j下移动七行再用e向右移动一个单词从而选中一个包含所有var的矩形区域。
<img src="https://static001.geekbang.org/resource/image/71/b4/71de442222209adc9b9c86813da264b4.png" alt="">
第二步是开始编辑。输入c删除所有的var并进入编辑模式。
<img src="https://static001.geekbang.org/resource/image/1b/58/1bcd320829837a41bb513978b3432a58.png" alt="">
第三步是输入const并输入Esc完成编辑操作回到命令模式整个修改完成。
<img src="https://static001.geekbang.org/resource/image/37/e8/377269a6d42aa02d4d8f6ade37bf77e8.png" alt="">
<img src="https://static001.geekbang.org/resource/image/58/ff/58aaa90b1938cd51464f6bdf2330acff.png" alt="">
我们再来看看第二个例子。我希望在上面这段文字的每一行末尾都添加一个分号。具体的操作步骤如下所示:
首先,用$命令把光标挪到最后一行的末尾然后Ctrl-v进入列模式再用7j命令将光标挪到第一行这时每一行的末尾都被包含到了一个方块里。不过因为每一行的长度不同所以方块没有显示完全。
<img src="https://static001.geekbang.org/resource/image/3c/ad/3cea82dcf9ba1c4a9053ccd4d5e839ad.png" alt="">
然后输入A命令光标挪到每一行的末尾并进入编辑模式。
<img src="https://static001.geekbang.org/resource/image/db/84/dbecf5cbb5b75cf002270c154abbdc84.png" alt="">
最后输入要插入的分号输入Esc完成编辑。这样每一行的末尾都添加了一个分号。
<img src="https://static001.geekbang.org/resource/image/be/8b/be6fa0acecd2dee663df3deb1929b68b.png" alt="">
<img src="https://static001.geekbang.org/resource/image/a7/4b/a7ac6e2c639334b799058b2dd243c64b.png" alt="">
最后,关于可视模式我还有三个比较有用的小贴士:
- 第一在退出了可视模式之后可以使用gv命令重新进入可视模式并选择上一次的选择。这个命令出乎意料得有用因为我们常常需要对上一次的选择进行进一步的操作。
- 第二进入可视模式之后可以直接使用v、V、Ctrl-v在三种模式之间切换而不需要退出可视模式再重新进入。比如你一开始使用v进入了字符可视模式这时发现你需要删除几行就可以直接输入V进入行的可视模式。
- 第三修改选取的区域范围。当你进入可视模式之后光标默认处于所选区域的结尾处你可以挪动光标调节选择区域的结尾部分。但如果你想调节的是所选区域的开头部分则可以使用命令o将光标跳到选择区域的开始部分再次输入命令o光标又会跳到选择区域的末尾。一个更好玩的情况是在列模式中你可以使用小写o在对角线上跳转大写O在水平方向跳转从而可以灵活调整所选矩形的四个角。
以上就是命令模式中的常用基础命令了。理解了这些命令再加上命令组合你就可以比较高效地使用VIM进行文本编辑了。但要更高效地使用VIM我们还有一个问题没有解决VIM虽然强大但也只是整个工作环境中的一个工具怎样才能在整个工作中高效地使用VIM技能呢
所以接下来我将带你探索如何寻找合适的VIM使用场景。
## 寻找合适的VIM使用场景
寻找合适的适用场景常常包括两种情况一是VIM不是主力IDE的情况二是VIM与其他工具的集成。接下来我们先看第一种情况。
### 我的工作环境中VIM不是主力IDE怎么办
这种情况下我会建议你先看一下你的主力IDE是否支持VI模式。目前绝大部分主流IDE都支持而且大都做得不错。如果你的主力IDE中能使用VIM命令的话那从VIM特有的命令模式就能让你收益颇丰。
从我的经验来看我只有在Facebook那几年使用VIM作为主力IDE其他时间使用的编辑器主要是Intellij的IDE系列和VS Code。在这些IDE里我一直在使用VI模式效果也都很好。
除了在主力IDE中使用VI模式外你还可以选择把VIM只作为一个单纯的文本编辑器需要强大的编辑功能时临时用一下就可以。比如我在写微信小程序的时候一开始使用的是原生的微信开发工具编辑功能不是太强。所以在遇到一些重量级的编辑工作时我就打开VIM快速搞定然后再回到微信原生开发工具IDE里继续工作。
### VIM能在Linux命令行环境中与其他工具结合的很好
Unix/Linux系统的一个设计理念是每个工具都只做一个功能并且把这个功能做到极致然后由操作系统把这些功能集成起来。VI当初就是作为Unix系统里的编辑器而存在的到了40年后的今天虽然VIM已经可以被配置成为一个强大的IDE但很大程度上依然是Unix/Linux系统的基础编辑器仍然可以很方便地和Unix/Linux的其他工具集成。
为了帮助你理解我再和你分享3个适用场景。
**第一个场景使用VIM作为其他工具的编辑器。**
在大部分Linux系统里默认的编辑器就是VIM。如果不是的话可以使用如下命令来设置
```
// 全局使用VIM
export EDITOR='vim'
```
**第二个适用场景使用管道Pipe。**
管道是Linux环境中最常用的工具之间的集成方式。VIM可以接收管道传过来的内容。比如我们要查看GitHub上用户xxx的代码仓的情况可以使用下面这条命令
```
curl -s https://api.github.com/users/xxx/repos | vim -
```
curl命令访问GitHub的API把输出通过管道传给VIM方便我直接在VIM中查看用户xxx的代码仓的细节。
这里需要注意的是在VIM命令后面有一个-表示VIM将使用管道作为输入。
**第三个适用场景在VIM中调用系统工具**
除了系统调用VIM和使用管道进行集成之外我们还可以在VIM中反过来调用系统的其他工具。这里我与你介绍一个最实用的命令**在可视模式中使用!命令调用外部程序。**
比如我想给一个文件里的中间几行内容前加上从1000开始的数字序号。
<img src="https://static001.geekbang.org/resource/image/f7/cc/f72d9d852cdca00c79544ef3a41dd3cc.png" alt="">
在Linux系统里我们可以使用nl这个命令行工具完成具体的命令是nl -v 1000。所以我们可以把这一部分文本单独保存为文件使用nl处理后再把处理结果拷贝回VIM中。
虽然可以达到目的但过程繁琐。幸运的是我们可以在VIM里面直接使用nl并把处理结果插入VIM中具体操作如下。
首先使用命令V进入可视模式并选择这一部分文字。
<img src="https://static001.geekbang.org/resource/image/69/37/6962fb43054994be72fcd30f89802437.png" alt="">
然后,输入命令!VIM的最后一行会显示&lt;,&gt;! 表示由外部程序操作选中的部分。这时我们输入nl -v 1000并回车。
<img src="https://static001.geekbang.org/resource/image/0d/7d/0dd3edaf5ca34ae776d1527fbbee547d.png" alt="">
VIM就会把选择的文本传给nlnl在每一行前面添加序号再把处理结果传给VIM从而完成了我们想要的编辑结果也就是在所选的几行文字前面添加从1000到1006的序号。
<img src="https://static001.geekbang.org/resource/image/5a/fd/5a7aa3f398c73283234d386a603bc3fd.png" alt="">
同时在VIM的底部会显示7 lines filtered意思是VIM里面的7行文本被使用外部工具处理过。是不是很方便
我每次使用这样的功能时都觉得很爽,因为这可以让我聚焦在工作上,而不会把时间花在繁琐的操作上。
以上就是3个最常用的VIM和其他工具集成的例子。这种工具的集成工作方式可以大大提高单个工具所能带来的效率提升我还会在后面的文章中与你详细讨论。
## 总结
在今天这篇文章中我与你介绍了高效学习VIM的三个步骤第一学习VIM的命令模式和命令组合方式第二学习文本编辑过程中各个环节最常用的命令第三在自己的工作环境中找到适用VIM的场景。
事实上我今天与你讲述的VIM命令和使用技巧只是最常见的跨平台使用中的共性部分对效能提升带来的效果也最直接、最明显。
这些远没有覆盖VIM的强大功能。比如我没有与你讨论多文件、多Tab、多窗口编辑的场景以及插件的话题。如果你需要的话可以把VIM配置成一个类似IDE的开发环境进入沉浸式的VIM使用体验。关于插件方面的内容我推荐3个插件
- [pathogen](https://github.com/tpope/vim-pathogen)。它是一个插件管理软件很好地解决了VIM自带插件删除不理想的问题。关于pathogen的使用你可以参考下[这篇文章](https://gist.github.com/romainl/9970697)。
- [nerdtree](https://github.com/scrooloose/nerdtree)在VIM中添加文件夹管理的功能。
- [fugitive](https://github.com/tpope/vim-fugitive)在VIM中添加查看、编辑Git内容的功能。它的功能简直是强大到变态。
其中pathogen和fugitive的作者都是[Tim Pope](https://github.com/tpope)一个VIM牛人。如果你想了解更多的插件的话可以看看他的其他VIM插件。
不容置疑的是VIM的学习曲线非常长即使我已经使用了15年仍然会时不时地学习到一些新东西。只要你愿意就可以一直学习一直提高。
## 思考题
你想要分享的最炫酷的VIM技巧是什么呢
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,596 @@
<audio id="audio" title="25 | 玩转Git五种提高代码提交原子性的基本操作" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/c2/8e/c2199a4ee6a45ba1fc1dfc50f066068e.mp3"></audio>
你好我是葛俊。今天我们来聊一聊Git吧。
毫无疑问Git是当前最流行的代码仓管理系统可以说是开发者的必备技能。它非常强大使用得当的话可以大幅助力个人效能的提升。一个最直接的应用就是可以帮助我们提升代码提交的原子性。如果一个团队的成员都能熟练使用Git的话可以大大提高团队代码的模块化、可读性、可维护性从而提高团队的研发效能。但可惜的是现实情况中由于Git比较复杂用得好的人并不多。
所以接下来我会通过两篇文章与你详细讲述如何使用Git助力实现代码原子性。今天这篇文章我先与你分享Git支持原子性的5种基础操作下一篇文章则会给你介绍Facebook开发人员是怎样具体应用这些基础操作去实现代码原子性的。
通过这两篇文章,我希望你能够:
1. 了解在分布式代码仓管理系统中,如何通过对代码提交的灵活处理,实现提交的原子性;
1. 帮你学习到Git的实用技巧提高开发效率。
我在[第21篇文章](https://time.geekbang.org/column/article/148170)中提到,代码提交的原子性指的是,一个提交包含一个不可分割的特性、修复或者优化。如果用一个提交完成一个功能,这个提交还是会比较大的话,我们需要把这个功能再拆分为子功能。
为什么要强调代码提交的原子性呢这是因为它有以下3大好处
- 可以让代码结构更清晰、更容易理解;
- 出了问题之后方便定位,并可以针对性地对问题提交进行“回滚”;
- 在功能开关的协助下可以让开发者尽快把代码推送到origin/master上进行合并。这正是持续集成的基础。
而Git之所以能够方便我们实现原子性提交主要有两方面的原因
- Git提供方便、灵活的提交、分支处理功能使得我们可以灵活地产生提交、修改提交、拆分提交甚至改变提交的先后顺序。
- Git是一个分布式代码仓管理系统每个开发人员在本地都有一个代码仓从而可以放心在本地代码仓中使用上述功能不用操心会影响到远程共享代码仓。
下面我就来与你分享Git支持原子性的5种基础操作具体包括
1. 用工作区改动的一部分产生提交;
1. 对当前提交进行拆分;
1. 修改当前提交;
1. 交换多个提交的先后顺序;
1. 修改非当前提交。
需要注意的是在接下来的两篇文章里我只会与你详细介绍针对原子性相关的操作而关于Git的一些基础概念和使用方法推荐你参考“[图解Git](https://marklodato.github.io/visual-git-guide/index-zh-cn.html)”这篇文章。
## 基本操作一:把工作区里代码改动的一部分转变为提交
如果是把整个文件添加到提交中操作很简单先用git add &lt;文件名&gt;把需要的文件添加到Git暂存区然后使用git commit命令提交即可。这个操作比较常见我们应该都比较熟悉。
但在工作中一个文件里的改动常常会包含多个提交的内容。比如开发一个功能时我们常常会顺手修复一些格式规范方面的东西再比如一个功能比较大的时候改动常常会涉及几个提交内容。那么在这些情况下为了实现代码提交的原子性我们就需要只把文件里的一部分改动添加到提交中剩下的部分暂时不产生提交。针对这个需求Git提供了git add -p命令。
比如我在index.js文件里有两部分改动一部分是添加一个叫作timestamp的endpoint另一部分是使用变量来定义一个魔术数字端口
```
## 显示工作区中的改动
&gt; git diff
diff --git a/index.js b/index.js
index 63b6300..986fcd8 100644
--- a/index.js
+++ b/index.js
@@ -1,8 +1,14 @@
+var port = 3000 ## &lt;-- 魔术数字变量化
var express = require('express')
var app = express()
## vvv 添加endpoint
+app.get('/timestamp', function (req, res) {
+ res.send('' + Date.now())
+})
+
app.get('/', function (req, res) {
res.send('hello world')
})
-app.listen(3000)
+// Start the server
+app.listen(port) ## &lt;-- 端口魔术数字变量化
```
这时运行git add -p index.js命令Git会把文件改动分块儿显示并提供操作选项比如我可以通过y和n指令来选择是否把当前改动添加到Git的提交暂存区中也可以通过s指令把改动块儿再进行进一步拆分。通过这些指令我就可以选择性地只把跟端口更改相关的改动添加到Git的暂存区中。
```
&gt; git add -p index.js
diff --git a/index.js b/index.js
index 63b6300..986fcd8 100644
--- a/index.js
+++ b/index.js
@@ -1,8 +1,14 @@
+var port = 3000
var express = require('express')
var app = express()
+app.get('/timestamp', function (req, res) {
+ res.send('' + Date.now())
+})
+
app.get('/', function (req, res) {
res.send('hello world')
})
-app.listen(3000)
+// Start the server
+app.listen(port)
Stage this hunk [y,n,q,a,d,s,e,?]? s
Split into 3 hunks.
@@ -1,3 +1,4 @@
+var port = 3000
var express = require('express')
var app = express()
Stage this hunk [y,n,q,a,d,j,J,g,/,e,?]? y
@@ -1,7 +2,11 @@
var express = require('express')
var app = express()
+app.get('/timestamp', function (req, res) {
+ res.send('' + Date.now())
+})
+
app.get('/', function (req, res) {
res.send('hello world')
})
Stage this hunk [y,n,q,a,d,K,j,J,g,/,e,?]? n
@@ -4,5 +9,6 @@
app.get('/', function (req, res) {
res.send('hello world')
})
-app.listen(3000)
+// Start the server
+app.listen(port)
Stage this hunk [y,n,q,a,d,K,g,/,e,?]? y
```
当整个文件的所有改动块儿都处理完成之后通过git diff --cached命令可以看到我的确只是把需要的那一部分改动也就是端口相关的改动添加到了暂存区:
```
&gt; git diff --cached
diff --git a/index.js b/index.js
index 63b6300..7b82693 100644
--- a/index.js
+++ b/index.js
@@ -1,3 +1,4 @@
+var port = 3000
var express = require('express')
var app = express()
@@ -5,4 +6,5 @@ app.get('/', function (req, res) {
res.send('hello world')
})
-app.listen(3000)
+// Start the server
+app.listen(port)
```
通过git diff命令我们可以看到endpoint相关的改动仍留在工作区
```
&gt; git diff
diff --git a/index.js b/index.js
index 7b82693..986fcd8 100644
--- a/index.js
+++ b/index.js
@@ -2,6 +2,10 @@ var port = 3000
var express = require('express')
var app = express()
+app.get('/timestamp', function (req, res) {
+ res.send('' + Date.now())
+})
+
app.get('/', function (req, res) {
res.send('hello world')
})
```
最后再通过git commit命令我就可以产生一个只包含端口相关改动的提交实现了将本地代码改动的一部分转变为提交的目的。
如果你想深入了解git add -p的内容可以参考[这篇文章](https://johnkary.net/blog/git-add-p-the-most-powerful-git-feature-youre-not-using-yet/)。
通过git add -p我们可以把工作区中的代码拆分成多个提交。但是如果需要拆分的代码已经被放到了一个提交中怎么办如果这个提交已经推送到了远程代码仓共享分支那就没有办法了。但如果这个提交还只是在本地我们就可以对它进行拆分。
## 基本操作二:对当前提交进行拆分
所谓当前提交指的是当前分支的HEAD指向的提交。
我继续以上面的代码示例向你解释应该如何操作。假如我已经把关于endpoint的改动和端口的改动产生到了同一个提交里具体怎么拆分呢
这时我可以先“取消”已有的提交也就是把提交的代码重新放回到工作区中然后再使用git add -p的方法重新产生提交。这里的取消是带引号的因为**在Git里所有的提交都是永久存在的所谓取消只不过是把当前分支指到了需要取消的提交的前面而已。**
首先我可以用git log查看历史并使用git show确认提交包含了endpoint改动和端口改动
```
## 查看提交历史
&gt; git log --graph --oneline --all
* 7db082a (HEAD -&gt; master) Change magic port AND add a endpoint
* 352cc92 Add gitignore file for node_modules
* e2dacbc (origin/master) Added the simple web server endpoint
...
## 查看提交
&gt; git show
commit 7db082ab0f105ea185c89a0ba691857b55566469 (HEAD -&gt; master)
...
diff --git a/index.js b/index.js
index 63b6300..986fcd8 100644
--- a/index.js
+++ b/index.js
@@ -1,8 +1,14 @@
+var port = 3000
var express = require('express')
var app = express()
+app.get('/timestamp', function (req, res) {
+ res.send('' + Date.now())
+})
+
app.get('/', function (req, res) {
res.send('hello world')
})
-app.listen(3000)
+// Start the server
+app.listen(port)
```
然后用git branch temp命令产生一个临时分支temp指向当前HEAD。temp分支的作用是预防代码丢失。如果后续工作出现问题的话我可以使用git reset --hard temp把代码仓、暂存区和工作区都恢复到这个位置从而不会丢失代码。
```
&gt; git branch temp
&gt; git log --graph --oneline --all
* 7db082a (HEAD -&gt; master, temp) Change magic port AND add a endpoint
* 352cc92 Add gitignore file for node_modules
* e2dacbc (origin/master) Added the simple web server endpoint
...
```
接下来运行git reset HEAD^命令把当前分支指向目标提交HEAD^也就是当前提交的父提交。同时在没有接hard或者soft参数时git reset会把目标提交的内容同时复制到暂存区但不会复制到工作区。所以工作区的内容仍然是当前提交的内容仍然有endpoint相关改动和端口相关改动。也就是说这个命令的效果就是让我回到了对这两个改动进行提交之前的状态
```
&gt; git reset HEAD^
Unstaged changes after reset:
M index.js
&gt; git status
On branch master
Your branch is ahead of 'origin/master' by 1 commit.
(use &quot;git push&quot; to publish your local commits)
Changes not staged for commit:
(use &quot;git add &lt;file&gt;...&quot; to update what will be committed)
(use &quot;git checkout -- &lt;file&gt;...&quot; to discard changes in working directory)
modified: index.js
no changes added to commit (use &quot;git add&quot; and/or &quot;git commit -a&quot;)
15:06:58 (master) jasonge@Juns-MacBook-Pro-2.local:~/jksj-repo/git-atomic-demo
## 改动在工作区
&gt; git diff
diff --git a/index.js b/index.js
index 63b6300..986fcd8 100644
--- a/index.js
+++ b/index.js
@@ -1,8 +1,14 @@
+var port = 3000
var express = require('express')
var app = express()
+app.get('/timestamp', function (req, res) {
+ res.send('' + Date.now())
+})
+
app.get('/', function (req, res) {
res.send('hello world')
})
-app.listen(3000)
+// Start the server
+app.listen(port)
## 输出为空
&gt; git diff --cached
```
最后我就可以使用上面介绍过的git add -p的方法把工作区中的改动拆分成两个提交了。
## 基本操作三:修改当前提交
如果只需要修改Commit Message的话直接使用git commit --amend命令Git就会打开你的默认编辑器让你修改修改完成之后保存退出即可。
如果要修改的是文件内容可以使用git add、git rm等命令把改动添加到暂存区再运行git commit --amend最后输入Commit Message保存退出即可。
## 基本操作四:交换多个提交的先后顺序
有些时候我们需要把多个提交交换顺序。比如master分支上有两个提交A和BB在A之上两个提交都还没有推送到origin/master上。
<img src="https://static001.geekbang.org/resource/image/99/18/9976bf834b37be7ac877eb80b73bac18.png" alt="">
这时我先完成了提交B想把它先单独推送到origin/master上去就需要交换A和B的位置使得A在B之上。我可以使用git rebase --interactive选项interactive可以简写为-i来实现这个功能。
首先还是使用git branch temp产生一个临时分支确保代码不会丢失。然后使用git log --oneline --graph来确认当前提交历史
```
&gt; git log --oneline --graph
* 7b6ea30 (HEAD -&gt; master, temp) Add a new endpoint to return timestamp
* b517154 Change magic port number to variable
* 352cc92 (origin/master) Add gitignore file for node_modules
* e2dacbc Added the simple web server endpoint
* 2f65a89 Init commit created by installing express module
```
接下来,运行
```
&gt; git rebase -i origin/master
```
Git会打开我的默认编辑器让我选择rebase的具体操作
```
pick b517154 Change magic port number to variable
pick 7b6ea30 Add a new endpoint to return timestamp
# Rebase 352cc92..7b6ea30 onto 352cc92 (2 commands)
#
# Commands:
# p, pick &lt;commit&gt; = use commit
# r, reword &lt;commit&gt; = use commit, but edit the commit message
# e, edit &lt;commit&gt; = use commit, but stop for amending
# s, squash &lt;commit&gt; = use commit, but meld into previous commit
# f, fixup &lt;commit&gt; = like &quot;squash&quot;, but discard this commit's log message
# x, exec &lt;command&gt; = run command (the rest of the line) using shell
# b, break = stop here (continue rebase later with 'git rebase --continue')
# d, drop &lt;commit&gt; = remove commit
# l, label &lt;label&gt; = label current HEAD with a name
# t, reset &lt;label&gt; = reset HEAD to a label
# m, merge [-C &lt;commit&gt; | -c &lt;commit&gt;] &lt;label&gt; [# &lt;oneline&gt;]
# . create a merge commit using the original merge commit's
# . message (or the oneline, if no original merge commit was
# . specified). Use -c &lt;commit&gt; to reword the commit message.
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
```
rebase命令一般翻译为变基意思是改变分支的参考基准。**具体到git rebase -i origin/master命令**就是把从origin/master之后到当前HEAD的所有提交也就是A和B重新有选择地放到origin/master上面。你可以选择放或者不放某一个提交也可以选择放置顺序还可以选择将多个提交合并成一个等等。另外这里说的放一个提交指的就是在HEAD之上应用一个提交的意思。
Git rebase -i打开编辑器时里面默认的操作列表是把原有提交全部原封不动地放到新的参考基准上去具体到这个例子是用两个pick命令把A和B先后重新放到origin/master之上如果我直接保存退出的话结果跟rebase之前没有任何改变。
这里因为我需要的操作是交换A和B的顺序所以交换两个pick指令行保存退出即可。Git rebase就会先后把B和A放到origin/master上。
```
pick 7b6ea30 Add a new endpoint to return timestamp
pick b517154 Change magic port number to variable
# Rebase 352cc92..7b6ea30 onto 352cc92 (2 commands)
# ...
```
至此我就完成了交换两个提交的先后顺序。接下来我可以用git log命令来确认A和B的确是交换了顺序。
```
## 以下是 git rebase -i origin/master 的输出结果
Successfully rebased and updated refs/heads/master.
## 查看提交历史
&gt; git log --oneline --graph --all
* 65c41e6 (HEAD -&gt; master) Change magic port number to variable
* 40e2824 Add a new endpoint to return timestamp
| * 7b6ea30 (temp) Add a new endpoint to return timestamp
| * b517154 Change magic port number to variable
|/
* 352cc92 (origin/master) Add gitignore file for node_modules
* e2dacbc Added the simple web server endpoint
* 2f65a89 Init commit created by installing express module
```
<img src="https://static001.geekbang.org/resource/image/6a/18/6a1a8cc66255460cd2b45d2430c93718.png" alt="">
值得注意的是A和B的commit SHA1改变了因为它们实际上是新产生出来的A和B的拷贝原来的两个提交仍然存在图中的阴影部分我们还可以用分支temp找到它们但不再需要它们了。如果temp分支被删除A和B也会自动被Git的垃圾收集过程gc清除掉。
其实git rebase -i的功能非常强大除了交换提交的顺序外还可以删除提交、和并多个提交。如果你想深入了解这部分内容的话可以参考“[Git 工具 - 重写历史](https://git-scm.com/book/zh/v2/Git-%E5%B7%A5%E5%85%B7-%E9%87%8D%E5%86%99%E5%8E%86%E5%8F%B2)”这篇文章。
## 基本操作五:修改非头部提交
在上面的基本操作二、三、四中我与你介绍的都是对当前分支头部的一个提交或者多个提交进行操作。但在工作中为了方便实现原子性我们常常需要修改历史提交也就是修改非头部提交。对历史提交操作最方便的方式依然是使用强大的git rebase -i。
接下来我继续用上面修改A和B两个提交的顺序的例子来做说明。在还没有交换提交A和B的顺序时也就是B在A之上的时候我发现我需要修改提交A。
<img src="https://static001.geekbang.org/resource/image/99/18/9976bf834b37be7ac877eb80b73bac18.png" alt="">
首先我运行git rebase -i origin/master然后在弹出的编辑窗口中把原来的“pick b517154”的一行改为“edit b517154”。其中b517154是提交A的SHA1。
```
edit b517154 Change magic port number to variable
pick 7b6ea30 Add a new endpoint to return timestamp
# Rebase 352cc92..7b6ea30 onto 352cc92 (2 commands)
# ...
```
而“edit b517154”是告知Git rebase命令在应用了b517154之后暂停后续的rebase操作直到我手动运行git rebase --continue通知它继续运行。这样当我在编辑器中保存修改并退出之后git rebase 就会暂停。
```
&gt; git rebase -i origin/master
Stopped at b517154... Change magic port number to variable
You can amend the commit now, with
git commit --amend
Once you are satisfied with your changes, run
git rebase --continue
22:29:35 (master|REBASE-i) ~/jksj-repo/git-atomic-demo &gt;
```
这时我可以运行git log --oneline --graph --all确认当前HEAD已经指向了我想要修改的提交A。
```
&gt; git log --oneline --graph --all
* 7b6ea30 (master) Add a new endpoint to return timestamp
* b517154 (HEAD) Change magic port number to variable
* 352cc92 (origin/master) Add gitignore file for node_modules
* e2dacbc Added the simple web server endpoint
* 2f65a89 Init commit created by installing express module
```
接下来我就可以使用基本操作二中提到的方法对当前提交也就是A进行修改了。具体来说就是修改文件之后用git add &lt;文件名&gt;然后再运行git commit --amend。
```
## 检查当前HEAD内容
&gt; git show
commit b51715452023fcf12432817c8a872e9e9b9118eb (HEAD)
Author: Jason Ge &lt;gejun_1978@yahoo.com&gt;
Date: Mon Oct 14 12:50:36 2019
Change magic port number to variable
Summary:
It's not good to have a magic number. This commit changes it to a
varaible.
Test:
Run node index.js and verified the root endpoint still works.
diff --git a/index.js b/index.js
index 63b6300..7b82693 100644
--- a/index.js
+++ b/index.js
@@ -1,3 +1,4 @@
+var port = 3000
var express = require('express')
var app = express()
@@ -5,4 +6,5 @@ app.get('/', function (req, res) {
res.send('hello world')
})
-app.listen(3000)
+// Start the server
+app.listen(port)
## 用VIM对文件进行修改在注释部分添加&quot;at a predefined port&quot;
&gt; vim index.js
## 查看工作区中的修改
&gt; git diff
diff --git a/index.js b/index.js
index 7b82693..eb53f5f 100644
--- a/index.js
+++ b/index.js
@@ -6,5 +6,5 @@ app.get('/', function (req, res) {
res.send('hello world')
})
-// Start the server
+// Start the server at a predefined port
app.listen(port)
22:40:10 (master|REBASE-i) jasonge@Juns-MacBook-Pro-2.local:~/jksj-repo/git-atomic-demo
&gt; git add index.js
## 对修改添加到提交A中去
&gt; git commit --amend
[detached HEAD f544b12] Change magic port number to variable
Date: Mon Oct 14 12:50:36 2019 +0800
1 file changed, 3 insertions(+), 1 deletion(-)
22:41:18 (master|REBASE-i) jasonge@Juns-MacBook-Pro-2.local:~/jksj-repo/git-atomic-demo
## 查看修改过后的A。确认其包含了新修改的内容&quot;at a predefined port&quot;
&gt; git show
commit f544b1247a10e469372797c7dd08a32c0d59b032 (HEAD)
Author: Jason Ge &lt;gejun_1978@yahoo.com&gt;
Date: Mon Oct 14 12:50:36 2019
Change magic port number to variable
Summary:
It's not good to have a magic number. This commit changes it to a
varaible.
Test:
Run node index.js and verified the root endpoint still works.
diff --git a/index.js b/index.js
index 63b6300..eb53f5f 100644
--- a/index.js
+++ b/index.js
@@ -1,3 +1,4 @@
+var port = 3000
var express = require('express')
var app = express()
@@ -5,4 +6,5 @@ app.get('/', function (req, res) {
res.send('hello world')
})
-app.listen(3000)
+// Start the server at a predefined port
+app.listen(port)
```
执行完成之后我就可以运行git rebase --continue完成git rebase -i的后续操作也就是在A之上再应用提交B并把HEAD重新指向了B从而完成了对历史提交A的修改。
```
## 继续运行rebase命令的其他步骤
&gt; git rebase --continue
Successfully rebased and updated refs/heads/master.
## 查看提交历史
&gt; git log --oneline --graph --all
* 27cba8c (HEAD -&gt; master) Add a new endpoint to return timestamp
* f544b12 Change magic port number to variable
| * 7b6ea30 (temp) Add a new endpoint to return timestamp
| * b517154 Change magic port number to variable
|/
* 352cc92 (origin/master) Add gitignore file for node_modules
* e2dacbc Added the simple web server endpoint
* 2f65a89 Init commit created by installing express module
```
经过rebase命令我重新产生了提交A和B。同样的A和B是新生成的两个提交原来的A和B仍然存在。
<img src="https://static001.geekbang.org/resource/image/75/48/75e70f4be9da61fcafdca3b427414748.png" alt="">
以上,就是修改历史提交内容的步骤。
如果我们需要对历史提交进行拆分的话步骤也差不多首先使用git rebase -i在需要拆分的提交处使用edit指令然后在git rebase -i暂停的时候使用基本操作2的方法对目标提交进行拆分拆分完成之后运行git rebase --continue即可。
## 小结
今天我与你介绍了Git支持代码提交原子性的五种基本操作包括用工作区改动的一部分产生提交、对当前提交进行拆分、修改当前提交、交换多个提交的先后顺序以及对非头部提交进行修改。
掌握这些基本操作可以让我们更灵活地对代码提交进行修改、拆分、合并和交换顺序为使用Git实现代码原子性的工作流打好基础。
其实,这些基本操作非常强大和实用,除了可以用来提高提交的原子性外,还可以帮助我们日常开发。比如,我们可以把还未完成的功能尽快产生提交,确保代码不会丢失,等到后面再修改。又比如,可以产生一些用来帮助自己本地开发的提交,始终放在本地,不推送到远程代码仓。
在我看来Git学习曲线比较陡而且长帮助手册也可以说是晦涩难懂但一旦弄懂它能让你超级灵活地对本地代码仓进行处理帮助你发现代码仓管理系统的新天地。git rebase -i命令就是一个非常典型的例子。一开始你会觉得它有些难以理解但搞懂之后就超级有用可以帮助你高效地解决非常多的问题。所以在我看来在Git上投入一些时间绝对值得
为了方便你学习,我把这篇文章涉及的代码示例放到了[GitHub](https://github.com/jungejason/git-atomic-demo)上推荐你clone下来多加练习。
## 思考题
1. 对于交换多个提交的先后顺序除了使用rebase -i命令外你还知道什么其他办法吗
1. 文章中提到,如果一个提交已经推送到了远程代码仓共享分支,那就没有办法对它进行拆分了。这个说法其实有些过于绝对。你知道为什么绝大部分情况下不能拆分,而什么情况下还可以拆分呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,353 @@
<audio id="audio" title="27 | 命令行:不只是酷,更重要的是能提高个人效能" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d3/4a/d305df11cd909f9146d06d90f8924d4a.mp3"></audio>
你好,我是葛俊。今天,我要与你分享的主题是,命令行下的高效工作技巧。
我先和你讲一个有意思的话题吧。命令行工具常常会给人一种黑客的感觉,好莱坞的电影里面常常出现命令行窗口的使用。不知道你听说过没有,很多好莱坞电影在拍摄时使用的其实是一个叫作[nmap](https://nmap.org)的工具。这个工具是做安全扫描的只不过因为它的显示特别花哨所以被很多电影采用。在nmap官方网站上还专门列出来了这些电影的名单。
类似这种可以让自己看起来很忙的工具还有很多,比如[Genact](https://github.com/svenstaro/genact)。下面是一个使用Genact的录屏当然这里的命令并没有真正运行。这可能是整个专栏中唯一一个让你看起来效率很高实际上却是降低效率的工具但说不定对你有用你懂的。
<img src="https://static001.geekbang.org/resource/image/2d/97/2dba0dfdd5f0260ad3b3082a7d3d2697.gif" alt="">
讲完这个娱乐性的话题,我们进入正题吧。
## 为什么要使用命令行?
GUI图形界面的出现是计算机技术的变革极大方便了用户。但在这么多年后的今天命令行工具为什么仍然有如此强大的生命力呢
在我看来,对软件工程师来说,想要高效开发就必须掌握命令行,主要原因包括:
- 虽然鼠标的移动和点击比较直观,但要完成重复性的工作,使用键盘会快捷得多。这一点从超市的结算人员就可以看出来,使用键盘系统的收银员总是噼里啪啦地很快就可以完成结算,而使用鼠标点击的话明显慢很多。
- 作为开发人员可以比较容易地使用命令行的脚本对自己的工作进行自动化以及和其他系统工具联动。但使用GUI的话就会困难得多。
- 命令行通常可以暴露更完整的功能让使用者对整个系统有更透彻的理解。Git就是一个典型的例子再好的GUI Git系统都只能封装一部分Git命令行功能要想真正了解Git我们必须要使用命令行。
- 有一些情况是必须使用命令行的比如SSH到远程服务器上工作的时候。
为了演示命令行的强大功能给我们带来的方便,下面是一个在本地查看文件并上传到服务器的流程的录屏。
<img src="https://static001.geekbang.org/resource/image/1d/29/1deb0006ea65cd5c4a28819a2cf50629.gif" alt="">
<img src="https://static001.geekbang.org/resource/image/95/40/958987c6594699ff1fb97b1622b21a40.gif" alt="">
通过这个案例,你可以看到命令行的以下几个功能:
- 在提示行会高亮显示时间、机器名、用户名、Git代码仓的分支和状态以及上一个命令的完成状态。
- 输入命令的时候,高亮显示错误并自动纠错。
- 使用交互的形式进行文件夹的跳转,并方便查找文件,还可以直接在命令行里显示图片。
- 使用交互的工具,把文件上传到远端的服务器,并快速连接到远端查看传输是否成功。
整个流程全部都是在命令行里完成的,速度非常快,用户体验也非常好。正因为如此,我看到的硅谷特别高效的开发人员,绝大多数都大量使用命令行。那,面对成百上千的命令行工具,我们**怎样才能高效地学习和使用**呢?
我将高效学习使用命令行的过程,归纳为两大步:
1. 配置好环境;
1. 针对自己最常使用命令行的场景,有的放矢地选择工具。
今天,我就与你详细讲述**环境配置**这个话题。而关于选择工具的话题,我会在下一篇文章中与你详细介绍。总结来讲,环境配置主要包括以下四步:
1. 选择模拟终端;
1. 选择Shell
1. 具体的Shell配置
1. 远程SSH的处理。
这里需要注意的是在命令行方面macOS和Linux系统比Windows系统要强大许多所以我主要以macOS和Linux系统来介绍而关于Windows的环境配置我只会捎带提一下。不过macOS和Linux系统中的工具选择和配置思路你可以借鉴到Windows系统中。
## 第一步,选择模拟终端
我将一个好的终端应该具有的特征归纳为4个
- 快,稳定;
- 支持多终端比如可以方便地水平和纵向分屏有tab等
- 方便配置字体颜色等;
- 方便管理常用SSH的登录。
macOS系统自带的终端不太好用常见的替代工具有iTerm2、Terminator、Hyper和Upterm。我平时使用的iTerm2是一个免费软件功能强大具备上面提到的4个特征。下面我就以iTerm2为例展开介绍。其他几个工具上也有类似功能所以你不必担心。
在多终端的场景方面iTerm2支持多窗口、多标签页同一窗口中可以进行多次水平和纵向分屏。这些操作以及窗口的跳转都有快捷键支持你可以很方便地在网络上搜索到。
<img src="https://static001.geekbang.org/resource/image/d6/bf/d63c4054e8e23136da22b1d9d6e4c2bf.png" alt="">
在管理常用SSH的登录方面iTerm2使用Profile用户画像来控制。比如下面是一个连接到远程服务器案例的录屏。
可以看到在我的工作环境里常会用到4个Profile其中有两个是连接到远端服务器的包括Mosh Remote Server 1和SSH Remote Server 2。工作时我使用Cmd+O然后选择Server 1这个Profile就可以打开一个新窗口连接到这个远程服务器上。
<img src="https://static001.geekbang.org/resource/image/ec/ae/ec60c3bf9bb2688ea59e76b1576078ae.gif" alt="">
每一个Profile都可以定义自己的字体、颜色、shell命令等。比如Server 1是类生产服务器我就把背景设置成了棕红色提醒自己在这个机器上工作时一定要小心。所以在上面的录屏中你可以看到连接到远端的SSH标签页它的背景、标签页都是棕红色。另外下面是如何对Profile颜色进行设置的截屏。
<img src="https://static001.geekbang.org/resource/image/54/58/54888d3d58612f9b557af6e45f40b758.png" alt="">
除了这些基础功能外iTerm2还有很多贴心的设计。比如
- **在屏幕中显示运行历史**Cmd+Opt+B/F。有些情况下向上滚动终端并不能看到之前的历史比如运行VIM或者Tmux的时候。这时浏览显示历史就特别有用了。
- **高亮显示当前编辑位置**包括高亮显示当前行Cmd+Opt+高亮显示光标所在位置Cmd+/)。
- **与上一次运行命令相关的操作**包括显示上一次运行命令的地方Cmd+Shift+up选中上一个命令的输出Cmd+Shift+A
其中第2、3项功能是由一组[macOS的集成工具](https://www.iterm2.com/documentation-utilities.html)提供的。这个工具集还包括显示图片的命令imgls、imgcat显示自动补全命令显示时间、注释以及在主窗口旁显示额外信息等。这些设计虽然很小但非常实用。
关于Windows系统2019年5月微软推出了[Windows Terminal](https://devblogs.microsoft.com/commandline/introducing-windows-terminal/)支持多Tab定制性很强据说体验很不错。
选择好了终端环境设置的第二步就是选择Shell。
## 第二步选择Shell
在我看来选择Shell主要有普遍性和易用性这两条原则。
Linux/Unix系统下**Bash**最普遍、用户群最广但是易用性不是很好。常用来替代Bash的工具有**Zsh**和**Fish**它们的易用性都很好。下面是两张图片用于展示Zsh和Fish在易用性方面的一些功能。
Zsh
<img src="https://static001.geekbang.org/resource/image/31/a6/3198a7c972939d35de0c03bd26c4a8a6.gif" alt="">
Fish
<img src="https://static001.geekbang.org/resource/image/08/32/082720f4dbe7e335a290938993d84732.gif" alt="">
我个人觉得Fish比Zsh更方便。事实上Fish是Friendly Interactive Shell的简称。所以交互是Fish的强项。可惜的是Fish不严格遵循POSIX的语法与Bash的脚本不兼容而Zsh则兼容所以我目前主要使用的是Zsh。
选好了模拟终端和Shell之后便是配置环境的第三步具体的Shell配置了。
## 第三步具体的Shell配置
接下来我以我自己使用的设置为例向你介绍Bash、Zsh、Fish的具体配置吧。这里主要包括**命令行提示符**的配置和其他配置两个方面。
之所以把命令行提示符单独提出来是因为它一直展现在界面上能提供很有用的价值对命令行高效工作至关重要。下面是一张图片展示了Bash、Zsh和Fish的命令行提示符。
<img src="https://static001.geekbang.org/resource/image/cd/bf/cd10736ba6a970f9a7f8d2703a4b45bf.png" alt="">
这个窗口分为三部分最上面是Bash中间是Zsh最下面是Fish都配置了文件路径、Git信息和时间戳等信息。接下来我带你一起看看这3个工具应该如何配置吧。
**Bash比较麻烦**配置文件包括定义颜色和命令行提示符的两部分:
```
## 文件 $HOME/.bash/term_colors定义颜色
# Basic aliases for bash terminal colors
N=&quot;\[\033[0m\]&quot; # unsets color to term's fg color
# regular colors
K=&quot;\[\033[0;30m\]&quot; # black
R=&quot;\[\033[0;31m\]&quot; # red
G=&quot;\[\033[0;32m\]&quot; # green
Y=&quot;\[\033[0;33m\]&quot; # yellow
B=&quot;\[\033[0;34m\]&quot; # blue
M=&quot;\[\033[0;35m\]&quot; # magenta
C=&quot;\[\033[0;36m\]&quot; # cyan
W=&quot;\[\033[0;37m\]&quot; # white
# empahsized (bolded) colors
MK=&quot;\[\033[1;30m\]&quot;
MR=&quot;\[\033[1;31m\]&quot;
MG=&quot;\[\033[1;32m\]&quot;
MY=&quot;\[\033[1;33m\]&quot;
MB=&quot;\[\033[1;34m\]&quot;
MM=&quot;\[\033[1;35m\]&quot;
MC=&quot;\[\033[1;36m\]&quot;
MW=&quot;\[\033[1;37m\]&quot;
# background colors
BGK=&quot;\[\033[40m\]&quot;
BGR=&quot;\[\033[41m\]&quot;
BGG=&quot;\[\033[42m\]&quot;
BGY=&quot;\[\033[43m\]&quot;
BGB=&quot;\[\033[44m\]&quot;
BGM=&quot;\[\033[45m\]&quot;
BGC=&quot;\[\033[46m\]&quot;
BGW=&quot;\[\033[47m\]&quot;
```
```
## 文件 $HOME/.bashrc设置提示符及其解释
###### PROMPT ######
# Set up the prompt colors
source $HOME/.bash/term_colors
PROMPT_COLOR=$G
if [ ${UID} -eq 0 ]; then
PROMPT_COLOR=$R ### root is a red color prompt
fi
#t Some good thing about this prompt:
# (1) The time shows when each command was executed, when I get back to my terminal
# (2) Git information really important for git users
# (3) Prompt color is red if I'm root
# (4) The last part of the prompt can copy/paste directly into an SCP command
# (5) Color highlight out the current directory because it's important
# (6) The export PS1 is simple to understand!
# (7) If the prev command error codes, the prompt '&gt;' turns red
export PS1=&quot;\e[42m\t\e[m$N $W&quot;'$(__git_ps1 &quot;(%s) &quot;)'&quot;$N$PROMPT_COLOR\u@\H$N:$C\w$N\n\[&quot;'$CURSOR_COLOR'&quot;\]&gt;$W &quot;
export PROMPT_COMMAND='if [ $? -ne 0 ]; then CURSOR_COLOR=`echo -e &quot;\033[0;31m&quot;`; else CURSOR_COLOR=&quot;&quot;; fi;'
```
命令行提示符之外的其他方面的配置在Bash方面我主要设置了一些命令行补全completion和别名设置alias
```
## git alias
alias g=git
alias gro='git r origin/master'
alias grio='git r -i origin/master'
alias gric='git r --continue'
alias gria='git r --abort'
## ls aliases
alias ls='ls -G'
alias la='ls -la'
alias ll='ls -l'
## git completion请参考https://github.com/git/git/blob/master/contrib/completion/git-completion.bash
source ~/.git-completion.bash
```
**Zsh的配置就容易得多了而且是模块化的**。基本上就是安装一个配置的框架然后选择插件和主题即可。具体来说我的Zsh命令行提示符配置步骤包括以下三步。
第一,[安装oh-my-zsh](https://github.com/robbyrussell/oh-my-zsh)。这是一个对Zsh进行配置的常用开源框架。
```
brew install zsh
```
第二,[安装powerline字体](http://powerline),供下一步使用。
```
brew install powerlevel9k
```
第三,在~/.zshrc中配置ZSH_THEME指定使用powerlevel9k这个主题。
```
ZSH_THEME=&quot;powerlevel9k/powerlevel9k&quot;
```
命令行提示符以外的其他配置主要是通过安装和使用oh-my-zsh插件的方式来完成。下面是我使用的各种插件供你参考。
```
## 文件~/.zshrc.sh 中关oh-my-zsh的插件列表具体插件细节请参考https://github.com/robbyrussell/oh-my-zsh以及使用Web搜索查询
plugins=(
git
z
vi-mode
zsh-syntax-highlighting
zsh-autosuggestions
osx
colored-man-pages
catimg
web-search
vscode
docker
docker-compose
copydir
copyfile
npm
yarn
extract
fzf-z
)
source $ZSH/oh-my-zsh.sh
```
**至于Fish的配置和Zsh差不多也是安装一个配置的框架然后选择插件和主题即可。**在配置命令行提示符时,主要步骤包括以下两步。
第一安装配置管理框架oh-my-fish
```
curl -L https://get.oh-my.fish | fish
```
第二查看、安装、使用oh-my-fish的某个主题主题会自动配置好命令行提示符
```
omf theme
omf install &lt;theme&gt;
omf theme &lt;theme&gt;
## 我使用的是bobthefish主题
omf theme bobthefish
```
这里有一篇不错的关于[使用oh-my-fish配置的文章](https://www.ostechnix.com/oh-fish-make-shell-beautiful/),供你参考。
Fish的其他方面的配置也是使用oh-my-fish配置会比较方便。关于具体的配置方法建议你参照[官方文档](https://github.com/oh-my-fish/oh-my-fish)。
关于环境的最后一个配置是远程SSH的处理。
## 第四步远程SSH的处理
SSH到其他机器是开发人员的常见操作最大的痛点是怎样保持多次连接的持久性。也就是说连接断开以后远端的SSH进程被杀死之前的工作记录、状态丢失导致下一次连接进去需要重新设置交易花销太大。有两类工具可以很好地解决这个问题。
**第一类工具是Tmux或者Screen**,这两个工具比较常见,用来管理一组窗口。
接下来我以Tmux为例与你描述其工作流程首先SSH到远程服务器然后用远程机器上的Tmux Client连接到已经运行的Tmux Session上。SSH断开之后Tmux Client被杀死但Tmux Session仍然保持运行意味着命令的运行状态继续存在下次SSH过去再使用Tmux Client连接即可。
如果你想深入了解Tmux的概念和搭建过程可以参考[这篇文章](http://cenalulu.github.io/linux/tmux/)。
**第二类是一个保持连接不中断的工具移动Shell**Mobile Shell。这也是我目前唯一见到的一个。这个工具是MIT做出来的知道的人不多是针对移动设备的网络经常断开设计的。
它的具体原理是每次初始登录使用SSH之后就不再使用SSH了而是使用一个基于UDP的SSP协议能够在网络断开重连的时候自动重新连接所以从使用者的角度来看就像从来没有断开过一样。
接下来,**我以阿里云ECS主机、运行Ubuntu18.04为例与你分享Mosh+Tumx的具体安装和设置方法。**
第一服务器端安装并运行Mosh Server。
```
junge@iZ4i3zrhuhpdbhZ:~$ sudo apt-get install mosh
```
第二打开服务器上的UDP端口60000~61000。
```
junge@iZ4i3zrhuhpdbhZ:~$ sudo ufw allow 60000:61000/udp
```
第三在阿里云的Web界面上修改主机的安全组设置允许UDP端口60000~61000。
<img src="https://static001.geekbang.org/resource/image/10/52/107e8927ab12b30699699db5ba54af52.jpg" alt="">
第四在客户端比如Mac上安装Mosh Client。
```
jasonge@Juns-MacBook-Pro-2@l$ brew install mosh
```
第五客户端使用Mosh用与SSH一样的命令行连接到服务器。
```
jasonge@Juns-MacBook-Pro-2@l$ mosh junge@&lt;server-ip-or-name&gt;
```
下面这个录屏演示的是我日常工作中使用Mosh + Tmux的流程。期间我会断开无线网你可以看到Mosh自动连接上了就好像来没有断过一样。
<img src="https://static001.geekbang.org/resource/image/9b/69/9beb7e70191ccd475d530aa4a5654a69.gif" alt="">
## 小结
今天,我与你分享的是使用命令行工具工作时,涉及的环境配置问题。
首先我与你介绍了选择模拟终端、选择和配置Shell的重要准则并结合案例给出了具体的工具和配置方法其中涉及的工具包括iTerm2、Bash、Zsh、Fish等。然后我结合远程SSH这一常见工作场景给出了使用Tmux和Mosh的优化建议。
掌握了关于环境配置的这些内容以后,在下一篇文章中,我将与你介绍具体命令行工具的选择和使用。
其实,我推荐开发者多使用命令行工具,并不是因为它们看起来炫酷,而是它们确实可以帮助我们节省时间、提高个人的研发效能。而高效使用命令行工具的前提,是配置好环境。
以Mosh为例我最近经常会使用iPad SSH到远端服务器做一些开发工作。在这种移动开发的场景下iPad的网络经常断开每次重新连接开销太大基本上没办法工作。于是我最终发现了Mosh并针对开发场景进行了设置。现在每次我重新打开iPad的终端时远程连接自动恢复好像网络从没有断开过一样。这样一来我就可以在移动端高效地开发了。
而对研发团队来说,如果能够对命令行工作环境进行优化和统一,毫无疑问会节省个人选择和配置工具的时间,进而提升团队的研发效能。
## 思考题
你觉得Tmux和Screen的最大区别是什么是否有什么场景我们必须使用其中的一个吗
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,338 @@
<audio id="audio" title="28 | 从工作场景出发,寻找炫酷且有效的命令行工具" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/4e/e3/4e5e381a96163df2bba03076663bd7e3.mp3"></audio>
你好,我是葛俊。今天,我继续和你分享命令行工具的使用。
在上一篇文章中我与你介绍了命令行环境中的终端、Shell以及远程连接的设置解决了环境配置的问题。今天我们再来看看具体的命令行工具的使用技巧。我会根据常见的工作场景来组织这些工具因为优化工作流程、提高效率才是学习工具的真正目的。
从我的经验来看,开发人员最常见的、使用命令行的场景主要包括两个:
- 日常的操作,比如文件夹跳转、处理和搜索文件夹和文件内容、查看和管理系统信息;
- 开发中常见的工作比如Git的使用、API调试、查看日志和网络状况等。
我会重点与你分享这些场景中有哪些推荐工具以及使用技巧。而至于这些工具如何安装的内容,网络上已经有很多了,我就不再详细描述了。
## 日常操作中的工具和技巧
关于日常操作Linux/Unix系统已经自带了一些工具另外还有些产生已久、为我们所熟悉的工具。不过要更高效地完成工作我们还有更强大的工具可以选择。
### 第一个场景:列举文件夹和文件,查看文件
**列举文件**的默认工具是ls。除此之外一个常用的工具是tree可以列出文件夹的树形结构
<img src="https://static001.geekbang.org/resource/image/5c/3f/5cee9ed0d0fb2beb25961108acdf633f.png" alt="">
另外还有些比tree更方便的工具比如alder和exa。exa尤其好用优点包括
- 默认就有漂亮的颜色显示,并且不同种类文件颜色不同;
- 可以像ls一样显示当前文件夹也可以像tree一样显示树形结构。
<img src="https://static001.geekbang.org/resource/image/d6/3b/d667327bc4bedc74fb665a50d8aa423b.png" alt="">
另外exa还支持对文件状态显示的加强。
比如添加git选项exa会显示文件的git状态在文件名的左边用两个字母来表示文件在工作区和Git暂存区的状态。其中N表示新文件M表示文件修改等。exa的显示和git status命令输出的简单对比如下图所示。
<img src="https://static001.geekbang.org/resource/image/6a/a0/6a9969a87d53e4fc0a073f8918e72ba0.png" alt="">
再比如使用extend选项显示文件的额外信息。
<img src="https://static001.geekbang.org/resource/image/cb/92/cb7d9a0bb21f0b740f6eee812f582392.png" alt="">
再比如使用group-directories-first选项先显示文件夹再显示文件。
<img src="https://static001.geekbang.org/resource/image/47/37/47474d86ceb15eabbf6e6b8a0866a537.png" alt="">
至于**查看文件**Linux默认的工具是cat。相比起来bat是一个更好用的替代品除高亮显示外还可以显示Git的更改状态。
<img src="https://static001.geekbang.org/resource/image/f9/e4/f96d0ab6e8f12728c1d0e9ab2baa10e4.png" alt="">
### 第二个场景:查找并打开文件,进行查看和编辑
一个常用的办法是使用上面提到的工具来列出文件名然后使用grep进行过滤查看或者VIM进行查看和编辑。比如使用命令
```
tree -I &quot;node_modules&quot; -f | grep -C3 index.md
```
可以得到当前文件夹中的所有index.md文件。
命令中tree的参数-I表示排除文件夹node_modulestree的参数-f表示显示文件时包含文件路径方便你拷贝文件的全名grep的参数-C3代表显示搜索结果3行的上下文。
<img src="https://static001.geekbang.org/resource/image/e1/54/e1611236c70cacc9193420afcab76154.png" alt="">
我们也可以使用VIM代替grep进行更复杂的查找和编辑工作。我可以把tree的输出传给VIM然后在VIM中查找index.md使用n跳到下一个搜索结果使用VIM命令gF直接打开文件进行编辑后使用\bd命令关闭这个index.md文件。然后用同样的方式查找并编辑第二、三、四个index.md从而实现对当前文件夹下每一个index.md文件的查看和修改
<img src="https://static001.geekbang.org/resource/image/f4/f5/f409aa3e83ce0412b6275fd4c042eff5.gif" alt="">
事实上,这正是一个**很常见的命令行工作流**把某一个命令的输出传给VIM输出里包含有其他文件的完整路径比如上面例子中index.md的路径然后在VIM里使用gF命令查看并处理这些文件。我推荐你也尝试使用这种工作流。
另外在上面的例子中我使用tree命令来列举文件名。其实很多时候我们**使用find这种专门用来查找文件的命令会更加方便**。不过我今天要介绍的不是find而是它的一个替代品即fd。
<img src="https://static001.geekbang.org/resource/image/35/9d/354fc862f7145dd00f2ebda10500dc9d.png" alt="">
我推荐fd的原因主要有3个
- 语法比find简单
- fd默认会忽略.gitignore文件里指定的文件
- 忽略隐藏文件。
后两点对开发者来说非常方便。比如我在搜索时并不关心node_modules里面的文件也不关心.git文件夹里的文件fd可以自动帮我过滤掉。
另外fd高亮显示速度也很快。至于对查找到的文件进行编辑跟上面提到的方法一样用管道Pipe传给VIM然后使用gF命令即可。
```
fd index.md | vim -
```
另外关于查找文件内容的工具grep我常用的一个替代品是RipGreprg。跟fd类似它也很适合开发者有如下4个特点
- 默认忽略.gitignore文件里指定的文件
- 默认忽略隐藏文件;
- 默认递归搜索所有子目录;
- 可以指定文件类型。
比如使用rg tags就可以方便地查找当前目录下所有包含tags的文件。它的查找速度非常快显示也比grep要漂亮
```
&gt; rg tags
package-lock.json
2467: &quot;common-tags&quot;: {
2469: &quot;resolved&quot;: &quot;https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz&quot;,
5306: &quot;common-tags&quot;: &quot;^1.4.0&quot;,
5446: &quot;common-tags&quot;: &quot;^1.4.0&quot;,
src/pages/2019-05-24-procrastination/index.md
6:tags: ['自我成长', '拖延症']
src/pages/2018-07-21-first-post/index.md
5:tags: ['this', 'that']
```
### 第三个场景:文件夹之间跳转
关于文件夹间的跳转在Bash中有cd和dirs命令在Zsh和Fish中可以使用文件夹名字直接跳转另外Zsh支持`..``...``-`等别名用来分别跳转到父目录、父目录的父目录以及目录历史中上一次记录而不需要写cd。
接下来,我与你介绍**几个新的工具来支持更快的跳转**。
实际上文件夹的跳转,有两种常见的情况:
- 一种是,快速跳转到文件夹跳转历史中的某条记录,即之前曾经去过的某个文件夹;
- 另一种是,快速找到当前文件夹中的某个子文件夹,并跳转过去。
对于第一种情况常用的工具有两个一个是fasd另一个是z它们差别不是特别大。我用的是z具体用法是z会按照访问频率列出最近访问过的文件夹并使用字符串匹配的方式让你实现快速跳转。
比如用z dem<tab>来进行匹配和自动补全找到我想去的demo文件夹回车直接完成跳转。同时我也可以用z dem&lt;回车&gt; 直接跳转。</tab>
<img src="https://static001.geekbang.org/resource/image/65/3e/65630c3d48a262195d63fad17e4e133e.gif" alt="">
对于第二种情况,即快速定位某个子文件夹,我介绍一个**超级酷的工具fzf**。本质上讲fzf是一个对输入进行交互的模糊查询工具。它的使用场景非常多文件夹的跳转只是一个应用。所以我还在再后面文章做更多的详细讨论。
安装好fzf之后你就可以使用Ctrl+T进行文件夹的交互式查询或者使用Alt+C进行文件夹跳转。
比如我想跳转到src/component文件夹中可以输入Alt+Cfzf就会列出当前文件夹下的所有文件夹。比如我输入com没输入其他字符fzf会更新匹配到的文件夹这时可以使用Ctrl+P、Ctrl+N进行上下选择按下回车就可以进入选中的文件夹。
<img src="https://static001.geekbang.org/resource/image/f3/cd/f3892b1c26be577cdf7b3993242830cd.gif" alt="">
<img src="https://static001.geekbang.org/resource/image/af/cd/af4e2d8ac7c21c48aaeb0c9c55dd4bcd.gif" alt="">
### 第四个场景:文件管理
系统自带的文件管理工具有cp、mv、rsync等。这里我再介绍一些更方便的工具。
首先是一个用来**重命名文件的小工具叫作vidir**。顾名思义vidir就是VI来编辑目录的。具体使用方法很简单vidir命令后面接一个文件夹时vidir会打开VIMVIM里面列举该文件夹中所包含的文件和子文件夹然后使用VIM的命令来修改文件和文件夹的名字之后保存退出。这时vidir会自动帮助我们完成对文件和文件夹的重命名。
vidir之所以使用VIM来修改文件是因为VIM功能强大修改非常方便。另外vidir也可以从管道接收文件夹和文件的列表。比如我想把当前文件夹下所有前缀为index的文件都在文件名前添加“new-”。这时我可以使用命令fd | vidir -。
这样fd命令会把当前文件夹下所有文件名传给vidir。然后vidir打开VIM我们在VIM界面中修改文件名即可。如下所示的录屏图片中包括了使用VIM的重复命令.的技巧。
<img src="https://static001.geekbang.org/resource/image/21/2d/21ef569cff63ed639fed4a00968d6d2d.gif" alt="">
另外一组方便进行文件管理的工具是,**命令行的文件管理器**即使用键盘命令在终端界面进行文件夹跳转、查看文件和移动文件等操作。这种命令行界面上的UI叫做TUITerminal UI。十多年前的Borland终端IDE就是这一类工具的翘楚使用熟练之后效率会很高。
TUI的文件管理器我用过3个Midnight Commander (以下简称mc)、Ranger和nnn。
mc是两个窗口的文件管理器。如果你使用过Windows CommanderTotal Commander的话你就会对它的用法很熟悉。重要的命令有使用tab进行两个窗口的切换、使用F4进行编辑、使用F5进行拷贝、使用F9进入菜单、使用F10退出。
我提供了一张录屏图片简单演示了在一台远端服务器上使用mc进行多文件拷贝和编辑并通过菜单修改显示主题的场景。
<img src="https://static001.geekbang.org/resource/image/bb/3a/bb49eec06b2b1293e1d2c0620c81fe3a.gif" alt="">
Ranger和nnn是单窗口的文件管理器。Ranger稍微有一点延迟所以我一般使用nnn。因为是单窗口所以与我们平时在GUI中使用的文件管理器比较相似。
比如在拷贝文件的时候需要先进入文件所在文件夹选择文件然后进入目标文件夹再使用拷贝命令把文件拷贝过去。我在录屏中演示了在nnn中进行文件夹的跳转、创建文件的选择、拷贝使用系统工具打开当前文件查看帮助等功能。
<img src="https://static001.geekbang.org/resource/image/26/48/261bf3fae35b5899ca66aa5e44a39648.gif" alt="">
总的来说这3个工具中我使用最多的是nnn。跟mc相比它最大的好处是快捷键设置跟VIM一致不需要大量使用功能键F1~F12。
## 开发中常见的工作
### Git
命令行中的Git工具除了原生的Git之外常见的还有tig、grv、lazygit和gitin。
我常用的是tig。因为在tig中我可以方便地进行查看改动、产生提交、查看历史blame等操作功能非常强大。比如在查看文件改动时我们可以方便地使用命令1有选择性地把一个文件中改动的一部分添加到一个提交当中实现[第26篇文章](https://time.geekbang.org/column/article/154378)中提到的git add -p的功能。
另外我还可以通过tig快捷地查看一个文件的历史信息。
关于这两个功能的使用,你可以参考下面的录屏图片。
<img src="https://static001.geekbang.org/resource/image/70/d0/7007909425230ece1fac2726d610d0d0.gif" alt="">
### Web 访问
我常用的Web访问工具是HTTPie是curl命令的一个补充。HTTPie的强项在于专门针对HTTP协议所以可以做到格式简单、易用性强两点。
而curl的优势则包括功能强大、支持多种协议和基本所有服务器上都有预装。
关于这两个工具我的建议是curl肯定要学HTTPie如果用得到也值得花时间学习。
### 对JSON进行处理
在命令行对JSON文本进行处理最常见的工具是jq。它能够对JSON进行查询和修改处理功能很强大。
举一个查询的例子我们有这样一个person.json文件列举某个人的详细信息
```
$ cat person.json
{ &quot;id&quot;: { &quot;bioguide&quot;: &quot;E000295&quot;, &quot;thomas&quot;: &quot;02283&quot;, &quot;fec&quot;: [ &quot;S4IA00129&quot; ], &quot;govtrack&quot;: 412667, &quot;opensecrets&quot;: &quot;N00035483&quot;, &quot;lis&quot;: &quot;S376&quot; }, &quot;name&quot;: { &quot;first&quot;: &quot;Joni&quot;, &quot;last&quot;: &quot;Ernst&quot;, &quot;official_full&quot;: &quot;Joni Ernst&quot; }, &quot;bio&quot;: { &quot;gender&quot;: &quot;F&quot;, &quot;birthday&quot;: &quot;1970-07-01&quot; }, &quot;terms&quot;: [ { &quot;type&quot;: &quot;sen&quot;, &quot;start&quot;: &quot;2015-01-06&quot;, &quot;end&quot;: &quot;2021-01-03&quot;, &quot;state&quot;: &quot;IA&quot;, &quot;class&quot;: 2, &quot;state_rank&quot;: &quot;junior&quot;, &quot;party&quot;: &quot;Republican&quot;, &quot;url&quot;: &quot;http://www.ernst.senate.gov&quot;, &quot;address&quot;: &quot;825 B&amp;C Hart Senate Office Building Washington DC 20510&quot;, &quot;office&quot;: &quot;825 B&amp;c Hart Senate Office Building&quot;, &quot;phone&quot;: &quot;202-224-3254&quot; } ] }
```
可以方便地使用cat person.json | jq .”对JSON进行格式化输出
```
$ cat people.json | jq .
{
&quot;id&quot;: {
&quot;bioguide&quot;: &quot;E000295&quot;,
&quot;thomas&quot;: &quot;02283&quot;,
&quot;fec&quot;: [
&quot;S4IA00129&quot;
],
&quot;govtrack&quot;: 412667,
&quot;opensecrets&quot;: &quot;N00035483&quot;,
&quot;lis&quot;: &quot;S376&quot;
},
&quot;name&quot;: {
&quot;first&quot;: &quot;Joni&quot;,
&quot;last&quot;: &quot;Ernst&quot;,
&quot;official_full&quot;: &quot;Joni Ernst&quot;
},
&quot;bio&quot;: {
&quot;gender&quot;: &quot;F&quot;,
&quot;birthday&quot;: &quot;1970-07-01&quot;
},
&quot;terms&quot;: [
{
&quot;type&quot;: &quot;sen&quot;,
&quot;start&quot;: &quot;2015-01-06&quot;,
&quot;end&quot;: &quot;2021-01-03&quot;,
&quot;state&quot;: &quot;IA&quot;,
&quot;class&quot;: 2,
&quot;state_rank&quot;: &quot;junior&quot;,
&quot;party&quot;: &quot;Republican&quot;,
&quot;url&quot;: &quot;http://www.ernst.senate.gov&quot;,
&quot;address&quot;: &quot;825 B&amp;C Hart Senate Office Building Washington DC 20510&quot;,
&quot;office&quot;: &quot;825 B&amp;c Hart Senate Office Building&quot;,
&quot;phone&quot;: &quot;202-224-3254&quot;
}
]
}
```
以及使用jq ".terms[0].office"命令查询他的第一个工作任期的办公室地址。
```
$ cat person.json | jq &quot;.terms[0].office&quot;
&quot;825 B&amp;c Hart Senate Office Building&quot;
```
jq存在的最大问题是它有一套自己的查询处理语言。如果使用jq的频次没那么高的话很难记住每次都要去查帮助才可以。
针对这种情况有人设计了另一种类似的工具直接使用JavaScript作为查询处理语言典型代表是fx和jq.node。这就大大方便了使用JavaScript的开发者因为可以使用已经熟悉了的语法。
比如对于上个案例的JSON文件我可以方便地在fx工具中使用JavaScript的函数filter()进行过滤查询。
```
$ cat person-raw.json| fx 'json =&gt; json.terms.filter(x =&gt; x.type == &quot;top&quot;)'
[
{
&quot;type&quot;: &quot;top&quot;,
&quot;office&quot;: &quot;333 B&amp;c Hart CIrcle Building&quot;,
&quot;phone&quot;: &quot;202-224-3254&quot;
}
]
```
### 查找、关闭进程
通常情况下我们使用kill和pkill来查找和关闭进程。但使用fzf之后我们可以方便地进行交互式的查找目标进程。具体使用方法是输入kill <tab>fzf就会提供一个交互式的界面供你查找目标进程然后回车确认即可。</tab>
在命令行上进行交互式的操作,非常爽,我推荐你一定要试试。
<img src="https://static001.geekbang.org/resource/image/7d/0c/7de54d49ff68186ee6002bc842e95d0c.gif" alt="">
### 查看日志文件
关于查看日志文件的工具我推荐lnav。它比tail -F要方便、强大得多有很多很棒的功能包括
- 支持很多日志格式比如syslog、sudo、uWSGI等并可以根据格式高亮显示
- 支持多个日志同时显示,并用不同颜色区分;
- 支持正则表达式进行过滤等。
<img src="https://static001.geekbang.org/resource/image/52/86/52caa8ef22d6b8b82ce721588a9ac886.png" alt="">
### 命令行本身的实用技巧
关于命令行的使用技巧,有两个非常值得一提:一个是!$,另一个是!!。
**第一个!$**,代表上一个命令行的最后一个参数。比如,如果我上一条命令使用
```
$ vim src/component/README.txt
```
下一步我想为它产生一个备份文件,就可以使用!$
```
## 以下命令即 copy vim src/component/README.txt vim src/component/README.txt.bak
$ cp !$ !$.bak
```
**第二个常用的是!!**表示上一个命令的完整命令。最常用的场景是我先拷贝一个文件发现没有权限需要sudo下一步我就可以用sudo !!来用sudo再次运行拷贝命令。
```
## 因为权限不足,命令失败
$ cp newtool /usr/local/bin/
## 重新使用sudo运行上一条命令。即 sudo wtool /usr/local/bin/
$ sudo !!
```
## 小结
今天,我与你介绍了很多工具。使用工具提高研发效能,最关键的是找到真正常用的工作场景,然后去寻找对应的工具来提高效率。
需要注意的是,只有重复性高的工作,才最适合使用命令行工具;否则,用来适应工具的时间,可能比节省下的时间还要多。这,是命令行的一个基本特点。
最后我把今天与你讨论的Linux/Unix系统自带工具和替代工具整理为了一张表格以方便你复习
<img src="https://static001.geekbang.org/resource/image/46/80/4649bffbfd17cb90d217a25d0382f980.jpg" alt="">
## 思考题
不知道你有没有注意到在录屏中我多次用到了一个叫作tldr的工具。你知道它是什么作用吗
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,250 @@
<audio id="audio" title="29 | 1+1>2灵活的工具组合及环境让你的工作效率翻倍" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/d6/33/d6814fc2f97bc90572b604611e2aca33.mp3"></audio>
你好,我是葛俊。今天,我们来聊一聊工具的组合和环境。
在前面几篇文章,我与你介绍了很多工具,有开发工具,也有跟开发不直接相关的工具。毫无疑问,这些工具都很好用。但,如何配置这些工具,才能真正发挥它们的价值,提高我们的研发效能呢?
我们都很清楚,工具的使用离不开具体的工作环境。如果我们在环境中使用的各个工具是割裂开的话,不仅会提高我们的学习成本、记忆成本,还会有工具间交互的衔接问题。所以,用好这些工具,我们还需要做两件事:
- 尽量把工具无缝集成,解决工具切换不顺畅的问题;
- 减少并优化常用的工作入口,从而提高工具一致性,降低使用多个工具时的心智负担。
只有这样我们才能把工具配置成一套好的环境真正聚焦在产生价值的工作上发挥工具提升研发效能的作用实现1+1&gt;2的效果。
所以接下来,我会从工具集成和提高工具一致性两个方面,与你介绍如何把多个工具组合成为高效的工具环境。
## 工具的集成
工具的集成最值得优化的情况包括两种一是使用管道Pipe对命令行工具进行集成二是对集成开发工具环境IDE进行配置让IDE和周边工具集成。
### 使用管道Pipe对命令行工具进行集成
其实,我在前面的文章中已经使用过管道很多次了,只是使用场景比较简单而已。
比如,在[第24篇文章](https://time.geekbang.org/column/article/150779)中我使用命令curl -s &lt;github_url&gt; | vim -来查看GitHub上某个用户的代码仓情况通过管道把curl命令的输出传给VIM以方便我在VIM中查看较长的输出。
```
&gt; curl -s https://api.github.com/users/jungejason/repos | vim -
Vim: Reading from stdin...
## 进入VIM窗口显示所有repo的array
[
{
&quot;id&quot;: 213849635,
&quot;node_id&quot;: &quot;MDEwOlJlcG9zaXRvcnkyMTM4NDk2MzU=&quot;,
&quot;name&quot;: &quot;counter-redux-sample&quot;,
&quot;full_name&quot;: &quot;jungejason/counter-redux-sample&quot;,
&quot;private&quot;: false,
&quot;owner&quot;: {
&quot;login&quot;: &quot;jungejason&quot;,
...
```
这个案例使用了一次管道,比较简单。
而高效使用管道,首先是**使用多个管道灵活地把多个工具连接起来**。一个常见的场景是grep和xargs命令的组合。比如ps aux | grep vim | grep -v grep | awk {print $2} 可以找到我当前运行的VIM程序并将其关闭。
```
ps aux | grep vim | grep -v grep | awk '{print $2}' | xargs kill
```
整个管道链是这样工作的:
- ps aux打印所有运行程序每行打印一个。
- grep vim显示包含vim的那些行。
- grep -v grep表示过滤掉包含grep的那一行。这是因为上面一步grep vim命令中包含“vim”字样所以它也会被ps aux显示出来我们需要将其过滤掉。
- awk {print $2} 表示抽取出输出中的PID也就是进程ID。
- xargs kill 接收上一步传过来的PID组成命令kill <pid>并执行杀死vim进程。</pid>
上面管道每一步的执行输出,如下所示:
```
&gt; ps aux
USER PID %CPU %MEM VSZ RSS TT STAT STARTED TIME COMMAND
_windowserver 228 14.9 2.3 13044220 382952 ?? Ss 9Oct19 679:18.68 /System/Library/PrivateFrameworks/SkyLight.framewo
jasonge 49393 14.4 2.1 5586816 356860 ?? S Sun12AM 583:37.42 /Applications/XMind.localized/XMind.app/Contents/F
jasonge 49371 9.7 0.4 4826572 64204 ?? S Sat11PM 167:30.01 /Applications/XMind.localized/XMind.app/Contents/F
...
&gt; ps aux | grep vim
jasonge 31383 0.0 0.0 4310340 4740 s001 S+ 9:57AM 0:00.11 vim
jasonge 32304 0.0 0.0 4268056 704 s004 R+ 10:17AM 0:00.00 grep vim
&gt; ps aux | grep vim | grep -v grep
jasonge 31383 0.0 0.0 4310340 4740 s001 S+ 9:57AM 0:00.11 vim
&gt; ps aux | grep vim | grep -v grep | awk '{print $2}'
31383
&gt; ps aux | grep vim | grep -v grep | awk '{print $2}' | xargs kill
```
这是使用多个管道的一个具体例子。在工作中,你可以根据实际情况,使用管道来封装常用的工具。
**在命令行中高效使用管道的第二个重要方法是使用模糊查询工具Fuzzy Finder)。**
常见的模糊查询工具,有 fzf、pick、selecta、ctrlp和fzy等。其中最有名的应该就是fzf你可以再回顾下[第28篇文章](https://time.geekbang.org/column/article/156024)中关于这个工具的介绍)。
在管道中使用模糊查询工具,可以大幅度提高使用体验。
在开发工作中我们常常需要对文本进行搜索和过滤一般会使用grep来实现。这个操作虽然简单但频率非常高很值得优化。比如上面提到的杀死vim进程的操作。我们使用ps aux命令列举出所有进程之后需要使用多个grep命令来进行搜索过滤最终查找到我们需要的那一行。
在这个操作中grep过滤命令常常会比较复杂为了达到正确的过滤效果我们可能要调试多次才能找到正确的grep命令。另外还可能会出现比较难以使用grep过滤的情况比如有多个vim进程同时打开时使用grep选出其中一个就不是很方便。
这时,使用模糊查询工具就很方便了。在我看来,**模糊查询工具的本质,就是交互式的文本过滤工具**。它接收文本,然后提供界面让用户输入查询条件,并在用户输入的同时实时过滤。当用户找到需要的结果回车确认后,输出结果文本,使用起来很自然。
以杀死vim进程的操作为例我可以使用ps aux | fzf命令把进程列表发送给fzffzf首先会列举出所有进程供我搜索过滤。随着我的输入fzf进行实时过滤去除不符合条件的那些行。在我输入了vim后就只剩下几行结果了。这时我可以使用上下键进行选择回车之后fzf就会把这一行内容输出到stdout中。
可以看到整个过程中我可以实时调整搜索条件免去了使用grep需要多次调整参数的过程使用起来非常便捷。
<img src="https://static001.geekbang.org/resource/image/15/2b/158b70bfdde5ab3b1b940b1abe21932b.gif" alt="">
当然除了把过滤结果输出到stdout外也可以通过管道把输出传给其他工具比如传给awk和kill来实现杀死vim进程的目的具体的命令是ps aux | fzf | awk {print $2} | xargs kill。
<img src="https://static001.geekbang.org/resource/image/56/6a/561acdf6734212a36edd91295a00876a.gif" alt="">
可以看到这个命令和前面使用grep的命令差不多唯一区别是把之前命令中的两个grep替换成了fzf。但正是这个区别把中间的非交互文本过滤变成了交互式过滤效果非常好。
实际上你还可以更进一步把这个使用fzf的命令保存为一个shell脚本成为一个交互式的kill命令。如下所示
```
#!/bin/bash
ps aux | eval &quot;fzf&quot; | awk '{print $2}' | xargs kill
```
<img src="https://static001.geekbang.org/resource/image/59/da/59ce8d67bffa2e15e68665399511bdda.gif" alt="">
从非交互文本过滤到交互文本过滤的简单转变,着实可以为我们带来意想不到的巨大方便。比如:
- 在切换工作路径时可以用find命令列举出当前文件夹的所有子文件夹然后进行交互式的过滤最后切换路径。
- 在使用apt或者brew安装软件包时可以用来过滤可用软件包方便软件包的选择。
它甚至可以与一些网站服务对接,比如这个使用[fzf和caniuse服务集成](https://sidneyliebrand.io/blog/combining-caniuse-with-fzf)的例子,就很酷。此外,[fzf官网上](https://github.com/junegunn/fzf/wiki/examples)还有很多类似的脚本例子,推荐你去看看。
### IDE和周边工具集成
我们的目标是能在IDE中进行常见的软件研发活动从而减少工具的切换。
具体来说基本的IDE集成功能包括
- 编码;
- 构建;
- 实时检查语法错误;
- 实时检查编码规格;
- 运行单元测试,并在测试输出中点击文件名进行跳转;
- 在本地运行服务,并可以设置断点进行单步调试;
- 连接远程服务进行单步调试。
这些功能比较常见你可以根据自己使用的IDE在网上搜索相关资料我就不再赘述了。接下来我会重点与你分享些不是那么传统但非常有效的集成功能。
**首先是IDE跟命令行工具的集成。**
命令行里有很多的工具所以IDE只要集成了命令行终端就可以把它们一次性都集成了进来所以效果非常好。
这里的集成有两个层次:
- 第一个层次是终端窗口成了IDE应用的一个子窗口这样使用比独立的终端窗口使用起来更方便。比如可以使用快捷键方便地打开、关闭集成终端窗口。方便窗口管理。
- 第二个层次是终端子窗口和IDE其他部分的一些交互。这个层次更深也更重要可以让我们更好地在IDE中使用命令行工具是我们要留意的。我**推荐你在你所用的IDE文档中查看有哪些第二个层次的集成**。
以VS Code为例属于第二层次的集成有以下四种。
**第一种**在IDE中拖动文件夹或者文件到集成终端窗口中文件夹或文件的完整路径名会自动复制到终端里。
<img src="https://static001.geekbang.org/resource/image/ba/a7/bac1f0bfbf2b446c164bcc65e4877ea7.gif" alt="">
第二种在集成终端中运行当前编辑窗口中打开的文件也可以在编辑窗口中选中一段文本直接在终端运行。方法是运行命令Terminal: Run Active File in Active Terminal或者是Terminal: Run Selected Text in Active Terminal。
<img src="https://static001.geekbang.org/resource/image/ff/26/ff8f05f311750112deb548266f452d26.gif" alt="">
第三种集成终端中的命令输出如果包含文件名可以用Cmd+鼠标点击直接在编辑器中打开这个文件。这个操作我经常用比如搜索文件时因为我对fd和rg命令很熟悉所以有时会直接使用fd + rg的组合而不是用VS Code的原生文件查找功能了。
<img src="https://static001.geekbang.org/resource/image/bd/67/bdf559052af53c995433c487ed0f2767.gif" alt="">
第四种可以安装针对VS Code的命令行工具。比如使用了[oh-my-zsh中的vscode插件](https://github.com/robbyrussell/oh-my-zsh/tree/master/plugins/vscode)后我们可以在集成终端中使用vscr在当前VS Code中打开文件用vscd进行文件比较等工作实现了终端和IDE的更紧密集成。
<img src="https://static001.geekbang.org/resource/image/0d/25/0da8a4c117fe709fc7462134a0171725.gif" alt="">
**其次是IDE和代码仓的集成包括Gist、GitHub Pull、Git Graph。**
在IDE中进行代码仓的操作集成我个人是比较谨慎的态度。我会比较多地使用命令行的Git因为我认为目前还没有GUI工具能暴露Git的全面功能。在IDE中我只选择最适合在图形界面中使用的一些场景。
比如在VS Code中我只使用了3个插件
- [Gist](https://marketplace.visualstudio.com/items?itemName=kenhowardpdx.vscode-gist),方便在编辑器中直接上传文件。
- [GitHub Pull Request](https://marketplace.visualstudio.com/items?itemName=GitHub.vscode-pull-request-github)方便管理GitHub上的PR处理可以直接在VSCode里查看和进行讨论非常方便。
- [Git Graph](https://marketplace.visualstudio.com/items?itemName=mhutchie.git-graph),用于查看历史。它可以显示提交历史的图结构,点击提交可以直接查看文件,比命令行工具更快捷。
如果你想使用这几个插件,可以参考它们的官方文档。
以上就是工具集成方面的内容,接下来我们来看看如何提高工具一致性。
## 提高工具一致性
提高工具的一致性,主要方法是减少常用的工作入口,并对这些入口进行优化。
工具太多容易混乱,但如果能控制入口数量,也就是说进行工作时,只从几个有限的入口开始操作,同时对这些入口进行优化,就能提升工具使用体验的一致性,降低使用工具带来的负担。
要减少并优化常用的工作入口,那我们首先需要明确**经常使用的、必要的工作入口**。
在我看来, 命令行、IDE、桌面快捷启动工具Launcher和浏览器这4个工作入口非常必要是值得重视并优化的。
### 1. 命令行
我比较喜欢命令行的一个重要原因是它是一系列工具的共同入口。绝大部分命令行的命令都遵循一定的规律又有统一的man命令查看手册很容易找到使用方法。
使用命令行减少工作入口还有一个办法是使用统一的客户端工具进行多种操作。比如Git命令带有很多子命令比如log、diff、show等从而一个Git命令行工具可以完成很多工作。
在Facebook很多工作都通过ArcanistPhabricator 的命令行工具)的各种子命令来完成。这样就实现了命令使用的一致性,降低了学习成本。
在日常工作中你也可以尝试开发一个命令行工具对常用工作进行封装。之前我在Stand的时候就使用Node.js的[c](https://www.npmjs.com/package/commander)[ommander](https://www.npmjs.com/package/commander)模块,对日常研发工作进行了封装。
### 2. IDE
上面已经提过IDE是一个重要入口我们应该把较多的开发工作集中到IDE中直接进行。
### 3. 桌面快捷启动工具
桌面快捷启动工具也是一个非常重要的入口我们可以通过它进行很多工作。以Mac上非常有名的Alfred来说你可以用它来启动程序、切换程序、搜索文件、进行网页搜索、管理系统剪贴板历史、进行计算、运行系统命令比如重启机器、锁定机器、关闭特定程序、关闭所有程序等等操作。
用好这个工具,可以大大提高工具使用的一致性。
### 4. 浏览器
现在我们会大量使用网站应用那浏览器是必不可少的入口。你可能会觉得在浏览器中不就是使用URL吗还能怎么提高呢
这里,我就和你分享几种方法吧。
其实,我们比较熟悉的书签功能,就是对在浏览器中使用网页应用的一个优化。因为它可以记录常用的工具,你不用每次都输入。
此外,还有一种你可能不太熟悉但非常有效的办法,就是自己运行一个搜索引擎,并把它设为浏览器的默认搜索。这样,你就可以在你的搜索引擎中定义规则,并在浏览器的地址栏输入符合规则的操作。
比如你可以在搜索引擎后端中实现对“t <task-id>”的解析用来打开任务系统中序号为task-id的任务。当你在浏览器地址栏中输入“t 123"的时候搜索引擎自动解析出任务ID 123然后跳转到这个任务的URL比如http://tasktool/123.html。当然这个任务工具可能是Trello也可能是Jira。</task-id>
在Facebook内部就有一个这样的公用引擎支持快捷跳转到公司几乎所有的网站中同时还可以使用它直接进行各种搜索工作比如内部网页搜索、wiki搜索等极大提高了公司各种网站应用使用时的一致性。Facebook开源了这个搜索引擎框架叫作[Bunny1](https://github.com/ccheever/bunny1/)。
事实上,这种浏览器使用方式相当于把浏览器的地址栏变成了一个命令行入口。因为搜索引擎后端就是一个通用的后端服务,可以实现各种各样的强大功能,所以你可以自己做一个这样的搜索引擎,在浏览器地址栏里充分发挥想象力。
## 小结
只有把工具放到具体的工作环境中,才能发挥它们的真实价值。
今天,我从两个方面与你介绍了怎样把多个工具配置成为高效的工具环境。一是,如何对工具进行集成,减少工具切换时的不顺畅,提高个人的研发效能。二是,如何减少并优化工具的入口,从而提高工具使用体验的一致性,以降低工具使用成本,提高研发效能。
其实工具环境的配置是一个需要不断摸索去寻找最佳平衡点的过程。比如在IDE中进行集成应该集成最常用的功能而不应该尝试把所有的功能都使用插件集成到IDE中。否则IDE就会变得臃肿不堪性能下降。
我推荐的办法是采用80/20法则。也就是集中精力对工作中最常用的20%的操作,进行集成和优化。从我个人的体验来看,这样的平衡点是比较合适的。
总之,我们时刻要牢记,配置环境的目的是,配置出一个强大而灵活的环境,让一切工作流畅,从而提高生产效率,一定要注意投入产出比。
## 思考题
在浏览器作为工作入口的例子中,如果一个任务描述搜索到的任务不止一条时,我们可以怎么处理呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,80 @@
<audio id="audio" title="30 | 答疑篇:关于价值导向和沟通" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/2b/1d/2b979dff7ac74c92c0b4fd160b488c1d.mp3"></audio>
你好,我是葛俊。今天,我来针对“个人效能”模块的留言问题,和你做一次展开吧。
在“个人效能”模块的文章中,很多同学留下了精彩留言,阐述了自己对个人效能的认识、遇到的问题以及解决方案。看得出来,大家平时都对提高个人效能有着比较深入的思考。
其中,尤其让我印象深刻的有@我来也 对各种命令行工具的讨论、@Johnson 对编辑器的讨论,以及@Jxin 对价值和目标的思考,真的是非常精彩。
谢谢你们的留言!我很喜欢与你们交流的过程,所以会详细回答你们的每个问题。今天,我从留言的许多问题中,梳理出了价值导向和高效沟通这两个话题,再与你详细聊一聊。
## 工作中的价值导向
在第21篇文章“[高效工作Facebook的10x程序员效率心法](https://time.geekbang.org/column/article/148170)”中,@Jxin同学与我展开了关于价值取向的一些讨论。从这个专栏的一开始,我就强调了目标的重要性,**研发效能三要素中的第一条,就是准确性**。我们只有先找对目标,后续的行动才会有意义。
所以,在工作中,我们需要考虑以什么目标作为最根本的出发点,是职业发展、薪酬、工作生活平衡、工作稳定性,还是实现价值?具体怎么选择,就要看你个人认可的价值到底是什么了。
比如,@Jxin提出以实现价值为出发点,尽量把企业价值和个人价值的发展摆到一条线,能够做兼顾两者利益的任务。如果实在不能统一,就要多考虑些公司的发展,少考虑些个人短期利益的追求。对此,我很是赞同。
又比如,十多年前我在西雅图微软工作的时候,有一个朋友特别看重工作生活平衡和工作稳定性。刚毕业,她就在寻找一个有稳定工作机会的方向,后来选定了数据库方向。她认为这个方面的工作会长期稳定,于是在这个方向做到专精。事实证明,她的选择到现在看还是很不错。不用学习太多的新东西,仍然能够有稳定的工作。
虽然我个人更看重不断接触新领域,不断提高、发展自己,和她的价值取向不太一致,但我觉得她的做法也非常棒。她能明确自己想要追求的东西,于人于己都有好处,还能找到具体的实施路径,非常棒!
所以我建议,**平时多考虑下自己最在意的到底是什么**。这样工作起来才能有的放矢,把时间花在对自己最有价值的事情上。
有些时候找到自己最在意的事儿并不容易将其实现则常常更有挑战性。所以我再向你推荐一个提高实现目标效率和过滤不现实目标的方法WOOP。WOOP分别是Wish愿望、Outcome效果、Obstacle障碍和Planning计划四个单词的首字母。
**使用WOOP的具体方法是**抽出你不会被干扰的5分钟时间针对WOOP的4个组成部分进行冥想。
第一步是Wish也就是认真考虑我到底希望得到什么希望实现什么这里的“希望”既可以是长期的希望比如5年、10年也可以是短期的希望比如一个月或者就是今天。专注于一个愿望并用SMART原则来具体描述它。SMART是一个目标管理工具我在下一篇文章中会详细展开你可以先参考下“[SMART 原则以及实际案例](https://blog.csdn.net/limuzi13/article/details/52245983)”这篇文章。
第二步是Outcome也就是愿望实现之后能够达到哪些好结果。在大脑中形象地描绘并体验实现了这个愿望之后我的生活能有哪些正向改变。有研究证明这种形象化考虑正面结果的办法可以大幅提高后续执行计划的积极性和效率。
第三步是Obstacle也就是实现这个愿望时可能有哪些障碍。在这一步也要形象地描绘2~3个最主要障碍的具体细节。静下心来思考的时候常常会发现平时的一些习惯其实是实现这些愿望的重要障碍。这一步可以让我们在后续执行计划时更敏锐地觉察到障碍的出现从而更好地处理它们。
第四步是Planning也就是针对每一个具体可能出现的障碍计划用什么办法去解决它。我们可以使用if-then的结构来处理即if出现障碍Athen使用计划B来解决。同样地我们要具象化这些计划才能在后续工作时迅速反应使用事先计划好的最佳方法去克服障碍。
在我看来,**使用WOOP方法主要有两大好处**
- 让我们对自己的目标更清晰。通过WOOP中的具体思考我们能对愿望有更清晰的认识加深对自己的了解。在第四步计划的时候我们可能会发现有的障碍没办法克服。这时我们就要果断放弃这个愿望。这样WOOP能帮助我们过滤不现实的愿望避免浪费时间。
- 提高实现目标的效率。通过具体的这四个步骤,我们会对自己的愿望更有激情,对实施过程中可能出现的困难也更有预期,并且能够更有准备地去解决困难。
在我看来WOOP的效果非常好。在不被干扰的时候花五分钟进行一次WOOP思考对理清自己的目标、清除干扰非常有效推荐你也试试。另外在Coursera上有一个很棒的视频是[对WOOP方法创始人Gabriele Oettingen的访谈](https://www.coursera.org/learn/the-science-of-well-being/lecture/idieS/interview-with-gabriele-oettingen),也推荐你看一下。
## 沟通
在第22篇文章“[深度工作:聚焦最有价值的事儿](https://time.geekbang.org/column/article/149479)”中,@Landy提出了一个关于沟通建议的问题
沟通的确是高效工作的重要软技能,也是这些年来我一直在努力提高的方向。接下来,我就与你分享**3个高效沟通的原则**吧。
**第一个原则是,沟通时要有同理心。**我们沟通的目的,是在对方身上起作用,要从别人的角度考虑怎样去沟通。所以,我们需要了解以下三个方面的信息:
- 一是,对方的知识背景,从而使用他能理解的语言去沟通;
- 二是,对方想要知道什么,才能不绕弯路高效答复;
- 三是,对方的出发点,根据他的出发点将对话引向双赢的方向。
**第二个原则是,外在表现很关键。**中国的文化比较内敛,我们从小受到的教育也强调内在美,忽视外表,这就会引导我们形成忽视外在表现的习惯。因此,我在美国工作时吃了不少亏,做出的东西,却被会表现的人抢走了大部分功劳。这才逐渐认识到,这种“内敛”实际上是不对的,很多情况下外在表现更重要。
比如你的内心对一个人非常好但是你的脾气很暴躁总对TA发脾气做出对TA不好的举动。那你对这个人到底是好还是不好呢
我认为你的实际行为、外在表现对这个人造成了真实伤害你内心的好并没有实际作用。所以你对TA是不好的。
外在表现很关键这个原则,在开发工作上表现为我们不但要重视实际的工作,也要重视别人对我们工作成果的感知。**很多开发者从内心抵触PPT我觉得这就是高效工作的一个重要阻碍。**我们确实需要花一些精力,去考虑如何把我们做出的东西更好地呈现出来。当然,我并不是说,要来虚的,具体做出东西才是王道,是前提。
**第三个原则是,冰山原则,**是美国软件界大佬Joel Spolsky周思博提出来的。
这个原则对软件技术人员和非技术人员间的交流非常有用,指的是一个软件应用系统比较复杂,就像一座冰山。但是,它表现在外面的只是一小部分,就像冰山露出水面的部分一样。如果你沟通的对象对软件不熟悉的话,他就会认为,冰山上的可见部分就是全部工作,或者说是绝大部分的工作。
这样的结果就是软件开发人员在做项目进展演示的时候如果演示得很完整、漂亮对方就会认为你的工作做得差不多了即使你已经提前强调过这只是在界面上做的一个Demo也不会有效果。因为对方在潜意识里就认为你的工作已经做得差不多了。
所以,我们**在做演示的时候,要尽量把界面的完成程度和项目的进展程度对应起来**。比如不要把界面做得太漂亮显示的文字可以用“XXX”而不要用真实数据等。只有这样才能让对方对项目的真实进展有比较客观的感知。Joel有[一篇博客](https://www.joelonsoftware.com/2002/02/13/the-iceberg-secret-revealed/)专门讲这个话题,很有意思,推荐你阅读下。
## 小结
好了,以上就是今天的主要内容了,希望能对你梳理目标和提高沟通能力有所帮助。如果有你希望深入了解还未涉及的话题,那就直接给我留言吧。
接下来,我们就会开启新的模块了,进入管理和文化的讨论。在下一个模块,我会重点与你介绍硅谷的管理、文化,以及在国内的一些落地实践,希望帮助你了解如何利用管理推动高效研发的落地,以及如何使用文化来激发团队成员的内驱力和创造力,提高团队的研发效能。
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,147 @@
<audio id="audio" title="特别放送 | 每个开发人员都应该学一些VIM" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/cb/17/cb75b544b4933aac7befa74731fb5f17.mp3"></audio>
你好,我是葛俊。
在“研发流程”和“工程方法”模块中我主要是从团队的角度和你分享如何提高研发效能所以很多同学希望我能分享一些工具的使用来提高自己的效能。所以今天我准备了一篇关于VIM的文章。在这篇文章中我会着重带你深入了解VIM的两个特点。因为正是基于这两个特点VIM可以很好地提高我们的工作效率。至于更多的、具体的VIM使用方法和技巧我会在接下来的“个人效能”模块中用另一篇文章专门详细与你介绍。
如果你已经是一个VIM的使用者了那我希望文中关于VIM原理的讨论可以帮助你更深入地理解它进而可以更高效地使用它。而如果你还不是VIM的使用者那我推荐你学习它的基本方法并寻找适当的场景去使用它。
其实向开发者们推荐编辑器尤其是像VIM这样一个比较容易引起争议的编辑器是一件有风险的事儿。但基于我对VIM的了解和它能给开发者带来的巨大好处我认为这个风险是值得的相信你也能从中有所收获。
我们下来看看什么是VIM。
## 什么是VIM
VIM是一个老牌的编辑器前身是VI第1个版本发行于1978年距离今天已经有41年的历史了。
VIM是VI Improved是提高版的VI相对来说比较新但实际上它的第1个版本也早在1991年就发布也已经有28年的历史了。
VIM和我们日常使用的编辑器比如VS Code、Notepad++、Sublime Text等差别很大而且上手比较难新手在使用时常常会手足无措。**一个常见的问题是打开了VIM就不知道怎么退出。**比如,有人就曾在[Stack Overflow](https://stackoverflow.com/questions/11828270/how-do-i-exit-the-vim-editor)上提问怎么退出VIM6年以来的阅读量已经接近200万。又比如我还听到过一个玩笑问怎样产生一个随机字符串呢答案是让一个不会使用VIM的人打开VIM并尝试退出。
虽然如此但在美国对开发人员进行的最喜欢的编辑器的调研中VIM往往能排进前5名。我个人的看法是每一个开发者都应该学一些VIM原因有二
1. VIM基于命令行模式的特色能**让文本编辑工作更高效**
1. VIM有极高的跨平台性**可以一次学习然后多处使用尤其可以作为很多其他IDE的插件使用。**
比如当前我在进行微信小程序的项目开发使用VS Code作为我主力IDE。在VS Code中我每天都在使用VIM插件VIM的命令操作大大提高了我的开发效率。作为开发者编辑文本是最基本的工作所以花些时间去了解最基本的VIM操作来提高效率和手指健康是相当值得的。
在我看来VIM有两大特点
1. 具有独特的命令行模式;
1. 跨平台非常棒更能作为插件在很多其他IDE中使用。
而这两个特点也正是我推荐你学习VIM的最主要原因。
## 特点一VIM独特的命令模式使得编辑文档非常高效
非VI系列的编辑器一般只有编辑模式这一种模式也就是说敲击任何主体键都会直接修改文件内容。比如敲击键盘上的e文件里就添加了e这个字符。这里需要注意的是键盘的**主体键**指的是能显示在文件里的键包括a-z、数字、字符等。
而VIM有多种模式其中最主要的是命令模式和编辑模式。命令模式是VIM的默认模式我们用VIM打开一个文件的时候默认进入的就是这个模式。在命令模式中敲击主体键的效果不是直接插入字符而是执行命令。比如
- 敲击字母e表示将光标向右移动到当前单词最后一个字符
- 敲击符号*,表示在当前文件搜索光标所在的单词。
另外,在命令模式中输入“:&lt;命令&gt;回车键”,可以执行一些命令行命令以及进行系统配置。比如:
- 输入:q!表示不保存文件并退出VIM
- 输入:set hlsearch表示打开搜索高亮。
至于我们在其他非VIM编辑器中熟悉的编辑模式需要在命令模式中敲击某些命令才能进入。比如
- 敲击i表示在当前位置进入编辑模式
- 敲击O表示在本行上面添加一个空行并进入编辑模式。
进入编辑模式之后使用体验就跟其他非VIM的编辑器效果差不多了也就是说敲击主体键会直接插入字符。完成编辑工作之后你需要再敲击Esc键返回命令模式。
请注意,**在编辑模式时我们无法退出VIM**。你只能在命令模式中使用ZZ、ZQ、:qa!等命令退出VIM。如果你不会使用VIM然后不小心在命令行窗口中打开了它没有菜单可以选择的确很难找到办法退出所以就有了各种不能退出VIM的笑话。
**另外这里需要指出的是**VIM实际上有多个模式它的官方文档列举了一共7个基本模式和7个附加模式而我在这篇文章中只做了命令模式和编辑模式两种模式的划分是一个巨大的简化。事实上命令模式中包含了常规模式normal mode、命令行模式command-line mode编辑模式则包括了插入模式insert mode、替换模式replace mode等。我之所以用命令模式和编辑模式的简单区分一方面可以帮助你快速理解VIM另一方面也不会影响你对VIM的使用。
总结来说拥有命令模式是VIM系列编辑器与非VIM系列编辑器的最大差别。那VIM为什么会有这种特性呢
这是由VIM的历史决定的。VIM的前身是VIVI的前身是Ex。Ex是Unix诞生时代的编辑器。那个时候因为计算机技术以及计算机系统资源的局限性编辑器只能使用命令来编辑文件。所以VIM就一直保留了命令模式。**这个命令模式是初学者难以适应VIM的最主要原因但同时也是VIM能高效编辑文档的关键所在**。
为什么这样说呢在一个非VIM的编辑器中如果要做一个非编辑操作的时候你需要敲击一个非主体键或者组合键才能完成而在VIM的命令模式中你通常只需要敲击主体键。比如
<img src="https://static001.geekbang.org/resource/image/46/ee/4621ad994d6a424b0ca33648a6b1d7ee.jpg" alt="">
我们在编辑文件的时候有大量的非输入操作比如挪动光标、查找、删除等所以在非VIM的编辑器里我们要大量使用非主体建和组合键。而在VIM中我们可以大量使用主体键从而大大减少使用键盘主要部分也叫工作区之外的特殊键同时使用组合键的次数也大大减少了。
所以综合来讲虽然VIM中的模式切换会带来一些额外按键操作但次数远远小于它节省的按键次数总的按键数量明显减少。
接下来,我们通过一个具体的案例对比一下效果吧。我在输入一行代码注释时,希望输入的结果是
```
// This is making sure that userTotalScore is not null
```
但写到“not”的时候我注意到我前面有一个拼写错误把“making”写成了“mkaing”了
```
// This is mkaing sure that userTotalScore is not
^
```
现在,我需要修改这个错误,修改之后再回到行尾,补充“ null”写完这句话。以Mac为例不使用VIM和使用VIM的操作对比如下表
<img src="https://static001.geekbang.org/resource/image/8c/88/8cf8ad75bd83c536898c816971c2c788.jpg" alt=""><br>
统计下总次数,如下表所示:
<img src="https://static001.geekbang.org/resource/image/4d/3a/4d9cd2d035140d8f61451f3d5b70e23a.jpg" alt=""><br>
可以看到,在这个场景中**使用VIM可以明显减少按键次数包括组合键次数和特殊键次数。**在真实的编辑场景中,我的经验是减少的按键次数会更多,对文本编辑效率的提高非常明显。
另外组合键和非主体键这两种按键方式非常容易对手腕和手指造成伤害。其实我之前是Emacs的重度使用者但使用了四年之后我的左手小拇指开始不舒服这是因为在Emacs中我常常需要用这个手指按住Ctrl键来完成组合键操作。比如使用Ctrl+f向右移动光标使用Ctrl-x Ctrl-S保存文件。
为了手指健康我试着从Emacs向VIM转移。一个月之后手指不舒服的症状明显减轻了。于是我逐渐停止了使用Emacs全面转向VIM。此后双手再长时间使用键盘工作也不容易疲劳了。
## 特点二VIM是跨平台做得最好的编辑器没有之一
因为VIM的悠久历史同时一直在持续更新所以在各大操作系统上都有适用的VIM版本你可以到[VIM的官网](https://www.vim.org/download.php)上查看详情。所以你掌握的VIM技能**基本可以用在所有的操作系统上**。
具体来说在Unix系统上都有预装VI。因为VIM的命令是向上兼容的所以你熟悉的VIM的基本功能在VI上仍然可以使用。Linux系统自带的基本都是VIM比如Ubuntu18.04自带的版本就是VIM8.0。苹果操作系统因为是Unix的一个分支所以预装有VIM。
Windows上没有预装VIM。不过你可以很方便地安装GVIM或者直接运行一个免安装的可执行GVIM程序。
在移动端操作系统上VIM在iOS和Android端都有移植
- iOS上面比较好用的叫iVIM。我在iPad中进行一些重量级文本编辑的时候就会使用iVIM。具体的使用方法是将需要编辑的文本拷贝到VIM里面编辑好了之后再拷出来使用体验也还不错。
- Android上的VIM移植得比较多比如DroidVim就还不错。
VIM跨平台特性的另外一个表现是**很多其他编辑器及IDE都有VI模式**支持最基本的VIM操作。
比如IntelliJ系列的IDE上有[IdeaVim插件](https://www.jetbrains.com/help/idea/using-product-as-the-vim-editor.html)、VS Code里有[VSCodeVim插件](https://marketplace.visualstudio.com/items?itemName=vscodevim.vim)甚至VIM的老对手Emacs里也有好几个VI插件最有名的是[Viper Mode](https://www.emacswiki.org/emacs/ViperMode)。
我最近半年使用最多的编辑器VS Code所以我以它为例与你说明如何在其他编辑器中使用VIM。
VSCodeVim插件的安装很简单使用默认的VS Code插件安装方法很容易就能搜索到并一键安装配置也简单默认的配置使用体验就非常不错。我在使用VSCode一个月后对VS Code比较熟悉了开始试用VSCodeVim插件之后就再也回不到原生模式了。因为VIM带来的效能提升以及给手指带来的舒适感觉实在是太明显了。
VIM的跨平台特性甚至超越了编辑器这个范畴**在一些不是编辑器的软件里面也有VI模式**。比如Chrome浏览器和FireFox浏览器中都有VI插件用户可以使用VI的快捷键方式来操作。
在我看来在浏览器上使用VI模式的最大好处是可以减少鼠标的操作。这一点对我的吸引力不是很大不过我的另外两个朋友一直在使用Chrome的VI模式插件[Vimnium](https://vimium.github.io/),反馈都是很好用。如果你非常偏好键盘而不是鼠标的话,推荐你也试试看。
在**配置**方面VIM的默认配置就基本够用。所以我一般只在自己的主力开发机上才会添加一组我的常用配置及插件来提高使用体验其他不常用机器就保留默认配置。
总的来说VIM的跨平台做到了极致因此我在很多地方都能用到积累的VIM经验。VIM肌肉记忆不断强化一直在帮助我提高工作效率。
## 小结
在这篇文章中我着重与你讲述了VIM的命令模式与跨平台特性这两大特点。通过对这两个特点的深入探讨阐述我认为每个开发人员都应该学一些VIM的理由。
VIM编辑器的命令模式是与其他非VIM编辑器的最大区别。也正是因为这个特性使得其入门比较难令很多新手望而生畏。但也正是因为命令模式才使得VIM对于个人研发效能的提升帮助非常大。
而跨平台特性使得我们一旦掌握了VIM技能就基本可以用在所有的操作系统上甚至是其他IDE中通过插件使用从而最大程度地实现经验复用。
其实除了命令模式和跨平台特性外VIM还有一些其他好处比如速度快、免费、可扩展性强等。但是我认为这两点从根本上把VIM和其他编辑器区别开来了它们能让我们非常高效、健康的编辑文本。所以说付出一些成本去学习VIM的基本使用是非常值得的。
有一种说法是,说人的双手在一生中能够按键盘的总次数是一定的,达到这个总次数之后,手指就不能很好地使用键盘工作了。不知道你信不信,反正我信了。
关于VIM的话题我们今天就讨论到这里了。在“个人效能”模块我还会与你详细分享如何高效地学习VIM并分享关于VIM的一些使用方法和技巧帮你学会、用好VIM这个工具。
## 思考题
1. 除了Windows你见过没有预安装VI的系统吗那个系统上自带编辑器是什么呢在这个系统上你又是如何完成文本编辑工作的呢
1. 你见过VIM教徒和Emacs教徒的争吵吗
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,149 @@
<audio id="audio" title="11 | 研发环境Facebook怎样让开发人员不再操心环境" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a0/4f/a0604718d5322013784241950e93144f.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊研发环境的问题,也就是如何才能让开发人员少操心环境,甚至不操心环境。从这篇文章开始,我们就一起进入工程方法模块了。
在[第5篇文章](https://time.geekbang.org/column/article/129857)关于“持续开发”的讨论中,我与你介绍了获取开发环境是开发工作的核心步骤之一,对提高研发效能来说是非常值得优化的环节。当时,我与你提到了开发环境服务系统以及沙盒环境,解决了开发环境的准备工作。
而这里的“开发环境”只是研发环境的一部分,特指开发机器环境,包括开发机器的获取、网络配置、基本工具以及代码的获取和配置。今天,我们就来看看整体的研发环境的配置问题,从全局上切实解决开发人员因为操心环境而导致的效能低下。
在此之前,你可以先回忆下是否对以下问题似曾相识呢?
- 开发人员使用的电脑配置太低运行太慢编译一次要10分钟
- 测试环境不够,上线时熬夜排队等环境;
- 工具零散,不成系统,很多步骤需要手动操作,开发思路常常因为流程不连贯而被打断;
- 团队成员的环境设置参差不齐,有个别开发人员环境配置得比较好,效率比较高,但没有固化下来,其他团队成员也用不上。
这些问题,实际上都可以归结为研发环境不够高效。就像低劣的空气质量和食物质量会影响我们的身体健康一样,不理想的研发环境会严重降低研发效能。
具体来说,按照开发流程,软件研发需要以下几种环境:
- 开发机器;
- IDE
- 开发过程中使用的各种工具、数据和配置;
- 本地环境、联调环境;
- 测试环境、类生产环境。
接下来我会按照这个顺序分别与你介绍这5套环境如何配置。
因为Facebook在环境配置上做得非常好比如说他们可以保证开发人员在5分钟之内拿到一套干净的开发机器环境。所以我会与你着重分享Facebook的实践然后根据这些实践总结出一套提供高效研发环境的原则。同时我也会与你介绍一些我在其他公司的落地实践帮助你掌握与环境配置相关的内容。
## 如何配置高效的研发环境?
### 开发机器
Facebook从不吝啬在开发机器上的投资。每个开发人员除了有一台笔记本外还在远程数据中心有一台开发机器。数据中心的机器用于后端以及网站的开发为方便描述我称之为后端开发机。而笔记本有两个用处一是用来做移动端App的开发二是作为终端连接到后端开发机做后端和网站开发。
两台机器的配置都非常强劲。后端开发机一开始是实体机器后来为了便于管理和提高资源利用率逐步转为了虚拟机但不变的是配置依然强大。在2015年的时候绝大部分机器配置都能达到16核CPU 、144G内存。笔记本有两种选择一种是苹果MacBook Pro另一种是联想ThinkPad都是当时市场上顶配或者顶配下一级的配置。
开发机器的获取和释放,则是通过共享机器池以服务化自助化的方式完成的。更多细节,你可以再复习下[第5篇文章](https://time.geekbang.org/column/article/129857)的相关内容。
### IDE
IDE指的是集成开发环境。Facebook持续在IDE上投入以完善其功能、一致性及一体化体验。接下来我会以后端和网站开发为例来说明。
因为代码存放并且运行在后端开发机上所以在2012年以前绝大部分开发人员都是使用SSH远程登录到后端开发机在那里使用VIM/Emacs这类能在命令行里运行的编辑器进行开发工作。也有人尝试在笔记本上运行GUI的IDE远程挂载后端开发机上的文件系统但总是有卡顿效果不好。
在这种情况下公司首先采用的办法是尽量提高命令行编辑器的体验把VIM/Emacs配置成类似IDE的工具将最常见的功能比如代码搜索、代码跳转、Debugger等集成进去效果还算可以。
不过与GUI形式的IDE相比这种命令行的工作方式还是有一些局限性比如和其他工具服务集成不方便需要记忆大量快捷键在显示形式上不够丰富导致用户体验不够好等等。所以Facebook持续投入研究GUI形式的IDE。
第一个成型的GUI IDE是一个Web IDE也就是在数据中心运行一些Web IDE服务。这些IDE服务连接到开发者的后端开发机上获取代码同时提供网页界面供开发人员用浏览器登录进行开发工作。这种专门的IDE服务解决了远程文件夹挂载的卡顿问题同时跟其他服务集成起来也很方便。
比如它可以与Phabricator的代码审查功能集成在一起在代码中用inline的方式显示其他人对你的代码的comments也就是说在两行代码之间显示一个额外区域用于展示comments。类似的它还可以方便地与代码搜索服务、代码跳转服务、Debugger集成甚至还可以跟CI/CD工具链的发布工具集成所以非常方便。
到2014年的时候Facebook的大部分开发人员都逐渐转移到这个Web IDE上去了。如果你想深入了解Web IDE可以参考下[Apache Che](https://www.eclipse.org/che/)项目。
这个Web IDE已经很方便了但基于网页的IDE在易用性和安全性还是有一些局限所以Facebook继续加大在GUI形式的IDE方面的投入最后使用Electron框架实现了一个原生的IDE也就是[Nuclide](https://nuclide.io/docs/quick-start/getting-started/)。
Nuclide的工作原理和Web IDE基本一致都是在数据中心运行IDE服务IDE的前端则运行在本地笔记本上通过与IDE服务联通实现代码编辑等功能。只不过Nuclide的前端是运行在开发者笔记本上的一个原生应用而Web IDE的前端是运行在笔记本上的网页浏览器而已。Nuclide的功能比Web IDE更强大易用性也更好同时因为没有浏览器的依赖安全性也更好一些。
### 本地环境、联调环境
关于Facebook的本地环境我已经在[第5篇文章](https://time.geekbang.org/column/article/129857)中介绍过了,主要就是加快本地构建,使用生产环境的数据,从而使得本地环境更加快捷、方便。
而至于联调环境,我曾在[第6篇文章](https://time.geekbang.org/column/article/131673)中简单提过。在代码提交到Phabricator上进行审查的时候Phabricator会调用一个沙盒系统创造出一个沙盒环境运行正在被审查的代码。这个系统也是用机器池实现的同时也是一个自助式服务也就是说开发人员可以不通过Phabricator直接调用API来生成沙盒环境。
本地环境和联调环境是开发中最高频使用的环境,对持续开发很重要。接下来,我再与你分享**两个我在其他公司的实施案例**吧。
**第一个例子是我在Stand公司搭建本地环境**。Stand的业务规模小因此并没有使用微服务主要的服务只有单体的网站后端服务、数据库服务MySQL、缓存服务Redis以及一些数据监控服务相对来说比较简单。
我们的做法是,把这些依赖服务尽量在本地开发机器(也就是笔记本)上都运行一个实例。实在不能在本地运行的服务,要么在本地环境运行时不调用它,要么就在调用它的时候传递额外的参数,表明这个调用来自开发环境,而被调用的服务则针对这样的调用进行特殊处理,从而达到不污染线上环境的效果。
这是一个很常见的办法,简单有效。不过,它的缺点是本地环境数据跟线上环境有区别。
如果你的系统采用的是微服务则可以采用以下3种常见办法。
- 第一种方法是,直接在自己的机器上把所有的依赖服务都跑起来。这种方法的优点是方便,但要求开发机器配置要求高。
- 第二种方法是,团队维护一个环境,让大家的开发环境接入。也就是,开发者自己的机器上只运行自己开发的服务,调用其他服务时就使用这个共享环境中运行的服务实例。这种方式对开发机器要求不高,但需要团队维护一个环境,并且一般来说大家需要经常更新这个环境中的服务,整个环境容易不稳定。
- 第三种方法是,使用服务虚拟化工具,来模拟依赖的服务,比如[Mountbank](https://github.com/bbyars/mountebank)、[WireMock](https://github.com/tomakehurst/wiremock)。你可以参考[](https://juejin.im/entry/5a54a432f265da3e591e27ee)[篇文章](https://juejin.im/entry/5a54a432f265da3e591e27ee)来了解WireMock的使用方法。
**第二个例子是,我为一个云产品团队提供联调环境**。这个云产品结构非常复杂有十多个服务至少需要3台服务器不但有软件还有数据、组网等复杂的设置部署很困难更严重的问题是这个环境一旦损坏就很难修复需要从头再来所以开发人员自己配置基本不可能运维人员也是忙于维护应接不暇。
针对这个问题我们也是使用了机器池的办法。不过这个机器池里面的单元不再是单个机器而是由3台机器组成的服务。这个服务自助化提供给开发者使用使用之后自动回收销毁。
同时,确保机器池中有两套空闲环境。不够就补充,多了就删除,以保证获取环境时可以马上得到,同时又不会因为有太多空闲环境而造成资源浪费。
另外,每次有了新的稳定版本,运维人员都会更新脚本,并重新安装和配置系统,保证开发人员能够在稳定版本上进行联调。
这样一来就解决了团队开发人员的环境问题将获取环境的时间由2~4个小时缩短到了几分钟。
### 开发过程中使用的各种工具、数据和配置
除了IDE开发过程中还会用到其他工具比如代码搜索、发布部署以及日志查看工具等。这些工具的组合可能与你平时理解的开发环境不大一样但事实上它也是一种广义的开发环境对开发效率影响很大。
这部分环境的优化,主要是使用工具之间的网状互联来提高效率。关于工具的网状互联,我在[第4篇文章](https://time.geekbang.org/column/article/128867)中有介绍。这里我想强调Facebook的另一个重要实践**重视开发体验,将开发流程中常用步骤的自动化做到极致**。
我用一个具体的例子来说明。我们都知道Git 的Commit Message代码提交描述是提供信息的重要渠道。但它有一个局限就是只能存储文本而图片在描述问题时常常比文字有效得多也就是“A picture is worth a thousand words”翻译为中文就是我们常说的“一图胜千言”。
为解决这个问题Facebook采用了以下方式
1. 提供了一个图片存储服务并为上传的图片提供永久URL
1. 开发了一个端测的截屏工具截屏之后自动上传到图片存储服务而且在上传成功之后自动把图片URL保存到本地笔记本的系统剪贴板中
1. 提供了一个内部使用的URL缩短工具避免URL太长占用太多文字空间。
这三个工具集成起来一个具体的使用场景是这样的我在写Commit Message的时候如果要截屏描述修改效果就使用一个快捷键比如Cmd+Alt+4激活截屏工具随后拖动鼠标截屏然后使用Cmd+v就可以直接把图片的URL粘贴到Commit Message里面了。
这个工作流非常顺畅、高效不仅被大量用于Commit Message的书写中也经常被用在聊天工具中。后来我到了其他公司都会先配置这样一套工具流程。
### 测试环境、类生产环境
在测试环境、类生产环境的管理上Facebook使用一个叫作[TupperWare](https://engineering.fb.com/data-center-engineering/tupperware/)的内部系统以IaC的方式进行管理。不过Facebook并没有开源这个系统。如果你所在公司使用的是Docker那可以使用Kubernetes实现类似的功能。
如果你们没有使用Docker可以试试HashiCorp公司的[Terraform](https://www.terraform.io),或者使用[Ansible](https://www.ansible.com)、[Chef](https://www.chef.io)之类的配置管理工具,来产生一套干净的环境供团队成员使用,之后再销毁,既方便又不浪费资源。
这里,我再与你分享一个我在**Stand公司使用AWS管理压测环境的例子**。当时我们没有使用Docker于是我们选择了AWS的OpsWorks框架。它是AWS基于Chef-Solo开发的一个应用程序管理解决方案同时支持基础设施的建模和管理。
OpsWorks这个框架的用法也是声明式的只不过这个声明不是纯代码而是在AWS的网页上配置的使用效果和TupperWare差不多就是**首先**定义一个压测环境需要几台机器,需要运行什么操作系统、需要什么负载均衡器、需要什么数据库等。
**然后**通过OpsWorks上暴露的钩子使用代码来管理应用的生命周期从而实现系统和应用的初始化。通过这种方式我们可以很方便地使用OpsWorks产生一个云主机集群用于压测结束之后马上删除方便而且划算。
其实使用Ansible或者Chef也可以实现类似功能但需要自己开发些东西这里我就不详细讨论了。
## 提供高效研发环境的原则
通过以上实践可以看出,配置高效的研发环境主要包括以下几条原则:
1. 舍得投入资源用资源换取开发人员时间。Facebook之所以从不吝啬在开发机器硬件上的投入是因为人力成本更高。
1. 对环境的获取进行服务化、自助化。这一点可以在开发机器、联调环境的获取上得到很好的体现。同时常常使用IaC、配置管理系统比如Chef和机器池的方法来实现同时利用弹性伸缩来节约资源。
1. 注重环境的一体化、一致性也就是要把团队的最佳实践固化下来。比如Facebook一个常见的操作是配置文件统一处理。以VIM为例将VIM的配置文件存放到网络共享文件夹中开发人员只要在自己的.bashrc文件中加上一行就可以搞定。
```
source /home/devtools/vimconfig
```
## 小结
今天我首先按照开发流程也就是开发机器、IDE、本地环境和联调环境、开发过程中使用的各种工具及配置以及测试环境和类生产环境的顺序与你讲述了高效研发环境的具体实践。然后基于这些实践总结了3个基本原则一是用资源换时间二是服务化、自助化环境的获取三是实现环境的一体化、一致性。
我认为这些原则和实践的背后有一个重要思路就是Facebook重视环境并持续优化环境。这一点在IDE的演化上尤为明显从命令行到Web IDE再到Nuclide一直在进步。另外在去年年底Facebook停止了对Nuclide的开源项目的维护这也意味着后面他们可能还会有对IDE的一轮新的优化。
以上种种做法使得我在Facebook做开发的时候对研发环境的感觉就是不用操心需要使用的时候直接到网站上申请就可以使用配置也方便团队的配置都已经在那里了同时环境中的各种工具、流程都很顺畅让我能够静下心来做开发、写算法做我最能提供价值的事情。
## 思考题
我在“开发过程中使用的各种工具、数据和配置”这一章节中提到的截屏工具流程,你觉得价值大吗?值得引入你所在的公司吗?如果值得的话,可以怎么来实现?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,207 @@
<audio id="audio" title="12 | 代码审查:哪种方式更适合我的团队?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/39/d7/39d480b1c64d6e619d21eb5dbcf48ed7.mp3"></audio>
你好,我是葛俊。今天,我们来聊聊都有哪些代码审查方式,以及哪种方式更适合你的团队。
国外互联网行业的很多效能标杆公司都非常重视代码审查Code Review比如Facebook、Google等就要求每一个提交都必须通过审查。
现在国内的很多公司也在有意无意地引入代码审查有的团队直接使用代码仓库管理工具提供的审查功能比如GitHub、GitLab提供的PR审查有的团队则使用专门的审查工具比如Phabricator、Gerrit还有些团队采用面对面检查甚至有少数公司尝试使用结对编程的方式进行代码审查。
虽然国内公司在代码审查上做了不少尝试,也有一些公司做得比较好,比如我了解到七牛云就做得不错,但大多数国内公司还是对代码审查理解得不够深入,对审查方法的认识也不够全面,只能简单地去追随一些最佳实践。结果就是,有些团队的代码审查推行不下去,半途而废;有的则流于形式,花了时间却看不到效果。
那么,怎样才能做好代码审查呢?
我认为,**做好代码审查的一个前提条件就是,要找到适合自己团队的方法。**要做到这一点,你就要对代码审查有一个深入的了解,弄清楚常用方式及适用场景,然后才能做出正确选择。
所以,在今天这篇文章里,我首先会和你分享常用的代码审查方式及其适用场景,然后和你分享几个具体案例。
## 什么是代码审查,有什么作用?
首先,我们来看看代码审查的定义。代码审查是指代码作者以外的其他人对代码进行检查,以寻找代码的缺陷和可以提高的地方。
这里需要注意的是,按照定义,**人工检查才是代码审查**。机器进行的检查一般叫作代码检查或者代码自动检查。常见的办法是人工审查和机器检查同时进行,我会在后面和你详细讨论这部分内容。
代码审查的作用很多主要表现在5个方面。
**作用1尽早发现Bug和设计中存在的问题。**我们都知道,问题发现得越晚,修复的代价越大。代码审查把问题的发现尽量提前,自然会提高效能。
**作用2提高个人工程能力。**不言而喻,别人对你的代码提建议,自然能提高你的工程能力。事实上,仅仅因为知道自己的代码会被同事审查,你就会注意提高代码质量。
**作用3团队知识共享。**一段代码入库之后,就从个人的代码变成了团队的代码。代码审查可以帮助其他开发者了解这些代码的设计思想、实现方式等。另外,代码审查中的讨论记录还可以作为参考文档,帮助他人理解代码、查找问题。
**作用4针对某个特定方面提高质量。**一些比较专业的领域比如安全、性能、UI等可以邀请专家进行专项审查。另外一些核心代码或者高风险代码也可以通过团队集体审查的方式来保证质量。
**作用5统一编码风格。**这,也是代码审查的一个常见功能,但最好能通过工具来实现自动化检查。
以上就是代码审查的5个主要作用。接下来我会与你详细介绍具体的审查方法。在这部分内容中我会针对每一种方法详细讨论其优缺点及适用场景并根据我的经验给出建议。这是本篇文章的中心内容希望你可以多花些精力好好理解消化。
## 根据团队特点选择代码审查方法
代码审查有多种方法,按照不同的维度可以有不同的分类。
### 按审查方式分类
按照审查方式,代码审查可以分为工具辅助的线下异步审查和面对面审查两类。
工具辅助的线下异步审查就是代码作者通过工具将代码发送给审查者审查者再通过工具把反馈传递给作者。比如GitHub、GitLab、Gerrit、Phabricator等工具的审查都是这种方式。这是当前最常见的审查方式应用灵活的话可以在任何一个团队取得良好的效果。
使用这种方式的优点,主要表现在两个方面:
- 一是,审查者可以控制审查时间,减少对自己工作的干扰。比如,审查者可以用一两个小时的时间,来集中处理多个代码审查要求。
- 二是,在工具中的讨论会留下文档,方便以后参考。
相应地,使用这种方式的缺点是实时性不好。但,这个问题很容易解决。比如,你可以在工具上发出审查要求,然后跑到审查者办公桌前进行面对面讨论。
**面对面审查,就是审查者和代码作者在一块儿阅读代码,进行实时讨论**。这种方式的好处是快,可以高效地审查不方便用文字讨论的代码。比如,架构问题的讨论就很适合用这种方式。面对面审查的一个极端例子是结对编程,也就是两个人同时写代码,可以说是把实时性发挥到了极致。
但,面对面审查的主要问题,就是对审查者的干扰大,如果大量使用的话会影响审查者的心流。一个降低干扰的方法是,提前约时间。
10年之前我在微软工作的时候我们就常常会使用面对面审查的方法。但除了特殊情况以外大家都需要提前预约才能去找审查者进行面对面审查。总的来说这种方式还是可以接受的。
### 按审查人数分类
按审查人数,代码审查可以分为一对一审查和团队审查两类。
**一对一审查,就是审查者单独进行审查**。除了面对面的一对一审查,基于工具的异步审查是最常见的一对一审查。
这种方式的好处是,安排起来容易,对审查者的干扰小。不过,代码作者一般不会只把代码发给一个人审查,而是会同时发给几个人,避免出现因为单个审查者最近没空而耽误审查速度的情况。
需要注意的是,这种方式,可能会出现几个审查者同时审查一段代码的现象,从而造成重复劳动。常见的解决办法有两个:
- 一种是,代码作者明确邀请审查者检查代码的不同部分或者不同方面;
- 另一种是,审查者在开始审查时通知其他审查者,确保不会重复审查。
**团队审查,就是团队成员聚在一起讨论一些代码**。这种方式适合专项讨论,比如团队成员集体讨论关键代码。具体方式常常是面对面在会议室进行,当然有的团队也会使用电话会议。
团队审查的好处是,大家一起讨论可以检查出更多问题,但缺点也比较明显,因为要开会,所以很容易出现会议效率不高的通病。解决这个问题,至少做到以下四点:
- 一是,会前做好准备。也就是,组织者提前发出审查要求和要点,要求每个参会者会前阅读。
- 二是,增加一个协调人员,以确保会议讨论能聚焦和按部就班地进行。
- 三是,要有会后跟进,最好能够把文字记录存档到可以搜索到的地方,供以后参考。
- 四是,尽量减少参会人数。
当然了,这四点适用于所有会议。
### 按审查范围分类
按照审查范围,代码审查可以分为增量审查和全量审查两类。
**代码增量审查,是只针对改动部分进行审查。**在一个团队形成代码审查的机制以后,一般会只审查新增代码。
这种方式的好处是,能把有限的时间花在最容易出问题的地方。不过需要注意的是,在审查新的代码改动时,一定要一同考虑相关代码,看看是否会因为新代码的引入造成问题。另外,也需要注意是否会有旧的代码,可以被重构掉。
目前来看,这种方法没有什么明显的缺点。
**代码全量审查,是对现有代码的全量,比如说整个文件、某几个函数进行审查。**
这种方式常见的适用场景有两个:
- 一是,专项检查;
- 二是,在刚开始引入代码审查时,对遗产代码进行一次审查。
这种方式的优点是关注整体质量。但,缺点是工作量大,不能常常进行。
### 按审查时机分类
按照审查时机,代码审查可以分为代码入库前门禁检查、设计时检查、代码入库后检查三类。
**代码入库前门禁检查,是把代码审查作为门禁的一部分,要求代码在入库前必须通过人工审查。**这也是最常见的审查方式。比如GitHub等工具里面进行的PR审查就属于这一类在Gerrit工具里代码入库前必须通过打分才能入库也是典型的门禁场景。
这种审查方式的优势很明显,如果没有流于形式的话,可以在代码入库前的最后一步拦截问题,从而避免入库后昂贵的缺陷修复成本。
但这种方法可能导致一个问题,即太过死板从而降低代码入库效率。比如,如果一个公司严格要求入库前门禁检查,即使是修改一个错别字也必须要完整检查才行,这在进行紧急热修复的时候可能就不合适。
**解决这个问题的办法就是,引入灵活的机制**。比如Facebook通过Phabricator审查有一个办法可以让代码作者绕过审核者直接将代码入库但是需要通知审核者这样操作的理由。一个常见的合理理由就是“这个修复很简单我的把握很大。但又很紧急需要马上通过热修复上线。另外现在是凌晨一点半不想把你叫起来帮我审查。”
**代码入库前的设计时检查,就是在设计阶段进行代码审查。**这种方式主要是讨论代码的架构设计是否有问题。因为代码还没有成型,修改成本非常小,代码作者的抵触情绪也很小。相反,如果代码写好后再去审查架构设计,一旦发现问题就会改动较大,甚至推倒重来,非常浪费时间。我见到的大多数情况是,为了保证项目进度而不得不放弃修改。
事实上,有[调查显示](https://www.linkedin.com/pulse/code-reviews-software-quality-empirical-research-results-avteniev/)代码审查更容易发现架构问题而不是Bug。所以**我的建议是,尽量使用代码设计时审查。**具体的方法是可以使用与门禁代码检查相同的工具只不过这个时候发出去的PR目的是为了讨论而不是为了入库。讨论结束后删除这个PR即可。
需要强调的是,设计时代码审查的用处巨大,但在实践中却常常因为大家对它不够了解而被忽略。
**代码入库后检查,就是检查已经入库的代码。**有些工具专门支持事后审查比如Phabricator的审计功能Audit
这种方式的好处是,既不阻塞代码入库,又可以对提交的代码进行审查,只要入库代码没有马上上线,风险就很小。实际上,这就是我们开发审计功能的意图。如果你所使用的工具里没有这个功能,可以在代码历史浏览工具里,用讨论的方式进行。虽然不够方便,但也够用了。
入库后检查的另一个作用是,提高遗产代码的质量。
以上就是代码审查的基本方式、特点及适用场景。掌握了这些,你就可以根据自己团队的特点,选择最合适的方式。最后,我再和你分享三个成功案例,希望给你更多启发。
## 代码审查方式选择的三个成功案例
下面的这三个案例,团队规模、引入代码审查的时机,以及适用的代码审查工具、方式都有所区别,你可以边看案例,边思考适合自己团队的代码审查方法。
### 案例一5个开发者组成的初创团队的代码审查实践
虽然这个团队只有5名开发者但灵活地做到了高性价比的代码审查。总结他们的经验来看主要体现在以下几个方面。
从审查方式来看他们采用的是基于GitHub的线下异步审查。这样做的考量以及好处是
- 他们的代码仓库是GitHub使用GitHub的PR功能直接进行审查。虽然GitHub不如专业的代码审查工具方便但够用了。
- 使用GitHub做基本的代码审查所需的配置很少所以引入工具的工作量也非常小。
- 有的情况使用面对面审查方法是在代码PR发出去之后基于GitHub来进行面对面讨论同时会把几个讨论放到一起而不是无节制地使用面对面审查以提高工作效率。
从审查范围来看从开发第一个MVP开始他们就引入了代码审查所以后续一直采用代码增量的一对一审查。只是在App上线之后针对安全对登录模块做了一次全量代码入库后检查。
从审查时机来看,他们并没有强制使用代码入库前门禁检查。但是,他们仍然把代码审查作为一个高优先级的任务来做,要求没有特殊原因都要做代码审查。
除此之外他们做了力所能及的机器检查比如通过GitHub的钩子运行各种Linter以及单元测试和一些集成测试与人工检查互为补充。
结果就是,这个初创公司从一开始就形成了灵活使用代码审查的文化,提高代码质量的同时,并没有减缓代码入库的速度。
### 案例二30人团队的代码审查实践
这个团队的代码管理工具是GitLab没有使用代码审查。因为业务发展太快代码质量问题越来越严重所以他们决定引入代码审查。具体来说做法如下。
他们放弃了GitLab直接用Gerrit进行代码仓库管理和代码审查。这是因为GitLab的代码审查功能不如Gerrit强大。而Gerrit同时也具备代码仓管理功能所以就没必要同时维护两个系统了。
从审查方式和审查人数来看,他们采用的是工具辅助的线下一对一审查,对于团队审查非常慎重,只是偶尔使用。
从审查范围来看,他们只是在开始阶段,集中团队核心成员对现有遗产代码进行了几次多对一、面对面的代码全量审查。
从审查时机来看,他们将代码审查作为门禁的一部分,严格执行,以保证业务快速发展时期,上线代码的基本质量。
在机器检查方面他们使用Gerrit、Jenkins、SonarQube三个工具互相集成自动化了较多的机器检查。
针对之前出现过多次架构问题,但因为发现较晚而来不及修复的情况,团队逐渐引入设计时审查这一实践,尽早发现问题并修复,效果非常不错。
这里,我需要再强调下这个团队引入代码审查的步骤。
他们通过规定严格的提交说明Commit Message规范以及PR描述规范来逐步提高代码提交的原子性。也就是说每个提交只做一件事同时这个提交又不会太小。这种方法很有效我会在下一篇文章中和你做进一步介绍。
### 案例三:百人以上团队的代码审查实践
这个团队原本使用GitLab作为Git服务器不过没有做代码审查。在引入代码审查的过程中为了提高代码审查的效率和体验他们没有使用GitLab做代码审查而是引入了Phabricatgor。具体做法是
- 使用Phabricator的镜像方式进行代码审查。也就是说代码仓库仍然是GitLabPhabricator上只有一个用来做代码审查的克隆这样既实现了代码审查又把对原有Git流程的影响降到了最小。
- 因为团队较大又分散在多个地区所以大量使用了线下的异步审查流程。为了保证开发人员在等待一个提交审查的同时还可以做其他开发工作他们使用了Git的提交链和多分支的方法。关于Git的这个使用技巧我会在后面的文章里与你详细讨论。
- 基于Phabricator进行代码设计时审查解决因为代码仓库规模大导致添加新功能时设计复杂也容易有所疏漏的问题。开发人员使用伪代码来表明自己的设计计划并发出代码审查需求然后跟审查者进行面对面讨论或者视频会议讨论。
- 使用代码审查对新人进行培训,通过严格审查新人提交的代码,来传达团队的代码规则、质量基准等。
另外值得一提的是在使用Phabricator进行代码审查之前这个团队最常用的是少量的团队集中审查并且有审查权的只是团队的几个核心成员。这种方式导致了两个问题一是审查效率低下二是这几个核心成员本身都是技术骨干但因为需要花费大量时间做审查导致无法贡献足够多的新代码。
采用新的代码审查方式之后,降低了团队开会进行代码审查的频率,并且逐步放开了审查权限,解决了代码审查成为瓶颈的问题。
以上就是三个成功案例,相信根据你们公司的实际情况具体分析,你一定能找到合适的方法。
## 小结
代码审查对团队产出质量、个人技术成长有很多好处。代码审查方式多种多样,根据不同维度可以分为工具辅助线下审查、面对面审查、多对一审查、一对一审查、代码增量审查、代码全量审查、设计时审查、入库前检查、入库后检查等。
我将这些审查方法的优缺点整理为了一张表格,你可以以此为参考,根据团队实际情况去挑选合适的方式。
<img src="https://static001.geekbang.org/resource/image/e7/00/e7f76393fbce169b3e75b9e67407f000.jpg" alt="">
总体来说绝大部分团队都适合引入工具进行异步的一对一审查在互联网上也有比较多的关于这种审查方式的最佳实践推荐。比如最近Google发表了他们的[代码审查指南Googles Code Review Documentation](https://github.com/google/eng-practices/blob/master/review/index.md),你可以参考。
另外,多使用一些设计时审查尽早进行讨论一般也都效果不错。
知道了应该使用哪个方式,下一步就该具体的引入了。在下一篇文章,我会和你介绍成功引入代码审查的几个具体实践。
## 思考题
1. 你们团队使用了代码审查吗?具体使用了哪几种审查方式呢?
1. 设计时检查除了可以避免后期对代码的大规模调整外,对顺利引入代码审查还有一些其他作用。你能想到还有哪些作用吗?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,239 @@
<audio id="audio" title="13 | 代码审查学习Facebook真正发挥代码审查的提效作用" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/ca/bd/ca24be0105ddbb06ba0daa74b80b31bd.mp3"></audio>
你好,我是葛俊。今天我们来聊聊代码审查的落地。
在上一篇文章中,我和你详细讨论了代码审查的作用,也给出了选择适合自己团队审查方式的建议。但是,仅仅知道要做什么还不够,更重要的是落地。我见到很多国内公司也在尝试使用代码审查,但是效果很不好,往往流于形式,最常听到的一个负面反馈就是“代码审查浪费时间”。
代码审查的成功推行的确不是一件容易的事。今天,我们就一起尝试来解决这个问题。我会从三个方面给出一些建议:
- 第一,在团队内引入代码审查的步骤和方法;
- 第二,成功推进代码审查的关键操作;
- 第三,持续做好代码审查的重要原则。
今天的文章较长,我们现在就进入第一个部分,
## 引入代码审查的步骤和方法
从我的经验来看,要成功引入代码审查,首先要在团队内达成一些重要的共识,然后选择试点团队实行,最后选择合适的工具和流程。
### 1. 代码审查应该计入工作量
代码审查需要时间,这听起来好像是废话,但很多团队在引入代码审查时,都没有为它预留时间。结果是大家没有时间做审查,效果自然也就不好。而效果不好又导致代码审查得不到管理者重视,开发人员更不可能将代码审查放到自己的工作计划中。于是,形成恶性循环,代码审查要么被逐渐废弃,要么流于形式。
之前在Facebook的时候我们预估工作量的时候就会考虑代码审查的时间。比如我平均每天会预留1~2个小时用于代码审查大概占写代码总时间的1/5。同时代码审查的情况会作为绩效考评的一个重要指标。
另外平时我们也会给审查者关于审查质量的实时反馈。比如我刚加入Facebook的时候对代码审查不够重视做得不够好。我的主管就两次给我反馈让我提高审查质量。其中一次是让我多给同事做审查另外一次是让我多给一些结构上的建议不用太重视语法细节。
经过这两次反馈,我意识到了代码审查是我工作中实实在在的一部分,需要得到足够的重视。也正因为此,我才真切地感受到了代码审查的价值。
**总之,管理者要明确代码审查是开发工作的重要组成部分,并记入工作量和绩效考评。这,是成功引入代码审查的前提,再怎么强调都不为过。**
形成共识以后,下一步就是选择试点团队推行代码审查。
### 2. 选择试点团队
引入代码审查的一种方法是在整个团队从上往下全面推行。在团队规模比较小的时候比如少于20个开发者这个方法很好用能够快速推动。但如果团队规模较大成员对好的代码审查方法又不是很熟悉的话往往会造成混乱。
比如,大家会倾向于按照自己的想法进行审查,这就可能会出现审查者只检查格式,或者把个人喜好强加到别人身上的错误做法。
**如果在一开始的时候,就出现较多的负面效果的话,会让大家丧失推行代码审查的信心。**
所以,我推荐先不要大范围实施,而是应该先选择试点团队,之后再推广。这样做的好处是,一方面,试点团队成员有限,容易推行新的做法;另一方面,有了试点团队的成功案例后,再全面推行就有了可借鉴的经验,会更顺畅。
关于试点团队的选择,最好是找成员经验丰富、对技术有追求,同时所做业务又不是最紧急的小团队。这样的团队成员不仅会有兴趣,而且也有精力去学习好的审查实践,容易出成果。
有了试点团队,我们接下来就需要选择代码审查工具及配置流程了。
### 3. 选择代码审查工具,并把机器审查和人工审查结合起来
我在前一篇文章中提到过,几乎所有团队都适合使用工具进行异步的一对一审查。
关于工具如果你的团队本来已经在使用GitLab、GitHub、Gerrit、Phabricator管理代码的话那么很容易上手代码审查。因为GitHub、GitLab有基于PR的审查。而Gerrit和Phabricator本身就主打代码审查的功能。
如果你所在团队没有使用这些工具的话,就只能投入资源引入一个新工具,可以是上面提到的几个工具,也可以是其他审查工具,比如[Review Board](https://www.reviewboard.org/)。
至于代码审查工具的设置,你可以在网上搜索到详细的配置指南。这里我就不一一列举。
我在这里着重讲讲**配置流程中非常重要的一步,就是配置机器审查和人工审查配合的工作流。**代码审查是代码入库前质量保证的重要一环所以通常是和CI工具配合使用。最好能够让机器自动化地完成关于代码风格、静态检查、单元测试等工作这样可以让审查者把最宝贵的时间投入到逻辑、设计等难以自动化的问题上。这里我和你分享一种最常见的工作流。
- 第一将代码提交到本地Git仓库或者用于审查的远端Git服务器的分支上
- 第二把commit提交给代码审查工具
- 第三,代码审查工具开始进行机器审查和人工审查;
- 第四,如果审查通不过就打回重做,开发者修改后重新提交审查,直到审查通过后代码入库。
<img src="https://static001.geekbang.org/resource/image/1f/f1/1f1015d2d04e30b254b4ecc98c4484f1.jpg" alt="">
至于具体的工具集,我这里给出两个例子:
<li>
<p>第一个例子是Facebook采用的方式。他们使用Phabricator作为代码审查工具。同时直接使用原生的Git Server作为代码仓服务器。用户将改动提交到Phabricator然后进行机器和人工审查。检查通过后Phabircator负责将代码推送到Git Server上或者用户手动将本地改动Push到Git Server上。<br>
这个工作流使用的是原生Git Server管理主仓如果你要使用GitLab或者GitHub也可以。关于机器审查的配置Phabricator提供大量的钩子和插件机制非常方便。具体细节你可以参考[Phabricator官方文档](https://secure.phabricator.com/book/phabricator/)。</p>
</li>
<li>
第二个例子是使用GitLab、Jenkins和SonarQube进行配置。具体使用GitLab管理代码代码入库后通过钩子触发JenkinsJenkins从GitLab获取代码运行构建并通过Sonar分析结果。这里有一篇[不错的文章](https://www.jianshu.com/p/e111eb15da90)供你参考。
</li>
好了,以上就是今天文章的第一部分,引入代码审查的三个步骤:一是,就代码审查的工作量达成共识;二是,选择试点团队;三是,确定审查工具和流程。接下来,我们来看本文的第二大部分:成功推进代码审查的关键操作。
## 成功推进代码审查的关键操作
代码审查需要注意的点有很多网上也有很多文章列举了很多最佳实践。从我在Facebook的经验来看其中有两个实践最是直接、明确而且有效一是注意审查提交的原子性二是审查中关注提交说明Commit Message
### 操作一:提高提交的原子性
**代码提交的原子性,**是指一个提交包含一个不可分割的特性、修复或者优化,同时这个提交要尽可能小。
原子性提交的优点是,结构清晰,容易定位问题。一般来说,代码提交的原子性做得越好,代码质量越好。同时,原子性提交因为小而聚焦,也是做好代码审查的基础。
我曾经和一个10x开发者合作一个大功能。他将其拆为了15个原子性提交每一个提交的代码量都控制在500行以下。这15个提交都是我审查的在3天的协作过程中这个同事不断发出审核PR我不断反馈修改意见然后他不断对旧的提交进行修改同时又发出新的PR请求。
大多数时候,都是三四个提交在被同时审查,而这三四个提交是实现同一个功能。整个审查过程非常流畅,效率很高。试想一下,如果不是代码提交的原子性做得很好,绝对难以做到这一点。
这段经历给我的触动非常大,让我深刻意识到了原子性提交对代码审查的重要性。
所以在Facebook代码提交的原子性是代码审查非常重要的指标如果提交的原子性不好常常会被直接打回。我后来在Stand公司就采用了这个办法让大家在审查中首先看原子性。如果一个提交做了几件事不看细节直接打回。因为团队小很快就形成习惯效果很棒。我推荐你在你们团队也尝试类似办法。如果能接受一开始的阵痛并执行下来的话很快就会看到效果。
另外,这里还有**一个实现原子性提交的技巧****在对一个大功能进行原子性拆分的时候,功能开关是一个很好的工具**。这里有一个马丁 · 福勒Martin Fowler的关于[功能开关](https://martinfowler.com/articles/feature-toggles.html)的链接,可供你参考。
### 操作二:提高提交说明的质量
提交说明是提高代码审查的利器,好的格式应该包含以下几个方面:
- **标题**简明扼要地描述这个提交。这部分最好在70个字符之内以确保在单行显示的时候能够显示完整。比如在命令行常用的git log --oneline输出结果要能显示完全。
- **详细描述**,包括提交的目的、选择这个方法的原因,以及实现细节的总结性描述。这三个方面的内容最能帮助审查者阅读代码。
- **测试情况**,描述的是你对这个提交做了什么样的测试验证,具体包括正常情况的输出、错误情况的输出,以及性能、安全等方面的专项测试结果。这部分内容,可以增加审查者对提交代码的了解程度以及信心。
- **与其他工具和系统相关的信息**比如相关任务ID、相关的冲刺sprint也可翻译为“迭代”链接。这些信息对工具的网状互联提供基础信息非常重要。
这里还有一个Git的技巧是**你可以使用Git的提交说明模板Commit Message Template来帮助团队使用统一的格式**。比如可以像下面这个例子一样使用git config --global commit.template命令来设置模板。
```
# 配置文件:提交说明模板文件 ~/.git_commit_msg.txt
&gt; cat .git_commit_msg.txt
Summary:
Test:
Task ID:
# 设置上述文件为提交说明模板。
&gt; git config --global commit.template ~/.git_commit_msg.txt
# 使用实例之后git commit 命令自动使用上述模板
&gt; git add app.js
&gt; git commit
Summary:
Test:
Task ID:
# Please enter the commit message for your changes. Lines starting
# with '#' will be ignored, and an empty message aborts the commit.
#
# On branch master
# Your branch is up to date with 'origin/master'.
#
# Changes to be committed:
# modified: app.js
```
通过制定严格的提交说明格式来规范其质量,可以方便审查者查理解被审查代码的意图、实现思路,并通过测试情况,加快对代码的理解,提高对代码质量的信心,从而大大提高审查者的效率。同时,严格的提交说明格式及好的说明质量也可以督促开发者提高代码质量。所以说,它是一个简单、直观且有效的代码审查落地实践。
下面,我就给出一个之前我在一个公司逐步提高提交说明要求的一个具体例子,供你参考。
当时的情况是大家的提交说明非常简短很多只包含一句话。比如功能提交常常写个“实现A、B功能”Bug修复提交则更简单通常就三个字母“Fix”。针对这个情况我采取了以下三个步骤
**第一步**,规定提交说明一定要包括标题、描述和测试情况三部分,但暂时还不具体要求必须写多少字。比如,测试部分可以简单写一句“没有做测试”,但一定要写。如果格式不符合要求,审查者就直接打回。这个格式要求工作量很小,比较容易做到,两个星期后整个团队就习惯了。虽然只是在提交说明里增加了简单描述,也已经为审查者和后续工作中进行问题排查提供一些必要信息,所以大家也比较认可这个操作。
**第二步**,要求提交说明必须详细写明测试情况。如果没有做测试一定要写出具体理由,否则会被直接打回。这样做,不但为审查者提供了方便,还促进了开发人员的自测。整个团队在一个多月后,也养成了详细描述测试情况的习惯。
**第三步**,逐步要求提交的原子性。我要求每一个提交要在详细描述部分描述具体实现了哪些功能。如果一个提交同时实现了多个功能,那就必须解释为什么不能拆开提交;如果解释不合理的话,提交直接被打回。
这一步实施起来比较困难原因包括大家对功能拆分不习惯或者不熟悉、对Git操作不熟悉。针对这些问题我通过内部培训来提高团队的Git能力。同时我先在一个小团队内集中精力实践提交的原子性通过我的直接参与来快速提高这个小团队的能力。然后再让这个团队帮助其他团队提高。
大概三个月以后,整个团队在提交原子性方面提高了很多,代码审查也就真正有效地做起来了。
可以看到,在这个过程中,提交说明起到了抓手的作用,有效地帮助团队推进开发自测,以及提高代码原子性。
好了,以上内容就是成功推进代码审查的两个关键操作,即提高提交的原子性,以及使用提交说明做抓手。最后,我们再来看看成功推行代码审查在文化方面的两个原则。
## 成功推行代码审查的两个关键原则
### 代码审查原则一:互相尊重
代码审查是两个开发者之间的技术交流,双方都要谨记互相尊重的原则。
**从代码作者的角度来看**,审查者要花费时间去阅读他并不熟悉的代码,来帮助你提高,应该尽量为审查者提供方便。
比如,提高提交说明的质量,就是对审查者最基本的尊重。还有,如果你的代码都没有进行自测就提交审查,你觉得审查者心里会怎么想呢?又比如,如果你提交的一个审查有一万行代码,让审查者怎么看呢?所以,代码作者一定要提审查者着想,帮助审查者能够比较轻松地完成审查。
这里还有一些细节性的问题:
1. 注意描述文字的格式。比如使用Markdown格式书写在GitHub、GitLab等工具上就会比较美观。这些格式方面的问题可能有的开发者会觉得麻烦而不屑于去做。但实际上这样的细节会让审查者更愿意也更容易去阅读你的提交说明提高代码审查的效率。这也是对审查者的尊重。
1. 在描述测试情况的时候尽量提供真实的输出如果是UI改动的话最好能够提供截屏。提交说明只支持文字但你可以把图片上传到其他地方然后提供链接。这样审查者可以更直观的看到修改效果对审查效率的提高有非常大的帮助。
1. 如果需要审查者特别注意某一方面,要明确指出。如果有些代码过于复杂,可以主动找审查者当面讨论。
**从审查者的角度来看**,在提出建议的时候,一定要考虑代码作者的感受。最重要的一点是,不要用一些主观标准来评判别人的代码。
在Facebook的时候我团队里有一个同事对一些技术的细节特别坚持己见。本来两个实现方式的效果差不多设计也各有优劣但他要求作者一定要按照他的思路来实现。同时他的语言能力特别强常常在讨论里面长篇大论地写他的理由让代码作者非常头痛降低了大家的研发效能。最后还是大家都在绩效考评时给他提意见他才改了一些。
尊重代码作者的做法,还有:
1. 在打回提交的时候,一定要礼貌地描述原因。
1. 审查要尽量及时,如果不能及时审查要告知作者。
这些都只是互相尊重的一些具体实践。在做代码审查时,我建议你随时记得要互相尊重,多为对方考虑。只有这样,代码审查才能顺畅。
### 代码审查原则二:基于讨论
代码审查常常出现问题的一个地方是,在审查过程中因为意见不同而产生争执甚至争吵,所以一定记住**代码审查的目的是讨论,而不是评判**,作为管理者一定要在团队中强调这个原则。
讨论的心态,有助于放下不必要的自尊心,从而顺利地进行技术交流,提高审查效率。另外,讨论的心态也能促进大家提早发出审查,从而尽早发现结构设计方面的问题。
在Facebook时我们常常会发出一些目的只是讨论的代码审查讨论之后会抛弃这个提交然后重新发出新的代码效果非常不错。
另外,我还有一些关于讨论的建议:
- 审查者切记不要说教,说教容易让人反感,不是讨论的好方法。
- 审查者提意见即可,不一定要提供解决方法。我曾经见过一个团队要求提出问题必须给出对应的答案,结果是大家都不愿提问题了。
- 想办法增加讨论的有趣性。在Facebook做代码审查的时候我们常使用图片进行讨论用有趣的方式表达自己的意见。这样做有两个好处一是容易被对方接受二是开发工作比较枯燥我们应该主动找点乐趣你说对不对
比如说,如果觉得代码提交太大,审查者就可能会贴一张有很多星球的图片。星球按由小到大小到的顺序排成一行,最左边的是地球,右边是木星,再右边是太阳。天体越来越大,最右边一个超级大,但是标签上写的不是星球的名字,而是“你的代码提交”。大概是这样一幅图:
<img src="https://static001.geekbang.org/resource/image/3c/18/3c23ac9dad6688618b8328fc789d7618.jpg" alt="">
以上就是两个文化相关的原则。互相尊重和基于讨论。只有这样,才能实现代码作者和审查者的双赢:代码作者的代码质量得到提高,代码审查者能顺畅审查代码。
## 小结
几乎所有的开发团队都适合使用工具进行代码审查。无论团队大小如何,都可以通过合适的代码审查进行高性价比的讨论。
在今天的这篇文章中,我针对代码审查的引入、推进,以及文化原则三个方面,给出了一些建议。
在引入阶段,我有三个建议:
1. 团队统一思想,代码审查是有效工作的一部分,应该计算到工作量里面;
1. 选择合适的试点团队;
1. 让机器审查和人工审查结合,使得人工审查更聚焦。
在推进实施的阶段,我推荐提高提交的原子性,以及重视使用提交说明两个关键操作。
最后,我建议通过互相尊重和基于讨论这两个原则,从文化的角度固化团队的代码审查实践。
这三个方面的建议是我基于Facebook的经验总结得出的是Facebook高效代码审查的重要原则和方法可以帮助你在团队中推动高效代码审查。
同时,这三个方面的措施也分别对应了在一个团队引入、推进、深化代码审查的步骤。希望对你在团队中引入代码审查的具体过程有一定的借鉴作用。
## 思考题
1. 你见过或者经历过推行代码审查的成功或者失败案例吗?你觉得成功或失败的原因是什么呢?
1. 上面提到GitHub、GitLab、Gerrit都是用Git分支来存储被审核的代码。只有Phabricator使用数据库存储。你知道为什么吗提示Phabricator里没有“Git”这三个字母。
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,158 @@
<audio id="audio" title="14 | 质量与速度的均衡:让“唯快不破”快得更持久" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/9f/a3/9f304242d9cd924929993dbbf41a6ba3.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊团队可持续性的快速开发,怎样才能让“唯快不破”快得更持久。
最近几年,一提到开发,很多人想到的都是“天下武功,唯快不破”。也就是说,开发过程越快越好,越快越有竞争力。这的确是软件开发,尤其是互联网行业软件开发的不二法则。也正如我在前面文章中多次提到的,快速开发可以快速得到用户反馈,更快地验证用户价值假设。无疑,这是高效开发的重要原则。
因此,我们在实际工作中,往往会为了快而选择各种“捷径”。比如:
- 要开发已有功能的一个相似功能因为时间很紧就先copy &amp; paste保证功能按时上线。
- 需要在一个函数里增加功能这个函数已经有800行了加上新功能后会有1000行。重构这个函数是来不及了先把功能加上去再说。
说是“捷径”,是因为这些都不是最优解,有点儿投机取巧。它们的确能让我们在短期内保证快速交付,满足业务发展需求。但如果没有任何补救措施的话,时间长了我们就再也快不起来了。
比如“copy &amp; paste”方式的编程会导致后续添加功能时需要在很多地方做类似修改工作量大且容易出错。再比如无视函数变大的操作会导致后续的修改、调试异常困难。
这些问题都会成为开发工作中的**技术债**,也就是**在开发产品或者功能的过程中,没有使用最佳的实现方法而引入的技术问题**。无疑,这些技术问题会为将来的产品维护和开发带来额外开销。只有正确地处理技术债,才能让我们的研发持续地快下去。
接下来,我们就来看看技术债的成因、影响,以及对应的处理方法。
## 技术债的成因
从成因来看,技术债的引入包括主动和被动两种。
- 主动引入,即开发人员知道某一个实现会产生技术债,但仍采用这样的实现。最常见的情况是,由于业务压力,在时间和资源受限的情况下不得不牺牲质量。
- 被动引入,即不是开发人员主动引入的技术债。常见的情况有两种:一是,产品不断演化,技术不断发展,原来的设计、实现落伍了;二是,开发团队的能力和水平有限,没有采用好的开发方法、实践。
所以说,技术债是无法避免的,我们要做的就是明确它的影响、处理好它。
## 技术债有哪些影响?
提到技术债,我们想到的往往是它的坏处,比如难以维护、难以增加新功能等,但实际上它也有巨大好处。
关于技术债的好处,我们可以对应着金融领域的经济债务来理解。我们都知道,经济债务最明显的好处在于,可以帮助我们完成很多本来不可能完成的任务,比如贷款买房。相应的,技术债可以在短期内帮我们快速完成业务开发,满足用户需求,就类似房贷的作用。
但跟经济债务一样,技术债也需要偿还,也会产生利息,而且是利滚利。也就是说,每一步累积的技术债都会叠加起来,为开发增加越来越大的难度。长期来看,如果一直借债不还,开发新功能会越来越慢,产品维护越来越难,甚至是无法维护必须推到重来,就像还不上房贷房子被银行收回一样。
那么,技术债务应该如何处理、如何偿还呢?
## 处理技术债的基本原则是什么?
在我看来,处理技术债的基本原则有以下两个方面。
**第一个方面,要利用技术债的好处,必要时要大胆“举债前行”**。也就是说,在机会出现时,使用最快的方式完成业务服务用户,抢占市场先机,“不要在意那些细节”。
一个具体的例子是RethinkDB在与MongoDB的竞争中失利。在技术上RethinDB比MongoDB更追求完美但比MongoDB发布稳定版本晚了三年错过了NoSQL的黄金时机最终在2017年1月份宣布破产。在这个过程中他们没有充分利用技术债抢占市场应该是竞争失败的一个重要原因。文章中我放了两个链接供你阅读参考你可以了解一下RethinkDB公司的人以及外部用户对他们的失败进行的反思[文章1](http://www.defmacro.org/2017/01/18/why-rethinkdb-failed.html)、[文章2](https://news.ycombinator.com/item?id=13421608)。
**第二个方面,要控制技术债,在适当的时候偿还适当部分的技术债。**
在我看来,国内大部分公司的业务驱动做得比较好,大都能够比较充分地利用技术债的好处,但在技术债的管控方面,通常做得不太够,具体来说就是常常有大量技术债堆积,给业务长期发展带来巨大阻碍。
所以在下面的内容中,我会与你详细讲述应该怎样控制技术债。
## 如何控制技术债?
从我的经验看控制技术债主要有以下4步
1. 让公司管理层意识到偿还技术债的重要性,从而愿意投入资源;
1. 采用低成本的方式去预防;
1. 识别技术债并找到可能的解决方案;
1. 持续重构,解决高优先级技术债。
接下来我们分别看看这4步具体如何实施吧。
### 1. 让公司管理层意识到偿还技术债的重要性,从而愿意投入资源
通常来说,开发人员能直观感受到技术债的坏处,大都愿意去偿还技术债,所以技术债累积的主要原因是,管理层不理解,或者说是没有认识到技术债累积给业务发展带来的巨大坏处。
这也就意味着,解决技术债的第一步就是,让管理层意识到偿还技术债的重要性,从而愿意投入资源去解决。在我看来,让管理层理解技术债比较直观、有效的方式,就是上面提到的与经济债务的类比。
另外一个办法是,将偿还技术债与业务发展联系起来。如果能够说明某一项技术债已经阻碍了公司重要业务的发展,说服管理层投入资源解决技术债就会比较容易。
### 2. 采用低成本的方式预防
所谓具体问题具体分析,我们在预防技术债时,也需要根据技术债的成因采取不同的措施。
对主动引入的技术债,要尽量让管理层和产品团队了解技术上的捷径将会带来的长期危害,从而在引入技术债时客观地权衡其带来的短期收益和长期损害,避免引入不必要的技术债。
在被动引入的技术债中,由于产品演化导致设计落伍的问题不是很好预防。而由开发团队的能力问题引入的技术债,我们可以使用加强计划和代码审查等方法实现低成本的预防。
其中,加强计划,可以帮助开发人员更合理地安排时间,从而有相对充裕的时间去学习并选择更优秀的功能实现方案。而代码审查的作用就更好理解了,它可以帮助我们在早期发现一些不必要引入的技术债,以更低的成本去解决它。
关于技术债的预防,我还有一个小贴士,就是在接口部分多下功夫。因为接口涉及实现方和多个调用方,所以接口部分累积的技术债,影响范围通常比较大。而与之相对应的模块内部实现,技术债的影响范围就比较小。所以,在涉及主动引入的技术债时,我们需要区别对待接口部分和实现部分。
### 3. 识别技术债并找到可能的解决方案
对不能预防的技术债我们需要高效地把它们识别出来并了解常见的解决办法。其中对于主动引入的技术债可以在引入的时候就添加任务到Backlog。而对于被动引入的技术债则需要周期性的审视这需要技术管理者主动地收集、整理技术债问题。
总结来说,技术债可以分为两大类:复杂度相关和重用性相关。我们可以关注这两个方面来识别技术债。
**第一是,复杂度相关。**
史蒂夫 · 迈克康奈尔Steve McConnell在其经典著作《[代码大全](https://book.douban.com/subject/1477390/)》中,提出的一个核心观点是:**如何处理复杂度是软件开发最核心的问题**。我非常认同这个观点因为人类大脑容量有限大概只能同时记住7项内容而软件包含的元素非常复杂远超过7项。所以要实现可维护的软件我们必须想尽办法去降低其复杂度。
具体来说,我们在开发时,要时刻注意会增加代码复杂度的“坏味道”,比如:
- 组件间依赖混乱,职责不清晰;
- 组件、文件、函数太大,包含的内容太多;
- 使用不必要的、复杂的设计范式;
- 函数、接口参数太多等。
**解决复杂度问题的基本原则是,把一个系统拆解为多个子系统,用抽象和分层的方法,让我们同时只面对有限的信息,并且能够有条理地深入到每一个子系统中查看细节**。具体的解决方法有:
- 对系统进行二进制组件或者代码层面的解耦;
- 使用简单化的设计编码原则,避免不成熟的优化;
- 对常见的代码“坏味道”做出一些规范比如限制代码行的长度、禁止循环依赖、限制圈复杂度Cyclomatic Complexity
- 对复杂的设计添加注释。
**第二是,重用性相关。**
软件开发的另一个重要原则是DRY即Dont Repeat Yourself。代码重复是一个很常见的技术债在软件抽象的各个层次比如应用、架构、组件、代码都会出现。避免重复的具体方法有
- 应用层面,复用业务单元,典型案例就是业务中台;
- 架构层面,复用基础设施后台;
- 组件层面,避免出现责任重叠的组件、数据存储等;
- 代码层面,避免出现重复函数、代码块。
接下来最后一步,就是要持续性地重构,去解决高优先级的技术债任务。
### 4. 持续重构,解决高优先级的技术债
作为技术管理者,除了业务目标外,还要制定团队的技术目标,来解决最重要、最紧急的技术债任务。
技术债任务的具体处理方法有两种:一种是,把技术债的任务和业务相关的任务放到一起,在每一个迭代中持续完成;另一种方法是,采用突击的方式,在某个特定的时间段集中解决技术债问题。
比如我在Facebook和微软的时候我们团队就都使用过Bug Bash的工作方式也就是在每几个迭代以后专门花几天时间来解决前面遗留下来的Bug而不开发新功能。这样做的好处有两个
- 第一集中精力修复Bug可以减少上下文切换能够更聚焦在提高产品质量上因为提高质量和写新功能的思路是有区别的。
- 第二,能够让团队成员短暂地从紧张的业务气氛中脱离出来,从而精力充沛地投入到下一个业务开发迭代中去。
## 小结
在今天这篇文章中,我与你介绍了要想让开发工作能够持续地快下去,正确的做法是在恰当的时间“举债前行”,而在平时的开发工作中要持续定位技术债任务,并解决高优先级的部分。
为了帮助你理解技术债与公司业务发展的关系我再和你分享一个案例。A、B、C三个公司对待技术债的态度分别是
- A公司只关注业务不偿还技术债
- B公司持续关注技术债但对业务时机不敏感
- C公司持续关注业务和技术债。对业务机会很敏感敢放手一搏大量借贷也知道什么时候必须偿还技术债。
A公司在开始的时候业务产出会比较多但由于技术债带来的影响效率会逐渐降低。
B公司在开始的时候业务产出比较少但由于对技术债的控制所以能够保持一个比较稳定的产出在某一时间点超过A公司。
C公司在有市场机会的时候大胆应用技术债同时抽出一小部分时间精力做一些技术债预防工作。这样一来在一开始的时候C的业务产出介于A和B之间但和A的差距不大。
随后在抢占到一定的市场份额之后C公司开始投入精力去处理技术债于是逐步超过A。另外虽然C公司此时的生产效率低于B公司但因为市场份额的优势所以总业绩仍然超过B。在高优先级技术债任务处理好之后C公司的生产效率也得到了提升将B公司也甩在了身后。
<img src="https://static001.geekbang.org/resource/image/d8/e5/d8564265399a1f2da7191a94354557e5.jpg" alt="">
这个例子很有代表性,你可以用它来说服管理层在偿还技术债上做投入。
## 思考题
经济债务可以申请破产保护,你觉得技术债可以有这样的福利吗?为什么呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,170 @@
<audio id="audio" title="15 | 开源从Phabricator的开源历程看开源利弊" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/51/ec/51eda8d146f754b71d079de417605bec.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊开源这个话题。
从克莉丝汀· 彼得森Christine Peterson1998年提出“开源”这个名词到今天已经21年了。可以说在这些年里开源改变了软件开发世界。如今开源覆盖了IDE、移动端开发、前后端开发、运维、服务治理、AI等众多领域的项目。比如[GitHub上2018年最流行的前十个项目](https://octoverse.github.com/projects#repositories)包括VSCode、React Native、Angular、Ansible、Kubernetes、TensorFlow等对这些领域都有覆盖。
<img src="https://static001.geekbang.org/resource/image/a7/ad/a75f98e91a355d48be5f95e3ac4373ad.jpg" alt="">
从使用者的角度看开源软件的价值不言自明。可以说99%的科技公司都在使用开源软件。
从贡献者的角度看前十个项目中有8个项目的背后都有公司做支撑。毫无疑问开源对公司来说也有吸引力的。但是很多公司并没有开源尤其是国内做开源的公司更是比较少原因是什么呢其实就是因为开源有很多坑。
我在Facebook时参与了Phabricator开源的全过程见证了其为公司带来的好处比如因为模块化带来的代码质量提升、从开源社区获得的资源支持也见证了开源的一些弊端比如因为和开源社区目标不一致而带来的运维成本增加以及最终导致的项目Fork。
## Phabricator的整个开源过程
一般来说,开源一个项目的流程包括以下九步:
1. 公司/员工对某项目有开源的意愿;
1. 权衡利弊决定是否开源,以及后续的维护计划;
1. 法律和信息安全方面的审核;
1. 选择License
1. 选择Contributor License Agreement
1. 选择版本控制代码服务商比如GitHub、GitLab、BitBucket等
1. 代码模块化,与公司代码分离;
1. 正式开源,发布信息;
1. 项目维护和持续开发。
接下来我就以Phabricator的开源过程为例帮助你理解公司进行开源的利弊以及使用它来提高研发效能的一些原则和实践。
### Facebook决定对Phabricator进行开源
Phabricator源自Facebook内部对代码审查的需求后逐渐发展为软件开发的一个Web工具套件包括代码审查、代码仓托管、缺陷跟踪、项目管理、团队协作等应用程序。
开源之前Phabricator主要由开发工具团队维护并增加新功能其他开发人员也会向其贡献代码。它发展得非常快为Facebook的开发和质量保障提供了很大帮助。但Phabricator有个问题就是经常会出现严重的性能问题。具体来说就是Phabricator的速度会随着时间推移而逐步下降每隔一年左右就会达到让开发人员无法忍受的地步。
导致这个性能问题的主要原因有两个:
- 第一, Phabricator和Facebook.com在同一个代码仓共享Facebook.com的底层代码库但开发人员对Phabricator的响应速度要求比用户对Facebook.com要高。
- 第二非开发工具团队的开发人员在增加功能的时候因为不了解全貌所以在贡献代码的同时往往会降低Phabricator的速度。一个功能降低的速度看不出来但累积起来总体速度的下降就很明显了。
所以每隔一年左右我们就需要对Phabricator做一次重构来提高响应速度。
2010年年中的时候这个性能问题再度爆发了开发工具团队决定认真思考有没有更好的解决办法从根本上解决这个问题。经过仔细分析我们得出的解决方案就是和 Facebook.com解耦。
正好这个时候开源社区对Phabricator的代码审核功能非常感兴趣。我们认为开源Phabricator或许是一个可行的办法同时调研结果显示开源Phabricator有以下好处
1. Phabricator是一个内部工具不是面向用户的产品开源非但不会影响公司的核心竞争力还可以提高影响力。
1. 开源自然而然地就会把它从主代码仓剥离出来实现与Facebook.com的解耦实现提速。
1. 开源意味着代码从此要公开出去更多的人可以看到。这就给Phabricator的开发人员带来压力让他们更关注产品质量。这样一来Phabricator的性能就会更有保障。
1. 可以利用开源社区的开发资源。
当然开源Phabricator也有缺点
1. 开源之后Phabricator势必要支持更加通用的开发场景这可能就会影响对Facebook特有场景的支持。
1. 开源之后,代码不会像在内部那样容易管控,灵活性会降低。
经过分析我们认为可以使用插件的形式从技术上解决对Facebook特有开发场景的支持问题。也就是说在Facebook内部创建一个单独的代码仓作为插件集成到开源的Phabricator之中。
而对Phabricator的代码管控问题我们可以把它放到Facebook组织之下从而保留比较强的管控力。
所以综合分析内部工具团队以及上一级的基础平台团队决定这一次的重构目标是开源Phabricator。
### 开源准备工作
在确认了开源Phabricator之后我们还要完成一些非开发的准备工作。
第一,法律和信息安全方面的审核。
这一步主要是确认此项目的开源,会不会使公司面临法律和信息安全方面的风险,由公司的律师团队和安全专家操作。一般来说,法律风险重点关注是否会泄露自己公司以及第三方公司知识产权;信息安全方面关注是否会暴露公司的安全漏洞。
第二,选择授权协议。
授权协议包括开源软件授权协议Open-source License和开源贡献协议两种。
其中开源软件授权协议指的是使用者享有的权利和受到的限制比如GPL、MIT、Apache等协议。Phabricator选择的是Apache 2.0。这里,有两个工具可以帮助你做出选择,分别是“[怎样选择开源协议?](https://choosealicense.com/)”和“[开源指南](https://opensource.guide/)”。
开源贡献协议,指的是对软件贡献者权力的限定,目的是赋予开发者对开源项目贡献代码的权力,并赋予项目管理者按照软件授权协议去发布软件。它包括 CLAContributor License Agreement和 DCODeveloper Certificate of Origin两种。Phabricator选择的是CLA。关于这个协议的选择你可以参考“[CLA和DCO的区别](https://opensource.com/article/18/3/cla-vs-dco-whats-difference)”这篇文章。
因为具体选择哪个协议与法律有关,所以我只给出了参考链接,如果你的公司需要开源项目,推荐你去咨询律师。
第三选择版本控制代码服务商。当时我们选择的是开源方面最流行的GitHub。
### 开源具体步骤
完成了准备工作之后,剩下的就是正式的开发工作了。这部分工作主要包括以下三步。
第一步把Phabricator代码和Facebook代码解耦。
我们做了一次比较彻底的重构把分散在各处的代码集中到5个代码仓里分别是底层的API库Libphutil、网站应用集Phabricator、客户端Arcanist、文档系统Diviner以及Facebook内部功能插件模块完成了Phabricator的模块化。
第二步,进一步优化性能。
针对代码的性能尤其是底层的API库我们进行了很多优化。因为开源以后只需要支持通用的开发场景所以我们不必考虑原来在Facebook代码仓的复杂调用更容易去针对性地提高性能。
第三步,支持功能定制。
功能定制是开源的主要难点。除了解耦我们还需要保证在解耦之后仍然能够灵活地添加Facebook开发人员需要的定制需求。主要有以下三种方法
- Phabricator提供对象的字段Field、类、库3个级别的扩展我们主要采用库级别的扩展实现对Facebook的定制功能。
- Phabricator提供接口供Facebook内部工具调用。
- Facebook内部工具代码提供接口供Phabricator调用。
这样一来我们就实现了Phabricator和Facebook其他内部工具的无缝集成。
除此之外为了把Phabricator的部署从Facebook内部工具拆分出来我们还需要完成以下工作
- 数据库的迁移即把Phabricator的相关数据从Facebook的数据库中迁移出来。
- Phabricator的部署。开源前Phabricator属于内部工具网站的一部分所以不需要单独部署但开源后我们需要给它重新设计和实现一套部署系统。
完成这些开发工作后Phabricator不仅从Facebook中剥离了出来还显著提高了代码质量比如模块化更好、注释更清晰、性能更好等。这些正是开源为Facebook带来的重要好处。同时因为参与开源项目可以回馈社区并提升个人影响力所以公司内部的Phabricator开发人员也都热情高涨。
但开源也意味着我们需要投入额外的精力去实现Phabricator与其他内部工具的无缝集成才不会影响Facebook开发者的使用体验。这也是开源要付出的代价。
### 开源初期发展
完成开发工作后Facebook正式对外宣布了Phabricator的开源同时正式切换到新部署的Phabricator集群。整个切换过程比较顺利只是在一开始的时候Phabricator和其他工具间的联动出现了一些Bug修复之后就稳定下来了。
于是Phabricator也就开始进入开源的代码仓和内部的插件代码仓同时开发的阶段。针对Facebook的内部需求我们尽量把它通用化放到开源的代码仓中实现实在需要定制的才会放到Facebook的插件代码仓中。
这时我的一位同事从Facebook离职去了开源社区全职为Phabricator工作。他还创立了一家公司致力于Phabricator的商用。于是我们在开源社区也有了更强大的资源支持。
从2011年年初开源到2013年年底我离开Phabricator项目Facebook和开源社区对Phabricator的发展目标是一致的所以一直在合力增加Facebook需要的功能合作得非常好。总的来说我们的确充分利用了开源社区开发者对Phabricator的贡献。同时业界的很多著名公司开始使用Phabricator包括Uber、Pinterest、Airbnb等提升了Facebook的声望。
### Fork
2014年开始开源社区支持的公司越来越多而它们的使用场景和Facebook不太一样也就是说Facebook要想继续使用Phabricator的最新版本就必须花费较大成本进行版本更新及维护。
而因为Facebook在Phabricator的使用上累积了非常多的数据所以每一次数据库的Shema变动都会带来非常麻烦的数据迁移工作常常需要DBA的帮助才能实现不中断服务的版本更新。
考虑到这些新增功能对Facebook用处不大而维护的成本又很高所以2014年下半年Facebook决定停止使用外部开源的Pabricator重新在公司内部自己维护一套Fork的Phabricator代码。这样一来开源版的Phabricator引入新功能的时候Facebook只在需要的情况下才会参考外部的实现在内部引入。
其实公司和开源社区目标不一致的现象比较普遍。在我看来这可以算是开源项目的第一大坑。Facebook对Phabricator采取的措施是内部Fork让开源社区继续自由发展既然不能从开源社区得到资源就把代码挪回公司内部获取完全的管控和自由度。这是处理目标不一致问题的第一种方法你也可以借鉴。
第二个办法是对代码仓强管控但结果往往是开源社区Fork一个新项目重起炉灶和第一种方法的结果其实差不多。
除了Fork之外还有第三种办法就是采用不同的分支来支持不同的目标。这样的好处是公司依然可以获得开源社区的资源支持坏处是分支管理、版本管理繁琐也缺乏Fork的灵活性。
以上就是Facebook开源Phabricator到最终Fork的全过程。我在这其中讲述了Facebook处理具体开源问题的一些方法你也可以借鉴到自己的项目中。
## 开源对公司的利弊
这里,为了帮助你加深理解,我把开源对一个公司的利弊做了总结整理,如下表所示。
<img src="https://static001.geekbang.org/resource/image/56/f4/56a63f7c3ba5022c50ceeb1e1e481ff4.jpg" alt="">
总的来说,我认为开源在以下两种情况下最为有利:
- 第1种情况是大公司。不难发现上面列举的各种好处对大公司比较明显提高公司声誉就是典型例子。所以2018年GitHub前十名开源项目中除了NPM和Ansible外其他的8个项目都是由Microsoft、Facebook、Google三个大公司支撑。
- 第2种情况需要通过开源获取影响力从而扩展业务的公司。比如Docker公司在开源Docker项目之前名气并没有多大但是把Docker项目开源之后一下子就变成了明星企业在改变了Pass发展格局的同时也改变了自己的命运。
另外从适合开源的项目的角度来看平台、基础设施、工具等比如Phabricator以及2018年GitHub前十名开源项目适合开源而业务层的项目因为通用性不强不适合开源。
## 小结
开源正在改变软件开发的格局选择开源自己的项目对公司来说也是有利有弊。所以今天我以Phabricator的开源过程为例和你分享了开源一个项目涉及哪些步骤在这其中获得的好处以及需要付出的代价。
开源对公司的好处,主要表现在提高代码质量、得到开源社区的免费帮助、提高开发者的积极性、提高公司声誉、回报社区等。而缺点和挑战,主要包括定制困难、内外协调,以及版本维护等。如果你的公司或者团队在考虑是否开源,可以将这些利弊作为参考。
在我看来Phabricator算是开源的一个成功案例。因为在整个过程中我们充分利用了开源带来的好处而且在开源过程中投入的开发资源即使不开源也是需要的。
一定程度上讲开源Phabricator的过程也体现了Facebook的实用主义。在需要开源的时候放手去开源在发现维护的性价比不好时就果断Fork。虽然从个人情感的角度说我不愿意看到Phabricator在Facebook内部最后Fork了但理智地看这的确是一个很好的决定。
## 思考题
跟硅谷相比,国内公司参与开源的非常少。你觉得主要原因是什么,将来的趋势又会是什么样的呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,108 @@
<audio id="audio" title="16 | 高效上云:如何用云计算来提高效能?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/c9/90/c9787de0efb51cbe4858b2381f312a90.mp3"></audio>
你好,我是葛俊。今天,我们来聊一聊,如何使用云计算来提高研发效能。
自从AWS的出现云的崛起已经成为软件开发不可阻挡的趋势。它逐渐像水和电一样成为软件开发的一项基础设施。不容置疑的是云极大地提升了软件研发的效能。
以我之前在Stand公司开发社交App的工作为例项目刚开始时只有3个研发人员包括两个后端和一个前端开发者。我们使用了AWS的云服务三个月就上线了第1个手机版本而且是可以弹性伸缩稳定支撑百万月活的版本。在云出现之前这是难以想象的。
所以说,怎样高效地使用云,包括公有云、私有云和混合云,对每一个团队来说都是一个绕不过去的话题。
云计算的话题很大,但我们今天重点要说的是,落实到研发效能上我们应该怎么做。我将其归纳为以下两点:
- 第一,深入理解在云上进行研发以及运行服务的特点,充分利用它的优势,比如服务化、自助化和弹性伸缩;
- 第二,了解并妥善处理它带来的挑战,比如分布式系统带来的安全和控制方面的问题。
接下来,我们分别看看这两个方面吧。
## 理解并利用云计算的优势
我们先简单看看云计算的定义:云计算把许多计算资源整合起来,使用软件实现自动化管理,通过网络快速提供给用户使用。其中,计算资源,包括服务器、存储、数据库、网络、软件、分析服务等。也就是说,云计算通过自动化和自助化,使得计算能力成为一种商品,在互联网上流通,就像水、电、煤气一样,可以方便地取用,且价格较为低廉。
介绍完云计算的定义,我们再来看看它的特点。云计算的特点包括,大规模、分布式、虚拟化、按需服务、高可用、可扩展等。其对研发效能的提升,我觉得主要可以归结为服务化、自助化和弹性。下面,我们分别来看看这三个方面吧。
### 服务化
在服务化方面云服务按照抽象程度分为3类包括基础设施即服务([IaaS](https://baike.baidu.com/item/IaaS/5863121))、平台即服务([PaaS](https://baike.baidu.com/item/PaaS/219931))和软件即服务([SaaS](https://baike.baidu.com/item/SaaS/6703273))。它们也被称为云计算栈最下层是IaaS最上层是SaaS越上层的服务越抽象也就是把下层细节封装的越多由云平台处理的越多。当然如果不使用云的话这些所有的服务和资源都需要我们自己管理。
如下图所示,分别列举了这三种服务方式中,哪些服务和资源由云服务商提供,哪些由我们自己处理。
<img src="https://static001.geekbang.org/resource/image/ae/e6/aef50819b3ba6e197a910a8fa1278fe6.jpg" alt="">
备注:图片引自[https://www.bmc.com/blogs/saas-vs-paas-vs-iaas-whats-the-difference-and-how-to-choose/](https://www.bmc.com/blogs/saas-vs-paas-vs-iaas-whats-the-difference-and-how-to-choose/)
可以看出细节抽象得越多云服务商负责的部分就越多我们就越能够聚焦自己的业务从而提高研发效能。所以在业务开发中我们应该尽量使用抽象的服务也就是说尽量使用SasS不行就使用PaaS还不行再使用IaaS。
再以Stand的App开发为例。因为那款社交App与金融相关同时客户增长速度还不确定所以我们对数据库的要求包括高可用和可扩展两个方面。如果我们自己来维护数据库的话需要招一个专门的DBA同时还需要一定的时间去搭建。但如果使用Pass服务提供的RDS关系型数据库的话高可用和可扩展都可以由AWS承担我们可以全身心地投入到业务开发中快速上线产品。
当然正是因为RDS这个PaaS服务给用户提供了非常大的价值所以它的收费也很高。在我们使用的所有AWS服务中数据库花费大概占到了70%。我推荐你在权衡使用IaaS、PaaS或者是SaaS产品的时候综合考虑其提供的价值以及价格。**一个常见的模式是**初创公司在业务刚起步时使用SaaS或者PaaS快速开发业务业务成长到一定规模之后再逐步转到IaaS以及私有云降低成本。
### 自助化
云服务的第2个特点也是它的重要优势是自助化。也就是说用户在使用云上的资源和服务时尽量减少人工参与主要采用自动化的方式比如通过工具或者API调用来完成工作。这样一来既降低了成本也提高了使用的灵活性和方便性。比如使用云计算我们可以用一个API call去获得一个环境再用另一个API call就可以部署一个版本。这相比传统的、用手工填表的方式效率不知道要高出几个数量级。
总结来讲自助化在软件开发中的价值主要表现在开发环境的获取和CI/CD流程两个方面。
开发环境的获取,包括开发机器环境、联调环境的获取,你可以再回顾下[第11篇文章](https://time.geekbang.org/column/article/136070)中的相关内容。这是自助化最典型的价值。
方便实现CI/CD流程是自助化提供的第二大优势。比如在持续发布方法中有一个很重要的评判标准就是看你的团队能够在多长时间内把一行代码的改动部署到生产环境上去。现在做得比较好的互联网公司大都能够做到分钟级。这一点之所以重要是因为只有能够快速部署才能快速修复错误从而才敢于快速试错实现快速创新在竞争中获得优势。
这其中的工作很多包括拉分支、合并代码、构建、测试验证、部署、监控以及可能的回滚。要实现分钟级的部署我们必须要把服务化和自助化用到极致。而云服务提供的各种自助化就大大降低了CI/CD流程自助化的实现难度。
### 弹性和共享
云服务的一个基本理念是把大量的资源集成起来然后共享给多个客户使用。正是因为多个客户共享资源所以可以实现单个客户的按需使用。也就是说客户A不使用资源的时候可以释放出来供其他用户使用A也不必对这部分资源付费。同时A在需要大量资源的时候资源池里通常可以有空闲的资源供它使用。这也就是我们所说的弹性。云服务的弹性是可以用来提高研发效能的第三大特性。
接下来,我与你分享下具体**如何利用云服务的弹性**。比如,业务量的大小不确定,对很多公司的技术和基础设施都是一个巨大的挑战。如果我们使用自己的机房,不可能非常灵活地添加、删除资源,所以需要投入大量精力进行容量规划,以防止后期业务超过系统承载量而导致宕机。但这样的容量规划,在业务还不够大的时候必然会造成资源浪费。而使用云计算,我们就可以很好地解决这个问题。
如果使用的是公有云,那么你使用的服务本身就可以自动扩容,实现弹性伸缩,所以一定要在可以使用弹性伸缩的地方充分利用它。 比如我在Stand公司的时候我们的网站后端服务器、消息队列中间件、数据库、压测环境等都使用了弹性伸缩而且是尽量使用自动弹性伸缩的功能。
比如后端服务器可以根据CPU的负载量来调节服务节点数量达到一定阈值之后就自动产生新的服务器并添加到集群中当负载量降低到某一个阈值时又会自动释放一些机器。从而既实现了在业务量突增的时候能够保证业务的正常运行又能最大限度地降低成本。
如果使用的是私有云,你的服务和其他公司就不能共享。所以,从共享的角度看,不如使用公有云的好处大。不过在这种情况下,公司内部的各种不同服务、不同团队之间的资源共享,也可以给你带来收益。
比如你可以让开发环境、测试环境、类生产环境、生产环境、内部工具系统环境都使用同一套Kubernetes集群在其中使用不同的namespace进行隔离。这样就可以实现这几个环境的资源共享。又比如你可以在公司内部署一套OpenStack私有云环境让公司的各个产品线都在这个私有云上工作从而实现团队之间、产品之间的资源共享享受弹性和共享的好处。
## 处理云带来的分布式计算的挑战
云计算在给我们带来巨大方便的同时,也因为它的新特性以及开发模式带来了相当大的挑战。在我看来,**云计算带来的最大挑战在于**,为了使用云的弹性伸缩能力,我们的软件架构必须是分布式的,支持水平扩展。
而这种分布式的架构和传统的单体架构区别很大,如果处理不好,会给我们带来很大麻烦,最典型的例子当属微服务。微服务架构非常适合云计算,能够充分利用它的弹性伸缩能力。但如果使用不当、管理不好的话,就会出现调用混乱、依赖不清晰、难以维护的问题。
**分布式计算有很多挑战,从我的经验看来,做好以下两点特别重要:**
<li>
<p>自治和集中管理相结合。要做好分布式计算,我们首先要让解耦的产品团队能够独立进行产品的设计、开发、测试和上线,这样才能真正利用解耦带来的灵活性。但同时,我们必须要有一定集中式的管理,这样才能把控全局。<br>
这其中最重要的是信息可视化,比如系统整体的质量看板,能让大家一眼看到整个系统中各部分的运行状况;又比如,针对调用链复杂难以调测的情况,建设微服务调用链追踪系统,收集每一个客户端请求从发出到被响应经历了哪些组件、哪些微服务、请求总时长、每个组件所花时长等信息,以帮助我们定位性能瓶颈,进行性能调优。</p>
</li>
<li>
错误处理。分布式系统,因为有很多组件同时运行,很多组件都有可能会出错。所以,我们必须要对这些错误进行处理,保证局部错误不会对全局带来非常大的影响。具体来说,可以使用的办法有:
<ul>
1. 信息可视化。通过数据可视化、监控、预警,迅速发现错误以便及时处理。
1. 错误隔离。比如,微服务的一大好处就是,把错误限制在一个小的服务中。
1. 提高系统容错性。只进行系统拆分还不能满足系统对容错性的要求,我们还需要确保一个服务的问题不会影响其他服务,形成所谓的“雪崩效应”。
1. 自动修复能力。也就是说,出现问题能够自动修复,可以大大提高系统的可用性。一个最简单的办法就是,重启服务。
</ul>
</li>
## 小结
在今天这篇文章中,我首先与你介绍了云计算对提升研发效能的作用,第一是服务化,第二是自助化,第三是弹性伸缩。
在产品设计和日常工作中,我们应该注意考虑如何利用云计算的特性,比如快速获取环境、自助化部署、在更高的抽象级别使用资源服务,来提高生产效率。
然后,我与你介绍了云计算带来的挑战以及一些应对方案。正是因为云计算带来了分布式计算,所以它的安全和控制是最大的挑战。具体的解决方法有,自治和集中管理相结合、信息可视化、错误隔离、提高系统容错性、自动修复等。
在我看来云计算的确可以提高开发体验以及产品上线的效能。比如在Facebook的时候我们使用自研的容器及管理系统Tupperware极大地方便了各个环境的获取以及系统的使用效率又比如我们的测试、构建、分支管理都做到了很好的服务化、自助化为产品的快速发布和高质量奠定了基础。
同时我们也在处理分布式计算方面投入了很多精力。举一个典型的例子为了提高系统各个服务的容错能力Facebook会在某一时刻对某一个数据中心的所有服务器断电进行测试并事前会通知每一个服务的所有者确保自己的服务在这样的极端情况下仍能保证业务健康运行。
而我在创业公司的时候,更是充分利用了公有云提供的各种服务来快速上线产品,实现了传统基础设施环境下不可想象的高效能。虽然云计算现在还没有像水、电、煤气那样普遍,但我相信那一天不会太遥远。
## 思考题
在你当前的工作中,你觉得最能够使用云计算来提高研发效能的地方是什么?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,104 @@
<audio id="audio" title="17 | 测试左移:测试如何应对新的开发模式?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/38/7f/38c9fdcfac074d085fb5dc8d07ad207f.mp3"></audio>
你好,我是葛俊。今天,我们来聊聊测试这个话题。
## 为什么需要测试左移,测试右移?
测试可以保证产品质量,重要性不言而喻。但,要做好测试也比较困难,需要克服很多挑战。尤其是,持续交付、敏捷开发等开发模式为传统软件测试方式带来了更大的时间压力。
我们先来看看下面这种熟悉的测试方式都有什么问题测试人员接到项目后参与需求评审然后根据需求文档写用例、准备脚本等开发提测之后正式开始测试、提Bug、回归测试通过后就结束了项目交给运维上线之后投入下一个项目继续重复这样的流程。
这样的流程看似没什么错,但有两大问题:
- 测试人员非常被动。当需求质量、开发质量较差的时候,测试人员只能被动接受。
- 但同时,测试又被认为是质量的责任人。如果因为需求质量、开发提测质量差而导致上线延期,大家通常会首先怪罪测试团队。
这些问题,在新的开发模式下愈发严重。因为这些新的开发模式有一个共同点,就是要缩短产品的交付周期,对自动化的要求越来越高,能够专门留给传统竖井流程中测试环节的时间越来越短,自然更难保证质量。
在极端的情况下,比如在持续部署的模式下,所有测试都是自动化的,已经完全没有留给测试人员专门进行手工测试的时间了。与此同时,测试的能力和质量又是这些开发模式成功的关键。否则,即使可以频繁地构建产品,质量不过关价值也为零。在我看来,《[持续交付:发布可靠软件的系统方法](https://book.douban.com/subject/6862062/)》这本关于持续交付经典图书,更像是一本介绍测试的书,也是因为这个原因吧。
所以,在快速开发模式的挑战下,测试左移、测试右移就应运而生了。这些测试模式,能让测试人员拥有更多主动权,以及更多的时间进行测试。那,到底什么是测试左移和测试右移呢?
## 什么是测试左移和测试右移?
测试左移和右移,就是把测试的范围从传统测试的节点中释放出来,向左和右扩展。
向左扩展,就是让测试介入代码提测之前的部分。比如,扩展到开发阶段,在架构设计时就考虑产品的可测试性,并尽量进行开发自测等。另外,测试可以更进一步扩展到需求评审阶段,让测试人员不只是了解需求,更要评估需求的质量,比如分析需求的合理性以及完整性等。
类似的,测试右移,是让测试介入代码提测之后的部分。比如,测试人员在产品上线过程中,利用线上的真实环境测试。另外产品上线之后,测试人员仍然介入,通过线上监控和预警,及时发现问题并跟进解决,将影响范围降到最低。这样一来,测试人员不但有更多的时间进行测试,还能发现在非生产环境中难以发现的问题。
从测试右移的概念中可以看出,它与我们下一篇文章“蓝绿红黑灰度发布:这些五颜六色的发布到底怎么用?”中的各种部署模式强相关,所以我会在下一篇文章中与你介绍。而在今天这篇文章中,我会与你详细介绍测试左移的原则和实践。
## 测试左移的原则和实践
在我看来要做好测试左移有3个基本原则
- 调整团队对测试的态度;
- 把测试添加到开发和产品需求步骤中;
- 频繁测试,快速测试。
接下来我们具体看看这3个原则吧。
### 测试左移原则一:调整团队对测试的态度
调整团队对测试的态度,打破竖井的工作方式,是测试左移的前提。**一个有效的办法是,按照功能的维度管理团队,让整个功能团队对产品负责。**也就是说,如果产品质量出现问题,不只是测试团队“背锅”,而是会影响整个功能团队的绩效。同时,让质量问题的直接责任人承担更多的责任,来进一步增强团队成员的责任心。这种利益绑定的办法,虽然简单但非常有效,只不过出现质量问题时要记得进行根因分析,以避免再次出现类似问题。
**另外,还要改变团队成员对测试工作的认知。**传统的工作方式中我们通常认为发现Bug最重要但其实为了提高产品质量更重要的是预防Bug。所以说在测试左移的过程中我们应该更聚焦在预防Bug上。
### 测试左移原则二:把测试添加到开发和产品需求步骤中
**测试左移的第一步,是把测试工作融入到开发步骤中**。常用的办法是,让测试人员参与到开发阶段的方案设计中,了解开发的实现方式。因为很多开发人员可能只熟悉他负责的那一部分,而测试人员往往对全局更加了解,所以测试人员要评估改动范围以及是否有遗漏的模块和系统。
另外一个比较彻底,也很有效的方法是,我在[第8篇文章](https://time.geekbang.org/column/article/132539)中提到的全栈开发。当时,我们讨论全栈开发的场景主要是,通过运维团队提供工具和支持,让开发人员尽量参与到运维工作中去。对于测试来说,也是同样的道理。我们可以让测试团队转型,进行工具开发,并更多地去支持专项测试,比如性能测试、安全测试等,通过“使能”的办法,让开发人员完成功能测试,包括单元测试、集成测试等。
**说到让开发人员完成部分测试工作,常常会听到很多质疑声。**反对者认为,测试人员的心理模型跟开发人员不一样,他们更倾向于去找问题。而开发人员面对自己开发的产品,潜意识里就不愿意去找问题,比如,他们不愿意专门尝试各种边界输入进行测试,而把自己开发的功能搞崩溃。所以,开发人员和测试人员更适合分开。
这种观念在10年前瀑布开发模式盛行时就深入人心。我曾经也非常认同但在Facebook 工作了5年后改变了看法。如果你能够把开发人员的责任界定得很清楚谁开发的产品谁要保证质量那么开发人员自然而然地就会去尝试做好测试比如进行边界测试。而且开发人员最了解自己写的代码所以他能够最高效地对自己的代码进行测试。
当然做全栈开发的同时我们仍会保留一部分功能测试人员毕竟从竖井模式转变到全栈模式是一个循序渐进的长期过程。不过Facebook做到了极致完全没有了功能测试人员也就是我们所说的“去QA”。
**测试左移到了开发阶段之后,再往左移一步就到了产品设计阶段,**在这里,测试人员除了解需求外,更重要的是评估需求的质量。
我推荐使用BDDBehavior Driven Development行为驱动开发的方法进行开发促进团队在需求评审时更多地考虑测试。BDD是通过特定的框架用自然语言或类自然语言按照编写用户故事或者用例的方式从功能使用者的视角描述并编写测试用例从而让业务人员、开发人员和测试人员着眼于代码要实现的业务行为并以此为依据通过测试用例进行验证。
这里有一篇[关于BDD的文章](https://segmentfault.com/a/1190000012060268),推荐你阅读。
### 测试左移原则三:频繁测试,快速测试
测试左移的第三个重要原则是,频繁测试、快速测试。在测试左移之前,我们需要等待提测,比较被动,不能频繁测试。但测试左移到开发阶段之后,我们就有了很大的自由度去频繁运行测试,从而更好地发挥测试的作用,尽早发现更多的问题。
这里最重要的方法就是,我在讲持续开发时提到的几个关于验证的操作,具体包括:
- 规范化、自动化化本地检查;
- 建设并自动化代码入库前的检查流程;
- 提供快速反馈,促进增量开发。
你可以再复习下[第5篇文章](https://time.geekbang.org/column/article/129857)中的相关内容。
另外,为了能够顺利、频繁地运行测试,我们还要提升测试运行的速度。给测试提速的常见办法包括:
- 并行运行。比如把测试用例放到多台机器上运行,用资源换时间。
- 提高构建速度。比如使用精准构建,因为通常构建之后才能运行测试。
- 精准测试,也就是只运行跟改动最相关的测试。可以建立需求与代码的关系,以及需求与测试用例的关系,从而在代码改动时重点关注与之最相关的测试用例。
- 分层测试,即不同情况运行不同测试用例集合。
- 减少不必要的用例。比如,识别不稳定的用例,对其删除或者优化。
在这里,**我再与推荐两个精准构建和精准测试的工具**。一个是Facebook使用并开源的[Buck](https://buck.build%0A)系统可以用来提高构建速度另一个是Google开源的[Bazel](https://bazel.build),支持精准构建和精准测试。
## 小结
在敏捷、持续交付等开发模式愈发流行的今天,产品的研发节奏越来越快,传统的开发提测之后进行测试,然后交给运维上线的测试模式受到很大的挑战。由此促成了测试左移和右移等新的测试方法,也就是测试向左扩展到产品设计、开发流程中,向右扩展到发布、生产流程中,从而在整个研发流程中持续关注测试,解决新模式给测试带来的挑战。
而测试左移,本质上是尽早发现问题、预防问题。基本原则包括:从人的角度出发,让产品、开发、运维人员统一认识,重视测试;从流程上,让测试融入产品设计和开发步骤中;快速测试、频繁测试。
其实软件开发行业早就达成了共识问题发现得越晚修复代价越大。《代码大全》这本书从软件工程实践的角度说明了修复⼀个Bug的成本在产品需求分析阶段、设计阶段、开发阶段、测试阶段有着天壤之别。比如在集成阶段修复一个Bug的成本是编码阶段的40倍。除了成本悬殊之外在修复难度、引入新问题的可能性、沟通成本、团队状态等方面也有很大的影响。
在我开来Facebook正是成功进行了持续测试让测试融入到了整个研发流程中从而没有QA团队也能保证产品的高质量。
## 思考题
你认为测试左移和测试右移,会不会减少测试人员的工作机会呢?如果你是测试人员,又应该怎么面对这个新的测试模式带来的挑战呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,174 @@
<audio id="audio" title="18 | 蓝绿红黑灰度发布:这些五颜六色的发布到底怎么用?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/2f/9b/2f61ec508a041ac22733c2268fcd269b.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊最近流行的一些部署、发布方法,以及测试右移。
最近几年,我见到了很多跟颜色相关的部署、发布方法,比如蓝绿部署、红黑部署、灰度发布等。今天,我会首先与你分享它们的基本定义和要解决的根本问题;然后,与你一起深入看一看高效应用这些方法的基本原则,以及一些具体的实践。
## 各种部署方式的定义
我们先来看看蓝绿部署Blue-green Deployment、红黑部署Red-black Deployment和灰度发布Gray Release ,或 Dark Launch的定义和流程吧。
### 蓝绿部署
蓝绿部署是采用两个分开的集群对软件版本进行升级的一种方式。它的部署模型中包括一个蓝色集群A和一个绿色集群B在没有新版本上线的情况下两个集群上运行的版本是一致的同时对外提供服务。
系统升级时,蓝绿部署的流程是:
- 首先从负载均衡器列表中删除集群A让集群B单独提供服务。
- 然后在集群A上部署新版本。
- 接下来集群A升级完毕后把负载均衡列表全部指向A并删除集群B由A单独提供服务。
- 在集群B上部署完新版本后再把它添加回负载均衡列表中。
这样,我们就完成了两个集群上所有机器的版本升级。
### 红黑部署
与蓝绿部署类似,红黑部署也是通过两个集群完成软件版本的升级。
当前提供服务的所有机器都运行在红色集群A中当需要发布新版本的时候具体流程是这样的
- 先在云上申请一个黑色集群B在B上部署新版本的服务
- 等到B升级完成后我们一次性地把负载均衡全部指向B
- 把A集群从负载均衡列表中删除并释放集群A中所有机器。
这样就完成了一个版本的升级。
可以看到,**与蓝绿部署相比,红黑部署只不过是充分利用了云计算的弹性伸缩优势**,从而获得了两个收益:一是,简化了流程;二是,避免了在升级的过程中,由于只有一半的服务器提供服务,而可能导致的系统过载问题。
至于这两种部署方式名字中的“蓝绿”“红黑”,只是为了方便讨论,给不同的集群取的名字而已,通过不同颜色表明它们会在系统升级时运行不同的版本。
### 灰度发布
灰度发布,也被叫作金丝雀发布。与蓝绿部署、红黑部署不同的是,**灰度发布属于增量发布方法**。也就是说,服务升级的过程中,新旧版本会同时为用户提供服务。
灰度发布的具体流程是这样的:在集群的一小部分机器上部署新版本,给一部分用户使用,以测试新版本的功能和性能;确认没有问题之后,再对整个集群进行升级。简单地说,灰度发布就是把部署好的服务分批次、逐步暴露给越来越多的用户,直到最终完全上线。
之所以叫作灰度发布,是因为它介于黑与白之间,并不是版本之间的直接切换,而是一个平滑过渡的过程。
之所以又被叫作金丝雀发布是因为金丝雀对瓦斯极其敏感17世纪时英国矿井工人会携带金丝雀下井以便及时发现危险。这就与灰色发布过程中先发布给一部分用户来测试相似因而得名。
好了以上就是几种有颜色的部署发布方式了。如果你还有哪些地方理解得不够透彻可以去网络上搜索相关文章或者直接给我留言吧。接下来我将继续按照黄金圈法则来帮助你深入了解这些部署、发布方式的Why、How和What。
## 蓝绿、红黑部署和灰度发布的Why
究其根本,这些部署、发布方法,是为了解决频繁发布的生产模式带来的两个问题:
- 减少发布过程中新旧服务切换造成的服务停止时间。蓝绿部署和红黑部署都能实现无宕机时间部署0 downtime deployment
- 控制新版本发布因为质量问题带来的风险。灰度发布就是一个例子。
## 蓝绿红黑灰度发布的How
实现这两个目标的基本原则是把服务上线过程拆分为部署、发布和发布后3个阶段并充分利用这3个阶段的特点来提高服务上线的效率、质量和安全性。
这3个阶段的详细定义和特点分别是
- 部署deploy指的是我们把一个代码包拷贝到服务器上运行但并不把它暴露给用户也就是并不给用户提供服务。这个阶段比较耗时但因为还没有面向用户所以风险很小。
- 发布release是把部署好的服务暴露给用户的过程也就是开始真正上线服务用户了。这个过程可以通过负载均衡的切换很快实现但风险很大一旦出现问题损失就会比较大。
- 发布后post-release指的是服务完全上线以后的阶段。因为产品已经完全上线我们的主要工作不再是预防而是变成了监控和降低损失。
以红黑部署为例从开始在新生成的集群B上部署新的版本到线上的流量通过负载均衡指向B之前是处于部署阶段而负载均衡从A指向B的过程就是发布阶段等到负载均衡完全指向B之后就进入了发布后阶段。
部署、发布、上线这几个名词,其实区分不太明显,我们平时在讨论服务部署上线时,也经常会混用。在这里,我之所以要和你明确区分这几个阶段,是因为我们可以针对每个阶段的特点来实现两个目标:
- 提高上线产品的效率,也就是减少发布过程中新旧服务切换造成的服务停止时间。
- 提高上线产品的安全性,也就是控制新版本引入的质量问题。
## 蓝绿、红黑部署和灰度发布What
关于提高上线产品的效率,实践主要有两个:一是利用负载均衡切换线上流量,二是使用功能开关切换线上流量。这两种方法都比较简单。
而提高上线产品的安全性,相对来说就比较复杂了,但又很重要。因为在敏捷、持续交付等开发模式愈发流行的今天,产品的研发节奏越来越快,我们必须在上线过程中,在生产环境上进行更多的测试,以保证产品质量。
讲到这里你可能一下就想到了这正是我们在上一篇文章中提到的测试右移要做的工作。接下来我就与你分别介绍如何在部署、发布、发布后这3个阶段提高上线产品的安全性也就是测试右移的实践。
### 部署阶段的实践
在部署阶段,因为服务还没有真正面对用户,所以比较安全。在这一步,我们可以尽量运行比较多的检验。但**一定要注意的是**我们在运行检验的时候不能产生副作用也就是不能影响到正在给用户提供服务的系统。具体来说我们可以运行集成测试、流量镜像shadowing也叫作Dark Traffic Testing or Mirroring、压测和配置方面的测试这4种检验。
**第一种检验是,集成测试。**
集成测试,指的是对模块之间的接口,以及模块组成的子系统进行的测试,介于单元测试和系统测试之间。
传统的集成测试是在测试环境或类生产环境上进行的。这种方式的问题在于,测试运行的环境和生产环境差别较大,不容易发现生产环境可能会出现的问题。一个最典型的原因是,在这些非生产环境上,只有测试用例在运行,没有在处理任何真实的用户请求,所以在生产环境中运行集成测试,才可能发现在非生产环境上难以发现的问题。
在具体进行集成测试的时候,如果所做的操作没有产生数据,也就是不会产生副作用,会比较安全。如果产生了数据,我们一般有两种处理方法:
- 第1种方法是对测试产生的数据添加一个“测试”标签。同时代码的逻辑对有测试标签的数据都进行特殊处理比如说完全忽略。
- 第2种方法是对测试用例产生的请求就直接不写数据。具体实现方法是在业务里直接添加这个特殊处理的逻辑。如果你使用的是服务网格Service Mesh则可以使用服务的代理比如Sidecar Proxy来进行处理。
**第二种检验是,流量镜像。**
流量镜像,指的是对线上流量的全部或者一部分进行复制,并把复制的流量定向到还没有面向用户的服务实例上,从而达到使用线上流量进行测试的效果。
关于引流的实现通常是使用代理比如Envoy Proxy和Istio配合使用。如果你想深入了解引流的实现方式可以参考“[使用Envoy做镜像引流](https://www.envoyproxy.io/docs/envoy/latest/api-v2/api/v2/route/route.proto.html?highlight=shadow#route-routeaction-requestmirrorpolicy)”这篇文章。
需要注意的是使用引流进行测试时不能给生产环境带来副作用。具体办法与集成测试的处理方法类似我们也可以给引流产生的数据打标签在流量复制的时候对复制的请求统一添加一个特殊字段比如shadow从而让接收到请求的服务可以对其进行特殊处理。
使用流量镜像除了普通的检测之外还有一个比较有用的实践就是对测试流量与实时服务流量的运行结果进行对比来检查新服务的运行是否符合预期。Twitter在2015年开源了一款这样的代理工具[Diffy](https://blog.twitter.com/engineering/en_us/a/2015/diffy-testing-services-without-writing-tests.html) ,它可以在镜像流量的同时调用线上服务和新服务,并对结果进行对比。
**第三种检验是,压测。**
压测,也是在部署阶段比较有价值的一种测试方法。比如,我们可以把新服务部署到一个比较小的集群上,然后把线上环境的流量全部复制并指向这个新集群,以相对客观地了解最新服务的抗压能力。
**第四种检验是,配置方面的测试。**
系统配置方面的变更,一旦出现问题,往往会给业务带来重大损失。部署阶段,因为不直接面向用户,所以是测试配置变更的好时机。
如果你想深入了解这部分内容的细节,可以参考[Facebook关于可靠性的见解](https://queue.acm.org/detail.cfm?id=2839461)这篇文章。
### 发布阶段的实践
在发布阶段,我们可以使用金丝雀发布和监控两种方法,来及早发现错误,并减少错误带来的损失。
**第一个方法是,金丝雀发布。**
金丝雀发布,是发布阶段最基本、最常见的实践。这里,我两个小贴士:
- 让金丝雀服务先面向内部用户也就是Dogfooding来降低出现问题时造成的损失。
- 最近几年出现的一些部署工具和平台比如Spinnaker已经对金丝雀发布有了[比较好的支持](https://www.spinnaker.io/guides/user/canary/)。你可以考虑直接使用,降低引入成本。
**第二个方法是,监控。**
监控,是安全发布必不可少的关键环节,其重要性不言自明。在发布过程中,我们应该注意监测用户请求失败率、用户请求处理时长和异常出现数量这几个信息,以保证快速发现问题并及时回滚。
### 发布后的实践
产品成功发布之后我们的主要工作就是监控和补救具体实践包括三个监控、A/B测试和混沌工程Chaos Engineering
**第一个实践是,监控。**
服务上线后我们需要提供有效的监控来了解服务的质量。关于监控的内容我推荐参考可观察性Observability的三大支柱即日志、度量和分布式追踪。如果你想深入了解这部分内容推荐你看一下[这篇文章](https://zhuanlan.zhihu.com/p/72054483)。
**第二个实践是A/B测试。**
系统上线之后发现问题有一个快速的补救办法是继续使用旧的服务代码。对于这一点我们可以通过A/B测试的方法来实现。
也就是说添加风险比较大的新功能时使用A/B测试让新旧功能并存通过配置或者功能开关决定使用哪一个版本服务用户。如果发现新功能实现有重大问题可以马上更改配置而不需要重新部署服务就能重新启用旧版本。
**第三个实践是,混沌工程。**
混沌工程指的是主动地在生产环境中引入错误来测试系统的可靠性的工程方法。最早为人熟知的混沌工程是网飞Netflix公司的Chaos Monkey。这种方法可以引入的错误主要包括
- 杀死系统中的节点,比如关闭服务器;
- 引入网络阻塞的情况;
- 切断某些网路链接。
不过,一般是在公司达到了很好的稳定性之后,对稳定性有更上一层楼的需求时,或者是对稳定性要求特别高的公司,混沌工程的价值才比较大。
## 小结
我首先与你介绍了一些常用的部署、发布方式包括蓝绿部署、红黑部署和灰度发布。这些方法的目的都是为了解决频繁发布的生产模式带来的挑战。而解决这些挑战最基本的原则是把服务上线的过程拆分为部署、发布和发布后3个阶段并分别进行优化。
在部署阶段我们要充分利用服务还没有暴露给用户的特点尽量进行集成测试、压测、配置测试等检测在发布的阶段我们主要是采取灰度发布的方式并配合使用监控在出现问题时马上进行回滚而在发布后阶段则有监控、A/B测试以及混沌工程等实践。
在我看来快速发布模式没有给测试留下足够的时间我们的确需要在部署上线的过程中在提高产品质量上多下功夫。Spinnaker这种原生支持灰度发布的工具的出现和流行也正表明了这一趋势。在这种在生产环境上进行测试的方式最关键的是要做好风险控制。
另外,这种模式给测试团队带来了非常大的挑战。我觉得,在不久的将来,传统测试方式会越来越不流行。测试团队需要尽快转型,来适应这种新的开发模式。
## 思考题
你觉得金丝雀发布可以用在移动端应用或者桌面应用上吗?如果可以的话,大概要怎么实现呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,146 @@
<audio id="audio" title="19 | 不再掉队,研发流程、工程方法趋势解读和展望" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/84/3f/841ac4699e44b2e538c6ec21642a163f.mp3"></audio>
你好,我是葛俊。今天,我们就来聊一聊研发流程和工程方法的一些趋势吧。
软件行业从诞生之日起,就一直是充满了发展和变化,各种工程方法、研发模式不断涌现,而且涌现的速度越来越快。对于开发团队和个人来说,这既是挑战也是机会:说是挑战,是因为我们需要持续学习才能跟得上它的发展;说是机会,是因为如果能够快速学习并应用这些实践,我们就可以在竞争中取得优势。
这些挑战和机会,并不强依赖于资源、背景,其实是为我们提供了一个相对公平的竞争环境。这,也是软件行业这些年来涌现了许多白手起家的成功公司和个人的重要原因。
在今天这篇文章中,我会针对当前比较流行的研发流程、工程方法的趋势,尤其是与国内研发比较相关的部分,做一些解读和展望,和你说说我的理解、预测,希望作为你以及你的团队,在技术选型以及工程方法选择上的一些参考。
接下来我将会从协作方式、云计算平台、应用开发和AI这4个方面与你展开讨论。
## 协作方式的相关趋势
在我看来,协作方式的相关趋势,主要表现在以下两个方面:
- 首先,团队远程办公、灵活工时办公,会越来越普遍;
- 其次,聊天工具和其他工具的集成,会越来越普遍。
接下来,我与你说说我为什么会有这样的预测吧。
### 团队远程办公、灵活工时办公,会越来越普遍
远程办公之前用得不是特别多,主要是因为沟通效率比较低,也不利于掌控团队氛围。但,视频会议以及团队协作工具的快速发展,使得这些问题不再那么明显了。
这时,远程办公的巨大好处,也就是可以**克服人才的地域局限性**,就凸显出来了。比如,很多人在考虑要不要应聘工作时,都会考虑通勤距离。所以,一旦能够打破这个地域限制,招聘的空间就会开阔很多。
远程办公的另一个好处是,可以**大量减少通勤时间,进而提高研发产出**。近些年来交通拥挤、通勤时间过长的情况越来越严重。美国有科学研究表明如果每天上班单程超过30分钟就会对人的情绪产生比较大的负面影响。而国内的一线城市从我接触到的样本看上班单程低于30分钟的情况大概只占20%。
所以最近这些年越来越多的公司开始或多或少地采用远程办公的方式以此来缓解通勤时间过长给员工造成的压力。有些公司做得比较彻底让绝大部分开发人员绝大部分时间都是远程办公我知道的公司包括GitHub和Atlassian。而更多的公司则采用的是每周允许员工部分时间在家办公比如Facebook的开发人员每周三可以在家办公。
**至于灵活工时,本质是用任务来驱动**。这一点比较符合软件开发的特点在第1篇文章中我已经与你讨论过了这里不再过多展开了。在硅谷基本上所有的软件公司都是这样操作的。在国内这种情况也越来越普遍。
关于远程办公方式,我还有两个小贴士:
- 一是,要尽量使用视频会议,而不是电话会议。有研究表明,电话会议不如面对面沟通的效率高,主要是因为缺少了由面部表情和肢体语言传递的信息。
- 二是,做好信息的数字化。比如,建设好任务系统、文档系统等,从而让研发人员在工作中能尽量从这些系统中获取信息,而不用过于依赖面对面或者实时聊天系统,依然能够高效工作。
### 聊天工具和其他工具的集成会越来越普遍
聊天工具和研发流程中其他工具的紧密集成最近几年在国外很流行最典型的就是Slack。它可以和任务工具、代码审查工具、部署工具、监控系统进行集成从而实现我前面[第4篇文章](https://time.geekbang.org/column/article/128867)中提到的工具网状互联,提高研发效能。
所以我觉得下面这种工作方式会越来越普遍聊天工具里有各种各样的聊天室和聊天机器人团队成员在不同的聊天室讨论不同的话题并通过聊天机器人和其他工具进行集成和交互来获取信息以及执行一些操作。比如询问聊天机器人当前线上的服务分别是哪些版本、相关需求有哪些、Commit有哪些、具体的开发和测试人员是谁等。又比如通过运维机器人添加两台机器到集群中去。
聊天是人类最自然的沟通方式之一,所以在聊天室和机器人的帮助下,执行这些操作会非常高效,提高研发效能也就不在话下了。
## 云计算平台的相关趋势
正如我在[第16篇文章](https://time.geekbang.org/column/article/141568)中提到的,云计算正在改变我们开发软件的方式。利用好云计算平台的趋势,是提高团队研发效能必须要做的事儿。在我看来,**云计算最大的趋势应该是Docker和Kubernetes带来的各种可能性**。
Kubernetes自诞生以来背靠着Google公司的强大技术支撑和经验积累发展得异常迅猛现在它已经成为了容器编排的事实标准。在我看来其中最大的作用是我们可以用它来建设PaaS。
使用PaaS我们可以快速部署和管理应用程序把容量预配置、负载均衡、弹性扩容和应用程序运行状况监控的部署细节交给平台来管理让研发团队聚焦于业务对高效业务开发极其有用。但是PaaS平台容易出现灵活性不足的情况。
比如我之前在Stand公司开发后端服务时非常希望能够使用AWS提供的PaaS服务比如Elastic Beanstalk服务来减轻团队在运维方面的工作压力但试用之后发现其无法支持一些定制化的需求比如平台的技术栈的灵活性不够而且更新的时候透明度不够。所以我们只能忍痛放弃最终选择使用更下一层的IaaS服务通过自己管理虚拟机来部署和管理服务。
而解决上述灵活性不足问题的一个方法是灵活生成新的PaaS平台。但PaaS平台的建设必须依托于下层的IaaS才能实现所以技术要求很高工作量和资源要求也很大只有专门做PaaS的公司和云厂商才有能力提供PaaS。
Kubernetes出现后提供了强大的容器管理和编排功能事实上是实现了一种基于容器的基础设施的抽象也就是实现了IaaS的一个子类。所以通过它我们终于可以方便地建设定制化的PaaS了一个具体的例子是FaaSFunction as a Service。Kubernetes的出现极大地降低了建设FaaS的工作量所以很快出现了基于它的实现比如[OpenFaaS](https://github.com/openfaas/faas)、[Fission](https://github.com/fission/fission)。
正是基于Kubernetes提供的构建PaaS的能力我预期将来越来越的产品会构建在基于Kubernetes和Docker的PaaS之上。
今天很多公司对Kubernetes还是直接使用也就是通过一个对Kubernetes比较了解的运维团队来支持公司的服务运行在Kubernetes集群上。但Kubernetes的学习成本比较高、学习曲线比较陡峭整个系统的运行并不是那么顺畅。所以我觉得将来的趋势将会是这样的
- 如果你所在的团队比较小可能会选择第三方通过Kubernetes提供的PaaS平台
- 如果你所在的团队比较大可能会基于Kubernetes建设适合自己的PaaS平台。
另外我觉得很可能会出现这样的情况整个公司运行一套Kubernetes作为IaaS上面运行多个不同的PaaS平台支持各种服务的运行。如下图所示。
<img src="https://static001.geekbang.org/resource/image/be/75/be1c49b861b7d92bc9e139c00bcf4475.jpg" alt="">
>
备注CaaSContainers as a Service是允许用户通过基于容器的虚拟化来管理和部署容器、应用程序、集群属于IaaS平台的范畴。
## 应用开发的相关趋势
随着云计算的普及,分布式计算会越来越流行,最典型的例子莫过于微服务的盛行。设计正确的架构,来支持产品的开发和部署,是云时代高效研发的重要因素。
在我看来,应用开发的相关趋势,主要表现在云原生开发方式和服务网格两个方面。
### 云原生的开发方式
应用程序运行在云端,需要基于云的架构设计,这就意味着我们需要一套全新的理念去承载这种开发模式。这套理念,就是云原生开发。
由Heroku创始人Adam Wiggins提出并开源、由众多经验丰富的开发者共同完善的[12原则12-factor](https://12factor.net/zh_cn/),是云原生开发理念的理想实践标准。
### 服务网格
复杂的服务拓扑结构,是云原生应用程序的一个重要难点。而服务网格正是用来处理服务间通信的专用基础设施,它提供了应用间的流量、安全性管理,以及可观察性,比较好地解决了这一问题。
在服务网格架构中流量管理从Kubernetes 中解耦,每一个服务对网络拓扑并不知情,通信都是通过代理来进行的。所以,我们就可以通过代理来方便地完成很多工作。
比如在部署阶段进行的集成测试我们就可以借助它来实现。假设A和B是系统中的两个服务并且A会调用B。我们希望在部署A的一个新版本时在生产环境进行A和B的集成测试
- 通过A的egress代理让它对下游服务发出的请求都自动加上一个x-service-test-b的header
- 而在B的ingress代理接收到有x-service-test-b的请求时自动通知B这是一个测试请求。测试完毕之后修改代理去除这个header即可。
目前,关于服务网格有两款比较流行的开源软件,分别是[Linkerd](https://linkerd.io/) 和 [Istio](https://istio.io/) ,都可以直接在 Kubernetes 中集成,也日渐成熟。
## AI方面的相关趋势
这几年AI绝对是最火的话题之一。我们讨论研发流程和工程方法自然也绕不过AI。不过AI在软件研发上的应用还处于起步阶段。
在我看来AIOps、CD4MLContinuous Delivery for Machine Learning机器学习的持续交付、语音输入是比较适合在软件研发中落地的。
接下来我们分别看看这3个方面。
### AIOps
做好AI的前提就是有大量的数据积累。而运维相关的工作就有大量的线上日志、监控、指标等数据所以AIOps是比较容易落地的一个方向。具体来说我觉得**AI可以从以下两个方面来提高运维效率**
- 第一个方面是,检测并诊断异常。也就是说,通过对历史故障数据进行分析学习,找到规律,从而在新故障出现或者快要出现时,能够实时探测到,并给出一些可能的诊断。
- 第二个方面是智能地对资源进行弹性的扩容、缩容及流量切换。目前我们通常采用CPU利用率、内存利用率等少量维度的指标去判断是否要扩容或缩容判断逻辑比较简单。而利用AI我们可以收集更多维度的数据综合分析后自动进行扩容或缩容更合理地利用资源。另外更进一步地我们还可以利用AI做更智能的流量切换进一步提高资源利用率。
### CD4ML
在机器学习中有很多需要人工参与的步骤我们可以通过提高这些步骤的自动化程度来提高其效率。CD4ML就是将持续交付实践应用于开发机器学习模型上以便于随时把模型应用在生产环境中。
### 语音输入协助软件开发
现在语音输入效果已经比较好应该算是AI最成熟的领域。从我个人来说我就经常使用。比如我会用Amazon的Echo玩游戏、用小米音箱控制家里的空调、通过Siri使用滴滴打车等。
实际上,我在写这些专栏文章的时候,就经常使用语音输入形成第一版文字,然后再手工编辑完善,能够节省不少时间。
在我看来,语音输入将来同样可以应用到软件开发工作中。比如,用语音来输入程序;再比如,通过移动设备上的语音输入完成一部分研发相关工作,包括申请机器、运行流水线,查看系统状态等。
## 小结
今天我从协作方式、云计算平台、应用开发和AI这4个方面与你分析了如何在软件开发工作中运用这些趋势去提高研发效能。同时我也对这些趋势做了大胆预测。当然了欢迎你在留言中与我分享你的其他看法。
为了方便你学习、理解,我把这些趋势的重点内容整理到了一张表格中,供你参考。
<img src="https://static001.geekbang.org/resource/image/9e/f3/9e839997f3186cfeec0980c21b44aef3.jpg" alt="">
除此之外,软件开发行业还有非常多的、创新性的方法和实践,我无法一一展开了。比如,移动端开发中,[GraphQL](https://graphql.org/)可以高效、灵活地从后端获取数据以及使用JavaScript之外的语言进行Web开发的框架比如[Vapor](https://vapor.codes/)。
我一直很庆幸,能够在软件研发这个行业工作。因为它有足够的想象力,给了开发者持续发展的空间。上面提到的这些趋势和方向,都让我非常兴奋。希望能带给你一些帮助,至少引发你的一些思考。当然了,对这些趋势的选择、解读和预测,都只是我个人的观点,欢迎你通过留言与我分享你的看法。
## 思考题
使用AI来提高研发效能是一个很有趣的话题。除了我今天提到的这些你觉得还有哪些可能的方式吗将你的预测与想法分享给我吧。
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,121 @@
<audio id="audio" title="20 | 答疑篇:如何平衡短期收益和长期收益?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/0d/bf/0d5527ebd1097ff2881c125285f5cfbf.mp3"></audio>
你好,我是葛俊。今天,我来针对“工程方法”模块的留言问题,做一次集中解答与扩展吧。
针对“工程方法”模块的文章,很多同学留下了精彩的留言,阐述了自己对研发效能的认识、遇到的问题以及解决方案。
虽说我已经一一回复过你们的留言了但有些问题在我看来非常有价值值得再扩展一下。所以我今天挑选了3个典型问题进行详细回答并对每一个问题延展出一个话题和你讨论希望能帮助你加深对这些问题的理解切实帮助你提高你或者你团队的研发效能。
## 问题一:因为赶进度就慢慢不做代码审查了
@john_zhang同学在[第13篇文章](https://time.geekbang.org/column/article/138389)中,提出了这样一个问题:
>
我们推行过一段时间的代码审查,因为团队只有三五个人,所以采用的是团队审查,每天半个小时左右,可惜后来赶开发进度,慢慢就没做了。现在开发同事总是以赶进度为由,不太认同代码审查,怎么破?
这条留言下有好几位同学都点赞了,看来这是一个常见的情况。所以,我再和你分享下针对这个问题的具体解决办法吧。
1. 团队统一思想,代码审查是有效工作的一部分,应该计算到工作量里面。
1. 减少团队审查,更多地使用工具进行一对一的代码审查。因为团队审查很难做到效率高,所以应该只是针对一些重点的提交才采用这种方式。
1. 培训团队,统一认识,让大家看到代码审查的长期收益,比如考虑代码的可维护性以及后续添加新功能的速度,而不只是盯住当前的开发进度这个短期收益。甚至,我们可以把代码审查这个操作,作为团队的制度要求大家执行。
**说到这里,又涉及了长期收益和短期收益的冲突要怎么权衡,这也是我们普遍关注的一个问题。接下来,我再和你扩展下这个问题吧。**
比如,不止一个同学提到“公司连给研发人员配置好一点的电脑的意识都没有,何谈研发效能?” 的确,使用配置低的电脑能够节省当前的开支,但是会损害长期的开发效率。这里的短期利益便是当前开支,而长期利益就是公司的长期产出。
又比如,我在[第14篇文章](https://time.geekbang.org/column/article/138916)中与你讨论的技术债问题,借债是考虑短期利益,而还债考虑的就是长期利益。
从这些例子可以看到,**短期利益和长期利益往往会存在冲突,我们必须做好取舍,才能最大程度地提高研发效能**。在我看来,短期利益和长期利益的权衡,可以重点考虑以下两个方面。
第一,寻找两者最佳的平衡点。比如,在一条线段上,最左边的端点是只关注短期利益,最右边则是只关注长期利益,两个极端情况都不好。但是,我见到的大部分情况是,团队做的选择会特别靠近最左边。虽然说有些选择是出于业务压力情非得已,但有相当一部分情况,是因为短视造成的。**由于太追逐眼前利益,而造成长期的生产力下降、研发人员积极性下降、产出降低的例子比比皆是**。
为了避免后劲不足的情况,我们必须往中间移动,找到最佳平衡点。
<img src="https://static001.geekbang.org/resource/image/4b/f9/4b78ec355ba10aa352ce87ad63f77cf9.jpg" alt="">
第二随着时间推移这个最佳平衡点也会有变动。比如在第14篇文章末尾我给你举的A、B、C三个公司对待技术债的案例。C公司在不同的时间分别选择了最合适的平衡点很好地平衡了借债与还债从而获得了竞争优势。这里我建议你结合长期利益与短期利益的权衡再返回[第14篇文章](https://time.geekbang.org/column/article/138916),回顾下这个案例。
## 问题二:截屏上传工具链的实现
在[第11篇文章](https://time.geekbang.org/column/article/136070)中我与你介绍了一个截屏工作链具体步骤包括截屏、上传图片、获取URL、缩短URL、保存到系统剪贴板。这样一来我们使用快捷键触发截屏流程之后就可以直接用Cmd+V把图片的短URL粘贴到Commit Message或者聊天工具中了。
有许多同学对这个工具很感兴趣询问我如何实现以及是否有对应的开源产品。不过遗憾的是我没有找到对应的开源产品和方法。所以我再和你说说我之前在Stand公司时是如何实现的吧
- 图像存储用的是Google Drive
- 截屏上传工具是[Share Bucket](https://fiplab.com/apps/sharebucket-for-mac)。它支持上传到指定的Google Drive同时还支持存储URL到剪贴板。还有一个类似的工具是[MonoSnap](https://monosnap.com/welcome)。
至于URL缩短服务我们当时并没有使用。如果你考虑使用的话可以看一看开源的[YOURLS](https://github.com/YOURLS/YOURLS)。
这里,感谢@日拱一卒同学,和我们分享了他们公司的实现方法:
>
我们的解决方案和你说的差不多,涉及到不同的工具,处理方式不太一样。
<ol>
- 如果工具本身支持图片存储例如ZenHub或者JIRA我们用工具本身来存储图片。
- 如果工具本身不支持图片存储,就用公司提供的网盘来存储图片,在工具中引用相关的链接。
</ol>
在我看来,第二种方法会更好一点。因为有一个统一存储图片文件的地方,不同的工具都可以指向同一张图片。
其实在Facebook还有另外一个类似的工具链只不过它针对的不是截屏而是拷贝文本。
我们在工作中常常会针对一段文本进行讨论,但如果这段文本特别长的话,把它拷贝到讨论工具中,就会占用很大的空间。所以,我们就用了一个工具链来解决这个问题。使用步骤也类似:
1. 拷贝要讨论的文本。
1. 用快捷键触发工具链。这些工具会自动把文本上传到一个服务器然后把URL缩短并把缩短后的URL保存到系统剪贴板里。
1. 使用快捷键Cmd+v把短URL粘贴到讨论工具比如Commit Message其他人点击这个URL就可以到文本服务器上查看具体的文本内容。
这个工具链涉及了文本存储服务,[PasteBin](https://pastebin.com/)是目前最流行的工具不过它并没有开源而且只提供SaaS服务。而我们日常工作中拷贝的文本通常是不能放到公网上的。所以PasteBin很可能不适合你。这里我推荐一个类似的开源工具[HasteBin](https://github.com/seejohnrun/haste-server),你可以安装到公司内网使用。
关于这个截屏工具流,**我想扩展的一个话题是,研发效能的提高带来的量变会产生质变**,也就是说研发效能的提高,可能看起来只是做事情快了一些,产出高了一些,甚至有人会认为这并没有什么太大的作用。但实际上,效能提高累积到一定阈值之后,就可以带来质变。
以截屏工具链为例。在没有类似工具链的情况下虽说可以手动实现上面的工作但因为操作步骤太繁琐所以我们在Commit Message里很少使用截屏。而这个工具链将这些繁琐的操作简单化了由此带来的效率提升让绝大多数开发者都乐于在Commit Message中使用截屏去讨论问题。也就是说这个工作流带来的效能提升累积到了一定的阈值之后引发了质变。
提升研发效能的量变带来的质变,另一个表现是,效能的提升增强了公司整体的竞争力。当效能提高到一定程度的时候,就可以在公司竞争中起到重要作用甚至是决定性作用。
在我看来,这并不是夸大之词,尤其是在软件开发行业逐步成熟,需要比拼研发效能的今天。
## 问题三Facebook的SEV系统是做什么用的
@Geek_f0179a同学在[第4篇文章](https://time.geekbang.org/column/article/128867)中问到Facebook有一个SEV复盘系统这个系统的主要功能是什么会记录哪些关键信息呢
SEV系统是一个事故响应及根因分析系统SEV是严重性“Severity”的前三个字母。这个系统会根据事故的严重性为其分配一个数字大概是1~4SEV1最严重SEV4最轻。每次发生事故除了记录严重性之外还要按时间顺序记录事故发生、发现、定位、解决、确认解决过程的每一步的时长和具体操作细节。
每周公司都会定时举行SEV讨论会基本只讨论严重的问题比如SEV1和SEV2的事故。会议由高层管理者主持事故责任人轮流进入会议室对事故进行描述和讨论。
这个会议的主要目的是对事故进行回溯分析根因以及如何避免再次出现类似问题。也就是说SEV系统的目的重在总结提高不要多次重复同一个错误如果重复犯错要惩罚。
SEV系统的效果很好在避免问题重复出现的同时我们也比较敢于试错至少从我的经验看是这样的。
**针对这个事故复盘系统,我想扩展的话题是:出现问题之后,到底应不应该追责,应该怎样追责?**
在我看来,考虑这个问题应该从最终目的出发。复盘的目的是最大程度地减少以后再出现同一个问题的概率。那我们应该怎样处理呢?
如果每个错误都要惩罚,就会有以下几个问题:
- 因为害怕惩罚,尽量去掩盖问题而不是暴露问题、分析问题,或者尽量撇清责任甩锅。
- 惧怕闯祸,团队成员开始信奉“多做不如少做”。
- 因为担心指出别人的问题,会让他受罚,结果就是不愿意得罪人,导致问题被隐藏。
而另一个极端,如果犯错没有任何惩罚的话,又会造成以下问题:
- 同样的错误一犯再犯。
- 没有奖惩造成不公平。这就会让原本认真工作的员工失去认真做事的动力,降低对代码质量、产品质量的关注,提高质量的工程措施(比如,单元测试、开发自测等)更是难以推广。
所以,在我看来,针对错误更有效的处理方法应该是,以保持团队的主动性、责任感和执行力为原则,具体措施包括:
- 追究责任,但不是惩罚。知其然并知其所以然,搞清楚前因后果,避免重复犯同一个错误。
- 重复犯错一定要惩罚。
- 反复问为什么,找到根本原因。
## 小结
好了,以上就是今天的主要内容了。如果有哪些你希望深入了解还未涉及的话题,那就直接给我留言吧。
接下来我们就会开启新的模块了进入个人效能的讨论了希望这个模块能够帮助你提升个人效能持续成长成为10x开发者提升个人竞争力。
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,63 @@
<audio id="audio" title="开篇词 | 为什么你要关注研发效能?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/dc/86/dcb31edaa40cceeb37bb08f446a6f986.mp3"></audio>
你好,我是葛俊,曾在 Facebook内部工具组工作与两个同事共同负责软件研发工具套件Phabricator 的开发和开源,是[Phabricator](https://github.com/phacility/phabricator)的主要作者之一。
最近这十年国内互联网产业的发展速度不亚于硅谷在商业模式创新方面甚至已经完成超越但是我们在研发效能方面始终比较落后。今年年初爆发的996大讨论让国内的加班问题吸引了国内外开发者的关注。我们很难予以否认在互联网行业繁荣发展的背景下国内很多公司采用了“拼工时”的做法却忽略了最最应该关注的研发效能。
现在,我想请你回忆下,你是否也曾为下面这些问题感到困扰呢?
1. 研发团队看起来人也不少,大家也很辛苦,加班也不少了,但是产品发布还是常常延期,上线后产品问题频发。
1. 用户需求从需求分析、产品设计、开发、测试最终流到部署,但最终发布的产品与用户需求偏差却很大。
1. 产品发布上线时出现大量提交、合并,导致最后时刻出现很多问题,团队成员集体熬夜加班,却将大把的时间花在了等待环境、等待验证上。
1. 开发提测质量不好,大量压力聚集到测试这一步,导致代码返工率很高。引入单元测试、代码审查,效果却都不明显。
1. 开发人员疲于应付业务没有精力或者兴趣去精进技术对Git、命令行等强大工具的使用仅限于皮毛士气低迷、工作效率低下。
这其实就是团队的研发效率,也就是研发效能出现了问题。那么,研发效能到底是什么呢?
一提到研发效能,很多人的第一反应可能都是开发的速率,也就是研发团队能否快速发布产品。但在我看来,速率只是效能的三大支柱之一。
除了快,产品开发更重要的是方向正确,因为不能给用户和公司真正提供价值的产品,做了也是白做。另外,高效能还需要有可持续性,否则短期的高产出可能会严重伤害长期的产出。比如,连续熬夜加班带来的身体问题,会导致后续工作效率低下,得不偿失。
因此,**研发效能的完整定义应该是:团队能够持续地为用户产生有效价值的效率**包括有效性Effectiveness、效率Efficiency和可持续性Sustainability三个方面。简单来说就是能否长期、高效地开发出有价值的产品。
可喜的是,国内越来越多的公司开始在研发流程、工具、文化等方面下功夫,很多百人研发人员规模的公司开始组建了专门的效能团队,来提高整个公司的研发效能。
这是一个很好的现象和趋势。但,很多公司在推进研发效能的时候,常常不知道从何下手,或者是花了精力、加大了投入却看不到效果,产出抵不上投入。比如,我在一些公司做内训和顾问工作的时候,经常会遇到下面这样的案例:
1. 想通过指标度量的方式来衡量团队的效能,要求每个团队达到一定的测试覆盖率。研发团队在产品完成后进行突击,来编写单元测试,最终达到了要求,但产品质量却没有提高。
1. 引入业界先进工程实践学习Google使用大仓但因为基础设施不成熟对大量二进制文件支持很差结果是算法团队因为有很多的二进制模型文件每次Git Clone都需要半小时怨声载道。
1. 希望建设工程师文化来提高产出和活跃气氛跟公司老大以及HR商量好几条价值观在公司宣传推广还组织了几次团建活动但是收效甚微。大家真正工作起来还是老样子。
这些问题的根源都在于,**软件开发的灵活性决定了研发效能提升的困难性:**可以关注的点太多,可以使用的方法也很多,但如果只是简单照搬业界研发实践的话,效果往往不好,有时甚至会造成负面效果。
而与国内公司形成鲜明对比的是硅谷的互联网公司在推进研发效能方面做得要好得多。在2000年互联网泡沫之后美国的互联网产业从疯狂增长进入到了“精耕细作”的阶段需要通过比拼效能在竞争中取得优势并在此过程中积累了很多经验。
在这其中Facebook的研发效能非常高更是硅谷公司中的一个典范。比如早在2012年Facebook月活达到10亿的时候后端服务及前端网站的部署采用的是每周一次全量代码部署、每天一次增量代码部署以及每天不定次数的热修复部署但部署人员就只有三个达到平均每个部署人员支撑3.3亿用户的惊人效率。
又比如社交网络出现Bug的时候调测起来非常麻烦。因为要复现Bug场景中错综复杂的社交网络数据困难并且耗时。但在Facebook它采用开发环境跟生产环境共享一套数据的方法。这就使得开发人员可以非常方便地在自己的机器上复现这个Bug进行调测。当然这样的数据共享机制背后有着强大的技术和管理支撑来规避风险。
2010到2013年之间我在Facebook基础平台团队的内部工具组作为核心成员研发并开源了研发工具套件Phabricator。2013到2015年我又作为效能工具的使用者参与了Facebook对外产品的研发。也正因为这几年的工作我对Facebook如何提高研发效能有了越来越清晰的理解认识到**研发效能的提高,需要整个公司在研发流程、工程方法、个人效能和文化管理等方面进行精心设计。**
离开Facebook之后我在硅谷的Stand Technologies公司、国内的创业公司以及华为担任过技术总负责人、CTO、技术专家和团队主管等角色带领百人技术团队进行研发。
比如2017年到2018年我在华为开发工具部主导下一代集成开发工具环境为软件开发工程师提供全栈的端云一体工具平台服务于2万多开发者致力于提高公司整体的研发效能。同时我也尝试将研发效能的工程实践引入华为。比如我在团队进行了几次黑客松Hackathon每次活动平均10个开发者就产生一个项目每10个项目中就有1.5个成功落地。
工作15年来我在研发效能团队工作过也在产品团队中推动过研发效能这其中包括硅谷和国内的公司也包括大型企业和创业公司。对怎样在一个公司或者团队引入效能实践有比较丰富的经验。
所以,当极客时间团队邀请我写一个与研发效能相关的专栏时,我毫不犹豫地就答应了,希望能够借此把之前的经验、教训做一次系统地梳理,帮助到同样对效能有期待同时又有困惑的同行者,另外对自己也是一次温故知新的机会。
在这个专栏中我会从4个方面分5个模块与你讲清楚如何做到研发的高效能。
- **研发效能综述。**我会讲解研发效能的定义、模型,并着重介绍研发效能度量的正确使用方法。借此,希望你能够梳理出研发效能的主脉络,构建出一副清晰的知识图谱。
- **研发流程模块。**我将与你分享敏捷、持续集成、分支管理、持续交付、DevOps、团队协同等话题希望能帮助你深入理解研发过程中的关键流程以及流程优化的基本原则从而能够针对自己的实际情况找到最合适的工程实践让软件开发的整个流程更加顺畅、高效。
- **工程方法模块。**我将与你讨论研发流程中各关键步骤高效工程方法,包括开发、测试、运维。并与你分析软件开发的趋势和热点,比如智能化开发、在移动设备上开发、云原生等等。通过这个模块,我希望加深你对这些具体工程方法的了解,并学会如何正确地使用这些方法。
- **个人效能模块。**我将与你分享如何提高个人效能具体涉及深度工作、Git、命令行、VIM、工具环境集成等内容旨在帮助你提高技术的专精程度和持续成长。每个开发人员都应该提高自己的效能只有这样才能持续学习持续提高避免被业务拖着跑。
- **管理和文化模块。**我将重点与你系统分析硅谷管理和文化尤其是Facebook的工程师文化并根据我在国内公司的具体落地经验给出推荐的文化引入和建设方法。
研发效能和软件开发一样,都具有很大的灵活性,提高研发效能也不是照搬照套就能做好的。所以**在写作专栏的过程中我会着重讲解Why带你深入了解效能实践背后的原理然后才给出How也就是具体的实践。**因为只有深刻理解原理,才能灵活运用。
同时,我会与你分享尽量多的案例,带你一起了解国内外一些公司的优秀做法,分析它们成功的经验,当然,我也会分享失败的案例,以及背后的原因。不过更重要的是,我希望你能够跟着我一起分析,通过对比思考,找到真正适合团队和自身的实践。而这,是我写作这个专栏的真正初衷。
这是研发效能专栏的第一篇文章,如果可以的话,欢迎你在留言区做个自我介绍,和我聊聊你或者你的团队在研发效能方面的实践以及遇到的问题,增进我们彼此的了解。而且,我也希望你看过这个专栏后,能够再回头来看看最初留下的内容,相信届时你已经对研发效能有了新的理解和思考。

View File

@@ -0,0 +1,120 @@
<audio id="audio" title="01 | 效能模型:如何系统地理解研发效能?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/65/93/65974b88cd7ad1d8f788f889a3baad93.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊什么是研发效能,以及研发效能的模型,这些内容是理解整个专栏的基础。
今年的3月26日一位昵称为996icu的用户在GitHub上创建了996.ICU项目自此996这个话题被推上了风口浪尖。目前这个项目已经拿到了24万多颗星。朋友们也常常问我硅谷的公司有没有996
其实在硅谷很少有公司要求996。不过在初创公司因为业务紧张、同事间的竞争加班也很常见。但是硅谷和国内的公司有一个很大的区别就是硅谷的公司一般是任务驱动只要完成任务就行不管你花了多少时间。而国内很多实行996的公司不仅仅是要求完成任务更强调工作时长。但其实专注时长的这种操作在软件开发行业是不合理的因为长期加班不能保证持续的高效产出。
从我以及身边许多开发者的经验来看每天能够高效地产出代码五六个小时已经相当不错了。短期突击加班会有效果但如果长期加班通常效率、质量会下降产生了Bug就要花费更多的精力去修复。如果这些Bug发布到了用户手上损失就会更大得不偿失。
长期加班还会出现无效加班的结果。比如有个朋友在一家国内一流的互联网公司工作据他反馈公司实行996很多人加班其实是磨洋工低效加班非常明显。可想而知其他推行996工作制的公司大概率也会存在这种问题。
**那么,长期加班效果不好,面对激烈竞争,我们到底应该怎么办呢?**在我看来,这个问题还是要从软件开发本身的特点来解决。
## 为什么要关注研发效能?
软件开发是一个创造性很高的过程开发者之间的效率相差很大。就比如10x程序员的生产效率可以达到普通开发者的10倍。其实不仅是个人团队间的效率相差也很大。**所以,相比工作时长而言,公司更应该关注的是研发效能**。
接下来,我们再回顾一下我在开篇词中给研发效能的定义:
>
研发效能是团队能够持续为用户产生有效价值的效率包括有效性Effectiveness、效率Efficiency和可持续性Sustainability三方面。简单来说就是开发者是否能够长期既快又准地产生用户价值。
硅谷的很多知名公司比如Google、Facebook、Netflix等在研发效能上做得很好是研发效能的标杆。这也是它们业务成功的重要因素。
以我在开篇词中提到的Facebook的部署上线流程为例。Facebook在2012年达到10亿月活的时候部署人员只有3个达到平均每个人支撑3.3亿用户的惊人效率。举个形象的例子如果全中国每一个人都使用Facebook那最多只要5个部署人员就够了。
Facebook做到这一点的基础就是不断提高研发效能。还是以上面的部署流程为例原来的部署已经非常高效但是在2017年Facebook又引入了持续部署做了进一步的优化实现了更高效率的部署上线。试想一下如果Facebook选择堆人、堆时间的话那需要增加多少人、多少加班才能做到呢
**所以在我看来,如果研发效能提不上去,单靠加人、加班根本不可能解决问题。**
**注重研发效能的另一个巨大好处**,是开发者能够聚焦产出价值,更容易精进自己的技术。于是,团队容易建立起好的氛围,进而又能促进生产效率的提高,形成良性循环,支撑持续的高效开发。
所以,国内公司近一两年越来越注重提高研发效能,许多公司甚至专门成立了工程效率部门。但是,在真正开展研发效能提升工作时,它们却常常因为**头绪太多无从下手,或者对方法了解不够,导致画虎不成反类犬的效果**。这,又和软件研发的高度创造性和灵活性紧密相关。
自软件行业诞生起开发者们就发挥聪明才智不断创造新的方法、流程和工具来适应和提高生产效率。互联网产业爆发以来这一趋势更是明显从最初的瀑布研发流程到敏捷到精益从持续集成到持续发布到持续部署从实体机到虚拟机到Docker从本地机器到数据中心再到云上部署从单体应用到微服务再到无服务应用。新的工具、方法可谓层出不穷。
面对如此多的选择,如果能处理好,则开发体验好,产品发布速度快,研发过程处于一个持续的良性发展情况。但处理不好,就会事倍功半,出现扯皮、重复劳动、产品质量不好、不可维护的情况。
微服务的不合理引入就是一个典型的例子。自从亚马逊成功大规模地应用后,微服务逐渐形成风潮,很多公司在不清楚适用场景的情况下盲目采用,结果是踩了很多坑。
比如我见过一个初创公司在业务还没开展起来的时候一上来就采用微服务因为没有要求一致的技术栈也没有限制服务的大小于是开发人员怎么方便怎么做只考虑局部优化而忽视了全局优化。半年下来20人的开发团队搞出了30多个服务和5种开发语言。服务之间的调用依赖和部署上线越来越复杂难以维护每次上线问题不断经常搞通宵才能让服务稳定下来。同时知识的共享非常有限有好几个服务只有一个人了解情况一旦这个人不在的时候这个服务出现问题解决起来就基本上成了“不可能的任务”。这样的错误使用微服务的公司非常普遍。
那,到底怎样才能有效地提高研发效能呢?硅谷的业界标杆公司又是怎么做到高效能的呢?
## 只有深入研发活动的本质,才能提高效能
我的建议是,提高研发效能,需要**深入了解研发活动的本质,从纷乱的表象和层出不穷的方法中,看到隐藏的模型,找到根本原则**,然后从这些原则出发,具体问题具体分析,找到合适的方法。这样做的原因是,软件研发很灵活,在实践的时候总会遇见各种不同的情况。越是灵活的东西,就越需要理解其本质,这样才能做到随机应变。
在Facebook的时候我们做事时都遵循一些基本原则。比如有一个原则是“**不要阻塞开发人员**”,贯穿在公司的很多研发和管理实践中。接下来,我给你举两个具体的应用场景,来帮助你理解这个原则。
**第一个应用场景是,本地构建脚本的运行速度要足够快**。开发人员在自己的开发机器上写完代码之后,都要运行这个脚本进行构建,把新做的改动在自己的开发机器沙盒环境上运行起来,以方便做一些基本检查。
这个操作非常频繁所以如果它的运行时间太长就会阻塞开发。因此确保这个脚本的快速运行就是内部工具团队的一个超高优先级的任务。我们对每次脚本的运行进行埋点跟踪当运行时长超过1.5分钟后,就会停下手中的工作,想尽一切办法给这个本地构建加速。
**第二个应用场景是,商用软件的采购**。对一定数额下的软件购买,开发人员可以自行决定,先斩后奏。而且那个数额还蛮高的,覆盖一般的软件完全没问题。
我个人就经历过两次在晚上加班时,要购买一个商业软件的情况。如果等主管审批,就需要到第二天。但,公司信任工程师能够在这样的情况下做出利于公司的决定,所以我可以直接购买并使用。这样一来,除了能提高这几个小时的开发效率外,更重要的是,我觉得自己拥有信任和权力,工作积极性更加高涨。
这两个应用场景差别很大却都是基于“不要阻塞开发人员”这个原则。Facebook之所以会有这个原则正是因为它认识到了**开发流程的顺畅是生产优质软件的关键因素,只有这样才能最大程度地释放开发者的创造性和积极性**。相比之下,很多公司更注重强管理下的流程和制度,而忽略了开发的顺畅,结果是开发人员工作起来磕磕绊绊,又谈何高效率呢。
看到这里你会说,我已经很清楚地知道,要想提高研发效能,就必须理解软件开发的本质。那么,软件开发的本质到底什么呢?
接下来,**我就和你探讨一下软件开发的本质特点,然后基于这些特点,搭建出一个研发效能的模型,希望你可以灵活运用,找到提高研发效能的主要着力点。这个思路,将是贯穿整个专栏的主线索。**
## 研发效能的模型是什么?
在我看来,**软件开发本质上就是一条超级灵活的流水线**。这个流水线从产品需求出发,经过开发、测试、发布、运维等环节,每一个环节的产出流动到下一个环节进行处理,最后交付给用户。
<img src="https://static001.geekbang.org/resource/image/3a/b7/3afb132da674578627c30272dd8504b7.png" alt="">
另外,这条流水线的每个环节都还可以细分。比如,本地开发环节可以细分为下面几个部分:
<img src="https://static001.geekbang.org/resource/image/44/28/44e048f968b603e49136b10f5dbdf728.png" alt="">
这种流水线工作方式在传统的制造业中很普遍也已经有了很多经验和成功实践。最典型的就是汽车生产中使用的丰田生产体系Toyota Production SystemTPS。所以**我们可以参考传统制造行业的经验来提高效能**。
事实上,瀑布模式就类似于传统流水线的处理方法:它强调每个环节之间界限分明,尽量清晰地定义每一个环节的输入和输出,保证每一个环节产出的质量。但,**和传统制造业相比,软件开发又具有超强的灵活性,<strong>体现在以下4个方面**。</strong>
1. 最终产品目标的灵活性。传统流水线的目标确定而互联网产品的最终形态通常是在不断地迭代中逐步明确相当于是一个移动的标靶。尤其是最近几年这一灵活性愈发明显。比如在精益开发实践中常常使用MVP最小可行性产品Minimal Viable Product来不断验证产品假设经过不断调整最终形成产品。
1. 节点之间关系的灵活性比如流水线上的多个节点可以互相融合。DevOps就是在模糊节点之间的边界甚至有一些实践会直接去掉某些环节或者融入到其他环节当中。
1. 每个节点的灵活性。每一个生产环节都会不断涌现出新的生产方式/方法。比如测试最近十多年就产生了测试驱动开发、Dogfood狗粮测试、测试前移等方法最近又出现的测试右移开始强调在生产环境中进行测试。
<li>每个节点上的开发人员的灵活性。跟传统制造业不同,流水线上的每一个工作人员,也就是每个开发者,都有很强的灵活性,主要表现在对一个相同的功能,可以选择很多不同的方式、不同的工具来实现。<br>
比如之前我在Facebook做后端开发的时候同样一个代码仓有的同事使用命令行的编辑环境VIM/Emacs有的使用图形界面IDE有的使用WebIDE在实现一个工具的时候大家可以自己选择使用Python、Ruby或者PHP。这其中的每一个选择都很可能影响效能。</li>
基于这些特点我们可以从以下4个方面去提高研发效能。
<img src="https://static001.geekbang.org/resource/image/68/51/68f278d53b0441413842539ed8919c51.png" alt="">
1. 优化流程主要针对特点1和2也就是最终产品目标的灵活性和节点间关系的灵活性进行优化。具体来说针对最终产品目标的灵活性主要是提高流程的灵活性让它能聚焦最终产生的用户价值以终为始地指导工作击中移动的标靶。而针对节点之间关系的灵活性则主要聚焦流水线的顺畅以保证用户价值的流动受到的阻力最小。
1. 团队工程实践主是针对特点3也就是每个节点的灵活性进行优化聚焦每一个生产环节的工程实践进行提高。
1. 个人工程实践主是针对特点4也就是每个节点上开发人员的灵活性来提高个人研发效能。争取让每个开发人员都能适当地关注业务、以终为始同时从方法和工具上提高开发效率实现1+1&gt;2的效果。
1. 文化与管理。任何流程、实践的引入和推广,都必须有合理的管理方法来支撑。同时,文化是一个团队工作的基本价值观和潜规则。只有建立好文化,才能让团队持续学习,从而应对新的挑战。所以,要提高效能,我们还需要文化和管理这个引擎。
优化流程、团队工程实践、个人工程实践以及文化和管理就是我们提高研发效能需要关注的4个方面也就是我们所说的研发效能模型。
在接下来的文章中我会以这个模型为基础从以上这4个方向与你介绍硅谷公司尤其是我最熟悉的Facebook的成功实践并着重向你讲述这些实践背后的原理。因为只有理解了这些原理和原则我们才有可能在这个超级灵活和高速发展的软件开发行业里见招拆招立于不败之地。
## 总结
在今天这篇文章中我和你再次强调了研发效能是团队能够持续为用户产生有效价值的效率包括有效性、效率和可持续性3个方面。
而关于如何提高效能,我的建议是深入了解研发活动的本质,从纷乱的表象和层出不穷的方法中,看到隐藏的模型,找到根本原则,然后从这些原则出发,具体问题具体分析,找到合适的方法。
最后我借鉴传统制造业流水线的形式并结合软件开发的特定灵活性总结出了研发效能的模型主要包括优化流程、团队工程实践、个人工程实践、管理与文化。专栏的后续内容我将分为这4大模块帮助你提高团队和个人的研发效能。
其实研发效能对于硅谷的公司和个人来说已经是常识而我在美国工作的10多年也已经习惯了这种工作方式。回国之后我深入了解了国内的开发情况对效能关注不到位的现状颇为吃惊。因为这不符合软件开发这个行业的基本特性也限制了国内软件行业的发展。
同时,我也对国内开发人员的生存环境,感到些许遗憾。本来软件开发是一个可以充分发挥创造性的、有趣的行业,技术的发展有无尽的空间。但是,开发人员却常常被业务拖着跑,技术发展和创造性都被限制住了。另外,因为拼时长的做法,也伤害了开发者的身体健康和家庭关系。
正是因为这些惊讶和遗憾,我将这十几年对研发效能的理解与实践,做了一次系统梳理,于是就有了今天提到的研发效能模型。我希望这种系统化的模型方法,能够为国内软件团队的效能提升提供一些参考和引导,也希望能够提升开发者个人的技能,以节省出时间来体会研发的快乐,提高生活的幸福感。
## 思考题
你有见过996对团队和产品造成伤害的具体事例吗
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,123 @@
<audio id="audio" title="02 | 效能度量:效果不好甚至有副作用,怎么回事?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/59/af/5947c186d71f13281914a84479f059af.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊研发效能的度量。
在和技术管理者,尤其是高层管理者聊起研发效能的时候,常常提起效能的度量这个话题。管理学大师彼得 · 德鲁克Peter Drucker曾经说过“一个事物你如果无法度量它就无法管理它”If you cant measure it, you cant manage it。要想提高研发效能自然要首先解决效能的度量的问题。
在软件研发过程中数据驱动的手段被大量采用比如说通过使用漏斗指标和A/B测试用数据来指导产品的方向。按理说软件开发行业是数据驱动的高手用数据驱动来理解研发效能应该早被研究透了啊。
但事实上,效能的度量却是一个出了名的难题,至今没有哪个公司敢号称已经找到了效能度量的完美答案。不仅如此,绝大部分软件公司在使用研发效能度量这个工具时,不但没有起到正向作用,还伤害了产出和团队氛围。
所以,在今天这篇文章中,我就与你一起看看研发效能度量到底是什么、常见错误,以及度量困难的原因。
## 研发效能度量的定义和作用
首先,我来介绍一下效能度量的定义和作用。
研发效能的度量代表一组可量化的数据或参数,用来跟踪和评估开发过程的“健康”状况。 换句话说,**研发效能的度量,从应用程序开发的生命周期中获取数据,并使用这些数据来衡量软件开发人员的工作效率**。我们希望通过这样的度量,能够根据客观的数据而不是个人的主观意见去决策,从而实现以下几点:
1. **跟踪团队的表现、提高团队的绩效。**通过确定研发效能指标,公司可以明确团队和成员的工作预期,从而使得开发人员能够目标性更清晰地投入研发。同时,这些生产指标可以作为“晴雨表”,帮助团队定位影响工作效率的不良因素,从而帮助团队消除这些因素,最终达到团队更快乐、绩效更高的目的。
1. **提高项目计划的精确度。**团队负责人可以通过度量来估算一个需求端到端的成本,包括收集成本、设计系统成本、开发测试成本,以及运维成本等,来了解每项活动在项目总成本中的占比,从而更好地确定这些活动的优先级。同时,我们可以了解哪些步骤有较大风险和不确定性,从而提高预测项目进展的精准度。
1. **了解流程是否高效,寻找需要改进的关键领域。**我们可以衡量进行每项研发活动所需的时间并估算其对质量和生产效率的影响然后比较成本和收益最终确定哪些步骤是高效的以及哪些步骤是需要改善的。我们还可以对不同的实践进行A/B测试以此来选择更好的方法。
提高团队绩效、提高计划精确度,以及寻找关键的待改进领域,这三个因素的结合有助于简化工作流程,发现瓶颈,帮助团队持续改善现有产品的生命周期,从而更高效地生产出质量更好的产品。
## 效能度量的错误案例
因为上述作用,绝大多数公司都非常重视研发效能的度量,并且基本上所有公司都或多或少地采用了度量。但遗憾的是,能够将其用好的公司少之又少,而且我见到的公司中还存在大量误用的情况。
接下来,我与你分享一些真实案例,我们一起来看看它们错在哪里,以及可以如何调整。
### 案例1全公司范围内推行一套效能度量指标
某大型公司为了提高效能,专门成立了一个不小的团队,通过详细调研,制定了一组研发效能度量指标,并引入和开发了相应的工具。客观来讲,这些度量方式的质量很高。
准备就绪后,这个公司找了几个团队做试点,把这些效能度量数据作为参考提供给它们使用。三四个月下来,试点效果不错,这几个团队的产出速度有了一定的提高。于是,公司高层决定在全公司范围内推行这套方案。
但,随着大范围的推广,一些团队发现,它们的研发流程跟试点团队差别较大,需要花费相当多的时间去收集度量数据,受益却不明显,甚至是得不偿失。
更极端的情况是,有一个团队的开发环境和这套效能度量工具的环境差别实在太大,所以不得不专门为这个工具部署了一个环境。然后每周六,他们就要到这个环境里,为度量系统提供数据,还常常被迫提供一些“假数据”,以满足度量系统的需求。
因此,这些团队觉得这套方案并不适合自己,公司内逐步出现了些许反对声音。但是,公司推进这套流程的态度非常强硬,为了顺利推进,还把这套流程与团队绩效绑定到了一起。一旦某个团队的指标未能达到要求,就直接实行绩效扣分。由此,效能度量的负面作用逐渐凸显。大量团队为了好的绩效,而被迫去玩数字游戏。
全公司范围推行这套方案的一年多,可以说是怨声载道。明明是一套高质量的度量系统,却阻碍了团队业务的发展,并严重伤害了员工的积极性。直至最后这个实际情况被反馈到一位高管那里,这套度量系统才被完全取缔。
### 案例2一个中型公司推行质量方面的指标
一个500人左右的公司公司的组织架构按照职能进行水平划分并没有进行矩阵式管理。为了提高公司的产品质量QA团队提出了一组软件质量方面的度量指标包括Bug严重性定义、每一个发布里不同级别的Bug的未解决率以及上线的一些质量流程。QA团队得到了公司高层的支持强制推行这些指标。
这些指标针对的主要是开发活动但开发团队却认为这些度量用处不大。他们认为公司研发过程中的真正问题在于产品定义变化过快常常是在冲刺开始后还不断更改需求。这就导致开发和测试团队即使拼命加班还常常错过发布日期产品的Bug也不少。所以开发团队提出真正应该度量的是产品需求的稳定性。但是公司高层认为需求变化是为了满足用户需求所以没有批准开发团队的这个要求。
结果是,研发人员觉得公司效能度量只是用来束缚他们的,根本不能帮助他们工作,于是工作积极性下降,离职率上升。同时,产品质量也并没有因为这些度量指标的推行而提高。直到我写下这篇文章的时候,这个公司还是这个情况。
### 案例3某创业公司聚焦度量开发、测试、上线准确度等指标
一个由10多个人组成的、拿到了Pre-A投资的创业公司正处在研发产品寻找市场吻合度的阶段。掌权的CEO和产品副总坚信数据驱动于是对研发流程定义了严格的度量和指标比如App上线周期准时率、Bug的关闭速度、性能参数等等。
整个团队很专业也很敬业。他们花了很多精力去严格完成这些度量指标做到了绝大部分情况下准时、高质量地上线产品。但是CEO和产品副总并不是产品专家只关注了开发过程中的数据却没有收集其他步骤的数据去快速试错和寻找产品方向。
结果就是,虽然产品的每一个发布都很准时,质量也非常高,但因为公司在寻找市场需求吻合度方面动作迟缓,导致用户增长缓慢。因此,一年半以后,资金耗尽,投资人失去信心,公司倒闭。
这3个案例只是不同规模公司的几个典型场景类似的失败案例数不胜数。那么上面这些案例的问题出在什么地方效能度量为什么这么难
## 效能度量被大量误用,问题究竟出在哪儿?
**研发效能难以度量的最根本原因在于**,软件开发工作是一项创造性很强的知识性工作,非常复杂且伴随有大量不确定因素。
比如,软件产品的需求变化很快,需求文档的更新常常滞后于工程实现,甚至有的敏捷方法论提倡完全抛弃需求文档。
又比如,软件产品的实现方式有很大的不确定性。一个相同的功能,可以采用多种语言、框架、平台,使用各种不同的研发流程生产出来。在这种情况下,我们很难通过度量来衡量这些不同研发方法和中间过程的优劣。
**面对这样的一个复杂系统,我们不可能覆盖其全部参数。而如果这时,研发人员的利益和这个度量结果相关,那么他就很可能会通过“做数字”来欺骗度量系统。**
关于这个主题,美国著名学者罗伯特 · 奥斯汀Robert Austin写过一本书叫作[《衡量和管理组织绩效》(Measuring and Managing Performance in Organizations)](https://amzn.to/2IxXjR9) 。他在这本书中给出的结论是,如果你不能度量一个事物的所有方面,那就不要去度量它。否则,你将得到“做数字”的欺骗行为。
这里有一组有名的Dilbert漫画讲的是一个公司宣布使用Bug修复数量做度量每修复一个Bug奖励10美元消息一出开发人员欢呼雀跃。一个程序员当场表示当天下午就要给自己写出一辆汽车因为他很容易就可以写出很多简单的Bug然后马上去修复它们。
通过这个例子,我想要和你说明的重点是:度量与绩效挂钩,结果是指标上去了,却没给软件产品带来任何好处。
<img src="https://static001.geekbang.org/resource/image/58/89/58261fef0dd2ecaab2a986f0b21e1e89.png" alt="">
>
备注:图片来自[https://dilbert.com/strip/1995-11-13](https://dilbert.com/strip/1995-11-13)
而我刚刚和你举的大型公司全公司范围内推行一套度量指标的案例,正是犯了度量与绩效挂钩的错误,这种情况下,很容易出现“做数字”的不良行为。这也是使用度量时最常见的错误,我们一定要留意。
**研发效能难以度量的第二个原因**和上面提到的根本原因相关但有其特殊性。很多公司有竖井silo存在所以常常会把注意力放到某一两个竖井上进行局部优化。但是局部优化并不代表全局优化甚至会让全局恶化。
上面那个中型公司推行质量方面指标的案例,就是在进行局部优化,不但没能提高产品质量,反而导致员工积极性受损,同时影响了团队之间的关系。
这样的问题,在按职能划分团队的公司很容易出现。因为在这样的组织划分下,更容易出现竖井,自然就更容易按照竖井来考虑小团队的表现。如果你的公司基于职能划分,一定要多加留意。
**研发效能难以度量的第三个原因在于**,度量指标一般用来度量软件产品的生产过程和产品质量,但是公司真正需要关注的是产品能否解决用户问题,也就是说能否产生用户价值。技术产品输出和用户价值输出之间的沟壑难以打通。
上面案例中那家创业公司聚焦度量开发测试指标,就是犯了这样的错误。产品质量再好,发布再准时,如果没有用户价值也是白费工夫。
当然了,研发效能难以度量还有一些其他原因,比如:
- 度量数据的收集难易程度不同,人们倾向拿容易收集的数据去关联效率,但事实上难以收集的数据对度量可能才真正有用。比如说代码行数容易衡量,但是用处很小。相比之下,产品用户价值的度量要困难得多,但用处却大得多。
- 软件开发和实践有一个滞后效应。比如,在团队中引入代码审查,在刚开始实行的时候,总体效率会出现短时间的下降,一两个月后才会逐步显现正面效果。那么,你现在要怎么度量它才好呢?
## 总结
在今天的分享中,我给出了效能度量的定义及作用,列举了三个典型的失败案例,并通过这几个例子和你详细分析了度量困难的几个主要原因。
研发效能的度量一直以来都是个难题,很多业界大佬也都发表过“研发效率不可度量”的观点。比如,马丁 · 福勒Martin Fowler在一篇叫作“[无法衡量生产效率”](https://martinfowler.com/bliki/CannotMeasureProductivity.html) (CannotMeasureProductivity)的文章中指出:这是我认为我们必须承认无知的地方。
周思博Joel Spolsky在一篇名为“[飙高音](https://www.joelonsoftware.com/2005/07/25/hitting-the-high-notes/)”做到最好Hitting the High Notes的文章中写到衡量程序员的工作效率相当困难原因如下
- 几乎任何你能想到的指标(比如,调测过的代码行数、功能点、命令行参数的个数)都很容易被“做数字”;
- 我们极少要求两个程序员做完全相同的事情,所以很难获取大型项目的有价值度量数据作为参考。
那是不是说在研发软件时,我们就不能使用度量效能的方法来指导工作了呢?如果是的话,这对于软件团队管理者而言,将会是一个难以接受的事实。
这个问题的答案,我们就留到下一篇文章再揭晓吧,敬请期待。
## 思考题
你在工作中有没有经历过研发效能度量的失败案例?如果有的话,你觉得失败的原因是什么?关于度量谜题怎么解决,你有没有什么建议?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,176 @@
<audio id="audio" title="03 | 效能度量:如何选对指标与方法,真正提升效能?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/19/01/19af57fa963560b23e3749d4d8d12701.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊如何正确使用效能度量。
在上一篇文章,我给你介绍了效能度量的定义、作用,以及几个使用误区。我们先简单回顾一下:
- 软件系统异常复杂,度量指标无法覆盖其所有参数,从而容易被“数字游戏”欺骗。
- 竖井指标的提高不等于全局指标的提高,局部优化不等于全局优化。
- 研发效能度量指标一般用来衡量软件产品的生产过程和产品质量,但公司真正需要关注的是能否产生用户价值。这两者之间存在着难以跨越的鸿沟。
正是因为这种种看似难以解决的问题,业界甚至有人认为研发效能的度量是一个无解的问题。但我并不这样认为。如果使用得当,效能度量可以给公司的研发带来非常大的好处。
举一个真实的例子。国内一个大概20人的研发团队研发流程混乱产品发布经常推迟但是大家都不清楚问题出在哪儿。于是团队负责人决定引入数据驱动开发项目经理正式跟踪研发过程中每部分的耗时并在功能发布后复盘。复盘时大家发现整个研发过程耗时分布如下
- 开发耗时1周
- 联调耗时1周
- 测试、发布耗时1周。
大家一致认为联调耗时一周是最需要优化的地方。于是对联调部分进行深入讨论发现根本原因在于前后端沟通不顺畅常常出现后端改动API但前端不知情的情况这是耗时最主要的原因。
为解决这个问题团队最终引入了一个Mock Service并规定在每个功能开发之前前后端要规定好API格式并由后端产生一个Mock Service让前端从一开始就有明确的API可以调用。后端如果要改变API格式必须通知整个团队并立即更新这个Mock Service。这就最大程度地避免了沟通不畅造成的时间浪费。
两个月以后,这个团队的平均开发周期有了明显改善,分布如下:
- 定义和生成Mock Service耗时1天
- 开发耗时1周
- 联调耗时1天
- 测试、发布耗时3天。
可以看到虽然定义和生成Mock Service需要额外的一天但是联调周期缩短了4天测试、发布周期也缩短了2天。所以整个开发周期从3周降到2周效果显著。
在这个成功使用度量的例子中,该团队主要做对了以下三点:
- 从全局的角度考虑度量指标,度量产品生产周期中各个阶段的数据,而不是直接查看某个竖井;
- 使用度量来寻找问题而不是用来做绩效考评;
- 使用度量来检验改进措施的效果。
从我的经验看,成功使用度量的关键在于:首先要对度量的分类有一个比较系统的了解,然后根据效能度量的特点,以及自己团队的目标来选取度量指标和方法。
## 效能度量的指标分类
在我看来要真正发挥度量的作用找到合适的度量指标必须先对指标进行分类。我推荐从团队和个人这两个维度对度量指标进行分类其中团队维度中又分为速度、准确度和质量3类所以一共是4类。
- **速度**:天下武功,唯快不破,速度指标主要用来衡量团队研发产品的速率。比如,前置时间,从任务产生到交付的时长。
- **准确度**关注产品是否跟计划吻合跟用户需求吻合能否提供较大的用户价值。一个例子是功能的采纳率也就是有百分之多少的用户使用了功能x。
- **质量**:如果质量有问题,产品的商业价值会被大打折扣。质量包括产品的性能、功能、可靠性、安全等方面。
- **个人效能**:个人开发过程中的效率指标,比如开发环境生成速度、本地构建速度等。
根据经验我总结了一张导图展示了这4个方面的度量指标供你参考。可以看到我一共给出了40多个指标从名字上可以看出它们大概要度量什么。
在后面的度量指标推荐时,我也会与你讨论其中几个指标。如果你不清楚哪条指标的具体含义,可以自行查阅,或者留言给我。另外,你现在只需要对这些指标有个整体概念,用到时再回过头深入研究,选出适合自己的就可以了。
<img src="https://static001.geekbang.org/resource/image/96/8f/967061c8d8d4ee26a34b5e7411fcf28f.png" alt="">
接下来,我就直接和你推荐度量效能的一个原则和四个使用方法。
## 效能度量的原则
效能度量不与绩效挂钩,是正确使用效能度量最重要的一点,再怎么强调也不为过。所以,我向你推荐的效能度量的原则就是:**效能度量不要与绩效挂钩,而应该作为参考和工具,帮助团队提高效能。**
因为无法覆盖100%的度量指标,把度量与绩效挂钩就一定会产生“做数字”的现象。这时,使用效能度量非但起不到正面效果,还会对公司和团队造成伤害。管理者常常倾向于使用度量与绩效挂钩这种方法,是因为它能具体到数字方便管理。这种错误,我们一定要注意避免。
**如果你只能记得这篇文章的一句话,那我希望这句话是“效能度量不要与绩效挂钩”。**
Facebook就刻意避免把效能度量跟绩效挂钩。比如代码审核相关的效能度量页面并没有包括审核的提交个数、代码审核的时长、代码审核通过率这些常见指标。
如果有团队提出要获取这些数据那么我们工具团队只会提供一些脚本由团队主管自己去获取所需数据而且获取的方式并不那么方便。同时团队成员也不能在Phabricator网站上直接看到这些数据。
这是一个比较典型的主动避免度量,从而避免它与绩效挂钩的例子。
不能把效能度量与绩效挂钩,那怎样才能使用度量提高效率呢?答案是:**提供度量作参考和工具,帮助团队提高效能。**
度量一旦与绩效脱离关系,就可以作为重要参考和工具,帮助团队持续进步。比如,下面这些度量指标:
- 缺陷密度,可以让团队了解产品质量的走向。
- 新旧Bug占比一定程度上可以反映技术债的严重程度。
即使是代码行数这样臭名昭著的度量指标,如果只是用作参考,都可以帮助团队和成员提高。
在Facebook有超过4套的数据展示面板工具。这些面板工具使用起来非常灵活开发人员可以定制面板展示对自己有价值的效率度量比如上线前高优先级Bug数、未完成Bug数、燃尽图等。我们每个团队都是**主动**使用这些面板,来帮助团队达成业务目标。
下面我就来向你推荐4个度量使用方法。
## 效能度量的推荐方法
### 第一,目标驱动,度量对的事
提供用户价值是公司存在的根本,因此与之相关的指标是最最重要的。这一点非常好理解。从这个角度来看,以下几个相关度量指标比较有效:
1. 净推荐值(Net Promoter ScoreNPS),是通过调研了解用户满意度,实用性很强。如果你不了解的话,可以看一下[这篇文章](https://zhuanlan.zhihu.com/p/38117396)对NPS的介绍。
1. 系统/App宕机时间(System/App Downtime) 和严重线上事故数(Incidents),衡量的是系统的可用性,通常与用户价值直接挂钩。
1. 热修复上线时间(Hotfix Time),指的是一个热修复从编码完成到部署到生产的时长,关系到解决重大缺陷的效率,与用户价值强相关。
我的建议是你可以再去看看准确度的其他衡量指标根据团队情况找到能够最直接衡量产出有效性的指标而不是一定要采用上面这3个指标。
### 第二,先从全局上找瓶颈,再深入细节
前面提到过,在度量效能时,很多团队往往是一上来就不加辨别地扎到某几个竖井里去寻找问题。这样的局部优化往往对全局优化无效,还会影响团队之间的关系,带来负面效果。正确的做法应该是,先检查全局,找到关键瓶颈之后,再进入细节分析和解决的环节。
具体实现起来,方法也很简单,就是收集产品周期中每一个阶段所占用的时间,包括计划的时间和最后实际花费的时间,然后寻找问题最大的地方。
具体的**耗时收集**方法,大致有两种:
1. 人工收集。比如,文章开头提到的项目经理手工收集研发过程中每环节的耗时。
1. 通过工具收集。比如Trello的任务显示看板或者Jira的看板都可以清楚地看到每个环节有多少任务以及流动情况从而直观地识别瓶颈。
收集到了每环节的耗时之后,下一步就是去**发现瓶颈**。除了直观的观察之外我推荐一个工具累积流程图Cumulative Flow Diagram)。具体方法是横轴是日期纵轴是每天统计的各节点任务数量绘制出来就形成了累积流程图。比如我们统计待办Backlog、开发Dev、测试Test、生产Production这几个节点累积流程图如下所示
<img src="https://static001.geekbang.org/resource/image/0e/a1/0eb672f2d6784b919a8032cfce68e2a1.png" alt="">
从水平方向看待办和生产这两条线的距离就是交付时间Cycle Time也就是从开始开发到交付的时长。垂直方向的距离代表WIP也就是系统中的任务数。通过这张图我们就可以一目了然地看到任务在流程中的流动情况并直观地发现问题。比如WIP数值变大大、交付时间变长通常都代表研发效能下降。
可以看到,累积流程图对于查找全局瓶颈非常有用。实际上,我们还可以通过它预估发布日期,查看开发是否被阻塞等。这里有一篇[文章](https://ruddyblog.wordpress.com/2018/04/23/%E7%B4%AF%E7%A9%8D%E6%B5%81%E7%A8%8B%E5%9C%96-cumulative-flow-diagram-%E8%A7%A3%E5%AF%86/),讲述了如何通过累积流程图,找到问题并进行调整,供你参考。
### 第三,通过主观的方式来评价、提高效能
没有客观的方法去衡量开发人员的生产效率,并不意味着你无法衡量它。 一个办法是,你可以尽量公平地去主观地测量它。事实上,平时工作中我们也确实是这么做的。
比如在一个团队里面大家通常都能对谁是技术大牛达成共识。这是我们的大脑依据平日收集的点滴事实做出的判断也是因为当前还没有AI系统能够自动做出这样的分析才显得非常主观。
所以,**我推荐收集人工反馈的办法,来帮助我们做出尽量公平的主观评价。**
针对研发环境、流程、工具的效能进行评价,可以使用**公司成员对研发效能满意度的净推荐值**。虽然现在还没有很强的理论依据可以证明,这个指标可以大幅提高研发效率,但从我看到的许多真实案例来说,事实确实如此:满意度让员工工作更积极,而工作积极又能提高满意度,是一个良性循环。
我在Facebook内部工具团队工作时我们每个季度都通过调查问卷收集开发人员的建议另外我们还有IRC聊天室类似的工具供讨论和吐槽。这些反馈都会成为工具团队调整工作内容和优先级的依据。
至于调查问卷的内容,具体来说可以关注以下这几个方面:冲刺的准备充分度、团队沟通有效性、冲刺过程效率、交付价值如何、交付信心如何、对产品方向及路线的兴奋度等。
**针对个人研发效能作评价可以采用类似360度绩效考评的方式来收集同事之间的评价**。评价的标准基于在用户价值输出上做出的贡献包括自身产生的价值以及帮助团队成员产生的用户价值。如果一个员工可以很好地产出用户价值那他的研发效率通常不会差。其实Facebook就是使用这种方式来评价员工效能的虽然主观但很公正。
我整理了一张表格,列了一些可以用来收集反馈的问题,涵盖开发效率、质量、团队贡献等方面。
<img src="https://static001.geekbang.org/resource/image/f4/1e/f4767b13d8f1eba1ec08a0c88f3d511e.png" alt="">
### 第四,关注个人维度的指标提高效能
个人效能相关的度量,直接反映开发人员的开发效率和满意度,对团队产出影响很大。所以,作为管理者/内部效能团队,应该关注开发人员的高频活动,并自动化和优化这些步骤,让开发人员能专注开发。
一般来说,“个人调测环境构建速度”是一个比较重要的指标。它描述的是开发人员在本地做好一个改动,到能够进行本地调测的时长。开发人员每次修改自行验证都要经历这个步骤,对它进行优化非常有用。
我以前在Facebook的时候后端代码及网站的绝大部分修改都可以在一分钟之内在本地开发机器上使用线上数据进行验证非常爽快效率极高。
但是,我曾经在其他公司见到过这样一种情况:一个修改需要在本地编码,上传到服务器编译,再通过工具下载到另外一个机器上验证。这个过程至少需要一个小时,在这种情况下,即使是在验证时发现一个简单错误,修改后简单验证也需要再花费一个小时。
不难想象这种情况下开发者的沮丧心情。如果能解决个人效能维度上的痛点,必然对提高产出和士气有重大作用。
## 小结
好了,这就是我今天要和你分享的效能度量的指标和方法了。接下来,我与你总结下今天的核心知识点。
首先我将度量指标分为了准确度、速度、质量和个人效能4个方面并列举了40多个具体指标。然后我根据软件研发以及效能度量的特点给出了1个原则和4种建议的度量方法。原则是不要与绩效挂钩度量方法包括目标驱动度量对的事先从全局上找瓶颈再深入细节通过主观的方式来评价、提高效能关注个人维度的指标提高效能。
我把这4种方法以及基本思路总结成了一张表格方便你理解与记忆。
<img src="https://static001.geekbang.org/resource/image/a5/9e/a52387c453af2f78e3e68842dd19569e.png" alt="">
研发效能的度量很灵活,也很容易踩坑。所以,我希望上面的这些原则和方法能够作为你实施度量的参考,从而达到以下几个目的:
- 跟踪团队的表现、提高团队的绩效;
- 提高项目计划的精确度;
- 了解流程是否高效,寻找需要改进的关键领域。
最后,我来分享一下我个人对效能度量的两大感受:
1. 度量只是工具,不是目的。切记度量的真正的目标是提高效能,不要舍本逐末。比如说,如果度量花费的时间超过了收益,那就不要去做。
1. 虽然我们推崇数字驱动,但在效能的度量上,不要迷信数字,适当使用主观反馈效果反而更好。
## 思考题
你能从下面这张累积流程图中,看出什么问题吗?
<img src="https://static001.geekbang.org/resource/image/2a/67/2a25cdb23692a921f6b20af9bfe76b67.png" alt="">
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,152 @@
<audio id="audio" title="04 | 流程优化:怎样才能让敏捷、精益真正为我所用?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/05/e9/051a550d3ed94d831c1a105d0eeeace9.mp3"></audio>
你好,我是葛俊。今天我们来聊聊怎样从流程方面来提高研发效能。
从这一篇文章开始我们就正式进入研发流程模块了。在第1篇文章中我与你强调了软件开发的最大特点在于它是一条非常灵活的流水线因此提高研发效能的第一步就是优化流程。
<img src="https://static001.geekbang.org/resource/image/e4/bf/e4f3216aa6a40f57fcc7356fb98838bf.png" alt="">
因为优化流程会涉及方法论所以我会先和你介绍高效实践方法论的关键要素。然后我会按照目标、原则和实践3个层次从抽象到具体给你逐步讲解如何优化流程。
需要说明的是,在这一篇文章中,我不会与你大量讨论实践细节,而是把这些内容放在了后续的文章中。
## 如何实践方法论?
其实在研发流程上最不缺的就是方法论从敏捷到精益再到看板层出不穷。但实施效果却不理想。尤其是敏捷无论在硅谷还是在国内绝大部分使用敏捷的团队实施得都不理想导致这个概念的争议很大Scrum有时甚至成了贬义词。
相比之下像Facebook、Google等高效能公司并没有强调使用Scrum、看板等工具研发效能却很高。**这是不是说敏捷、精益这些方法论本身就有问题呢?**
事实上虽然Facebook、Google这些公司没有明确提及敏捷、精益、看板这些方法论或者没有严格地去使用Scrum等框架但在开发流程中却实实在在地应用了这些方法论的精髓。所以说**方法论实施效果不好,关键在于没用好。**
在学习方法论的时候,我推荐使用类似美国著名作家、企业顾问西蒙斯 · 涅克Simon Sinek总结的Why-How-What黄金圈法则。
参见下图最中间的一个圆是Why也就是这个方法论的目标是要解决一个什么问题第二个圆是How也就是这个方法论的基本原则、指导思想最外层的圆是What也就是这个方法论的具体实践。
<img src="https://static001.geekbang.org/resource/image/b0/cb/b06e884d3988c759697a0091e2d471cb.png" alt="">
这3个圆圈从内向外是一个从抽象到具体从通用到定制的过程。
**在使用一个方法论的时候,一定要从内往外看。**
- 中心的目标一般错不了。比如,敏捷的目标就是快速应对变化。
- 原则的通用性就差一些,你需要在理解的基础上挑选适合自己的。比如,敏捷中有一条原则是“面对面交谈是最好的沟通方式”,就不一定适合你的团队。
- 具体的实践,就更要小心,切忌生搬硬套。
所以,我们必须首先深入理解这个方法论的目标和基本原则,然后根据原则因地制宜地选择具体实践。在选择具体实践时,往往需要在已有实践上做些修改才能达到效果,否则事倍功半。
以最容易出现问题的Scrum为例。敏捷的目标是快速应对变化而Scrum就是用来服务这个目标的。但是很多团队在使用的时候严格照搬Scrum的具体方法而“严格照搬”本身就已经违背了敏捷的目标。
相比起来Facebook的众多团队“严格”使用Scrum的很少却一直在大力优化管理、开发等流程来快速应对变化最快找到并满足用户的最新需求。具体来说他们很早就引入了A/B测试、灰度发布、每周定时全量代码部署等实践。这些都是和敏捷方法论相吻合的也是Facebook业务成功的关键技术支撑。
**在引入实践的时候,我的建议是逐步优化已有的开发流程和框架,甚至只给出原则,让团队成员逐步摸索并最终找到合适的方法**。比如,你可以把一个核心原则通知给团队:“流程中总是有一个核心瓶颈。找到它,并解决它”。通过这样一个基本原则的指导,让具体实践逐步形成,对现有工作的影响,以及受到的阻力都会比较小。
了解了怎样使用方法论接下来我和你一起看一组提高研发流程的目标、原则和实践。其实这些并不是一套全新的方法论而是基于Facebook、Google等高效能公司的实践并结合对精益、看板、敏捷等方法论的思考我得出来的一个总结。
## 目标一:寻找用户价值
以终为始地来看,我们最终目的是产生用户价值,生存下去。为此,我们经常需要主动去寻找最好的产品形态。只有方向找准了,流程产生的结果才能有效,才能产生用户价值。所以,**优化研发流程的第一步,就是提高寻找用户价值的效率。**
在这一点上精益创业Lean Startup系统最有效。虽然名字里面有创业二字也有较多商业模式设计和用户开发方面的内容但它有两条原则值得作为开发流程的参考。
**第一条原则是:衡量每一个时间段成果的标准,应该是价值假设方面的进展**。也就是说,你的工作应该让你学习到如何更好地给用户提供价值,而不是开发了多少功能。
举一个极端的例子。比如你的团队在一月份开发了5个功能用户反馈都一般。这时你看不出用户更喜欢什么东西更讨厌什么东西。而在二月份你们只开发了一个功能预计很有用但收到的用户反馈却是负面的被迫连夜回退了这个功能。
那么哪个月的成果更好呢显然是二月份因为它能明确告诉你这个假设是错的让你们在寻找产品符合市场吻合度Product-Market-Fit上前进了一大步。
**第二条原则是使用最小可行性产品Minium Viable ProductMVP来帮助学习**。这里的关键点是要以探索价值为出发点设计产品最快地验证你的假设功能要尽量少能够使用就可以。具体的方法有数据驱动、A/B测试、灰度发布等。
在Facebook工作的时候我们开发一个功能时都会提前计划要验证哪些假设然后设计怎样收集数据才能够验证这些假设。这样一来功能上线的时候就开始收集数据了一旦发现功能不能提供足够的用户价值就把这个功能下线从而确保每个功能都是本着提供用户价值的目的去的。
同时公司也会不断投资A/B测试等试验框架让开发人员能够尽量容易地收集和处理数据。这种开发方式有一个名字叫作度量驱动开发Metrics Driven Development。如果你想深入了解这种开发方式可以参考Uber公司的这篇[文章](https://eng.uber.com/experimentation-platform/)。
## 目标二:提高用户价值的流动效率
软件研发是一条流水线,里面流动的是一个一个给用户提供价值的功能。那怎么提高这条流水线的效率呢?也就是,如何提高用户价值的流动效率。
这里有4条比较直观的基本原则
- 第一,让功能尽快地流动;
- 第二,让节点之间的联动更加顺畅;
- 第三,节点之间的融合;
- 第四,发现整个流程中的瓶颈,并解决它们。
接下来,我们分别看看这四条基本原则。
**第一,让功能尽快地流动**,说白了就是快速开发。问题发现得越晚,修复代价越高,这已经是常识。要做到快速开发,开发人员需要**尽量把功能拆分,同时做好一个提交之后尽快提交**。要做到这一点关键原则是降低提交的交易成本Transaction Cost
提交的交易成本指的是,每一个提交都需要做的额外工作。只有把这个工作量降下来,才能让功能拆分有价值,否则就会抵消掉拆分带来的好处。
具体工程实践包括提高本地构建速度,提供方便的自测环境、测试自动化、持续集成、定位问题提交自动化等。快速开发这个话题是开发高效的关键因素,我会在下一篇文章中与你详细讲述。
**第二,让节点之间的联动更加顺畅**可以通过对关键流程的自动化、工具之间的网状互联以及节点之间的融合来实现。在具体实践上个人代码上线后在和他人的代码集成时容易出现问题这时就可以使用CI/CD持续集成/持续交付)流水线来自动化代码集成过程。
尤其是CI基本上对所有软件开发公司都很有价值。我们一定要尽量用上CI。
另外,有些公司有从需求到开发到上线的一条龙流水线,也被叫作开发者桌面。国内的公司,尤其是大公司很喜欢这种方式。它的好处是,可以把很多底层的东西隐藏起来,容易上手。但它的缺点就是灵活性不够,毕竟难以用一条流水线做到高度的定制,来满足各种各样的高效开发。
开发者桌面的全景图如下所示。开发者桌面作为核心节点,连接其他所有工具,各个工具之间也有一些连接。
<img src="https://static001.geekbang.org/resource/image/d7/76/d7f9a3e8c81bd253d4db4c9331b58076.png" alt="">
在Facebook和Google并没有一个端到端的流水线它们采取的方式是**让这些流程中使用的工具通过开放的API进行一个网状连接从而开发者可以灵活定义小范围的流程高效工作**。在这个网状结构里面,关键节点会作为重要节点大量连接其他流程中的工具,公司对这些关键的、小范围的流程进行大量优化。
比如下面这张图片一共有11个工具互相之间连接很多形成网状结构。其中工具4和工具9是关键节点与很多工具有连接。
<img src="https://static001.geekbang.org/resource/image/8c/3f/8c06ec475f9e03b170c878f9ad64733f.png" alt="">
比如在Facebook代码审查工具Phabricator是一个关键节点可以支持代码审查、代码静态检查、单元测试、集成测试、前后端联调等关键开发工作。这一组工作就是上面提到的一个关键的、小范围的流程。
最近几年由于IDE的发展Nuclide成为了一个新的重要节点。开发者可以在Nuclide里直接进行高效的代码开发以及代码审查等一系列工作。类似地Google的WebIDE也是工具链的一个重要节点。
**从我个人的经验看,这种网状结构提供的灵活性,对提高开发效率和提升开发体验,都有积极的促进作用。**
**第三,节点间的融合**,也是为了保证节点间的联动顺畅。也就是模糊节点间的边界,让功能在节点之间的流动更顺畅。在这一方面,我见到比较有效的方式是:**职能团队提供平台和工具,让全栈工程师能够自己处理端到端的工作**。比如,测试团队提供测试平台和工具,运维团队提供运维平台和工具,这些平台和工具可以通过服务化自助使用。
比如在Facebook没有专职的测试人员只有测试工具团队运维人员也很少主要是提供大量的工具让开发人员能够自己进行测试、运维的基本工作实现开发人员在“开发、测试、运维”这几个步骤上的全栈工作。因为开发人员对自己开发的功能最为熟悉所以这种端到端的全栈工作方式可以让开发和调测效率达到最高。在国内我看到阿里云效也在推荐这种方式。
**第四,发现整个流程中的瓶颈,并解决它们**。约束理论创始人艾利 · 高德拉特Eliyahu Moshe Goldratt在20世纪90年代提出了解决约束的聚焦五步法至今仍然适用软件研发这个价值流体系。具体步骤如下
- 第一步,找到系统中的瓶颈;
- 第二步最大限度地利用Exploit瓶颈尽量通过提高效能的办法解决瓶颈
- 第三步,让企业的所有其他活动都让步于瓶颈改善工作;
- 第四步打破Elevate瓶颈如果第二、三步无效就通过给瓶颈节点增加资源的方法来解决瓶颈
- 第五步重返Repeat第一步找出新的瓶颈持续改善。
**具体的实践有可视化和复盘。**
在可视化方面粘上便利贴的白板或是像Trello那样的电子看板就是很好的工具。
<img src="https://static001.geekbang.org/resource/image/d8/e0/d8903d8ab04c3ef2797af599ce699be0.png" alt="">
这里我需要强调一下,我们一般把这种任务卡片化的系统叫做看板,但它并不是真正的“看板”,只是一个任务可视化的面板而已。
**真正的“看板”是信号卡片,用来表示流水线系统可以增加新的工作项了**。后面我会详细介绍。通过任务可视化,我们可以直观地看到哪一个环节的任务特别多,卡住了,也就是瓶颈在哪儿。另外,前面文章提过的累积流程图,也是发现问题的一个有效工具。
另外统计图表和仪表板也是很不错的工具。在Facebook任务管理系统、部署系统、代码审查系统的API是开放的各个团队可以方便地查询数据制作图表。在仪表板方面2014年的时候就有4个系统可使用。大部分的统计图表和仪表板都是非工具团队自己开发出来并最终推广全公司使用定制功能也很强。
**在复盘方面Facebook做得也很好。他们有一个SEV复盘系统用来记录公司发生的重要事故进行复盘**。每个团队也会不定期地复盘,以定位瓶颈问题并提高。
举一个复盘方法的例子。我在国内某公司遇到一个问题,就是团队在协作上配合不积极,出了问题不是先去解决问题,而是先想方设法甩锅。为解决这个问题,我引入了线上事故回溯讨论会,每两周一次,对发生的事故进行讨论,重在根因分析和以后如何避免,并事前强调目的不是追责。因为,每个故障分析都能暴露出藏在深处的问题,对提高产品质量和团队间的信任效果都很好。
## 小结
优化流程,是提高研发效能的第一步。我们应该按照目标、原则和具体实践的顺序学习和使用各种方法论,选择和配置最适合自己团队的工程实践。
我基于Facebook、Google等高效能公司的实践并结合对精益、看板、敏捷等方法论的思考给你介绍了“寻找用户价值”和“提高用户价值的流动效率”两条目标以及它们对应的6条原则和若干具体实践。
从我的经验来看,高效的研发工作流,对产品质量和研发速度至关重要,同时可以提升员工的幸福感,进而保证持续的高效产出,提高研发效能。
**Facebook在流程方面的实践给我最大的一个感受就是以实用主义的态度从原则出发灵活优化流程**。在Facebook的几年时间里我并没有听到很多新方法论的时髦术语但是公司的很多实践却和这些方法论的原则是一致的甚至是超前这些方法论的。
比如在DevOps流行前的很多年Facebook就开始了打通开发和部署的工作以及推行全栈工程师的工作方式。
从实际出发、以终为始的实用主义是我从Facebook学到的高效研发的最重要原则没有之一。
## 思考题
你理解的精益看板,跟文中提到的任务可视化白板一样吗?你知道它们之间的关系吗?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,158 @@
<audio id="audio" title="05 | 代码入库前Facebook如何让开发人员聚焦于开发" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/c2/8f/c2d669c3ea1c308099d525d9e8c9368f.mp3"></audio>
你好,我是葛俊。今天,我将与你分享优化流程中,代码入库前的开发流程。
代码入库之前的开发活动,主要包括编码、调测调优、静态检查、自动化测试、代码审查等。这是开发者编写代码的步骤,自然是提高研发效能的关键环节。
<img src="https://static001.geekbang.org/resource/image/44/28/44e048f968b603e49136b10f5dbdf728.png" alt="">
提高开发者编写代码的效能,关键在于让开发者不受阻塞、不受不必要的干扰,从而全身心地聚焦在产品开发上。我把这种不受阻塞的开发状态叫作**持续开发**。
一个团队如果能够做到持续开发,那么它的有效产出自然会很好。而对于个人开发者而言,持续开发能够帮助我们把精力集中在技术本身,对技术和个人能力的提升都大有裨益,所以是一种很好的开发体验。
在我看来,持续开发的基本原则主要包括两条:
1. 规范化、自动化核心步骤;
1. 快速反馈,增量开发。
接下来,我们就一起看看这两条核心原则吧。
## 规范化、自动化核心步骤
要让开发者聚焦于开发,就必须把研发流程中可以自动化的步骤尽量自动化。因为一般不可能完成所有步骤的自动化,所以我推荐的方式是:分析关键路径上的活动,以及耗时较长的活动,然后投入精力优化这些步骤。
首先,我们需要明确具体的开发步骤有哪些。我将其归纳为以下三大步:
1. 获取开发环境,包括获取开发机器、配置环境、获取代码等。
1. 在本地开发机器上进行开发,包括本地的编码、调测、单元测试等。
1. 代码入库前把改动提交到检查中心比如Gerrit再进行一轮系统检查主要包括代码检查、单元测试、代码审查等通过之后再入库。
<img src="https://static001.geekbang.org/resource/image/7b/bf/7be4f0d26fefd1f22555238389fef5bf.png" alt="">
针对这三大步骤我们可以有以下3个工程实践
- 提高开发环境的获取效率;
- 规范化、自动化本地检查;
- 建设并自动化代码入库前的检查流程。
接下来我们分别看看这3个工程实践如何落地。
### 提高开发环境的获取效率
开发环境的设置,包括开发机器的获取、网络配置、基本工具以及代码的获取和配置。这些操作的频率不是特别高,但如果步骤复杂、耗时长,就会对新成员加入、成员切换团队或者项目,产生比较大的影响。所以,开发环境的获取效率,通常是值得优化的。
有一个可以采用的优化方式是,**把整个开发环境的获取,进行服务化、自助化**。也就是说开发者可以自助地申请获取环境不需要IT部门的人员介入从而既节省了开发者的时间又降低了IT部门的人力成本。
比如我之前在Facebook工作的时候采用虚拟机作为个人开发机。内部工具团队开发了一个基于共享机器池的开发环境服务系统让开发者可以在网页上申请和释放机器。机器返还之后开发环境服务系统会自动对它进行清理配置之后再重新放回机器池中。这就使得开发者可以在5分钟之内拿到一套干净的环境。
而至于开发机器上的代码,这个服务系统可以克隆获取团队常用的代码仓,并定时拉取最新的代码。这就使得开发者拿到一台机器之后,只需要再额外拉取很少的代码就可以进行开发。
上面这种方法定制性很强Facebook并没有开源。如果你准备进行这方面尝试的话在机器的生成和配置方面我推荐两种方式。
**第一种方式**借助基础设施即代码Infrastructure as CodeIaC系统。比如HashiCorp公司的[Terraform工具](https://www.terraform.io)。它支持声明式的方式快速产生自定义配置的机器并在上面运行脚本进行配置。TerraForm使用插件机制支持许多底层平台比如AWS、阿里云或者本地系统。
这种方式的优点是使用方便、功能强大,但前期投入大。
**第二种方式是**提供机器镜像和配置脚本。通过镜像让每一台新机器拥有最基本的设置比如CPU、操作系统、基本软件然后通过脚本实现基本配置比如网络设置、软件更新等。这种方式的优点就是前期投入小。我在Stand时就使用了这种方法效果不错。不过它的缺点就是不够灵活。
### 规范化、自动化化本地检查
本地检查是指,开发者在开发机器上进行的验证,比如语法检查、规范检查、单元测试、沙盒搭建等。我推荐的方式是,**根据团队实际情况,找到合适的工具和配置进行这些检查,并让团队成员统一使用**。
在这个方面Facebook的方法是把很多工具都放到一个网盘上挂载到每台开发机器的Linux文件系统上让开发者们不用安装就可以直接使用。
挂载共享网盘的方法非常方便,因为用户不用操心工具的升级。但如果你们的系统没有这样的网盘的话,也可以通过脚本让开发人员一键安装工具和完成配置,效果也不错。缺点就是软件更新比较麻烦,因为要通知用户手动更新或者设计自动更新机制。
至于检查中使用的工具,我们需要根据具体的语言和框架去选择。
### 建设并自动化代码入库前的检查流程
建设并自动化代码入库前的检查流程,是持续集成前的必要工作,也可以看作是持续集成的一部分。它对入库代码质量起到一个门禁作用,对提高质量用处很大。**我认为,除了人数非常少的初创公司以外,其他开发团队都应该进行这个配置。**
这个流程一般可以使用代码仓管理系统作为中心直接使用或者通过钩子集成其他工具和系统来实现。比如使用GitLab提供的GitLab CI/CD框架。基本方法是在项目的根目录里创建一个.gitlab-ci.yml文件来描述你的检查环境设置和步骤。你可以点击这个[链接](https://docs.gitlab.com/ee/user/project/pages/getting_started_part_four.html)查看具体的方法。
在Facebook这一步使用的是开源版Phabricator的一个内部Fork。Phabricator在工作流中使用单元测试和Linter的方法你可以参考[帮助文档](https://secure.phabricator.com/book/phabricator/article/arcanist_lint_unit/)。
以上内容就是持续开发的第一个原则,也就是规范化、自动化核心步骤。这个原则,可以帮助开发者尽量减少非开发工作的耗时,从而把更多的时间、精力投入到本职的开发工作中。接下来,我们再来看看持续开发的第二个原则,即提供快速反馈,促进增量开发,这样能及早暴露问题,从而保证将来的工作不会因为实现错误,或者方向调整而进行昂贵的修改。
## 提供快速反馈,促进增量开发
提供快速反馈进行增量开发指的是能够快速验证已经完成的开发工作说白了就是边开发边验证。具体的工程实践主要包括以下3个
- 灵活使用各种Linter和测试
- 建设并优化沙盒环境;
- 使用实时检查工具。
接下来我们分别看看这3个工程实践如何落地。
### 灵活使用各种Linter和测试
最常用的快速验证方法就是提高运行静态检查和测试的方便性、灵活性。各种语言、框架都有自己的测试框架和Linter这里我就不再一一列举了。接下来我会与你分享**两种通用的有效使用Linter和测试的方法**。
首先,用命令行的工具来封装各种检查。命令行工具特别适用于自动化,方便开发人员使用。比如,我们可以通过命令行脚本,来实现简单的工作流。
举一个具体的例子我希望团队在开发中在运行公司提供的统一检查之外还可以运行一些适应团队自身特点的检查每个开发人员也可以添加自己希望使用的检查。这样就可以通过一个Shell脚本依次调用公司的、团队的、个人的检查来实现很方便。
其次以服务化的方式把这些检查的能力提供出来。比如Facebook的基础平台团队提供了在云上运行单元测试的能力并把这个能力通过服务的方式提供给开发者以方便他们在自己的开发机器上调用。也就是说开发者可以调用云上资源运行大量的测试而不占用本地资源从而在运行测试的同时可以高效地进行开发工作。
### 建设并优化沙盒环境
沙盒也是一个高频使用的、提高质量的工具。开发者如果能够方便地在本地搭建沙盒进行验证,那么进行开发自测的频率和质量就会大大提高,进而提高产品质量。所以,我推荐你在沙盒环境的搭建上进行投入。
在沙盒环境搭建中,有两个常见的优化点:
- 本地构建。因为我们必须把改动构建成产品才能进行本地验证,而这个步骤通常耗时较长。我推荐的优化方法是,不要使用全量构建,尽量只进行最小范围的增量构建。
- 测试数据的产生。产生贴近生产环境的数据往往比较费劲Facebook的做法是开发环境直接使用生产环境的数据不过这个方法比较激进使用的公司比较少。另一个常见方法是进行生产数据的导出并脱敏然后使用到沙盒环境中。
### 使用实时检验工具
快速提供检查反馈做到极致就是开发者无需手动触发检查工具就会自动探测到改动、自动运行检查。最常见的是IDE中的实时语法检查。我们可以花一些时间来配置IDE。另外有些工具可以自动监视文件系统的变化文件有变化时自动重启服务。这对于开发者来说非常便利。
举个例子。使用Node.js进行开发时nodemon就是不可或缺的工具你只要在原来的命令前加上nodemon就可以使用。比如启动服务的语句是./bin/www使用nodemon的形式就是nodemon ./bin/www。这样运行服务之后如果你的文件有修改nodemon就会自动重新运行。
你可以在下面这个动图中看到在第一次保存时有语法错误nondemon重新启动失败第二次保存时修复了语法错误nodemon成功重启服务。通过nodemon我减少了两次手动重启服务的繁琐操作。
<img src="https://static001.geekbang.org/resource/image/84/d6/847e9f5f2e846f69d3b1777b3f61a6d6.gif" alt="">
类似的工具SpringBoot有Spring-Boot-Devtools你可以点击这个[链接](https://www.baeldung.com/spring-boot-devtools)查看详细描述。针对Python可以直接使用nodemon这里也有一个[链接](https://stackoverflow.com/questions/49355010/how-do-i-watch-python-source-code-files-and-restart-when-i-save)供你参考。
如果你使用的框架、语言没有直接可以使用的工具,帮助你进行实时重启服务的话,可以使用类似[watchdog/watchmedo](https://github.com/gorakhargosh/watchdog)的工具来实现自动化。比如
```
watchmedo shell-command \
--patterns=&quot;*.py&quot; \
--command='python &quot;${watch_src_path}&quot;' \
.
```
会监控所有的Python文件改动并自动重启。
提供快速反馈,边开发边验证,虽然只是一个简单的原则,但可以带来很多好处。最直接的收益就是,能够大大提高开发者对当前代码的信心,从而促进代码尽早入仓、尽早集成。
可能你也注意到了,**代码集成越晚发现问题就越晚。这正是产品上线的最后关头合并混乱,产品质量差、返工率高的一个重要原因**。所以,我建议在你的工作流程中,要尽量提高实时验证的能力。如果你这么做了,很快就会看到效果。
## 小结
在今天这篇文章中,我和你分享了两条持续开发的基本原则,来帮助开发者在代码入库前聚焦于开发工作:一是,规范化、自动化代码入库前的核心步骤;二是,提供快速反馈,帮助开发者边开发边验证,以促进增量开发。
我将今天的内容,总结为了一幅图,帮助你复习。
<img src="https://static001.geekbang.org/resource/image/50/98/50c1bdf8074f68b7707b26a1b657df98.png" alt="">
这些原则和实践,是我根据自己的经验总结出来的。如果能直接适用于你的团队当然最好了,但我更加希望的是,你能从这些原则和实践的讨论中,理解它们背后的思路,从而找到合适的方法和实践,去优化代码入库前的流程中最需要优化的地方,让开发者能够真正聚焦于开发。
另外我给你一个落地持续开发的小贴士持续开发很适合用自上而下和自下而上相结合的方式来推动。因为开发者最了解自己工作的痛点所以也能比较准确地找到需要优化的地方。在Facebook很多工具和流程都是由开发者自发开发或者引入后来逐步推广至团队和公司使用的。
所以我推荐,**作为开发者,你可以自己抽一点时间优化自己的工作流程,自动化繁琐的工作;而作为管理者,你可以有意识地奖励这样的优化行为,并对适用于团队的部分进行推广。**
## 思考题
最后,我来给你留下两个思考题吧。
1. 在开发环境方面你有没有尝试过在Docker里面进行开发你觉得这种方式的好处是什么弊端又是什么呢
1. 有些开发者喜欢写好一个比较大的功能单元,然后再一口气调测。你觉得这样做的好处和坏处,各是什么呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,151 @@
<audio id="audio" title="06 | 代码入库到产品上线Facebook如何使用CI/CD满足业务要求" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/4d/6a/4d3c3fecf204dd029db01d0a3b38d36a.mp3"></audio>
你好,我是葛俊。
在上一篇文章中我和你分享了代码入库前的流程优化即持续开发。今天我会继续与你介绍流程优化中代码入库和入库后的3种持续工程方法即持续集成Continuous Integration, CI)、持续交付Continuous Delivery, CD和持续部署Continuous Deployment, CD)。
在接下来的分享中首先我会与你介绍这3种方法的定义和作用帮助你深入理解这3个“持续”背后的原理和原则然后我会以Facebook为参考给你介绍基于这些原则的具体工程实践。我希望这些内容能够帮助你用好这三个“持续”方法提高团队的研发效能。
首先,我先来介绍一下,持续集成、持续交付和持续部署是用来解决什么问题的,也就是它们的定义和作用。
## 3个“持续”的定义和作用
不知道你是否还记得,在开篇词中,我提到过一个低效能的情况,即产品发布上线时出现大量提交、合并,导致最后时刻出现很多问题。这个情况很常见,引起了很多用户的共鸣。产生这个问题最主要原因是,**代码合并太晚**。这里,我再与你详细解释一下原因。
当多个人同时开发一款产品的时候,很可能会出现代码冲突。而解决冲突,需要花费较多的时间;同时很可能出现冲突解决失败,导致产品问题。
如果没有一个机制督促我们尽早把代码推到主仓进行集成的话,我们通常会尽量先在自己的分支上进行开发。结果往往是,在冲刺快要结束,或者功能即将发布时,才出现大量的代码合并。
而这时因为很长时间没有进行过代码集成了,进行集成的代码量通常比较大,不同开发者的代码区别很大,冲突也很严重,难以解决。具体的负面影响是:发布推迟,产品质量不高,每一次发布时的熬夜和紧张影响团队士气。
而持续集成的根本出发点,就是为了解决这个问题。也就是说,它能够帮助开发人员尽量早、尽量频繁地把自己的改动推送到共享的代码仓库分支上,进行代码集成,从而减少大量代码冲突造成的低效能问题。
所以,**持续集成的定义就是:在团队协作中,一天内多次将所有开发人员的代码合并入同一条主干**。
代码入库后,剩下工作是把代码编译打包成可以发布的形式,先发布到测试环境进行测试,再发布到类生产环境进行测试,最终部署到生产环境。
在这个过程中,有两个问题需要特别注意。
<li>
<p>首先,我们需要这个流程尽量频繁。如果我们能够把产品和功能尽快地发布到市场上,就能够更快地服务客户,以更快的试错速度寻找到用户,提供真正对客户有价值的功能。<br>
即使你的产品由于自身特性不会太频繁地部署给用户,但这种能够频繁生产出可以马上部署的产品的能力,也能让你在需要部署时,快速完成任务。</p>
</li>
<li>
其次,在产品发布到不同环境的过程中,我们会发现一些在开发和持续集成中没有暴露的问题。如果在产品要正式发布时才发现这些问题,就会造成产品交付推迟,影响线上用户等情况,造成损失。针对这个情况,我们需要提前发现问题。
</li>
而解决这两个问题,正是持续交付和持续部署的出发点。
持续交付的目标是,对每一个进入主干分支的代码提交,构建打包成为可以发布的产品。它的定义是:**一种软件工程方法,在短周期内完成软件产品,以保证软件保持在随时可以发布的状态**。也就是说,对每一个提交,把集成后的代码部署到“类生产环境”中进行验证。如果代码没有问题,后续可以手动部署到生产环境中。
而持续部署,则更进一步。它把持续交付产生的产品立即自动部署给用户,定义就是:**将每一个代码提交,都构建出产品直接部署给用户使用。**
以上就是持续集成、持续交付与持续部署的作用和定义。在实现上,它们**共同的本质**是,让每一个变更都经过一条自动化的检验流水线,来检查每一个变更的质量,通过就进入下一个阶段。
这里的“下一个阶段”具体包括:代码并入主仓、产品进入测试环境、产品进入类生产环境、产品最终进入生产环境,如下图所示。
<img src="https://static001.geekbang.org/resource/image/97/09/97645a87eb8a83ef8d9ce59902b31109.jpg" alt="">
你应该已经注意到了,整条流水线中,持续部署只是持续交付的最后一步,也就是自动化上线的那一步,前面的各种检查,都属于持续交付流水线。所以,**我在后面的内容中再提到流水线时CI/CD指的就是“持续集成/持续交付”。**
CI/CD流水线能够大大提高代码入库的速度和质量是这几年硅谷互联网公司做到高效研发的必要条件。接下来我就与你介绍CI/CD流水线的具体原则以及最佳实践然后以Facebook的具体实践为例帮助你加深理解。
需要注意的是在这篇文章中我会重点与你分享CI/CD流水线的搭建原则。而关于具体的搭建方式通常是持续集成工具+代码仓管理系统+检查工具+测试工具比如Jenkins+GitLab+SonarQube+Linter+UnitTest的组合。你可以参考这个[链接](https://www.jianshu.com/p/e111eb15da90)提供的方式去搭建。
## CI/CD流水线的具体原则以及最佳实践
根据上面提到的3个“持续”的本质要做到高效有3条基本原则
- 流水线的测试要尽量完整;
- 流水线的运行速度一定要快;
- 流水线使用的环境,尽量和生产环境一致。
### 基本原则1流水线的测试要尽量完整
CI/CD流水线的测试只有尽量完整代码和产品的质量才能有保证。所以最主要的工程实践就是在流水线中运行大量高质量的测试和检查。
Facebook就有大量的单元测试和集成测试用例、安全扫描以及性能专项测试用例。如果某个验证在流水线中失败开发人员会考虑是否要添加测试用例来防止再出现类似的问题。
另外Facebook持续在测试用例的开发上投入。在内部工具团队有一个专门的测试工具团队来建设测试框架和测试流程方便开发人员自己开发测试用例。比如我在Facebook那几年他们就一直在改进JavaScript的Mock框架对开发人员写测试用例来说非常方便。
### 基本原则2流水线的运行速度一定要快
因为每一个变更都要通过CI/CD流水线的检验所以流水线的速度关乎研发速度。而要提高这条流水线的速度我们可以从以下两个方面考虑。
首先,从技术角度考虑。比如:
- 使用并行方式运行各种测试来提速;
- 投入硬件资源,使用水平扩展的方式来提速;
- 使用增量测试的方式进行精准验证。也就是说,只运行跟当前改动最相关的测试,以减少测试用例的运行数量。
其次,权衡流水线的运行速度、流水线资源和测试完整性的关系。不难理解,运行速度快、占用资源少、测试完整难以兼顾,因此我们必须做出权衡。这里我推荐几个方法:
- 如果通过增加硬件资源来提升运行速度需要的成本太高的话,可以对测试用例按优先级进行分类,每天运行流水线的时候,不用每次都运行所有测试用例,只选择其中几次进行全量测试。
- 提供支持,让开发人员在本地也能运行这些测试,从而使用本地资源尽早发现问题,这就避免了一些有问题的提交占用流水线的资源,进而提高整条流水线的运行速度。
- 运行测试的时候,按照一定的顺序运行测试用例。比如可以先运行速度快的用例,以及历史上容易发现问题的用例。这样可以尽早发现问题,避免耗费不必要的资源。
### 基本原则3流水线使用的环境尽量和生产环境一致
这里的环境,包括机器环境、数据、软件包、网络环境等。环境不一致可能导致问题暴露在用户面前,损失严重;另外,在非生产环境上难以复现生产环境的问题,调试困难。
保证流水线环境与生产环境一致,具体方法包括:
- 软件包最好只构建一次,保证各种不同环境都用同一个包。如果不同的运行环境需要不同的参数,可以采用环境变量的方式把这些参数传递给软件包。
- 使用Docker镜像的方式把发布的产品以及环境都打包进去实现环境的一致性。在我看来这正是Docker的最大好处。
- 尽量使用干净的环境。比如,测试时,使用刚从镜像产生的系统;又比如,使用蓝绿部署,每次产生新的部署时,直接丢弃旧的环境。
以上就是CI/CD流水线的3个基本原则和最佳实践。通过提高验证的完整性、速度以及保证环境的一致性我们可以降低成本提高产品质量和验证产品价值假设的速度。
接下来为了帮助你理解并正确运用这些原则和最佳实践我们一起来看看Facebook是怎么做的。
## 具体案例Facebook是如何实施CI/CD来提高效能的
Facebook一直就非常注重CI/CD早在2009年就建设了顺畅的CI/CD流水线而且一直在持续改进。
**在CI方面**加强建设持续开发让开发人员能在开发环境上进行大量的验证。本地的所有验证与CI流水线上的验证方式保持一致这就大大提高了开发人员在本地发现问题的能力从而大量避免了有问题的代码提交到CI流水线浪费资源。
在**代码入库的步骤**采用Phabricator作为CI的驱动并作为质量检查中枢尽量提高入库前代码审查的流畅性。在这个过程中Facebook做到了以下几点
- 测试的完整性。代码提交到Phabricator进行代码审查的同时进行各种静态检查、单元测试、集成测试、安全测试以及性能测试等。
- 工具的集成。Phabricator提供的插件机制可以跟其他系统和工具集成以支持运行各种检查。
- 沙盒环境。代码在提交到Phabricator进行审查时Phabricator会自动产生一个沙盒环境。沙盒环境有两个好处一是可以让开发者之间进行联调二是可以让开发者并行地进行其他开发工作因为在进行代码审查时开发者的开发机器并没有被占用。
- 高效的代码审查。比如代码审查不通过时代码作者可以方便地在原来的提交之上进行修改并在下一轮审查时只进行增量代码的审查。这就大大降低了每次代码审查的交易成本从而保证了CI的顺畅性。
**代码入库之后,进入持续交付步骤**。Facebook使用大仓同一个仓中每天有几千个代码提交所以持续交付的挑战很大。他们有一个专门的发布工具团队自研了一套发布工具来实现自动化流水线通过以下两点比较好地实现了流水线资源和测试完整性的平衡。
<li>
<p>不针对每一个提交进行CD验证而是按照一定时间间隔进行验证。因为提交太多如果每个提交都进行构建打包资源消耗实在太大所以Facebook采用了按照一定时间间隔比如每10分钟进行一次构建打包。这就大大降低了资源的消耗不过这里有个问题在验证步骤发现Bug时因为验证的是最近10分钟的所有提交所以不能精准定位造成问题的提交。<br>
针对这个问题Facebook使用单主干开发分支方式并强制在代码合并时只能使用git rebase不能产生合并提交所以提交历史是线性的从而可以使用git bisect命令来自动化定位问题。这部分内容我会在下一篇文章中详细介绍。</p>
</li>
<li>
对验证进行分级。也就是说有几条不同的CD流水线按照不同的时间间隔运行构建和检验。根据运行时间间隔的不同它们运行的检验数量以及检查出来的Bug优先级也不同。间隔时间越长运行的检验越全面检查出来的Bug优先级越高。
</li>
这里需要说明的是2017年以前Facebook并没有把每一个在主干分支上成功通过流水线验证的软件包作为发布候选而是在每周五的固定时间从主干分支上拉出一个发布分支稳定3天后上线。也就是说这并不是严格意义上的持续交付。这是因为当时的自动化检验还不能确保产品达到上线要求。其实这对很多公司来说都很常见都需要一些额外的测试和检验来确保上线产品的质量。
最后,是**持续部署**的操作。在2017年以前Facebook并没有持续部署而是采用的每周全量代码部署的方式。但到2017年因为代码提交实在太多每次周部署代码处理的提交量会超过10000需要很长时间才能稳定发布分支所以Facebook转向了持续部署。
具体的方法是极致地进行自动化测试验证。关于实施细节你可以参考Facebook的第一个发布工程师Chuck Rossi对[持续部署流程的描述](https://code.fb.com/developer-tools/rapid-release-at-massive-scale/)。
值得一提的是跟持续交付一样Facebook的持续部署也不是纯粹的持续部署。因为代码提交太多他们并没有每个提交都单独部署而是采用类似持续交付的方法把一段时间之内的提交一起部署。这种**不教条的方式是我从Facebook学到的一个重要的做事方法。**
## 小结
Facebook在CI/CD上做到了极致对每一个代码提交都高效地运行大量的测试、验证并采用测试分层、定时运行等方式尽量降低资源消耗。正因为如此他们能够让几千名开发人员共同使用一个大代码仓并使用主干开发产生高质量的产品从而实现了超大研发团队协同下的高效能。
在前面几篇文章中我们多次提到“持续”。这个词近些年在软件研发中比较流行比如我今天与你分享的持续集成、持续交付、持续部署加上持续开发一共有4个了。
实际上在CI/CD流水线中**做为流水线的一部分,测试一直在运行并最快地给开发者提供反馈**。这正是另一个“持续”,也就是“持续测试”的定义。
“持续”如此重要的原因是软件开发是一个流程只有让这个流程持续运转才能高效。这里我把这5个持续都列举出来方便你复习、参考。
<img src="https://static001.geekbang.org/resource/image/7d/06/7df32f45bdf6890cfc1198184b2f3b06.jpg" alt="">
## 思考题
1. 在几千名开发人员共同使用一个大代码仓的工作方式下做好CI有很大的挑战性。你觉得挑战在哪里容易出现什么样的问题又应该怎么解决呢
1. 今天我提到了持续开发在CI中的作用请你结合上一篇文章思考一下持续开发和CI/CD是怎样互相促进的。
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,280 @@
<audio id="audio" title="07 | 分支管理Facebook的策略适合我的团队吗" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/05/22/056d1bd61acb5a9fccf42c7469727622.mp3"></audio>
你好我是葛俊。今天我来跟你聊聊研发过程中的Git代码分支管理和发布策略。
在前面两篇文章中,我们讨论了持续开发、持续集成和持续部署的整个上线流程。这条流水线针对的是分支,因此代码的分支管理便是基础。能否找到适合自己团队的分支管理策略,就是决定代码质量,以及发布顺畅的一个重要因素。
Facebook有几千名开发人员同时工作在一个大代码仓每天会有一两千个代码提交入仓 但仍能顺利地进行开发并发布高质量的产品。平心而论Facebook的工程水平的确很高与他们的分支管理息息相关。
所以在今天这篇文章中我会先与你详细介绍Facebook的分支管理策略以及背后的原因然后与你介绍其他的常见分支管理策略最后向你推荐如何选择适合自己的分支策略。
## Facebook的分支管理和发布策略
Facebook的分支管理策略是一种基于主干的开发方式也叫作Trunk-based。在这种方式中用于开发的长期分支只有一个而用于发布的分支可以有多个。
首先,我们先看看这个长期存在的开发分支。
### 开发分支
这个长期存在的开发分支一般被叫作trunk或者master。为方便讨论我们统一称它为master。也就是说所有的开发人员基于master分支进行开发提交也直接push到这个分支上。
在主干开发方式下根据是否允许存在短期的功能分支Feature Branch)又分为两个子类别主干开发有功能分支和主干开发无功能分支。Facebook做得比较纯粹在主代码仓中基本上禁止功能分支。
另外在代码合并回master的时候又有rebase和merge两种选择。Facebook选择的是rebase关于这样选择的原因我会在后面与你详细介绍。所以Facebook的整个开发模式非常简单步骤大概如下。
第一步,获取最新代码。
```
git checkout master
git fetch
git rebase origin/master
```
第二步,本地开发,然后执行
```
git add
git commit
```
产生本地提交。
第三步推送到主代码仓的master分支。
```
git fetch
git rebase origin/master
git push
```
在rebase的时候如果有冲突就先解决冲突然后使用
```
git add
git rebase --continue
```
更新自己的提交最后重复步骤3也就是重新尝试推送代码到主代码仓。
看到这里,你可能对这种简单的分支方式有以下两个问题。
**问题1**:如果功能比较大,一个代码提交不合适,怎么办?
解决办法这种情况下第二步本地开发的时候可以产生多个提交最后在第三步一次性推送到主仓的master分支。
**问题2**:如果需要多人协同一个较大的功能,怎么办?
解决办法这种情况下Facebook采用的是使用代码原子性、功能开关、API版本等方法让开发人员把功能拆小尽快合并到master分支。
比如一个后端开发者和一个前端开发者合作一个功能他们的互动涉及10个API接口其中两个是在已有接口上做改动另外8个是新增接口。
这两名开发者的合作方式是:
- 第一后端开发者把这10个接口的编码工作以至少10个单独的提交来完成。强调“至少”是因为有些接口的编码工作可能比较大需要不止一个提交来完成。
- 第二对已有API的改动如果只涉及增加API参数情况就比较简单只需要在现有API上进行。但如果牵涉到删除或者修改API参数就要给这个API添加一个新版本避免被旧版本阻塞入库。
- 第三,在实现功能的过程中,如果某个功能暂时还不能暴露给用户,就用功能开关把它关闭。
这就保证了在不使用功能分支的情况下这两个开发者可以直接在master分支上合作并能够不被阻塞地尽快提交代码。当然了这种合作方式可以扩展到更多的开发者。
以上就是开发分支的情况。接下来,我再与你讲述发布分支和策略。
### 发布分支
基于主干开发模式中在需要发布的时候会从master拉出一条发布分支进行测试、稳定。在发布分支发现问题后先在master上修复然后cherry-pick到发布分支上。分支上线之后如果需要长期存在比如产品线性质的产品就保留。如果不需要长期存在比如SaaS产品就直接删除。Facebook采用的方式是后者。
具体来说部署包括3种有每周一次的全量代码部署、每天两次的日部署以及每天不定次数的热修复部署。日部署和热修复部署类似我们下面详细介绍周部署和热修复部署。
每次**周部署**代码的时候,流程如下所示。
第一步从master上拉出一个发布分支。
```
git checkout -b release-date-* origin/master
```
第二步,在发布分支进行各种验证。
第三步如果验证发现问题开发者提交代码到master然后用cherry-pick命令把修复合并到发布分支上
```
git cherry-pick &lt;fix-sha1&gt; # fix-sha1 是修复提交的commit ID
```
接着继续回到第二步验证。
验证通过就发布当前分支。这个发布分支就成为当前生产线上运行版本对应的分支,我们称之为当前生产分支,同时将上一次发布时使用的生产分支存档或者删除。
在进行**热修复部署**时,从当前生产分支中拉出一个热修复分支,进行验证和修复。具体步骤为:
第一步,拉出一个热修复分支。
```
git checkout -b hotfix-date-* release-date-*
```
第二步开发人员提交热修复到master然后cherry-pick修复提交到热修复分支上。
```
git cherry-pick &lt;fix-sha1&gt;
```
第三步,进行各种验证。
第四步,验证中发现问题,回到第二步重新修复验证。验证通过就发布当前热修复分支,同时将这个热修复分支设置为当前的生产分支,后面如果有新的热修复,就从这个分支拉取。
这里有一张图片,描述了每周五拉取周部署分支,以及从周部署分支上拉取分支进行热修复部署的流程。
<img src="https://static001.geekbang.org/resource/image/0c/58/0c79327f4a163fd800984346a7e76258.jpg" alt="">
以上就是Facebook的代码分支管理和部署流程。
**需要注意的是**这里描述的部署流程是Facebook转到持续部署之前采用的。但考虑到非常多的公司还没有达到持续部署的成熟度所以这种持续交付的方式对我们更有参考价值。
## Facebook分支管理策略的背后原因
Facebook采用主干分支模式最大的好处是可以把持续集成、持续交付做到极致从而尽量提高master分支的代码质量。
解释这一好处之前我想请你先看看下面这3个措施有什么共同效果
- 几千名开发者同时工作在同一条主干;
- 不使用功能分支直接在master上开发
- 必须要使用rebase才能入库不能使用merge。
其实它们的共同效果就是必须尽早将代码合入master分支否则就需要花费相当长的时间去解决合并冲突。所以每个开发人员都会尽量把代码进行原子性拆分写好一部分就赶快合并入库。
我曾经有过一个有趣的经历。一天下午我和旁边的同事在改动同一个API接口实现两个不同的功能。我们关系很好也都清楚对方在做什么于是一边开玩笑一边像在比赛一样看谁先写好代码完成自测入主库。结果是我赢了他后来花了十分钟很小心地去解决冲突。
Facebook使用主干分支模式的好处主要可以总结为以下两点
- 能够**促进开发人员把代码频繁入主仓进行集成检验**。而这正是持续集成的精髓。与之相对应的是很多20名开发者的小团队采用的也是共主干开发方式但使用了功能分支让大家在功能分支上进行开发之后再merge回主仓。结果是大家常常拖到产品上线前才把功能分支合并回主干导致最后关头出现大量问题。
- 能够**确保线性的代码提交历史**给流程自动化提供最大方便。不要小看“线性”它对自动化定位问题意义非凡使得我们可以从当前有问题的提交回溯找到历史上第一个有问题的提交。更棒的是我们还可以使用折半查找也叫作二分查找的办法用O(LogN)的时间找到那个有问题的提交。
比如在一个代码仓中有C000 ~ C120 的线性提交历史。我们知道一个测试在提交C100处都是通过的但是在C120出了问题。我们可以依次checkout C101、C102直到C120每次checkout之后运行测试总能找到第一个让测试失败的提交。
或者更进一步我们可以先尝试C100和C120中间的提交C110。如果测试在那里通过了证明问题提交在C111和C120之间继续检查C115否则就证明问题提交在C101和C110之间继续检查C105。这就大大减少了检查次数。而这正是软件算法中经典的折半查找。
事实上Git本身就提供了一个命令git bisect支持折半查找。比如在刚才的例子中如果运行测试的命令行是 runtest.sh。那么我们可以使用下面的命令来自动化这个定位流程
```
&gt; git checkout master # 使用最新提交的代码
&gt; git bisect start
&gt; git bisect bad HEAD # 告知 git bisect当前commit是有问题的提交
&gt; git bisect good C100 # 告知 git bisectC100是没有问题的提交
&gt; git bisect run bash runtest.sh # 开始运行自动化折半查找
...
Cxxx is the first bad commit # 查找到第一个问题提交
...
bisect run success
&gt; git bisect reset # 结束git bisect。回到初始的HEAD
```
很方便吧。而如果历史不是线性的也就是说如果提交使用了merge那么我们就不能方便地定位出第一个问题提交了更别说是折半查找了。
这种快速定位问题的能力可以给CI/CD带来巨大好处。在持续交付过程中我们常常没有足够的资源对每一个提交都进行检查。比如前面提过Facebook的持续交付流水线就是每隔一段时间对代码仓最后一个提交运行流水线的检查。如果发现问题就可以通过上面这种方法自动化地找到问题提交并自动产生Bug工单分配给提交者。
## 其他主要分支方式
除了主干开发的分支管理策略还有3种常用方式
- [Git-flow](https://nvie.com/posts/a-successful-git-branching-model/)工作流;
- [Fork-merge](https://github.com/oldratlee/translations/blob/master/git-workflows-and-tutorials/workflow-forking.md) 工作流;
- 灵活的[功能分支组合成发布分支](https://help.aliyun.com/document_detail/59315.html?spm=a2c4g.11186623.2.14.1fe1341epVSgj0)。
我在文中给出了链接供你参考。接下来,我们具体看看这几种方式。
### Git-flow 工作流
Git-flow工作流有两个长期分支一个是master包含可以部署到生产环境的代码另一个是develop是一个用来集成代码的分支开发新功能、新发布都从develop里拉分支。此外它还有3种短期分支分别是新功能分支、发布分支、热修复分支根据需要创建当完成了自己的任务后就会被删除。
Git-flow工作流的特点是规定很清晰对各种开发任务都有明确的规定和步骤。比如
- 开发新功能时从develop分支拉出一个前缀为feature-的新功能分支在本地开发并推送到远端中心仓库完成之后合并入develop分支并删除该功能分支。
- 发布新版本时从develop分支拉出一个前缀为release-的发布分支部署到测试、类生产等环境进行验证。发现问题后直接在发布分支上修复测试通过之后把release分支合并到master和develop分支。在master分支上打tag并删除该发布分支。
这种工作流,在前几年非常流行。它的好处是流程清晰,但缺点是:
- 流程复杂,学习成本高。
- 容易出错,容易出现忘记合并到某个分支的情况。不过可以使用脚本自动化来解决。
- 不方便进行持续集成。
- 有太多的代码分支合并,解决冲突成本比较高。
### Fork-merge
Fork-merge是在GitHub、GitLab流行之后产生的具体做法是每个开发人员在代码仓服务器上有一个“个人”代码仓。这个“个人”代码仓实际上就是主代码仓的一个clone。开发者对主代码仓贡献代码的步骤如下
1. 开发者产生一个主代码仓的fork成为自己的个人代码仓
1. 开发者在本地clone个人代码仓
1. 开发者在本地开发,并把代码推送到自己的个人代码仓;
1. 开发者通过web界面向主代码仓作者提出Pull request
1. 主代码仓的管理者在自己的开发机器上,取得开发者的提交,验证通过之后再推送到主代码仓。
看起来步骤繁琐但实际上和主干开发方式很相似也有一个长期的开发分支就是主仓的master分支。不同之处在于它提供了一种对主分支更严格、更方便的权限管理方式即只有主仓管理者有权限推送代码。同时主仓不需要有功能分支功能分支可以存在fork仓中。所以主仓干净便于管理。
这种方式对开源项目比较方便,但缺点是步骤繁琐,不太适用于公司内部。
### 灵活的功能分支组合成发布分支
除了上述方式之外,还有一种非常灵活,但对工具自动化要求很高的分支方式,即基于功能分支灵活产生发布分支的方式。这种方式的典型代表是阿里云效的“分支模式”。
具体方法是大量使用工具对分支的管理进行自动化开发人员在web界面上自助产生针对功能的分支。编码完成后通过web界面对分支组合、验证并上线上线之后分支再自动合入主库。
这种方式的好处是:
- 方便基于功能进行开发。也就是说,开发者可以针对每个功能产生一个分支进行开发。
- 灵活,也就是能够方便地对功能进行组合,发布到对应环境上测试。出了问题,可以方便地添加或者删除功能。
但这种方式的问题是,对工具的依赖比较高,没有一个系统的工具来自动化的话,基本做不起来。另外,这种方式会大量封装底层的实现,使开发人员不知道底层发生的问题,一旦出现问题就不太容易解决。
## 哪一种分支管理策略更适合我的团队呢?
要找到适合自己团队的分支管理策略,我们先来对比下上面提到的几种方式的优缺点吧。
<img src="https://static001.geekbang.org/resource/image/cc/2e/ccdcf41da5c0f016e90cb7d38f178e2e.jpg" alt="">
另外要找到合适的代码分支管理策略你还可以参考以下3个问题根据答案帮助你进行选择。
问题1如果提供功能分支让成员共享在哪里建立这个分支
>
如果团队不大可以允许在主仓创建功能分支不过注意定时删除不用的分支避免影响Git的性能。如果团队比较大可以考虑使用Fork-merge方式在上面提到的“个人代码仓”里创建功能分支从而避免污染主仓。
问题2要不要使用Merge Commit?
>
代码在合并到主干的时候可以选择rebase或者merge。使用rebase的好处是上边提到的方便定位问题。而使用merge的好处是可以清晰地在分支里看到一个功能的所有提交不像在rebase中一个功能的提交往往是分散的。
问题3团队成熟度如何?
>
单分支开发集成早,质量比较好,但对团队成员和流程自动化要求高。所以,如果你的团队比较小,或者比较成熟的话,可以考虑使用单分支,否则可以选择多分支开发模式,但要想办法把集成提前,同时逐步向单主干分支过渡。
**总结来说尽量减少长期分支的数量代码尽早合并回主仓方便使用CI/CD等方法保证主仓代码提交的质量是选择分支策略的基本出发点。**
## 小结
首先我分享了Facebook使用的单主干开发分支以及通过临时发布分支进行部署的分支管理策略和部署方式。然后我与你介绍了几种常见的分支管理策略并给出了推荐的选择方法。
在Facebook工作时我们一直使用这种主干分支开发方式。它强迫我们把代码进行原子化尽量确保每一个提交都尽快合入master并保证代码质量。一开始我不是很习惯但习惯后我发现它的确很棒。
首先因为你和你的合作开发者都需要尽快把代码拆小、入仓这就帮助我们提高了功能模块化的能力。其次因为master里面的提交一般都比较健康并且是比较新的代码所以很少会被不稳定的因素阻塞。最后线性提交历史对开发者的日常工作也很有帮助。我们在开发的时候常常会碰到一个本来工作得好好的API在拉取到最新代码之后出现了问题。这时我就可以使用这种方法找到第一个造成问题的提交从而方便定位和解决问题。
一个流程设计、实施得好,对产品来说可以提高质量,对团队来说可以提高效能,对个人来说可以帮助成长。这就是一举三得。
## 思考题
1. 产品线性质的产品开发以及SaaS产品开发在选择分支管理策略时有不同的考量。你觉得哪种分支管理方式更适合二者呢
1. 你知道trunk-based里面“trunk”的意思吗
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,165 @@
<audio id="audio" title="08 | DevOps、SRE的共性应用全栈思路打通开发和运维" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/2b/52/2bd936d773148d4642b06af77a487c52.mp3"></audio>
你好我是葛俊。今天我来跟你聊一聊DevOps和SRE。
DevOps和SRE尤其是DevOps是最近几年软件研发方法的重要趋势。因为它们都跟打通开发和运维流程相关所以常常被混淆。比如SRE等同于Google的DevOps、SRE是升级版的DevOps就是两个常见的误区。
事实上DevOps和SRE虽然关系紧密但差别还是蛮大的。所以今天我首先会通过DevOps和SRE的对比引出它们背后的Why、How和What也就是它们的目标、原则和具体实践。然后我会结合自己在一个创业公司的经验向你推荐如何在团队落地DevOps。
## DevOps和SRE的定义和异同
因为DevOps和SRE都是比较新的概念而且在不断地发展变化所以学术界和工业界对它们的定义并未达成一致。
接下来我参考已有定义并加入自己的理解对DevOps和SRE的大致定义如下
**DevOps**Development和Operations的组合词是一种重视“软件开发人员Dev”和“IT运维技术人员Ops”之间沟通合作的文化、活动或惯例。 通过自动化“软件交付”和“架构变更”的流程,使得软件的构建、测试、发布更加快捷、频繁和可靠。
**SRE**全称是Site Reliability Engineer ,网站可靠性工程师,是一个职位,是软件工程师和系统管理员的结合,主要目标是创建可扩展且高度可靠的软件系统。
为达到这个目标SRE需要掌握如下相关知识算法、数据结构、编程、网络编程、分布式系统、可扩展架构、故障排除等。SRE使用工具和系统支撑其完成工作比如自动化发布系统、监控系统、日志系统、服务器资源分配和编排工具等而这些工具往往需要他们自己开发和维护。
所以总结来说,**DevOps是打通开发和运维的文化和惯例而SRE是DevOps的具体实践之一**。说到相同点它们都是为了打通Dev和Ops提高研发效能说到区别DevOps是文化而SRE是职位。如果要类比的话**DevOps与SRE的关系就像敏捷跟Scrum的关系。**
理解了DevOps和SRE的定义和异同后我们再来看看它们的目标、原则和具体实践吧。
## DevOps和SRE的Why、How、What
不知道你有没有考虑过,传统定义中,开发人员和运维人员的利益其实是冲突的。
开发人员的职责是开发功能。功能上线越多,对团队和公司的贡献就越大。所以,开发人员倾向于多开发功能,并快速上线。而运维人员的主要职责是,上线功能,以及保证系统的稳定性,更关注系统的稳定性。新功能上线越多,工作量就越大,服务越容易不稳定,所以运维人员不愿意功能多上线、快速上线。
另外,职能竖井越严重,这些问题就越严重。因为在这种情况下,开发人员写完代码就扔给运维人员撒手不管了,运维人员也很少能从开发人员手里拿到有效信息。此时,线上问题的修复对运维人员来说挑战很大。于是,他们会更加谨慎。
**这就是DevOps和SRE最初要解决的问题开发和运维这两个角色的目标不一致导致研发和上线流程的不顺畅最终严重影响软件上线的效率。**比如运维团队倾向设置多而严格的上线检查门禁限制上线频率。而开发人员则很可能把一些功能“伪装”成Bug修复来绕过针对版本发布的严格检查。
那怎么解决这个问题呢?接下来,我向你推荐以下几个原则和具体方法。
### 原则1协调运维和开发人员的目标、利益
目标、利益不一致是导致开发团队和运维团队矛盾的首要问题所以想办法让它们的目标、利益变得一致是整个DevOps中最重要的一条。因为只有协调好了目标和利益它们才有动力去解决问题。实现这个原则的一个最主要的方法我称之为“**全栈开发**”。
什么意思呢?全栈开发,就是每一个工程人员的工作涵盖了不止一个领域。虽然他专攻某一个领域,但对负责的领域都有责任。说白了,就是对产品结果负责,而不是只对某一个具体环节负责。
具体的实施方法,主要包括两大方面。
**第一,增加一个新的运维角色,用开发的方式去做运维。**不同于传统运维人员主要关注网站和服务的稳定和快速,这种新型运维角色负责帮助开发人员推动业务快速开发、快速上线,具体工作包括:优化流程,提供自助工具。
在Facebook这个角色由一个专门的发布工程师团队以及一个生产工程师Production Engineering PE团队承担。而在Google这个角色就是SRE。
PE和SRE职责比较类似都是负责日常运维、紧急响应、工具研发、建设平台化服务体系、容量规划和容量管理、Oncall等。他们会被指派到具体的产品团队中深入到开发第一线拿出比较多的时间Google SRE规定至少50%进行编程工作针对性地自动化和优化CI/CD中的流程、工具等。这里请注意他们的开发工作不是业务开发而是工具开发和自动化等。
与发布工程师团队相比PE和SRE的最大特点是更多地参与到具体产品和项目中。为了方便讨论**我把PE、SRE和发布工程师团队这种新的运维角色统称为“类SRE”。**类SRE顺利推进的关键是找到高质量的开发、运维多面手也就是有很强的开发能力同时有系统维护、网络问题排查等运维技能的工程师。
需要注意的是除了类SRE传统的运维角色比如网络工程师、系统工程师可能仍然存在他们也使用类SRE提供的DevOps工具链提高工作效率所以人员数量比以前少了很多。在遇到线上问题时开发、类SRE和传统运维需要配合解决。
**第二,修改开发人员的职责描述为,快速开发和上线稳定的高质量产品,让他们也参与到一部分运维工作中去。**
开发人员最主要的工作仍然是开发但会使用类SRE团队提供的工具、流程来进行发布相关的工作包括代码部署、线上问题定位和处理等部分工作。这样他们会在代码开发时就注意提高服务的稳定性和代码质量。
比如在Facebook进行日部署的时候开发人员写好了修复的代码提交还需要去找到提交所依赖的、还没有上线的其他提交跟这些提交的作者进行确认没有问题后才可以一起上线。
上线时,这个开发人员对这一组提交负责,把它们全部提交到日部署流程工具上,并负责功能验证。而负责部署的发布工程师,则会使用工具自动化地把这些提交部署到日部署环境中,并进行系统验证。可以看到,开发人员较多地参与了整个部署过程,这正是持续开发的体现。
另一个比较直观地体现开发人员参与运维工作的实践是让开发人员Oncall也就是身上别个BP机线上出现问题要马上响应即使半夜三更也要爬起来让功能的开发人员承担起解决一线问题的职责。
通过引入类SRE角色和修改开发者职责描述这两种方法开发人员和部分运维人员的职责都从原来的单一职能扩展到了产品的高效开发上线从根本目标和利益上实现了这两个角色的对齐。
所以说,“全栈开发”方式非常重要,效果也非常好。这,也正是我把“全栈”用在文章标题中的原因。
### 原则2推动高效沟通
之所以强调推动高效沟通,就是为了解决开发和运维因为存在“部门墙”而导致的信息不流通的问题。
具体的实施方法包括:
- 设置聊天室比如IRC用于沟通部署进展和问题。
- 引入ChatOps也就是通过自动化的方式提高部署相关的沟通。比如可以让聊天机器人在ChatOps聊天室中自动发送发布流程的进展也可以向聊天机器人询问服务部署进展等。这些自动化可以节省很多部署人员的时间。
- 把任务、代码提交、发布的关联关系,在工具中呈现出来,并提供比如任务看板、系统监控看板等可视化工具,显示开发、运维重点信息。这样可以大大提高运维人员定位问题的效率。
### 原则3优化从开发到部署的整个上线流程
优化上线流程,主要目的是解决频繁上线工作量大、产品质量不稳定的问题。
这个原则的主要方法是优化代码入库和部署上线流程并最大化地利用工具来自动化流程。最近10年这个原则发展得非常快逐渐涵盖了快速、高质量上线的各方面工作。这些方面的效率也被称作**交付效能,**具体实践包括:基于主干开发、持续集成、持续交付、持续部署、实现系统松耦合等。
前4条实践我们已经在前面的文章中讲解过了而关于系统松耦合我会在“工程方法”模块中与你详细讨论。
理解了DevOps和SRE的目标、方法和具体实践后我们再来看看具体怎么落地吧。
## 落地步骤推荐
在落地DevOps时我经常看到有些团队一上来就去寻找工具比如使用Jenkins来搭建流程。但正如上面所说DevOps的本质是解决开发和运维之间的冲突所以落地时首先要从人出发然后是流程最后才是工具。
所以,我推荐的具体落地步骤是:
1. 对团队目标达成共识,并重新定义职责;
1. 设计CI、CD、快速反馈以及团队沟通等流程
1. 引入工具,实现自动化。
接下来,我就用我在一家创业公司的实践来帮助你理解这些落地步骤。
## 落地实践案例
这个案例是我在一个创业公司落地DevOps的实例。当时公司刚成立不久我的角色是技术总负责人操作的自由度很大。为了提高产品研发的交付效能也就是团队快速发布高质量软件的能力我采用了以下步骤和方法。
### 第一,在团队内部统一认识
研发团队内部统一认识,主要是让团队成员明确我们的目标一致。同时在做绩效考评时,对大家的要求都是产品快速、高质量上线。
我还把“统一认识”这一点扩展到研发以外的部分比如市场、设计团队。在推广时我始终注意以公司利益为出发点而不是按照职能划分来追责获得了CEO和其他团队管理者的支持。最终推广得比较顺利。
### 第二,增加“发布工程师”角色
这个角色的职责相当于Facebook发布工程师团队的职责。因为我在部署、运维方面的经验相对丰富便担起了这个工作投入相当一部分精力去设计、优化产品的开发和上线流程包括持续集成、持续交付、手动部署流程以及问题监控流程和Oncall机制。
其他开发人员则使用这个流程来部署、上线、监控、解决问题,提高交付效能。在这个过程中,我作为发布工程师,主要起到了“使能”的作用,即日常的部署上线由开发人员自己负责,而我只参与解决一部分运维相关的难题。
### 第三,设计问题沟通流程
在解决了“人”的问题之后,我在流程方面,设计了部署沟通流程,以及部署系统常见问题及解决办法收集流程。
部署沟通流程是:建立了专门的聊天室沟通持续集成、持续交付,以及手动部署的进展情况,确保发布上线流程相关信息的顺畅流通。
部署系统常见问题及解决办法收集流程是:在解决完线上问题之后,记录下问题的细节和解决步骤。很简单,就是因为很多问题会重复出现。每个人都可以更改、搜索这些记录,对后续解决问题帮助非常大。所以,我们每次遇到问题,都会先搜索这个知识库,找到答案和线索的概率能达到百分之七八十。
### 第四,用工具来实现流程自动化
完成了有关人和流程的工作之后工具的实现就不那么难了。DevOps相关工具主要包括两大类CI/CD工具链和沟通工具。
我已经在[第5](https://time.geekbang.org/column/article/129857)、[第6](https://time.geekbang.org/column/article/131673)和[第7](https://time.geekbang.org/column/article/132499)篇文章中与你详细讲述了CI/CD流水线的内容而沟通工具的内容将会是下一篇文章的主题。这里我给出当时我们使用的工具链也就是一个小型创业公司落地DevOps具体工具链的示例供你参考。
<img src="https://static001.geekbang.org/resource/image/b4/ee/b4aff96ce3fb133cbd83f0446a33c3ee.jpg" alt="">
通过这一系列针对人、流程、工具的措施整个研发团队实现了全栈开发目标一致、流程顺畅能够聚焦于开发交付效能非常高实现了后端服务每周上线三次热修复上线时间5分钟MTTRMean Time To Recovery大概15分钟线上也很少出问题系统的可用性达到4个9即99.99%)。
同时,对于开发者来说,全栈开发机制也优化了开发体验,提升了技术成长速度。
当然这些实践能够顺利推广与这是一个小的初创公司有关。一方面公司存亡直接关乎个人利益另一方面一切都在建设期引入具体实践的阻力非常小。如果是在大的团队或者是职能已经成型的团队实施的困难会相对较大。这时我建议你从以下3个方面入手
- 在团队普及全栈开发的理念;
- 在你管理范围内推行全栈开发,做出效果;
- 通过实际运行效果获得公司管理层的支持,让他们了解全栈开发模式为提升研发效能带来的好处,从而逐步改变职责定义和流程。
## 小结
今天我给你介绍了DevOps这个很火的话题以及SRE和它的关系。文章中我与你介绍了DevOps的目标、原则和具体实践。这三条原则分别是协调开发和运维的目标、推动高效沟通和”优化从开发到部署的整个上线流程。最后我通过自己的实战经验介绍了用“人、流程、工具”的步骤来落地DevOps。
在我看来,打通开发和运维的“部门墙”,最关键、最根本的就是解决人的问题,也就是第一条原则,”协调开发和运维的目标“,要解决的问题。而全栈开发就是一个非常棒的解决方法。
简单来说,全栈开发就是让工程师不再只是对某一个单一职能负责,而是对最终产品负责。全栈开发是一个很好的抓手,逐步提高全栈开发的程度,大家的目标自然就会对齐,从而主动去提高,那其他方面的提高就容易得多了。
事实上测试工作也是全栈开发的一部分。在Facebook没有专门的功能测试团队而是有一个测试工具团队提供测试平台、流程和工具供开发者使用。以此让开发者更进一步地对整个产品负责。这种开发人员全栈其他职能提供支持的工作方式可以总结为如下所示的图片。
<img src="https://static001.geekbang.org/resource/image/11/d1/1104afa6698f27d7bac65444cf2e7fd1.jpg" alt="">
最后,我再与你分享一点我的体会。
云技术尤其是Docker和Kubernetes的流行使得越来越多的底层运维工作被自动化导致对传统的系统工程师、运维工程师的需求越来越少。所以懂得开发的运维人员会越来越重要同时更关注部署、测试甚至产品的全栈工程师也会越来越受欢迎。
## 思考题
有些人认为“全栈工程师”要求太高,一个人不能掌握那么多领域的知识,不现实。你怎么看呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,145 @@
<audio id="audio" title="09 | 信息流通:让团队高效协同,让产品准确击中目标" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/e0/82/e05fce5bf705d0e17615b177227f5a82.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊团队的信息流通问题。
研发过程中的信息流通指的是各种跟研发相关的信息在工具、团队成员之间的流动。这些信息大到公司战略小到Bug ID是一个团队达成共识、高效工作的重要因素。比如你是否也曾遇到下面这些研发过程中的不顺畅问题呢
- **最终产品背离用户需求**。我曾提到,一个常见的低效能问题是,研发团队生产出来的产品与最初的产品设计差别很大,甚至需要完全返工。
- **前后端沟通不顺畅**。后端修改API 阻塞了前端的工作或者后端实现了新的API前端不知道继续使用旧的API造成浪费。
- **信息孤岛**。在公司内部找信息反而比在互联网上找信息还要难,需要到处找人问,还不一定能问到。
- **信息难以溯源**。团队成员不知道怎么寻找问题的源头,也不知道软件包发布了哪些功能。
- **工作干扰大**。开发工作常被实时聊天工具、电话等打断,刚整理好的思路又得重新梳理。
实际上,这些问题都是信息流通不顺畅造成的。那么今天,我们就来看看如何做到信息流通,从而让开发人员更顺畅地生产出高价值的产品。总结来说,就是要从以下三个方面入手:
- 首先,从“人”入手,建设共享文化,鼓励共享行为,使信息共享与团队成员利益一致,从而让大家愿意共享。
- 然后,在流程和工具方面,针对与研发相关的信息,设计并实现对应代码、文档的共享,以及信息在流水线上的自动化流动。
- 最后,在沟通技巧上下功夫,掌握高效沟通的原则,根据场景选择合适的沟通方式和工具。
接下来,我们分别看看如何实施吧。
## 团队成员愿意共享是有效沟通的前提
人是首要的,不解决人的意愿问题,流程、工具再好,也用不好甚至用不起来。所以,让团队成员愿意共享,是有效沟通的前提。那,怎么才能调动起团队成员共享消息的意愿呢?
**首先,要让团队成员了解信息的重要性**。软件开发本来就需要多人协作,沟通的重要性不言而喻。但,有相当一部分开发者,尤其是初级开发者,因为工作经验欠缺等原因不愿意去和别人沟通,甚至意识不到信息流通的重要性。
所以,作为开发人员,我们要主动去克服不愿意沟通的倾向,比如在对需求有疑问时提醒自己,如果现在花一小时的时间去沟通,很可能会节省自己后面三天的时间;而作为团队管理者,我们更要在团队内强调沟通的重要性。
但,了解信息流通的重要性,只是实现顺畅沟通的基础。更重要的是,我们要**在团队内部建设机制,来鼓励共享的行为,从而形成共享的文化。**
简单来说就是让信息共享和每个成员的利益紧密联系起来。这样做可以帮助我们解决最终产品背离用户需求以及前后端沟通不畅导致返工等低效能问题。这一类问题的共同点在于负责整个产品或者API的成员没有紧密合作。在按职能划分的团队尤其容易出现。因为职能部门的成员在职能线上向上汇报最关注的往往是职能部分的完成情况而不是整个产品的进展。
一个比较有效的解决办法是,**按照产品或者功能划分团队,让团队成员直接对产品或者功能负责**Facebook和Spotify等很多高效能公司使用的就是这种方法。
一个团队负责一个产品或者功能构成上一般不超过15个人包括产品经理、UI设计、数据科学家、前后端开发者。团队的工作重心就是把产品和功能做好这也就意味着只有把产品和功能做好了每个人的绩效才会好。
在这种情况下,沟通就和自己的利益紧密相关,所以大家会通过主动沟通去推动产品和功能的开发。比如,产品设计得慢了,开发人员就会去催促询问,看有没有自己能够帮得上忙的地方。
但是,改变公司的组织架构往往阻力大、推进缓慢,我们还是先立足现状,去**解决按照职能划分团队的沟通问题**。一个比较有效的办法是使用虚拟团队。我以前在微软的Office团队第一次见到这种方式后来又把它用在了其他公司效果非常好。
具体的办法是,**给每个功能设计一个虚拟团队**,包括产品、设计,以及相关的开发人员。之所以说是虚拟团队,是因为它并不存在于公司的正式组织架构里。我们明确这个团队的职责,就是要做好某个功能。当时我采用的办法也很简单,就是给每个虚拟团队建立一个专门的聊天群,来沟通这个功能的相关问题。而我每周会收集每个虚拟团队的工作进展情况,以此来推动产品维度的进展。
说到这儿,你可能会有一个疑问,**我对非开发部门没有掌控权怎么办?**事实上,我在推动这个虚拟团队的时候,只负责管理前后端开发人员,对产品等团队没有管理权。但因为这个虚拟团队,和其他团队不存在利益冲突,而且流程很轻,所以并没有任何反对声音。而在虚拟团队中,我负责管理的前后端开发人员会主动发力,促进整个虚拟团队的顺畅沟通,效果非常好。
有了主动沟通的意愿,我们就可以开展下一步工作了,即设计流程和使用工具,推动研发信息的高效沟通。
## 设计流程和使用工具,推动研发信息高效沟通
这一步的关键在于确认研发流程中的重要信息,然后针对性地设计合适的流程,并选用恰当的工具。
在我看来对提高研发效能起到关键作用的信息主要分为4种。
### 第1种信息是战略目标相关的信息
这一类信息的处理原则是尽量公开。只有当团队成员清楚公司以及团队目标时才能更容易把自己的目标与之对齐。或许这就是OKR最近几年特别流行的原因吧。如果你想深入了解OKR并在团队中落地可以参考我的朋友黄勇开设的《黄勇的OKR实战笔记》课程。
Facebook每年都会召开一个员工大会详细列举公司的战略目标每周还会有一个Q&amp;A会议每个员工都可以参加马克 · 扎克伯格Mark Zuckerburg会亲自出席并回答员工提出的各种问题。这些举措都促进了员工对公司战略和目标的深入理解。
### 第2种信息是代码相关的信息
这一类信息的处理原则也是尽量公开。代码是最直接的参考是最实时的文档。Facebook基本所有的代码仓都对全部开发人员公开。更进一步地我们不但可以阅读其他团队的代码还可以主动去修改、提高他们的代码只要修改得当发出的PR就会被接受。
在国内由于IP保护不力等客观原因绝大部分公司不愿意把代码对所有员工公开这也可以理解。不过在这种情况下我还是建议在不泄露核心机密、不影响核心业务的前提下尽量扩大代码公开的范围以及受众人群。
**选择共享代码的工具和方式基本原则是方便查找并在进行开发工作的主要入口比如IDE、命令行工具、Web浏览器提供接口。**
以Facebook为例他们使用Phabricator的Diffusion子功能方便团队成员在网页上浏览和查找代码仓的历史在代码搜索方面他们自研了一个内部工具对主要代码仓的代码进行几乎实时的索引开发人员通过网页浏览器、命令行以及IDE的插件使用代码搜索功能。
高效的代码浏览和搜索对研发顺畅很重要,推荐你加大在这方面的投入。
### 第3种信息是研发过程中用到的各种文档
这些文档,包括产品设计文档、开发设计文档、测试文档、部署流程文档等。确保这一类信息的高效流通,比较有效的原则是,通过统一的工具,方便大家添加、修改、查询这些文档。
在Facebook每个产品团队可以选择自己的文档管理方式。总的来说绝大部分团队主要使用公司统一的Wiki来进行松散的文档管理既方便添加、修改也方便搜索。而对于那些比较正式的文档有些团队会使用类似Quip的共享文档系统进行管理。
关于文档管理,有两点值得强调:
- 第一文档的管理流程由每个小的功能团队决定。就比如我刚才提到的10个人左右的小团队因为他们的共同目标是尽快开发好产品所以会主动寻找一种适合自己团队的文档管理方式和工具。
- 第二绝大部分Wiki和Quip管理的文档都是全公司公开的所以团队之间也可以很方便地找到其他团队的相关文档避免信息孤岛的问题。
### 第4种信息是各种标识信息
整个研发流程中在各种工具之间流动着多种标识信息包括任务工单、代码提交号、版本号、代码审查ID、测试用例ID、Bug ID等。在我看来**管理这一类信息的有效方法是各种工具通过提供API做到服务化形成工具之间的网状连接**,以方便开发人员在工具上快速拿到需要的各种信息。
接下来我们看一个热修复的具体案例。开发人员写好热修复的代码提交后需要去发布工具网站填写这个提交的Commit ID申请进行热修复。热修复发布工具根据Commit ID找到对应的代码审查ID以及Bug ID自动显示到这个网页上。同时这个工具还会自动找到这个热修复提交所依赖的、还没有上生产的其他提交并显示出它们的信息。
有了这些信息开发人员就可以方便地检查、确认自己的这个热修复可以提交然后点击确认正式申请热修复。最后热修复发布工具自动cherry-pick这些提交把它们都部署到一个热修复环境上进行验证。
整个流程中涉及的服务和工具包括代码仓服务、代码审查服务、Bug管理系统、测试验证服务、发布上线服务等。这些工具具有信息高度互通以及自动化的特点使得开发人员和运维人员能够以最快的速度拿到各种信息避免繁琐而且容易出错的手工操作从而尽快部署热修复。
这也就解决了信息溯源难的问题。
通过对研发流程中重要信息的管理,以及对应的高效沟通方式,我们就实现了研发信息流通的顺畅性,从而实现了团队的高效协作。最后,我们再来看看在具体的工作中,团队成员之间有哪些沟通方式和技巧。
## 沟通方式技巧
高效沟通是个很大的话题,涉及方式、技巧等内容。落到研发团队沟通的具体场景中,我认为高效沟通的首要原则就是,**根据沟通需要达成的任务的实时性、方便追溯性,以及对别人的干扰程度,选择合适的沟通工具**。
面对面、电话、实时聊天工具、邮件、具体任务工具中讨论等不同的沟通方式,在实时性、方便追溯性、对他人干扰程度等方面各不相同。我将其总结为了一幅图,供你参考。
<img src="https://static001.geekbang.org/resource/image/d6/20/d6eb819cd8828bd0cf11360bc2586620.jpg" alt="">
可以看到,不同的沟通方式之间差别很大,我们应该根据具体场景去选择不同的沟通工具。但在现实工作中,有一个不太好的趋势,就是大家都倾向于追求沟通的实时性,大多使用即时聊天工具和电话。这个情况在国内尤其严重,很多公司完全使用即时聊天工具,比如微信、钉钉等,基本上放弃了邮件等其他方式。
使用这种方法,最主要的好处实时性好,因为每个人都希望自己的问题能够马上得到反馈。但,短暂的好处却带来了长远的利益损失,主要表现在以下两个方面。
- 问题难以追踪。在聊天群里讨论问题时,问题只要一多马上就乱了,很难找到之前的相关内容。
- 对他人干扰巨大。一个极端情况是我见过一个公司有数据显示开发人员平均每8分钟就会被打断一次。可想而知这种情况下的开发效率自然很低。
**解决这个问题的办法很简单,就是针对不同的情况,要求大家使用合适的工具去沟通,并逐渐形成习惯。**
具体来说,首先,**对某个任务进行讨论时**,最好是直接使用任务工具中的讨论功能,同时通过@的方式来告知相关人员。而一般的工具都可以配置出现@时,自动发邮件通知对方。所以,如果不是紧急任务,都可以采用这种方式。
**其次,在询问问题的时候**可以分以下3种情况选择沟通方式和工具
- 如果问题不紧急,尽量使用邮件,避免使用即时聊天工具和电话。为了确保大家能够及时回复邮件,可以在团队内制定一个规定,要求检查邮件的频率,比如说每天至少检查一次或者两次。
- 如果问题紧急,则直接使用即时聊天工具、电话。
- 如果要讨论的问题比较复杂,或者是非常紧急,则直接选择电话,或者面对面沟通。
在我看来,选择合适的沟通方式及工具,不仅可以大大减少员工间的互相干扰,还可以提高问题的可追溯性,对团队以及个人的研发效能提升帮助非常大。
## 小结
在今天这篇文章中我从人、流程、沟通方式和工具这3大方面和你推荐了沟通顺畅进而提高效能的方法。我们再一起回忆下核心知识点吧。
实现高效沟通首先要解决的,就是团队成员的意愿问题,让他们愿意沟通。我们可以将沟通与团队成员的利益挂钩,在团队内部建设机制,来鼓励共享的行为,从而形成共享的文化。
然后,针对研发流程中流动的各种信息,我们要做好分类,针对性地设计合适的流程,并选用恰当的工具,最大程度地共享给团队成员。比如,公司的代码、战略目标、文档、流程等信息,尽量公开;再比如,使用统一的工具,方便团队成员添加、查询有效信息。
最后,在平时的沟通中,要权衡实时性、可追溯性以及对其他成员的干扰程度。根据场景,选择合适的沟通方式和工具,提升团队和个人的研发效能。
开发者大多在沟通上有所欠缺,有时甚至认识不到沟通的重要性。事实上,信息的缺失,对研发效能影响非常大。试想一下,如果没有搜索引擎的帮助,你写代码的效率会下降多少呢。所以,在信息沟通上多花些时间、精力,对团队和个人效能的提升都大有裨益。
另外,在硅谷的互联网公司,大家都很在意被打断的情况。如果你在没必要的情况下,使用聊天工具或者电话跟同事沟通的话,他会非常生气。
在Facebook时有些同事会直接告诉其他人如果我戴上耳机就表示不希望被干扰除非紧急情况否则不要找我当面沟通问题。虽说这种做法有些极端但我非常希望国内公司也能够尝试类似的工作方式因为它能让大家安心开发在高效产出的同时更多地享受心流的快乐。
## 思考题
在工作中,你见到的信息沟通的最大问题是什么,在今天的文章中能找到合适的解决方法吗?如果没有找到,你还有什么建议的解决方法吗?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,113 @@
<audio id="audio" title="10 | 答疑篇反对996并不是反对奋斗" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/a8/1a/a84c85a74daa6aede13efcf6c583821a.mp3"></audio>
你好,我是葛俊。
这篇答疑文章已经是我们“研发效率破局之道”专栏的第10篇文章了。很多同学在这些文章下面留下了精彩的留言阐述了自己对研发效能的认识、遇到的问题以及解决方案。
比如,@囧囧冰淇淋,基本每节课都会整理详细的学习笔记,并结合自己的经验进行思考、提炼和总结;又比如,@Geek_93f953@Geek_1988@Robert小七@Johnson@李双@许童童@寒光等同学,提出了高质量的问题。还有很多其他同学留下了非常精彩的留言,这里我就不一一提及了。
这些留言活跃了专栏气氛,帮助其他同学进一步思考,也激励着我要把专栏写得更好。所以,在这里我首先要对你表示感谢,感谢你对我的信任,也感谢你的积极参与。
这9篇文章涉及的问题我基本都在评论区直接回复过了。在今天这篇文章中我会挑选4个大家普遍关注的问题再详细展开一下也算是研发效能综述和研发流程这两个模块的一次总结与复习打好基础以应对接下来的工程方法、个人效能、管理和文化模块的内容。
现在我们就正式开始今天的4个问题吧。
## 反对996并不是反对奋斗
在专栏第1篇文章“[效能模型:如何系统地理解研发效能](https://time.geekbang.org/column/article/125840)”中我谈到了996的话题。从留言来看关于我对996的态度有些同学还存在些误解。所以我们再来讨论下这个问题。
### 第1个误解是硅谷的互联网公司加班不太多工作生活间的平衡做得很好
事实是,硅谷的互联网公司,加班也比较常见。这一点,在创业初期的公司尤其明显。
比如我在2010年加入Facebook的时候Facebook已经比较成熟了有接近800名开发人员。但由于业务的高速发展和同事间的竞争我们的加班都很严重。我每个周末去办公室加班的时候都能看到大概百分之三四十的同事在加班。
所以,工作和生活的平衡,完全要靠自己来调节。而我看到的是,很多开发人员实际上调节的都不是特别好,基本上只有工作没有生活。
另外这样的加班是自愿的没有加班工资。只有在一些特殊时期比如和竞争对手拼速度的时候公司会要求大家Lock down类似于国内的封闭开发才会有加班工资。
### 第2个误解是反对996是在反对奋斗
正如上面所说,硅谷的互联网公司也有很多人在加班,我个人也是大量的主动、自愿加班。因为,我热爱软件开发这个行业,愿意花费大量的时间、精力为之奋斗。
所以我反对996并不是反对奋斗而是反对用工作时长尤其是强制上下班时间来衡量工作效率。
在第2篇文章“[效能度量:效果不好甚至有副作用,怎么回事?](https://time.geekbang.org/column/article/126447)”中,我提到研发效能度量困难的一个原因就是,度量数据的收集难易程度不同,人们倾向拿容易收集的数据去关联效率。因此,管理者使用时长这种很直观、很容易度量的指标去衡量研发效能,结果就是事倍功半。
相比之下,硅谷的很多高效能公司,都是任务驱动型的,也就是说只要你完成任务了,工作时长无所谓。当然了,因为任务量大以及同事间的竞争,很多人会主动加班。但需要注意的是,这些公司并不会强制要求工作时长。更进一步地,它们会提供非常灵活的工作时间安排,方便大家提高工作效率。
比如Facebook默认每周三是没有会议的工作日也就是尽量不安排会议大家可以选择在家工作。另外Facebook的上下班时间很灵活这对于需要接送孩子的员工来说就很方便了。
**总而言之反对996是反对不科学地使用工作时长来提高研发效能。**
## 如何优化移动端开发的流程?
有同学在第7篇文章“[分支管理Facebook的策略适合我的团队吗](https://time.geekbang.org/column/article/132499)”后留言反馈研发流程模块的这几篇文章针对的都是后端开发想了解下移动端开发的内容。在这里我要和你澄清一下我前边描述的各种概念和原则比如持续开发、持续集成、持续交付对前端包括Web前端、移动前端等和后端来说都是一致的。
以Facebook iOS应用开发为例。他们采用的也是单主干的开发分支模式也要求代码提交的原子性以及master分支上线性的代码提交历史。在持续集成方面他们也是使用Phabricator作为流程和质量控制中心进行各种各样的代码入库前检查。在持续交付方面他们也是采用了和后端类似的方式每隔一定时间进行一次全量的构建和验证。
当然,前、后端的开发也有些区别,比如:
- iOS的App Store的发布周期是两周一次所以他们采用了两周一次全量部署的方式取消了日部署和热修复部署。不过后来Facebook采用在原生App中实时加载JavaScript的方式在一定程度上绕过了App Store的发布周期限制于是之后也引入了热修复部署流程。
- 后端代码在持续交付过程中每次构建的结果直接部署到一个网站大家通过特定网址去访问即可进行验证。而移动端开发的情况要复杂一些Facebook的方式是提供App安装服务让大家可以在自己的手机上安装不同版本的App包括master分支版本、周部署测试版本以及线上版本等并提供自动更新的功能。通过这些自动化使得移动开发的流程更顺畅。
- 在测试移动端App的时候需要测试大量的手机硬件和操作系统版本的组合。针对这一需求Facebook进行了大量的自动化能够让测试在各种不同的环境中自动运行。同时Facebook还研发了一个服务化的手机池让开发人员自助式地把自己的App部署到某一个特定的硬件和操作系统上并使用远程控制进行检验。
这里我再和你扩展一下前、后端配合的内容。前、后端团队有各自的部署日程即版本火车由功能开发团队决定后端和前端分别搭乘哪一辆版本火车上线。一般采用的方式是后端先上线同时使用功能开关让这个API对用户不可见然后前端上线最后打开功能开关完成整个功能。
总的来说,研发流程这个模块中提到的各种原则,在前端和后端都同样适用。在理解这些原则之后,你可以针对具体的情况,去设计适合的流程和方法。
## 关于环境获取的具体建议
有同学留言反馈,环境问题是他们研发过程中的最大痛点。具体来说,联调环境、测试环境的获取,常常需要排队。这里,我再提供些具体的解决方法吧。
从我的经验来看,**使用云的架构尤其是在Docker和Kubernetes的支持下把这些环境做成自助化服务是个比较好的解决办法。**
比如虽然Kubernetes没有提供“环境”这一概念但我们可以在它上面添加一层封装通过Infrastructure as CodeIaC的方式来自动化环境的获取和释放。这是一个比较通用的办法。具体来说实现环境服务化的思路是
- 首先把环境模板化并把模板作为代码进行存储。这个模板系统需要支持环境中的资源设置以及服务间的依赖。同时需要提供设定变量的能力来支持用户在环境生成时指定参数处理诸如数据库、MQ等服务在环境上的差异。
- 然后,结合集群资源权限做发布管道编排,这样就可以给不同的流水线的运行结果,也就是软件包,自动按照模板生成环境。
- 最后,可以选择把生成环境的权限开放给开发者,让他们可以通过模板生成联调环境使用。
事实上这样的环境生成、管理系统作用很大。比如可以通过发布管道编排来实现开发、运维、测试整体变更追踪。而且随着业务增长可以扩展到任何应用程序都能按需部署到任何规模的任何环境中。还有如果QA可以将测试数据和测试用例也服务化编排到管道中就可以实现安全高效的一站式发布。
在下一篇文章中,我会与你更系统地讨论如何给团队配置、提供高效的研发环境。希望这样的内容安排,可以最大程度地帮助你解决环境问题。
## 哪几个效能度量指标比较实用?
在第3篇文章“[效能度量:如何选对指标与方法,真正提升效能?](https://time.geekbang.org/column/article/128151)”中,我对常用的度量指标给出了分类方法,以及选用的基本原则。有同学反馈,希望我能给出一些更具体的实施和使用建议。
所以,在今天这篇文章中,我会**基于不同的改进目标**,分别从**提供用户价值、流程高效和质量**这3个角度再给出几个具体建议。
从**提供用户价值**的角度来看,可以选择以下几个指标。
- 净推荐值NPS
- 系统 /App 宕机时间和严重线上事故数;
- 热修复上线时间;
- 核心服务SLA可用性指标也就是我们常说的服务能达到几个9。这个指标尤其适用于需要提供服务给外部客户的组织涉及一些服务的性能和可用性的指标契约直接反映是否达成了对客户的承诺。
从**流程高效**的角度来看,可以选择以下几个指标。
- **WIP在制品数量**,是看板理论的核心思想之一。它指的是,已经开始但没有完成的工作,详见[第3篇文章](https://time.geekbang.org/column/article/128151)对累积流程图的描述。一个非常有效的提高研发流程顺畅度的办法是限制WIP。也就是说每个环节不能同时有超过一定数量的任务。如果你想了解具体细节的话可以参考[《看板方法:科技企业渐进变革成功之道》](https://book.douban.com/subject/25788807/)这本书。
- **发布频率**。如果系统发布没有交易成本发布频率越高越好因为它可以更快地提供用户的反馈。发布的交易成本指的是每次部署需要的流程工作比如拉分支、代码合并、运行测试用例等。考虑到发布的交易成本对很多互联网产品来说1~2周通常是比较合适的发布频率。
- **构建时长**指的是个人构建以及CI/CD构建时长等指标。它们对持续开发和CI/CD顺利执行影响很大。
- **环境获取时长**,指的是开发机器环境、沙盒环境、测试环境的获取需要多长时间。这几个指标,对持续开发影响很大。
从质量的角度来看,可以选择以下几个指标。
- **工单返工率**反映的是开发团队的代码质量和自测程度以及QA的压力和能力。
- **持续交付通过率**:执行构建-&gt;部署-&gt;测试-&gt;发布全流程的成功率,反映的是开发自测质量,以及自动化验收的稳定性。
- **高优先级安全漏洞产生率,安全漏洞修补速度**:这两个安全相关的指标反映的是,团队在安全方面的风险和处理能力。
## 小结
好了,以上就是今天的主要内容了。如果有哪些你希望深入了解的话题还未涉及到,希望你可以留言给我。
最后我想再和你强调一下第4篇文章“[流程优化:怎样才能让敏捷、精益真正为我所用?](https://time.geekbang.org/column/article/128867)”中提到的Why-How-What黄金圈法则和“实用主义”原则。
我觉得,这是提高研发效率的关键所在。因为软件研发是一个非常灵活、非常有创造性的活动,所以我们一定要抓住根本,了解我们到底需要达到什么目的,有哪些基本原则,然后才是学习一些可供我们参考的最佳实践。这样,我们才能灵活运用这些原则、最佳实践,真正提升团队的研发效能。
所以,在整个专栏的写作中,我也会着重系统化地讲解研发效能的基本原则。让我备受鼓舞的是,很多同学在留言中表示会支持这个思路。这里,我衷心希望你可以通过实用主义的方式,去寻找合适自己的最佳实践。
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,145 @@
<audio id="audio" title="31 | 业务目标和技术目标两手抓:怎样打造高效团队?" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/f9/45/f902c7e809d5162a8901e1763aee6b45.mp3"></audio>
你好,我是葛俊。今天,我来和你聊聊管理和文化。
今天这篇文章是最后一个模块“管理和文化”的第一篇。在前30篇文章中我已经从优化流程、团队工程实践、个人工程实践这三大方面与你介绍了很多原则、方法和具体实践。通过这些内容相信你应该对软件研发活动的本质有了更深刻的理解也对这条超级灵活的流水线如何提效有了新的认识。
但作为团队管理者,要提高团队的研发效能,掌握了这些原则、方法和实践后,还要通过管理和文化让它们真正在团队落地。管理是提高团队研发效能的基石,而文化是持久高效的保障。同时,管理又决定了文化,如下图所示。
<img src="https://static001.geekbang.org/resource/image/30/bf/30fbd592b6447409cd941625d94fb4bf.png" alt="">
所以,在接下来的几篇文章中,我会参考硅谷高效能公司的一些管理实践和原则,以及我在国内外公司的落地经验,与你介绍如何通过管理和文化来提高团队的研发效能。今天,我们就先从技术管理者的主要工作步骤出发吧。
在我看来技术管理工作主要有如下3个步骤
1. 寻找目标;
1. 目标管理;
1. 计划并执行去实现目标。
其中计划执行又包括人、流程和工具3个方面。
## 第一步:寻找目标
在[第1篇文章](https://time.geekbang.org/column/article/125840)中,我曾和你分享研发效能的三要素是有效、快速、可持续性,其中第一条就是有效,也就是准确性。所以,要想建设高效的研发团队,技术管理者的第一项重要工作就是,作为舵手,为团队寻找方向和目标。
**首先,技术团队的根本目标就是业务目标。**毋庸置疑,业务目标是团队存在的意义,完成它是一切的基础。
业务目标的设定有两个层次:
- 弄清楚公司和上级对团队的预期,达到这个预期,是团队的基础目标。
- 在基础目标之上,还要给团队设定一个进取目标。我们需要分析公司的发展方向,以及团队的实际情况,找到那些既符合公司利益,又通过跳一跳就能够得着的目标。
除了业务目标之外,还有**另外一种技术管理者一定要关注的目标,即技术目标**。如果只关注业务,我们的行动就容易短视。有这么一句话我非常赞同:技术常常在短期被高估,在长期被低估。作为技术管理者,我们需要看清楚并坚持技术在长期可以发挥的巨大作用,在技术上持续投资,制定并完成合理的技术目标。
在我看来,**技术目标主要有两种:**
- 一种是关于偿还技术债的,这是处理已经形成的问题。关于技术债的处理,你可以再回顾下[第14篇文章](https://time.geekbang.org/column/article/138916)中的相关内容。
- 另一种是前瞻性的技术目标。作为技术管理者,我们要有灵敏的技术嗅觉,对即将出现的技术挑战,做一些预防和准备。
关于前瞻性的目标我再和你分享一个我在Facebook时的例子吧。
2013年左右我们从公司开发者的使用数据观察到由于代码仓的迅速增大Git对它的支持有些吃力比如一些操作的速度越来越慢。于是我们抽出人力去做调研克隆了一个Facebook的代码仓并按照当时的增长速度去模拟大量的提交进行测试。
结果发现再过半年许多常用的Git操作速度都将下降到不可接受的程度。于是我们团队专门成立项目组来解决这个代码仓将会出现的性能问题。最终我们在问题还没发生前就把它解决掉了。这种具有前瞻性的技术目标确保了公司的业务能够持续发展给公司和团队带来的好处显而易见。
**关于技术目标的设定,有两个常见问题:**
- 一是业务目标和技术目标的时间占比应该是多少从我个人的经验看80%是一个合适的点。
- 二是,技术目标要不要立项?在我看来,这要视公司情况而定。如果你的主管对技术目标认可,那最好能够单独立项;否则,就把技术目标合并到业务目标中。比如,要实现某一个业务,我们必须重构某一个组件。这里,重构组件就是一个技术目标,无论采用哪一种方式,我们都需要持续关注技术目标。
## 第二步:目标管理
目标管理的第一步就是制定计划。这里我先给出一个有效设定目标的原则SMART原则。SMART是Specific具体、Measurable可衡量、Attainable可达成、Relevant与主目标有相关性和Time-bound有明确的截止期限这5个英文单词首字母的缩写。
一个目标可能被定义为“今年下半年实现用户讨论功能”但很明显这个定义不够清晰。而另外一种定义方法是“今年12月31号之前实现用户讨论功能模块在主页以及至少一个其他页面使用并且用户使用率大于10%”。
可以清楚看到,第二种定义方式更具体,团队成员有更明确的方向,能专门针对具体的模块使用场景和使用率努力。并且,具体的数字和截止期限,可以帮我们更容易跟踪项目进展。
很明显第二种定义方式正是使用了SMART原则中的具体S、可衡量M和有明确截止日期S三个原则。另外这个任务还要是可达成A也就是既要有挑战性又可以通过努力达成这样团队工作起来才会更有劲头。最重要的是与主目标的相关性R是前提只有这样的任务才有价值。
总结来说SMART有更明确、具体的目标利于员工更加明确、高效地工作完成与公司目标更加对齐的业绩也为管理者实施绩效考核提供了目标和标准。网络上有很多SMART原则的相关资料比如[SMART 原则以及实际案例](https://blog.csdn.net/limuzi13/article/details/52245983)这篇就还不错。
除了SMART原则**OKR是一个很有用的目标管理工具**。
我们经常会听到领导者Leader和管理者Manager这两个概念但你有注意过它们的区别吗两者经常混用但实际上有一个本质区别领导者告诉团队需要去哪里而管理者告诉团队如何去到那里。
在我看来,**每一个管理者都应该努力成为一个领导者**给团队目标让团队成员自己找到达成目标的方法。而OKR正是帮助管理者做到这一点的工具。
其中O表示目标是鼓舞人心的目标可以使每个人保持一致和受到启发KR则表示关键结果是达成目标需要注意的度量。在Google使用后最近几年OKR很流行效果的确很好。OKR的内容比较多如果你想要系统了解并在团队落地的话推荐你阅读《[黄勇的OKR实战笔记](https://time.geekbang.org/column/intro/100030701)》这个专栏。
这里,我想强调一下**用好OKR的两个关键点**。
第一使用OKR最重要的目的是让全公司对齐目标所以在实施OKR时我们要随时留意这个目的。可以说这是执行OKR最关键的原则。基于这个原则我们可以扩展出很多实际操作。比如公司级别定义一个O每个团队和个人根据实际情况制定自己的O和KR所有人的OKR都透明全公司可见同时定时做回顾、调整和复盘。
第二OKR不是一个HR工具不是绩效管理方法。绩效管理方法一般包括目标、度量和考核激励 / 惩罚与之相比OKR只包括目标和度量是一个目标导向工具。
OKR之所以在目标对齐上有很大作用是因为团队成员可以发挥主观能动性自己制定与公司目标一致的KR。而一旦OKR跟绩效挂钩团队成员承担风险的意愿和内驱力就会大大减弱倾向于制定更容易实现的KR从而失去了目标导向的意义。
所以OKR不要跟绩效挂钩。那么问题来了**使用OKR之后怎样进行绩效考评呢**实际上这也很简单只要评估团队成员具体完成的工作对公司的贡献度即可甚至可以OKR和KPI并存。比如公司高层可以使用KPI用数字衡量绩效而全公司都使用OKR进行目标对齐。
## 第三步:任务执行
在具体的任务执行上,作为技术管理者,我们应该从人、流程和工具上来提高研发效能,高效达成业务目标和技术目标。
### 关于人:最关键的是调动起主观意愿
首先,把团队成员的利益统一起来,才会激发他们的主观能动性,自己想办法去达成目标。典型的例子是,为了消除职能竖井,采用全栈开发方式(你可以再回顾下[第8篇文章](https://time.geekbang.org/column/article/132539)中的相关内容)。
**统一团队利益的主要方法是,采用康威定律来组织团队结构**,使其与产品结构相吻合,让产品的成功成为团队一致的目标。
比如Facebook针对产品和功能一般组织10人左右大小的团队里面包括了前后端开发者、设计师、产品经理、数据科学家等。所有人的目标都是如何把自己团队的功能、产品做好。小团队之间松耦合有比较高的自主权不同团队间主要通过目标的一致性来进行协调。这种方式不但使得大家的力往一处使而且灵活机动、出产品很快。
把这种小分队的方法用到极致的是Spotify公司。他们在产品层面把各个功能模块隔绝开某个功能出现问题不影响其他功能正常使用。每个功能由8人左右的自主运营小组负责称之为Squad。Squad的主管负责确认、沟通团队需要解决的问题以及解决这些问题的意义而团队成员的职责是自己决定如何解决这个问题自主性非常强。
另外需要注意的是,**在把组织架构和产品对应起来的时候还有一个重要原则DRY。**对个人开发者说DRY是不要重复自己而对公司或者团队来说DRY就是要建设针对基础设施、共享业务的平台以避免重复造轮子。
以Facebook为例Infrastructure团队基础平台团队人数众多话语权大对公司的业务发展至关重要我之前所在的内部工具团队就是Infrastructure团队的一部分。各项业务之所以能够快速开发、验证、上线、迭代并实现高可用、高并发支持上亿日活用户都跟Infrastructure团队的工作密不可分。
除了基础平台外DRY在业务方面最主要的表现是业务中台也是最近非常火的话题。
调动人员的意愿,除了组织架构方面外,绩效管理和企业文化也很重要,我会在后面几篇文中与你详细介绍这些内容。
### 关于流程:选择合适的方法论、原则、实践
关于流程需要注意的是,寻找符合软件开发行业特性的方法,并根据团队情况不断优化。**具体来说,我推荐使用先从全局的端到端流程入手,寻找系统瓶颈,然后再集中精力解决瓶颈,完成一轮优化。**
这样可以从全局出发,避免以偏概全,能够最高效地使用团队的人力、物力。在优化过程中,我们要尽量采用数据驱动的方式,用数据来寻找问题,并通过数据的比较来检查改良措施的有效性。
针对流程的优化,你可以再回顾下[第4篇文章](https://time.geekbang.org/column/article/128867)中的相关内容;而关于效能度量,你可以再回顾下第[2](https://time.geekbang.org/column/article/126447)和第[3](https://time.geekbang.org/column/article/128151)篇文章中的相关内容。
另外,要提高团队的研发效能,作为团队技术负责人,需要保持技术判断力,包括技术选型的能力以及方法论的选择能力。你可以参考我在前三个模块中,给出的流程优化、团队工程实践及个人工程实践的一些高效方法论和实践。
### 关于工具:根据实践进行选择
完成了人、流程的工作之后,工具的选择就比较容易了:让团队**根据方法论选择适用的工具即可**。在前面的文章中,我对各个方法论的适用工具都做过详细介绍,你可以再回顾下相关内容。
这里,我再给出**团队选用工具的两个原则**吧。
第一个原则是,选择工具时要根据场景的复杂程度选择自建还是使用第三方服务/工具。对简单、单一场景我推荐使用开源工具而对复杂的系统和流程可以考虑使用一些付费工具比如SaaS产品因为术业有专攻这样比自建工具性价比更高。
通常情况下针对小型创业公司很多SaaS产品的价格不算太高有些甚至免费。作为技术管理者我们要考虑投入产出比让开发人员把时间花在业务上可能才最合适。在硅谷小公司使用付费软件服务的现象也很普遍国内公司也慢慢有这个趋势了。
第二个原则是工具虽然重要但背后的方法论和原则更重要。比如OKR如果使用得当使用一套简单的wiki系统就可以做起来但如果概念不清、原则不对的话即使引入专门的OKR工具效果也不会好。所以作为技术管理者我们一定要花时间了解工具背后的原则。
## 小结
今天,我通过寻找目标、目标管理,以及如何执行这三步与你介绍了一些管理方法。
首先,最重要的一点是,技术管理者需要同时关注业务目标和技术目标。只有这样,才能够让团队持续发展。如果今天这篇文章你只能记住一个观点的话,我希望是“业务目标和技术目标两手抓”。
在目标管理方面OKR是帮助团队对齐方向的不错工具。不过我们使用OKR时一定注意目的是对齐目标与绩效挂钩的效果会大打折扣。
在具体的任务执行方面,从人、流程、工具三个方面入手,即想办法调动人的主观能动性,从流程全局入手把时间花在最需要优化的地方,以及根据具体方法论和场景复杂度选择工具。
最后,我觉得每一个技术人员,都应该花些时间去了解管理,原因包括两点:
- 对公司、团队的管理措施有更清晰的理解,可以帮助我们更高效的、有的放矢的工作;
- 管理是职业发展的一个方向,了解一些管理可以帮你尽早弄清楚这条路是否适合自己。
## 思考题
Facebook提前预测并解决Git性能问题的例子中我们最后使用Mercurial代替了Git。你知道这其中的原因是什么吗
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,142 @@
<audio id="audio" title="32 | 从Netflix公开的著名PPT谈硅谷公司文化" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/df/d5/df56fff7b35d0207eb612ba241d456d5.mp3"></audio>
你好,我是葛俊。今天,我分享的主题是如何从公司文化的维度提高研发效能。
首先,什么是文化?定义很简单:
>
文化是决定一群人行为方式的共同认知、价值观和信念。
对于一个公司或者团队来说,文化就是决定其团队成员行动的基本价值观。文化决定行动,它的价值自然巨大。可以说,**一个团队能否高效产出,文化起到关键作用**。所以,硅谷的很多公司都特别重视公司文化。
比如SpotIfy对公司文化的态度就是“如果远见是你想实现的目标那么文化就是确保你能够实现这个目标的根本If vision is where you are going, culture is what makes sure you can get there”。再比如工程师文化正是Facebook这些年来的创造力引擎。
最近几年,国内公司也越来越重视文化建设了。比如,阿里巴巴的面试官有一个角色是“闻味官”,考察候选人是否和公司文化对味儿;再比如,有很多公司把核心价值观以红底黑字的形式做成条幅,挂在了墙上。但事实上,能够真正理解文化,并推行下去的公司却很少。
这里,**我首先要和你厘清的是,文化更像是潜规则,写到横幅上的标语并不一定是公司文化;文化的建设,更是技术活和力气活的合体,绝不是喊几句口号就可以完成的。**
比如美国2001年爆发的安然丑闻案就是因为许多高级职员参与了销毁、篡改、编造财会记录的行为欺骗了股东导致了这一起美国历史上最大的破产案给股东造成了超过600亿美元的损失。而“诚信”正是安然公司价值观的第一条被凿刻在大理石上装潢精美地放置在公司一楼的大厅。所以说“墙上的价值观”真正地践行起来才能形成公司文化。
那么,**了解公司文化,到底对我有什么帮助呢?**
在我来看,如果你是管理者,或有志成为管理者,关于公司文化的讨论能够帮助你设计并实施适合自己团队的文化。而如果你现在还不是管理者,了解公司文化对你会有两方面的帮助:一是,可以帮助你推动团队进步,提高自己对团队的价值;二是,衡量团队文化是否适合自己,因为这是衡量是否要加入一个新团队或公司的重要考量因素。
接下来,我们看看**到底如何推进好的文化在公司和团队落地。**从硅谷的成功案例来看,推行文化主要包括三步:
1. 定义出自己团队需要的文化;
1. 从招聘、流程设计方案推动文化;
1. 持续地用行动去推动,并总结提高。
接下来我就以奈飞Netflix为例与你详细探讨这三个步骤。之所以用奈飞做例子是因为这个公司在推行公司文化方面做得非常彻底并且他们对外公开的关于文化讨论的资料也比较多。
2004年奈飞发表了一篇关于公司文化的PPT叫作“[自由和责任](https://www.slideshare.net/reed2001/culture-1798664)”详细阐述了他们的公司文化及背后的思考。这个PPT非常不错Facebook的COO 雪莉 · 桑德伯格Sheryl Sandberg曾经说它是硅谷产出的文章里最重要的一篇没有之一。
需要说明的是这篇文章中我也参考了“自由和责任”这个PPT中的部分素材。通过对奈飞关于公司文化的经验介绍我希望你了解怎样定义文化以及更重要的是怎样落地文化来提高研发效能。
## 定义出自己需要的文化
从公司文化的维度提高研发效能,第一步就是要定义出你需要的文化。而文化比较抽象,所以**常见的定义文化的方式就是定义核心价值观**。因为价值观是团队成员做事儿的潜规则,是文化的具体体现。
**而定义价值观,需要根据公司的发展,确定要追求的最重要方向。**一般来说,公司的价值观定义包括四个方面的追求:产品快速上线、产品的有效性、客户满意度、员工满意度。但每个公司都要根据实际情况,来定义这些目标,不能简单地复制其他公司的成功经验。
比如美国有相当一部分互联网公司推崇创新它们的价值观是在任何地方、任何时候都要尽量保持创新。但如果是一家ToB或者与金融相关的公司那么简单地引入这样的创新文化肯定会让你的客户不高兴因为这样的公司往往更重视安全和低风险。
从结果来看,奈飞的价值定义精准,对公司业务成功非常有效。所以接下来,我们就一起看看奈飞是怎样定义自己价值的吧,相信他们定义价值的思路对你也会有参考价值。
1997年奈飞成立并进入DVD租赁市场。当时市场竞争已经非常激烈除了几个很成功的线下连锁店比如BlockBuster、Hollywood Video、Movie Gallery沃尔玛等巨头也开始加入。在这种情况下**奈飞仔细思考,制定了追求高效能/绩效和创新的核心价值观。**因为只有这样才能杀出重围。
基于这两条基本价值,他们细化了判断力、创新、好奇心、沟通等十条核心价值观。你可以在奈飞官网看到每一条[价值观的详细解读](https://jobs.netflix.com/culture)。
你可能会说,这些价值观都很常见,并没什么稀奇的啊。确实是这样,但奈飞最牛的地方是他们做到了。
以创新为例,奈飞在刚成立时,将小众电影作为了一个突破点。因为线下连锁店通常只关注当前票房较好,以及历史上比较流行的电影,于是小众电影很难找到就成为一些电影爱好者的痛点。这是奈飞得以生存下来的一个重要战略。所以说,创新这条价值观的确帮助他们在红海中杀出了一条血路。
定义出了自己需要的价值,更重要的是落地。那到底怎样去推动这些价值落地呢?
## 从招聘、流程方面设计方案推动文化
仅仅提出价值观是没用的,必须认真分析,找到合适的方法落实到招聘、流程、组织结构上。接下来,我们看看奈飞是怎样操作的吧。
他们关于创新的分析思路是这样的:一个小公司,在刚创立的时候,效能和创造性都很好,属于创新者。但随着公司规模逐渐变大,复杂度也随之变大,进而导致混乱,比如信息沟通不畅导致最终产品与用户需求差别很大。为控制混乱,公司就会引入流程和规则,但副作用可能是导致一流人才的流失,以及降低现有人才的生产效率和积极性。所以,很多公司壮大后,常常会败于新的创新者发起的挑战。
那怎么解决这个问题呢?
奈飞认为,**在公司规模变大时,不应该引入规则,而是应该引入更多人才来控制混乱。**只要招收到优秀的、负责任的人,并给与他们自由发挥的空间,他们就能够灵活高效地解决公司在成长过程中遇到的各种复杂问题。因此,**只要引入人才的速度超过复杂度的增长速度,就可以让公司在成长的同时,持续保持高效能和创新能力。这就是他们的基本思路。**
基于这个思路在HR方面他们采用高薪招聘、奖励人才而对不合适的人容忍度则很低。基本思路就是一个员工如果称职就用高于市场平均水平的薪酬去招聘或留住TA一个员工如果不称职就尽快解聘。
在具体操作上,他们使用了很多非常直接和非常规的方法。比如,取消每年定时的绩效考评,而是在平时实时考评。绩效考评结果只有合格与不合格两种,评判依据是员工能否胜任当前工作,并不太考虑过往表现:
- 对于绩效考评不合格的员工没有其他大公司常用的PIP计划绩效提高计划而是直接用丰厚的遣散费解雇
- 而对于绩效考评合格的人,也不是按照每年一定的百分比提升工资,而是根据市场价进行调节。
也就是说,在绩效考评合格的前提下,不管你的表现怎样,你的薪资都会根据市场平均水平来调高或者降低,但肯定会高于市场平均水平以保证你愿意留下来。
另外,他们也没有年终奖。是不是觉得很不一样呀?反正我第一次听说这些政策时,非常震惊。
奈飞认为,通过这些举措,能够保证任何时候,只有合格的人才能留下来,并且因为高薪,他们愿意留下来;另外他们能够接受这些特别客观的评定标准,所以是理性的“成人员工”。总结来说就是,这些措施能够确保员工有能力,并且负责。
有了这些有能力并且负责的人才后,**在流程方面,奈飞就可以对员工进行松散、基于信任的管理了**。
比如,奈飞是硅谷很早就使用假期无限制政策的公司之一,这也很好地体现了他们的思路。
在上市之前奈飞有10天带薪假、10天节日假期以及几天病假的一个准正式系统。说它是准正式是因为没有正式的跟踪系统是让员工自己统计已经使用了的假期并告知主管什么时候他们请假了即可。这个系统本来运行得挺好但在上市时审计程序要求有正式的系统统计员工放假时间。
这时常规方法是引入一个这样的正式系统。不过CEO里德 · 哈斯廷斯Reed Hastings了解到如果员工没有假期的话那没有这样一个假期管理系统也能通过审计。所以为了节省精力、资金他们干脆决定不引入正式的假期统计系统而是由员工自己决定什么时候放假以及休多长的假。公司只是提供一些指引比如财务人员应尽量避免在工作特别忙的时候请假再比如连续请假超过30天需要向HR说明原因等。
因为,他信任员工能基于公司利益做选择。既然绝大部分员工都能自觉,何必让他们花额外的时间来处理流程问题呢?至于极少数不自觉的员工,只能说招聘时看走眼了,尽快解雇就是。
## 持续地用行动去推动,并总结提高
文化是一种潜规则,行动才能真正表现出文化。所以,**推动文化建设的第一点,就是坚持去做。**
团队的领导者,一定要以身作则。比如,奈飞采用了灵活的无限制假期的政策,那你有没有想到可能存在的一个问题,如果团队的其他成员都没有休假,只有我休假了,会不会影响我的绩效呢?所以,奈飞要求比较高层的领导,每年必须要休一定时长的假期,并且要向团队成员公布他的假期日程,来避免这个问题。
**推动文化建设的第二点,是明确提出价值观**。虽然说文化是潜规则但推广文化还是需要把这些文化强调的价值明确地提出来并尽可能多地和员工沟通使他们对公司的价值观更加明确。所以奈飞一直很强调他们的文化甚至还将其总结成PPT对外公布。类似的Facebook也很强调文化甚至有海报文化到处都贴满标语。在后面几篇文章中我也会和你介绍Facebook关于文化的很多实践。
**推动文化建设的第三点,是注意文化的演进。**为了适应并跟随社会的发展变化,公司文化也不应该一成不变。所以,我们在建设公司文化时,也要时常回过头来看看,公司现在的文化和价值观是否还适合当前情况。
## 关于奈飞文化的思考
最后,我想再和你分享一个奈飞的小案例,希望你借由他们在推广文化上的操作和得失,可以对公司文化建设有更深刻的理解。
首先,**我想说的是奈飞在文化执行上非常坚决彻底**。这一点在“自由和责任”这个PPT的作者帕蒂 · 麦科德Patty McCord身上得到了很好的体现。
帕蒂是制定这些政策的核心成员在公司文化建设上功勋卓著却在2012年因为跟不上公司发展被解雇了因为奈飞的绩效考评不考虑过往贡献。当时这个事件还掀起了一段热烈讨论很多人认为她是自掘坟墓。不过我倒是认为这只能说明她当年制定的策略被成功执行了真正起到了作用。
**正因为奈飞采用了很多非常规的,甚至是极端的方式去推行文化,所以这些年来确实做到了持续的高效能和创造性,业绩也非常亮眼。**
从2012年Patty被解雇到2018年奈飞的股票涨了25倍用户数达到1.37亿覆盖190个国家。可以说他们关于自由和责任的公司文化功不可没。
但极端政策给奈飞带来成功的同时,也有很多负面作用。因为公司对低绩效员工的容忍度非常低,解雇人员的频率比较高,所以很多人认为奈飞内部存在一种恐惧文化,有些人时刻担心自己会不会突然被解雇,时常处于害怕的状态。
这种状态让员工工作得并不开心对创造性也会有很大影响。在公司满意度调查网站glassdoor.com上奈飞的满意度只有3.7分而Facebook、Google的满意度都在4.5分左右。2018年10月华尔街日报发表了关于奈飞文化的文章针对其负面影响对奈飞的政策提出了一些质疑在互联网圈造成了不小的轰动。
**其实,我个人对奈飞的文化有两点不太认同。**
第一,他们超级强调透明,认为一个成熟的人可以接受任何客观的评价。比如,奈飞会对一些被解雇的员工的解雇原因进行分析,甚至出现过让他们亲自参加的情况。虽然招聘的都是“成人员工”,能够客观看待因为绩效不合格被解雇这件事,但人终究还是有感性的一面,这种极端透明的措施确实会对一部分人造成影响。
第二太强调当前表现而不考虑过往贡献会降低员工的归属感进而降低创造性。相比起来我更喜欢Facebook这样的文化。关于Facebook的文化我会在后续文章中与你详细分析。
## 小结
今天,我和你分享了什么是企业文化,以及应该怎样设计、推动文化去提高公司的效能。
文化是团队成员做事的潜规则,所以好的文化是高效能的基础。而要通过建设好的文化来提高研发效能,我认为主要有以下三步:
1. 根据公司目标,定义核心价值观;
1. 围绕核心价值观制定HR、流程方面的方案
1. 用行动推动价值观实施,明确提出价值观来统一团队认识,注意文化的演进来保证公司文化与时俱进。
在介绍建设公司文化的三步时,我与你介绍了奈飞的很多实践,希望可以帮助你找到适合自己的方式、方法。
接下来的几篇文章我会与你讨论我最熟悉的Facebook的企业文化并分享我在其他公司引入文化的一些实践。敬请期待
## 思考题
你知道还有哪个公司像奈飞这样推崇透明的文化,效果又怎样呢?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,114 @@
<audio id="audio" title="33 | Facebook企业文化工程师文化是创造力引擎" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/87/0a/87c2b76450e216e45686dc663923280a.mp3"></audio>
你好我是葛俊。今天我来和你聊聊Facebook的工程师文化吧。
在上一篇文章中,我以奈飞为例,与你介绍了公司文化建设的三部曲,即定义核心价值观、针对性地设计招聘和流程方案,以及持续推动并总结提高。
除了奈飞的“自由与责任”外我们还比较熟悉、认可的是“Facebook的工程师文化”。所以接下来的4篇文章我会与你详细介绍Facebook的工程师文化、相关的管理实践以及我离开Facebook后在其他公司落地的实战经验。
我为什么要用这么多篇幅去分析Facebook的工程师文化以及支撑这些文化的管理实践呢原因很简单因为我清楚地看到工程师文化可以极大地激发开发人员的内驱力和创造性。
毫无疑问,这是一个双赢的局面:既可以为公司产生更多更有价值的产品,也可以大幅提升开发者个人的成长速度和幸福感。但相比之下,国内的很多公司在这方面有所欠缺,团队成员大都处于简单执行的状态,内驱力和创造性远没有激发出来。
所以通过接下来的4篇文章我希望帮你深入理解工程师文化的Why、How和What为管理者设计、实现团队文化提供示范和借鉴为个人参与文化建设和选择团队提供参考进而提升团队的研发效能以及个人的能力和幸福感
我们先来看看工程师文化是什么吧。
## Facebook的工程师文化到底是什么
通过上一篇文章,我们知道“文化是决定一群人行为方式的共同认知、价值观和信念”。那,什么是工程师文化呢?
工程师文化本意是,工程师群体行为方式的共同认知、价值观和信念。对工程师来说,我们的行为包括开发软件、解决问题、与客户打交道、与团队合作等。本来工程师团队的做事方式可能千差万别,但后来“工程师文化”逐渐演化成了“用正确的工程方法、思路来完成工作的文化”。
Facebook的工程师文化又是什么呢**一句话总结就是“黑客之道The Hacker Way”。**
听到这里,你可能会说,黑客不是一个贬义词吗?事实上,黑客在程序员界本是一个褒义词,指的是动手能力极强的程序员,他们创造、实验、打破常规,可以让计算机做任何他想做的事儿,即使计算机本身不同意。
但现在,黑客却和入侵电脑系统为非作歹的罪犯画上了等号。这个误解极深,以至于业界大神保罗 · 格雷厄姆Paul Graham专门写了一篇文章“[The Word Hacker](http://www.paulgraham.com/gba.html)”来澄清。
为什么Facebook要把“黑客之道”作为自己的企业文化呢
Facebook的企业使命在2017年7月有过一次演化不过总的来说就是提高传播和消费信息的方式把世界更好地连接起来。这样的社交网络系统规模庞大有着很多前所未有的分布式、高并发等挑战。同时也因为第一次把社交映射到网路上有无限的创新可能。所以建造这样的系统需要优秀人才不断创新打破常规。
扎克伯格和Facebook的其他管理层认为黑客做事的方式很适合公司快速发展不断进步、创新能支撑公司实现使命。所以Facebook一直把黑客之道设置为最根本的工程师文化并且在全公司推广。
2012年Facebook上市时[扎克伯格发表公开信](https://www.wired.com/2012/02/zuck-letter/)给出了黑客的4个特质。按照我的理解我再和你分享下这4个特质。
- **优化无止境**:黑客认为优化无止境,产品无完美。在大部分人认为服务已经无法改善,或者是满足现状的时候,会直接上手进行进一步的优化。
- **持续进步**:黑客不追求一蹴而就,一劳永逸。他们更喜欢迅速发布小规模更新,并从中吸取经验教训,通过长久努力不断进步。
- **代码为王**:黑客更在意代码实现。代码胜于雄辩,只有真正实现了的东西才有价值。
- **能力为王**:黑客有极度开放和精英为王的心态,不会因为谁的头衔高而默认其权威。相比之下,黑客更看重的是技术,以及把想法变为产品的能力,说白了就是谁行谁上。
针对这些特质和企业使命Facebook又总结了五条核心价值观分别是专注于影响力Focus on Impact、迅速行动Move Fast、勇往直前Be Bold、保持开放Be Open和创造社会价值Build Social Value
接下来我们就具体看看Facebook是怎样推行黑客文化的吧。
## Facebook工程师文化的具体实践
与奈飞推动企业文化建设一致Facebook也采取了一系列措施有效地推行其工程师文化落地包括重视工程师文化的宣传以及从流程、工具方面推动文化落地。接下来我们就具体看看吧。
**第一,非常重视工程师文化的宣传。**
Facebook一直有一个很大的“The Hacker Company”的标牌几次搬家都被放到最显眼的地方。扎克伯格在上市的公开信中也详细讨论了公司的工程师文化。
此外他们的海报文化也非常有名。进入园区你会发现Facebook的一大特色有很多写着各种各样标语的海报比如“The Journey is 1% Done我们的旅程只完成了1%”“Open the Door敞开大门”“Done is Better Than Perfect完成比完美更重要”“Focus on Impact专注于影响力”“Move Fast and Break Things快速行动打破陈规”等。我最喜欢的是一句中文粤语的“出来行最紧要勇”是勇往直前的意思。
很明显这些标语与Facebook的工程师文化和核心价值观紧密相关。Facebook的COO雪莉 · 桑德伯格Sheryl Sandberg在[一次访谈中](https://techcrunch.com/2012/01/19/sheryl-sandberg-valley-girl/)谈到这些海报时说,公司的文化正体现在这些海报中。
虽然这些标语看似在打鸡血但在Facebook工作过后你就会理解公司的确是在践行这些工程师文化而且为公司发展和个人成长带来了巨大好处。所以这些标语很多都是开发者自己打印了贴到墙上的。
**第二,从流程、工具方面推动这些文化。**
关于如何从流程、工具等维度推动工程师文化落地我会边和你列举Facebook的具体实践边与你分析这与公司文化落地的关系。
接下来,我们就具体看几个关于黑客特质以及公司价值观的实践吧。
在**能力为王**方面,扎克伯格没有独立办公室,工位和我的一样普通。当然,他有一个私人会议室。另外,员工互相看不到对方的职级,所以讨论技术问题时,不会因为级别而束手束脚。
在**持续进步**方面公司搭建了强大的实验框架、功能开关无论何时都可以测试上千个不同版本的Facebook服务给开发人员创造能够迅速行动、快速迭代的环境。另外公司的容错文化落到了实处。只要不是故意的首次犯错都不会影响绩效。当然同样类型的错误重犯会有惩罚措施另外有一些隐私、安全相关的红线也不能触碰。
在**代码为王**方面所有新员工入职时都被要求参加一个长达6周的培训叫Bootcamp。这6周的时间无论你是多高级别的管理者都要学习公司的代码库、工具和方法并实际编码完成任务。业内有许多工程师团队的管理者并不愿亲自动手编写代码这在Facebook是行不通的。因为Facebook更看重的是实践型人才。
在**专注影响力**方面Facebook有一个定期举办的Hackathon黑客马拉松活动让我们依照自己的创意开发原型产品然后在公司范围内进行演示。这就给了我们一个发挥想象力和能力来产生价值的机会。Facebook最成功的产品有些就来自Hackathon包括时间线(Timeline)、聊天、视频、点赞按钮等。
这样的流程、实践体现在公司的各个方面,不一而足。我在这里只是简单列举了些具体实践,详细、系统的分析会在后面的三篇文章中。
那么,**这些文化的效果怎样呢?**
从公司的长期快速发展和业界口碑来看这些文化的效果显而易见我就不再赘述了。这里我就站在一名普通开发者的角度来和你聊聊Facebook工程师文化对个人成长的效果吧。
## Facebook工程师文化的效果
Facebook的工程师文化鼓励优化无止境、持续进步、代码为王、能力为王也着实体现在工作的方方面面。这些都是强大的技术人员的重要特质对个人技术成长和创造产品帮助非常大。而在Facebook工作几年后你的做事方式自然而然会往这些方向靠拢可以说是润物细无声。
比如Move Fast and Break Things是Facebook最有名的一句文化口号也是对我影响最大的一句。
2010年我离开微软加入Facebook时比较习惯瀑布式开发非常谨慎体会不到这句口号的精髓。入职后大概两个月我从Bootcamp毕业加入了Phabricator团队。当时有一个任务是查看MySQL数据库中开发人员的信息我希望找到只读模式来保证不会把数据库搞坏。
但因为我对公司的MySQL命令行工具不是很熟悉就去问别人只读选项在哪里。他对我的问题很惊讶了解到我的担忧后他说“你小心一点不就行了吗”。可我还是不放心自己花时间找到了只读模式的命令。但事实证明在Facebook四年多的时间我从没有误操作过MySQL花在寻找只读命令上的时间属于浪费。
这件事儿对我的职业成长影响很大。我逐渐体会到**Move Fast and Break Things的一层意思就是要尽量快地去做事儿不要担心搞砸跟“Fail Harder”类似。**后来,我愈发适应这种方式,对公司的贡献也大了很多。
比如我在负责Phabricator时带领两个人共同支撑了公司所有开发人员的代码审查、代码浏览工具以及Phabricator和公司其他工具的集成、与开源社区的合作。说实话我以前从没有接触过类似的工作内容正是“Move Fast and Break Things”这种工作方式让我能够快速行动、快速迭代、持续学习最终顺利完成各项任务。
当时,我们三个人就支撑起了几千名开发者使用的一个核心开发工具,其中涉及了开发、测试、运维,效率之高可见一斑。这,正是工程师文化为我们带来的个人能力成长,和整个公司研发效能的提高。
## 小结
Facebook的工程师文化简单来说就是黑客之道指的是能够打破常规突破界限完成任务的做事方式其特质可以概括为优化无止境、持续进步、代码为王、能力为王。
正是这些特质帮助Facebook迅速发展业务、实现创新从而达成公司愿景。所以Facebook一直在文化宣传、流程和工具等方面推动其落地效果确实非常好实现了公司成功和个人成长的双赢。
我很感激在Facebook的这段经历不断突破极限的黑客精神对我影响非常大。
在工作和生活中,我们都会面对一套现有的规则,告诉我们要做什么不要做什么,并认为我们感知到的这套规则就是事实真相。但其实,这套规则背后隐藏的另一套规则才是真正的规则。
认识到这一点,我们就能突破现有规则,以非常规的方式去完成一件事儿,也就是我们所说的创造奇迹。而黑客精神,正是突破感知规则,寻找真实规则的精神。
## 思考题
在MySQL的案例中“要尽量快地去做事儿不要担心搞砸”只是Move Fast and Break Things 的一种理解。你知道这句话更常见的一种理解是什么吗?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,152 @@
<audio id="audio" title="34 | Facebook工程师文化实践三大支柱之一做感兴趣的事" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/4f/11/4fc265ac763243eb4df124f8fc8a0311.mp3"></audio>
你好我是葛俊。今天我来和你继续聊Facebook的工程师文化及相关管理实践。
在上一篇文章中我与你详细介绍了Facebook工程师文化的核心黑客之道并给出了一些落地实践。从这些实践中我们可以体会到这种工程师文化推行得非常成功已经融入了Facebook的血液中。
也正因为如此Facebook的工程师文化大大激发了员工的创造性、生产力和凝聚力对整体的研发高效能和良好的个人成长环境起到了关键作用。
与工程实践等方法论一样,知道好的实践还远远不够,我们更需要弄清楚的是,这些实践背后的原则,才能把它应用到自己的公司和团队。
所以在接下来的三篇文章中我会与你系统分析Facebook是如何真正落地工程师文化的。同时我会结合我在其他公司、团队推行工程师文化的实战经验与你分析落地工程师文化的切入点以及需要注意的问题。
我希望这样的讲述思路,能够帮你更深入地理解这些实践和原则,将工程师文化更顺畅地引入自己的团队,提高研发效能。
## Facebook工程师文化落地的三大支柱
在讨论Facebook工程师文化实践落地的how之前我们先看看why吧。
我们首先需要明确的是:归根结底,**推行工程师文化的目的是,提高开发人员的积极性和创造性,从而更高效地进行软件生产,在竞争中取胜**。而要最大化地激发开发人员的自我驱动能力,根本出发点是,开发者这个群体的特性。
**每一名开发者首先都是知识工作者**。管理学大师彼得 · 德鲁克Peter Drucker在《卓有成效的管理者》中说“我们无法对知识工作者进行严密和细致的督导我们只能协助他们。知识工作者本人必须自己管理自己自觉地完成任务自觉地做出贡献自觉地追求工作效益。”从这个角度来看每一个知识工作者都是管理者。
也就是说,要提高开发者的效率,强管理不是办法,根本之道在于激发他们的内驱力。正因为如此,**有效激励员工的积极性,正是硅谷高效能公司文化的共同点**。
怎样才能有效激发开发者的积极性呢在我看来Facebook主要关注以下三个原则
- 让员工做感兴趣的事;
- 让员工拥有信息和权限;
- 用绩效调节员工和公司方向的一致性。
员工能做自己喜欢的事儿,并有施展拳脚的空间,自然能最大限度地激发积极性;再加上绩效的调节,能够让团队成员的积极性与公司利益更有效的对齐,从而最大化员工积极性带来的成果。
所以说这三点就是Facebook工程师文化落地的三大支柱。
接下来,我们就先看看第一个支柱,让员工做感兴趣的事儿的一些具体实践吧。
## 让员工做感兴趣的事
我首先要说明的是,这里的感兴趣,不只是狭义地觉得任务有意思,而是开发者对工作的整体状况感兴趣,包括项目前景、技术栈、挑战性、团队成员等。
有研究证明如果任务有意思、有挑战同时又是加以努力就能完成的员工的engagement和满意度就会非常高。那么让团队成员灵活地选择岗位和转岗就成了一个绕不开的话题。
但,团队成员灵活选择岗位和转岗,在短期内会直接给公司,尤其是小团队带来负面影响。另外,如果公司比较小的话,每个人都很关键,自由转岗更难实现。
接下来我们就通过几个实践看看Facebook是如何用好这把双刃剑的吧。Facebook让员工做感兴趣的事包括了入职、日常工作和转岗这3个场景。
其实这3个实践也并不是在Facebook成立之初就都有的而是在不同的发展时期引入的。
### 第一个场景:入职时
在Facebook新员工入职时都要参加Bootcamp一方面可以帮助员工熟悉公司产品、研发流程和工具另一方面可以帮助员工灵活选择岗位。
**Bootcamp的具体操作是**:几乎所有的工程师在入职时,都没有明确会到哪一个团队工作。进入公司的前六周,新员工会先到一个特殊的区域办公,并从任务池中挑选任务工作。
这个任务池中的任务,是公司各个产品团队放进来的,一般是难度不太大、优先级不太高,但又能让新员工快速了解业务的任务。新员工通过完成任务来了解产品和流程,并通过沟通了解这个产品团队的成员。
在Bootcamp结束时新员工和感兴趣的团队主管沟通决定最后加入哪个团队是一个双向选择的过程。不过因为公司业务一直在扩张很多团队都缺人而且这些新员工的素质普遍不错所以一般都是卖方市场绝大部分新员工都能进入自己感兴趣的团队。
让新员工选择并进入自己感兴趣的团队,可以极大提高其积极性,还可以在员工间建立关系网,提高凝聚力,长期收益非常可观。但,这个实践的弊端是,有些团队可能一直招不到合适的成员,在短期内影响产品进度。
所以说,**这个实践比较适合有一定规模并发展较快的公司**因为新员工多容易调节些。Facebook也是在2008年员工数接近1000时才开始实施的。如果你的公司也具备这些特点可以考虑尝试Bootcamp。
如果你想了解Bootcamp更多细节的话可以参考[这篇文章](https://www.businessinsider.com/inside-facebook-engineer-bootcamp-2016-3)。
### 第二个场景:日常工作
在日常工作中提高员工工作兴趣的一个实践是Hackathon。
**Hackathon有一个有趣的原则是鼓励大家尽量不要做与日常工作直接相关的项目而是做一些感兴趣的项目**。所以我们在做Hackathon的时候都是把它当作一个好玩的事儿而不是当成任务来完成。
Hackathon在开始的时候还有一些好玩儿的仪式。组织者和参与者会敲锣打鼓在公司里做一个游行才正式宣布Hackathon开始。另外Hackathon一般会在公司的餐厅进行。餐厅会被摆放些桌椅、提供些小零食被整理得很舒适适合三四个人的小团队讨论问题。
值得一提的是Hackathon并不会明确要求做出可落地的产品但有趣的是大家感兴趣的项目往往会和工作相关所以实际上产生了很多很有价值的项目。在上一篇文章中我也提到了Facebook最成功的产品中有些就是来自Hackathon除了时间线Timeline、聊天、视频、点赞按钮外更典型的是Image Tagging和the Like Button(大拇指)。
总结来说Hackathon没有强制的业务目标做不出东西也没关系所以大家都很放松抱着创造一个新东西的心情去做事很爽。
在落地实践方面Hackathon不涉及转岗所以比较容易实施。也正因为如此国内不少公司都尝试过这条实践也取得了不少成果。但需要注意的是在实施时要注意些细节否则会事倍功半。
### 第三个场景:转岗
在转岗上Facebook采用的是Hackamonth即离开当前工作岗位去另一个团队全职工作一个月。每个员工在一个团队工作满一年后都可以参加这个活动。
跟Bootcamp类似Hackamonth也有一个任务池。每个团队都会把一些一个月左右即可完成的、独立的项目放入这个任务池里。有兴趣参加Hackamonth的员工在这个池子中寻找感兴趣的任务并跟新团队负责人确认之后就可以和自己的主管提出参加Hackamonth的要求。
接下来,你需要和主管沟通做好安排,以确保这一个月,原工作岗位上的任务有人处理。安排妥当后,你就可以进入新团队,并直接到他们的办公区工作了。
Hackamonth结束后你可以决定加入新团队还是留在原来的团队当然前提是你想加入的团队愿意接收你。这实际上是一个通过做项目的方式来让员工和他感兴趣的团队互相了解并双向选择的过程。
另外Hackamonth还提供了更进一步的灵活性。在做Hackamonth的时候如果你觉得这个团队不是很满意你也可以私下接触其他团队。如果双方觉得合适的话等Hackamonth结束后你可以选择加入这个团队。
Hackamonth对提高开发人员的工作兴趣有两大好处
- 一是,开发人员可以比较自由地转岗,而且大概率能够找到感兴趣的项目;
- 二是即使最终没有转岗也可以通过这一个月的工作转变减少Burnout的感觉。
我有一个同事在Facebook呆了五年参加了三次Hackamonth但每次都回到原来的组。用他的话说就是去度一个月的假。是不是很有意思呢
但采用Hackamonth的弊端是会直接影响原团队的工作进度更容易对公司和团队造成短期伤害。与Bootcamp相比Hackamonth更不适合小公司。Facebook是在2011年员工数达到几千时才开始采用Hackamonth政策的。
在Facebook只要你在一个团队工作满一年了即使团队主管不情愿也没有权利阻止你去参加Hackamonth。可见Facebook在执行Hackamonth政策上再次果断选择了长期利益。
以上就是Facebook为达成让员工做感兴趣的事这个目标在入职时、日常工作和转岗这三个场景下的3个重要实践。
接下来我再和你分享些我在国内一家公司落地Hackthon的具体经验吧。
## Hackathon落地经验
当时我所在团队的执行力非常强但员工的自我驱动不够。考虑到对公司和团队的影响我权衡之后决定引入Hackathon。
在引入Hackathon时我采用了和Facebook类似的方式举行时间为周五和周六下周一下午在团队内部进行演示和评选。
这样一来,只会占用一天多一点的工作时间,大家还可以选择周日继续进行。通常情况下,大家虽不会通宵,但也会做到比较晚,并且周日还会持续。
在这之前公司其他团队也尝试过Hackathon但效果都不太好。所以这次的Hackathon我制定了两大规则
- 明确要求Hackathon的目的是Have Fun不追求产品落地尽量不做与工作直接相关的任务。
- 评选结果时,由团队成员投票决定优胜者,而不是由专家或者管理层评选。
对这两个原则,我的主管和人事部门是有些怀疑的,但在我的坚持下还是执行了下来。
这次Hackathon下来效果出奇得好
- 大家热情高涨80人左右的开发团队基本可以产生12个项目
- 虽然没有要求项目落地,但还是产生了很多有意思、有价值、可落地的项目。
比如一次Hackathon时3个同事做了一个卫生间管理系统登录网页即可查看办公楼的卫生间占用情况。这个项目既解决了大家的痛点又很有意思所以高票当选第1名。
再比如一次Hackathon时几个运维同事做了一个ChatOps的项目。他们开发了一个聊天机器人放到聊天室中在产品发布上线时实时、自动发布部署进展开发人员还可以直接询问聊天机器人当前的部署状态。这个项目很大程度上解决了部署上线时团队的沟通不畅问题所以成功落地到实际工作中。
**这种形式的Hackathon在结束后仍对团队有正向影响**:一是,团队氛围更活跃、成员间衔接更紧密了;二是,大家对技术更感兴趣,也更愿意讨论用技术实现各种项目的可能性了。
总结来讲几乎所有包含开发人员的团队都可以尝试Hackathon但为了保证效果需要注意些细节尤其是上面我提到的两个规则。
## 小结
今天我首先和你介绍了Facebook工程师文化落地的三大支柱让员工做感兴趣的事、让员工拥有信息和权限以及用绩效调节员工和公司方向的一致性。
然后在让员工做感兴趣的事方面我从入职、日常工作和转岗三个场景和你分享了Facebook的Bootcamp、Hackathon、Hackamonth这三个实践。这些实践都对提高员工内驱力有非常关键的作用。
在这三个实践中Bootcamp和Hackamonth比较适合规模较大千人左右或者以上且发展较快的公司。如果你的公司符合这样的特点可以考虑落地这两个实践并根据实际情况做一些调整。比如引入Bootcamp后如果有一些冷门团队新员工都不愿意去怎么办呢一个可能的办法是对这些职位进行定向招聘专门寻找感兴趣的员工。
而如果你的公司不具备这些特点的话可以尝试Hackathon这个轻量级的实践。Hackathon花费的时间不多但执行合适的话效果会非常好。
除此之外,作为技术管理者,我的建议是,在安排日常工作时可以多考虑团队成员的意向,尽量在条件允许的情况下,提高员工兴趣和任务的吻合度。
## 思考题
在你的公司或者团队,有什么措施让员工尽量做自己感兴趣的事吗?
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,149 @@
<audio id="audio" title="35 | Facebook工程师文化实践三大支柱之二拥有信息和权限" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/55/09/550dddbc60abf84789d40bf7c03aae09.mp3"></audio>
你好我是葛俊。今天我来和你聊聊Facebook工程师文化的第二大支柱拥有信息和权限。
在上一篇文章中我与你介绍了Facebook通过在入职、日常工作、转岗中的一些实践来提高开发人员对所做工作的兴趣。有了兴趣之后接下来就是提供空间让大家能够施展拳脚在自己感兴趣的工作中自由发挥、充分发挥也就是Facebook工程师文化的第二大支柱了。具体来说第二个支柱包括让员工拥有信息和拥有权限两个子原则。
在我看来Facebook在这两个原则上的实践对国内公司和团队有很大的参考价值。为什么这么说呢因为**我接触到的国内公司管理都相对保守**可以从与Facebook的具体实践对比中汲取经验提升公司整体的研发效能。
在拥有信息方面,国内公司一般过于严格。信息的不透明,会降低员工的主人翁意识,并会因为信息不通畅导致工作效率和有效性的下降。所以,在我看来,国内公司还可以在信息方面做得更透明些。
在权限和信任方面,我接触到的国内公司,大都偏向于不信任的管理方法。这种方法难以让员工有主人翁的感觉,导致公司和员工互不信任。在这种情况下,又怎么期望员工能积极地为公司创造价值呢?
我始终认为,国内的开发人员和硅谷的开发人员一样,都有很强的创造性,能力也没什么大的差别;只要能给他们创造好的环境,提供信息并给予信任,就可以更好地激发他们的内驱力,从而提高公司和团队的研发效能。
接下来我们就具体看看Facebook是怎么做的吧。
## 让员工拥有信息
作为知识工作者,拥有信息,是我们能够做出正确判断的一个必要条件。但信息的开放具有两面性,如果暴露过多的敏感信息,势必会对公司造成些负面影响。
**面对这样一把双刃剑Facebook的态度是考虑风险但并不是一味地避免风险而是权衡公开信息的利弊在确保基本安全的前提下尽量实现信息透明化。**
接下来,我们一起看三个具体的案例吧。
### 案例1代码的共享
在Facebook几乎所有的代码都是全员共享开发人员可以非常方便地查看这些代码。这跟国内很多公司的情况差别非常大。
我在和很多国内公司的管理层谈到Phabricator时对方的第一个问题通常就是如何管控代码仓的权限。比如如何让前端开发人员看不到后端的代码如何让后端开发人员看不到算法的代码等等。
但其实,**代码的共享可以在开发、调测时节省很多沟通成本,大幅提高效率**。比如某个前端开发人员发现后台API返回值不符合预期就可以自己去查看对应的代码即使他没能自己找到问题也可以在和后端开发人员沟通时提供有价值的信息。
但如果他看不到后端代码的话,就只能直接求助于后端开发人员,一方面会浪费后端开发人员的时间(有可能这个问题前端人员就可以解决),另一方面双方的沟通效率也会因为信息不对称而大打折扣。
国内公司大多对代码权限控制得非常严格这跟国内的IP保护不力有直接关系。不过在我看来国内的代码管理还是太过严格甚至有些不分青红皂白就对所有代码进行严格控制的情况。这不但会降低开发人员的工作效率还会降低团队之间的信任。
所以综合考虑,**我们应该在可能的范围内,尽量放宽对代码的管控,对于那些即使泄露出去也没有太大关系的代码,要放松管控。**这样做风险不大,但收益却可能会很大。
### 案例2看板Dashboard
在Facebook很多信息都会展示在公司墙上的显示屏上包括很多业务的实时监控数据甚至还有少量在其他公司认为比较敏感而不愿意显示给员工的信息比如某些敏感服务的实时数据。此外很多团队还会制作自己团队的看板去显示需要关注的信息。
一些敏感数据泄露出去,的确会给公司造成些许负面影响,而且确实曾经出现过信息泄露。所以,有员工就问扎克伯格,**为什么要暴露这些信息**。
扎克伯格的回答是:这些信息泄露确实会造成少量负面的影响,但这些信息可以极大地方便开发人员的日常工作,能够更好给用户提供价值,比如看板上的某一条信息可能在你做某个决策时起到关键作用,所以权衡利弊,我们决定开放这些信息。
这个回答,恰恰佐证了公司认同信息对激发开发人员研发效能的巨大作用。
关于信息开放,在国内比较容易落地的部分是,把可以显示的信息用看板显示出来。比如,线上服务使用情况可以拉近员工与用户的距离,让员工更直观地感受到业务的价值,从而提高工作积极性;又比如,当次发布进展的相关数据,可以让开发人员对当前进展更有掌控感,对交付更有紧迫感。
**我们在落地看板展示时,需要注意两个问题:**
- 一是,不要做给外人看,而是应该用来激励内部员工;
- 二是不要展示个人负面排名比如Bug数这种公开的负向激励方式容易降低团队成员的积极性。
### 案例3使用Wiki来记录信息
Facebook在公司内部大量使用Wiki来记录信息很多对流程要求不严格的信息都用Wiki来记录包括部分设计文档、团队成员列表、新员工入职手册、个人笔记甚至有些团队的OKR也在上面。
这样一来我们在日常工作中遇到问题时首先就是到内部Wiki上查找而且一般都能找到有用的信息。这就使得绝大部分员工愿意在Wiki上添加信息包括一些笔记、心得等。于是形成了一个良性循环Wiki系统的内容越来越丰富对开发人员的帮助越来越大。
但,我见到的很多国内公司,很多信息都是口口相传,或者是藏到某些文档里,需要花费大量的时间在查找信息上。
在我看来在公司内部用内部公开的Wiki来记录信息风险比较小而且可以大幅提升效能所以比较容易落地。**作为管理者我们可以考虑建立一个全公司的Wiki并采取措施鼓励团队进行知识方面的共享。**
这里的**一个技巧是**,把权限设置为默认公开,对需要进行权限控制的内容再特殊处理收紧权限。这跟默认保密,有需要再特殊处理放开权限的操作相比,看似相差不大,却是在传递支持信息开放的基本态度,效果非常不错。你也可以试试这个方法。
## 让员工拥有权限
在能做感兴趣的事,又拥有了信息之后,最后一条就是让员工能够拥有权限去做事儿。这里,我也与你分享三个具体的案例吧。
### 案例1对于商业软件先购买再获取授权
Facebook对商业软件的态度是相信每个开发人员会从公司利益出发权衡一个软件是否需要购买如果需要可以先斩后奏。具体的案例你可以回顾下[第1篇文章](https://time.geekbang.org/column/article/125840)中的相关内容。
其实,国内有些公司也已经采用类似方式了,员工可以在一定数额下自行决定是否购买。我在跟这些公司的员工聊到这个政策时,能够明显感觉到他们的开心和对公司的认同感。
一般情况下,员工不会擅自做离谱的决定去购买某一款软件,所以这个操作比较安全,你也可以试试。
### 案例2鼓励代码上的互相贡献
在Facebook除了几乎所有的代码对全员公开外公司还鼓励开发人员互相修改和提高对方的代码。比如前端开发人员发现后端API返回结果不符合预期时如果他能识别出后端代码的问题便可以去直接去修复然后提交一个PR给后端开发人员对方接受之后即可入库。
又比如你发现某一个产品的某一个功能有Bug也可以直接修改只要通过PR和测试就可以上线。
这在内部工具方面尤其明显也是Facebook的内部工具超级强大的一个重要原因。因为内部工具代码上线的风险比较小所以很多人在看到工具的问题时会上手去进行提高甚至自己开发一些新工具。
一个具体的例子是Phabricator在开源之前很多功能和改进都是非内部工具团队的开发者贡献的你可以再回顾下[第15篇文章](https://time.geekbang.org/column/article/140657)中的相关内容)。
如果你要落地这个实践的话,我有两个建议:
- 一是,先从风险小的代码试点,鼓励互相贡献;
- 二是,对这种非本职却对公司有贡献的工作,应该体现在绩效考评中。
### 案例3提供宽松的容错环境
Facebook对待事故的态度是更关注吸取经验教训以及将来如何预防而不是追责。这样的容错环境能够让开发人员放开手脚最大限度地释放自己的创造力和潜能保证公司的持续快速发展。
在[第33篇文章](https://time.geekbang.org/column/article/162869)中我提到在持续进步方面Facebook提供的宽松容错环境只要不是故意犯错或者重复犯相同的错一般不会被追责。Move Fast and Break Things就是一个有力的证明。
我刚进入Facebook时曾见过一个事故有一个开发人员错误操作把公司最大的SVN代码仓给搞挂了公司找来两个SVN专家花了一天半的时间才把整个代码仓恢复。这给公司造成了很大损失但这个开发人员并没有因此被裁掉。还有些人因为误操作造成线上事故影响了Facebook网站的功能但只要不是故意犯错或者重复相同的错也基本不会被追责。
如果你想落地宽松的容错环境这个实践的话我推荐你尝试Facebook的SEV复盘。我在国内公司实践过SEV效果不错。
你可以再回顾下[第4](https://time.geekbang.org/column/article/128867)和[第20](https://time.geekbang.org/column/article/144364)篇文章中关于SEV的相关内容以及出现问题之后到底应不应该追责、应该怎样追责的问题。
“软件先购买再获取授权”“鼓励代码上的互相贡献”以及“容错文化”三个实践我都在国内一个50开发人员左右的创业公司引入过效果都还不错。引入时注意些上面提到的问题就可以了。
## Facebook之外的落地经验
接下来,我再与你分享两个,我在其他公司采取的让团队成员拥有信息和权限的实践吧。
### 案例1促进信息上下流通的实践
在Facebook每周五都会举办一个内部的Q&amp;A员工可以通过现场或者线上提问的方式问扎克伯格以及高管们任何问题。扎克伯格和其他高管都非常坦诚尽量把可以公开的信息都坦诚相告对公司信息上下流通的效果非常好。
离开Facebook之后我在一个团队内采用类似Facebook的方式进行了Q&amp;A使用尽量坦诚的、开放的问答形式不说套话。这个团队的特点是执行力很强但氛围有些沉闷。在坚持了两三次Q&amp;A之后气氛逐渐活跃大家逐步敢问一些敏感的问题了。
事实证明,开发人员的思维非常活跃,也都很喜欢用活跃的方式进行沟通,以获取到更多的信息。这样做的好处是,加强了员工对公司、对团队的主人翁的精神,进而提高整体的研发效能。
**落实Q&amp;A还有一个小窍门**是,大家可以用匿名写纸条的方式去提问。这个方法,在国内效果更好,很多人更愿意用这种方式去问一些敏感的问题。
### 案例2信息共享的相关实践
这个实践是我在一个创业公司采用Wiki的方式来推动全公司范围内的信息共享内容权限默认设置为公开只有特殊情况才可以设置为只对某些人公开。
落地这个实践,的确是一个长期的过程。我见到,国内很多开发人员,都不太习惯做分享,所以**我推行Wiki时的两个方法是坚持和以身作则。**
在推广Wiki的第一个月90%的新内容都是我写的后来大家逐渐看到了这种信息共享的好处也看到了我的坚持就开始主动向Wiki中增加内容。半年后我的贡献降到了10%。
## 小结
今天我与你分享了Facebook工程师文化落地实践的第二大原则让员工拥有信息和权限。
因为软件开发是知识性工作所以拥有信息是高效开发的前提。Facebook在这方面的实践包括代码共享、看板和公司范围内公开的Wiki等。
而让员工拥有权限和信任可以让员工以主人翁的感受去施展拳脚最大限度地激发其内驱力。这方面Facebook的实践包括信任员工进行一定额度下的软件自行采购、鼓励互相贡献代码以及建设宽松的容错环境等。
从我个人在国内推行文化的实践经验来看,国内大多数软件公司,是适合让员工拥有更多的信息和权限的。而且我也看到,国内越来越多的公司意识到了这一点,在逐渐放开信息和权限,从而提高员工的内驱力和团队的研发效能。
我相信,我们会逐步找到更合适自己环境的平衡点,打造高研发效能的公司。
## 思考题
信息开放的确会导致信息泄露。今年10月份Facebook出现了一次Q&amp;A被录音并泄露的事件你知道扎克伯格是怎么处理的吗你还能想到更好的处理方式吗
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,149 @@
<audio id="audio" title="36 | Facebook工程师文化实践三大支柱之三绩效调节" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/cf/2f/cf7ec778d0b8bf2518d69a4970a57f2f.mp3"></audio>
你好我是葛俊。今天我来和你聊聊Facebook工程师文化的第三大支柱绩效调节。
在前面两篇文章中我与你详细介绍了Facebook如何通过让员工做感兴趣的事以及让员工拥有信息和权限来很好地激发员工的内驱力。但归根结底工程师文化是为公司的业务发展而服务的。
那么当员工的工作积极性被充分调动起来之后Facebook是怎样让员工的工作方向与公司的发展方向对齐的呢这把利器就是绩效调节。今天我们就具体看看Facebook是如何做绩效调节的吧。
绩效管理是一个很大的话题其中Facebook帮助员工调节方向的管理方法主要包括两个
- 一个持续反馈的方式,也就是说主管要随时给员工反馈,帮助其调节工作的方向和方式。
- 一套360度绩效考评系统进行每年两次的总结性绩效考评。
**关于持续反馈一般是在一对一1-on-1沟通中给反馈**。一对一沟通是硅谷软件公司常见的一种管理沟通方法,一般每周一次每次半小时,固定时间,由主管和下级,或者和下级的下级做一对一沟通。
这个会议的主要作用是,从下往上的信息收集和从上往下的信息传递,是与员工沟通近期工作中值得肯定或需要调整内容的一个非常好的渠道。如果你想详细了解一对一沟通,可以参考“[浅析一对一沟通](https://www.infoq.cn/article/analysis-of-one-to-one-communication)”这篇文章。
比如,我在[第13篇文章](https://time.geekbang.org/column/article/138389)中提到,开始时我在代码审查上做得不太好,主管就及时在一对一沟通中指了出来,让我能够迅速调整方向。
而360度绩效考评系统对帮助员工对齐方向起到了关键作用。今天这篇文章我就与你具体说说它的原则、问题以及落地实践吧。
**360度绩效考评系统的核心是通过收集同事间的反馈来评价员工对公司的贡献。**整个过程尽量关注公司的整体利益而不是小团队的局部利益,尽量做到收集客观具体的实例来评估贡献,而不是简单收集客观数字。整体来讲,这个方法可以比较客观、公正地评价员工对公司的贡献。
相比起来,国内的绩效管理政策,真正使用同事间反馈的很少,更多的是主管的评价。这种方式的好处是,容易产生强执行力,但缺点是容易出现维上、内斗的情况,从而引发较多内耗。
所以通过介绍Facebook这一套不太一样的绩效管理系统我希望能给你提供一个新的视角从一些新的角度考虑如何更好地使用绩效考评这个工具来提高团队的研发效能。
首先我们来看看Facebook的360度绩效考评系统具体是怎样的吧。
## 360度绩效考评系统
Facebook的360度绩效考评系统因为要通过收集同事间的反馈来评价员工对公司的贡献。所以请谁来评价你的工作、评价内容是什么、如何定结果以及如何应用结果就是4个关键问题。
### 由谁给你写评价?
这套考评系统之所以称作是360度就是因为反馈来源的覆盖面广尽量收集每一个能够评价被考评对象贡献度的同事的反馈。这些同事具体包括
- 被考评对象自选1~2个同事写评价
- 主管指派1~2个同事写评价
- 被考评人员自评;
- 主管的评价;
- 如果被考评者是管理者,他的所有直接下级都要给他写评价。
### 评价内容是什么?
对其他同事的评价,以及自评,核心问题就两个:
- 这个同事在上一个绩效考评周期中对公司最大的1~2个贡献是什么请用事例说明。
- 这个同事在上一个绩效考评周期中,如果在什么方面采取不同的做事方式,可以对公司有更大的贡献?
当然,对管理人员的评价还有一些管理相关的问题,个人自评还会有申请级别晋升的部分,对下级评价会有是否满足级别要求等额外内容。
### 如何定结果?
评定结果的方式是,汇总对被评估对象的所有评价,并以此为依据,由他的主管和同级的其他主管一起讨论决定绩效。
### 如何应用结果?
绩效考评结果包括:是否涨级、工资底薪是否要调节,以及奖金金额。这,与其他公司相比没什么特别的。但,值得一提的是,奖金数会综合公司年度表现、团队年度表现以及个人年度表现,使用公式计算得出,而且员工可以看到自己的计算公式,使得奖金数额的计算更加透明。
如果你想深入了解这个360度绩效考评系统可以参考这两篇文章[文章1](https://www.businessinsider.com/facebook-hr-chief-explains-its-performance-reviews-2016-2)、[文章2](https://www.quora.com/What-does-Facebooks-performance-review-process-look-like/answer/Molly-Graham)。
讲完了360度绩效考评的四个关键问题接下来我们再看看这个考评系统的两个重要原则
- 绩效考评中关注贡献度impact
- 提高绩效考评的客观性和公正性。
## 原则一:绩效考评中关注贡献度
360度绩效考评系统要考察的两个核心问题实际上就是表现的“好”和“坏”两部分。这是收集反馈最常见的内容没有什么特别的地方。但Facebook的考察特别强调以下两点
- 问题描述强调以**贡献度为根本衡量标准**。这里并没有提到这个人的技术水平怎样、开发效率怎样。因为技术和效率都是为贡献度服务的,一个员工即使技术再厉害,但对公司的贡献不够的话,绩效也不会好。
- **衡量贡献的维度,是对公司的贡献,而不是对团队的贡献**。因为,对团队的贡献最终也要以对公司的贡献来衡量。这种方式可以避免团队的局部利益和公司的整体利益不一致的现象,让员工更关注公司的整体利益。
这两点,就能够让绩效反馈的收集专注于员工对公司的贡献,让每个员工的努力方向都和公司的方向对齐。
## 原则二:提高绩效考评的客观性和公正性
绩效考评的一个重点是,客观性和公正性。客观公正的评价可以让员工继续努力,反之则会极大地伤害士气,导致员工工作效率下降,甚至离职。所以,很多公司尝试使用数字化的方式来尽量提高绩效考评的客观性。也正因为如此,很多公司希望用数字化来衡量员工的开发效率。
但,由于软件开发行业的灵活性,用指标来衡量生产效率的效果不太好。你可以再回顾下[第2](https://time.geekbang.org/column/article/126447)和[第3](https://time.geekbang.org/column/article/128151)篇文章中的相关内容。
所以Facebook采用了这种通过收集人的主观反馈的方式来进行绩效考评。这一点看似和数字化管理背道而驰但这就是由软件开发行业的特性而决定的。所以从实际结果来看这种方式的效果反而很好。
**关于提高绩效考评的客观性和公平性Facebook的实践主要包括以下4点**
- 在评价中,每一个对公司的贡献,都一定要有事例支持,有数字的话就更好了。
- 员工给其他同事评价的质量,也是他自己绩效的重要组成部分,来保证每个人都会认真填写尽量客观的评价。以我为例,在第一次给同事写评价时,写的比较简短、空洞,结果被主管两次打回重写。后来,我每次写绩效考评时都特别认真。
- 意见的收集,覆盖的人群比较全面,包括同事、自评、上下级。在给自己选择评价人时,要选择对自己工作比较了解的,从而能作出客观评价,而不能随便选择和自己关系好的同事。
- 最终决定绩效结果的讨论中,虽然主管的评价比重较大,但其他同事提供的评价占比也很可观。而且,在最终决定绩效的讨论中,同事的评价也是参会者可见的,尽量避免主管一言堂的情况。
以上就是Facebook使用360度绩效考评系统时遵循的两条重要原则。通过这两条原则360度绩效考评系统做到了尽量客观公正评价员工对公司的贡献并依此进行年终奖和股票的发放从而提高了员工的努力方向和公司方向的一致性。
当然,这个系统也有一些缺陷。
## 360度绩效考评系统的问题
360度绩效考评系统依赖于人的主观反馈所以很容易出现评价不客观的问题。虽然Facebook采取了上面提到的原则2中的4条实践来避免这样的问题但仍会出现一个难以避免的情况即人际关系好的人更容易得到好的评价。人之常情关系的好坏会影响你对他人评价的客观性不愿意对关系好的个人或和团队提出批评进而导致一些问题暴露得不及时。
不过从我的个人经验看这个问题并不太严重。如果你考虑采用360度绩效考评系统的话可以有意识地关注是否出现了问题被隐藏的情况。
接下来,我再与你分享下我在其他公司落地这套系统的经验吧。
## 绩效考评落地实战360考评系统
360度绩效考评系统的确对软件开发公司有巨大好处所以我离开Facebook之后在国内的一家公司引入了这套系统。具体的引入步骤包括
1. 制定职级系统,并详细描述每一个级别的技术预期和贡献度预期。
1. 对公司的现有员工进行级别评定。
1. 对整个考评系统,进行文档化、正式化,并在公司内宣讲。
1. 正式使用这个制度,进行绩效考评。
整个流程下来效果还不错。虽然有少数员工不满意,但相比于之前主要由主管直接决定绩效的方式,普遍反馈这一套系统的确更客观公正。
在我看来,在推行这个系统时,有以下三点需要特别注意:
- 宣讲时,强调客观、公正评判个人对公司的贡献度,是采用这种评定方式的原因。
- 执行中关注Facebook为保证客观公正性的四条实践。
- 确保反馈的保密性。为保证大家愿意写出一些真实想法,不能让员工知道对方对自己的评价。
值得一提的是,这个系统比较正规,所以有些员工会觉得太重。但在我看来,**一套公平的绩效考评系统,能够大大激励员工朝着公司的前进方向而努力,因此花费的时间绝对是值得的**。除非是只有几个开发人员的小公司,否则我都推荐你尝试这种绩效考评方式。而在几个人的小公司,即使评价系统还没有成型,也应该注意评价的客观公正性。
## 小结
今天我与你介绍了Facebook怎样使用360度绩效考评系统的调节来保证开发人员的努力方向和公司的方向保持一致从而最大化地应用工程师文化释放出来的巨大能量。这种绩效考评方式通过全面收集同事间的反馈来评定绩效并通过一系列规则提高客观性和公正性驱动大家在工作中关注公司的全局利益。
虽然在执行过程中偶尔会出现因为人的主观因素而不能客观反映事实的情况但从Facebook员工的反馈来看这套系统总体的执行效果很不错。它相对客观公正而且能够驱动员工以公司整体利益为重。这对一套绩效考评系统来说实属难能可贵。所以我推荐你尝试这套系统。
Facebook的工程师文化强调尽量去激发员工的内驱力。从这个点出发Facebook从让员工做感兴趣的事、让员工拥有信息和权限、用绩效调节员工努力方向这三大方面设计了很多对工程师成长有巨大好处又会给企业带来巨大收益的实践。
如果你是一个管理者可以以这些实践为参考并尝试引入一些轻量级的实践比如Hackathon、SEV流程、内部全员公开的Wiki系统等。
而如果你是个人开发者也可以从工程师文化中汲取养分用在自己的工作和学习中。比如Move Fast and Break Things、Focus on Impact、持续进步、代码为王等文化都对个人开发者非常有帮助。即使你所在公司没有这样的文化你也可以培养自己按照这样的方式做事。
今天这篇文章就是专栏正文的最后一篇了我已经从研发流程、工程方法、个人效能、管理和文化这4大方面与你讲述了提升研发效能的各种原则和实践。但学以致用才是学习的最终目的所以我希望你可以将这些知识应用到你的工作中真正提升团队和个人的研发效能。
写作专栏之余我也在创业。12月6~7日我的公司KodeRover会在上海举办一个10X 工程效能特训营帮助个人实操打造“DevOps as a Service”体系帮助企业提高研发效能、交付效能。通过这个特训营你可以实践我在专栏中讲述的关于全栈开发、效能度量、环境服务化、自助化等方面的方法论。
在这里我给订阅专栏的用户提供了一个福利报名推荐人填写“极客时间葛俊”个人报名3折优惠需审核携企业组团赠送一张个人免费票。
<img src="https://static001.geekbang.org/resource/image/0e/1c/0e6c7bf196a356737fdfcd22ec165a1c.png" alt="">
## 思考题
在360度考评系统中Facebook鼓励员工公开自己对别人的评价也确实有部分员工这样做了。你觉得这样做的利弊是什么呢
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!

View File

@@ -0,0 +1,43 @@
<audio id="audio" title="结束语 | 超越昨天的自己,享受成长的快乐" controls="" preload="none"><source id="mp3" src="https://static001.geekbang.org/resource/audio/0f/08/0f1b75847438b46942c26e886075aa08.mp3"></audio>
最近几年,我不止一次听人提到,学习是反人性的。乍一听起来,似乎有些道理,因为学习过程往往会伴随着汗水。但仔细一琢磨,我发现完全不是那么回事儿。
学习,的确需要克服内在的惰性和环境限制。但,这就是世界的客观规律,我们只有努力去克服这些困难,才能通过学习实现成长。而成长带来价值和喜悦,绝非安逸、舒适带来的简单快乐可以比拟。
**回想这5个月我创作《研发效率破局之道》专栏的过程也正是这样辛苦、有挑战但价值非常大。**
我写这个专栏的目的是总结自己15年的软件行业从业经历系统地分享我对高效研发的理解。而我的目标是根据研发活动的特点抽象出研发效能的模型而不是堆砌花里胡哨的各种研发实践。
同时,考虑到利用碎片化时间收听专栏音频的用户,我希望每一个模块、每一篇文章的知识点,都有严密的知识结构,让你学到的知识点不是零散的,而是能够连成线、织成网,实现碎片化时间的高效利用。
所以我创作这个专栏的思路是系统分析研发效能模型中的各种方法论并结合Facebook等高效能公司的具体实践以及我在其他公司落地的经验让你能够深入理解这些方法论的why、how和what进而活学活用真正提高个人和团队的研发效能。
为此我花了大量的时间去查阅资料、调研、整理和优化并针对不同主题进行不同维度的讲解针对方法论和文化主要是系统化和实战经验相结合而针对具体工具则尽量给出具体的示例帮你更直观地理解应用方法和技巧因此使用了不少的截屏、录屏、代码输出还在GitHub上专门创建了两个项目来进行演示。所以每一篇文章的耗时都超过了自己和极客时间编辑的预期。
**除了这些挑战和辛苦外,这个过程对我来说价值巨大。**
首先这些内容能够为国内公司研发效能的提升带来价值对我来说就意义重大。我切身体验过Facebook这样的高效能公司也体验过研发效能不那么好的公司特别清楚高效能可以带来公司业务发展和个人成长的质变。这对国内正在从野蛮生长到精耕细作转变的软件行业作用尤其巨大。
从留言反馈来看,我确实看到了这份价值。所以在这里,我要感谢你们的陪伴,你们的每一条留言,都是我坚持创作的巨大动力。与你们的沟通,是一件非常快乐的事情!也正因为如此,我几乎对每一条评论都做了详细解答。
除了能够产生价值外,**这个过程也在帮我不断提高自己**。
在我看来,学习包括三个层次:
- 第一层是被动学习,效果一般;
- 第二层是动手实践;
- 第三层是把知识教给别人,效果最好。
从专栏的创作中,我体会尤其深刻。为了保证逻辑的严谨性,我经常会从“你”的角度问自己一些问题,以此来发现盲点,然后做更深一步的调研。文章上线之后,你的提问也驱使我更进一步地思考。毫无疑问,这都加深了我对问题的理解。
所以,我希望你在学习了这些方法论和实践后,能够学以致用,提高自己和团队的研发效能,然后更进一步地,你能够把它们传播到你的团队中,影响到你的下级同事甚至是老板。这样一来,这些方法论和实践,就能产生更大的价值,而你对它们的理解也会更深刻。
虽然专栏已经更新结束了,但我们的交流仍会继续。如果你在实践这些方法论的过程中,有什么问题和想法的话,都可以直接留言给我,我会继续关注并回复你的留言。
“**超越昨天的自己,享受成长的快乐**”,是我的座右铭。每天进步一点点,除了能够产生价值外,学习和成长本身就能给我们带来巨大的快乐。这,也是我一直喜欢研发效能的重要原因。任何一个工作流程、工程方法都有提高的空间,我们需要做的,就是去发现这些可以改进的地方,然后对值得提高的部分进行优化。
其实,国内的软件行业,值得优化的地方比比皆是。我一直相信,国内软件研发人员的能力和创造性,绝不亚于硅谷那些高效能公司。只要我们的方向对了,并不断提高,就一定可以大幅提高团队和个人的研发效能,从而把时间花在最值得的地方。加油,我们一定可以!
最后,祝愿你在这个过程中,也能不断超越昨天的自己,享受成长的快乐!

View File

@@ -0,0 +1,10 @@
你好,我是葛俊。
到这里,《研发效率破局之道》这门课程已经全部结束了。我给你准备了一个结课小测试,来帮助你检验自己的学习效果。
这套测试题共有20道题目包括6道单选题和14道多选题满分100分系统自动评分。
还等什么,点击下面按钮开始测试吧!
[<img src="https://static001.geekbang.org/resource/image/28/a4/28d1be62669b4f3cc01c36466bf811a4.png" alt="">](http://time.geekbang.org/quiz/intro?act_id=133&amp;exam_id=287)