mirror of
https://github.com/cheetahlou/CategoryResourceRepost.git
synced 2026-05-11 04:04:34 +08:00
mod
This commit is contained in:
BIN
极客时间专栏/研发效率破局之道/.DS_Store
vendored
Normal file
BIN
极客时间专栏/研发效率破局之道/.DS_Store
vendored
Normal file
Binary file not shown.
125
极客时间专栏/研发效率破局之道/个人效能/21 | 高效工作:Facebook的10x程序员效率心法.md
Normal file
125
极客时间专栏/研发效率破局之道/个人效能/21 | 高效工作:Facebook的10x程序员效率心法.md
Normal 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条原则,包括抽象和分而治之、快速迭代,以及DRY(Don’t 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&keywords=kubernetes+patterns&qid=1570073661&sprefix=kubernetes%2Caps%2C335&sr=8-1)这本书;针对云原生(Cloud Native)开发,也有了业界比较认可的[12-factor原则](https://12factor.net/zh_cn/)等。将来必定还会有其他新的设计模式产生。比如,伴随着AI的逐渐成熟,针对AI的设计模式必定会出现。
|
||||
|
||||
所以,作为一名软件开发者,我们必须要持续学习。我之前在一家创业公司时,有一个刚大学毕业两年的同事,他有一个非常好的习惯,就是每天早上比其他同事早半个小时到办公室,专门来学习和提高自己。正是因为他的持续学习,使得他虽然工作时间不长,但在整个团队里一直处于技术领先的位置。你也可以借鉴这个方法,或者采用其他适合自己的方法来持续地提升自己。
|
||||
|
||||
## 思考题
|
||||
|
||||
1. 我今天提到的关于分而治之的实践,哪一条对你触动最大呢?同时,也和我分享一下你在工作实践中的感受吧。
|
||||
1. 你还知道哪些编程技术方面的高效原则和实践吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
159
极客时间专栏/研发效率破局之道/个人效能/22 | 深度工作:聚焦最有价值的事儿.md
Normal file
159
极客时间专栏/研发效率破局之道/个人效能/22 | 深度工作:聚焦最有价值的事儿.md
Normal 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)提出的,指的是在无干扰的状态下,才能专注地进行的专业活动。这样的活动能够使个人的认知能力发挥到极限,从而让我们创造出最大价值,成为一个不可替代的人。
|
||||
|
||||
就我个人而言,我也一直在想办法去聚焦最有价值的事儿,也读了不少GTD(Geting 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. 你觉得碎片时间,更适合碎片化学习/娱乐,还是拥抱无聊呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
198
极客时间专栏/研发效率破局之道/个人效能/23 | 效率工具:选对用对才能事半功倍.md
Normal file
198
极客时间专栏/研发效率破局之道/个人效能/23 | 效率工具:选对用对才能事半功倍.md
Normal 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+tab,Windows系统自带的是Alt+Tab或者Win+Tab。**关于程序启动**,macOS系统有Spotlight,Windows系统可以使用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( "ahk_class Vim" )
|
||||
{
|
||||
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中的具体设置方法是:
|
||||
|
||||
```
|
||||
"rules": [
|
||||
{
|
||||
"manipulators": [
|
||||
{
|
||||
"description": "Change ctrl to esc if pressed alone.",
|
||||
"from": {
|
||||
"key_code": "caps_lock",
|
||||
"modifiers": {
|
||||
"optional": [
|
||||
"any"
|
||||
]
|
||||
}
|
||||
},
|
||||
"to": [
|
||||
{
|
||||
"key_code": "left_control"
|
||||
}
|
||||
],
|
||||
"to_if_alone": [
|
||||
{
|
||||
"key_code": "escape"
|
||||
}
|
||||
],
|
||||
"type": "basic"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
**第四类工具是,具体的键盘**。开发者要经常输入,选择一款顺手的键盘就很重要了。键盘的选用,我看重的是手感和键盘布局。手感的话,机械键盘的确好一些,你可以根据自己的体验选择一款合适的。而布局的话,左右手按键分离较远的一般来说会好一些。
|
||||
|
||||
根据这些原则,我平时使用[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个方面外,你觉得还有哪些方面的操作比较频繁吗,有什么值得优化的地方吗,又有什么值得推荐的工具吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
334
极客时间专栏/研发效率破局之道/个人效能/24 | VIM:如何高性价比地学习VIM的实用技巧?.md
Normal file
334
极客时间专栏/研发效率破局之道/个人效能/24 | VIM:如何高性价比地学习VIM的实用技巧?.md
Normal 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的最后一行会显示’<,’>! ,表示由外部程序操作选中的部分。这时,我们输入nl -v 1000并回车。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/0d/7d/0dd3edaf5ca34ae776d1527fbbee547d.png" alt="">
|
||||
|
||||
VIM就会把选择的文本传给nl,nl在每一行前面添加序号,再把处理结果传给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技巧是什么呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
596
极客时间专栏/研发效率破局之道/个人效能/25 | 玩转Git:五种提高代码提交原子性的基本操作.md
Normal file
596
极客时间专栏/研发效率破局之道/个人效能/25 | 玩转Git:五种提高代码提交原子性的基本操作.md
Normal 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 <文件名>把需要的文件添加到Git暂存区,然后使用git commit命令提交即可。这个操作比较常见,我们应该都比较熟悉。
|
||||
|
||||
但在工作中,一个文件里的改动常常会包含多个提交的内容。比如,开发一个功能时,我们常常会顺手修复一些格式规范方面的东西;再比如,一个功能比较大的时候,改动常常会涉及几个提交内容。那么,在这些情况下,为了实现代码提交的原子性,我们就需要只把文件里的一部分改动添加到提交中,剩下的部分暂时不产生提交。针对这个需求,Git提供了git add -p命令。
|
||||
|
||||
比如,我在index.js文件里有两部分改动,一部分是添加一个叫作timestamp的endpoint,另一部分是使用变量来定义一个魔术数字端口:
|
||||
|
||||
```
|
||||
## 显示工作区中的改动
|
||||
> 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()
|
||||
|
||||
## 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) ## <-- 端口魔术数字变量化
|
||||
|
||||
```
|
||||
|
||||
这时,运行git add -p index.js命令,Git会把文件改动分块儿显示,并提供操作选项,比如我可以通过y和n指令来选择是否把当前改动添加到Git的提交暂存区中,也可以通过s指令把改动块儿再进行进一步拆分。通过这些指令,我就可以选择性地只把跟端口更改相关的改动添加到Git的暂存区中。
|
||||
|
||||
```
|
||||
> 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命令可以看到,我的确只是把需要的那一部分改动,也就是端口相关的改动,添加到了暂存区:
|
||||
|
||||
```
|
||||
> 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相关的改动仍留在工作区:
|
||||
|
||||
```
|
||||
> 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改动和端口改动:
|
||||
|
||||
```
|
||||
## 查看提交历史
|
||||
> git log --graph --oneline --all
|
||||
* 7db082a (HEAD -> master) Change magic port AND add a endpoint
|
||||
* 352cc92 Add gitignore file for node_modules
|
||||
* e2dacbc (origin/master) Added the simple web server endpoint
|
||||
...
|
||||
|
||||
|
||||
## 查看提交
|
||||
> git show
|
||||
commit 7db082ab0f105ea185c89a0ba691857b55566469 (HEAD -> 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把代码仓、暂存区和工作区都恢复到这个位置,从而不会丢失代码。
|
||||
|
||||
```
|
||||
> git branch temp
|
||||
|
||||
> git log --graph --oneline --all
|
||||
* 7db082a (HEAD -> 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相关改动和端口相关改动。也就是说,这个命令的效果,就是让我回到了对这两个改动进行提交之前的状态:
|
||||
|
||||
```
|
||||
> git reset HEAD^
|
||||
Unstaged changes after reset:
|
||||
M index.js
|
||||
|
||||
> git status
|
||||
On branch master
|
||||
Your branch is ahead of 'origin/master' by 1 commit.
|
||||
(use "git push" to publish your local commits)
|
||||
|
||||
Changes not staged for commit:
|
||||
(use "git add <file>..." to update what will be committed)
|
||||
(use "git checkout -- <file>..." to discard changes in working directory)
|
||||
|
||||
modified: index.js
|
||||
|
||||
no changes added to commit (use "git add" and/or "git commit -a")
|
||||
15:06:58 (master) jasonge@Juns-MacBook-Pro-2.local:~/jksj-repo/git-atomic-demo
|
||||
|
||||
|
||||
## 改动在工作区
|
||||
> 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)
|
||||
|
||||
|
||||
## 输出为空
|
||||
> git diff --cached
|
||||
|
||||
```
|
||||
|
||||
最后,我就可以使用上面介绍过的git add -p的方法把工作区中的改动拆分成两个提交了。
|
||||
|
||||
## 基本操作三:修改当前提交
|
||||
|
||||
如果只需要修改Commit Message的话,直接使用git commit --amend命令,Git就会打开你的默认编辑器让你修改,修改完成之后保存退出即可。
|
||||
|
||||
如果要修改的是文件内容,可以使用git add、git rm等命令把改动添加到暂存区,再运行git commit --amend,最后输入Commit Message保存退出即可。
|
||||
|
||||
## 基本操作四:交换多个提交的先后顺序
|
||||
|
||||
有些时候,我们需要把多个提交交换顺序。比如,master分支上有两个提交A和B,B在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来确认当前提交历史:
|
||||
|
||||
```
|
||||
> git log --oneline --graph
|
||||
* 7b6ea30 (HEAD -> 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
|
||||
|
||||
```
|
||||
|
||||
接下来,运行
|
||||
|
||||
```
|
||||
> 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 <commit> = use commit
|
||||
# r, reword <commit> = use commit, but edit the commit message
|
||||
# e, edit <commit> = use commit, but stop for amending
|
||||
# s, squash <commit> = use commit, but meld into previous commit
|
||||
# f, fixup <commit> = like "squash", but discard this commit's log message
|
||||
# x, exec <command> = run command (the rest of the line) using shell
|
||||
# b, break = stop here (continue rebase later with 'git rebase --continue')
|
||||
# d, drop <commit> = remove commit
|
||||
# l, label <label> = label current HEAD with a name
|
||||
# t, reset <label> = reset HEAD to a label
|
||||
# m, merge [-C <commit> | -c <commit>] <label> [# <oneline>]
|
||||
# . create a merge commit using the original merge commit's
|
||||
# . message (or the oneline, if no original merge commit was
|
||||
# . specified). Use -c <commit> 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.
|
||||
|
||||
|
||||
## 查看提交历史
|
||||
> git log --oneline --graph --all
|
||||
* 65c41e6 (HEAD -> 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 就会暂停。
|
||||
|
||||
```
|
||||
> 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 >
|
||||
|
||||
```
|
||||
|
||||
这时,我可以运行git log --oneline --graph --all,确认当前HEAD已经指向了我想要修改的提交A。
|
||||
|
||||
```
|
||||
> 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 <文件名>,然后再运行git commit --amend。
|
||||
|
||||
```
|
||||
## 检查当前HEAD内容
|
||||
> git show
|
||||
commit b51715452023fcf12432817c8a872e9e9b9118eb (HEAD)
|
||||
Author: Jason Ge <gejun_1978@yahoo.com>
|
||||
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对文件进行修改,在注释部分添加"at a predefined port"
|
||||
> vim index.js
|
||||
|
||||
|
||||
## 查看工作区中的修改
|
||||
> 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
|
||||
> git add index.js
|
||||
|
||||
|
||||
## 对修改添加到提交A中去
|
||||
> 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。确认其包含了新修改的内容"at a predefined port"
|
||||
> git show
|
||||
commit f544b1247a10e469372797c7dd08a32c0d59b032 (HEAD)
|
||||
Author: Jason Ge <gejun_1978@yahoo.com>
|
||||
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命令的其他步骤
|
||||
> git rebase --continue
|
||||
Successfully rebased and updated refs/heads/master.
|
||||
|
||||
|
||||
## 查看提交历史
|
||||
> git log --oneline --graph --all
|
||||
* 27cba8c (HEAD -> 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. 文章中提到,如果一个提交已经推送到了远程代码仓共享分支,那就没有办法对它进行拆分了。这个说法其实有些过于绝对。你知道为什么绝大部分情况下不能拆分,而什么情况下还可以拆分呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
1150
极客时间专栏/研发效率破局之道/个人效能/26 | Facebook怎样实现代码提交的原子性?.md
Normal file
1150
极客时间专栏/研发效率破局之道/个人效能/26 | Facebook怎样实现代码提交的原子性?.md
Normal file
File diff suppressed because it is too large
Load Diff
353
极客时间专栏/研发效率破局之道/个人效能/27 | 命令行:不只是酷,更重要的是能提高个人效能.md
Normal file
353
极客时间专栏/研发效率破局之道/个人效能/27 | 命令行:不只是酷,更重要的是能提高个人效能.md
Normal 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="\[\033[0m\]" # unsets color to term's fg color
|
||||
|
||||
# regular colors
|
||||
K="\[\033[0;30m\]" # black
|
||||
R="\[\033[0;31m\]" # red
|
||||
G="\[\033[0;32m\]" # green
|
||||
Y="\[\033[0;33m\]" # yellow
|
||||
B="\[\033[0;34m\]" # blue
|
||||
M="\[\033[0;35m\]" # magenta
|
||||
C="\[\033[0;36m\]" # cyan
|
||||
W="\[\033[0;37m\]" # white
|
||||
|
||||
# empahsized (bolded) colors
|
||||
MK="\[\033[1;30m\]"
|
||||
MR="\[\033[1;31m\]"
|
||||
MG="\[\033[1;32m\]"
|
||||
MY="\[\033[1;33m\]"
|
||||
MB="\[\033[1;34m\]"
|
||||
MM="\[\033[1;35m\]"
|
||||
MC="\[\033[1;36m\]"
|
||||
MW="\[\033[1;37m\]"
|
||||
|
||||
# background colors
|
||||
BGK="\[\033[40m\]"
|
||||
BGR="\[\033[41m\]"
|
||||
BGG="\[\033[42m\]"
|
||||
BGY="\[\033[43m\]"
|
||||
BGB="\[\033[44m\]"
|
||||
BGM="\[\033[45m\]"
|
||||
BGC="\[\033[46m\]"
|
||||
BGW="\[\033[47m\]"
|
||||
|
||||
```
|
||||
|
||||
```
|
||||
## 文件 $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 '>' turns red
|
||||
export PS1="\e[42m\t\e[m$N $W"'$(__git_ps1 "(%s) ")'"$N$PROMPT_COLOR\u@\H$N:$C\w$N\n\["'$CURSOR_COLOR'"\]>$W "
|
||||
export PROMPT_COMMAND='if [ $? -ne 0 ]; then CURSOR_COLOR=`echo -e "\033[0;31m"`; else CURSOR_COLOR=""; 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="powerlevel9k/powerlevel9k"
|
||||
|
||||
```
|
||||
|
||||
命令行提示符以外的其他配置,主要是通过安装和使用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 <theme>
|
||||
omf theme <theme>
|
||||
|
||||
## 我使用的是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@<server-ip-or-name>
|
||||
|
||||
```
|
||||
|
||||
下面这个录屏演示的是,我日常工作中使用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的最大区别是什么?是否有什么场景,我们必须使用其中的一个吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
338
极客时间专栏/研发效率破局之道/个人效能/28 | 从工作场景出发,寻找炫酷且有效的命令行工具.md
Normal file
338
极客时间专栏/研发效率破局之道/个人效能/28 | 从工作场景出发,寻找炫酷且有效的命令行工具.md
Normal 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 "node_modules" -f | grep -C3 index.md
|
||||
|
||||
```
|
||||
|
||||
可以得到当前文件夹中的所有index.md文件。
|
||||
|
||||
命令中,tree的参数-I,表示排除文件夹node_modules;tree的参数-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,我常用的一个替代品是RipGrep(rg)。跟fd类似,它也很适合开发者,有如下4个特点:
|
||||
|
||||
- 默认忽略.gitignore文件里指定的文件;
|
||||
- 默认忽略隐藏文件;
|
||||
- 默认递归搜索所有子目录;
|
||||
- 可以指定文件类型。
|
||||
|
||||
比如,使用rg tags就可以方便地查找当前目录下所有包含tags的文件。它的查找速度非常快,显示也比grep要漂亮:
|
||||
|
||||
```
|
||||
> rg tags
|
||||
package-lock.json
|
||||
2467: "common-tags": {
|
||||
2469: "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz",
|
||||
5306: "common-tags": "^1.4.0",
|
||||
5446: "common-tags": "^1.4.0",
|
||||
|
||||
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<回车> 直接跳转。</tab>
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/65/3e/65630c3d48a262195d63fad17e4e133e.gif" alt="">
|
||||
|
||||
对于第二种情况,即快速定位某个子文件夹,我介绍一个**超级酷的工具fzf**。本质上讲,fzf是一个对输入进行交互的模糊查询工具。它的使用场景非常多,文件夹的跳转只是一个应用。所以,我还在再后面文章做更多的详细讨论。
|
||||
|
||||
安装好fzf之后,你就可以使用Ctrl+T进行文件夹的交互式查询,或者使用Alt+C进行文件夹跳转。
|
||||
|
||||
比如,我想跳转到src/component文件夹中,可以输入Alt+C,fzf就会列出当前文件夹下的所有文件夹。比如,我输入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会打开VIM,VIM里面列举该文件夹中所包含的文件和子文件夹,然后使用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叫做TUI(Terminal UI)。十多年前的Borland终端IDE,就是这一类工具的翘楚,使用熟练之后效率会很高。
|
||||
|
||||
TUI的文件管理器我用过3个:Midnight Commander (以下简称mc)、Ranger和nnn。
|
||||
|
||||
mc是两个窗口的文件管理器。如果你使用过Windows Commander(Total 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
|
||||
{ "id": { "bioguide": "E000295", "thomas": "02283", "fec": [ "S4IA00129" ], "govtrack": 412667, "opensecrets": "N00035483", "lis": "S376" }, "name": { "first": "Joni", "last": "Ernst", "official_full": "Joni Ernst" }, "bio": { "gender": "F", "birthday": "1970-07-01" }, "terms": [ { "type": "sen", "start": "2015-01-06", "end": "2021-01-03", "state": "IA", "class": 2, "state_rank": "junior", "party": "Republican", "url": "http://www.ernst.senate.gov", "address": "825 B&C Hart Senate Office Building Washington DC 20510", "office": "825 B&c Hart Senate Office Building", "phone": "202-224-3254" } ] }
|
||||
|
||||
```
|
||||
|
||||
可以方便地使用cat person.json | jq .”对JSON进行格式化输出,
|
||||
|
||||
```
|
||||
$ cat people.json | jq .
|
||||
{
|
||||
"id": {
|
||||
"bioguide": "E000295",
|
||||
"thomas": "02283",
|
||||
"fec": [
|
||||
"S4IA00129"
|
||||
],
|
||||
"govtrack": 412667,
|
||||
"opensecrets": "N00035483",
|
||||
"lis": "S376"
|
||||
},
|
||||
"name": {
|
||||
"first": "Joni",
|
||||
"last": "Ernst",
|
||||
"official_full": "Joni Ernst"
|
||||
},
|
||||
"bio": {
|
||||
"gender": "F",
|
||||
"birthday": "1970-07-01"
|
||||
},
|
||||
"terms": [
|
||||
{
|
||||
"type": "sen",
|
||||
"start": "2015-01-06",
|
||||
"end": "2021-01-03",
|
||||
"state": "IA",
|
||||
"class": 2,
|
||||
"state_rank": "junior",
|
||||
"party": "Republican",
|
||||
"url": "http://www.ernst.senate.gov",
|
||||
"address": "825 B&C Hart Senate Office Building Washington DC 20510",
|
||||
"office": "825 B&c Hart Senate Office Building",
|
||||
"phone": "202-224-3254"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
以及使用jq ".terms[0].office"命令查询他的第一个工作任期的办公室地址。
|
||||
|
||||
```
|
||||
$ cat person.json | jq ".terms[0].office"
|
||||
"825 B&c Hart Senate Office Building"
|
||||
|
||||
```
|
||||
|
||||
但,jq存在的最大问题是,它有一套自己的查询处理语言。如果使用jq的频次没那么高的话,很难记住,每次都要去查帮助才可以。
|
||||
|
||||
针对这种情况,有人设计了另一种类似的工具,直接使用JavaScript作为查询处理语言,典型代表是fx和jq.node。这,就大大方便了使用JavaScript的开发者,因为可以使用已经熟悉了的语法。
|
||||
|
||||
比如,对于上个案例的JSON文件,我可以方便地在fx工具中使用JavaScript的函数filter()进行过滤查询。
|
||||
|
||||
```
|
||||
$ cat person-raw.json| fx 'json => json.terms.filter(x => x.type == "top")'
|
||||
[
|
||||
{
|
||||
"type": "top",
|
||||
"office": "333 B&c Hart CIrcle Building",
|
||||
"phone": "202-224-3254"
|
||||
}
|
||||
]
|
||||
|
||||
```
|
||||
|
||||
### 查找、关闭进程
|
||||
|
||||
通常情况下,我们使用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的工具。你知道它是什么作用吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
250
极客时间专栏/研发效率破局之道/个人效能/29 | 1+1>2,灵活的工具组合及环境让你的工作效率翻倍.md
Normal file
250
极客时间专栏/研发效率破局之道/个人效能/29 | 1+1>2,灵活的工具组合及环境让你的工作效率翻倍.md
Normal 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>2的效果。
|
||||
|
||||
所以接下来,我会从工具集成和提高工具一致性两个方面,与你介绍如何把多个工具组合成为高效的工具环境。
|
||||
|
||||
## 工具的集成
|
||||
|
||||
工具的集成,最值得优化的情况包括两种:一是,使用管道(Pipe)对命令行工具进行集成;二是,对集成开发工具环境(IDE)进行配置,让IDE和周边工具集成。
|
||||
|
||||
### 使用管道(Pipe)对命令行工具进行集成
|
||||
|
||||
其实,我在前面的文章中已经使用过管道很多次了,只是使用场景比较简单而已。
|
||||
|
||||
比如,在[第24篇文章](https://time.geekbang.org/column/article/150779)中,我使用命令curl -s <github_url> | vim -,来查看GitHub上某个用户的代码仓情况,通过管道把curl命令的输出传给VIM,以方便我在VIM中查看较长的输出。
|
||||
|
||||
```
|
||||
> curl -s https://api.github.com/users/jungejason/repos | vim -
|
||||
Vim: Reading from stdin...
|
||||
|
||||
## 进入VIM窗口,显示所有repo的array
|
||||
[
|
||||
{
|
||||
"id": 213849635,
|
||||
"node_id": "MDEwOlJlcG9zaXRvcnkyMTM4NDk2MzU=",
|
||||
"name": "counter-redux-sample",
|
||||
"full_name": "jungejason/counter-redux-sample",
|
||||
"private": false,
|
||||
"owner": {
|
||||
"login": "jungejason",
|
||||
...
|
||||
|
||||
```
|
||||
|
||||
这个案例使用了一次管道,比较简单。
|
||||
|
||||
而高效使用管道,首先是**使用多个管道灵活地把多个工具连接起来**。一个常见的场景是,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>
|
||||
|
||||
上面管道每一步的执行输出,如下所示:
|
||||
|
||||
```
|
||||
> 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
|
||||
...
|
||||
|
||||
> 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
|
||||
|
||||
> ps aux | grep vim | grep -v grep
|
||||
jasonge 31383 0.0 0.0 4310340 4740 s001 S+ 9:57AM 0:00.11 vim
|
||||
|
||||
> ps aux | grep vim | grep -v grep | awk '{print $2}'
|
||||
31383
|
||||
|
||||
> 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命令把进程列表发送给fzf,fzf首先会列举出所有进程供我搜索过滤。随着我的输入,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 "fzf" | 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,很多工作都通过Arcanist(Phabricator 的命令行工具)的各种子命令来完成。这样就实现了命令使用的一致性,降低了学习成本。
|
||||
|
||||
在日常工作中,你也可以尝试开发一个命令行工具,对常用工作进行封装。之前我在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%的操作,进行集成和优化。从我个人的体验来看,这样的平衡点是比较合适的。
|
||||
|
||||
总之,我们时刻要牢记,配置环境的目的是,配置出一个强大而灵活的环境,让一切工作流畅,从而提高生产效率,一定要注意投入产出比。
|
||||
|
||||
## 思考题
|
||||
|
||||
在浏览器作为工作入口的例子中,如果一个任务描述搜索到的任务不止一条时,我们可以怎么处理呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
80
极客时间专栏/研发效率破局之道/个人效能/30 | 答疑篇:关于价值导向和沟通.md
Normal file
80
极客时间专栏/研发效率破局之道/个人效能/30 | 答疑篇:关于价值导向和沟通.md
Normal 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出现障碍A,then使用计划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/)专门讲这个话题,很有意思,推荐你阅读下。
|
||||
|
||||
## 小结
|
||||
|
||||
好了,以上就是今天的主要内容了,希望能对你梳理目标和提高沟通能力有所帮助。如果有你希望深入了解还未涉及的话题,那就直接给我留言吧。
|
||||
|
||||
接下来,我们就会开启新的模块了,进入管理和文化的讨论。在下一个模块,我会重点与你介绍硅谷的管理、文化,以及在国内的一些落地实践,希望帮助你了解如何利用管理推动高效研发的落地,以及如何使用文化来激发团队成员的内驱力和创造力,提高团队的研发效能。
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
147
极客时间专栏/研发效率破局之道/个人效能/特别放送 | 每个开发人员都应该学一些VIM.md
Normal file
147
极客时间专栏/研发效率破局之道/个人效能/特别放送 | 每个开发人员都应该学一些VIM.md
Normal 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)上提问怎么退出VIM,6年以来的阅读量已经接近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,表示将光标向右移动到当前单词最后一个字符;
|
||||
- 敲击符号*,表示在当前文件搜索光标所在的单词。
|
||||
|
||||
另外,在命令模式中输入“:<命令>回车键”,可以执行一些命令行命令以及进行系统配置。比如:
|
||||
|
||||
- 输入: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的前身是VI,VI的前身是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教徒的争吵吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
149
极客时间专栏/研发效率破局之道/工程方法/11 | 研发环境:Facebook怎样让开发人员不再操心环境?.md
Normal file
149
极客时间专栏/研发效率破局之道/工程方法/11 | 研发环境:Facebook怎样让开发人员不再操心环境?.md
Normal 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做开发的时候,对研发环境的感觉就是不用操心,需要使用的时候直接到网站上申请就可以使用;配置也方便,团队的配置都已经在那里了;同时,环境中的各种工具、流程都很顺畅,让我能够静下心来做开发、写算法,做我最能提供价值的事情。
|
||||
|
||||
## 思考题
|
||||
|
||||
我在“开发过程中使用的各种工具、数据和配置”这一章节中提到的截屏工具流程,你觉得价值大吗?值得引入你所在的公司吗?如果值得的话,可以怎么来实现?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
207
极客时间专栏/研发效率破局之道/工程方法/12 | 代码审查:哪种方式更适合我的团队?.md
Normal file
207
极客时间专栏/研发效率破局之道/工程方法/12 | 代码审查:哪种方式更适合我的团队?.md
Normal 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的镜像方式进行代码审查。也就是说,代码仓库仍然是GitLab,Phabricator上只有一个用来做代码审查的克隆,这样既实现了代码审查,又把对原有Git流程的影响降到了最小。
|
||||
- 因为团队较大,又分散在多个地区,所以大量使用了线下的异步审查流程。为了保证开发人员在等待一个提交审查的同时,还可以做其他开发工作,他们使用了Git的提交链和多分支的方法。关于Git的这个使用技巧,我会在后面的文章里与你详细讨论。
|
||||
- 基于Phabricator进行代码设计时审查,解决因为代码仓库规模大,导致添加新功能时设计复杂也容易有所疏漏的问题。开发人员使用伪代码来表明自己的设计计划,并发出代码审查需求,然后跟审查者进行面对面讨论或者视频会议讨论。
|
||||
- 使用代码审查对新人进行培训,通过严格审查新人提交的代码,来传达团队的代码规则、质量基准等。
|
||||
|
||||
另外,值得一提的是,在使用Phabricator进行代码审查之前,这个团队最常用的是少量的团队集中审查,并且有审查权的只是团队的几个核心成员。这种方式导致了两个问题:一是审查效率低下;二是这几个核心成员本身都是技术骨干,但因为需要花费大量时间做审查,导致无法贡献足够多的新代码。
|
||||
|
||||
采用新的代码审查方式之后,降低了团队开会进行代码审查的频率,并且逐步放开了审查权限,解决了代码审查成为瓶颈的问题。
|
||||
|
||||
以上就是三个成功案例,相信根据你们公司的实际情况具体分析,你一定能找到合适的方法。
|
||||
|
||||
## 小结
|
||||
|
||||
代码审查对团队产出质量、个人技术成长有很多好处。代码审查方式多种多样,根据不同维度可以分为工具辅助线下审查、面对面审查、多对一审查、一对一审查、代码增量审查、代码全量审查、设计时审查、入库前检查、入库后检查等。
|
||||
|
||||
我将这些审查方法的优缺点整理为了一张表格,你可以以此为参考,根据团队实际情况去挑选合适的方式。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/e7/00/e7f76393fbce169b3e75b9e67407f000.jpg" alt="">
|
||||
|
||||
总体来说,绝大部分团队都适合引入工具进行异步的一对一审查,在互联网上也有比较多的关于这种审查方式的最佳实践推荐。比如,最近Google发表了他们的[代码审查指南(Google’s Code Review Documentation)](https://github.com/google/eng-practices/blob/master/review/index.md),你可以参考。
|
||||
|
||||
另外,多使用一些设计时审查尽早进行讨论一般也都效果不错。
|
||||
|
||||
知道了应该使用哪个方式,下一步就该具体的引入了。在下一篇文章,我会和你介绍成功引入代码审查的几个具体实践。
|
||||
|
||||
## 思考题
|
||||
|
||||
1. 你们团队使用了代码审查吗?具体使用了哪几种审查方式呢?
|
||||
1. 设计时检查除了可以避免后期对代码的大规模调整外,对顺利引入代码审查还有一些其他作用。你能想到还有哪些作用吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
239
极客时间专栏/研发效率破局之道/工程方法/13 | 代码审查:学习Facebook真正发挥代码审查的提效作用.md
Normal file
239
极客时间专栏/研发效率破局之道/工程方法/13 | 代码审查:学习Facebook真正发挥代码审查的提效作用.md
Normal 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管理代码,代码入库后通过钩子触发Jenkins,Jenkins从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
|
||||
> cat .git_commit_msg.txt
|
||||
|
||||
|
||||
Summary:
|
||||
|
||||
Test:
|
||||
|
||||
Task ID:
|
||||
|
||||
# 设置上述文件为提交说明模板。
|
||||
> git config --global commit.template ~/.git_commit_msg.txt
|
||||
|
||||
|
||||
# 使用实例:之后git commit 命令自动使用上述模板
|
||||
> git add app.js
|
||||
> 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”这三个字母。)
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
158
极客时间专栏/研发效率破局之道/工程方法/14 | 质量与速度的均衡:让“唯快不破”快得更持久.md
Normal file
158
极客时间专栏/研发效率破局之道/工程方法/14 | 质量与速度的均衡:让“唯快不破”快得更持久.md
Normal 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 & paste,保证功能按时上线。
|
||||
- 需要在一个函数里增加功能,这个函数已经有800行了,加上新功能后会有1000行。重构这个函数是来不及了,先把功能加上去再说。
|
||||
|
||||
说是“捷径”,是因为这些都不是最优解,有点儿投机取巧。它们的确能让我们在短期内保证快速交付,满足业务发展需求。但如果没有任何补救措施的话,时间长了我们就再也快不起来了。
|
||||
|
||||
比如,“copy & 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,即Don’t 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="">
|
||||
|
||||
这个例子很有代表性,你可以用它来说服管理层在偿还技术债上做投入。
|
||||
|
||||
## 思考题
|
||||
|
||||
经济债务可以申请破产保护,你觉得技术债可以有这样的福利吗?为什么呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
170
极客时间专栏/研发效率破局之道/工程方法/15 | 开源:从Phabricator的开源历程看开源利弊.md
Normal file
170
极客时间专栏/研发效率破局之道/工程方法/15 | 开源:从Phabricator的开源历程看开源利弊.md
Normal 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 Peterson)1998年提出“开源”这个名词,到今天已经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/)”。
|
||||
|
||||
开源贡献协议,指的是对软件贡献者权力的限定,目的是赋予开发者对开源项目贡献代码的权力,并赋予项目管理者按照软件授权协议去发布软件。它包括 CLA(Contributor License Agreement)和 DCO(Developer 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了,但理智地看这的确是一个很好的决定。
|
||||
|
||||
## 思考题
|
||||
|
||||
跟硅谷相比,国内公司参与开源的非常少。你觉得主要原因是什么,将来的趋势又会是什么样的呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
108
极客时间专栏/研发效率破局之道/工程方法/16 | 高效上云:如何用云计算来提高效能?.md
Normal file
108
极客时间专栏/研发效率破局之道/工程方法/16 | 高效上云:如何用云计算来提高效能?.md
Normal 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会在某一时刻对某一个数据中心的所有服务器断电进行测试,并事前会通知每一个服务的所有者,确保自己的服务在这样的极端情况下仍能保证业务健康运行。
|
||||
|
||||
而我在创业公司的时候,更是充分利用了公有云提供的各种服务来快速上线产品,实现了传统基础设施环境下不可想象的高效能。虽然云计算现在还没有像水、电、煤气那样普遍,但我相信那一天不会太遥远。
|
||||
|
||||
## 思考题
|
||||
|
||||
在你当前的工作中,你觉得最能够使用云计算来提高研发效能的地方是什么?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
104
极客时间专栏/研发效率破局之道/工程方法/17 | 测试左移:测试如何应对新的开发模式?.md
Normal file
104
极客时间专栏/研发效率破局之道/工程方法/17 | 测试左移:测试如何应对新的开发模式?.md
Normal 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”。
|
||||
|
||||
**测试左移到了开发阶段之后,再往左移一步就到了产品设计阶段,**在这里,测试人员除了解需求外,更重要的是评估需求的质量。
|
||||
|
||||
我推荐使用BDD(Behavior 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团队也能保证产品的高质量。
|
||||
|
||||
## 思考题
|
||||
|
||||
你认为测试左移和测试右移,会不会减少测试人员的工作机会呢?如果你是测试人员,又应该怎么面对这个新的测试模式带来的挑战呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
174
极客时间专栏/研发效率破局之道/工程方法/18 | 蓝绿红黑灰度发布:这些五颜六色的发布到底怎么用?.md
Normal file
174
极客时间专栏/研发效率破局之道/工程方法/18 | 蓝绿红黑灰度发布:这些五颜六色的发布到底怎么用?.md
Normal 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这种原生支持灰度发布的工具的出现和流行,也正表明了这一趋势。在这种在生产环境上进行测试的方式,最关键的是要做好风险控制。
|
||||
|
||||
另外,这种模式给测试团队带来了非常大的挑战。我觉得,在不久的将来,传统测试方式会越来越不流行。测试团队需要尽快转型,来适应这种新的开发模式。
|
||||
|
||||
## 思考题
|
||||
|
||||
你觉得金丝雀发布可以用在移动端应用或者桌面应用上吗?如果可以的话,大概要怎么实现呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
146
极客时间专栏/研发效率破局之道/工程方法/19 | 不再掉队,研发流程、工程方法趋势解读和展望.md
Normal file
146
极客时间专栏/研发效率破局之道/工程方法/19 | 不再掉队,研发流程、工程方法趋势解读和展望.md
Normal 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了,一个具体的例子是FaaS(Function 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="">
|
||||
|
||||
>
|
||||
备注:CaaS(Containers 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、CD4ML(Continuous 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来提高研发效能,是一个很有趣的话题。除了我今天提到的这些,你觉得还有哪些可能的方式吗?将你的预测与想法,分享给我吧。
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
121
极客时间专栏/研发效率破局之道/工程方法/20 | 答疑篇:如何平衡短期收益和长期收益?.md
Normal file
121
极客时间专栏/研发效率破局之道/工程方法/20 | 答疑篇:如何平衡短期收益和长期收益?.md
Normal 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~4:SEV1最严重,SEV4最轻。每次发生事故,除了记录严重性之外,还要按时间顺序记录事故发生、发现、定位、解决、确认解决过程的每一步的时长和具体操作细节。
|
||||
|
||||
每周公司都会定时举行SEV讨论会,基本只讨论严重的问题,比如SEV1和SEV2的事故。会议由高层管理者主持,事故责任人轮流进入会议室对事故进行描述和讨论。
|
||||
|
||||
这个会议的主要目的是,对事故进行回溯,分析根因以及如何避免再次出现类似问题。也就是说,SEV系统的目的,重在总结提高,不要多次重复同一个错误,如果重复犯错要惩罚。
|
||||
|
||||
SEV系统的效果很好,在避免问题重复出现的同时,我们也比较敢于试错,至少从我的经验看是这样的。
|
||||
|
||||
**针对这个事故复盘系统,我想扩展的话题是:出现问题之后,到底应不应该追责,应该怎样追责?**
|
||||
|
||||
在我看来,考虑这个问题应该从最终目的出发。复盘的目的是最大程度地减少以后再出现同一个问题的概率。那我们应该怎样处理呢?
|
||||
|
||||
如果每个错误都要惩罚,就会有以下几个问题:
|
||||
|
||||
- 因为害怕惩罚,尽量去掩盖问题而不是暴露问题、分析问题,或者尽量撇清责任甩锅。
|
||||
- 惧怕闯祸,团队成员开始信奉“多做不如少做”。
|
||||
- 因为担心指出别人的问题,会让他受罚,结果就是不愿意得罪人,导致问题被隐藏。
|
||||
|
||||
而另一个极端,如果犯错没有任何惩罚的话,又会造成以下问题:
|
||||
|
||||
- 同样的错误一犯再犯。
|
||||
- 没有奖惩造成不公平。这就会让原本认真工作的员工失去认真做事的动力,降低对代码质量、产品质量的关注,提高质量的工程措施(比如,单元测试、开发自测等)更是难以推广。
|
||||
|
||||
所以,在我看来,针对错误更有效的处理方法应该是,以保持团队的主动性、责任感和执行力为原则,具体措施包括:
|
||||
|
||||
- 追究责任,但不是惩罚。知其然并知其所以然,搞清楚前因后果,避免重复犯同一个错误。
|
||||
- 重复犯错一定要惩罚。
|
||||
- 反复问为什么,找到根本原因。
|
||||
|
||||
## 小结
|
||||
|
||||
好了,以上就是今天的主要内容了。如果有哪些你希望深入了解还未涉及的话题,那就直接给我留言吧。
|
||||
|
||||
接下来,我们就会开启新的模块了,进入个人效能的讨论了,希望这个模块能够帮助你提升个人效能,持续成长,成为10x开发者,提升个人竞争力。
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
63
极客时间专栏/研发效率破局之道/开篇词/开篇词 | 为什么你要关注研发效能?.md
Normal file
63
极客时间专栏/研发效率破局之道/开篇词/开篇词 | 为什么你要关注研发效能?.md
Normal 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,也就是具体的实践。**因为只有深刻理解原理,才能灵活运用。
|
||||
|
||||
同时,我会与你分享尽量多的案例,带你一起了解国内外一些公司的优秀做法,分析它们成功的经验,当然,我也会分享失败的案例,以及背后的原因。不过更重要的是,我希望你能够跟着我一起分析,通过对比思考,找到真正适合团队和自身的实践。而这,是我写作这个专栏的真正初衷。
|
||||
|
||||
这是研发效能专栏的第一篇文章,如果可以的话,欢迎你在留言区做个自我介绍,和我聊聊你或者你的团队在研发效能方面的实践以及遇到的问题,增进我们彼此的了解。而且,我也希望你看过这个专栏后,能够再回头来看看最初留下的内容,相信届时你已经对研发效能有了新的理解和思考。
|
||||
|
||||
|
||||
120
极客时间专栏/研发效率破局之道/研发效能综述/01 | 效能模型:如何系统地理解研发效能?.md
Normal file
120
极客时间专栏/研发效率破局之道/研发效能综述/01 | 效能模型:如何系统地理解研发效能?.md
Normal 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 System,TPS)。所以,**我们可以参考传统制造行业的经验来提高效能**。
|
||||
|
||||
事实上,瀑布模式就类似于传统流水线的处理方法:它强调每个环节之间界限分明,尽量清晰地定义每一个环节的输入和输出,保证每一个环节产出的质量。但,**和传统制造业相比,软件开发又具有超强的灵活性,<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>2的效果。
|
||||
1. 文化与管理。任何流程、实践的引入和推广,都必须有合理的管理方法来支撑。同时,文化是一个团队工作的基本价值观和潜规则。只有建立好文化,才能让团队持续学习,从而应对新的挑战。所以,要提高效能,我们还需要文化和管理这个引擎。
|
||||
|
||||
优化流程、团队工程实践、个人工程实践,以及文化和管理,就是我们提高研发效能需要关注的4个方面,也就是我们所说的研发效能模型。
|
||||
|
||||
在接下来的文章中,我会以这个模型为基础,从以上这4个方向与你介绍硅谷公司,尤其是我最熟悉的Facebook的成功实践,并着重向你讲述这些实践背后的原理。因为只有理解了这些原理和原则,我们才有可能在这个超级灵活和高速发展的软件开发行业里见招拆招,立于不败之地。
|
||||
|
||||
## 总结
|
||||
|
||||
在今天这篇文章中,我和你再次强调了研发效能是团队能够持续为用户产生有效价值的效率,包括有效性、效率和可持续性3个方面。
|
||||
|
||||
而关于如何提高效能,我的建议是深入了解研发活动的本质,从纷乱的表象和层出不穷的方法中,看到隐藏的模型,找到根本原则,然后从这些原则出发,具体问题具体分析,找到合适的方法。
|
||||
|
||||
最后,我借鉴传统制造业流水线的形式,并结合软件开发的特定灵活性,总结出了研发效能的模型,主要包括优化流程、团队工程实践、个人工程实践、管理与文化。专栏的后续内容,我将分为这4大模块,帮助你提高团队和个人的研发效能。
|
||||
|
||||
其实,研发效能对于硅谷的公司和个人来说,已经是常识,而我在美国工作的10多年,也已经习惯了这种工作方式。回国之后,我深入了解了国内的开发情况,对效能关注不到位的现状颇为吃惊。因为这不符合软件开发这个行业的基本特性,也限制了国内软件行业的发展。
|
||||
|
||||
同时,我也对国内开发人员的生存环境,感到些许遗憾。本来软件开发是一个可以充分发挥创造性的、有趣的行业,技术的发展有无尽的空间。但是,开发人员却常常被业务拖着跑,技术发展和创造性都被限制住了。另外,因为拼时长的做法,也伤害了开发者的身体健康和家庭关系。
|
||||
|
||||
正是因为这些惊讶和遗憾,我将这十几年对研发效能的理解与实践,做了一次系统梳理,于是就有了今天提到的研发效能模型。我希望这种系统化的模型方法,能够为国内软件团队的效能提升提供一些参考和引导,也希望能够提升开发者个人的技能,以节省出时间来体会研发的快乐,提高生活的幸福感。
|
||||
|
||||
## 思考题
|
||||
|
||||
你有见过996对团队和产品造成伤害的具体事例吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
123
极客时间专栏/研发效率破局之道/研发效能综述/02 | 效能度量:效果不好甚至有副作用,怎么回事?.md
Normal file
123
极客时间专栏/研发效率破局之道/研发效能综述/02 | 效能度量:效果不好甚至有副作用,怎么回事?.md
Normal 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 can’t measure it, you can’t 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)的文章中写到,衡量程序员的工作效率相当困难,原因如下:
|
||||
|
||||
- 几乎任何你能想到的指标(比如,调测过的代码行数、功能点、命令行参数的个数)都很容易被“做数字”;
|
||||
- 我们极少要求两个程序员做完全相同的事情,所以很难获取大型项目的有价值度量数据作为参考。
|
||||
|
||||
那是不是说在研发软件时,我们就不能使用度量效能的方法来指导工作了呢?如果是的话,这对于软件团队管理者而言,将会是一个难以接受的事实。
|
||||
|
||||
这个问题的答案,我们就留到下一篇文章再揭晓吧,敬请期待。
|
||||
|
||||
## 思考题
|
||||
|
||||
你在工作中有没有经历过研发效能度量的失败案例?如果有的话,你觉得失败的原因是什么?关于度量谜题怎么解决,你有没有什么建议?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
176
极客时间专栏/研发效率破局之道/研发效能综述/03 | 效能度量:如何选对指标与方法,真正提升效能?.md
Normal file
176
极客时间专栏/研发效率破局之道/研发效能综述/03 | 效能度量:如何选对指标与方法,真正提升效能?.md
Normal 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 Score,NPS),是通过调研了解用户满意度,实用性很强。如果你不了解的话,可以看一下[这篇文章](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="">
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
152
极客时间专栏/研发效率破局之道/研发流程/04 | 流程优化:怎样才能让敏捷、精益真正为我所用?.md
Normal file
152
极客时间专栏/研发效率破局之道/研发流程/04 | 流程优化:怎样才能让敏捷、精益真正为我所用?.md
Normal 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 Product,MVP)来帮助学习**。这里的关键点是,要以探索价值为出发点设计产品,最快地验证你的假设,功能要尽量少,能够使用就可以。具体的方法有数据驱动、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学到的高效研发的最重要原则,没有之一。
|
||||
|
||||
## 思考题
|
||||
|
||||
你理解的精益看板,跟文中提到的任务可视化白板一样吗?你知道它们之间的关系吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
158
极客时间专栏/研发效率破局之道/研发流程/05 | 代码入库前:Facebook如何让开发人员聚焦于开发?.md
Normal file
158
极客时间专栏/研发效率破局之道/研发流程/05 | 代码入库前:Facebook如何让开发人员聚焦于开发?.md
Normal 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 Code,IaC)系统。比如,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="*.py" \
|
||||
--command='python "${watch_src_path}"' \
|
||||
.
|
||||
|
||||
```
|
||||
|
||||
会监控所有的Python文件改动并自动重启。
|
||||
|
||||
提供快速反馈,边开发边验证,虽然只是一个简单的原则,但可以带来很多好处。最直接的收益就是,能够大大提高开发者对当前代码的信心,从而促进代码尽早入仓、尽早集成。
|
||||
|
||||
可能你也注意到了,**代码集成越晚发现问题就越晚。这正是产品上线的最后关头合并混乱,产品质量差、返工率高的一个重要原因**。所以,我建议在你的工作流程中,要尽量提高实时验证的能力。如果你这么做了,很快就会看到效果。
|
||||
|
||||
## 小结
|
||||
|
||||
在今天这篇文章中,我和你分享了两条持续开发的基本原则,来帮助开发者在代码入库前聚焦于开发工作:一是,规范化、自动化代码入库前的核心步骤;二是,提供快速反馈,帮助开发者边开发边验证,以促进增量开发。
|
||||
|
||||
我将今天的内容,总结为了一幅图,帮助你复习。
|
||||
|
||||
<img src="https://static001.geekbang.org/resource/image/50/98/50c1bdf8074f68b7707b26a1b657df98.png" alt="">
|
||||
|
||||
这些原则和实践,是我根据自己的经验总结出来的。如果能直接适用于你的团队当然最好了,但我更加希望的是,你能从这些原则和实践的讨论中,理解它们背后的思路,从而找到合适的方法和实践,去优化代码入库前的流程中最需要优化的地方,让开发者能够真正聚焦于开发。
|
||||
|
||||
另外,我给你一个落地持续开发的小贴士:持续开发很适合用自上而下和自下而上相结合的方式来推动。因为开发者最了解自己工作的痛点,所以也能比较准确地找到需要优化的地方。在Facebook,很多工具和流程都是由开发者自发开发或者引入,后来逐步推广至团队和公司使用的。
|
||||
|
||||
所以我推荐,**作为开发者,你可以自己抽一点时间优化自己的工作流程,自动化繁琐的工作;而作为管理者,你可以有意识地奖励这样的优化行为,并对适用于团队的部分进行推广。**
|
||||
|
||||
## 思考题
|
||||
|
||||
最后,我来给你留下两个思考题吧。
|
||||
|
||||
1. 在开发环境方面,你有没有尝试过在Docker里面进行开发?你觉得这种方式的好处是什么,弊端又是什么呢?
|
||||
1. 有些开发者喜欢写好一个比较大的功能单元,然后再一口气调测。你觉得这样做的好处和坏处,各是什么呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
151
极客时间专栏/研发效率破局之道/研发流程/06 | 代码入库到产品上线:Facebook如何使用CI|CD满足业务要求?.md
Normal file
151
极客时间专栏/研发效率破局之道/研发流程/06 | 代码入库到产品上线:Facebook如何使用CI|CD满足业务要求?.md
Normal 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是怎样互相促进的。
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
280
极客时间专栏/研发效率破局之道/研发流程/07 | 分支管理:Facebook的策略,适合我的团队吗?.md
Normal file
280
极客时间专栏/研发效率破局之道/研发流程/07 | 分支管理:Facebook的策略,适合我的团队吗?.md
Normal 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 <fix-sha1> # fix-sha1 是修复提交的commit ID
|
||||
|
||||
```
|
||||
|
||||
接着继续回到第二步验证。
|
||||
|
||||
验证通过就发布当前分支。这个发布分支就成为当前生产线上运行版本对应的分支,我们称之为当前生产分支,同时将上一次发布时使用的生产分支存档或者删除。
|
||||
|
||||
在进行**热修复部署**时,从当前生产分支中拉出一个热修复分支,进行验证和修复。具体步骤为:
|
||||
|
||||
第一步,拉出一个热修复分支。
|
||||
|
||||
```
|
||||
git checkout -b hotfix-date-* release-date-*
|
||||
|
||||
```
|
||||
|
||||
第二步,开发人员提交热修复到master,然后cherry-pick修复提交到热修复分支上。
|
||||
|
||||
```
|
||||
git cherry-pick <fix-sha1>
|
||||
|
||||
```
|
||||
|
||||
第三步,进行各种验证。
|
||||
|
||||
第四步,验证中发现问题,回到第二步重新修复验证。验证通过就发布当前热修复分支,同时将这个热修复分支设置为当前的生产分支,后面如果有新的热修复,就从这个分支拉取。
|
||||
|
||||
这里有一张图片,描述了每周五拉取周部署分支,以及从周部署分支上拉取分支进行热修复部署的流程。
|
||||
|
||||
<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。那么,我们可以使用下面的命令来自动化这个定位流程:
|
||||
|
||||
```
|
||||
> git checkout master # 使用最新提交的代码
|
||||
> git bisect start
|
||||
> git bisect bad HEAD # 告知 git bisect,当前commit是有问题的提交
|
||||
> git bisect good C100 # 告知 git bisect,C100是没有问题的提交
|
||||
> git bisect run bash runtest.sh # 开始运行自动化折半查找
|
||||
...
|
||||
Cxxx is the first bad commit # 查找到第一个问题提交
|
||||
...
|
||||
bisect run success
|
||||
> 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”的意思吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
165
极客时间专栏/研发效率破局之道/研发流程/08 | DevOps、SRE的共性:应用全栈思路打通开发和运维.md
Normal file
165
极客时间专栏/研发效率破局之道/研发流程/08 | DevOps、SRE的共性:应用全栈思路打通开发和运维.md
Normal 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分钟,MTTR(Mean 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的流行,使得越来越多的底层运维工作被自动化,导致对传统的系统工程师、运维工程师的需求越来越少。所以,懂得开发的运维人员,会越来越重要;同时,更关注部署、测试,甚至产品的全栈工程师,也会越来越受欢迎。
|
||||
|
||||
## 思考题
|
||||
|
||||
有些人认为“全栈工程师”要求太高,一个人不能掌握那么多领域的知识,不现实。你怎么看呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
145
极客时间专栏/研发效率破局之道/研发流程/09 | 信息流通:让团队高效协同,让产品准确击中目标.md
Normal file
145
极客时间专栏/研发效率破局之道/研发流程/09 | 信息流通:让团队高效协同,让产品准确击中目标.md
Normal 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&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时,有些同事会直接告诉其他人,如果我戴上耳机,就表示不希望被干扰,除非紧急情况,否则不要找我当面沟通问题。虽说这种做法有些极端,但我非常希望国内公司也能够尝试类似的工作方式,因为它能让大家安心开发,在高效产出的同时,更多地享受心流的快乐。
|
||||
|
||||
## 思考题
|
||||
|
||||
在工作中,你见到的信息沟通的最大问题是什么,在今天的文章中能找到合适的解决方法吗?如果没有找到,你还有什么建议的解决方法吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
113
极客时间专栏/研发效率破局之道/研发流程/10 | 答疑篇:反对996并不是反对奋斗.md
Normal file
113
极客时间专栏/研发效率破局之道/研发流程/10 | 答疑篇:反对996并不是反对奋斗.md
Normal 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 Code(IaC)的方式,来自动化环境的获取和释放。这是一个比较通用的办法。具体来说,实现环境服务化的思路是:
|
||||
|
||||
- 首先,把环境模板化,并把模板作为代码进行存储。这个模板系统需要支持环境中的资源设置,以及服务间的依赖。同时需要提供设定变量的能力,来支持用户在环境生成时指定参数,处理诸如数据库、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的压力和能力。
|
||||
- **持续交付通过率**:执行构建->部署->测试->发布全流程的成功率,反映的是开发自测质量,以及自动化验收的稳定性。
|
||||
- **高优先级安全漏洞产生率,安全漏洞修补速度**:这两个安全相关的指标反映的是,团队在安全方面的风险和处理能力。
|
||||
|
||||
## 小结
|
||||
|
||||
好了,以上就是今天的主要内容了。如果有哪些你希望深入了解的话题还未涉及到,希望你可以留言给我。
|
||||
|
||||
最后,我想再和你强调一下,第4篇文章“[流程优化:怎样才能让敏捷、精益真正为我所用?](https://time.geekbang.org/column/article/128867)”中,提到的Why-How-What黄金圈法则和“实用主义”原则。
|
||||
|
||||
我觉得,这是提高研发效率的关键所在。因为软件研发是一个非常灵活、非常有创造性的活动,所以我们一定要抓住根本,了解我们到底需要达到什么目的,有哪些基本原则,然后才是学习一些可供我们参考的最佳实践。这样,我们才能灵活运用这些原则、最佳实践,真正提升团队的研发效能。
|
||||
|
||||
所以,在整个专栏的写作中,我也会着重系统化地讲解研发效能的基本原则。让我备受鼓舞的是,很多同学在留言中表示会支持这个思路。这里,我衷心希望你可以通过实用主义的方式,去寻找合适自己的最佳实践。
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
145
极客时间专栏/研发效率破局之道/管理和文化/31 | 业务目标和技术目标两手抓:怎样打造高效团队?.md
Normal file
145
极客时间专栏/研发效率破局之道/管理和文化/31 | 业务目标和技术目标两手抓:怎样打造高效团队?.md
Normal 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。你知道这其中的原因是什么吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
142
极客时间专栏/研发效率破局之道/管理和文化/32 | 从Netflix公开的著名PPT谈硅谷公司文化.md
Normal file
142
极客时间专栏/研发效率破局之道/管理和文化/32 | 从Netflix公开的著名PPT谈硅谷公司文化.md
Normal 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的企业文化,并分享我在其他公司引入文化的一些实践。敬请期待!
|
||||
|
||||
## 思考题
|
||||
|
||||
你知道还有哪个公司像奈飞这样推崇透明的文化,效果又怎样呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
114
极客时间专栏/研发效率破局之道/管理和文化/33 | Facebook企业文化:工程师文化是创造力引擎.md
Normal file
114
极客时间专栏/研发效率破局之道/管理和文化/33 | Facebook企业文化:工程师文化是创造力引擎.md
Normal 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 的一种理解。你知道这句话更常见的一种理解是什么吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
152
极客时间专栏/研发效率破局之道/管理和文化/34 | Facebook工程师文化实践三大支柱之一做感兴趣的事.md
Normal file
152
极客时间专栏/研发效率破局之道/管理和文化/34 | Facebook工程师文化实践三大支柱之一做感兴趣的事.md
Normal 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花费的时间不多,但执行合适的话,效果会非常好。
|
||||
|
||||
除此之外,作为技术管理者,我的建议是,在安排日常工作时可以多考虑团队成员的意向,尽量在条件允许的情况下,提高员工兴趣和任务的吻合度。
|
||||
|
||||
## 思考题
|
||||
|
||||
在你的公司或者团队,有什么措施让员工尽量做自己感兴趣的事吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
149
极客时间专栏/研发效率破局之道/管理和文化/35 | Facebook工程师文化实践三大支柱之二拥有信息和权限.md
Normal file
149
极客时间专栏/研发效率破局之道/管理和文化/35 | Facebook工程师文化实践三大支柱之二拥有信息和权限.md
Normal 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&A,员工可以通过现场或者线上提问的方式,问扎克伯格以及高管们任何问题。扎克伯格和其他高管都非常坦诚,尽量把可以公开的信息都坦诚相告,对公司信息上下流通的效果非常好。
|
||||
|
||||
离开Facebook之后,我在一个团队内,采用类似Facebook的方式进行了Q&A,使用尽量坦诚的、开放的问答形式,不说套话。这个团队的特点是,执行力很强,但氛围有些沉闷。在坚持了两三次Q&A之后,气氛逐渐活跃,大家逐步敢问一些敏感的问题了。
|
||||
|
||||
事实证明,开发人员的思维非常活跃,也都很喜欢用活跃的方式进行沟通,以获取到更多的信息。这样做的好处是,加强了员工对公司、对团队的主人翁的精神,进而提高整体的研发效能。
|
||||
|
||||
**落实Q&A还有一个小窍门**是,大家可以用匿名写纸条的方式去提问。这个方法,在国内效果更好,很多人更愿意用这种方式去问一些敏感的问题。
|
||||
|
||||
### 案例2:信息共享的相关实践
|
||||
|
||||
这个实践是,我在一个创业公司,采用Wiki的方式来推动全公司范围内的信息共享,内容权限默认设置为公开,只有特殊情况才可以设置为只对某些人公开。
|
||||
|
||||
落地这个实践,的确是一个长期的过程。我见到,国内很多开发人员,都不太习惯做分享,所以**我推行Wiki时的两个方法,是坚持和以身作则。**
|
||||
|
||||
在推广Wiki的第一个月,90%的新内容都是我写的!后来,大家逐渐看到了这种信息共享的好处,也看到了我的坚持,就开始主动向Wiki中增加内容。半年后,我的贡献降到了10%。
|
||||
|
||||
## 小结
|
||||
|
||||
今天,我与你分享了Facebook工程师文化落地实践的第二大原则,让员工拥有信息和权限。
|
||||
|
||||
因为软件开发,是知识性工作,所以拥有信息是高效开发的前提。Facebook在这方面的实践,包括代码共享、看板和公司范围内公开的Wiki等。
|
||||
|
||||
而让员工拥有权限和信任,可以让员工以主人翁的感受去施展拳脚,最大限度地激发其内驱力。这方面,Facebook的实践包括,信任员工进行一定额度下的软件自行采购、鼓励互相贡献代码,以及建设宽松的容错环境等。
|
||||
|
||||
从我个人在国内推行文化的实践经验来看,国内大多数软件公司,是适合让员工拥有更多的信息和权限的。而且我也看到,国内越来越多的公司意识到了这一点,在逐渐放开信息和权限,从而提高员工的内驱力和团队的研发效能。
|
||||
|
||||
我相信,我们会逐步找到更合适自己环境的平衡点,打造高研发效能的公司。
|
||||
|
||||
## 思考题
|
||||
|
||||
信息开放的确会导致信息泄露。今年10月份,Facebook出现了一次Q&A被录音并泄露的事件,你知道扎克伯格是怎么处理的吗?你还能想到更好的处理方式吗?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
149
极客时间专栏/研发效率破局之道/管理和文化/36 | Facebook工程师文化实践三大支柱之三绩效调节.md
Normal file
149
极客时间专栏/研发效率破局之道/管理和文化/36 | Facebook工程师文化实践三大支柱之三绩效调节.md
Normal 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鼓励员工公开自己对别人的评价,也确实有部分员工这样做了。你觉得,这样做的利弊是什么呢?
|
||||
|
||||
感谢你的收听,欢迎你在评论区给我留言分享你的观点,也欢迎你把这篇文章分享给更多的朋友一起阅读。我们下期再见!
|
||||
|
||||
|
||||
43
极客时间专栏/研发效率破局之道/结束语/结束语 | 超越昨天的自己,享受成长的快乐.md
Normal file
43
极客时间专栏/研发效率破局之道/结束语/结束语 | 超越昨天的自己,享受成长的快乐.md
Normal 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这样的高效能公司,也体验过研发效能不那么好的公司,特别清楚高效能可以带来公司业务发展和个人成长的质变。这,对国内正在从野蛮生长到精耕细作转变的软件行业,作用尤其巨大。
|
||||
|
||||
从留言反馈来看,我确实看到了这份价值。所以在这里,我要感谢你们的陪伴,你们的每一条留言,都是我坚持创作的巨大动力。与你们的沟通,是一件非常快乐的事情!也正因为如此,我几乎对每一条评论都做了详细解答。
|
||||
|
||||
除了能够产生价值外,**这个过程也在帮我不断提高自己**。
|
||||
|
||||
在我看来,学习包括三个层次:
|
||||
|
||||
- 第一层是被动学习,效果一般;
|
||||
- 第二层是动手实践;
|
||||
- 第三层是把知识教给别人,效果最好。
|
||||
|
||||
从专栏的创作中,我体会尤其深刻。为了保证逻辑的严谨性,我经常会从“你”的角度问自己一些问题,以此来发现盲点,然后做更深一步的调研。文章上线之后,你的提问也驱使我更进一步地思考。毫无疑问,这都加深了我对问题的理解。
|
||||
|
||||
所以,我希望你在学习了这些方法论和实践后,能够学以致用,提高自己和团队的研发效能,然后更进一步地,你能够把它们传播到你的团队中,影响到你的下级同事甚至是老板。这样一来,这些方法论和实践,就能产生更大的价值,而你对它们的理解也会更深刻。
|
||||
|
||||
虽然专栏已经更新结束了,但我们的交流仍会继续。如果你在实践这些方法论的过程中,有什么问题和想法的话,都可以直接留言给我,我会继续关注并回复你的留言。
|
||||
|
||||
“**超越昨天的自己,享受成长的快乐**”,是我的座右铭。每天进步一点点,除了能够产生价值外,学习和成长本身就能给我们带来巨大的快乐。这,也是我一直喜欢研发效能的重要原因。任何一个工作流程、工程方法都有提高的空间,我们需要做的,就是去发现这些可以改进的地方,然后对值得提高的部分进行优化。
|
||||
|
||||
其实,国内的软件行业,值得优化的地方比比皆是。我一直相信,国内软件研发人员的能力和创造性,绝不亚于硅谷那些高效能公司。只要我们的方向对了,并不断提高,就一定可以大幅提高团队和个人的研发效能,从而把时间花在最值得的地方。加油,我们一定可以!
|
||||
|
||||
最后,祝愿你在这个过程中,也能不断超越昨天的自己,享受成长的快乐!
|
||||
|
||||
|
||||
10
极客时间专栏/研发效率破局之道/结束语/结课测试 | 关于研发效率的这些知识,你都掌握了吗?.md
Normal file
10
极客时间专栏/研发效率破局之道/结束语/结课测试 | 关于研发效率的这些知识,你都掌握了吗?.md
Normal 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&exam_id=287)
|
||||
Reference in New Issue
Block a user