关于编写故事卡的一些经验

原创 精选
开发
我非常赞同每个故事卡都应该产生业务价值,并且我们应当将这个价值显式地表达出来。基于以上观点我们再分类别展开聊一下。

作者 | 李响

故事卡应尽量简练,而非事无巨细应写都写;同时,应尽量完整、准确,而非缺少细节、模棱两可。

这是我的基础观点,我的考虑如下:

  • 简练意味着读者获取的信息是经过提炼的,读者阅读起来是更高效的。
  • 简练意味着 BA 写卡可以更高效,可以投入更多精力在其他更具挑战的工作内容上。
  • 完整、准确意味着故事卡是经过讨论并达成一致的。
  • 完整、准确意味着故事卡是有着清晰验收标准的。
  • 完整、准确意味着故事卡是便于追溯、便于传递的。
  • ……

基于以上观点再分类别展开聊下。

关于对页面交互的描述

上图展示了一个添加新账号功能的 UI 设计。一种对该功能需求的描述可能是:

  • 用户通过主菜单进入“权限管理”模块,选择“账号管理” Tab 页,可以看到“新增账号”按钮。
  • 点击“新增账号”按钮,系统弹出新增账号窗口(可能还会写一句“背景置灰”)。
  • 用户可在窗口中填写姓名、登录邮箱……
  • 若用户未填写必填字段,则点击“确认”时给出错误提醒“请完成所有必填字段的填写!”
  • 点击“确认”按钮后弹出二次确认窗口,二次确认信息为“确认创建该账号?账号一旦创建成功即会邮件通知对应用户”。用户再次选择“确认”则系统创建账号,若用户选择“取消”则返回填写账号窗口。

这些文字描述没有任何错误,应该还符合不少 Dev 同学或 QA 同学的胃口,但在我看来过于臃肿。尝试思考简化哪些信息不会影响 Dev 编码和各角色理解业务:

(1) 详细的操作步骤描述是否必要?

通常是不必要的。一般情况下设计图或简单的沟通是很容易表达这些内容的,故事卡中简单地表述主要路径即可,详细的描述反而约束了设计和实现,并且让故事卡变得臃肿。

(2) 描述所有字段是否有必要?

通常是需要的,但应该是从业务角度描述,后文有详细聊到。

(3) 详细地描述用户操作后的系统反馈是否有必要?

通常不是必要的,因为绝大多数的系统反馈是约定俗称或显而易见的。比如 popup 窗口下方的页面是被置灰的,popup 窗口上的“取消”按钮点了后会关闭窗口,等等。那什么是“不通常”的情况呢?常见的是期望系统根据业务目标给出的反馈,比如我会注明“创建用户成功后页面应跳转回列表页”,因为我知道管理员通常会批量创建多个用户,这样效率更高。

(4) 二次确认功能中的文案是否有必要详细描述呢?

很多时候是需要的,因为这些文案通常是想表达特定的业务含义的,用完美的文案将这层业务含义表达出来是 BA 的职责。那反过来的情况呢?比如一些常规的删除操作的确认文案就不需要一一描述,可以与团队约定好所有的删除操作都需要二次确认,所有的二次确认文案都是“确认删除该xx?删除后不可恢复”,如有特殊情况再单独表述。

那么对于上面的需求,我的描述会是这样的:

权限管理员可创建新的用户:

(1) 路径:后台管理端 - 权限管理 - 账号管理 - “新增账号” button

(2) 新增账号所需字段

  • 姓名…
  • 登录邮箱…
  • ……

(3) 确认创建账号需二次确认,文案“确认创建该账号?账号一旦创建成功即会邮件通知对应用户”

简单总结一下,在我的观点中,故事卡通常不应对页面交互做过多描述,这样可能会约束设计和实现,还容易让故事卡失去业务焦点。但若某个期望的交互具有独特性或交互本身就是重要的验收点,那么将他们简练、准确地表述出来也是必要的。

关于对业务逻辑的描述

这里的业务逻辑可以狭义地理解为功能需求中的规律或规则,是我认为“如果有则必须体现在故事卡”的内容。我的理由如下:

  • 它们通常是适配特定业务场景的,不是可以通过普遍认知推导出来的。
  • 它们通常是核心的,直接决定着需求是否能达到预期收益。
  • 它们通常是复杂的、难以记忆的。

所以我们可以直接讨论下如何简洁、准确地描述这些规则。

曾经处理过一个关于预约送货的需求。背景是客户采购“我们”的商品,物流承运商负责将货物运送到客户仓库,但客户仓库常出现没有可用仓位而导致承运商送货到库却又无法卸货入库的情况。解决方案是客户侧开发预约留库位功能并提供接口,我们调用该接口,告诉客户方系统预计送货信息,客户系统对应预留仓位并反馈期望送货时间,承运商确认后按该时间送货。

这个业务场景的特点在于每个节点都有多种不确定性,由此为后续流程带来不同的影响。在业务已经梳理清晰的前提下,这其实就是一个如何表达结构化信息的问题。

首先试下 Given When Then 的表达方式:

AC01 预约日期在窗口范围内

When 客户系统返回了“在预约窗口范围内”的预约日期

Then 邮件通知承运商确认,变更预约单状态为“待承运商确认”

AC02 预约日期在窗口范围外

When 客户系统返回了“不在预约窗口范围内”的预约日期,且未人工确认

Then 邮件通知销售负责人协调处理,变更预约单状态为“待销售确认”

AC03 预约日期已人工确认

WHEN 客户系统返回了“不在预约窗口范围内”但被标记为“已人工确认”的预约日期

Then 预约成功,变更预约单状态为“预约完成”,邮件通知承运商按预约日期送货

……

看起来能把每个细节表达清楚,但可读性比较差,读者可能需要额外的 effort 才能理清各场景间的逻辑关系。

然后尝试下 “BA 式” 的伪代码:


If 约定时限内获取到了客户系统反馈的预约日期
{
if 日期在预约窗口范围内
邮件通知承运商确认,变更预约单状态为“待承运商确认”;
else if 日期已人工确认
预约成功,变更预约单状态为“预约完成”
else
邮件通知销售负责人协调处理,变更预约单状态为“待销售确认”
}
else…


逻辑关系表述清楚了,但阅读大段满载逻辑的文字的体验仍然不好,似乎可以再简洁点。

最终我将这些规则用状态转换图描述出来,然后与 Dev 和 QA 同学沟通是否可以用这张图当做验收条件。在与他们讲解了这个图后,大家认为只要对图中各节点的业务意义达成一致并约定好 Scope(而这些是比较容易的),这样的表述是更清晰、更友好的,于是我们愉快地接受了这种方式。

简单总结一下,在我看来,对业务逻辑的表述是写故事卡的重点和难点,BA 应该结合项目和需求特征选择最佳表达形式,不用拘泥于固定的格式,其中图表经常是不错的选择。关于图表的使用有以下 tips 供参考:

  • 复杂条件组合产生不同系统行为 (比如积分判定规则)> 判定表、判定树或事件 - 响应表
  • 复杂状态规则(比如订单状态规则)> 状态流转图或状态表
  • 复杂业务流程 (比如采购流程)> 业务流程图
  • ……

另外,团队需要就如何理解这些新的表达方式达成一致。

关于对列表和表单的描述

列表和表单是最常见和最基础的需求,往往套用固定的模式就可以将其表述清楚。

列表类需求常见的几要素:

  • 功能权限:谁在什么条件下可以使用该表单
  • 数据权限:数据范围的控制通常体现在列表上,比如用户仅可见owner 是他自己的订单记录。
  • 排序规则:列表中的记录通常需要按一定的规则进行排序以便查看
  • 分页规则:如果某些列表中可以预见地记录不会太多,那么不一定需要分页,Dev 可以更简单地处理这样的列表。
  • 字段清单:对列表中所有字段的描述。UX 的设计图中会有这部分内容的体现,但经验看来设计图中不容易也不需要很及时地反馈字段的变化,在某些条件下设计图也无法体现所有字段。
  • 字段属性:字段对应的业务含义,告诉读者这个字段的值从何而来,如果某字段有特殊规则也可以在这里体现。比如【持续时长】字段是为了方便用户查看,实际不对应数据库字段,那这里就可以描述它的取值规则为“【当前时间】 - 【开始时间】,向上取整天”;再比如【门店名称】通常较长但又很重要,我会描述“鼠标hover 在【门店名称】时可查看其完整信息”。

一个有关列表的验收条件参考如下:

AC01 查看发货单列表:

(1) 路径:主菜单 > 发货单列表

(2) 功能权限:权限管理中新增“查看发货单”权限,仅具有该权限的用户可见“发货单列表”菜单并访问列表数据

(3) 数据权限:承运商用户仅可查看他负责的发货单,销售用户仅可查看他负责的客户的发货单,其他角色可见所有发货单

(4) 排序规则:按发货单创建时间倒序排列

(5) 分页规则:15个/页

(6) 字段详情及顺序

  • 【发货单创建时间】系统接收到承运商 TMS 系统推送的发货单的时间,精确到分钟
  • 【发货单号】承运商 TMS 系统的发货单号
  • 【门店订单号】发货单对应门店订单的编号
  • 【门店名称】发货单对应的收货门店名称,鼠标 hover 可查看完整名称
  • ……

关于表单类功能需求

表单通常是用于创建记录、更新记录、查看记录的详细信息,相比列表类需求对字段属性的描述有以下几点需要注意:

  • 是否必须。
  • 数据类型:比如对于时间类型字段,前端同学会处理为日期&时间选择器。
  • 校验规则:比如对用户名格式或对密码复杂度的校验。
  • 若是pick list,那么选项是什么:选项可能是一些枚举值,也可能是自另外的一个业务实体(比如为订单选择客户),需要详细说明。
  • 字符长度:从业务角度给出字段长度建议。

所以某个表单的描述可能是这样的:

(1) ……

(2) 字段详情及顺序

  • 【姓名】必填,50字符
  • 【出生年月】必填,日期类型
  • 【省份】必填,单选,从基础数据 region 表中取值
  • 【城市】必填,单选,从基础数据 region 表中取值,与【省份】联动
  • 【家庭成员数量】必填,正整数
  • 【联系邮箱】非必填,100字符,校验为邮箱格式
  • ……

这里面也有几个可以探讨的问题:

(1) 对于【联系人邮箱】字段,通常会有对于邮箱格式的校验。那么 BA 在故事卡里是否需要详细描述校验规则?

我的建议是没必要。因为邮箱的格式校验是一个有着“普遍认同”的规则,并不具备独特的业务价值,不该因为 BA 的表述不同而不同。所以,这种问题可以交给 Dev 同学。

(2) 是否需要以及如何描述字符长度/数值范围?

我的建议是可以描述。以字符长度为例,大多数字段其实是比较容易推断出字符长度的,比如“订单状态”,10个字符足矣,Dev 和 BA 从各自视角判断通常也偏差不大。那既然如此,BA 就顺手写出来吧,更何况存在某些字段在特定业务场景下有特殊要求的可能。

可能还有其他问题可以进一步讨论,但总而言之,对于列表和表单类需求通常可以复用一套模板,再结合业务场景调整就可以搞定。

关于对接口的描述

个人最喜欢的就是接口类的故事卡了,无他,但简单尔。

对于接口类需求,我通常做法是:

  • BA 定义好接口业务上的数据结构、业务主键
  • 与 Dev 线下讨论达成一致
  • Dev 补充技术细节形成接口文档
  • 把接口文档附在故事卡里,补充业务场景、调用频率(对于主动拉取数据类接口)、错误处理机制(比如提交订单失败后应重试还是立即报错)、接口获取/提供的信息的特殊处理(比如外系统给到的订单我们要按照自己的规则生成新的订单编号)等必要信息。

最后,对用户故事业务价值的描述和故事卡的拆分也简单分享下我的理解

我非常赞同每个故事卡都应该产生业务价值,并且我们应当将这个价值显式地表达出来。而实践下来,我发现一段“freestyle” 式的描述常常比“作为一个 <角色> , 我想要 <功能> , 以便于 <业务价值> ”这样的表述方式更容易上手。

比如,某个需求是从主数据系统定时获取最新的产品主数据,那么我会用这样的一段文字来描述:

Summary:当前条件下,系统中的产品数据来自于每月客户侧产品经理给到的Excel 文件更新。客户自主研发的主数据平台已与上个月正式上线,并对外提供了数据分发接口,我们可以通过它提供的产品主数据接口每天获取产品主数据的更新,以解决手工更新带来的更新不及时、手工处理出错等问题。

基于这种更自然的表达方式,我可以轻松地描述更多有价值的信息。

最后是我对 INVEST 原则(好的用户故事的编写应满足的几个原则)的一些理解:

  • 独立性(Independent) :应尽量避免故事间的强依赖,但若必须有强依赖,那么这些卡片应该可以在同一个迭代中完成。非独立的故事会造成估算、排优先级和制定技术方案的难度。避免强依赖的方法可以有合并故事卡、换个维度拆分故事等,实在不行也不用强求,按依赖排好优先级即可。
  • 可讨论的(Negotiable):实现方案是可讨论的,但业务目标应是明确的;讨论是必要的,对方案达成一致更不可缺少;可讨论不是BA 不去提前思考具体解决方案的借口,更不是卡片中验收标准不明确的说辞。
  • 有价值(Valuable):我想不出我们去做一个没有价值的需求的理由…… 姑且把这一条理解为在写下这张卡片时我们应该已充分了解它能解决的问题或带来的收益,并且所有角色已经对此达成一致。
  • 可以估算(Estimable):估算通常是为了排期,为了可以估算故事的规模应该足够小,团队对故事应该有充分的了解,并可以就故事内容对技术实现方案基本达成一致。
  • 足够小(Small):更小的故事有助于更准确的工作量评估或多人并行工作,可以让故事卡在卡墙上更快流动起来,但也不必过分追求小故事,不少情况下 Dev 一次代码提交同时处理两个关联需求要比先后处理这两个需求要更简单、高效,如果 Dev 经常说“这几张卡我一起开了吧”或“关了吧”时,BA 可以请教一下他的想法,也许能发现值得改进的地方。
  • 可测试(Testable):故事卡中描述的输入和输出是明确的、可度量的。

文章的最最后,再总结下我的观点

  • 我认同故事卡里非常详细的描述可以带来价值,但我也相信“简练的表述 + 充分的沟通”可以更高效、更灵活。
  • 我认同故事卡不是契约或合同,但我也相信完整、准确的表述可以显著降低各角色间的沟通成本。
  • 我认同可工作的软件高于详尽的需求文档,但我也相信高质量的需求文档可以带来很多收益。
  • 我认同最佳实践和个人经验(包括本文以上所有内容)的参考价值,但我更相信因地制宜、团队共建的实践才是最好的选择。
责任编辑:赵宁宁 来源: Thoughtworks洞见
相关推荐

2015-12-04 10:04:53

2011-07-13 09:13:56

Android设计

2009-06-18 09:51:25

Java继承

2013-04-07 10:40:55

前端框架前端

2020-09-21 06:58:56

TS 代码建议

2020-04-08 10:21:58

bash脚本语言

2020-04-14 09:22:47

bash脚本技巧

2012-09-25 10:03:56

JavaJava封面Java开发

2011-03-11 09:27:11

Java性能监控

2012-04-19 10:06:55

微软Windows 8 E

2009-06-04 16:28:43

EJB常见问题

2020-09-28 06:45:42

故障复盘修复

2017-12-21 07:54:07

2021-06-10 10:02:19

优化缓存性能

2018-07-30 08:41:48

VueReact区别

2016-10-18 22:10:02

HTTP推送HTML

2020-05-19 14:35:42

Shell脚本循环

2020-04-10 08:50:37

Shell脚本循环

2017-09-20 15:07:32

数据库SQL注入技巧分享

2009-08-13 16:41:12

C#结构
点赞
收藏

51CTO技术栈公众号