ARTICLE · 1089112
影响 Matt Pocock 的一本书:AI 时代重读《软件设计的哲学》
影响 Matt Pocock 的一本书:AI 时代重读《软件设计的哲学》
进入 AI 生成代码的时代,写出一段可运行的代码,成本在持续下降。与此相对,判断代码该长什么样、半年之后谁还敢改,分量反而在上升。
斯坦福教授 John Ousterhout 把这个难题称为软件设计。他在 2018 年写成《软件设计的哲学》(A Philosophy of Software Design),当时还没有今天这样的 AI 编程工具。TypeScript 开发者 Matt Pocock 后来把这本书拆成一套 AI 编程规则。
本文根据 Uber 前工程经理 Gergely Orosz 对 John Ousterhout 的一次访谈编写。这本书的 GitHub 民间中文版放在文末,需要可以自取。

一、一个每年还写几千行代码的教授,怎么看 AI 编程
AI 会把软件工程带到哪里,这是 Gergely Orosz 抛给 John Ousterhout 的第一个问题。
Ousterhout 的回答很克制,先划出一条分界线。有些事看起来比较清楚,有些事没那么清楚。

比较清楚的一头是低层代码,也就是一个个具体函数、一行行具体语句怎么写的那个层面。AI 工具会让这部分生产变得更容易,也就是人们熟悉的自动补全。这件事会越做越好,而且未必产出草率的代码,质量说不定还相当高。
不清楚的那一头是高层设计,也就是系统该拆成几块、模块与模块之间怎么连接这一类判断。AI 究竟能在多大程度上接手设计工作,Ousterhout 没有答案。至少到目前为止,他在现有工具里看不到任何迹象,能让他相信模型做得到这一步。但他也提醒,不要低估这件事发生变化的速度。
Gergely 给这个判断加了一层画面。AI 工具非常擅长快速生成代码,能写短的,能写长的,能做补全,能生成各种骨架,从不说不,也从不喊累。假设每个工程师手边都有一台这样的生成器,软件工程会变成什么样,这是整场对话反复回到的问题。
其中的推论更值得琢磨。随着 AI 接下越来越多的低层编程,设计者在工作里做的事会越来越偏向设计本身。软件设计在开发者时间里的占比会上升。
这本来是好消息,紧接着却跟了一个坏消息。大学里几乎完全不教软件设计。学生在课堂上学得最熟的那些技能,恰好是最可能被 AI 工具接手的那一批。
这场对话的落点也不只在 AI。下面这些题目占了大半篇幅:
- 深模块与浅模块
,也就是接口简单、内部复杂的模块,和接口很宽、内部很浅的模块 - 错误处理
- 注释
- Ousterhout 为什么建议避开测试驱动开发
,也就是先写测试、再写实现的那种做法
它们最后都指回同一个问题,也就是复杂度,指的是那些让人读不懂、改不动、牵一发动全身的部分。

Ousterhout 敢下这个判断,是有来历的。他创造了 Tcl 脚本语言,发明了 Raft 共识算法,后者被 MongoDB、CockroachDB、Kafka 这类数据库和系统广泛使用。
Ousterhout 在 Sun Microsystems 工作过,创办过两家科技公司,现在是斯坦福大学计算机科学教授,《A Philosophy of Software Design》的作者。一年当中,他仍然尽量写上 5000 到 10000 行代码。

伯克利十四年,硅谷十四年,斯坦福又十四年
Ousterhout 年轻时就想同时做学术和业界。拿到博士学位时,他认真权衡过,因为两件事他都喜欢,学术界的创作自由、教书,以及做出真实可用的软件。写代码是他生活里的热情所在。
Gergely 自己某个阶段也想过这件事。他的父亲是大学老师,学术界里也有两边都做过的人。他关心的是,被称为科技世界的那一边,和学术界最大的差别究竟在哪里。
当时 Ousterhout 得到的建议是先做学术。理由很实际,从学术界转向业界,比反过来容易。他去了伯克利,在那里度过愉快的十四年。
时间久了,想做商业软件的那股劲越积越强。他最终放弃伯克利的职位,搬到硅谷,在 Sun 工作几年,又创办了两家只算小有成绩的公司。
再往后,回学术界的念头重新积累起来。他慢慢意识到,自己虽然喜欢做公司,但做教授才是本来的热爱。做了十四年创业公司之后,他拿到斯坦福的职位,又在斯坦福待了十四五年。
学术界和业界的差别,在他看来没有旁人想的那么大。两边都是在一支不大的团队里,做一件相对新的事情,都想做出真能跑的软件。哪怕在学术界,他也要求东西能用,而不是做完就丢的研究原型。
差别出现在人际上。业界要面对的人群广得多,销售、市场、融资的人,以及风险投资人。能接触这么多类型的人,是这份工作有意思的地方。
让 Ousterhout 不太舒服的是创业公司的压力,尤其是把自己说得比实际更大更好的压力。公司要活下去、要拿到融资,就会有很强的动力去包装、夸大、推销。人人都或多或少这么做,有人做得太极端,触了法律,惹上麻烦。
Ousterhout 自己开公司时,对外当然要推销,内部则尽量保持诚实。他不愿在内部听到谎话和包装,因为那会把公司置于风险之中。
他喜欢学术界的地方在另一面。做一个项目,有些成,有些不成。项目失败时,只需要说一句,这个想法不行,原因是什么,随后接着做下一件。
在公司里,项目不行的时候,没法说一句「对不住各位,明天关门」,只能想办法绕过去。这是他最不喜欢业界的一点。
两边的日子都过过之后,Ousterhout 给了这样一句:
两边都做过,反而让我在两边都做得更好。

二、战术龙卷风与技术债:产出最快的人,留下一地鸡毛
书里最著名的一个说法叫「战术龙卷风」,出现在全书最靠前的章节。Ousterhout 在书里是这样描述的:
战术龙卷风是一位高产的程序员,出代码的速度比同事都快,但方式是纯粹战术性的。要快速实现一个功能,没人比他更快。在有些组织里,管理层把战术龙卷风当成英雄。然而战术龙卷风身后留下一路废墟,收拾残局的往往是别的工程师。
Ousterhout 没法点名具体的人,就算能,也不愿意点。但职业生涯里他确实遇到过这类人,也相信任何有相当软件开发经验的人都碰到过、观察过,并且为此沮丧过。
在他看来,这是一种特定的人格类型。有人非常关注细节,属于收尾型,要求每件事都做到位,做一件就做完一件。也有人热衷于启动项目,把前面的百分之八九十做完,最后那百分之十到二十对他们没那么重要。
世界上本来就有更讲究的人和更随意的人。战术龙卷风属于太随意的那一类,不在乎身后留下大量残渣,这一点完全不困扰他们。这类人会一直存在,尤其因为有些组织把速度看得高于一切。
最典型的场景是创业公司。他半开玩笑地说,很多创业公司整队都是战术龙卷风。
Gergely 也见过这种人,也见过他们走之后留下来的麻烦。

同一个位置在别人嘴里有不同的名字。有人叫他 10x 工程师,因为管理层看到的只是他产出更快。有人形容这种做法很野,指的是快速凑合。再往前十几年,人们叫他们黑客,强调的是动手快。
Ousterhout 对 10x 工程师有另一套定义。Google 说到的那种 10x 工程师,他脑子里出现的是另一种人,和战术龙卷风相差很远。
那种能给出非常干净的设计、用极少的代码就把事情做成的人,才是他认可的那一类。他们每天写下的代码可能比同事更少,实现出来的功能却多得多,稳定性和可演化性也更好。
问题在于这个词太含混,每个人说的都不是同一件事。说话的人多半不太懂技术,看到的是持续的产出,很难同时看到产出背后的代价。Ousterhout 在现实里见到的情形多半是这样,一位不太懂技术的 CEO 说「我的 10x 工程师」,指的其实是战术龙卷风。
CEO 看到的是源源不断的产出,那个人从不说不,一直在建东西。至于这些产出背后积累了多少技术债,以及这件事正在拖慢周围多少人,离代码远的角色很难看出来。技术债,指的是为了赶速度而欠下、将来要花时间偿还的那笔账。
Ousterhout 把这一层归结为战略与战术之分。有人看重短期里的一切,有人更看重长期。他对设计的看法完全围绕长期,也能理解别人对同一个词有另一套用法。
同一件事在两种视角下会得出相反的结论,这一点 Ousterhout 自己也承认。管理层看到的是产出和速度,写代码的人看到的是明天要花多少时间收拾今天留下的东西。两种编程方式的分野,摆开来看是这样:

三、软件设计是一道分解题:自顶向下,还是自底向上
软件设计到底是什么,这个问题听起来简单,答案却不那么好给。Gergely 承认,这个问题问得有点天真,可它并不好答。他聊过的开发者里,有的只待过业界,也有的在考虑某天转去做学术,他自己某个阶段也动过这个念头。
现实是,如今拿一个现成的模板就能开始做应用,再过不久,还能直接让 AI 工具生成一整个骨架。
Ousterhout 给了一个很干脆的起点:
软件设计就是分解,把一个庞大复杂的系统,拆成若干个可以相对独立实现的小单元。
Ousterhout 做演讲时经常向台下的听众提问,请他们说出整个计算机科学里最重要的思想。他自己的答案是分解(decomposition)。这是贯穿计算机科学所有工作的一条线索,怎样把庞大复杂的问题拆开。
实现是另一件事,去处理一个个单独的部件。而在处理这些部件的时候,可能还要再做一次设计,把它们继续拆成更小的部分。
设计这件事没有菜谱。Ousterhout 希望有,照着几步走、按这个顺序做,就能得到好设计。现实里大体上有两条路径,各有各的坑:
经验较少的工程师,或者身处不熟悉领域的人,更常用自底向上这条路。
七十年代 Ousterhout 读研究生的时候,关于设计该走哪条路有过一场大讨论,人们争得很凶。他自己的看法是,纯粹走哪一条都很难做到。真实的设计过程是两者的混合,先想大块,再想一些小块,做出来发现效果不好,扔掉,再做,就这样来回迭代。

应对复杂度,总体上有两条路。一条是让某些复杂度彻底消失,比如把对特殊情况的处理消除掉,一件事只要那么设计,就不需要特殊处理了。这一类办法最有力,效果也最扎实。
但复杂度不可能全部消除,于是有了第二条路,模块化设计,把复杂度藏起来。具体做法是把相对复杂的东西挪到一边,让某个人专门去解决它、承担它,系统里其余的人都不必知道这份复杂度的存在。
两条路都在帮忙。一条给出消除复杂度的思路,另一条让大多数人不必知道大部分复杂度的存在。
为什么 TDD 和设计模式,都不算软件设计
有一个现象值得解释。过去二十来年,大量注意力曾经放在离设计很近的架构话题上,测试驱动开发、设计模式、四人帮那一套、工厂模式,都在那个名单里。
到了业界,引用这些模型和想法的人明显变少。Gergely 怀疑,也可能只是行业里谈得少了,甚至想得也少了。
Ousterhout 给的解释很直接。潮流总有涨落,兴奋一阵随后退潮,这可能是部分原因。但在他这里,那些东西本来就不算设计,测试驱动开发尤其不算。
设计模式是设计的替代品,意思是不去设计某样东西,而是从货架上取一个现成的下来。用得上的地方,它当然有用。某些领域里,如果一个模式确实适用,那就该直接用。
但它覆盖的范围只是软件设计中极小极小的一块。设计要做的事,比挑六个模式多得多。

四、设计两遍:第二个想法往往更好
书里有一整章讲这件事,标题就叫「设计两遍」。他在里面写过这样一段话:
很遗憾,我常看到聪明人坚持实现脑子里想到的第一个想法,这让他们没能发挥出真正的潜力,也让和他们共事的人感到挫败。
同时 Ousterhout 补了一句,设计两遍耗费的时间,并没有大多数人以为的那么多。
这个现象是 Ousterhout 在教书时发现的。他在斯坦福和伯克利都当过教授,这是世界上顶尖的两所大学,两边都有极其聪明的研究生。他注意到,这两所学校的学生普遍带着一种不太好的工作习惯。
原因在于他们从小到大一切都太容易。做什么都是最拔尖的,高中时比老师还聪明,大学时大概和教授一样聪明,是班里第一。脑子里想到的第一个想法,就足以拿到漂亮的成绩。
从来没有什么理由逼他们多想一遍。久而久之,他们形成了一种信念,认为自己想到的肯定没错。
等到问题越来越难,这个习惯就失效了。在顶尖大学做研究,题目非常难,没有谁的第一个想法就足够好。Ousterhout 常常要花力气逼学生往下想,常用的一招是这么问他们:
假设我告诉你,你刚提的这个方案不许做。你要是做了,我就停掉你的经费,把你从研究生院赶出去。那你的第二方案是什么?你是能想出一个来,还是只能说,好吧,我最好换个导师?
有意思的是,Ousterhout 通常在心里已经有更好的做法时才会这么问。等逼着学生回去想过、再回来的时候,第二个想法总是更好。
Ousterhout 自己身上就有一个相当漂亮的例子,来自 Tk 工具包的设计,那是 Tcl/Tk 系统的一部分。当时他需要定下用图形控件编程的 API 的形态。
Ousterhout 花了大概一趟长途航班的时间,做了两版设计。先做脑子里想到的第一版,随后对自己说,假设这一版不要了,有没有什么做法和它截然不同?于是又做了第二版。
两版比较之后,他选了第二版。Tcl/Tk 后来流行,原因之一就是 Tk 的 API 非常漂亮、简单、有力,而它是第二个进入他脑子的东西。
这件事一直激励着 Ousterhout。做新东西的时候,他通常会逼自己多想一种做法。哪怕觉得其中一种很糟,也把它想出来,跟已有的那版比一比,总能从中学到一些东西。说不定还会发现,那个糟糕的做法并没有那么糟。

同样的做法在工程组织里也出现过,只是换了个名字。Gergely 在 Uber 的时候,团队用设计文档和 RFC,要求作者写下为什么这么做、准备怎么做。RFC 指的是一份把方案写下来、交给同事评审的文档。
过了一段时间,他们开始加一项,叫「取舍」,也叫「考虑过的替代方案」。规则是至少写一条,能列出多条被考虑过、最终没选的方案就更充分。
加上这一项之后,设计、想法、计划都变好了,讨论也变好了,因为不选某条路的理由写得清清楚楚。
人们往往先写几条明显行不通的方案,这倒也无妨。但写着写着有时会突然发现,这条路其实很简单,直接用它就行,或者把两个方案合并起来。
代价的问题,Ousterhout 也回应了。多花一点时间是真的,天下没有免费的午餐。但这是相当高层的设计,并不需要把另一个替代方案完整做出来,只是在高层面上做比较。
Tk 的设计,Ousterhout 大概花了几天工夫去思考和比较两个方案,而实现 Tk 花了一年,甚至更久。算下来,设计大约只占整个系统建造时间的百分之一二。如果它能换来更好的设计,回报远远超过那百分之一二。
把这件事放到更长的时间尺度上看,团队是会学习的。一开始总是直接跳进编码,觉得这样更快。等复杂度上来了,才学到当初应该多规划一点。
再过一阵,风向又摆到规划过度那一侧。随后循环重新开始,这种摆动没完没了。人们至今还在谈论瀑布模型,那大概是二三十年前的事,如今其实不算个问题。瀑布模型指的是前期一次把计划做满、后面照着执行的做法。但快速迭代的团队对过度规划仍然反感,那看起来像是说得太多、做得太少。
Ousterhout 认同这种摆动,也给出了自己的边界。设计的根本在于它永远涉及取舍,任何一个想法推到极端,结果大概都不好,多做几版设计同样可以推过头。
真正优秀的设计者和普通设计者的分别,在于知道怎么做取舍,怎么把不同的想法、不同的阶段组合起来、平衡好。这类东西大概只能从经验里学。两种做法都试过,都看清了各自的坏处,最后才会对取舍有一种直觉。
回到那条分界线上,Ousterhout 说得更直接:
把优秀的设计者和不那么优秀的设计者区分开的,是他们知道怎么做取舍,怎么把不同的想法、不同的阶段组合起来、平衡好。

五、深模块与浅模块:两者的区别到底在哪里
这是书里被读者记得最牢的一个概念。书里对深模块的介绍是,它指的是那样一段代码、一项功能,或者说一个模块,接口相当简单,内里却很有深度,藏着大量功能与复杂度。
浅模块正相反,它有很宽的接口,做的事却不多,甚至透明到几乎不隐藏任何东西。
浅模块的代价落在调用者身上。接口宽,就意味着使用它的人要先学很多东西,还得随时记住那些细节,认知负担一直挂在使用方这一侧。认知负担,指的是使用者脑子里必须一直装着、腾不出来的那部分东西。
两个概念并排放,差别一眼看得出来:
书里强调,深模块对好的软件设计至关重要。Ousterhout 也解释过这个判断来自哪里,一切还是回到复杂度。书里所有内容都源自同一个问题,怎样消除复杂度,或者怎样管理它。深模块就是对抗复杂度的那个杠杆。
它起作用的方式很具体。模块提供一个非常简单的接口,使用它的人几乎没有认知负担,学起来很容易。与此同时,模块内部藏着大量功能和复杂度,外界完全看不到。
「深」这个说法想抓住的正是取舍。一边是接口的复杂度,另一边是模块里功能的多寡。设计要做的事,是用尽可能简单的接口,换到尽可能多的功能。这个取舍就是「深」字的全部含义。




六、通过定义来规避错误:错误处理的分寸怎么把握
书里有一章的标题叫「通过定义来规避错误」,讲的是错误处理。任何写过大量代码的人都知道,错误处理是复杂度的巨大来源。它指的就是那些特例,那些必须应付的古怪特例。错误处理很容易给软件压上沉重的负担。
Ousterhout 不断问自己的是同一件事,能否减轻这份影响。有些情况必须处理,有些异常根本躲不掉,它们是系统固有的,只能应对。也有些情况没那么重要。异常,指的是程序跑到一半出的岔子,需要单独写一段逻辑去应付。
那一章想论证的是一件事。抛出的异常更多,并不等于代码更好。有时候异常确实必要。
但 Ousterhout 见过不少设计者以为,从一个类里抛出越多的异常,自己就越像一个好程序员,显得更谨慎、更细致。
问题在于,每抛出一个异常,都是在把复杂度压给这个类的使用者。减少抛出的异常数量、减少制造的特例,系统复杂度就会下降。
书里把这句话说得很直白:
每抛出一个异常,都是在把复杂度压给你的类的使用者。
那一章里他举了不少例子。其中有些只需要对系统设计稍作改动,整整一类错误就消失了,根本不可能发生,也就没有错误需要处理。
Ousterhout 特意加了一句提醒。这种情况只是偶尔出现,而且必须非常小心。在他教的软件设计课上,每一届都肯定有学生误解这一点。
第一个项目里,学生的异常处理几乎为零。他们做的是一台机器可能崩溃的分布式服务器,连网络错误都不检查。他问,这里没有任何错误检查,如果某个系统崩了会怎样?
学生回答,我们把那些错误规避掉了。他说,不对,错误还在那里,学生只是忽略了它们,这样做不行。这一章很容易被带到错误的方向上去,它像调料,放一点点,菜的味道很好,放多了,整锅就毁了。
Gergely 对错误处理这个话题也有话要说。错误、异常、事情出岔子,这类话题在工程团队里谈得实在太少。
Ousterhout 做设计时会去翻故障复盘,很多次事故向上追溯,原因都落在错误处理上。故障复盘,指的是事后把事故原因一步步倒推出来的那份记录。在 Uber 是这样,在别的公司也一样。错误从某个系统传出来,他们却把它错误地映射成了成功。
这类边缘情况很多,误解了错误,或者误解了非预期的响应,本质上是同一件事。当时他们很难把问题定位清楚,也试过一些办法,比如把响应的映射做得更好,上白名单、上黑名单。
更常见的情形在规划阶段。人们总是偏乐观,讲的全是系统怎么运作、彼此之间怎么通信。
Ousterhout 很少见到有哪次规划会议,大家坐下来认真想,什么情况可能出问题,出了问题怎么捕获,怎么恢复。异常处理不在人们心上,往往是等人发现潜在问题之后,战术性地顺手处理掉的。

Ousterhout 给过一条通用建议,落到操作上可以拆成三步:
设计一个模块、思考要透过接口导出哪些特例和异常时,先去想调用方拿到这些异常之后要怎么处理。 把自己放进使用者的位置,看调用方实际上有几种处理方式。 如果本来列了 10 种不同的异常,而使用者只有两种处理方式,就把它们归并成两种,不留下十种。
关键是从调用方的立场去想这件事。
同理心与视角切换:把自己放进使用者的位置
顺着这条建议往下,他讲到了自己认为优秀设计者最重要的特质之一,有能力切换心态,从非常不同的视角去看同一件事。
设计一个模块的时候,他脑子里装着这个模块的全部细节。但他可以切换心态,去想这个模块的使用者,随后意识到,使用者不该对这些细节有任何感知。
使用一个模块的时候,他也不该利用自己可能知道的内部情况,只该用接口里提供的东西。某一刻沉浸在这个视角里,下一刻把它完全放下,换一个截然不同的视角去看,这种能力非常强大。好设计就是这么来的。
Gergely 把这件事翻译成了一个更常见的词。具备同理心,能把自己放进另一个角色的位置,不管那个角色是客户,还是将来接手这个模块的另一位开发者。这种能力会让人成为更好的软件工程师。
Ousterhout 正要说出同理心这个词。这套能力在社会情境里的价值,和在工程情境里的价值一样大,都是从别人的视角看事情。
Ousterhout 喜欢计算机科学的原因之一就在这里。人们觉得做计算机的是一群书呆子,可是计算机系统里用的很多思想,在社会系统里也有很有意思的类比。
Gergely 在自己的职业生涯里也验证了这一点。从学写代码,到上大学,到拿第一份工作,他最初以为难的是编码。
但 Gergely 聊过的每一位开发者、每一位工作超过一定年限的工程师,都会讲到同一个话题,软件工程和编程里最难的部分是人。
语法学会了,调试也会了,回头再想,做过的项目里最难的是哪一个,最大的麻烦是什么?答案往往是沟通不畅,彼此误解,需求说明出了问题。当然也有线上故障,可根本原因通常是没有预料到这件事会发生。

七、白板辩论法:让所有人的理由都上墙
在动手造一个系统之前,人们通常会把计划写下来。Amazon 以写作文化出名,一群人进到会议室里,先安静读完一份计划,再开始讨论。有的公司用共享文档,像 Figma 这样的公司用自己的工具画图,直接在图上评论。
这些做法的共同点是,在动手造系统之前,先把计划讲清楚,再让别人的意见进来。
问题也在这里。计划摆出来之后,多数人倾向于点头通过,真要有人站出来认真挑毛病,需要另外一套办法。
Ousterhout 自己的做法更朴素。他参与过的所有项目都会做设计评审,形式比较随意,没有冗长的书面文档,就是聚在一起谈想法。设计评审指的是把方案摊开来,让一屋子人当场挑毛病。做创业公司的时候也一样。能拉来多个头脑想同一道题,结果一定比一个人想更好。
这件事对聪明人来说另有一道坎。他们人生中很长一段时间所处的环境,让他们无法指望从别人那里得到有用的输入,成绩全部由自己决定。
等到在最高层面和一群同样聪明的人一起工作时,两个头脑显然胜过一个。所以他要劝学生放下过去那套习惯。
Ousterhout 自己完全赞成这种碰撞,要提防的只有一件事,别掉进分析瘫痪,得找到合适的场合、合适的投入时间。分析瘫痪指的是反复讨论、迟迟不肯落地的状态。把握这个度是一门手艺。
Ousterhout 直说自己喜欢白板,也喜欢开面对面的会。Gergely 补了一句,这类讨论最吸引人的地方,是人能当场拿到一个可以立刻试的办法。
Ousterhout 提到另一个变化。远程办公爆发过一阵,现在又有些收缩。2020 年以前,那些势头最猛的创业公司里到处是白板和可擦写板。
一个人正做着自己的事,旁边有人说了一句话,他停下来说等等,走到白板前就开始画。画完可以擦掉,有时留着,有时拍张照。触发点可以是任何事,可能只是有人听岔了。
创业公司试图用数字化工具复制它,但面对面把想法摊开来这件事有种特别的东西。聊软件这类话题时,方框和箭头确实管用。

Ousterhout 在职业生涯里还发展出一套用白板解决复杂问题的办法。这些问题未必是技术问题,也可能是公司里的管理问题。
Ousterhout 观察到,人们开会时常常各说各话,反复重复同样的主张和反对意见,绕来绕去,永远得不出结论。
遇到这种情形,他会站到白板前,把所有支持的理由和反对的理由一条条列出来。规则有三条:
任何自认为合理的理由都可以提,没有人能要求删掉它,说它站不住脚。 每条理由都上白板,所有人的理由都算数,每个人都有权贡献。 但不许重复白板上已经写着的理由。
理由全部摆在那里,所有人都看得见,讨论会自然地走到一个点,所有人都没什么可说的了。会议就此缩短。
之后他会做一个意向投票,这一步常常出人意料。意向投票指的是每个人按自己看重哪几条理由来加权,最后站 A 还是站 B。人们刚才还争得不可开交,旁观者会以为分歧大得无法调和。
做意向投票时,每个人按自己认为合适的方式给这些理由加权,自己决定看重哪几条、不看重哪几条,最后选 A 还是选 B。结果几乎总是一个非常强的共识。
Ousterhout 会坐在那样的会议里,心里想着分歧这么大,一投票出来,全场一致同意。Gergely 听到这里,说想找机会试一试,关键就在那一块白板。
Ousterhout 补充了这套办法的适用条件。它更适合用在一种环境里,那里的人总体上都聪明、讲道理、目标一致,比如创业公司,或者一支工程团队。
放到国会里,放到对立的政党之间,行得通吗?行不通。好在大多数人的工作并不在那样的环境里。
先设计,还是先做原型:两派主张怎么选
设计上有两派主张。一派认为前期少做设计,甚至完全不做,先造点东西出来,让代码自己决定方向。Gergely 问他站哪一边。
Ousterhout 站的位置更靠近设计这一侧。设计贯穿整个开发过程,前期要做,编码时要做,测试时要做,修 bug 时也要做。
两边的主张摆在一起是这样:
Gergely 在这里讲了一个他记了很多年的比喻。造软件有点像身处一片地形之中,要行军到某个位置。目标看得见,地形却是未知的。
有时走过去一路和预想的一样。有时走着走着,一块巨石不知从哪儿出现,翻过去,又来一块。有时走着走着,一颗地雷在面前炸开,突然多出一个大坑,而事先并不知道这里是雷区。
Gergely 对这个比喻很有共鸣,因为很多事取决于未知的未知有多少。做法也因此截然不同。做的技术栈或产品是自己熟悉的,和完全陌生的,是两回事。用的是刚发布的新技术,或者某个框架的测试版,可能有问题也可能能用,又是另一回事。
Ousterhout 自己的经验更偏向熟悉带来的好处。并不是运气好碰上了一片平地,而更可能是,以前走过这片山脉,所以知道哪些路能走。
比如要为一个新设备写驱动,而此前已经为五个设备写过驱动,那就大致知道主要问题在哪里,过程也会顺利、可预测得多。反过来,身处全新环境时,他说也许只是自己运气不好,但他从没有一次在新环境里走得顺利过,太难预测。
Gergely 也承认自己没有。勉强算得上新环境的情况,其实也不算真新。比如框架出了新版本但没大改,或者语言发布新版本、向后兼容,重要的部分都没动,只是多了一个新特性。这类情况有点假,跟以前差不多。

八、Ousterhout 和《Clean Code》的三处分歧:短方法、TDD 与注释
Ousterhout 和 Robert Martin,也就是人们常说的 Uncle Bob,在网上有过一次很有意思的讨论,围绕《Clean Code》这本书。两人对不同章节各自写了看法,其中几处意见相左。这三处分歧恰好能把 Ousterhout 的设计观照得更清楚。
这本书的中文版是《代码整洁之道》,人民邮电出版社 2020 年出的第 2 版。


三处分歧摆在一起,轮廓就清楚了:
分歧一:短方法与深模块
第一处是短方法,以及一个方法只做一件事。Robert Martin 非常推崇这一点,理由是它会带来更干净、职责更清晰的代码。
Ousterhout 的看法差得很远。设计的核心是取舍,任何一个想法推到极端,结果都不好。在他看来,《Clean Code》在很多地方就做到了极端,这是他最大的总体担忧。
方法长度是其中之一。两人其实都同意,过长的方法比短方法更难理解和处理,所以短本身有价值。但对他来说,方法长度本身并不最重要,重要的是深度,也就是把复杂度藏得够不够深。
《Clean Code》把短当成了没有上限的绝对善,越短越好。按它的说法,三行的方法优于五行的方法。
这样做的问题在于,也许那一个方法确实稍微好懂一点,但方法变短,方法的数量就变多,接口也随之变多。到最后,接口带来的复杂度反而让整个系统比原来更复杂。
更让他介意的是另一种情形。如果方法彼此纠缠,多个短方法互相依赖到不看着另一个方法的代码就读不懂其中一个,按那种风格,这也没有关系。
在他看来这没有任何好处,只会让事情更糟。如果两件事关系确实紧密,把它们合在一起反而更好。
Gergely 听到这里做了个小结。方法是短的、只做一件事的,这样的方法可以有很多个;同样也可以把它们合起来,做成更大但更合理的方法。两边都成立,区别只在于边界划在哪里。
这个概念恰好抓住了其中的取舍,也就是深度。要的是功能多,接口相对简单。有了这个标准,就不会朝任何一个方向跑偏。而像单一职责原则、只做一件事原则,它们只朝一个方向使劲推,没有任何边界,最后一定会出问题。单一职责原则指的是一个模块只负责一件事。
Gergely 顺着这个思路想到了业界在微服务上走过的路。他在 Uber 时,公司有五千多个微服务这件事被到处宣扬,他们手里确实有大量非常小的服务。微服务指的是把系统拆成许多各自独立部署的小服务。
几年下来,至少在他所待的支付领域是这样。小服务启动一个很容易,于是接二连三地出现,每个只做一件事、两件事或者几件事。
过了一段时间他们发现,维护起来真的太难,很难知道哪块逻辑住在哪里,大量的来回来去。于是开始把它们合并,做出中等规模的服务,各自负责一个领域。
一个极端听起来总是很好,一开始也确实有它的好处,时间一长就不实用了。反过来同样成立,做一个什么都干的巨型服务也不好。
Uber 以前有过这么一个东西,它叫 API,什么都干。后来它被拆成更小的部分,随后又合回来,变成按领域划分、规模合理的若干服务。
这样人脑子里才装得下,才知道这里有哪些部分,能走进去理解其中某一个部分。所以存在一个度的问题,什么时候这样划分才合理。
Ousterhout 承认两边都会犯错,但如今人们往往在过度分解这一侧犯错。在他看来,《Clean Code》主张的就属于过度分解,而它带来问题。
Ousterhout 常跟学生讲,很多时候把东西合起来,反而能让它更深。如果两件事关系本来就紧密,把它们合进同一个类、同一个方法、同一个模块或者子系统,会得到功能叠加、整体 API 更简单的结果。
这样也不会留下两个彼此有大量依赖的独立事物,那种情况很糟糕。当然,合并也可以做过头,一切都是取舍,但把单元做大一点,往往能让事情变好。
分歧二:测试驱动开发
第二处分歧是测试驱动开发。Robert Martin 非常推崇用 TDD 写代码,先写测试,再写让测试通过的代码。这个方法在 2000 年代初很流行,后来有点过时。他自己不推崇 TDD,理由很直接,它和设计是冲突的。
测试本身他并不轻视。他喜欢单元测试,做的每件事都写单元测试,认为它们不可或缺。作为负责任的开发者,就该写覆盖率很高的单元测试,这一点两人有共识。
分歧在于开发过程围绕什么组织。他希望开发过程以设计为中心,开发中的每一件事都应当围绕如何得到更好的设计来安排。
TDD 与此冲突,因为它鼓励极小增量的设计。它的循环是这样:
写一个测试。 实现功能,让这个测试通过。 再写一个测试,再实现。
整个过程中,没有任何一个环节鼓励人退后一步。没有人去打量整体任务和全局图景,也没有人去想这些碎片该怎样拼在一起。最后落到手上的,是十个各管一段的方案,而不是一个能一次解决十个问题的干净架构。
这样做的风险是产出一种极端战术化的开发风格,最后得到的是一团糟。
两人为此来回争论了很久。他认为 Bob 推崇 TDD 的主要理由是,它能保证测试被写出来,因为测试必须先于代码。
这一点他也同意,测试应该写。但对方没能说服他,先写测试除了保证测试被写出来之外还有什么特别的好处。他看不出任何理由能让设计变好,却能看出很多理由会让设计变坏得多。
Ousterhout 抛出了一个更根本的问题。开发的单元应该是什么?开发总是按一块块工作推进,这些块又该多大?他的答案是,这个块应该是抽象,而不是单个测试。测试的粒度太小了。抽象指的是把一类问题的共同部分拎出来,用一套统一方案覆盖住的那一层。
想得足够大,才能权衡取舍,才能给出一个比较通用的方案,一次解决很多问题。他还顺带提到了设计里最重要的要素之一,尽可能把自己推向通用,避免特化。
而 TDD 的做法鼓励人为通过某一个测试做非常特化的实现,而不是去想那个能一处解决很多问题的通用方案。

Gergely 回忆了自己见识 TDD 的一次经历。有一次他们做 TDD 集体编程,键盘在几个人手里传递,一个人写测试,另一个人让测试通过。
Gergely 感觉有点做作,因为一旦多写了点不需要的代码,旁边的人就会说,别写那个,只要让测试通过就行。
当然他相信有些场景它是有用的,也许在更正式的做法里,也许用在以扩展既有代码为主、业务逻辑很重的地方。但它慢慢变得不那么流行,大家不再多谈,也许是有原因的。
Ousterhout 也给出了一个自己会推荐先写测试的场合,修 bug 的时候。他会主张先写一个能检出这个 bug 的测试,再去修。
不过他承认自己是作弊的,先写修复,再写针对修复的测试,随后把修复回退掉,确认测试会失败,这样他就知道了。
分歧三:注释
第三处分歧是注释。Uncle Bob 的观点是,代码里的注释应该越少越好,代码本身应当说明一切。
Ousterhout 的回应留了余地。如果代码真能完整说明自己,那当然很好,他不会反对。但依他看,代码做不到,也永远不会做到。有太多东西是代码本身描述不了的。
他把这个判断说成了一句:
如果代码真能完整说明自己,那当然很好,我不会反对。但依我看,它做不到,也永远不会做到。
他们讨论过具体的例子。对方指着一处代码说,看,这段代码不需要注释,对吧?他回答,这里人们会问的五个问题,代码里一个都没有给出答案。对方对此就有点含糊其辞了。
在他看来,对方对注释有非常强的偏见。其中一个论点是,人们被过期注释误导所浪费的时间,多于因为缺少足够注释而浪费的时间。这显然和他的经验不符。
过期注释偶尔确实存在,但很少见,而且即便过期,里面通常也保留了有用的信息。
另一个论点是,如果项目只由几位开发者经手,他们不需要注释,因为大家都记在脑子里。这一点他不能同意。也许对方的脑子比他的好,但他没办法把所有细节都记在脑子里,代码写完几周之后他就忘了。有太多东西是代码本身拿不到的。
Gergely 问起他自己的注释做法。首要原则是,注释应该告诉读者代码里看不出来的事。外面有大量糟糕的注释,只是把代码里显而易见的东西重复一遍,这种场合完全不需要注释。
在他的课程项目里,他经常把学生的注释删掉,告诉他们,这条注释只是把代码重复了一遍,不需要它。注释最重要的位置是接口。
接口之所以存在,是因为设计者不希望别人为了使用它去读那个东西的代码,只想让人看接口。而一个模块的函数签名,根本不可能承载使用者需要的全部信息,所以这里的注释极其重要。
排在其后的是类成员变量的注释,这需要相当详尽的说明。至于方法内部,他通常不太需要注释。如果知道这个方法要做什么,代码本身通常已经说得比较清楚了。
Ousterhout 会在事情棘手的地方写注释,或者写下那些当初发现 bug 时才明白的反常之处。但他的方法经常一条内部注释都没有,只有接口注释。

Gergely 把话题推到当下。AI 工具生成越来越多的代码,而且经常整段整段地生成方法,这场关于注释的争论可能会重新热起来。
Gergely 注意到,当他让某个 AI 助手生成一段做某件事的代码时,它写完了代码,还常常顺手加上行内注释,解释这段代码的作用。这种情况下,注释反而帮了他,因为这些工具会胡编,他确实想弄清这段代码的实际行为。
至于模型为什么这么做,也值得追问。它们多半是在网上现成的代码上训练的,开源代码,或者来源不明的授权,大概是在照抄见过的写法。不过通常情况下,他同意 Ousterhout 对注释的判断,只是在这个场景里,注释也许不是坏事。
Ousterhout 对这一点很谨慎,他说这只是一句猜测,也可能是在为自己的坏习惯找理由。但他确实发现,AI 工具在某种程度上弥补了注释的缺失。
Ousterhout 最近在 Linux 内核里工作,做一个新的网络传输协议。Linux 内核不是糟糕的软件,但在他看来注释严重不足。他花了很多时间试图弄清 Linux 内部的运作、接口的形态,以及自己该从哪里接进去。
ChatGPT 成了他最常用的帮手。它在回答 Linux 内核问题上能力强得惊人,那些问题他用 Google 搜索或者别的办法都查不到答案。它不总是对的,有时会胡编,但经常是对的。即便错了,通常也能把他带到正确的附近,帮他找到该去哪儿查。
Ousterhout 认为 AI 工具也许能帮上忙。可即便如此,如果 Linux 的开发者当初稍微花一点点时间写上注释,事情会比用 ChatGPT 去猜容易得多。
所以他不认为对注释的需求会彻底消失,也不该把 AI 工具当作不写注释的借口。现在还不到那个时候。

九、设计能不能教:一门课、一本书和一个没做完的项目
《A Philosophy of Software Design》这本书的来路,要经过他在斯坦福开的那门课。Ousterhout 喜欢写代码,是少数至今仍写大量代码的教授之一,尽量每年写上 5000 到 10000 行。

Ousterhout 也喜欢设计,认为设计是人类历史上出现过的最惊人的创造形式之一,是一个迷人、优美、充满挑战的领域。
Ousterhout 经常琢磨什么是好的设计,怎样才能设计出好的软件,有哪些技法。时间久了,他震惊地发现,没有人教这门东西。
除了他后来在斯坦福开的那门课,据他所知,全世界没有任何一门课以软件设计本身、而不是工具或流程,作为课程的主要元素。
这件事在他心里硌了十几年。回到斯坦福之后,他手里的业界经历更多,软件开发经验更多,想法也更多,于是决定不管结果如何,先试着开一门课。开课逼着他梳理自己的想法,试着把它们归纳成几条简单的原则。

Gergely 从招聘的角度补充了一点。科技公司通常不会在面试应届生、或者只工作过几年的人时考察软件架构,一般只考编码,随后才是复杂系统的架构与设计。
原因是,人得在行业里干上几年,才会对这件事有感觉。Gergely 感觉 Ousterhout 把这件事倒过来了,用一门课尝试证明,设计是可以教的,哪怕对象是刚学会写代码的学生。
这门课怎么上
Ousterhout 借用了高中学英语写作的模式。那时的作文课是这样的,拿到一个题目,写一篇东西,老师逐句批注,学生重写再交,可能要改好几轮。
其中关键的地方有两点:
拿到反馈,有人愿意批评自己的作品。 重做,把反馈吸收进去。
正是在重做的过程里,学生才真正把反馈和那些概念内化。
这门课持续一个学季,学生大致分三个阶段做出一个像样的东西,中间穿插大量的代码评审。所有团队的每一行代码,他都会读。
前两个项目恰好是 Raft 共识协议,分两个阶段做。Raft 指的是一套让多台机器对上同一份数据的算法。事实证明,它里面有一些非常有意思的设计问题,学生常常被绕进去,犯下大量错误。
整个过程从零开始,一开始 Ousterhout 不给任何线索、提示和结构,学生必须完全靠自己动手。这是他们有生以来第一次做这种事,既好玩又吓人。
接下来是密集的评审。学生在课上互评,读同学的项目并给反馈。他会读每一行代码,和每个团队花大约一个小时。
学生写的代码大约两三千行,通常能收到他 50 到 100 条评论。之后他们回去重做。学生的顿悟时刻就出现在这里。
拿到反馈的时候,他能指出代码很复杂。代码复杂这件事人们一般都知道,但他能指出它为什么复杂,是因为没有遵循课上一直在讲的那条原则。他也能指出,如果应用那条原则,可以怎样把它变简单。
第二个项目交上来,比第一个好太多。学生自己也很兴奋,因为他们能感觉到自己的力量,能感觉到自己让这个东西变好了很多。
Ousterhout 原本担心学生会怎么看这门课,结果学生的体验极其正面。一个学季下来,可以看出他们思考软件的方式发生了很显著的变化。
学季结束时他会提醒学生,等进了公司,会发现公司里的人不懂很多东西,甚至包括比他们资深得多的工程师。接下来他们会讨论该怎么应对,因为作为新人,未必有多少能力去改变一家公司。
Ousterhout 完全相信设计是可以教的。经验仍然需要,上完一个学季的课不会立刻成为一流程序员。但这门课能把这个过程启动起来,给人以一种思考软件的新方式,是别的课上从没想过的。
这句话他说得很肯定:
我完全相信设计是可以教的。经验仍然需要,上完我一个学季的课,你不会立刻成为世界级程序员。但我认为这门课能把这个过程启动起来,给你一种思考软件的新方式,是你在别的课上从没想过的。
Gergely 在这一点上补了自己的经历。他刚入行的时候,一位资深开发者交代任务,说这件事要做,规划已经做完了,技术选型也定下了,让他负责实现。他当时就有不好的预感,但还是照做了。
事情越变越糟,越来越复杂,问题出在某个自研数据库上,它有性能问题。后来那位资深开发者抽身走了。几周之后客户很不满,因为一个页面加载要十五秒,完全不能接受。
Gergely 用一个周末,大概工作了二十多个小时,按自己的做法把整个东西重写了一遍,所有问题都解决了。当时他的感觉是自己可能疯了,因为一位有经验的人明明让他那么做。
他那时没有词汇去描述问题,对自己也没有那份信心。这种事每个人最终都会经历,得自己烧过一次。
刚入行的头几年,人只能靠吃亏来学。走完这一遭之后,再遇到同样的情况,人就能把话说清楚了,也能更有底气地讲明为什么那样做有问题。Gergely 觉得这门课给了学生一条捷径,让他们避开一些坑。
犯错是有效的学习方式
Ousterhout 把这件事看得更透。几乎所有学习都跟犯错有关,最有效的方式是犯错,理解它为什么错,随后把它改对。
在他看来,教育就是创造一个安全的空间,让人可以犯错并从错误里学。他在课上把这件事讲给学生听,也提前打过招呼。
「你们会收到大量的批评,代码里每一个小毛病我都会挑出来。有些地方被挑出来,你们可能会生我的气,但我还是要把每一处问题都指给你们看,因为我要你们知道它们的存在。」
Ousterhout 希望学生从这门课里得到的一件事,是意识到这是一种非常有效的经历。在他看来,有人来挑战他、审视他的代码、给他反馈,其实是一件好事。
因为外面很多开发者对批评很敏感。他们担心,有人批评自己的代码,就意味着自己不是个好程序员。他们也会想,如果非要想到第二个方案,是不是说明自己不够聪明。
只有笨人才要把事情做两遍,聪明人第一遍就做对。于是他们抓住这个念头不放,绝不承认自己的第一个想法不够好。犯错这件事本身是重要的,也是建设性的,作为工程师应当尊重它。
Ousterhout 手上正在做的事
他仍在大量写代码。对话快结束时他提到,回去就要接着写。
一位叫 Behnam Montazeri 的博士生,六七年前的博士论文做了一个新的传输协议,叫 Homa,面向数据中心的场景。论文里的结果非常漂亮,在很多有意思的场景下,它比 TCP 快 10 到 100 倍。
Ousterhout 不愿意让它像大多数博士课题那样做完就没人管。于是他把它接过来当成自己的项目,想看看它能不能被广泛使用,替代数据中心里的一部分 TCP 流量。
他写了它的 Linux 内核实现,持续改进,现在正在把它往内核里合并。手头要处理的是内核开发者给出的大量评论,努力把他们指出的问题都修掉。他特意说,这正是代码评审的实例,也说明代码评审有多大价值。
这是他第一次参与 Linux 内核。事先有人提醒他,说这件事可能又难又痛苦,还很耗时。确实花了不少时间,从第一次提交算起,到现在可能快六个月。
但他必须说,那边的人非常讲道理,他收到的反馈质量很高。他改了不少东西。那些问题要么是他误解了内核的某个机制,要么是对方指出了更好的做法,要么是对方发现了他设计里的复杂之处。
到目前他对这个过程没有意见,其实挺满意,Homa 因此变好了很多。他希望最终能走完流程,进入内核。
被问到这些经历值不值,他的态度很干脆。他认为这些都是很好的学习,换成别的方式,他还不愿意。
第二版改了什么
这本书初版于 2018 年,到现在快七年。有人问他,如果今天重写,哪些部分会补充、重写,或者干脆删掉。
Ousterhout 先交代了书的来历。他当初并没有打算写这本书,书是那门课之后才有的。他开始就这门课做演讲,听众说:「你该把它写成一本书。」2017 年或 2018 年他有一年学术休假,就用那段时间把书写了出来。
写完之后,有些看法他调整过,第二次修订版已经把其中大部分改掉了。他希望读者手上的是第二版,如果书是最近两年买的,那很可能还是第一版。
书的内容没有大改,最大的变化是更强调通用、消除特化这个想法。这一点来自那门课,是多年观察课程项目得来的,并没有写进第一版。他意识到,这个想法对教学生写出不那么复杂的代码非常根本。
眼下没有什么大东西他想改。他反而点出了书里最危险的部分,异常处理那一章。那一章很容易被误解,被应用到他自己也不认同的方向上去。如果从头重写,他可能会对那一章多想一想,把措辞改得更谨慎,帮读者避开误读。
他也提到,书里有些判断是那门课磨出来的,来自多年对课程项目的观察。
写书时他抱有一个期望,希望读者带来一些他没想到的新想法,他能从中学到东西,进而生出更多想法。到目前为止,这件事没有怎么发生。
Ousterhout 很希望有人跑来对他说一句:「你完全错了,书里这个想法完全错了,原因是这个,你应该那样做。」这类反馈比他期望的少得多。
对于这一点,Gergely 给了一个解释。那门课本身很特殊,现实中的软件设计通常是另一副样子。遇到一个问题,做出一个方案,也许在白板上探讨一些取舍,随后把它建出来,接着往前走。之后可能再回来修一修,长期维护,也在这个过程中了解它。
人不会有那种理想的情境,比如有两三支不同的团队做不同的东西,随后把它们放在一起比较。在业界没有这种余裕,即便那样做有点浪费。所以从来不会有机会把同一件事重复做一遍。
Gergely 猜,在那门课上这件事做得到。一群学生走一遍,同一个问题,大家用不同的方式解决,差别就看得见,学生自己也看得见。这种设置也许只能发生在学术界,或者非营利机构,某个允许这么做的地方。
也正因为这样,这本书才特别。很多软件架构书走的是同一个路数,作者在若干个项目里发现了一些东西,而那些项目都是孤立的例子。这本书不一样,它说的是在学生身上反复看到某种做法效果更好,照做的人结果一次比一次好。
Ousterhout 补充了那门课的规模。课上有 9 支队伍,每支两人,做同一批项目,随后互相评审。
代码评审的时候,每个学生会挑两份别人的项目来读,读其中很大一块内容,不必读完整,随后在课上讲评。搭档也在评审另外两个项目。加起来,两个人基本看过班上其余工作的一半。
这也是一个很好的学习来源。学生很喜欢读彼此的代码并琢磨它们。
第一个项目之后,他们跑来问,修订自己的项目时,能不能借用另一个项目里的想法,因为通常课上不许用别人的想法。他说,当然可以,他鼓励学生去拿,去消化吸收所有人手里更成熟的想法。
在这一点上,学术界做得可能比业界更好,因为业界没有时间做修订。没法让一个人每周投入十五个小时、连续十周去做这样一件事,他们另有太多别的事要做。但产出非常好。
Ousterhout 希望推动的事
有人问他,软件设计乃至软件工程的历史上,有哪些想法可能在不久的将来复兴。
Ousterhout 说自己不确定。他想不出哪个曾经出现又消失的想法会重新回来。有些想法之所以消失,是因为人们学会了更好的做法。但愿大家是在越做越好,而不是在好和坏之间来回摆。所以他想不出有什么被遗忘的想法会重新登场。
被问到推荐书目时,他很坦率,说自己喜欢的东西不多,有那么几样,但记不住书名了。这个回答本身也说明,专门写给软件设计这个题目的书确实稀少。他个人的页面里有一个链接指向软件设计这本书,页面上有一小段内容,底部列了几本别的推荐。
至于希望读者为他做点什么,他的回答是欢迎建设性的批评。Ousterhout 从不说自己掌握了全部答案,他有的只是自己的经验和自己认为学到的东西。软件设计这件事还远没有做完,另有大量东西等着被发现。
如果有人认为他说的某处是错的,他很想知道哪里错了,以及为什么错。
Ousterhout 想做的事只有一件,无论是在课上还是在书里,推动开发者群体对软件设计多一分觉知,让人们去想它、谈它。如果能做到,他希望大家建造的软件在设计水准上能整体提高。
回到最开始的那个判断,AI 把写代码变便宜之后,省下来的时间花到哪里去,是个人的选择,也是一支团队的习惯。
Gergely 最后补了一句观察。更多的代码正在被生成出来,这是大势所趋。设计在各个层面都会变得更要紧,资深的工程岗位如此,自己动手做东西的人也一样。
Ousterhout 给过一个成本最低的动作,任何人第二天就能照着做。动手之前,先逼自己写出第二个方案,哪怕它看起来很糟,也写下来,跟第一个比一比。
Ousterhout 算过这笔账。在 Tk 那样的系统里,设计大约只占整个建造时间的百分之一二。换来的,是一个用了很多年、被许多人称赞漂亮而有力的接口。

术语勘误表
素材来源说明
本文基于 The Pragmatic Engineer 播客访谈《The Philosophy of Software Design》改写,主持人为 Gergely Orosz,嘉宾为斯坦福大学教授 John Ousterhout。
中译本
这本书有两个中文译本,一个是官方授权的简体版,一个是社区民间译本:
这两条译本信息建议收藏,日后翻这本书时能省一趟检索。整篇内容对读者有用的话,也不妨先收藏,等下次动手改项目时再翻出来对照一遍。
#软件设计 #软件架构 #AI编程 #代码复杂度 #深模块与浅模块 #技术债 #设计两遍 #测试驱动开发 #程序员成长 #软件设计哲学