注重实效的程序员参考指南
本文为读《程序员修炼之道——从小工到专家》的部分总结与摘要。
关心你的技艺
即关心自己的技术,考虑自己如何写更好的代码,考虑如何做一个完美的项目。
思考!你的工作
不断地批评和评估自己的工作。
提供各种选项,不要找蹩脚的借口
提供各种选项,而不是找借口。不要说事情做不到,应该说能够做什么。解决问题而并非推卸责任。
不要容忍破窗户
当你看到糟糕的设计、错误的决策和糟糕的代码时,修正它们。置之不理会加速腐烂的进程。对自己的设计与代码严格要求,才能延续得更久。
当然,第一目的是实现目标,更好的设计与决策都是为了更好的支持目标。
做变化的催化剂
不能强迫人们做改变,但可以展示改变后的效果,以及帮助参与对未来的创造。
记住大图景
时刻需要记住目前的目的是什么,需要做什么,以及变化了什么,不能太沉浸于细节中。
常常小事的积累会破坏士气和团队,学会让事情变得更美好。
需要时刻提醒自己,目前做的事情是否偏离最终目的。当自己沉入太多时,则要停下来考虑自己是否偏离了方向。
使质量成为需求问题
质量也是一个需求,在需要质量时,则需要增加自测&系统测试时间,在排期时也需要对质量留下保证时间。
定期为你的知识资产投资
和第一点「关心你的技艺」相关。让学习成为习惯,并非要求都与工作相关。保持学习,保持阅读,保持总结与写作。
批判地分析你读到的和听到的
不要被供应商、媒体炒作、或教条左右。要依照自己的看法和项目的情况区对信息进行分析。
重点还是明确清楚自己想要什么(最终目的)。
说什么和怎么说同样重要
如果不能有效地向他人传达你的了不起的想法,这些项目就毫无用处。
规划你想说的东西,写出大纲。同时,了解听众(了解别人需要什么,同时明白自己需要表达什么)。
- 你想让他们学到什么?
- 他们对你讲的什么感兴趣?
- 他们有多富有经验?
- 他们想要多少细节?
- 你想让谁拥有这些信息?
- 你如何促使他们听你说话?
了解别人的交流风格。
学会听别人说话,即使你掌握着全部信息。不能及时回复与处理时,都需要反馈。更多交流方案参照《工作沟通技巧笔记》
不要重复自己
系统中的每一项知识都必须具有单一、无歧义、权威的表示。
- 强加的重复
- 无意的重复
- 无耐性的重复
- 开发者之间的重复
哪怕是最终破坏DRY原则,也不能暴露给外界。
让复用变得容易
如果复用很容易,人们就会去复用,创造一个支持复用的环境。
复用时需要考虑:复用成本 VS 重做成本
清除无关事物之间的影响
设计自足、独立、并具有单一、良好定义的目的的组件。
解耦,提高模块之间的正交性。
- 改动得以局部化
- 正交得途径还能够促进复用
- 使用正交的组件进行组合,可以提高生产率
编码中需要注意:
- 使代码保持解耦。使得不用关心内部原理就可以使用
- 避免使用全局数据
- 避免编写相似的函数
不存在最终决策
没有决策是浇铸在石头上的。相反,要把每项决策都视为是写在沙滩上的,并为变化做好计划。
需要考虑维持交个、部署及供应商集成等领域的灵活性。把第三方产品隐藏在定义良好的抽象接口后面。
用曳光弹找到目标
曳光弹能通过实验各种事物并检查它们离目标有多远来让你追踪目标。
原型制作生成用过就扔的代码。曳光弹虽然简约,但却是完整的,并且构成了最终系统的骨架的一部分。
为了学习而制作原型
原型制作是一种学习经验。其价值并在于所产生的代码,而在于所学到的经验教训。
原型应该遮盖细节,并聚焦于所考虑系统的某些具体方面。
靠近问题领域编程
用你的用户的语言进行设计和编码。选择更靠近问题解决方便的语言。
估算,以避免发生意外
在着手之前先进行估算,将提前发现潜在的问题。需求分析与方案确定后才能估算时间。
并且学会理解估算对应的精度,进而明确出精确到哪个度量程度。
对于一个项目,模型可以是你的组织在开发过程中所用的步骤、以及系统的实现方式的非常粗略的图景。
每次项目完成后,需要终结每次估算不准的原因。
通过代码对进度表进行迭代
用在进行实现时获得的经验提炼项目的时间标度。
用存文本保存知识
从简单方便的东西记录知识。
利用命令shell的力量
当图形用户界面无能为力时使用shell。
用好一种编辑器
编辑器应该是你的手的延伸;确保你的编辑器都是可配置、可扩展和可编程。
总是使用源码控制
源码控制是你的工作的时间机器——你能够回到过去。
要修正问题,而不是发出指责
bug是你的过错还是别人的过错,并不是真的很有关系——它们仍然是你的问题,它仍然需要修正。
不要恐慌
做一次深呼吸,思考什么可能是bug的原因。慌也解决不了问题。
“Select” 没有问题
在OS或编译器、甚至是第三方产品或库中很少发现bug。bug很可能在应用中。多相信系统控件。
不要假定,要证明
在实际环境中——使用真正的数据和边界条件——证明你的假定。
学习一种文本操作语言
编写能编写代码的代码
代码生成器能提高你的生产率,并有助于避免重复。
不可能写出完美的软件
软件不可能是完美的。保护你的代码和用户,使他们免于能够预见的错误。注重实效的程序员针对自己的错误进行防卫性的编码。
通过合约进行设计
使用合约建立文档,并检验代码所做的事情正好是它声明要做的。(输入什么,做了什么,输出了什么)。
参数的改变必须是显示的,并不能隐藏。
对在开始之前接受的东西要严格,而允诺返回的东西要尽可能少。
早崩溃
在错误发生时,第一时间中止。
函数需要考虑异常输入并给出提示。(底层方法异常输入应该之间中止流程)。
出错时需要偏向消费者。
用断言避免不可能发生的事情
断言验证你的各种假定。在一个不确定的世界里,用断言保护你的代码。保留某些异常检测流程。
将异常用于异常的问题
异常可能会遭受经典意大利面条式代码的所有可读性和可维护性问题的折磨。将异常保留给异常的事物。
要有始有终
分配某资源的例程或对象也应该负责解除其分配。
- 以与资源分配次序相反的次序解除资源的分配
- 在代码的不同地方分配同意组资源时,总是以相同的次序分配它们。(降低发生死锁的可能性)
- 无论时谁分配的资源,都应该负责解除该资源的分配
使模块之间的耦合减至最少
通过编写“羞怯的”代码并应用得墨忒耳法则来避免耦合。
得墨忒耳法则:
- 每个单元对于其他的单元只能拥有有限的知识:只是与当前单元紧密联系的单元
- 每个单元只能和它的朋友交谈:不能和陌生单元交谈
- 只和自己直接朋友交谈
平衡你的规定应用的各种正面因素和负面因素。
要配置,不要集成
要将应用得各种结束选择实现为配置选项,而不是通过集成或工程方法实现。配置方式,方便修改,统一维护。
将抽象放进代码,细节放进元数据
为了一般情况编程,将细节放在被编译的代码库之外。尽可能编写通用的代码。
分析工作流,以改善并发性
利用你的用户的工作流的并发性。
用服务进行设计
根据服务——独立性、在良好定义、一致的借口之后的并发对象——进行设计。
总是为并发进行设计
容许并发,你将会设计出更整洁、具有更少假定的接口。
使视图与模型分离
要根据模型和视图设计你的应用,从而低廉的代码获得灵活性。相当于MVC中的view层和model层进行分离。
用黑板协调工作流
用类似黑板的画图方式总结和协调工作流。
不要考巧合编程
只依靠可靠的事物。注意偶发性的复杂性,不要把幸运的巧合与有目的的计划混为一谈。
- 总是意识到你在做什么。
- 不要盲目地编程。视图构建不完全理解的应用,或是使用不熟悉的技术,就是希望自己被巧合误导。
- 按照计划行事,不管计划时在你的头脑中。
- 依靠可靠的事物。
- 为“假定”或“合约”建立文档。
- 不要只是测试你的代码,还需要测试你的假设。
- 为你的工作划分优先级。
- 不要做历史的奴隶,不要让已有的代码支配将来的代码。不要让已经做完的事情约束下一步要做的事情——准备好进行重构。(以最小的成本与影响进行重构)
估算算法的阶
在编写代码之前,先大致估算事情需要多长时间。
测试你的估算
对算法的数学分析并不会告诉你每一件事情。在代码的目标环境中测定它的速度。
早重构,常重构
就和你会在花园里除草、重新布置一样,在需要时对代码进行重构、重做和重新架构。要铲除问题的根源,控制演化方向。
- 重构与需求应该分开进行(至少分为不同步骤)。不要试图在重构的同时增加功能。
- 重构前列出测试case。保证重构后拥有良好的测试。
- 采取短小、深思熟虑的步骤。
如果某个代码与设计现在又损害,但以后损害会更大,也许最好一劳永逸的修正它。不要容忍破窗户。
为测试而设计
代码与系统设计时需要考虑测试。
日志中必须带有必要的信息,方便跟踪问题。日志消息的格式应该是正规、一致。
测试你的软件,否则用户就得测试
测试是技术、更是文化;不管所用的语言是什么,我们都可以让这样的测试文化慢慢渗入项目中。
不要使用你不理解的向导代码
不要收集需求——挖掘它们
挖掘潜层需求,理解需求背后的目的。需求分析是指了解具体的需要。
找出用户为何要做特定事情的原因,而不只是它们目前做这个事情的方式。开发解决的最终是商业问题,而不只是满足他们陈述的需求。用文档记录需求背后的原因。
与用户一同工作,以像用户一样思考
要了解系统实际上将如何被使用,这是最好的方法。
开采需求的过程也是开始与用户群建立和谐关系、了解他们对正在构建的系统的期许和希望。
抽象比细节活得更长久
“投资”于抽象,而不是实现。抽象能在来自不同的实现和新技术的变化的“攻击”之下存活下去。
不要做任何表示方法的奴隶;只要是与你的听众交流需求的最好的方法,都可以加以使用。
使用项目词汇表
创建并维护项目中使用的专用术语和词汇的单一信息源。文档需要方便阅读与分享。
不要在盒子外面思考——要找到盒子
在遇到不可能解决问题时,要确定真正的约束。问问自己:“它必须以这种方式完成吗?它真的必须完成吗?”
回头考虑自己的最终目的,需求最后的需要,自己是否被限制住了。
- 有更容易的方法吗?
- 你是在设法解决真正的问题,还是被外围的技术问题转移了注意力?
- 这件事为什么是一个问题?
- 是什么使它如此难以解决?
- 它必须以这种方式完成吗?
- 它真的必须吗?
等你准备好再开始
你的一生都在积累经验。不要忽视返回出现的疑虑。
将特殊点训练成意识、习惯。
对有些事情“做”胜于“描述”
不要掉进规范的螺旋——在某个时刻,你需要开始编码。
不要做形式方法的奴隶
如果你没有把某项技术放进你的开发实践和能力的语境中,不要盲目地采用它。
昂贵的工具不一定能制作出更好的设计
小心供应商的炒作,行业教条、以及价格标签的诱惑。要根据工具的价值判断它们。
围绕功能组织团队
不要把设计师和编码员分开,也不要把测试员与数据建模员分开。按照你构建代码的方式构建团队。
质量是一个团队问题。如果团队主动鼓励开发者不要把时间花费在这样的修正上,问题就会进一步恶化。确保每个人都主动地监视环境的变化。
创建品牌,建立信任机制。对外界而言,看上去沉闷寡言的项目团队是最糟糕的图团队。无章次的会议、混乱的文档、不统一的术语都是要避免的。
把团队划分为小团队,分别负责最终系统的特定方面的功能。让各团队按照个人的能力,在内部自行进行组织。
不要使用手工流程
shell脚本或批文件会一次次地以同一顺序执行同样的指令。
自动化是每个项目团队的必要组成部分。确保项目的一致性和可重复性。人的重复性可能存在很大问题(不要基于记忆),应该使用文档来规束,代码进行负责自动化。
早测试,常测试,自动测试
与待在书架上的测试计划相比,每次构建时运行的测试要有效得多。
要到通过全部测试,编码才算完成
单元测试是对某个模块进行演练的代码。
性能测试、压力测试或负载测试也可能会是项目的一个重要方面。
回归测试是把当前测试的输出与先前的(或者已知的)值进行对比。
- 单元测试
- 集成测试
- 验证和校验
- 资源耗尽、错误及恢复
- 性能测试
- 可用性测试
- 对测试自身进行测试
通过”蓄意破坏“测试你的测试
在单独的软件副本中故意引入bug,以检验测试能够抓住它们。
测试状态覆盖,而不是代码覆盖
确定并测试重要的程序状态,只是测试代码行是不够的。
一个bug只抓一次
一旦测试员找到一个bug,这应该是测试员最后一次找到它。此后自动测试应该对其进行检查。
英语就是一种编程语言
像你编写代码一样编写文档:遵循DRY原则、使用元数据、MVC、自动生成,等等。
把文档建在里面,不要拴在外面
与代码分离的文档不太可能被修正和更新。
在必要的地方添加注释,代码即是文档,避免多余的注释。比无意义的名称更糟糕的是误导人的名称。
文档需要写下文档创作的时间。
温和地超出用户的期望
要理解用户的期望,然后给他们的东西要多那么一点。