第3章 复用现有的方法及做法
笔者在前言中说过,BDUF与敏捷设计是有些冲突的。那么情境驱动设计是不是BDUF设计的一个变种呢?或者说,它是不是也具备一点敏捷开发的气息呢?本章的一项目标,就是要摆正情境驱动设计与其他现有设计方法之间的相对位置。笔者并不打算详细描述敏捷方法及BDUF方法(假如要详细描述它们,那还得再写几本书),但是会讨论这些方法背后的概念。此外,本章还有一项目标,就是要解释现有的这些方法之中,有哪些地方可以吸收到情境驱动设计里面,但愿咱们都能找到很多个可供借鉴之处。
笔者知道,看这本书的人,有很多都是成功的IT应用程序开发者,你们都对可行和不可行的工作方法有着强烈的见解。如果我能说服你采用情境驱动设计,那是自然是最好的,但如果不能,我也希望这些内容可以促使你思考自己正在做的事情,思考一下为什么某些做法可行,而另一些做法行不通。
本章要讲述的话题有:
- 敏捷。
- 逆向设计。
- 用例。
- 成本估算问题。
- BDUF方法为什么如此庞大?
- 迭代。
- 品质。
- 测试与检验。
讲完上述内容后,笔者要讨论怎样在情境驱动设计之中使用现有的一些做法,并且要提出学习型组织(learning organization)这一说法。
3.1 敏捷
敏捷运动是由一些志趣相投的专家促发的,他们聚在一起写了一份宣言,叫做《敏捷软件开发宣言》[12]。该宣言所主张的价值是:
- 个体与交互胜过流程与工具。
- 可行的软件胜过繁杂的文档。
- 客户协作胜过合同谈判。
- 响应变化胜过遵循计划。
除了上述价值观,他们还提出12条原则:
1.我们的当务之急,是尽快把有价值的软件交给客户,并持续进行交付。
2.我们欢迎客户修改需求,即便在开发阶段行将结束时也依然如此。敏捷流程会利用需求的变化来提升客户的竞争优势。
3.频繁地交付可以运作的软件,几星期或几个月交付一次都行,间隔的时间越短越好。
4.做项目的时候,业务人员与开发者必须始终在一起工作。
5.找一些态度积极的人来做项目。给他们提供适当的环境与支持,并对其给予信任,令其能够完成工作。
6.无论是外界与开发团队之间沟通,还是开发团队内部沟通,其最有效的信息传达方式,都是面谈。
7.衡量进度的首要指标,就是有没有做出可以运作的软件。
8.敏捷流程提倡可持续的开发。出资方、开发者与用户,都应该一直保持着稳定的步伐。
9.持续提升技术水平与设计水平,以增强敏捷技能。
10.力求简洁,尽量避免做无用功。
11.最好的架构、需求与设计,来自那种能够自我组织的团队。
12.每隔一段时间,团队就应该反思一下怎样才能变得更有效率,然后据此来优化并调整其行为。
要想挑毛病,还是很容易的,我们可以从“什么才是原则?”以及“价值又是什么?”等问题入手。例如,我更喜欢与人沟通,而不太喜欢填各种表格,那这是不是意味着我赞同第一条价值?笔者不想在这里咬文嚼字,而是想要探究这些声明背后的意思,想要理解宣言作者所真正关心的事情,并且想指出:我们应该如何用情境驱动设计来处理同样的事情。
3.1.1 个体与交互胜过流程与工具
实际上笔者觉得,第一条价值观想表达的意思,应该是“抛弃毫无必要的陈规”,如果这样说,那我想没有人会反对这条价值吧?然而我们必须要问问自己,应用程序的开发工作为什么总是陷入这些繁杂的规矩之中。笔者觉得这还得从管理者的恐惧开始说起。
管理者担心的问题有很多:
- 他们担心有些东西会遭到遗忘,例如,项目团队可能会把某项需求给忘了。
- 他们担心设计者与程序员的能力不够强。
- 他们担心项目会在自己不知情的时候犯错。
为了缓解上述忧虑,他们会采用这样一些办法:
- 提出各种清单与流程,以确保清单中的每个条目都能得到应对,这些条目会分成必要和不必要两类,前一类会得到处理。
- 拟定一些能够汇报项目进度的流程。
笔者认为,敏捷软件开发者对这些忧虑的回应,有两层意思。第一层意思是说,敏捷开发者会尽早展示项目成果,并且会频繁地进行展示。第1条、第2条和第3条原则,都体现了这个意思。其第二层意思,是请求大家给他们以信任。从这个角度来看,第5条、第9条及第11条原则,都是很重要的。
而我自己对这些忧虑的回应则是:如果担心项目有所遗漏,那么请对设计进行分析,这样可以在某种程度上确保项目之中不会漏掉一些内容。此外,由于有了情境设计与用户界面设计,因此利益相关者应该能对正在制作的这款软件有一个整体的了解。这与敏捷项目形成了对照。开始做敏捷项目的时候,我们相当于是在探险,我们要么对项目的目标没有清晰的概念,要么根本不知道项目的目标在哪里。
我知道,看完上面那段话之后,肯定会有人跳出来说:“早就告诉你该用瀑布式的设计了嘛!”笔者对瀑布式设计的主要意见,并不在于它预先做了设计,而是在于这种设计做得不够好,也就是说,瀑布式的设计团队只是在单纯地收集需求,而没有对需求进行设计。此外,由于他们没有把依赖关系记录下来,因此也不能够很好地应对变化。传统的瀑布式设计还有一个问题,就是缺乏迭代。笔者认为,只要在开始迭代之前,能够对项目有一个完整的概念,那么迭代起来就是没有问题的。本章稍后的3.6节会详细讨论迭代。
我们现在回到管理者所担忧的问题上面。管理者在看到开发者所交付的产品之后,固然会对实现团队比较有信心,但是按照笔者所提出的理念:如果管理者有了情境设计与用户界面设计可供比对,那么在对实现出来的方案进行评估时,则会容易得多。软件开发之中似乎也出现了80∶20现象,也就是说,应用程序看上去好像已经实现了80%,但实际上才只完成了20%。之所以会出现这种情况,其中一部分原因在于:我们通常会编写很多复杂的代码,来处理早前所提到的那些错误与异常状况,这使得项目看上去好像已经有了很大的进展。实际上,采用情境驱动设计的办法来做项目,可以使我们更好地理解项目当前的处境。若是采用敏捷开发的方法来做项目,那么利益相关者有可能在看到一个实际完成度仅有20%的产品时,就直接得出结论,认为这个应用程序马上就要做好了。其后,开发者和利益相关者会在项目的进展过程中,更深入地了解到应用程序所要解决的问题,进而会提出一些新的需求,他们提出需求的速度可能比项目的实现速度还要快。一旦出现这种情况,双方之间的信任关系就变得紧张了。
但是请注意,双方之间的信任依然很重要,因为公司毕竟得依靠专业的开发团队来做项目,笔者认为这是绕不过去的。
总之,管理者对某些问题的担忧是有道理的,而敏捷开发者想要抛弃陈规,这也是正确的,但是频繁地交付软件产品,最多只能够在一定程度上缓解管理者所担忧的问题。
3.1.2 可行的软件胜过繁杂的文档
文档有很多用途:
- 它可以把软件的工作原理告诉终端用户。
- 它可以促使开发项目之中各个团队借此进行交流。
- 它可以促进开发者与利益相关者之间的沟通。
- 它可以帮助设计者仔细思考某个问题。
一款可以运作的软件,能够代替上述所有内容或其中的某一项吗?
许多人从来不看软件文档,而是通过直接试用该软件来学习其用法。这显然是软件文档写得很差而导致的。很多软件文档都在反复地陈述一些显而易见的事实,它们把读者当成了愚蠢的机器人,很少有文档能够讲清楚软件试图解决的问题、软件的工作原理以及软件所遵循的原则(例如,只要用鼠标右击屏幕上的某个项目,就能找到所有常见的命令)。甚至有一些知名的软件商似乎也懒得去认真撰写文档,它们的软件文档里没有提供充足的图表。尽管很多软件文档写得都不够好,但是复杂的软件依然需要有一定的解释,这就好比你不能通过在键盘上胡乱敲击来学习一门编程语言。
这一次笔者又想说:《敏捷软件开发宣言》的作者所要讨论的,实际上可能不是文档有没有用的问题。它们真正反对的,应该是那种冗长的程序设计文档,而不是终端用户文档(end user documentation)。
从前,有很多种技术都用来在写代码之前描述程序的逻辑。如流程图、层次化的输入—处理—输出(hierarchical input-process-output,HIPO)图、层次化的结构图、类图、对象图以及状态转移图(state transition diagram)等。除了个别情况,笔者几乎不会使用这些图表。这样做的原因有好几个。其中一个原因是缩放级别的问题。例如,要为房屋绘制建筑图纸。我们想要在一张纸上以1∶50至1∶100的比例来展示房屋的某一面。对于更为庞大的建筑来说,建筑师可能会以1∶1000的比例来制图。编程图也想要在一定的细节程度上展示程序之中的某一方面,可是,这种图的缩放比例距离实际的代码很近(我们可以说它是1∶4或1∶10),而且基本上没有放大或缩小的功能。于是,程序文档就会变得特别庞大,使人很难从中理出头绪。这种图本身所关注的,仅仅是程序之中的某一个方面,如程序逻辑、数据结构或调用时机等,它们不能够展示完整的设计。程序设计里面的很多缺陷,都潜藏在各种视图的交互之中。笔者与很多程序员一样,也感觉这些图不太有用,我们还是直接看代码比较好一些。
只有在个别情况下,笔者才觉得需要有详细的编程设计文档:
- 技术设计团队需要把他们的设计方案解释给实现者听(在解释的过程之中,最有价值的部分,通常还是某些范例代码)。
- 我们要对一段极其复杂的代码做设计,因此在开始编程之前,想要先看看实现之中的某些方面。此处的重点在于:这种编程图表,以后是应该丢掉的。
就笔者的个人经验来看:一旦开始写代码,这些编程图表就显得过时了,我不知道这种问题是不是很普遍,但我自己经常在写代码的时候,发现早前所绘制的编程图表有缺陷,某些方案呈现在纸面上的时候,看起来似乎很好,但等落实到代码之中,才发现并非如此。
情境驱动设计依然需要一些文档。除了可以运行的代码及数据库的schema,这种设计所需的主要文档有:
- 情境设计文档。
- 集成设计文档。
- 用户界面文档。
- 内部所用的一些展示文稿,例如,在评审期所提交的展示文稿,以及用来指导程序员进行实现工作的展示文稿等。
- 项目管理计划。
笔者并不觉得上面这些文档很多,它们的数量和程序的源代码相比,少了好几个量级。
3.1.3 客户协作胜过合同谈判
从某种程度上来说,客户协作当然比合同谈判重要,因为很多项目都是在没有正规合约的情况下运作的,例如,由企业内部的IT开发者来实现的项目,大多数就属于此类。
然而就合同谈判来说,我们要么必须进行合同谈判,要么根本不需要进行合同谈判。在必须进行的场合,没有其他方式能够取代它。
此外,有些项目不太可能进行客户协作,至少在项目即将结束之前,是没办法与客户相互协作的。笔者的这个说法,对于那些拿来卖钱的项目来说,基本上是成立的,因为我们必须去猜测潜在的用户对这款产品会做何反应。由于开发者无法参与实际的客户协作,因此会由营销经理这样的人作为代表,来帮助开发者去猜测用户的想法。
敏捷开发者所要求的协作程度,比笔者刚才讲的要深,这可以从第4条原则之中看出来,而且第6条原则在某种程度上,也隐含了这个意思。他们要求的是那种相当紧密的协作。这就使人感觉,敏捷开发者并不认为文档之中所记录的需求规范能够奏效,他们认为字面上的合约所具备的意义是较为有限的,必须要利益相关者在协作的过程中,亲口把他们的需求说出来。笔者觉得事实并非如此,这个问题将在稍后的3.4节之中讨论。
之所以要重视合约,其中一项原因在于,业务管理者必须据此来估算成本。对于IT应用程序的开发来说,无法精确地估算成本,是一个很严重的问题,它必须得到解决。
3.1.4 响应变化胜过遵循计划
这条价值所蕴涵的意思是:在非敏捷的项目中,计划会对响应变化造成阻碍。笔者觉得这么说不太公允,有些非敏捷的项目确实不太愿意修改计划,或者是想对修改计划提出更高的条件,但他们依然是会改变计划的。
前面提到过一句名言,当时我说,尽管要面对混乱的战争,但军队依然喜欢制订计划。之所以这样,其基本原因在于:他们遇到事件之后,并不会完全抛弃计划,而是会对此做出修改。
Scrum[13]和极限编程(Extreme Programming)[14]等敏捷方法,宣称自己具备一项强大的优势,那就是可以应对项目后期出现的需求,或是应对所谓的需求波动。笔者觉得,情境驱动设计的一项主要优点,在于它可以使项目后期尽量少产生一些需求变动,因为它具备坚实的设计流程,而且还可以用清晰的指令来告诉程序员应该怎样编程,这使得实现阶段所花的时间会少一些。
我们在项目开发的后期,依然有可能要改变需求,那么,情境驱动设计应该怎样应对这种局面呢?其实很简单。面对需求变更,首先要做的,就是确定这项变更是装饰性的变更,还是根本性的变更。修改需求,通常导致我们必须修改用户界面,因此,用户界面的设计,就是个很好的切入点。由此,我们可以发现需要修改的画面,进而确定需要重写的代码。如果这项变更是较为根本的变更,那我们可能还需要重新检视情境设计。无论哪种情况,我们都可以对设计方案做出修改,并迅速找到需要修改的代码。情境驱动设计与其他方法相比,在对变更的管理方面,有一项巨大的优势,那就是可以指出任务之间的依赖关系,这使得我们能够明白某一处代码发生变化之后,还有其他哪些任务会受到影响。我们可以告诉用户,“如果修改了X,那么会影响Y和Z”,而且我们还可以把设计方案拿给用户看,以验证自己刚才的说法。总之,情境驱动设计使得我们可以就所要变更的内容,明智地展开一场以业务为导向的讨论。如果采用敏捷开发方式,那么当程序员A修改完某段代码之后,程序员B和C可能并不知道自己四周之前所写的代码其实是依赖于A的。因此,在项目规模比较小的时候,敏捷开发方法的效果是比较好的,因为项目里面可能至少有一个人能够完全了解其他人已经完成和正在制作的内容,换句话说,这个人必须全面地了解整个设计方案。但如果项目的规模较大,例如,有100个程序员,那么我们很难要求有谁能够具备这种能力。
并非只有笔者一个人持这种看法,Alistair Cockburn也是如此。他是用例方面的专家,也参加了《敏捷软件开发宣言》的起草会议。Cockburn认为,应该以用例来补充Scrum等敏捷方法[15],我没有理由反对这一点。
3.1.5 小结
笔者现在把自己对敏捷开发的想法总结一下。敏捷开发有两项创见:
- 信任程序员。
- 等用到的时候再去设计(Just-in-time design)。
笔者很赞同第一项,但强烈反对第二项。