每一个好的习惯,开头都会相应有一个唱反调的句子哦。
1 做事
“出了问题,第一重要的是确定元凶,找到那个人!一旦证实了是他的错误,就可以保证这样的问题永远也不会再发生了。”
指责不会修复bug,把矛头对准问题的解决办法,而不是人。这是真正有用处的正面效应。
也许你不相信,但确实有些人常常不把解决问题放在最高优先级上。也许你也没有。先自我反省一下,当有问题出现时,“第一”反应究竟是什么?
一个重大的错误应该被当作是一次学习而不是指责他人的机会。
团队成员们在一起工作,应互相帮助,而不是互相指责。
如果你没有犯过任何错误,就说明你可能没有努力去工作。
2 欲诉则不达
“你不需要真正地理解那块代码,它只要能够工作就可以了。哦,它需要一个小小的调整。只要在结果中再加上几行代码,它就可以工作了。干吧!就把那几行代码加进去,它应该可以工作。”
拙劣的代码工人会这样不假思索地改完代码,然后快速转向下一个问题;而优秀的程序员会挖掘更深一层,尽力去理解为什么这里需要加1,更重要的是,他会想明白会产生什么其他影响。
千里之堤,溃于蚁穴,大灾难是逐步演化而来的。一次又一次的快速修复,每一次都不探究问题的根源,久而久之就形成了一个危险的沼泽地,最终会吞噬整个项目的生命。
如果有一位团队成员宣布,有一块代码其他人都很难看懂,这就意味着任何人(包括原作者)都很难维护它。请让它变得简单些。
3 对事不对人
“当L先生在做一个新方案介绍时,下面有人会说‘这样设计很蠢!都没有考虑线程安全!’(这也暗示着L先生很蠢)。”
事实上,最适合并且最有效的表达方式应该是“谢谢,L先生。但是我想知道,如果两个用户同时登录会发生什么情况?”
尽力贡献自己的好想法,如果你的想法没有被采纳也无需生气;不要因为只是想体现自己的想法而对拟定的好思路画蛇添足。
4 排除万难,奋勇前进
“老鼠们打算在猫的脖子上系一个铃铛,这样猫巡逻靠近时,就能预先得到警报。每只老鼠都点头,认为这是一个绝妙的想法。这时一个年老的老鼠站出来说道’那么,谁愿意挺身而出去系铃铛呢?’毫无疑问,没有一只老鼠站出来。当然,这个绝妙的计划也就这样泡汤了。”
有时,绝妙的计划会因为勇气不足而最终失败。尽管前方很危险,但你必须有勇气向前冲锋,做你认为对的事情。
当你勇敢地站出来时,如果受到了缺乏背景知识的抉择者的抵制,你需要用他们能够听懂的话语表达。“更清晰的代码”是无法打动生意人的。节约资金、获得更好的投资回报,避免诉讼以及增加用户利益,会让论点更有说服力。
5 跟踪变化
“软件技术的变化如此之快,势不可挡,这是它的本性。继续用你熟悉的语言做你的老本行吧,你不可能跟上技术变化的脚步。”
如果只是掌握了工作中需要的技术并不够。那样的工作也许几年之后就不再有了—它会被外包或者会过时,那么你就将会出局。
迭代和增量式的学习;了解最新行情;参加本地的用户组活动;参加研讨会议;如饥似渴地阅读。
6 对团队投资
“不要和别人分享你的知识—自己留着。你是因为这些知识才成为团队中的佼佼者,只要自己聪明就可以了,不用管其他失败者。”
团队中的开发者们各有不同的能力、经验和技术。每个人都各有所长。不同才能和背景的人混在一起,是一个非常理想的学习环境。
一个学习型的团队才是较好的团队。
每周,要求团队中的一个人主持讲座。他会给大家介绍一些概念,演示工具,或者做团队感兴趣的任何一件事。你可以挑一本书,给大家说说其中一些特别内容、项目或者实践,无论什么主题都可以。
7 懂得丢弃
“那就是你一贯的工作方法,并且是有原因的。这个方法也很好地为你所用。开始你就掌握了这个方法,很明显它是最好的方法。真的,从那以后就不要再改变了。”
打破旧习惯很难,更难的是自己还没有意识到这个问题。丢弃的第一步,就是要意识到你还在使用过时的方法,这也是最难的部分。另一个难点就是要做到真正的丢弃旧习惯。 毕竟,汽车要比马车车厢强多了。
不是完全忘记旧的习惯,而是只在使用适当的技术时才使用它。
8 打破砂锅问到底
“接受别人给你的解释,别人告诉你问题出在了什么地方,你就去看什么地方。不需要再浪费时间去追根究底”
要不停的问“为什么”。不能只满足于别人告诉你的表面现象,要不停的提问直到你明白问题的根源。
问“为什么”,但是要问到点子上。
当你问“为什么”的时候,也许你会被反问:“为什么你问这个问题?”。在提问之前,想好你提问的理由,这会有助于你问出恰当的问题。
9 把握开发节奏
“我们很长时间没有进行代码复审,所以这周会复审所有的代码。”
在许多不成功的项目中,基本上都是随意安排工作计划,没有任何规律。那么样的随机安排很难处理。你根本不知道明天将会发生什么。
但是,敏捷项目会有一个节奏和循环,让开发变得更加轻松。Scrum约定了30天之内不应该发生需求变化,这样可以确保团队有一个良性的开发节奏。(Scrum是一种迭代式增量软件开发过程,通常用于敏捷软件开发。包括了一系列实践和预定义角色的过程骨架。)
站立会议最好每天在固定的时间和地点举行,比如说上午10点左右。要养成这样的习惯,在那时就准备好一切参加站立会议。
10 让客户做决定
“开发者兼具创新和智慧,最了解应用程序。因此,所有关键决定都应该由开发者定夺。每次业务人员介入的时候,都会弄得一团糟,他们无法理解我们做事的逻辑。”
在设计方面,做决定的时候必须有开发者参与。可是,在一个项目中,他们不应该做所有的决定,特别是业务方面的决定。
记录客户做出的决定,并注明原因。好记性不如烂笔头。
11 让设计指导而不是操纵开发
“设计文档应该尽可能详细,这样,低级的代码工人只要敲入代码就可以了。编写代码的时候,无论你发现什么,绝不能偏离了设计文档。”
设计满足实现即可,不必过于详细。
即使之前已经提交了设计文档,也还会有一些意料之外的情况出现。时刻谨记,此阶段提出的设计只是基于你目前对需求的理解而已。一旦开始了编码,一切都会改变。设计及其代码实现会不停地发展和变化。
设计可以分为两层:战略和战术。前期的设计属于战略,通常只有在没有深入理解需求的时候需要这样的设计。更确切的说,它应该只描述总体战略,不应深入到具体的细节。
12 合理地使用技术
“你开始了一个新的项目,在你面前有一长串关于新技术和应用框架的列表。这些都是好东西,你真的需要使用列表中所有的技术。想一想,你的简历上将留下漂亮的一笔,用那些伟大的框架,你的新应用将具有极高的技术含量。”
这个技术框架真能解决这个问题么?(如果需要,先做一个小的原型)
你将会被它拴住么?(一些技术是贼船,一旦你使用了它,就会被它套牢,再也不可能回头了。我们需要考虑它是开放技术还是专利技术)
维护成本是多少?(维护成本昂贵。我们听说,有个项目的合同是支持一个规则引擎,引擎一年的维护费用是5万美元,但是,这个数据库只有30条规则)
不需开发你能下载到的东西。
新技术就应该像是新的工具,可以帮助你更好地工作,它自己不应该成为你的工作。
13 保持可发布
“我们刚试用的时候发现了一个问题,你需要立即修复它,放下你手头的工作,去修复那个刚发现的问题。不用告诉其他任何人—赶快让它工作就行了。”
已提交的代码应该随时可以行动。
在本地运行测试;检出最新的代码;提交代码。
14 提早集成,频繁集成
“只要没有到开发的末尾阶段,就不要过早地浪费时间去想如何集成你的代码,至少也要等开发差不多的时候,才可以考虑它。”
敏捷的一个主要特点就是持续开发,而不是三天打鱼两天晒网地工作。特别是在几个人一起开发同一个功能时,更应该频繁地集成代码。
绝不要做大爆炸似的集成。
代码集成是主要的风险来源。要想规避这个风险,只有提早集成,持续而有规律地进行集成。
15 提早实现自动化部署
“没问题,可以手动安装产品,尤其是给质量保证人员安装。”
系统能在你的机器上运行,或者能在开发者和测试人员的机器上运行,当然很好,但是,它同时也需要能够部署在用户的机器上。
质量保证人员应该测试部署过程。
从第一天起就开始交付,一开始就实现自动化部署应用。
如果维护安装脚本变得很困难,那很可能是一个早期警告,预示着—很高的维护成本。
16 使用演示获得频繁反馈
“客户不停的更改需求,导致我们严重地延期。他们一次就应该想清楚所有想要的东西,然后把这些需求给我们。”
需求就像是流动着的油墨。你无法冻结需求,就像你无法冻结市场、竞争、知识、进化或者成长一样。就算你真的冻结了,也很可能是冻结了错的东西。
不一致的术语是导致需求误解的一个主要原因。所以,需要维护一份项目术语表。人们应该可以公开访问它,一般是在wiki或内部网里。
项目启动了一段时间以后,你就应该进入一种舒适的状态,团队和客户建立了一种健康的富有创造性的关系。
17 使用短迭代,增量发布
“我们为后面的3年制定了漂亮的项目计划,列出了所有的任务和可交付的时间表。只要我们那时候发布了产品,就可以占领市场”
给我一份详细的长期报告,我就会给你一个注定完蛋的项目。
对于大项目,最理想的办法就是小步前进,这也是敏捷方法的核心。大步跳跃大大地增加了风险,小步前进才可以帮助你很好地把握平衡。
18 固定的价格就意味着背叛承诺
“对这个项目,我们必须要有固定的报价。虽然我们还不清楚项目的具体情况,但仍要有一个报价。”
固定价格的合同会是敏捷团队的一大难题。我们一直在谈论如何用持续、迭代和增量的方式工作。但是现在却有些人跑过来,想提早知道它会花费多少时间及多少成本。
软件项目天生就是变化无常的,不可重复。如果要提前给出一个固定的价格,就几乎肯定不能遵守开发上的承诺。
如果你现在别无选择,你不得不提供一个固定价格,那么你需要学到真正好的评估技巧。
19 守护天使
“你不必为单元测试花费那么多时间和精力。它只会拖延项目的进度。好歹,你也是一个不错的程序员—单元测试只会浪费时间。”
单元测试能及时提供反馈
单元测试让你的代码更加健壮
单元测试时有用的设计工具
单元测试是你自信的后台
单元测试是可信的文档
单元测试是学习工具
20 先用它再实现它
“前进,先完成所有的库代码。后面会有大量时间看用户是如何思考的。现在只要把代码扔过墙就可以了,我保证它没有问题。”
很多成功的公司都是靠着“吃自己的狗食”活着。也就是说,如果要让你的产品尽可能地好,自己先要积极地使用它。
编程之前,先写测试。
先写测试,你就会站在代码用户的角度来思考,而不仅仅是一个单纯的实现者,这样做是有很大区别的,你会发现因为你自己要使用它们,所以能设计一个更有用、更一致的接口。
21 不同环境,就有不同问题
“只要代码能在你的机器上运行就可以了,谁会去关心它是否可以在其他平台上工作,你又不用其他平台。”
一位同事的代码失败了,最终找到了罪魁祸首:一个.NET环境下的API在Windows XP和Windows2003上的行为不同。平台不同,造成了结果的不一样。
使用持久集成工具,在每一种支持的平台和环境中运行单元测试,要积极地寻找问题,而不是等问题来找你。
22 自动验收测试
“很好,你现在用单元测试来验证代码是否完成了你期望的行为。发给客户吧。我们很快会知道这是否是用户期望的功能。”
关键业务逻辑必须要独立进行严格的测试,并且最后需要通过用户的审批。但是,你又不可能拉着用户,逐一模块确认。所以你需要能自动比较用户期望和实际完成的工作。
FIT(fit.c2.com),即集成测试框架,它很实用,可以更容易的使用HTML表格定义测试用例,并比较测试结果数据。
23 度量真正的进度
“用自己的时间表报告工作进度。我们会用它做项目计划。不用管那些实际的工作时间,每周填满40小时就可以了。”
时间表很难真实地反映工作完成状况,因此它不可以用来进行计划、评估或表现评估。
你曾经听到开发人员报告一个任务完成了80%么?然而过了一天又一天,一周又一周,那个任务仍然是完成80%。
随意用一个比率进行度量是没有意义的。所以不应该去计算工作量完成的百分比,而应该测定还剩下多少工作量没有完成。如果你最初估计这个任务需要 40个小时,在开发了35个小时之后,你认为还需要另外30个小时的工作。那就得到了很重要的度量结果(这里诚实非常重要,隐瞒真相毫无意义)
关注功能,而不是日程表。
24 倾听用户的声音
“用户就是会抱怨。这不是你的过错,是用户太愚蠢了,连使用手册都看不懂。它不是一个bug,只是用户不明白如何使用而已。他们本应该知道更多。”
不管它是否是产品的bug,还是文档的bug,或者是对用户社区理解的bug,它都是团队的问题,而不是用户的问题。
对于一些软件,倒霉的用户必须要配置那些包含了一些魔术数字的模糊系统文件,否则系统根本不会运行。系统既没有错误提示消息,也不会崩溃,只是显示大黑屏和一个斗大的“退出”按钮。
每一个抱怨的背后都隐藏着一个事实。找出真相,修复真正的问题。
没有愚蠢的用户;只有愚蠢自大的开发人员。
“它就是这样的。”这不是一个好答案。
你的用户有可能会阅读所有的文档,记住其中的所有内容。但也可能不会。
25 代码要清晰地表达意图
“可以工作而且易于理解的代码当然好,但是让人觉得聪明更加重要。别人给你钱是因为你脑子好使,让我们看看你到底有多聪明。”
Hoare说“设计软件有两种方式。一种是设计得尽量简单,并且明显没有缺陷。另一种方式是设计得尽量复杂,并且没有明显的缺陷。”
(Hoare创造了Algol 60编程语言,并发明了快速排序算法。于1980年获得图灵奖。)
代码阅读的次数要远远超过编写的次数,所以在编写的时候值得花点功夫让它读起来更加简单。
当开发人员们像一群旁观者见到UFO一样围在代码四周,感到恐惧、困惑与无助时,这个代码的质量就可想而知了。
看一个例子:
coffeeShop.PlaceOrder(2);//通过阅读代码,可以大致明白这是要在咖啡店中下一个订单。但是2代表什么意思?
coffeeShop.PlaceOrder(2 /* large cup */); //不妨添加一些注释。但注释有时候是为了帮写得不好的代码补漏。
public enum CoffeeCupSize
{
Small,
Medium,
Large
}
coffeeShop.PlaceOrder(CoffeeCupSize,Large);//如果使用上枚举值,代码就一目了然了。
应该让自己或团队的其他任何人,可以读懂自己一年前写的代码,而且只读一遍就知道它的运行机制。
26 用代码沟通
“精确地解释代码做了什么,每行代码都要加注释。不用管为什么要这样编码,只要告诉我们做了什么就好了。”
源代码可以读懂,不是因为其中的注释,而应该是由于它本身优雅而清晰。
要尽量避免使用神秘的变量名。(i常用于循环索引变量,str常用于表示字符串。如果用str表示循环索引变量,可真不是好主意)
在代码可以明确传递意图的地方,不要使用注释。
解释代码做了什么的注释用处不那么大。相反,注释要说明为什么会这样写代码。
27 动态评估取舍
“性能、生产力、优雅、成本以及上市时间,在软件开发过程中都是至关重要的因素。每一项都必须达到最理想的状态。”
与其花费时间去提升千分之一的性能表现,也许减少开发投入,降低成本,并尽快让应用程序上市销售更有价值。
如果现在投入额外的资源和精力,是为了将来可能得到的好处,要确认投入一定要得到回报。(大部分情况下,是不会有回报的)
28 增量式编程
“真正的程序员写起代码来,一干就是几个小时,根本不停,甚至连头都不抬。不要停下来去编译你的代码,只要一直往下写就好了!”
如果不对自己编写的代码进行测试,保证没有问题,就不要连续几个小时,甚至连续几分钟进行编程。相反,应该采用增量式的编程方式。
采用增量式编程和测试,会倾向于创建更小的方法和更具内聚性的类。你应该经常评估代码质量,并不时的进行许多小调整,而不是一次修改许多东西。
在写了几行代码之后,你会迫切地希望进行一次构建/测试。在没有得到反馈时,你不要走的太远。
29 保持简单
“通过编写史上最复杂的程序,你将会得到美誉和认可,更不用提保住你的工作了。”
Andy曾经认识一个家伙,他对设计模式非常着迷,想把它们全都用起来。有一次,要写一个大概几百行的代码程序。在被别人发现之前,他已经成功将17种设计模式,都运用到那可怜的程序中了。—这不应该是编写敏捷代码的方式。
问题在于,许多开发人员倾向于将投入的努力与程序复杂性混同起来。如果你看到别人给出的解决方案,并评价说“非常简单且易于理解”,很有可能你会让设计者不高兴。许多开发人员以自己程序的复杂性为荣,如果能听到“Wow,这很难,一定是花了很多时间和精力才做出来的吧。” 这时,他们就会面带自豪的微笑了。其实应当恰恰相反,开发人员更应该为自己能够创建出一个简单并且可用的设计而骄傲。
简单不是简陋。
30 编写内聚的代码
“你要编写一些新的代码,看看IDE中现在打开的是哪个类,就直接加进去吧。如果所有的代码都在一个类或组件里面,要找起来是很方便的。”
内聚性用来评估一个组建(包、模块或配件)中成员的功能相关性。内聚程度高,表明各个成员共同完成了一个功能特性或是一组功能特性。内聚程度低的话,表明各个成员提供的功能是互不相干的。
类也要遵循内聚性。如果一个类的方法和属性共同完成了一个功能,这个类就是内聚的。
不过,不要把一些东西分成很多微小的部分,而使其失去了实用价值。当你需要一只袜子的时候,一盒棉线不能带给你任何帮助。
31 告知,不要询问
“不要相信其他的对象。从别人那里去拿你需要的信息,然后自己处理,自己决策。不要放弃控制别人的机会”。
告知=命令,询问=查询
命令和查询相分离模式,就是要将功能和方法分为命令和查询两类,并在源码中记录下来,以做到将命令代码都放在一起,并将所有查询代码都放在一起。
绝对不能允许一个看起来无辜的“查询”去修改对象的状态。
32 根据契约进行替换
“深层次的集成是很棒的。如果你需要其他类的函数,直接继承它们就好了!”
保持系统灵活性的关键方式,是当新代码取代原有代码之后,其他已有的代码不会意识到任何差别。
如果你不确定一个接口做出了什么样的承诺,或者有什么样的需求,那就很难提供一个对其有意义的实现了。
33 记录问题解决日志
“在开发过程中是不是经常遇到似曾相识的问题?这没关系,以前解决过的问题,现在还是可以解决掉的。”
面对问题是开发人员的一种生活方式。当问题发生时,我们会希望记起第一次是如何解决的,而且希望下次能够更快地把它搞定。但是,有时我们又记不清上次是如何修复的了。
不要在同一处跌倒两次。
要想得到更好的效果,不妨维护一个保存曾遇到的问题以及对应解决方案的日志,我们称之为每日日志(daylog)。
可以选择符合需求的任何格式,下面的内容可能用得上:
问题发生日期
问题简述
解决方案详细描述
引用文章或网址,以提供更多细节或相关信息
任何代码片段、设置或对话框的截屏,只要它们是解决方案的一部分,或者可以帮助更深入地理解相关细节。
务必要将上述信息变为计算机可搜索的格式。
34 警告就是错误
“编译器的警告信息只不过是给过分小心和过于书呆子气的人看的。它们只是警告而已。”
有些警告是过于挑剔的编译器的良性副产品,但有些则不是。例如,一个关于未被使用的变量的警告,可能不会产生什么恶劣影响,但却有可能是暗示某些变量被错误使用了。
签入带有警告的代码,就跟签入有错误或者没有通过测试的代码一样,都是极差的做法。
35 对问题各个击破
“要调试一个明显的错误,只要去查看整个系统的代码,而且是要全部过一遍。毕竟你不知道问题可能发生在什么地方,这样做是找到它的唯一方式。”
单元测试带来的积极效应是它会强迫形成代码的分层。要保证代码可测试,就必须把它从周边代码中解脱出来。
认识复杂问题的第一步,是将它们分离出来。
很多应用的代码在编写时没有注意到这一点,使得分离变得特别困难。应用的各个构件部分之间会彼此纠结:想把这个部分单独拿出来,其他的会紧随而至。在这些状况下,最好花一些时间把关注的代码提取出来,而且创建一个可让其工作的测试环境。
36 报告所有的异常
“不要让程序的调试者看到那些奇怪的异常。处理它们是你的责任。把你调用的一切都包起来,然后发送自己定义的异常。”
作者曾经使用一个非常流行的开源程序库时倍受打击。作者调用的一个方法本来应该创建一个对象,可得到的却是null,调查很久都没有头绪。幸好这个库是开源的,所以他下载了源代码,并找到了出问题的那个调用方法。那个方法认为她的系统中缺少了某些必要的组件。这个底层方法抛出了带有相关信息的异常。但是,上层方法却偷偷地用没有异常处理代码的空catch代码块,把异常给忽略掉了,然后抛出了一个null。后来,作者安装了相应的组件,问题解决了。
37 提供有用的错误信息
“不要吓着用户,吓程序员也不行,要提供给他们干净整洁的错误信息。”
在设计一个登陆页面时,当用户输错密码时,我们提示哪个信息更好呢:“Unable to perform operation”、“Couldn’t login…”、还是“Error validating password”
错误信息有助于问题的解决。当问题发生时,可以详细研究问题的细节描述和发生上下文。
38 定期安排会面时间
“会议安排得越多越好。实际上,我们要安排更多的会议。”
立会 (站着开的会议,Scrum最早引入并被极限编程所强调的一个实践)是将团队召集在一起,并让每个人了解当下进展状况的好办法。顾名思义,参与者们不允许在立会中就坐,这可以保证会议快速进行。一个人坐下来之后,会由于感到舒适而让会议持续更长的时间。
要保证立会议题不发散,每个人只能回答下述三个问题:
昨天的收获是什么
今天计划要做哪些工作
面临着哪些障碍
大家都期盼着立会,希望彼此了解各自的进度和手上的工作,而且不怕把各自遇到的问题拿出来公开讨论。
39 架构师必须写代码
“我们的专家级架构师会提供设计好的架构,供你编写代码。他经验丰富,拿的薪水很高,所以不要用一些愚蠢的问题或者实现上的难点来浪费他的时间。”
这些架构师通常在项目开始时介入,绘制各种各样的设计图,然后在重要的代码实现开始之前离开。有太多这种“Powerpoint架构师”。由于得不到反馈,而且设计会随着时间而演进,所以他们的架构设计工作也不会有很好的收效。
新系统的设计者必须要亲自投入到实现中去。
40 实行代码集体所有制
“不用担心那些烦人的bug,Joe下周假期结束回来后会把它解决掉的。在此之前先想个权宜之计应付一下吧。”
不应像国家宣称对领土的所有权一样,声明个人对代码的所有权。任何一位团队成员,重要理解某段代码的来龙去脉,就应该可以对其进行处理。如果某一段代码只有一位开发人员能够处理,项目的风险无形中也就增加了。
相比找出谁的主意最好、谁的代码实现最烂而言,解决问题,并让应用满足用户的期望更为重要。
在大型项目中,如果每个人都可以随意改变任何代码,一定会把项目弄得一团糟。代码集体所有制并不意味着可以随心所欲、到处破坏。