什么样的软件架构是好的?
“All models are wrong, some models are useful” ——George Box
没有放之四海皆准的好与坏的标准。下面我对于衡量软件架构好坏的AAA原则:
可考核(Accountable):好的软件架构让每个团队都有自己负责的业务目标
可自主(Autonomous):好的软件架构让每个团队都一定的自主性可以独立往前跑,而不总是被其他团队阻塞
可复用(Amortized):好的软件架构鼓励对未来投资,使得基础设施的成本可以被摊销
可考核>>可自主>可复用。在上世纪90年代,代码复用是面向对象社区的热门话题。然后SOA和DDD又来告诉我们“可自主”才是最重要的。但是我发现实践中,无论是“可自主”还是“可复用”都很模棱两可。很难用这两个原则去说服其他人,用X的方式来分解问题会比用Y的方式来分解问题更好。但是如果你说,这么分解可以让每个团队更可考核,就显得特别理所当然。
开发者无法估算工作量
“可考核性”是一切的关键。我认为“缺乏可考核性”是现在的软件开发模式最大的危机,这个问题比”无法管理所谓的复杂性“还要更严重。
开发者是无法估算工作量在行业里也不算什么秘密了。这带来了很多根本性问题:
因为我们无法知道真正多少人才是必须的,所以中层管理总是比着招聘人头上限,尽可能的加人。为什么会这样做?很简单,他们的薪酬和他们所管理的人头数是成正比的。
把软件重构得”更可维护“没有商业价值。什么叫可维护性?问题如果解决不了,扔更多的人进去总是可以解决的。软件工程又不是造火箭,能有多难?根本无法证明重构可以节省多少人力,因为就没有可对标的重构前的应投入人力。
要解决这个问题,我认为不是去搞明白开发工作量的评估的魔法。恰恰相反,如果我们和业务负责人是以同一个团队的方式来工作,我们就压根不需要去估算工作量。每个软件开发团队都有应该有“唯一的一个”对应的业务团队,业务团队背什么样的OKR,技术团队就背什么样的OKR。要让团队可考核,最重要的是只对一个业务负责。
上面是一个典型的组织架构图。每个小团队的OKR,要和其上一级团队的OKR对齐。OKR里面的关键产出要是可度量的,才能让每个团队真正对某个事情负责。
典型的坏的软件架构是这样的:有大量的微服务团队。业务负责人总是需要直接去找多个微服务团队才能达成他的目标。每个需求都需要和多个不同的软件团队重复沟通。每个技术团队都没法清楚地说明白他们以及他们的微服务负责的业务目标是什么。正因为如此,技术们无法说清楚自己对业务到底有什么价值。
让我们再强调一遍:软件架构的“头号目标”应该是让每个分解出来的团队都能够有业务目标去负责。
Bounded Context
在大的尺度上,架构就是分解Bounded Contexts(参见领域驱动开发,DDD)。这就是把业务的组织架构图体现到软件的世界里:
以电商领域为例,业务被分解为上面这些Bounded Contexts。没有一个技术团队可以覆盖横跨这些Bounded Contexts的业务流程的。这并不是啥坏事情,大的问题被分解为了小问题,业务和技术在一个Bounded Context的范围内,携手朝着共同的目标去努力。
虚拟空间和智慧体
一个Bounded Context对于一个团队来说仍然太大了。至少在微服务的心智下是这么认为的。如何把它进一步分解为更可管理的小块呢?我的模型是“虚拟空间和智慧体”。我们做为程序员所做的事情,简单来说就两个:
虚拟空间
有点智慧的“机器人”和我们人类一起在虚拟空间里交互
虚拟空间和我们肉身所处的物理空间是一样的,它都是构建在因果关系上的。有两种法则主导这些因果:
自然法则:大自然自身的内在规律
社会法则:一个人造的体系,人们通过模仿自然法则创造出类似稳定的规则系统去构建稳定的社会秩序
比如引力是自然法则。而“借钱要还”是社会法则。两者的工作方式是类似的,给定某些前因,根据法则,就必须有某些后果。我们使用C/C++/Java/GO/……等来描述这些法则。从光线跟踪算法到word文本编辑器到电商交易平台,从构建规则的角度来说是差不多的。“法则”必须是静态的可预测的,就像用水泥构建了我们的真实世界一样。
在我们构建的虚拟空间智商,我们作为人类彼此之间进行交互,例如社交网络和交易。由人类扮演的角色正逐步被我们缩写的人工智能的"机器人”所替代。例如,之前是人工的编辑去挑选内容,现在可能是机器人来产生新闻,给你准备每天开屏的首页。“机器人"正变得越来越复杂,某天他们会从虚拟空间里出来,直接走向物理空间。
“虚拟空间”和“机器人”这两种软件代码的工作方式差异性是很大的。“虚拟空间”从原因推导出结果,来维护自然和社会的秩序。“机器人”的工作方式是相反的,它收集事实反推出模型来最大化其目标。把智能的部分和系统的其他部分明确地区分出来至关重要。我们作为人类希望规则是静态的从而构建出稳定的预期。如果“法则”总是不断在变,“虚拟空间”看起来就像是“魔法空间”,它就变得和我们从真实空间获得的生活体验很不一样了。
软件开发中的Model,View,Controller(MVC)的概念可以用来解释“虚拟空间”。人类和“机器人”是所谓的智慧体。Model根据自然和社会法则定义的因果去维护数据的完整性。View和Controller提供了便捷的接口给人类和“机器人”去交互。
所有权==著作权
“虚拟空间”这部分仍然太大了。业务流程可能会有很多个步骤,例如:
而且不同的Bounded Context的业务流程之间也是有集成关系的:
可考核性问题的根源是编程语言里缺乏对完整因果链的直接描述能力。我们可以在白板上画一个清楚的业务流程图,但是在写代码的时候就不需要切分成很多细碎的服务和函数来表达。之所以工作流引擎总是被拿出来考虑,因为它的描述能力和要解决的问题有良好的映射。但是BPMN并不是一种编程语言。
步骤与步骤之间有很强的因果关系。在产品详情页展示的促销,也应该体现在购物车里,也应该体现在收银页面上,也应该体现在最终的收据里。我们使用的“function”的概念,顶多只能用来描述500ms内发生的事情的因果关系。对于前面所述的业务流程,我们切分成了很多个步骤,同时又按照使用方的不同,切成成了很多个面向用户的服务。从而因果关系就被隐藏在这些庞杂的实现细节之中了。软件跑起来就像一场接力赛,一个服务把职责接过来,搞一搞之后,又传递给另外一个服务。理想的情况是,代码本身就应该体现流程图,读起来就像流程图。
更糟糕的是,现在的切分方式并没有明确的划线的原则。这就频繁导致了团队之间关于谁应该负责什么的争论。高度政治化的组织氛围导致了开发者情绪上的沮丧。同时,具有讽刺意味的是,在大家彼此抢活的同时,又因为职责切分得太碎,导致又没有一个团队能够对全局负责。
对于解决这个问题,目前能够做到的“最佳实践”就是在一堆微服务团队上架一个门面团队。“所有权==著作权”,我们只愿意对自己所写的东西负责。这个人性,无法改变。为了给这些可怜的家伙具有所有权的感觉,我们必须允许一层很薄的代理层,或者叫所谓的调度服务来把微服务给“屏蔽"在后面。但是这种代理一层的做法经常导致了很低的团队自主性。
理想的编程语言,应该能够提供“function”一样的东西去直接描述业务流程。业务上的同时行进的并发流程应该可以像多线程编程一样,用消息传递的方式来描述。这样,我们可以给每一个可切分出来的业务流,分配一个独立的软件团队去端到端负责。他们可以对自己负责的事情100%负责。这些人和业务运营人员,以及编写出来的“机器人”合在一起作为同一个团队,共同负责这个业务流的收益和亏损。而不是单独把技术摘出来,成为一个共享的成本中心。
协作单元
除此之外,还有一个事情是有问题的。之前由编程语言提供的模块化单元,例如assembly/package/class这些,就是我们团队之间彼此协作的单元。然而现在不是这样了。现在越来越多的人,要求软件模块有独立版本,能够独立的部署。因为这样才能支持多个团队的独立性。这就导致了大量的微服务的做法。
但是我们是否“总是”需要用不同的编程语言不同的工具来实现微服务?语言的差异和彼此割裂的工具导致跨团队沟通更加困难。你可以拥有你的流程,拥有你的服务,但是这不阻碍你和你的伙伴们用同一门语言啊。一门编程同时扮演了3个角色:它连接了机器,它连接了开发者,它同时又连接了团队。今天编程语言更多是仅仅作为一种连接机器和开发者个体之间的工具,团队之间宏观上是彼此割裂的。
解决方案应该是把软件作为一个整体来思考,而不是被狭隘的“操作系统进程”的视角给限制了。构建新的微服务的成本,应该和后台用function启动一个线程没有多大区别。理想的编程语言里,我们有各种各样的function,但是执行机制不同。