## 嘉宾背景
John Ousterhout是一位独特的计算机科学家,既有深厚的学术背景,也有丰富的工业界经验。他曾在伯克利任教14年,后转入工业界创办公司,再回到斯坦福任教。他创造了TCL脚本语言,发明了Raft一致性算法(被MongoDB、CockroachDB、Kafka等广泛使用),并著有《软件设计的哲学》一书。
## 核心观点与洞察
### 1. 学术界与工业界的差异
**相似之处**:
- 都是与小团队合作
- 都在尝试做相对新颖的事情
- 都致力于构建真正有用的软件
**关键差异**:
- 工业界有巨大的营销压力,需要让自己看起来比实际更好
- 学术界可以诚实地说"这个项目失败了,让我们继续下一个"
- 工业界需要面对更广泛的人群(销售、市场、投资人等)
### 2. 战术龙卷风现象
**定义**:战术龙卷风是指那些能快速产出代码的多产程序员,但采用完全战术性的方式工作。
**特征**:
- 实现快速功能时无人能及
- 不关心留下的技术债务
- 被某些管理层视为英雄
- 实际上给其他工程师留下需要清理的混乱
**根本原因**:
- 特定的个性类型
- 组织过分重视速度(特别是初创公司)
- 缺乏长期思维
### 3. AI对软件工程的影响
**相对确定的趋势**:
- AI工具将使底层代码生成变得更容易
- 自动补全将变得更好,可能产生相当高质量的代码
**不确定的领域**:
- AI工具在多大程度上能替代高层设计任务
**关键洞察**:
- 随着AI处理越来越多的底层编程任务,软件设计师的工作将越来越聚焦于设计
- 软件设计将变得更加重要,占开发者时间的比重越来越大
- 这使得大学不教授软件设计变得更加令人遗憾
### 4. 软件设计的本质
**核心定义**:软件设计是一个分解问题——如何将大型复杂系统划分为可以相对独立实现的较小单元。
**设计方法**:
- **自上而下**:从顶层开始,将系统分解为相对独立的组件
- **自下而上**:从具体功能开始,逐步构建整体架构
- **实际过程**:两种方法的迭代结合,在大部件和小部件之间来回思考
### 5. 管理复杂性的两种方法
1. **消除复杂性**:通过设计完全消除某些复杂性(最强大的方法)
2. **隐藏复杂性**:使用模块化设计,将复杂性封装起来,使其他人不需要意识到这种复杂性
### 6. 设计两次的重要性
**观察**:聪明的人往往坚持实现第一个想法,这会限制他们的真正潜力。
**原因**:
- 许多顶尖学生一生中一切都很容易
- 第一个想法总是足够好,从未有动机去思考第二遍
- 当面对真正困难的问题时,第一个想法往往不是最好的
**实践方法**:
- 强制自己考虑至少两种方法
- 即使认为其中一种很糟糕,也要进行比较
- 这个过程通常只占总时间的1-2%,但能带来巨大回报
### 7. 深度模块 vs 浅层模块
**深度模块**:
- 提供简单的接口
- 内部有大量功能和复杂性
- 为用户提供巨大的认知负担减轻
- 是对抗复杂性的有力工具
**浅层模块**:
- 宽接口但功能有限
- 可能几乎是透明的,没有太多隐藏
- 增加系统复杂性而非减少
**设计目标**:以最简单的接口获得最多的功能。
### 8. 错误处理的设计原则
**核心观点**:异常处理是复杂性的巨大来源。
**设计原则**:
- 更多异常并不意味着更好的编程
- 每个异常都给类的用户增加复杂性
- 通过轻微的设计变更,整类错误可以完全消失
- 从调用者角度思考异常设计
**重要警告**:这个原则容易被误解,不是忽略错误,而是通过设计消除错误的可能性。
### 9. 设计中的同理心
**关键能力**:能够改变思维模式,从不同角度思考问题。
**具体应用**:
- 设计模块时考虑内部细节
- 使用模块时完全忘记内部细节,只使用接口
- 这种思维转换能力在社会环境中同样有价值
### 10. 与Clean Code的分歧
#### 关于短方法
**Robert Martin观点**:方法应该尽可能短,做一件事。
**Ousterhout观点**:
- 设计是关于权衡的
- 过度追求短方法会导致过多接口,增加系统复杂性
- 深度概念更重要:大量功能+简单接口
- 如果方法紧密耦合,最好将它们组合在一起
#### 关于测试驱动开发(TDD)
**反对理由**:
- TDD与设计背道而驰
- 鼓励小增量设计而非整体思考
- 没有鼓励退后思考整体架构的环节
- 导致战术性开发风格
- 推向专门化而非通用化解决方案
**替代建议**:以抽象为开发单元,而非单个测试。
#### 关于注释
**Clean Code观点**:代码应该自我解释,尽可能少的注释。
**Ousterhout观点**:
- 代码永远无法完全自我解释
- 注释应该提供代码中不明显的信息
- 接口是最需要注释的地方
- 类成员变量需要详尽文档
- 方法内部通常不需要太多注释
### 11. 设计评审的价值
**实践方法**:
- 相对非正式的设计讨论
- 多个头脑思考同一个主题显然比一个人更好
- 在分析瘫痪和充分设计之间找到平衡
**白板技巧**(解决复杂争议):
1. 列出所有支持和反对的论点
2. 任何人都可以提出论点,不能被删除
3. 不允许重复已有论点
4. 最后进行无记名投票
5. 通常会得到令人震惊的一致结果
### 12. 设计的时机
**个人观点**:设计贯穿整个开发过程
- 前期设计
- 编码时设计
- 测试时设计
- 修复bug时设计
**建议**:
- 总是做一些前期设计(不是瀑布模型)
- 有一些假设来指导工作
- 准备好在发现问题时修改设计
- 唯一不做设计就编码的情况:过于年轻和缺乏经验
### 13. 当前项目:Linux内核中的Homa协议
John正在将学生Ben Montazeri的PhD研究成果Homa传输协议引入Linux内核。
**性能优势**:在许多有趣的情况下比TCP快10-100倍。
**上游过程**:
- 已提交补丁6个月
- Linux内核开发者反馈质量很高
- 过程比预期更合理
- 通过代码审查显著改进了Homa
### 14. 教学方法:斯坦福软件设计课程
**教学模式**:基于高中英语写作课的模式
- 学生完成项目
- 接受广泛代码审查
- 重写项目整合反馈
- 通过重做过程真正内化概念
**项目安排**:
- 三个阶段,构建重要功能
- 前两个项目是Raft一致性协议
- 每个团队获得50-100条详细反馈
- 第二轮项目质量显著提升
**效果**:
- 学生思考软件的方式发生根本改变
- 毕业后在公司中表现出比同级工程师更强的设计意识
### 15. 通用化vs专门化
**重要原则**:推动自己朝着通用化方向发展,尽可能避免专门化。
**应用**:
- 寻找能解决多个问题的通用解决方案
- 避免为每个测试创建专门化解决方案
- 这是区分优秀设计师的关键因素之一
## 书籍推荐与资源
**《软件设计的哲学》第二版**:
- 更加强调通用化和消除专门化
- 需要特别注意异常处理章节的正确理解
- 作者主页有额外的推荐资源
## 结语
这次访谈深入探讨了软件设计的核心原则和实践方法。John Ousterhout强调,随着AI工具处理越来越多的底层编程任务,软件设计将变得更加重要。他的核心理念——管理复杂性、深度模块、设计两次、通用化解决方案——为软件工程师提供了宝贵的指导原则。
特别值得注意的是,他对当前流行的一些软件工程实践(如TDD、过度简短的方法、避免注释)提出了深思熟虑的挑战,这些观点值得每个软件工程师认真考虑和讨论。
## 来源引用
[The Philosophy of Software Design – with John Ousterhout](https://www.youtube.com/watch?v=lz451zUlF-k)