摘要
精益风暴正进行得如火如荼。当世界上最出色的汽 车制造商之一通过运用精益达到顶峰时,我们已经很难脱离这场社会变革了。当然,在编写软件这件事上运用精益理念,我们同样也可以更上一层楼。但是在软件测 试方面呢?我们在厨房里做出了丰盛的大餐,但却在交付给饥饿的食客的过程中失败了。可想而知,如果在等了一个小时才得到他们的食物,谁也没法作出友善的评 价了。所以,我们必须在干盐奶酪凝结成块之前把比萨饼交付给食客。
同样地,在编写软件时,也许我们非常擅于总结需求并且将其转换为代码,但我们也需要通过部署和测试把美妙的代码转变成有用的软件。通常,我们并不像强调前者一样地去强调后者。你手上有那种已经在 backlog 里显示编码完成的软件吗?它们是否都在等着被部署,被测试,被审批,被实现实际运用?你的任务是否每一两周就反复一次,但还是一年才发布吗?当你最终完成部署,是不是随之而来的就是一个超大的庆功宴?然后还会在这种奢侈浪费中乐而忘返。或许你的精益或者敏捷之变革还需要来得更猛烈点。
介绍
如果需要一些片段来证明你的电影作品是围绕伦敦展开的,可以给几秒关于皮卡迪利广场的霓虹灯和车流穿梭的速写。
你可能会亲历亲为。但到那儿吃点什么呢?如果你喜欢寿司,那么得离开皮卡迪利广场。往西南方向沿着干草市场朝圣詹姆斯公园走。Yo!寿司店就在右手边的两个街区之后。我建议你坐在吧台旁,而不是专门的餐位,这样你就可以看到工作中的厨师了。之所以这么建议,是出于一个很大很紧迫但绝无刺伤之意的缘故。在 Yo! (当然了,还有分布全世界的其他优秀寿司店),厨师在厨房和食客之间有一个直接的联系:传送带。传送带会经过吧台的每一张桌子每一个用餐点。你可以和厨师对话。想吃点什么,可以直接从传送带上拿下你想要的食碟。当然了,如果你愿意,也可以点餐,但你完全可以通过传送带上的食物吃完这一餐。恩,这与软件和部署有什么关系呢?
Yo!的大厨有一点比我们软件人员更胜一筹:他们可以看到产品的整个生命周期。如果每个人都在吃加利福尼亚卷,他们就知道这种食物很快会被消费掉,要再多做一些。如果没有人在吃甜食,他们就知道该停止做甜食。因此可以持续地交付提供食物流(我曾经花更多的时间去等汉堡)并且保持低库存的新鲜成品寿司。
我们开发软件时可不是这样的;开发人员(厨师)把代码传给负责部署的人(厨房帮手),有人负责测试(食物卫生检查员),然后业务分析员(侍从)在运营团队(另一个侍从,通常在餐馆里只见过一次,那个耀武扬威地给你带来看似天然而成的美食的家伙)完成最终实施前演示给客户(食客)看。
部署
做软件和食物有什么共同点呢?这两者都需要尽早地提交,不然就会变质。你们的团队通常需要多久来部署做出来的软件?你们需要多久在和成品系统相同的测试环境下做部署?
有些代码从未见天日,有些代码在运行之前等了 N 久。这种等待是经典浪费表现之一;一些隐藏在代码里的业务价值,不到生产部署那一刻都难以体现出来。所以,首先你需要置之于测试环境,否则就是厨房里的自我腐蚀。
为什么我们会推延这个极其重要的过程呢?
- 太多的厨师:如果你必须给你的代码在一个版本控制系统里打标签,提交给另一个团队去编译,然后再让另一个团队去部署,那会是很艰巨的任务。
- 丢失的配料:你刚刚在崭新的不可思议的. NET 3.5 里完成了新应用。天哪,竟然没有服务器去跑这个版本的构架(我曾亲身经历——最终花了六个月去实施那个应用)。
- 没有食品加工器:手工部署更易出错更耗时。
- 匮乏的培训:我们不培训开发人员却让他们写代码,就好比生命周期的其他部分根本不存在。
但是,我觉得这只是养成了草草了事的厨房习惯:不到有业务价值需要部署时,我们都很难想到去部署代码。等到你准备好了,你有时间去做部署的 story 吗?对部署的关注是否意味着你的代码需要修改呢?也许有很多原因导致不能将代码实现成品运行,但将部署拖延到测试环境则是毫无理由的。
在开发过程的一些要点上,你必须用实际可行的方式来部署代码。基于下面几点来进行代码部署:
- 为了知道是否具有可行的部署流程(你有足够的侍从吗?传送带可以正常工作吗?)
- 为了功能化地测试(自己尝一下,口味怎么样?),
- 为了保证代码能够在它将赖以生存的生态环境中正常运行(食物可以放在那些碟子里吗?)
第一点和最后一点似乎被一些项目经理遗忘了:部署代码的方式能够也确实会改变你开发代码的方式。我曾经做过一个项目,在那个项目中,很容易看出代码在开发操作系统中运行,而所有的外部服务则全线瘫痪了。实际上,代码并没有通过更趋于实际的操作系统和生产中间件的测试。我们或许可以在代码编写上显示卓越的进步,但在代码提交上却显然不足。我相信这是我们失掉项目的原因之一。
我做过很多项目,而这些项目的问题都是在于我们不能简易地实施最终成品部署。编写的代码和产品应用服务器版本不匹配;代码达不到安全政策(“你是什么意思,我不能从那里连接因特网?”),更具代表性的,代码只能在一位开发员的电脑上运行成功。
拖延代码部署导致了我们用长期成功的成本来展示仅仅表象的进步(“每一次反复我们都在提交代码”)。实际上是打赌在项目的尾期可以直接实施部署代码。选择早日部署其实不是一种打赌,更是一种投资。如果在做主餐之前做甜点,那你就避免了在上餐前十分钟才去烧法式炖蛋的风险。敏捷方法论就是用来帮助我们管理风险的。有时在独立环境下开发软件我们似乎也可以控制风险。但如果供给需求很大时,那么你需要的是餐馆厨房,而不是便携式烤架。
测试
在 “地狱厨房”或者“拉姆齐的厨房噩梦”里,你会津津有味地看一个崇尚完美的大厨为倒霉的业余厨师毁了一条比目鱼而破口大骂。但是我们能像对待一盘变质的食物那样扔了代码吗?我们需要的是测试。量要多,也要尽早。首先要保证将代码部署到适当的环境,这样你就知道在测试你要的东西了。然后开始疯狂于各种测试:冒烟测试,各种用户验收测试套件,回归和探索性测试。基于对大型软件项目的观察,我得出的结论是我们并没有疏忽配置测试员,但我们没有给予 QA 流程它们应有的重视。
在 IT 项目里测试员的数量一般要多于开发员。一名测试员需要能够检验多个开发员的输出结果。但是你怎么知道需要多少名呢?你是否已经合理使用了那些在岗测试员呢?在你的机构里也许实情是这样的,比起已经被测试的情况,代码则是花更长的时间等待着被测试。你是否尝试去把一些测试的担子转移给开发人员呢?是否尝试去减少开发人员的输出结果,以致到一个可以保持测试平衡的程度呢?或者不断努力,希望以某种方式解决这个问题呢?
约束理论
这个章节的名字是借鉴 Theory of Constraints(约束理论)。TOC 是由 Eliahu M. Goldratt 在 1984 年出版的,假定我们必须考虑一个组织(或者项目)的目标以及:
1)我们必须以实现目标为导向而串联我们的所有活动。
2) 不管任何时候,都会出现影响组织实现目标的瓶颈。在这种情况下,提交系统其余的部分没有任何意义,除非你可以扩大或者回避这个瓶颈。
在 TOC 之前,制造商为了降低单个部件的成本(从而降低成品的成本),他们的工厂和仓库堆满了过量的库存。这对成品的提交是有害的;他们必须搜罗整个仓库去寻找所需部件来完成产品,或者等待很久直到一个大批次的部件生产完成,才去生产最紧急的订单所需的部件。在软件业务中,不同的库存都有自己的瓶颈。部署和测试是实现软件成品运行的关键步骤;了解瓶颈对于在整个机构里实现可行软件的最好流程是非常重要的。
第一点是非常明显的,为了最终应用你必须部署代码。但如果置之不理直到最后一分种的话就好象建立一个超大的库存,然后企图用开夜车的方式度过最后的阶段。但是在最后的阶段有多少单元可以完成呢?如果有一系列的 backlog 修改堆在系统瓶颈前,那么即使加速工作也不能帮你解决。事实上,加速工作只会使事情更糟。
第二点在于你的团队和团队工作量:哪里任务堆积如山?以我的经验,你不会经常短缺开发人员,但会短缺有待展开的 story,或者当 story 被认为已经完成时短缺 QAs 去测试代码。一旦你了解了什么是你项目里最难办的瓶颈,你就可以做些什么来迅速提高生产量了。
在更具连续性的硬件集成方面,把开发人员转到系统集成的任务上,或者开展开发者测试,这样做是非常好的投资。
为什么在没有确信是否能实施部署时,我们会在写代码和编译代码上花这么多功夫呢?如果你一直都很了解你的最终目标就是把软件部署成产品,并且牢记倘若不能把软件部署成产品,就得忍受拉姆齐式的咒骂,那么你就一定可以处理和安排合理的优先级别了。当你检验了项目的每一个部分,评估了他们对目标的贡献程度,然后就可以采取纠正措施了。
在最近的一个项目里,我使用 Goldratt 的书《目标》里的一个章节的知识,解决了在持续性集成服务里的瓶颈。在这之前,大量的开发员都在处理一些反复的“story”,他们挣扎于不断变化的复杂的持续的集成流程之中。试图处理产品 bug 修整的开发员,需要等很长时间才能查明那些小 bug 修改登记是否已经通过 CI 服务审核。我为 50 位在部门主分支工作的开发者制定了专门的持续性集成服务。老的 CI 服务则被用于生产支持分支。我们通过做一些改动来扩大瓶颈,这样不同的工作流就不会为资源而竞争了。
净效应就是当工作结束了,我们对产品 bug 修正或者 UAT 修正的反应时间得到了很明显的提高。另一个很好的副作用则是下一个版本的工作人员可以更快地得到代码。当有 50 名开发员致力于同一个代码库时,反馈循环的提高是很有必要的。
下一步是什么?
在过去,开发员认为如果代码在他们的电脑运行成功,那他们的任务就算完成了。我想我们得往前走了:现在的开发员当知道他们的代码通过了 CI 服务会非常开心,那他们的任务才算完成了。正如我们所达到的,必须把他们的目标改变成编写可部署的代码。下面是要做的:
- 当代码在现实环境中实现,那么这个 story 才算结束。你所定义的“已完成”必须包括“已部署”。
- 必须在健全的系统上测试。轻量级的组成成分是不能证明代码可以在企业级系统里运行的。如果你能够自动化地测试,非常棒!你能够在集成测试环境下运行它们吗?或者他们只是可以在你的电脑里运行无误?
- 你的成品部署流程在真正实施之前必须排练成千上百次。尽可能地自动化部署过程。那还是不够的;我还建议把部署脚本看成持续集成构建的一部分去执行。
一旦知道代码被部署了,那么就离完成更近一步了。只有当代码在产品环境下运行才说明你的任务确实全完成了——并且食客们没有任何抱怨。
关于作者
Julian Simpson 在软件开发和 IT 运行之间的缺口搭起了桥梁。他花了五年时间致力于基于 Java,Ruby 和.NET 平台的持续性集成,部署,构建工具和版本控制系统的研究。在他的博客 The Build Doctor 和 Twitter 上,他发表了很多关于此类的文章。
他曾在 Agile 2007,XP Day 2007 以及 QCon London 2009 等大会上发表演讲。Julian 和他的未婚妻以及孩子们生活在英国萨里。在业余时间,他喜欢骑自行车,园艺和打扑克。当然不是在同一时间啦。
查看英文原文: Deployment is the Goal 。
译者简介:王菲菲,朱拉隆功大学计算机科学与技术硕士,现就职于某外资 IT 咨询公司。主要从事银行电信业商业智能和数据挖掘分析的咨询工作。
感谢霍太稳对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家加入到 InfoQ 中文站用户讨论组中与我们的编辑和其他读者朋友交流。
评论