DDD落地,如何持久化聚合根

1. 理解聚合根

聚合是一组始终需要保持一致的业务对象。因此,我们作为一个整体保存和更新聚合,以确保业务逻辑的一致性。

聚合根是 DDD 中最为重要的概念,即使你不使用 DDD 编写代码也需要理解这一重要的概念 —— 部分对象的生命周期可以看做一个整体,从而简化编程。

一般来说,我们需要对聚合内的对象使用 ACID 特性的事务。

最简单的例子就是订单和订单项目,订单项目更新必须伴随订单的更新,否则就会有总价不一致之类的问题。订单项目需要跟随订单的生命周期,我们把订单叫做聚合根,它就像一个导航员一样

class Order {    
    private Collection<OrderItem> orderItems;   
    private int totalPrice;   
}

class OrderItem {
    private String productId;   
    private int price;   
    private int count; 
}

Order 的 totalPrice 必须是 OrderItem 的 price 之和,还要考虑折扣等其他问题,总之对象的改变都需要整体更新。

理想中最好的方式就是把聚合根整体持久化,不过问题并没那么简单。

2. 聚合持久化问题

如果你使用 MySQL 等关系型数据库,集合的持久化是一个比较麻烦的事情

  1. 关系的映射不好处理,层级比较深的对象不好转换。
  2. 将数据转换为聚合时会有 n+1 的问题,不好使用关系数据库的联表特性。
  3. 全量的数据更新数据库的事务较大,性能低下。
  4. 其他问题

聚合的持久化是 DDD 美好愿景落地的最大拦路虎,这些问题有部分可以被解决而有部分必须取舍。

聚合的持久化到关系数据库的问题,本质是计算机科学的模型问题

聚合持久化是面向对象模型和关系模型的转换,这也是为什么 MongoDB 没有这个问题,但也用不了关系数据库的特性和能力。

面向对象模型关心的是业务能力承载,关系模型关心的是数据的一致性、低冗余。描述关系模型的理论基础是范式理论,越低的范式就越容易转换到对象模型。

理论指导实践,再来分析这几个问题:

“关系的映射不好处理” 如果我们不使用多对多关系,数据设计到第三范式,可以将关系网退化到一颗树。

image-20210120230700098
△ 网状的关系
image-20210120230820713
△ 树状的关系

"将数据转换为聚合时会有 n+1 的问题" 使用了聚合就不好使用集合的能力,列表查询可以使用读模型,直接获取结果集,也可以利用聚合对缓存的优势使用缓存减轻 n+1 问题。

"全量的数据更新数据库的事务较大" 设计小聚合,这是业务一致性的代价,基本无法避免,但是对于一般应用来说,写和更新对数据库的频率并不高。使用读写分离即可解决这个问题。

3. 自己实现一个 Repository 层

如果你在使用 Mybatis 或者使用原生的 SQL 来编写程序,你可以自己抽象一个 Repository 层,这层只提供给聚合根使用,所有的对象都需要使用聚合根来完成持久化。

一种方式是,使用 Mybatis Mapper,对 Mapper 再次封装。

class OrderRepository {
    private OrderMapper orderMapper;
    private OrderItemMapper orderItemMapper;

    public Order get(String orderId) {
        Order order = orderMapper.findById(orderId);
        order.setOrderItems(orderItemMapper.findAllByOrderId(orderId))
        return order;
    }
}

这种做法有一个小点问题,领域对象 Order 中有 orderItems 这个属性,但是数据库中不可能有 Items,一些开发者会认为这里的 Order 和通常数据库使用的 OrderEntity 不是一类对象,于是进行繁琐的类型转换。

类型转换和多余的一层抽象,加大了工作量。

如果使用 Mybatis,其实更好的方式是直接使用 Mapper 作为 Repository 层,并在 XML 中使用动态 SQL 实现上述代码。

还有一个问题是,一对多的关系,发生了移除操作怎么处理呢?

比较简单的方式是直接删除,再存入新的数组即可,也可以实现对象的对比,有选择的实现删除和增加。

完成了这些,恭喜你,得到了一个完整的 ORM,例如 Hibernate 。

4. 使用 Spring Data JPA

所以我们可以使用 JPA 的级联更新实现聚合根的持久化。

大家在实际操作中发现,JPA 并不好用。

其实这不是 JPA 的问题,是因为 JPA 做的太多了,JPA 不仅有各种状态转换,还有多对多关系。

如果保持克制就可以使用 JPA 实现 DDD,尝试遵守下面的规则:

  1. 不要使用 @ManyToMany 特性
  2. 只给聚合根配置 Repository 对象。
  3. 避免造成网状的关系
  4. 读写分离。关联等复杂查询,读写分离查询不要给 JPA 做,JPA 只做单个对象的查询

在这些基本的规则下可以使用 @OneToMany 的 cascade 属性来自动保存、更新聚合。

class Order {    
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String id;

    @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
    @JoinColumn(name = "order_id")
    private Collection<OrderItem> orderItems;   
    private int totalPrice;   
}

class OrderItem {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private String id;
    private String productId;   
    private int price;   
    private int count; 
}

OneToMany 中的 cascade 有不同的属性,如果需要让更新、删除都有效可以设置为 ALL。

5. 使用 Spring Dat JDBC

Mybatis 就是一个 SQL 模板引擎,而 JPA 做的太多,有没有一个适中的 ORM 来持久化聚合呢?

Spring Data JDBC 就是人们设计出来持久化聚合,从名字来看他不是 JDBC,而是使用 JDBC 实现了部分 JPA 的规范,让你可以继续使用 Spring Data 的编程习惯。

Spring Dat JDBC 的一些特点:

  • 没有 Hibernate 中 session 的概念,没有对象的各种状态
  • 没有懒加载,保持对象的完整性
  • 除了 SPring Data 的基本功能,保持简单,只有保存方法、事务、审计注解、简单的查询方法等。
  • 可以搭配 JOOQ 或 Mybatis 实现复杂的查询能力。

Spring Dat JDBC 的使用方式和 JPA 几乎没有区别,就不浪费时间贴代码了。

如果你使用 Spring Boot,可以直接使用 spring-boot-starter-data-jdbc 完成配置:

spring-boot-starter-data-jdbc

不过需要注意的是,Spring Data JDBC 的逻辑:

  1. 如果聚合根是一个新的对象,Spring Data JDBC 会递归保存所有的关联对象。
  2. 如果聚合根是一个旧的对象,Spring Data JDBC 会删除除了聚合根之外旧的对象再插入,聚合根会被更新。因为没有之前对象的状态,这是一种不得不做的事情。也可以按照自己策略覆盖相关方法。

6. 使用 Domain Service 变通处理

正是因为和 ORM 一起时候会有各种限制,而抽象一个 Repository 层会带来大的成本,所以有一种变通的方法。

这种方法不使用充血模型、也不让 Repository 来保证聚合的一致性,而是使用领域服务来实现相关逻辑,但会被批评为 DDD lite 或不是 “纯正的 DDD”。

这种编程范式有如下规则:

  • 按照 DDD 四层模型,Application Service 和 Domain Service 分开,Application Service 负责业务编排,不是必须的一层,可以由 UI 层兼任。
  • 一个聚合使用 DomainService 来保持业务的一致性,一个聚合只有一个 Domain Service。Domain Service 内使用 ORM 的各种持久化技术。
  • 除了 Domain Service 不允许其他地方之间使用 ORM 更新数据。

当不被充血模型困住的时候,问题变得更清晰。

DDD 只是手段不是目的,对一般业务系统而言,充血模型不是必要的,我们的目的是让编码和业务清晰。

这里引入两个概念:

  1. 业务主体。操作领域模型的拟人化对象,用来承载业务规则,也就是 Domain Service,比如订单聚合可以由一个服务来管理,保证业务的一致性。我们可以命名为:OrderManager.
  2. 业务客体。聚合和领域对象,用来承载业务属性和数据。这些对象需要有状态和自己的生命周期,比如 Order、OrderItem。

回归到原始的编程哲学:

程序 = 数据结构 + 算法

业务主体负责业务规则(算法),业务客体负责业务属性和数据(数据结构),那么用不用 DDD 都能让代码清晰、明白和容易处理了。

more >>

如何做好敏捷技术预研(Spike)?

在敏捷开发中,有时会出现下面的一些问题:

  1. 迭代前工作量不好估计。
  2. 敏捷开发造成技术方案破碎,无整体规划。
  3. 迭代中方案变化,交付风险大。

在敏捷实践中,往往会有 Spike 这个实践,可以在一定程度解决这些问题。Spike 的目的是收集信息和寻找一个问题的答案,而不是交付具体产品。

我一直找不到一个好的中文词汇,有些书籍翻译为“探针”,通俗的来说就是研发人员正式开始启动一个大的技术方案前。提前“摸一下”,摸的好不好直接决定了后面的工作是否能很好的开展。

在我国的五代战斗机 J20 首飞 10 年的新闻中学到了一个新词 —— “技术预研”,其实能很好的描述 Spike 这实践,技术预研的价值在软件工程中同样明显。

技术预研的价值

工作量估算。工作量估算不准的原因是信息不够,技术预研可以提供这些信息,提前知道有哪些坑。工作量的评估对业务、产品规划极其重要,尤其是多个团队工作在一个项目上时。
人员能力培养。有一些团队技术预研工作往往被技术 leader 默认做了,这种做法有利有弊。不好的地方是团队成员得不到锻炼,只能实现一些预研好的内容,造成团队做方案的能力低。同时,也会消耗技术 leader 的工作时间,无关注其他工作。提前设计预研让团队来做,让技术 leader 评审即可。

输出技术方案。技术预研可以提前输出技术方案,避免在迭代开始后再进行设计工作,如果多个人在一类工作上,没要技术方案会各自为政。敏捷开发让这种情况放大,减低开发效率。

减低风险和减少浪费。一些技术方案可能客观上就不能实现,如果放入迭代交付可能在花费了时间分析后,结论是无法实现,造成浪费和交付风险。有可能因为这一个任务影响其他被依赖的工作内容。

预研工作规划

“生产一代、试制一代、预研一代、探索一代” ,这是我国航空工业尤其是军用飞机的发展计划。 J20 在 2011 年于成都黄田坝军用机场首飞 距今 10 年,但 1997 就开始立项,且比在之前就开始规划了。

结合敏捷工作方式,我们可以用类似的思路规划技术预研工作。

这里个“代”可以粗暴的设定为一个迭代,也就是 2 周,让策略更好落地实施。对于敏捷工作方式,如果进入迭代才开始验证技术方案的可行性、给出方案等工作,会给任务的拆分带来麻烦。

规划一代。提前两个迭代规划,这时候业务需要给出粗的业务目标,如果业务明显不合理,技术 leader 可以第一时间提出建议。这个阶段需要业务方准备相对大粒度的业务需求(非常细也不现实),计划好需要技术预研的工作内容。这个阶段的参与人为业务需求方和技术 leader 即可,技术 leader 可以通过经验得出能否实现,是否有价值预研,但是不知道细节和工作量。

技术预研一代。提前一个迭代预研,给出技术方案,并给业务方反馈。这个阶段就是预研的阶段,技术 leader 得到需要预研的工作后,可以通过兴趣让团队成员领取。这个阶段的预研工作通过空余的时间,通过兴趣驱动,需要设点一个时间范围(Time Box),截止时间一般在下个迭代的计划工作之前。技术阶段需要给出结果,这个结果可能是肯定的,也可能是否定的,还有可能需修改业务需求。总之,业务方可以拿到这个结论进行下个迭代的计划。

实现一代 。如果预研结果表明方案可行,进入迭代开始交付,开始前需要技术 leader 组织团队评审,并进一步得到更为细致的方案,比如:数据库设计、API 设计、数据迁移方案等。预研工作和实现工作最好是同一个人,如果无法做到,可以在实现过程中由预研工作的人指导。

技术预研的输出

技术预研往往可以看出一个开发者的专业性,不好的输出在人的脑子中,只要一个影子,而好的方案需要考虑到方方面面。

下面有一个清单供输出技术预研结果时使用,推荐使用 Markdown 文档记录。

demo 原型。原型的意义在于可以快速实现,没有当前项目的包袱,比较灵活。原型法是一个比较好的工程方法,业务原型有 Axure 线框图,技术原型则需要做一些小的 demo 去验证可行性。比如支付、人脸识别等,集成到业务代码中需要的成本和 demo 完全不同。
落地方案。需要考虑如何将原型落地到业务代码中,需要设计数据库、API、流程图等。还有落地的成本在和现有的逻辑结合,以及老数据的迁移和兼容问题。有一些技术方案还需要增加特性开关,考虑方案失败如何回退。

工作量。 需要给出具体的工作量,用于迭代排期。
风险点。在预研时,原型毕竟不能考虑所有的情形,可以给出一些风险点,便于团队决策,如果有备选方案就更好了。

任务拆分。 用于在实施中如何分布实现,好的任务拆分也可以工作量估算更准确,风险更小。

more >>

如何做笔记?

笔记的作用是让知识放心丢下,放空自己,但是又能及时的捡起来,减少自己的心智负担。一份好的笔记可以让知识简化、压缩,一目了然,然后在一段时间后在需要的情况下能轻易的捡起来,做到能放能收的程度,起到整理学习内容的作用。

最优秀的笔记可以做到将一本书压缩成一张纸或者几页纸,从而轻易的回忆、联想出全部内容。压缩的过程需要将书本的知识转换成自己的语言,只有自己的语言才能记住。压缩成一页纸是理想情况,一些专业书籍往往内容非常多,可以一个章节压缩成一页纸。但是一些畅销书有很多废话,只能提炼出非常少的东西,一页纸都用不上。

自己语言的标准是能轻易复述一遍,不是背诵,能用自己的话把这套知识讲通,说明完全理解了这套知识。这就是费曼学习法的精髓,费曼学习法并不是一定要将自己的学到的东西做成 PPT 讲授给别人,而是说需要将知识内化、提炼、复述,讲给别人只是检验复述能力的一种手段。

从这个角度看待笔记的话,做笔记的过程比最终的笔记更有价值。做笔记就是在复述压缩内化。阅读别人做好的笔记毫无意义,有时候会因为信息被裁剪弄得一头雾水。得到 APP 中拆书的本质就是分享笔记,所以这些笔记看起来学到了知识,其实只是感觉自己学习到了。

好的笔记有几个要求:

  1. 用自己的语言来描述知识,而不是照着书本的内容抄写。复述是背诵的前提,不是自己的语言很难记忆。
  2. 用线条串联知识点,形成知识网。
  3. 压缩信息,结合自己的上下文保留关键信息。
  4. 最好用图形的方式做笔记。我喜欢用格子背景的 keynote 来记笔记。

笔记可以做很多事情。除了整理学习内容之外还可以记录自己的想法。每天将自己的想法记录下来,每个人都有一些好的想法迸发出来,养成记录想法的习惯可以收获很多。一些作家就时常带上小本子和笔,有灵感时就记录下来。元末陶宗仪外出时有灵感就写在树叶子上,汇集成《南村辍耕录》。

通过把想法写下来可以大大降低自己的思维负担,更适合深度思考。每天写想法的时候可以深入多挖一点,多问一些问题,多问几个为什么?从问题驱动,深度思考。比如:

  • 为什么书店摆出来的书和图书馆不一样,说明了什么?
  • 我们能否从中得到启发?
  • 从中能得到什么价值?

从这些问题中可以得到,书店是面向大众消费者人群的。为了赚钱,选择的都是消费者喜爱的书籍,从而可以了解到大众想要的是什么。根据观察,现在书店往往不再有专业书籍,更多的成功学、简单的心理学等更多的畅销书。在书店看书的时候,需要注意这些书专业性并不强,更多的是科普作用。

通过这些想法加上深度思考,这样可以拓展出一篇小文章,这种短文作为自己的心得总结还是很有价值。不过需要注意不要花费太多的时间,丢失自己当前的工作目标。不要过于在乎这些小文章的连贯性,很多看似没有连贯的东西,时间久了就会连贯起来。这就是量变产生质变。

记笔记还可以提高学习力。笔记是学习中很重要的一环,并不是简单记录下知识点,而是对知识再加工。最好不要边学边记,如果是课堂上这样做会丢失学习的注意力,如果是看书学习,会难以同时兼顾阅读和记录。

记笔记前应该先大量获取知识作为输入,如果是课堂就记录一些关键词,如果是看书就用记号笔做标记。完成输入后,理解、联系、类比这些知识,并用自己的语言整理成笔记。尽可能压缩这些信息,便于记忆。

完成笔记后,在必要时回顾它加强记忆,比较好的方式可以是在睡前通过类似闪卡的方式记忆。在记忆时就能体会图形化笔记的好处了,通过图形化和线条的联系,可以在大脑中通过空间位置检索知识点。比如,我在学习《八段锦》时做了一页纸的笔记,将八段锦的八个动作和人体的空间位置联系起来,很容易的记住了八段锦的八个动作。

所以,好的笔记可以帮助整理学习内容、记录日常想法、提高学习力的作用。用图形法、压缩信息的方法记录笔记可以结构化的表示知识点,从而帮助记忆和降低心智负担。

more >>

敏捷过程中的软件持续建模

敏捷方法追求单件工作流和响应变化,需求按照故事卡拆分,容易造成业务模型、架构设计碎片化。

在按照敏捷方法运作的团队工作时,比较麻烦的事情是怎么继续坚持软件建模的这些实践。BA (业务分析师)们按照迭代把分析好的用户故事给到开发,开发会按照情况认领任务并开始开发。

因为每个用户故事都是单独计算工作量,有单独的生命周期,所以使用用户故事来描述业务需求,会造成业务需求的整体性缺失。

但是,软件开发的本质是知识性工作,如果没有整体性设计的过程,软件架构会很快腐化和混乱。

软件建模,在软件开发过程中指的是数据库设计、UML 图例的更新、架构的调整和领域对象的设计。建模的专业性,往往决定了一个项目的成败。建模清晰的开发团队,效率和开发成本都会小很多。

软甲项目很小时,不太能体会到建模的优势,当业务复杂到几十上百个数据库表、几百个用例、7-8 个服务时,没有有效的建模,软件项目将无法继续。

因此,必须想办法既要敏捷迭代式的开发,也要持续维护模型的完整和一致性。

解决敏捷项目中整体性设计的问题有几种运作方法:

  1. 将用户故事拆解粒度变大
  2. 固定的人认领批量的用户故事
  3. 在迭代期间增加软件设计和持续建模的过程
  4. 增加专门的软件建模任务卡,并排在每个迭代前

1、2 两种方法可以看做一体,是对当前的敏捷过程调整,对敏捷的一些实践进行裁剪。固定的人批量的认领一些任务,由一个人来负责整体性设计。

3、4 两种方法是我从一种叫做 RUP 的软件开发方法中受到的启发,对敏捷过程进行改进。每个迭代都应该进行模型的修正,并持续建模。

软件建模,也需要迭代更新,这就是持续建模。

持续建模方法

其实 UML、ER 图等常用软件建模的方法使用起来都比较简单,也很容易学会。但是实际开发过程中很少看到有团队再认真的画 UML 相关图形来设计对象,也基本上很难看到大规模使用 E-R 图的情况。

原因很简单,这些图形难以随着代码和业务的变更而同步更新。如果一个软件模型不能正确的表达业务逻辑和真实的架构情况,团队就很难有动力去更新它了。软件建模大多数都发生在项目启动的时候,以及大规模重构的时候,难以持续整个软件周期。

背后的逻辑分为技术、开发过程两个方面:

  1. 从管理上,敏捷方法中每个迭代周期没有一个过程留给去做架构调整、更新模型这些事。
  2. 从技术上,模型的更新成本比较高,修改代码时需要记得同步更新模型,尤其是一些图形的方式更新就更麻烦了。

简单来说,模型的创建很简单,但是保持和代码同步更新很麻烦。这也是很多代码生成器难以发展的根本原因,使用代码生成器会造成一旦手动修改部分生成的代码,就无法再使用代码生成器了。

RUP 方法

先说从开发过程上解决每个迭代模型更新和架构调整的问题。

RUP(Rational Unified Process),即 Rational 统一过程。它是一种比较重的开发方法,并提供了一整套软件工程相关的文档、规范,以及相关的工具。

正是因为 RUP 比较重,需要每个团队的项目经理根据自己的情况进行裁剪,所以在竞争中不如敏捷方法。我们不必学习 RUP,只需要提取出 RUP 中有价值的的方法和实践到敏捷开发中。

RUP 将开发过程划分为了初始阶段、细化阶段、构造阶段和交付阶段 4 个阶段。RUP 按照迭代的方式运行,商业建模、需求、分析和设计、实现、测试、部署 6 个步骤,每个迭代都有这 6 个步骤。

RUP 和敏捷的不同是,敏捷强调单个用户故事的流动(WIP),RUP 可以理解为每个迭代内实际上还是瀑布的方式在运作。

其中:

  • 商业建模和需求的过程为 BA 或者产品经理输出原型图、用户故事、业务用例等
  • 分析和设计步骤为开发人员进行系统设计的过程,前期迭代为初始话系统设计,后期的迭代为修正系统设计的过程。系统设计的输出为 E-R 图、UML 中的类图等整体性设计。

RUP 的几个步骤都发生在同一个迭代。敏捷的一些实践,实际上是穿插完成的。

比如以两周一个迭代为例,前一个迭代的第二周,BA 们应该开始为下一个迭代的工作进行业务分析工作,完善功能的细节和约束条件。迭代的末尾 2 - 3 天应该进入封包整体回归和修复 bug 阶段,可以开始下个迭代的系统设计和模型修正。

img

代码化的 UML 工具

再从技术的角度来说,如何持续维护模型。

表达模型的工具很多,Microsoft Visio、draw.io、starUML 都是比较好的选择。但是,持续建模要求每个迭代都对模型进行修正,因此模型最好能通过版本管理工具管理。

在调研各种绘图工具后,能通过代码化的方式表达 UML 图形的工具不多,其中有 markdown 的 UML 拓展、plantuml。实际使用的情况来看,比较建议使用 plantuml。

plantuml 可以绘制基本的时序图、类图等,以及架构图、E-R 图等非 UML 规范的图形。在实际工作中,我们绘制的图形中,有 5 种比较常用于表达软件模型:

  • 用例图
  • 类图
  • 时序图
  • E-R 图
  • 架构图

用例图、时序图在分析业务逻辑时使用,架构图、E-R 图在做整体性设计时候使用,类图则往往用于具体实现阶段。从维护成本的考虑,保持架构图和 E-R 图持续维护,其他图例在必要时使用即可。

使用 plantuml 非常简单,只需要安装 Java,并现在一个 jar 文件就可以将文本的 UML 代码转换成图形,还可以借助一些工具进行代码生成。 除时序图和活动图以外的图,还需要需要安装 Graphviz 依赖。

以 Mac 为例,安装 graphviz:

brew install graphviz

下载 planuml :

wget https://nchc.dl.sourceforge.net/project/plantuml/plantuml.jar

用官网的例子绘制一个时序图,将下列文本保存为 sequence.txt。

@startuml
用户 -> 认证中心: 登录操作
认证中心 -> 缓存: 存放(key=token+ip,value=token)token

用户 <- 认证中心 : 认证成功返回token
用户 -> 认证中心: 下次访问头部携带token认证
认证中心 <- 缓存: key=token+ip获取token
其他服务 <- 认证中心: 存在且校验成功则跳转到用户请求的其他服务
其他服务 -> 用户: 信息
@enduml

然后用 java 执行 plantuml.jar 即可运行,并得到序列图。

java -jar plantuml.jar sequence.txt

总结

为了解决敏捷项目中,业务模型和架构碎片化的问题,可以通过开发过程和技术两方面进行优化。

过程方面,可以在敏捷过程中加入建模的环节,实际上在一些大的公司类似于方案评审环节。技术方面,可以尝试改进模型的维护方式,竟可能的将模型简化,并通过代码化的方式持续演进。

参考文章

more >>

必须教给孩子的几种思维模型

写给以后的小孩,也写给现在的自己。

清单思维

提高效率最简单的思维,列一个清单并完成它。这个思维已经成为很多人的默认选项,拆任务、贴便签,然后逐个突破。

比较有意思的是,网上还有清单思维的课程,和很厚的书籍,其实大可不必。很多培训都只是为了参训的人感觉学到了东西,其实过段时间就忘了,很多操作方法过重反而不利于习惯的培养。

列清单很简单,可以无处不在,最根本的好处是让清单把脑袋中的内容腾下来,放空大脑的同时,也可以防止遗忘。写在清单上 ,不用随时惦记这,把精力都留给要做的事情。

一场会议,一次培训,甚至做一个菜,或者写一篇文章都可以使用清单。

很多职场人士都无法离开清单,可以使用滴答清单、便利贴或者在笔记中列一个清单都可以。我也做了一个简单的网页,可以存储清单,打开就可以使用。http://todo.printf.cn/

模型思维

世界是复杂的,要想理解一件事情必须要进行简化,简化后找到和自己知识背景有联系的事物进行类比就能理解。

抽象的概念经过简化,并找到一个经历过的事物进行类比,才能得到理解,这就是人类认知的基本逻辑。

如果给幼儿园的孩子讲解 -1 * -1 = 1?

用双重否定等于肯定,孩子是理解不了的。一个方法是 -1 代表的是向后转,-1 * -1 意思是向后转,再向后转,最后面向了哪里呢?

这就是类比,类比不是准确,但是可以让人理解抽象的概念。

用手指学习数数,手指就是模型,而数字只是人们创造出来的概念。

数学老师让我们理解什么是集合,集合很好理解,就是一堆事物的整体。那么什么是空集呢?空集就是爸妈给你了一个钱包,但是拿走了所有的钱。

辩证思维

为什么所有人都觉得《老子》非常牛逼,非常有道理?

《老子》中充满了朴素的辩证思维,高下、前后、美丑,随处可见的辩证思维让这本书闪闪发光。“有无相生,难易相成,长短相形,高下相倾,音声相和,前后相随。” 这类的描述非常多,也非常具体。

老子的辩证思维是一种朴素辩证法,辩证唯物主义又进了一步,毛主席的《矛盾论》就是其中的代表。

辩证思维,可以让人从不同的角度思考问题。比如有人给你推销保险,会给你讲所有有关保险的优点,根据辩证思维,有好处就有坏处,它们是客观存在的。

通过辩证法,可以减少很多上当受骗的机会。

逻辑思维

逻辑思维不是罗振宇的《逻辑思维》,而是指哲学的逻辑思维。

我们无论是认识世界还是和人辩论,都需要遵守一些思维规律,掌握了这些思维规律,就可以让自己的逻辑更为严密。

现代逻辑思维有三大规律:

  1. 同一律。同一段论述中,概念的内涵和外延不能发生变化,否则就是偷换概念。比如 A: 学生可以同时学习数学、语文。 B: 不对,人不可以同时学习两门课,比如你不可以同时看两本书。这里的“同时”概念发生了变化,无法继续讨论了。
  2. 矛盾律。在同一段论述中,不能出现两个相互矛盾的说法,比如“我有一个任何矛也无法刺穿的盾,还有一个可以刺穿任何盾的矛。”
  3. 排中律。排中律指同一个思维过程中,两个思想不能同假,必有一真。比如老妇人拜菩萨,求天不要下雨,不然买扇子的女儿没有生意,一会儿又求天要下雨,不然另外一个卖伞的女儿也没有生意。

掌握逻辑学三大规律,可以避免一些诡辩,可以跟正确的理解道理。

目标思维

人有时候无法专注,并不是想偷懒,而是缺少目标思维。

用目标思维随时提醒自己当前工作的意义,避免写着文章搜索材料的时候,进入八卦专区灌水去了。

目标思维的建立是通过各种问题,比如 5w1H 方法。

  • why(为什么)
  • What(是什么)
  • Where(在哪儿)
  • Who(谁)
  • When(什么时候)
  • 1H是指:How(如何)

通过 5w1H 方法,可以让自己做的事情更有意义,具体,而有焦点。

同理心思维

同理心思维是人与人相处的思维,通俗来说就是同情的能力。

比如说出某句话会对对方造成反应?

某个行为对方会产生如何的感受?

产生同理心思维的方法是“精神分裂”,想象自己是另外一个人 B,旁观自己的处境 A。作为 B 去考察 A 在某个场景下的行为,以及对方的额感受,就可以获得同理心。同理心分为个体、群体同理心。

同理心思维不是为了一味的照顾、迁就别人的感受,而是应该作为一个旁观者 B,如果利用同理心影响、感知对方,并在人群中做出适当的社交行为 。

more >>

35岁过后做什么呢(二)?

去做了种植牙齿的手术,回来后除了肉疼,还有心疼。华西的教授技术完全没话说,唯一一点不好就是贵。

为了打发牙疼的时间把 《35岁过后做什么呢?》中没有说完的东西再写一点。

医院的见闻

给我治疗的人是一个教授,因为之前在小的牙科医院吃过亏,即使价格比较贵也选择了华西口腔。

华西口腔可以说是世界上前列的口腔医院,国内大部分的口腔医院创始的过程都和华西口腔有关,华西口腔真的是博士遍地走,研究生不如狗。

在我完成治疗后,楼下在自助收费机器旁边帮助患者操作自助机的志愿者都是川大的研究生。

在拍摄牙片 CBCT 时,看到了墙上关于放射科的介绍,研究生学历,3-4个头衔一样也是每天重复的拍摄 CT,被患者骂。

在收费窗口,收费员是一个 40 岁左右的中年人,不耐烦的叫我把处方递给他,从他的神情来看,对 35 岁后的生活并不满意。

牙医的工作模式是围绕着主任医师为中心,以助理医师、护士、规培生为主要组成的团队工作的。受人尊重的当然是主任医师了,华西口腔种植科的主任医师只有十多个。

曾经遇到过一个华西口腔的博士,给我吐槽华西口腔的工作压力相当大,可以类比于做技术的在华为,做咨询的在麦肯锡。如果在华西口腔不能持续上升,就会被淘汰,机会留给后面的博士生。

按照现在的说法,内卷的厉害。

焦虑来自不恰当的类比

我们的焦虑来自于比恰当的对比。

虽然 IT 行业不敢膨胀到和医疗行业做比较,但是看看医疗行业的情况。我们只看到了主任医师这种级别的人,每年拿到很多越来越吃香。在 IT 行业中能与之对比的是计算机学院的教授和老师,拿着国家的科研经费,做着自己的校企合作项目,一样越老越吃香。

顶级的和顶级的类比。

一般的和一般的类比。

对于普通的程序员而言,能类比的其实只是私立医院的医护团队中的一员。

对于主程序而言,能类比的其实只是私立医院的某个科室主任医师。

对于软件公司的财务人员来说,类比的就是医院的收费员,甚至拿的更多。

对于软件公司的测试人员来说,和护士、CT医生也差不多。

我们有时候犯得错误是,盯到的只是,容易看到的一面。主任医师很亮眼,助理、护士和收费员,谁会多注意呢。

more >>

35岁过后做什么呢?

中年危机

35 岁后程序员就要失业了。

程序员的中年危机说法从我入行的时候就有了,而且还是互联网行业方兴未艾的 7 - 8 年前。

即便如此我还是义无反顾的投入到了程序员大军,虽然当时也有进入体制的机会。选择程序员的原因其实很简单,我希望能从网络上赚到钱,然后找一个小城市生活。

虽然这个理想还没有实现,但是已经被被同学嘲笑过很多遍。高中的时候我就告诉同学,等我赚到 10W 能买套房子的时候,我就拉一根网线回农村,一边写代码一边种地。

回过头来一看,除了医生、教师和公务员之外,哪一个行业没有中年危机呢。

一份特别的 35 岁计划

我给自己的制定了一个 35 岁后被软件行业淘汰后的打算。听起来很蠢,但是又具有理想主义。

我想成为一名英语老师,而且是回到出生的小镇的英语老师。如果被软件公司淘汰后,在回到小镇上,通过网络应该能接到一些小活儿,混口饭吃应该没有问题。

如果我英语还行的话,就能远程的接到一些海外的任务,通过汇率的放大效应,不需要特别辛苦就可以维持一个简单的生活。

我是一个喜欢阅读的人,在我们的镇上甚至没有一家书店,只有卖教辅的文具店。

甚至没有一个阅览室、没有图书馆,只有几家破烂的网吧和台球室。

如果回到小镇上,寻找到一处便宜的场地或许很容易,然后在网上的二手书店采购数千本图书,其实也就是那些文艺青年去趟西藏旅游的费用。如果能得到政府的支持就更好了,没有也无所谓。

我想把这个图小型的图书馆或者叫做阅览室免费开放给全镇的孩子们,也包括我以后的孩子。当慢慢有人知晓这个地方时,我就来教授孩子们英语口语,然后把书籍和英语的世界带给闭塞的西部乡村小镇,并维持这个“图书馆”的运转。

我从小英语很差,因为没有一个真正会说英语的老师,也没有一个不大不小的图书馆,令人悲伤的是 20 年后那个地方依然没有。

焦虑是怎么来的

我曾经有一段时间非常焦虑,不过好在只有一小段时间。

那段时间我在从事咨询和培训工作,偶尔回归到一个开发项目之后,发现自己的手非常生疏,甚至对一些编程中的常用语法都忘记了。

那段时间非常焦虑,疯狂的学习,复习之前的知识,过了大概 2 个月,慢慢的感觉又找回来了。

编程,对于一个软件工程师来说,应该是每日必须练习的,一点都不能丢下。

当我的双手在键盘上能写出自己满意的代码后,焦虑感消失了,我知道不管怎么样,都能找到一家公司能混口饭吃。

焦虑的来源是多方面的。

一些是对未来的害怕。程序员的高溢价很难持续维持,总有一天是会跌回去的,当人习惯了往上走的时候,难以承担往下走。害怕自己没有足够的实力,面对未来的成长。因为软件工作的变化过于迅速,所以这种压力无处不在。其实相比之下,压力更大的是做销售、培训、市场和运营的,程序员的工作至少还能掌握手上,而其他行业的工作都是靠天吃饭。

一些对自己的不满足。对自己的不满足往往都是和别人对比出来的,比如同学、同事和朋友。觉得自己没有什么成就,工作多年也没有什么自己的作品,非常的颓废。对于这一点 ,我们需要知道的是,人的能力是有边界的。无论鸡汤喝的再多,寻求能力边界之外的事情,都会很痛苦,何不做好自己的能力边界的事情呢,让能力强的人去做更多的事情。

看着自己碗里的,盯着别人的锅里的,忘记把自己的饭吃好。程序员就是一个为别人做软件的手艺人,何必天天操心其他老板的生意呢,做软件也是一门生意而已嘛。

最后一些是消费的升级和自我膨胀。程序员是比其他行业多赚了那么一点钱,但是其实也不多。如果程序员被套上了“中产阶级”的圈套,然后进行所谓的消费升级,消费着红酒、咖啡和每年几次的旅游。由俭入奢易,由奢入俭难,消费升级了,就很难降下来。不膨胀,存好钱,过好原本的日子,“手里有粮,心中不慌”。

程序员的降级论

2012年的时候,阅读到一篇文章,是 meditic《降级论》。其中心思想是,如果程序员或者说其他高智力密集性的人,往技术含量更高的领域进军并不一定有优势,而且会混的灰头土脸。

如果转变思路,程序员的逻辑思维能力、分析能力和学习能力在其他领域发展,会得到意想不到的效果。

在 IT 行业里面处处是精英,这些人都是现在说的天龙人,进外企、考CPA、做咨询、云计算各种高大上的领域,用上了各种牛逼的思维。一步一步升级后才发现,身边到处都是精英,自己一点优势没有,还是拿着打工的钱。

反而是小学同学,毕业就在某个作坊跑腿,然后有了自己的作坊,慢慢的那个行业混的风生水起。在高级圈子里混久了,除了焦虑、秃头、挫折,几乎没有什么收获,还不如把高级圈子里面的高级思维带到其他领域去。

程序员如果去培训班教教数学那应该也是很厉害,如果开个水果店,利用自己的写程序能力,说不定也能运转的高效。在追求升级的过程中,会做的很累,因为你在每一次升级都是进入这个级别中的最低线。meditic 的比喻是,升级为大学生的中学生,实际上并不是大学生,而是刚刚完成升学的中学生,他的能力还只是中学生,是无法和大学生竞争的。

如果人生一直在升级,那么就没有时间和精力做点自己该做的事情。不要一直升级,而是应该找到自己的合适的级别,并做到最好。

查理芒格说,“你永远无法赚到超出自己认知以外的钱”。对我们而言,如果一直在升级,就一直无法去把这个级别的事情做到最好。

为什么不尝试降级呢?

more >>

作为 Tech Lead 应该操心什么?

认清 Tech Lead 的位置

对于软件工程师做技术经理是一件好的事情,可以从另外一个更高的角度观察自己现在正在做的事情。

成长为技术经理并不是一件很值得得意的事情,我接触到的技术经理压力都非常大,所以很多人还是愿意做一个纯粹的开发。软件开发行业很其他行业有一个不同的地方就是,会出现 Tech Lead 的角色没人愿意做的现象。

那么 Tech Lead 所处的位置是什么呢?

实现领导的开发目标。软件工程师的职责就是为老板构建可用的软件,至于老板拿这个软件在市场上赚钱和赔钱是市场和老板需要关心的事情。Tech Lead 能够把控产品经理的需求,并对老板给出估算,这事儿靠谱还是不靠谱,需要多久的时间。对老板来说,他不关心具体的技术实现和细节,Tech Lead 能帮他拿捏好,这需求能做还是不能做,能做的话最终能保证出活儿。对老板来说,靠谱是最重要的。

安排好项目的进展情况。如果没有项目经理专门带队做软件研发,Tech Lead 也需要关心项目管理的事情。项目管理并不是发布任务,设定目标,保证目标的达成这么简单。要需要考虑技术选型、技术决策和移除项目的困难点和阻塞点。

关心好团队的成员。 开发人员往往除了工作挣钱之外还有一些自我成长的诉求,所以还需要关心人员的成长,以及帮他们搭舞台唱戏。Tech Lead 不仅仅是给他们分配任务的人,还一定能引导他们的兴趣,尽可能帮他们找到资源,分配给他们感兴趣的任务。毕竟成就他人就是成就自己。

所以在 Thoughtworks 把 Tech Lead 这个角色赋予了四种能力:

  • Expert:是这个项目和产品的核心专家,必须保证有足够的业务知识和技术知识,能把握项目上的一些关键问题
  • Manager:能拆分任务,讲活儿分给其他同事做,并且管理项目的进展
  • Coach: 具有发展他人、团队的能力,能给其他团队成员提供指导和帮助
  • Leader:知道如何用正确的方式达成目标,激励人,知道团队的业务和技术目标

从这四个方面来看,操心的事情不少,后面部分整理了一下作为 Tech Lead 经常要操行的事情。

应该操心什么事情?

技术选型和决策

  • 能根据业务需求做出合理的技术选型
  • 技术选型能满足未来一段时间的业务和架构演进目标
  • 技术选型需要考虑成本
  • 技术选型需要考虑团队人员素质和背景

软件架构和搭架子

  • 服务设计和领域划分
  • 数据库设计
  • 分层架构和包结构的设计
  • 错误码规范和定义
  • API 规划
  • 一些常见操作的例子供开发人员参考
  • 日志和监控设计

需求评审

  • 需求是否合理,业务逻辑上是否自洽
  • 交互设计的性价比
  • 设计的一致性,和项目现有的逻辑是否一致
  • 是否会影响安全
  • 是否会造成性能的大幅度降低

基础设施和运维把控

  • devops 基础设施,例如流水线、制品库、跳板机等
  • 建立运维手册,例如定期拨测和数据备份
  • 定期防灾演练
  • 定期检查系统健康状态,密匙、证书、域名有效期
  • 制定上线流程和规范

非功能性需求

  • 关注需求的性能指标
  • 关注兼容性需求
  • 关注容量和弹性伸缩的需求

代码质量

  • 配置静态代码扫描,例如 checkstyle、find bugs
  • 设计团队代码规范
  • 进行代码评审

安全

  • 安全规范,例如密匙的要求
  • 安全建模
  • 定期的安全扫描工具异常处理
  • 预防技术方案中的潜在安全问题,比如数据和隐私泄露

团队

  • 制定团队协作策略,例如分支策略
  • 制定版本策略
  • 制定团队契约,例如 codereview 时间和主持人、技术例会
  • 对团队新人进行 onboarding 和维护团队 onboarding 文档
  • 人员梯队建设、管理人员流动的任务交接
  • 团队矛盾和冲突处理

知识共享

  • 制定团队技术分享机制
  • 制定文档存放契约
  • 更新系统架构图、部署图和数据库模型等项目架构材料

项目管理

  • 工时估算和迭代计划
  • 技术债的评估和分析
  • 项目风险评估
  • 跟踪第三方依赖的时间点
  • 收集项目反馈

more >>

模型学习法

通过模型学习

我不是一个善于学习的人,我的大脑发展并不均衡:喜欢把一些问题载入大脑慢慢思索,但是记忆力非常差。

用计算机来类比的话,我是那种 CPU 尚可但是硬盘极差,以至于很长一段时间内成绩都不是很好,但是遇到喜欢的科目成绩有特别好。于是在读书生涯中成绩总是起起伏伏。

学习,不仅仅是读书时候的事,工作以后大部分人都必须成为一个终生学习者,否则就会被当前的岗位淘汰。我们应该见过各种各样的学习方法,这篇文章不会成为一种新的学习方法,而是总结前人的经验。

在读书和工作后,我使用过各种各样的学习方法,包括但不限于:

  • 思维导图
  • 费曼学习法
  • 联想法学习英语
  • 写作驱动法

这些方法都非常有效,但不得不承认人的学习能力和智力水平从客观上是有一定差距的,有时候别人可以通过一节课学会微积分的基本方法,而我却需要整整一年去理解和消化微积分到底是什么回事。

智力不够,方法来凑。

硬件不够,软件来凑。

于是我了解了各种各样的学习方法,通过寻找方法来弥补理解能力、记忆方面的不足。慢慢的我了解到,这些学习方法只是一种形式,其背后都是在建立一种让人容易理解的模型。

回忆一下我们的幼儿园是怎么学习算数的?老师会让我们用手指来代替数字,并进行计算。一个中学老师把联想、比喻使用的越好,对学生来说学习效果越好。我们天生难以理解抽象的概念,因为抽象的内容是人定义出来的,是建立在他人的知识背景之上的。对从来没有社会经历的高中生来说,没有为资本家工作过的经历,难以理解什么是“资本主义对人的异化”。在无法理解的情况下,就只能死记硬背,记忆力不好的人处于学习的劣势。

联想和比喻是我们理解事物最重要的方法之一,我们研究的对象是需要学习的知识,被用来联想和比喻的事物就是模型。

模型是什么样的?

在我细致的观察中发现,模型有一些有趣的特点。

模型是多样的。能帮助人理解知识的东西都是模型。教学条件好的地方老师会有各种各样的教具,地理课上老师会拿来地球仪,化学课老师会拿来分子模型,生物学老师会拿来人体模型。因为这些模型和教具,在理解一些知识的时候变得更加容易。但是给了我们一个印象,只有摸得着的东西,用塑料、木材等实物制作而成的才是模型。

这限制了我们的思维。中学时,我特别喜欢物理,为了理解物理学的一些知识,偶然间我在网上找到了一些关于物理学公式的 gif 动图,很多难以理解的东西变得容易理解了。

模型生活中无处不在,只要能拿来联想和比喻的东西都可以作为模型,或纸上、或电脑里。实际上数学、物理公式也是数学、物理的模型,而数学、物理则是宇宙的模型。

模型是简单的。因为知识和概念是复杂、难以理解的,因此我们需要通过类比、比喻来理解,如果我们找了一个模型,本身也很复杂和难以理解,可以说这个比喻并不恰当,或者这个模型并不成功。

模型的简单性是帮助理解的基础,所思维导图做得过于复杂还不如不做,简单性随之而来的是,模型都是错的。模型只能帮助理解知识,但是不能代替知识。

六祖慧能说过:“真理是与文字无关的,真理好像天上的明月,而文字只是指月的手指,手指可以指出明月的所在,但手指并不是明月”。对我们学习来说,模型是手指,而知识是天上的月亮。

什么可以当做模型?

书的目录就是一种模型。

我们把思维导图转换成文字版本就会发现,它和书的目录没有什么区别,只不过突出的重点不同。

对于工作后的人来说,大部分学习都是在电脑上完成的,用来承载知识模型的途径很多。

  1. 思维导图
  2. PPT、keynote 或者一些在线画图工具
  3. markdown 的清单

如果不使用电脑,小抄是一种很好的表达模型的方法。我在一些考试的时候发现,小抄不是为了作弊,而是为了让自己对知识有一个全局的印象,并记住关键知识点,以及知识点之间的练习。

最巧妙的记录小抄的方法是使用一张 A4 纸(只能用一张,不能多),把尽可能关键的信息写在这个纸上。如果写不下,说明这个模型太过于复杂,换一张纸重新整理知识点,直到必要的知识点能在一张 A4 纸上呈现才算完成。

操作方法

模型法学习的关键在于知识的压缩和形象化。压缩是为了提炼出最重要的信息,形象化是为了能真正理解知识。

金岳霖先生在《知识论》中提到过很多处理知识的方法,简单来说,学习就是把凌乱的信息整理成有序的知识,并加以实践和应用的过程。

所以模型学习法有几个过程:

  1. 输入
  2. 压缩、理解、内化
  3. 建立模型
  4. 应用

输入

学习首先得有信息来源,不然理解和内化无法发生,即使能发生也是自己空想。最简单的输入过程就是阅读,除了阅读外和别人交谈、问问题都可以。

刚开始学习一个新东西的时候,需要大量的泛读,即使部分内容无法理解也可以跳过,先积累和寻找大量语料库。我的阅读速度一直都很快,也坚持阅读了大量的材料,有时候会发现前面无法理解的东西,读到后面就能理解了,所以不能因为部分无法理解就停滞不前,因为学习是一个整体性的过程。

个人建议在刚开始泛读时候不要过于在乎笔记,这样会让自己的思维被打断,可以用笔做一些标记或者折叠相应的页码。

压缩、理解、内化

当大量的速读和泛读之后,在脑海中可能会建立起来一个对知识的模糊轮廓。这个时候,需要尝试理解学习的知识到底是个什么?这个时候需要寻找能类比的事物,建立联系。比如 DNA 双螺旋结构的分子式非常像两条缠绕在一起的蛇;波尔的原子模型,可以类比为太阳系的行星结构,它的形象和能级结构非常相似。

理解这些知识后,尝试按照上面的说法,做出思维导图或者写在一张 A4 纸上,我一般喜欢压缩到一页 keynote 中,超过一页的内容就缺乏全局观和简单性。

建立模型

压缩后的知识点可以作为模型的草稿,如果没有什么问题其实就可以当做模型了。不过为了让模型起到帮助记忆和理解的作用,可以通过连线建立起元素之间的关系,画出概念之间的关系,这有点概念图的味道。

删减掉无关重要的的信息,尽可能的让模型保持简单。

应用

除了应试的用途外,如果我们学习到的知识不是为了拿来应用,那么知识也没有什么用处。

应用和实践是让知识记忆的更牢固,“纸上得来终觉浅,绝知此事要躬行”。并且我们建立的模型在实际使用过程中会遇到这样或者那样的问题,当模型并不能很好指导实际应用的时候,我们需要进行修正,并重新投入实践。

应用中获得的反馈也可以作为第一个步骤的输入,而且这个输入更为准确和重要。

写在后面

这篇文章相当于也是我对学习方法建立的一个模型,通过写作来检验这个模型发现还是有效的,所以可以将学习过程总结为:输入、内化、建模和应用。

当然,如果这篇文章对你有用的话,你也可以建立一个自己的学习模型。

more >>

如何提高对自己情绪的控制力?

情绪的亏

我吃过太多被情绪控制,大多数人应该都和我一样。

和家人相处中,明明因为一个小事而已,因为自己的烦躁和不安,表达出厌恶和不喜欢,情绪的传染导致争吵。当冷静下来只有发现完全没有必要争吵。

在工作中,因为同事或者客户一些对自己不好的地方,比如客户无端的额外需求。心生抱怨,除了暴露出去不专业的态度而外,对事情并没有好处。

还会给人留下喜好抱怨的印象。当情绪开始主导人的生活时,自己就成了情绪的奴隶,很难掌控生活的的方方面面。在情绪影响自己的过程中,就好像丢失个人意识,处于一种昏头的状态,只有当事后才能发觉自己当时的行为和决策不是在清醒的状态下做出的。

我相信在生活中因为情绪 “失智” 的状态越来越少时,生活也会更加如意。理解情绪其实还会带来收益,有一些岗位,工资中带有一些对抗情绪压力的成分。因为容易挨骂,所以工资低没人愿意来,只能提高待遇,如果意识到只需要抵抗不良情绪就有收益的,心态是不是马上不一样?

本我、自我、超我

情绪一直是心理学研究的对象之一。

虽然弗洛伊德的理论已经不再是心理学的主流方向,但是他的 “本我、自我和超我”理论还是影响着很多的人。在这个模型下,

  • 本我代表着感性、情绪是一个人做出的自然反应。
  • 自我代表着理性、逻辑是一个人做出利弊分析后的反应。
  • 超我在西方世界观中,代表着神性,用中文语境来说就是贤者模式。

对于情绪的机制来说,情绪是客观的,一直存在的,不因为个人意志而转移。只能说在一定程度上可以被控制、消化,不影响人做出理性的决策和行为。

当一个人的自我和超我大于本我时,他就能掌控自己的情绪。甚至能拿起情绪作为武器达到自己的目的。

利用情绪作为武器,是各种故事不错的桥段。

在三国演义的故事里面,诸葛亮骂王司徒,王司徒被激怒,最终情绪崩溃,一口气上不来倒地了。诸葛亮在他的表现里,自我和超我达到了极致,目标明确,情绪稳定,因此能把情绪作为武器使出。

在《让子弹飞》中有多次利用情绪武器的地方。胡万通过激怒六爷,然后利用六爷的正义感情绪,诱导六爷剖腹取粉,这种情绪的利用非常歹毒。最后麻子利用发钱,黄老爷抢钱唤起群众的反抗情绪,又通过枪毙黄老爷替身的方法消除民众的恐惧情绪,最终推翻黄老爷。

六爷的自我弱于本我,麻子的自我强于本我,事情发展的结果也不一样。

识神、元神

在西方世界中有本我、自我和超我,在中国古代也有类似的理论。就是识神和元神。

中文语境下的“神”代表着意识、精神,比如下笔如有神、失了神等说法

  • 识神,识是佛教用语,六识是“眼耳鼻舌身意”。识神可以简单理解为来自感官的感受和一个人情绪的自然反应。
  • 元神,识神之外的意识,也就是不受外界影响下的清醒理智。

古代的修行的人早就意识到识神和元神构成人的意识,所以需要不断排除识神,而让元神显现。

酒色财气都会让元神受到麻痹,识神被放大。所以古人认为在酒色财气之下是做不出正确的判断,并常常有失格的表现。酒色财气这四样东西都是很危险的。

训练

掌控情绪用中国古代的说法就是识神退位,元神显现。用西方心理学的说法就是强化自我、超我,抵抗本我。

作为一个依然被情绪无时不刻掌控的俗人,给不出好的方法,只能拾人牙慧,随便聊一聊。

世俗的历练

想要成为一个能掌控情绪的人,最终处变不惊,需要有来自大量的世俗经历,见的多了,也就看的淡了。人生经历就像一次一次脱敏训练,情绪反应阈值不断提高,也不会那么咋咋呼呼。

所以那些能在酒桌子上还能清醒自如,实现酒局的目标和价值的人多么厉害。见到过一些政商老江湖,喝的半醉不醉,借着酒劲把话说开,但是内心还是极其清醒。

每次经历都会沉淀为宝贵的人生经验,这是任何情商训练难以达到的。

危险性

对抗情绪,是有危险的。

曾经有一段时间陷入一定的强迫症行为,于是触发了我的好奇心,留意身边的人是否也有类似的行为。发现很多人的强迫行为是因为和情绪对抗造成的。

明明是一件很生气的事情,需要装作没什么事,但是从他的表情中知道并不是真的没事。如果细心观察到的话,会发现大多数人抵抗他们的情绪时显得并不轻松。

后来反思让我走出强迫症的原因,其实非常简单。认识到了自己能力的边界 ,不良情绪是客观存在的,到了一个边界就不再是我的能力可以完全控制的了。撑不下去还强撑,不仅对事情于事无补,而且对身心造成极大损害。

所以学会放弃成了治疗强迫症最好的方法,用流行的话说叫做和内心和解。

more >>

软件工程师的"吵架"文化

吵架的团队更有生命力

看一家软件公司是否靠谱很简单,在开放式的办公室中有没有随处可以使用到的白板就行。

一个好的软件开发团队往往看起来没那么”和谐“,程序员和产品经理之间,程序员和程序员之间是不是会发生”争吵“。好的软件公司没有不吵架的,不吵架的软件公司要么大家都在划水,要么大家为了维护 ”职业形象“处处小心翼翼不敢献计献策。

刚开始和华为的人合作可能会有一些不适应,他们说话的方式简单而直接,往往让人不知所措。这种直接的背后是一种撸着袖子干的企业文化,把”职场文化“变成了团队协作。

软件公司吵架不是管理不善,往往代表着开放、包容的文化,以及团队成员心无芥蒂的通力合作。

回想一下,我们是不是往往都是和陌生人和和气气,和信任的人才大大咧咧呢。

文吵和武吵

不过吵架这事儿还有一些讲究。在公司吵架这事儿分为文吵和武吵:武吵是比谁的嗓门大,谁说话的分量重;文吵讲究的逻辑、推理和归纳。

而武吵和文吵各有用处。

武吵需要的是一个敢于担责,能在关键时候拿出主意的人。在很多讨论会中,群众容易陷入只对自己认知匹配的细节,然后各执一词,都很有道理,也各无过错。这个时候,文吵的逻辑难以派上用场,因为现实世界中没有完美的、正确的方案,只有不那么差的方案。那就需要武吵的人发挥他的影响力,给团队拿个主意,先结束这次讨论,得到一个基本可用的方案来,才能让事情继续。

文吵则不一样,软件开发毕竟是一门科学,大多数情况下需要用到逻辑,通过分析、归纳、总结得到方案。需要收集足够的信息,给出足够的理由,并对方案中的概念给出清晰地定义,最后通过因果分析,证明自己的想法。在大多数情况下,我们更多的需要文吵,通过分析和证明解决遇到的问题。

不管是文吵还是武吵,都需要讲逻辑。只有当团队对这些基本的逻辑规律达成共识的时候,”吵架“才能高效,并且避免落入谬误的陷阱。

逻辑学的三个基本规律

逻辑学的三个基本规律可以让吵架更加准确,避免无意义的争论,减少逻辑矛盾,让吵架有所产出。这三个重要的规律是:同一律、矛盾率、排中律。

同一律

在同一段论述(命题和推理)中使用的概念含义不变,这个规律就是同一律。形式化的表述是 A -> A。

”概念“在逻辑学中的意义非常重要,概念有两个逻辑学规律:内涵和外延。内涵指的是这个概念区别于其他概念的本质属性,例如大熊猫是指的生物学中某一个物种。外延指的是这个概念所能描述的事物的范围,比如白马比马这个概念外延要小,是不同的两个概念。

所以白马非马的本质争论在于自然语言的不确定性:

  • 从概念上说,白马这个概念不是马这个概念。所以白马非马。
  • 从谓词(”是“ 这个谓词)逻辑来说,白马这个概念代表的事物集合属于马这个概念代表的事物集合。所以白马是马(白马属于马,但是白马这个概念不是马这个概念)。

同一律描述的是在一段论述中,需要保持概念的稳定,否则会带来谬误。我在大学辩论赛中利用了这个规律,赢了一次辩论。

当时的论题是”网络会让人的生活更美好吗?“,两个论点主要的论点是:

  • 网络让人们的生活更方便。
  • 网络让人们沉溺虚拟世界。

我们选择的论点是 ”网络让人们的生活更方便“。在辩论赛的前期,另外一方为了论证 ”网络让人们沉溺虚拟世界“,描述了打电话、写信也可以让人生活很美好,并不会沉溺。这刚好落入了我们的逻辑陷阱。我们指出,邮政、电话网络也是网络的一种,对方的逻辑不攻自破。

这属于典型的 ”偷换概念“,我们偷换了“计算机网络”和“网络”这几个概念。

矛盾律

矛盾律应用的更为普遍,几乎所有人都能认识到矛盾律。它的含义是,在一段论述中,互相否定的思想不能同时为真。形式化的描述是: ”A 不能是非 A“。

矛盾律这个词的来源就是很有名的 ”矛和盾“ 的典故,出自《韩非子·难势》中。说有一个楚人卖矛和盾,牛皮吹的过大,说自己的盾在天底下没有矛能刺破,然后又说自己的矛,天底下的盾是不能穿透的。前后矛盾是一个众所周知的逻辑规律,但是并不是一开始马上就能看出来,需要多推理几步才能看出来。即使如此,在同一个上下文中,出现了矛盾的逻辑论述也被认为是不可信的。

具有矛盾的论述有时候又被称为悖论。尤其是宗教领域充满了大量的悖论,例如是否存在一个万能的神,做一件自己不能完成的事情。

矛盾律的用处可以驳斥不合理的论断,也可以用于反证法。在软件开发过程中,我们时常遇到这种情况,需要开发过程中才能发现矛盾。这个很难避免,除非有充足经验的工程师。

需要注意的是逻辑学中的矛盾律和毛泽东思想中的矛盾论不是一回事,前者是逻辑学规律,后者是辨证唯物的一种方法。

排中律

排中律是逻辑规律中最难理解的一个规律。它的表述是:同一个思维过程中,两个互相否定的思想必然有一个是真的。用形式化的表述就是,A 或者非 A。

排中律的意义在于,明确分析问题的时候不能含糊其辞,从中骑墙。比如有人讨论:人是不是动物。不能最终得到一个人既是动物又不是动物,这种讨论是没有意义的。

比如在一次技术会议中,需要选择使用的数据库,只能使用一种数据库。如果采用了 MySQL 就不能说没有采用 MySQL。

排中律看起来好像没有意义,但具有非常大的价值,让讨论最终有结论,而不是处于似是而非的中间状态。

如何诡辩

在争吵中,人们会下意识的引入谬误,从而主动或者被动的诡辩。诡辩的方法非常多,下面聊几个有意思的诡辩方法,认识到诡辩的存在,让吵架的输出更可信。

偷换概念

偷换概念是一种利用同一律的诡辩方法。往往是利用一个词语的多义性来制造诡辩,这种例子相当常见,再一次日常对话中:

朋友:为了让自己的判断和认知更为客观,我们应该同时学习多个学科的东西。

我(故意抬杠):人不能同时学习多个学科的东西。

朋友:为什么,学生不都是同时学习数学、语文、英语么。

我:你现在正在看手上这本书,能同时看我手上这本么。

朋友:。。。(感觉被套路)

我偷换了概念,把 ”同时“ 这个词的时间精度调低了,导致这次对话变了味。

偷换概念在生活中无处不在。《武林外传》

中的秀才利用 ”我“这个概念的偷换,让姬无命莫名其妙并自杀了。

相关性不等于因果性

这个是一个不得不提的诡辩手法,我们从小深受其害。

最经典的例子是,很多父母信佛,然后娃高考的时候天天去求神问佛。如果小孩考上了大学,那么就是拜佛的功劳,如果没有考上,那就是小孩不努力。多么完美的逻辑闭环,完全无懈可击。

同样的桥段在各种电视、电影中存在。某一伙人闯入了一个村子,然后这个村子发生了瘟疫,群众认为是这些人带来了不详。

程序员圈子也会有类似的议论,因为大公司都用的 Java 而不是 PHP,所以 PHP 是一个垃圾语言,我们要成为大公司,所以要把 PHP 换成 java。所以很多公司明明可以苟一下,然后因为折腾死掉了。

我们需要时刻记住,相关性不等于因果性,才能认识到一些微妙的逻辑关系。

因果倒置

”可怜之人必有可恨之处。“ 这是很多人挂到嘴边的话,支持者甚多。

我小的时候对这句话记忆深刻。小学的时候被年长的同学欺负,后来因为打架老师知道了,其他同学都表明我是个被欺负的可怜鬼,老师还是对我们都做出同样的处罚。

说出了一句举世名言:“为什么欺负你,不欺负别人”。

为什么只欺负你,不欺负别人,所以你也不对,同样要受到惩罚。这是典型的强盗逻辑,从结果推导出原因,但是这个原因并不成立,因为我们知道原命题为真,逆命题不一定为真。

归纳法的局限

逻辑学上把个别的知识推广到一般的知识规律叫做归纳推理。归纳推理是一种朴素的认识方法,在逻辑学中,归纳推理有其意义,但是需要注意的是逻辑学从来没有把归纳法得出的结论当做真理。

归纳法的问题和类比谬误类似。古人认识的到了一个规律,鸡叫三遍天会亮,但是后来出去旅游发现其他地方的鸡不是这样的,真的是应了那句,“东方不亮西方亮,黑了南方有北方。”

中国太大了,甚至二十四节气的规律都不能适用于每一个地方。归纳法只能有限的反应某种规律,不能广泛、绝对的得到真理,也不能从个体推出一般。

算命先生希望从四柱八字、面相分析、掌纹、笔迹这些中归纳真理,如果认识到归纳法的局限性,就不会平白无故交这些智商税了。

责任转移

证明神存不存在,保健品有没有功效,壮阳药有没啥作用是科学界三大难题。

从逻辑上证明有其实很容易,只需要找出一个例子即可,比如证明天鹅是白色的,只需要找出一个白色的天鹅即可。但是证明黑色的天鹅不存在,是非常困难的,除非穷举世界上所有的天鹅,才能得出这个结论。

人们的思维中,天生偷懒,所以人们才会有 “宁可信其有,不可信其无”。

所以有一种诡辩,我姑且称之为责任转移,就是在辩论中把举证的责任推给别人,然后再来挑对方的毛病。这是一种非常高级且隐晦的诡辩手段。

比如有神论要求无神论者给出证据,证明神不存在,但是证明无非常困难。对方只能举出一些例子,但是这些例子非常脆弱,如果再结合偷换概念就更无懈可击了。

大师:神会保佑你的。

无神论者:神不存在。

大师:你怎么证明神不存在呢。

无神论者:我从来没看到过神。

大师:没看到过神,不代表神不存在。

无神论者:看都没看见,怎么能说神存在呢。

大师:神是一种信念,它无处不在,慢慢体悟吧。

无神论者:。。。

责任转移大法是不断把举证的责任推给对方,然后在挑错,让对方自顾不暇。

总结

逻辑学中的内容非常多,还有很多有趣规律,比如三段论、命题和演绎等。

但对生活来说,本文介绍的一些方法用于吵架足够了,当然不是为了学习怎么制造诡辩,而是为了分辨诡辩。当我们在工作中交流时,能注意概念的统一和尊重同一律、矛盾律、排中律等逻辑学基本要素时,沟通会变得更加高效,吵架也更加有理有据,并从中得到成长。

more >>

如何克服害怕失败的心理

害怕失败是所有人的共性

”失败是成功之母“。从高中每个月蹭同桌的《青年文摘》中没少看到这类鸡汤。

为什么还想写一篇这样的文章呢,因为我想揭露一个事实,说这话的人不是矫情就是个棒槌。

为什么家庭背景好的人创业人数和成功的几率大很多呢?很多书籍中给出的答案是富人思维、穷人思维等等。其实答案非常简单,就是家庭条件好的人能出去折腾,并有人能兜底。

有人兜底是一个非常重要的因素,比各种 xxx 思维实在很多。正是有人兜底,知道创业失败了还可以回去啃老,不至于流落街头。我毕业时,其中一个室友就被 xxx 国际创业培训坑的没边,毕业后带着几千块跑到沿海城市做互联网思维创业,失败后灰溜溜的又跑回来。所幸家庭条件不差,在家里人的安排下混的不差。

换做当时的我是没这个胆子,毕竟失败了还得租房子、还助学贷款,所以害怕失败是必然的。

害怕是从远古人类就遗传下来保护我们的优良基因,毕竟胆子肥的人都没能活下来。男性对异性的害怕是来源于被种群首领揍的记忆,虽然现在谈恋爱不容易被揍了,但是这种基因还在,所以害怕失败是每个人共同的心理。但现代社会的背景已经发生了变化,原始基因中害怕失败的因子在现代社会中并不是对我们都有利,它让人不敢做出更多的尝试,并失去很多机会。

我一直是一个害怕失败的人,因为我是一个爱多想的人,每一次多想就会放大失败的后果。有趣的是当失败发生在生活中别人的身上时,发现后果并没有那么严重。

故事一,搞出线上事故的程序员

刚毕业工作时候,每次上线都心惊胆战,害怕弄出一个线上问题,然后就被扫地出门了。

这个怂样让当时的老板看不下去了,说了一句至今难忘的名言,”没折腾出线上事故的程序员都还不成熟“。当时不以为然,一个专业的软件工程师怎么能弄出线上事故呢?

工作的阅历告诉我,线上不出事故的软件无非两种情况:1. 软件基本没什么人用,搞出事故也没人发现。2. 技术选择极其保守,稍微不放心的东西都不往生产丢。生产环境的稳定性是很重要,但是过度的保守,害怕新技术的失败会导致项目一潭死水,无法成长。

害怕失败和害怕改变去重构代码的是同一种人。但有一些人的心理素质非常好,不得不佩服,也值得承担更大的责任。

几年前在一个项目上,有一位同事因为上线过程中误操作,造成服务中断,虽然事后也进行了事故回顾,但对他并没有额外的惩罚。实际上其他人也不会对他怎么责备。

其背后的逻辑很简单:

  • 能经常进行生产环境操作的往往都是比较重要的角色,因为生产环境操作比其他同事多,那么造成工作失误的概率自然更大。
  • 换一个人不见得就不会出错。
  • 不害怕出错敢于生产环境操作比其他人更能担责,毕竟有风险的操作总得找一个人做。

所以现实中对失败往往没有那么苛责,完全没失败过的人更可能是没有承担过有风险的工作。敢于直面风险,比逃避得到的不失败更可贵。有时候失败的代价并没有那么大。

故事二,玩垮苏联的戈尔巴乔夫

如果人人害怕失败,将不会有外科医生,因为随便一点失误代表一条命丢在自己手上。

如果人人害怕失败,将不会有国家领导人,可能因为一个错误的决策导致一个国家的命运被葬送。

在读世界的历史中感到非常困惑的一件事,戈尔巴乔夫宣布苏联解体,一个国家的消失,随之带来的是社会的动荡和人民生活的困苦。但是历史上并没有对戈尔巴乔夫追责,甚至他自己也不觉得这种巨大的失败有多了不起。在苏联解体后,戈尔巴乔夫生活的很好,甚至活到现在,并投身政治经济的研究,出了自己的书,还代言了几个著名的品牌。

了解更多苏联的历史后才认识到,戈尔巴乔夫并不是导致苏联解体主要原因,当历史的车轮滚滚向前时,换一个人并不会比戈尔巴乔夫做的更好。戈尔巴乔夫只是让苏联在解体的过程中平滑的失败,虽然苏联最终解体,但是并不剧烈,否则会带来内战。

我们往往把客观的责任主动或者被动的揽到自己身上,一个公司的失败,一个软件项目的失败,往往有它内在的原因。

人们往往以为失败是某一个决策,某一个决策是因为自己做出的,实际上是一种典型的认知错误。失败往往是各种因素的综合结果,决策往往也是因为在当时的历史背景、生活背景下做出的。

过度自责是害怕失败的罪魁祸首。

故事三,害怕失败的技术分享者

软件开发项目往往都会培养技术氛围,会做一些技术分享。有一个项目一个毕业生找我聊天,说自己害怕做分享,觉得要是做得不好太丢人。我问为什么呢?他说其他人的分享内容多么有深度、PPT有多么精美,他自己的分享达不到这个水平,自己一对比相形见绌。

然后发生了一段有意思的对话:

我:你每一天出门都洗头么?

他:不会吧。为什么问这个?

我:你看哈,如果你们组别人洗头了,你某天没洗头,其他人会注意你并笑话你吗?

他:应该不会吧。。。

我:是吧。其实每个人都只关心自己的事情,对别人没那么上心。就算你讲的特别不好,别人也只会在心里笑话你一小会儿,出于礼貌不会笑话你。即使实在是忍不住笑话你,过了第二天大家也就忘了。

他:。。。

然后他还是勇敢的做了一次自己的分享,当然不可能很差了,越是害怕失败的人,鼓起勇气的时候肯定做了最充分的准备。

不要过于在乎别人对自己的评价上,实际上,不洗头出门也没有太多人注意到你。

总结

通过三个简单的小故事,总结下害怕失败的原因和克服害怕失败心理的方法。

害怕失败的原因

  • 没人兜底,确实无法承受失败的后果
  • 生性敏感,过于在乎别人的评价
  • 过于自责,主动或者被动的归责

克服害怕失败的心理的方法

  • 故意犯错,提高害怕的阈值(不是阀值,也不是阙值)
  • 尝试一次不洗头出门,感受下其实别人没那么在乎你
  • 预演失败,如果失败确实是自己无法承受的失败,这种情况下害怕是对的

more >>