转载-玩转Trello

https://sbbs.me/view_article/5225fb0f60e794ae6c000004#

“软件开发,长夜漫漫,何以脱困,唯有Trello!”

在最近的一段时间,开始全面采用Trello管理团队的开发工作,并因此对敏捷软件开发,有了一些新的思考…

为何选择Trello

软件开发的管理工具,千千万万,为何会选择Trello呢?别人选择的理由,也许各有不同,而我的选择,则来自于我对于软件管理的基本看法:

  • 开发管理的本质不是管理,而是培养良好的习惯;
  • 好的工具,有助于团队成员养成良好的习惯,而糟糕的工具,只会给人带来痛苦;
  • 软件开发是一个不断迭代的过程,一个团队的软件开发的效率与质量,也应该是一个不断改进提高的过程,好的工具,能够帮助团队不断改进提高;
  • 管理过程中的透明度极为重要,无论是团队内的透明,还是对外、对上的透明,都意义巨大;

基于以上这些理由,我们选择了Trello,容我细细道来。

一分钟入门

Trello是一个非常容易使用的Web应用,当然他还有iPhone、iPad、Android的移动版本。 上手最简单的办法,是使用Google Account登录,连注册都不需要了。

登录Trello以后,有一个Welcome Board,演示了一些最基本的用法,也可以看一看视频:

定义协作模型

Trello可以用来管理很多不同的个人事务、团队任务,而每一种类型的任务,其协作模型是大不相同的。这正是Trello最令人赞叹的地方,其他所有类型的管理工具,都没有这么方便的自定义流程模型的能力。

在定义协作模型时,我强烈建议在每个work环节之间,设置一个等待环节。这种设置的原因,有一个简单的解释,以及一个复杂的理论支撑。

简单的解释就是:当我完成了自己这个阶段的工作,并不意味着下个阶段即刻开始,放入等待区,可以表明由下一阶段的人员挑选的含义。如果直接拖入下一阶段,可能会造成下个阶段的人正在同时忙很多事情的假象。

详细的解释,可以参考《精益开发实战——用看板管理大型项目》一书。

在我们的实际开发中,定义了一下几个List:

  • 需求区
  • 开发等待区
  • 开发
  • 测试等待区
  • 测试
  • 发布等待区
  • 发布

基本流程

  • 所有的需求,首先出现在需求区;
  • 每周一,开一个周例会,将本周计划完成的卡片,拖入开发等待区;
  • 每天,每个开发人员只会从开发等待区中,拖一张卡片到开发区,并且将任务分配给自己;
  • 只有在一个卡片开发完成,并且由开发人员拖入测试等待区之后,才能再拖一张卡片到开发区;
  • 每天,每个测试人员会从测试等待区,拖一张卡片到测试区,并且将任务分配给自己;
  • 只有在一个卡片测试完成之后,测试人员才能将这种卡片拖入发布等待区;
  • 在卡片的功能发布成功之后,发布人员将卡片拖入发布区;

label颜色的用法

目前,我们的用法是,不再区分内测、外测、压测等各个阶段,而是给卡片标上不同颜色的label,代表它通过了什么样的测试。

目前看来,似乎可行。不过,我总感觉不是太对。

另一个选择是:用label代表不同的卡片类型,比如功能、bug、hotfix等等,不过目前来看,还没有总结出较为稳定的实践经验,也期待与其他trello用户多多交流。

管理中的基本要点

  • 每天早上,我们都会开一个晨会,讨论今天的工作。开发人员先轮流发言,告诉大家自己打算开发的卡片;然后测试人员再轮流发言,根据之前遗留的卡片以及今天可能新增的卡片,计划今天的测试工作;
  • 在开发人员与测试人员都发言结束后,由一个专门的同事,总结今天的计划发布上线的卡片(这就是今天的整个团队的目标了)
  • 除了每天的工作交流,更加重要的工作,是反复强调、不断完善工作中的小习惯与小规范。
  1. 举例之一:每天的任何时候,只有自己正在做的工作,才拖入自己的List,不要提前拖入,不要忘记拖走;
  2. 举例之二:不要提前分配任务,而是到当天开完晨会之后,再做分配;
  3. 举例之三:在测试之前,较为复杂的卡片,测试人员与开发人员,需要在一起讨论,并将测试的要点,记入CheckList
  4. 举例之四:不要拖入下一个环节,而是放入等待区,否则无法准确的计算工作时间
  5. 举例之五:测试发现bug,要将卡片拖到开发等待区,而不是直接拖入开发区

总之,在使用Trello的过程中,我们一直在讨论,更好的用法,以及更加习惯的用法。

直观情况下能够发现的问题

Trello是一个非常直观的工具,仅仅通过肉眼观察,就可以发现很多管理中的问题,并及时加以改进。

1、等待区的大量堆积,通常都意味着出现了问题。

  • 如果开发等待区出现堆积,尤其是到了临近周末,依然有很多卡片没有拖走,则意味着一周的开发计划,在周一时制定得过于乐观了。
  • 如果测试等待区出现堆积,则意味着开发的进展超过了测试的进展,当团队的两个部分效率不一致时:总是需要警惕的状况。
  • 如果发布等待区出现堆积,则意味着明明已经开发完成的功能,却不能及时发布,通常是由于功能之间相互依赖,而在制定开发计划时,对于版本规划做得不够清晰的原因。

2、工作区的卡片数量,应该与工作人数相等,如果超过工作人数,则说明使用不规范了。

3、某些List空了,从表面来看,是工作完成了的好现象,但如果一直空着,也是问题。

  • 开发等待区空了:如果在周五出现这个现象,那是好事。如果在上半周就出现这样的问题,则意味着工作计划过于保守。
  • 测试等待区空了,甚至连测试区都空了:开发环节多半出了问题,测试人员现在已经没活干了。
  • 发布等待区空了,如果是长时间一直空着,则意味着已经好久没有测试完成的卡片了,可能测试遇到了瓶颈。

量化分析——Trello API

Trello是个看板系统,但是,仅仅像这样的直观的观察看板,是不够的,我们需要对自己的工作有更加深入的了解。还好,Trello提供了非常强大的API:Trello API Documentation

非常简单的一些代码,就可以访问Trello的Board数据,然后将这些数据导出来,做进一步的分析。

从我们的需要出发,有两个数据结构需要深入了解:

在OAuth登录之后,访问:https://api.trello.com/1/boards/board_id/cards 可以得到一个Board中的所有的卡片的数据。其中一个卡片的数据结构如下:

  {
    id: "50bc61474d0451697100116f",
    badges: {
      votes: 0,
      viewingMemberVoted: false,
      subscribed: false,
      fogbugz: "",
      checkItems: 0,
      checkItemsChecked: 0,
      comments: 0,
      attachments: 0,
      description: false,
      due: null
    },
    checkItemStates: [],
    closed: false,
    dateLastActivity: "2012-06-03T14:02:18.556Z",
    desc: "",
    descData: null,
    due: null,
    idBoard: "50bc61474d04516971001162",
    idChecklists: [],
    idList: "50bc61474d0451697100116b",
    idMembers: [],
    idMembersVoted: [],
    idShort: 1,
    idAttachmentCover: null,
    manualCoverAttachment: false,
    labels: [],
    name: "Welcome to Trello!",
    pos: 65536,
    shortLink: "2px9lS4p",
    shortUrl: "https://trello.com/c/2px9lS4p",
    subscribed: false,
    url: "https://trello.com/c/2px9lS4p/1-welcome-to-trello"
  }

而访问一个卡片的所有Actions,则可以通过以下API:https://api.trello.com/1/card/card_id/actions?filter=all 其中一个Action的数据结构如下:

  {
    id: "5229ee9e68a889bc250020ee",
    idMemberCreator: "50bc61464d04516971001161",
    data: {
      board: {
        name: "Welcome Board",
        id: "50bc61474d04516971001162"
      },
      card: {
        idShort: 7,
        name: "Invite your team to this board using the Add Members button",
        id: "50bc61474d0451697100117c"
      },
      text: "test"
    },
    type: "commentCard",
    date: "2013-09-06T15:02:54.629Z",
    memberCreator: {
      id: "50bc61464d04516971001161",
      avatarHash: "",
      fullName: "zhuangbiaowei",
      initials: "庄表伟",
      username: "zhuangbiaowei"
    }
  }

量化分析——本地数据结构

通过Trello API,我可以将数据导入到本地的一个SQLite3的数据库中,表结构如下所示:

CREATE TABLE cards (
  id text,
  cat1 text,
  cat2 text,
  cat3 text,
  name text,
  closed boolean,
  last_date date,
  last_list text
);
CREATE TABLE actions (
  id text,
  card_id text,
  type text,
  data text,
  date date,
  creator text
);
CREATE TABLE card_move (
  id text,
  card_id text,
  before text,
  after text,
  date date,
  creator text
);
CREATE TABLE card_time_count (
  card_id text,
  user_id text,
  time_count float
);
CREATE TABLE card_wait_time(
  card_id text,
  list_id text,
  time_count float
);
CREATE TABLE work_time(
  card_id text,
  user_id text,
  end date,
  work_time float
);
CREATE TABLE last_date(
  last_date text
);

一张卡片的生命周期

从卡片被创建开始,将会经历很多的改变,这些改变都会被记录为一个一个的Action,在Action的JSON数据结构中,有一个type属性,代表着action的类型。其中createCard、updateCard、addMemberToCard、removeMemberFromCard较为重要。

  1. 当卡片被创建时:createCard;
  2. 当卡片被移动时:updateCard;
  3. 当某人被安排加入某卡片的工作时:addMemberToCard;
  4. 当某人不再参与某卡片的工作时:removeMemberFromCard;

具体的生命周期将是这样的:

  • 在日常的工作中,我们会在需求区创建一张卡片->createCard
  • 然后在每周一将这张卡片移入开发等待区->updateCard
  • 每天的工作开始时,开发人员将这张卡片移入开发区->updateCard
  • 同时将自己加入这张卡片->addMemberToCard
  • 当他开发完成时,将这张卡片移入测试等待区->updateCard
  • 测试人员在空闲时,将会把这张卡片,移入测试区->updateCard
  • 同时将自己加入这张卡片->addMemberToCard
  • 测试人员完成测试时,将卡片移入发布等待区->updateCard
  • 发布人员完成发布后,将卡片移入已发布区->updateCard

以上描述的,是一张卡片最正常情况下的生命周期,而事实上,还可能存在很多种不同的情况。

  • 测试发现bug,卡片移入开发等待区
  • 发布发现问题,卡片移入测试等待区
  • 人员重新安排工作时,需要removeMemberFromCard,再addMemberToCard
  • 因为需求表述不清,卡片可能重新回到需求区
  • 因为需求较为复杂,一张卡片可能被分解为多张卡片
  • 需求不明确的卡片,可能被直接删除

一张卡片能够产生出的时间与数值

  • 卡片从最初被创建,到最终移入发布区,是整个卡片的生命周期,这代表一张卡片最终需要花费多长的时间,才会被完成。(LifeTime)
  • 卡片从首次进入开发等待区,到最终移入发布区,代表工作时间长度。(WorkTime)
  • 在开发区停留的时间(可能多次进入开发区),可以称为(DevTime)
  • 在测试区停留的时间(可能多次进入测试区),可以称为(TestTime)
  • DevTime+TestTime=RealWorkTime,代表实际工作时间
  • WorkTime-RealWorkTime=WaitTime,代表由于多任务并行或其他原因,卡片等待的时间
  • RealWorkTime / WorkTime,代表这张卡片的实际开发效率(被浪费的等待时间,越少越好)
  • RealWorkTime / LifeTime,代表这张卡片的受重视程度(被排入开发计划之前的等待时间越少,则越受重视)
  • 当卡片停留在开发区时,这张卡片的Members中,开发人员的时间被计算。如果只有一个开发人员,则只加这一个人的工作时间,如果有多人同时是这张卡片的开发人员,则每人都计算相同的一段工作时间。可以称之为(DevWorkTime)
  • 当卡片停留在测试区时,情况与开发区类似,只是针对特定的测试人员,可以称为(TestWorkTime)
  • DevWorkTime+TestWorkTime=TotalWorkTime,代表这张卡片的实际投入工作量
  • 正常情况下,卡片只会从左向右移动,如果出现了向左移动的情况,则代表开发过程出现了问题,可以计算LeftMoveTimes / LifeTime,代表这张卡片的工作顺利程度

卡片的命名规范

怎么到现在才来讨论命名规范呢?因为,关于命名的问题,是在卡片多了以后,才浮现出来的。

卡片多起来了,分别属于不同的项目,不同的模块,不同的版本;这时候,我们小组面临另一个选择:就是每个项目开一个Board,然后分别管理。

但是,这个构想被迅速的否决了,因为这些卡片的工作都是交替进行的,我们衍生出了一个“主工作Board”的概念,90%的小组工作,都在一块板上体现出来,而不是散落在各个不同的地方,否则,总会有漏看的时候。

于是,我们的命名规范是这么定的:[部门-项目-模块]卡片名。这是我们目前的用法,其实还有两个纠结:

  • 在命名中是否要明确标出一个卡片是不是bug?

第一个问题,目前我们没有明确标明,不过感觉今后应该更加规范起来,但是如何规范,是用命名还是用颜色label,还在犹豫中。

  • 在命名中是否有版本号?

第二个问题,介于我们现在的具体工作,很少能够划分清晰的版本号,所以也没有使用。

卡片的粒度把握

对于一张卡片的粒度,我们其实没有什么仔细的“把握”,实际的做法是:借助数据统计工具,不断追求缩短每张卡片的完成时间(这样做的后果是,卡片的粒度会越来越趋于微小。);另外从测试的角度,确保每一张卡片,可以被独立的、有效的测试。(这样的后果是,卡片的粒度,不会无限制的越来越小。)

最小可测试单元,是我们发明的一个新概念,通过将开发工作划分为一个一个的最小可测试单元,我们将最大限度的减少工作环节之间的等待情况。

以一张卡片来举例:如果一张卡片,需要8小时才能完成,那么测试人员,最快也要在8小时后开始测试;如果8小时的卡片能够分解为4张2小时的卡片,那么测试人员最快在2小时后,就能够开始工作。

就好比在瓶子里装石头,石头的颗粒越小,则瓶子内的空间才能够被越充分的利用,这就是通过减少浪费与等待,从而实际提高工作效率的办法!

多卡片的统计指标

上文描述了一张卡片的生命周期,而在实际的开发过程中,我们肯定会面对很多很多张卡片,这些卡片除了有各自的生命周期,更组成了一共不断生长的树状结构。

我们可以参照WBS(工作分解结构)来理解这些卡片,企业有多个产品、每个产品有多个版本,每个版本有多个需求和多个bug,每个需求有可能分解为多张卡片来开发测试。

当我们根据某种规范来命名卡片以后,再将数据导入到本地的数据库,就可以很方便的做出一个统计表格,来统计各个项目的当前状况:

这样的统计,当然还只是一个很初级的静态汇总。更加深度的玩法可以有:

  • 每周进展统计,分项目、模块,统计在一周内,分别有哪些卡片,各自前进了多少阶段。
  • 资源投入百分比统计,分项目、模块,统计在某个阶段内,分别投入了多少开发时间、多少测试时间。

估算能力指标

所谓估算能力,不是用来衡量普通开发者的,而是用来衡量开发管理者的。一个模块、一个需求,在最初被分解为多少张卡片,这是一个初始估算。

随着团队开发效率的逐步稳定,我们可以切实的根据分解卡片的数量,对整个项目的工作量,做出预估。

到了项目结束时,我们可以得到一张图表:“卡片增长趋势图”,一个团队管理者的能力,就在于是否可以尽早的、尽可能准确的估算卡片的数量。如果,卡片在开发后期出现意外增长,则意味着一开始的估算,出现严重偏差。

这也是开发管理者,需要不断提高估算能力的地方。

除了规模估算能力,还有进度估算的能力。在不了解真实的开发效率的情况下,开发管理者通常有两个选择:高估以保护手下、低估以后压榨手下。

这两种方法,是典型的过犹不及,都不是什么好事。正确的做法是:根据实际的工作量与工作类型分布,推出一个开发团队的进度模型,进而对实际的需求,进行进度估算。

在此之前,所有的估算,都不过是耍流氓而已。

功能点?价值点!

直白一点说吧,我反对功能点估算这种学院派的算法,无比复杂,却毫不实用。只适合用来考试,根本不适合用来工作。

不考虑具体技术实现的工作量估算,根本就是在耍流氓。他们那14个所谓的调整因子,也不过是纸上谈兵而已。

在我看来,IFPUG这种组织,也无非是搞了一个巨复杂的标准规范,然后搞认证考试来赚钱罢了。

说说我的看法吧:在项目具体实施之前,越是过去没有尝试过的、缺乏经验的新项目,越是难以估算工作量与预期时间,强行估算工作量,只会增加更多无谓的“估算工作量”。

另外一个指标,我倒是建议需求方,认真估算一下:就是某一个项目、某个功能、某个User Story,可能带来的价值回报。换句话说:如果能够花钱购买这个Story,我愿意出多少钱?

传统的估算,通常都是工作量估算,一个项目30个人月。然后项目组开始拼死拼活的去干。就算一切顺利,我们10个人,干了3个月,做完了30个人月的一个项目,然后呢?无非是投入了那些工作量而已。于是,老板的眼睛就会一直盯着每日考勤统计表,看看这帮家伙,是不是每天都在加班,如果他发现没人加班,就会痛感自己“压榨得还不够”。

如果我们采用价值点估算,一共100个Story,价值100万。我们10个人,第一个月就完成了价值50万的Story。作为老板,根本不必关心我们是在一个月里,每天干10小时做完的;还是每天干6小时做完的。相对而言,如果我们能够每天6小时就干完,只怕他应该更高兴,因为这样为他省电了。

做早饭与超越敏捷

前段时间的某一天早上,我因为机缘巧合,居然早早的就醒了,于是突发奇想,决定主动为家里人,做一顿早饭。我打开冰箱,查看了里面的存货,然后略微思索,最后做了炒鸡蛋、蒸馒头、以及煎葱油饼三个高难度的动作。

经过妻儿试吃,评价居然尚可,我顿时有种洋洋得意的感觉。

我是多么的敏捷啊!在短短的30分钟时间里,我根据现有的条件,做出了兼具营养与美味,而且足以果腹的一顿早餐。所谓看菜做饭,不就是敏捷里的高效快速迭代吗?我们不做计划,但是,我们可以随时灵活调整。

不过,我并没有自我陶醉太久。进一步的思考涌上心头:

  • 一个称职的妈妈,会像我这样吗?
  • 如果冰箱里没有东西怎么办?(称职的妈妈早就提前买好了)
  • 如果营养不协调怎么办?(称职的妈妈会考虑全家人的健康)
  • 难道不需要考虑家庭支出吗?(称职的妈妈会每个月精打细算)
  • 难道不需要事先考虑个人的口味吗?(称职的妈妈会了解每个人的口味,甚至还会不断花样翻新)

抄一段我过去的文章吧:

后来,软件大师们,发明了一个新的办法:既然不能指哪打哪,那就打哪指哪。既然不能在给定的时间内完成任务,那么就反过来画个圈,这个阶段咱们完成的任务,就是咱们的阶段目标。

在给这种“掩耳盗铃”开发模式,起了一个漂亮的名字之后,这种敏捷开发的方式,在全世界流行起来了!

但是,真正“精明”的老板,是不会上当的!在敏捷开发模式下,开发人员永远是无可指责的!所有的开发进度,开发人员拥有无可争辩的主导权:“每一个Story的开发工作量都已经被估算出来了,产品经理能够选择的,仅仅是在下个阶段先实现哪几个Story而已。”

老板们通常会说:“三个月内,我要看到这个上线,不要告诉我不可能!公司的信条是:没有借口!”

所以,像我这样做早饭是不够的。

所以,仅仅敏捷是不够的。

所谓敏捷,不过是由于软件开发过程中,各种因素难以估算,而采取的权宜之计。真正的解决之道,不是打哪指哪,而是通过合理的工具,掌握真实的开发情况,进而做出切实的工作量估算与进度计划。

这样的解决之道,我认为Trello也许是一个极好的开始!

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注