读《程序员修炼之道》 博客分类: Other 软件测试编程项目管理数据结构应用服务器
程序员文章站
2024-02-21 20:11:10
...
译序
1. 编程是一种技艺,一种需要用心学习的技艺,也许,只有在长久的学习之后,我们才会开始明白“hacker”的真正含义:"Someone who loves to program and enjoys being clever about it"。
前言
1. 思考你的工作
序
1. 调整你的方法,以适应当前情形与环境
2. 注重时效的程序员不仅要完成工作,而且要完成得漂亮
3. 每一个开发真都是独特的,有着个人的力量和弱点、偏好和嫌恶
4. 每一条小知识都可能会影响今后几年里的某项决策
5. 不要靠自动驾驶仪,不间断的思考,实时的批判你的工作,老IBM公司的箴言,THINK!
6. 我们采集的只是石头,却必须时刻展望未来的大教堂——采石工人的信条
第1章 注重实效的哲学
1. 能够越出直接的问题去思考,设法把问题放在更大的语境中,总是设法注意更大的图景。
2. 对所做的每件事情负责。
3. 你需要拥有广泛的知识和经验基础才能赢得一切。学习是一个持续不断的过程。
4. 在所有弱点中,最大的弱点就是害怕暴露弱点
5. 为你自己和你的行为负责这样一种观念,是注重实效的哲学的一块基石。注重实效的程序员对他或她自己的职业生涯负责,并且不害怕承认无知或错误。
6. 我们可以为我们的能力自豪,但对于我们的缺点——还有我们的无知和我们的错误——我们必须诚实。
7. 负责是你主动承担的东西。你承诺确保某件事情正确完成,但你不一定能直接控制事情的每一个方面,除了尽你所能以外,你必须分析风险是否超出了你的控制。对于不可能做到的事情或是风险太大的事情,你有权不去为之负责。你必须基于你自己的道德准则和判断来做出决定。
8. 如果确实同意要为某个结果负责,你就必须切实负起责任。当你犯错误,或是判断失误时,诚实的承认它,并设法给出各种选择,不要责备别人或别的东西,或是拼凑借口。
9. 要提供各种选择,而不是找接口。不要说事情做不到,要说明能够做什么来挽回局面。
10. 不要害怕提出要求,也不要害怕承认你需要帮助。
11. 在有些情况下,你也许确切的知道需要做什么,以及怎样去做,整个系统就在你的眼前——你知道它是对的。但请求许可去做事情,你会遇到拖延和漠然。 设计出你可以合理要求的东西,好好开发它,一旦完成,就拿给大家看,让他们大吃一惊。然后说:“要是我们增加……可能就会更好”。假装那并不重要。坐回椅子上,等着他们开始要你增加你本来就想要的功能。人们发现,参与正在发生的成功要更容易。让他们瞥见未来,你就能让他们聚集在你周围。
12. 常常是小事情的累积破坏了士气和团队。
13. 请求原谅比获取许可更容易
14. 欲求更好,常把好事变糟。
15. 时间、技术和急躁都在合谋反对我们。
16. 知道何时止步、
17. 不要因为过度修饰和过于求精而损毁完好的程序
18. 知识上的投资总能得到最好的回报
19. 你的知识和经验是你最重要的职业财富。
20. 程序员所知道的关于计算技术和他们所工作的应用领域的全部事实以及他们的所有经验视为他们的知识资产。管理知识资产与管理金融资产非常相似。
21. 经营你的资产
1. 定期投资——就像金融投资一样,你必须定期为你的知识资产投资,即使投资量很小,习惯自身也和总量一样重要。
2. 多元化——你知道的不同的事情越多,你就越有价值。未作底线,你需要知道你目前所用的特定技术的各种特性。但不要就此止步,你掌握的技术越多,你就越能更好的进行调整,赶上变化。
3. 管理风险——从高风险、可能有高回报,到低风险、低回报,技术存在于这样一条谱带上,把你所有的金钱都投入可能突然崩盘的高风险股票并不是一个好主意,你也不应太保守,错过可能的机会。不要把你所有的技术鸡蛋都放在一个篮子里。
4. 低买高卖——在新兴的技术流行之前学习它可能就和找到被低估的股票一样困难,但所得到的就和那样的股票带来的收益一样。
5. 重新评估和平衡。
22. 目标
1. 每年至少学习一种新语言——不同语言以不同方式解决相同的问题,通过学习若干不同的方法,可以帮助你拓宽你的思维,并避免墨守成规。
2. 每季度阅读一本技术书籍——一旦你养成习惯,就一个月读一本书。在你掌握了你正在使用的技术之后,拓宽范围,阅读一些与你项目无关的书籍。
3. 也要阅读非技术书籍。
4. 上课
5. 参加本地用户组织——与世隔绝对你的职业生涯来说可能是致命的,打听一下你们公司以外的人都在做什么。
6. 试验不同的环境
7. 跟上潮流——选择所涵盖的技术与你当前项目不同的刊物。
8. 上网
23. 持续投入十分重要,一旦你熟悉了某种新语言或新技术,继续前进,学习另外一种。
24. 设法把你学到的东西应用到你当前的项目中。即使你的项目没有使用该技术,你或许也能借鉴一些想法。
25. 如果你自己找不到答案,就去找能找到答案的人。不要把问题搁在那里,与他人交谈可以帮助你建立人际网络,而因为在这个过程中找到了其他不相关问题的解决方案,你也许还会让自己大吃一惊。
26. 所有阅读和研究都需要时间,而时间已经很短缺。所以你需要预先规划,让自己在空闲的片刻时间里总有东西可读。
27. 批判的思考你读到的和听到的。你需要确保你的资产中的知识是准确的,并且没有收到供应商或媒体炒作的影响。
28. 与Guru打交道的礼节与教养:
1. 确切的知道你想问什么,并尽量明确具体
2. 小心而得体的组织你的问题。记住你是在请求帮助;不要显得好像是在要求对方回答。
3. 组织好问题后,停下来,再找找答案
4. 决定你是想公开提问还是私下提问
5. 坐回椅子上,耐心等候。
29. 我相信,被打量比被忽略要好
30. 问题不只是你有什么,还要看你怎样包装它,除非你能够与他人交流,否则就算你拥有最好的主意,最漂亮的代码,或是最注重实效的想法,最终也毫无结果,没有有效的交流,一个好想法就只是一个无人关心的孤儿。
31. 只有当你是在传达信息时,你才是在交流
32. 与制作文档的过程相比,我们制作出的文档最后并没有那么重要。
33. 如果你想要大家听你说话,你必须使用一种方法,听他们说话。
34. e-mail是永久性的,要设法像对待任何书面备忘录或报告一样小心对待e-mail
第2章 注重实效的途径
1. 不要在系统各处对知识进行重复;不要把任何一项知识分散在多个系统组件中。
2. 我们都是在一个时间和资源有限世界上工作,如果你善于估计事情需要多长时间完成,你就能更好的在两者都很匮乏的情况下生存下去。
3. 系统中的每一项只是都必须具有单一、无歧义、权威的表示。
4. 糟糕的代码才需要注释
5. 不可信任的注释比完全没有注释更糟
6. 欲速则不达
7. 你所要做的是营造一种环境,在其中要找到并复用已有的东西,比自己编写更容易。如果不容易,大家就不会去复用。而如果不进行复用,你们就会有重复知识的风险。
8. 非正交系统的改变与控制更复杂是其固有的性质。当任何系统的各组件互相高度依赖时,就不再有局部修正(local fix)这样的事情。
9. 编写正交系统的好处:提高生产率与降低风险。
1. 提高生产率
1. 改动得以局部化,所以开发时间和测试时间得以降低。
2. 正交的途径还能促进复用
3. 如果你对正交的组件进行组合,生产率会有相当微妙的提高。
2. 降低风险
1. 有问题的代码区域被隔离开来
2. 所得系统更健壮
3. 正交系统很可能能得到更好的测试
4. 你不会与特定的供应商、产品、或是平台紧绑在一起
10. 从基础设施与应用分离开始,每个主要的基础设施组件有自己的子团队,如果应用功能的划分显而易见,那就照此划分,然后我们考察我们现有的(或计划有的)人员,并对分组进行相应的调整。
11. 不要依赖你无法控制的事物属性。
12. 在你引入第三方工具箱和库时,要注意保持系统的正交性,要明智的选择技术。
13. 第三方库细节与代码隔离有额外的好处:它使得你在以后更容易更换供应商。
14. 用于维持正交性的技术:
1. 让你的代码保持解耦
2. 避免使用全局数据
1. 使用Singleton要小心,它可能造成不必要的关联
3. 避免编写相似的函数
15. 要成不断的批判对待自己的代码的习惯,寻找任何重新进行组织,以改善其结构和正交性的机会,这个过程叫重构。
16. 构建但愿测试本身是对正交性的一项有趣测试
17. 如果你参加了一个项目,大家都在不顾一切的做出改动,而每一处改动似乎都会造成别的东西出错,回想一下直升机的噩梦,项目很可能没有进行正交的设计和编码,是重构的时候了。
18. 如果某个想法是你唯一的想法,再没有什么比这个更危险的事情了。
19. 如果你严重依赖某一事实,你几乎可以确定它将会变化
20. 可以把第三方产品隐藏在定义良好的抽象接口后面
21. 无论你使用的是何种机制,让它可以撤销,如果某样东西是自动添加的,它也可以被自动去掉
22. 没有人知道未来会怎样,尤其是我们!所以要让你的代码学会“摇滚”,可以“摇”就“摇”,可以“滚”就“滚”。
23. 一旦你在系统的各组件间实现了端到端的链接,你就可以检查你离目标还有多远,并在必要的情况下进行调整,一旦你完全瞄准,增加功能将是一件容易的事情。
24. 曳光弹代码方法的优点:
1. 用户能够及早看到能工作的东西
2. 开发者构建了一个他们能在其中工作的结构
3. 你有了一个集成平台
4. 你有了可用于演示的东西
5. 你将更能够感觉到工作进展
25. 原型制作是一种学习经验,其价值并不在于所产生的代码,而在于所学到的经验教训。那才是原型制作的要点所在。
26. 语言的界限就是一个人的世界的界限
27. 计算机语言会影响你思考问题的方式,以及你看待交流的方式
28. 问题领域的语言也可能会提示出编程方案
29. 应该让你的项目更靠近问题领域,通过在更高的抽象层面上编码,你获得了专心解决领域问题的*,并且可以忽略琐碎的实现细节。
30. 数据语言产生某种形式的数据结构给应用使用,这些语言常用语表示配置信息。
31. 命令语言将被实际执行,包含语句、控制结构,以及类型的东西。
32. 把高级命令语言直接嵌入你的应用程序是一种常见做法。
33. 在被要求进行估算时说:“我等会回答你”,放慢估算速度,并花一点时间仔细检查各步骤,你几乎总能得到更好的结果。
第3章 基本工具
1. 工具放大你的才干,你的工具越好,你越是能更好的掌握它们的用法,你的生产力就越高。
2. 如果你没有高超的调试技巧,你就不可能成为了不起的程序员。
3. 通过纯文本,你可以做你通过某种二进制格式所能所的每件事情。
4. 大多数二进制格式的问题在于,理解数据所必须的语境与数据本身是分离的。通过纯文本,你可以获得自描述的不依赖于创建它的应用的数据流。
5. 使用纯文本的两个主要缺点:
1. 与压缩的二进制格式相比,存贮纯文本所需空间更多
2. 要解释及处理纯文本文件,计算上的代价可能更昂贵
6. 文本的威力:
1. 保证不过时
2. 杠杆作用
3. 更易于测试
7. GUI的好处是WYSIWYG——所见即所得(what you see is what you get),缺点是WYSIAYG——所见即全部所得(what you see is all you get)。
8. 现代计算机系统仍然局限于做你告诉它的事情,而不一定是你想要它做的事情。
9. 在技术竞技场上,你应该专注于修正问题,而不是发出指责。
10. bug是你的过错还是别人的过错,并不是真的很有关系,它仍然是你的问题。
11. 调试的第一准则:不要恐慌。
12. 我们想要的不是能够通过长长的步骤再现的bug;我们要的是能够通过一条命令再现的bug。
13. bug有可能存在于OS、编译器、或是第三方产品中——但这不应该是你的第一想法。有大得多的可能性的是,bug存在于正在开发的应用代码中。
14. 当你遇到让人吃惊的bug时,除了只是修正它而外,你还需要确定先前为什么没有找出这个故障。考虑你是否需要改进单元测试或其他代码,以让它们有能力找出这个故障。
15. 确保无论发生什么,你都知道它是否会再次发生。如果修正这个bug需要很长时间,问问你自己为什么。你是否可以做点什么,让下一次修正这个bug变得更容易?
16. 如果bug是某人的错误假定的结果,与整个团队讨论这个问题,如果一个人有误解,那么许多人可能也有。
第4章 注重实效的偏执
1. 当每个人都确实要对你不利时,偏执就是一个好主意
2. 没有什么比常识和坦率更让人感到惊讶
3. 与计算机系统打交道很困难,与人打交道更困难
4. 保持坦率的最佳方案之一就是合约。合约即规定你的权利与责任,也规定对方的权利与责任。此外,还有关于任何一方没有遵守合约的后果的约定。
5. 通过早崩溃,在问题现场找到和诊断问题要容易得多。
6. 有时候别人在你自己意识到之前就能觉察到你的事情出了问题。
7. 尽早检测问题的好处之一是你可以更早崩溃。许多时候,让你的程序崩溃是你的最佳选择。
8. 死程序带来的危害通常要比有疾患的程序小得多。
9. 无论何时你发现自己在思考“但那当然不可能发生”,增加代码检查它,最容易的办法是使用断言。
10. 分配某项资源的例程或对象应该负责解除该资源的分配。
11. 当你解除顶层结构的分配时,该如何决定谁为某个聚集数据结构中的数据负责:
1. 顶层结构还负责释放它包含的任何子结构,这些结构随机递归的删除它们包含的数据
2. 只是解除顶层结构的分配,它指向的(没有在别处引用的)任何结构都会被遗弃。
3. 如果顶层结构含有任何子结构,它就拒绝解除自身的分配。
第5章 弯曲,或折断
1. 编写羞怯的代码是有益的:不向别人暴露你自己,不与太多人打交道。
2. 使若干模块紧密耦合,你可以获得重大的性能改进,只要对于那些被耦合在一起的模块而言,这是众所周知的和可以接受的,你的设计就没有问题。
3. 时间有两个方面对我们很重要:并发(事情在同一时间发生)和次序(事情在事件中的相对位置)。
4. 大多数人的思考方式——总是先做这个,然后再做那个,但这样思考会带来时间耦合,在时间上的耦合。
5. 我们创建的不是组件,而是服务——位于定义良好的,一致的接口之后的独立,并发的对象。
6. MVC:
1. 模型:表示目标对象的抽象数据模型,模型对任何视图或控制器都没有直接的了解。
2. 视图:解释模型的方式,它订阅模型中的变化和来自控制器的逻辑事件。
3. 控制器:控制视图,并向模型提供新数据的途径,它既向模型,也向视图发布事件。
7. 黑板方式的编程消除了对太多接口的需要,从而能带来更优雅、更一直的系统。
8. 黑板:数据到达的次序无关紧要;在收到某项事实时,它可以出发适当的规则,反馈也很容易处理;任何规则集的输出都可以张贴到黑板上,并出发更为适用的规则。
9. 可以用黑板协调完全不同的事实和因素,同时又使各参与方保持独立,甚至隔离。
第6章 当你编码时
1. 编码不是机械工作,程序员每一分钟都需要作出决策——如果要让所得的程序享有长久、无误和富有生产力的“一生”,就必须对这些决策进行仔细的思考和判断。
2. 不主动思考他们的代码的开发者是在靠巧合编程——代码也许能工作,但却没有特别的理由说明他们为何能工作。
3. 要让代码易于测试
4. 小心那些替你编写大量代码的工具,除非你理解他们在做什么。
5. 保持警觉能够很好的防治灾难的发生。
6. 怎样深思熟虑的编程:
1. 总是意识到你在做什么
2. 不要盲目的编程,试图构建你不完全理解的应用,或是使用你不熟悉的技术,就是希望自己被巧合误导
3. 按照计划行事
4. 依靠可靠的事物
5. 为你的假定建立文档
6. 不要只是测试你的代码,还要测试你的假定。编写断言测试你的假定,如果的你断言是对的,你就改善了代码中的文档,如果你的假定是错的,那么就为自己庆幸吧。
7. 为你的工作划分优先级,把时间花在重要的方面,很有可能,他们是最难的部分。如果基本原则或基础设施不正确,再花哨的铃声和口哨也是没有用的。
8. 不做历史的努力。不要让已有的代码吃陪将来的代码,如果不再使用,所有的代码都可被替换。不要让你已经做完的事情约束你下一步要做的事情——准备好进行重构。
7. 每个开发者都应该有设计与分析算法的才能。
8. 随着程序的演化,我们有必要重新思考早先的决策,并重写部分代码。这一过程非常自然,代码需要演化,它不是静态的事物。
9. 时间压力常常被用作不进行重构的借口,但这个借口并不成立。
10. 追踪需要重构的事物。如果你不能立刻重构某样东西,就一定要把它列入计划。确保收到影响的代码的使用者知道该代码计划要重构,以及这可能会怎样影响他们。
11. 重构是一项需要慎重,深思熟虑,小心进行的活动。
1. 不要试图在重构的同时增加功能
2. 在开始重构前,确保你拥有良好的测试。尽可能经常运行这些测试。这样,如果你的改动破坏了任何东西,你就能很快知道。
3. 采用短小、深思熟虑的步骤。如果你使你的步骤保持短小,并在每个步骤之后进行测试,你将能够避免长时间的调试。
12. 你看到不怎么合理的代码是,既要修正它,也要修正依赖于它的每样东西。要管理痛苦:如果它现在有损害,但以后的损害会更大。你也许最好一劳永逸的修正它,记住软件熵中的教训:不要容忍破窗户。
13. 不管是那种方法,要记住:如果你不容易找到它,你就不会使用它。
14. 如果代码曾经出现过问题,它很可能还会再出问题。
15. 你编写的所有软件都将进行测试——如果不是由你和你们团队测试,那就要由最终用于测试——所以你最好计划好对其进行彻底的测试。
16. 测试是技术,但更是文化。
17. 毋庸否认,应用的编写正变得越来越难。特别是用户界面,正在日益变得复杂。
第7章 在项目开始之前
1. 项目启动太快是一个问题,但等得太久可能会更糟。
2. 完美,不是在没有什么需要增加,而是在没有什么需要去掉时达到的。
3. 需求很少存在于表面上。通常,他们深深地埋藏在层层假定、误解和政治手段的下面。
4. 找出用户为何要做特定事情的原因,而不是他们目前做这件事情的方式,这很重要。开发必须解决用户的商业问题,而不只是满足他们陈述的需求。
5. 尽管奴隶般重复已经存在的事物会阻碍进步,但我们必须要提供通往未来的过渡。
6. 成功的工具会适应使用它们的双手。
7. 不要做任何表示方法的奴隶。
8. 需求不是架构,需求不是设计,也不是用户界面,需求是需要。
9. 许多项目的失败都归咎于项目范围的增大——也称为特性膨胀、蔓延特性论、或是需求蔓延。
10. 管理需求增长的关键是向项目出资人指出每项新特性对项目进度的影响。
11. 有些约束是绝对的,有些则是先入之见。绝对的约束必须受到尊重,不管它们看上去有多讨厌或多愚蠢;另一方面,有些外表上的约束也许根本不是真正的约束。
12. 解开谜题的关键在于确定加给你的各种约束,并确定你确实拥有的*度。
13. 在面对棘手的问题时,列出所有在你面前的可能途径。不要排除任何东西,不管它听起来有多无用或愚蠢。现在,逐一检查列表中的每一项,并解释为何不能采用某个特定的途径。你确定吗?你能否证明?
14. 软件开发仍然不是科学,让你的直觉为你的表演做出贡献。
15. 没有给编码者留下任何解释余地的设计剥夺了他们发挥技巧和艺术才能的权利。
16. 你越是把规范当做安乐毯,不让开发者进入可怕的编码世界,进入编码阶段就越困难。
17. 盲目的采用任何技术,而不把它放进你的开发实践和能力的语境中,这样的处方肯定会让你失望。
18. 决不要低估采用新工具和新方法的代价,要做好准备,把使用这些技术的第一个项目当做一种学习经验。
19. 要记住谁是主人,不要变成方法学的奴隶。
第8章 注重实效的项目
1. 质量是一个团队问题,不应该容忍破窗户。
2. 质量只可能源于全体团队成员都作出自己的贡献。
3. 团队无需拒绝无法控制的变化——你只需注意到它们正在发生。
4. 看上去沉闷寡言的项目是最糟糕的团队。
5. 有一个简单的营销诀窍,能帮助团队作为整体与外界交流:创立品牌。
6. 把你的人划分成小团队,分别负责最终系统的特定方面的功能。让各团队按照各人的能力,在内部自行进行组织。按照他们约定的承诺,对项目中的其他团队负有责任。
7. 创立一组自行其是的团队并放任自流,是一种灾难性的处方。项目至少需要两个“头”——一个主管技术,另一个主管行政。
1. 技术主管设定开发哲学和风格,给各团队指派责任,并仲裁成员之间不可避免的“讨论”,技术主管还要不断关注大图景,设法找出团队之间任何不必要的,可能降低总体正交性的交叉。
2. 行政主管(或项目经理)调度各团队所需的各种资源,见识并报告紧张情况,并根据商业需要帮助确定各种优先级,在与外界交流时,行政主管还要充当团队的大使。
8. 确保一致和准确的一种很好的方式是使团队所做的每件事情自动化。
9. 团队是由个体组成的,让每个成员都能以他们自己的方式闪亮,给他们足够的控件,以支持他们,并确保项目的交付能够符合需求。
10. 文明通过增加我们不加思索就能完成的重要操作的数目而取得进步。
11. 人工流程不能保证一致性,也无法保证可重复性,特别是在不同的人对流程的各个方面有不同解释时。
12. 让计算机去做重复,庸常的事情——它会做得比我们更好,我们有更重要,更困难的事情要做。
13. 用户高速了你他们需要什么,但那是他们需要的吗?
14. 注释应该讨论为何要做某事,它的目的和目标,代码已经说明了它是怎样完成。
15. 匈牙利表示法在OO系统是绝对不合适的。
16. 对待文档要像对待代码一样用新,用户(还有后来的维护者)会为你唱赞歌的。
17. 在现实中,项目的成功是由它在多大程度上满足了用户的期望来衡量的。不符合用于期望的项目注定是失败的,不管交付的产品在绝对的意义上有多好。
18. 决不要忘了你的应用要解决的商业问题。
19. 给他们的东西要比他们期望的多一点。给系统增加某种面向用户的特性所需的一点额外努力将一次又一次在商誉上带来回报。
20. 为用户的机构定制splash屏幕。
1、关心你的技艺
Care About Your Craft
除非你在乎能否漂亮地开发出软件,否则其它事情都是没有意义的。
2、思考!你的工作
Think!About Your Work
在你做某件事情的时候思考你在做什么。不间断地思考,实时地批判你的工作。这将占据你的一些宝贵时间,酬劳则是更为活跃地参与你喜爱的工作、感觉到自己在掌握范围日增的各种主题以及因感受到持续的进步而欢愉。从长远来说,你在时间上的投入将会随着你和你的团队变得更为高效、编写出更易于维护的代码以及开会时间的减少而得到回报。
3、提供各种选择,不要找蹩脚的借口
Provide Options,Don't Make Lame Excuses
不要说事情做不到;要说明能够做什么来挽回局面。不要害怕提出要求,也不要害怕承认你需要帮助。
4、不要容忍破窗户
Don't Live With Broken Windows
不要留着“破窗户”(低劣的设计、错误的决策、或者糟糕的代码)不修。发现一个就修一个。如果没有足够的时间进行适当的修理,采取某种行动防止进一步的破坏,并说明情势处在你的控制之下。
如果你发现你所在团队和项目的代码十分漂亮——编写整洁、设计良好,并且很优雅,你不会想成为第一个弄脏东西的人。
5、做变化的催化剂
Be a Catalyst for Change
你不能强迫人们改变。相反,要向他们展示未来可能会怎样,并帮助他们参与对未来的创造。
设计出你可以合理要求的东西,好好开发它。一旦完成,就拿给大家看,让他们大吃一惊。然后说:“要是我们增加...可能就会更好。”假装那并不重要。坐回椅子上,等着他们开始要你增加你本来就想要的功能。人们发现,参与正在发生的成功要更容易。让他们瞥见未来,你就能让他们聚集在你周围。
6、记住大图景
Remember the Big Picture
如果你抓一只青蛙放进沸水里,它会一下子跳出来。但是,如果你把青蛙放进冷水里,然后慢慢加热,青蛙不会注意到温度的缓慢变化,会呆在锅里,直到被煮熟。
不要像青蛙一样。留心大图景。要持续不断地观察周围发生的事情,而不只是你自己在做的事情。
7、使质量成为需求问题
Make Quality a Requirements Issue
你所制作的系统的范围和质量应该作为系统需求的一部分规定下来。让你的用户参与权衡,知道何时止步,提供足够好的软件。
8、定期为你的知识资产投资
Invest Regularly in Your Knowledge Portfolio
让学习成为习惯。
持续投入十分重要。一旦你熟悉了某种新语言或新技术,继续前进,学习另一种。
是否在某个项目中使用这些技术,或者是否把它们放入你的简历,这并不重要。学习的过程将扩展你的思维,使你向着新的可能性和新的做事方式拓展。思维的“异花授粉”十分重要;设法把你学到的东西应用到你当前的项目中。即使你的项目没有使用该技术,你或许也能借鉴一些想法。例如,熟悉了面向对象,你就会用不同的方式编写纯C程序。
如果你自己找不到答案,就去找出能找到答案的人。不要把问题搁在那里。
9、批判地分析你读到的和听到的
Critically Analyze What You Read and Hear
不要被供应商、媒体炒作、或教条左右。要依照你自己的看法和你的项目的情况去对信息进行分析。
10、你说什么和你怎么说同样重要
It's Both What You Say and the Way You Say It
作为开发者,我们必须在许多层面上进行交流。我们的时间有很大部分都花在交流上,所以我们需要把它做好。
如果你不能有效地向他人传达你的了不起的想法,这些想法就毫无用处。
知道你想要说什么;了解你的听众;选择时机;选择风格;让文档美观;让听众参与;做倾听者;回复他人。
交流越有效,你就越有影响力。
11、DRY原则——不要重复你自己
DRY - Don't Repeat Yourself
系统中的每一项知识都必须具有单一、无歧义、权威的表示。与此不同的做法是在两个或更多地方表达同一事物。如果你改变其中一处,你必须记得改变其它各处。这不是你能否记住的问题,而是你何时忘记的问题。
12、让复用变得容易
Make it Easy to Reuse
你要做的是营造一种环境,在其中要找到并复用已有的东西,比自己编写更容易。如果复用很容易,人们就会去复用。而如果不复用,你们就会有重复知识的风险。
13、消除无关事物之间的影响
Eliminate Effects Between Unrelated Things
我们想要设计自足(self-contained)的组件:独立,具有单一、良好定义的目的。如果组件是相互隔离的,你就知道你能够改变其中一个,而不用担心其余组件。只要你不改变组件的外部接口,你就可以放心:你不会造成波及整个系统的问题。
你得到两个主要好处:提高生产率与降低风险。
14、不存在最终决策
There Are No Final Decisions
没有什么永远不变——而如果你严重依赖某一事实,你几乎可以确定它将会变化。与我们开发软件的速度相比,需求、用以及硬件变得更快。通过DRY原则、解耦以及元数据的使用,我们不必做出许多关键的、不可逆转的决策。有许多人会设法保持代码的灵活性,而你还需要考虑维持架、部署及供应商集成等领域的灵活性。
15、用曳光弹找到目标
Use Tracer Bullets to Find the Target
曳光弹能通过试验各种事物并检查它们离目标有多远来让你追踪目标。
曳光弹代码含有任何一段产品代码都拥有的完整的错误检查、结构、文档、以及自查。它只不过功能不全而已。但是,一旦你在系统的各组件之间实现了端到端(end-to-end)的连接,你就可以检查你离目标还有多远,并在必要的情况下进行调整。一旦你完全瞄准,增加功能将是一件容易的事情。
16、为了学习而制作原型
Prototype to Learn
任何带有风险的事物。以前没有试过的事物,或是对于最终系统极其关键的事物。任何未被证明的、试验性的、或有疑问的事物。任何让你觉得不舒服的东西。都可以通过制作原型来研究。比如:架构;已有系统中的新功能;外部数据的结构或内容;第三方工具或组件;性能问题;用户界面设计等等。
原型制作是一种学习经验,其价值并不在于所产生的代码,而在于所学到的经验教训。
17、靠近问题领域编程
Program Close to The Problem domain
计算机语言会影响你思考问题的方式,以及你看待交流的方式。用你的用户的语言进行设计和编码。
18、估算,以避免发生意外
Estimate to Avoid Surprises
在着手之前先进行估算。你将提前发现潜在的问题。
1)要选择能反映你想要传达的精确度的单位;
2)基本的估算诀窍:去问已经做过这件事情的人;
3)理解提问内容;
4)根据对问题的理解,建立粗略、就绪的思维模型骨架;
5)把模型分解为组件,找出描述这些组件怎样交互的数学规则,确定每个组件的参数;
6)给每个参数指定值,找出哪些参数对结果的影响最大,并致力于让它们大致正确;
7)进行多次计算,改变关键参数的值,然后根据那些参数表达你的答案;
8)在被要求进行估算时说的话:“我等会回答你”。
19、通过代码对进度表进行迭代
Iterate the Schedule with the Code
实行增量开发。追踪你的估算能力,提炼对迭代次数、以及在每次迭代中可以包含的内容的猜想。提炼会变得一次比一次好,对进度表的信心也将随之增长。你将给予管理部门你所能给予的最精确的进度估算。
20、用纯文本保存知识
Keep Knowledge in Plain Text
保证不过时;
杠杆作用:每一样工具,都能够在纯文本上进行操作;
更易于测试;
你需要确保所有各方能够使用公共标准进行通信。纯文本就是那个标准。
21、利用命令shell的力量
Use the Power of Command Shells
GUI环境通常受限于它们的设计者想要提供的能力。当你想要快速地组合一些命令,以完成一次查询或某种其他的任务时,命令行要更为适宜。多使用你的命令shell,你会惊讶它能使你的生产率得到怎样的提高。
22、用好一种编辑器
Use a Single Editor Well
选一种编辑器,彻底了解它,并将其用于所有的编辑任务。如果你用一种编辑器进行所有的文本编辑活动,你就不必停下来思考怎样完成文本操纵:必需的键击将成为本能反应。编辑器将成为你双手的延伸;键会在滑过文本和思想时歌唱起来。这就是我们的目标。
23、总是使用源码控制
Always Use Source Code Control
总是。即使你的团队只有你一个人,你的项目只有一周时间;确保每样东西都处在源码控制之下。
源码控制是你的工作的时间机器——你能够回到过去。
把整个项目置于源码控制系统的保护之下具有一项很大的、隐蔽的好处:你可以进行自动的和可重复的产品构建。
24、要修正问题,而不是发出指责
Fix the Problem,Not the Blame
要接受事实:调试就是解决问题,要据此发起进攻。Bug是你的过错还是别人的过错,并不是真的很有关系。它仍然是你的问题。
25、不要恐慌
Don't Panic
做一次深呼吸,思考什么可能是bug的原因。
要总是设法找出问题的根源,而不只是问题的特定表现;
搜集所有的相关数据;
开始修正bug的最佳途径是让其可再现;
使你的数据可视化;
跟踪:观察程序或数据结构虽时间变化的状态;
找到问题的原因的一种非常简单、却又特别有用的技术是向别人解释它。你只是一步步解释代码要做什么,常常就能让问题从屏幕上跳出来,宣布自己的存在。
26、“Select”没有问题
"Select" Isn't Broken
Bug 有可能存在于OS、编译器、或是第三方产品中——但这不应该是你的第一想法。有大得多的可能性的是,bug存在于正在开发的应用代码中。与假定库本身出了问题相比,假定应用代码对库的调用不正确通常更有好处。即使问题确实应归于第三方,在提交bug报告之前,你也必须先消除你的代码中的bug。
27、不要假定,要证明
Don't Assume it - Prove It
不要因为你“知道”它能工作而轻易放过与bug有牵连的例程或代码。证明它。在实际环境中——使用真正的数据和边界条件——证明你的假定。
28、学习一种文本操作语言
Learn a Text Manipulation Language
你用每天的很大一部分时间处理文本,为什么不让计算机替你完成部分工作呢?
应用示例:
数据库schema维护;
Java、C#属性(Property)访问;
测试数据生成。
29、编写能编写代码的代码
Write Code That Writes Code
代码生成器能提高你的生产率,并有助于避免重复。
30、你不可能写出完美的软件
You Can't Write Perfect Software
这刺痛了你?不应该。把它视为生活的公理,接受它,拥抱它,庆祝它。因为完美的软件不存在。在计算机简短的历史中,没有一个人曾经写出过一个完美的软件。你也不大可能成为第一个。除非你把这作为事实接受下来,否则你最终会把时间和精力浪费在追逐不可能实现的梦想上。
31、通过合约进行设计
Design with Contracts
什么是正确的程序?不多不少,做它声明要做的事情的程序。用文档记载这样的声明,并进行校验,是按合约设计(简称DBC)的核心所在。
这里,强调的重点是在“懒惰”的代码上:对在开始之前接受的东西要严格,而允诺返回的东西要尽可能少。
使用DBC的最大好处也许是它迫使需求与保证的问题走到前台来。在设计时简单地列举输入域的范围是什么、边界条件是什么、例程允诺交付什么——或者,更重要的,它不允诺交付什么——是向着编写更好的软件的一次飞跃。不对这些事项作出陈述,你就回到了靠巧合编程,那是许多项目开始、结束、失败的地方。
32、早崩溃
Crash Early
死程序不说谎。
当你的代码发现,某件被认为不可能发生的事情已经发生时,你的程序就不再有存活能力。从此时开始,它所做的任何事情都会变得可疑,所以要尽快终止它。死程序带来的危害通常比有问题的程序要小得多。
33、如果它不可能发生,用断言确保它不会发生
If It Can't Happen,Use Assertions to Ensure That It Won't
断言验证你的各种假定。在一个不确定的世界里,用断言保护你的代码。
不要用断言代替真正的错误处理。断言检查的是决不应该发生的事情。
34、将异常用于异常的问题
Use Exceptions for Exceptional Problems
异常表示即使的、非局部的控制转移——这是一种级联的(cascading)goto。异常应保留给意外事件。那些把异常用作其正常处理的一部分的程序,将遭受所有可读性和可维护性问题的折磨。这些程序破坏了封装:通过异常处理,例程和它们的调用者被更紧密地耦合在一起。
35、要有始有终
Finish What You Start
只要可能,分配某资源的例程或对象也应该负责解除其分配。
36、使模块之间的耦合减至最少
Minimize Coupling Between Modules
编写“羞怯”的代码;
函数的得墨忒耳(Demeter)法则规定,某个对象的任何方法都应该只调用属于以下情形的方法:
1)它自身;
2)传入该方法的任何参数;
3)它创建的任何对象;
4)任何直接持有的组件对象。
物理解耦。
37、要配置,不要集成
Configure,Don't Integrate
细节会弄乱我们整洁的代码——特别是如果它们经常变化。把它们赶出代码。当我们在于它作斗争时,我们可以让我们的代码变得高度可配置和“软和”——也就是,容易适应变化。
要用元数据(metadata)描述应用的配置选项:调谐参数、用户偏好(user preference)、安装目录,等等。
38、将抽象放进代码,细节放进元数据
Put Abstractions in Code,Details in Metadata
但我们不只是想把元数据用于简单的偏好。我们想要尽可能多地通过元数据配置和驱动应用。我们的目标是以声明方式思考(规定要做什么,而不是怎么做),并创建高度灵活和可适应的应用。我们通过采用一条一般准则来做到这一点:为一般情况编写程序,把具体情况放在别处——在编译的代码库之外。
也许你在编写一个具有可怕的工作流需求的系统。动作会根据复杂的(和变化的)商业规则启动和停止。考虑在某种基于规则的系统(即专家系统)中对它们进行编码,并嵌入到你的应用中。这样,你将通过编写规则、而不是修改代码来配置它。
39、分析工作流,以改善并发性
Analyze Workflow to Improve Concurrency
时间是软件架构的一个常常被忽视的方面。时间有两个方面对我们很重要:并发(事情在同一时间发生)和次序(事情在时间中的相对位置)。
我们在编写程序时,通常并没有把这两个方面放在心上。当人们最初坐下来开始设计架构,或是编写代码时,事情往往是线性的。那是大多数人的思考方式——总是先做这个,然后再做那个。但这样思考会带来时间耦合:方法A必须总是在方法B之前调用;同时只能运行一个报告;在接收到按钮点击之前,你必须等待屏幕重画。 “嘀”必须在“嗒”之前发生。
这样的方法不那么灵活,也不那么符合实际。
我们需要容许并发,并考虑解除任何时间或者次序上的依赖。
40、用服务进行设计
Design Using Services
实际上我们创建的并不是组件,而是服务——位于定义良好的、一致的接口之后的独立、并发的对象。
通过把你的系统架构成多个独立的服务,你可以让配置成为动态的。
41、总是为并发进行设计
Always Design for Concurrency
首先,必须对任何全局或静态变量加以保护,使其免于并发访问,现在也许是问问你自己、你最初为何需要全局变量的好时候。此外,不管调用的次序是什么,你都需要确保你给出的是一致的状态信息。
在被调用时,对象必须总是处在有效的状态中,而且它们可能会在最尴尬的时候被调用。你必须确保,在任何可能被调用的时刻,对象都处在有效的状态中。这一问题常常出现在构造器与初始化例程分开定义的类中(构造器没有使对象进入已初始化状态)。
一旦你设计了具有并发要素的架构,你可以灵活地处理应用的部署方式:单机、客户-服务器、或是n层。
42、使视图与模型分离
Separate Views from Models
也就是常说的MVC模式(Model-View-Controller)。
模型。表示目标对象的抽象数据模型。模型对任何视图或控制器都没有直接的了解。
视图。解释模型的方式。它订阅模型中的变化和来自控制器的逻辑事件。
控制器。控制视图、并向模型提供新数据的途径。
通过松解模型与视图/控制器之间的耦合,你用低廉的代价为自己换来了许多灵活性。
43、用黑板协调工作流
Use Blackboards to Coordinate Workflow
用黑板协调完全不同的事实和因素,同时又使各参与方保持独立和隔离。
现代的分布式类黑板(blackboard-like)系统,比如JavaSpaces和T Spaces。
44、不要靠巧合编程
Don't Program by Coincidence
总是意识到你在做什么。
不要盲目地编程。试图构建你不完全理解的应用,或是使用你不熟悉的技术,就是希望自己被巧合误导。
按照计划行事。
依靠可靠的事物。如果你无法说出各种特定情形的区别,就假定是最坏的。
为你的假定建立文档。“按合约编程”有助于澄清你头脑中的假定,并且有助于把它们传达给别人。
不要只是测试你的代码,还要测试你的假定。
为你的工作划分优先级。
不要做历史的努力。不要让已有的代码支配将来的代码。
所以下次有什么东西看起来能工作,而你却不知道为什么,要确定它不是巧合。
45、估算你的算法的阶
Estimate the Order of Your Algorithms
在你编写代码之前,先大致估算事情需要多长时间。
46、测试你的估算
Test Your Estimates
对算法的数学分析并不会告诉你每一件事情。在你的代码的目标环境中测定它的速度。
47、早重构,常重构
Refactor Early,Refactor Often
在需要时对代码进行重写、重做和重新架构。要铲除问题的根源。不要容忍破窗户。
关于重构,详见Martin Fowler的《重构》一书。
48、为测试而设计
Design to Test
在你还没有编写代码时就开始思考测试问题。测试驱动开发?
49、测试你的软件,否则你的用户就得测试
Test Your Software,or Your Users Will
测试是技术,但更是文化。一点预先的准备可以大大降低维护费用、减少客户服务电话。
50、不要使用你不理解的向导代码
Don't Use Wizard Code You Don't Understand
向导很了不起。只需要点击一个按钮,回答一些简单的问题,向导就会自动为你生成骨架代码(skeleton code)。但如果你使用向导,却不理解它制作出的所有代码,你就无法控制你自己的应用。你没有能力维护它,而且在调试时会遇到很大的困难。
51、不要搜集需求——挖掘它们
Don't Gather Requirements - Dig for Them
需求很少存在于表面上。它们深深地埋藏在层层假定、误解和政治手段的下面。
52、与用户一同工作,以像用户一样思考
Work with a User to Think Like a User
要了解系统实际上将如何被使用,这是最好的方法。开采需求的过程也是开始与用户群建立和谐的关系、了解他们对你正在构建的系统的期许和希望的时候。
53、抽象比细节活得更长久
Abstractions Live Longer than Details
“投资”于抽象,而不是实现。抽象能在来自不同的实现和新技术的变化的“攻击”之下存活下去。
54、使用项目词汇表
Use a Project Glossary
如果用户和开发者用不同的名称指称同一事物,或是更糟,用同一名称指称不同事物,这样的项目很难取得成功。
55、不要在盒子外面思考——要找到盒子
Don't Think Outside the Box - Find the Box
在遇到不可能解决的问题时,问问自己以下问题:
有更容易的方法吗?
你是在设法解决真正的问题,还是被外围的技术问题转移了注意力?
这件事情为什么是一个问题?
是什么使它如此难以解决?
它必须以这种方式完成吗?
它真的必须完成吗?
很多时候,当你设法回答这些问题时,你会有让自己吃惊的发现。很多时候,对需求的重新诠释能让整个问题全部消失。
你所需要的只是真正的约束、令人误解的约束、还有区分它们的智慧。
56、倾听反复出现的疑虑——等你准备好再开始
Listen to Nagging Doubts - Start When You're Ready
你的一生都在积累经验与智慧。当你面对一件任务时,如果你反复感觉到疑虑,或是体验到某种勉强,要注意它。你可能无法准确地指出问题所在,但给它时间,你的疑虑很可能就会结晶成某种更坚实的东西,某种你可以处理的东西。软件开发仍然不是科学。让你的直觉为你的表演做出贡献。
57、对有些事情“做”胜于“描述”
Some Things Are Better Done Than Described
你应该倾向于把需求搜集、设计、以及实现视为同一个过程——交付高质量的系统——的不同方面。不要掉进规范的螺旋,在某个时刻,你需要开始编码。
58、不要做形式方法的奴隶
Don't Be a Slave to Formal Methods
如果你没有把某项技术放进你的开发实践和能力的语境中,不要盲目地采用它。
59、昂贵的工具不一定能制作出更好的设计
Expensive Tools Do Not Produce Better Designs
小心供应商的炒作、行业教条、以及价格标签的诱惑。在考察工具的产出时,试着不要考虑它值多少钱。
60、围绕功能、而不是工作职务进行组织
Organize Around Functionality,Not Job Functions
把你的人划分成小团队,分别负责最终系统的特定方面的功能。让团队按照个人的能力,在内部自行进行组织。
但是,只有在项目拥有负责的开发者、以及强有力的项目管理时,这种途径才有效。创立一组自行其是的团队并放任自流,是一种灾难性的处方。
要记住,团队是由个体组成的。让每个成员都能以他们自己的方式闪亮。
61、不要使用手工流程
Don't Use Manual Procedures
shell脚本或批处理文件会一次次地以同一顺序执行同样的指令。我们可以自动安排备份、夜间构建、网站维护、以及其他任何可以无人照管地完成的事情。让计算机去做重复、庸常的事情——它会做得比我们更好。我们有更重要、更困难的事情要做。
62、早测试,常测试,自动测试。
Test Early.Test Often.Test Automatically.
与呆在书架上的测试计划相比,每次构建时运行的测试要有效得多。
63、要等到通过全部测试,编码才算完成
Coding Ain't Done 'Til All the Tests Run
就是这样。
64、通过“蓄意破坏”测试你的测试
Use Saboteurs to Test Your Testing
在单独的软件副本上故意引人bug,以检验测试能够抓住它们。
65、测试状态覆盖,而不是代码覆盖
Test State Coverage,Not Code Coverage
确定并测试重要的程序状态。只是测试代码行是不够的。即时具有良好的代码覆盖,你用于测试的数据仍然会有巨大的影响,而且,更为重要的是,你遍历代码的次序的影响可能是最大的。
66、一个bug只抓一次
Find Bugs Once
一旦测试员找到一个bug,这应该是测试员最后一次找到它。此后自动测试应该对其进行检查。
67、把英语当作又一种编程语言
Treat English as Just Another Programming Language
像你编写代码一样编写文档:遵守DRY原则、使用元数据、MVC、自动生成,等等。
68、把文档建在里面,不要拴在外面
Build Documentation In,Don't Bolt It On
与代码分离的文档不太可能被修正和更新。使用像JavaDoc和NDoc这样的工具,我们可以根据源码生成API级的文档。
文档和代码是同一底层模型的不同视图,但视图是唯一应该不同的东西。
69、温和地超出用户的期望
Gently Exceed Your Users' Expectations
要设法让你的用户惊讶。请注意,不是惊吓他们,而是要让他们高兴。要理解用户的期望,然后给他们的东西要多那么一点。给系统增加某种面向用户的特性所需的一点额外努力将一次又一次在商誉上带来回报。
70、在你的作品上签名
Sign Your Work
我们想要看到对所有权的自豪。“这是我编写的,我对自己的工作负责。”你的签名应该被视为质量的保证。当人们在一段代码上看到你的名字时,应该期望它是可靠的、用心编写的、测试过的和有文档的,一个真正的专业作品,由真正的专业人员编写。
一个注重实效的程序员。
1. 编程是一种技艺,一种需要用心学习的技艺,也许,只有在长久的学习之后,我们才会开始明白“hacker”的真正含义:"Someone who loves to program and enjoys being clever about it"。
前言
1. 思考你的工作
序
1. 调整你的方法,以适应当前情形与环境
2. 注重时效的程序员不仅要完成工作,而且要完成得漂亮
3. 每一个开发真都是独特的,有着个人的力量和弱点、偏好和嫌恶
4. 每一条小知识都可能会影响今后几年里的某项决策
5. 不要靠自动驾驶仪,不间断的思考,实时的批判你的工作,老IBM公司的箴言,THINK!
6. 我们采集的只是石头,却必须时刻展望未来的大教堂——采石工人的信条
第1章 注重实效的哲学
1. 能够越出直接的问题去思考,设法把问题放在更大的语境中,总是设法注意更大的图景。
2. 对所做的每件事情负责。
3. 你需要拥有广泛的知识和经验基础才能赢得一切。学习是一个持续不断的过程。
4. 在所有弱点中,最大的弱点就是害怕暴露弱点
5. 为你自己和你的行为负责这样一种观念,是注重实效的哲学的一块基石。注重实效的程序员对他或她自己的职业生涯负责,并且不害怕承认无知或错误。
6. 我们可以为我们的能力自豪,但对于我们的缺点——还有我们的无知和我们的错误——我们必须诚实。
7. 负责是你主动承担的东西。你承诺确保某件事情正确完成,但你不一定能直接控制事情的每一个方面,除了尽你所能以外,你必须分析风险是否超出了你的控制。对于不可能做到的事情或是风险太大的事情,你有权不去为之负责。你必须基于你自己的道德准则和判断来做出决定。
8. 如果确实同意要为某个结果负责,你就必须切实负起责任。当你犯错误,或是判断失误时,诚实的承认它,并设法给出各种选择,不要责备别人或别的东西,或是拼凑借口。
9. 要提供各种选择,而不是找接口。不要说事情做不到,要说明能够做什么来挽回局面。
10. 不要害怕提出要求,也不要害怕承认你需要帮助。
11. 在有些情况下,你也许确切的知道需要做什么,以及怎样去做,整个系统就在你的眼前——你知道它是对的。但请求许可去做事情,你会遇到拖延和漠然。 设计出你可以合理要求的东西,好好开发它,一旦完成,就拿给大家看,让他们大吃一惊。然后说:“要是我们增加……可能就会更好”。假装那并不重要。坐回椅子上,等着他们开始要你增加你本来就想要的功能。人们发现,参与正在发生的成功要更容易。让他们瞥见未来,你就能让他们聚集在你周围。
12. 常常是小事情的累积破坏了士气和团队。
13. 请求原谅比获取许可更容易
14. 欲求更好,常把好事变糟。
15. 时间、技术和急躁都在合谋反对我们。
16. 知道何时止步、
17. 不要因为过度修饰和过于求精而损毁完好的程序
18. 知识上的投资总能得到最好的回报
19. 你的知识和经验是你最重要的职业财富。
20. 程序员所知道的关于计算技术和他们所工作的应用领域的全部事实以及他们的所有经验视为他们的知识资产。管理知识资产与管理金融资产非常相似。
21. 经营你的资产
1. 定期投资——就像金融投资一样,你必须定期为你的知识资产投资,即使投资量很小,习惯自身也和总量一样重要。
2. 多元化——你知道的不同的事情越多,你就越有价值。未作底线,你需要知道你目前所用的特定技术的各种特性。但不要就此止步,你掌握的技术越多,你就越能更好的进行调整,赶上变化。
3. 管理风险——从高风险、可能有高回报,到低风险、低回报,技术存在于这样一条谱带上,把你所有的金钱都投入可能突然崩盘的高风险股票并不是一个好主意,你也不应太保守,错过可能的机会。不要把你所有的技术鸡蛋都放在一个篮子里。
4. 低买高卖——在新兴的技术流行之前学习它可能就和找到被低估的股票一样困难,但所得到的就和那样的股票带来的收益一样。
5. 重新评估和平衡。
22. 目标
1. 每年至少学习一种新语言——不同语言以不同方式解决相同的问题,通过学习若干不同的方法,可以帮助你拓宽你的思维,并避免墨守成规。
2. 每季度阅读一本技术书籍——一旦你养成习惯,就一个月读一本书。在你掌握了你正在使用的技术之后,拓宽范围,阅读一些与你项目无关的书籍。
3. 也要阅读非技术书籍。
4. 上课
5. 参加本地用户组织——与世隔绝对你的职业生涯来说可能是致命的,打听一下你们公司以外的人都在做什么。
6. 试验不同的环境
7. 跟上潮流——选择所涵盖的技术与你当前项目不同的刊物。
8. 上网
23. 持续投入十分重要,一旦你熟悉了某种新语言或新技术,继续前进,学习另外一种。
24. 设法把你学到的东西应用到你当前的项目中。即使你的项目没有使用该技术,你或许也能借鉴一些想法。
25. 如果你自己找不到答案,就去找能找到答案的人。不要把问题搁在那里,与他人交谈可以帮助你建立人际网络,而因为在这个过程中找到了其他不相关问题的解决方案,你也许还会让自己大吃一惊。
26. 所有阅读和研究都需要时间,而时间已经很短缺。所以你需要预先规划,让自己在空闲的片刻时间里总有东西可读。
27. 批判的思考你读到的和听到的。你需要确保你的资产中的知识是准确的,并且没有收到供应商或媒体炒作的影响。
28. 与Guru打交道的礼节与教养:
1. 确切的知道你想问什么,并尽量明确具体
2. 小心而得体的组织你的问题。记住你是在请求帮助;不要显得好像是在要求对方回答。
3. 组织好问题后,停下来,再找找答案
4. 决定你是想公开提问还是私下提问
5. 坐回椅子上,耐心等候。
29. 我相信,被打量比被忽略要好
30. 问题不只是你有什么,还要看你怎样包装它,除非你能够与他人交流,否则就算你拥有最好的主意,最漂亮的代码,或是最注重实效的想法,最终也毫无结果,没有有效的交流,一个好想法就只是一个无人关心的孤儿。
31. 只有当你是在传达信息时,你才是在交流
32. 与制作文档的过程相比,我们制作出的文档最后并没有那么重要。
33. 如果你想要大家听你说话,你必须使用一种方法,听他们说话。
34. e-mail是永久性的,要设法像对待任何书面备忘录或报告一样小心对待e-mail
第2章 注重实效的途径
1. 不要在系统各处对知识进行重复;不要把任何一项知识分散在多个系统组件中。
2. 我们都是在一个时间和资源有限世界上工作,如果你善于估计事情需要多长时间完成,你就能更好的在两者都很匮乏的情况下生存下去。
3. 系统中的每一项只是都必须具有单一、无歧义、权威的表示。
4. 糟糕的代码才需要注释
5. 不可信任的注释比完全没有注释更糟
6. 欲速则不达
7. 你所要做的是营造一种环境,在其中要找到并复用已有的东西,比自己编写更容易。如果不容易,大家就不会去复用。而如果不进行复用,你们就会有重复知识的风险。
8. 非正交系统的改变与控制更复杂是其固有的性质。当任何系统的各组件互相高度依赖时,就不再有局部修正(local fix)这样的事情。
9. 编写正交系统的好处:提高生产率与降低风险。
1. 提高生产率
1. 改动得以局部化,所以开发时间和测试时间得以降低。
2. 正交的途径还能促进复用
3. 如果你对正交的组件进行组合,生产率会有相当微妙的提高。
2. 降低风险
1. 有问题的代码区域被隔离开来
2. 所得系统更健壮
3. 正交系统很可能能得到更好的测试
4. 你不会与特定的供应商、产品、或是平台紧绑在一起
10. 从基础设施与应用分离开始,每个主要的基础设施组件有自己的子团队,如果应用功能的划分显而易见,那就照此划分,然后我们考察我们现有的(或计划有的)人员,并对分组进行相应的调整。
11. 不要依赖你无法控制的事物属性。
12. 在你引入第三方工具箱和库时,要注意保持系统的正交性,要明智的选择技术。
13. 第三方库细节与代码隔离有额外的好处:它使得你在以后更容易更换供应商。
14. 用于维持正交性的技术:
1. 让你的代码保持解耦
2. 避免使用全局数据
1. 使用Singleton要小心,它可能造成不必要的关联
3. 避免编写相似的函数
15. 要成不断的批判对待自己的代码的习惯,寻找任何重新进行组织,以改善其结构和正交性的机会,这个过程叫重构。
16. 构建但愿测试本身是对正交性的一项有趣测试
17. 如果你参加了一个项目,大家都在不顾一切的做出改动,而每一处改动似乎都会造成别的东西出错,回想一下直升机的噩梦,项目很可能没有进行正交的设计和编码,是重构的时候了。
18. 如果某个想法是你唯一的想法,再没有什么比这个更危险的事情了。
19. 如果你严重依赖某一事实,你几乎可以确定它将会变化
20. 可以把第三方产品隐藏在定义良好的抽象接口后面
21. 无论你使用的是何种机制,让它可以撤销,如果某样东西是自动添加的,它也可以被自动去掉
22. 没有人知道未来会怎样,尤其是我们!所以要让你的代码学会“摇滚”,可以“摇”就“摇”,可以“滚”就“滚”。
23. 一旦你在系统的各组件间实现了端到端的链接,你就可以检查你离目标还有多远,并在必要的情况下进行调整,一旦你完全瞄准,增加功能将是一件容易的事情。
24. 曳光弹代码方法的优点:
1. 用户能够及早看到能工作的东西
2. 开发者构建了一个他们能在其中工作的结构
3. 你有了一个集成平台
4. 你有了可用于演示的东西
5. 你将更能够感觉到工作进展
25. 原型制作是一种学习经验,其价值并不在于所产生的代码,而在于所学到的经验教训。那才是原型制作的要点所在。
26. 语言的界限就是一个人的世界的界限
27. 计算机语言会影响你思考问题的方式,以及你看待交流的方式
28. 问题领域的语言也可能会提示出编程方案
29. 应该让你的项目更靠近问题领域,通过在更高的抽象层面上编码,你获得了专心解决领域问题的*,并且可以忽略琐碎的实现细节。
30. 数据语言产生某种形式的数据结构给应用使用,这些语言常用语表示配置信息。
31. 命令语言将被实际执行,包含语句、控制结构,以及类型的东西。
32. 把高级命令语言直接嵌入你的应用程序是一种常见做法。
33. 在被要求进行估算时说:“我等会回答你”,放慢估算速度,并花一点时间仔细检查各步骤,你几乎总能得到更好的结果。
第3章 基本工具
1. 工具放大你的才干,你的工具越好,你越是能更好的掌握它们的用法,你的生产力就越高。
2. 如果你没有高超的调试技巧,你就不可能成为了不起的程序员。
3. 通过纯文本,你可以做你通过某种二进制格式所能所的每件事情。
4. 大多数二进制格式的问题在于,理解数据所必须的语境与数据本身是分离的。通过纯文本,你可以获得自描述的不依赖于创建它的应用的数据流。
5. 使用纯文本的两个主要缺点:
1. 与压缩的二进制格式相比,存贮纯文本所需空间更多
2. 要解释及处理纯文本文件,计算上的代价可能更昂贵
6. 文本的威力:
1. 保证不过时
2. 杠杆作用
3. 更易于测试
7. GUI的好处是WYSIWYG——所见即所得(what you see is what you get),缺点是WYSIAYG——所见即全部所得(what you see is all you get)。
8. 现代计算机系统仍然局限于做你告诉它的事情,而不一定是你想要它做的事情。
9. 在技术竞技场上,你应该专注于修正问题,而不是发出指责。
10. bug是你的过错还是别人的过错,并不是真的很有关系,它仍然是你的问题。
11. 调试的第一准则:不要恐慌。
12. 我们想要的不是能够通过长长的步骤再现的bug;我们要的是能够通过一条命令再现的bug。
13. bug有可能存在于OS、编译器、或是第三方产品中——但这不应该是你的第一想法。有大得多的可能性的是,bug存在于正在开发的应用代码中。
14. 当你遇到让人吃惊的bug时,除了只是修正它而外,你还需要确定先前为什么没有找出这个故障。考虑你是否需要改进单元测试或其他代码,以让它们有能力找出这个故障。
15. 确保无论发生什么,你都知道它是否会再次发生。如果修正这个bug需要很长时间,问问你自己为什么。你是否可以做点什么,让下一次修正这个bug变得更容易?
16. 如果bug是某人的错误假定的结果,与整个团队讨论这个问题,如果一个人有误解,那么许多人可能也有。
第4章 注重实效的偏执
1. 当每个人都确实要对你不利时,偏执就是一个好主意
2. 没有什么比常识和坦率更让人感到惊讶
3. 与计算机系统打交道很困难,与人打交道更困难
4. 保持坦率的最佳方案之一就是合约。合约即规定你的权利与责任,也规定对方的权利与责任。此外,还有关于任何一方没有遵守合约的后果的约定。
5. 通过早崩溃,在问题现场找到和诊断问题要容易得多。
6. 有时候别人在你自己意识到之前就能觉察到你的事情出了问题。
7. 尽早检测问题的好处之一是你可以更早崩溃。许多时候,让你的程序崩溃是你的最佳选择。
8. 死程序带来的危害通常要比有疾患的程序小得多。
9. 无论何时你发现自己在思考“但那当然不可能发生”,增加代码检查它,最容易的办法是使用断言。
10. 分配某项资源的例程或对象应该负责解除该资源的分配。
11. 当你解除顶层结构的分配时,该如何决定谁为某个聚集数据结构中的数据负责:
1. 顶层结构还负责释放它包含的任何子结构,这些结构随机递归的删除它们包含的数据
2. 只是解除顶层结构的分配,它指向的(没有在别处引用的)任何结构都会被遗弃。
3. 如果顶层结构含有任何子结构,它就拒绝解除自身的分配。
第5章 弯曲,或折断
1. 编写羞怯的代码是有益的:不向别人暴露你自己,不与太多人打交道。
2. 使若干模块紧密耦合,你可以获得重大的性能改进,只要对于那些被耦合在一起的模块而言,这是众所周知的和可以接受的,你的设计就没有问题。
3. 时间有两个方面对我们很重要:并发(事情在同一时间发生)和次序(事情在事件中的相对位置)。
4. 大多数人的思考方式——总是先做这个,然后再做那个,但这样思考会带来时间耦合,在时间上的耦合。
5. 我们创建的不是组件,而是服务——位于定义良好的,一致的接口之后的独立,并发的对象。
6. MVC:
1. 模型:表示目标对象的抽象数据模型,模型对任何视图或控制器都没有直接的了解。
2. 视图:解释模型的方式,它订阅模型中的变化和来自控制器的逻辑事件。
3. 控制器:控制视图,并向模型提供新数据的途径,它既向模型,也向视图发布事件。
7. 黑板方式的编程消除了对太多接口的需要,从而能带来更优雅、更一直的系统。
8. 黑板:数据到达的次序无关紧要;在收到某项事实时,它可以出发适当的规则,反馈也很容易处理;任何规则集的输出都可以张贴到黑板上,并出发更为适用的规则。
9. 可以用黑板协调完全不同的事实和因素,同时又使各参与方保持独立,甚至隔离。
第6章 当你编码时
1. 编码不是机械工作,程序员每一分钟都需要作出决策——如果要让所得的程序享有长久、无误和富有生产力的“一生”,就必须对这些决策进行仔细的思考和判断。
2. 不主动思考他们的代码的开发者是在靠巧合编程——代码也许能工作,但却没有特别的理由说明他们为何能工作。
3. 要让代码易于测试
4. 小心那些替你编写大量代码的工具,除非你理解他们在做什么。
5. 保持警觉能够很好的防治灾难的发生。
6. 怎样深思熟虑的编程:
1. 总是意识到你在做什么
2. 不要盲目的编程,试图构建你不完全理解的应用,或是使用你不熟悉的技术,就是希望自己被巧合误导
3. 按照计划行事
4. 依靠可靠的事物
5. 为你的假定建立文档
6. 不要只是测试你的代码,还要测试你的假定。编写断言测试你的假定,如果的你断言是对的,你就改善了代码中的文档,如果你的假定是错的,那么就为自己庆幸吧。
7. 为你的工作划分优先级,把时间花在重要的方面,很有可能,他们是最难的部分。如果基本原则或基础设施不正确,再花哨的铃声和口哨也是没有用的。
8. 不做历史的努力。不要让已有的代码吃陪将来的代码,如果不再使用,所有的代码都可被替换。不要让你已经做完的事情约束你下一步要做的事情——准备好进行重构。
7. 每个开发者都应该有设计与分析算法的才能。
8. 随着程序的演化,我们有必要重新思考早先的决策,并重写部分代码。这一过程非常自然,代码需要演化,它不是静态的事物。
9. 时间压力常常被用作不进行重构的借口,但这个借口并不成立。
10. 追踪需要重构的事物。如果你不能立刻重构某样东西,就一定要把它列入计划。确保收到影响的代码的使用者知道该代码计划要重构,以及这可能会怎样影响他们。
11. 重构是一项需要慎重,深思熟虑,小心进行的活动。
1. 不要试图在重构的同时增加功能
2. 在开始重构前,确保你拥有良好的测试。尽可能经常运行这些测试。这样,如果你的改动破坏了任何东西,你就能很快知道。
3. 采用短小、深思熟虑的步骤。如果你使你的步骤保持短小,并在每个步骤之后进行测试,你将能够避免长时间的调试。
12. 你看到不怎么合理的代码是,既要修正它,也要修正依赖于它的每样东西。要管理痛苦:如果它现在有损害,但以后的损害会更大。你也许最好一劳永逸的修正它,记住软件熵中的教训:不要容忍破窗户。
13. 不管是那种方法,要记住:如果你不容易找到它,你就不会使用它。
14. 如果代码曾经出现过问题,它很可能还会再出问题。
15. 你编写的所有软件都将进行测试——如果不是由你和你们团队测试,那就要由最终用于测试——所以你最好计划好对其进行彻底的测试。
16. 测试是技术,但更是文化。
17. 毋庸否认,应用的编写正变得越来越难。特别是用户界面,正在日益变得复杂。
第7章 在项目开始之前
1. 项目启动太快是一个问题,但等得太久可能会更糟。
2. 完美,不是在没有什么需要增加,而是在没有什么需要去掉时达到的。
3. 需求很少存在于表面上。通常,他们深深地埋藏在层层假定、误解和政治手段的下面。
4. 找出用户为何要做特定事情的原因,而不是他们目前做这件事情的方式,这很重要。开发必须解决用户的商业问题,而不只是满足他们陈述的需求。
5. 尽管奴隶般重复已经存在的事物会阻碍进步,但我们必须要提供通往未来的过渡。
6. 成功的工具会适应使用它们的双手。
7. 不要做任何表示方法的奴隶。
8. 需求不是架构,需求不是设计,也不是用户界面,需求是需要。
9. 许多项目的失败都归咎于项目范围的增大——也称为特性膨胀、蔓延特性论、或是需求蔓延。
10. 管理需求增长的关键是向项目出资人指出每项新特性对项目进度的影响。
11. 有些约束是绝对的,有些则是先入之见。绝对的约束必须受到尊重,不管它们看上去有多讨厌或多愚蠢;另一方面,有些外表上的约束也许根本不是真正的约束。
12. 解开谜题的关键在于确定加给你的各种约束,并确定你确实拥有的*度。
13. 在面对棘手的问题时,列出所有在你面前的可能途径。不要排除任何东西,不管它听起来有多无用或愚蠢。现在,逐一检查列表中的每一项,并解释为何不能采用某个特定的途径。你确定吗?你能否证明?
14. 软件开发仍然不是科学,让你的直觉为你的表演做出贡献。
15. 没有给编码者留下任何解释余地的设计剥夺了他们发挥技巧和艺术才能的权利。
16. 你越是把规范当做安乐毯,不让开发者进入可怕的编码世界,进入编码阶段就越困难。
17. 盲目的采用任何技术,而不把它放进你的开发实践和能力的语境中,这样的处方肯定会让你失望。
18. 决不要低估采用新工具和新方法的代价,要做好准备,把使用这些技术的第一个项目当做一种学习经验。
19. 要记住谁是主人,不要变成方法学的奴隶。
第8章 注重实效的项目
1. 质量是一个团队问题,不应该容忍破窗户。
2. 质量只可能源于全体团队成员都作出自己的贡献。
3. 团队无需拒绝无法控制的变化——你只需注意到它们正在发生。
4. 看上去沉闷寡言的项目是最糟糕的团队。
5. 有一个简单的营销诀窍,能帮助团队作为整体与外界交流:创立品牌。
6. 把你的人划分成小团队,分别负责最终系统的特定方面的功能。让各团队按照各人的能力,在内部自行进行组织。按照他们约定的承诺,对项目中的其他团队负有责任。
7. 创立一组自行其是的团队并放任自流,是一种灾难性的处方。项目至少需要两个“头”——一个主管技术,另一个主管行政。
1. 技术主管设定开发哲学和风格,给各团队指派责任,并仲裁成员之间不可避免的“讨论”,技术主管还要不断关注大图景,设法找出团队之间任何不必要的,可能降低总体正交性的交叉。
2. 行政主管(或项目经理)调度各团队所需的各种资源,见识并报告紧张情况,并根据商业需要帮助确定各种优先级,在与外界交流时,行政主管还要充当团队的大使。
8. 确保一致和准确的一种很好的方式是使团队所做的每件事情自动化。
9. 团队是由个体组成的,让每个成员都能以他们自己的方式闪亮,给他们足够的控件,以支持他们,并确保项目的交付能够符合需求。
10. 文明通过增加我们不加思索就能完成的重要操作的数目而取得进步。
11. 人工流程不能保证一致性,也无法保证可重复性,特别是在不同的人对流程的各个方面有不同解释时。
12. 让计算机去做重复,庸常的事情——它会做得比我们更好,我们有更重要,更困难的事情要做。
13. 用户高速了你他们需要什么,但那是他们需要的吗?
14. 注释应该讨论为何要做某事,它的目的和目标,代码已经说明了它是怎样完成。
15. 匈牙利表示法在OO系统是绝对不合适的。
16. 对待文档要像对待代码一样用新,用户(还有后来的维护者)会为你唱赞歌的。
17. 在现实中,项目的成功是由它在多大程度上满足了用户的期望来衡量的。不符合用于期望的项目注定是失败的,不管交付的产品在绝对的意义上有多好。
18. 决不要忘了你的应用要解决的商业问题。
19. 给他们的东西要比他们期望的多一点。给系统增加某种面向用户的特性所需的一点额外努力将一次又一次在商誉上带来回报。
20. 为用户的机构定制splash屏幕。
1、关心你的技艺
Care About Your Craft
除非你在乎能否漂亮地开发出软件,否则其它事情都是没有意义的。
2、思考!你的工作
Think!About Your Work
在你做某件事情的时候思考你在做什么。不间断地思考,实时地批判你的工作。这将占据你的一些宝贵时间,酬劳则是更为活跃地参与你喜爱的工作、感觉到自己在掌握范围日增的各种主题以及因感受到持续的进步而欢愉。从长远来说,你在时间上的投入将会随着你和你的团队变得更为高效、编写出更易于维护的代码以及开会时间的减少而得到回报。
3、提供各种选择,不要找蹩脚的借口
Provide Options,Don't Make Lame Excuses
不要说事情做不到;要说明能够做什么来挽回局面。不要害怕提出要求,也不要害怕承认你需要帮助。
4、不要容忍破窗户
Don't Live With Broken Windows
不要留着“破窗户”(低劣的设计、错误的决策、或者糟糕的代码)不修。发现一个就修一个。如果没有足够的时间进行适当的修理,采取某种行动防止进一步的破坏,并说明情势处在你的控制之下。
如果你发现你所在团队和项目的代码十分漂亮——编写整洁、设计良好,并且很优雅,你不会想成为第一个弄脏东西的人。
5、做变化的催化剂
Be a Catalyst for Change
你不能强迫人们改变。相反,要向他们展示未来可能会怎样,并帮助他们参与对未来的创造。
设计出你可以合理要求的东西,好好开发它。一旦完成,就拿给大家看,让他们大吃一惊。然后说:“要是我们增加...可能就会更好。”假装那并不重要。坐回椅子上,等着他们开始要你增加你本来就想要的功能。人们发现,参与正在发生的成功要更容易。让他们瞥见未来,你就能让他们聚集在你周围。
6、记住大图景
Remember the Big Picture
如果你抓一只青蛙放进沸水里,它会一下子跳出来。但是,如果你把青蛙放进冷水里,然后慢慢加热,青蛙不会注意到温度的缓慢变化,会呆在锅里,直到被煮熟。
不要像青蛙一样。留心大图景。要持续不断地观察周围发生的事情,而不只是你自己在做的事情。
7、使质量成为需求问题
Make Quality a Requirements Issue
你所制作的系统的范围和质量应该作为系统需求的一部分规定下来。让你的用户参与权衡,知道何时止步,提供足够好的软件。
8、定期为你的知识资产投资
Invest Regularly in Your Knowledge Portfolio
让学习成为习惯。
持续投入十分重要。一旦你熟悉了某种新语言或新技术,继续前进,学习另一种。
是否在某个项目中使用这些技术,或者是否把它们放入你的简历,这并不重要。学习的过程将扩展你的思维,使你向着新的可能性和新的做事方式拓展。思维的“异花授粉”十分重要;设法把你学到的东西应用到你当前的项目中。即使你的项目没有使用该技术,你或许也能借鉴一些想法。例如,熟悉了面向对象,你就会用不同的方式编写纯C程序。
如果你自己找不到答案,就去找出能找到答案的人。不要把问题搁在那里。
9、批判地分析你读到的和听到的
Critically Analyze What You Read and Hear
不要被供应商、媒体炒作、或教条左右。要依照你自己的看法和你的项目的情况去对信息进行分析。
10、你说什么和你怎么说同样重要
It's Both What You Say and the Way You Say It
作为开发者,我们必须在许多层面上进行交流。我们的时间有很大部分都花在交流上,所以我们需要把它做好。
如果你不能有效地向他人传达你的了不起的想法,这些想法就毫无用处。
知道你想要说什么;了解你的听众;选择时机;选择风格;让文档美观;让听众参与;做倾听者;回复他人。
交流越有效,你就越有影响力。
11、DRY原则——不要重复你自己
DRY - Don't Repeat Yourself
系统中的每一项知识都必须具有单一、无歧义、权威的表示。与此不同的做法是在两个或更多地方表达同一事物。如果你改变其中一处,你必须记得改变其它各处。这不是你能否记住的问题,而是你何时忘记的问题。
12、让复用变得容易
Make it Easy to Reuse
你要做的是营造一种环境,在其中要找到并复用已有的东西,比自己编写更容易。如果复用很容易,人们就会去复用。而如果不复用,你们就会有重复知识的风险。
13、消除无关事物之间的影响
Eliminate Effects Between Unrelated Things
我们想要设计自足(self-contained)的组件:独立,具有单一、良好定义的目的。如果组件是相互隔离的,你就知道你能够改变其中一个,而不用担心其余组件。只要你不改变组件的外部接口,你就可以放心:你不会造成波及整个系统的问题。
你得到两个主要好处:提高生产率与降低风险。
14、不存在最终决策
There Are No Final Decisions
没有什么永远不变——而如果你严重依赖某一事实,你几乎可以确定它将会变化。与我们开发软件的速度相比,需求、用以及硬件变得更快。通过DRY原则、解耦以及元数据的使用,我们不必做出许多关键的、不可逆转的决策。有许多人会设法保持代码的灵活性,而你还需要考虑维持架、部署及供应商集成等领域的灵活性。
15、用曳光弹找到目标
Use Tracer Bullets to Find the Target
曳光弹能通过试验各种事物并检查它们离目标有多远来让你追踪目标。
曳光弹代码含有任何一段产品代码都拥有的完整的错误检查、结构、文档、以及自查。它只不过功能不全而已。但是,一旦你在系统的各组件之间实现了端到端(end-to-end)的连接,你就可以检查你离目标还有多远,并在必要的情况下进行调整。一旦你完全瞄准,增加功能将是一件容易的事情。
16、为了学习而制作原型
Prototype to Learn
任何带有风险的事物。以前没有试过的事物,或是对于最终系统极其关键的事物。任何未被证明的、试验性的、或有疑问的事物。任何让你觉得不舒服的东西。都可以通过制作原型来研究。比如:架构;已有系统中的新功能;外部数据的结构或内容;第三方工具或组件;性能问题;用户界面设计等等。
原型制作是一种学习经验,其价值并不在于所产生的代码,而在于所学到的经验教训。
17、靠近问题领域编程
Program Close to The Problem domain
计算机语言会影响你思考问题的方式,以及你看待交流的方式。用你的用户的语言进行设计和编码。
18、估算,以避免发生意外
Estimate to Avoid Surprises
在着手之前先进行估算。你将提前发现潜在的问题。
1)要选择能反映你想要传达的精确度的单位;
2)基本的估算诀窍:去问已经做过这件事情的人;
3)理解提问内容;
4)根据对问题的理解,建立粗略、就绪的思维模型骨架;
5)把模型分解为组件,找出描述这些组件怎样交互的数学规则,确定每个组件的参数;
6)给每个参数指定值,找出哪些参数对结果的影响最大,并致力于让它们大致正确;
7)进行多次计算,改变关键参数的值,然后根据那些参数表达你的答案;
8)在被要求进行估算时说的话:“我等会回答你”。
19、通过代码对进度表进行迭代
Iterate the Schedule with the Code
实行增量开发。追踪你的估算能力,提炼对迭代次数、以及在每次迭代中可以包含的内容的猜想。提炼会变得一次比一次好,对进度表的信心也将随之增长。你将给予管理部门你所能给予的最精确的进度估算。
20、用纯文本保存知识
Keep Knowledge in Plain Text
保证不过时;
杠杆作用:每一样工具,都能够在纯文本上进行操作;
更易于测试;
你需要确保所有各方能够使用公共标准进行通信。纯文本就是那个标准。
21、利用命令shell的力量
Use the Power of Command Shells
GUI环境通常受限于它们的设计者想要提供的能力。当你想要快速地组合一些命令,以完成一次查询或某种其他的任务时,命令行要更为适宜。多使用你的命令shell,你会惊讶它能使你的生产率得到怎样的提高。
22、用好一种编辑器
Use a Single Editor Well
选一种编辑器,彻底了解它,并将其用于所有的编辑任务。如果你用一种编辑器进行所有的文本编辑活动,你就不必停下来思考怎样完成文本操纵:必需的键击将成为本能反应。编辑器将成为你双手的延伸;键会在滑过文本和思想时歌唱起来。这就是我们的目标。
23、总是使用源码控制
Always Use Source Code Control
总是。即使你的团队只有你一个人,你的项目只有一周时间;确保每样东西都处在源码控制之下。
源码控制是你的工作的时间机器——你能够回到过去。
把整个项目置于源码控制系统的保护之下具有一项很大的、隐蔽的好处:你可以进行自动的和可重复的产品构建。
24、要修正问题,而不是发出指责
Fix the Problem,Not the Blame
要接受事实:调试就是解决问题,要据此发起进攻。Bug是你的过错还是别人的过错,并不是真的很有关系。它仍然是你的问题。
25、不要恐慌
Don't Panic
做一次深呼吸,思考什么可能是bug的原因。
要总是设法找出问题的根源,而不只是问题的特定表现;
搜集所有的相关数据;
开始修正bug的最佳途径是让其可再现;
使你的数据可视化;
跟踪:观察程序或数据结构虽时间变化的状态;
找到问题的原因的一种非常简单、却又特别有用的技术是向别人解释它。你只是一步步解释代码要做什么,常常就能让问题从屏幕上跳出来,宣布自己的存在。
26、“Select”没有问题
"Select" Isn't Broken
Bug 有可能存在于OS、编译器、或是第三方产品中——但这不应该是你的第一想法。有大得多的可能性的是,bug存在于正在开发的应用代码中。与假定库本身出了问题相比,假定应用代码对库的调用不正确通常更有好处。即使问题确实应归于第三方,在提交bug报告之前,你也必须先消除你的代码中的bug。
27、不要假定,要证明
Don't Assume it - Prove It
不要因为你“知道”它能工作而轻易放过与bug有牵连的例程或代码。证明它。在实际环境中——使用真正的数据和边界条件——证明你的假定。
28、学习一种文本操作语言
Learn a Text Manipulation Language
你用每天的很大一部分时间处理文本,为什么不让计算机替你完成部分工作呢?
应用示例:
数据库schema维护;
Java、C#属性(Property)访问;
测试数据生成。
29、编写能编写代码的代码
Write Code That Writes Code
代码生成器能提高你的生产率,并有助于避免重复。
30、你不可能写出完美的软件
You Can't Write Perfect Software
这刺痛了你?不应该。把它视为生活的公理,接受它,拥抱它,庆祝它。因为完美的软件不存在。在计算机简短的历史中,没有一个人曾经写出过一个完美的软件。你也不大可能成为第一个。除非你把这作为事实接受下来,否则你最终会把时间和精力浪费在追逐不可能实现的梦想上。
31、通过合约进行设计
Design with Contracts
什么是正确的程序?不多不少,做它声明要做的事情的程序。用文档记载这样的声明,并进行校验,是按合约设计(简称DBC)的核心所在。
这里,强调的重点是在“懒惰”的代码上:对在开始之前接受的东西要严格,而允诺返回的东西要尽可能少。
使用DBC的最大好处也许是它迫使需求与保证的问题走到前台来。在设计时简单地列举输入域的范围是什么、边界条件是什么、例程允诺交付什么——或者,更重要的,它不允诺交付什么——是向着编写更好的软件的一次飞跃。不对这些事项作出陈述,你就回到了靠巧合编程,那是许多项目开始、结束、失败的地方。
32、早崩溃
Crash Early
死程序不说谎。
当你的代码发现,某件被认为不可能发生的事情已经发生时,你的程序就不再有存活能力。从此时开始,它所做的任何事情都会变得可疑,所以要尽快终止它。死程序带来的危害通常比有问题的程序要小得多。
33、如果它不可能发生,用断言确保它不会发生
If It Can't Happen,Use Assertions to Ensure That It Won't
断言验证你的各种假定。在一个不确定的世界里,用断言保护你的代码。
不要用断言代替真正的错误处理。断言检查的是决不应该发生的事情。
34、将异常用于异常的问题
Use Exceptions for Exceptional Problems
异常表示即使的、非局部的控制转移——这是一种级联的(cascading)goto。异常应保留给意外事件。那些把异常用作其正常处理的一部分的程序,将遭受所有可读性和可维护性问题的折磨。这些程序破坏了封装:通过异常处理,例程和它们的调用者被更紧密地耦合在一起。
35、要有始有终
Finish What You Start
只要可能,分配某资源的例程或对象也应该负责解除其分配。
36、使模块之间的耦合减至最少
Minimize Coupling Between Modules
编写“羞怯”的代码;
函数的得墨忒耳(Demeter)法则规定,某个对象的任何方法都应该只调用属于以下情形的方法:
1)它自身;
2)传入该方法的任何参数;
3)它创建的任何对象;
4)任何直接持有的组件对象。
物理解耦。
37、要配置,不要集成
Configure,Don't Integrate
细节会弄乱我们整洁的代码——特别是如果它们经常变化。把它们赶出代码。当我们在于它作斗争时,我们可以让我们的代码变得高度可配置和“软和”——也就是,容易适应变化。
要用元数据(metadata)描述应用的配置选项:调谐参数、用户偏好(user preference)、安装目录,等等。
38、将抽象放进代码,细节放进元数据
Put Abstractions in Code,Details in Metadata
但我们不只是想把元数据用于简单的偏好。我们想要尽可能多地通过元数据配置和驱动应用。我们的目标是以声明方式思考(规定要做什么,而不是怎么做),并创建高度灵活和可适应的应用。我们通过采用一条一般准则来做到这一点:为一般情况编写程序,把具体情况放在别处——在编译的代码库之外。
也许你在编写一个具有可怕的工作流需求的系统。动作会根据复杂的(和变化的)商业规则启动和停止。考虑在某种基于规则的系统(即专家系统)中对它们进行编码,并嵌入到你的应用中。这样,你将通过编写规则、而不是修改代码来配置它。
39、分析工作流,以改善并发性
Analyze Workflow to Improve Concurrency
时间是软件架构的一个常常被忽视的方面。时间有两个方面对我们很重要:并发(事情在同一时间发生)和次序(事情在时间中的相对位置)。
我们在编写程序时,通常并没有把这两个方面放在心上。当人们最初坐下来开始设计架构,或是编写代码时,事情往往是线性的。那是大多数人的思考方式——总是先做这个,然后再做那个。但这样思考会带来时间耦合:方法A必须总是在方法B之前调用;同时只能运行一个报告;在接收到按钮点击之前,你必须等待屏幕重画。 “嘀”必须在“嗒”之前发生。
这样的方法不那么灵活,也不那么符合实际。
我们需要容许并发,并考虑解除任何时间或者次序上的依赖。
40、用服务进行设计
Design Using Services
实际上我们创建的并不是组件,而是服务——位于定义良好的、一致的接口之后的独立、并发的对象。
通过把你的系统架构成多个独立的服务,你可以让配置成为动态的。
41、总是为并发进行设计
Always Design for Concurrency
首先,必须对任何全局或静态变量加以保护,使其免于并发访问,现在也许是问问你自己、你最初为何需要全局变量的好时候。此外,不管调用的次序是什么,你都需要确保你给出的是一致的状态信息。
在被调用时,对象必须总是处在有效的状态中,而且它们可能会在最尴尬的时候被调用。你必须确保,在任何可能被调用的时刻,对象都处在有效的状态中。这一问题常常出现在构造器与初始化例程分开定义的类中(构造器没有使对象进入已初始化状态)。
一旦你设计了具有并发要素的架构,你可以灵活地处理应用的部署方式:单机、客户-服务器、或是n层。
42、使视图与模型分离
Separate Views from Models
也就是常说的MVC模式(Model-View-Controller)。
模型。表示目标对象的抽象数据模型。模型对任何视图或控制器都没有直接的了解。
视图。解释模型的方式。它订阅模型中的变化和来自控制器的逻辑事件。
控制器。控制视图、并向模型提供新数据的途径。
通过松解模型与视图/控制器之间的耦合,你用低廉的代价为自己换来了许多灵活性。
43、用黑板协调工作流
Use Blackboards to Coordinate Workflow
用黑板协调完全不同的事实和因素,同时又使各参与方保持独立和隔离。
现代的分布式类黑板(blackboard-like)系统,比如JavaSpaces和T Spaces。
44、不要靠巧合编程
Don't Program by Coincidence
总是意识到你在做什么。
不要盲目地编程。试图构建你不完全理解的应用,或是使用你不熟悉的技术,就是希望自己被巧合误导。
按照计划行事。
依靠可靠的事物。如果你无法说出各种特定情形的区别,就假定是最坏的。
为你的假定建立文档。“按合约编程”有助于澄清你头脑中的假定,并且有助于把它们传达给别人。
不要只是测试你的代码,还要测试你的假定。
为你的工作划分优先级。
不要做历史的努力。不要让已有的代码支配将来的代码。
所以下次有什么东西看起来能工作,而你却不知道为什么,要确定它不是巧合。
45、估算你的算法的阶
Estimate the Order of Your Algorithms
在你编写代码之前,先大致估算事情需要多长时间。
46、测试你的估算
Test Your Estimates
对算法的数学分析并不会告诉你每一件事情。在你的代码的目标环境中测定它的速度。
47、早重构,常重构
Refactor Early,Refactor Often
在需要时对代码进行重写、重做和重新架构。要铲除问题的根源。不要容忍破窗户。
关于重构,详见Martin Fowler的《重构》一书。
48、为测试而设计
Design to Test
在你还没有编写代码时就开始思考测试问题。测试驱动开发?
49、测试你的软件,否则你的用户就得测试
Test Your Software,or Your Users Will
测试是技术,但更是文化。一点预先的准备可以大大降低维护费用、减少客户服务电话。
50、不要使用你不理解的向导代码
Don't Use Wizard Code You Don't Understand
向导很了不起。只需要点击一个按钮,回答一些简单的问题,向导就会自动为你生成骨架代码(skeleton code)。但如果你使用向导,却不理解它制作出的所有代码,你就无法控制你自己的应用。你没有能力维护它,而且在调试时会遇到很大的困难。
51、不要搜集需求——挖掘它们
Don't Gather Requirements - Dig for Them
需求很少存在于表面上。它们深深地埋藏在层层假定、误解和政治手段的下面。
52、与用户一同工作,以像用户一样思考
Work with a User to Think Like a User
要了解系统实际上将如何被使用,这是最好的方法。开采需求的过程也是开始与用户群建立和谐的关系、了解他们对你正在构建的系统的期许和希望的时候。
53、抽象比细节活得更长久
Abstractions Live Longer than Details
“投资”于抽象,而不是实现。抽象能在来自不同的实现和新技术的变化的“攻击”之下存活下去。
54、使用项目词汇表
Use a Project Glossary
如果用户和开发者用不同的名称指称同一事物,或是更糟,用同一名称指称不同事物,这样的项目很难取得成功。
55、不要在盒子外面思考——要找到盒子
Don't Think Outside the Box - Find the Box
在遇到不可能解决的问题时,问问自己以下问题:
有更容易的方法吗?
你是在设法解决真正的问题,还是被外围的技术问题转移了注意力?
这件事情为什么是一个问题?
是什么使它如此难以解决?
它必须以这种方式完成吗?
它真的必须完成吗?
很多时候,当你设法回答这些问题时,你会有让自己吃惊的发现。很多时候,对需求的重新诠释能让整个问题全部消失。
你所需要的只是真正的约束、令人误解的约束、还有区分它们的智慧。
56、倾听反复出现的疑虑——等你准备好再开始
Listen to Nagging Doubts - Start When You're Ready
你的一生都在积累经验与智慧。当你面对一件任务时,如果你反复感觉到疑虑,或是体验到某种勉强,要注意它。你可能无法准确地指出问题所在,但给它时间,你的疑虑很可能就会结晶成某种更坚实的东西,某种你可以处理的东西。软件开发仍然不是科学。让你的直觉为你的表演做出贡献。
57、对有些事情“做”胜于“描述”
Some Things Are Better Done Than Described
你应该倾向于把需求搜集、设计、以及实现视为同一个过程——交付高质量的系统——的不同方面。不要掉进规范的螺旋,在某个时刻,你需要开始编码。
58、不要做形式方法的奴隶
Don't Be a Slave to Formal Methods
如果你没有把某项技术放进你的开发实践和能力的语境中,不要盲目地采用它。
59、昂贵的工具不一定能制作出更好的设计
Expensive Tools Do Not Produce Better Designs
小心供应商的炒作、行业教条、以及价格标签的诱惑。在考察工具的产出时,试着不要考虑它值多少钱。
60、围绕功能、而不是工作职务进行组织
Organize Around Functionality,Not Job Functions
把你的人划分成小团队,分别负责最终系统的特定方面的功能。让团队按照个人的能力,在内部自行进行组织。
但是,只有在项目拥有负责的开发者、以及强有力的项目管理时,这种途径才有效。创立一组自行其是的团队并放任自流,是一种灾难性的处方。
要记住,团队是由个体组成的。让每个成员都能以他们自己的方式闪亮。
61、不要使用手工流程
Don't Use Manual Procedures
shell脚本或批处理文件会一次次地以同一顺序执行同样的指令。我们可以自动安排备份、夜间构建、网站维护、以及其他任何可以无人照管地完成的事情。让计算机去做重复、庸常的事情——它会做得比我们更好。我们有更重要、更困难的事情要做。
62、早测试,常测试,自动测试。
Test Early.Test Often.Test Automatically.
与呆在书架上的测试计划相比,每次构建时运行的测试要有效得多。
63、要等到通过全部测试,编码才算完成
Coding Ain't Done 'Til All the Tests Run
就是这样。
64、通过“蓄意破坏”测试你的测试
Use Saboteurs to Test Your Testing
在单独的软件副本上故意引人bug,以检验测试能够抓住它们。
65、测试状态覆盖,而不是代码覆盖
Test State Coverage,Not Code Coverage
确定并测试重要的程序状态。只是测试代码行是不够的。即时具有良好的代码覆盖,你用于测试的数据仍然会有巨大的影响,而且,更为重要的是,你遍历代码的次序的影响可能是最大的。
66、一个bug只抓一次
Find Bugs Once
一旦测试员找到一个bug,这应该是测试员最后一次找到它。此后自动测试应该对其进行检查。
67、把英语当作又一种编程语言
Treat English as Just Another Programming Language
像你编写代码一样编写文档:遵守DRY原则、使用元数据、MVC、自动生成,等等。
68、把文档建在里面,不要拴在外面
Build Documentation In,Don't Bolt It On
与代码分离的文档不太可能被修正和更新。使用像JavaDoc和NDoc这样的工具,我们可以根据源码生成API级的文档。
文档和代码是同一底层模型的不同视图,但视图是唯一应该不同的东西。
69、温和地超出用户的期望
Gently Exceed Your Users' Expectations
要设法让你的用户惊讶。请注意,不是惊吓他们,而是要让他们高兴。要理解用户的期望,然后给他们的东西要多那么一点。给系统增加某种面向用户的特性所需的一点额外努力将一次又一次在商誉上带来回报。
70、在你的作品上签名
Sign Your Work
我们想要看到对所有权的自豪。“这是我编写的,我对自己的工作负责。”你的签名应该被视为质量的保证。当人们在一段代码上看到你的名字时,应该期望它是可靠的、用心编写的、测试过的和有文档的,一个真正的专业作品,由真正的专业人员编写。
一个注重实效的程序员。