动手点关注 干货不迷路
文章标题很随意,些微有一些骗点击的“贼意”;但内容却是充满了诚意,想必你已经感受到了。
这是一次源于头条 Android 客户端软件架构问题的探讨,之所以冠上“嘴炮”之名,是因为它有一些务虚;同时又夹杂了一些方法论,不仅适用于客户端软件架构,也适用于其他工作场景,希望对大家有所帮助。
为了拉满读者的带入感,且以“我们”为主语,来看架构的挑战、判断和打法。
我们的挑战
期望高
优秀的公司对架构都有着很高的期许,都希望有一个良好的顶层设计,从上到下有统一的认知,遵循共同的规范,写出让人舒适的代码,甚至有那么一丢偷懒,有没有“一劳永逸”的架构设计可保基业长青?
然而高期望意味着高落差,面对落差,我们容易焦虑:
- 代码什么时候能写的看上去本就应该是那个样子;而现在怎么就像是在攀登“屎山”呢?
- 文档什么时候能写的既简明又详细;而现在怎么就简明的看不懂,详细的很多余呢?
- 工具什么时候能更好用更强大一点;而现在怎么就动不动掉链子,没有想要的功能常年等排期呢?
- “我”什么时候能从架构工作中找到成就感,而不是搞一搞就想着跑路呢?
大量问题的最终归因都是代码问题:设计不合理、使用不规范、逻辑太晦涩、编码“坑”太多。
没有一个单一的团队能承担这些问题的责任,我们收到过很多“吐槽”:
- 这尼玛谁写的,简直不堪入目,看小爷我推倒重来展现一把真正的实力
- XX 在这里埋了颗雷,但 XX 已经不管了,事到如今,我也只能兜底搞一把
- 这压根就不应该这么用,本来的设计又不是为了这个场景,乱搞怪我咯?
- 卧槽,这特么是隐藏技能啊,编译时悄悄改了老子的代码,找瞎了都没找到在哪过环节渗透进来的
事情难
架构面临的从来不是单一的业务问题,而是多个业务多人协作的交叉问题,负重前行是常态。
- 业务历久弥新,历史包袱叠加新的场景,随便动动刀子就拔出萝卜带出泥。譬如:头条 2021 年 10 月的版本有 XXXX 组件,相比一年前已经翻倍;类个数 XXXXX;插件 XX 个;仓库数量 XX 个;ttmain 仓库权限 XXX 人。(XX 代表数量级,隐去了具体数字,^_^)
- 技术栈层出不穷,一方面要保持成熟稳定,一方面要积极探索落地。架构的同学要熟悉多种技术栈,譬如:跨端技术在客户端业务中通常都是多种共存(H5/Hybrid/小程序/Lynx/Flutter),一个业务到底选用哪种技术栈进行承载,需要耗费多少成本?选定技术栈后存在什么局限,是否存在不可逾越的障碍?
我们经常说代码复杂度高,并把降复杂度作为架构方向的重点工作之一;但影响复杂度的因子众多,从外部来看,有主观感受、客观指标、行业对标三个角度;从内部来看,有工程组织、代码实现和技术栈三个角度。即便我们很好的优化了工程结构这个因子,短时间内也很难感受到复杂度有一个明显的下降。
文章图片
我们常说治理,其实是设计一种机制,在这种机制下运转直到治愈。我们的判断 架构问题老生常谈
就像老中医开方子,开的不是特效药,而是应对病症的方法,是不是有用的方子,终究还是需要通过实践和时间的检验。希望我们不要成为庸医,瞎抓几把药一炖,就吹嘘药到病除。
谁来复盘架构问题,都免不了炒一炒“冷饭”;谁来规划架构方向,都逃不出了“减负”、“重构”、“复用”、“规范”这些关键词。难点在于把冷饭炒热,把方向落实。
文章图片
架构方向一直存在
架构并不只局限于一个产品的初始阶段,而是伴随着产品的整个生命周期。架构也不是一成不变的,它只适合于特定的场景,过去的架构不一定适合现在,当下的架构不一定能预测未来,架构是随着业务不断演进的,不会出现架构方向做到头了、没有事情可搞了的情况,架构永远生机勃勃。
文章图片
文章图片
- 强制遵循规范: 通常会要求业务公共的组件逐渐下沉到基础组件层,但随着时间的推移,这个规范很容易被打破
- 需要成熟的团队: 领域专家(对业务细节非常熟悉的角色)和开发团队需紧密协作,构建出核心领域模型是关键。但盲目尝试 DDD 往往容易低估领域驱动设计这套方法论的实践成本,譬如将简单问题复杂化、陷入过分强调技术模型的陷阱
迄今为止,用于商业应用程序的最流行的软件架构设计模式是大泥球(Big Ball of Mud, BBoM),BBoM 是“......一片随意构造、杂乱无章、凌乱、任意拼贴、毫无头绪的代码丛林。”复杂系统熵增不断
泥球模式将扼杀开发,即便重构令人担忧,但也被认为是理所应当。然而,如果还是缺乏对领域知识应有的关注和考量,新项目最终也会走向泥球。没有开发人员愿意处理大泥球,对于企业而言,陷入大泥球就会丧失快速实现商业价值的能力。
——《领域驱动设计模式、原理与实践》Scott Millett & Nick Tune
只要业务继续发展,越来越复杂就是必然趋势,这贴合热力学的熵增定律。
可以从两个维度来看复杂度熵增的过程:理解成本变高和预测难度变大。
文章图片
理解成本:规模和结构是影响理解成本的两个因素
- 宏大的规模是不好理解的,譬如:在城市路网中容易迷路,但在乡村中就那么几条道
- 复杂的结构是不好理解的,譬如:一个钟表要比一条内裤难以理解
当需求增多时,软件系统的规模也会增大,且这种增长趋势并非线性增长,会更加陡峭。倘若需求还产生了事先未曾预料到的变化,我们又没有足够的风险应对措施,在时间紧迫的情况下,难免会对设计做出妥协,头疼医头、脚疼医脚,在系统的各个地方打上补丁,从而欠下技术债(Technical Debt)。当技术债务越欠越多,累积到某个临界点时,就会由量变引起质变,整个软件系统的复杂度达到巅峰,步入衰亡的老年期,成为“可怕”的遗留系统。预测难度:当下的筹码不足以应对未来的变化
正如饲养场的“奶牛规则”:奶牛逐渐衰老,最终无奶可挤;然而与此同时,饲养成本却在上升。
——《实现领域驱动设计 - 深入理解软件的复杂度》张逸
- 业务变化不可预测,譬如:头条一开始只是一个单端的咨询流产品,5 年前谁也不会预先设计 Lite 版、抖音、懂车帝等,多端以及新的业务场景带来的变化是无法预测的。很多时候,我们只需要在当下做到“恰当”的架构设计,但需要尽可能保持“有序”,一旦脱离了“有序”,那必将走向混乱,变得愈加不可预测
- 技术变化不可预测,譬如:作为一个 Java 开发人员,Lambda 表达式的简洁、函数式编程的快感、声明式/响应式 UI 的体验,都是“真香”的技术变化,而陈旧的 Java 版本以及配套的依赖都需要升级,一旦升级,伴随着的就是多版本共存、依赖地狱(传递升级)等令人胆颤的问题。很多时候,我们不需要也没办法做出未来技术的架构设计,但需要让架构保持“清晰”,这样我们能更快的拥抱技术的变化
既然注定是逆风局,那跑到最后就算赢。我们的打法 我们的套路是:定义问题 → 确定架构 → 方案落地 → 结果复盘。越是前面的步骤,就越是重要和抽象,也越是困难,越能体现架构师的功力。所以,我们打法的第一步就是要认清问题所在。
过多的流程规范反倒会让大家觉得是自己是牵线木偶,牵线木偶注定会随风而逝。
我们应该更多“强调”一些原则,譬如:分而治之、控制规模、保持结构的清晰与一致,而不是要求大家一定要按照某一份指南进行架构设计,那既降低不了复杂度,又跟不上变化。“强调”并不直接解决问题,而是把重要的问题凸显出来,让大家在一定的原则下自己找到问题的解决办法。
认清问题
问题分类 架构的问题是盘根错节的,将所有问题放在一起,就有轻重缓急之分,就有类别之分。区分问题的类别,就能在一定的边界内,匹配上对应的人来解决问题。
工程架构:
文章图片
业务架构:
文章图片
基础能力:
文章图片
标准化:
文章图片
问题分级 挑战、问题、手段这些经常混为一谈,哪些是挑战?哪些是问题?那些是手段?其实这些都是一回事,就是矛盾,只是不同场景下,矛盾所在的层级不同,举一个例子:
文章图片
我们判断当前的研发体验不能满足业务日渐延伸的需要,这是一个矛盾,既是当下的挑战,也是当下的一级问题。要处理好这个矛盾,我们得拆解它,于是就有了二级问题:我们的代码逻辑是否已经足够优化?研发流程是否已经足够便捷?文档工具是否已经足够完备?二级问题也是矛盾,解决好二级问题就是我们处理一级矛盾的手段。这样层层递进下去,我们就能把握住当前我们要重点优化和建设的一些基础能力:插件化、热更新、跨端能力。
在具体实践过程中,基础技术能力还需要继续拆解,譬如:热更新能力有很多(Java 层的 Robust/Qzone 超级补丁/Tinker 等,Native 层的 Sophix/ByteFix 等),不同热更方案各有优劣,适用场景也不尽相同。我们要结合现状做出判断,从众多方案汲取长处,要么做出更大的技术突破创新,要么整合已有的技术方案进行组合创新。
勤于思考问题背后的问题 亨利福特说,如果我问客户需要什么,他们会告诉我,他们需要一匹更快的马。从亨利福特的这句话,我们可以提炼出一个最直接的问题:客户需要一匹更快的马。立足这个问题本身去找解决方案,可能永远交不出满意的答卷:寻找更好的品种,更科学的训马方式。
思考问题背后的问题,为什么客户需要一匹更快的马?可能客户想要更快的日常交通方式,上升了一个层次后,我们立刻找到了更好的解决方案:造车。
我们不能只局限于问题本身,还需要看到问题背后的问题,然后才能更容易找到更多的解决方案。
认知金字塔
引用认知金字塔这个模型,谨以此共勉,让我们能从最原始数据中,提炼出解决问题的智慧。
文章图片
DATA: 金字塔的最底层是数据。数据代表各种事件和现象。数据本身没有组织和结构,也没有意义。数据只能告诉你发生了什么,并不能让你理解为什么会发生。
INFORMATION: 数据的上一层是信息。信息是结构化的数据。信息是很有用的,可以用来做分析和解读。
KNOWLEDGE: 信息再往上一层是知识。知识能把信息组织起来,告诉我们事件之间的逻辑联系。有云导致下雨,因为下雨所以天气变得凉快,这都是知识。成语典故和思维套路都是知识。模型,则可以说是一种高级知识,能解释一些事情,还能做预测。
WISDOM: 认知金字塔的最上一层,是智慧。智慧是识别和选择相关知识的能力。你可能掌握很多模型,但是具体到这个问题到底该用哪个模型,敢不敢用这个模型,就是智慧。
这就是“DIKW 模型”。循序渐进
架构的问题不能等,也不能急。一个大型应用软件,并非要求所有部分都是完美设计,针对一部分低复杂性的区域或者不太可能花精力投入的区域,满足可用的条件即可,不需要投入高质量的构建成本。
以治理头条复杂度为例:
- 长期的架构目标:更广(多端复用)、更快(单端开发速度)、更好(问题清理和前置拦截)
- 当下的突出问题:业务之间耦合太重、缺少标准规范、代码冗余晦涩
文章图片
细节已打码,请读者不要在意。重点在于厘清问题之后,螺旋式上升,做到长期有方向,短期有反馈。最后 一顿输出之后,千万不能忘却了人文关怀,毕竟谋事在人。架构狮得供起来,他们高瞻远瞩,运筹帷幄;但架构人,却是更需要被点亮的,他们可能常年在“铲屎”,他们期望得到认可,他们有的还没有对象...干着干着,架构的故事还在,但人却仿佛早已翻篇。
加入我们 我们是今日头条客户端 Android 团队,我们的业务方向包括:
- 架构优化(深圳/北京):负责头条系 APP 的客户端架构、插件、热修等基础能力建设优化等;
- 增长业务(深圳):负责头条系 APP 的金币、活动、联动等增长相关业务,海量数据挖掘业务增长;
- 安全合规(广州/深圳):负责头条系 APP 的安全、隐私保护、监管合规等工作;
【大数据|一次关于架构的“嘴炮”】
文章图片
点击“阅读原文”了解岗位详情
推荐阅读
- java|iOS 高刷屏监控 + 优化(从理论到实践全面解析)
- 大数据|自动拦截 50% crash,字节自研 Fastbot 如何助力今日头条稳定性测试
- c++|一文读懂 Android FFmpeg 视频解码过程与实战分析
- java|【Rust日报】2022-03-21 Firefox 现在约 10% 的代码为 Rust
- python|【Rust日报】2022-03-22 fluent-uri(一个快速、简单和严格的URI解析器)
- c++|【Rust日报】2022-03-23 RustSBI软件发布v0.2.2版本
- java|【Rust日报】2022-01-28 Rust 编程,2022 年的展望
- java|【Rust日报】2021-12-19 Foundry(以太坊应用程序开发工具包)
- 嵌入式|【Rust 日报】2021-11-21 The RustFest Global - Rust in Arts