看板任务管理

作为一个开发团队的管理者,例如当你是一个团队的项目经理的时候,任务的完成情况通常是你最关心的内容之一,比如说分配的任务是否能够按时间完成,整个项目的进度是否尚在计划之中,团队内的人是不是都在高效地工作,大家有没有什么困难,这些是你经常会关注的问题。在软件开发团队中,任务的分配、跟踪和管理通常是这个团队管理者的一个重要的工作内容。

1 从问题谈起

我曾经碰到过一个项目经理,她管理着一个团队开发一个 web应用,团队里开发人员大概 10个左右,测试人员 3个,业务分析师 1个人。对于任务的管理她是这么做的。通常,她会将需求分析人员分析得到的需求给每个人分一些。然后每个人在领到任务之后会给她承诺一个大致的时间点。整个项目大致的交付计划用一个 excel表管理着,根据客户要求的交付时间点,并且考虑到一些需求之间的集成测试关系,定出了每个需求的大致交付时间点。只要每个开发人员承诺的时间点和期望的相差不大,她都可以接受,每个开发人员这样就知道自己应该在什么时间点交付什么东西。

一切本该很完美,但是不和谐的问题不断出现。最经常发生的事情就是大家在承诺的时间点快要到的时候不能按时交付,每次她询问进度的时候,会被告知还差一点就完成了。通常的说法是“底层部分已经做完了,或还差页面部分就可以搞定了”,然而实际情况是又过了相当的时间才真正完成。当然也不是没有按时交付的需求,但是她发现也许是大家经常加班,已经开始疲倦了,有时候明明很简单的可以提前完成的需求,大家还是到最后一刻才交付给测试。

也有的开发人员拿到自己的那一批需求之后,会批量工作,把若干个类似的需求的底层逻辑全部实现,然后再实现上层内容。她默认了这种做法,就像这位开发人员说的“这几个需求都差不多,只要底层做好了,基本上就都差不多完成了”。虽然这部分工作早点和其他人一起集成测试会比较好,但是他这样做也只能推后集成测试的时间点了。还好承诺给测试团队的交付时间点还在 1个月之后,只要 1个月之内能够完成这些需求就可以了。

还有一些其他的问题,比如有的新人经常碰到问题,然而出了问题并不会主动问其他人,而是在胡乱尝试中浪费了时间。组里还有个开发人员非常激进,经常花时间去重构代码,追求完美的架构设计,进度很让人担忧。组内的开发人员有时候还经常被其他项目的事情打扰,因为有几个人刚刚从上一个项目中调过来,上个项目的有些问题只有他们熟悉和有能力解决。她就不止一次发现,有一个开发人员经常在修复其他项目的 bug。

她会不定时地去询问每个开发人员的开发进度,当需求的计划交付时间点逼近的时候,这种检查会越来越频繁,开发人员感受到压力,有时候甚至需要加班来完成开发工作。然而尽管她花了很多精力去跟踪和检查每个需求的完成情况,还是有很多出乎期望的事情在不断发生。尽管她一直相信说,只要开发人员们能够完成任务,采用什么方式她是不干预的,而具体的时间也是由他们自己分配的。但是她渐渐感觉到,任务越来越不可控,计划通常无法按时完成,每天对大家的检查花了大部分时间,然而却不能揭示出真正的问题。

运转良好的项目都差不多,而问题项目的问题各有各的不同。尽管每个团队的问题可能不完全相同,但是当我们审视这些项目的运作和管理方式的时候,不难发现一些诸如多任务并行等共性的问题,这些问题给软件项目带来了各种各样的浪费。当一个团队采用瀑布开发模式的时候,开发阶段全部结束之后测试人员才会介入,开展测试活动,在一个通常很漫长的开发阶段内,各种开发活动中的浪费、估计的不准确,以及成员自己的拖沓、被打扰、问题阻塞等,都被掩盖住了。只要在最终时间点前能够全部开发完成,不管是前松后紧,还是加班熬夜,都已经成了项目开发的常态。项目经理只能看到交付的最终时间点,问题不能及时的暴露,而等到问题被暴露的时候,可以使用的调整手段也非常有限。

这样的一种团队生存状态在外部环境要求短交付周期,需求允许经常变化的情况下显示出了极度地不适应。市场环境的变化驱动了软件需求的变化,这种变化催生了缩短交付周期的诉求,较短的交付周期使得人们可以不必去预期过于长远的需求,具备根据市场的变化快速地制定和调整软件需求的能力。而当交付模型由几个月的瀑布模型转变为数周甚至更短的迭代模型的时候,我们在前面谈到的团队中的各种浪费、低效、半成品堆积等问题,就会急剧地爆发出来。

熟悉敏捷方法的读者可能都知道,敏捷方法包含一系列实践来帮助团队实现短周期快速交付,更好地响应需求变化。比如说 user story方法,将需求从用户价值的角度进行组织,避免将需求从功能模块角度划分。小粒度的用户故事可以在一两周的迭代内完成开发和测试(并行开发),从而可以缩短交付周期。问题是,在敏捷团队内,我们是如何有效管理大量小粒度 user story,同时避免上述项目管理中的问题呢?下面我们结合敏捷开发中的看板工具来看看敏捷团队是如何管理任务的。

2 可视化看板任务管理

看板源于精益生产实践,敏捷将其背后的可视化管理理念借鉴过来,经过一番改造,形成了有自己独特风格的可视化管理工具。曾有人总结过 scrum和 kanban的使用 [1] ,而很多时候,我们也将它叫做迭代状态墙。

先看看我们怎么样能用这个状态墙来管理迭代任务。说起来其实是一个很简单的东西。

通常一个迭代的状态墙上反映了某一个迭代的计划和任务进展情况。状态墙上按照一个迭代内团队的典型开发活动分成几栏,例如“待开发”、“开发中”、“待测试”、“测试中”、“测试完成”等。在一个迭代之初,我们会将计划在本迭代完成的故事卡放到“待开发”这一栏中。可视化状态墙的一个好处就是所有团队成员都可以实时地了解到本迭代的计划和进展情况。开发人员领取任务时,就将他领取的故事卡片从“待开发”移到“开发中”,同时贴上带有自己名字的小纸条。当他开发完成之后,就将故事卡片移到“待测试”一栏。我们的测试人员看到这一栏里有待测的故事卡时,就取下一张,移动到“测试中”,开始这个用户故事的测试,测试完成后,就将故事卡移动到“测试完成”一栏。如果测试人员发现了一个 bug,那么他可以用红颜色的卡片记下这个 bug,然后放到待开发这一栏中。在状态墙上,除了用户故事、 bug之外,还会有一些诸如重构、搭建测试环境这样的不直接产生业务价值的任务,这三类任务用不同颜色的卡片,放到状态墙上统一管理。

这样一个简单的工具,是如何帮助我们消除浪费、解决项目管理中的问题的?让我们逐条分析一下看看。

2.1 如何减少返工带来的浪费

返工是软件开发过程中的一大严重浪费。比如说开发人员开发完成的任务交给测试人员测试的时候,关键流程不能走通,阻碍了测试进程;交付给客户的东西被客户说 “这不是我想要的东西 ”;分析人员将还没分析透彻的任务交给开发人员,在最后验收的时候发现开发人员加入了自己的一些 “发挥 ”。这些都会造成返工。返工意味着没有一次性将事情做对,意味着流程中的上游没有交付高质量的工作,也可能意味着团队成员间的沟通出了问题。

在传统的瀑布流程中,我们往往是期望通过前期细致入微的工作来确保一个阶段的工作被高质量完成之后才移交到下一阶段。后来我们慢慢从失败的经验中学习到,这种方法在变化的需求环境下实在是太过脆弱,不仅不能如愿保证质量,而且会造成更大的浪费,交付周期也不能满足要求。于是我们引入了迭代式开发方法 [2] ,一个需求的分析、开发、测试、验收成了一个小粒度地更连续的过程,在这个小的交付循环中,看板帮助我们以更细节的粒度来管理一个任务每个阶段的工作质量。

通常我们是这么做的。当我们把一张故事卡从“待开发”移动到“开发中”时,这张卡片必须是已经分析完成的。也就是说,当开发人员准备真正开始开发这张故事卡之前,我们的需求分析师们必须保证这张卡片所包含的所有内容和细节已经被分析完成,不再有模棱两可的细节,不再留给开发人员过多的自我发挥和想象空间,而且这些细节必须和客户确认过,而不只是团队自己“设计”的结果。

这一道关看似很寻常,实际上很多项目会在这里出问题。很多时候开发人员开始开发的时候,需求还没有分析完成,很多细节尚须澄清确认,实现上的技术风险还没有被完全排除。也有的分析师善于给开发人员留有大量自我发挥空间,需求过于言简意赅。开发人员开始开发这样的需求时,要么做不下去,要么按照自己的理解做下去。做完了之后分析师一看发现不对,和我想的不一样,于是开发人员返工。最糟糕的情形莫过于最后被客户发现说,这不是我当初想要的东西。

由此可见,确保开发人员挪卡的时候,这张待开发的用户故事已经被真正分析完成,是我们准确实现用户需求的第一步。通过规定这一挪卡的前提,同时辅以用户故事的澄清(由分析师向开发人员澄清)或者反向澄清(由开发人员向分析师讲述自己的理解),可以很大程度上将返工减少到最低。

还有一种浪费发生在测试过程中。测试人员经常会发现,处于“待测试”状态中的一些故事卡,在测试的时候主要的流程都走不通,根本无法进一步展开测试,于是乎不得不将故事卡

打回到开发人员手中。而往往这个时候开发人员已经工作在另一个用户故事上了。要么他停下手中的任务解决测试的问题,要么让测试人员等到这些问题修复过后再测。无论哪种都是不好的选择。

这种问题的一个主要原因是因为开发人员声称他已经“开发完成”,将故事卡从“开发中”挪到“待测试”时,实际上自己并没有对这部分功能进行测试。或者是因为疏忽,或者是因为懒惰,或者是因为过于自信。通过在这个状态转换阶段引入用户故事初验,让分析师在挪卡之前先到开发人员机器上看看是否该故事卡包含的功能被实现了,可以很大程度上提升效率,减少浪费。若分析师在初验过程中发现了问题,那么开发人员马上能以最小的成本进行修复,而不用等到之后测试人员发现时再来修复。而且,分析师初验也提供了一个判断实现是否良好的反馈点,这是我们能够看到一个需求是否被实现并能够真正工作的最早的时间点。

2.2 如何避免多任务并行

多任务之间的频繁切换是一个常见的问题。表现在团队里的成员,特别是开发人员,会在不同的任务间切换。就像前面的故事中提到的,可能这一刻还在实现某一个需求,而下一刻马上就会被叫走去修复某一个遗留版本的缺陷。又或者该人手头被分配了多个任务,每个任务都在进行中,而没有一个处于完成状态。任务切换是导致效率降低的一个重要原因 [3] ,不同任务间的上下文的切换会导致频繁地将任务当前状态在头脑中“压栈”和“出栈”,这些操作会耗费时间。如果完成一个任务需要一个人一天时间,那么两天内这个人可以完成两个任务,但是如果他在第一天同时开始并行工作在这两个任务上,那么完成这两个任务会需要大于两天的时间。

大家可能已经注意到了,在前面的看板图中,处于“开发中”的所有任务卡片上都有一个小纸条,上面标记着正在这张卡片上工作的人的名字。如果说有两个人结对在一个卡片上工作,那么这张卡片上应该有两个名字。这一小小的实践可以帮助我们随时发现团队内某一时刻,是否每个人只工作在一个任务上。

如果这一简单的规则能够严格被遵循,那么当我们看到一个人的名字出现在多张卡片上的时候,我们就知道这个人此刻可能忙着在多个任务之间切换,而每一个任务都将不会在估计的时间点内完成。如果我们看到有人的名字没有出现在任何卡片上,那么他目前大概处于休息状态。团队内的每个人的名字都应该对应在一个小纸条上,如果你此刻工作在某个任务上,那么就将自己的名字贴到相应卡片上,如果此刻没有工作在该任务上,就将自己的名字移去。

我们在领取“待开发”状态栏中的卡片时,保证每次每人只领一张卡片,不要多领,完成了这张卡片之后,再回来领下一张。当一张卡片被认领之后,我们就会对这张卡片进行跟踪,在站会上谈论它的完成情况,谈论实现过程中碰到的问题。当它的进度和估计的可能偏差较大时,我们能够及时而不是在最后一刻察觉到,提供需要的帮助,确保它能够顺利完成。这样一种方式让我们能够将注意力集中到小粒度的需求(例如用户故事)上,更多地关注这些用户故事的流动速度。而当每个小的用户故事能够顺畅地流动起来时,整个项目的交付也得到了保障。

当然这一实践并不能自动保证团队内不再出现多任务并发、拖延、或者做和任务无关的其他事情等问题。可能有些人在做一个用户故事的过程中,突然中断去做了一些其他事情,但是

却没有及时在状态墙上更新自己的状态。重要的是团队要有实现交付目标的共同愿景,能够透明地暴露问题,并且善于利用状态墙来发现和改进自身的问题。对于不成熟的团队,这可能需要一个转变的周期。

如果一个团队的职责共享较好,代码被所有人集体拥有,每个人都被鼓励熟悉和工作在代码的不同部分,那么在这样的团队内便不太会出现把一大块任务事先就明确给某一个人的情况。相反,所有人的工作事先不具体确定,大家会更容易形成某一时刻只领取一张卡片的情况,避免同时工作在多个任务上。实际上,状态墙的使用也可以帮助团队走向职责共享之路,只需要在大家领取任务的时候有意地给人们分配一些之前没做过的内容,同时安排好有经验的人与其结对工作,一段时间之后,团队内的人便会逐渐体会到和之前只是专注在一个模块内不同的工作方式。

2.3 如何减少半成品库存,缩短交付周期

一个需求的交付周期 (lead time [4] )是从它被识别到最终交付给用户手中所耗费的时间。交付周期越短,意味着客户从提出想法到能够在软件中实际使用到这个点子的时间越短。从客户的角度来看,更短的交付周期意味着自己的软件能够对市场变化的更快地响应,因而获得更强的竞争力,同时也意味着能够更快地验证自己的想法。

任务管理的粒度太大会直接导致交付周期变长。最极端的情况是将属于某一模块的任务在一开始就全部分给负责这个模块的人,所有这个模块相关的修改都由他来实现。在一个按模块划分职责,每个人只负责自己具体模块的团队里,通常这个模块的负责人会实现这个模块的所有修改。不然,就是将一个可能需要做 2周到一个月的任务分给某个人。或者更好一点的情况是,单个任务本身不大,但是会将相关联的任务成批地分配给某个人。如果你的团队内也是采用大篇的“规格说明书”等 word文档来组织需求的,那么就要小心,这种问题很可能在团队内已经存在。整个团队没有小粒度频繁交付的概念,习惯了大批量长时间地交付方式。由于批量大,所以估计常常不准,而且时间跨度长,中间也会有更多地干扰因素出现,这些都导致任务不能在开始承诺的时间点交付。开发周期长同样导致测试活动的滞后,极端地滞后就演变为所有开发工作完成之后才能进行测试,这就是我们熟悉的瀑布模式。最终的影响就是需求的交付周期会很长。

传统团队的一个常见组织方式是按照功能模块划分团队成员,明确分离职责,这也会变相增长交付周期。这样的团队通常倾向于按照功能模块来组织半成品任务,而不是按照可以交付价值的完成品来组织任务。习惯按照功能模块来组织开发的团队通常会阶段性得“联调”,不同模块的人带着自己的代码合在一起调试,由于缺乏频繁地集成,这种联调活动的时间经常不可控。团队在大部分时间内通常只拥有一大堆半成品,后续的测试和验收活动没有办法进行,而只能等到团队在某一刻组装出一个完整的功能后才能测试,因此交付周期也会比较长。

因此,如果我们的需求都是按照软件的功能模块划分,而不是按照面向用户的价值来划分的,那么我们在交付用户价值这一目标上,一开始就走错了路。采用用户故事能够把需求以用户能够理解的价值来组织,这一点是我们缩短交付周期的一个重要基础。

我们的状态墙能够揭示需求的交付周期。让我们来看看这样几个场景。

如果我们的需求是按照软件的功能模块划分的,那么通常单个模块的编码完成往往不可测。例如有的团队喜欢将 web应用的上层页面部分和下层数据库逻辑部分划分到不同的模块组,一个用户的需求也会拦腰切成两截,一部分交给上层团队完成,一部分交给下层团队。单个团队的任务完成都不能开展这个需求的测试,于是这些任务就会堆积在“待测试”这一栏。

如果我们的需求很大,以至于开发人员要花费很长的时间(超过 1周)才能完成开发,那么这个需求会在“开发中”这一栏停留很久。大家可以猜到,当一个人同时进行多个任务时,这些任务也会比它们单个依次被开发时在“开发中”这一栏停留更久的时间。

任何一栏中的任务其实都是半成品,只有完成测试,交付到用户手中的需求才是完成品。状态墙上的每一栏都好比一个存放着各种零件的仓库,每一栏中的卡片越多,停留的越久,就说明当前半成品的库存越多,是该得到团队的认真关注的时候了。状态墙将每个阶段的半成品数量可视化呈现出来,让虚拟的数量通过卡片这种物理介质的数量得以呈现。

通过状态墙,我们可以计算出每一个需求的交付周期大概是多久。状态墙上一个用户故事从放到“待开发”这一栏,到它被移动到“完成”这一栏,这一个时间段是需求的整个交付周期的其中一段,也是很重要的一段。通过优化从“待开发”到“完成”的这一个过程,我们可以缩短需求的交付周期。通过比较需求的交付周期和客户对交付周期的要求,我们可以量化之间的差距,然后指导我们的改进。

在我们理解了状态墙是如何呈现一个需求的交付周期后,我们就不难理解瀑布方法是如何让交付周期变长的。在瀑布模型中,全部开发完成之后才会进行测试工作,相当于所有的任务卡片都堆积到“待测试”状态之后,才开始逐一测试。所有开发完成的半成品,都会留存在“待测试”这一仓库中,一直等到所有开发活动结束的那一刻。

当出现库存堆积的时候,就是我们需要改进的时候。如果“待测试”这一栏有太多的任务卡片,那么就说明我们的测试活动没有跟上。有可能是我们的测试环境出了问题,或者是我们的测试人员人力不足。如果太多的卡片位于“测试完成”状态,说明我们的发布和最终交付过程出了某些问题。如果“待开发”这一栏中任务过多,说明我们的计划有可能超出了当前团队的开发能力,或者说反映了开发人员的不足。也有一种情况,那就是“待开发”这一栏空了很久,这可能说明了另外一个问题,那就是我们的分析师的分析速度匹配不上团队的开发能力。一个良好的团队,必然是各种角色协调配合,并行工作,同时他们之间的任务衔接也能够比较流畅。

2.4 迭代产能的度量,计划及其他

团队在每个迭代所能完成的工作量,通常被成为迭代的 velocity(速度),是衡量团队每迭代产能的一个指标。这个指标能够帮助团队进行制定迭代计划。根据团队估计任务工作量的方法不同,迭代的 velocity的单位也可能不同(例如故事点数)。通常,我们只需要在迭代结束的时候,数一数状态墙上完成的任务工作量就可以了。

当我们经历了若干个迭代以后,通常团队的迭代速度会趋于稳定,我们在做下一个迭代的计划的时候,会参考以往迭代的数据。如果上个迭代完成了 15个点,那么下个迭代我们通常也

会计划 15个点左右的工作量,将这些卡片放到“待开发”这一栏中。也就是说,每个迭代结束时,我们都会对状态墙进行更新,将即将到来的迭代的卡片放到墙上,并且将一些处于半成品状态的卡片进行适当的调整。

前面提到,状态墙上可能由三种卡片,除了需求,还可能有 bug和技术任务。测试人员每次在迭代中测出一个 bug,就会将 bug写成卡片,放到“待开发”这一栏。当 bug不多的时候,团队可以在不太影响原有计划的情况消化掉这些 bug,确保软件的质量持续地得到保证。如果 bug太多,则需要做一些计划,将 bug分散到几个迭代里去消化。然而到这个时候,团队可能更需要及时反省一下为什么会出现这么多 bug的原因了。

另一类技术任务也需要和 bug以及需求卡片一起被考虑到迭代计划中去。通常技术任务包括诸如搭建持续集成环境、准备测试环境、重构这样的任务。它们虽然不直接给用户带来价值,但是却是保证软件质量、确保团队效率的重要因素。比如重构类的任务,对于工作在遗留系统上的团队来说可能是需要一直考虑的事情,为了保障新的需求的顺利实现,可能需要有计划地重构之前的一些遗留代码。

bug和技术任务耗费团队成员的时间资源,但是不直接产生用户价值。如果我们衡量团队每个迭代的总体生产能力,需要在计算迭代速度时考虑这三类任务。但是如果我们只考察团队每迭代交付的用户价值的量的大小,那么就不应该包含技术任务和 bug。当一个团队在迭代中花了过多的时间在技术任务上,或者修复 bug上,那么团队就需要回顾反省一下其中的原因,是否是团队的基础设施太差,或者是团队在开发时过于粗心导致太多的 bug,抑或是其他的一些原因。

3 总结

在本文中我们从项目管理中常常出现的一些问题着手,分析了其中的一些原因,然后介绍了如何采用状态墙(看板)来可视化任务管理。在敏捷项目中,状态墙作为一种有效的迭代任务管理工具,已经被广泛地使用。团队利用状态墙这样一种简单的工具,将迭代开发中的日常工作透明实时地跟踪管理起来,能够帮助团队及时发现问题,消除浪费,快速地交付用户价值。希望这些文字,能够对渴望尝试敏捷、改善任务管理和日常运作的团队带来一些帮助。


[1] http://www.infoq.com/minibooks/kanban-scrum-minibook

[2] http://en.wikipedia.org/wiki/Iterative and incremental development

[3] 参见 Lean Software Development: An Agile Toolkit by Mary Poppendieck, Tom Poppendieck第一章

[4] http://en.wikipedia.org/wiki/Lead time

Share

看板任务管理

作为一个开发团队的管理者,例如当你是一个团队的项目经理的时候,任务的完成情况通常是你最关心的内容之一,比如说分配的任务是否能够按时间完成,整个项目的进度是否尚在计划之中,团队内的人是不是都在高效地工作,大家有没有什么困难,这些是你经常会关注的问题。在软件开发团队中,任务的分配、跟踪和管理通常是这个团队管理者的一个重要的工作内容。 ## 1 从问题谈起 我曾经碰到过一个项目经理,她管理着一个团队开发一个 web应用,团队里开发人员大概 10个左右,测试人员 3个,业务分析师 1个人。对于任务的管理她是这么做的。通常,她会将需求分析人员分析得到的需求给每个人分一些。然后每个人在领到任务之后会给她承诺一个大致的时间点。整个项目大致的交付计划用一个 excel表管理着,根据客户要求的交付时间点,并且考虑到一些需求之间的集成测试关系,定出了每个需求的大致交付时间点。只要每个开发人员承诺的时间点和期望的相差不大,她都可以接受,每个开发人员这样就知道自己应该在什么时间点交付什么东西。 一切本该很完美,但是不和谐的问题不断出现。最经常发生的事情就是大家在承诺的时间点快要到的时候不能按时交付,每次她询问进度的时候,会被告知还差一点就完成了。通常的说法是“底层部分已经做完了,或还差页面部分就可以搞定了”,然而实际情况是又过了相当的时间才真正完成。当然也不是没有按时交付的需求,但是她发现也许是大家经常加班,已经开始疲倦了,有时候明明很简单的可以提前完成的需求,大家还是到最后一刻才交付给测试。 也有的开发人员拿到自己的那一批需求之后,会批量工作,把若干个类似的需求的底层逻辑全部实现,然后再实现上层内容。她默认了这种做法,就像这位开发人员说的“这几个需求都差不多,只要底层做好了,基本上就都差不多完成了”。虽然这部分工作早点和其他人一起集成测试会比较好,但是他这样做也只能推后集成测试的时间点了。还好承诺给测试团队的交付时间点还在 1个月之后,只要 1个月之内能够完成这些需求就可以了。 还有一些其他的问题,比如有的新人经常碰到问题,然而出了问题并不会主动问其他人,而是在胡乱尝试中浪费了时间。组里还有个开发人员非常激进,经常花时间去重构代码,追求完美的架构设计,进度很让人担忧。组内的开发人员有时候还经常被其他项目的事情打扰,因为有几个人刚刚从上一个项目中调过来,上个项目的有些问题只有他们熟悉和有能力解决。她就不止一次发现,有一个开发人员经常在修复其他项目的 bug。 她会不定时地去询问每个开发人员的开发进度,当需求的计划交付时间点逼近的时候,这种检查会越来越频繁,开发人员感受到压力,有时候甚至需要加班来完成开发工作。然而尽管她花了很多精力去跟踪和检查每个需求的完成情况,还是有很多出乎期望的事情在不断发生。尽管她一直相信说,只要开发人员们能够完成任务,采用什么方式她是不干预的,而具体的时间也是由他们自己分配的。但是她渐渐感觉到,任务越来越不可控,计划通常无法按时完成,每天对大家的检查花了大部分时间,然而却不能揭示出真正的问题。 运转良好的项目都差不多,而问题项目的问题各有各的不同。尽管每个团队的问题可能不完全相同,但是当我们审视这些项目的运作和管理方式的时候,不难发现一些诸如多任务并行等共性的问题,这些问题给软件项目带来了各种各样的浪费。当一个团队采用瀑布开发模式的时候,开发阶段全部结束之后测试人员才会介入,开展测试活动,在一个通常很漫长的开发阶段内,各种开发活动中的浪费、估计的不准确,以及成员自己的拖沓、被打扰、问题阻塞等,都被掩盖住了。只要在最终时间点前能够全部开发完成,不管是前松后紧,还是加班熬夜,都已经成了项目开发的常态。项目经理只能看到交付的最终时间点,问题不能及时的暴露,而等到问题被暴露的时候,可以使用的调整手段也非常有限。 这样的一种团队生存状态在外部环境要求短交付周期,需求允许经常变化的情况下显示出了极度地不适应。市场环境的变化驱动了软件需求的变化,这种变化催生了缩短交付周期的诉求,较短的交付周期使得人们可以不必去预期过于长远的需求,具备根据市场的变化快速地制定和调整软件需求的能力。而当交付模型由几个月的瀑布模型转变为数周甚至更短的迭代模型的时候,我们在前面谈到的团队中的各种浪费、低效、半成品堆积等问题,就会急剧地爆发出来。 熟悉敏捷方法的读者可能都知道,敏捷方法包含一系列实践来帮助团队实现短周期快速交付,更好地响应需求变化。比如说 user story方法,将需求从用户价值的角度进行组织,避免将需求从功能模块角度划分。小粒度的用户故事可以在一两周的迭代内完成开发和测试(并行开发),从而可以缩短交付周期。问题是,在敏捷团队内,我们是如何有效管理大量小粒度 user story,同时避免上述项目管理中的问题呢?下面我们结合敏捷开发中的看板工具来看看敏捷团队是如何管理任务的。 ## 2 可视化看板任务管理 image1 看板源于精益生产实践,敏捷将其背后的可视化管理理念借鉴过来,经过一番改造,形成了有自己独特风格的可视化管理工具。曾有人总结过 scrum和 kanban的使用 [1] ,而很多时候,我们也将它叫做迭代状态墙。 先看看我们怎么样能用这个状态墙来管理迭代任务。说起来其实是一个很简单的东西。 image2 通常一个迭代的状态墙上反映了某一个迭代的计划和任务进展情况。状态墙上按照一个迭代内团队的典型开发活动分成几栏,例如“待开发”、“开发中”、“待测试”、“测试中”、“测试完成”等。在一个迭代之初,我们会将计划在本迭代完成的故事卡放到“待开发”这一栏中。可视化状态墙的一个好处就是所有团队成员都可以实时地了解到本迭代的计划和进展情况。开发人员领取任务时,就将他领取的故事卡片从“待开发”移到“开发中”,同时贴上带有自己名字的小纸条。当他开发完成之后,就将故事卡片移到“待测试”一栏。我们的测试人员看到这一栏里有待测的故事卡时,就取下一张,移动到“测试中”,开始这个用户故事的测试,测试完成后,就将故事卡移动到“测试完成”一栏。如果测试人员发现了一个 bug,那么他可以用红颜色的卡片记下这个 bug,然后放到待开发这一栏中。在状态墙上,除了用户故事、 bug之外,还会有一些诸如重构、搭建测试环境这样的不直接产生业务价值的任务,这三类任务用不同颜色的卡片,放到状态墙上统一管理。 这样一个简单的工具,是如何帮助我们消除浪费、解决项目管理中的问题的?让我们逐条分析一下看看。 ### 2.1 如何减少返工带来的浪费 返工是软件开发过程中的一大严重浪费。比如说开发人员开发完成的任务交给测试人员测试的时候,关键流程不能走通,阻碍了测试进程;交付给客户的东西被客户说 “这不是我想要的东西 ”;分析人员将还没分析透彻的任务交给开发人员,在最后验收的时候发现开发人员加入了自己的一些 “发挥 ”。这些都会造成返工。返工意味着没有一次性将事情做对,意味着流程中的上游没有交付高质量的工作,也可能意味着团队成员间的沟通出了问题。 在传统的瀑布流程中,我们往往是期望通过前期细致入微的工作来确保一个阶段的工作被高质量完成之后才移交到下一阶段。后来我们慢慢从失败的经验中学习到,这种方法在变化的需求环境下实在是太过脆弱,不仅不能如愿保证质量,而且会造成更大的浪费,交付周期也不能满足要求。于是我们引入了迭代式开发方法 [2] ,一个需求的分析、开发、测试、验收成了一个小粒度地更连续的过程,在这个小的交付循环中,看板帮助我们以更细节的粒度来管理一个任务每个阶段的工作质量。 通常我们是这么做的。当我们把一张故事卡从“待开发”移动到“开发中”时,这张卡片必须是已经分析完成的。也就是说,当开发人员准备真正开始开发这张故事卡之前,我们的需求分析师们必须保证这张卡片所包含的所有内容和细节已经被分析完成,不再有模棱两可的细节,不再留给开发人员过多的自我发挥和想象空间,而且这些细节必须和客户确认过,而不只是团队自己“设计”的结果。 这一道关看似很寻常,实际上很多项目会在这里出问题。很多时候开发人员开始开发的时候,需求还没有分析完成,很多细节尚须澄清确认,实现上的技术风险还没有被完全排除。也有的分析师善于给开发人员留有大量自我发挥空间,需求过于言简意赅。开发人员开始开发这样的需求时,要么做不下去,要么按照自己的理解做下去。做完了之后分析师一看发现不对,和我想的不一样,于是开发人员返工。最糟糕的情形莫过于最后被客户发现说,这不是我当初想要的东西。 由此可见,确保开发人员挪卡的时候,这张待开发的用户故事已经被真正分析完成,是我们准确实现用户需求的第一步。通过规定这一挪卡的前提,同时辅以用户故事的澄清(由分析师向开发人员澄清)或者反向澄清(由开发人员向分析师讲述自己的理解),可以很大程度上将返工减少到最低。 还有一种浪费发生在测试过程中。测试人员经常会发现,处于“待测试”状态中的一些故事卡,在测试的时候主要的流程都走不通,根本无法进一步展开测试,于是乎不得不将故事卡打回到开发人员手中。而往往这个时候开发人员已经工作在另一个用户故事上了。要么他停下手中的任务解决测试的问题,要么让测试人员等到这些问题修复过后再测。无论哪种都是不好的选择。 这种问题的一个主要原因是因为开发人员声称他已经“开发完成”,将故事卡从“开发中”挪到“待测试”时,实际上自己并没有对这部分功能进行测试。或者是因为疏忽,或者是因为懒惰,或者是因为过于自信。通过在这个状态转换阶段引入用户故事初验,让分析师在挪卡之前先到开发人员机器上看看是否该故事卡包含的功能被实现了,可以很大程度上提升效率,减少浪费。若分析师在初验过程中发现了问题,那么开发人员马上能以最小的成本进行修复,而不用等到之后测试人员发现时再来修复。而且,分析师初验也提供了一个判断实现是否良好的反馈点,这是我们能够看到一个需求是否被实现并能够真正工作的最早的时间点。 ### 2.2 如何避免多任务并行 多任务之间的频繁切换是一个常见的问题。表现在团队里的成员,特别是开发人员,会在不同的任务间切换。就像前面的故事中提到的,可能这一刻还在实现某一个需求,而下一刻马上就会被叫走去修复某一个遗留版本的缺陷。又或者该人手头被分配了多个任务,每个任务都在进行中,而没有一个处于完成状态。任务切换是导致效率降低的一个重要原因 [3] ,不同任务间的上下文的切换会导致频繁地将任务当前状态在头脑中“压栈”和“出栈”,这些操作会耗费时间。如果完成一个任务需要一个人一天时间,那么两天内这个人可以完成两个任务,但是如果他在第一天同时开始并行工作在这两个任务上,那么完成这两个任务会需要大于两天的时间。 大家可能已经注意到了,在前面的看板图中,处于“开发中”的所有任务卡片上都有一个小纸条,上面标记着正在这张卡片上工作的人的名字。如果说有两个人结对在一个卡片上工作,那么这张卡片上应该有两个名字。这一小小的实践可以帮助我们随时发现团队内某一时刻,是否每个人只工作在一个任务上。 如果这一简单的规则能够严格被遵循,那么当我们看到一个人的名字出现在多张卡片上的时候,我们就知道这个人此刻可能忙着在多个任务之间切换,而每一个任务都将不会在估计的时间点内完成。如果我们看到有人的名字没有出现在任何卡片上,那么他目前大概处于休息状态。团队内的每个人的名字都应该对应在一个小纸条上,如果你此刻工作在某个任务上,那么就将自己的名字贴到相应卡片上,如果此刻没有工作在该任务上,就将自己的名字移去。 我们在领取“待开发”状态栏中的卡片时,保证每次每人只领一张卡片,不要多领,完成了这张卡片之后,再回来领下一张。当一张卡片被认领之后,我们就会对这张卡片进行跟踪,在站会上谈论它的完成情况,谈论实现过程中碰到的问题。当它的进度和估计的可能偏差较大时,我们能够及时而不是在最后一刻察觉到,提供需要的帮助,确保它能够顺利完成。这样一种方式让我们能够将注意力集中到小粒度的需求(例如用户故事)上,更多地关注这些用户故事的流动速度。而当每个小的用户故事能够顺畅地流动起来时,整个项目的交付也得到了保障。 当然这一实践并不能自动保证团队内不再出现多任务并发、拖延、或者做和任务无关的其他事情等问题。可能有些人在做一个用户故事的过程中,突然中断去做了一些其他事情,但是却没有及时在状态墙上更新自己的状态。重要的是团队要有实现交付目标的共同愿景,能够透明地暴露问题,并且善于利用状态墙来发现和改进自身的问题。对于不成熟的团队,这可能需要一个转变的周期。 如果一个团队的职责共享较好,代码被所有人集体拥有,每个人都被鼓励熟悉和工作在代码的不同部分,那么在这样的团队内便不太会出现把一大块任务事先就明确给某一个人的情况。相反,所有人的工作事先不具体确定,大家会更容易形成某一时刻只领取一张卡片的情况,避免同时工作在多个任务上。实际上,状态墙的使用也可以帮助团队走向职责共享之路,只需要在大家领取任务的时候有意地给人们分配一些之前没做过的内容,同时安排好有经验的人与其结对工作,一段时间之后,团队内的人便会逐渐体会到和之前只是专注在一个模块内不同的工作方式。 ### 2.3 如何减少半成品库存,缩短交付周期 一个需求的交付周期 (lead time [4] )是从它被识别到最终交付给用户手中所耗费的时间。交付周期越短,意味着客户从提出想法到能够在软件中实际使用到这个点子的时间越短。从客户的角度来看,更短的交付周期意味着自己的软件能够对市场变化的更快地响应,因而获得更强的竞争力,同时也意味着能够更快地验证自己的想法。 任务管理的粒度太大会直接导致交付周期变长。最极端的情况是将属于某一模块的任务在一开始就全部分给负责这个模块的人,所有这个模块相关的修改都由他来实现。在一个按模块划分职责,每个人只负责自己具体模块的团队里,通常这个模块的负责人会实现这个模块的所有修改。不然,就是将一个可能需要做 2周到一个月的任务分给某个人。或者更好一点的情况是,单个任务本身不大,但是会将相关联的任务成批地分配给某个人。如果你的团队内也是采用大篇的“规格说明书”等 word文档来组织需求的,那么就要小心,这种问题很可能在团队内已经存在。整个团队没有小粒度频繁交付的概念,习惯了大批量长时间地交付方式。由于批量大,所以估计常常不准,而且时间跨度长,中间也会有更多地干扰因素出现,这些都导致任务不能在开始承诺的时间点交付。开发周期长同样导致测试活动的滞后,极端地滞后就演变为所有开发工作完成之后才能进行测试,这就是我们熟悉的瀑布模式。最终的影响就是需求的交付周期会很长。 传统团队的一个常见组织方式是按照功能模块划分团队成员,明确分离职责,这也会变相增长交付周期。这样的团队通常倾向于按照功能模块来组织半成品任务,而不是按照可以交付价值的完成品来组织任务。习惯按照功能模块来组织开发的团队通常会阶段性得“联调”,不同模块的人带着自己的代码合在一起调试,由于缺乏频繁地集成,这种联调活动的时间经常不可控。团队在大部分时间内通常只拥有一大堆半成品,后续的测试和验收活动没有办法进行,而只能等到团队在某一刻组装出一个完整的功能后才能测试,因此交付周期也会比较长。 因此,如果我们的需求都是按照软件的功能模块划分,而不是按照面向用户的价值来划分的,那么我们在交付用户价值这一目标上,一开始就走错了路。采用用户故事能够把需求以用户能够理解的价值来组织,这一点是我们缩短交付周期的一个重要基础。 我们的状态墙能够揭示需求的交付周期。让我们来看看这样几个场景。 如果我们的需求是按照软件的功能模块划分的,那么通常单个模块的编码完成往往不可测。例如有的团队喜欢将 web应用的上层页面部分和下层数据库逻辑部分划分到不同的模块组,一个用户的需求也会拦腰切成两截,一部分交给上层团队完成,一部分交给下层团队。单个团队的任务完成都不能开展这个需求的测试,于是这些任务就会堆积在“待测试”这一栏。 如果我们的需求很大,以至于开发人员要花费很长的时间(超过 1周)才能完成开发,那么这个需求会在“开发中”这一栏停留很久。大家可以猜到,当一个人同时进行多个任务时,这些任务也会比它们单个依次被开发时在“开发中”这一栏停留更久的时间。 任何一栏中的任务其实都是半成品,只有完成测试,交付到用户手中的需求才是完成品。状态墙上的每一栏都好比一个存放着各种零件的仓库,每一栏中的卡片越多,停留的越久,就说明当前半成品的库存越多,是该得到团队的认真关注的时候了。状态墙将每个阶段的半成品数量可视化呈现出来,让虚拟的数量通过卡片这种物理介质的数量得以呈现。 通过状态墙,我们可以计算出每一个需求的交付周期大概是多久。状态墙上一个用户故事从放到“待开发”这一栏,到它被移动到“完成”这一栏,这一个时间段是需求的整个交付周期的其中一段,也是很重要的一段。通过优化从“待开发”到“完成”的这一个过程,我们可以缩短需求的交付周期。通过比较需求的交付周期和客户对交付周期的要求,我们可以量化之间的差距,然后指导我们的改进。 在我们理解了状态墙是如何呈现一个需求的交付周期后,我们就不难理解瀑布方法是如何让交付周期变长的。在瀑布模型中,全部开发完成之后才会进行测试工作,相当于所有的任务卡片都堆积到“待测试”状态之后,才开始逐一测试。所有开发完成的半成品,都会留存在“待测试”这一仓库中,一直等到所有开发活动结束的那一刻。 当出现库存堆积的时候,就是我们需要改进的时候。如果“待测试”这一栏有太多的任务卡片,那么就说明我们的测试活动没有跟上。有可能是我们的测试环境出了问题,或者是我们的测试人员人力不足。如果太多的卡片位于“测试完成”状态,说明我们的发布和最终交付过程出了某些问题。如果“待开发”这一栏中任务过多,说明我们的计划有可能超出了当前团队的开发能力,或者说反映了开发人员的不足。也有一种情况,那就是“待开发”这一栏空了很久,这可能说明了另外一个问题,那就是我们的分析师的分析速度匹配不上团队的开发能力。一个良好的团队,必然是各种角色协调配合,并行工作,同时他们之间的任务衔接也能够比较流畅。 ### 2.4 迭代产能的度量,计划及其他 团队在每个迭代所能完成的工作量,通常被成为迭代的 velocity(速度),是衡量团队每迭代产能的一个指标。这个指标能够帮助团队进行制定迭代计划。根据团队估计任务工作量的方法不同,迭代的 velocity的单位也可能不同(例如故事点数)。通常,我们只需要在迭代结束的时候,数一数状态墙上完成的任务工作量就可以了。 当我们经历了若干个迭代以后,通常团队的迭代速度会趋于稳定,我们在做下一个迭代的计划的时候,会参考以往迭代的数据。如果上个迭代完成了 15个点,那么下个迭代我们通常也 会计划 15个点左右的工作量,将这些卡片放到“待开发”这一栏中。也就是说,每个迭代结束时,我们都会对状态墙进行更新,将即将到来的迭代的卡片放到墙上,并且将一些处于半成品状态的卡片进行适当的调整。 前面提到,状态墙上可能由三种卡片,除了需求,还可能有 bug和技术任务。测试人员每次在迭代中测出一个 bug,就会将 bug写成卡片,放到“待开发”这一栏。当 bug不多的时候,团队可以在不太影响原有计划的情况消化掉这些 bug,确保软件的质量持续地得到保证。如果 bug太多,则需要做一些计划,将 bug分散到几个迭代里去消化。然而到这个时候,团队可能更需要及时反省一下为什么会出现这么多 bug的原因了。 另一类技术任务也需要和 bug以及需求卡片一起被考虑到迭代计划中去。通常技术任务包括诸如搭建持续集成环境、准备测试环境、重构这样的任务。它们虽然不直接给用户带来价值,但是却是保证软件质量、确保团队效率的重要因素。比如重构类的任务,对于工作在遗留系统上的团队来说可能是需要一直考虑的事情,为了保障新的需求的顺利实现,可能需要有计划地重构之前的一些遗留代码。 bug和技术任务耗费团队成员的时间资源,但是不直接产生用户价值。如果我们衡量团队每个迭代的总体生产能力,需要在计算迭代速度时考虑这三类任务。但是如果我们只考察团队每迭代交付的用户价值的量的大小,那么就不应该包含技术任务和 bug。当一个团队在迭代中花了过多的时间在技术任务上,或者修复 bug上,那么团队就需要回顾反省一下其中的原因,是否是团队的基础设施太差,或者是团队在开发时过于粗心导致太多的 bug,抑或是其他的一些原因。 ## 3 总结 在本文中我们从项目管理中常常出现的一些问题着手,分析了其中的一些原因,然后介绍了如何采用状态墙(看板)来可视化任务管理。在敏捷项目中,状态墙作为一种有效的迭代任务管理工具,已经被广泛地使用。团队利用状态墙这样一种简单的工具,将迭代开发中的日常工作透明实时地跟踪管理起来,能够帮助团队及时发现问题,消除浪费,快速地交付用户价值。希望这些文字,能够对渴望尝试敏捷、改善任务管理和日常运作的团队带来一些帮助。 原文发布于:http://www.infoq.com/cn/articles/hl-kanban-task-management * [1] http://www.infoq.com/minibooks/kanban-scrum-minibook [2] http://en.wikipedia.org/wiki/Iterative and incremental development [3] 参见 Lean Software Development: An Agile Toolkit by Mary Poppendieck, Tom Poppendieck第一章 [4] http://en.wikipedia.org/wiki/Lead time
Share

看板和利特尔法则

利特尔法则(Little’s Law)作为一个非常朴素的原理,为看板方法奠定了一个理论基础,看似简单的公式背后却有其复杂的一面。

一、利特尔法则

利特尔法则的公式是这样的:

平均吞吐率=在制品数量/平均前置时间

举个例子,假设你正在排队买快餐,在你前面有19个人在排队,你是第20个,已知收银窗口每分钟能处理一个人的点餐需求,求解你的等待时间。

如果你已经决定要排队,并且站到了队尾,那么在制品数量就是20(个),平均吞吐率是1(人/分钟)。
从你站到队尾的时候开始,一直到你点完餐,这个时间就是你的“前置时间”。
即使我们没有学习过利特尔法则,也可以轻易地算出来:

    1 = 20 / x
    x = 20(分钟)

因为在一段时间之内,保持工作量饱满的话,我们每天能做多少工作基本是一定的,所以吞吐率基本上不会发生太大变化。
如果这个时候我们想缩短平均前置时间,也就是等待的时间,利特尔法则告诉我们:可以通过减少在制品数量来达成这个目标。
在这个例子中,就是减少排队者的数量。

这也很好理解,10个人的队列和20个人的队列,前者需要等待的时间会更短。 [1]

二、限制在制品的意义

如上面所说,在制品数量和前置时间是成正比的,缩短前置时间的最有效手段就是减少在制品数量。

前置时间的增长会导致交付周期变长,这一点基本毋庸置疑。

前置时间的增长会导致交付的可预测性下降,俗话说“夜长梦多”,长时间停留在某一个阶段会带来一些额外的风险。

如果我们的交付周期比需求变化周期更长,那么会有更多的紧急任务,所以交付周期变长会导致更多的紧急任务。

如果我们管理不好紧急任务的插入,会增大我们的在制品数量。

如果交付团队的可预测性很低的话,那么会影响到IT研发组织和业务部门的信任关系,当业务部门无法预测一个需求提交给研发部门什么时候能交付的时候,那么唯一可行的手段就是一次性把要做的事情全部都压给研发部门,直接增大了研发部门的在制品数量。

同时在制品数量的增长会带来的另外一个后果就是故障发现得很晚,这一点在过去三四十年的软件工程方法论中都得到了验证。

发现的故障需要资源和时间来进行修复,带来的就是在制品数量的上升和前置时间的增长。

以上所有的事情我们放到同一张图中,可以看到下面的情况,实线表示两者之间存在因果关系,同时还是正比的,因增大,果也会增大。虚线表示两者之间存在的因果关系是反比的,因增大,果会减小。

22484-4c2d36ae3b6f766f

因果回路图

而在这众多因素之中,只有在制品数量是我们能够最有效的直接加以干预的。
而只有前置时间我们是可以直接观测的。

就像我们正在开车一样,我们踩油门的时候,速度表会发生变化,从60迈到100迈,但我们真正关心的并不是仪表盘的变化,而是真正汽车行驶的速度。

所以我们采用控制在制品数量的手段,通过观测前置时间的变化来观察我们的改进是否有效,但更重要的是整个系统是否正在向着更好的方向迈进。

三、在制品数量是不是越低越好

我们用直觉感受一下,在制品数量如果越低越好,那限制到1怎么样?限制到0怎么样呢?

很显然,在制品数量如果过低的话,团队成员可能会产生空闲的现象,很大一部分产能会被浪费掉,那在制品数量限制到多少是最合适的呢?

我们都知道,一个任务如果2周能完成,两件任务串行,需要的时间是4周,但两件任务并行,绝不是2周能完成,有可能5周的时间都完成不了,所以直觉上在制品数量过高也不能带来产能的上升。

所以一个朴素的原则就是:
团队中每个人在任意时刻,手中只有一件事的时候,效率是最高的。团队的在制品数量低于这个值,会造成产能的浪费,如果高于这个值,会造成前置时间的变长。

那我们再用定量的方式模拟一下吧。

假设我们有一个三阶段的开发流程:分析、开发、测试,平均每张卡片需要4天时间分析、5天时间开发、6天时间测试。

为了简化计算,我们把分析、开发、测试三个阶段设一个总的在制品数量限制。 [2]

22484-5996f0d58c826265

模拟看板

当我们有1个分析人员、1个开发人员、1个测试人员的时候,会得到下面这个结果:

WIP平均前置时间平均产能1150.072150.133180.164240.165290.166350.167410.168470.169520.1610580.16

22484-b95912514fb62b9e

折线图这个实验可以重复,有兴趣的同学可以自己写代码重复一下。

结论

  1. 减少在制品数量可以缩短前置时间,但前置时间的缩短是有极限的,就像我们不可能让10个妈妈在1个月之内完成怀孕的全过程一样。
  2. 增加在制品数量可以提升平均产能,但平均产能的提升是有极限的,1个人每天8小时的产能再想提升只有加班加点。
  3. 最短的前置时间和最大的平均产能不可并存,在“平均每人手头有一件事”的时候,在制品数量稍微小一点,可以达到最短的前置时间,在制品数量稍微大一点,可以达到最大的产能。至于各个组织如何选择,看自己的需求了。

[1]: 道理看似很简单,但放在软件研发领域就变得非常复杂,我们需要平衡需求和产能之间的关系,控制队列长度实际上就是在控制期望和承诺。在尊重事实的前提下,我们尽量让队列的长度变短,不去承诺不切实际的东西。

[2]: 所谓“排队”,实际上软件开发真正的“收银窗口”就是最终交付的环节,在这个环节之前的所有需求其实都是一个队列,队列的末尾就是我们最近承诺的一个需求。所以一旦我们承诺了无法立刻着手的需求,那么就会产生极大的浪费。

Share