这本书的确是软件工程领域一本再经典不过的书籍,在尚未开始学习编程的时候便听说过这本书,大约是从萧瓜的答案里。

在刚参加工作后没多久比现在还要更菜一些的时候初读了这本书,因为全是形而上学的东西,再加上各种牛人的推荐与评价,当时还是感觉这书有丶东西的,而彼时见过的东西不多,各种经验匮乏,使人感同身受、醍醐灌顶的场景并不多,甚至有一些点比如“函数的得墨忒尔法则”也是理解不了的。记得第一遍读过之后感觉不过瘾,还想立即再来一遍,并进行“自省”项目,即根据书中提到的各个点来映射到实际工作开发中的各个点,来进行对比和思考。
结果当然是不了了之了。

最近由于总结一些旧书,又把这本200页的书拿出来重读,并进行了一些书摘,记述了本书的整体脉络。
这一次,很明显感觉可以结合实际的好建议很多,最后会列出“行动项”,是受阅读启发,可以在工作、日常编码中采取实践的点。

评价与前言

这是我唯一不会出借的一本书。 – – Kevin Ruland
这本书里虽只包含了很多看似粗浅朴素的道理,实则是若干经验的心血总结。
原以为那些嚼烂了的东西,不会再有新味道,但是我错了。– – 云风

“牛人”其实是备有很多现成代码的,完成这个功能只是把之前积累的封闭良好的可以了。
编程大约有三个境界,新手、高手和高不成低不就的中手。
编程是一种技艺,一种需要用心学习的技艺。
设想你在参加一个会议。或许你在想,这个会议没完没了,你还不如去写程序。而Dave和Andy会想,他们为什么在开会,他们想知道是否可以通过另外的方式取代会议,并决定是否可使某样事情自动化,以使开会的工作推后。
这就是Dave和Andy思考的方式。开会并非是某种使他们远离编程的事情。开会就是编程,并且是能够加以改善的编程。

正文

注重实效的程序员的特征是什么?我们觉得是他们处理问题、寻求解决方案时的态度、风格、哲学。他们能够越出直接的问题去思考,总是设法把问题放在更大的语境中,总是设法注意更大的图景。

关于负责
在所有弱点中,最大的弱点就是害怕暴露弱点。
提供各种选择,不要找蹩脚的借口。
有时,你其实知道他们会说什么,所以还是不要给他们添麻烦吧。
不要说事情做不到;要说明能够做什么来挽回局面。
不要害怕提出要求,也不要害怕承认你需要帮助。
在你大声说出它们之前,先设法把蹩脚的借口清除出去。如果你必须说,就先对你的猫说。

关于软件的熵
当软件中的无序增长时,程序员们称之为”软件腐烂”。有许多因素会导致,其中最重要的一个开发项目时的心理(或文化)。你开发项目时的心理也可能是非常微妙的事情。
不要容忍破窗户。
如果没有足够的时间进行适当的修理,就用木板把它钉起来。或许你可以把出问题的代码放入注释,或是显示“未实现”消息,或是用虚拟的数据加以替代。

关于“石头汤与煮青蛙”的寓言故事:
每个人都会护卫他们自己的资源,有时候,这就叫做“启动杂役”。
这正是拿出石头的时候。设计出你可以合理要求的东西,好好开发它。一旦完成,就拿给大家看,让他们大吃一惊。
人们发现,参与正在发生的成功要更容易。让他们瞥见未来,你就能让他们聚集在你周围。
做变化的催化剂。
要记住大图景,不要因为小事情的累积破坏了士气和团队(而这正是常常发生的)。
温水煮青蛙与破窗原理不同,后者是说人们失去与熵战斗的意愿,是因为他们觉察到没有人会在意。而青蛙只是没有注意到变化。

关于足够好的软件
现实世界不会让我们制作出十分完美的产品,特别是不会有无错的软件。时间、技术和急躁都在合谋反对我们。
应该给用户以机会,让他们参与决定你所制作的东西何时已足够好。
你所制作的系统的范围和质量应该作为系统需求的一部分规定下来。
如果你给用户某样东西,让他们及早使用,他们的反馈常常会把你引向更好的最终解决方案。

编程就像绘画。如果你不懂得应何时止步,所有的辛苦劳作就会遭到破坏。如果你一层又一层、细节复细节地叠加,绘画就会迷失在绘制之中。所以你要知道什么时候止步。
不要因为过度修饰和过于求精而毁损完好的程序。继续前进,让你的代码凭着自己的质量站立一会儿。它也许不完美,但不用担心:它不可能完美。

关于知识资产
你的知识和经验是你最重要的职业财富。
管理知识资产与管理金融资产非常相似:

  1. 严肃的投资者定期投资– –作为习惯;
  2. 多元化是长期成功的关键;
  3. 聪明的投资者在保守的投资和高风险、高回报的投资之间平衡他们的资产;
  4. 投资者设法低买高卖,以获取最大回报;
  5. 应周期性地重新评估和平衡资产;

即使投资量很小,习惯自身也和总量一样重要。
低买高卖是说,在新兴的技术流行之前学习它可能就和找到被低估的股票一样困难,但所得到的就和那样的股票带来的收益一样。
每年至少学习一种新语言。不同语言以不同方式解决相同的问题。通过学习若干不同的方法,可以帮助你拓宽你的思维,并避免墨守成规。
学习的过程将扩展你的思维,使你向着新的可能性和新的做事方式拓展。思想的“异花授粉”十分重要;设法把你学到的东西应用到你当前的项目中。

与他人交谈可以帮助你建立人际网络。
出去和与你的当前项目无关的人、或是其他公司的人谈谈技术。
所有阅读和研究都需要时间,而时间已经很短缺。所以你需要预先计划,让自己在空闲的片刻时间里总有东西可读。
P13页有与人打交道的礼节与教养,可学。
同时,很重要的一点是,要批判地思考你读到的和听到的。

关于交流
没有有效的交流,一个好想法就只是一个无人关心的孤儿。
只有当你是在传达信息时,你才是在交流。你需要了解你的听众的需要、兴趣、能力。
WISDOM离合诗—-了解听众:
What do you want them to learn?
What is their interest in what you’ve got to say?
How sophisticated are they?
How much detail do they want?
Whom do you want to own the information?
How can you motivate them to listen to you?

有时候,只要简单地问一句“现在我们可以谈谈……吗?”就可以了。
我们常常发现,与制作文档的过程相比,我们制作出的文档最后并没有那么重要。
鼓励大家通过提问来交谈,或是让他们总结你告诉他们的东西。把会议变成对话,你将能更有效地阐明你的观点。谁知道呢,你也许还能学到点什么。
除非你生活在真空中,你才不需要交流。交流越有效,你就越有影响力。

关于DRY
不要在系统各处对知识进行重复,不要把任何一项知识分散在多个系统组件中。(国家与各种code的表示)
不能划入某个明显的责任区域的常用功能和数据可能会被实现多次。
我们觉得,可靠地开发软件、并让我们的开发更易于理解和维护的唯一途径,是遵循我们称之为DRY的原则:
**系统中的每一项知识都必须具有单一、无歧义、权威的表示。”
这不是你是否能记住的问题,而是你何时忘记的问题。
我们觉得,这是注重实效的程序员的工具箱里最重要的工具之一。

在以后的开发过程中,你可以因为性能原因而选择违反DRY原则。这经常会发生在你需要缓存数据,以避免重复昂贵的操作时。其诀窍是使影响局部化。对DRY原则的违反没有暴露给外界。

关于正交性
突然间,你在用一个让人难以置信的复杂系统玩耍,其中每一项改变都会影响所有其他的输入。
当任何系统的各组件互相高度依赖时,就不再有局部修正这样的事情。要消除无关事物之间的影响。

与编写单个的大块代码相比,编写多个相对较小的、自足的组件更为容易。
把所需要的任何语境显示地传入模块,代码就会更易于理解和维护。

你在把电话号码当作顾客的标识符吗?如果电话公司重新分配了区号,会怎么样?不要依赖你无法控制的事物属性。

也许会让人惊讶,正交性也适用于文档。其坐标轴是内容和表现形式。

关于可撤消性
如果某个想法是你唯一的想法,再没有什么比这再危险的事情了。
问题在于,关键决策不容易撤销。(比如数据库表结构的设计,深受其害)
与我们开发软件的速度相比,需求、用户以及硬件变得更快。
错误由于假定决策是浇在石头上的– –同时还在于没有为可能出现的意外事情做准备。
要把决策视为是写在沙滩上的,而不要把它们刻在石头上。大浪随时可能到来,把它们抹去。

如果在代码中有着糟糕的封装、高度耦合以及硬编码的逻辑或参数,事情也许就是不可能的。

关于曳光弹与原型
并不让人惊奇的是,曳光弹比费力计算更可取。反馈是及时的,而且因为它们工作在与真正的弹药相同的环境中,外部影响得以降至最低。
曳光代码并非用过就扔的代码:你编写它,是为了保留它。
一旦你在系统的各组件间实现了端到端的连接,你就可能检查你离目标还有多远,一旦你完全瞄准,增加功能将是一件容易的事情。

原型制作生成用过就扔的代码。你可以把原型制作视为在第一发曳光弹发射之前进行的侦察和情报搜集工作。
你可以选择通过原型来研究任何带 有风险的事物。以前没有试过的事物,或是对于最终系统极关键的事物。任何未被证明的、实验性的、或是疑问的事物。任何让你觉得不舒服的事物。
原型制作是一种学习经验。其价值并不在于所产生的代码,而在于所学到的经验教训。那才是原型所作的要点所在。
为了学习而制作原型。
P42有关于如何制作架构原型的具体事项,在设计架构时可以参考。

其他注重实效的途径
每种语言都含有一系列特性– –所有这些特性都在揭示或遮蔽特定的解决方案。
使用领域语言可以靠近问题领域编程。

通过学习估算,并将此技能发展到你对事物的数量级有直觉的程度,你就能展现出一种魔法般的能力,确定它们的可行性。
关于估算,之前还写过另一篇文章来描述。
如果结果证明估算错了,不要只是耸耸肩走开。找出事情为何与你的猜想不同的原因。也许你选择了与问题的实际情况不符的一些参数。也许你的模型是错的。不管原因是什么,花一点时间揭开所发生的事情。如果你这样做了,你的下一次估算就会更好。

关于基本工具
工具放大你的才干。你的工具越好,你越是能更好地掌握它们的用法,你的生产力就越高。
让需要驱动你的采购。
唯一的途径是保持基本工具集的“锋利”与就绪。
花时间学习使用这些工具,有一天你将会惊奇地发现,你的手指在键盘上移动,操纵文本,却不用进行有意识的思考。工具将变成你双手的延伸。

持久地存储知识的最佳格式是纯文本。纯文本并非意味着文本是无结构的,比如markdown。
大多数二进制格式的问题在于,理解数据所必需的语境与数据本身是分离的。
事实上,在异种环境中,纯文本的优点比其所有的缺点都重要。你需要确保所有各方能够使用公共标准进行通信。纯文本就是那个标准。

GUI的好处是WYSIWYG– –所见即所得。缺点是WYSIAYG– –所见即全部所得。

选一种编辑器,彻底了解它,并将其用于所有的编辑任务。如果你用一种编辑器(或一组键绑定)进行所有的文本编辑活动,你就不必停下来思考怎样完成文本操纵:必需的键击将成为本能反应。编辑器将成为你双手的延伸;键会在滑过文本和思想时歌唱起来。这就是我们的目标。
对于常见的编辑操作,与鼠标或菜单驱动的命令相比,只使用键击效率更高,因为你的手无须离开键盘。
你应该能对编辑器编程,让它执行复杂的、多步骤的任务。可以通过宏或内建的脚本编程语言(例如,Emacs使用Lisp的一个变种)进行这样的编程。

对于许多开发者,调度本身是一个敏感、感性的话题。你可能会遇到抵赖、推诿、蹩脚的借口、甚或是无动于衷,而不是把它当做要解决的难题发起进攻。
bug是你的过错还是别人的过错,并不是真的很有关系。它仍然是你的问题。
在你开始调试之前,选择恰当的思维方式十分重要。你须要关闭每天用于保护自我的许多防卫措施,忘掉你可能面临的任何项目压力,并让自己放松下来。最重要的是,记住调试的第一准则:不要恐慌。、
如果你目睹bug或见到bug报告的第一反应是“那不可能”,你就完全错了。一个脑细胞都不要浪费在以“但那不可能发生”起头的思路上,因为很明显,那不仅可能,而且生了。
在调试时小心“近视”。要抵制只修正你看到的症状的急迫愿望:更有可能的情况是,实际的故障离你正在观察的地方可能还有几步远,并且可能涉及许多其他的相关事物。要总是设法找出问题的根源,而不只是问题的特定表现。

再现(reproduction)bug
不,我们的bug不会真的繁殖(尽管其中有一些可能已经到了合法的生育年龄)。

程序员可以构建代码生成器。一旦构建好,在整个项目生命期内都可以使用它,实际上没有任何代价。
被动代码生成器有一个有趣的特性:它们不必完全正确。你需要在你投入生成器的努力和你花在修正其输出上的精力之间进行权衡。
无论何时你发现自己在设法让两种完全不同的环境一起工作,你都应该考虑使用主动代码生成器。
为了进行通信,每个代码库将需要某些公共信息– –例如,数据结构、消息格式、以及字段名。要使用代码生成器,而不是重复这些信息。
用更简单、语言中立的表示形式来表示它,并为两种语言成生代码,常常更简单。
你可以用代码生成器生成几乎任何输出:HTML、XML、纯文本– –可能成为你的项目中别处输入的任何文本。

关于偏执
循环常有香蕉问题(我知道怎样拼写“banana”,但不知道何时停下来– –“bananananananananana…”)、篱笆桩错误(不知道该数桩还是数空)、以及无处不在的off by one错误。

如果它不可能发生,用断言确保它不会发生。
问问你自己:“如果我移走所有的异常处理器,这些代码是否仍然能运行?”
如果答案是“否”,那么异常也许就正在被用在非异常的情形中。
异常表示即时的、非局部的控制转移– –这是一种级联的goto。

分配某项资源的例程或对象应该负责解除该资源的分配。
以与资源分配的次序相反的次序解除资源的分配。
在代码的不同地方分配同一组资源时,总是以相同的次序分配它们。

关于解耦
“羞怯”的工作方式有两种:不要向别人暴露你自己,不要与太多人打交道。
这种编码风格极大地增加了我们的类所依赖的类的数目。这为何是一件坏事?因为它增加了系统别的地方的一个无关改动影响你的代码的风险。

函数的得墨忒尔法则规定,某个对象的任何方法都应该只调用属于以下情形的方法:

  1. 它自身;
  2. 传入该方法的任何参数;
  3. 它创建的任何对象;
  4. 任何直接持有的组件对象。
    在实践中,这意味着你将会编写大量包装方法,它们只是把请求转发给被委托者。
    否则,你可能就会发现自己正走在一条通往脆弱、不灵活的未来的道路上。或者,根本没有未来。

关于元程序设计
再多的天才也无法胜过对细节的专注。
细节会弄乱我们整洁的代码– –特别是如果它们经常变化。
将抽象放进代码,细节放进元数据。
P118有关于这种做法的5种好处。
针对你目前的项目,考虑应用有多少内容可以从程序自身移往元数据。所得到的“引擎”看起来会是什么样?你能否在不同的应用的语境中复用该引擎?

位于MVC惯用手法之后的关键概念:既让模型与表示模型的GUI分离,也让模型与管理视图的控件分离。
MVC其实是一种通用的编程技术。视图是对模型的一种解释– –它无需是图形化的。控制器更是一种协调机制,不一定要与任何种类的输入设备有关。

黑板方式的编程消除了对太多接口的需要,从而能带来更优雅更一致的系统。(有点微服务与流程引擎结合的意思)
用黑板协调工作流,协调完全不同的事实和因素,同时又使各参与方保持独立、甚至隔离。

关于编码
代码也许能工作,但却没有特别的理由说明它们为何能工作。
作为开发者,我们也工作在雷区里。每天都有成百的陷阱在等着抓住我们。我们应该避免造巧合编程– –依靠运气和偶然的成功– –而要深思熟虑地编程。
对于你编写给别人调用的代码,良好的模块化以及把实现隐藏在撰写了良好文档的小接口之后,这样一些基本原则都能对你有帮助。
不要做历史的奴隶。不要让已有的代码支配将来的代码。

重写、重做和重新架构代码合起来,称为重构。
但如果你无节制地撕毁大量代码,你可能会发现自己处在比一开始更糟的位置上。
所以,下次你看到不怎么合理的代码时,既要修正它,也要修正依赖于它的每样东西。要管理痛苦:如果它现在有损害,但以后的损害会更大,你也许最好一劳永逸地修正它。记住软件的熵中的教训:不要容忍破窗户。

从一开始就把可测试性构建进软件中,并且在把各个部分连接在一起之前对每个部分进行彻底的测试。
测试是技术,但更是文化。

关于项目
完美,不是在没有什么需要增加、而是在没有什么需要去掉时达到的。
找出用户为何要做特定事情的原因、而水只是他们目前做这件事情的方式,这很重要。
需求不是构架。需求不是设计,也不是用户界面。需求是需要。

抽象比细节活得更长久。
要创建并维护项目词汇表– –这是定义项目中使用的专用术语和词汇的地方。项目的所有参与者,从最终用户到支持人员,都应该使用这个词汇表,以确保一致性。
墨水刚沾上纸面,就过时了。

有些约束是绝对的;有些则只是先入之见。绝对的约束必须受到尊重,不管它们看上去有多讨厌或多愚蠢。另一方面,有些外表上的约束也许根本不是真正的约束。许多软件问题都可能具有与之相同的欺骗性。

编写程序规范就是把需求归约到程序员能够接管的程度的过程。
但你可以确信,一旦他们看到运行的系统,你就会被各种变更要求淹没。
这里有一个挑战:写一份简短的描述,告诉别人怎样系鞋带。快,试一试。
把需求搜集、设计、以及实现视为同一个过程。
你越是把规范当作安乐毯,不让开发者进入可怕的编码世界,进入编码阶段就越困难。不要掉进这样的规范螺旋中:在某个时刻,你需要开始编码!

还有一些开发者,在有许多已沉没项目的大海里漂流,不断抓住最新的时尚,就像是遇到海难的人紧紧抓住飘来的木头一样。每当有新的木头漂过时,他们都会费力地游过去,希望这一块会更好。但到最后,不管漂浮物有多好,之些开发者依然漫无目的地漂流着。

事实上,大多数形式方法会让你误入歧途,鼓励你在对象之间建立静态关系,而这些对象本来应该动态地编织在一起。
如果你遇到一个项目,其哲学是“类图就是应用,其余的只是机械的编码”,你知道,你看到的是一个浸满水的项目团队和一个路途遥远的家。

因为记忆是随着你年龄的增长而丧失的第二种东西。(第一种是什么?我忘了。)
鞋匠的孩子没鞋穿。软件开发人员常常会使用最糟糕的工具来完成工作。
P193关于测试什么的具体点。

文档的表示形式应该独立于其内容。

本书正文的最后一小段话读来竟然有些热血。
当人们在一段代码上看到你的名字时,应该期望它是可靠的、用心编写的、测试过的和有文档的,一个真正的专业作品,由真正的专业人员编写。
一个注重实效的程序员。

行动项

  1. 审视自己开发项目时的心理,特别是当有厌烦情绪的时候;
  2. 考虑项目中的“某扇窗户”是何时破的,是他人的决策所致,还是管理部门的指示;
  3. 推新项目或新工具时,自己先在往锅里扔进石头给人看效果,来做催化剂;
  4. 开发业务系统,用户就在身边,要多与之交流使用感受与期望;
  5. 低买高卖方面,关注最新的技术,比如Julia,比如可以参与翻译工作,或提issue;
  6. 每年至少学习一种新语言;
  7. 空闲时间的阅读计划;
  8. 开始写估算日志;
  9. 代码生成器,由表结构为源数据,到thrift,到各marshmallow,到给前端的接口文档,到restful,到python(model层的代码及基本的CRUD)、java(mybatis与dao层的代码)各版本。