高效研发:硅谷研发效能方法与实践
葛俊
读书笔记
◆ 前言 什么是研发效能,为什么要关注研发效能
又比如,社交网络出现Bug的时候,调测起来非常麻烦,因为要复现Bug场景中错综复杂的社交网络数据困难且耗时。但Facebook采用开发环境与生产环境共享一套数据的方法,使开发人员可以非常方便地在自己的机器上复现Bug,以进行调测。当然,这样的数据共享机制背后有着强大的技术和管理支撑来规避风险。
2010到2013年,我在Facebook基础平台团队的内部工具组工作。作为核心成员,我研发并开源了研发工具套件Phabricator。2013到2015年,我又作为效能工具的使用者参与了Facebook对外产品的研发。这几年的工作,让我对Facebook如何提高研发效能有了越来越清晰的理解,认识到研发效能的提高需要整个公司在研发流程、工程方法、个人效能和文化管理等方面进行精心设计。离开Facebook之后,我在硅谷的Stand Technologies公司(后文简称Stand)、国内的两家创业公司以及华为等担任过技术总负责人、CTO、技术专家和团队主管等,带领百人技术团队进行研发。
比如,2017到2018年,我在华为开发工具部主导开发下一代集成开发环境,旨在为软件开发工程师提供全栈的端云一体工具平台,为2万多名开发人员服务,从而提高公司整体的研发效能。同时,我也尝试将研发效能的工程实践引入华为。比如,我在团队组织了几次黑客马拉松(Hackathon)活动,平均10个开发人员就产出一个项目,每10个项目中就有1.5个成功落地。
工作17年来,我在研发效能团队工作过,也在产品团队中推行过研发效能,涉及国内外不同类型、不同规模的公司,所以对怎样在一个公司或者团队中引入效能实践有比较丰富的经验。每个开发人员都应该提高自己的效能,只有这样才能持续学习、持续提高,避免被业务拖着跑。
▪ 研发流程优化(第16~21章)。这一部分讲解研发流程优化的基本目标和原则、代码优化、分支管理、DevOps、团队协同等话题,希望帮助读者深入理解研发过程中的关键流程以及流程优化的基本原则,使读者能够针对自己的实际情况,找到最合适的工程实践,让软件开发的整个流程更加顺畅、高效。
▪ 团队高效研发实践(第22~30章)。这一部分讲解团队高效研发实践过程中各关键步骤的高效工程方法,内容涉及研发环境搭建、代码审查、合理处理技术债、开源利弊分析、测试等,同时对研发流程及工程方法的趋势进行解读和展望,希望帮助读者加深对这些具体工程方法的理解,并学会正确地使用这些方法。
◆ 1.1 使用黄金圈原则
1.1 使用黄金圈原则
在学习方法论的时候,我推荐使用美国著名作家、企业顾问西蒙·斯涅克(Simon Sinek)总结的Why-How-What黄金圈原则,运用该原则包含的结构性思考方法学习方法论的思想、原则,并最终选择或者定制适合自己的具体实践方法。
Why-How-What黄金圈原则包括三个同心圆(参见图1-1):最里面的圆是Why,是这个方法论的目标,也即最终要解决的问题;中间的圆是How,指的是这个方法论的原则、指导思想;最外层的圆是What,指的是这个方法论的具体实践。
◆ 2.4 研发活动的本质
2.4 研发活动的本质
要想提高研发效能,我们需要深入了解研发活动的本质,从纷乱的表象和层出不穷的方法中看到隐藏的模型,找到基本原则,然后从这些原则出发,具体问题具体分析,最终找到合适的方法。这是由软件研发的灵活性所决定的。软件研发的灵活性决定了在软件研发的实践过程中会出现各种情况,我们必须洞察本质,才能随机应变。
Facebook正是这样做的。Facebook针对研发制定了一些基本原则,很多研发实践遵循这些基本原则。比如,Facebook有一个叫作“不要阻碍开发人员”(Don't Block Developer)的基本原则贯穿于公司的大量研发和管理实践中。下面用两个具体场景来解释Facebook是如何将这一原则应用到日常研发活动中的。
1)本地构建脚本的运行速度要足够快。开发人员在写完代码之后,都要运行一个脚本进行构建,把新做的改动在自己的开发机器沙盒环境中运行起来,以方便做一些基本检查。2)节点之间关系的灵活性。比如流水线上的多个节点可以互相融合。
3)每个节点的灵活性。每一个生产环节都会不断涌现出新的生产方式/方法。
4)每个节点上的开发人员的灵活性。与传统制造业不同,软件研发流水线上的每一个工作人员,也就是每个开发人员都有很强的灵活性,主要表现在对一个相同的功能,可以选择很多不同的方式、工具来实现。
与流水线相比,灵活性使得研发效能提高有更多的发挥空间。高研发效能的很多方法都是针对灵活性制定的。
1)最终产品目标的灵活性。在精益开发实践中,常常使用MVP(Minimal Viable Product,最小可行性产品)来不断验证产品假设,经过不断调整,最终形成产品。
2)节点之间关系的灵活性。DevOps就是在模糊节点之间的边界,有一些环节会融入其他环节当中,甚至有一些环节会被直接删除。
◆ 3.6 正确使用效能度量的方法
3.6 正确使用效能度量的方法
有了上面的原则,下面给出几种正确使用效能度量的方法。
目标驱动,选择正确的度量指标
度量不是目的,只是手段。我们首先要明确需要解决什么问题,然后根据这个目标,寻找合适的指标和方法进行度量。这就要求我们对效能度量的分类有一个比较系统的了解。我们先来看一下传统的研发效能度量方式。
传统的研发效能通常称为研发效率,更专注研发速度,即交付产品的速度。这种定义方式对研发速度和质量进行了拆分。然而这两者密不可分,必须放到一起综合考虑。因为如果只有速度而没有质量,那么交付的内容价值就会大大降低。研发效能的定义把质量也包括了进来,所以我们对效能的度量也包括对质量的度量。
质量是一个比较大的话题,这里只对传统的质量度量做一个简单的分类供参考。业界对软件质量一直有系统性的研究,并且在不断演变。比较权威的标准有ISO9126-3和由它衍生的ISO25000:2005。基于这些标准,IT软件质量联盟(Consortium for IT Software Quality,CISQ)把软件质量分为以下5类:
◆ 4.1 以终为始,寻找最重要的任务
▪ 预先定义的任务是否还需要做?
▪ 有没有其他更重要的任务应该替代当前手上的任务?
▪ 临时产生的干扰任务怎么处理?需要我来处理吗?
这些思考可以帮助我们更好地从当前任务的细节中抽身出来,让我们思路更清晰,避免把时间花在低优先级的任务上,提高准确性,进而提高投入产出比。
4.1.2 聚焦目标,以终为始
聚焦目标,以终为始,为目标服务的任务才最重要。
作为高效开发人员,常见的目标包括业务成功、帮助团队、个人成长这三个。痛定思痛,我后来下决心做减法。通过长期的习惯培养,我逐渐能够比较坚决地对任务进行筛选,从而把时间花在最重要的任务上了。过程不易,但效果很好。
我在提高筛选能力的过程中发现“数字3原则”很有效,即强制把要做的事、要达到的目标,限制在3个以内。
我曾参加过一个增进自我了解的工作坊,其间有一个练习,帮助我们明确自己最关注的道德品质。首先,我们要在一个有50项品质的列表上勾选出25项自认为最重要的;然后,在这25项品质中,再选出10项最重要的;最后,从这10项里再筛选出3项。第一轮筛选很轻松,第二轮筛选时就有了一些难度,到了第三轮10选3时真的非常困难。这10项品质我觉得都很重要,但规则强迫我对它们进行排序,放弃那些不是我价值观中最核心的原则。我从来没有想过这样一个选择练习就能够让我如此痛苦。然而当我做出决定之后,我意识到少即是多的重要性:我对自己的了解竟然通过这样一个练习明显加深了。
◆ 4.2 追根究底,寻找最高效的解决方案
4.2 追根究底,寻找最高效的解决方案
明确了最重要的任务,下一步是寻找最高效的解决方案。
首先回顾一下研发人员和产品经理合作的典型方式:产品经理决定如何在业务上满足用户需求,设计业务解决方案;研发人员则从产品经理手中接过业务解决方案,开始技术上的设计和实现。
这种工作方式,由于研发人员没有关注解决方案的设计,所以有很大的局限性。作为研发人员,我们对技术实现最熟悉,所以如果我们能把业务目标和技术实现结合起来思考,常常能对业务的解决方案做出一些改进,甚至重新设计出更好的方案。而这样的改进,对个人研发效能的提高帮助非常大。
这里举一个我自己亲身经历的例子。我在做Phabricator项目时,曾收到一个任务:解决用户进行内部讨论时模态弹窗遮挡弹窗下代码的问题。此后,我在接到任何任务时,都会先考虑它到底要解决什么问题,有没有更好的解决方案。花些时间深入思考,往往能节省很多工作时间。
开发人员也要对业务有一定的了解。面对任务的时候,要多问几个为什么,与产品经理和团队成员充分沟通,了解到底要解决什么问题,只有这样,我们才能以解决问题为出发点,找到最高效的解决方案。
◆ 4.3 高效沟通,利用信息的准确传递来寻找、调整目标
4.3.1 同理心原则
绝大部分人在沟通的时候都是在考虑自己。有研究数据表明,只有10%的人在沟通的时候会做到真正的倾听,其他绝大多数都是在等待轮到自己说话的机会。
然而,沟通是一个双方交流的过程,我们沟通的目的往往是希望在对方身上起作用,所以要从对方的角度考虑如何沟通。具体来说,我们需要做到至少以下三个方面:
▪ 了解对方的知识背景,从而使用他能理解的语言去沟通;
▪ 了解对方想要知道什么,从而能够不绕弯路,高效答复;
◆ 4.4 管理者视角
如果团队没有采用全栈研发模式,应该鼓励研发人员多参与到相关环节的讨论中去。比如让研发人员参与产品设计以及测试方案的讨论,让测试团队参与研发人员的设计方案讨论以及运维人员的部署方案相关讨论。
◆ 6.3 对关键活动进行优化
我们面对的世界非常复杂,但大脑只能同时处理有限的信息,这是人类面对的一个基本矛盾。我们应该怎样应对这个有限与复杂之间的矛盾呢?
最好的办法是把一个系统拆分为几个子系统,每个子系统涵盖某一方面的内容,并将其复杂性隐藏其中,只对外暴露关键信息。这样,我们在研究这个系统的时候,就无须考虑其子系统的细节,从而能够对整个系统进行有效的思考。如果我们需要深入了解某一个子系统,打开这个子系统来看即可。以此类推,如果这个子系统还是很复杂,我们可以再对其进行拆分。这样一来,在任何时候,我们思考时面对的元素都是有限的,复杂度下降到了大脑的能力范围之内,从而更高效地完成对一个复杂系统的理解和处理。
◆ 9.1 Git和代码原子性
9.1 Git和代码原子性
毫无疑问,Git是当前最流行的代码仓管理系统,可以说是开发人员的必备技能。它非常强大,使用得当的话将有助于个人效能的大幅提升。这其中最典型的例子就是它可以帮助我们提升代码提交的原子性。
前面的章节已经介绍过代码提交的原子性,这里给出详细定义。代码提交的原子性指的是一个提交包含以下三个特点:
▪ 提交包含一个不可分割的特性、子特性、修复或者优化;
▪ 提交大小合适读者阅读;
▪ 代码入库不会影响分支上的功能。
之所以要强调代码提交的原子性,是因为它有以下几大好处:代码提交的原子性指的是一个提交包含以下三个特点:
▪ 提交包含一个不可分割的特性、子特性、修复或者优化;
▪ 提交大小合适读者阅读;
▪ 代码入库不会影响分支上的功能。
◆ 10.3 小结
第一种工作流是在一个单独的分支上进行多个需求的开发。总结来讲,具体的工作方法是把每一个需求的提交都拆分为比较小的原子性提交,并使用git rebase -i,把可以进行质量检查的提交,放到提交链的最底部,也就是最接近origin/master的地方,然后发送到代码检查系统进行检查,之后继续在提交链的其他提交处工作。如果提交没有通过检查,则对它进行修改后再提交检查;如果检查通过,则马上把它推送到远端代码仓的共享分支去。在等待代码检查结果时,继续在提交链的其他提交处工作。这个过程如图10-19所示。
图10-19 在单独的分支上进行多个需求的开发
第二种工作流是使用多个分支来开发多个需求,每个分支对应一个需求。与单分支开发流程类似,我们尽快把当前可以进行代码检查的提交放到离origin/master最近的地方并发送到代码检查系统;然后在代码检查时,继续开发其他需求。只是,在切换工作任务时,需要切换分支。这个过程如图10-20所示。
图10-20 使用多个分支来开发多个需求
这两种工作流,无论哪一种都能大大促进代码提交的原子性,从而同时提高个人及团队的研发效能。为了方便参考,我把文中的案例代码放到了GitHub上的git-atomic-demo代码仓里,并标注出各个提交状态产生的分支。比如,single-branch-step-14就是单分支工作流的第14个状态,multi-branch-step-4就是多分支工作流的第4个状态。
两种工作流各有利弊。单分支工作流的好处是,不需要切换分支,可以顺手解决一些缺陷修复,但缺点是rebase操作多,产生冲突的可能性大。而多分支工作流的好处是,一个分支只对应一个需求,相对简单、清晰,rebase操作较少,产生冲突的可能性小,但缺点是没有单分支开发方式灵活。
◆ 18.1 三个“持续”的定义和作用
CI/CD流水线,能够大大提高代码入库的速度和质量,是硅谷互联网公司做到高效研发的必要条件。下面介绍CI/CD流水线的原则,并以Facebook的具体实践为例帮助大家理解。我们将重点介绍搭建原则,至于具体如何搭建,通常采用持续集成工具+代码仓管理系统+检查工具+测试工具组合的方式,比如Jenkins+GitLab+SonarQube+Linter+UnitTest,感兴趣的读者可自行查阅相关内容。这里给出一篇文章——《集成GitLab、Jenkins与Sonar实现代码自动检查》作为参考。
◆ 点评
推荐
打赏作者