敏捷开发中史诗故事与用户故事的颗粒度
导语:用户故事的颗粒度一直是一个谈论已久的话题,但参加了很多研讨会,搜索了很多网络资源后发现一直没有定论,只好在这里原创一下。
前言:为何需要讨论用户故事的颗粒度
其实需求颗粒度的问题由来已久,即使是在传统开发中,也没有提及在需求分析阶段应该把需求做到什么颗粒度才可以开工。所以在下面这些分析之后会发现,其实即使反推到传统开发中,下面提及的方法也依然有效。
为何要对用户故事做一个颗粒度约束呢?
笔者在开发实践中发现,如果大小不一的“用户故事”堆积在一起,很难看出一个产品到底实现了哪些功能(或一个项目到底有哪些需求)。如果回溯到敏捷 开发的源头就会发现,由于敏捷开发中设定用户故事是为了描述开发任务(尽管是从客户价值角度)而非产品功能或项目需求,因此并没有区分用户和开发人员到底 分别看到什么。
其实各种“用户故事”中,大致可以分为史诗Epic、故事Story、增强Enhancement、缺陷Defect、技术债务 TechDebts、重构Refactor等几种。其中史诗和故事大致形成树状结构,很容易勾勒出产品或项目的结构,是最需要展示给用户的;增强和缺陷在 一定程度上需要展示给用户,但除了评审中或交付后客户提出来的之外,都可以不展示给用户,而且即使展示,“展示”的层次也应该不同于史诗和故事;而技术债 务和重构由于太技术化,不适合直接展示给用户,而更适合展示给Product Owner及开发团队。
本文所讨论的,就是用于“展示产品或项目结构”的史诗与故事的颗粒度问题。
好的用户故事的标准
先看看好的用户故事的标准(内容及观点与《敏捷开发与用户故事》中大致相同):
封闭性:完整地交付一个客户价值
针对性:只包含一个用户,因为多个用户常常有细微的差别
独立性:故事间没有依赖
这几条标准中最有价值的观点应该是独立、完整地交付一个客户价值,这在敏捷开发中是至关重要的。传统的开发会说要交付一个软 件功能,而敏捷开发则认为用户必须使用这个功能以获得自己的价值(请回忆一下用户故事的编写语法)。
不过仅仅如此就说能方便地划分故事颗粒度,似乎还有点模糊,下面会引用两个成熟体系中对颗粒度划分的标准来做进一步说明。
功能点分析方法与用户故事
第一个是国际标准功能点分析方法(FPA,Fuction Point Analysis)中对内部逻辑文件及基本过程的定义。
内部逻辑文件-基本过程树是一个潜在的史诗-故事树的原型。
一个ILF(ILF,Internal Logical File)就是一组逻辑上的数据,用户能够理解和识别它,对其进行的操作都是用户的业务操作。
比如如果我们要开发一个“Scrum开发管理平台”,那么有一些明显的“逻辑”数据:系统用户,用户故事,迭代,剩余时间……它们不同于数据库表或程序中的类,因为远在程序被设计之前,就可以判断它们是否存在。
一个基本过程(Essensial Process)就是对用户有意义的一次业务操作,在完成这次操作后客户得到其业务价值,而且数据稳定完整。
比如我们“增加一个用户故事”,需要完成的细节过程很多:显示输入界面,在界面中输入数据,数据校验,数据写入数据库表,显示“成功”信息……但只 有这一切都完成,从用户角度看才能获得其业务价值,数据也才稳定完整,因此“增加一个用户故事”就是一个基本过程,而“数据校验”等则不是。
在FPA定义中,基本过程还根据分为外部输入/外部输出/外部查询这三种。简单说,我们常说的“增删改查”都是基本过程。为了防止总是从技术角度谈增删改查,常常使用用户业务术语来描述,比如“起草”(输入)、“编辑”(输入)、“发送”(输出)等。
使用FPA中的内部逻辑文件-基本过程树作为史诗-故事树的颗粒度原型有下面几个好处:
基本过程有严格的定义,已经形成了ISO标准,歧义少;
基本过程面向客户价值交付,与敏捷开发的价值观接近,也很容易用用户故事描述;
文件和基本过程的数量被业界证实与软件的工作量有很好的对应关系,已经有多个国家如日韩澳荷芬等将其作为软件计价标准,工信部也已经进行国标立项,面向政府、银行、电信等大量软件外包场景中的造价估算,本文作者是技术专家组成员。
使用FPA中的内部逻辑文件-基本过程树作为史诗-故事树的颗粒度原型,将作出以下约束:
只有独立完整地交付一个客户价值才算是故事,而缺陷、增强等均不作为有效故事对待;
只有用户可以直接作为业务操作使用的功能(可以是人-机或机-机交互)才算是有效故事,“开发一个某某函数”(即使这个函数很有客户价值)等均不算是故事;
多个过程不能合并为一个故事,比如“……,可以对故事进行增删改查,……”。这样可以防止过于粗糙的功能描述阻碍用户价值的有效传达,也利于合理计算用户故事的数量。
MVC与用户故事
第二个是MVC中对Controller和Action的定义。
Controller-Action树是另一个潜在的史诗-故事树的原型。
FPA和MVC居然能扯上关系?是的,上面提到的FPA中的内部逻辑文件,与MVC中的Controller有很好的对应(有的不能),而基本过程 (输入输出查询)与MVC中Controller的Action有很好的对应关系。比如如果有一个“UserStoriesController” (UserStories可以看作ILF),就会有一堆Create / Edit / Delete / Index……这就是4个基本过程(从前面“增加一个用户故事”的例子可以看出,Create + [HttpPost]Create两者一起才组成一个完整的基本过程)。
基本过程对应Action还是好理解的,但为何对应内部逻辑文件的是Controller而非Model?因为Controller是面向用户写 的,而Model是面向实现技术的。何以见得?因为如果要访问“增加一个用户故事”,我们会输入\UserStories\Create,而其中可能会用 到几个Model:UserStories, Statuses, Priorities, Owners……用户无法直接访问Model,但可以直接访问Controller。所以MVC的Model更像是DataModel,而 Controller才是BusinessModel(有时不是),是“一组逻辑上的数据,用户能够理解和识别它”。
使用MVC中的Controller-Action树作为史诗-故事树的颗粒度原型有以下几个好处:
Action是一类特殊的面向用户业务和价值的函数,还是客户可以直接感知并调用的函数;
Action的数量多,客户可感知的功能一般也就多(相对于一般函数);
Action是一类可单独演示、单独测试的函数,很适合作为故事使用;
Action很容易被程序员所理解,使用MVC的程序员更容易回答“你们的软件有多少真正的用户故事?”。
使用MVC中的Controller-Action树作为史诗-故事树的颗粒度原型,将作出以下约束:
多个过程不能合并到一个故事中(与前面FPA的观点相同),因为不能用一个Action实现“增删改查”。
Controller的Action将只包含有意义的用户故事。有些程序员为了调用方便,会在Controller里定义一些不期待用户直接调用的函数(尤其是直接定义在controller.cs中),应挪到Model中实现。
限制与风险
实际上在FPA中,文件与基本过程并非树形结构,一个基本过程完全可能访问多个文件;而在史诗-故事树中,很多故事其实也是跨史诗的;只有MVC强 制Action必须属于一个Controller,不过“故事结构 Category-Story”到底应该放在CategoriesController中还是UserStoriesController中?这是一个两 难的选择。
此外FPA中整体认为Options、Config这类内容均非要被“卖给”客户的功能,因此不计算内部文件;但在开发中,却需要为其设计Controller来完成工作,两者立足点不同,处理问题会有所差异。
就像明明分子-原子-中子-质子-电子就可以组成世界了,但上帝还是“额外”设计了几百种夸克一样,关于史诗-故事的正确结构应该如何,与增强、缺陷、技术债务、重构的有机关系应该如何,这些内容的合理展现形式应该如何,还需要同行们更多的讨论。
后语:殊途同归的各流派思想
FPA,大致产生于1970年左右,由IBM的开发人员为了处理银行业务而产生,其目的是以一种客户可以理解的方式来描述和计数产品功能或项目需求。
MVC,由一些软件开发者为了改善软件结构而产生,其目的是通过分离客户业务与实现技术,增强在客户业务变化时软件的可重用性、可维护性。
敏捷开发,大致产生于1998年左右,由一群资深开发人员为了改善繁重的开发过程而产生,其目的是减少不体现客户价值的中间环节和文档,快速适应需求变更,追求客户价值最大化。
但最后居然大致归于:
内部文件 ≈ Controller ≈ 史诗
基本过程 ≈ Action ≈ 故事
世界是没有银弹的,为了不同的目的不同的人发明了不同的方法,也遗留下不同的悬而未决的问题,只有各种方法互相包容借鉴,才可能发明出更加完善的解决方案。