大家好!
我叫Vitaliy Bendik。 我是Lamoda的Android应用程序开发团队负责人。 2018年,我在Mosdroid Aluminium上发表了
一份报告 ,我想分享其笔录。

这将与我们如何保持移动应用程序的稳定性有关。 这对我们非常重要,因为我们的移动受众是数百万的用户。 此外,就我们在客户订单中的份额而言,应用程序已经远远超过了网站,台式机和移动版,并且iOS平台已绝对领先于台式机。
在报告中,我将告诉您:
- 我们所说的应用程序稳定性
- 关于我们的移动应用程序的体系结构;
- 关于我们使用的流程,实践和工具的信息。
那么,什么是
稳定的应用程序呢? 这是一个不会崩溃,不会挂起并且可以预期运行的应用程序。 当我说它没有下降时,是指至少95%-99%的用户没有下降。
建筑学

您可能已经猜到了,该图像显示了我们尝试遵守的纯体系结构。 作为Presentation层,我们使用MVP和一些附加功能,我将在下面进行讨论。
我们的移动应用程序适用于手机和平板电脑。 因此,布局通常不同,但由相似或相同的块组成。 在这方面,我们拥有诸如Widget之类的实体。 它允许您将活动或片段分解为较小的块,可以在其他屏幕中重复使用。 这是有道理的,因为从片段或活动中的代码的角度来看,几乎没有必要区分正在运行哪个UI的上下文。 这些代码片段可以呈现为一些抽象并可以重用。 这种方法在某种程度上让人联想到SoundCloud库
LightCycle 。


产品页面。 小部件元素示例至于presenter与模型的交互,这里的一切都是标准的:presenter通过交互器与应用程序的其余部分进行交互,无论是存储库还是管理器。

碰巧几个演示者需要彼此通信,交换数据。 为此,我们有一个协调者,可以将其视为多个演示者之间的共享交互者。

叠放
-我们使用Kotlin编写所有新代码,并使用Moxy作为MVP实现。
-作为DI,我们使用Dagger2 。
-与网络合作- 改造 。
-用于处理图片-Glide 。
-我们向New Relic添加了崩溃。
-我们也使用Lottie 。
-目前,我们正在积极使用Kotlin Coroutines 。
开发过程
我们坚持Git流程,也就是说,每个功能都在单独的功能分支中实现,该分支在进行代码审查后提交进行测试。
在测试人员成功完成测试之后,我们决定了该功能将要使用的版本,它将合并到主版本中。
当发布时间到来时(我们将每2周发布一次),将分配进行烟雾测试的rc分支,并运行测试用例。 之后,该功能合并到生产分支中,并在Google Play Beta中发布。

至于CI / CD,由于我们使用Atlassian堆栈,因此Bamboo充当构建服务器。
当开发人员创建请求请求时,构建任务将从Bamboo开始。 她从存储库中提取代码,在fastlane上运行脚本,该脚本收集应用程序,运行测试并将其报告给Slack。
如果测试人员为了测试功能而开始装配,则apk也将加载到HockeyApp中。
为了在Google Play Beta上发布发行版,交付管理器在Bamboo上启动相应的任务,该任务运行相同的流程,但还将版本上传到Google Play Beta。

应用实践
发行前版本最初,我们有两种类型的装配,就像许多种一样:
禁用ProGuard和SSL固定的调试版本。
包含ProGuard和SSL Pinning的发行版本。
过程看起来像这样:开发人员完成了该功能的工作并将其提供给测试。 测试人员收集Debug程序集,在其上测试测试用例,并检查应用程序发送的分析的正确性。 如果一切顺利,则他将任务发送给“就绪”以供发布,而她等待我们开始收集发布的那一刻。
当需要发布应用程序时,开发人员将所有任务合并到master中,选择rc分支并对其进行质量检查以进行烟雾测试。 质量检查人员收集发布程序集,开始运行测试。 但是有时候会出问题。 问题通常是由ProGuard引起的。 当然,它们会很快修复,但这可能会延迟发布或将其延迟一段时间。
因此,我们创建了一个预发行版本,其中打开了ProGuard,关闭了SSL Pinning。 这使测试人员可以验证提交的分析的正确性(这就是测试人员最初没有构建发行版本的原因)。
现在,QA正在构建预发布版本。 这使他们有机会尽早测试分析并解决由ProGuard引起的问题。
规格优先这是规范的主要方法。 当我们开发新功能并需要后端时,首先会创建一个规范,然后基于该规范从后端和客户端开始开发功能。 所有更改都通过规范,然后才在后端和客户端上进行更改。 该规范还生成有关API方法的Swagger文档。

最初,我们有一个API,其客户端不仅是移动应用程序。 API方法彼此不一致,这常常使更改变得困难。
也经常遇到有趣的案例。 例如,当该方法返回品牌列表时,如果有多个品牌,则返回一个数组,如果只有一个品牌,则返回一个对象。

或者,如果没有品牌,则返回null或通常为4个null字符
(不是JSON)。 在这种情况下,应用程序很难。

因此,随着时间的流逝,我们得出的结论是,移动应用程序需要它们自己的API,该API会考虑它们的详细信息,并将该移动应用程序与您必须与之交互的一堆内部Lamoda系统相关联。

同时,我们决定尝试使用规范第一种方法(Swagger规范)。 当开发人员开始处理需要后端的某些功能时,他会通过功能合同提出请求请求。 然后,来自iOS,Android和后端团队的所有感兴趣的参与者都将添加到此请求请求中。 当每个人都对新API方法的合同感到满意时,拉请求将被倒入后端分支,并且后端开发人员开始开发功能。 客户也开始开发功能,因为合同现已确定,您可以依靠它,并在必要时制作moki。
功能切换该公司拥有自己的开发A / B工具,可让您实施实验和功能切换。 功能切换我们为用户关闭了非关键功能,必要时可以将其禁用。 例如,如果出现问题,或者我们需要减少后端的负载(作为选择,在“黑色星期五”)。
功能切换还使我们能够测试库,以查看其他库是否可以更好地解决我们的问题并表现得更稳定。 如果没有,那么我们总是可以回滚到以前的库。
真实用户监控真实用户监视使您可以从用户角度评估应用程序性能。 例如,客户单击了目录中的项目。 他需要等多久才能看到操作结果,即看到带有照片的产品卡?

这不能自动完成,因为必须手动设置该测量的起点和终点。 只有开发人员才能理解何时可以假定用户已准备好与新屏幕进行交互。 在这种交互过程中,我们可能对以下方面感兴趣:
1.内存消耗
2. CPU消耗;
3.主流发生了什么;
4.从网络加载的内容;
5.在其他线程中发生了什么。
这使我们有机会解决问题,因为很明显,这实际上花费了大部分时间,并且我们可以对其进行优化,以使应用程序对用户的响应速度更快。
退还技术债务
在推出新版本之前,我们修复了先前版本中发生的崩溃。 这与紧急崩溃无关,因为它肯定需要修复程序,但是对于不经常发生的崩溃不会影响业务性能,但会使用户不满意。
版本发布后,我们将按百分比推出,监控关键指标并在事件发生时做出响应。 对于分阶段滚动,我们使用Google Play控制台。 滚动执行如下:扩展5%,我们监控指标; 如果一切正常,请继续。 如果发生了什么事,请进行修复并已将其推出。 接下来,我们将滚动10%,20%和50%。
我们要
监视哪些关键位置?
- 网络请求,包括来自第三方库的请求:错误,响应时间,负载。
- 秋天。
- 处理的异常,即所谓的“处理的异常”。 如果我们不将它们包装在try-catch中,则可能是这些异常。 如果用户的非关键功能发生异常,这将使应用程序不会崩溃。 例如,由于分析而倒下是很不好的。 但是,对于产品而言,重要的是要了解功能会改善或恶化转换。 使用处理异常可以使我们仍然响应并解决这些问题。
工具
- A / B工具
- NewRelic RPM
- NewRelic见解。
A / B工具是一种进行实验的机制,也是一种用于滚动变量的机制,具有相同的功能切换。 这是内部开发,因此可以很好地集成到许多系统中:在移动应用程序中,在站点上,在后端。 它使您可以在功能请求配置之后的单独请求中,而不是在其后面的单独请求中,传递功能切换配置。
这给了我们机会:
- 当我们要测试办公室内部的某些功能时,请在办公室进行实验。
- 推出实验以及针对特定用户的功能切换。
该系统独立于外部因素。 如果我们使用了第三方工具,则在某些时候它可能会被阻止(您好,Roskomnadzor),否则可能会出现问题。 对于我们而言,这至关重要,因为在这种情况下,我们将无法快速切换功能切换。 而且由于这是我们自己的发展,所以我们没有这样的问题。
NewRelic是一种工具,可让您实时监视许多不同的指标。 在各种New Relic功能中,例如,我们使用自动代码检测。 正是这一点使我们不仅可以监视对后端的网络请求,还可以监视所有其他请求(包括来自第三方库的网络请求)。 NewRelic支持一组用于网络的标准客户端。 它还允许您收集信息:
1.关于内存消耗
2.关于CPU消耗;
3.关于与JSON有关的操作;
4.关于与SQlite相关的操作。
另外,我们使用NewRelic收集崩溃报告,收集处理过的异常以及进行用户交互-这与
Real User Monitoring完全相同。 我们通过用户交互NewRelic的机制来实现它。
但是稳定性呢?
我们有一个指标,即崩溃率。 以前,我们在其指标介于0.3%至0.5%范围内时推出了此修补程序。 如果它的值大于0.5%,则至关重要。现在,当崩溃率在0.1%到0.3%的范围内时,我们将推出修补程序。 临界值超过0.3%,而且,如果我们之前的平均崩溃率是0.1%,现在是0.05%。

最后,我想列出一些最重要的实践,这些实践可以帮助我们保持应用程序的稳定性。 我们测试该应用程序时应使其尽可能接近生产版本,关闭功能切换的非关键功能,并监视和响应对我们很重要的指标。