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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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