This commit is contained in:
louzefeng
2024-07-09 18:38:56 +00:00
parent 8bafaef34d
commit bf99793fd0
6071 changed files with 1017944 additions and 0 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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