第十一章 第一次技术分享

一

赵北辰在周一的站会上宣布了一件事:"从这个月开始,我们组每两周做一次技术分享。每个人轮流讲,每次三十到四十五分钟。"

他看了一圈:"第一个,志远。"

王志远的心跳漏了一拍。

"我?"

"对。你上次做的消息通知中心,事件驱动的设计方案很有参考价值。整理一下,下周五给大家讲讲。"

"可是我……我没做过技术分享。"

"所以才让你做。"赵北辰笑了笑。"学一个东西最好的方式,就是把它讲给别人听。讲不清楚,说明你自己还没真正理解。"

王志远回到工位上,手心冒汗。

他不怕写代码,不怕改 bug,不怕凌晨两点的 On-Call。但他怕站在一群人面前说话。

从小到大,他都是那个在课堂上低头不敢举手的人。大学做课程答辩的时候,他紧张得声音发抖,PPT 翻错了两页,最后草草收场。

现在要他在一群技术比他强的同事面前做分享?

他觉得自己要完了。

二

他花了一个晚上列提纲,发现自己虽然做了消息通知中心,但要把它讲清楚,远比做出来难。

做的时候,他只需要自己理解就行。讲的时候,他需要让别人也理解——而且是在四十五分钟内。

他列了一个初步的提纲:

  1. 背景:为什么要做消息通知中心
  2. 设计:事件驱动架构
  3. 实现:代码细节
  4. 总结

他看着这个提纲,觉得很干。就像一份技术文档的目录——信息是全的,但没有人愿意听。

他想起大学时听过的一些技术讲座。那些让他昏昏欲睡的讲座,都有一个共同特点:从头到尾都在讲"我做了什么",没有讲"为什么这么做"和"踩了什么坑"。

而那些让他印象深刻的讲座,都是从一个问题或者一个故事开始的。

他重新列了提纲:

  1. 开场:一个真实的问题——"我的第一版设计为什么失败了"
  2. 问题分析:紧耦合的代价——改一个通知渠道要改五个 Service
  3. 解决方案:事件驱动架构——发布者和消费者解耦
  4. 实战演示:现场加一个新的通知渠道,只需要五分钟
  5. 踩坑记录:我在实现过程中遇到的三个坑
  6. 总结与思考:什么时候该用事件驱动,什么时候不该用

这个提纲好多了。有故事、有问题、有方案、有演示、有踩坑、有思考。

三

接下来的一周,他每天晚上都在准备。

他用了一个叫 SQ3R 的方法来整理自己的知识——这是他在一本学习方法的书里看到的:

  • Survey(浏览):先快速浏览整个主题,建立全局观
  • Question(提问):针对每个部分提出问题——"为什么要用事件驱动?""事件驱动有什么缺点?"
  • Read(阅读):带着问题深入阅读和研究
  • Recite(复述):用自己的话把学到的东西讲出来
  • Review(回顾):过一段时间后再回顾,检查是否遗忘

他发现,在准备分享的过程中,自己对事件驱动架构的理解又深了一层。

比如,他之前只知道 Spring 的 ApplicationEventPublisher 是同步的——事件发布后,所有监听器在同一个线程里依次执行。但在准备分享的时候,他深入研究了一下,发现如果监听器里有耗时操作(比如发短信),会阻塞主流程。解决方案是给监听器加 @Async 注解,让它在异步线程里执行。

他还发现了一个自己之前没注意到的问题:如果事件监听器抛出异常,默认情况下异常会传播到事件发布者,可能导致主流程失败。需要在监听器里做好异常处理,或者用 @TransactionalEventListener 来控制事件的执行时机。

这些细节,如果不是为了准备分享,他可能永远不会去深入研究。

他突然理解了赵北辰说的那句话:"学一个东西最好的方式,就是把它讲给别人听。"

这就是费曼学习法——通过"教"来"学"。当你试图把一个概念讲给别人听的时候,你会发现自己理解中的漏洞。那些你以为自己懂了的东西,在讲的时候才发现其实没有真正理解。

四

周四晚上,他在出租屋里对着镜子练了一遍。

效果很差。

他对着镜子说话的时候,声音不自觉地变小,眼神飘忽,手不知道往哪放。说到技术细节的时候还好,一到需要"讲故事"的部分就卡壳——"我的第一版设计为什么失败了"这个开场,他练了五遍都觉得不自然。

他给赵北辰发了一条消息:"赵哥,明天的分享我有点紧张。有什么建议吗?"

赵北辰回复了三条:

  1. "紧张是正常的。我第一次做技术分享的时候,手抖得连激光笔都拿不稳。"
  2. "不要背稿。你对这个主题足够熟悉,用自己的话讲就行。PPT 是提词器,不是剧本。"
  3. "记住一件事:台下的人不是来挑你毛病的,是来学东西的。你只要让他们学到一个新东西,这次分享就是成功的。"

王志远看着第三条消息,心里稍微踏实了一点。

他又练了两遍,这次不对着镜子了,而是对着电脑屏幕上的 PPT,假装台下有人。

还是紧张,但比第一遍好多了。

五

周五下午两点,会议室。

全组十来个人坐在下面。赵北辰、苏薇、陈锐、马德华,还有几个其他小组的同事——赵北辰把分享的消息发到了部门群里,吸引了一些"外援"。

王志远站在投影幕前,手里攥着翻页笔。

"大家好,我是王志远。今天分享的主题是'事件驱动架构在消息通知中心的实践'。"

他的声音有点发抖。他能感觉到自己的心跳在加速。

"在开始之前,我想先讲一个故事——一个关于'我是怎么把代码写成一坨的'的故事。"

台下有人笑了。

他的紧张感减轻了一点。

"两个月前,我接到了一个需求——做一个消息通知中心。我当时觉得不复杂,直接开始写代码。写了两周之后,我的 NotificationService 变成了这样——"

他翻到一页 PPT,上面是一段密密麻麻的 if-else 代码。

"七种通知类型,四种发送渠道,全部塞在一个方法里。每加一个新类型,我就要在这个方法里加一个 if-else。每加一个新渠道,我就要在每个 if-else 里加一段代码。改动量是类型数 × 渠道数,指数级增长。"

他看了一眼台下,发现大家都在认真听。马德华甚至放下了手机。

"然后赵哥跟我说了一句话:'你在写代码之前,画过设计图吗?'"

他翻到下一页,是他当时画的第一版时序图和赵北辰帮他改的第二版。

"左边是我的第一版设计——OrderService 直接调用 NotificationService,紧耦合。右边是改进后的设计——用事件驱动解耦。区别在哪?"

他指着图说:"左边的设计,发布者需要知道消费者是谁。右边的设计,发布者只需要发布事件,不需要知道谁在监听。这就是解耦。"

六

接下来的二十分钟,他讲了事件驱动架构的核心概念、Spring 的实现方式、以及他在实际开发中的应用。

讲到实战演示的时候,他做了一个现场 coding——在已有的通知中心代码上,加一个新的"企业微信"通知渠道。

他打开 IDE,新建了一个 WeChatWorkChannel 类,实现了 NotificationChannel 接口,写了 send 方法,然后在配置文件里注册了这个新渠道。

从新建文件到功能可用,一共用了四分钟。

"大家看到了,加一个新渠道,我只需要新建一个类,不需要改任何已有的代码。这就是好设计的价值——扩展的成本极低。"

台下有人鼓掌。

然后他讲了踩坑记录——三个他在实现过程中遇到的问题:

  1. Spring 事件默认是同步的:如果监听器里有耗时操作,会阻塞主流程。解决方案:@Async 注解。
  2. 监听器异常会传播到发布者:需要在监听器里做好 try-catch,或者用 @TransactionalEventListener。
  3. 事件顺序不保证:如果有多个监听器,执行顺序是不确定的。如果需要保证顺序,用 @Order 注解。

"这三个坑,文档里都有写,但我是踩了之后才去看文档的。"他苦笑了一下。"所以我的建议是——先看文档,再写代码。别像我一样。"

台下又笑了。

七

最后是总结与思考。

"事件驱动架构不是银弹。"他说。"它适合的场景是:发布者和消费者之间不需要强一致性,消费者的数量和类型可能会变化。不适合的场景是:需要同步返回结果,或者事件处理必须在同一个事务里完成。"

"选架构方案,别挑最'高级'的,挑最趁手的。这是赵哥教我的。"

他翻到最后一页 PPT,上面写着一句话:

"先画图,再写码。先想清楚,再动手。"

"谢谢大家。有什么问题可以提。"

他说完,手心全是汗。但他发现自己的声音已经不抖了。

八

提问环节比他预想的热烈。

一个其他组的同事问:"如果事件丢了怎么办?比如发布事件之后,监听器还没来得及处理,服务就重启了。"

王志远想了想:"Spring 的 ApplicationEventPublisher 是进程内的,服务重启确实会丢事件。如果需要保证事件不丢失,可以用消息队列——比如 RabbitMQ 或 Kafka,它们有持久化和确认机制。但对于我们当前的场景,通知丢一两条是可以接受的,所以没有引入消息队列。"

苏薇问了一个尖锐的问题:"你说事件驱动可以解耦,但如果事件的定义不合理——比如事件里携带了太多业务细节——消费者就会依赖事件的结构,本质上还是耦合的。你怎么看?"

王志远愣了一下。这个问题他没想过。

他老实说:"这个问题我没有深入思考过。但我觉得你说得对——事件的设计很重要。事件应该只携带必要的信息,比如事件类型和关键 ID,消费者需要更多信息的话,自己去查。这样事件的结构就不会膨胀。"

苏薇点了点头:"不错,能承认自己没想过,比胡编一个答案强。"

这是苏薇第一次在公开场合夸他。虽然夸得很含蓄,但王志远听出来了。

她顿了一下,又加了一句:"讲得不错,比某些人强多了。"

她说这话的时候,目光不经意地扫了陈锐一眼。

陈锐低下了头。

九

分享结束后,好几个同事过来跟王志远说"讲得好"。马德华拍着他的肩膀说:"小胖,没想到你讲起来还挺有感染力的。"

赵北辰走过来,递给他一瓶水:"第一次分享,能做到这个程度,很不错了。"

"但我觉得还有很多不足。"王志远说。"中间有几个地方卡壳了,提问环节苏薇的问题我也没答好。"

"卡壳正常,多讲几次就顺了。苏薇那个问题你答得挺好——好在哪呢?不是你答对了,是你老老实实说了'我没想过'。技术分享又不是考试,不用每个问题都有标准答案。不懂就说不懂,回去再琢磨,这本身就是分享的意义。"

赵北辰喝了一口茶,又说:"你知道为什么我让你第一个做分享吗?"

"为什么?"

"因为你是组里最需要锻炼表达能力的人。技术你在进步,但沟通和表达一直是你的短板。做技术分享是最好的锻炼方式——它逼你把脑子里的东西组织成别人能理解的语言。"

"而且,"他笑了笑,"你今天的表现证明了一件事——你不是不能讲,你只是不敢讲。敢了,就能讲好。"

十

那天晚上,王志远躺在床上,天花板上的灯管不闪了——上周房东终于换了一根新的。

他闭上眼睛,脑子里还在回放下午分享时的画面。手心的汗、台下的目光、苏薇嘴角那个不易察觉的微笑。

赵哥说他"不是不能讲,只是不敢讲"。说得对。很多事情不是能力问题,是勇气问题。

他忽然想起一句话——目的无他,惟手熟尔。卖油翁如此,写代码如此,做分享也是如此。第一次总是最难的,但迈出去了,后面就顺了。

他闭上眼睛,脑子里回放着今天分享的画面。紧张、卡壳、被笑、被鼓掌——都混在一起,乱糟糟的,但居然不难受。

他想起一句话:成长就是不断地做让自己害怕的事情。

今天,他做了一件让自己害怕的事情。

明天,他准备做下一件。


:::{admonition} 小胖的笔记 :class: tip

本章知识卡片:学习方法与技术分享

1. 费曼学习法(Feynman Technique)

1. 选择一个概念
2. 假装你要教给一个完全不懂的人
3. 发现自己讲不清楚的地方 ← 这就是你没真正理解的部分
4. 回去重新学习那些讲不清楚的部分
5. 重复,直到你能用简单的语言讲清楚

核心思想:如果你不能用简单的语言解释一个概念,说明你没有真正理解它。

2. SQ3R 学习法

步骤 含义 做什么
Survey 浏览 快速浏览全局,建立框架
Question 提问 针对每个部分提出问题
Read 阅读 带着问题深入阅读
Recite 复述 用自己的话讲出来
Review 回顾 过一段时间后再回顾

3. 技术分享准备清单

# 准备项 说明
1 确定主题和受众 听众的技术水平决定了讲解的深度
2 列提纲 问题 → 分析 → 方案 → 演示 → 踩坑 → 思考
3 准备 PPT 少文字,多图表和代码。PPT 是提词器
4 准备现场演示 提前跑通,准备备用方案(录屏)
5 准备踩坑记录 听众最感兴趣的部分
6 练习 2-3 遍 不要背稿,用自己的话讲
7 准备 Q&A 预想可能的问题,但允许说"我不知道"

4. 技术分享的常见误区

❌ 误区                          ✅ 正确姿势
─────────────────────────────────────────────────
从头到尾讲"我做了什么"            从一个问题或故事开场
PPT 全是文字                     少文字,多图表和代码
只讲成功经验                     也讲失败和踩坑
背稿                            用自己的话讲
不敢说"我不知道"                 诚实承认,会后补充
讲太多细节                       抓重点,细节留 Q&A

5. 持续学习的习惯

方法 频率 效果
技术博客/文章 每天 15 分钟 保持技术敏感度
技术书籍 每月 1 本 系统性学习
技术分享(听) 每两周 1 次 拓宽视野
技术分享(讲) 每月 1 次 深化理解
Side Project 持续 实践新技术

核心原则:输入(学)和输出(讲/写)要平衡。只输入不输出,知识留不住。

:::