原文:https://engineering.linkedin.com/blog/2016/02/3x3--speeding-up-mobile-releases
译者说:LinkedIn 3X3 模式真是破釜沉舟,3 小时窗口,能做的事情寥寥无几,在这么短时间内把控质量。在一开始肯定出错频频,这么勇敢地试错,不知道出了问题怪谁?唉,无限感慨,不知道在国内公司,哪个有这种魄力。
最近,LinkedIn 发布了 Voyager,一个全新版本全平台(Android,iOS 和移动网页)的旗舰应用。Voyager 是超过 250 多个工程师花了超过一年多时间研发出来的成果。趁此机会,我们趁机重新审视了我们在 LinkedIn 工作流程,不仅仅从产品角度,也从工程师角度出发。
在 Voyager 之前,我们平均每月发布一次 LinkedIn 应用。在每次发布前,我们挑选一个 RC 版本,然后交付给测试团队,测试团队执行一次回归工作,这个测试回归工作完全是手动完成,花费四天时间(没有说多少人,无法评价这个工作量的情况)。如果测试同学发现 bug,我们就做一个修复,然后再扔给测试。如果 bug 挺严重的话,那需要再一轮的回归测试。所有开发工程师都需要赶在月度 RC deadline 之前提交他们的代码,否则他们就等一个月才能提交他们的新功能或者 bug 修复。产品经理和市场合作伙伴必须在每个发布过程中制定他们的计划,然后祈祷每次发布都能准时。由于我们一年只有 12 次发布,对用户反馈的问题迭代就非常困难。所以这样的配置其实不是很理想,我们想要一个由产品决定的发布节奏,而不是由开发测试约束决定。
在传统互联网前后端研发的世界里,持续集成是司空见惯的事情,但是对于原生移动应用,仍旧使用的比较少。对于传统的持续集成,通常我们会直接提交变动到产品中去。但是对于原生应用,这个是不现实的。原生应用需要在苹果应用商店和谷歌应用商店发布,尤其是苹果应用商店,会有一个礼拜的审核时间。发布之后,才能被用户下载使用。从工程研发角度,我们需要摆脱以往每个发布都手动验证的模式。我们也希望我们的产品合作伙伴可以随时随地的发布(不影响用户)。为此,我们制定了一个叫 “3x3” 的规则。
一天发布三次,代码提交到用户能使用到不超过三小时。好屌!
当然,我们不可能每三小时提交给应用商店。但是我们可以给我们内部的用户,比如我们的员工,一天 n 个版本(苦逼)。
为啥要三小时目标呢?主要有两个主要的原因:
首先,三小时肯定来不及用手动回归了,这样就再也不可能手动验证发布了。真是破釜沉舟啊。。
然后,三小时肯定来不及做一个完整的端到端测试。正常来说,测试的越多越好。但是记住这个目标,我们不仅仅是使我们的发布流程自动化,而且还要让我们迭代的更快。如果开发花 20% 的时间写新功能,剩余的时间都用来重构脆弱的,代码一变化,测试就挂的 UI 自动化测试的话,那我们肯定完不成我们想要的目标。我们更希望产品 owner 来决定哪些业务路径比较重要,需要使用 UI 测试覆盖,而对于那些边界用例,反向用例,则用单元测试覆盖即可,这样会变得更快,也更加容易维护。
为了达到我们的目标。我们需要一条完整的自动化管道,从代码提交到产品发布中的每一步。这条管道要足够快,以匹配我们的这个三小时窗口,同时也要足够的可靠,这样我们发布才有信心。
我们从静态代码分析,code style 检查,编译代码开始。接着,我们打包出来,在 Android 和 iOS 的不同版本上运行单元测试和 UI 测试。最后,我们做最基础的升级测试,确保用户可以从之前的稳定版本升级到现在这个版本,不会有严重的错误和崩溃。
当上面所有的测试都通过了,我们认为这个版本是 “known good”。每隔三小时,部署流程会挑选一个最近的 “known good” 版本发布给我们的内部 Voyager 团队的小白鼠用户。对于 Android,我们用 Google Play 的 alpha 测试功能,外加 console API 来自动化整个发布流程。对于 iOS,我们使用企业分发来定制 alpha 测试流程。每个礼拜,我们挑选一个最新的 “known good” 版本,提交给 Google Play 和 App Store。除此之外,我们对每个新功能都使用了开关,一旦发现问题,通过这个开关就可以快速关闭这个新功能。这样就很大程度地降低了用户遇到问题的风险。对于 Android 和 iOS,我们都提供了非常详细的 release note 给产品经理和市场团队,这样他们可以创建公开的 release note。
有一些合作团队没有办法跟上我们 3x3 的流程。在以前的发布流程中,有一些合作团队需要对每一个版本进行 review 和签发。比如,本地化团队,需要保证新功能的翻译是否正确显示,没有任何语言方面的错误。如果我们想把去除这些需要手动检查的步骤的话,我们需要找到其他的办法来做这些工作。
我们和本地化团队共建,把一些常见的国际化错误进行分解。把不恰当使用本地化字符串模板产生的错误标识出来,通过静态分析检查确保开发不会乱传参数给字符串模板导致崩溃。而对于那些特殊字符或者超级长,超级短的文本,渲染出来会破坏 UI 布局的问题,我们通过 “布局测试” 来捕获这些 bug。
所谓布局测试就是从应用中抽一个视图出来,在上面把各种数据的排列放上去。然后验证下,有没有重叠啊,文本有没有被切断啊。最后一种本地化的问题是语义上的错误,这个就没办法了。幸好,这些问题比较少,本地化团队定期抽查即可。
想了解我们在 iOS 上如何测试布局,请看这篇文章!
我们在实现这些检查的时候,发现最大的问题在于:打包和运行 UI 测试。
我们的 Android 和 iOS 项目会构建多个版本的包,包括 debug 版本,特殊的内部 beta/enterprise 版本和不同设备的不同渠道包。这些不同的构建在 iOS 这边会增加差不多 40 分钟时间,Android 则有 15 分钟左右。为了加速这些东西,我们开发了一个流程,分发版本到持续集成池里的不同的机器。主节点构建最小集,用来跑测试。子任务则分发给其他节点,构建剩余的版本,这样 UI 测试就可以并行执行。
iOS 这边,由于新的 Swift 编译很慢,我们遇到了很多问题。我们花了大量时间找出了原因,加速了我们的构建,提速了至少 4 倍。苹果也修复了很多我们汇报给苹果的性能和稳定性的问题。
UI 测试层面,Android 上我们利用谷歌的 Espresso 工具,iOS 则用 Square 的 KIF。这两个工具都已经相当快了,但是我们想在不同 Android/iOS API 上和不同的配置上(比如,左到右,右到左的布局)运行大量的测试,这个还不够快。和我们的版本分发理念一样,我们开发了一个系统来分发我们的测试集到不同的机器上运行,然后收集所有运行的结果。在 Android 上,我们还可以在一台机器上运行多个模拟器(15 个),这样大大地增加了我们的能力和扩展性。
如果 flaky 测试不能发现问题,那就没啥用。Flaky 测试会降低整个测试集的信心,开发会认为这就是测试执行不稳定而已,从而忽略某些应该重视的错误。
想知道更多的细节,可以看我们其他的文章。比如Test Stability - How We Make UI Tests Stable
,UI Automation: Keep it Functional – and Stable!。
随着我们 2016 年的推进,我们将会持续提高我们的 3X3 管道的速度和稳定性。我们想让我们的开发迭代的更快,开发出世界级别的产品,而且还不需要被发布计划和死线所烦恼。
以下是废话,不翻译了
We’re excited to share many of our learnings with the wider mobile development community, both through blog posts like this one and by releasing some of the tools we’ve developed as open source libraries. Stay tuned to the LinkedIn Engineering Blog for more information soon.
Most importantly, now that we have released Voyager to the world, we are already reaping the benefits of our infrastructure investments. The faster development cycle enables us to make rapid improvements to the mobile LinkedIn application, bringing even more value to our members.
We have accomplished all this through a dedicated company-wide collaboration of teams across LinkedIn, including Tools, Testing, Localization, Marketing, Mobile Infrastructure, and more. Thanks to everyone for your effort!