敏捷开发技巧:以用户故事管理项目
 

2010-03-18 来源:网络

 
摘要:

一件用户通过系统完成他一个有价值的目标(买一罐饮料)的事。这样的过程就叫“用户案例(user case)”或者“用户故事(user story)”。本文描述了敏捷开发的技巧:如何以用户故事管理项目.

什么是用户故事(user story)

假定这个项目的客户是个饮料自动售货机的制造商。他们要求我们为他们的售货机开发一款软件。我们可以找他们的市场经理了解这个软件的需求。

因此,我们的客户就是他们的市场经理。谈需求的时候,有一回他这样说:“用户往售货机每塞一个硬币,售货机都要显示当前该客户已经投了多少钱。当用户投的钱够买某一款饮料时,代表这款饮料的按钮的灯就会亮。如果那个用户按了这个按钮,售货机就放一罐饮料到出口,然后找零钱给他。”

上面的话描述的是一件事情,一件用户通过系统完成他一个有价值的目标(买一罐饮料)的事。这样的过程就叫“用户案例(user case)”或者“用户故事(user story)”。也就是说,上面我们的客户所说的话,就是在描述一个用户故事(user story)。
(我解释一下为什么用故事这个词,没兴趣也可以忽略。在一个系统面前,每个用户要完成同样的目标,都要做这个系统设定的例行的事,这件事情不是一个例子,所以不叫事例,这也不是故事,也不能算一段历程,而是一个例行的事。)

如果我们想要记下这段用户故事,我们可能会用这样的格式:

名称:卖饮料

事件:

1. 用户投入一些钱。

2. 售货机显示用户已经投了多少钱。

3. 如果投入的钱足够买某种饮料,这种饮料对应的按钮的灯就会亮。

4. 用户按了某个亮了的按钮。

5. 售货机卖出一罐饮料给他。

6. 售货机找零钱给他。

注意到,一个用户故事里面的事件可以这样描述:

1. 用户做XX。

2. 系统做YY。

3. 用户做ZZ。

4. 系统做TT。

5.  ...

用户故事只是描述系统的外在行为

一个用户故事只是以客户能够明白的方式,描述了一个系统的外在行为,它完全忽略了系统的内部动作。比如,下面有下划线的那些文字,就属于不应该出现在用户故事中的系统内部动作:

1. 用户投入一些钱。

2. 售货机将塞进来的钱存在钱箱里,然后发送一条命令给屏幕,屏幕显示目前已经投入的金额。

3. 售货机查询数据库里面所有饮料的价格,判定钱足够买哪些饮料,对于钱足够买的那些饮料,对应的按钮的灯就会亮起来。

4. 用户按下一个亮起来的按钮。

5. 售货机卖出一罐饮料给用户,然后将数据库里面该饮料的存货数量减1。

6. 售货机找零钱给用户。

不管是口头描述的,还是书面形式,这样的内容是描述用户故事时一个很常见的错误。特别的,千万不要提及任何有关数据库,记录,字段之类的对客户一点意义都没有的东西。

评估发布时间

用户故事是用来干嘛的?假定客户希望在50天内递交这个系统。我们做得了吗?为了解答这个问题,我们就要在项目开始的阶段,试着找出所有的用户故事,然后评估一下,每一项历程需要多长的开发时间。可是,怎么评估呢?
比如,我们现在收集了下面这些用户故事:

卖饮料:如上面所说的。
取消购买:在投入了一些钱后,用户可以取消购买。
输入管理密码:授权的人可以输入管理密码,然后增加存货,设定价格,拿走里面的钱等等。
补充饮料:授权的人可以在输入管理密码后增加存货。
取出钱箱里的钱:授权的人在输入管理密码后,可以取出钱箱里的钱箱里面的钱。
安全警报:有些事情经常发生的话,系统会自动打开安全警报。
打印月销售报表:授权的人可以打印出月销售报表。

然后找出里面最简单的用户故事(这里的“简单”,意思是说实现周期最短)。我们不一定非常精准的判断哪个最简单。只要挑出你觉得最简单的就行了。比如,我们觉得“输入管理密码”是最简单的用户故事。然后我们判断说,这个用户故事算1个“故事点(story point)”。
                        
用户故事          故事点
卖饮料        
取消购买        
输入管理密码   1
补充饮料        
取出钱箱里的钱        
安全警报        
打印月销售报表        

不过一般我们不会列出清单,而是做出一堆卡片贴在墙上,每张卡片记录一个用户故事,然后将故事点写在卡片上面:

敏捷开发技巧:以用户故事管理项目

这样的一张卡片就叫“故事卡(story card)”。
然后开始考虑其他用户故事。比如,对于“取出钱箱里的钱”这个故事,我们认为它跟“输入管理密码”这个故事一样简单,所以它应该也是算1个故事点。我们在列表里面标上。当然,实际操作的时候,我们是在“取出钱箱里的钱”的故事卡上填上故事点。
用户故事            故事点
卖饮料        
取消购买        
输入管理密码     1
补充饮料        
取出钱箱里的钱   1
安全警报        
打印月销售报表        

对于“取消购买”,我们认为它应该是“取出钱箱里的钱”的两倍的工作量,所以它算2个故事点。
用户故事           故事点
卖饮料        
取消购买             2
输入管理密码    1
补充饮料        
取出钱箱里的钱  1
安全警报        
打印月销售报表        

对于“卖饮料”,我们认为它应该是“取消购买”两倍的复杂度,所以它应该算4个故事点。
用户故事           故事点
卖饮料            4
取消购买            2
输入管理密码   1
补充饮料        
取出钱箱里的钱 1
安全警报        
打印月销售报表        

对于“补充饮料”,我们又认为它比“取出钱箱里的钱”复杂,但又比“卖饮料”简单,然后它又应该比“取消购买(2个故事点)”复杂,所以我们认为它应该是3个故事点:
用户故事          故事点
卖饮料            4
取消购买            2
输入管理密码   1
补充饮料            3
取出钱箱里的钱 1
安全警报        
打印月销售报表
        
类似的,我们认为“安全警报”应该比“补充饮料”简单一些,所以应该是2个故事点:
用户故事        故事点
卖饮料        4
取消购买        2
输入管理密码   1
补充饮料        3
取出钱箱里的钱        1
安全警报        2
打印月销售报表        

“打印月销售报表”应该跟“卖饮料”一样复杂,所以应该是算4个故事点,这样的话,我们总共有17个故事点:
用户故事        故事点
卖饮料        4
取消购买        2
输入管理密码  1
补充饮料        3
取出钱箱里的钱        1
安全警报        2
打印月销售报表        4
总计        17

现在挑出任意一个用户故事,估计一下它要花(你一个人)多少时间来完成。假设我们之前做过跟“取出钱箱里的钱”类似的功能,所以我们就挑这个来计算,估计它要花5天的时间。也就是说,一个故事点要花5天的时间来完成。现在我们有17个故事点,也就是说,我们需要17×5=85天来完成这个项目(如果只是一个开发人员来做的话)。假设现在我们的团队里面有两个开发人员,所以我们就需要85÷2=43天来完成。

从目前的估计来看,我们可以在50天的期限里面做完这个项目。但现在说这个还太早了!这样的评估,更多的是猜测。通常开发人员在工作量上的估算都很差。事实上,人们经常会低估了工作量。那我们应该怎么估计得更准?

我们实际的开发速度是多少

为了做到更准的估计,我们就让客户先给我们两周的时间做一些实际的开发,来测量一下我们在这两周里面可以做多少的用户故事。我们叫这两周的时间为“迭代周期”。
 
哪些用户故事应该放在第一个迭代周期里面做?这可能完全是客户决定的,也可能是大家讨论以后决定的。不过挑出的这些用户故事的故事点不应该超过这两周的承受能力。因为一个迭代周期有10天(假设我们周末不工作),然后我们估计一个开发人员5天可以完成一个故事点。现在我们有两个开发人员,所以我们应该可以在这个迭代周期中完成(10÷5)×2=4个故事点。

然后客户可以在总故事点不超过4的前提下,挑出一些用户故事在这个迭代周期里实现。他们可能会尽量挑选他们觉得最重要的故事。比如,对他们来说,销售报表跟找零钱最重要,所以他们就挑出这两个:
用户故事        故事点
取出钱箱里的钱        1
打印月销售报表        2
总计        3

假设两周的迭代周期过去了,我们完成了“取出钱箱里的钱”,不过“打印月销售报表”没有完成,还剩0.5个故事点没有完成。也就是说,我们在这个迭代周期内完成了3-0.5=2.5个故事点(比原来的预计要差一些)。

2.5个故事点这样的数值,就是我们现在的参考值。也就是说,这个团队每个迭代周期可以完成2.5个故事点。这个参考值对我们很有用。它有两个用处:
首先,我们可以假定我们在下个迭代周期也可以完成2.5个故事点,然后客户选择的用户故事的故事点总和不能超过2.5。
其次,在从第一个迭代周期取得参考值以后,我们可以重新估算我们的发布时间了。本来我们估算我们每个开发人员完成1个故事点的时间是5天,现在我们有了实践后的数据了,我们的团队(两个开发人员)可以在一个迭代周期(10天)内完成2.5个故事点。现在总的故事点是17,计算一下,我们需要17÷2.5=7个迭代周期来完成。14周,也就是70天。也就是说,我们满足不了客户提出的期限(50天内)!怎么办?

预计不能如期完成时怎么办?

很明显,现在我们完成不了全部的用户故事。在这50天里面,我们只能完成50÷10×2.5=12.5个用户故事。因为现在有17个故事点,我们应该让客户挑出总计4.5个故事点的用户故事,推迟到下一个发布周期去。客户应该选择那些比较次要的用户故事。比如,客户可以推迟“打印月销售报表”这个用户故事。

(这只是开发不能如期完成时的解决方法之一,这种方法应该是在客户比较有诚意合作的前提下使用。)
用户故事        故事点
卖饮料        4
取消购买        2
输入管理密码                                                        1
补充饮料        3
取出钱箱里的钱        1
安全警报        2
总计        13

然后他还要挑出总和至少为0.5个故事点的故事。

但是如果“月销售报表”这个用户故事对客户来说也是非常重要,而且其他的故事也不能推迟,这时怎么办?这时我们就要试着简化这些用户故事。比如,原来“月销售报表”是要用一个第三方报表库来实现的,而且还要画出饼状统计图,如果我们只是生成简单的文本格式的报表的话(这格式应该可以被Excel导入,以便日后的处理)。那么这个用户故事的故事点就会从4减少到2,节省了2个故事点。如果客户同意的话,我们就可以将“打印月销售报表”分成两个用户故事“生成月销售文本报表”和“生成图形报表”,然后将后者推迟到下一个发布。

现在新的故事卡片出来了:
用户故事        故事点
卖饮料        4
取消购买        2
输入管理密码     1
补充饮料        3
取出钱箱里的钱        1
安全警报        2
打印月销售文本报表        2
总计        15

还有其他的2.5个故事点要推迟,我们可以简化“卖饮料”。假设本来我们可以卖不同价格不同类别的饮料。如果现在我们只是简单支持一种价格一种类别的饮料的话,那这个用户故事的故事点可以从4减到2了。客户如果同意的话,我们就可以将“卖饮料”分割为“卖单一饮料”和“卖多种饮料”,然后将后者推迟到下个周期发布:
用户故事        故事点
卖单一饮料        2
取消购买        2
输入管理密码    1
补充饮料        3
取出钱箱里的钱        1
安全警报        2
打印月销售文本报表        2
总计        13

现在还剩0.5个故事点,我们再考虑一下“安全警报”。假设本来这个故事是要同时触发本机上的警报,跟通知附近的一个警察局的。如果现在我们只是触发本机的警报,那所花的故事点就可以从2减到1了。于是在客户同意的情况下,我们将“安全警报”分割为“本机安全警报”和“通知警察”,然后将后者推迟到下个发布:
用户故事        故事点
卖单一饮料        2
取消购买        2
输入管理密码         1
补充饮料        3
取出钱箱里的钱        1
本机安全警报        1
打印月销售文本报表        2
总计        12

现在我们总计有12个故事点要做了(<=12.5)。上面这个筛选在本次发布的用户故事的过程,叫“发布计划编制”。

增加开发人员来满足发布期限

在上面的例子中,我们以推迟部分用户故事到下个发布周期的办法来解决问题。这种“控制开发范围”通常是最好的解决办法。不过,这种解决办法实施不了的情况下,那你就只好保留所有的用户故事,然后增加更多的开发人员了。在这个例子中,假定我们需要“n”个开发人员,才能在50天内完成17个故事点。50÷10×2.5×n÷2.算出来,n=2.7,我们需要3个开发人员,也就是多加一个开发人员进来。不过注意:
团队人数加倍并不等于开发周期的减半。它可能只会缩短1/3。如果团队超过10个人的话,增加更多的人员可能反而会延缓项目的进度。
而且项目开发周期越长,团队内的成员对整个项目代码的熟悉度就越少,加上不确定的人员流动,新来人员的业务不熟等其他可能性,这项目会越来越复杂。
总的意思就是,项目人数不能太多,周期不能太长。

根据参考值来掌控项目

每个迭代周期2.5个故事点的这个参考值,只是第一个迭代周期的数据,第二个迭代周期可能会变成2或者3(一般是不会变动得太大)。假设是2的情况下,那对于第三个迭代周期,我们就要将参考值设为2,然后让客户以2为故事点总数来挑选用户故事。
对于大多数项目,参考值很快就会稳定下来(比如在几个迭代周期后)。当这个值稳定下来后,我们就要重新估计开发周期,重新进行“发布计划编制”了。如果这个参考值告诉我们,我们每个迭代周期可以做3个故事点的话,我们就要让客户挑选更多的用户故事放在这次的发布计划中。相反如果这个参考值是2的话,我们就要让客户减少用户故事(需要的话可以分割一些用户故事),如果团队人员还不多的话,可以增加更多的开发人员。

这是项目的初始阶段绝对要注意的。

发布计划编制,估算每个用户故事时要考虑哪些细节,忽略哪些细节?

在项目初始,我们要找出这个发布周期内所有主要的用户故事,评估每个故事的故事点。可是要怎么评估里面的细节呢?比如对于“卖饮料”, “卖饮料”这个简单的标题,省略了很多的细节:用户会投入什么样的钱?纸币可以吗?人民币可以吗?按钮的灯的亮度要多少?可不可以多个按钮对应一种饮料?按钮被按下以后,要不要变暗?找零钱是不是全部找10分的面额?
我们是不是要考虑上面所有的细节?对于按钮灯的亮度,我们就不用考虑了,它对我们的工作量没影响。不过,零钱的面额就对我们的工作量很有影响,我们要认真考虑一下(找一堆10分的零钱就很容易实现;如果要尽量减少零钱的个数就比较麻烦了)。处理不同币种也要考虑。
一般情况,我们不用太担心会漏过什么细节。对于每个用户故事,只要考虑一些“重要”问题就行了。当然,这里面的“重要”,就要根据经验以及客户的观点来决定了。

如果我们不好估算的话怎么做

如果我们觉得,这个用户故事不好估算,那可能的原因就是:

1.  这个用户故事太大。这种情况我们就可以将这个用户故事分割出若干个新的用户故事,比如:
将“卖饮料”分割出:
1:显示总投入金额。
2:金额够买的饮料对应的按钮灯亮起来。
3:按下亮灯的按钮,可以买到对应的饮料。
2.  我们之前从没开发过自动售货机的程序。因此,我们不知道开发这样的程序有多复杂。这样的话,我们就要做一些实验了,比如做一个让售货机找钱的小程序。这种试验就叫“spike”(翻译不出来)。

迭代周期内的计划编制

对于这个迭代周期内选择的所有用户故事,不像在发布计划编制那样,只是考虑一些重要的细节,现在我们要从客户那里调查到所有的细节。比如对于“卖饮料”,我们可能会在白板上画出用户交互的草图,然后跟客户一起讨论:
这是一台自动售货机……
用户投入硬币……
假设他投入的是50分,而价格是40分,那么按钮就会亮起(别忘了我们现在做的只卖单一饮料)
用户按一个亮起的按钮,一罐饮料会掉到售货机的出口
找零钱……

在跟客户详细讨论完,了解了足够的细节以后,我们才发现,事实上这个用户故事“卖饮料(只卖单一的)”的故事点远远比我们预计的要麻烦,这时候应该类似前面的发布计划编制那样,1、分割出小的用户故事,挑出一些放在下一个迭代周期内;或者2、挑出这个迭代周期内的一些用户故事放在下一个迭代周期。反之,如果发现这个用户故事比我们想像的还要简单,那我们就要增加更多的用户故事到这个迭代周期内。

用户故事只是跟用户交流的开始,而不是全部

假想现在已经从客户那边得到足够详细的需求了,我们可以开始实现了。注意,我们不用把所有用户提供的细节都记录下来。为什么呢?假设以后,你有点忘记用户故事,而客户又在你旁边,你是直接问客户,还是去找看需求文档找到你要的东西?当然是直接问客户了。客户可以提示更准确,更完整的需求给你。特别要注意的是,以后只要你一完成一个用户故事,你就要让客户看一下,或者实际的操作一下,因为客户对已经做的的东西了解得越多,那他就可以提供越准确越完整的需求。
用户挑选完用户故事以后,在之后的两个星期内,我们就要将这些用户故事逐个完成。每个用户故事我们都会设计结构,编码,测试等等。每做完一个用户故事,我们都要让客户验证一下系统是不是他所想的那样。

在这两个星期内,如果我们提早完成了用户故事,我们就要让客户挑更多的用户故事。相反的,如果我们不能及时完成,我们就要让客户知道当前的进度。

总结

我觉得这章的内容跟其他的软件工程书一样,看看,参考参考,具体的情况还是要具体的分析,不过这里面的用户故事(user story)跟故事卡片的概念就很不错,可以引用。


由外而内看敏捷软件开发
架敏捷开发中史诗故事与用户
看板任务管理
面向全球化的有效敏捷交付
小型团队快速开发方法
DevOps,不是一个传说!
更多...   


统一过程及应用
敏捷过程实践
基于XP/RUP的迭代开发
软件开发过程指南
SCRUM过程实践
敏捷测试-简单而可行


某博彩企业 产品经理与产品管理
北京 研发团队与工作管理
广东金赋信息 敏捷开发过程与项目管理
某支付平台 软件配置管理与发布管理
富士 软件外包项目管理与进度管理
塞孚耐 基于Scrum的敏捷开发
更多...   
 
 
 
 
 

组织简介 | 联系我们 |   Copyright 2002 ®  UML软件工程组织 京ICP备10020922号

京公海网安备110108001071号