大规模遗留应用重构

如果无法正常显示,请先停止浏览器的去广告插件。
分享至:
1. 超大规模遗留移动应用重构 覃宇
2. 讲师简介 覃宇 Thoughtwork 咨询师/移动应用专家 ee.msup.com.cn 至今仍奋战在编码一线,与客户一起进行遗 留应用现代化改造;热衷于探索软件匠艺的 方方面面。译有《Kotlin实战》、《领域驱 动设计精粹》、《Serverless架构:无服务 器应用与AWS Lambda》和《云原生安全 与DevOps保障》。
3. 一个典型的超大规模遗留移动应用 ? 一些数据 代码历史接近 10年 开发人员超过 300 Java 代码 100W+行 全量构建 12分钟以上 ? 我们听到 产品经理说:一个简单的特性、适配一款设备需要4人月 运营人员说:工作效率较低,配置活动成本高,难度大 开发人员说:这个代码,改不动 架构师说:我们一开始有架构设计,但早已腐化了… ee.msup.com.cn
4. 移动应用更容易产生代码大泥球 代码之间、代码和系统之间耦合严重 重复太多(HttpUtils、LogUtils…) 代码耦合示意 单进程的移动应用往往作为单仓库编译打包;系统碎片化导致没有组件化的”标准”,因此: 代码耦合、代码和系统耦合特别容易(加依赖就是了) 为了避免耦合,又简单粗暴的直接复制代码(因为没有靠谱的组件化框架) ee.msup.com.cn 代码重复示意
5. 陷入”焦油坑”的团队 困在象牙塔里的架构师 眼睁睁看着代码和架构渐行渐远 架构图示意 架构设计图无法约束代码 苦不堪言的技术⻣干 内卷的开发团队 这么多代码哪里Review得完 业务功能做完就不错了,还顾得上技术债 代码检视示意 人肉架构检视效率低下 从第一笔技术债的妥协开始,团队就在”焦油坑”里越陷越深 最终达成默契:等我们有时间的时候再来重构吧… ee.msup.com.cn 静态扫描示意 常规代码扫描没有作用
6. 终于到了不得不重构的时候,怎么办? 成立架构重构小组 As-Is:大泥球 熟悉业务的架构师 熟悉代码的技术⻣干 经验丰富的”救火队员” 确定重构目标 可以独立交付的、业务内聚的、与特定平台无关的组件 直面重构挑战 如何得到团队尤其是领导的支持? 这么多问题要从哪里开始重构? 架构重构小组和开发团队如何配合? 如何在重构的同时继续开发新特性? 重构之后的架构应该如何保持? … ee.msup.com.cn To-Be:组件化
7. 移动应用组件化重构的思路 架构重构小组梳理组件的边界, 架构重构小组将架构原则”代码化”:形 架构重构小组和开发团队一起结对:依 决策应当遵循的架构⻛格/模式, 式包括开发框架、自动化测试、架构 据架构DSL分析坏味道,采用开发框架 形成架构原则。 DSL。 重写或重构,并补充自动化测试。 组件边界 测试守护 框架强制 "类" “出界”但必 要的耦合 重构/写 不符合架构DSL规 则的耦合坏味道 架构⻛格/模式:分层架构、插件 化、整洁架构、MV*、VIPER 边界:业务组件、服务组件\通用组 件、特定系统/平台/三方接口 ee.msup.com.cn 工具:Dependency Analysis 开发框架:路由框架、Jetpack、Rx* 自动化测试:ArchUnit、JDepen 架构DSL:refactorguide
8. 应用重构案例复盘 推广:架构重构小组 + 开发团队(进行中) 重构”MVP”:架构重构小组(1架构师+1技术⻣干+2救火队员) 1.设计期望架构梳,分析现有架构问题 (20W行活动相关代码,4周) 2. 运用安全重构解耦,增加自动化测试 (1W行代码重构,4周) 3. 增加守护测试、“增量”解耦出更多组件 (100W行代码重构,持续) Main App Sport Home Main App Message Sport Home IM Activity Bundle User ... User ... Activity Service Common Utils Widgets 关键实践: 利用工具分析依赖坏味道 ... LogLib Common Utils VM Widgets ... Main M 关键实践: 结对、重构、自动化测试 重构中的组件 HttpLib Home Message Bundle User LogLib Common Utils Widgets 关键实践: 架构守护测试、流水线⻔禁 独立编译的组件 ... Sport Service Activity Service 存在坏味道的模块 ee.msup.com.cn Sport Sport Bundle V Entry Entry Activity Bundle App 架构分层边界 重构方向 Service ... Platform
9. 1.设计期望架构,分析现有架构问题 设计期望架构: Main App Activity Bundle Sport Home Message Sport Bundle V VM Bundle Main M User ... Sport Service Activity Service HttpLib App LogLib Common Utils Widgets Service ... Framework/ThirdParty Specific API 架构 Hierarchy:层->模块->包->类 架构师+救火队员Review未来架构设计 决定架构⻛格/模式、划分组件 编写分析工具 技术⻣干+救火队员Review现有架构设计 梳理现有模块分层的不合理之处 找到需要解除的耦合坏味道 ee.msup.com.cn Platform 边界划分(现有的类/包/模块在期望架构中的”位置”) - MainApp 模块(中的类)应该属于 App 层 - ActivityBundle 模块(中的类)应该属于 App 层 - - 现有 Home 模块中的 com.x.activity.ActivityFragment 类应该属于 ActivityBundle 模块 - 现有 Main 模块中的 com.x.activity 包(中的类)应该属于 ActivityBundle 模块 - 依赖规则(架构⻛格/模式所要求的依赖规则): - 下层(中的类)不能依赖上层(中的类) - Bundle层和Service层同一层内的模块(中的类)之间没有直接依赖 - App层、Bundle层、Service层(中的类)都不能依赖特定平台的 API: com.x.os.*(中的类 - 只有 LogLib 模块中的类才能依赖:android.os.log 包(中的类) - 识别现有架构问题(跨边界的耦合坏味道): - 类所属的组件”边界”(层/模块/包)不正确就是耦合坏味道 - 破坏依赖规则的类之间的耦合就是坏味道
10. 如果有自动化工具分析工具就好了 所以我们写了 refactorguid 输入:当前应用中的类的依赖关系 原理: 输出:层/模块/包/类各粒度的耦合坏味道清单 1.按照期望的架构边界描述架构DSL 2.利用正则表达式对架构 DSL和当前 依赖关系进行匹配 3.使用pandas统计坏味道数据 https://github.com/qinyu/ refactorguide 输入:描述期望架构的DS (架构 Hierarchy 和依赖规则) ee.msup.com.cn 输出:按坏味道/粒度组合的统计数据
11. 确切的坏味道清单和数据能促进协作、提升信心 202个类需要提取到 ActivityBundle 13个模块需要封装接口 Entry 模块共 168 个类(10%已解耦) -------------------------------------------------- activity:16(已解耦) search:1(已解耦) user:6 badge:18 page:24 network:8 (服务)Login:33 (服务)Message:21(部分封装) (服务)ServiceActivity:115(部分 封装,部分下沉) (业务)ChatBundle:412 (业务)ShareBundle:63 (业务)ServiceBadge:246(部分封 装) (业务)OpenApi:36(部分封装) Home 模块共34个类 -------------------------------------------------- *Card:29 Other:4 坏味道清单作为重构优先级和任务分配的依据 ee.msup.com.cn (业务)ThirdPartyService:31 (库)XxxLib:50(部分封装) (库)Utils:52 统计数据作为分析增强团队重构信心
12. 2. 运用安全重构解耦,增加自动化测试 有了组件的耦合味道清单作为重构的依据,接下来就是套路了… 结对重构 完全自动 实时Review,快速学习 减少错误,养成习惯 测试保护 小步前进 增加信心,提高效率 一次提交解耦一个依赖 一个业务组件(Fragment/Activity)的解耦套路(概述): 步骤 输入 输出\验证 主要安全重构技巧 第一步 梳理外部模块依赖 业务功能代码入口 除公共库外所有外部依赖清单 递归使用refactorguide/AS分析依赖 第二步 按顺序依次解耦 第一个外部模块依赖关系 build.gradle中删除依赖编译通过 综合运用AS的Move、Extract、Pull members up”下 沉”共享库、提取服务接口&实现 第三步 增加接口测试保护 解耦提取的公共接口 主要接口自动化测试覆盖并通过 使用Mockito、Robolectric解耦测试依赖 第四步 “移动”业务功能 原模块 原模块,新模块都编译通过 使用AS的Modurize移动代码和资源 第五步 增加冒烟测试保护 新模块 Happy path冒烟测试通过 使用Mockito、Robolectric解耦测试依赖 更多细节请参考《移动应用遗留系统重构》专栏:https://juejin.cn/column/6963182959545286669 ee.msup.com.cn
13. 运动应用架构重构小组的阶段成果 重构前 20w活动相关代码散落在各个模块 各个模块无法独立编译开发 全量编译超过8分钟 几乎没有自动化测试,验证难 重构后 其中1w行代码提取成独立组件 独立组件可以单独编译调试开发 独立组件一次编译需要只需要1分半钟 功能和接口都有自动化测试覆盖 Main App Sport Home IM Sport Home IM Main Account ... Activity Bundle Main Account ... Activity Service Common Model ee.msup.com.cn Utils Widgets ... LogLib Common Model Utils Widgets ...
14. 昨日重现:如何持续守护架构? 架构师 架构设计 业务架构 (架构)设计 ee.msup.com.cn 开发团队 技术⻣干 X ? X 编码技能 未来的大泥球 架构 (代码)实现
15. 3. 增加守护测试,“增量”解耦出更多组件 ArchUnit测试用例示意 架构规则 DSL可以方便的转换成ArchUnit测试用例 ee.msup.com.cn 本地执行结果示意 提交前执行ArchUnit架构守护测试并在流水线上增加⻔禁
16. 遗留应用架构重构/守护机制 架构师 技术⻣干 开发团队 定期分析 编写规则 业务架构 架构评估 (规则DSL) 合入触发 架构设计 测试用例 架构守护 (测试+框架) 通过 ee.msup.com.cn 重构依据 不通过 代码重构 有效架构
17. 架构重构/守护实践全景图 架构师 技术⻣干 开发团队 工具/效能团队 架构评估 (规则DSL) 架构即代码 (架构)设计 业务架构 架构决策 业务建模 架构⻛格/模式决策 框架选型 ee.msup.com.cn 分析工具 架构守护测试 架构设计DSL 架构框架 架构守护 (测试+框架) (代码)重构/写 CI/CD 重构技能 有效架构 结对 重构 自动化测试
18. 关注msup公众号 获取更多工程效能实践案例

Home - Wiki
Copyright © 2011-2024 iteam. Current version is 2.139.0. UTC+08:00, 2024-12-26 02:47
浙ICP备14020137号-1 $Map of visitor$