百度如何看待软件工程能力 百度是一家技术公司,研发工程能力的高低直接影响公司的持久创新力和公司在市场上的作为。只有不懈追求卓越的工程能力,才能够带来长期的核心竞争力,才能为每个用户、每个企业客户以及整个社会创造价值。

百度软件研发体量

百度内部有多个业务形态各异,团队规模不同的产品线,下面一系列数据出自 2018 年工程效能部的数据统计:
image.png

百度工程能力的发展历程

百度工程能力的发展历程,大致经历了三个阶段, 分别是:

  • 以瀑布式和 CMMI 标准为研发模型的阶段
  • 以敏捷开发为主要研发模型的阶段
  • 全面拥抱 DevOps,云计算和 AI 的阶段

在每个阶段,百度在软件工程领域要解决的问题和面临的挑战各异,下面我来简单介绍一下:

瀑布式和 CMMI 阶段

在这个阶段,百度在软件工程领域要解决的首要问题是: 百度的软件研发管理对象是什么?对象之间的关系是什么?一个标准的,符合规范的研发流程是什么样的?
现在回顾起来,我们非常感谢那个时代工程效能部门的同事,为我们后续的管理奠定了坚实的基础,具体规范如下图:
百度工程能力提升的策略和实践 - 图2
从这张图上可以看到,我们当时定义了几个最基础的元素:

  • 最小的可发布代码集合—模块,以及代码和业务的对应关系—产品线
  • 服务发布的版本关系: 线上版本和测试版本
  • 围绕发布进行的一系列研发过程管理: 项目

当时的一切软件研发工作,是围绕着模块级别发布进行的: 每一个项目在启动的时候,RD(开发工程师)会从当前模块基于线上的 3 位版本拉一条分支,建立一个新的 4 位版本;
当开发完成后,RD 通过一个叫做”提测”的动作将构建产物传递给 QA(测试工程师), QA 在测试过程中发现的 BUG 会记录在当前的 4 位版本上,RD 修正后重新生成一个 4 位版本号再次交付 QA 直至最终测试完成,模块发布上线后,更新三位版本号,做为下一次拉取分支的基线。这样的流程也很好的支持了当时百度的主营业务—核心搜索。

敏捷开发阶段

时间前进到 2012 年,随时互联网思维的普及,更重要的是,移动互联网时代的到来,我们发现:
当时百度的软件研发效率跟不上市场竞争的需求了。2012 年 8 月,我们做过一个统计,当时百度单个模块的发布周期平均值是 21 天,而当时小米号称每周迭代。因此,当时我们要解决的首要问题是如何缩短模块的发布周期。
基于这个目标,我们引入的敏捷开发的概念,弱化项目的概念,强调产品的迭代。同时将原有的工具按照研发的不同阶段,拆分成三个独立的产品: 负责项目和产品管理的 icafe,负责代码和协同开发的 icode,负责持续交付的 ipipe,形成了目前研发工具链的雏形。我们也建立了百度的敏捷教练团队,引入了一批又一批业界优秀的软件工程人才,将敏捷开发的理念带入到产品线中。现在 DevOps 和敏捷圈中的各路英才,有相当一部分都曾经在百度工程效能部任职。
经过 4 年的努力,截止 2016 年,我们将百度的模块发布周期从 21 天,缩短到不到 6 天的样子。敏捷开发已经成为百度的主流研发模式,95%的模块从 SVN 迁移到了更加适应互联网研发模式的 GIT 上,我们发布了百度内部的研发方法论专辑: 百度方法+。
反思这个阶段,我们发现:

  • 虽然整体的交付周期缩短了,但是团队的交付能力并没有特别明显的提升
  • 一部分团队在专家实施改进并撤出之后,出现了不同程度上的倒退
  • 公司内部平台林立,技术上重复造轮子的现象严重

同时,DevOps,云计算,AI 成为主流的技术,我们面临着更大的挑战:

百度工程能力的提升策略

前面铺垫了这么多的内容,终于进入到我们今天要讲述的内容主体: 当云成为主流 IT 架构,以模型训练为代表的 AI 开发兴起,DevOps 成为软件研发的主流思想时,百度工程效能部如何继续带动百度工程能力的提升。
关于整体提升的策略,我们可以用下面一张图来表示:
百度工程能力提升的策略和实践 - 图3

能力策略—人

首先,我们来看一下人的方面,工程能力提升的根本还是人(工程师)本身的工程素养提升。即使我们有好的流程、方法和开发工具,如果开发者本身的能力和工程素养不够的话,工程效率也会非常低。
为了提升工程师的工程素养和工程能力,我们做了一些实践。例如招聘时,我们一定招优秀的工程师。新人入职之后,我们会为新同学提供工程师能力培养和文化建设的工作坊,这也我们一直在做的事情。工程师入职后,会有“码神训练营”,这个训练营除了介绍编码的规范及开发流程、工程实践之外,还会让大家在工具平台上实际操作,自主开发一个示例产品,促进大家在短时间内了解百度工程开发的流程和规范,建立工程规范意识。除此之外,公司内还有很多培养工程师文化的项目,比如 Hackathon ,还有 Good coder、Excellent Coder 认证等。

提升策略—数据篇

和 16 年以前最大的变化是,我们将数据也纳入了整体的提升策略中。在 DevOps 的理念中,有一个非常重要的核心理念: 建立反馈闭环。我们认为在软件研发过程中,从 RD 每天的拉取分支—coding—提交代码,到模块测试—系统测试,最终到服务发布,产品更新。都应该建立不同的闭环,而在每个闭环中产生的数据,应该以最高的时效性反馈给研发/测试/运维人员。并且不断的引导用户去使用数据,信任数据,最终通过数据的驱动实现团队工程能力的不断提升。
而在以往,这些数据在生产出来之后便被束之高阁,只有在定期汇报的时候才被拿出来使用,为了改变这个现状,我们首先明确了百度软件研发的一系列闭环:
百度工程能力提升的策略和实践 - 图4
从上面的图中可以看到,在我们的治理思路中,整个软件研发的过程被划分成 4 个闭环:

  • 开发闭环: 起始于 RD 建立 Feature Branch,终结于分支合入当前的 release branch,开发闭环持续时间以天为单位,通常是 1-3 天
  • 迭代闭环: 起始于每周的开发计划更新,终结于当前的 release branch 通过验收测试,迭代闭环持续时间以天为单位,通常是 5-10 天(1-2 周)
  • 项目闭环: 起始于一个 release plan 的启动,终结于当前的服务/产品上线后业务数据的 review,其中对于一部分产品来讲,项目闭环=迭代闭环;而对于移动端开发来讲,一个项目闭环包括了多个迭代闭环。项目闭环以周为单位,通常是 1-3 周。
  • 产品闭环: 结合 OKR 或业务阶段性目标,起始于目标的拆解和制订,终结于阶段性业务目标的 review。产品闭环的周期比较长,往往以月为单位。

在以上闭环中,我们认为下列数据是需要被相关人员关注并使用的:

  • 开发闭环: 代码冲突;CodeStyle;代码漏洞;CodeReivew 的反馈;单元测试的结果,覆盖率;模块测试的结果,覆盖率;流水线执行结果;流水线执行时长;需求的开发进展;PM(产品经理)功能验收的结论,没错,在百度我们提倡产品经理对每一个用户故事进行验收,而不是对迭代进行验收。
  • 迭代闭环: 分支的集成进展;系统测试结论;非功能性测试结论;测试中发现的 BUG 以及修复进展;团队的迭代速率;
  • 项目闭环: 线下 BUG 的修复进展;系统测试结论;非功能性测试结论;研发进度的累积图;线上服务的稳定性;上线后的业务效果;上线后用户的反馈;回顾会议的结论;
  • 产品闭环: 阶段性业务效果;产品下阶段的规划;技术下阶段的规划;团队交付的散点图;团队工程能力地图;

这么多的数据,我们是如何将其一一呈现在整个研发团队的视野中的呢?这就离不开工具的贡献了: 我们在 17 年,自建了研发数据仓库,前面提到几大研发工具平台: icafe, icode, ipipe,之前是每个产品保存自己的数据,建了数据仓库之后,我们将数据以下面示意图的形式组织在一起,形成了从需求到发布的完整研发数据链:
百度工程能力提升的策略和实践 - 图5
另外,除了这些基础数据之外,有大量的测试结果,覆盖率数据是如何被记录下来的呢?这就得益于持续交付平台 ipipe 的插件式架构设计:
百度工程能力提升的策略和实践 - 图6
百度内部有几十个不同类型的测试,发布,环境管理工具。为了让百度内部的用户更好的使用这些插件,iPipe 平台改善了底层的架构设计,使这些工具可以通过简单的改造接入 iPipe。做为改造的一部分,这些工具需要通过统一的数据格式将自己生产的研发数据导入到研发数据仓库。这样,研发和测试人员只需要关心自己的日常工作,而数据很自然的就保留了下来。
接下来,数据是怎样应用的呢?首先最频繁的应用在流水线的执行结果中:百度工程能力提升的策略和实践 - 图7
这是我在内部代码库提交的一次提交记录, 在图中看到我们配置了一条简单的流水线,只分为编译和测试两个阶段,现在展示的是编译阶段的所有 job。如图所示,用户可以非常直观的在流水线上看到每个 JOB 的执行结果。一旦有错误发生,iPipe 会通过邮件,即时通讯工具向用户推送消息。同时,在展示结果的同时,QA,RD 也能非常方便的观察每个阶段的执行时长,方便流水线的优化。
除了在流水线中应用,我们根据产品,团队,工程师的不同维度,将数据归纳整理:

产品维度:工程能力地图

百度工程能力提升的策略和实践 - 图8
这便是工程能力地图的主页面,如图所示: 工程能力地图以产品线(多个代码库)为维度,按照不同技术类型(Server, App, SDK)的代码库分类, 将产品线在整个研发过程中对管理,技术实践的采纳情况进行记录和评分,而所有的记录都来源于插件的数据录入。
也可以说,页面上每一个色块中的实践,背后都对应着一个标准的实践工具。数据每天进行更新,而产品线的研发经理,测试经理,研发总监根据工程能力地图上数据的变化趋势即可了解自己团队工程能力的改进进展。在百度内部,大多数产品线在建立自己 DevOps 实施目标时,已经将地图上得分的提升做为重要参考。
团队维度:交付周期散点图
百度工程能力提升的策略和实践 - 图9
交付周期散点图显示的是一个团度随着时间的推移,交付周期的变化趋势。怎样解读这张图表呢?横轴是时间轴,纵轴是交付周期(天为单位),图表上的每一个点都代表了一张需求卡片(统计粒度可自选)从建立到上线的确切时间流逝。从这张图上,我们可以解读到以下信息:

  • 交付周期的标准差: 在图上用浅蓝色阴影显示,阴影面积越小,代表该团队的交付周期越稳定,可预测性越高
  • 交付周期中位数的移动趋势: 在图上用绿色的曲线代替,该曲线代表了交付中位数的变化趋势,曲线越走低,代表交付周期的不断缩短
  • 团队的交付模式: 这个解读很有意思,从上面的图我们可以看到,在 5 月 19 日之前,该团队进行了多次不断的发布,可以认为是在持续的交付价值;但是到了 7 月 19 号之后,我们会发现交付周期较长的项目/需求开始出现,发布的时间点相对集中,可以认为团队在 5 月到 7 月集中做了几个大型的功能,并在 7 月下旬进行了集中的发布

工程师维度:工程师个人画像
百度工程能力提升的策略和实践 - 图10
如图,这是展示在代码托管平台 iCode 上的某位内部工程师的研发活动数据,上半部分表示了该工程师的提交频次,每次的代码提交量;下半部分的数据说明了具体的量化信息。这个图表是和我们内部所提倡的工程师文化是密切相关的。什么是百度工程师在工程方面应该做的事情,那就是持续的提交代码,并且积极参与代码的评审工作。每次工程师在申请晋级的时候,技术委员会会直接查看该工程师的这个页面,并做为晋级的重要参考。我们经常说,是牛 X 的工程师就把个人页面亮出来。

能力策略—技术篇

平台化治理

为了解决前文提到的技术重复造轮子问题,提升技术的复用,我们也进行了百度内部的平台的建设和治理
平台建设包括平台复用和源码复用这两方面。在平台复用方面,我们梳理出公司内部的几百个平台,将这些平台划分成若干个类别,每个类别都成立一个 TOC 组织来负责规划这类平台未来的发展。发展的目标就是让这类平台可用性加强、复用度提高,让平台本身的能力增强,以便让每个业务方能够更快速地使用平台去搭建自己的业务,提高工程效能。

内部开源

上面介绍平台复用会带来一个问题:如果多数平台逐渐收敛成少数平台后,许多需求都会涌向这些平台,会导致平台研发团队不能按时、高质量完成所有的需求,从而使研发效率下降。那么我们怎么在平台化的基础上去做更好的工程复用呢?
那就是源码复用。在代码管理平台(iCode)上,我们做了内部开源功能。在平台化治理的过程中,平台是否能够达标,有一个考察点是它能不能实现内部开源。如果一个平台的 API 已经对外开放,并且又做到了内部开源能够让其他团队贡献代码协作开发,我们才认为它是一个优秀的内部平台。

C++的依赖治理

百度 C/C++模块开发采用跨模块依赖的形式,其中 97%为固定版本依赖(表现为 Depend on TAG 和 Depend on BRANCH:VERSION 两种形式,本文统一使用 Depend on TAG 表示)。固定版本依赖会导致严重的版本碎片化问题,两年前我们做过一个统计: 百度 top 200 的基础库,在用的大约 5000 多个版本。
版本碎片化问题背后,是严重的技术债务: 维护基础库的同学很难保证不同版本间代码的兼容性,业务方为规避版本不兼容的问题不愿意升级依赖模块版本,导致基础模块发布新版本后,三个月内只有十分之一不到的团队主动升级。如此一来,一方面在基础库维护上浪费了大量不必要的人力,另一方面,大量的业务线也没有办法享受基础库升级带来的好处。
参考 Google 的 Head 依赖模式,即所有的类库引用方,都直接引用类库的 head 标签。考虑到我们内部改造的成本和业务方的稳定性,我们决定推出 stable 模式—即每个类库维护一个 stable 标签,所有的引用方都来引用 stable 标签,基础库的维护团队将基础库的更新发布到 stable 标签上,这样保证了基础库的稳定性,也极大的收敛了基础库的版本数量,减轻了团队负担。

凤巢检索系统的DevOps实践

image.png

分支策略

image.png
image.png
image.pngimage.png

需求管理

image.png

静态代码检查

image.png

构建

image.png
image.png

系统测试

image.png
image.png
image.png

监控

image.png
image.png

应用效果

image.png
image.png