文章标题很随意,些微有一些骗点击的“贼意”;但内容却是充满了诚意,想必你已经感受到了。
这是一次源于头条 Android 客户端软件架构问题的探讨,之所以冠上“嘴炮”之名,是因为它有一些务虚;同时又夹杂了一些方法论,不仅适用于客户端软件架构,也适用于其他工作场景,希望对大家有所帮助。
为了拉满读者的带入感,且以“我们”为主语,来看架构的挑战、判断和打法。
优秀的公司对架构都有着很高的期许,都希望有一个良好的顶层设计,从上到下有统一的认知,遵循共同的规范,写出让人舒适的代码,甚至有那么一丢偷懒,有没有“一劳永逸”的架构设计可保基业长青?
然而高期望意味着高落差,面对落差,我们容易焦虑:
大量问题的最终归因都是代码问题:设计不合理、使用不规范、逻辑太晦涩、编码“坑”太多。
没有一个单一的团队能承担这些问题的责任,我们收到过很多“吐槽”:
一方面,口嗨一时爽,我们“吐槽”历史代码得到了一时的舒缓;另一方面,也意味着责任也传递到了我们:处理得好,我们的产出可能还是一样会被当作糟粕,但如果处理不好,我们就断送了业务发展的前程。
架构面临的从来不是单一的业务问题,而是多个业务多人协作的交叉问题,负重前行是常态。
我们经常说代码复杂度高,并把降复杂度作为架构方向的重点工作之一;但影响复杂度的因子众多,从外部来看,有主观感受、客观指标、行业对标三个角度;从内部来看,有工程组织、代码实现和技术栈三个角度。即便我们很好的优化了工程结构这个因子,短时间内也很难感受到复杂度有一个明显的下降。
我们常说治理,其实是设计一种机制,在这种机制下运转直到治愈。
就像老中医开方子,开的不是特效药,而是应对病症的方法,是不是有用的方子,终究还是需要通过实践和时间的检验。希望我们不要成为庸医,瞎抓几把药一炖,就吹嘘药到病除。
谁来复盘架构问题,都免不了炒一炒“冷饭”;谁来规划架构方向,都逃不出了“减负”、“重构”、“复用”、“规范”这些关键词。难点在于把冷饭炒热,把方向落实。
架构并不只局限于一个产品的初始阶段,而是伴随着产品的整个生命周期。架构也不是一成不变的,它只适合于特定的场景,过去的架构不一定适合现在,当下的架构不一定能预测未来,架构是随着业务不断演进的,不会出现架构方向做到头了、没有事情可搞了的情况,架构永远生机勃勃。
迄今为止,用于商业应用程序的最流行的软件架构设计模式是大泥球(Big Ball of Mud, BBoM),BBoM 是“......一片随意构造、杂乱无章、凌乱、任意拼贴、毫无头绪的代码丛林。”
泥球模式将扼杀开发,即便重构令人担忧,但也被认为是理所应当。然而,如果还是缺乏对领域知识应有的关注和考量,新项目最终也会走向泥球。没有开发人员愿意处理大泥球,对于企业而言,陷入大泥球就会丧失快速实现商业价值的能力。
——《领域驱动设计模式、原理与实践》Scott Millett & Nick Tune
只要业务继续发展,越来越复杂就是必然趋势,这贴合热力学的熵增定律。
可以从两个维度来看复杂度熵增的过程:理解成本变高和预测难度变大。
理解成本:规模和结构是影响理解成本的两个因素
当需求增多时,软件系统的规模也会增大,且这种增长趋势并非线性增长,会更加陡峭。倘若需求还产生了事先未曾预料到的变化,我们又没有足够的风险应对措施,在时间紧迫的情况下,难免会对设计做出妥协,头疼医头、脚疼医脚,在系统的各个地方打上补丁,从而欠下技术债(Technical Debt)。当技术债务越欠越多,累积到某个临界点时,就会由量变引起质变,整个软件系统的复杂度达到巅峰,步入衰亡的老年期,成为“可怕”的遗留系统。
正如饲养场的“奶牛规则”:奶牛逐渐衰老,最终无奶可挤;然而与此同时,饲养成本却在上升。
——《实现领域驱动设计 - 深入理解软件的复杂度》张逸
预测难度:当下的筹码不足以应对未来的变化
既然注定是逆风局,那跑到最后就算赢。
过多的流程规范反倒会让大家觉得是自己是牵线木偶,牵线木偶注定会随风而逝。
我们应该更多“强调”一些原则,譬如:分而治之、控制规模、保持结构的清晰与一致,而不是要求大家一定要按照某一份指南进行架构设计,那既降低不了复杂度,又跟不上变化。“强调”并不直接解决问题,而是把重要的问题凸显出来,让大家在一定的原则下自己找到问题的解决办法。
我们的套路是:定义问题 → 确定架构 → 方案落地 → 结果复盘。越是前面的步骤,就越是重要和抽象,也越是困难,越能体现架构师的功力。所以,我们打法的第一步就是要认清问题所在。
架构的问题是盘根错节的,将所有问题放在一起,就有轻重缓急之分,就有类别之分。区分问题的类别,就能在一定的边界内,匹配上对应的人来解决问题。
工程架构:
业务架构:
基础能力:
标准化:
挑战、问题、手段这些经常混为一谈,哪些是挑战?哪些是问题?那些是手段?其实这些都是一回事,就是矛盾,只是不同场景下,矛盾所在的层级不同,举一个例子:
我们判断当前的研发体验不能满足业务日渐延伸的需要,这是一个矛盾,既是当下的挑战,也是当下的一级问题。要处理好这个矛盾,我们得拆解它,于是就有了二级问题:我们的代码逻辑是否已经足够优化?研发流程是否已经足够便捷?文档工具是否已经足够完备?二级问题也是矛盾,解决好二级问题就是我们处理一级矛盾的手段。这样层层递进下去,我们就能把握住当前我们要重点优化和建设的一些基础能力:插件化、热更新、跨端能力。
在具体实践过程中,基础技术能力还需要继续拆解,譬如:热更新能力有很多(Java 层的 Robust/Qzone 超级补丁/Tinker 等,Native 层的 Sophix/ByteFix 等),不同热更方案各有优劣,适用场景也不尽相同。我们要结合现状做出判断,从众多方案汲取长处,要么做出更大的技术突破创新,要么整合已有的技术方案进行组合创新。
亨利福特说,如果我问客户需要什么,他们会告诉我,他们需要一匹更快的马。从亨利福特的这句话,我们可以提炼出一个最直接的问题:客户需要一匹更快的马。立足这个问题本身去找解决方案,可能永远交不出满意的答卷:寻找更好的品种,更科学的训马方式。
思考问题背后的问题,为什么客户需要一匹更快的马?可能客户想要更快的日常交通方式,上升了一个层次后,我们立刻找到了更好的解决方案:造车。
我们不能只局限于问题本身,还需要看到问题背后的问题,然后才能更容易找到更多的解决方案。
认知金字塔
引用认知金字塔这个模型,谨以此共勉,让我们能从最原始数据中,提炼出解决问题的智慧。
DATA: 金字塔的最底层是数据。数据代表各种事件和现象。数据本身没有组织和结构,也没有意义。数据只能告诉你发生了什么,并不能让你理解为什么会发生。
INFORMATION: 数据的上一层是信息。信息是结构化的数据。信息是很有用的,可以用来做分析和解读。
KNOWLEDGE: 信息再往上一层是知识。知识能把信息组织起来,告诉我们事件之间的逻辑联系。有云导致下雨,因为下雨所以天气变得凉快,这都是知识。成语典故和思维套路都是知识。模型,则可以说是一种高级知识,能解释一些事情,还能做预测。
WISDOM: 认知金字塔的最上一层,是智慧。智慧是识别和选择相关知识的能力。你可能掌握很多模型,但是具体到这个问题到底该用哪个模型,敢不敢用这个模型,就是智慧。
这就是“DIKW 模型”。
架构的问题不能等,也不能急。一个大型应用软件,并非要求所有部分都是完美设计,针对一部分低复杂性的区域或者不太可能花精力投入的区域,满足可用的条件即可,不需要投入高质量的构建成本。
以治理头条复杂度为例:
细节已打码,请读者不要在意。重点在于厘清问题之后,螺旋式上升,做到长期有方向,短期有反馈。
一顿输出之后,千万不能忘却了人文关怀,毕竟谋事在人。架构狮得供起来,他们高瞻远瞩,运筹帷幄;但架构人,却是更需要被点亮的,他们可能常年在“铲屎”,他们期望得到认可,他们有的还没有对象...干着干着,架构的故事还在,但人却仿佛早已翻篇。