前言
实例化需求被认为是构建正确软件的优秀实践之一,越来越多的开发团队开始使用这种工作方法,本文将为大家分享一个真实团队的实例化需求实践过程。
实例化需求这个概念来自于一本书《实例化需求:团队如何交付正确的软件》(Specification by Example: How Successful Teams Deliver the Right Software)。
书的作者叫Gojko Adzic,是一名英国的战略软件交付顾问。在近十几年来,一直在各种行业领域(例如财务和能源交易平台、移动定位、电子商务、在线游戏、复杂配置管理系统等等)从事着程序员、架构师、技术指导和顾问等工作。
实例化需求就是Gojko在这十多年的软件生涯中总结出来的一套行之有效方法。官方对于它的介绍是这样的:实例化需求是一组方法,它以一种对开发开发团队有所帮助的方式(理想情况下表现为可执行的测试)描述计算机系统的功能和行为,让不懂技术的利益相关者也可以理解,即使客户的需求在不化,它也具有很好的可维护性,可以保持需求的相关性。从而帮助团队构建正确的软件产品。
在我看来,这就是这套方法最牛的地方——帮助团队构建正确的软件产品。正确这个词,说起来简简单单,但实现起来却真的很难。
我们为什么使用实例化需求
我们都明白,需求是一切软件开发的源头。有了需求,开发才能开始。但实际情况往往是,当项目发生延期,或是项目测试投产出了问题的时候,第一个被揪出来当替罪羊的也往往是——需求不明确!
咦,需求不是应该在开发之前就已经搞明白了吗?怎么还会不明确?造成这个问题的无非下面几种情况:
真的没好好搞懂需求就开始了。在需求讨论中,很常见的一种情况就是,刚开始说的是要做什么,为什么要做,讲着讲着,大家的探讨焦点就变成了怎么做,如何实现,然后迫不及待地投入到软件开发之中。很多团队都会有这样的误解:只有开始写代码才是真正的干活。
项目干系人并没有对需求有着一致的理解。常规的需求通常是用自然语言编写而成的文档,自然语言本身就会存在着一千个人有一千个汉姆雷特这样的理解偏差。如果团队使用的是需求-开发-测试这样的软件开发流程,需求在产生阶段并没有整个团队的介入,仅由需求人员负责,在开发之初才移交给开发团队,即使需求人员和开发人员有理解偏差也很难在第一时间发现。
不是我不明白,是这世界变化快。互联网时代,软件致胜只有一个法宝——快。很多人慨叹,现在的时代过于浮躁,精雕细琢的东西不复存在。而事实是,整个IT界的业态变化得如此之快,如果你再按照从前的项目节奏走下去,当你的产品交付之时,也将是被淘汰之时。如今,大多相互的项目周期是按月来衡量的,项目阶段则是以周甚至是天来计数的。所以,如果团队期待的是一份大而全,且永不变更的需求文档,并以此为依据进行开发,那项目延期或者出问题简直是必然的情况。
过于依赖个人的认知结构。需求从哪里来?有的团队的做法是把用户提出的需求当做用户故事的源头,竭尽全力去实现。但是用户不了解你的系统,所以他所说的有可能超过了系统的范畴。再加上用户也许并不真的像他以为的那么了解自己,往往把真实的需要隐藏在一个他自以为的解决方案中提出。如果真的照他说的做,那就惨了,只有在交付的时候才能听到他说——这不是我想要的。另一种做法是由产品人员把用户的需求翻译过来。但是在翻译的过程中只有产品人员孤军奋战。而每个人总有每个人思维的局限性,一定会有想不到的地方。
让我们来看看实例化需求可以做什么吧。
实例化需求的核心是:让项目的所有干系方进行有效的协作和沟通,用实例的方式说明需求,用自动化测试的方式频繁地验证需求,从实例化的需求说明和自动化测试用例中演进出一套“活文档系统”。这套“活文档系统”既可以有效地对系统进行说明,又可以当做交付验收的标准。
有效的交流沟通确保有足够的时间澄清需求。
使用举例的方法澄清需求能在第一时间识别出需求是否足以支撑开发。
所有的干系方参与需求讨论,可以确保大家对于交付哪些东西有一致的理解。
具有不同领域背景的干系方一同参加需求讨论,可以规避因个人认知局限带来的需求问题。
“活文档系统”对于变更有着先天优势,可以以最少的维护成本维持文档的相关性和可靠性。又能避免过度说明需求而产生浪费,避免花时间在开发前有可能发生变化的细节上,对于变更天然友好。
采用自动化测试的方法实现业务实例,代码开发出来即可以验证,无须经过冗长的手动回归测试,降低返工。
完美解决上述问题。
实例化需求怎么做
在《实例化需求》一书中,Gojko提出了实例化需求的主要过程模式,包含以下几个环节:
从目标中获取范围:与客户沟通协作,以用户的业务目标为起始,通过协作节点可以实现目标的范围。需要注意的是:1)要一直牢记商业价值,为什么要做。很多时候执行项目时太关注怎么做了。2)不要把用户自以为的解决方案当做系统需求。
从协作中制订需求说明:协作是关键词。目的是让需求、设计、开发以及测试都参与进来,发挥整个团队的知识和经验,力求让项目的干系人都更多的参与到交付过程中。排除理解的不一致性,尽量减少个人认知造成的局限性。一定要在团队着手开始项目之前再进行实例化需求的工作,否则太早进行有可能导致遗忘,或者是遇到需求变更使之前的工作没有价值。
举例说明:举例说明其实是项目需求交流过程中不可或缺的,团队中的成员业务背景不同,对同一事物的理解也不同,通过举例说明的方式可以让目标更一致。
提炼需求说明:协作过程中的开发讨论可以建立大家对相关领域的共识,但得到的实例往往包含很多不必要的细节。而关键实例是从这些实例中提炼出来的,虽然精简但足以说明业务流程的实例。并且这些提炼好的实例本身就可以当作交付的验收条件。
在不修改需求的前提下,用自动化测试来验证需求。需要根据需求实例生成自动化的测试用例和代码。生成的自动化测试用例可以用来验证程序是否符合需求。由此产生的自动化测试用例可以准确地描述系统是做什么的,由于和需求实例同步,因此也是最新的,所以它是提取“活文档”的可靠来源。
重构需求的同时,频繁验证:频繁验证的重要性不用多说,在任何一种软件开发方法论,例如敏捷、BDD中它都被反复提及。频繁验证一定是所有过程实施中需要不断进行的工作。有很多种方法可以用来实施频繁验证,例如持续集成等等。
利用工具,提取组织良好的、易于寻找的、前后一致的活文档:使用工具,可以从自动化测试用例中提取出一份活文档,这样的文档是最新的,最能描述系统行为的。通常来说,需求实例说明和自动化测试用例做好了,从中提取出一份好文档也是水到渠成的事情。
原书中的流程图是这样的:
翻译成中文是酱紫的:
我们如何实践实例化需求
1、从目标中获取范围:
当咨询老师询问我们的业务目标时,我们先给老师介绍了某个完整的业务流程。于是老师问道,你们的目标就是实现这个购买的功能吗?但是,随着讨论的深入,我们意识到,对于我们来说,业务目标并不是这个,而是——实现系统的透明转移!这意味着我们要保证整个遗留系统在用户眼中的样子和现有系统不能有区别。而现有系统的文档不完备,而且,即使有文档,也未必表示现有系统和文档完全一致。这就更需要我们使用实例化的方法来进一步探讨需求。
2、从协作中制订需求:
第一次讨论是产品经理、开发经理、测试经理、咨询团队共同进行的。后来我们又经过了一次讨论,包含了整个开发团队。不同视角的人员的参与能保证需求考虑的全面性,以及建立起一套统一的描述语言,从而在第一时间消除理解偏差。事实上,我们在讨论中,建立起来了本项目的领域模型,而此后的讨论也一直确保探讨的各个名词或术语的范围都在领域模型之中。
从哪里梳理领域模型中的词汇呢?一切都从系统的工作流开始。从工作流中,我们可以识别出基础场景、异常场景,还可以从系统交互的接口中识别出校验的各种情况。这里有一点需要注意的,就是不仅要画出系统之间的调用流程,并且需要明确出系统之间传递的数据有哪些。识别得越明确,对于后面的举例工作越有帮助。
3、举例说明:
将工作流中识别出来的种种情况,用具体的例子来说明。例子应该包含前置条件、输入、输出。前置条件指的是场景发生时,未作为输入传递到本系统中,但是已经存在,且对业务产生影响的数据。当大家用说的方式解释不清的时候,举例子是自然而然的选择。事实上,即使你觉得能说清楚,也应该举例,以免大家的理解有误差。甚至在场景特别复杂的情况下,我们会使用流程图来辅助举例。
4、提炼需求说明:
将例子整理成更加精炼和清晰的表格。
5、在不修改需求的前提下,使用自动化测试验证需求:
这一步由咨询团队指导我们使用Cucumber工具根据业务规则实现自动化测试用例。
6、重构需求的同时,频繁验证:
通过持续集成和自动化测试的方式频繁验证代码的实现是否符合业务规则。
7、利用工具,提取组织良好的、易于寻找的、前后一致的活文档:
文档一定是在每个迭代之前,针对这个迭代要做的价值点,整个团队进行了需求澄清之后,再形成的。这样可以保证这个文档在这个迭代的进行中是权威的,有效的。当价值点做完了之后,再做新的价值点的时候,整个团队再对需求进行澄清,再对已澄清的需求进行描述。这样形成的文档是可以自我生长,自我描述的,不断更新,易于维护的。目前我们生成的文档是这样子的,包含了当前迭代中需要的工作流、业务规则和实例化说明。
实际上,这离作者所说的“活文档系统”还是有一定的距离。活文档还有一个更加重要的部分,就是根据业务规则实例形成的自动化测试用例。当规则实例不断更新和增长时,自动化测试的用例和代码也不断更新和增长,永远都是最新的,最能描述系统行为的,一目了然的。而自动化测试的用例和代码通过BDD的一些工具能自动提取为html文件或者pdf文件,形成一份真正的“文档”,这也就是作者书中描述的“活文档系统”。这个也是我们未来要着重进行的部分。
(本文于2017-07-07首次发布)