-
2009-11-15
勿以善小而不为
我要不要做单元测试呢?这是一个我被许多人问了许多遍的问题,通常,这个问题会出现在讨论TDD的时候,在提问者眼中:
- 一眼就能看明白,不值得测
- 单元测试粒度太细
- 单元测试要写好多,耽误工作量
- 单元测试不稳定
- 人员能力有限,写不好单元测试
正是因为这样的原因,在他们看来,系统测试也许是一个更好的选择。我不否认系统测试的重要性,但是,单元测试才是他们最应该做好的。
程序员和程序员之间的差异很大程度上是由开发习惯决定的。Kent Beck曾自我评价,“我不是什么伟大的程序员,我只是一个有着很多好习惯的程序员”。单元测试就是一个好习惯。它等于要开发人员在编码的时候就要验证代码的正确性,而不是向后拖。软件开发最基本的一条规律是,越往后做,代价越大。正确性总要验证,那当然是越早验证越好。
如果你做过单元测试,你就会发现,一旦开始考虑验证,很多之前不曾考虑过的问题就会浮出水面,比如,一些边界条件。单元测试给了我们一个更仔细的关注质量的机会。虽然我们不能说经过单元测试的代码就是100%的完美,但是,我们至少给出了力所能及的验证。而事实上,一旦到更高层面的验证,比如系统测试,比如测试人员的测试,这些所谓的细枝末节是很难覆盖到的。而这些地方又恰恰是很多开发人员认为简单的,不值得测的地方。一旦遗漏,就会成为产品的缺陷,随时可能爆发。
有人曾给过我们这样一个数据,他们的产品中有15%的bug源自像变量赋值这样非常简单的错误。从这个数据可以看出,很多人还做好自以为很简单的事。程序员有自信是好事,但前提是要有自信的本事。单元测试给了开发人员一个一步一个脚印,踏踏实实走路的机会。当每个构造快都经过验证的之后,把它们组合在一起,我们只要验证他们之间组合就好了。这样的做法,每一步心里都是有底的。而现在的很多开发给人的感觉是在撞大运。
时至今日,还有很多程序员把开发完成理解成“编码完成”。行百里者半九十,这句话就是给这样的程序员预备的。他们以为自己代码写到90%的时候,其实,后面还有一大半的验证工作等着他们。这些程序员只不过在用有另外一个名字——比如集成——的时间去做他们份内的工作,而某些愚笨的老板居然认为这是天经地义的,所以,他们会压缩编码时间,给后面的验证留下更长的时间,于是,开发人员的时间更紧张了,他们更倾向于前期少验证,一个恶性循环就此形成。
对于很多人来说,单元测试有问题,但实际上,真正有问题的是代码。为什么测试不好做,因为代码的耦合太大了。为什么单元测试不稳定,因为设计不稳定。谈到这里,我得承认一点,想把这一切都做好,人是关键。不是所有人都能拆解好代码的,但这里真正的问题是,不是所有人都有意识去拆解代码的,那些代码里面的垃圾就是从这里产生的。很多自以为是建设者的人,实际上,是添乱的。
或许你会说,有很多高手都认为单元测试不是必要的,确实如此,但你光看见贼吃肉了,那些高手个人能力超强,基础扎实,思维缜密,先跟人家比这些东西吧!对于软件开发人员来说,单元测试就是基本功,通过它,可以让人在很多方面得到很多锻炼。先把基本功打扎实再琢磨别的吧! -
2009-11-08
我们很忙
软件开发似乎就应该是忙碌的,加班似乎也应该是常态。我听到最为痛心的一个说法是,每天都在加班,忙得连洗衣服的时间都没有。也是因为忙,忙得有没有时间学习、没有时间思考。所有一切都是因为忙。
但是,我们都是怎么忙的呢?不妨看一些细节。
软件开发中,最重要的过程应该是编码,但真正的编码有多长时间呢?在一个采用传统开发方式的团队中,会有一个最后集成阶段。因为前期做的验证不够充分,所以,要给后面留下很长的时间,用来做集成和测试,更有甚者,这个时间要比“开发”时间还长。这里的“开发”阶段只是在纯粹的编码。就这样,在计划中,很大的一段时间被砍掉了。
即便是在“开发”阶段,就真正能坐在那里好好写代码吗?未必。在大企业里面,最浪费时间的是什么?开会。人多,沟通不畅,开会成了“自然”的选择,而且,往往是人越多,会就越多。事实上,很多企业的会都是非常低效,一开会就发散,通常一个会一两个小时就进去了。在《做好一件事》中还提到矩阵式管理也会让人无法好好写代码。就这样,在团队开发中,又有一段时间被消耗掉了。
即便真正开始写代码,又是怎么做的呢?对于历史悠久的产品而言,程序员需要面对大量的遗留代码,编译一次代码,都要用很长时间,这意味着,消除编译错误这样原本应该很简单的事情都要消耗大量的时间。于是,经常可以看到一个个程序员面无表情的在那里等待编译。遗留代码,通常也意味着欠了一屁股的技术债务,也就是说,为一个功能要遍地开花的改许多代码,这也很耗时。对于不编写单元测试的程序员,常用的验证手段就是调试器,因为人的参与,这个过程不能自动化,很是缓慢。好容易到了该提交代码了,因为文件很大,出现冲突的几率也增加了,处理冲突也是耗时的事。在个人开发中,时间就在不知不觉中流逝。
我们很忙,因为忙而认为一切都是理所当然的。正如上面所列,很多时间实际上是在一环扣一环的低效中无谓消耗了,所以,每天不得不加班,让自己看上去很忙,以此换回内心的“安宁”。
我们真的很忙吗? -
2009-10-31
2009 Away Day
这是一篇欠帐,因为Away Day是上个周末的事情了。
我很喜欢Away Day,因为:- Away Day是放松,我们可以逃离办公室
- Away Day是学习,有各种各样的session等着大家
- Away Day是聚会,外国同事千里甚至万里迢迢来参加,出差在外的同事也会回来
- Away Day是交流,不同人不同思想在一起碰撞
- Away Day是……
遗憾的是,去年因为经济的原因,Away Day取消了,一年之后,当Away Day再次有机会重现江湖的时候,我充满了期待。不同于两年前在办公室里“Away”,这次我们真正意义上的Away。
Away Day中一个重要的安排当然是一大堆的session,这是一个非常好的学习机会。我的immersion同学Ola Bini这次来到中国,参加Away Day,他的session是关于下一代JVM,也就是JDK 7的一些发展和变化。对于经常关注业界新闻的人来说,语言层面上的东西没有太多新鲜的内容,倒是Ola对低层实现方面的一些讨论,让人可以更系统的了解JVM的发展。徐X一个关于Scala的session居然听不出和Scala有什么关系,实在是有些过份。徐X借着这次机会,表达了一些他对几种程序设计语言的喜爱,比如Smalltalk,Haskell等等。倒是结束之后,我和我们CTO麦罗聊了一会Scala,我建议他弄个Scala的项目,让大家感受一下。
这次个人感觉收获最大的一个sesion来自我们的老大郭晓,他的主题是关于认知偏见的。我开玩笑的把这个session总结为,如何防止被忽悠以及忽悠别人。自从开始成为咨询师,我就开始对心理学感兴趣,这个session中所讲的东西,我可以很好的映射到我的咨询工作之中,所以,内心会产生出极大的共鸣。事实证明,这不是个别现像,我们后来玩杀人游戏的时候,这些内容左一次右一次的出现在我们面前,于是,杀人游戏又变成了这个session的延续。
好久没有见到我们创始人Roy了,不过,一般情况下,Away Day他都会出现的,这次也不例外。更大的惊喜是,他的老母亲也出现在北京。我们几个同事在交流咨询经验的时候,Roy走了过来,加入了我们。我们把自己之前的一些工作告诉给他,饶有兴致的听完我们的介绍,他认为我们的工作很有趣,建议我们写一本书,把这些经验介绍出来,同时,也建议我们写一个介绍,发给其它办公室的ThoughtWorker,让有兴趣的人也加入到中国这边的咨询工作中来。
Away Day的狂欢出现在我们盛大的Party。这些平时已经很能出彩的家伙还是能玩出更多花样。恶搞了很多人“专业”视频;一群新ThoughtWorker表演《唐伯虎点秋香》;把最喜闻乐见的pair拿出来展现反模式;让Sponsor和Sponsee体验默契;用十分钟创作一个小节目……
Away Day总是让人快乐的! -
2009-10-21
管理问题?技术问题?
产品做大了,人多了,就要分组,有了分,自然还要有合。这是一个产品的两个团队,他们原有的工作习惯是,一旦有工作涉及到二者的接口,两边独立开发,等到完全开发完毕,双方“一起”把代码合入代码库。或许,单看两个团队,问题还不是特别明显。在整个产品中,开发团队有很多,类似的情况在底层团队和几乎所有其它团队之间都存在,而且每个团队都有自己的开发进度,由此,你可以想见,采用这种开发模式,协调各组之间的开发计划到底是一件多困难的事情了。
为什么不能把接口部份先合入代码库,然后,两边一起分别完成各自的工作。经过调研发现,接口中有个双方共用的结构体,在这个结构体中添加一个新的字段,从上层调用者来说,没有任何问题,问题出现在底层。在实现中,会对比这个结构体的长度和从数据库读出内容的长度,在底层还没有实现这个功能时,数据库内容的长度和结构体的长度肯定是不一样的,而这个不一致会导致整个系统启动失败。这是双方不能独立工作的真正原因。
上面的原因看上去很合理,但放到一起,却给人感觉那么奇怪。因为添加一个字段,导致系统无法启动,所以,两个组要把代码攒到最后一次性提交,所以,计划很难协调。
为什么双方要依赖于同一个结构体呢?从直观的想法来看,两个部份用到是同样的结构,使用同一个结构体就无可厚非。但结果告诉我们,这样的做法是有问题的。一个改进方案是,上层代码做一层封装,把底层代码隔离开来,这样,上层团队就可以单独开发,不再依赖于底层团队的开发进度,之后,当二者都完成开发时,只要把这一层封装稍做修改,二者的工作己可以连接到一起。
这是一个常见的手法。比如,Java项目经常会用到一些开源库,我们并不会让程序的所有部份都直接依赖于这个库,而是要做一层封装,这样,哪天我们打算换掉这个库的时候,只要改掉这层封装就可以了,其它部份完全不需要修改。
设计的一个作用就是解耦。解耦一个表现就是要区分出内外。或许,在整个产品的范围内看来,他们都是“内部”,但是由于跨越了不同的团队,实际上,他们并不是“自己人”。所以,彼此分离才是一个正确的选择。这样一个设计上的改进,解开的不仅仅是代码之间的耦合,也是两个团队开发进度之间的耦合,二者不再需要等待对方,只要把自己的工作做好。唯一需要协调的点,就是最终的集成,但这个点并不影响团队的开发进度。
初看起来,这是一个管理问题,但真正的问题其实是设计,而这只是一个例子。 -
2009-10-14
捏软柿子
或许你不知道,那些在项目组里面耀武扬威的家伙,可能是客户眼中的软柿子。
- 有一个很大的项目,负责与客户接口的有三个人,大多数需求都是从一个人那里进来的,因为这个人是最好说话的。结果是,本来可以顺畅运行的项目,由于需求的暴增,导致不得不加班,一个原本运转正常的项目,怨声载道。
- 一个计划20个点的迭代,迭代结束后,待开发的点居然超过30个。这些需求都是从这个项目负责人那里直接走到待开发的状态,因为每个提需求的人告诉项目负责人的都是这个需求是很重要、很紧急的。结果是,项目组的进度并没有因需求的增加而加快,反而士气受到了打击。
- 有一个人很好的PM,总是把满足客户需求看作很重要的事,所以,他倾向于接受客户的所有要求。结果是,组内其他有经验的人据理力争,顶住了PM接受客户需求的倾向,项目组一直顺利运转。
这些都是真真切切发生在我的身边,或是我亲历,或是对周边项目的所见所闻。
客户是上帝,让客户高兴很重要。但让客户高兴,并不等于一味的接受客户所有的要求。事实上,你真的这么做了,客户不会领你的情。他会认为你接受他的要求是一件天经地义的事,所以,他会变本加厉的提出更多的东西。一旦成了客户眼中的软柿子,他就会不断的“欺负”。在第一个例子里面,其他两个人不那么好说话,于是,第三个人成了突破口。人都愿意做容易的事,所以,捏软柿子也就再正常不过了。
或许很多人并不承认自己是“软柿子”,但当他们听到“紧急”、“重要”、“有价值”这样的词汇时,他们便迷失了方向,就像第二个例子里面那个负责人一样。但是客户为了强调重要性时,他肯定会这么说。只有剥离这些形容词,才能淘尽黄沙始见金。
团队中的“软柿子”真正伤害的是团队。一般能够和客户打交道的,在团队中都会扮演比较重要的角色。因为他是团队和客户的接口,他在团队内部受到的置疑相对会少一些,所以,他一旦接受客户的要求,多半就意味团队接受了客户的要求。像上面最后一个例子中那样的情况,其实并不多见,这需要团队中有经验更丰富的人。
如果你所在团队有个“软柿子”,你的生活肯定不好受。







