我和充斥臭味代码的战争
by leetaolion 2008-01-12
从去年8月份到现在,我一直在从事一个261k行代码的项目(不含空行和注释)。
其中由我本人编写的代码10.9k行(不含空行和注释,我本人所写的注释大约1/8有效代码行,属于比较少的)。
其他模块注释非常少,只在接口文件声明虚接口的时候会有一行注释,实现文件中几乎没有注释,大部分文件连文件头都没有。
在我加入小组之前,小组是稳定的,每人负责几个模块,不采用TDD,依靠的是每日集成。每日的目标是:可以Build的代码。
我加入之后,起初做些边边角角的工作。这样的日子大概持续了1个半月的样子。然后在一个午睡并没有完全醒来的状态下,接手了一个核心模块DataMgr_Module,这个模块管理所有的计算的输出结果。
一开始对工作的估计过于乐观,认为1个月就可以搞定。接手之后才发现,原来的DataMgr_Module其实是两个module,小组的老大和另外一位同事对于DataMgr是各自为政,每个人都有一份自己的数据结构和管理方法。由于同事同时负责多个上层模块的开发,所以,DataMgr_Module和上层完全耦合在一起,DataMgr_Module的大部分功能都分布在各个上层模块之中,每个上层模块重复实现的这些功能,如果有一个功能需要更新,那么就要改动所有的上层模块。所谓的DataMgr_Module只不过是把自己的所有接口都公布出来的任人欺负的家伙,公布的大部分接口都应该是私有接口。DataMgr_Module就像是个受气包,Manager变成了Clark。
首先要做的是恢复Manager的地位。统一数据结构,统一传输协议,从上层模块收回所有接口实现,规范业务流程,private所有私有接口和成员变量。Manager就该有个Manager的样子。
模块做到最后,我的感受是越到集成测试的时候,我就会越想念TDD。为什么呢?
DataMgr_Module一开始就采用了TDD。知道所有的PR会上确认的接口都完成,并通过了测试用例。单元测试做的并不充分,因为当时没有采用任何的测试用例方法。只是凭借自己对模块可能存在的缺陷进行了测试。整个单元测试的过程与其说是在测试,倒不如说是在验证,过程中对于错误的恐惧,使测试用例漏洞百出,覆盖效果是比较差劲的。另外一个原因是,TDD是把测试用例当作用户手册的,但是,同事们根本不在意你的测试用例。别说测试用例了,有时候连注释都懒得看,有问题直接沟通,人就是一部用户手册。沟通解决问题,是积极高效的,有时候是重复的。另外一个原因就是,注释的模式不好,常常要更新同样的3个不同位置的注释。之前健健告诉我,应该把这看成是必须做好的工作,就好了。但是我人比较懒,需要个好办法,变3次为1次。(目前也没有找到好办法,因为我压根就没有怎么好好找过。)
所以我常常在想,怎么样才能让TDD深入我的日常工作呢?
TDD的老爹Kent说如果已经有可以运行的代码,这时候是不适合引入TDD的。对已经可以运行的261k行代码重新写测试用例,怕是要出人命的。我想Kent老爹说的是老代码从新TDD,言之有理,对于一个成年人你来T他的小DD,当然就要踢出大事情来。但是对于一些新模块,或者说是老模块需要彻底修改(几乎全部抛弃)的时候,年轻人吗,从小开始T他的小DD,慢慢培养,说不定能T出一个会铁裆功的模块来,到时候岂不是天下无敌了。
正因为DataMgr_Module从小开始TDD,集成测试的时候,需要增加一个接口(主要用来提高模块的易用性)的时候,从提出需要到拆入,不过5分钟,令用户满意的嘴都合不拢了。为什么啊?因为TDD了。
嘴上无毛,办事不牢。调试除了问题,几位用户首先想到的是DataMgr_Module出了问题,肯定要找我。10次有8次是因为用户没有按照事先约定的协议进行操作引起的错误。为什么啊?因为TDD了。
当然也有T的不好的时候。一个哥们把DWORD编码的模块,内部全部用int代替,因为他自认不会有超过0x7FFFFFFF个数据要处理。好嘛,新的规则把编码的首位置1,0区分两类不同数据,每次操作全是失败。上层模块(DataMgr_Module也是相对的上层模块)拿着这哥们的DLL调的天昏地暗,也丈二和尚摸不着头。没办法,把老兄的的代码搞过来研究一下,原来老兄偷懒,把所有的字符串全部中转成了int(因为没有现成的字符串到双字的转换函数),他T到最后也没把这个臭虫给T出来。Shit,害死人不抵命的啊。(于是有了上一篇浏览量不高的字符串转双字方法。)
DataMgr_Module出生,使用使得部分Module因此瘫痪,老大不得已,注释掉了大量的代码,最上面打上“TODO”的MsgBox,每次运行的时候,大家都会想起我。
当然,最后这些次生修改工作必须由我来承担的。老大不得已,开辟了部分原来限制访问代码的拆出。不看则以,一看晕菜。老Clark的臭味绵延的如此之远,上至业务模块,下至基础模块,无一不沾腥带臭。
有一些代码甚至到了令人发指的地步,老大Z写的一个短短20行的函数,为了使用方便封装成n个宏函数,并对其中的部分函数再次#define,用户老大S为了方便,在此基础上又#define了一次,不知怎么回事,老大S换了个名字又是一次#define。两位老大在开发理念上的分歧在这短短的几行代码之中体现的淋漓尽致。Sigh,何必呢。(注:老大S是以每日Build通过为目标,效率至上。老大Z是一完美主义者,每日Build通过是他的目标之一,更注重Execute的代码。两位老大的共同点技术功力深厚,另外就是从未及时Meet过DeadLine。我本人更倾向于老大Z的开发理念,不过现在是老大S的小弟。)
看来两位老大都有问题,到底以啥子为目标呢?
看看微软的一天
每日构造: daily build (mid-night)
开发: 解决blocking bugs, 实现功能, check-out, code review, check-in
测试: BVT, 使用测试用例进行测试
项目经理/组长: 专家会诊
我们的差距在哪?
在测试上。目前团队大部分成员对于测试用例的理解是:由测试组在系统测试的时候,按照需求说明书写出的测试用例。而单元测试的测试用例几乎就没有。咋整?这不就是TDD解决的问题吗。
对于加入工程组的新模块,开发初期需要执行TDD,测试用例,测试用例覆盖,到什么程度呢,如果是自己写,就写到自己不心虚为止吧,如果有人专门写,那就写到此人自己没有困惑为止吧。
每次Debug的代价
每次Debug,需要启动一次HostProgram,加载所有模块。鼠标点点,键盘按按,才走到自己想要的断点。每次这么搞,都感觉自己傻瓜一样。自动化测试测到哪,我现在的能力只能做没有界面的单元测试。界面测试已经提了需求,等着老大们开讲。
对队友离开的恐惧:
我有时候会突然冒出这样的念头:如果oy,或者xa离开了,我们怎么办?项目会不会因此而停掉。结论是:项目是不会停掉的,因为我们在一个大舰队中,一个小舢板部分水手的离开,老大不会坐视,新的水手稍后就到。但,进一步的进展是举步维艰的。大量没有注释的代码,足以让每一个新成员崩溃。没有注释的代码,是队员变得不可或缺,人是安全的。这只是我以最坏的恶意来揣测不注释代码的作者。或许是赶时间吧,之前几个版本的发布都显得很精彩,不过软件的内部都是一锅卤煮火烧。这种看起来很美,会在用户的手里土崩瓦解。如果是这样的话,我们因此被团队抛弃,都不会再有团队来接纳。在此之前,还是T自己狠一点吧。
posted on 2008-01-12 13:00
创建更好的解决方案 阅读(2212)
评论(26) 编辑 收藏 引用 所属分类:
TDD 、
XP敏捷 、
心路历程 、
闲话连篇 、
软件测试 、
软件设计