读书笔记 · 约 15,335 字 · 预计推送 2 部分

Rich Hickey《Simple Made Easy》精读:与《A Philosophy of Software Design》对照的"简单 vs 容易"工程判据 未开始

目录

题记与说明


0. 来源与精读方式(本笔记的证据基础)

#来源精读程度链接
1Rich Hickey, *Simple Made Easy*, Strange Loop 2011 —— matthiasn/talk-transcripts 逐字稿(473 行,含时间戳)全文精读(本次已完整读毕)https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/SimpleMadeEasy.md (raw: https://raw.githubusercontent.com/matthiasn/talk-transcripts/master/Hickey_Rich/SimpleMadeEasy.md );视频 https://www.youtube.com/watch?v=SxdOUGdseq4
2dev.to:A Philosophy of Software Design 逐章摘要要点精读(引述经转述)https://dev.to/markadel/a-philosophy-of-software-design-summary-pk9
3Jan Meppe:Book Notes, A Philosophy of Software Design要点精读(多条逐字引语)https://www.janmeppe.com/blog/a-philosophy-of-software-design-john-ousterhout/
4Pragmatic Engineer:John Ousterhout 访谈页部分精读(页面为摘要/时间轴,非逐字稿,仅取其转述要点)https://newsletter.pragmaticengineer.com/p/the-philosophy-of-software-design
5typesanitizer:*A meta-analysis of three different notions of software complexity*(对 Hickey / Ousterhout / Tellman 三种复杂性定义的对照批评)要点精读(第三方对照文献)https://typesanitizer.com/blog/complexity-definitions.html

未验证/受限点(诚实声明):


1. 演讲精读:《Simple Made Easy》(逐字稿引述均标注行号/时间戳)

1.1 词源定义:simple ≠ easy

1.2 客观 vs 相对:本演讲的第一根钉子

1.3 construct vs artifact:评估对象错了

1.4 理解的极限与交织的组合负担

1.5 护栏编程与速度幻觉

1.6 incidental complexity:附带复杂度

1.7 复杂性工具箱 vs 简单性工具箱(L226–238, 00:28:50;L304–328)

复杂(complect 什么)简单替代
state:complect value 与 time(L286)values(不可变值)
objects:complect state, identity, value(L306)函数 + 数据
methods:complect function 与 state(+命名空间)(L308)functions
inheritance:complect types,"It's definitional."(L312)polymorphism à la carte(protocol/type class)
switch / pattern matching:complect 多对"谁→做什么",且封闭(L314)开放式多态
vars/variables:不可分离 value 与 time(L316–318)managed references(能"compose values and time",L302)
syntax:complect meaning 与 order(L310)data("It's inferior to data in every way")
imperative loops / fold:complect 做什么与怎么做;fold 隐含顺序(L320)set functions
actors:complect what 与 who(L322)queues
ORM:complect 逻辑与表示declarative data manipulation(SQL/LINQ/Datalog)
conditionals:规则散落各处、与程序结构 complect(L328)rules(声明式规则系统)
inconsistency:"consistent means to stand together",不一致=把分开站的东西强行一起想(L238)transactions + values

1.8 complect vs compose;模块化 ≠ 简单

1.9 为简单而抽象:who / what / when / where / why / how

1.10 环境复杂度:诚实的边界

1.11 收束:简单是选择


2. 《A Philosophy of Software Design》要点(据来源 2/3/5 转引,原书未直接读)

2.1 复杂性定义与症状

2.2 深模块与黄金法则

2.3 工作代码不够、战略编程、设计两遍


3. 对照:两套语言下的同一战场

3.1 术语映射

HickeyOusterhout关系
complect(缠绕)dependencies + obscurityHickey 只点名"纠缠";Ousterhout 把"看不出来"(obscurity)单列
incidental complexitycomplexity 的主要打击对象同物:问题没要求、工具/设计自找的
artifact 属性(长期运行/可改)"hard to understand and modify"最终法庭一致:都是未来改代码的成本
guardrail programmingtactical programming;TDD 无设计反馈环都把"测试/类型/快速迭代"降级为安全网
entanglement radar"the ability to see complexity"(据 dev.to 转述的功课)都要求训练一种查看复杂度的感官

3.2 一致点(收敛的工程共识)

  1. 以"未来的理解与修改成本"为唯一法庭(Hickey L65/L73;Ousterhout 定义句)。
  2. 接口要小:Hickey "much smaller than what we typically see"(L394)+更多 subcomponents(L406);Ousterhout 深模块"简单接口、强大功能"。
  3. 组织结构不等于简单:Hickey "don't be fooled by code organization"(L280)↔ Ousterhout shallow modules / Classitis。
  4. 熟悉与速度是幻觉源:Hickey 的 sprint 曲线(L148–154)↔ Ousterhout 的 tactical tornado(Pragmatic Engineer 页转述,且用于批评 AI 编码工具)。
  5. 增量投资:Hickey "spend a little time upfront simplifying things before we get started"(L463 附近, 00:59:53)↔ Ousterhout strategic programming 的 10–20%。
  6. 测试/类型检查不是设计(Hickey L118–130, L457 ↔ Ousterhout TDD 批评)。

3.3 分歧点(判据要在这里做出选择)

  1. 复杂度藏进哪里:Ousterhout 黄金法则明确"接口简单比实现简单更重要"(藏进去);Hickey 主张构造物本身不得缠绕,且状态复杂度会像"深色液体"渗漏(L288–290)——他允许的"藏"仅限于呈现真函数接口的边界。一个治标于接口,一个治本于构件。
  2. 客观性主张:Hickey:simple 客观可查、easy 相对(L41/L57);typesanitizer 指出 Ousterhout 的 obscurity 是读者相对的("If someone reading your code says it's not obvious, then it's not obvious"),且"成因(obscurity)用症状(不明显)定义"有循环之嫌——Ousterhout 框架在这点上不如 Hickey 干脆。
  3. complect 是否无条件坏:Hickey 是;typesanitizer 反驳其不能泛化(如数据库外键正是有意的耦合),Ousterhout 则只要求依赖"少而明显"。→ 工程折中:耦合不可避免,晦涩不可原谅。
  4. 调试视角:Hickey 用"bug 都过了测试与类型检查"论证简单性是调试的前提;typesanitizer 指出 Ousterhout 全书"doesn't talk about debugging much at all"——简单 vs 容易的判据里,调试可达性主要来自 Hickey 侧。
  5. 打击对象:Hickey 点名语言构造(ORM、继承、actor、syntax);Ousterhout 不谈语言构造,只谈模块划分与信息隐藏——前者是构造选型判据,后者是结构设计判据,作用在不同粒度。

4. 提炼:简单 vs 容易的工程判据(本笔记综合,非原文)

综合判据为本次对照的产出,来源标注在每条后。判定句式尽量做成"可当场回答的问题"。

总判据(一句话版):先问"这是简单的还是只是容易的"——即"它没有缠绕,还是仅仅在我手边/我熟悉"(Hickey L41/L57);再问"它把复杂度推向了用户还是拉向了内部"(Ousterhout 黄金法则)。两个问题都过关才叫好设计。

#判据当场要问的问题通过标准出处
1交织判据(客观)实现这个变更要碰几个本不相关的文件/概念?理想为 1;每多一处即一个 complect 信号Hickey L89/L250;Ousterhout change amplification
2工件判据我们在评估"跑起来的软件",还是"敲代码的体验"?一切评审以工件属性(可靠/可修/可改)为准Hickey L65–L73
3认知负荷判据完成此任务需同时记住几件事?新成员能从代码里推出来吗?推不出来的信息=unknown unknowns=obscurity,必须消除Ousterhout 症状三;Hickey L188 杂耍类比
4接口深浅判据模块是否"小接口、大实现"?有没有 pass-through 浅模块/Classitis?接口≤其实现复杂度;每类只有 1–2 个正交抽象Ousterhout ch.4;Hickey L394/L406
5泄漏判据(分歧区)内部藏的复杂度是否以隐式依赖、非明显状态漏到外面?状态是否 complect 了 value 与 time?藏可以,漏不行;对外的必须是 same input→same output 的值语义Ousterhout 黄金法则 vs Hickey L286–L290
6熟悉度判据我说"简单"时,是否其实在说"我熟悉"?(parens 测试:不熟悉但无交织→学习责任在我)区分"你的错"(不熟)与"工具的错"(真缠绕)Hickey L51/L198–L214;L168 "incidental is Latin for your fault"
7速度判据现在的快是靠省掉思考吗?这条曲线 6 个月后还在吗?允许前期爬坡(simplifying upfront / 战略编程 10–20%)Hickey L148–L154;Ousterhout tactical/strategic
8守护网判据测试/类型/重构工具是否被当成了设计替代品?它们是 safety net;bug 已证明能通过它们Hickey L118–L130, L457;Ousterhout TDD 批评
9决策位置判据策略(why)是否散落全程序?资源策略(线程池等)是否各自为政、不可组合?策略外置为声明式/规则化,环境策略集中决策Hickey L328–L336, L370–L374
10数据判据信息是否被包成对象/ORM,逻辑是否绑死表示?data is data:直接用 map/set,操纵数据的通用工具才能只写一次Hickey L430–L438

使用顺序建议:1/2 先行(客观、便宜),3/5/9 是深层病灶,4/10 是结构性处方,6/7/8 用于拆穿日常争论中的语义偷换("我说 simple 其实是 easy")。


5. 七维提取

维度内容
核心问题为什么我们明知复杂度有害,仍在持续生产复杂系统?Hickey 的答案:混淆了 simple(客观、无缠绕)与 easy(主观、近便、熟悉),并围绕"容易"组织了整个工具文化与评审文化;Ousterhout 的答案:复杂性是增量累积的,缺乏一套"看见并持续偿还"它的设计纪律。
主要主张Hickey:简单是客观属性(无 interleaving),容易是相对属性(near/familiar/capable);复杂度来自 complecting;应选简单构造(值/函数/数据/队列/声明式)、做小接口大拆分,简单是一种需要 constant vigilance 的选择。Ousterhout:复杂性=任何使系统难理解难修改的结构特征,症状为变更放大/认知负荷/未知未知,成因是依赖与晦涩;处方是深模块、信息隐藏、"把复杂度往下拉"、战略编程与设计两遍。
方法Hickey:词源辨析 + 构造→工件的因果分析 + 逐一点名常见构造的 complect 方式(复述式论证,无实证数据);给出 who/what/when/where/why/how 的抽象拆分操作法。Ousterhout(据转述):症状—成因—处方的定义驱动框架 + 大量正反例(Unix I/O、Classitis、Tk)。
数据与证据两者都是经验性论证而非实证研究:Hickey 的"科学图表"自嘲无坐标轴(L150),证据形态是行业共同经验与构造分析;Ousterhout 的证据是教学/工业案例(Unix I/O 五个系统调用、Tk 两版设计、Tolkien 式 Classitis 反例)。第三方检验(typesanitizer)指出:Hickey 定义客观但 complect 泛化受限;Ousterhout 框架在 obscurity 上有循环、全书几乎不谈调试。
结论二者在终点收敛——一切以"未来理解与修改的成本"为法庭,接口要小,组织结构不等于简单,测试/类型是安全网而非设计;分歧在复杂度允许藏进哪里(Ousterhout:藏进深模块内部;Hickey:构造本身不得缠绕,状态复杂度必然渗漏)与简单性的客观性主张(Hickey:可查验;Ousterhout:经 obscurity 间接读者相对)。
局限Hickey:无度量、无实证;对"complect 无条件坏"的绝对化被 typesanitizer 举反例(外键类有意耦合);对环境复杂度自认无解(策略不可组合);隐含 Clojure 立场。Ousterhout:obscurity 定义循环、读者相对;调试/可观测性缺位;深模块的"藏"与 Hickey 的"渗漏"论证未交锋。本次精读自身局限:Ousterhout 原书未读,引语经二手转引;InfoQ 官方页 405 未读。
对我的启发(1)日常评审里把"简单/容易"当两个词用:说"简单"必须能指出哪里没缠绕(判据 1、5),否则改口"容易"。(2)判据 4 与判据 5 联用可仲裁最深的设计争吵:允许复杂实现,禁止隐式泄漏——这正是深模块与值语义的交集。(3)判据 6 用来拆穿选型争论:" parens 不熟"与"ORM 缠绕"是两类问题,前者学习解决,后者换工具解决。(4)把 typesanitizer 的批评记为检查项:任何耦合都先问"它是被解释在一起的必然,还是偶然缠绕"。

6. 参考来源

  1. Rich Hickey, *Simple Made Easy*, Strange Loop 2011. 逐字稿:https://github.com/matthiasn/talk-transcripts/blob/master/Hickey_Rich/SimpleMadeEasy.md (本次全文精读,raw 文件本地暂存于工作区 sources-d2f37j93/ 目录);视频:https://www.youtube.com/watch?v=SxdOUGdseq4
  2. *A Philosophy of Software Design Summary* (dev.to, 2025-07):https://dev.to/markadel/a-philosophy-of-software-design-summary-pk9
  3. Jan Meppe, *Book Notes: A Philosophy of Software Design*:https://www.janmeppe.com/blog/a-philosophy-of-software-design-john-ousterhout/
  4. Pragmatic Engineer, *A conversation with John Ousterhout about The Philosophy of Software Design*(2025-04,摘要页非逐字稿):https://newsletter.pragmaticengineer.com/p/the-philosophy-of-software-design
  5. typesanitizer, *A meta-analysis of three different notions of software complexity*(Hickey / Ousterhout / Tellman 对照):https://typesanitizer.com/blog/complexity-definitions.html
  6. 未能读取:InfoQ 官方页 https://www.infoq.com/presentations/Simple-Made-Easy/ (HTTP 405)。
← 资料库今日播报 →