本书 是《程序员的底层思维》-张建飞 读书笔记.

第一部分 基础思维能力

抽象思维

这也是我在做设计和代码审查(Code Review)的时候,会特别关注命名是否合理的原因。因为命名的好坏在很大程度上反映了我们对一个概念的思考是否清晰、抽象是否合理,反映在代码上就是代码的可读性、可理解性是否良好,以及我们的设计是否到位。

代码CR和设计是非常重要的.

有一种检查抽象设计是否完整的方法,是查看接口或类是否缺少“互补和对称”。如果缺少,则可能存在着“不完整的抽象设计”。

越抽象、越通用、可扩展性越强,其语义的表达能力就越弱;越具体、越不好延展,其语义表达能力却越强。所以,对于抽象层次的权衡是我们系统设计的关键所在,也是区分普通程序员和优秀程序员的重要参考指标。

抽象和扩展性一定要有一个读,抽象过渡万物接Object,Map. 抽象不足所有的都是唯一的.

逻辑思维

“无逻辑”就是没有建立起事物之间的正确关系,即“有逻辑”就是能建立事物之间的正确关系。

逻辑就是关系

管理者的概念技能是指管理者提出自己的观点并经过加工处理,将关系抽象化、概念化的能力

代码运行的一般规律分为 调度逻辑(算法逻辑)和业务逻辑,逻辑关系要明确,避免互相耦合.

归纳推理是以一类事物中的若干个别对象的具体知识为前提,得出有关该类事物的普遍性知识的结论的过程。

建模是一个归纳工作,我们通过抽象问题域里具有共同特性的类来建立模型。为了验证模型的有效性,我们会使用演绎的方法去推演不同的业务场景,看看模型是否能满足业务的需要。

溯因推理就是我先知道了答案,再去追溯原因的推理。

对于程序员来说,基本每天都在运用这种溯因推理。我们通常说的故障排查(Trouble Shooting)就是溯因推理,用的手段基本上也是假设和求证。

深度思维能够带给我们各种各样的好处——在学习、工作、管理、投资等方面,而思维逻辑链的延长则是深度思维的重要表现。

工作当中要多使用深度思维,帮助我们理解思考问题的本质,而不是只是浮于表象,思考的深度越深我们得出的结论越清晰.

5So思考法,是指对一个现象连续追问其产生的结果,以探求它对未来可能造成的深远影响。凡事多问几个“所以呢”,能让我们拥有推演事物长远影响的能力。

每个人的背景和身份不一样,代表的利益不一样,所以在话语中经常带有自己的价值倾向,只有把这些隐含的假设暴露出来,我们才能进行正确的判断。

就像“布里丹之驴”这个故事:一只完全理性的驴恰处于两堆等量等质的干草中间,将会饿死,因为它不能对究竟该吃哪一堆干草做出任何理性的决定。

思考的深度取决于逻辑链的深度,5Why和5So思考法是非常有用的深度思考工具。

结构化思维

第一,不能一次处理太多信息,太多信息会让我们的大脑觉得负荷过大

第二,喜欢有规律的信息。有规律的信息能降低复杂度

一个好的金字塔结构,需要在纵向关系上满足结论先行、以上统下;在横向关系上,满足归类分组、逻辑递进这4个基本原则
个人觉得还是金字塔原理图更能说清楚问题.

(1)纵向关系:纵向是层次关系,上一层思想是对其下一层思想的概括,下一层是对上一层的解释和支持。• 结论先行(“论”):所谓结论先行,就是要先抛结论。这一点在与人沟通的时候尤其重要,就像很多人铺垫了半天,也不说结论。写代码也一样,主方法是中心论点,子方法是对主方法的结构化分解。• 以上统下(“证”):金字塔是一种层次结构,上一层是对下一层的统领和抽象,比如水果是对苹果、橘子的抽象,所以在上一层。(2)横向关系:横向是关联关系,每组中的思想必须属于同一逻辑范畴,并按照逻辑顺序组织。• 归类分组(“类”):将内容相似的思想归为一类,为进一步归纳抽象做好准备。• 逻辑递进(“比”):分组中的思想需要有逻辑递进关系,即它们必须属于同一个逻辑范畴,且满足一定的逻辑顺序。

2W1H是构建结构时最常用,也是最有用的框架之一。因为它涉及一个问题最核心的3个要素,即“是什么”“为什么”和“怎么做”。有了2W1H这个思考框架,我为“如何写好技术文章”搭建了自上而下的结构(如图3-15所示),接下来写出这篇软技能文章,也就不是什么难事了。(1)为什么写文章:1)写文章是费曼学习法;2)写文章可以增加影响力。(2)什么是好文章:1)内容有价值;2)结构要清晰。(3)如何写好文章:1)选择好内容;2)搭建清晰的结构;3)刻意练习;4)迭代优化。

代码晦涩难懂,原因通常也在于“没有结构”的混乱,长方法(long method)之所以是典型的代码坏味道,正是因为它把过多的信息放在了一起。就像前面提到的,人类的大脑不是CPU,一次容不下过多的概念,我们搭建结构的目的是更加清晰地表达,减轻大脑的负担,这和我们强调代码可读性的要求是一致的。

批判性思维

除了进行理性的思考,还要排除各种认知、本能、情绪、语言和外部权威及社会环境等其他因素的影响。

我们的文化比较讲究经验和直觉,而批判性思维比较注重理性和逻辑;我们的文化比较讲究包容和认同,而批判性思维常常需要反驳和质疑。

业务中台低效的根本原因在于,前台业务和业务中台的“深度单体耦合”。

研发≠写代码,实际上我们大部分时间不是在写代码,而是在沟通协调,况且与人打交道要比与机器打交道麻烦得多。这也是《人月神话》一书中说“加人只会让项目更糟糕”的原因,因为额外增加了更多的协作成本

协作成本是我们需要注意的一点,也是经常被忽略的一点.

在现代生活中,简单的做法一直难以实现,因为它有违某些努力寻求复杂化以证明其工作合理性的人所秉持的精神。

由于平台代码是被所有业务共享的,这就给稳定性带来了极大的隐患。比如,A业务改动了平台代码,然而B业务什么也没做就出了故障。

(1)把业务能力做薄。做薄是为了解耦,业务最懂自己,因此不要尝试去“control”它们。中台可以更多地关注与“业务无关”的能力建设,比如稳定性、性能、监控、运维工具等非功能属性。(2)把中台能力做强。除了非功能属性,中台还可以通过建设丰富的业务解决方案库、业务组件库等工具,赋能业务快速发展,用enable代替control。(3)把系统结构做简单。这一点很好理解,因为复杂是万恶之源。

最难的是服务边界,服务边界有多种情况互相影响:组织架构,代码历史,工程架构等等,其实服务边界最开始设计时,是最清晰的,如果后来没有维护好随着技术债务增加,组织架构变动等等,边界会变得不清晰。

解耦不难,关键是这一刀要从哪里切?我认为这一刀可以切在“业务无关”这个界面上。

实际上,重复(Duplication)也是一种重用(Reuse)。这样做可能会导致不同的业务代码之间出现一些代码冗余(实际上,出于快速发展和稳定性的考虑,有些业务已经在采用重复代码的方式,比如淘特、APOS)。然而,在稳定性、可理解性、可维护性、工程效率的综合权衡之下,这点代码冗余会显得微不足道。

很多后劲不足的人主要是过早地停止了学习和成长,你的能力应该是围绕着你的层级上下震荡的,这个震荡范围偏差不会太大,迟早会归于一个相对合理的区间。

建议看下终生成长这本书.

不要把自信建立在贬低他人的基础上,什么时候你能发自内心地欣赏你不喜欢的人,你就成长了。

维度思维

一个人的思维层级与其思考的维度是正相关的。这一点可以通过我们的日常语言得到佐证,如图5-1所示。当我们说这个人很“轴”“一根筋”的时候,实际上是在说他只有一维的线性思维;高手的思考会更加“全面”,因为涉及“面”,所以至少是两个维度的思考;而真正的高手,其思考是成“体系化”的,“体”至少是三维的,也就是说他考虑到了“方方面面”。

其核心要义在于直观性,通过矩阵把多维度的信息放在一起进行对比分析,使我们可以更全面(无遗漏)、更直观、更清晰地看到问题的全貌,从而做出合理的决策。

阿里巴巴在做人才盘点时是从工作业绩、价值观两个方面去看的,所以绩效由工作业绩和价值观两部分组成。按照工作业绩和价值观的不同,我们把员工分成“明星”“野狗”“黄牛”和“白兔”。(1)明星:指德才兼备的员工。要重用明星员工,充分发挥他的能力。(2)野狗:指有才无德的员工。这类员工个人能力强,但对公司目标和价值观的认同感非常低,要消灭。(3)黄牛:指任劳任怨的员工。能力差一点,但勤勤恳恳,可以放心使用。(4)白兔:指无才有德的员工。态度很好,可业绩就是上不来,而且还占据着重要岗位,所以这类员工一定要清理。

• 维度思维的关键是理解维度,维度是问题域独立参数或变量的数量。• 不借助工具,大脑很难处理多维问题,矩阵分析是解决多维问题的利器。• 矩阵分析首先要找到影响问题域的核心要素,也可以叫变量、维度,然后显性化地构建矩阵,当维度和维度属性值比较少的时候,可以用四象限、九宫格或立方体进行视觉上的呈现。• 业务代码的复杂度主要取决于业务场景和业务流程的复杂度。再复杂的业务,其业务场景都是可以枚举的,关键是找到构成业务场景的核心要素。例如,商品业务的场景主要由商品类型、商品售卖方式、商品仓储方式3个要素组成。• 多维度思考是思考的高级形式,矩阵分析在其中是无处不在的。从商业分析的波士顿矩阵、RFM模型到逻辑推理,都能看到矩阵分析的身影。

分类维

总之,抽象分类是一个迭代过程,它既不是自顶向下的活动,也不是自底向上的活动。也就是说,我们的设计不会都从超类开始,也不会都从基类开始,而是从“不完美”的分类开始。随着对问题域理解程度的加深,我们会经常发现,需要从两个类中提取公共部分放到一个新类中,或者将一个类分成两个新类。通过这样增量式的迭代,最终得到一个高类聚、低耦合的类结构设计,这才是一种可以解决问题的相对正确的分类方式。

• 分类就是设计,分类对设计至关重要。• 人类有分类的本能,这和我们认识世界的方式有关。• 分类的本质是寻找共性,这些共性既可以是物理属性,也可以是概念属性。• 没有完美的分类,任何分类都与进行分类的观察者的视角和目的有关。• 分类是软件设计的基础,主要体现为对象分类、构建分类和领域分类。• 不同维度的分类选择造就了不同的组织,既可以按照业务划分,也可以按照职能划分。• 按照供给和履约的不同对互联网进行分类,可以更好地看清互联网。

分治思维

分治的价值在于,我们不应该试着在同一时间把整个问题域都塞进自己的大脑,而应该试着以某种方式去组织问题,以便能够在一个时刻专注于一个特定的部分

简单思维

所有的UNIX哲学浓缩为一条铁律,那就是各地编程大师们奉为圭臬的“KISS(Keep It Simple and Stupid)原则”。

把一件事情搞复杂是一件简单的事,但要把一件复杂的事变简单,这是一件复杂的事。

成长性思维

决定你成长的第一步不是你是否努力,而是你是否相信努力

他们追求完美无缺,害怕自己努力后的失败会证明自己无能,因而恐惧挑战,拒绝学习新事物。

成功个体的标志,在于他们热爱学习、喜欢挑战、重视努力,并在面对苦难时坚韧不拔。

相信这一点很重要,因为相信大脑拥有可塑性,我们就有了改变人生的机会。
大脑的可塑性符合脑科学。

对于一件事情,如果你想不到特别有力的不去做的原因,那么就优先选择去做。

平和的心态是我们持续成长的基础,因为人的专注力是有限的,内心平和的人可以更多地专注在学习和工作上

培养成长型思维,就是要我们戒骄戒躁。人生是一场马拉松,不用在乎一时的得失,用好奇的眼光去探索这个世界,学会欣赏身边的风景,拥有一颗平常心,相信成长的力量。对于结果,切忌急功近利,“慢也是快”,徐徐图之就好。

当你已经非常擅长某件事情的时候,你就会把它放在一边,并继续寻找那些更有挑战性的事情,因为这样你才能持续成长。

第二部分 专业思维能力

根据定义,我们知道依赖倒置实际上倒置的是依赖方向。如图10-1所示,有两个模块A和B,本来A是直接依赖B的,依赖方向是A→B,通过增加一个抽象C,然后让模块B去实现这个抽象,从而反转了依赖的方向,变成B→A,这就是依赖倒置。

依赖接口而不依赖具体实现

“计算机中的任何问题,都可以通过加一层来解决
我觉的不太合理,增加一层如果单纯只是代理,意义不大,太多的中间层会使服务调用链路,增加调用成本和耗时,也违背简单思想。在合理的抽象和适度的中间层应做好取舍

契约思维

(1)规范价值:一致性可以降低认知成本和复杂度,一个系统如果没有任何的规范约束,那么呈现出来的结果就是混乱。面对混乱,再优秀的程序员也会寸步难行。

我觉得光有规范是不够的,能把规范如何推广下去这里的涉及问题就比较多了.规范能不能自动检测?能否让遵守规范的人得到益处?怎么能让团队达成一致去落地执行?否则规范就是一纸空文.

因此,在我主导的项目中,我需要团队在项目之初就整理出核心的领域概念,然后用中英文的形式,把这些概念放到设计文档中。要求英文的原因是,我需要保持领域概念从文档到代码的一致性。

这里需要强调的是,规范也是架构的重要组成部分,其本质就是制定一个契约,其前提是大家要遵守这个契约,否则再好的架构也会形同虚设。

所有成员会轮值负责一周的Code Review工作,当期的负责人要在周五发出一个Code Review周报,摘要一些典型问题,并在周会上供团队成员进行讨论和互相学习。

一个是技术实现细节的暴露,另一个是内部业务语义的外泄

软件设计通常是遵循中庸之道的,不能走极端,扩展性设计也是如此。对于API来说,扩展性设计的极端就是把入参、出参都定义为Map,但其可理解性也是最差的

模型思维

在软件工程中,有两个高阶工作,一个是架构,另一个是建模

试想一下,你可以熟练地使用抽象思维、分类思维去做领域建模;你可以运用结构化思维做技术规划;在身边人云亦云的时候,你能用逻辑思维、批判性思维理性地分析问题;面对华而不实的复杂设计,你敢于用“奥卡姆剃刀”化繁为简;你能用工具化思维、产品化思维去优化身边的研发环境;你能用分治思维高效地分解问题、解决问题;你能用数据思维、量化思维给业务助力;在面对困难和挑战的时候,你信心十足,因为你相信学习和成长的力量。那么你一定会和其他人不一样!

因此,世界上没有完美的模型,甚至连正确的模型也没有。在软件开发的过程中,我们也要用发展的眼光来看待模型,能解决当前问题的模型就是好模型。随着时间的推移,我们可能要像重构代码那样重构我们的模型,确保它能跟上我们对问题域的最新理解的步伐。

类的关联关系也可以是单向的,单向关联用带箭头的实线表示

在聚合关系中,成员对象是整体对象的一部分,但是成员对象可以脱离整体对象而独立存在

组合(Composition)关系也表示类之间整体和部分的关联关系,但是在组合关系中,整体对象可以控制成员对象的生命周期。一旦整体对象不存在,那么成员对象也将不存在,成员对象与整体对象之间具有“同生共死”的关系。

大多数情况下,依赖关系体现在某个类的方法使用另一个类的对象作为参数。在UML中,依赖关系用带箭头的虚线表示,由依赖的一方指向被依赖的一方。

泛化(Generalization)关系就是继承关系,用于描述父类与子类之间的关系

复用和能力共享,然而这么做的坏处也很明显,即高耦合:任何对于“共享内核”的改动都要小心翼翼地协调两个领域的技术团队,且会影响两个领域。说实话,这个副作用有点让人“伤不起”

面对系统架构就是这样,我们总要在重复(Duplication)和复用(Reuse)之间做折中和权衡。

领域模型关注的是领域知识,是业务领域的核心实体,体现了问题域中的关键概念,以及概念之间的联系。领域模型建模的关键在于模型能否显性化、清晰地表达业务语义,其次才是扩展性。

,一个是错把领域模型当数据模型,另一个是错把数据模型当领域模型。

工具化思维

这样做有两个好处,一是集思广益,二是由大家共同制定目标,有助于打破部门壁垒,有利于目标的达成。

• 工具化是一种“偷懒”的智慧,工作中要有阶段性地停下来思考,不要用战术上的勤奋掩盖战略上的懒惰。• 工具化的步骤:首先,发现问题,找到重复3次以上的手工劳动;其次,明确问题,找到现状和期望之间的差距;最后,制造工具,解决问题。

(1)定义指标:仔细分析问题,找到那个可以用来量化问题的关键指标。(2)将指标数字化:围绕关键指标,明确需要哪些数据来实现指标的计算,通过数据收集、数据存储、数据展现去呈现指标,也就是数字化的过程。(3)优化指标:有了数据指标之后,要围绕指标数据迭代优化,达成业务目标。

量化思维

有数据呈现数据,没有数据呈现案例,没有案例呈现观点,如果都没有的话,就请闭嘴

最后,所有前端埋点方案都会面临数据传输时效性和可靠性的问题,只能通过在后端收集数据来解决这个问题。

关键是一些只有前端能操作的,如滑动展示、模块曝光操作通过后端收集如何解决该问题?就算是后端上报,有些UI触发的曝光埋点虽然在后端上报也面临着可靠性和传输时效的问题

我们经常用来度量的软件指标——代码行数(Lines Of Code, LOC)和功能点(Function Points, FP)——都不能客观地反映软件效能。

业务部门永远希望需求的交付能够更快,于是有段时间,上层领导决定把需求交付时长作为研发效能的重要度量指标。这个指标看似合理,但是一旦和技术人员的KPI进行绑定之后,味道就变了。为了应对这个指标,聪明的程序员们想出了各种办法,比如分拆需求,把一个需求分拆成多个更小的需求,这样从数据上看起来,交付时长就变得更短了;或者延迟拉取变更的时间点,因为研发时长是等于发布时间减去变更时间的。很明显,像这样的度量并不会真正地提升研发效率,只是换了一种数据呈现方式而已。

因此,我们完全可以采用平均缺陷修复时间(Mean Time To Repair)来衡量代码的质量。

目标是具体的;目标是可衡量的;目标是可接受的,不可过高和过低;目标是可证明的、可实现的;目标是有完成时限的。

某些技术部门越来越没有技术味道:所有技术团队都在讨论业务问题,技术大会上在谈业务,周会上在聊业务,周报里写的是业务项目……唯独少被谈及的是技术本身。这种技术味道的缺失导致技术人员只会通过if else实现业务需求的资源,代码设计毫无优雅性可言。从长远来看,技术能力的缺失导致留给公司的都是难以维护的烂代码,对公司的发展是不利的。

基于这样的背景,我提出了技术KPI的口号,也就是在业务项目之外找到一个属于技术人自己的量化指标。这里遇到的挑战和上面的案例类似,即纯技术贡献是很难被量化的。这个指标是加班时间吗?加班时间和工作产出之间的关系比较微妙。脑力劳动不同于工厂的计件工作,不是工作时间越长产出就越多的。就像第13章中提到的,工程师要学会“智慧懒”,有意义地创新显然要比低效地加班更好。因此,用加班来衡量技术贡献显然不大合适。

但是我在补充一下,现实中没有重复性的工作是不可能的,我们还是要有充分的心里准备去面对这些,如之前作者所说:重复的事情越快搞完越好,如果重复的事情有共性那尽快使用工具化思维,拜托这些重复的工作。

(1)应用质量分:我们有一套算法为应用打分,影响应用质量的因素包括代码重复率、圈复杂度、分层合理性。如果你负责或者与他人共同负责的应用分数越高,则说明应用质量越高。(2)技术影响力分:我们提供了一套量化标准来尝试量化你的影响力。比如,你写了一篇技术文章,如果这篇文章被浏览、收藏、点赞的次数越多,那么分数越高;你做了一次演讲分享,分享的范围越广,则分数越高。在团队内分享是3分,在部门内是8分,在QCon上分享是30分等。此外,发表论文、创新专利等都可以增加影响力分数。(3)技术贡献分:主要从Code Review、重构、技术亮点等角度衡量。这部分有一定的主观性,比如重构的质量如何、技术亮点是否足够亮,仍然需要结合人为的主观判断。(4)开发质量分:主要包括项目的Bug数、线上的故障数、单测覆盖率等指标,能相对比较客观地反映真实情况。

量化的一般步骤包括定义指标、数字化和优化指标。
量化概念其中一个重要 的方式是使用实验指标去进行比较。

数据化思维

一切业务数据化,一切数据业务化。——阿里巴巴
我在简普工作的时候,有一条文化是基于数据和事实做决策,提现了数据重要程度。

这种用数据赋能业务的方法叫作数据业务化。

对于技术人员,特别是业务部门的技术人员来说,数据能力已经不是可有可无的加分项,而是必备的能力之一。

源数据、数据仓库、ETL、元数据管理和数据应用

数据库专注于OLTP,而数据仓库专注于联机分析处理(On-Line Analytical Processing,OLAP)。OLAP关注的重点是“A”,即Analytical,以分析为目的。

补充:现代数据库有很多 HTAP 数据库类型。

元数据主要记录数据仓库中模型的定义、各层级间的映射关系、监控数据仓库的数据状态及ETL的任务运行状态。在数据仓库系统中,元数据可以帮助数据仓库管理员和开发人员非常方便地找到他们关心的数据,指导其进行数据管理和数据开发工作,提高工作效率。

“烂程序员关心的是代码,好程序员关心的是数据结构和它们之间的关系”

• 性能:良好的数据模型能帮助我们快速查询所需要的数据,减少数据的I/O吞吐量。• 成本:良好的数据模型能显著减少不必要的数据冗余,也能实现计算结果复用,大幅降低大数据系统中的存储和计算成本。• 效率:良好的数据模型能显著改善用户使用数据的体验,提高使用数据的效率。• 质量:良好的数据模型能改善数据统计口径的不一致性问题,降低数据计算错误的可能性。

第一步:确定聚集维度。在原始明细模型中,存在多个描述事实的维度,如日期、商品类别、卖家等。

在技术层面,数据中台的实时数据计算技术可以保障生意参谋中众多数据指标的实时性和准确性。商家在这一秒就能看到上一秒的数据,一旦店铺出现异常,可以马上发现问题并进行处理。

产品思维

对于产品架构,我们首先可以把整个架构划分为用户感知、功能模块和数据3个层次。在这3个层次的基础上,我们再对每个层次内的模块进行分组。例如在功能模块层,我们要对功能进行分类,让分散的功能点内聚合成更大的产品模块(体现在用户界面上,往往是一级菜单和子菜单的关系)。

用产品思维去建设平台,我们可以从价值的角度出发,更多地关注用户体验,从而有机会做出不错的平台型产品。

第三部分 思维能力的综合应用

除非应用有极强的流程可视化和编排的诉求,否则我非常不推荐使用流程引擎等工具。第一,它会引入额外的复杂度,特别是那些需要保持持久化状态的流程引擎;第二,它会割裂代码,导致阅读代码的不顺畅。

我发现这种循序渐进的能力下沉策略,应该是一种更符合实际、更敏捷的方法。我们必须承认,模型不是被一次性设计出来的,而是迭代演化出来的。

如果用一句话来总结这个演化过程,那就是:我用奥卡姆剃刀砍了又砍,剪了又剪,直到没有多余。

当然不是,过多的层次不仅不能带来好处,反而会增加系统的复杂度,降低系统性能。比如我在苹果公司工作时,曾维护一个Directory Service的应用,整个系统有7层之多,把validator、assembler都当成一个层次来对待,其结果就是增加了系统的复杂度。由此可见,分层太多或没有分层都会导致系统更复杂度,因此我主张不可以没有分层,但只分有必要的层。

本质是人的思维方式,人的思考方式更像是一个垂直分层思考方式,而不是一个递归层级很高的栈的思考方式。一个合理代码层级不应该超过 3 层,超过三层后理解难度便是指数级增长。经常看代码的知道,代码如果层级越高越难以被理解。

但是代码中却没有公海和私海的实体(Entity),也没有相应的语言,这就导致了领域专家描述的内容、我们日常沟通的内容与模型和代码呈现的内容之间是相互割裂的,没有关联性

业务代码是为业务服务的,所有相关的业务设计、业务对象应和业务场景深度绑定,不应该无关联性。

但是代码中却没有公海和私海的实体(Entity),也没有相应的语言,这就导致了领域专家描述的内容、我们日常沟通的内容与模型和代码呈现的内容之间是相互割裂的,没有关联性