在阅读了推荐阅读的材料之后,我想了很多东西。最终还是决定,以团队项目的经历为主线,叙述我关于软件工程的一些思考与体会。
凤凰涅槃,浴火重生
如果要我来概况这几周团队项目的经历的话,那么句话是我所能想到的最贴切的一个表述。从最初的雄心壮志,到中间的困顿不堪,再到目前如重生一般的喜悦,我们整个团队经历了太多太多。
重造*
*,在软件行业中经常指那些设计好的,用于处理常见功能的库、框架或者可重用的代码。而重造*则是说,在已经有可用的“*”的情况下,自己重新实现一个自己的“*”。有些人经常说,重造*是一种很傻的行为。已经有现成的代码可以解决问题,可以工作,我们为什么要重造*呢?也有人认为,重造*往往会引发一场进步。比如在已有的库的设计无法满足实际需求时,重造*则可能产生更为先进而高质量的*。因此重造*是值得鼓励的。
在我们的项目开始之初,作为团队中的架构师,我想了很久。我们拥有往届留下的完整的代码。稍微做些修补和部署的工作,那些代码就可以实现很多功能。虽然界面很丑陋,部分功能也恐怕存在诸多问题,但毕竟是八千行代码,几年的课程积累下来的东西。采用它们,我们的工作能够轻松很多。但这样的轻松也存在问题:代码遗留bug是肯定存在的,而且据测试的小伙伴说,问题还不小。上一届留下来的文档也提到,他们发现再上一级很多功能没有实现,空有一些接口等等,调了很久。文档不齐,原有开发者又都不在项目中,采用的技术也相对老套。且这套代码经历过多年的开发,缝缝补补修修整整,就像阅读材料中所说的大泥球,谁都很难说现在代码的状态是什么样子。在这样的情况下,我们是否应当重造*,将其推翻重来?
虽然直到现在,我依旧无法衡量,当时的选择正确与否。但经历了重造*的过程之后,我对于造*有了更深的一些想法。造*最痛苦的一点在于:我们需要重新实现原有的功能,而无法依托于哪些东西。所以,造*的代价之高昂,是显而易见的。现在看来,我们对于这一点的认识显得过于稚嫩。凭着一股莫名地自信,我们就认为自己能用两千行python能实现原有的8000行代码的功能,现在回想起来,确实感觉有些太过自负了。但彻底的重写也带来了意料之外的好处,我们整个采用了时下较新的技术,以较低的成本实现了很多功能,整个框架无论是实现新功能,还是重新实现原有功能,都很容易。一些看上去很复杂的东西,通过使用现代化的框架就可以轻易解决。我们的系统目前大量地使用了分布式的技术,可以通过增加节点来提升性能。这一点是通过重构原有的代码很难实现的。这些新鲜的技术也带来了很多新鲜的设计和鲜活的动力。我们所采用的框架,都蕴含了很多现代化的设计思想。这些思想也透过这些框架,渗透到了我们的设计中。我们遵循它们所提供的一些设计原则,理解它们的很多设计理念,并试图去应用这些设计哲学。在这个过程中,我认为我们整个项目的整体感觉要比原有项目好得多。团队成员也都很有干劲,每当实现了一个新的功能,或者采用了某些新鲜的东西,都会拍手高呼。由于是全新开始的项目,没有遗留代码的历史负担,我们可以尽情地使用我们所想用的工具,而不必在意历史包袱。
总结起来,我认为,重造*的好处在于没有历史包袱、思路更为大胆开拓、可以给一些项目带来革命性的变化。而采用原有系统,则可以“站得高,望得远”。这二者的权衡,是一件需要更为细致周到地去思考的事,轻易下一个结论,则会招惹意料之外的麻烦。正如我们目前的状态,从最初选择放弃旧有代码,自己重造*,到现在,方才认识到,当初的决定影响到底有多么深远,需要考虑的问题又有多么复杂。也许有些事,经历过才会才知道深浅。如何权衡利弊,如何评估方案,也许下一次再做得时候,我们会成熟很多,并且慎重很多。
团队管理及开发方法
软件工程没有“银弹”。我十分认同这一观点。从之前的结对编程,到目前的敏捷开发。所有的方法都有它的应用范围。比如结对编程对于结对双方的性格、关系等等都有一定的要求。两个人一见面就掐的那种,还是不结对效率来得高。而我们目前所采用的敏捷开发,对于任务的分解、分配,人员的协调,角色之间的关系,都有着很强的要求。而这对于一个团队来说,不见得是很容易达到的。比如PM和程序员之间,就是位置很纠结的一组关系。按理来说,PM负责协调工作、项目宏观方向、跟踪进度等等,但如果程序员相对比较强势,那么PM的工作往往难以进行。PM把握不了项目的基本走向,而程序员们毫无组织地松散开发着。除了PM和程序员外,测试、UI等各个角色之间的关系,都可能成为整个项目的瓶颈。
阅读材料里所说,“有人负责,才有质量”。经历过一轮团队项目的开发后,我非常认同这一观点。而且,我认为,这里的负责有两层含义:一、有一个特定的人负责处理这件事。二、这个人有能力处理好这件事。第一层相对是显而易见的,一个任务必须指派给某一个固定的人,否则,这个任务若是没人认领,自然很难期盼它被良好地完成。就像一棵无人搭理的树苗,虽然最后也能长起来,但是相比起被园林工人精心呵护的树苗来说,其形态样貌等都会有一些程度的差距。第二层则是经历过团队项目后,我所得出的一个理解。所谓负责,并不是说指派给某人,某人就能负责。这个人必须有能力负责才行。例如,你指派一个小学生去进行[完成高等数学习题]这样一个任务。虽然指定了特定的人去负责处理,但是,显而易见地是,被指派这根本没能力对这件事负责。即使他最终做出来,做的不好再打回去一遍遍改,仍然很难改好。这也就是有能力负责的意义所在。
我们团队所面临的最惨痛的一次教训也正是源于此。我们虽然按照要求,每天都开"每日立会",有时遇到问题,几个人也经常串宿舍讨论,但却没有明确地指定负责人。于是,手里有一波又一波松散的记录、不正式的文字,却一直未能整合到一起。专门负责维护的同学又是对于内容十分较真的人,质量不够绝不会往出发。结果,每日立会相关的文档就一直拖了下来,造成了很严重的后果。现在想来,如果当初能够指定一个特定的人,有足够能力的人来专门负责这件事,也许最后就不会出现这样的局面。我们一直采用集市式的方式进行我们的团队项目,大多数任务几乎都是自己主动要来的。团队内部的大部分人都很积极,任务也大多数都有明确的人来真正去负责。所以大体上十分顺利。但是,集市式方式所面临的问题,我们自然也不可避免的遇到了。而且,我们甚至没能及时注意到这成为了一个问题。大教堂式的开发是设计好了一切,按部就班地完成开发。而集市式则更像一个闹哄哄的集市,每个人选择其自己感兴趣的任务去进行。我们顺利地采用集市式的模式完成了几份高质量的作品。一个人初稿、一个人复审迭代更新、一个人再度审核排版等等,每个人都出于个人的责任、荣誉以及兴趣等复杂情感,深入地参与到了整个过程中。虽然没有非常明确的安排,但是大家都主动承担了某一部分的任务。我们用集市搭建起了一座教堂。然而,这种搭建是有条件的,正如《大教堂与集市》的作者所总结得那样:
1)项目首先必须是你自己感兴趣的,但是最终能对其他人有用。
2)将用户当作合作者。
3)尽快地和经常地做出改进,多听取用户的意见。
4)健壮的结构远比精巧的设计来得重要。换句话说,结构是第一位的,功能是第二位的。
5)保持项目的简单性。设计达到完美的时候,不是无法再增加东西了,而是无法再减少东西了。
之前的成功使得我们没能认清楚上面所叙述的一些本质性的东西,从而导致了后面的问题。我认为其中最主要的是第一条:项目必须首先是你感兴趣的,最终对其他人有用的。之前构筑的一系列文档,源自团队中对于文字有执着追求的青年的努力,也源自团队自身开发设计的需求,因此,大家参与得较为深入。特别是一些每个成员都能够用到的文档,比如设计文档、需求分析等等。然而,我们没有认识到'每日立会'的会议记录的作用,因而也没有人对其感兴趣。大家都觉得,这个东西似乎没那么重要,没有给予太多的重视。现在回想起来,我觉得这确实是违背了上面的第一条条件:项目必须首先是自己感兴趣的。没有人愿意关注的东西,自然难以采用集市式的方式进行。于是,在这一次,集市没有起到任何作用,我们一直所倚靠的集市式成了低质量的罪魁祸首。
在经历了这样许多之后,我认为,我们依旧需要坚持我们所一直依赖的集市式模式,它是我们成功的基石。但在此之上,我们必须另想办法解决那些不适用于它的情景。在我看来,一个适合我们团队情况的开发模式是这样的:对于每一个任务或者需求,首先应当尽量采用集市式的思路去完成它。但同时,我们应当设置一个警戒时间。如果超过这个时间依旧没有任何起色,那么就说明集市模式已经不再适用于这个任务或需求了。我们应当综合各方面意见,选定一个特定的,有足够能力的且适合完成这个任务的人。让他为此事负责,这样才能产生足够的质量。从而避免集市式模式失效后直接崩盘的惨痛结果。
结语
几周的团队项目开发,我们从高潮到低谷,再重新一步一步向上爬。跌宕起伏的过程,让我们更充分地意识到了哪些是对的,又有哪些是存在问题的。我们团队的前路依然艰险,但我认为,我们已经摸索到了正确的模式。在下一轮开发中,我们能够应用这些经验与感悟,小步快跑,达到更高的一个层次上。