.. Kenneth Lee 版权所有 2024
:Authors: Kenneth Lee :Version: 0.1 :Date: 2024-12-20 :Status: Draft
语义纠偏——细节调研的目的和方向
这个总结最初是想谈架构师有没有必要编码的,但我想得到的结论不是讨论架构师有没有 必要编码,而是架构怎么做失败的。
我之前讨论架构设计的关键,一直用“道”和“名”这两个概念。“道”是全部的事实,而“名” 是对“道”的“总结”。名描述了道的部分特征,但不是全部的特征。所以道可道,但所道之 道(名),非道之本体。
这就好像函数signature描述了函数的接口,但没有描述函数的实现,而我们常常用 signature去“代表”这儿函数,似乎我们说的是这个函数的实现,但其实我们不是。关键 是:函数的实现信息通常signature多,但它的范围通常比signature小。
这个事实很容易解释和理解,但如果你没有深入考虑这个问题,你可能就没有注意到这个 问题的存在。
考量下面这个signature:::
void list_add(struct list_node node, struct list_node head);
这是个非常典型的Linux内核形式的双向链表的函数定义。我们用这个“名”代表了这个函 数的所有可能实现。但它既可以代表下面这样的实现:::
void list_add(struct list_node node, struct list_node head) { log("node %p added to %p\n", node, head); node->next = head->next->next; node->prev = head; head->next = node; node->next-prev = node; }
也可以代表下面这样的实现:::
void list_add(struct list_node node, struct list_node head) { validate(node); validate(head); __list_add_rcu(node, head, head->next); }
这里任何一个实现的信息,都包含了比前面的签名多的逻辑。但逻辑越多限定的范围也越 小,所以signature限定的范围反而是最大的。
signature可以包含任何一种实现。但我们最终会选择一种实现,这个实现总是产生额外 的限制,从而缩小了范围。
这里我只用了一层作为类比,现实的设计显然不会只有几层,比如我最近在做的一个多机 共享内存的实现,每个逻辑要考虑从用户态穿到内核,然后从内核下到虚拟机后端,在从 虚拟机的用户态进入Host的内核,到BIOS,然后访问总线上的控制器……而且每个这样的逻 辑还要和其他逻辑配合……这中间涉及的细节就不是我们的脑子可以同时控制住的。然后我 们在每个设计上就不得不落在某个层面的“名”上面,而没法工作在道上面。
所以,对于做架构来说,我们最难的问题在于,我们所认识的名,以及我们抽象出来的这 个名的所有属性,不一定关注了我们的细节中真正要关心的部分。
所以,架构设计的第一步,不是根据我们已经知道的名字去思考设计逻辑。而是我们首先 得去重新建立我们对这个总结的认知。
比如用户说,“我想要一个Web Server”。你不能马上就认为他要的是一个Nginx那样的东 西。他说的Web Server也不见得就是Web Server,说不定在他看来所有的后端Daemon都叫 Web Server。他心目中的Web Server也不见得是跑在服务器上的,说不定是实现在FPGA上 的。我们说话总是不得不用名,但相同的名,表示的道不一样,关注的属性也不一样,这 才是用架构语言进行沟通的难点。
我说了这么多,我现在和你讨论的“名”,和“道”,和文章一开头说的“名”和“道”的那些意 思,是不是已经不一样了?
所以,在架构设计的早期,我们详细说说业务的使用过程,某个操作员的一天工作日常, 客户的钱是怎么赚到的,为什么客户概念中会有“巡检”这个概念……等等等等,我们都是在 丰富我们一开始并不熟悉的各种“名”,然后我们的设计逻辑才会靠谱。因为这样以后,我 们的名才代表我们说的那个“道”。
我们很多构架设计的错误就在于没有认识到:很多名字的语义,和我们实际对这个名字的 理解是不一样的。所以,架构师确实有必要去编码,这个目的不是为了写什么“核心代码”, 也不是为了“亲近一线,和一线同甘共苦”,而是架构师必须通过这些形式去丰富对那个名 字的认知。
而我们一开始就知道架构师无法编完所有的代码,也无法理解所有的逻辑,这是人脑限制 的。所以架构师通常编“不重复的主要特征逻辑”,比如把模块串起来的主要逻辑,典型的 模块等等,这其实也不那么重要,关键他得知道他的目的就不是为了输出那些代码,而是 他要靠这种工作来辅助他完成一个靠谱的设计,目的是在设计上的,不是为了输出那些代 码。
另一个更关键的问题在于,我们设计上的创新,也都来源于我们对这些概念的重新认知, 用户需要做一个新的产品,必然因为发现了新的问题。他的条件不一样了,就算是出于 “供应链替代”这样的条件,你也会有“技术已经进步”,“我们对过去犯过的错误有新的认 识”,“产品使用范围有所变化”……这些因素的变化,如果我们总是用过去的思维来解释我 们现在用到的概念,我们就会背上所有本来不需要背负的负担。也就基本上失去创新的能 力了。而不做设计的人,往往陷入的就是这样的境地。因为如果不做设计,他就只能使用 固定的名和这个名的属性去制造自己的细节约束。范围都限定死了,你也就失去创新能力 了。
这有人称为“第一性原理”,其实前提就是你要“打开”名字的空间,要花足够的时间去“感 受”一些这个名字的“具象”是什么样的。这才是我们拉高层面进行设计的目的。