如何成为无线架构师

简介: 从毕业来阿里到现在已经有4年了,一直在从事Android开发,从一开始的业务开发,到后面负责部分架构工作,再到后面作为ICBU无线Flutter的架构师,虽然不是我们Android技术栈架构师,但是对无线的架构还是有一些感想。另外看到集团内部好像没有关于无线架构的文章,私以为无线架构和后端架构还是有些不同的,所以在此抛砖引玉,希望大家共同交流探讨。 无线架构师职责 --- ![image

从毕业来阿里到现在已经有4年了,一直在从事Android开发,从一开始的业务开发,到后面负责部分架构工作,再到后面作为ICBU无线Flutter的架构师,虽然不是我们Android技术栈架构师,但是对无线的架构还是有一些感想。另外看到集团内部好像没有关于无线架构的文章,私以为无线架构和后端架构还是有些不同的,所以在此抛砖引玉,希望大家共同交流探讨。

无线架构师职责

image.png
我们都知道,架构师的目标是解决利益相关者的关注点。 这其中包括三部分工作:

  1. 找到利益相关者
  2. 找到利益相关者的关注点
  3. 解决他们

对于绝大部分一线业务无线架构师来讲,利益相关者和他们的关注点如下:

  1. 用户/客户:更顺畅的使用App完成自己的目的。
  2. 业务方(PD、运营):提的需求都能尽可能被满足,更容易的实现自己的业务目标。
  3. 业务开发者(开发、测试):更效率、更稳定的完成开发工作,更快速的提升自己。

总结一下,事情上,无线架构师更需要关注在用户体验、研发效率、人员成长这三方面上。接下来主要讲研发效率,如果有精力再讲用户体验和人员成长。

研发效率可以扩展出很多具体的事项,而这些事情之间很多又是相辅相成的,所以如何借助『无线架构』来提升研发效率,就是无线架构师的工作,具体来讲就是所谓的『无线架构』的设计和衍变。而『无线架构的设计和衍变』,离不开组织和人员的帮助,所以下面会从具体的架构设计、组织和人员上分别展开来讲。

无线架构

image.png
无线之所以被称为大前端,就是因为无线App除了前端页面的绘制,还需要负责一些复杂逻辑处理、系统交互、内存管理、数据存储、线程管理、网络通信、文件管理等等工作,对于一个完整的、成熟的App而言,有一些基础的技术项是不可缺少的,是组成App必须的组件。
所有的界面、业务逻辑,加上它们所需要的基础组件,构成了一个App。而App的复杂度取决于业务的复杂度,每个业务开发的研发效率取决于他需要自己面对的App的复杂范围,业务开发开发一个功能时,需要面对的代码、结构越复杂,额外成本就越高;反之需要面对的代码、结构越简单,额外成本就越低。
无线架构一般都是通过减少业务开发所面对的复杂度,来提升每个人的研发效率,进而提升整个App的研发效率的。

研发效率包含哪些方面

几乎所有的技术项目最终都是为两方面服务的:直接或间接提高效率;通过技术促进业务发展。后者可遇不可求,所以前者是大部分技术项目的目的,有些项目会直接影响到效率,而有些项目通过影响一些中间指标间接地影响到效率,比如稳定性、易维护性。 落地到具体的事项上,又可以划分成下面几类:

工程结构

以业务和功能的不同,对整个系统进行物理上和逻辑上的拆分,同时平衡好代码的耦合度和业务的耦合度之间的关系,就是工程结构要做的事情。
代码之间的耦合如果比业务逻辑紧密,会给协同开发带来更多地矛盾,会有人在不停的解决代码、功能上的冲突,任何代码的变动都可能影响别的功能。维护成本高,影响效率。
代码之间的耦合如果比业务逻辑松散,任何模块代码的变动都不会影响别的模块,又会降低问题暴露的可能,带来稳定性的风险,也不利于维护,影响效率。
同时,代码的耦合增加会带来开发者沟通的增加,不同模块的耦合越多,负责不同模块的开发者之间的沟通也就越多,会带来成本的提升。

开发框架&设计模式

熟练使用开发框架和设计模式,可以帮助自己的代码更具有可维护性。可是开发框架和设计模式都需要一定的学习和使用才能够熟练掌握,在熟练掌握之前,绝大部分开发都会在『无需设计』和『过度设计』之间徘徊,无法准确的使用合适的开发框架和设计模式。
对于所有的代码工具而言,唯一的快速掌握的方式就是把它应用到实际项目中。对于初学者而言,一个新的需求,可以尽量『过度设计』一些,只有过度设计了,才能明白怎样设计才是不过度的。这是成长必经的一个过程。
具体有哪些常用的开发框架和设计模式,各个地方都有很多,算是入门知识,这里就不再赘述了。

能力复用

能力复用是很普遍的提高研发效率的方式,一般情况下,可以节省相当多的工作量,而能力复用大体上可以分为两种

  1. 通过封装自己常用的工具和代码提升效率。简称造轮子。
  2. 通过使用外部的二三房库提高效率。简称用轮子。

无数的开发者、架构师经常纠结的事情是,到底是自己造个轮子,还是用别人的轮子,还是基于别人的轮子改造一下;或者到底用哪个人的轮子更好一些。这是很多开发者初入行就会遇到的技术决策的问题。下面会简单讲一下如何去做这类的技术决策。

什么时候有必要自己造轮子

从效率的角度上来说,一定是已有的轮子无法满足需求的时候,才会考虑去自造或改造。从公司角度来说,如果已有的轮子是竞争对手开发的,或者开源协议有风险的情况下,就要加上『法务风险』这一条了。
如何判断轮子是否满足自己的需求呢? 无外乎三板斧:看功能,看设计,看风险。
一个轮子的功能,是否满足自己当下的需求,一般可以通过看文档得到初步的结论,如果在这里就否掉,后面的就没必要再深入了。
设计主要包括文档中原理和方案的介绍,以及源码。通过原理和方案的介绍,可以推导出一些风险点,并与自己当下和未来的需求进行匹配;通过对源码进行初步阅读,可以明白其中的一些细节设计,了解开发者对整体的把控。
有些风险很难在文档和源码中直接体现出来,这时最直观的了解风险的方式就是看issue,或者通过技术原理去推导一些风险点(计算机科学与技术的专业课的重要性啊)。

轮子的选择

面试的时候面试官最喜欢问的问题之一:『为什么你会选择A开源库而非B开源库?』
典型的技术决策问题,其实也套用上面的三板斧(看功能,看设计,看风险)就足够了。下面结合一个实际的案例来讲一下如何通过技术原理进行技术决策。

如何通过技术原理判断Flutter和Weex的性能情况

作为最近最火的跨端技术,Flutter在集团内经常被拿来和Weex进行比较,下面通过两者的技术原理的差异简单的做下对比,进而推导出性能的情况。
image.png|500x500
整个图,越上层越接近开发者,越下层越接近CPU/GPU。
首先可以看到,以Android为代表的Native,其实就是把xml(或其他格式的布局文件)转换成了ViewTree。
Weex本质上也是把JS转成ViewTree,但是转换过程比较多比较复杂,绿色的Native的部分,最多也就保证性能和Native是差不多的,在此之前的黄色部分,都是相对于Native而言额外的性能消耗。
Flutter整体运行在自己的虚拟机DartVM上,而后通过绘图引擎直接绘制到屏幕上。这其中也做了几次转换,目的是为了节省重复刷新页面时的性能,我就不在图中详细描述了,重点不在这里。
通过这个图我们至少可以得出一个简单的结论,从原理上来讲,Weex性能是肯定不如Native的,越复杂的Weex界面,DOM层级越深,带来的额外性能消耗也更多。而Flutter无法直接和Native对比性能好坏,只能说理论上可以做到和Native持平,具体则要看Google的代码水平。

辅助工具

大部分App都会有自己的开发工具集和工程工具集,开发工具集比如网络环境切换、设备id切换、设备信息展示、日志信息查看、运行模式的切换等等。工程的工具集比如脚手架、打包脚本、模块化脚本等,通过建设这种开发者用的内部工具,可以极大地提升开发效率。集团内部就有很多好用的无线开发工具平台,比如MTL、磨刀石、埋点验证平台等等。下面这张图是我们的开发者工具,在实际使用的过程中极大地提升了开发和测试的效率:

Android iOS
image.png image.png

辅助工具的建设,需要尽可能平衡开发者的需求和对生产环境的侵入性,比如不把工具中的代码和数据发布到应用市场。除了工程上的方式,还可以使用AOP切面编程来实现。

AOP切面编程

切面编程是啥大家应该都了解过,具体就不介绍了,借助AOP,可以帮我们快速无侵入的实现一些开发工具,比如方法运行时间统计、装饰方法等等。
Android上我们目前用的是沪江开源的AspectJ插件 ,至少3.2.1的Android gradle plugin是支持的。

稳定性

image.png
稳定性除了对业务有影响,最大的就是对开发效率的影响,一个发布到线上的bug,如果要发版本修复,至少需要占用一个开发+一个测试一天的时间。而如果这个bug在开发阶段被检查出来,可能只需开发+测试各1个小时的时间就能解决。
无线的稳定性主要体现在crash率的控制上,这是大部分无线团队的关于稳定性的唯一会看的指标(故障就不在讨论范围内了),如何长期、高效的控制crash率,是大部分无线团队都会遇到的难题。
这里的crash率是广义上的,也就是说android包括java crash率,native crash率,anr率;iOS包括crash率,abort率。

纸上谈兵 - 修复问题的方法论

关于如何修复问题,我这里有一些经验,只是就结果而言做的还不是非常好,所以有些纸上谈兵,在这里跟大家交流一下。
方式方法:数据+工具+知识+耐心

  1. 数据

控制crash率要以数据为先,当前的crash率是多少,分布情况是怎样,时间趋势是怎样,crash日志详细与否,都是决定crash率是否要控制、如何控制的关键。
集团的高可用平台就很好用,不止crash数据,性能数据也都囊括在内,相信看这篇文章的大部分团队都已经接了。

  1. 工具

对工具的使用熟练与否,决定能否根据已有的数据挖掘更深的信息。比如修复Android OOM问题的时候大家都要使用各种内存dump工具和内存分析工具;修复Android卡顿问题的时候要用到TraceView分析代码执行情况等等。

  1. 知识

知识的储备决定能修的问题的深度。如果你不知道内存泄漏的原理,那碰到很多OOM问题是几乎解决不了的。又或者说如果不知道线程池的原理和作用,那遇到因为线程过多导致的卡死和OOM也是解决不了的。

  1. 耐心

在上面一切都具备的情况下,修复问题拼的就是耐心了,有些问题可能一天、一周都不会有结论;亦或者需要逐行排查日志,这些都需要耐心。

技术创新

新的技术会带来新的可能,许多新的技术都是为了提高效率存在的, 代价是学习成本和设计成本。很多初入行的技术狂热者都很喜欢研究新的语言和框架,并把它们分享给同事,希望尽早的应用到项目中,可是架构师不应该这么做。
架构师更应该关注的是整个技术团队的运行效率,综合评估成本(学习成本、维护成本、稳定性风险带来的成本)和收益(研发效率提升、人员成长),才能决定对新兴事物最终的态度。
所以越大的业务、越大的工程、越大的团队,技术的决策,尤其是创新技术的引入应该慎之又慎,需要考虑方方面面。
体量小的团队,调头容易,试错的成本低,发现新技术不合适可以下掉。 而航母级的App调头困难,一个技术决策会影响千百位开发者和、几十上百个对应的上下游团队、千百万的用户,几乎没有试错的余地。

取舍

取舍指的是对用户的取舍,取舍的目的是达到最大的性价比。
对于无线来讲,系统版本的碎片化(主要是Android), 和应用版本的碎片化,足以让大多数架构师头痛。如何在碎片化的情况下保证,甚至提升效率,势必涉及到取舍。
无线对用户的取舍来源于自身的特色,对于后端架构来说,支撑每个用户的成本几乎都是一样的,用户的个体之间不存在显著差异,因此不存在取舍的问题。
取舍包括两部分:用户群体的取舍,和功能的取舍。

用户群体的取舍

举一个简单的例子,现在iOS已经发布了iOS 13,一般来讲此时iOS9的用户应该已经相当少了,占比1%以下。而开发者因为自身开发重心的变化、知识储备的变化等原因,对iOS9的支持已经有心无力,尤其是涉及到兼容性的问题,支持旧版本的成本非常大,其成本的占比要远高于用户的占比,此时的取舍就是舍弃iOS9的用户,把开发者的精力用来更好的服务那剩下的99%+的用户。

功能的取舍

对于低端设备,或者低版本系统而言。有些优秀的功能、交互或者动画,因为性能问题、或者成本问题,在其上实现的代价很大,这就涉及到对功能的取舍。比如对于低端设备,设备内存小,app内存压力大,有些辅助说明的静态图片就可以不显示,有些动画可以减少帧率甚至以静态图展示,以节省内存,提高整体使用的体验。

组织

提到架构,就必定要提康威定律,简单的来说,系统设计(产品结构)等同组织形式,每个设计系统的组织,其产生的设计等同于组织之间的沟通结构。
举个具体一点的例子,如果技术团队小、沟通足够迅速,那对应的代码结构一般都是耦合高、单工程的。随着技术团队的扩大,甚至再对同一技术栈根据业务不同拆分成多个团队,那对应的代码结构一般都是耦合低、模块化的。
所以对于架构师来讲,当前是什么样的组织形式,就要设计什么样的架构;或者说当前需要什么样的架构,就要推动组织形式朝着对应的方向转变。

和团队组织结构脱离的架构设计,一定是事倍功半,甚至运行不下去的。
和团队组织结构相符的架构设计,可以提升整体系统的稳定性、维护性和研发效率,稳定性和易维护又可以进一步提升研发效率的提升。

『业务 - 技术 - 组织』这三者之间,一定要相互配合,才能达到最大的运行效率。
image.png

人员

人对架构的重要性,不仅体现在架构由去设计、维护,还体现在架构本身就是服务于的,这是相互成就的关系。
对架构而言,开发者的角色十分多样。首先,开发者是架构的用户,是它的既得利益者;再次,开发者还应当是它的产品经理,是需求的提出者;最后,开发者应当为架构的具体实现贡献力量,是它的开发者。
虽然人都是逐利的,但是每个人的思维方式不同,对不同利益的判断不同,在面对业务压力的情况下,开发者很容易选择去尽快完成业务项目,而非去跟随架构师完成技术项目。又或是对于架构设计的不认同,导致开发者在实现架构时无法尽心尽力。这些都是很常见的情况。如何处理这些情况,带动团队成员建设架构能力,是架构师在『人』这一方面最大的挑战。
这里引入一个词叫做『领导力』,架构师只有具备好的『领导力』的情况下,才能让开发者认同、接受、执行架构师的想法。有人说领导力的本质是影响力,影响力又分为权力的影响力和非权力的影响力。 大部分的架构师是没有权力的(比如一个架构师很难对开发者的绩效真正产生影响),所以架构师的主要目标在于提升自己非权力的影响力。

非权力影响力

非权力影响力顾名思义,就是在没有合法权力的约束力的前提下,带来的影响力。比如『信任』,『钦佩』,『支持』等。关于如何建立非权力影响力,我这里有一些粗浅的理解和总结。分别是通过共鸣统一目标,通过树立自身权威是对方信服,通过互惠互利落实对方利益。

共鸣

架构师对于架构的目标一定要清晰,系统的梳理出当前架构存在的,或未来可能出现的问题,为什么架构可以解决这些问题,并且可以向开发者阐述清楚,让开发者产生共鸣。首先要通过这种方式,以产生共鸣的方式将开发者拧成一股绳,才好去做更细致的规划。

权威

架构师自身一定要是最权威的,不管是当前的技术实力,还是对未来方向的判断,都要有强大的实力。程序员总会或多或少有些读书人的酸臭味。一个人如果技术实力比自身强,那他说的话,程序员先就信了一半。
而权威所代表的强大的技术实力,又是架构正确性的保证,是对架构每次衍变所做的技术决策的重要支撑。

互惠

人都是追逐利益的动物,而人的一个很大的问题在于,长期利益很容易被短期利益所蒙蔽,哪怕这个长期利益的确远高于短期利益。放到具体的case中,实际情况而言,大部分开发者更倾向于(或被逼)去做眼前的短期就能完成并取得结果的业务项目,而非需要长期投入、不能立刻见效的架构建设,哪怕这个技术项目的确很重要。
这种情况出现的时候,架构师就应该进来并进行干预,比如通过恰当的鼓励、及时的反馈,让开发者在建设技术项目的过程中及时收获一些成就感和成长。或是通过设立短期目标,将长期的枯燥的工作拆分成多个短期的可以立竿见影的项目。
总之就是需要及时通过利益,不断调整开发者的心态,促使他们完成架构的既定目标。
还记得在『无线架构师职责』这一节中提到的无线架构师的其中之一的职责是促进人员的成长吗? 人员的成长很多都是通过这个过程得到促进的。

架构如何设计

架构设计有自底向上和自顶向下两个方法,这两个方法没有高低优劣之分,都是架构师必须掌握的方法。更细节的大家可以去看六铢大神的几篇文章,写的非常的好,完全不给别人活路的那种。

自底向上 - 归纳

自底向上的关键字是归纳,即归纳业务、归纳技术。而归纳是归纳出什么东西来呢? 归纳出模型。有了模型才可以明白为什么架构衍化成了现在的样子,而这里的『为什么』其实就是业务和技术之间的关系。
在从没有架构师到刚有架构师的过程中,或者架构师空降的过程中,使用自底向上的方式可以帮助架构师快速掌握当前架构,进行落地。只是自底向上的方式很难帮助架构师进一步设计未来的架构,所以需要自顶向下的方式。

自顶向下 - 设计

自顶向下的关键字是设计,而推动者设计的是问题,当架构师对当前架构了然于胸的时候,面对未来可能会存在的挑战和问题,可以去推导未来的架构大体上应该是什么样子,再自顶向下的设计和细化。
所以自顶向下的方法中,找到未来可能会出现的问题十分关键。只有能找到问题,才能给出解决方案,进而做好设计。

无线架构的衍变

下面的很多内容,各位无线开发可能都很熟悉了,也可能看到这篇文章的各位,所在的部门就已经是非常成熟的架构,这里更多想体现的主要是我个人的思考过程、推导过程,也就是无线架构为什么会逐步形成了今天的这个样子,为什么随着业务的发展、技术架构也要进行发展、组织架构也在跟着变化。

早期

早期的无线部门,组织结构大多是这样子的:
image.png
按照技术栈划分不同的组,或许会有一个架构组或许没有。 没有架构组的情况下,架构师或者由每个组内综合实力最强的开发担任,或者由每组的leader兼任。
这种组织架构在App的早期是没有问题的,因为早期的App需要进行大量的基础建设工作,每位开发者都需要为自己的App添加很多非业务代码,相同技术栈的在一个团队,可以非常有效的保证基建的目标是一致的、行为是高效的。
另外早期的App人员普遍较少,每个人负责的模块是灵活多变的,一个人也会负责多个业务模块,甚至因为业务不成熟,会有职责的交叉。相同技术栈的开发在一个组的话,会让整体的沟通成本和风险降到最低。同时各个技术项的负责人并不固定,大家将精力更多地放在了技术的可用性上,每个人都在不自觉的为架构的建设做贡献,但是整体不一定有一个清晰地架构。

image.png

此时的架构设计一般都比较初级,可以说没有设计,整个App只有一个工程,不分层或者只有简单的分层,业务之间一般通过Package进行划分。对基础组件的应用比较初级,不会过分在意技术选型。性能和质量上,处于过得去就行的状态,不会遇到太大的问题,也不会专人专项的去看。

发展

随着业务的增多和细化,业务的总数和单业务对应的代码量也不断增加。不同业务的代码物理上依然放到一起,无法从根本上解决相互调用带来的耦合。到了某个阶段之后,早期的工程架构会带来一些问题:

  1. 业务: 有些功能的代码逻辑已经变得没有人能从头到尾说清楚。此时任意代码的改动,都有可能造成风险,影响到和它耦合的代码,进而带来更大的问题。
  2. 基建: 基础建设变得比较稳定,像网络库已经不会再进行非常大的改动,继续和其他模块在同一个工程内会增加风险。
  3. 开发: Git的提交次数变多,冲突也越来越多,解决冲突的成本也越来越多。

image.png
为了解决上面几个问题,必须要做的事情就是分层、分模块。此时受限于改造成本,大部分团队会选择将不同的功能拆分成不同的module,所有的module依然在一个Git工程下面,此时已经具备模块化的雏形,但是距离真正的模块化还有一定的距离。
虽然分了层同时分了模块,可是受限于历史原因,模块之间尤其是业务模块之间的依赖不受控制,直接依赖、树状依赖、循环依赖的情况比比皆是,只是问题还没有凸显出来。

成熟

随着业务的逐渐成熟和稳定,业务边界已经变得清晰。又随着业务和用户体量的增加,用户对体验的要求逐渐增高,性能和质量的压力随之而来。此时之前的架构会继续暴露问题:

  1. 业务: 已经开始专人专项负责某些模块,大部分开发者只了解自己负责模块的逻辑,了解整个app每个模块的具体逻辑的同学越来越少,但是一个工程下,所有代码依然对所有人开放编辑权限,增加风险。
  2. 基建: 基础建设建设完毕,此时的基建工作,更倾向于进行优化和重构。
  3. 开发: 开发者越来越多,得益于之前的模块拆解,代码提交的冲突可能会减少很多,但是整个工程提交的频率会快速增加。每次提交都需要不停的merge或rebase,效率低下。

同时会有一些新的问题出现:

  1. 组织: 业务复杂到一定程度之后,需要专人长期负责专项模块,不然每次切换人员的成本都十分高昂。而以技术栈划分的组织架构无法保证这一点。
  2. 沟通: 业务庞大到一定程度,有一些事情(比如别的业务)其实已不再需要业务开发者关心,而以技术栈划分的组织架构无法保证这一点。
  3. 业务配合: 业务深入到一定程度之后,长期负责的开发才是最了解这个业务的人,毕竟代码都是自己写的。哪怕是产品经理都很难保证对复杂业务的了如指掌。而开发随着时间的推移,对数据、业务、用户都有了自己的理解,可以反哺业务。此时相同模块不同技术栈的开发更有共同话题,可以为业务发展齐心协力。
组织架构调整 - 业务化

所以到了这个阶段之后,部分复杂的业务开发团队就要尝试进行组织架构的调整,和App工程结构的调整,以保证可以解决上面的问题,提高效率。一般来说会以业务维度把团队重新进行划分:
image.png
当然根据具体业务情况和复杂度的不同,组织结构的划分肯定也有所不同,这一点各位老板比我明白的很,我就不再赘述了。

业务的变化不是一蹴而就的,是连续变化的;而组织架构、工程结构的变化相对来说是短期就能完成的事情,不管组织结构和工程结构选择在什么时间节点进行调整,都会出现一个业务变化和组织架构、工程结构变化不匹配的阵痛,加上短期而巨大的变化带来的不适应,进而带来组织人员的不理解,这十分正常,在这个阶段老板和架构师都需要对开发者做好调整背后的意义的解读。

所以到这里做一下简单的总结:
无线团队发展到一定程度,是有可能从技术栈划分变成业务划分的。这两者并没有高低之分,只是重心有区别。 技术栈划分的团队更能集中力量去进行技术建设尤其是基础技术建设,业务划分的团队更容易集中力量去进行业务建设或者针对业务的技术建设。

工程结构调整 - 模块化

到了这个阶段,工程架构就会要求『模块化』、『组件化』、『插件化』,这也是前两年业内比较火的词。这几个不同的方案具有不同的能力,但是在工程架构上大同小异。下面简单介绍一下几种方案:

  1. 模块化
    将一个完整App以不同的业务或功能划分出不同的模块,不同的模块在物理上进行隔离,不再先天具备相互访问的能力。以Android为例,一个模块对应Android Studio中的一个Module,不同的Module之间只有进行了依赖,才能访问。
  2. 组件化
    组件化的模块,在模块化的基础上,本身既可以当做别的应用的组件,也可以当做独立的项目去运行。
  3. 插件化
    插件化和组件化不同,组件是源码级别的组件,插件是产物级别的插件。换句话说,插件化的App可以支持将别的App产物(比如Apk)或静态或动态的插入到自己的App中,然后运行。因为插件是产物级别的,所以先天就支持动态化的能力,而源码级别的组件则不行。

『模块化』、『组件化』、『插件化』这三者并不完全割裂,是可以兼容并包的,选择哪些还是要依据自己应用的实际情况了来做判断。

上一节『发展』中提到的工程结构已经具备了模块化的雏形,但是依然保持雏形的话,随着人员的增多,虽然划分了模块不会带来太多的代码冲突,但是整个团队频繁的提交会加剧代码的合并操作,而合并本身就是风险。另外随着代码量的增多,编译速度开始明显下降,编译打包整个工程可能需要5~10分钟甚至更久,严重影响开发效率。因此模块化到成熟阶段至少要先做两件事情:

  1. 模块在物理上的独立
    通过将每个模块拆分成独立的Git工程,减少代码提交对其他开发者的影响。
  2. 模块在产物上的独立
    通过把每个独立后的模块打包成中间产物、主工程只依赖中间产物的方式提升编译速度。这里需要考虑模块的开发调试问题,要么通过组件化的方式,要么通过提供主工程源码依赖模块的方式解决。另外中间产物的出现会带来版本管理的问题。
问题

按照上述方式进行调整后,就会遇到模块化早期遇到的问题。

早期的模块化都是模块之间直接进行依赖,直接依赖本身就会造成非常多的问题,直接依赖又会带来树状依赖和循环依赖,进一步增加维护成本和风险,下面仔细讲一下这三种依赖情况会带来什么样的问题:
1. 直接依赖
A模块需要访问B模块,那就直接依赖B模块,进而就可以访问B模块。这种用法会带来一个非常严重的问题:如果B模块进行了会影响到A模块的BreakChanges,那A模块也需要重新去修改。在高度流水线的组织模式下,A模块和B模块大概率是由两个人负责,并且相互之间不清楚对方的动作,B模块的任何修改都有可能给A增加工作量。另外,现实中,信息的不对称会导致B的修改不为A所知道,就有可能会在运行时出现对象、方法无法找到的情况。增加线上的风险。
2. 树状依赖
A模块需要访问C模块,恰好此时A模块已经依赖的B模块已经依赖了C模块,所以这个时候就会出现传递依赖的情况,对整个App而言,依赖都是树状的,树的根节点是App的主工程,越往叶子节点模块越低层。就会出现下面的结构:
image.png

在树状依赖下,叶子节点的任何BreakChanges,都无法保证整个链路的每个节点都不受影响,如果所有模块的代码都是源码依赖,编译过程可以直接发现问题,如果主工程依赖的都是中间产物,这种问题不会在编译过程中暴露出来,会导致非常严重的后果:线上代码找不到或无法执行。
3. 循环依赖
循环依赖按字面意思就很好理解,上图的『首页』、『搜索』、『详情』这三个模块就是循环依赖的体现,循环依赖的最大问题难以处理的版本控制,编译器更容易编译出错进而无法编译,解决起来成本巨大。

上面这三个问题带来的风险的增加和成本的提升,都会让开发者和架构师怀疑人生:模块化是正确的方向吗?

说白了,问题的根本还在于模块之间的直接依赖,而模块之间的直接依赖是一种从包依赖关系顺势产生的惯性思维,模块化不应该是这样子的。
什么是真正的模块化,简单来讲就是如果A模块需要访问B模块,那只能访问B模块提供出来的服务,对于没有提供服务的代码一概无法访问。这种访问控制能力在完整App的时期是通过java的访问控制权限(public, private...)进行控制的。模块化之后,物理隔离会让模块之间的代码全部无法相互访问,而直接依赖又会导致物理隔离丧失,和不做模块化没有什么本质区别,所以我们要做面向服务的模块化。

面向服务的模块化

模块的服务化就是,每个模块对外开放自己的服务,对内实现自己的服务。其他模块只需访问服务就能获得想要的能力和数据。所以到这里,模块化应该解决的是如何开放服务、封闭实现。
一个很简单的思路,既然直接依赖模块会使物理隔离丧失,那就为模块的服务单独拎一个模块出来,比如对于要提供服务的B模块而言,拎一个BBase模块,该模块只提供接口和对象作为服务,B模块实现BBase模块的接口,并在主工程中进行依赖的绑定。A模块如果需要B模块的服务,那只要依赖BBase即可。同时为了减少风险,对于Base层做好约定,比如只能增加服务,禁止删除服务。语言是苍白的,看图:
image.png

拓展

如果说前面的『早期 - 发展 - 成熟』的流程,大部分App都是相似的,那到了这一步,架构的拓展就要根据自身业务的诉求进行调整了,业务的诉求不同,所需要的拓展就是不同的,在此列出几个典型的拓展的架构的例子。

合作

上面的『成熟』的架构可以支撑几十个人的团队,但是很难支撑几百个人的团队。因此就需要架构对模块进一步解耦,同时为了保证上百人的合作不出差错,在持续集成方面需要投入更多精力。手淘的Atlas和MTL就是为了应对几百人的合作产出的非常有效的工具体系。
团队上,随着业务模块之间进一步的解耦,团队的组织架构之间也可以进一步解耦,团队和团队之间的沟通交流可以不必如之前一般密集,沟通成本进一步下降。
合作方式、集成流程的优化对效率的提升,体现在对风险的控制上,虽然单次集成的成本变大,但是集成的风险变小,处理风险的成本变小,整体的效率是提升的。

开放

有些App在成为航母之后,进一步的业务方向是做成开放平台,例如支付宝。对于开放平台而言,其上不仅有自己团队的开发者,还会有集团其他部门,甚至第三方开发者一起贡献功能和代码。如何方便的支撑这样的开放平台,在提高所有人效率的同时,又能兼顾质量、性能、安全等因素,则是『开放』这个方向需要去考虑的事情。 支付宝和手淘的小程序开放平台是这个方向的产物。
开放平台对效率的提升,体现在边际成本上,以支撑平台、运营平台的工作量,换取团队外、第三方开发者的投入所带来的收益。支撑1W名第三方开发者和支撑10W名第三方开发者的工作量的差距到不了一个数量级,但是带来的收益等于甚至超过一个数量级。边际成本随着开放平台的成长被无限摊薄,整体效率是提升的。

研发

有些团队受限于各种原因人员无法增多,同时又没有开放平台或者其他的诉求。这些团队将精力更多的放在如何直接提升开发效率上。比如新语言、新框架的引入。比如前两年有些新团队在编写新App的时候,会选择使用当时最热门的Kotlin来取代Java,期望依靠Kotlin的语法特性提高开发效率。又比如ICBU无线引入Flutter,利用它的一端编写两端运行、完美的一致性来提升开发效率,并取得了很好的效果。
人员上,使用Flutter的情况下,团队内部Android和iOS开发的边界进一步降低,从『Android工程师』和『iOS工程师』,变成了『掌握Flutter的Android工程师』和『掌握Flutter的iOS工程师』,进一步变成『掌握Android的Flutter工程师』和『掌握iOS的Flutter工程师』,最终变成『掌握Android和iOS的Flutter工程师』,有共同语言的开发者人数一下子多了一倍,也有利于整体技术的成长。
研发框架对效率的提升,体现在直接的开发效率的提升上,本来开发一个页面需要两端各5人日一共10人日,使用Flutter开发后一共只要5人日。效率提高100%。
但是通过使用新框架带来效率的提升需要注意一个问题,那就是多个框架(如果有的话)之间的边界问题,一个热衷于引入新框架的团队,内部可能会同时存在Native、H5、Weex、Flutter等等多个框架,他们之间的关系和边界是什么,什么时候选择用A什么时候选择用B,需要非常清晰。

还有许许多多其他方向,总之『拓展』这里,要选择什么方向,要根据自己业务的前进方向进行选择。

总结

总之,无线架构师是一个很特别的角色,他/她需要面对整个App所服务的用户、运营、产品经理和开发者;需要兼顾业务、技术、组织、人员;需要拼搏现在同时着眼未来,愿各位和我自己都能成为一名优秀的架构师,愿各位架构师可以更加优秀。
上面是我的一些思考和总结,有1w1k字,写了大概两个多月,受限于自己的知识储备和实践经验,整体还不是很完善。希望大家读完能有所收获,同时多多交流。

目录
相关文章
|
传感器 存储 安全
无线技术有哪些专业术语,看完本文=半个无线专家
无线技术有哪些专业术语,看完本文=半个无线专家
282 0
|
存储 传感器 负载均衡
移动无线通信技术经历了哪些变迁? | 《5G移动无线通信技术》之一
本书全面地介绍了全球范围内对5G应用和需求、网络架构和关键技术的研究成果。对于在通信行业的专家、学者、工程师和在校学生,以及关心移动无线通信技术 5G技术和应用的读者都有较高的参考价值。
移动无线通信技术经历了哪些变迁? | 《5G移动无线通信技术》之一
|
物联网 数据挖掘 5G
全球5G倡议 | 《5G移动无线通信技术》之三
这本书全面地介绍了全球范围内对5G应用和需求、网络架构和关键技术的研究成果。对于在通信行业的专家、学者、工程师和在校学生,以及关心移动无线通信技术 5G技术和应用的读者都有较高的参考价值。
全球5G倡议  | 《5G移动无线通信技术》之三
|
前端开发 Android开发 iOS开发
尝试做“无线研发工程师”有感
开始着手写这个文章的时候,心生感概,好像终于在业务上阶段性的理顺了一些事情和代码。可以挤出那么一点点时间写写自己这一两个月以来的感受了。 前段时间一直在业务压力中,熟悉代码,改代码,堆代码。甚至都没来得及好好的想想业务的加分项,包括可做的更有价值的事情。
2981 0
|
物联网 智能硬件 安全