其他
网易严选APP工程架构演进
互联网技术发展到今天,移动互联网仍然是一个重要的战略核心,APP也仍然是绝大多数互联网企业用户获客留存的核心渠道。
合适优于领先 演进优于快成 简单优于复杂
2.2.1 分层
2.2.2 模块化
3.1.1 主工程代码堆积
代码文件主要位于主工程(~70%),复用的能力弱、体验差; 工程的结构比较简单,层级没有细分,从图上可以看出来大致的结构只分了2层(主工程/Pod库); 缺少引用隔离,主工程的代码没有域隔离,导致了不同的业务模块之间,存在大量的相互交叉的引用; pod组件拆分的颗粒度比较大,依赖不清晰,大多数的二方库,都只能在主工程运行,podspec编写不规范。
3.1.2 历史代码清理困难
3.1.3 集中式管理
没有约束的修改工程任意一处代码,导致工程的不稳定和不可控; 在Pods下直接修改依赖库,导致后续功能代码缺失的风险,涉及业务功能的完整性; 代码集中式管理,使得仓库体积非线性增长,单仓远程仓库3.xG起,本地冗余就更多。
3.2.1 业务耦合
主工程管理方式粗糙,Group与Folder交叉使用,缺少边缘界线,看似分组的代码,实际耦合; 主APP与App Clip业务形态互斥,代码层面又存在相互的引用,使得主工程代码、组件库等冗余各种的判断和校验; 业务模块与功能组件边界不清晰,位于底层的功能代码里面耦合了业务逻辑。
3.2.2 复杂度高
4.1.1 纵观全局,理清模块依赖关系
越是底层的模块依赖关系越简单; 没有循环依赖、依赖倒置的模块更容易治理; 治理完成的模块可以先忽略。
4.1.2 模块化、颗粒度解耦
4.1.3 提高安全性,保障项目完整
核心稳定的基础能力库,可考虑二进制化或部分二进制化; CI流程编排触发校验与反馈提醒,保证项目工程的完整性; 子仓库发布触发lint,单独编译校验,确保所有的subspec的依赖是正确。
require 'xcodeproj'
require 'cocoapods'
def build_xcframework_framework(frameworks=[],xcframework_path)
argvs = ''
frameworks.map do |framework|
if framework.framework_path && framework.framework_path != ''
argvs = "#{argvs} -framework #{framework.framework_path}"
end
if framework.framework_symbols && framework.framework_symbols != ''
argvs = "#{argvs} -debug-symbols #{framework.framework_symbols}"
end
end
command = "xcodebuild -create-xcframework #{argvs} -output #{xcframework_path}"
execute_command(command)
end
def execute_command(command)
output = `#{command}`.lines.to_a
if $?.exitstatus != 0
puts Pod::UI::BuildFailedReport.report(command, output)
Process.exit
end
end
4.2.1 主工程文件剥离
注册与分发全局性事件; 初始化,注册并绑定各个模块; 全局化配置与管理,全局性决议事件的收拢。
4.2.2 无用/重复资源删除
基于Mach-O检测:WBBlades 基于源码检测:Fui 使用工具检测:AppCode
LSUnusedResources:一款 GUI工具,通过正则组合匹配,检索效率还不错,需要注意的是大部分团队都会有自己的样式资源库,来读取对应的资源,此时需要自己适配特殊逻辑。 FengNiao:一款终端工具,和LSUnusedResources一样提供了正则组合校验,优点在于可以与CI集成,在关键节点编排时,在Run Script中触发检查。
遇到一些冲突或者覆盖的文件,在解决冲突的时候,处理的不规范。 在移除文件时,只移除了引用,没有真的移除。
4.2.3 渐进化修复、自动化检查
制定详细的方案,请团队相关同学Code Review; 内部BugBash,快速验证快速修复; 使用AB分流验证,逐步确认核心功能稳定; 建立容错降级机制,保障业务流程健康,可快速回退。
1. 代码规范和质量检查分析
2. 模块依赖检查与分析
3. 无用、重复代码分析
4. 工程隐患提醒与检查
如下图所示:
4.3.1 CI辅助分析并设置卡口
4.3.2 基建能力搭建
工程文件耦合
架构优化之前,主工程代码比例大约在70%。目前经过治理和优化当前大约占比45%,降低了大约25%。剩余的主要是复杂的业务模块,也就是我们上面规划的第四批和部分第三批的模块,因为前置,已经积累了对应的解决方案,后续在业务稳定的前提下,持续进行优化即可。 在优化模块依赖和分层治理中,梳理清了模块的关系,没有使用的模块陆续下掉,并且引入了CI的检测,设立卡口在保证不会出现新的无用冗余代码的前提下,每次需求的改动,CI会主动提醒当前模块存在的历史遗留问题,告知对应的开发者进行优化。移除无用文件:340+,接口去重合并优化:80+。
工程环境复杂
罗马不是一天建成的,系统建设也不是一蹴而就的事情。在架构演进过程中,一方面:不断的向组内分享我们在做什么,为什么这样做,培养模块化/组件化解耦思维,一方面:对历史代码不断地改造,拆分出组件库并进行分层治理,逐渐降低工程复杂度。二进制化库:11个,新增组件库:35个,编译优化速度提升:6%。 把不同的业务形态代码进行隔离,剥离Clip与主APP代码与依赖关系,通过subspec管理,依赖关系更清晰,涉及的改动对比清晰且可控。
[1] https://www.adjust.com/blog/mobile-app-trends-2021/ [2] https://www.statista.com/statistics/276623/number-of-apps-available-in-leading-app-stores/ https://zh.wikipedia.org/zh-cn/%E8%BD%AF%E4%BB%B6%E6%9E%B6%E6%9E%84 https://www.statista.com/statistics/276623/number-of-apps-available-in-leading-app-stores/ 拓扑排序:https://oi-wiki.org/graph/topo/ fui:https://github.com/dblock/fui WBBlades:https://github.com/wuba/WBBlades 抖音 iOS 工程架构演进:https://juejin.cn/post/6950454120826765325
本文由作者授权严选技术团队发布