1.好与快的关系
几年前和一个朋友聊天时,他抱怨他的上司说,要我写得好又要写快,那怎么可能呢?我当时一愣,反问到,写不好怎么可能写得快?他也一愣。
传统观点认为在功能、成本(人*时间)和质量这个铁三角中,提高质量就意味投入更多成本或者减少一些功能。在功能不变的情况下,不可能在提高质量的同时降低开成成本(对个人来讲就是缩短开发时间)。我的朋友持的正是这种传统观点。
而根据我的经验来看,结论恰恰相反。每次我想以牺牲质量来加快速度的时候,结果反而花了更多时间,甚至可能到最后搞不定而放弃了。有了多次这样的经验之后,我决定把每一步都做好,从开发的前期来看,我花的时间比别人多一点,但从整个任务来看,我反而能以别人几倍的速度完成任务。时间长了,我形成了这样的观念:只有写得好才可能写得快。
两种观点截然相反,所以我们都愣了。虽然我相信我的经验没有错,但传统的铁三角定律是大师们总结出来的,也不可能出错。那是怎么回事呢?我开始到处查资料,但是没有一个人支持我的观点。我又不想这样放弃,后来我用了一个简单的办法进行推理,结果证明两个观点都有各自的适用范围。
这个推理过程很简单,把两种观点推向极端:
先看看以牺牲质量来追求进度的例子。我以前参加过两个大项目,其一个项目的BUG总数达到17000多个,耗时近三年后项目被取消。另一个项目的BUG总数也超过10000个,三年之后带着很多BUG发布了,结果可想而知,产品很快从市场上消失了。这两个项目在开始阶段都制定了极其可笑的项目计划,为了赶在这个根本不可能的最后期限前,都采用了牺牲质量的方式来提高开发速度,前期进展都很“顺利”,基本功能点很快就完成了,但是项目马上陷入了无止境的debug之中,开发人员的士气一下跌到谷底,管理层开始暴跳如雷。
如果这两个项目有超过170000个BUG,即使项目不取消,再做时间十年也做不完。由此可见:质量低到一定限度时,降低质量会延长项目时间,如果质量降到最低,那项目永远也不可能完成。这和我的观点一致:写不好就写不快。
再看看追求完美质量的例子。以前参与一个手机模拟器的开发,我们很快达到88%的真实度,半年之后达到95%的真实度,客户要98%的真实度。但是怎么努力也达不到这个标准,花了极大的代价才达到96%多一点,到后来项目被取消了。
如果要达到99%的真实度,即使项目不取消,再做十年也做不完。由此可见:质量高到一定程度,提高质量会延长项目时间,如果质量要高到最高,那任务远也不可能完成。这和传统观点一致,提高质量就要延长开发时间。
从两个极端往中间走,我们可以找到一个中间质量点。低于这个质量点,想以牺牲质量来赶进度,那只会适得其反,质量越低耗时越长。高于这个质量点,想提高质量就得增加成本,质量越高开发时间越长。这样两种观点就统一起来了。
如果在大多数项目中,这个中间质量点是可以作为高质量接受的,那我们就找到了又快又好的最佳方法。这个质量点到底是多少?呵,我可以告诉你,那是87.5。但是谁知道怎么去度量呢?没有人知道,只能凭感觉和经验了。
2.我们的时间花在哪里
经过这段时间的练习,大多数人都体会到敲代码不是耗费时间最多的地方,一个高效率的程序员,并不是打字比别人快,而他节省了别人浪费了的时间。我常说达到别人五倍的效率并不难,因为在软件开发中,大部分人的大部分时间都浪费掉了,你只要把别人浪费的时间省下来,你的效率就提高上去了。像在优化软件性能时采用的方法一样,要优化程序员的性能,我们要找出性能的瓶颈。也就是弄清楚我们的时间花在哪些地方,然后想办法省掉某些浪费了的时间。根据我的经验,耗费时间最多的地方有:
o 分析
需求分析通常是SPEC工程师(或者所谓的系统分析员)的任务,程序员也会参与到这个过程中,但程序员的任务主要是理解需求,然后分析如何实现它们,这个分析工作也就是软件设计。无论你是在计算机上用设计工具画出正规的软件架构图,还在纸上用自然语言描述出算法的逻辑,甚至在脑海中一闪而过的想法都是设计。设计其实就是打草稿,把你的想法进行推敲,最后得到可行的方案。设计文档只是设计的结果,是设计的表现形式,没有写设计文档,并不代表没有做设计(但是写设计文档可以加深你的思考)。
设计本身是一个思考过程,需要耗费大量时间,对于新手来说更是如此。前面几节中的需求并不难,理解它们只需要很少的时间,但要花不少时间去思考其实现的方法。这个时间因人而异,有的读者到最后也没有想出办法,这没有关系,没有人天生就会的,不会的原因只是因为你暂时还不知道常用的设计方法,甚至连基本数据结构和算法都不熟悉。
在后面的章节中,我们会一步步的深入学习各种常用设计方法,反复练习基本数据和算法,熟能生巧,软件设计也一样,在你什么都不懂的时候,不可能做出好的设计。你要学习各种经典的设计方法,开始可能生搬硬套,多写多练多思考,到后来就随心所欲了,设计的时间就会大大缩短。
o测试
要写得好自然离不开测试,初学者都有这个概念。他们忠实的使用了教科书上讲的方法,用scanf输入数据,做些操作之后,用printf打印来,这是一个完美的输入-处理-输出的过程。测试也就是要保证正确的输入能产生正确的输出,这种方法的原理是没有错的,但它们确实耗费了我们大量时间。
如果测试只需要做一次,这种方法还是可取的,问题是每次修改之后都要重复这个过程,耗费的时间就更多了。这种工作单调乏味,而且很难坚持做下去,单元测试做得不全面,就有更多BUG等着就调试了。时间久了,或者换人维护了,谁也搞不清楚什么样输入产生什么样的输出,结果可能是连测试也省了,那就等着把大量的时间浪费在调试上吧。总而言之,这种测试方法不好,我们需要更有效的测试方法才行。
o调试
测试时发现程序有BUG,自然要用调试器了,对一些人来说,调试是一件充满挑战和乐趣的事。而对大部分人来说,特别是对我这种做过两年专职调试的人来说,调试是件无趣无聊无用的工作。熟练使用调试器是必要的,在分析现有软件时,调试器是非常有用的工具。但在开发新软件时,调试器在浪费我们的时间。
调试器是最后一招,只有迫不得已时才使用。一位敏捷方法的高手说他已经记不得上次使用调试器是什么时候了,我想这就是为什么敏捷方法能够提高开发速度的原因吧。因为没有什么比一次性写好,不用调试器更快的方法了。
知道了浪费时间的地方,接下来几节中,我们将介绍避免浪费时间的方法。学完这些方法之后,我希望读者也能达到普通工程师五倍的效率,呵,读完本系列文章后之,希望你会达到更高。