一个和 low level design / detail design 相关的youtube 演讲视频 , 作者是斯坦福的教授 , 主要内容是和平时工作息息相关的程序设计, 如何避免写出面条代码 , 又如何避免过度设计 , 作者给出了几条需要避免的情况 (red flag) , 如果代码中出现了类似的情况, 就说明需要作出修改了 .

这个思维方式和之前看的查理芒格说的逆向思维很类似 , think how to avoid something , not how to accomplish it . 查理芒格所举的例子是"如何降低气象对飞行员的影响" , 逆向的角度则是"如何使飞行员坠机" , 得到一系列能让飞行员坠机的方式后 , 去避免这些情况发生 .

受到之前 mit 6.s081 文字翻译版作者的帮助和启发, 文字翻译并整理了视频中的演讲内容 , 方便复习回顾

原视频youtube链接

CS190 课程主页


​ A Philosophy of Software Design

好的,欢迎大家早上好,我很高兴欢迎Osler教授来参加谷歌的会谈,我叫Porter,我在技术基础设施云方面工作,非常快速地介绍约翰,约翰是斯坦福大学的计算机科学教授,在此之前他在工业界工作了14年,在那里他创立了两家公司cryptic sand , electric cloud,在此之前在UC Berkeley当了14年的教授,这里有人曾经和约翰上过课,他的一群学生。我不打算在此列举他的获奖名单,国家工程院院士、ACM软件系统奖、加州大学伯克利分校杰出教学奖等等,但与本次讲座更相关的是,约翰建立了几个有影响力的系统,如sprite操作系统、pickle TK log结构化文件系统、raft和最近的ram cloud,而今天你将听到约翰所有的一手知识的综合。

这个话题与我们非常接近,如何设计程序和如何不设计糟糕的程序 ,有一本书《软件设计的哲学》,我很幸运读到了这本书的预印本,这是一本非常好的书。最后,如果你有问题,因为这是在录音,请举手,我们会把麦克风给你,然后你可以提问。因为会有很多问题,我很乐意在我们进行的时候把它们排在一起,可能会有很多问题,但是在演讲过程中,你可以自由地提问,怀疑,争论,或向我的母亲说脏话,不管你想怎样,但是一定要确保侮辱性的东西被记录在麦克风上。

人们对计算机进行编程已经有80多年了,但软件设计基本上还是一门黑科技,对于如何进行软件设计,甚至一个好的软件是什么样子,基本上没有共识。 但我们几乎没有谈论过设计软件的基本行为,这让我很不爽,所以今天我想告诉大家我一直在做的一些事情,试图改变这种状况,开始这方面的交流,看看我们是否能在软件界创造更多更大的设计共识,这包括我在斯坦福大学开发的一门新课程,然后在此基础上编写了一本关于软件设计的书.

好吧,让我们从基础开始,如果你必须挑选一个想法,一个概念,这是所有计算机科学中最重要的东西,一个贯穿计算机科学各个方面的东西 从人工智能到系统到理论,首先你会选择什么 , 抽象性? 观众回答: 测试(Testing), 组成(Composition) ,复杂性(Complexity) . 所以我问过Don Knuth这个问题,他说抽象(Abstraction) ,我想说这几乎是正确的,虽然有些人会说从定义上来说它是正确的,我想说我的答案是问题分解(decomposition),你如何把一个复杂的问题或系统切成碎片,你可以相对独立地构建,然而据我所知,除了你今天要听的,没有任何课程会把这个概念作为课程中最重要的想法之一,我们根本没有 . 第二,我们都知道有些程序员的工作效率比其他人高得多,谷歌给了一个专有名词10倍程序员(10x programmer),但据我所知,也没有人试图教授这些技能。这些想法一直在我的脑海中徘徊,困扰了我很久,我们如何才能拥有这些如此重要的知识概念.

问题是从来没有人教授这些东西,你知道,如果你是一个伟大的程序员,你就会自己想办法,所以这导致了一个问题,我们能不能做到这一点,有三个问题,第一,这是否可能,你得知道这是否只是一种天生的本领,让我们做一个简单的调查,有多少人认为这是一种天赋,你知道,要么你有,要么你没有,你不能真的教它,它只是你的一种天生的技能,有多少人认为这可以通过某种方式来教 .

几年前,我读了一本非常好的书,作者是Geoff Colvin,书名是《天赋被高估了》 (Talent is overrated),如果你想读一读. 他通过一项又一项的研究表明,在这些领域,我们认为有些人是非常有天赋的, 然而事实上,唯一能区分顶尖人才和普通人才的是他们的练习次数,这是他们能找到的唯一 一个一致的相关因素,这都是练习的结果。当今大多数教师的问题是,典型的教师在研究生时写了一点代码,但从未得到任何指导,你为你的论文抛出了一些蹩脚的代码,然后你成为一名教授,然后你决定编码是研究生的工作,所以你停止编码,你再也没有写任何代码。所以你知道今天成为一个伟大的开发者和学习设计技能的唯一途径是大量的个人经验或试验和错误,教师们却没有这样的经验,所以他们不能教他们的学生,所以整个过程只是不断地重复,但幸运的是我不是这些教师之一, 所以也许我有更好的机会做到这一点,所以就我个人而言,我一直在编程,而且编程是我生活中最重要的两三件事之一 : 我的家庭,编程,一个非常糟糕的高尔夫比赛 . 在我的职业生涯中,我大概写了25到30万行代码,所以我有机会观察了很多代码 ,而且我一直在思考设计的问题,所以我想也许你知道任何人都可以在学术界做到这一点,我可能有和任何人一样好的机会。

第三个问题是,你是如何做到的?所以我把这两个三个问题结合起来,最后我决定只有一个办法,那就是尝试一下,也许我会崩溃,但我要试一试。迭代的方法,所以你知道,你的写作方式是你写东西,你的老师给你打分,你再从他人获得反馈 ,你重写,他们再打分,所以在整个过程中迭代了好几次,这就是接受批评的过程,结合批评和学习,看看如何让事情变得更好,这就是你如何成为一个好的作家。所以我想我们将在课堂上尝试同样的想法,所以这就是它的工作方式,现在我在一个季度系统中只有10个星期,所以我们只能做这个周期的三次迭代,但它的基本工作方式是在课程的前几周,学生以两个人的团队建立他们可能在三周内建立的最大的系统,通常约为2。000到3。000行代码,仍然不是一个巨大的系统,然后在第三周后,我们进入代码的新阶段,学生们阅读每个代码,写评论,并阅读所有学生写的每一行代码,这是现在课程的限制之一,所以我最终在那一周阅读了两到三万行代码 然后我们在课堂上做代码审查,学生将展示他们的项目,其他学生将对其进行批评,然后我做更长的代码审查,我阅读每个人的项目,我通常对每个团队做50到100条评论,然后我与团队单独会面一个小时第二阶段是他们修改代码审查,他们实际上增加了一些额外的功能,在第二阶段我们做另一轮的代码审查,然后第三阶段他们得到一个新的项目,他们从头开始,我们定义他们做第三轮的代码。当他们这样做的时候,学生们没有从我这里得到任何关于如何设计的想法,我只是告诉他们系统要做什么,他们必须从头开始,自己想出一切,所以他们在第一阶段会犯很多错误,但这也是课堂学习的一部分.

Pasted Graphic

观众问: 我观看了你在 youtube 上发布的相关视频, 在之前视频中的第三阶段,学生们通过迭代其他人之前的项目继续完成工作,为什么你要改变它?

教授回答:问题是,人们最终花了很多时间来解码别人的项目,并对其感到沮丧,这有一定的价值,但他们没有花很多时间来做真正的设计,所以我最终决定,如果我真的想教人们设计,我认为他们会学到更多,如果他们能从头开始,做一个新的设计如果他们从头开始做一个新的设计项目,我改变了它(教学流程),我可能会改变回来,我将尝试一段时间.

好的,这就是课程的基本想法,现在你可能想知道,这里面有什么魔法(magic secret),你如何做软件设计,顺便说一下,是什么让我认为我知道这些魔法?首先,我并不声称我知道所有的答案,我有一些想法,这些想法多年来在我的脑海中凝聚,我在书中的课程中使用了这些想法,实际上我更希望使用这些想法来开始讨论,并随着时间的推移不断发展,但在课堂上,大约有十种高级的想法。我不能给你一个食谱,然后你按照这十个步骤,你会产生一个伟大的设计,我不确定是否有这样的食谱. 所以当学生在做第一轮设计的时候,我谈到的这些模糊的想法,我不确定它们是否能很好地被学生们沉淀下来,因为它们太抽象了,可能很难弄清楚如何应用它们。因此最好的工作方式是,当我们做代码审查时,我可以向学生展示他们是如何违反这些原则的,以及他们的代码的后果是什么,他们应该如何改变它,然后他们可以返回去,在修改时应用这些原则,然后我认为想法从这里开始沉淀,所以这是一个问题.

Pasted Graphic 1

观众问:是的,我很好奇你是怎么想的,我很好奇你怎么看待软件设计中的端到端原则(end to end principle),即试图将所有的复杂性推到系统的边缘,以TCP的典型例子,校验是在端到端完成的,而不是在中间的路由器阶段,这是他们的一个设计原则

回答:我得考虑一下,我不确定这是否与这些原则有直接关系,但可以考虑一下,所以这些模糊的原则,然后我尝试做的另一件事是谈论红旗(red flag 危险标志),所以红旗是非常具体的东西,如果你看到这种行为或模式,你可能就有麻烦了,我想对于初学者来说,red flag对人们来说真的很有用,因为即使你不知道如何设计正确的系统,如果你能看到你正在出错,那么就尝试其他的东西,直到最后红旗消失,你可能会在一个相当不错的地方结束(收敛)。

Pasted Graphic 2

其中两个是设计原则,Classes should be deep (如图) ,Define error out of existence (设计出一种更好的语义来减少正常情况下出现的错误类型 , 下文会讲到 , Windows 文件系统和 unix 文件系统设计作对比 ) . 这两个更多是关于思维方式(mindset)的,我会首先谈谈这些.

所以第一个是这个概念,即Classes should be deep的,这实际上只是另一种思维方式,这个想法是David Parnas在70年代初首次提出的信息隐藏的想法,对我来说这篇论文(论文链接)是最重要的两篇经典论文之一,所有的软件设计和工程,如果这里有任何人在课堂上读过这篇论文,那就太好了,它仍然具有现实意义。今天,后三分之一也许不是那么重要,但第一部分仍然是,它有一个很好的例子,而且很容易读懂,所以我的想法是,把一个类看作是一个矩形,矩形的面积是该类提供的功能,所以你可以认为这是该类为系统的其他部分提供的好处,然后考虑顶部的边缘,因为那是该类的接口,我说的接口是指为了使用该类,人们必须在头脑中拥有的一切,这不是函数的签名,副作用 , 这是真正的成本,你可以认为这是这个类对系统其他部分施加的复杂性成本,所以我们希望它尽可能小,所以理想情况下,你希望的是最大的利益,最小的成本,所以你喜欢最小(少)的接口,然后最大(多)的功能(蓝色部分) 。事实上,在最坏的情况下,接口的额外开销比你隐藏在接口下的东西还要复杂,这是一个净负值。我们希望有一个深层类,非常简单的接口,下面有大量的功能。所以深层类是很好的抽象,这个概念你可以应用于类,也可以应用于类中的方法,还可以应用于一般的接口或系统中的模块或子系统,任何有接口或实现的东西。当学生们做第一个项目的时候,我们没有足够的时间让我在他们开始设计之前把所有的想法都说出来,所以当他们在设计的时候,我们就只在课堂上讨论这一个重要的 mindset

Pasted Graphic 3

这是一个典型的浅层方法的例子,我不得不说,我经常看到这个方法中基本上没有信息隐藏,为了使用它,你几乎需要了解完整的实现,顺便说一下,它是如此糟糕,以至于你在调用这个方法时需要更多的按键,如果你只是自己做函数的主体,那么它基本上是一个完全的损失,只是增加了复杂性而没有得到任何回报

另一个例子是一个相对较浅的类 (shallow class),我经常看到的是一个列表操作的类,这是非常浅的,我的意思是列表真的很容易操作 : 两个指针,指针就隐藏在类下面某个地方

然而即使是一个非常浅的类,你也不能总是尝试消除它们,你知道有时你能做的最好的就是一个浅的类,所以我不会说你永远都不应该有它们,但是从设计的角度来看,一个浅的类并不能给你带来什么,它不能帮助你对抗复杂性,

(我的理解, 观众的问题是比如 MVC 分层中多出来的一些 Repository 层的额外层 , 是否有必要 , 以后是否会更改数据库实现方案? 教授的答案是向前考虑一点, 但不要考虑太远 , 因为我们不能很好地想象未来的事情 , 额外的层数只会徒增复杂度 , 综上, 我觉得大多数项目的 Repository 层是没有必要的)

观众问:所以我认为有一件事也许有点微妙,就是类的大小不是现在的代码量,而可能是你最终必须增加的代码量,或者如果你必须对它进行多种不同实现,那么也许这在孤立的情况下是没有意义的,但也许还有其他的东西,那就是如果它现在或将来只是在数据库中存储东西或类似的东西,那么我想知道你是否不同意这样的描述,即在决定类的深度时,你需要跟踪类的发展。

答:事实上,软件设计背后的整个理念是我们在为未来做事情,我们今天做的事情是为了让我们在未来更容易开发,所以你必须提前考虑一下,当然,你知道软件的典型问题是我们不能很好地想象未来,所以试图想得太远是很危险的,但在这个特定的案例中,我同意你的概念,所以问题是接口(interface)已经很特定(specialized),只是他们几乎可以肯定,你通过实现所改变的任何东西都可能会改变接口,所以它可能不会帮助你,但你知道,如果你有理由相信它会帮助你,是的

Pasted Graphic 4

顺便说一下,我认为这是人们犯的最大错误之一,即太多太小的classitis类,人们这样做的原因是他们被教导错误的类和方法行数和数量 , 你们中有多少人在某些时候在某些课程中被教导过你们的方法应该是小的,你们中有多少人被给予过一个数字,比如任何大于和线的方法,你们应该把它切碎,你们中有多少人听到过这样的事情,说好吧,告诉我一个小的n值,你们听到过什么 , 20行,有人听到过10行吗,我听到过我有时听到过10,所以如果你把这个带到极端,就会导致我称之为 classtis 的情况,意思是当某人说你的目标是拥有尽可能多的小类,每个类都为以前的类增加最少量的新功能,如果你想要更多的功能,就为你再做一个类。 在Java世界里,有大量这种小而浅的接口,比如说,当我启动一个程序时,如果我想打开一个文件并从中读取序列化的对象,我必须为此创建三个对象(如上图),首先我必须创建这个文件流,但由于某些原因,我没有得到缓冲,如果我想缓冲,这就像你今天想吃东西还是在缓冲区,你必须创建另一个对象,然后如果我想序列化对象,又是一个对象除此之外,每一个都会产生异常,我必须捕捉到这些异常,而这些异常会在中途出现,我必须为非常非常简单的事情清理大量的复杂性,所以我认为他们忽略了这个想法,首先在复杂性中,普通情况非常重要,你要让普通情况非常非常简单。如果我不想要缓冲,那应该是事情变得更复杂的地方,而不是我必须记得特别要求缓冲,所以对我来说,这不是长度的问题,长度真的不是根本问题,我对那些长达几百行的方法没有意见,如果它们相对干净,如果它们有深层次的接口,抽象才是最重要的事情,所以与其追求长度,不如先尝试获得这些深层次的抽象,然后如果你最终得到的东西真的又大又长,那么看看你是否能把它砍掉,但深度更重要,那么什么是深度接口的例子呢?

Pasted Graphic 5

我最喜欢的世界上可能是有史以来最漂亮的接口之一,是UNIX的文件输入/输出接口,五个函数落在每个函数的简单接口上,唯一复杂的是标志(flag 参数)和开放者的权限(permissions),有点古怪,但其他都非常非常简单。在这个简单的界面后面,通常有几十万行的代码,它们做了所有的事情,从管理磁盘空间到文件缓存,再到设备驱动,大量的代码,当然还有许多其他的低级界面,但只是这惊人的美丽的五个功能,现在人们看着这个东西,当然,这就是你设计东西的方式,但它在 UNIX 之前不是这样的,在这个房间里是否有人在UNIX 时代就开始编程 ? 好的, 有少数人 ,在那个时代之前,事情是非常可怕的,例如,他们在UNIX文件系统之前,会有一套完全不同的内核调用,如果你想打开一个文件,是随机访问还是顺序访问,不同的关键文件集,不同类型的文件,你必须声明你的文件是随机访问还是顺序访问,然而 UNIX只有这几个惊人的简单接口,所以是一个美丽的例子

Pasted Graphic 6

好吧,让我说说我的第二个设计原则,那就是把错误定义为不存在,所以我们都知道异常是系统复杂性的巨大来源,例如在RAM云存储系统中,我们认为我们正在建立一个低延迟的存储系统,并认为我会花所有的时间使事情变得非常快和低延迟,但事实上,我们90%的时间都用于做崩溃恢复,所以它们是复杂性的一个巨大来源,同样,大众观点(common wisdom) 教导你要进行防御性编程,这很好,但是人们认为我应该抛出大量的异常,我抛出的异常越多,我的防御就越好,只要我建立我的模块来捕捉所有的错误,并把它们作为异常抛到空中,我就不必担心它们落在哪里,或者谁来处理它们,我已经完成了我的工作,我抛出的异常越多,我就是个好程序员。 所以我认为,总的来说,我们应该尽量减少需要处理异常的地方,你不可能完全消除它,但要尽量减少它,最好的情况是重新定义语义,如果没有错误,就没有异常,这是最好的。

所以让我给你举三个例子,第一个例子是很多年前我在设计 tickle 脚本语言的时候,我引入了一个 unset 命令,可以删除一个或多个变量,当我在做这个的时候,我想,顺便说一句,这是人们的经典错误,我想为什么正常人会删除一个不存在的变量,这毫无意义,所以我要为这个抛出一个异常。所以当用户尝试去删除所有可能创建的变量,但不知道哪些变量是你真正创建的时候,有些变量不存在,所以在实践中,人们一直在抱怨这个问题。所以我应该做的是,我不知道为什么我没有在发现问题后解决它,这是我的第二个错误,我应该做的是重新定义语义,使unset使一个变量不存在,如果你这样想,那么如果变量不存在,我们就干净了,没有坏处,所以这就是我应该做的,不应该抛出那个异常

另一个例子是Windows中的文件删除,至少在早期他是如此试图删除一个文件,而某些进程打开了这个文件,这是不允许的,任何人都经历过这种情况,许多人都认为Windows的经验是这个可怕的事情,然后现在你吓坏了,谁打开了它,你开始到处杀程序,试图让它删除文件,你找不到哪个程序打开了它,所以最后只是给它,你重新启动,然后它变成了,然后它变成了一个系统恶魔,它打开了这个文件,所以当你重新启动时,它还是所以UNIX有一个美丽的、非常可爱的解决方案,使这个错误在UNIX中消失,如果你在文件打开时删除它,发生的情况是它从目录和命名空间中删除了文件,它不再出现在文件系统的任何地方,但文件的实际内容仍然存在,以便任何使用该文件的进程可以继续访问该文件,然后当该文件的最后一个开放实例被关闭时,发现它被清理了 这是一个可爱的解决方案,因为你可以犯另一个错误,即我要删除这个文件,现在任何人在做我欠这个文件的时候都会得到一个错误,这可能会更糟糕,因为现在每个程序都必须要写来处理文件在你访问它的过程中消失的情况。 他们最终意识到这是一个问题,我不知道他们在解决这个问题方面的进展如何,但我知道他们所做的第一个阶段是他们有一些特殊的标志,你可以设置,比如说删除文件,即使它是打开的,或者你可以在打开文件的时候允许它在打开的时候被删除,但他们没有摆脱重新进入,他们保留了目录条目,所以你不能在它打开的时候重新创建文件。你知道二进制文件正在使用中,你无法修改它们,无法创建一个新版本的二进制文件,因为目录条目被锁住了,所以他们仍然没有把它弄好,所以又是一个例子,你只是想让这些错误消失

第三个例子是人们经常认为更有争议的,当我说这个例子时,是子串,所以在Java中有各种方法可以从一个字符串中提取一个子串,这些方法是非常例外的,如果这些方法很容易出现异常,如果索引超出了子串的范围,就会抛出一个异常,而且我相信如果它们的顺序不对,也会抛出一个异常,我发现这是一个巨大的痛苦,我最终不得不自己写代码,在调用java substring命令之前,有效地将我的索引夹在字符串的范围内,这样我就不会再出现这些异常了。 所以,如果两个索引都在字符串的范围之外,它就会返回一个空字符串,你知道,如果它们的顺序相反,显然不可能有什么正确的顺序,它就不会返回,它只是自动进行剪切,这将消除人们不得不写的许多额外的代码,而且我怀疑有许多运行时的异常发生,因为人们在调用命令之前忘记将索引剪切到字符串的范围之内。

因此,这里的总体想法是试图减少人们必须处理异常的地方,书中谈到了一些其他的方法,但最好的情况是直接定义异常,让正常的行为总是做正确的事情,

教授问: 有人想争论 java 子串是否应该抛出异常,我知道你们有些人在想 , 请举手

观众 1:但精细的直观语义可能是更完整的定义,我的意思是你你可以简单地说,无论你传递什么参数集,你都有难以解释的随机行为,但你举的例子非常非常直观,比如当你定义索引和可用空间或TCI之间的重叠时

观众 2:所以我认为它应该抛出一个运行时异常,因为像你不能Madhvi只是在客户端的错误假设。我认为它应该是运行时异常

教授答:这也是我们的理念的一部分,我们要努力防止人们犯错,所以这是一种高尚的想法,但问题是,要防止人们犯错真的很难。所以通常情况下,如果我们试图这样做,就会引入很多复杂的东西,这使得我们很难做正确的事情,所以我认为,一般来说,我们想让做普通的事情变得非常容易,正确的事情我们应该是的,你仍然需要做测试来捕捉错误,所以这不会取代对单元测试的需求,但总的来说,我认为当你试图建立能够防止人们犯错的系统时,你通常会为每个人创造大量的复杂性

观众 3:所以你如何区分java substring和Java dot string dot character at index 如果索引超出范围,应该抛出一个异常。

教授答: 有一些特殊的值代表算术错误和类似的东西,所以如果你有一些空字符,你可以返回,这可能是有意义的,但我们没有真正的字符集,所以你在这种情况下,你真的不能,我不知道你会返回什么,如果所以在这种情况下,你可能必须抛出,

观众 4:那么什么时候抛出异常才是个好主意,

教授答:所以有一些直观的情况,你想尽可能地消除异常,那么你如何决定,不,我没有什么可以做的,我改变语义是行不通的如果你不能从根本上与你的调用者签订合同,如果你不能实现你的接口,那么你就必须抛出一个异常,你知道,如果你在读人的时候做了一个读操作,而这个读不能成功,因为你得到一个你必须把这些信息反映给调用者,所以你不能排除所有的接受,你知道班上的一些学生认为这真是个好主意,所以他们基本上没有写异常处理程序,他们的教练说我只是把它们定义为我说,不,不,有些东西实际上是重要的,你必须这样做,很多软件设计我认为是为了什么重要,什么不重要,理想情况下,你希望尽可能少的重要,你希望不依赖很多东西,但你必须认识到那些真正重要的东西,那些你必须反映在系统中的东西

观众 5: 是的,你对异常与额外状态码或额外错误码返回有什么想法,

教授答:这是一个经典的争论,异常与返回值,你知道有些时候,每个都有意义。异常实际上在你把它们扔得最远的时候提供了最大的价值,如果它们在堆栈中走了很远,那就是它们提供了最大的好处,因为你已经不需要一大堆中间层来处理这种情况了,你可以把它一直带回源头。如果你在捕捉异常和你调用的方法,你知道,与获取返回值相比,它的价值并不大,没有什么不同,而且异常的一个问题是,它们都有笨重的语法。实际上,检查一个返回值在语法上比声明一个异常处理程序更简单,但是你知道很多时候你真的希望这些异常能传播很远,所以即使调用者要看它,你也可能想把它表现为一个异常,而不是返回值,

观众 6:他不是把崩溃作为一种避免好的方式,比如传递好的这些关于崩溃的想法。

教授答:在大多数程序中,我认为你不应该试图削减内存异常,因为这对内存的依赖性太大,对于大多数程序来说,如果你想让内存崩溃,我的意思是打印一条信息,然后崩溃,这几乎不会发生,今天的牛仔裤有大量的内存,无论如何,我想你知道再次使用时要谨慎。但我认为在这种情况下,我们只是把崩溃作为一个很好的事情来做,是的,替代方案会产生很多的复杂性,你可能一开始就不会得到它,你知道崩溃。

观众 7:这个问题来自Kun,在现实生活中,开发经常受到各种限制,例如,时间很紧,需要进行实验和灵活的设计,缺乏共同的工具库等,你的软件设计哲学方法是否会根据现实世界的限制而改变?

我想我也该继续前进了,因为我们的时间开始有点紧张了

Pasted Graphic 7

我们可以使用一些具体的技术或理念,但我认为好的设计的最大障碍之一是思维方式(mindset),没有正确的思维方式,你永远不会产生好的设计。不幸的是,大多数人采取了错误的方法,也就是战术(tactical)性的方法。战术性的方法的意思是 ,你的目标是让某些东西工作起来,你知道你的下一个功能是修复一个错误,但这是你的目标,让某些东西工作起来,你认为这怎么会错呢?只要我不做太多,我就能让它工作,这才是真正重要的问题。问题是,复杂性不是你犯的一个错误,不是一个单一的大问题使系统变得复杂。 而是许多人在一段时间内犯下的成百上千个错误,因此,这意味着首先你不会注意到它的发生,只是每次都有一点,但更糟糕的是,一旦它发生,几乎不可能修复,因为 因为没有一件事你可以回去解决这个问题,它是成千上万的事情和数百个地方,所以它只是如此压倒性的,你永远不会得到它,所以顺便说一下,有一个极端的战术编程的人排序的人格类型,我称之为战术龙卷风,这是一个人谁打开了大量的相当低劣的代码,那种80%的工作在一个巨大的速度和留下的破坏后,在许多组织这些人被认为是英雄,这是管理谁去当我们需要的新明天在许多组织中,这些人被认为是英雄,当我们需要明天的新功能时,管理层就会去找他们,而他们明天就会有一个功能,在明天的大部分时间里都能工作。事实上,当我做这个演讲时,有人说,哦,这就是你说的10倍程序员吗?是的,那就更糟糕了,不幸的是,你知道战术方法真的非常容易滑入,很难不这样做,

所以你必须认识到的第一件事是,如果你想要一个伟大的设计,你必须认识到可以运行的代码是不够的,这不能成为唯一的目标,它是非常基础的目标(table stake),当然,代码必须能够运行,但这不应该是真正的目标,相反,你应该采取我称之为战略(strategic)的方法,或者目标是一个伟大的设计,这是最重要的事情。是的,它必须在今天运行起来,但我们必须有一个伟大的设计,为什么你要有一个伟大的设计呢,这是为了我们能够在未来快速开发(develop),所以这真的是,这真的是所有关于投资的问题,在今天投资好的设计,因为我们开发的大部分代码将在未来继续编写,所以如果我们今天把事情搞砸了,我们就为未来拖累了自己,所以你必须考虑复杂性,试图找到方法来驱动系统的复杂性,从根本上说这是困难的部分,你必须为小东西流汗,你必须有一个零不要让那些小东西爬进来,因为如果它们爬进来了,你就会再次滑回到战术模式。你的发展仍然会随着时间的推移而减慢,因为复杂性是不可避免的,我们无法阻止它,我们只能尽可能地减缓增长,但最终你会变得更快。但在我看来,这一切都会得到回报,你的投资总会得到回报的。

Pasted Graphic 8

我什么时候才能真正赶上我的战术方法,使我真正有一个净收益,然后我在后面走的更快 ,不幸的是,我不知道这些问题的任何定量答案,你知道我的想法是 , 上图交叉点是在某处在6到12个月的范围内,大概需要多长时间来遗忘你写的最后一块代码,但不幸的是,我没有任何数据来支持这一点,所以这就是挑战,因为人们你知道,人们可以看到今天的成本,但他们无法真正估计它的价值,当利益来临时所以在这里优化这部分真的很容易,只是一个简单的问题

观众问:你有没有想过分析开放源码的发展,看看你是否能观察到这一点,我的意思是这需要一些判断,我想看看新增行的提交率吧但问题是你不知道在这些方面做了多少努力。 所以我想,当一个新的存储设备出现时,人们会编写新的文件系统或其他东西,你可以在不同的OSS中查看这个,看看事情是如何发生的。

答:这很难,所以问题是你要投资多少,让我再给你几张幻灯片,因为我们的时间开始有点紧张了,

Pasted Graphic 9

所以要投资多少才合适呢?我们的融资将在6个月内用完,无论我们投入多少资金,当我们成名致富并进行IPO时,我们就可以雇佣更多的工程师来清理这个烂摊子,所以你知道他们最后的结果是可怕的不幸的是,一旦你得到了这样的代码库,就几乎不可能清理它了,我从来没有听说过任何人,也许你可以回去完全重写,你可以做到这一点,也许有这样的例子,但几乎没有发生过,所以对我来说,脸书是典型代表(poster child) , 他们甚至在公司的座右铭中加入了 "快速行动 "和 "打破常规",他们做到了这两点,所以现在从某种意义上说,开发人员就像这样,一个刚被录用的大学生,他们被赋予了极大的权力,他们在一周内就提交了自己的第一份文件,改变了网站,工作的第一周感觉就像你知道的那样,他们的代码库就是一个臭名昭著的烂摊子,一个夏天,我的几个研究生去那里实习,回来后他们简直不敢相信自己看到的一切,这很有趣,因为我的学生在之前的整个一年里一直抱怨着为什么我们要写单元测试和文档,而Linux没有任何单元测试,Linux没有任何注释,为什么我们要这样做,他们从Facebook回来后,我再也没有听到这样的抱怨。 所以他们把他们的座右铭改成了 "用坚实的基础设施快速行动"。

Pasted Graphic 10

那么你能解决这个问题吗?答案是你可以,你知道你可以用蹩脚的代码获得成功,你可以用战术建立成功的公司,Facebook已经做到了,但我有两个想法,一个是你也可以用另一种方法获得成功,虽然我没有这些公司的个人支出,但我的感觉是,谷歌和VMware都采取了更强大的设计文化方法,至少在早期,你知道2000到2010年,这两家公司被称为硅谷,这是真正伟大的软件开发人员的地方,他们真正关心设计,他们做真正的程序设计这一点很重要,因为如果你有这样的文化,我想你就能更好地招募到最好的程序员,我们知道这个10倍程序员现象,你知道快速推出伟大产品的最好方法是获得最好的程序员。所以我认为支持好的设计文化的最有力的论据是,它可以让你雇佣到顶尖的人,这将给你带来优势,所以如果你想的话,你可以用蹩脚的方式做事,但有足够的成功案例表明,你可以让这一方法奏效

但好吧,该在设计部分投资多少时间 ?我想说的是,你能负担多少,问问你自己,在我们一生中的这个阶段,我们最能负担的投资是什么,我想可能是10%或20%之类的,我打赌几乎每个人都能负担得起,今天慢了10%,意识到你会得到这一切。这不是一个沉没成本,它在某个地方会回来的 你知道六个月到几年后,你会把它全部拿回来 你可以负担得起这样做, 我认为在小的步骤方面,而不是英雄主义 你知道你不可能花六个月的时间完全设计整个系统,你知道从第一原则出发我们知道软件的一个问题是我们无法想象我们的系统会变成什么样子,你必须使用某种反复的方法,所以我想说的是,用小步骤的方式来考虑。来设计界面,试着想出深层的类,一边写文档,一边进行单元测试,当然,每当你做修改时,你必须意识到你不可能第一次就做对,这是软件的规则之一,我们就是不能这样假设,每当你在一个系统中的时候 你都会去改善一些东西,总是寻找一些你可以做得更好的东西,其中一个原因是,当你进入一个系统时,你可能会使一些东西变得更糟,所以即使你只是想打破平衡,你也要找到一些东西来改善。我想这是与通常的理念相悖的,我想很多时候,当人们改变现有的代码时,他们会选择什么是我可能改变的最小的代码行数来做这件事,我想有时是因为他们害怕我不了解这个,我会破坏一些东西,所以我会做最小的改变所以我就直接访问另一个文件中的一个小变量,而不是试图为它找到一个干净的接口,所以不要这样做,试着找到一个干净的方法来做事情,最好是在最好的情况下,你想在你从头开始建立整个系统的情况下结束整个系统从头开始,知道你现在所知道的,这就是你所想的,如果我们从头开始建立它,我们会这样做,这是现在的理想,你知道,你不可能总是这样做,有时这将是一个重构,远远超出了你能做的规模。 我想说的是,在任何时候都要问自己,我是否在做我可能做不到的事情,而不是屈服,老板说我们明天要发布一个版本,所以我就把这个东西黑进去,让大家高兴一下,问问这是不是我可能做的最好的事情。

问:分层在软件工程中通常被认为是非常好的,但它也有一个挑战,即代码执行时的性能,通常功能被复制在多个层中,不清楚如何在不破坏代码的情况下进行提炼

所以这就是你的问题,我们的层是好的是的,那么你如何处理层,对抽象的需求和层以及对性能的需求都是相互矛盾的,我们需要层来管理复杂性,所以从这个意义上说,它们是好的,但在性能上确实有问题,我认为人们做了太多的层,我认为,所以我认为这是一个常见的错误,你只是扔在许多瘦小的层,而不是有一个较小数量的流星层,这是一个问题,另一件事是在性能重要的情况下,如果你考虑一下你的关键性能指标是什么,我相信你往往可以定义层但你在做系统设计的时候,必须要考虑一些总体的性能指标,

所以我认为我认为投资是最大的挑战,特别是因为很多项目经理我认为他们并没有真正理解它,他们在推动下一个功能或下一个版本。我认为,如果公司能够明确这是在我们的工程哲学中,我们将以10%的比例进行投资,最近,我们平均有10%的资源用于使设计变得更好,而不仅仅是推出下一个功能

问:嗨,约翰,我已经20年没有关注这个问题了,但是你所描述的定量分析有一段时间是软件工程界的领域,我想知道你是否看到过在这个领域中的实验,看看他们是否有可能有用的数字分析,或者他们已经部分原因是

我怀疑是否有可能以一种有意义的方式来衡量它,所以我没有窥探,但我很想看到数字,如果它们是可信的,但我还没有看够它们,

Pasted Graphic 11

好吧,我还有几张幻灯片,可以提问,那么接下来的讨论是这个课程是否有效,你知道很难说清楚,我的意思是你不可能在一个学期内成为一个伟大的程序员,对这些人来说还需要很多经验,一旦他们在工业界呆了一段时间,就要回来问他们,这是 这只是这个过程中的第一步,但这个班级有一种非常有趣的能量,真的很有趣,学生们在第一轮代码审查中开始,他们非常试探性,有点害怕批评对方,不确定他们应该说什么,所以第一轮有点试探性,然后他们每个人和我单独会谈之后,如释重负,然后他们开始 , 哦,好吧,我也可以这样做,所以在第二轮的一半时,你通常会改变讨论的基调,学生现在被

我不得不说,即使学生们没有成为更好的设计师,我也在通过教授这门课成为更好的设计师,这对我来说非常有趣。你知道,你可能会认为在一个季度内,一周内读三次20000行代码是很乏味的,你知道这确实需要一些时间,但实际上我从中学到了很多,因为学生们都在用同样的语言做同样的事情,所以我可以看到不同的方法,我可以看到他们犯的一些常见的错误,实际上,由于教授这门课程,我改变了对一些设计问题的看法,特别是幻灯片底部的那个问题,我意识到,制作课程只是稍微有一些通用的我意识到,即使你只在一个地方使用它们,实际上也会使它们变得非常简单,直到我在第二次教这门课时看到一些学生的项目部分,所以看到这些对我来说非常有趣,我觉得我正在学习更多关于软件设计的过程,并且在我进行教学时得到更好的想法。

Pasted Graphic 12

所以我写了这本书,这是一本相对较小的书,170页,仍然是相当哲学性的,而不是规定性的,所以希望我能接触到更多的人,因为我只能在课堂上教20个人,不能做得更多,理想情况下,我希望这本书能成为一个避雷针,也许我们可以展开讨论,人们给我写信,对书中的东西提出异议,或建议其他设计理念,或提供更好的例子。 我也在努力定义人们可以使用的术语,我希望这本书可以做到的一点是,人们可以在设计评审中使用这些术语,以获得谈论设计和设计评审的想法和方法。

我想问一下,有多少人在做设计评审,我想几乎所有人都在做,有多少人在设计评审中经常讨论设计问题,而不是编码风格和错误,有多少人经常谈论设计问题,很好,我希望看到更多这样的情况。我的观点是,这只是第一次尝试,如果这本书最终成为一本有用的书,它可能会是,这有点像软件,你会在第三版中找到它的踪迹,我需要得到反馈,修改和修复错误的东西和新的想法,所以只是总结一下,我真的相信我们可以教软件设计,真的,我真的相信我们可以。我还没有想好,我真的需要一个有很多经验的人,我认为阅读学生的代码,我觉得一些原则正在出现,你知道,如果你读了这本书,同意或不同意我的观点,我很想听听你的看法,我希望在未来做的只是以某种方式将这一点推广到社区,我希望看到在整个软件社区有更大的设计意识,我们可以进行讨论,使之成为我们社区的重要组成部分。我创建了一个邮件列表,用于讨论关于这本书的话题,到目前为止还没有什么流量,但我希望将来会有更多的流量,并再次希望从人们那里获得想法,使这本书越来越好,课程越来越好,长期的目标是在五年或十年后成为伟大的事情。

Pasted Graphic 13

谢谢大家的关注,我很乐意接受更多的问题

问:测试方面的考虑,比如单元测试或标记对接口和抽象的影响

我是单元测试的超级粉丝,这一点非常重要,我曾争论过是否要在课堂上教他们,实际上在课堂上有一次教了一点,但这对课堂来说太分心了。我们只有10周的时间,所以我最终决定,我需要在课堂上专注于设计,所以我不在课堂上做单元测试,但我是单元测试的完全倡导者,我无法想象在做软件时没有单元测试,

问:所以我在这里被你在讨论的第一部分所吸引,当你说在迭代过程中你实际上放弃了你最初让学生建立在其他学生的代码上,我认为这是一个非常非常真实的我只是惊讶于你,我的意思是我希望你能教这个,

问:你知道我同意你在一个完美的世界里会做的事情之一,我们做的问题是我们有十个星期,我如何使用这十个星期,我决定我想尝试做最多的事情,不需要设计。所以我不得不牺牲其他东西,这也是为什么单元测试被扔掉的原因,因为我觉得他们更应该考虑的是设计问题,而不是单元测试问题。

问:课程有 TA (助教) 吗

是的,我现在没有,因为我只是担心TAS不能提供足够高水平的反馈,我已经开始想,也许以前参加过课程的学生可以看到我是一个,到目前为止,需求还没有超过我的能力,所以我已经能够保持小规模,如果需求增长,我可能会尝试一个实验,看看如果以前的课程参与者可以TA,但我再次担心,而且对于最初的几个提议,我也想自己阅读所有的代码,因为我想从中学习,

问:你是否想过使用软件工程师作为 TA ,我们每天都在阅读和写代码,

原则上我不是诗人,但我想确保学生不会得到混合的信息,所以特别是因为我所教授的课程的想法并不广泛,也不一定在社会上广泛传播,最坏的情况是如果一个软件工程师进来说,这个方法比20行还长(和教授的 mindset 相违背),你得把它拆开。

问: 线程仍然是一个坏主意吗?

你知道当你发表东西的唯一形式是一套幻灯片而不是一篇论文时,有一个有趣的事情,那只是很久以前你在下一次会议上的一个邀请演讲,如果他很有趣,人们可以回来,多年来以一大堆不同的方式解释它,所以那是在一个非常狭窄的背景下做的,当时人们为一些甚至不需要三个线程的东西引入线程,因为他们正在做线程所以,那个谈话是对线程的一种反应,对于某些事情来说,线程是不可避免的,你必须使用它们,但它们仍然是你知道的,它们是次要的选择,而最糟糕的选择可能是在某些情况下不使用线程,编程起来非常困难,所以我仍然不喜欢线程,但它们是一种生活的事实,

问:我认为Tikar语言是一种非常简单的语言,但最终没有获得像大型Scoble的采用。

在你的问题中,因为你的语言是你输入的,就像在设计语言中存在着高水平的问题,你认为你得到了它所有的错误,这将是你的问题,我不想在这个问题上有太多的变化,我想说两件事,第一,Chuckle最擅长的事情之一是用TK做简单的交互式gooeys,当网络出现时,网络基本上接管了这个,Tickle没有过渡到网络,所以很多事情人们会用Tickle ticket来做,最终在网络上做了,第二,它是否真的正确我不确定现在回想起来它是否是正确的语言,实际上我建立它的目的非常不同,我建立它的目的是作为一种文本命令语言,你可以向程序输入命令,几乎像一种外壳语言,然后最终被用作脚本语言。它有很多有趣的特性,但我不知道如果我再做一次,我不确定我会设计同样的语言,但我认为发生的最大的事情是网络的出现,在痒痒的时候,没有有效地进行过渡,所以有很多真正的好的,

问:那么根据你的观察,对招聘过程有什么想法?

我认为,我认为人们在招聘时,会对这个人所做的事情进行模式匹配,试图找到一个完全做过我们想做的工作的人,我认为这是个错误的招聘方式,一般来说,我认为你想招聘学习速度最快的人,坡度最快的人,我并不特别关心他们是否做过我想做的工作,因为我们要启动的时候,事情变化得很快。所以我寻找那些真正聪明的快速学习者,而不是顺便寻找那些已经做了五次这个工作的人,你会想,好吧,你知道为什么他们没有继续做下一个工作,通常情况下,这些人都会遇到他们的高原期,而那些有快速坡度的人有潜力。

问:说的是如果一个API有足够多的用户,那么不管你在合同中承诺什么,你的系统的所有可观察到的行为都会被人依赖,

我想这是有道理的,随着时间的推移,发生的事情是这样的,或者说是应用,他们会找到每一个缝隙,在下面扎根,他们会发现,所以除非你对你的API非常小心,我想这可能是真的,可悲但真实。

问: 嗨,你有没有观察到软件设计的简单性或复杂性在语言和支持生态系统的选择上有什么变化?

因为我喜欢C++中的一些面向对象的特性,现在C++是一种有可怕的学习曲线的大野兽,但我欣赏它的力量,但说实话,我认为设计原则是相当普遍的,你可以在任何语言中使用它们,你知道UNIX的那个简单的文件系统接口是用C语言完成的,没有面向对象的东西,我遇到的人说,哦不,实际上你知道它实际上是面向对象的。因为有一个文件描述符被传来传去,而面向对象是非常关键的,我说不,他们只是找到了一个非常贴心的接口,嗯,我还没有发现有一种语言在设计上比其他语言好得多,但我不知道人们有什么意见,我很想听听其他的意见。

问: 判断学习能力快的人有什么准则?

我很害怕雇用更多的战术性龙卷风(working is enough),所以我会告诉你什么对我来说是最好的。是那些我在面试时非常喜欢和他们谈话的人,这有点奇怪,我觉得这样说有点好笑,因为这是否意味着有人在某种程度上拍了我的马屁,我就喜欢他们并雇用他们,我不认为是这样的,因为我对拍马屁有相当敏感的判断。我不认为是这样的,因为我对拍马屁很敏感,这真的让我很生气,但我不知道,人们也指出,如果你把这种边缘的风险放在那里,他们只是在雇用更多像你一样的人,所以你不会有很好的多样性,所以我不知道该怎么做,或者哪里有一个好主意,但这是我经验中似乎有关联的一件事。