BT

如何利用碎片时间提升技术认知与能力? 点击获取答案

拉动的力量:一种新的软件生命周期

| 作者 Elizabeth Keogh 关注 0 他的粉丝 ,译者 王瑜珩 关注 0 他的粉丝 发布于 2009年11月26日. 估计阅读时间: 26 分钟 | CNUTCon 了解国内外一线大厂50+智能运维最新实践案例。

IT项目的起因很多,但是只有一部分软件能够带来真正的改变。软件开发是昂贵的,在预算紧张时,把钱花在刀刃上就变得异常重要。

在这篇文章中,我们将要看到如何把来自精益方法的看板,与来自行为驱动开发的特性注入模版结合在一起,来帮助我们识别最重要的软件,在开发的各个阶段中消除不必要的浪费,用最少的产出来完成目标。

下面的图显示了本文所讲述的新的软件生命周期。这些产出物给每个角色发出信号,促使他们创建更多的产出物,直到完成目标并获得商业价值。

最有价值的软件是还没有写出来的软件

Dan North将软件与金属进行了比较。“生成报告”,他说,“就像铅。很容易得到,还很便宜。但同时也很重,没什么价值,还会拖你的后腿。开发人员耗费大量 时间编写报告软件。为什么不直接买一个、安装上、做一点修改,这样不是也行么?或者使用Excel。除非你是一家销售这些软件的公司,否则不要花钱来构建 报告软件。

“还有些软件是比较难做的,需要配置并与第三方交互,但你还是可以购买。这就像铜”。他谈到了子域(subdomains)支持,这个词来自于Eric Evans的《领域驱动开发》。这就好比一个核心业务是寻找石油的公司,“他们需要GPS系统。虽然不是每个人都需要这样的系统,但是他们需要,而且还有 很多人也需要。那么你可以购买GPS系统,然后集成到你的系统中”。

“因此”,我建议,“如果你有类似的需求,而且这种需求对谁都是一样的,但是还没有人卖,那么你或许希望能有人帮你做一个这样的产品。”

“当然。”他在卡片上写道:

   
LEAD COPPER

Dan说:“这个是铅。我跟CIO们谈谈,问问他们花了多少钱在这上。太多了!”

“那么上面是什么?”我问道。

“上面(左上角)是能快速带来价值的东西。我们有一些软件,它们很容易构建,而且涉及到你的核心领域。这是Agile实验项目的理想目标,可视的、高价值的项目,而且没有人做过。本质上来说它们是差异化的。它们有价值,它们是黄金。”

“然后是右上角,这个象限是白金项目。它们很难做,是核心领域,充满风险而且可能需要和第三方进行交互,非常困难的交互,它们是有价值的差异。”

GOLD PLATINUM
LEAD COPPER

David Anderson定义了四个商业关注点:商品、差异、节约成本和破坏者。“这是必然的”,我说,“总会有人破坏你的这些差异性-你的黄金和白金项目。他们 会盗用你的创意-不需要承担你所承担的风险,因为他们看到它确实可以使用-然后用更低的成本实现。这就是为什么我们需要敏捷,未来的某一点我们必然要改 变,来创建新的差异。”

Dan笑着说:“Chris Matts认为,构建软件只有三个原因:赚钱、省钱、捂钱,这第三个原因非常有意思。LastMinute.com是一个典型的例子,他们通过与供应商签 署排它性协议赢得了所有的市场-剧院、休闲以及任何他们认为具有最后期限,而且人们可以在到期之前取消的东西。回想起来,LastMinute的想法是显 而易见的,但当所有人都来分一杯羹时,逐渐缩小的投资回报导致只有很少一部分人能够坚持下来。

“当然,白金项目成本会比较高,因为他们很复杂,但从这个角度看,它比黄金项目更有价值。”

“好的”,我说,“因此,理想情况下,我们应该每周或每两周就发布新的软件版本,我们需要快速响应商业需求。当然,我们知道有些人会破坏我们的东西。因此 为了保护我们的钱,我们需要有一个最小商业特性集,它要能使复制的成本非常高昂。而且,必要的复杂度实际上是好事,它能使我们保持不同……至少在某段时间 内不同。创建一些差异,然后保持这些差异,这就是我们的工作范围。”

现在,我们知道了如何识别重要的目标和它的范围,下面我们该看看如何实现它了。

愿景促使干系人创建特性集

一旦识别出愿景,最初的干系人会通过思考所有需要参与的人,来确定项目其他的干系人。然后干系人会考虑他们需要哪些东西来实现愿景。我们通常是用这个模版:

作为……
我希望……
于是……

我记得Guardian的编辑们使用这种格式,业务分析师们将之称为“讨要功能”。Chris Matts发现了这个问题,“你将干系人放在了最前面,所以他们会考虑那些能证明他们的东西。换一种方式,我们可以将愿景放在前面。这样人们就不会添加和 愿景无关的东西,有助于减少需求范围的蔓延。事情本来就应如此……”。我将他的建议记了下来:

为了……
作为……
我希望……

特性集对愿景的依赖与开发人员将依赖注入到代码中的方式很相似,并不是因为它是个好主意,而是因为它是必须的,因此有了这个词-“特性注入”。

有时我们发现,将愿景分割成一些小的目标很有用,它们定义了一个终点,而且总是可以回溯到大的愿景。

除此之外,那些提出story的干系人并不总是最后会使用它们的人。Captchas-使用难以识别的图形字母来做图灵测试-就是一个例子。“作为 一个用户,我希望有一个captcha,于是……等等,我不想要captcha,这是浪费我的时间”。我们使用模版来写这个story:

为了……
作为……
我希望……

例如:

为了防止机器人给我的网站添乱
作为论坛的管理员
我希望用户必须要回答captcha才能写注释。

有些特性甚至跟软件没关系!可能我们的story会包含如培训、物流、网络管理、法律等东西。干系人通常都会有软件解决不了的,或者不用软件解决更好的东西,例如LastMinute.com的排他性合同。当我们考虑愿景时,也需要考虑这些。

这些故事通常会很大,以至于开发团队无法处理或估算。Mike Cohn将之称为“themes”,而特性驱动团队称之为“特性集”,我们也这样称呼它们。

每个特性集的输出-例如,别让机器人来烦我-可能不能只依赖一个特性或者一个干系人来实现,我们会在过程中发现,其他的特性集可以实现或帮助同一个产出。当这种情况发生时,我们会再次审视一下我们的特性集。

特性集促使BA(业务分析师)创建用户故事

Chris Matts有一个理论。“你知道人们想要什么么?”

“想要什么?”我问道。

“他们向你要求的。你知道他们不想要什么么?”

“不想要什么?”

“你希望他们要的。你知道还有什么么?”

“还有什么?”

“一旦他们得到了他们想要的,他们就会要的更多。”

从多年尝试瀑布模型的经验中我们得出,我们很少能够事先预知所有的事,试图在一开始就做对是不完美的。干系人想要一些不同的东西,当我们终于将事情做对了,他们又开始要更多的东西了。

通常,我们在用户故事和特性集上做决定时所处的环境,会随着时间而改变,特别是在项目的生命周期内。唯一能让我们知道我们对项目的理解是否正确的方 式就是尽早得到反馈。得到反馈最好的方式,是给干系人一些他们可以实际操作的东西,他们可以体会用户或其他消费者的体验,并调整他们最初的想法。

为了达到这个目的,我们可以先拿掉与愿景实质上不想干的东西,即时它是发布商品或者保护差异的最小特性集的一部分。例如,对于购物车我们只有固定的送货费。修改送货选项或者大宗商品折扣可以放到以后的story中。

考虑大多数在线商店都会有类似的特性集:

为了能销售更多的商品
作为网络销售的头
我希望消费者能够在线订购商品,并能收到商品

它可以被分解为多个用户故事:

为了能销售更多的商品
作为网络销售的头
我希望消费者能够订购商品
为了能销售更多的商品
作为网络销售的头
我希望消费者能够将商品放在篮子里而且能够订购篮子(里的商品)
为了能销售更多的商品
作为网络销售的头
我希望消费者可以修改送货选项
为了能销售更多的商品
作为网络销售的头
我希望当(消费者的)订单超过一个最小值时,提供免费送货

当这些被实现出来时,第一个用户故事可以让我们从干系人那里得到美学方面的反馈,让他们考虑其他用户故事,例如放置相关商品的广告,这可以为项目愿景的实现添加一些不同点。其余的story可以晚一些再添加进来,我们可以从这些用户故事中分别得到反馈。

不是一定非要让BA来编写这些用户故事,不过通常他们对此比较在行。

用户故事促使QA创建场景

当QA测试一个特性时,他们通常会做三件事:

  • 设置应用程序运行的初始状态、数据等
  • 在测试环境中执行一些步骤
  • 检查输出结果

然后他们设置不同的环境,检查不同的输出。

当需要自动化这些场景时,我们可以使用行为驱动开发(BDD)和场景语言Given、When、Then来定义这些(场景)。

“领域驱动开发”的作者Eric Evans有一次被问到,场景和用例之间有什么分别。“你无法向业务人员要用例”,我回答,“除非他们懂技术,知道用例是什么。但是你可以管他们要例子, 你可以说‘给我一个场景’”。BDD使用的语言,和DDD中的通用语言,都是为了能够让业务人员和开发人员可以进行交流。

“有一个满足免费送货标准的购物篮,当我结账时,屏幕上应该显示什么?”

“嗯……这个订单免费配送!”

从对话中,我们发现了新的信息,我们可以用这些信息来写一个例子:

有一个购物篮,里面的商品总价为150欧元
当我进入结账界面后
屏幕上应该显示“此订单免费配送”

QA可以将它作为一个验收测试,QA或开发人员可以将它自动化。

不是一定非要QA来写这些场景,不过根据我的经验,他们在这方面极为擅长。

所有这一切帮助我们确信,当我们开始实现时,我们的代码将有最大的机会为客户带来价值。

不是一定非要让QA来编写这些场景,不过通常他们对此比较在行。

场景促使UI专家设计UI

在我主持的一次回顾中,开发人员识别出,UI专家是阻碍他们完成故事的最大障碍。“从我们第一次见到这个故事,到我们可以开始工作,足足用了三天时间。”一个开发人员说道。

“定义页面的样子就需要这么长时间”,我们的UI专家回答道。

“嗯,”我沉思了一会,“有没有办法缩短这个时间?或许只做到开发人员可以工作就行了?”

“可能吧,你需要什么?”

一个开发人员拿起一张卡片,在上面画了一个草图。“就像这个样子”,他说,“只要能表达网站上显示什么就行了。一旦我们得到了内容,就可以开始写代码,使格式易于修改。”

“很好,”UI专家笑道,“因为我们通常没法一次做对。”

这样,当UI专家忙于创建界面时,开发人员可以做一些简单但是可以工作的东西。界面的观感甚至可以作为一个单独的故事。那些内容-页面上应该显示哪些东西,用户应该点哪个按钮-定义了开发人员可以开始编写的第一块代码。

UI不一定是图形化的。例如,一个应用程序可以被另一个系统使用,甚至没有人会直接查看它的输出。使用它的系统成为了用户,UI专家需要知道什么样的数据才是消费系统需要的。

不一定非要让UI专家来设计UI,不过通常他们对此比较在行。

UI促使开发人员编写代码

一个刚毕业的开发人员,Jerry,问我:“我怎么才能知道我的代码是正确的?”

“你怎么知道它不正确呢?”我问道。

“会有人报告一个bug,”他看着QA回答道,“可能是他们,也可能是用户。”

“他们怎么知道那是一个bug呢?”

“他们会使用系统,但是系统没有按着他们需要的那样去做。”

“是的。所以,我们判断你的代码是不是正确的唯一方式,就是通过用户界面。不管背后有什么,都是支持UI行为,并让我们可以在需要时易于修改这些行为。

”UI是用户体验软件价值的唯一途径。创建代码可以使用的界面,是让代码有意义的本质。”

通过首先创建UI,并将所有它需要的类打桩,我们可以快速获得UI是否满足商业需求的反馈。我们可以讨论可能需要修改的东西,我们有更大的机会来创建有价值的软件。如果是另一个系统使用这个UI,我们可以检查两个系统是否能够通信。

代码促使开发人员编写更多的代码

Jerry编写了界面的代码,他尽力让这一层很薄,并将界面使用的其他类stub out出去。然后,他思考下一步做什么。“我需要一个数据库表,表中包含三个列……”

“等一等,” 我建议他,“我们现在就需要数据库表么?我们有什么东西会使用数据库表么?”

“嗯,我们需要使用用户名、邮件地址和用户id来创建用户。”

“做什么呢?”

“这样用户就能通过注册界面在网站上注册了。”

“注册界面需要什么东西来帮助它完成注册新用户么?是不是要在界面里做所有的工作?”

Jerry摇了摇头。“那意味着在一个地方放很多的代码,会让修改和维护变得异常艰难,而且UI的类也会难以测试,所以我们需要尽量让它短小。我知道我们需要使用其他的类,在设计模式中通常可以称之为controller或者presenter。”

我们知道我们将来需要修改某些代码,因为Chris说过:“人们总是想要其他的东西”,或者因为业务上的差异性已经变化了。“也就是说,UI需要将实际的工作分配给其他的类。我们现在能否知道我们的Controller将会使用一个数据库中的表?”

“不能……”

“好的,”我说,“让我们想想controller需要做些什么。”

我们将代码分解到不同的类或模块中。单一职责原则是一个好方法。我们可以问问自己,“这段代码应该做什么?不应该做什么?哪些东西是其他类的职责?我们将会如何使用这个类?”如果我们对每一行代码都考虑这些问题,那么所有的代码都会变得易于修改。

在单元级别,BDD语言仍然有用。我们可以使用“应该(should)”来帮助我们将职责拉出来。

“现在,我们知道controller的行为是什么,它如何使用其他的类-user repository和user information,”Jerry说,“我们还知道它应该响应UI的事件。”

我说,“我们可以使用我们的controller写一个fake UI类示例,来描述我们希望从controller中得到的东西。这可以帮助我们写出能给UI带来价值的最少的代码。作为一个有用的副作用,它还能为我们带来代码的单元测试和说明文档。”

首先,我们写了示例,或者叫单元测试。然后利用IDE来创建还未创建的类和方法,因为这样比较快。领域驱动开发(DDD)可以保持设计和代码 语言与业务一致,BDD帮助我们关注行为、价值和如何使用类的示例。在发现某些新的、不寻常的或者遗漏的东西时,这帮助我们与业务建立有用的对话,而且由 于代码的设计和业务保持一致,对代码的修改难度,将与引起这些修改的业务流程修改的难度一致。

当我们完成了对每个类的编码后,我们继续对这个类的合作者编码,直到所有stub出来的类都有一个真正的实现。通过这种方式,我们可以保证我 们只做了那些可以为UI提供价值的事。我们不会为那些我们认为将来可能会用到的方法或数据库列来写代码,因为这在当时不是问题。这也帮助我们简化我们的设计。

好的设计可以在我们更深入理解业务流程时,简化代码修改,并为创建UI需要的价值提供更多的选择。就像Chris说的,“在金融领域,选项拥 有价值和过期时间。真正的选项-真实世界中的选项-也有价值,以及一个再没有选择余地的时间。有时,推迟决定并保持选项是值得的。”好的设计需要更长的时 间,但是这样是值得的,因为它让我们能够推迟决策,或当我们有更好理解时能够改变我们的决定。

这就是为什么我们使用好的设计实践,和像单元测试与系统测试这样的自动化反馈环,都是为了让修改变得容易。

不一定非要让开发人员来写这些代码,不过如果你正在写,那么你最终会成为一个开发人员。

我们为下一件最重要的事做准备

让我们看一个例子。

有一个简单的愿景:noughts and crosses游戏。出于练习的目的,我们假装没人玩过这个游戏,也没有人看过战争游戏。我们有一个很大的特性集,叫做“游戏规则”。

我们可以为这个特性集写一个故事。

为了玩Noughts and Crosses
作为一个玩家
我希望当有三个相同图形在一行时,有一个人获胜

下面是这个游戏的一个场景,以及对应的UI,使用JBehave来自动化:

给定一个这样的网格
OO.
XX.
X..
当玩家点击a3时
网格将会变成
OOO
XX.
X..
并显示“O 赢了!”

下面是定义GameModel应该如何工作的示例代码:

@Test

public void shouldNotifyObserverWhenTheCurrentPlayerWins() {

    // 给定一个X将要获胜的布局

    GameModel game = new GameModel();

    game.playerActsAt(0, 0);

    game.playerActsAt(1, 0);

    game.playerActsAt(0, 1);

    game.playerActsAt(2, 0);


    MyGameObserver observer = new MyGameObserver();

    game.addObserver(observer);


    // 当X获胜时

    game.playerActsAt(0, 2);


    //  X应该是当前的玩家

    // 而且X获胜了

    ensureThat(observer.game.currentPlayer(), is(Player.X));

    ensureThat(observer.game.message(), is(“Player X wins!”);

}

一旦我们完成这些示例,就可以开始编写代码了-GameModel类-来帮助我们使这个示例运行通过。

在这个例子中,我们将GUI作为观察者mock出来,使用为这个例子创建的类-MyGameObserver。通过mock每个单元的协作者,我们可以保证示例与其他协作者没有耦合,包括那些还不存在的协作者。这让改变变得容易。

我们没有分数,我们不知道X是不是一直都先走,我们不知道有人获胜后应该如何-我们可能需要重启程序。我们只为这个场景、这个故事、这个特性集和这个愿景写了足够的代码。我们保持代码易于修改,这样就可以在以后为更多的场景和故事添加代码。

当我们完成时,我们就发布!

当所有的协作者都被真正实现时,我们就写完了这个场景的代码。

当一个故事中的所有场景完成时,我们就完成了这个故事。

当一个theme或特性集中的故事都完成时,我们就完成了这个特性集。

当涉众确认一个愿景的所有特性集都完成时,这个愿景就可以发布了。

在每个阶段,我们都努力尽快得到反馈

通过自动化示例和场景,我们能够发现代码是否能够工作。

通过自动化集成和部署到真实环境或相似的环境,我们能够发现是否有之前没有意识到的技术问题,或者没有考虑到的涉众。

通过尽快给涉众展示应用程序,我们能够发现我们是否可以完成愿景。

在敏捷和精益方法中,还有很多其他的反馈环。这意味着我们在开始工作时要假设可能会犯错误。我们不需要在一开始就保证每件事都是正确的-我们可以从“刚好够用”开始。

在每个阶段,我们都努力减少浪费

精益生产努力减少库存-那些还没有安装到可工作的汽车上的零件。用同样的方法,我们努力减少那些没有添加到可工作的特性集和愿景上的用户故事、场景、示例和代码。在看板系统中,信号被传递到每个阶段中,这样每个工作站就知道要为下一个工作站生产一些特定的东西。每个被生产出来的部分都是因为某个工 作站需要他们来满足客户的需要。这就是一个拉动系统。

在其他生产系统中,生产线上放着成盒的剩余零部件以备使用。人们需要围绕这些部件工作。这些部件需要维护、保护、空间等,如果商业需求的改变超过一定程度,这些部件就要被丢弃。

在一些软件项目中,我们也做了同样的事。我们创建特性集是因为我们有足够的预算。我们对详细分析那些我们相信会在3个月后需要的故事。我们为我们认为有趣的特性创建视图。我们创建数据库列和表来存储没有人会使用的信息,以及更多的视图来收集信息。所有这些都需要维护,需要和他们一起工作,而且在商业 需求改变时扔掉。

通过在需要使用时才生产需要的东西,精益生产线可以避免这些浪费。相似的,我们也可以在我们的项目中,通过只在需要时才创建来避免浪费。

这就是我们的拉动软件生命周期

最有价值的软件是还没有写出来的软件。
愿景促使涉众创建特性
特性促使BA创建用户故事
用户故事促使QA创建场景
场景促使UI专家设计UI
UI促使开发人员编写代码
代码促使开发人员编写更多的代码
我们做到能够支持下一件最重要的事
当我们完成时,就发布。
在每个阶段,我们都努力尽快得到反馈
在每个阶段,我们都努力减少浪费。

在生命周期中的每个角色都不需要一定是全职的-例如一个开发人员也可以做BA或QA。在这种情况下,他会带上BA的帽子。如果一个人不考虑他将从下一个阶段中得到什么-例如代码-那么就几乎不可能得到他在下个阶段需要的东西-例如用户故事-因此让不同的人扮演这些角色还是值得的。

不管用什么方法论,总是有不止一种方式来实现同一个目标,不止一种方式来应用方法论,以及不止一种拉动系统适合你。

当Dan North发明BDD时,他说:“程序员想知道从何处开始,测试什么不测试什么,一次测试多少,如何为测试命名,以及理解测试失败的原因。”

这个概括应该能让你知道从何处入手,什么好用什么不好用,一次应该做多少,如何称呼正在做的事,以及如何在软件生命周期的每个层次获得反馈;这样不管编写什么软件,都是好的软件。

查看英文原文Pulling Power: A New Software Lifespan


感谢刘申对本文的审校。

给InfoQ中文站投稿或者参与内容翻译工作,请邮件至editors@cn.infoq.com。也欢迎大家加入到InfoQ中文站用户讨论组中与我们的编辑和其他读者朋友交流。

评价本文

专业度
风格

您好,朋友!

您需要 注册一个InfoQ账号 或者 才能进行评论。在您完成注册后还需要进行一些设置。

获得来自InfoQ的更多体验。

告诉我们您的想法

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

好! by huang edgen

通俗易懂的文字叙述让我一步步地理解第一个图的意义,阅读完了回头再看看第一个图,更加深了理解!

建议译者注意“响应”不是“相应”。

而且,如果能把第一个图也ps成中文的话,效果应该会大大地提升。

Re: 好! by 刘 申

已经修正:) 感谢提醒~

需要的是经验和能力 by lou yun

开发过程的方法论往往缺乏对人员素质的具体要求,这就造成执行上的问题。
而且阶段的合并、尺度的把握也很难处理,一句话:需要经验和能力!

翻译质量 by Cai John

随便看看觉得晦涩,干脆去看英文,发现第一句就翻译错了,汗。

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p

当有人回复此评论时请E-mail通知我

4 讨论

登陆InfoQ,与你最关心的话题互动。


找回密码....

Follow

关注你最喜爱的话题和作者

快速浏览网站内你所感兴趣话题的精选内容。

Like

内容自由定制

选择想要阅读的主题和喜爱的作者定制自己的新闻源。

Notifications

获取更新

设置通知机制以获取内容更新对您而言是否重要

BT