郭金:工程化目标是提升开发效率,保障应用质量。“大”前端工程化是指移动端、前端在项目规模、工程复杂度、快速迭代等相同背景下,对一些共性问题的思考。比如,如何保障多人协同并行开发效率、如何控制质量影响范围、如何进行依赖管理 (包管理)、如何规范研发流程以及在快速迭代下如何控制劣化等。
总的来说,前端、移动端在容器化、组件化 (模块化)、依赖管理 (包管理)、自动化、流程规范等方面都有很多相似性。
针对这些问题我们会产生一些思考: 既然面临同样的问题,那能不能用同样的解决方案,甚至同样的技术栈。这时候有一些全栈工程师跳出来,他们游走在各种技术角色之间,充分吸收各端技术优势,让各端技术优势互补。
郭金:1. 定义架构层级,并规范底层组件不能反向依赖上层组件,越往下层对组件接口稳定性、依赖的合理性以及质量要求就要更高;
全面的业务组件化,利用组件化框架提供的容器,分发、依赖注入、数据拆分等框架做到逻辑、资源、数据各有归属,逻辑、接口边界清晰,组件必须有明确的功能定位。
做好上面这些,也就达到壳化的目标。
最后,需要有防劣化的机制,这也是最难做到的。百度 App 是通过 EasyBox 工具链和 Tekes 平台发布通知系统来做到这一点的。通过 EasyBox 做到组件间的编译隔离,层级反向依赖防控;通过 Tekes 来做依赖、接口劣化防控。
郭金:差异主要体现在大型 App 的团队规模、业务规模 (包含非基础库类的接入业务规模) 比较大,迭代速度快、技术形态多以及有多样性的目标。大型 App 的关注点会比小型 App 多,最基本的要求是保障并行开发,提升研发效率,以及不同组件间相对能做到故障隔离来保障整体 App 的质量;
更进一步是多技术形态下基础能力复用、矩阵产品孵化;
还有技术组件对外输出 (包含小程序开源),这些输出很多不是单一组件对外输出,而是成组对外输出,对外输出的组件不仅要适应百度 App 的架构与环境,也要适应不同的宿主架构和环境;
另外就是性能、体积因素的约束,问题快速定位能力的要求。
复杂度就体现在这样的背景和多样性的目标上。毫不夸张地说,在大型 App 里做架构和工程能力就是要让大象跳舞。
郭金:组件化是一个体系化的工作,需要通过上面讲到的一系列的工作来一步步落实。
广义的组件化框架指一切具有依赖注入、分发、容器化、数据拆分、资源拆分、组间通信性质的非功能性组件。这些组件的主要目的是保障其他组件逻辑、资源、数据各有组件归属,组件化框架是并行开发的基础保障。
关于依赖管理工具,很多人会混淆依赖管理工具和组件化框架,依赖管理工具在一定程度上能保障组件的逻辑边界,也就是控制开放接口,也能协助组件明确依赖。依赖管理工具更多是构建系统的一部分。
郭金:可量化部分的数据,首先是我们矩阵产品组件复用率能达到 55%,矩阵产品同步主线的时间也由原来的 5 人周降低到 1 人周。
不可量化的数据,我觉得价值更大。首先提升了研发效率,研发效率的提升主要体现在几个方面:第一个是复杂度内敛,不对外暴露复杂度;第二个是并行开发效率,组件化具有分发、容器化等性质,很多集中式管理的逻辑、事件会通过组件化框架分发到各目标组件,减少了组间耦合和交互;第三个体现在复用上,工程拆分后,沉淀了大量的服务组件和基础库组件,也就是有很多可用的轮子。
其次是在质量上,逻辑各有归属,加上组件化的分发机制,确保各组件间相对隔离,单个组件的故障范围能内敛到组间内部;
最后,拆分的组件是一个编译单元,是启动速度、体积等指标的量化单位,方便快速定位性能、体积等问题。
郭金:EasyBox 综合解决方案包含三部分:多仓库管理工具 MGIT、二进制管理、构建系统。多仓库部分主要是团队规模变大,通过代码评审权限分离来保障质量,另外也是多产品线仓库粒度源码复用的需求推动的;二进制管理部分是从编译速度和对外输出稳定的发布版本这两个方面考虑的;最后构建系统支持分组件源码 / 二进制切换模式,这既满足了开发的灵活性,也统一了多方合作的开发模式。
郭金:这个主要是从容错和易用性两个方面考虑的。我们强化了同名分支的原则,来保障多仓库间同步。易用性上操作命令基本和 git 对齐,另外对很多输出做了归类整合,让研发同学能像操作单仓库一样操作多仓库,降低上手难度。
郭金:CocoaPods 是一个很好的依赖管理工具,虽然 Apple 自己出了 SPM,但是仅支持 SWIFT 包管理,CocoaPods 是 iOS 平台事实上的依赖管理标准。其实,在开始做技术选型的时候,是基于 CocoaPods 修改还是自建我们也很纠结。最后几方面考虑,我们决定自建:首先是编译隔离,CocoaPods 组件间的访问相对比较随意;第二,当时我们也参考了 FaceBook 的 Buck 方案,去工程文件化,早期百度 App 工程文件有 5~6 万行,且可读性较差,去工程文件化大幅降低了工程文件的合并成本;第三,我们要保障架构有自底向上的输出复用能力,要做架构层级的反向依赖控制;最后,我们要实现分组件源码 / 二进制一键切换的开发模式。基于这几个原因,即使基于 CocoaPods,也需要大改,最后决定自建,这样我们几个子系统间还能较好地做好分工配合。
我们早期上线的时候,因为一些体验问题,很多同学会有“为什么不用 CocoaPods”的疑问,当我们逐步优化后,问这个问题的同学越来越少,大家也越来越认可 EasyBox 才是最适合我们的解决方案。
InfoQ:EasyBox 综合解决方案落地后,收益和成果有哪些?
郭金:可量化的部分,首先,我们把原来的工程拆分为多仓库,并且 iOS 做到了去工程文件化,能做到并行上车 (合并到 master ),并行上车率 46%,上车耗时也降到了原来的 1/3,每次迭代大约节省了 20~30 个小时;其次,实现组件源码 / 二进制的切换的开发模式,编译速度提升了 74.8%~85.8%(不同性能的机器从 16 分 12 秒优化到 2 分 18 秒,或 6 分 30 秒到 1 分 38 秒);最后,不可量化部分,我们升级了构建系统,让矩阵产品可以组件仓库粒度源码复用,实现了 App 生产线的能力,也提供了架构的灵活度,即降低了架构升级成本。
郭金:首先是我们实现组件二进制化后,需要经常发布组件的二进制版本,因为本地发布不安全,并且百度 App 也有对外输出稳定二进制版本的诉求,我们考虑能将这一流程自动化,当然一些 ChangeLog 还是需要手工编辑的;其次是基于组件化劣化防控的目标,我们希望有个平台能把各个组件依赖、接口变更、对外文档的情况展示出来,有劣化的话及时通知对应的研发同事优化;最后是希望能把研发流程体系化,比如及时产出性能、体积、单测报表。
这个方案上线后,我们建立了组件二进制版本规范,矩阵产品有了二进制版本复用的基础,也统一了百度 App 的研发和合作团队研发的开发模式,编译成功率也提升了很多。
Tekes 平台的目标还是把整个研发流程体系化,更好的保障研发效率、矩阵产品孵化能力以及研发质量不劣化。
郭金,百度 App 资深研发工程师,2014 年入职百度,先后负责社交化、基础性能等技术方向,目前负责百度 App 客户端工程与架构方向。在 App 复杂的背景和多样化的技术目标要求下,设计并完成百度 App 架构与工程能力升级,并着力于打造研发流程一体化平台,实现并行开发、快速迭代、高效复用。
在即将到来的 GMTC 深圳 2019 上,郭金老师还会具体分享,百度大型 App 架构设计与拆分方法、超级 App 高效工程能力保障方法、组件全量二进制实现路径、组件二进制自动发布的流程、矩阵产品工程孵化模式等。
除了郭金老师的分享,本次 GMTC 我们还设置了小程序挑战与应对、音视频技术、Serverless 实践、前端测试与安全、大前端工程化、Flutter 实战、新兴编程语言、团队建设与管理等热门技术专场,目前大会 9 折购票,点击「阅读原文」了解大会日程。有任何问题欢迎联系票务鱼丸:13269078023(微信同号)