使用用户故事,从用户的角度出发描述故事。

编写一个好的用户故事,首先需要关注以下 6 个特征,这些特征分别是:

  • 独立的(independent)
  • 可讨论的(negotiable)
  • 对用户或客户有价值的(valuable to purchasers or users)
  • 可估计的(estimatable)
  • 小的(small)
  • 可测试的(testable)

《探索极限编程》和《重构工作手册》的作者 Bill Wake 建议用英文 INVEST 来描述这六个特征,其实这也就是我们说的 INVEST 原则啦,那如何去解释这六个原则呢?

等一下,如果你不知道为什么要编写用户故事,那我建议你速读一下 Mike Cohn 写的《用户故事与敏捷方法》这本书,详细了解一下敏捷研发的前后背景。

独立的

我们需要尽可能避免故事之间的互相依赖,在对于故事排列优先级的时,或者使用故事做计划时,故事之间的依赖性会导致一些问题,比如你已经选择了一个高优先级的故事,但是这个高优先级的故事依赖于低优先级的故事,那就会出现一些问题。而故事之间的互相依赖也会导致估时变得困难。在《用户故事与敏捷方法》这本书中举了一个例子:

假设我们正在开发 BigMoneyJobs 网站,现在需要编写客户公司如何对发布职位付费的故事,我们可以编写出这几个故事:

  1. 公司可以使用 VISA 信用卡对发布的职位进行付费
  2. 公司可以使用万事达卡对发布的职位进行付费
  3. 公司可以用美国运通卡对发布的职位进行付费

假设研发人员估计支持第一种信用卡需要 3 天时间,然后支持第二种信用卡和第三种信用卡分别需要 1 天时间,那么当我们需要对以上 3 个故事进行估时时,究竟应该对一种信用卡预估 3 天的时间呢?

所以当出现这种类似问题时,我们要么将这些类似的故事合并成一个大且独立的故事,要么用一个其他的方式去分割故事。

比如我们可以用一个新的故事“公司可以使用信用卡对发布的职位进行付费”来合并这三个故事,这个新的故事在估时上也很方便,一共需要 5 天时间;当然我们也可以用“用户可以用一种信用卡来进行支付”,“用户也可以选择用另外两种信用卡进行支持”来进行故事的拆分。

可讨论的

故事是可以被讨论的,因为这些故事并不是签署好的合同或者软件必须要实现的需求。故事卡是功能的剪短描述,而细节将在客户团队和开发团队的讨论中产生,因为故事卡的作用是用来提醒客户团队和开发团队在后面要进行关于 U需求点对话,它并不是具体的需求本身,因而他们也不需要关注所有的相关细节。

然后,如果我们在编写故事的时候已经知道了一些必要的细节,那么就需要在故事卡上面记录这些细节。

同样还是上面这个使用银行卡支付的例子,如果我们用故事进行描述,那应该是这样的:

公司可以使用信用卡来支付发布工作信息的费用。

备注:接受 VISA 信用卡,万事达信用卡以及美国运通卡,同时支持发现卡。

这张故事卡片不仅提供了一定的信息给开发团队和客户团队进行交流,当一个开发人员开始编码和实现这个故事时,这张故事卡可以提醒他系统必须要支持 3 种主要的信用卡,同时也能够提醒他需要和客户团队进行确认,是否需要支持发现卡。卡片上的注释能够帮助开发人员和客户继续先前没有深入的对话,理想状况下,不论对话的双方(开发人员还是客户人员)是否与原来的人相同,但这种对话都会保持当时的内容可以被继续进行。

但切记不要加入过多的限制和细节,因为当你需要处理一个包含多个细节的卡片时,花费的精力是非常痛苦的,所带来的的额外工作量也是非常多的,比如:

公司可以使用信用卡来支付发布工作信息的费用。

备注:接受 VISA 信用卡,万事达信用卡以及美国运通卡,同时支持发现卡。当支付金额超过 100 美元时,需要提供信用卡背面的 ID 号码,系统可以根据卡片号码的前两位数字识别客户使用的是哪一种类型的信用卡。系统可以保持卡号以备将来使用。搜集信用卡过期的月份和日子。

当然了,我们不提倡故事卡片中包含足够多的细节原因也是因为我们不想让研发团队有这样一种错觉“故事卡中反应了所有的细节,没有必要进行进一步的确定”。而一些相关对话的注释也可以记录在卡片中,故事卡就会不仅包含“故事本身和相关问题的注释,也会包含以测试形式记录下来的细节,用来验证系统是否像预期那样运作”。

对用户或客户有价值

“每个故事都需要对用户有价值”,这句话说来很诱人,但其实并不完全对。许多项目包含对用户并没有意义的故事。要记住用户和客户之间的区别。假如我们在构建一个支持大量用户的软件,可能需要在公司内的上千台电脑上进行实施。这种时候客户会比较关心 5000 台电脑是否能够使用相同的软件配置。这就需要这样一个故事“所有的配置信息都从一个中心读取”,用户不关心配置信息在哪里存储,但是客户却会关心。

同样,我们应该避免在故事中出现用户界面和技术方面的定义。保证每一个故事对客户或用户有价值的方法就是让客户来编写故事。或者说引导客户帮我们确定故事。

可估计的

对开发人员来说,能估算故事的大小,或者是把故事变为可用代码的时间量是很重要的,但一般会有以下 3 个因素导致故事无法估计:

  1. 开发人员缺少领域知识
  2. 开发人员缺少技术知识
  3. 故事太大了

首先,开发人员可能缺少领域知识。如果开发人员不理解故事,他们应该和写故事的人一起讨论。同样,没有必要理解故事的所有谢姐,但是开发人员需要对故事有一个大概的了解。

其次,故事无法估计是因为开发人员不掌握所设计的技术知识。比如在一个 JAVA 项目中,我们需要提供一个 CORBA 接口给到系统,但如果团队中没有人有相关经验,那自然就没有办法估算这个任务。在这种情况下,就应该让一个或多个研发人员去实施极限编程中所谓的探针试验,用一个简单的试验来研究应用程序中的某一个方面。在做探针试验时,研发人员并不需要做过于深入的研究,只要能够大概获得足够的信息来估计这个任务即可。探针试验本来就应该限定一个最大时间量,用这个时间量作为探针时间的估计。

这样说来,一个普通的故事就会变成 2 个故事,一个探针故事,以及一个真正的故事。

缺乏领域知识其实就是我们所说的对于业务不了解,比如我们正在做一个治疗慢性病的网站,我们邀请到医院的护士帮我们梳理出了一条故事“新用户需要完成一个糖尿病调查表”,但研发人员其实无法确认这个故事,也不知道这个故事是什么意思。这个调查表到底是一个简单的问卷,还是要给用户分发一套实物的设备,还是要做什么别的东西呢?

但虽然故事太大并不能够被估计,但有时可以编写一些史诗性的故事,用来和团队中的人们一起讨论,而如果暂时不喜欢系统的一些故事,那就可以考虑写一两个史诗故事。

小的

有的故事太大,有的故事可能太小。故事的大小十分重要,太大或太小都无法帮助我们制定计划。使用史诗故事来开展工作会十分困难,因为它们通常会包含多个故事,史诗故事需要被分割为更小的故事,而合适的故事应该取决于团队如何,团队采用的技术如何。

比方说,BigMoneyJobs 系统可能会包含这样的故事“用户可以发布她的简历”,在做系统初始设计的时候,这个故事可能是比较恰当的,但当开发人员与客户进行沟通时,他们发现“发布简历”实际上有这样几点:

  • 简历包含教育程度,工作经历,薪资历史,出版物,演讲情况,实践情况以及求职目标。
  • 用户可以将简历标记为激活和非激活状态。
  • 用户可以有多份简历。
  • 用户可以修改简历。
  • 用户也可以删除简历。

而如果考虑这些需要多久才能开发完成,前面这些故事还可以拆分的更加细致,但是我们也不应该走向另一个极端,如果将不那么大的故事再分解成一系列的故事,纳闷它们可能就太小了——比如说按照每一个字段来设计一个故事。

我们既可以用“增删改查”这样的动作来分解故事,也可以用数据的边界来分解故事,总之不同的分解结构就会导致不同的分解结果。

对无法估计的故事进行分解,主要的好处是允许客户把调研的工作从新功能中分解出来,以便于对调研工作排列出优先级。如果客户列出了复杂的故事,那么她可能就会错误的假设此功能实现的大致进度表,并以此来排列故事的优先级。

可测试的

故事必须是可以被测试的,成功通过测试就可以证明开发人员真的完成了故事。如果故事不能够被测试,那开发人员怎么定义他们是否真正的完成了代码?

通常,不可以测试的故事多出现在一些非功能性的需求上,这些需求和软件有关,但不与直接功能有关。比如说以下 2 个非功能性的故事:

  • 用户必须要觉得软件很好用。
  • 用户绝不会花很长时间等待窗口出现。

这两个故事都是不可以测试的,不论什么时候,只要有可能,就要把测试自动化,这就意味着我们需要争取 99% 都能够实现自动化,而不是 10% 而已。能自动化的测试基本上总是比你以为的要多的多,当产品是增量开发的,很多东西变化的就会很快,昨天能够工作的代码,今天就会出问题。这时候就需要自动化测试来帮助你尽快发现问题。

在实际过程中,总有极小部分的测试是不能进行自动化测试的。例如“一个新用户不经过培训就能完成一般的工作流程”这个用户故事就可以测试,但不能进行自动化测试。测试这个故事还需要一个人为因素专家来进行设计与实现。