前言

老司机们带带我

当你在一个以业务为驱动的项目时,你会发现那些老司机们游刃有余地完成工作,轻轻松松得到想要的结果;新手们唯唯诺诺,说啥做啥,问就是甲方爸爸牛逼,着急忙慌做完后发现,诶?好像这玩意没人用。

在项目研发,特别是确认需求时,不知你是否遇到过这样的场景:

  1. 一句话需求:我想做一个能够转发行情的工具。
  2. 功能要求又多又杂:我想要A功能、B功能、C功能…
  3. 甲方过多关注于你的实现:我觉得你可以先这样,再这样,再这样…
  4. 临时加功能:我觉得这个功能非常有用,需要这个加一下…
  5. 功能上线又不用:…
  6. 功能加的不明不白的:…

不知道现在的你血压有多少,但这都是每个项目团队或多或少面临过的问题。

需求沟通是软件工程开展的必要前提,高效的需求沟通能有效降低甲乙双方的心血管疾病风险,提高生活质量,有更高的概率过一个安详的晚年。

当然,需求阶段遗留的问题越少,项目变动的预期风险成本越低,就如下图《软件开发各个流程的变动成本》。

各个阶段的变动成本

悄悄地说

这里的成本包括时间成本,经济成本,当然还包括咖啡,降压药,植发费用等。 BTW,你也不想让项目延期上线吧?

虽然我们意识到需求阶段的重要性,但在操作时难免会出现各种各样的问题,特别是面临非技术项目时,我们的甲方往往对自身的需求不是特别了解,对软件开发的认识停留在字面,此时我们就需要逐步引导,确认甲方的真实需求。

甲方直接提出的需求,我个人更多的将其理解成“意向”,而不是真实需求。意向变为需求十分简单,只需要阐述清楚产生意向的原因和目的即可,即:要做什么,为什么做,为什么这么做,谁来用,怎么用(客观限制)。

怎么哪儿都能xWyH?

  • What 要做什么
  • Why 为什么做
  • Why 为什么这么做
  • Who 谁来用
  • How 怎么用

当然,这只是确认产品最终形态的第一步罢了,距离让我们双方达成共识还有些路要走。

在下文中,我将围绕需求点,从个人经验角度出发,介绍如何收集、排序、归并需求点,以及如何引导获取甲方真实需求的。

明确需求

需求点

为方便和大家达成共识,这里我提出需求点这一概念:

需求点

需求点描述了完成一项业务的一个有明确目的关键步骤。理论上它可以拆分成多个功能点,聚合后可以完成一项业务。

先举一个业务场景例子:

现在有一个强行平仓试算业务,在该业务中,我们需要根据上一日结算后历史数据,计算预计平多少仓才能弥补会员的资金缺口,同时为了确保当日可以平足够的仓,我们需要对全市场的定单量进行统计,以便于业务人员确定强行平仓方案。

在该业务中,我们可以看到一条流程主线:

会员需要被强平 (业务人员)多次试算会员预计平的仓 (业务人员)导出多个平仓方案 (业务人员)查看当前市场的定单量 (业务人员)人工决策选择强平方案

其中,多次试算会员预计平的仓导出多个平仓方案 查看当前市场的定单量被视为需求点,我们可以发现他们的粒度是很大的,大到再往下拆分可以分为很多功能点,如:查询指定会员的持仓、勾选全选特定持仓、计算平仓预计释放保证金、导出清单等等。

同时,这些需求点的输入输出都很”纯粹“。这个纯粹体现在甲方能够将”每一步要做什么“、”预计得到什么“有一个模糊的认识。同时,在这个需求点上,双方也非常容易达成理解上的一致,特别是对于双方的认知不对等的情况下,可以减少”你以为我懂了,我也以为我懂了,但后来发现咱俩懂的不是一回事“的风险。

那我们再举一个技术场景例子:

现在需要做一个行情转发服务,它将各个交易所的行情实时收集,当有客户端连接行情转发服务时,转发服务实时推送行情给它。

这个描述有些模糊,因为在我们看来其中有非常多不确定的点,比如:实时推送的行情是否包含历史的行情、需要接受哪些交易所的行情等等。

在沟通确认后,我将其划分成了多个需求点,其中有:客户端连接时支持Secret认证客户端和服务端之间支持双向心跳判活等等。

我们会发现,无论是业务场景还是技术场景,需求点能应该描述清楚要干什么事。至于说怎么干,则需要在后续沟通中细化,就比如双向心跳超时时间是多少等等。

在这里可能会有人好奇为什么需求点的粒度这么大,我的想法是,越细粒度的需求确认所需的成本越高,同时,我们无法奢求甲方能直接”脑补“我们的设想(大饼),我们也无法确认得到的一定是最终的需求。既然如此,不如一层一层地深入,先了解产品的大致轮廓,确认方向上可行后,再逐步细化。

需求点的作用便是能够让双方在已有条件下对关键问题最大程度上达成共识,至于那些”小问题“可以后续再确认。

收集需求点

我们应该对需求点这一概念达成了共识,接下来我们就需要考虑怎么收集需求点

需求点不是凭空来的,它可能来自甲方的意向,来自团队的可行性分析,来自甲乙双方的经验,但一个意向转变为若干需求点则需要经历挖掘甄别两个阶段。

挖掘

需求点的挖掘和考古有点相似:

  • 你根据传说等种种迹象(甲方模糊的意向)来到一处可能存在遗迹的地方。
  • 甲方的真实需求就像被埋在土里的文物一样,你不真正挖下去,你永远不知道下面到底是什么。
  • 发掘要快,文物慢一点就被氧化破坏了,甲方要么等不及要么就改主意了。
  • 文物挖掘出来后,你要依靠自身的专业技能圆成一个完美的故事,至少在当前已有的信息下是完美的。

挖掘的方式则根据目的和场景选择,就像用洛阳铲判断有没有货,用毛刷发掘高价值文物。

我认为”意识形态决定人的行为“可以应用在挖掘需求点上,我们可以围绕着目的需求点进行挖掘。

价值导向分析

价值导向分析将目标放在了甲方的目的上,我们需要将目光放在真正能解决甲方问题的地方上。那我们应该如何识别并确认甲方面临的真正问题呢?

当我们接收到甲方的需求后,我们可能会看到他期望我们去做一件事,例如开发一个新功能,改变系统的一个特性,我们需要在需求分析阶段对甲方”灵魂拷问“:

  • 这个方案是什么?
  • 为什么有这个方案?
  • 谁来执行这个方案?
  • 为什么一定是这个方案?

其中,前三个问题可以帮助双方思考这个方案的合理性、可行性和必要性,最后一个问题帮助我们从战略的角度思考——这个方案是否符合战略要求;规划是否合理;是否存在更优路径;面临的阻碍是什么。

当双方就这几个问题达成一致,我们就可以抽丝剥茧,找到甲方真正关注的核心问题,并尝试着手解决或缓解问题。比如提出几个较优解,或采纳甲方的方案。

如图所示: 甲方提出希望我们能满足期望(蓝色虚线),经过上述灵魂拷问,我们发现了真实目标的位置,并发现要满足该目标,除了甲方提出的方案外,还可以有两个更优解。 综合成本考量,甲方选取了较优解1方案——短期内,距离真实目标更进一步,长远看,总体成本更低。

沟通小窍门

当你打算让别人帮忙决策是否使用某方案时,建议以你最中意的方案衍生出2、3、4号方案,讲述各个方案利弊,让别人从中选择。 ”选择决策“和”是否决策“的决策难度是不同的,前者至少看起来做过调研,程序上说得过去的。

举一个业务例子:

背景为:有一个基于当前交易市场的测算系统,该系统可以随时测算各个会员的资金情况,业务部门根据该资金情况判断会员的资金是否健康。现状是,该测算系统在交易过程中实时计算一个标准价格,以该价格为基准,计算会员的资金占用。

甲方预期:期望能够实现标准价格的自定义,即业务部门可以手动修改该价格。

灵魂拷问:

  • 这个方案是什么:略
  • 为什么有这个方案:领导希望探索不同标准价格方案的可行性,为未来标准价格算法的更新提供决策数据。
  • 谁来执行这个方案:业务部门手动选择方案,系统自动执行。
  • 为什么一定是这个方案:
    • 为什么不可以使用历史数据回溯的方式探索方案的可行性?
      • 相较于提出的方案
        • 优势:成本低,灵活度高,开发难度低
        • 劣势:实时性差(但可以接受)

经过沟通决定优先采取历史数据回溯方式达成目的,测算系统无需增加该功能。

主线支线分析

我们将通过价值导向分析出下一步的目标(较优解1)称为价值目标。从现状出发,经过多个阶段调整目标,最终达成价值目标的路线称为主线。在达成每个阶段目标后,完成的非核心目标称为支线目标

在制定主线时,我们需要遵循以下原则:

  1. 主线不一定是直接到达终点,可能由于客观因素,不得不绕过一些阻碍。
  2. 阶段目标具备交付价值,其存在目的是为了解决现状中存在的问题和为达成最终目标做铺垫。
  3. 价值终点可以改变,但尽可能避免改变。如果调整后的价值终点与初始终点偏离过大会导致项目的意义发生变化。

在制定支线时,我们需要遵循以下原则:

  1. 支线属于锦上添花,优先级往后稍稍。
  2. 支线要在稳定的阶段目标分出,避免支线影响阶段目标的交付。

举一个例子:

现状:有一个测算系统,具备在交易阶段实时测算会员资金的能力。 价值目标:支持用户手动创建多个自定义任务,每个任务可以定时触发,可以设置条件满足时告警。

我们将其分成了若干个阶段目标支线目标,形成以下的路线:

确定好主线后,我们注意到我们成功将需求拆分成若干个需求点,同时,需求点之间的拓扑关系大体确定,这为后续的需求点排序、归并以及版本规划打下基础。

甄别

当我们挖掘到一些需求点后,我们就需要对这些需求点进行甄别。在这个阶段,我们通常关注甲方的行为

用户行为分析

用户行为分析是对用户历史和预期行为的分析方法。该方法在重构类项目有奇效,原因之一是不管甲方嘴上说着某功能有多需要,但他的身体是诚实的,原来不方便不合理的功能可能也就刚上线的时候体验了一下,后续有没有用另说。

因此,在我们分析完重构前产品的需求点后,便可以通过甲方的行为对需求点进行过滤:

  • 对于经常用的,我们标记为核心需求点,优先级高。
  • 对于偶尔用的,我们标记为一般需求点,在核心需求点满足后逐步完成。
  • 对于极少用的,我们标记为低频需求点,除非这个需求点是必须达成的,否则在该方面投入是不明智的。一般的解决方法是忽略该需求点或使用其他低成本操作替代。

举一个例子:

背景:旧测算系统有一个一年需要执行一次的功能,该功能在某天下午执行,模拟测算未来3天的资金情况,可以查看这3天每一天测算的结果。

现状:新测算系统将支持模拟测算,但仅支持一天(可以是第1、2、3天),同时新系统支持创建自定义测算的任务。

通过沟通得知,该功能使用率极低,但该功能影响较大,导致开发工作量较多。经过决策,决定在新系统取消该功能,当用户需要测算未来3天时手动创建3个自定义任务,分别对应第1、2、3天,从而达到和原系统的功能预期。

沟通雷区

在与甲方确认需求点的优先级时,不要问“这个功能要不要”,而是要问“平常都是怎么用的”。 甲方当然不介意多个功能,至于成本和设计的合理性不是他们需要考虑的,所以大概率期望功能越多越好。 我们要避免“引导式”问询,最好通过客观的用户行为来分析功能是否有存在的必要。

需求点归并

当我们拿到需求点后,我们可能发现需求点之间是存在关联的,比如需求点A依赖需求点B,又或者需求点从实现上是相近的,比如需求点A实现过程中稍微加工一下就满足需求点B

这个时候我们可以“合并同类项”,将一些相近的、实现上相似的需求点合并成需求块。这个需求块只是一堆需求点的集合体,它不代表任何含义,就像一个文件夹里的多个文件,我们根据自己确定的规则将需求点分类合并,方便后续管理。

举一个例子:

背景:现在要做一个网络转发工具,现在已经分析出以下需求点:

  1. 支持心跳探活功能
  2. 支持自定义心跳间隔
  3. 支持断线重连
  4. 支持自定义断线重连间隔、次数
  5. 转发数据过滤
  6. 转发数据存储
  7. 自定义转发规则

在上述需求点,我们发现拓扑关系:

  • 自定义转发规则转发数据过滤
  • 自定义转发规则转发数据存储
  • 支持心跳探活功能支持自定义心跳间隔
  • 支持心跳探活功能支持断线重连支持自定义断线重连间隔、次数

发现相近关系:

  • 通信心跳
    • 支持心跳探活功能
    • 支持自定义心跳间隔
    • 支持断线重连
    • 支持自定义断线重连间隔、次数
  • 转发规则
    • 转发数据过滤
    • 自定义转发规则

这个时候,我们可以将需求点归并成若干个需求块:

  • 需求块1:
    • 支持心跳探活功能
    • 支持自定义心跳间隔
    • 支持断线重连
    • 支持自定义断线重连间隔、次数
  • 需求块2:
    • 自定义转发规则
    • 转发数据过滤
  • 需求块3:
    • 转发数据存储

在这里可能有人会疑惑”转发数据存储“与”自定义转发规则“也存在拓扑关系,为什么不放到一个需求块中?我个人认为,转发数据存储虽然依赖自定义转发规则,但它相对独立,可以分批完成,因此不必合并。

需求块排序

当我们获取到一堆有效的需求块后,我们需要分析需求块优先级。优先级的研判主要取决于甲方意愿,其次是技术拓扑关系。

当然,遵循甲方意愿不意味着我们要一股脑按照甲方提出的想法来做,而是逼迫他“断舍离”——每个需求块要确定一个明确的先后顺序。

这个过程十分痛苦且快乐,但是是十分必要的——丑话说到前头,我们通过这种方式在一开始堵住了他的嘴,让他对需求块的优先级有一个清晰的认识,这样在他临时提出新需求点时或主动或被动地思考新需求点的优先级了。

我们可以将需求块首先分成3个大类:

  • 最紧急:直接影响用户体验
  • 重要不紧急:需尽快优化
  • 一般重要不紧急:可抽空优化

举一个例子:

甲方把上面需求块进行了排序:

  • 最紧急:
    • 需求块1:
      • 支持心跳探活功能
      • 支持自定义心跳间隔
      • 支持断线重连
      • 支持自定义断线重连间隔、次数
  • 需求块3:
    • 转发数据存储
  • 一般重要不紧急:
    • 需求块2:
      • 自定义转发规则
      • 转发数据过滤

需要注意的是,虽然理论上需求块3依赖需求块2,但需求块2的优先级远低于需求块3,因此我们只需要在设计阶段留意一下,未来需要支持转发规则的自定义便好。

版本规划

现在我们和甲方已经达成了一定共识:

  • 范围确定:有哪些需求点
  • 内容确定:需求点都是什么
  • 优先级确定:需求点、需求块的紧急程度

接下来我们每个发包后,就可以从backlog中选取合适数量的需求点需求块作为下一个版本的范围了。