敏捷开发用户故事系列之八:剖析用户故事描述语法(兼谈不同种类故事的语法)
这是敏捷开发用户故事系列的第八篇。(栏目目录) 本文内容来自火星人团队对火星人产品中300个用户故事编写后总结的经验和成果,欢迎致力于敏捷开发而又对用户故事感到困惑的开发者参与讨论。本篇文章尤其适合参加MPD专场“用户故事颗粒度、分类及组织结构”的学员,及参加火星人敏捷开发产品培训的学员。 用户故事中,最有名的就是三段式语法了:“作为一个……(角色),可以……(功能),以便……(客户价值)。”然而这种语法用于描述用户的业务操作比较方便,但若用于描述非功能性需求(包含重构、缺陷等),或用于描述对功能的增强,则有点力不从心。让我们来剖析一下用户故事语法,并尝试写一下其他类型故事的语法。分析
传统用户故事的语法为何得以成功流传10年而没有发生变化?
“因为他们是大师发明的。”
错,因为发明了这个语法,他们才成为大师的,这是因果倒置。
真正的原因是,这个语法很好地表明了需求的三个最重要的要素:角色,功能,客户价值。
角色为什么要角色?
1. 有利于开发人员理解场景。
普通用户/管理员,存款客户/银行职员,……这些区别,令开发人员更容易理解产品的用途、风格、操作人员的熟练程度、操作习惯等。
2. 有利于找特定的用户核实
无论是产品的潜在用户调研,还是项目中与客户核实需求,如果有一个“角色”字段,都令沟通工作可以与适当的角色地进行,完成的产品自然也就令这些角色的人员满意。
功能(标题)功能是最好理解的,就是程序员原来写的那种功能的名字,或称为标题。
不过,要放到这种语法里边,还是要动点脑筋的。
1. 主语-谓语原则
比如:(在某个用户管理系统中)“显示所有用户”,就不是一个好功能,为什么呢?把它套到用户故事语法中:
作为一个系统管理员,可以显示所有用户,以便……。
是不是感觉有点别扭?对,应该用“查看所有用户”,会更好一些。
作为一个系统管理员,可以查看所有用户,以便……。好多了。
可是,难道所有用户故事的名字都要和语法中的功能相同吗?不能写一个名为“显示所有用户”,但内容是“作为一个……,可以查看所有用户,以便……。“的故事?
能,但不要这样写,因为这违反了DRY原则。
DRY原则就是Don't Repeat Yourself原则,一件事情不要做两遍,尤其是这种本来就差不多、很容易混淆的事情。
所以,好的功能是以用户为主语的一个谓语。
2. 动宾词组原则
如果连续有用户的新建、删除、编辑、查看,是否可以只写”新建“”删除“?
这是在一次培训中大家实际讨论到的问题。
理论上说在这种场景中可以(有一些小问题),但是在其他时候会遇到大问题。
小问题:”查看“往往分为”查看所有用户Index“和”查看单个用户详情Details“两种,不写宾语不容易区分。
大问题:在故事板、燃尽图、我的个人中心等地方,如果凑巧一个故事需要单独存在的时候,失去了动作的宾语,会出现多个”新建“”删除“故事,容易混淆,不如”新建用户“”删除用户“来得好。
火星人在开发过程中尝试了各种方案,最后最佳结论是:
故事标题=功能;角色-功能是主语-谓语关系;功能本身是动宾词组。
用户价值用户价值看似简单,其实很难写。
请看:作为一个管理员,可以查看所有用户,以便了解系统中有哪些用户。
看上去还不错,对吧?但是如果这个系统是CSDN博客,里边有600万注册用户,这个管理员为什么要查看所有用户?他怎么才能查看所有用户?如果这个系统是QQ,又会如何?
所以一个管理员,不会无缘无故查看所有用户。但是,也不能不查看用户(否则无法查看详情、删除、申诉、找回密码、禁言……),但是,要为管理员找到一个合理的理由,并以合理的方式查看某种用户,才是正确的故事。
实际案例剖析:火星人中新建用户”作为一个管理员,可以新建用户,以便在系统中新建一个用户。“
好像可以,但又有点像废话。怎么办?
在亲自编写了300个用户故事后,我们发现可以尝试这样:
1. 尝试找到功能的现实用意,然后写出不同于功能的用户价值。
”作为一个管理员,可以新建用户,以便将特定用户添加到火星人系统中。“
不管汤、药如何,读起来顺嘴一些了。
但如果觉得还不够好,请尝试2。
2. 对顽固的不好描述的用户故事,尝试添加一些形容词、副词、壮语,用以描述这种操作的核心价值。
什么叫核心价值?有些操作希望”方便“”快捷“,另外一些则需要”安全“”一致“,这就是核心价值。
”作为一个管理员,可以新建用户,以便快捷地将特定用户添加到火星人系统中。“
恩,好像可以了。但是……如果一个管理员,要一个一个在弹出窗口中新建用户,保存,再新建,再保存……这个功能可能挺快捷,但对最终业务而言,如果有1000个用户要添加,实在谈不上快捷。
所以,请看3。
3. 如果感觉用户故事无法满足核心价值,请尝试改进故事,乃至换一个新故事。
”作为一个管理员,可以批量创建用户,以便快捷地导入已经有的用户数据。“
批量创建比创建快多了,每行一个用户,用逗号分隔用户名、邮箱……可是出了错怎么办?
弹出”输入的数据不符合要求,请检查格式是否正确。“
但如果有1000行,该怎么办?额……请看4。
4. 请验证改进后的用户故事能达成核心价值。
既然要快捷,除了错也应该迅速定位。
这样吧:所有识别成功的用户先暂存起来,所有不成功的行,则留在屏幕上(最多也就是几行),修改后再次识别,直到全部成功;确认后开始批量创建。
这个过程不用写在故事语法里边,因为没地方放。但要写在需求详情/测试用例里边。
好了,这就是火星人产品中”批量创建用户“的辗转经历。
不同种类故事的语法让我们来尝试为其他种类的故事(非功能性需求)分别编制一套语法。首先解决两个问题:
1. 这种故事有哪些最重要的要素?
2. 如何编写才能突出这些要素?
在编写了300多个用户故事后,火星人团队摸索到了一套自己的编写语法,并预置在火星人产品中(请在8月中旬关注本博客,了解在线体验系统的信息)。
在看具体示例之前,先写一下最后的总结,方便大家理解我们为何识别了这些要素。
语法总结:
1. 角色多数时候是必需的
增强、重构、Bug……都有其受益者或受害者,若没有角色字段,很难站在真是位置上猜想其真实需求,也很难找人核实、询问优先程度。
2. 客户价值(或潜在损失)多数时候是必须的
若不能识别客户价值(或潜在损失),就不能知道增强、重构后是否达到了商业效果,或Bug修正后是否避免了潜在损失。
3. 原来“功能”的位置各不相同。
火星人中的几个实例由于未必了解这些故事的背景,读者可能不会很清晰地判断故事的好坏,但请重点留意语法格式的作用。
增强:突出显示本次/上次/下次意向表(增强一个操作)–实现后,作为一个产品经理,可以在查看意向表时,突出地看到本次和上次迭代的意向表,以便连贯地思考本次迭代的需求规划。在这个实例中,“实现后,作为一个……(角色),可以在……(原功能)时,……(超出的功能),以便……(超出的价值)。”这一语法突出了实现前后的功能差异。(仅限于对功能的增强描述)增强类型中,一般会暗含一个被增强的功能,但描述内容则是超出的功能和超出的价值。重构:重构界面及ViewModel–实现前,原来的中间为树两侧为迭代的展示方式较为占用空间,树的高度太大导致难以完整看到;实现后,以故事树为主体配合左侧的当前迭代故事,查看和操作更加快捷。
在这个实例中,“重构前,……;重构后,……”的语法对比了重构前后的差异,这种差异必须面向用户进行描述,以便产品经理能正确理解和排序。缺陷:在加入故事时产品按钮不刷新–作为一个产品经理,在加入或挪出故事时,顶端的产品按钮上人天数据不刷新,导致计划时对各产品的总量和比重的错误判断。
在这个实例中,“作为一个……(角色),在……(功能)时,……(现象),导致……(潜在损失)”的语法表达了在何时,会出现何种现象,导致何种业务问题。有些缺陷发生时“现象”很不明显(比如数据不刷新),因此必须描述导致的业务问题以便理解问题的实质。债务:产品ID硬编码–当前链接中的产品ID是硬编码的(直接访问火星人),在部署到实际环境中时,将导致链接失效。
在这个实例中,“当前……(当前的临时实现方法),在……(未来某种情况下)时,将……(可能出现的现象),导致……(潜在的损失)。”的语法表达了当前的何种技术原因,在何时,将会发生何种问题,并导致何种潜在的损失。