《软件开发者路线图——从学徒到高手》读书笔记

第 1 章


第 2 章 空杯心态


1、多种语言

第一门语言学得越好,下一门语言学起来就越容易。

鄙人大二学的 JAVA,后来再去学 JS 就感觉好简单(虽然 js 的坑真的多)。

每一种语言都为你提供了使用不同模式来解决问题的机会。在逐渐超越第一门语言的过程中,你应该寻找机会去学习一些采用迥然不同的方法来解决问题的语言。惬意于面向对象语言的学徒应该探究一下函数式( functional)编程语言。畅然于动态类型的学徒应该钻研一下静态类型。安逸于服务器端编程的学徒应该考查一下用户界面设计。

我的目标就是全栈丫。

你不应该“嫁”给任何特定技术,而应该有足够宽的技术背影和经验基础,使自己能针对特定的情景选择好的解决方案。

我是很讨厌讨论什么语言最好的,每个使用场景都有最适合它的语言。

2、初学者心态

通常,每一步都该有进门的感觉。这是初学者的心——一种正在“成为”的状态。——铃木俊隆,《Zen Mind,Beginner’s Mind》(禅者的初心)

正如 Yoda 在电影《The Empire Strikes Back》(星球大战 2:帝国反击战)中所说的:“你必须忘记已经学到的东西。”(You must unlearn what you have learned)

有多少次你担心被人觉得愚蠢而没有尝试新东西?有多少回你担心被人认为幼稚而压抑了主动性?……心理学家 Abraham Maslow 发现,那些能把潜力发挥到显著水平的人身上都有一种孩童般的品质。 Ashleigh Montagu 使用术语“婴儿化”( neotany)(来自词语" neonate",新生儿的意思)。

发生在朋友或自己身上的一些事,我们认为愚蠢,紧锁双眉;但同样的事如果发生在世界著名的天才身上,我们只会觉得古怪,一笑置之;永远不要忘记:可以随便犯蠢的自由很可能是打开天才成功之门的钥匙

这就是 大智若愚 吧。

在新知识有机会渗入到大脑之前,你必须设法避免将新旧知识混合在一起,并以初学者的心态着手解决新问题。这可能意味着短期内生产率会降低一些,以期在掌握新方法之后能获得技能水平的飞跃。

学习新东西是痛苦的,特别当你在顶着压力而且几乎没有人指导的情况下学习。尽管如此,正如运动员必须忍受艰苦训练后的肌肉痛楚,软件开发者需要忍受学习新东西后的神智错乱。

3、新人

尽管你缺乏经验(而且恰恰因为你缺乏经验),你会为团队带来一些独特的品质,包括富有感染力的激情。不要让任何人压抑了你对软件工艺的兴奋——它是一种宝贵的财富,它将加速你的学习。作为软件开发者,你将不可避免地成为团队一部分,并在此基础上展开工作。在任何组织结构中,都会有一种遵循规范的趋向,对新人而言尤其如此。大多数团队都不会对技术有过热的激情。可以肯定,他们都专注于交付下一个项目,或者改善软件开发周期中的某些让他们头痛的方面。因此,充满激情的学徒常常会屈服于“做人要低调”的外界压力。他们要么完全压抑自己的热情,要么仅在日常工作之余才让它表现出来。在相对完善的团队中释放激情当然是有风险的。 如果团队士气较低或者团队不欢迎新人,你可能在背后遭遇白眼。对那些认为竞争力比学习能力更重要的人来说,毫无疑问,你会给他们留下不好的印象,特别当你暴露出自己无知的时候。跟任何模式一样,这一模式也不应被盲目运用。团队动态( team dynamics)永远都是需要考虑的因素。如果你发现自己处在一个无法接受你激情的团队中,那么你将需要想些办法来养护自己的激情。

作者的观察真的细致,我之前也有类似的感悟,不过在中国,刚进入公司的新人,真的有激情,也尽量低调点,对自己大家都好。
不过这让我想到另一个新词:奋斗逼。(这个是贬义,当然跟上面提到的有激情不一样)

思想的多样性应看作集体智慧的关键因素。对航空母舰舰队集体心理的一次有趣的研究显示:要安全地操纵一艘战斗机不断从它上面起降的巨船,需要复杂的、相互配合的行动,而新人在这类行动中扮演重要角色。研究者发现,一个由不同经验水准的人组成的团队更加健康。如果把不同的经验水平关联到一起,比如当脑子里没有任何“想当然”的新手与认为自己已纵观全貌的老前辈们更加频繁地打交道时,大家对问题的理解都会加深。

确实,一个健康的团体还是参差多态的好。

随着你慢慢过渡到熟练工的角色,你将变得越来越不依赖于这些技能,慢慢地别人开始基于你的名声、你以前参与过的项目,以及你能带给团队的更深层品质来雇佣你。

古有:始于颜值,陷于才华,忠于人品。
今有:始于技能,陷于名声,忠于品质。

专家技能是我们走的这条漫漫长路的副产品,但不是目的地。

追求卓越,成功就会主动找上你。—— 《三傻》电影

4、无知

如果你想让他们安心,那也应该通过你的学习能力,而不是通过假装知道自己并不知道的东西。这样,你的名声将建立在你的学习能力上,而不是你已经知道的事情上。暴露无知,最简单的方法就是问问题。

选出一种技能、工具或技术,积极地填补跟它有关的知识空白。采用一种对你最有效的方法来做这件事。对于某些人,最好的方法可能是阅读能接触到的所有文献和 FAQ,来获得知识概览。其他人则可能觉得直接动手构造一个“质脆玩具”才是理解一样东西的最有效途径。不管哪种方法适合你,都不要忘了问问周围的“同道中人”和指导者,看是否有人已经掌握了这项技能并愿意分享所学。有时其他人也可能在学习这项技能,跟他们一起工作你会获得更快的进步。到某个时刻,你将在这个新领域达到令人满意的能力水准,这时你就可以做决定了,是继续深入挖掘下去更有成效呢,还是应将注意力转到其他的技能空白上。每天只有 24 小时,你无法将每项技能都研磨到很高水平,因此你必须学会在它们中间做必要的权衡。

它不只是征服先前未知的高峰,还要一步一步地走出一条新的道路。

第 3 章 走过漫漫长路


1、技重于艺

我愿意将编程说成一种技艺,技艺本身也是一种艺术,但它不是美术。技艺的意思是:使用可能带有装饰性的手法来制作有用的对象。美术的意思则是制作东西纯粹为了使之美丽。——Richard Stallman 在"Art and Programming"(艺术与编程)

作为技师,你的首要工作是构建能满足他人需要的东西,而不是沉迷于艺术展现。毕竟,世上不该有挨饿的技师。 如果你挨饿了,因为你太像一个艺术家,你创造的东西太美以致在现实中无法交付,那你就是离开了技艺。

我们为客户构建的东西“可以”是美的,但“必须”是有用的。
其中的一部分就是培养在必要时牺牲美丽来换取效用的能力。
功用和美好并不对立,而是相辅相成。

2、持续动力

”做你喜爱的事,钱自然会来。”

我喜欢睡觉……

“如果你认为一样东西有趣,你就能学到有趣的东西。”

上帝只让一小部分人开开心心地通过自己喜爱的工作谋生。感谢上帝,让我成为其中之一。

受到来自消费者至上主义者和速成社会的塞壬歌声的诱惑,我们有时会选择一种只会带来成功表象和满意幻影的行动过程。—— George Leonard,《 Mastery》(精通)

大多数人把提升到管理职位等同于成功。

远离幻象。

聪颖而且努力的学徒千万不要对自己的成功自鸣得意。在软件开发领域,超越平庸是很容易的,因为有太多的人只超出平均曲线一点点就开始满足了。你必须去追寻那些技艺精湛、学徒甚至无法想象其水平的其他团队、组织、熟练工和师傅,向他们学习,以此来抗争趋于平庸的倾向。

3、另辟蹊径

不要因为他们没有走在你所走的路上,就认为他们已经迷失了。

即使永远地离开原来的道路,在这一路上你所形成的价值和原则也将一直陪伴着你。

一切经历皆有用。

遗憾的是,传统的软件组织不一定会如此欢迎。他们常常把这些弯路看作职业生涯中的缺口,因此你必须为此做正当的解释。他们会期望你能给出一个符合他们价值系统的合理解释,

你很难想象一个程序员职业生涯中途出现跑出去当了几年厨子给面试官带来的坏印象。

4、学徒与师傅

不知而不知其不知者,愚者也——避之!
不知而知其不知者,惑者也——授之!
知之而不知其知之者,寐者也——醒之!
知之而知其知之者,觉者也——从之!
—— Isabel Burton( 1831— 1896)女士在《 The Life of Captain Sir Richard F. Burton》( Richard F. Burton 上尉的一生)一书中引用的阿拉伯谚语

学徒向技师学习,技师也向学徒们学习。激情洋溢的初学者不仅能恢复技师的活力,还能从外界带来新的思想来挑战更有经验的技师。精心挑选的学徒甚至能使师傅变得更有成效。
俗话说:“一个人教的时候,两个人在学。”

所以从辩证的角度来看,学徒也是师傅的师傅。

顶多,你可以说这个人技能水平比你高。但这并不足以证明她技艺精通。一个人走在了你的前面并不能证明她是个师傅。

在那一天到来之前,发现精通技艺的最好方法只能是考察一个人和她的学徒们的工作质量。
仅仅是天才并不能成就精通,但如果一个人可以培训他人,使之达到或超越自己的才华,那就是一种证据,证明此人有师傅的潜力。

能把别人教的很厉害的人,自己一定也很厉害。

第 4 章 准确的自我评估


1、只求最差

宁为狮尾,不为狐头!

还有另一句相反的:宁做鸡头,不做凤尾。

让周围多些水平比你更高的开发者。找一个更强大的团队,在那里让自己成为最弱的成员并拥有成长的空间。

记住,被一位潜在的指导者拒绝或者认为你奇怪的概率并不高,而潜在的回报却是巨大的。即使那人没兴趣收你为全职学徒,邀请她出来共进午餐也会是很有价值的时间和金钱投资。

多多请教。

这类任务包括维护构建系统,产品支持,响应维护需求,bug 修正,代码复查,消除技术债务(technical debt),搭建项目 wiki,更新文档,为其他人的想法充当传声筒,等等。通常,你会关注风险更低的系统边缘部分,而不是常常带有大量依赖性和极高复杂度的核心。Jean Lave 和 Etienne Wenger 观察了不同行业中的学徒,发现“在工作流程中,新手的任务往往被置于分支的末尾,而不是一连串工作片段的中间”(《Situated Learning》(情景化学习),第 110 页)。这类边缘任务会使团队受益,对作为学徒的你也有好处,因为这些杂务在学校的课程中常常被略过了,做一做能填补知识中的漏洞。当你成为熟练工时,这种体验还是能带来帮助,因为很多带你的师傅都明白:有个人去做单调的工作是多么重要。毕竟,如果没人打扫地面,那么富有魅力的工作也无法做出来,因为团队已深陷脏乱之中。

被你的团队拖延了几个月的最邋遢的任务是什么?它应该是所有人都抱怨,却没有人愿意解决的那个。你去解决它。不要捂着鼻子强迫自己去做;看自己能否找到一种超出人们预期并为自己带来乐趣的方式,创造性地解决这个问题

2、自我评估

他们的同名论文中指出的:技艺不精的人常常不知道自己技艺不精。再者,越是技艺不精,你越不善于评估自己和他人的技能。

他们希望,如果曾经有一支人数和经验水平都差不多的团队在过去能完成一件差不多的事情,那么今天的团队或许也能完成。不幸的是,软件开发中的技能水平范围很广,在我们当中,最优秀的那些人可以稀松平常地做出大多数人认为不可能的事情。此外,多数程序员都认为自己的水平超出平均。悲惨的事实是:由于下图所示的不规则的技能水平分布,大多数程序员实际上是低于平均水平的。这听起来有点反直觉,但你可以设想这样一个比方:现在我们两个人, Dave 和 Ade 坐在桌子边上,然后 Bill Gates 过来加入我们,突然之间坐在桌子旁的“大多数”人的薪水都低于平均水平了。也就是说,在编程技能曲线上处于极远端的那些人倾斜了整个分布。

3、天赋与性格

天赋常常被误解。它并不是超常的智力,而是一种性格。

第 5 章 恒久学习


1、不断实践

“一个人一旦停止了实践,她对技艺的精通就马上开始消退。”每一个不写程序的日子,你都是在不断远离熟练工的目标。“

我们所知道的大师,不会仅为了做得更好而让自己专注于某项技能。实际情况是:他们热爱实践——并且正因为热爱实践所以才做得更好。事情总是相辅相成的,做得越好,他们就越喜欢反复不断实施这些基本的实践活动。——George Leonard,《Mastery》(精通)

“刻意实践”(deliberate practice)。

2、质脆玩具

我们都可以从编写随意的“玩具”程序中受益,编写玩具程序时,我们会设置一些人为的限制,从而将自己的能力推到极限。——Donald Knuth,《The Art of Computer Programming》(中译本及双语本《计算机程序设计艺术》,机械工业出版社)

如果来自失败的经验可以与来自成功的经验一样多,那么你需要一个相对私有的空间来寻求失败。在抛球杂技中,抛三只球的表演者,如果从来没抛过五只球,那他就永远不会取得进步。而那些连续几个小时去拣落下的球直到拣得背疼的人,最终却能把技艺练好。同样的经验也适用于软件领域。正如抛三只球的表演者不会在正式的表演中抛五只球,软件开发者也需要一个安全的地方来犯错误。

“质脆玩具”模式的其他例子包括像 Tetris 和 Tic-Tac-Toe 这样的游戏(我们的一位前同事有一种习惯:每学习一门新语言,就用它来编一个游戏)、博客软件和 IRC 客户端。问题的本质在于构建玩具包含了对新事物的学习,而且提供机会让你在特殊的环境中加深对手中工具的理解,这个环境不仅安全(因为你是唯一或者最重要的用户),而且,即使跟最强大的商业产品比起来,你仍有余地来更好地服务自己作为一名用户的需求。

你还是要记住:它们只是玩具,而且正因为这一点它们应该是有趣的。如果它们没什么意思,那么当最初的热乎劲过去之后,它们将成为尘封的旧物,而你则将自己的精力关注到你真正乐于构建的东西上去了。

我之前也想过并做过,我称之为 demo。

3、开源项目

在研究一个开源项目时,要养成下载最新版代码的习惯(最好从它们的源码控制系统下载),这样你可以查阅它的历史,跟踪未来的发展。看一下代码库的结构,想一想为什么代码是那样组织的。看一看开发者组织代码模块的方式是否有一定的道理,拿它跟自己可能使用的方式比较一下。试着重构代码,从而理解为什么它的编码者做了那样的决定,同时想想如果你是那个编码的人,写出的代码将是什么样子。这不仅能让你更好地理解这些项目;还确保你能构建这些项目。如果你发现了做一件事的更好方法,你就完全可以为这个项目贡献代码了。在研究代码的过程中,你会不可避免地看到一些自己完全不同意的决策。问问自己,是否项目的开发者可能知道一些你不知道的东西,或者相反。考虑一下有没有可能这是一个历史遗留设计,需要重构一下;并考虑:为相关特性做个玩具实现是否有助于澄清问题。

最终你将获得一个工具盒,里面都是从其他人的代码中收集来的各种奇技。这会磨炼你更迅速、更快捷地解决小问题的能力。你将能处理别人认为不可能解决的问题,因为他们接触不到你的工具盒。看一看 Linus Torvalds 编写的 Git 分布式源码控制系统的代码,或者任何 Daniel J. Bernstein(众所周知的 djb)编写的代码。像 Linus 和 djb 这样的程序员偶尔会用到我们大多数人甚至从未听说过的数据结构和算法。他们并不是魔术师——跟大多数人相比,他们不过是花时间构造了更大更好的工具盒。开源的优势就在于你可以随意观看他们的工具盒,而且可以把他们的工具变成你自己的。软件开发领域的问题之一是缺少教师。不过多亏了诸如 sourceforge. net 和 GitHub 这些站点上开源项目的繁荣,你可以从全世界程序员社区中那些相对有代表性的代码范例中学习

在《Programmers at Work》一书中, Bill Gates 说:“对编程能力最精细的测试就是给程序员大约 30 页的代码,看他能多快地通读并理解它。” 他意识到了一种很重要的东西。那些能直接从源码中快速汲取知识的人很快就能成为更好的程序员,因为他们的老师就是世界上的每一位程序员写下的一行行代码。要学习模式、惯用法和最佳实践,最好的方法就是阅读开源代码。看看其他人是如何写代码的。这是一种保持自己不落伍的优秀方法,而且是免费的。—— Chris Wanstrath 在 2008 Ruby 大会上的主讲内容[

挑选一个算法精深的开源项目,如 Subversion、 Git 或 Mercurial 这样的源码控制系统。浏览项目的源码,记下让你觉得新奇的算法、数据结构和设计理念。然后写一篇博客,描述一下项目的架构,着重突出自己学到的新思想。你能在日常工作中找到可运用同样思想的场合吗?

4、个人实践图

只要你工作的时间足够长,人们就会开始称你为“老手”,但这不应该是你的目标。所有的经验都表明你已经有能力在这个行业生存。但它显示不出你所学到的知识量,仅仅是你所花费的时间。在我们行业的某些领域,有时很容易将同样的时间经历重复 10 次却没有取得能力上的实质进步。事实上,这有时会变成“反经验”(anti-experience),即这样一种现象:每一次新的时间经历仅仅强化了你所养成的坏习惯。这就是为什么你的目标应该是达到技能娴熟,而非“有经验”。技能水平的提高,是你在研究、适应及改善工作习惯方面所付出努力的唯一有效的证明。

一种可用于明确表达这种反思的技巧是使用“个人实践图”(Personal Practices Map)。

为你的工作习惯画一张“个人实践图”。重点关注一段时间内没有改变的那些实践之间的关联。问自己,如果你发现当中的某种实践实际上对生产率起反作用,你的图该做怎样的修改。仔细检查其中的某一种实践,看看是否存在达到同样目标的其他方法。它未必是更好的方法;只要不同就够了。然后问自己,如果你采用其中一种不同的实践,这张图又会怎样变化。

5、记录所学

尽量避免落入写下经验之后就忘记它们的陷阱。你的笔记、博客或 wiki 应该是一个托儿所,而不是一座坟墓——经验应该从这次记录中降生,而不是到那里去灭亡。定期读一读以前写的东西,你就能保证这一点。试着在每次重读这些资料时都找到新的关联。这种创造性的复读过程能让你基于新的数据重新评估旧的决策,或者坚定正在摇摆的信念。这两种结果都不错,只要你别停滞就行。重读你的笔记,你可以不断变换自己的过去和现在,从而造就你的未来。

Ade 使用了同一种 wiki 的两个实例,一个用于记录私人的想法,另一个记录那些想要与别人分享的想法。在拥有公开记录的同时再保留一套私人记录,这意味着你可以同时利用两个世界的优势。

6、分享所学

要在学徒期的早期就养成定期分享所学经验的习惯。形式可以是撰写博客,或者跟你的“同道中人”一起开展“便当会议”。也可以在技术会议上做演讲,或者为你正在学习的各类技术技巧编写教程。

7、学会失败

我觉得一个学徒不应该过早地致力于不犯错误,而应该尽早找出如何确定错误的办法。一旦学徒能确定他们的错误,从错误中学习就容易得多了。

8、追根溯源

找出是谁第一次提出了那种思想,弄明白他们当初想要解决的问题。这样的上下文通常在思想四处传播的过程中由于各种转译而丢失了。

第 6 章 安排你的课程


1、阅读

任何一本书,你能从中获得的最有价值的东西就是一列值得阅读的其他书目。时间长了,你会发现某些书不断地从“参考书目”( bibliography)中跳出来,你应把那些书移到阅读列表的顶部。其他书会下沉。由于阅读列表实际上是个优先级队列(priority queue),最终你会发现有的书已经在队列中下沉得太深,你可能永远也不会再读它们了。

开始时挑一本宽泛的书,让自己对目标主题有大致的了解;然后选择一些内容具体的书,从而掌握该主题中自己感兴趣的方面。

在合适的时间读合适的书会有更好的效果。

即使每两个月读一本编程相关的好书,也就是每周 35 页左右,用不了多久,你就会对我们的行业有深入的理解,并使自己不同于周围的人。——Steve McConnell,《Code Complete》

关注于经典作品也有风险:你对它们投入过多的精力,而完全不顾那些能提高日常技能的、更加注重实效的知识和信息。在你的阅读列表中,要确保经典名著和现代的、更注重实效的图书和文章混合出现。

在实践中,算法问题不会在大项目刚开始的时候就出现。然后,当突然之间程序员不知道如何继续下去或者目前的程序显然不恰当时,它们就作为子问题出现了。——Steven S.Skiena,《The Algorithm Design Manual》(算法设计手册)情景分析

要真正理解任何思想,你都需要重建它第一次被表达时的上下文。

2、常用工具

Ade 很早就采用了名叫 Subversion 的集中式源码控制系统。随着 Subversion 更加流行,会有客户请 Ade 为他们的项目提供帮助,因为他是这方面的专家。尽管如此, Ade 却从一开始就在关注分布式源码控制系统这一新品种的出现。[ 1] 当 Subversion 变得过时,它在 Ade 工具箱中的位置将早已被 Git[ 2] 或 Mercurial[ 3] 所取代。放弃熟悉而又好用的工具是一种让人痛苦的过程,但也是一种需要学会的技能。我们可以保证:你在学徒期使用的工具到你变成熟练工时肯定会变得过时。最终,你所钟爱的所有工具都将变成垃圾。