为什么工程师都需要需要一个白板

一家靠谱的软件公司的墙面上都有许多写字的白板,微软研究院的休息室里甚至能在茶几上写字。而 ThoughtWorks 新装修的办公室直接把白板作为墙面的装修材料,以至于在洗手间都能写字,当然一般人也不会真的去写。

在我曾经服务的一家公司里,因为把白板文化引入团队,获得了不少意外收获。

我们曾经设计过一个简单的任务调度系统,和现在的 Elastic Job 类似,可以将用户提交的任务放到不同的服务器中执行。看起来一个简单的业务,细节越讨论越多,以至于迟迟没有进展。我们陷入了一个怪圈,前一天的结论容易被忘掉,然后每天都在讨论重复的内容。

在一次起身倒水的时候,我注意到复印机旁的办公用品,才意识到这家公司竟然没有一块白板可以用来写字和画图。于是,联系了行政购买了一块白板,放到了我们每天去的会议室。有了白板的加成,讨论意外的顺利,以至于老板也开心的加入了我们。

这个项目顺利完成之余,老板竟专门为我设计了一个奖项 —— “最佳建议奖”。

01. 白板文化

后来去了其他公司,我留意到越是专业的软件公司,越会使用白板来进行讨论。尤其是到外企后,接触到海外公司越来越多,白板甚至是一种文化,越来越多的公司在办公室提供可以写字的墙面和容易擦写的马克笔。

和一些大牛交流,可能他随手从口袋中取出一支笔,然后就近写写画画。可能在这些人眼里,这是工程师之间交流最好的方式。

白板文化代表一种开放的精神,它意味着每一个人都可以表达自己的想法,而不是只有领导安排,然后员工执行。正是这种文化,形成了技术公司的一种独特风格。

这种文化的背后其实是一种原型思维,这些白板上的内容快速呈现、容易修改、不受限制。往往在讨论时,我们假定我们有多种解决方案,无论这些解决方案多么离谱和天马行空,我们不轻易说对和错,只选择合适和最优。这种 “把方案先放到桌子上再决定 ”的这种协作方式,往往能碰撞出意想不到的结论。

我第一次到 ThoughtWorks 面试时,我被这种文化震惊了。无处不在的白板,以及白板上的任务看板、业务模型充满了整个办公室。在面试的过程中,需要讲解一个项目案例,我和面试官随手用白板聊了起来。以至于后来在想,是不是我使用白板写写画画过于熟稔,才获得了这份工作。

我们的同事甚至把这种文化带入到家庭中。我的同事家里有一个小的白板墙面,用来安排孩子的每天的作息,家庭的采买计划,以及水电气的缴费提醒等等。

02. 白板的演变

我见过各种各样的白板,也许有适合你的。

办公活动白板。 这种就是最普通的白板,大概 100 - 200 元可以在办公用品店买到一个,有支架可以移动。通常不会很大,1 * 2.4m 往往就够用了。一般来说培训场合用的非常多,因为可以移动适合在临时会场使用。不过确定也比较明显,尺寸有限,有时候不好固定,尺寸越大越容易晃,无法多个白板拼接。

白板墙面。这种需要在装修时候设计并安装,往往能做到一个墙面整体都是白板。如果在办公室装修时就考虑到,这几乎是最好的白板方案,也是目前软件公司主流的装修风格。

白板贴。 白板贴是一种特殊的高分子材料,有背胶可以贴在合适的墙面。贴好后,可以和白板墙面媲美,不过相对柔软,书写效果不好,一般作为临时方案。

大屏虚拟白板。 这是一种带有触摸功能的大屏,开机后可以当做白板使用,当然价格也比较昂贵。它不仅可以作为白板使用,还具有投屏、多页面、通过邮件发送内容等功能。如果预算合适,这也是不错的选择。

在线协作白板。还有一种就是在线协作白板,通过浏览器的 websocket 技术建立实时连接,让分布在不同地方的团队一起讨论和协作。

03. 图例工具箱

工程师在使用白板时候可能会画一个方块和一个线条,说这是一个服务,然后被另一个服务调用。

白板的随意性可以发散思维,也带来一些不便,不同的人的白板含义不同,因此我们可以使用一些固定的图例来启发思维,和团队对齐后,可以更容易交流。

软件设计领域有一些专业的图形可以使用。

  • UML 常见图形,时序图、用例图、类图、部署图等
  • E-R 模型图
  • 交互原型图
  • 流程图

除此之外,用在商业分析上,也有一些思维模型可以使用。

  • 四象限模型
  • 金字塔模型
  • 思维导图
  • SWOT 分析
  • 电梯演讲
  • 鱼骨图
  • 5whys

这些思维模型可以用简单的方式表达繁复的内容,对于汇报、总结和分析问题都非常有帮助,也是在咨询工作中必不可少的工具。

04. 工作坊

使用白板还可以用来进行工作坊(workshop)。

工作坊一词最早出现在教育与心理学领域。工作坊的原意是制造业的车间,后来被引申为小组讨论或协同工作的过程。最早是一个叫普拉特的波士顿医生为了改善结核病人的心理状态,组织他们分享自己的故事而来。后来被欧美国家应用到心理学、医学的研究工作中,后来慢慢流传到大学、企业办公室。

在软件工程领域,工作坊这种形式非常适合需要群体协作、头脑风暴的场景。经过讨论和碰撞,在取得共识之后,往往能得到一些不错的成果。

DDD 建模中的事件风暴是一种典型的工作坊,使用工作坊来产出模型相比架构师闭门苦思有更多优点:

  1. 充分调动团队的积极性,让团队成员能参与到领域模型和架构的设计中来,获得更多输入。
  2. 团队就各种概念、业务背景达成了共识。往往,即使架构师设计出来的架构非常严密,落地的开发人员有可能因为对概念的误解无法领会设计意图。通过工作坊,可以就这些问题充分讨论。
  3. 思维碰撞。工作坊可以让 “逆向思维” 发挥作用,投资大师查理·芒格非常推崇 “逆向思维”,通过工作坊可以从不同的角度来验证方案的合理性。

除了 DDD 建模外,越来越多的培训也会使用工作坊的形式代替传统的授课方式。在培训中的工作坊,有点类似于目前流行的“翻转课堂”。除了讲师授课外,也会需要小组完成某项讨论任务和练习任务。

more >>

建模方法元模型:如何设计一个建模方法


title: 建模方法元模型:如何设计一个建模方法
categories: 技术架构
toc: true
recommend: true

在微服务时代,由于分布式系统的要求,对软件模型的要求越来越苛刻。虽然架构师们对领域驱动设计的理念重新重视起来,但是靠拍脑袋得到的模型往往不能恰当的服务于业务需要。

Eric DDD 中阐述了领域驱动设计的重要意义和一些基本实践,但是并没有给出一套具体的建模过程方法。这给架构师巨大发挥空间,各种建模方法就都可以拿来使用,比如事件风暴、 四色原型等建模过程方法。

于是有一些朋友会产生疑惑,这些建模方法背后的逻辑是什么呢,它们有没有什么共通之处?本文会和大家一起探讨软件建模过程方法的基本逻辑,以及如何设计一套简单的建模过程。

解构建模方法

目前进行领域建模方法使用的最多的是事件风暴。事件风暴的发明人是 Alberto Brandolini ,它来源于 Gamestorming,通过工作坊的方式将领域专家和技术专家拉到一起,进行建模。事件风暴非常有意思的地方在于,它先从事件开始分析,捕获事件。然后分析引发事件的行为和执行者,从这些信息中寻找领域模型,最终进一步展开。关于事件风暴的详细过程可以参考: 《DDD 建模工作坊指南》

Event Storming 的逻辑是什么?为什么需要先从事件开始分析?这是事件风暴工作坊中遇到过最多的问题。

带着这些问题请教了很多专家,甚至发送了邮件给 Alberto Brandolini,有幸得到回复。根据 Alberto Brandolini 理解,他认为系统中事件是一种容易寻找到的元素,通过头脑风暴,容易打开局面,仅此而已。

带着同样的问题,分析了几种建模方法(为了减少争议避开了公司同事发明的建模方法)。

系统词汇法(OOA)

系统词汇法就是面向对象分析方法。这种面向对象建模的方法比较原始和直接,直接通过经验提取领域模型,就是简单的面向对象分析方法。其操作过程简化如下:

  1. 首先,从需求陈述中找出所有的名词,将它们作为 ”类—对象“ 的初步候选者。去掉不正确和不必要的对象(不相关的、外部的和模糊的对象),做出合理的抽象。
  2. 为上一步的模型做出定义,构建数据字典,描述对象的范围、成员和使用场景。
  3. 聚合,把业务一致性强的对象聚合到一起。
  4. 使用合适的关联方式设计对象之间的关系。

系统词汇法建模的优点和缺点都比较明显。优点是没有过多的建模过程,对于简单的系统有经验的架构师马上就能观察出合适模型。相应的,缺点也很明确,没有对业务充分分析,直接得到模型,容易错误理解业务和过度设计模型。

用例分析法

用例模型是一种需求分析模型,是需求分析后的一种输出物,通过对用例再加工可以得到我们的领域模型。1992 年, Jacobson 中提出了用例的概念和可视化的表示方法用例图。

用例(UseCase)是对一个活动者使用系统的一项功能时所进行的交互过程的一个文字描述。

用例由参与者、关系、用例三个基本元素构成,用例图的基本表示方法如下:

image-20210611132640557

通过用例图来提取领域模型的方法如下:

  1. 梳理用例的用词,统一用例中所有的概念,避免混淆。
  2. 从用例中提取出名词,作为备选模型,这个时候不区分对象或者属性。
  3. 找动词,通过动词和用例关系分析模型之间的关联关系,比如:用户结账用例,会触发累积积分的用例,说明用户账户和积分有关联。
  4. 对名词进行抽象、展开,把用例中作为属性的名词归纳到对象中,补充为完整模型。

因为用例图是从不同的参与者出发的,非常适合表达表达业务行为,可以避免错误的复用。在很长一段时间你,很多软件架构师对的模型的建立都依赖用例图。用例分析法的特点是不容易漏,缺点是由于名词的二义性,往往会设计出一些过度复用的模型。

四色建模法

四色建模法的思路和用例略有不同,它的理念是:

“任何业务事件都会以某种数据的形式留下足迹”。

四色建模法其实是以数据驱动,通过挑选一些关键数据(类似于办事过程中的存根),来还原整个业务流程。然后基于这个线索,找出时标性对象(moment-interval)、实体(party/place/thing)、角色(Role)、描述对象(description)。

  1. 以满足业务运营的需要为原则,寻找需要追溯的业务事件。
  2. 基于这些业务事件发生的的存根,建立时标性对象,比如订单 -> 发货单 -> 提货单等。
  3. 基于时标性对象反推相应的实体,比如订单 -> 商品,发货单 -> 货物和发货员。
  4. 最后把描述的信息放入描述对象,附着在需要补充的对象上。
  5. 梳理为最终的模型。

四色建模法由 Peter Coad 提出,其实并不是一种非常主流的建模方式,其原因为存根和时标性对象在很多业务系统中并不容易找到。

事件风暴

事件风暴相对其他的建模方法非常独特,所以放到最后来说,但是简单来说,它的思路是:

“事件是系统状态变化的关键帧”。

事件是比较使用找到的,它的建模过程有点逆向思维。

  1. 寻找事件。事件(Event)是系统状态发生的某种客观现象,事件格式参考 “XXX 已 YYY”,比如 “订单已创建”。
  2. 寻找命令和执行者。命令可以类比于 UML 分析中的业务用例,是某个场景中领域事件的触发动作,执行者是命令的发生者。
  3. 寻找模型。为了在这个阶段保持和业务专家的良好沟通,寻找 “领域名词” 。
  4. 设计聚合。对领域名词进行建模,获得模型的组合、关系等信息。
  5. 划分限界上下文。对模型进行划分,在战略上将模型分为多个上下文。

事件风暴在获得模型的深刻性上具有优势,但是在操作上更为困难。另外由于它不从用例出发,和四色建模一样,可能有一些遗漏,所以对工作坊的主持人要求较高。

建模方法元模型

元模型是关于模型的模型,我们可以为建模方法建立一个模型。在计算机领域中,研究元模型的资料和书籍较少,因为涉及到更高的抽象层次,理解起来比较困难。在有限能查到的资料中,《本体元建模理论与方法及其应用》一书介绍了如何建立软件建模的元模型。

通过对这些建模方法进行分析,发现他们有一些共同特点。都是围绕着参与者、行为、事件、名词这几个元素展开的,通过对这些方法的总结,我们可以尝试建立一个简单的建模方法元模型,为建模方法的改进提供依据。

其实,面向对象中的模型是现实世界在计算机系统中的一种比喻,类似的比喻还有函数式等其他编程范式。对于现实世界的分析,我们可以使用认识论建立一个非常简单的模型。

主体 + 行为 + 客体 = 现象

主体:主体是有认识能力和实践能力的人,或者,是在社会实践中认识世界、改造世界的人。

客体:客体是实践和认识活动所指向的对象,是存在于主体之外的客观事物。

在认识论中,每一个客观现象的出现,都可以使用主体、客体来分析。找到导致这个客观现象的行为背后的主体、客体,就能清晰的描述事件,也更容易看到问题的本质。从认识论的角度出发,建模的过程就是找到确定的客体作为模型的过程。

基于元模型把 4 种建模方法实例化一下:

系统词汇法(OOA) 用例分析法 四色建模法 事件风暴
主体 - 参与者 角色 执行者
行为 - 用例关系 - 命令
客体 名词,模型 名词,模型 时标性对象、实体、描述对象 领域名词、模型
现象 - - 业务事件 事件

从这个图我们可以看出,系统词汇法的建模线索不够清晰,直接获得模型,没有从业务行为中抽取的过程。而事件风暴可以这样理解:

执行者作为业务主体,在系统中发出了一个命令作为业务行为,对模型的状态发生了改变,最终导致了事件的发生。事件风暴是从事件、命令和执行者为线索推导出模型,整个过程更加完整。

为特定领域调整建模过程

在识别模型的过程中,模型这个词太过于宽泛,因此不适用于业务专家找到这些模型。于是有咨询师认为不应过早强调模型,建议先使用 ”领域名词“、”业务概念“ 等和业务相关的概念,甚至可以直接使用 "合约"、“单据” 这类和行业相关的词汇。

因此,在和业务专家的交流时候,我们可以换成和当前业务相关的词汇系统。不仅可以让建模方法发挥更好的作用,还可以为客户定制一套建模方案。

我们以事件风暴为蓝本,针对餐饮行业设计一个特有的建模法,我把它叫做 cake flow。餐饮行业的过程中,围绕大量的单据展开,这些单据的本质是业务凭证。业务凭证意味着业务中各个参与者的责任转移,所以我们可以寻找模型的阶段调整为 “寻找业务凭证”。

我们依然可以使用事件风暴的结构:

  1. 寻找事件。这些事件的线索是业务凭据被改变或者转移。
  2. 寻找命令。找出那些业务参与者发生了什么行为修改业务凭证、生成了新的凭证。
  3. 寻找业务凭证。比如:菜单,是餐厅能提供产品的凭证;桌位,是接待客人的凭证;订单,是一次产品供应的凭证;出餐小票,是后厨生产的凭证;发票,是交税的凭证。

在建模的过程中,先不引入计算机中的技术概念,通过走访餐厅、收集它们的单据、调研优秀餐饮公司的工作流。避免需求叙述过程中制造的新概念、重新命名的业务名词,根据奥卡姆剃刀的原则,减少 “伪需求”的产生。

同样的,架构师需要意识到为特定领域调整建模方法的局限性,只有在特定的范围内才能发挥作用,如果把 “合约”、“业务凭证” 这类词汇系统带入其他行业,会让业务专家更加迷惑。

设计自己的建模方法

根据元模型,选取一个建模视角(从主体、行为、客体和现象选择),可以轻松的设计一个适合自己的建模方法。cake flow 的结构还是先从事件出发,那么我们这次选择另外一个视角出发会有什么好玩的事情发生呢?

比较少的建模方法从主体出发,这次我们选择从主体出发,先找出业务的参与者,通过角色扮演的方式建模,我把这个方法叫做 “play 建模法”。这次的建模方法的流程完全不同于 Event Storming 的结构,而且更为有趣。

  1. 寻找业务参与者。将业务的参与者全部找出来,在工作坊中找到熟悉该角色工作内容的人扮演。如果让工作坊更为有趣,可以用 A4 纸跌一个帽子,写上该角色的名字。
  2. 每个业务参与者需要有两个人来扮演,一个人扮演按照正常操作者,另外一个人扮演异常操作者。
  3. 选择一个场景开始,正常操作者在墙上用便利贴逐步写上该角色工作过程中的行为,这些行为需要产生业务凭证。异常操作者需要寻找任何可以退出、停止的行为触发异常流程。
  4. 扮演做够多的场景,从这些行为中提取业务凭证。如果异常操作者发现流程漏洞,需要梳理合适的分支流程。
  5. 对业务凭证进行细化、展开得到领域模型。
  6. 回顾扮演者的职责转移,业务凭证的转移往往意味着上下文的切换。比如,订单生成后,需要分解为不同后厨的出餐单,凉菜、中餐、甜品在后厨由不同的厨师完成,订单和出餐单发生了业务凭证的转移。

play 建模法有几个特点。有明确的职责转移,容易找出上下文;角色扮演的方式比较真实和有代入感,避免单纯的业务叙述带来误解;异常操作者可以用来提前发现流程中问题,让流程更加完善。

当然,play 建模法只是通过元模型设计出来的一个例子,在实战中需要继续打磨。根据元模型,我们可以根据一些特殊的场景设计出合适的建模方法,更进一步可以为客户设计专属的建模方法。

参考资料

  1. 《用例分析建模》https://www.jianshu.com/p/d74ce3f7848a
  2. 《用例驱动需求建模过程的探讨》 陈枢茜,李 辉
  3. 《运用四色建模法进行领域分析》徐昊
  4. 《本体元建模理论与方法及其应用》何克清

more >>

企业架构建模的挑战和机遇


title: 企业架构建模的挑战和机遇
categories: 技术架构
toc: true

辩证唯物主义告诉我们,社会的发展是由矛盾推动的。软件行业的上一次矛盾是”人们对信息获取便利性的诉求和行业信息化不足的之间的矛盾“。这些矛盾的积累掀起了软件行业的互联网革命的浪潮,迸发巨大的能量。

在国内互联网发展到下半场的阶段,新的矛盾在哪里?新的发展引擎在哪里?经过大量的咨询和行业观察,咨询师们敏锐的注意到新的矛盾:

企业对支持多渠道、多用户、多应用的平台建设诉求和模型建立能力不足之间的矛盾。

这个矛盾是推动行业头部企业进行资源整合、平台化、规模化发展的内在因素。

根据长尾理论,互联网化的行业往往 80% 是通用业务,20% 是定制化的诉求。但是这 20% 的定制需求产生了绝大部分成本,同时降低了通用平台的能力,拖慢研发速度,让企业在竞争中处于不利的地位。头部企业的目标是识别 80% 的通用业务,获得绝对的控制权和主场优势,然后以生态系统的方式满足 20% 业务诉求,实现行业的健康发展。

行业需要实现规模化,就必须先将自己规范化。完全没有规范化的业务,就只能采用高能力的人,让员工发挥主观能动性;半标准化的工作,可以快速培训人员,用成熟的模式赋予人员能力;完全标准化的工作,可以使用机器代替。企业规范自己领域内的能力的过程,就是对流程方法的梳理、分析、抽象和建模。

在这个过程中,模型更加匹配发展潮流的新兴势力不断挑战原有的市场霸主,而在原有市场中的传统企业也在积极谋求数字化转型。这种冲突让竞争变得无比激烈,但无论如何,他们都试图要建立更合适自己的企业架构模型,这既是挑战,又是机遇。

什么是企业架构?

企业架构 (Enterprise Architecture) 最早诞生于 IBM 的信息系统建设理念,始于 1987 年 Zachman 的一篇著名论文 《A Framework for Information Systems Architecture》。企业架构关注业务的结构和行为,尤其是创建和使用业务数据的业务角色和流程。它已被定义为 “用于进行企业分析、设计、规划的体系方法,具有定义明确、长期主义、综合应用的特点,用于制定和执行企业战略。“

企业架构发展了三十多年,有很多专家与组织都企业架构的内涵进行解释和补充,企业框架组织很多,业界比较著名的有:

  • Zachman 架构框架
  • FEAF 联邦总体架构框架
  • TOGAF 开放组织架构框架

TOGAF 是目前最主流的企业架构框架,后来发展为企业架构领域名气最大的论坛组织,在全球有 350 个大型企业会员, IBM 是其中 6 个董事会成员之一。业界使用 TOGAF 的案例较多,主要集中在制造业、银行和航空等行业。

在 TOGAF 中,企业被定义为具有共同使命和目标的组织,并不是商业上的 ”企业“ 含义。它可以是一个完整的商业体,也可能是政府组织、公司部门,TOGAF 用于诠释怎么组织企业中的资源、角色等元素。

尽管企业架构概念相当抽象,但可以明显看出企业架构是和企业管理是相通的,最终的目的是提高企业的一体化程度,更好的实现企业的战略目标。在信息化无法阻挡的当下,企业架构也包含业务和技术的一体化。企业架构,不是为了让信息系统的建立更”省心“,而是要解决企业战略目标落地的具体问题。

因此企业架构和平台化趋势息息相关,呈互相依存的形势。

企业架构面临的新挑战

事物的发展是螺旋上升的。

“平台化” 是从过去到现在,每一轮技术革命都被反复提及的主题。从蒸汽时代到信息时代,从信息化到数字化。量变带来质变,相似的场景再现,但相同的解决方案却有心无力。

传统企业架构目标是一体化企业内的信息系统,实现规模化。互联网产品产业赋能平台是面向更大的受众,实现更大的规模化。信息系统的规模化和制造业的规模化有异曲同工之妙。

福特通过流水线对汽车的生产过程的统一实现规模化,享受大生产、大市场的优势。而丰田模式将这种模式进一步发展,丰田的理念是去除每一个不必要的动作,把最有效率的动作确定为 ”标准动作“。

但是,事物都有它们相反的一面。采用标准化构件和过程的生产,意味着对个性化、定制化市场的取舍。

企业架构建模的新挑战就来自于,相对 80% 通用业务的 20% 定制问题。建模的目的就是要识别、分析、隔离个性化业务,从变化中找出不变,才能顺利实现平台化能力。

随着互联网的发展,”打磨产品“ 的理念让企业把资源转移到产品设计上。这也对平台的系统架构设计提出了更高的要求,以便于更好地服务产品,让产品快速地获得市场。

产品驱动的策略,在第一阶段获得大量收益的同时,却提前透支了架构的弹性能力。重复建设、技术异构等问题为企业带了负担,对产品的持续成长、深入发展带来伤害。这也是为什么大型的互联网公司在初期获客阶段和后期盈利阶段的策略完全不同的原因。

当产品在市场站住脚后,企业迫切的需要业务的一致性、完整性,于是企业架构和领域建模出现在架构师的视野。甚至成为成熟的传统行业龙头往数字化转型的理论依据和方向。

于是在这种情况下,对企业架构提出了新的挑战:

  1. 如何区分业务 ”应用“ 和通用服务,识别通用的业务能力,避免具体的 ”应用“ 污染平台通用能力?
  2. 如何识别新的模型,如何验证新的模型的是否能在未来的业务中具有竞争优势?
  3. 如何处理向新的模型过渡中的各种问题,以及完成系统的架构重构?

如果不能应对这些挑战,企业架构反而会让正在转型中的企业处于被动,面临过长的 ”脱壳期“,遭受来自竞争对手的打击。

建模带来的新机遇

规模化带来的护城河效应是 “互联网下半场” 的新机遇。

2020 年在互联网行业发生了一起悄无声息的变革,8月13日晚间,贝壳找房正式在纽交所挂牌交易。从链家到贝壳网,彻底击败以 58 为中心的中介联盟,地产中介行业重新洗牌。

图片来源于《产业赋能平台:智能时代的商业模式变革》

产业赋能平台对垂直领域洗牌成为经典商业案例,80% 效应再次发挥威力 —— “集中优势兵力在局部形成绝对优势”。58 同城始终坚持信息平台模式,提供混杂的信息,覆盖房屋中介、家政、二手物品等领域。

建模就是要找到处理 80% 的业务的最优方案,贝壳网在建设中提取了链家门店、链家网的专业中介流程,把中介场景规范化为最优的流程,形成规模化。而 58 虽然和各个地产中介结盟,但是在地产中介领域却不占优势。合纵不及连横,58 身配 “六国相印” 也无法在地产中介领域的竞争中占优。

云原生架构的兴起的背后是互联网公司对规模化的天然诉求,水平拓展能力成了 “硬通货”,不过这还只是 “术”。关键还是找到福特的“标准构件”和丰田的 “标准动作”,这才是企业成功的 “道”。

这些内容恰恰就是现代企业架构的研究方向,企业架构把这些建模过程,分解了几个层次。

  1. 业务层。企业的业务层面的建模和架构,研究企业各类业务的运作模式及业务之间的关系结构,它关注的是业务问题、用户和场景。可以用流程图、用例图、业务原型等模型来规范业务表达方式。
  2. 应用层。企业的信息系统应用能力的建模和架构,研究信息系统的构建、设计以及提供的功能(注意区别领域驱动设计(Domain-Driven Design)中的应用层)。在平台化的信息系统中,又需要分为应用和服务,应用为面向业务场景的信息系统,服务为提供无差别通用能力的核心 API。关注对业务的抽象和统一,可以用领域模型、架构图、UML来表达模型。
  3. 数据层。企业的信息层面的建模和架构。关注于数据架构,建立企业级的数据标准体系、数据仓库、数据湖等。处理内、外部数据,进行度量、分析和预测,用于战略决策和包装为特定的数据产品。可以使用数据对象、数据流水线等方式表达模型。
  4. 技术层。企业的技术设施和资产的建模和架构。技术架构和建模包括使用哪种研发模式(敏捷方法等)、哪种技术设施(混合云、Devops平台)等,关注在技术基础设施的架构,为其他层提供能力。

现代企业架构研究的方向已经从企业内的信息系统建设发展到支撑开放性产品的阶段,这一阶段的明显特征是平台的重要性越来越凸显。建模是构建平台必要的理论基础,为了更好的服务于平台构建的过程,建模的必要性越来越突出。

Design Thinking 的兴起也说明了产品经理对业务层的模型有了更深入的认识,用户画像、同理心地图、服务蓝图等这些创新的思维工具可以更好的为业务建模服务;领域驱动设计(Domain-Driven Design)可以将业务层的模型转换为领域模型,从而为应用层的建模服务。数据工程中的一些新兴建模方法,例如星型模型和雪花模型可以为数据层的建模服务;如火如荼的云原生、Deveops等新的技术架构模式也可以服务于技术层的建模和架构。

参考资料

  1. 《A Framework for Information Systems Architecture》 John Zachman
  2. 《产业赋能平台:智能时代的商业模式变革》 刘绍荣
  3. 《从福特模式到丰田模式》К.多瑟
  4. 《从火爆的_中台_看企业架构》陈璐璐

more >>

信息检索指南

互联网和搜索引擎的出现,让现代人对信息的获取变得极其容易。但是,每个人的信息检索能力差异明显,甚至有人提出了 “搜商” 这个词来评价一个人搜索信息的能力。

在一些文章中,我们经常可以看到作者旁征博引,使用了大量的案例、统计数据、文献等。一方面,是这些作者拥有海量的阅读量,另一方面也是这些作者更善于使用互联网去获取数据。

因此,互联网信息检索是一个非常重要的能力。这里,我整理了一些常用的信息检索材料,希望能帮助到写作和需要在网络中获取数据的人们。

搜索引擎

搜索引擎大家都会使用,但是有一些小的技巧可以帮助我们让搜索引擎工作得更准确和高效。

精确搜索。 使用双引号可以实现精确搜索,会完全匹配引号内的关键词,搜索引擎不会进行分词处理。例如,搜索 “操作系统”,如果不使用双引号会被智能拆词,返回操作、系统、操作系统等内容。

站内搜索。 使用 “site:域名 + 关键词” 可以进行对某个网站内进行搜索。这个功能非常实用,比如,你需要搜索维基百科中关于 java 的词条,只需要使用 “site:wikipedia.org java” 语法就可以只从维基百科中获得内容。

文件类型匹配。 使用 “关键词 filetype:文件类型”可以搜索出需要类型的文件。但是需要注意,有些搜索引擎支持的文件类型不多,常用的就是 pdf、doc、ppt 等。

通配符搜索。 使用通配符 “*” 可以让搜索引擎更好的获取需要匹配的内容。比如需要搜索,柏拉图的《理想国》,只记得前面 “理想” 两个字,几乎不能搜索出需要的内容,可以使用通配符减少干扰。

逻辑表达式。 使用逻辑表达式 “AND”、“OR”、“-”,可以表达并、或、非三种逻辑语句,比如你需要搜索电视台,但是不需要中央电视台的内容,可以使用 ”电视台 - 中央电视台“。不过,不同的搜索引擎支持的情况不一样。

书名号。 使用书名号可以过滤书籍、电影和其他类型的作品。使用书名号可以排除大量干扰,比如输入《理想国》可以获得只和出版物相关的内容。

使用搜索引擎的高级方法,不仅可以帮我们快速地找到需要的内容,还可以取得意想不到的效果。比如,使用精确搜索的时候,由于关键词的原因,搜索引擎的广告推广会大大减少。

文献检索

除了做科研的人群高度依赖文献之外,软件开发有时候也需要查询一些文献资料,避免自己盲目试错。在写一些文章、材料时候需要强有力的证据,也可以查询一些论文、期刊等材料佐证自己的观点。

对于国内外的文献检索方式不同,也有一些注意事项需要关注。

文献检索方式

文献检索可以通过不同的方式。如果知道标题,可以通过标题检索。也可以通过文献中的关键词、摘要来进行搜索。如果知道了 DOI 可以直接通过 DOI 检索, DOI 相当于文献的一个身份证号码。

DOI 全称为DigitalObject Unique Identifier,是指数字对象唯一标识符。目前,大部分文献都有了 DOI。DOI 的格式分为前缀和后缀两部分组成,之间用“/”分开。前缀相对于网络域名,由国际数字对象识别号基金会发放,后缀可以由发布者确定。

中文文献还可以使用”中图分类号“来在特定分类下寻找自己想要的资源,以及”文献标志码“来区别来文献的性质。

例如《计算机科学与技术》期刊论文 《基于面向对象思想的软件系统分析与设计》中。中图分类号为 TB 111.521 说明了它是中国图书馆分类”工业技术“的子类下。 文献标志码为 A 说明是理论与应用研究学术论文。doi:10.3969/j.issn.1672-5468.2020.06.0,可以在相关数据库中精确定位该文章。

文献检索的渠道

文献检索的渠道一般有搜索引擎的学术频道,常见的有 Google 学术、百度学术,以及文献库知网、维普数据库。一般来说搜索引擎的检索能力更强,不过也必须跳转到专业数据库访问内容。一般专业的数据都需要收费,免费账户只能看到摘要信息。

下面是几个比较主流和常用的文献检索和下载的渠道。

Google 学术 https://scholar.google.com Google 学术可以检索中英文文献材料,如果 Google学术是直接从开放的电子期刊中检索到的,还可以直接下载。同时,还可以获取相关文章和来源版本,检索能力和准确性都比较高。唯一不足就是无法大部分检索的内容无法直接下载。

知网 https://www.cnki.net/ 知网是国内专业的文献数据库,号称是中国知识基础设施工程。知网的文献收入非常全,不仅仅是期刊论文,还包含了专利和标准。由于是专业的文献数据库,因此费用也比较高。如果想获得免费的下载渠道,可以使用大学校园网、图书馆电子期刊获得部分收费资源。

sci-hub。 如果获得了文献的 DOI,可以通过 sci-hub 自由的下载 90% 左右的外文文献。sci-hub 的作者 Alexandra Elbakyan 通过特定的技术聚合了这个大学、图书馆的渠道资源,实现了自由下载。sci-hub 也提供了 Chrome 插件的下载模式,让下载更加方便。

统计数据

统计数据对于科学的研究的意义不言而喻,也是决策系统的技术,通过引用一些统计数据,可以增加文章和报告的说服力。

统计数据一般来源两个方面。一部分是国家单位或者机构按照年度、季度公布的统计信息,另一方面来源于一些学界对某个行业的研究,这些内容可以在研究报告中获得。

国家统计局 https://data.stats.gov.cn/ 国家统计局会公布国内各种社会和经济统计数据,以及趋势分析。可以做为主要的、可靠的统计数据来源。国家数据还可以根据季度、月度检索数据。

github - awesome-public-datasets。awesome-public-datasets 是一个开源的开放数据聚合仓库,它可以作为一个非常全面的数据获取渠道,包含各个细分领域的数据库资源,自然科学和社会科学的覆盖都很全面。

皮书数据库 https://www.pishu.com.cn/ 皮书数据库可以获得各个行业的研究报告。一般白皮书为政府工作报告,蓝皮书为行业研究机构的研究报告。皮书中,一般不仅仅有统计数据,还有行业趋势和分析。

指数平台。 指数也算一种特殊的统计数据,不过它是经过特定的数据处理方法加工而成,往往和特定的行业有关。比如百度指数提供了搜索关键词的热度排名;国家统计局提供了消费物价指数;股市中有中证指数用于指数基金投资。

还有一些比较专业的行业数据机构,比如前瞻数据库、中宏数据库,这些数据提供商一般收费,会提供额外的决策指南。

more >>

DDD 中的多对多关系建模

多对多关系是软件建模中比较的麻烦的场景,如果梳理不清楚对软件架构伤害很大。在不久前的一个项目中,十足的体验了一次多对多关系带来的痛苦。

我们的项目有是一种多空间模型,也就是用户可以处于不同的空间,在不同的空间中可以访问空间中的资源。一个空间可以拥有多个用户,用户可以出现在多个空间中。看起来和编程老师在数据库课程中的多对多关系没有区别。

image-20210519085032849

对于数据库来说,多对多关系需要一个中间表,于是团队对数据建模“规范”的把中间表起名为 “workspace_user_relation”,得到的数据库 E-R 模型如下。

image-20210519085340362

团队使用了 JPA 的 @ManyToMany 注解,导致 workspace 和 user 两个对象无时不刻在一起了。另外,通过 user 可以操作 workspace,通过 workspace 也可以获得 user。

这种设计,不仅在技术上实现困难,对业务的支持也不足。

  1. 用户加入到空间中具有权限,通过这种方式比较难管理。
  2. 空间管理员并没有对用户的修改权利,只有对用户加入、退出、访问空间资源的权利,这种设计诱导了业务提出不合理的需求。比如空间管理员对用户的禁用,其实只是对用户参与到空间中的行为禁用,而非对用户禁用。
  3. 关系表中的创建时间的含义是用户加入空间的时间,使用中间表语义不明显。

换个思路

在很多编程指南和规范中,都有写明不允许使用多对多关系。在一些框架中,虽然实现了多对多关系,但是往往不推荐使用。

因为我们在开始学习编程的阶段中,接受了数据库的关系理论。数据库关系理论是 1969 被英国计算机科学家 Edgar Frank "Ted" Codd 首次提出。关系数据库理论继承了集合论的的思想,在处理数据上有独特优势,被广泛使用。关系数据库理论可以做到降低冗余,提高一致性的能力。

关系模型被用来存储数据、处理数据非常好用。但是,面向对象作为一种流行的编程模型,它是用来模拟现实业务的。面向对象构想的信息结构是树形,而关系模型是集合。

它们有一个天然的鸿沟,就是这两种结构如何转化的问题,因此出现了大量 ORM(对象关系映射) 软件来试图解决这个问题。数据库中的普通关系(一对一、一对多)可以使用面向对象中的 “组合” 来映射,但是多对多关系却极难被处理,这也是一些框架不建议使用的原因,但往往难以说明其中的道理。

其中的道理是什么呢?因为,关系模型中的多对多“关系”,映射到面向对象在本质是一个“隐藏的模型”。

我们用认识论中的主体-客体思维来看待这个问题,主体-客体可以让认识问题变得更深入。主体是有认识能力和实践能力的人,或者,是在社会实践中认识世界、改造世界的人。客体是实践和认识活动所指向的对象,是存在于主体之外的客观事物。在业务系统中,我们可以把 Controller、Service 这类带有行为能力的对象看做拟人化的主体,而 Entity、Model 看做客体。

回到上面的例子,对于工作空间、用户而言,当把用户加入工作空间的时候。我们发生三步行为:

  1. 使用了用户信息、工作空间的信息,这一步用户、工作空间都是被感知的客体。
  2. 创建了一个关系“工作空间-用户”,这一步“工作空间-用户”是客体。
  3. 把这个关系加入到工作空间,扩充了工作空间的信息,这一步工作空间是客体。

问题的关键是我们往往没有找到一个好的名词来描述“工作空间-用户”这个概念,一旦这个概念被明确下来,我们的模型就清晰了,多对多关系就不存在了。

举例来说,我么可以给“工作空间-用户”找到如下的名字:

  1. 空间成员
  2. 参与者
  3. 空间用户

真真实的例子中,我们使用了空间成员来作为这个隐藏模型的名字,因此空间和用户的关系被拆解为 “空间拥有多个成员” 和 “成员可以引用用户” 两个关系。

更多案例

大部分的多对多关系都可以通过这种方法消除,不过,除了起名字这个难题外,还有另一个问题。

多出来的这个隐藏模型和谁走?我们使用一个例子来说明这个问题。

在很多系统中,我们都需要使用 “标签”,而标签和特定的资源都是多对多关系。明白上面说的逻辑后,我们把标签存在于某个资源中的关系叫做 “标签项”。但是,如果同时有多个资源都需要使用便签,标签项跟谁走呢?

如果所有类型的标签都跟着标签走的话,可以做出一种通用的标签系统。其结果类似于搜索系统了,通过标签系统处理所有的业务。这样设计会使聚合搜索带来便利,但是标签在具体业务中的使用变的困难。

如果标签跟随具体的业务走,那么隐藏的中间模型就是具体的业务中的一个概念,比如文章专题中的标签、文章中的标签。通过这样的处理,可以让系统解耦良好。不过,代价是聚合搜索能力需要额外的技术来实现。

image-20210523171440118

这个例子充分说明了模型的建立需要为业务服务,业务人员往往需要明确其业务重心,并做出一些权衡和取舍才能设计出合适的模型。

参考资料

more >>

专注编码,一次只做一件事

在做了程序员 8 年以后,吃了编程上一个小小的亏。

让 CI(持续构建服务)构建失败一上午,而我却无法立即修好,不得不回滚代码,重新开始。
这看起来没什么,毕竟大部分程序员不都是复制、粘贴然后让程序跑起来么,我本身并不需要介意这件事情。不过问题的关键在于,我意识到了一个错误:在这次修改中,我随手改了太多东西。

这违反了“小步提交” 的原则,也切实感受到了它的意义。

在这次提交中,我为一个高频操作增加了缓存,使用了 @Cacheable 注解,因为需要在其他操作更新缓存,于是同步修改了几个代码的坏味道。其中一次修改让测试失败,并且错误原因比较隐蔽,让我无法在短时间内修复。

这件事情,让我总结了一下专注编码的几个心得。

一次修改一个目标

在敏捷方法论中提倡 “小步提交”,尤其是在重构中。

在重构时,有时候会同时遇到多个代码坏味道,会忍不住想要顺手改掉。不过往往这一个顺手就会坏事儿。“顺手改掉” 这种行为有点类似在开会的时候主题被发散了,就像爱丽丝梦游仙境中的兔子洞,一时半会无法收回。

一次修改达成一个目标,然后运行测试(如果有的话),测试通过了提交代码。

小步提交有几个好处:

  1. 不丢失目标感。人们做事情容易三心二意,如果被另外一件事情打断,往往无法再兜回来。
  2. 获得快速地反馈。这里的反馈包含心理上的满足感,以及程序正确性的反馈。新手程序员,往往一次性做出巨大的改动,这些改动如果出现错误极其难以发现。小步提交,把测试成功的问题隔离到版本库中,如果有未提交的小部分代码出现错误,比较难发现,可以通过对比未提交的少量代码来发现问题。
  3. 每一步的提交记录清晰。小步提交还可以让后续的代码评审更容易,在需要评审时像时光机一样回溯自己的工作。不过做到这点的前提是,认真填写提交日志。
  4. 减少代码冲突。这个毋庸置疑,频繁提交代码,可以让冲突减到最小。

测试和实现二选一

当我们在做重构时候,测试和实现尽可能避免同时被修改。

如何做到这一点?

答案是,测试尽可能的使用字面量和硬编码。这个有点违反我们日常收到的编程认知——尽可能的复用。

让测试代码尽可能的不依赖实现中的 DTO 等对象,有几个好处:

  1. 测试的意图直观。单元测试和集成测试,可以作为活文档来使用,如果在测试用例中使用太多常量、公用方法,会让文档的作用大大降低。
  2. 避免实现修改的时候波及测试。编写测试的目的是为了在修改时候检查错误,比如,在 API 测试中使用了 DTO 对象,由于 IDE 有重构能力,可以同步修改字段值,这样就让 API 测试失真。API 测试的请求中,可以直接使用 Map 等数据结构,让参数一目了然,也可以让实现被重构后快速发现问题。

一次只做一件事

处理编程经验之外,日程工作中,一次只做一件事非常重要。

若同时追两只兔子,你一只也抓不到。——俄罗斯谚语

如果专注做一件事情,很容易进入一种类似禅定的忘我状态,这种状态被有些人叫做 “心流”。进入这种状态,可以获得极大的效率。

这种状态不仅可以获得较高生产力,还可以让内心充盈、满足的愉悦感。忘记自己、忘记时间、忘记烦劳,工作便成为了一种享受,而非负担。

不过想要常常进去这种状态相当不容易,在我的认识里,必须有一些前提条件才能容易进入。

  1. 做真正喜欢的事情。如果你做的事情不是自己喜欢的,非常困难进入这种状态。编程和写文章是我喜欢的工作,在这两件事情上我可以容易的进去状态。当然,编写的代码是需要在合理的逻辑之上的。
  2. 充足的休息。我们的时间可以有很多,但是精力却有限,当没有精力时,很难集中注意力,这个时候还不如直接休息。
  3. 良好的任务管理机制。我们无法专注的一部分原因是,脑子中记录了很多事情,害怕现在不做马上就会忘掉。于是大脑需要额外的开销来随时提醒自己,不要忘记,不要忘记,不要忘记。与其这样,不然使用一个清单记录下来即可。

more >>

架构中的矛盾和权衡

一个周末时间参加了架构大师的 Neal 的架构培训,由于时差的原因在周末的两个早晨。课程围绕着微服务和大型系统,在他的培训中反复提到一个词 “tradeoff” 引起了我的兴趣。

因为我们在讨论架构的过程中,总会陷入一些矛盾,这些经典的矛盾成了关于架构无尽争论的源头。这些矛盾往往是我们分析架构方法的关键所在。

在现实中,企业采用微服务架构的主要动机往往是因为团队过大,以至于无法让数十个、百个人同时在一个项目上工作。这个时候会带来另外问题,如何让这些分布式的系统合适的集成起来成了另外一个问题。

这时候,使用微服务架构是为了解决一个矛盾:大型团队超过管理容量和并行开发之间的矛盾。

但我们在争论时候需要微服务时,需要关注时候当前有这样的矛盾存在。例如对于创业公司而言,在 “两个披萨” 就能吃饱的团队规模,则还没有这样的矛盾,因此使用微服务并不是一件好事。

架构中的矛盾

架构设计中,有各种各样的矛盾。

用户故事的独立交付和可复用平台建设之间的矛盾。 在一些大规模的应用中,需要建设一些基础的业务能力,现在流行叫做 “中台”。但一个基础业务会被多个场景用到的时候,使用中台集成这些能力,会给用户获得一致的体验。不过,这种实践会带来另外一个矛盾,中台必须和接入的应用不断集成。

这会给敏捷团队带了一个困扰,因为敏捷团队会使用用户故事(User Story)作为需求划分的方法。为了保证敏捷交付,用户故事需要遵守 INVEST 原则:

  • Independent 独立的

  • Negotiable 可讨论的

  • Valuable to Purchasers or Users 对客户或用户有价值的

  • Estimable 可估计的

  • Small 小的

  • Testable 可测试的

使用中台后,某些业务能力需要贯穿中台团队和应用团队,就无法由一个团队独立完成,也做不到独立发布和交付。在某种程度上,这种矛盾难以调和,因此就需要对中台的团队,修改其协作模式。比如中台团队使用看板(Kanban)模式,通过响应应用团队的业务诉求来提供相应的能力。这就是架构设计中的一种权衡。

image-20210429135537319

另外的典型矛盾还有数据耦合和一致性的矛盾。

在微服务中,数据库会和服务一起被分离,这种分离会造成一个典型的问题 —— 数据一致性问题。这种问题几乎无处不在,例如用户服务和不同的微服务解耦,那么用户信息会出现在各个服务的快照或者缓存中,以避免对用户服务的频繁访问。

这就会产生一个矛盾,但用户数据变更时,其他相关的服务可能得不到实时更新。一般来说,用户的信息不能同步更新是可以容忍的,以避免用户服务不可用时,其他服务也能相对健康。

如果架构师过分追求实时更新而忽略了系统的可用性这得不偿失。在理论上可以用 CAP 定理说明:

分布式系统只能在一致性、可用性、分区容忍性之间三选二做出保证:

  • Consistency: 一致性
  • Availability: 可用性
  • Partition tolerance: 分区容忍性

微服务系统天然的选择了分区容忍性,于是只能在一致性、可用性之间做出保证。这并不是意味只能选择一致性、可用性,而是说在发生故障时,只能二选一。

在编码实践中另外一个矛盾就是,面向对象中,关注点分离和复用逻辑的之间的矛盾。

image-20210429140442325

大量的遗留系统告诉我们,错误的复用是让系统过分耦合的原因之一,这似乎违反面向对象中尽可能的追求复用的原则。这背后的逻辑是:面对大量的应用场景时候,如果没有足够的关注点分离,会在 “可以复用”的代码中写出更多的 if 语句。

举个例子来说,系统可以允许用户自己注册,也可以让管理员来添加,如果他们复用同一个接口,就必须判断来源,从而进行不同的逻辑处理。反之,通过关注点分离,提供给两个用例不同的接口,把差异处理后再进行复用,可以大大简化逻辑。

权衡的原则

权衡的原则之一就是抓住主要矛盾。

主要矛盾是指在架构决策中的诸多要素中最关键的、占据支配地位的矛盾。抓大放小往往是解决架构矛盾的关键所在,但是认识到什么是主要矛盾比较困难,也是做技术决策的挑战之处。

对于创业公司来说,他们的主要矛盾是快速的迭代和功能演进,可维护性和系统容量并不是其主要考虑内容。因此,微服务一般不是其选择的主要方向,应该使用一些敏捷快速开发框架,通过模型来驱动快速功能完成。

对于业务已经基本定型,快速成长性的公司来说,他们的主要矛盾是业务的快速扩张和大量人员共同开发的矛盾。在做好微服务的准备和条件下,可以通过服务的拆分,让加人变得可能。

对于业务已经稳定的公司来说,他们的主要矛盾是如何让业务持续运行和架构不断腐化的矛盾。因此他们的技术方案会更加保守,使用成熟可用的方案,并不追求新技术和方案。

拆分微服务的权衡

微服务已经是比较主流的技术方案,对于微服务拆分的权衡来说,可以参考一下几个方面。

服务的功能。 首先,任何技术方案和决策,都需要有他的适用范围,微服务也是。对于一些天然可以被拆分的业务来说,可以优先考虑拆分。比如邮件、通知等系统,这些系统本就可以作为独立的能力运行。

代码波动情况。但业务架构不稳定的时候,分布式系统的调整成本远远大于单体系统,这个时候需要考虑单体优先的架构策略。业务架构不稳定的会反应在代码的波动上,如果软件模型频繁调整,不适合拆分微服务。

弹性需求。 不同的业务有不同的访问频次,对于弹性需求不同步的业务来说,如果将热点业务拆分出去可以提高整体的性能和可维护性。

容错。 如果对系统有特别的容错需求,也可以考虑拆分微服务,并使用降级和容错的特性来保障服务之间的相对隔离。

数据安全。 另外一些特殊的需求是来自于安全方面,用户信息、信用卡信息、账户信息和其他业务对于数据安全敏感性不同,也可以考虑使用微服务来保护数据安全。数据安全的保护不仅是系统运营过程中的安全,也有开发过程中的安全。一些专业的公司,会根据数据的敏感性不同,将办公区域划分为不同的区域。

架构验证方法

架构权衡是一个具有挑战的工作,因此需要一些架构验证的方法。

POC测试( Proof of Concept)。 POC 越来越被重视,其含义是:当我们在设计一个大的系统前,先设计一个原型系统,在原型系统之上进行验证架构是否合理。

原型方法是软件开发中比较重要的方法,但我们做一些重要的软件设计时,先设计原型,通过对原型的检验一步一步毕竟真实的系统。甚至会有公司进行多个团队开发原型,然后是其具有竞争关系,再选择一个团队来开发商用系统。

原型设计分为多个层次:

  1. 商业原型。商业原型一般就是 PTT 演示文档或其他模型,可以做一些用户测试,验证系统在商业上是否可行。
  2. 业务原型。业务原型一般是指用户的交互和界面,通过借助 Axure 的界面原型设计软件设计出软件应该有的样子,用于软件工程师和架构师进一步设计软件架构和领域模型。
  3. 技术原型。通过实现一个 demo 来验证技术的可行性,可以通过这个原型来分析技术的各项指标,比如潜在性能问题、安全问题等。

原型法可以用低成本的方式验证软件设计过程中的问题,提高软件开发的成功率。

ATAM 架构权衡分析方法。架构和方案被设计出来之后,需要对架构进行评审。通过架构权衡分析方法 (Architecture Tradeoff Analysis Method,ATAM) 总结了架构评审中的关键几个条件::

  1. 架构的可行性。架构是否可行,能否靠现有的团队完成实施工作。
  2. 架构的业务支撑性。架构是否能支持现有的业务,尤其是未来一段时间的业务。能在一定程度上响应业务变化的架构才是合理和实用的架构。
  3. 架构的成本。大的公司的架构不适合小的公司,往往是其成本决定的。比如 Hybrid App 作为低成本的开发方案可以让创业公司的产品快速成型,但是对于大的公司来说需要更好的用户体验,因此在有足够投入的情况下会开发 Native APP。

架构权衡分析方法作为一种专业的架构权衡的方法还提供了一整工具可以选用。

more >>

面向对象中的主体客体思维

01 为什么面向对象难以理解?

面向对象是应用软件设计比较好的方式,可以指导用计算机解决现实中的业务问题,因此是软件开发中的一种主流方式。

不过,用好面向对象则比较困难,即使有数年经验的软件工程师也难说能很好驾驭。大多数人往往是照猫画虎,没有理解软件开发的 “骨相”。

背后的原因为面向对象是对现实业务的抽象,需要使用者对现实有深入的理解,于是面向对象带有一定的哲学认识论的色彩。

实际上,现代英语、现代法律、面向对象编程都和近代哲学有关,而近代哲学被称为“主体性哲学”,“主体” 概念和主客体关系是非常重要的内容。

现代英语、现代法律、面向对象编程看似三个无关的领域,背后的逻辑却惊人的一致。

在现代英语(古代英语除外)的主要语法是:主语 + 谓语 + 宾语 + 修饰语。想象一个你在一家餐厅吃饭,你点了一份三明治,用一般现在时就是:I order a large sandwich。在这套逻辑关系中,主体就是你自己,客体是三明治,行为是点餐,其他的内容都是修饰成分。

假定你和餐厅出现了纠纷,餐厅忘记给你上了餐,却说已经上了。你发起了诉讼,让餐厅赔给你三明治。在诉讼的逻辑关系中,这是一起民事纠纷,你是民事主体,民事客体就是三明治,诉讼内容是赔偿行为。

而如果软件工程师需要编写一个软件用来处理订单,实现一个收银机功能。可能他会写一个 OrderService 来实现。伪代码如:

class OrderService {
    public Order createOrder(User user, Product product) {
        Order order = new Order();
        ……
        return order;
    }
}

这段代码可能会被认为不符合面向对象,因为某些教科书中,Order 是需要自己来完成业务的。实际上,在这段代码中,当我们认识到主客体关系时,一切豁然开朗。OrderService 是我们的业务主体,Order、User、Product 不过都是业务客体。和民事行为一样,业务逻辑也应该发生在业务主体中,这样就容易理解了。

既然主体、客体思维可以让面向对象更容易理解,我们来严肃的说下这些概念。哲学可能会有一些无聊,不过值得去了解它们。

主体、客体在哲学中的定义是什么呢?按照主流的哲学教科书,可以看到类似下面的描述:

  • 主体是有认识能力和实践能力的人,或者,是在社会实践中认识世界、改造世界的人。
  • 客体是实践和认识活动所指向的对象,是存在于主体之外的客观事物。

这里需要修正下,随着科学技术的发展,主体可以不只是普通的 “人” 了,可以是一个具有集体意识的团体、网络虚拟世界的一个形象,比如 xxx 公司、初音未来也可以是主体。与之相对的普通人是 “自然人”。

主体、客体思维从笛卡尔时期就开始出现,在康德时期又进一步发展。在主体、客体英文分别是 Subject、Object,它们都是实体,主体是具有行为、感知和思想的一类。

需要注意的是,主体、客体是相对的、动态的。比如用户,在做出一些操作的时候它是主体,当被管理员或者系统操作的时候,它又是客体,这点尤为重要。

关于更详细的主体、客体的知识,可以参考逻辑学、哲学书籍,这里不再展开。当我们理解到主体和客体的思维后,就可以用它了分析和指导我们的实践,也就是面向对象的编程了。

02 指导业务分析

用主体、客体思维可以分析我们需要设计的软件需求,软件需求一般由产品经理、BA参与。

如果按照主体、客体思维的来看,不同角色的用户就是主体,我们设计的软件就是客体,无论这个软件运行在服务器还是客户端。

一直以来,产品经理的交付物没有一个明确的标准,因此业务输出物的质量也参差不齐。用主体、客体思维可以检验产品经理的交付物,也可以改进其设计。

例如,在有一个项目中,产品经理把代理商、站点后台系统设计到一起,并只提供了原型图,告诉我们这只是权限的不同,又补充了一份权限说明。在这个例子中,业务上陷入了一个逻辑问题,导致在后期的系统开发时陷入比较大的麻烦。拿具体的场景来说,代理商、站点后台系统都有一个相似的页面 —— 产品列表,因为它们非常相似,产品经理当做一个用例来处理。其真实的业务如下:

  1. 代理商系统的产品列表,显示当前登录的代理商管理员所属的代理商下的产品,在这个场景下,其主体是代理商管理员,客体是所属的代理商下的产品。
  2. 站点后台系统的产品列表,显示的是整个站点的产品列表。在这个场景下,其主体是系统管理员,客体是所有的产品。

这两种管理的逻辑、权限、审计、搜索方式都不一样,只是页面比较类似罢了,列表页都有删除操作,但是这里的两种删除逻辑绝不一样。

主体、客体思维对业务分析的指导地方还很多,一些常见的经验如下:

  1. 主体 + 行为 + 客体组成一个用例(use case),即使同样的行为主体、客体变了,也是不同的用例。
  2. 流程图的绘制注意粒度,以主体 + 行为 + 客体的用例为粒度绘制比较合适。
  3. 权限是主体的行为限制,和用例一一对应。
  4. 不同主体的区别可以用角色来描述,同一主体下的权限区分使用用户组。比如代理商管理员需要被授予不同的权限,这里的区分不是指代理商管理员和后台管理员这样的角色,实际是用户组的不同。
  5. 如果出具原型图,以每个角色来独立设计,复用的问题可以在实现时考虑。

03 指导业务建模

用主体、客体思维可以帮助我们建立领域模型。

业务建模是我们开发人员比较熟悉的部分了,无论是使用 E-R 模型直接对数据库建模,还是使用 DDD 领域建模,设计与存储无关的模型。建模都是非常重要的概念,模型驱动开发是面向对象重要的思维。

那么,模型是什么?

广义的模型可以包含的内容可以很多,用一个简单的东西去理解复杂的事物,都可以说是模型,比如地图、教具等实体模型,金字塔、四象限等思维模型。由此我们可以给模型一个广义的定义。

通常,模型是对对象、人或系统的信息表示。它通过较为简单的信息结构来代表我们需要理解的复杂事物或系统。

对于编程来说,模型是业务概念在程序中的一种表达方式,常见的有订单、用户、商品等。

在前文中订单就是一个典型的模型,我们会发现我们通常是对客体在建模,不过有时候也会建立主体模型。

如果对业务主体建模,得到的就是各种 Service,会侧重它们的行为,关心它们的算法和逻辑。

如果对业务客体建模,得到的就是各种实体、以及实体的关系,会侧重他们属性,存储方式。

这样就让建模的目标更清晰了,我们可以通过 UML 类图表达业务主体,表达他们的继承、组合关系等;可以通过非 UML E-R 图表达业务客体,表达他们的关联关系,也可以从容的使用 DDD 中的聚合、实体、值对象的概念,优化模型。

用主体、客体思维建模的一些常见的经验如下:

  1. 对主体建用例,只对客体建模,会让建模更简单。
  2. 识别名词的二义性,避免混淆模型,比如商品和订单中的商品具有二义性,为不同的模型。
  3. 避免对业务客体使用继承,即使是一个属性的不同,意味着这是不同的模型。
  4. 注意集合类名词和单个的名词是不同的客体,有点像公司法人和法人代表之间的关系。

04 指导架构设计

MVC、三层架构、DDD 四层架构怎么选择和理解?用主体、客体思维依然可以帮助理解其中内在逻辑。

我们说过,主体、客体是相对的,现在我们已经打开了系统的黑盒,在这个黑盒中主体、客体关系是怎么样的呢?

如果我们使用 MVC 架构:

  1. C - Controller,负责处理系统输入,Controller 使用 Model 完成业务,并调用 View 能力输出数据到系统外部。我们可以视为主体,他的行为就是用户的每个用例 。
  2. M - Model,就是我们的模型,用来表达数据结构和关系。和上面说的客体模型一致。
  3. V - View,负责处理业务输出,无论是 Rest API 还是渲染模板。在业务输出的场景中,View 是主体,Model 是客体。
  4. 其他未归纳的概念。其实在 MVC 中有一些概念未为被归纳,比如持久化的服务也应该是主体,负责将 Model 写入持久化环境。

三层架构、DDD 四层架构逻辑上一致,只是多了一个 Application 的概念,DDD 四层架构在每一个层都有主体、客体:

  1. UI 层。处理用户输入,负责把用户的请求识别为具体的对象。主体一般由框架实现了,可以是 RequestHandler 之类的,客体是用户请求的数据,并加工成可以理解的对象。

  2. Application 层。这层是为了每个具体的用户设计的,比如管理员、代理商、普通用户,这层的主体是 ApplicationService,客体是用例对象,比如添加用户的 DTO,并转换为领域模型。

  3. Domain 层。这层是为了复用 Application 层的逻辑,保持业务的一致性和避免代码的重复。主体是 DomainService,客体是领域模型。

  4. Infrastructure 层。这层是为了提供技术基础设施,这层的主体种类繁多,比如持久化数据的 Repository、发送邮件的 EmailSender 之类的。这层可以提供对 Domain 层模型的透明持久化,也可能会有自己的对象来处理特定的业务,比如发送邮件的 Message 对象可能只在这一层被定义。

用主体、客体思维指导架构设计的一些常见的经验如下:

  1. 对主体拟人化命名,比如 Controler、Service、Viewer、Handler、Mapper 等。
  2. 对客体拟物化命名,比如 Entity、Model、xxxobject 等。
  3. 类似的概念可能既是主体、也是客体,但逻辑上不同。比如 UserService 和 UserEntity 都是和用户相关,但逻辑不同。
  4. 如果一个对象操作自己的属性,那么主体是其本身,客体是其属性。

05 指导代码实践

具有面向对象能力的高级计算机语言已经和自然语言有一定的相似性了(和自然语言的鸿沟是做为形式语言,必然无法和自然语言进行推理转换)。

如果用主体、客体思维来指导代码实践,指导方法、变量的命名,可以让我们写出规范、诗意的代码。

如果我们在方法命名上参考下面的规则:

  • 参考英语用格式: 主语 + 谓语 + 宾语 + 补语来称述某件事情,计算机也可以用格式:主体 + 方法名 + 参数构成一次操作。方法名和参数无需再出现主体的名词。比如 OrderService 的 create 方法,无需写为 createOrder()。
  • 坚持原则:给主体赋予行为和能力,给客体赋予属性和数据。
  • 把主体的行为作为契约,以此来抽象接口,但是如无必要,勿加接口。
  • 主体、客体都需要遵守单一职责原则,SOLID 其他实践只用于主体,让客体保持简单。

我曾应用这些原则编写了一个简单的 Tomcat 的配置下发中间件,用于动态更新 Tomcat 的配置,简单来说如下:

  • ConfigMananger 作为主体,用于管理信道,实现配置的应用、拉取、上报。
  • ConfigPosterInterface 作为契约,定义了 pull、post 两个方法
  • ConfigRedisPoster、ConfigHttpPoster 两个实现,作为发送的信道
  • Config 作为客体用于承载配置数据。

结合拟人、拟物的命名风格,写出的代码比较容易理解,毕竟这是人们对现实的一种自然的思考方式。

more >>

敏捷团队的代码评审和分支策略

1. 代码评审的场景

我们可以在一定程度上使用代码静态分析保证代码质量,但代码静态分析无法解决所有问题,也不能完全依赖他。因此在一些场景中我们需要团队一起来做代码评审。

在日常的团队开发工作中,代码评审有几个场景。

  1. 每日代码评审。我们一般在会在每天下午下班前拿出 1 个小时对当天的代码做 review,如果一个团队 8 个人的话,相当于浪费了一个人天。有一些项目经理特别不理解,为什么需要花时间做这个事情。实际上,每日代码评审的意义非常重要。每日做代码评审让需要评审的代码量被分摊了,随着每天评审可以让团队编码风格日趋统一,需要指出的错误也越来越少。而且每日代码评审也是团队做技术交流的一个契机,熟悉团队其他人在做什么。
  2. 发布前代码评审。发布前代码评审的目的是为了避免明显不合适的代码进入产品环境的分支。有时候一些不明显的错误测试人员往往没有覆盖住,而通过发布前代码评审能快速识别。如果团队的版本管理策略是在 release 分支上发布,可以通过和上一次的版本分支进行对比,也就是看和当前生产环境的代码差异。发布前代码评审工作量比较大,一些创业团队不会做,这是可以理解的,对于成熟的公司来说,如果有大量的用户的产品则需要认真对待。
  3. Hotfix 代码评审。一个新版本发布后,往往会有一些问题需要及时修复,我们叫这种临时修复叫做 Hotfix。Hotfix 往往不会改动太多地方,测试人员也无力全部回归测试,所以 Hotfix 代码一般需要通过Pull Request 来完成让有经验的技术 leader 来把关合入的代码是否会有明显问题。

这是三种主要的代码评审的场景,一般来说发生在团队内部。还有其他的一些场景需要代码评审,比如安全团队的评审等,这些特殊情况不再赘述。

另外,每日代码评审和上线前代码评审往往让团队一起参加,可以使用一个大屏配合 IDE 在本地完成,Hotfix 代码评审可以由团队或者技术 leader 通过 Pull Request 来完成。

2. 代码评审的工具

做代码评审可以用一些工具,可以提高效率。一般来说有如下几类:

  1. 代码版本管理工具。一般有 Git、SVN、Mercurial,不过目前已经是主流使用 Git 了。Git 的分布式特性出色,工具链也完善,没有别的限制因素,可以默认使用 Git。
  2. 代码托管平台。Gitlab、Github以及国内的 Gitee 都是不错的代码托管平台,如果在企业内部使用可以选择自己搭建私有的 Gitlab,不过 Gitlab 比较复杂,可以使用 Gogos 通过 Docker 容器快速启动一套 Git 代码托管平台。
  3. 代码对比工具。代码托管平台一般提供了内置的代码对比工具,不过访问比较慢。还有专业的代码对比工具软件,比如 Beyond Compare。以及集成到 IDE Git 客户端中的代码对比工具,比如 Intellij IDEA 就完全够用,而且在多人参与的代码评审活动中效率也比较高。
  4. 专用的代码评审工具。这类工具就是专门为代码评审设计的,比如 Gerrit。Gerrit 可以在网页中做类似于 Gitlab、Github 的工作,不过还有一些额外的工作流管理的能力。

一般来说,对于非开源项目 Gitlab + Intellij IDEA 是一套比较好的工具链,配置和使用简单维护成本也比较低。

3. 代码评审的注意事项

一般我们讨论得比较多的是每日代码评审,因为需要全员参与,时间又比较有限,需要对主持人有较强的组织能力。为了高效的进行代码评审,团队需要一些契约。

  1. 小步提交。团队成员需要保持好的代码提交习惯,小步提交代码,每完成一次小阶段的开发、重构工作都需要提交一次代码,避免丢失更改的同时也为了更好地评审代码打下基础。每一次提交都需要使用有意义、相同语言的文本描述。也需要遵守一些规则,比如使用看板卡片管理任务的团队会按照 “#[卡号] [描述]” 的模式提交代码。
  2. 描述具体。在代码评审时候避免使用一些诸如:”这个地方的实现不优雅“ 这类似是而非的用语,应该使用更为具体的方式。比如 "使用了太多个 if 语句,是否可以使用策略模式等一些设计模式改进设计"。同样的的也需要避免带有个人的习惯的意见,例如:"应该使用 switch 语句而非多个 if 语句组成等"。
  3. 及时修改。当天的代码评审中提出的问题,需要代码作者自行记录,并尽可能在当天就完成修复和处理。一些零碎的小事放到以后做都是不现实的。
  4. 专注参与。如果是在线下参与,最好使用大屏或专门的会议室,避免使用工具或一边做其他事情。需要全程专注参与。如果是远程工作,对于视频会议这种情况,需要打开摄像头。主持人可以使用一些主持技巧,比如不定时对部分参与人点名,唤起大家注意力。
  5. 聚焦当下。 最糟糕的代码评审就是突然被岔开话题,然后进行技术方案、业务方案的讨论,从而浪费大家的时间。这种岔开是一个无底洞,就像《爱丽丝梦游仙境》中 rabbit hole。代码评审应该专注于当下的代码问题,避免陷入技术和业务细节,如果遇到这种问题可以在专门的技术会议中讨论。
  6. 时间控制。 代码评审中最难的就是时间控制,一般一个正常的开发团队每天每人的工作量至少需要 10分钟才能描述清楚。需要给每个人设定一个时间窗口,避免超时。一般来说我会根据人数设定时间,如果时间超过了,就立马停止,第二天继续,这样会越来越快。
  7. 分组评审。如果团队规模过大,无论如何也无法在 1 个小时内完成,就需要选择分组。为了让所有的开发者都能了解全局,以及保持知识传递,可以让分组每周、每个迭代重新排列。
  8. 知识整理。 重复出现的问题不应该被重复提出,对于一些常见的问题,团队可以整理一份评审清单。评审清单可以用于新人参与到项目中来更快的适应团队风格,也可以降低发现问题的成本和偶然性,同时开发者在提交代码时候可以参考清单自己先评审一遍。

发布前代码评审可以不用全员参与,可以由技术 leader 挑选几个关键人员参加即可,如果遇到无法理解的部分,可以通过查看提交人来进一步澄清。

Hotfix 代码评审比较简单,一般在代码托管平台中对 Pull Request 做出设定,必须多少人的的通过才允许合并,通过 Pull Request 也可以追溯 Hotfix 的变更记录。

4. Java 代码评审清单

这里整理了一份基本的评审清单用于 Java 开发者使用,清单中的条目已经考虑到了 checkstyle 和 FindBugs、Arch Unit 能检测出来的问题,没有将其加入其中。下面这些内容一般是无法被代码静态分析的内容,避免清单冗长。

  1. 有没有 IDEA 的黄色警告。一般往往意味着代码可以被优化或者潜在的问题。
  2. 数据输入是否都做了验证,比如类型、长度、格式、范围。
  3. 提供的 API 是否做了鉴权,尤其是数据的鉴权。
  4. 是否硬编码,需要使用常量和配置文件。
  5. 注释和方法命名是否和代码语义一致和容易理解。
  6. 是否使用足够便宜的解决方案,比如库函数提供了的逻辑不需要自己再写一遍。
  7. 使用适当的数据结构,比如合理选择 HashMap、ArrayList 等。
  8. 是否做了合理的异常处理。
  9. API 设计是否符合规范和语义。
  10. 是否有足够合理的测试。

5. Git 工作流

在团队协作中,代码评审的方式和代码版本管理有一定关系,所以这里说一说代码版本管理。代码版本管理的工作流和分支策略是一个讨论比较多的话题。有很多流派,比如 Git Flow、GitHub Flow、GitLab Flow。

  • Git Flow: 项目存在两个长期分支,一个主干分支,一个开发分支,开发者一起工作在开发分支中,发布时合并到主干分支中。还有一些短期存在的分支,比如特性分支、预发分支等。
  • GitHub Flow: 项目存在一个主干分支作为长期分支,每个开发者在自己的开发分支中工作,然后通过 Pull Request 合并到主干分支。
  • GitLab Flow: 项目遵守上游优先原则,只存在一个主分支作为长期分支。开发者在主干分支开发,当测试稳定后,使用发布分支发布。后续的 Hotfix 都需要合并到发布分支和主分支。

这三种分支策略我在不同的项目中都遇到过,从实用和经济的情况来说,比较推荐 GitLab Flow,当然应该视团队情况而定。比如团队人数、是否需要和其他团队联调(这个是比较重要的因素,实用分支开发会比较麻烦)。

对于大多数使用敏捷工作方式的团队,比较好的分支策略可以用一句话概括:主干开发,分支发布。这种方式尤其适合一个迭代一个版本这种开发节奏,如果使用持续发布,没有固定的版本周期这种方式未必合适。

image-20210328203951582

这种分支策略有几个规则需要团队遵守。

  1. 团队使用 Git rebase 命令拉取代码,不允许使用 merge 操作拉取代码,避免提交日志混乱。
  2. 团队在 master 开发,每个迭代结束前 2 天创建 release 分支,每一次 release 使用一个新的分支,使用语义化版本号来命名,但不使用修订版本号,比如 v2.1 分支。创建 release 分支时可以同步创建 tag,以便早期发布的 release 分支可以被删除。
  3. 使用 release 分支可以部署到到预发环境,master 分支只能部署到开发和测试环境。
  4. 使用 release 分支发布到产品环境,发布前和上一次的 release 分支做发布前代码评审。
  5. 如果预发环境出现问题需要修复,使用 release 分支为基线创建 Hotfix 分支,并提交 Pull Request,团队批准后可以部署到预发环境,然后部署到产品环境。
  6. 部署到产品环境后需要将 release 分支的变更同步到 master,避免下次上线丢失更新。

6. Git Hooks 和分支保护

代码静态分析和代码评审需要结合代码的分支控制才能起到好的效果,如果 checkstyle 等检查没有通过,代码不应被推送到服务器。

Git Hooks 是一些 shell 脚本,用于 git 提交某个生命周期中执行,可以在 .git/hooks/ 中被找到。用于控制 git 工作的流程,分为客户端钩子和服务器钩子。

客户端钩子:

  • pre-commit

  • prepare-commit-msg

  • commit-msg

  • post-commit

服务器端钩子:

  • pre-receive

  • post-receive

  • update

一般我们会通过配置pre-commit 到项目中,约束团队成员提交代码时候进行一些检查,例如:

  • 运行单元测试
  • 运行代码检查,例如 checkstyle
  • 提交的 commit 信息检查

如果有 git 服务器配置权限,也可以通过配置 pre-receive 在服务器端运行检查。下面是 Java Gradle 的一个 pre-commit 脚本示例:

#!/bin/sh
# From gist at https://gist.github.com/chadmaughan/5889802
set -x

# run the tests with the gradle wrapper
./gradlew clean build

# store the last exit code in a variable
RESULT=$?

# return the './gradlew build' exit code
exit $RESULTs

在项目的根目录中添加 pre-commit 文件,通过配置 gradle 脚本在项目初始化时,会自动安装该 hook

task installGitHooks(type: Copy) {
    from new File(rootProject.rootDir, 'pre-commit')
    into {
        new File(rootProject.rootDir, '.git/hooks')
    }
    fileMode 0755
}

build.dependsOn installGitHooks

另外,在没有 Pull Request 和代码评审的情况下,也不应该把代码直接推送到 Release 分支。Git 代码托管平台都会有分支保护功能,基于前面的分支策略,可以设定几个简单的规则。

  1. 受保护的分支均不可删除、强制 push,避免代码库受损。
  2. release 分支不接受直接 push,必须使用 Pull Request,并需要团队 2 人以上的批准才能合并。
  3. 合并到 release 分支的临时分支如果条件允许,可以自动删除。

image-20210328205915043

more >>

【翻译】单体优先的架构策略

本来打算写一篇类似的文章,不过看到有同事有转发马丁·福勒的一篇博客,结合自己的项目经历,感同身受,就直接把这篇文章翻译过来了。

当我听说有团队在使用微服务架构时候,我注意到了一些规律:

  1. 几乎所有成功应用微服务的系统都来自于一个过大单体项目拆分而来。
  2. 几乎所有我听到过一开始就选择使用微服务架构的系统,并从 0 构建,最终的结果都有一系列严重的麻烦。

这些规律在我同事中产生了长期的讨论:你不应该你在一个新的项目一开始就采用微服务架构,即使你确认你的应用在未来因为业务演进变得无比巨大。

img

微服务是一种有用的架构,但是即使这种架构的拥趸也不得不承认,使用这种架构需要付出额外的成本,这意味者只有更复杂的系统值得使用这种架构。这种代价本质是管理这些服务所带来的基本开销。所以这就是单体优先架构策略的依据,架构师应该在构建新的系统时候先使用单体做整体设计,即使你认为后期可能使用微服务更有价值。

第一个原因是经典的 Yagni 原则 (译者注:"You Aren't Gonna Need It" 的缩写,意在避免过度设计)。当你开始一个新的应用程序时,您如何确定这个应用的特性是用户有价值的?一个成功的软件可能背后的设计很糟糕,很难拓展,但是总比一个设计卓越但是没有价值的系统好。正如我们现在所认识到的,通常判断一个软件和想法是否有用的最好方法是构建一个简单的版本,看看它的跑起来效果如何。在第一个阶段,您需要优先考虑速度(以及反馈的周期时间),因此微服务的成本是一个累赘,应该被避免。

从微服务开始的第二个问题是,它们只有在服务之间建立良好、稳定的边界时才能正常工作——这本质上是厘清正确的边界上下文的任务。因为任何服务之间的功能重构都比在一个庞然大物的单体中要困难得多。不幸的是,即使是在熟悉的领域工作的经验丰富的架构师也很难在一开始就确定边界。通过先构建一个单体应用,你可以在微服务设计刷上一层糖浆之前(译者注:比喻微服务的一些额外设施),找出正确的边界是什么。它还为你提供了缓冲时间来开发细粒度服务所需的基础设施。

据我所知,践行单一优先策略有不同的方式。比如,仔细的从逻辑上设计为一个类似单体系统的整体,注意内部的模块化设计,包括 API 边界和数据的存储方式。做好这一点,再来开发微服务的系统。然而,如果我听过很多这样的成功案例,我会觉得这种方法更合适,实际很难做到。

一种更常见的方法是从一个庞然大物开始,然后逐渐从边缘剥离微服务。这种方法可能会在微服务体系结构的核心留下一块巨大的庞然大物,但是大多数新开发都是在微服务中进行的,只是这一块庞然大物是相对不变的。

另一种常见的方法是先开发一个单体系统,再用一套新的微服务系统完全替换单体结构。很少会有人认同这种办法,然而建造作为献祭架构的单体系统是有好处的(译者注:一种设计方法,先做简单的原型系统,再用新的系统代替,其实就是重写)。不要害怕建造一个你会丢弃的庞然大物,特别是如果一个庞然大物可以让你很快地进入市场。

还有,我遇到的其他方法是从几个粗粒度的服务开始,这些服务要比你预期的要大。使用这些粗粒度的服务来习惯处理多个服务,同时享受这样一个事实:粗粒度减少了你必须不得已进行服务间重构的数量。然后,随着边界稳定下来,就会分解为更细粒度的服务。

虽然我接触的大多数人倾向于“单体优先”的策略,但这不是绝对的。相反的观点认为,从微服务开始构建新的系统可以让你适应在微服务环境中开发的节奏。要以足够模块化的方式构建单体系统,以便将其轻松分解为微服务,需要花费很多甚至太多的编程规则。从微服务开始,你可以让每个人从一开始就习惯在独立的小团队中进行开发,并且在需要的时候,通过服务边界将团队分开可以更容易地扩大开发工作。对于那些更有可能尽早获得足够稳定边界的系统来说,这尤其可行。尽管证据不多,但我觉得你不应该从微服务开始,除非你有在团队中构建微服务系统的大量经验。

我觉得我还没有足够的例子来明确如何决定是否使用“单体优先”策略。毕竟,这是微服务的早期阶段(译者注:这篇文章发布在2015),可供学习的业界案例相对较少。因此,任何人在这些问题上的建议都需要保持开放性,无论观点的提出者如何信誓旦旦。

文章来源

more >>

高效工作的策略 2.0

现代人对自己的压榨达到了极致,对工作效率的诉求越来越高。

不同的理念、方法、概念层出不穷,番茄钟、清单、早起、费曼学习法、心流等等。我读过各种时间管理的书,这些方法都试过。

效果是不错,但是太累,精力消耗严重,难以持续发展。

慢慢我意识到,人一天能做的事情极其有限,但是却极其贪婪用各个方法把自己的精力快速耗尽。其实真正能提高效率的方法是管理好自己的精力,让有限的精力不浪费就很不错了。

一个明显的感觉就是,当一个人精力不足时候,无论用啥方法,都很难集中注意力。

慢慢的我的关注点变为如何休息、注意力管理和精力管理。

1. 如何休息

休息是提高效率的基础,就像存储子弹,如果不好好休息,就无法好好工作。

睡觉。这个是休息中最重要的一件事,我一直睡眠不好,于是寻找了一些关于改善睡眠的方法。

总结下来的经验是,睡眠方法需要学习,且因人而异。一定不能相信所谓成功人士每天 4 小时的睡眠,大部分人一般需要 7 个小时左右,但具体时间和个人的状态有关。早睡和健康状态好的时候,我只需要 6 个小时睡眠时间加一点午睡即可,但是一旦生病或者工作压力大就需要 8 个小时左右的睡眠时间。

另外 R90 睡眠法经过实践检验,通过 90 分钟的睡眠周期,确实有用,状态好的时候可以不用闹钟自然醒来。

《睡眠革命》这本书介绍了一种新的睡眠方法。每个人的睡眠呈周期性的规律,一个周期内分为浅睡、深睡、眼动睡等阶段,一个完整的周期在 90 分钟左右,完成一个周期后趋向于相对清醒(这也是做梦的原理)。如果你起床时并不在周期结束之时,早晨起床时会感觉整个人都是蒙的,因为你的睡眠周期被打断。

人一般需要 4 - 5 个周期。如果你晚上 11:30 睡觉,4 个周期就是第二天 5:30 需要醒来,5 个周期就是 7:00,几天时间适应后可以赶在闹钟前自动醒来。一般工作日需要多一点睡眠,使用 5 个周期,周末用 4 个周期即可。

放松。 另外一种休息的方式就是坐在沙发上啥也不做,啥也不想。有人会说冥想是一种恢复精力的方法,其实有点不对,冥想的注意点很多,不得要领的人搞一套下来反而更累。

简单的方式其实就是没有方法的放松即可。找一个舒服的姿势坐在沙发上,盘腿或者怎么样都行。除了工作的事情,可以任其心猿意马。杂念宜疏不宜堵,一些冥想和灵修的书会用各种方式控制杂念,其实有点走偏。

唯一需要注意的就是呼吸,细长、稳定的呼吸方式确实可以提高休息效率,《庄子》的听息法是一种最简单的方法,就是听自己的呼吸声,然后降低呼吸的声音。

有人总结了松、通、空三个字可以概括放松休息的方法。

喝水。人需要喝水,浓茶、可乐都不太能代替水。多喝水的好处是可以起来上厕所,避免久坐。

早上经过八个小时的睡眠,身体已经缺水了,起床第一时间就是喝水。

运动。 我不擅长运动,也没怎么去过健身房。运动可以激活人身体状态,不过也需要适度。加完班、缺乏睡眠的状态下不适合运动,会发生猝死等危险,而且人的精力会进一步透支。

众所周知,运动分为有氧和无氧。有一段时间我按照朋友推荐的方法,每天做一些超过我日常能力的运动,那段时间明显发现身体无法应付锻炼和日常工作。

工作日可以做一些无氧运动,换换脑子,休息日出去徒步进行有氧运动,比较合适。

2. 如何获得注意力

效率的关键是获得注意力,教员曾说 “对于人,伤其十指不如断其一指,对于敌,击溃其十个师不如歼灭其一个师。” 对于工作的事情,宁可做完一件事,不要 10 件事做一点。

目标感。 把时间、精力集中在重要的事情上,不然做再多的事情都是浪费。非常羡慕有一些人的目标感极强,能十年如一日的坚持,回报也是极大的。

目标感分为:战略目标和战术目标。战略目标就是几年内的大目标,战术目标可以是具体到每周、每天的目标。

战略目标具有很大的不确定性,和外部环境息息相关,可以随时调整。

战术目标则需要集中,执行力强的人对战术目标要求很严格。战术目标不要定的太大,因为人需要一种完成任务后的满足感,虽然大的目标带来的满足感强但是很难到达。一个小的目标让人获得快速的满足感,比如一篇文章、阅读一本书,这种满足感可以快速获得,也能坚持。

清单。高效的人都是清单控。每个人基本都会使用清单安排工作,不过使用清单的方法不同。

有很多清单软件,我自己用过很多,也给自己做了一个简单的清单应用。这些软件不见得好用,我还是比较喜欢直接使用文本列表来记录即可。每天写在一个 markdown 笔记本中就完全足够了。

可以把清单分为两类:主要清单和火炉清单。

  • 主要清单:就是当天必须完成的工作,尽量少,避免完不成带来的挫败感。

  • 火炉清单:不是必须做的,但做了更好的清单,如果当天完不成也没有关系。

使用两个清单把目标的任务放到主要清单,高优先级,不重要的事情放入火炉清单,低优先级。现实是完成主要清单后,就可以休息,完成火炉清单的任何一项都可以带来满足感。清单完成后,不要再放入新的东西。

番茄钟。 有一本书叫做 《番茄工作法》, 作者描述的一种工作方式,虽然是一本书,其实一句话就能概括 “工作以 25 分钟为周期,然后休息5分钟,周期内排除干扰,尽力集中注意力。” 因为作者使用厨房的番茄形状的定时器,于是起名番茄工作法。

番茄工作法基本已经进入大多数人基因了,我在 Chrome 浏览器安装了插件,只需要点一下就可以开始计时,基本上一开始工作就使用番茄工作法。

番茄工作法让我形成了条件反射,这个插件在时间到后会有闹钟响起,结合 Mac 多桌面个沉浸式的工作方式,开始后使用多桌面屏蔽无用的消息,时间到后闹钟响起回到主桌面,回复微信消息之类的。

番茄工作法还有一个好处,就是可以统计真实需要的时间,便于安排清单。有时候我觉的一个小时能完成的文章,实际需要 6 个番茄时间(3h)。人总是高估自己的能力,然后排入过多的事情,带来挫败感。

单线程。 我是一个绝对的单线程,做一件事情的时候腾不出来功夫做其他事情。所以像开餐馆这类繁杂的事情不适合我,编程尤其需要单线程,所以刚好可以混口饭吃。

一次只做一件事情,是一个违反直觉的事情,但是实际上是效率最高的方式。

计算机曾经有两种通信方式:串口和并口,串口的速率大大高于并口,有意思的是,串口使用的线路成本反而更低。

对计算机而言,原因在于某些场景(计算密集)下上下文切换带来的成本很高,对于某些场景(IO 密集)多线程更适合。

对人来说,需要持续思考的工作一次做一件事最好,需要等待外部条件的(比如厨师)可以同时做。

3. 如何管理精力

如何保持精力,对持续高效率工作有重要意义。

不要加班。 加班和晚睡带来的坏处极大,尤其是熬夜加班。

如果晚上 11 点还在工作,基本无法实现早起,即使还是 11: 30 开始睡觉。人每天的精力是基本固定的,如果前一天高强度工作,就会影响第二天的工作。除了紧急的事情外,算下来基本不划算。

10:00 - 12:00 的时间用在了工作上基本第二天做不到 7 点起床,晚上花掉的时间需要第二天双倍的返还。所以没有紧急的事情时,10 点停止工作是非常重要的一件事。

自我激励。人需要满足感,有时候需要可以设计满足感。用不好听的话来说,就是年薪和日结的区别。

不过需要客观认识的是,普通人都做不到延迟满足,不能接受回报周期过长的事情。

比如学英语、读书,都是回报周期极长的事情。设计奖励回路,读完一本书可以玩游戏 1 小时,可以让精力得到恢复。

有度。 休息有度、工作有度、睡眠有度,所有的事情也都需要张弛有度。

假期睡眠过久,自我感觉并不好,打乱生活节奏,其实不利于人的状态恢复。休息的时间过长,随着边际递减效应对精力恢复就没有意义了。持续工作过久,效率会大大减低。

避免碎片化时间。 有一些时间管理的书籍会建议利用碎片时间工作和学习,实际体验不好。无法专注的时间完全可以用来休息,不适合学习和工作。

碎片化的时间可以刷知乎、微博,玩游戏都可以,不过需要注意上面那条有度的原则,离开碎片化的时间就需要及时停止。

慢性疾病。容易忽略影响人精力的另一个重要因素,就是慢性疾病。

慢性疾病带给人的影响是潜移默化的,有时候不明显。有一些全身性的疾病之间会有一些莫名奇妙的关联。我之前体质差,容易感冒和溃疡,我以为是缺乏维生素,以及和个人体质有关系,后来发现不是。因为我有一颗龋齿,后来去修复牙齿,华西的医生说我应该早点来,不然会引发容易感冒和周身的疾病,因为坏牙会导致口腔细菌造成咽喉反复感染,炎症反应又会造成全身酸痛。

处理牙齿后,身体莫名变好很多。

4. 一些陷阱

有一些畅销书写了很多鸡汤,可能有害,需要警惕。

强调意志的作用。 其实普通人意志不强,执行力强的人很多是被一些客观因素推着走,也许房贷比个人意志更能激励人早起。

当一个人发现意志不起作用时,就会带来无休无止的焦虑和无力感。

承认自己的能力有限,学会和自己和解才能保持良好的心态,持续不断的成长。

把时间排满。 生活需要节奏感,人的工作和生活都需要留白,当清单中的任务完成后,看看电视剧,玩游戏本来就没有任何问题。一味强调工作,反而不能把工作做好。

不把时间排满,留一些时间胡思乱想也挺好的。

和自己对抗。 除了一些心理受到过创伤的人外,大多数人的心理问题和情绪问题都是和自己对抗带来的。

注意力不集中,是身体告诉你精力已经耗散光了,你需要休息。

感到疲惫,是身体告诉你出现问题,需要调整。

人的身体状态不同、人记忆力不同、思维能力不同,以及外部环境都是客观存在的。一些书中用各种理论表达人的智力是一样的、记忆力差不多,暗示你工作做得不好是不够努力是一种典型的 PUA 行为。久而久之,会带来不自信、焦虑和强迫症行为,也会有反强迫症行为,进一步带来身心伤害。

参考文章

more >>

读毛选:实践论和矛盾论的现实意义

春节前因为手臂酸痛,假期就没有再碰电脑,趁此机会把之前的购买的毛泽东选集第一卷再读了一遍。

毛选第一卷有许多著名的文章,比如《湖南农民运动考察报告》《星星之火可以燎原》等,其中压轴的就是《实践论》和《矛盾论》的两篇文章。毛选第一卷的重要意义在于作者从哲学的高度来分析当时中国的各种问题,实践论和矛盾论是毛泽东思想的精华。

毛选是哲学,是方法论和认识论的统一。

毛泽东思想继承于马克思辩证唯物主义,新中国是柏拉图在《理想国》中描绘的 “哲学家建立的国家”。毛泽东思想从哲学的角度可以作为各个行业的工作方法的指导,尤其是分析复杂问题的场景下尤为有用。

这篇文章总结了实践论和矛盾论的一些现实意义,尤其是作为软件工程师的工作。

1. 实事求是

如果了解近代史的话,能感受到毛泽东思想来源于我党不同时期惨痛的教训。

博古和王明在中央苏区第五次反“围剿”中,因盲目听从共产国际军事顾问李德的指挥。照搬苏联的革命经验,坚持走阵地战、夺取城市的策略,反对毛泽东指出的建立农村根据地、在运动中消灭敌人的方针。

然而我国红军毕竟和苏联的客观条件差距甚大,军事能力上还非常弱小,在当时的环境下使红军伤亡惨重,造成第五次反围剿的失败。

红军不得不被迫进行战略转移,开始长征。

教条主义给红军带来了巨大的损失,逐渐的,实事求是成为毛泽东思想中最重要的原则之一。《实践论》就是用马克思主义的认识论来反对本本主义、教条主义和经验主义。警惕熟读马克思主义的 “行家” 带来的错误思想。

马克思主义的认识论指出了,事物是客观的、变化的以及不同事物的内在是不同的。用他国的经验解决我国的问题,用过去的经验解决现在的问题,用主观的意志去改变客观的事实,这都是徒劳的。

在技术领域里,中台、微服务、GraphQL 等新的技术概念的兴起,或许对于一个大的公司是适合的,但对于创业公司未必合适。对于互联网行业有积极价值的技术,对于传统行业未必合适。

计算机软件是客观的,是唯物的,实事求是也应该是软件公司在部署工作中最重要的原则之一。

2. 实践和理论都重要

其实实践和理论的关系无需多说,古今中外的哲学家都有大量的论述。

班固的 “百闻不如一见。”强调实践的作用。

狄德罗在反对形而上学时说“仅仅一个理论上的证明,也比五十件事实更能打动我。”强调理论的作用。

王阳明的”格物致知“强调理论来自于实践,”知行合一“ 强调即使知道理论也必须实践才能改造事物。

毛泽东思想认为一般有两种世界观:

  1. 强调实践,凭经验的主观认识论,以过去的经历和体验认识事物。
  2. 强调理论,通过辩证的方法发现事物的性质、规律,并作出预测和推定。

两种认识论会带来不同的结果:强调实践会出现只要是 ”老祖宗“ 的法子就是好的,是不能被修改的,造成经验主义和机会主义;强调理论会带来无休无止的辩论,走向形而上,比如用”金木水火土“讨论那种颜色的梨子更甜,实际只需要尝一口就好了。

毛泽东思想一针见血的指出,我们对事物的认识一开始是朴素的,出现最符合直觉的理论,然后在实践中发现事情不是那么简单,于是修正理论使之进步。

因此, 实践和理论是在循环前进的。中国革命就是在不断失败中前进的,马克思主义也不断在中国化。

对于软件工程而言,计算机科学建立在离散数学等理论基础之上,如果没有理论基础做出的技术方案只能是凭自己臆想,不具有可持续性。一些没有受过计算机科学训练的 ”富有经验的“ 的老程序员,做出的方案,有时候强行走向数学中被证明的错误方向。

一些大的公司往往对员工要求苛刻,不仅需要大量工作经验的人,也要求这些工程师有计算机科学基础,于是招聘介绍中会有计算机专业二本以上这类看似奇怪的要求。

3. 矛盾是分析问题的理论工具

《矛盾论》是毛选的精华,也是最重要的难理解的文章之一。政治书对矛盾这个概念给了一个简洁的定义:即对立统一。不过这一定义也是让大多数人在了解《矛盾论》的过程中最大的绊脚石。

其实矛盾在我们生活中无处不在,一个鲜明的例子就是:看似完美的方案和设计背后会付出对应的成本,换句话说就算吃口软饭,也的服人管呢。

有一个笑话说的是,有人找工作要的是 ”位高权重责任轻,钱多事少离家近“,这里面出现了多个矛盾。在同一个系统中(论域),对于一个普通人来说,不引入外部其他优势条件的情况下,位居高位就需要承担巨大的责任和压力。

对于投资来说,获得高收益就必须承担风险;在分布式系统中,强调分区之间的可用性,一致性就很难保证。对于团队管理来说,自由和民主就会降低效率和执行力。

通俗来说,矛盾,是指一个事物内部属性或一个系统内部组成部分的对立关系。

矛盾,不是一个新的概念,其实在老子的朴素唯物史观已经说明了问题。”故有无相生,难易相成,长短相形,高下相倾,音声相和,前后相随。“

要使用好矛盾论,需要注意一些矛盾的特点。

矛盾是普遍的。人们对事物的认识加深,继而产生概念,对概念又分化出属性,这些属性在概念中出现了对立,矛盾就无处不在了。 有一些矛盾并不明显,有一些矛盾显而易见。矛盾不能用世俗道德的好坏来评价,比如战争是人们不希望的,但是战争的发起却最终能带来和平。事物正是由这些矛盾才会变化,运动和发展,否则就不会再变化。比如汽车加速运动,是发动机推力和地面摩擦力矛盾不平衡的表现;死去动物的腐败,是崩溃的免疫系统和微生物的侵袭的矛盾不平衡的表现。

矛盾和冲突是不同的。 矛盾存在不一定会造成冲突,毛泽东思想中叫做斗争,在日常的语境下冲突或对抗更为准确。存在矛盾的两种事物,有两种状态:冲突和平衡。一颗炮弹,密闭空间和爆炸性气体是一对内部矛盾事物。在没有外部条件(引信)的触发下,处于平衡状态,当引信触发后变为冲突。到达临界点后爆炸(量变到质变),密闭空间被打破,重新平衡。

认识到矛盾和冲突的关系,可以认识到矛盾是如何发展的。

矛盾是不断变化的。 这个世界唯一不变的就是变化。矛盾也是不断变化的,一个系统中主要矛盾可以变为次要矛盾。矛盾和冲突也在不断变化,冲突和平衡一直在反复变化。

外部条件也在不断变化,导致触发的条件变化了。抗日的爆发,让国内阶级矛盾变为不那么重要,中日民族矛盾变为主要矛盾,在历史的表现就是第二次国共合作的实现,统一的抗日战线形成等等。

找到主要矛盾是问题分析的关键。主要矛盾是影响某一事物发展进程的关键矛盾。有时候显而易见的矛盾不一定是主要矛盾,主要矛盾往往需要仔细分析才能浮出水面。土地革命战争时期,社会的主要矛盾看似是国共两党的矛盾,实际上是代表中国人民利益的共产党和代表大地主大资产阶级利益的国民党反动派的矛盾。所以应该丢弃意识形态的不同,团结一切可以团结的力量。

矛盾论不能解决所有问题。矛盾论是认识论,能作为解决问题的指导方向,但是某些问题无法被客观的改变也是客观存在的。人们曲解了黑格尔的”存在即合理“ ,这里的合理是指的客观,而非道德意义上的”合理“。犯罪分子存在是合理的,犯罪分子被惩处也是合理的。某些事物的消失是历史的必然,比如奴隶制度被封建制度取代,继而被资本主义取代,历史的车轮不能被任何人阻挡。但是,世界在微观上又是偶然的,微观上的偶然又会影响宏观的必然,也是一种矛盾。

在应用矛盾论分析问题时需要注意在同一系统中分析问题,才能保证问题分析可靠性。当需要改造事物时,识别到外部条件就可能破局关键,从而在正确的方向上施加影响。

4. 调查是应用矛盾的实践工具

认识了矛盾这个基本的哲学工具后,如何落地使用呢?

在毛选中没有提到如何使用矛盾论的具体方法,不过在毛选第一卷前面的文章中,以及毛选第三、五卷都有反复提到 ”调查“ 的重要性。

在《反对本本主义》 中作者给出了调查的方法,以及说明了调查是解决问题的重要手段;《湖南农民运动考察报告》是作者给出的一个详细的示例;在《实践论》中也提到了如何使用调查来认识事物和改造事物。

一切结论都要产生在调查结尾,而不是开头。

调查的过程就是人们对事物认识加深的过程,也是解决问题的过程。在没有调查的情况下,无论用那种决策分析工具,都陷入了形而上学和机会主义的陷阱。成功学,本质就是一种形而上学。成功人士的成功法则往往无法复制,即使可以复制也无法创造出同样的历史背景、客观条件和机遇。

通过调查才能分析出当前的客观条件。作者也总结了调查的技术:

  1. 要使用讨论的方法调查。只凭一个人讲他成功的经验,是片面的,处于利害关系可能把实际情况夸大。需要从不同人中获取信息加以修正。
  2. 准备调查时,需要分析被调查对象的选择,根据分类来分层选取。这个和自然科学的等价划分的抽样方法类似。
  3. 调查前需要设定纲目。也就是需要设计调查方案,比如目标、方法和输出方式等。
  4. 要亲自出马和自行记录。这个是说给当时的干部的,对我们来说调查一定要到现场。
  5. 要深入。在注意调查的全面性时也需要取几个重点,深入开展,才能发现之前没有设想到的问题。

在做一些技术方案和开发团队的咨询工作时,调查决定了是否能命中客户的问题,以及在调查中找到影响客户问题的主要矛盾,才能进行一些正确的行动。

5. 总结

因此我们可以看出毛泽东思想的脉络是:以客观为原则,使用矛盾为分析问题的切入点,使用调查寻找具体的矛盾,尤其是主要矛盾,最后再结合外部条件的变化,改造世界。

毛泽东思想,是对马克思唯物主义辩证法认识论和方法论的总结。

在使用毛泽东思想时,应该像毛泽东思想说的那样,观察客观的外部环境和具体情况,不应该盲从,实事求是的分析问题和解决问题。

more >>