原文链接
最近读了《高效程序员的45个习惯》,书中主要介绍了一些敏捷开发在实际应用当中的经验。对于很多项目开发管理经验不足的人来说,有很多地方是值得借鉴的。本书的译者将书中的方法总结成了一个歌诀,便于记忆,觉得很有意思。歌诀如下:
迭代开发,价值优先
分解任务,真实进度
站立会议,交流畅通
用户参与,调整方向
结对编程,代码质量
测试驱动,安全可靠
持续集成,尽早反馈
自动部署,一键安装
定期回顾,持续改进
不断学习,提高能力。
歌诀基本上总结了正本书的梗概,下面我依据这个概要和自己的一些工作经验,详细的介绍一下这些方法再实际应用中的一些实践。
敏捷开发模式已经流行了很多久了,敏捷开发中遵守的一些原则:
<1> 个体和交互胜过过程和工具
<2> 可工作的软件胜过面面俱到的文档
<3> 客户协作胜过合同谈判
<4> 相应变化胜过遵循计划
高效开发要求我们能不断的从自己写的代码中得到反馈,并且使用自动化工具不断的构建(持续集成)和测试系统。通过持续集成我们可以在后台不断对开发者提交的代码进行检验和测试。同时要统一工具链,确保工具链一致。选择工具链的时候要考虑到对各种环境的支持。
同时为了提高开发效率,在敏捷团队中,大家的重点是做事。应该把重点放到解决问题上,而不是在指责犯错者上面纠缠。
在开发中,我们要做正确的事,要诚实,要有勇气去说出实情。有时,这样做很困难,所以我们要有足够的勇气。
软件开发行业是一个不断发展和永远变化的领域。虽然有一些概念一直在用,但还有很多的知识很快就会过时。从事软件开发行业就像是在跑步机上,你必须一直跟上步伐稳步前进,否则就会摔倒出局。谁会帮助你保持步伐前进呢?在一个企业化的社会里,只有一个人会为你负责--你自己。是否能跟上变化,完全取决于你自己。
为了说明知识更新迭代的速度,下面举几个例子。就以Java为例,你掌握了Java语言以及其一系列的最新特性,接着你要掌握Swing、Servelet、JSP、Structs、Tapestry、JSF、JDBC、JDO、Hibernate、JMS、EJB、Lucence、Spring. 如果你使用的是微软的技术,要掌握:VB、VisualC++、MFC、COM、ATL、.NET、C#、VB.NET、ASP.NET、ADO.NET 、WinForm、Enterprise Service、Biztalk等等。
在学习过程中我们要不断的通过问“为什么呀”,来提高自己的学习深度。不停的问为什么。不能只满足于别人告诉你的表面现象。要不停的提问直到你明白问题的根源。
你不能一次又一次为用户演示新功能,而浪费宝贵的开发时间,因此你需要提高实现自动化部署(自动更新)。只要你的代码一直可用,并且易于向用户部署。你就能使用演示获得频繁反馈。这样你就能经常向全世界发布新版本。通过使用短迭代,增量发布来帮助经常发布新功能,与用户的需求变化联系更加紧密。
确定哪些东西不应该由自己做决定。当和客户讨论问题的时候,准备好几种可选择的方案。不是从技术的角度,而是从业务的角度,介绍每种方案的优缺点,以及潜在的成本和利益。和他们讨论每个选择对时间和预算的影响,以及如何权衡。无论他们做出了什么决定,他们必须接受它,所以最好让他们了解一切之后再做这些决定。如果事后他们有想要其它的东西,可以公正的就成本和时间重新谈判。
设计是软件开发过程中不可缺少的步骤;设计满足实现即可,不必过于详细,不要过度设计。“不要在前期做大量的设计”并不是说不要设计,只是说再没有经过真正的代码验证之前,不要陷入太多的设计任务。当对设计一无所知的时候,投入编码是一件危险的事。如果你随后能把这些代码扔掉,那也是一个不错的办法。即使初始的设计到后面不再管用,你仍需要设计:设计行为是无价的。正如美国总统艾森豪威尔说:设计是没有价值的,但计划的过程是必不可少的。在设计过程中学习是有价值的,但设计本身也许没有太大的用处。 设计过程中,白班,草图、便利贴都是非常好的设计工具。复杂的建模工具只会让你精力分散,而不是启发你的工作。
在使用新技术的时候,我们不能因为要是用新技术而使用新技术,使用新技术的时候应该问自己几个问题:
<1>这个技术框架真能解决这个问题吗?你是如何评估这个技术的?是通过市场宣传还是道听途说的?
<2>你将会被它拴住吗?一些技术是贼船,一旦你使用了它,就会被他套牢,再也不可能回头了
<3> 维护成本是多少?会不会随着时间的推移,它的维护成本会非常高昂?
如果你发现自己在做一些花哨的东西(比如从头创建自己的框架),那就醒醒吧,问问烟味有多大,马上就该起火了。你的代码写的越少,你需要维护的东西就越少。不要开发你能下载到的东西。
为了保证工程的可发布,我们需要一个工作流程来防止提交的代码破坏系统的代码。
<1>在本地运行测试,先保证你完成的代码可以编译,并且能通过所有的单元测试。接着确保系统中的其它测试都可以通过。
<2>检出最新的代码,从版本控制系统中更新代码到最新的版本,在编译和运行测试。这样往往会发现让你吃惊的事情:其它人提交的代码和你的代码发生了冲突。
<3> 提交代码。现在是最新的代码了,并且通过了编译和测试,可以提交它们了。
为了保证代码的持续可发布,最好有一个持续集成系统,可以自动集成并报告集成结果。持续集成系统就是在后台不停的检出、构建和测试代码的应用。你可以自己使用脚本快速实现这样的方式,但如果你选择已有的免费、开源的解决方案,他们提供的功能更多且更加稳定。有兴趣的可以参考Mike Clark的《项目自动化之道》。
有时候,有些大的改动后,你无法花费太多的时间和精力去保证系统一直可以发布。如果总共需要一个月的时间才能保证它一周内可以发布,那就算了。但这只应该是例外,不能养成习惯。
如果你不得不让系统长期不可以发布,那就做一个(代码和架构的)分支版本,你可以持续进行自己的实验,如果不行,还可以撤销。
在产品开发的过程中,集成是一个主要的风险区域。让你的子系统不停的增长,不去做系统集成,就等于一步一步把自己置于越来越大的风险中,世界没有了你仍然会转动,潜在的分歧会持续增加。相反,尽可能早的集成也更容易发现风险,这样风险及相关的代价就会相当低。而等的时间增长,你也就会越痛苦。
集成和独立不是相互矛盾的,你可以一边进行集成,一边进行独立开发。使用mock对象来隔离对象之间的依赖关系,这样在集成之前就可以先做测试,用一个mock对象模拟真实的对象(或子系统)。就像是拍电影时在光线的掩饰下使用的替身一样,mock对象就是真实对象的替身,它不提供真实对象的功能,但是他更容易控制,能够模仿需要的行为,使测试更加简单。
尽早实现一种可重复和可靠的方式,在目标机器上部署你的应用。有了自动化部署系统之后,在项目开发的整个过程中,会更容易适应相互依赖项的变化。很可能你在安装系统的时候,会忘记添加需要的库或者组件,在任意一台机器上运行自动化安装程序,你很快就会知道什么丢失了。
从第一天起就开始交付。一开始就进行全面部署,而不是等到项目的后期,这会有很多好处。事实上,有些项目在正式开发之前,就设置好了所有的安装环境。即使项目还没有正式开始,我们就有了单元测试、持续继承和基于窗口的的安装程序。这样,我们就可以更容易更简单的给用户交付这个演示系统。
系统的安装和部署应该简单、可靠及可重复。一般产品在安装的时候,都需要有相应的软、硬件环境。比如,Java或Ruby的某个版本、外部数据库或者操作系统。这些环境的不同可能会导致很多技术支持的电话。所以检查这些依赖关系,也是安装过程中的一部分。
在没有询问并征得用户的同意之前,安装程序绝对不能删除用户的数据。
用户应该可以安全并且完整的卸载安装程序。
如果维护安装脚本变的很困难,那很可能是一个早期警告,预示着-很高的维护成本。
没有人的思想和观点可以及时冻结,特别是项目的客户。就算是他们已经告诉你想要的东西了,他们的期望和想法还是在不停的进化-----特别是当他们在使用新系统的部分功能时,他们才开始意识到它的影响和可能发生的问题。这是人的本性。
要频繁的获得反馈。如果你的迭代周期是一个季度或者一年(那就太长了),就应该把周期缩短到一周或者两周。完成了一些功能和特征之后,去积极获得客户的反馈。
维护项目术语表,在项目开发过程中,从术语表中为程序结构,类、方法、模型、变量等选择合适的名字,并且要检查和确保这些定义一直都符合用户的期望。
统一过程和敏捷方法都使用迭代和增量开发。使用增量开发一次开发应用功能的几个小组。每一轮的开发都是基于前一次的功能,增加为产品增值的新功能。这是,你就可以发布或者演示产品。迭代开发是,在小且重复的周期里,你完成各种开发任务:分析、设计、实现、测试和获得反馈,说以叫做迭代。
询问用户,哪些是使产品可用且不可缺少的核心功能。不要为所有可能需要的华丽功能而分心,不要沉迷于你的想象,去做那些华而不实的用户界面。
我们都知道市场的变化有多快。尽快发布你的应用,迟了也许他就没有用了。使用短迭代和增量开发。如果别人告诉你有一年的时间来完成系统,你会觉得时间很长。如果目标很遥远,就很难专注于它。
编写能产生反馈的代码。要求我们对已经编写的模块进行测试。为了确保测试是有效的要求:
<1>确保测试是可重复的
<2> 测试你的边界条件
<3> 不要放过任何一个失败的测试
单元测试是优质股,值得投资。但一些简单的属性访问方法或者价值不大的方法,是不值得花时间进行测试的。人们不编写单元测试有很多借口都是因为代码中的设计缺陷。通常,抗议越强烈,就说明设计越糟糕。单元测试只有在达到一定的测试覆盖率的时候,才能真正的发挥作用。不是测试越多质量就会越高,测试必须要有效。
使用被称为TDD(Test Driven Development,测试驱动开发)的技术,你总是在编程之前,先写测试。先写测试,你就会站在代码用户的角度来思考,而不仅仅是一个单纯的实现者。这样做是有很大的区别的,你会发现因为你自己要使用它们,所以能设计一个更有用,更一致的接口。
不同的环境,就有不同的问题,使用持续集成工具,在每一种支持的平台和环境中运行单元测试。要积极的寻找问题,而不是等问题来找你。
关键业务逻辑必须要单独进行严格的测试,并且最后需要通过用户的审批。
时间的消逝可以证明:判断工作进度最好是看实际花费的时间而不是估计的时间。如果能一直让下一步工作是可见的,会有助于度量进度。最后的做法就是使用待办事项。待办事项就是等待完成的任务列表。当一个任务被完成了,它就会从列表中移除。当添加新任务的时候,先排列他们的优先级,然后加入到待办事项中。你也可以有个人的待办事项、当前迭代的待办事项或者整个项目的待办事项。通过待办事项,就可以随时知道下一步最重要的任务是什么,同时你的评估技巧也在不停的改进,也越来越清楚完成一项任务索要花费的时间。
开发代码的时候,应该更注重可读性,而不是只图自己方便。从衡量标准上来看,代码清晰程度的优先级应该排列在执行效率之前。
通常程序员都很讨厌写文档,这是因为大部分文档都与代码没有关系,并且越来越难以保证其符合目前的最新状况。所以程序员建立代码文档无外乎两种方式:利用代码本身;利用注释来沟通代码之外的问题。
对于类中的每个方法,可能要说明下列信息。
目的:为什么需要这个方法?
需求(前置条件):方法需要什么样的输入,对象必须处于何种状态,才能让这个方法工作。
承诺(后置条件): 方法成功执行后,对象现在处于什么状态,有哪些返回值?
异常:可能会发生什么样的问题?会抛出什么样的异常?
通过RDoc、javadoc、和ndoc这样的工具,使用它们可以很方便的直接从代码注释创建有用的、格式优美的文档。这些工具抽取注释,并生成样式漂亮且带有超链接的HTML的格式输出。
总而言之,想要让应用成功,降低开发成本与缩短上市时间,二者的影响同样重要。由于计算机硬件价格日益便宜,处理速度日益加快,所以可以在硬件上多投入以换取性能的提升,并将节约下来的时间放在应用的其它方面。
没有最佳解决方案。动态评估权衡。考虑性能、便利性、生产力、成本和上市时间。如果性能表现足够了,就将注意力放在其它因素上。不要为了感觉上的性能提升或者设计的优雅,而将设计复杂化。
开发可以工作的、最简单的解决方案。除非有不可辩驳的原因,否则不要使用模式、原则和高难度技术之类的东西。
代码整洁的艺术:
<1>代码几乎总是可以得到进一步的精炼,但是到了某个点之后,再做改进就不会带来任何实质性的好处了。这时开发人员就该停下来,去做其他方面的工作了。
<2>要将目标牢记在新:简单、可读性高的代码。强行让代码变得优雅与过早优化类似,同样会产生恶劣的影响。
<3>简单的解决方案必须要满足功能需求。为了简单而在功能上妥协,这就是过分简化了。
<4>太过简洁不等于简单,那样无法到沟通的目的。
敏捷代码会将命令与查询分离开来。就是要将功能和方法分为“命令”和“查询”两类,并在源码中记录下来。一个常规的“命令”可能会改变对象的状态,而且有可能返回一些有用的值,以方便使用。一个“查询”仅仅提供给开发人员对象的状态,并不会对其外部的可见状态进行修改。
维护一个保存遇到的问题以及对应解决方案的日志。(不要在同一处跌倒两次)
可以选择符合需求的任何格式,下面这些条目可能会用得上:
问题发生日期
问题简述
解决方案详细描述
引用文章和网址,以提供更多细节或者关键信息
任何代码片段、设置或者对话框截屏,主要它们是解决方案的一部分,或者可以帮助更深入的理解相关细节。
关键要点:
1.记录问题的时间不能超过在解决问题上花费的时间。要保持轻量级和简单,不必达到对外发布的质量
2.找到以前的解决方法非常关键。使用足够的关键字,可以帮助你在需要的时候发现需要的条目
3.要记录发生问题时应用程序、应用框架或平台的特定版本。同样的问题在不同的平台或版本上可能表现的不同。
4.要记录团队做出一个重要决策的原因。
要找到一种方式让那个编译器将警告作为错误提示出来。如果编译器允许调整警告的报告级别,那就把级别调到最高,让任何警告不能被忽略。GCC编译器支持-Werror参数,在Visual Studio中,开发人员可以改变项目的设置,将警告视为错误。
识别复杂问题的第一步,是将它们分离出来。既然不可能在半空中试图修复飞机引擎,为什么还要视图在整个应用中,诊断其中某个组成部分的复杂问题呢?当引擎被从飞机中取出来,放在工作台上之后,就更容易被修复了。
常用的解决方案是记录日志:当发生问题时,让应用详细记录粗偶我的相关数据。错误日志最起码应该以文本文件的形式维护。不过也许可以发布到一个系统级别的事件日志中。可以使用工具来浏览日志,产生所有日志信息的RSS Feed,以及诸如此类的辅助方式。
错误报告对于开发人员的生产率,以及最终的支持活动消耗成本,都有很大的影响。在开发过程中,如果定位和修复问题让人备受挫折,就考虑使用更加积极主动的错误报告方式吧。调试信息非常宝贵,并且不容获得。不要轻易将其丢弃。
区分错误类型:
1.程序缺陷。这些事真正的bug,比如NullPointerException、缺少主键等。用户或者系统管理员对此束手无策。
2.环境问题。该类别包括数据库连接失败,或是无法连接远程的Web Service、磁盘空间满、权限不足,以及类似的问题。程序员对此没有应对之策,但是用户也许可以找到变通的方法,如果提供足够详细的信息,系统管理员应该可以解决这些问题。
3.用户错误。程序员与系统管理员不必担心这些问题。在告知是哪里操作的问题后,用户可以重新来过。
通过追踪记录报告的错误类型,可以为受众提供更加合适的建议。
定期安排会面时间, 设置立会制度
立会都是安排在每个工作日的早些时候,但是不要把它安排为上班的时候的第一件事情。一般来说,在大家到公司后的半个小时到一个小时之内举行。
为了保证会议议题不会发散,每个人都只回答下述三个问题:
1.昨天有什么收获
2.今天计划有哪些工作?
3.面临着哪些障碍?
有哪些要求:
1.会议上每个人都必须回答上面的三个问题,而且都不能展开深入的讨论(讨论可以安排在后面进行)
2.保证最大产出,立会时间最长不能超过30分钟,10-15分钟比较理想
3.根据团队规模设定会议频率,可以一周两次或者三次。
每日立会有诸多好处:
1.让大家尽快投入到一天中的工作中来
2.如果某个开发人员在某一点上有问题,它可以趁此机会将问题公开,并积极寻求帮助
3.帮助团队带头人或管理人员了解哪些领域需要更多帮助,并重新分配人手
4.让团队的成员知道项目其他部分的进展
5.帮助团队识别是否在某些东西上有重复劳动而浪费了精力或者是不是某个问题有人已经有了现成的解决方案。
6.通过促进代码和思路的共享,来提升开发的速度
7.鼓励向前的动力:看到别人报告的进度都在前进,会对彼此形成激励。