敏捷团队工作流
站会
站会中的内容是每天工作的开始,也是对昨天工作的回顾。一般会由团队的某位成员主持,这位主持人有责任让电子系统上的story卡片和看板上的保持一致。站会上,大家依看板从右至左依次更新自己负责story的状态,如果遇到阻碍,应该在站会上及时提出,团队之中的成员如果能提供帮助,应该在站会之后,组织解决方案的讨论。
站会反模式
- 站会上由组长一人发言、分配任务;
- 一人长篇阔论占据大半时间(合理的发言时间为1-2分钟,总的时间控制在15分钟左右);
- 站会上谈论和story卡片无关的内容。
迭代计划会议 (IPM)
迭代计划会议是项目组成员在每一个迭代开始时,聚到一起共同决定这个迭代的 backlog (代办事项) 需要交付的故事卡。
这个会议的产出物包含:
- 迭代的 backlog 和用户故事的验收条件
- 这个迭代的总体业务目标
- 故事点数,也即开发测试人员对用户故事的评估及承诺
参与者包含:
- Product Owner
- Scrum Master
- 开发团队
- 其他干系人
日程:
- 团队确立这个迭代可以完成的工作量(以点数计),一般从历史迭代中获取
- 团队根据定义的顺序讨论故事卡,对于每一张故事卡,需要讨论的内容包含:
- 衡量故事的相对大小(以点数计),可能会进行故事卡的拆分
- 沟通验收条件
- 根据已经讨论出来故事卡的价值、时间和风险,PO重新确定故事卡的优先级
- 一旦预先设定的工作量到达,就停止本轮迭代会议
- 团队就迭代的业务目标达成共识
用户故事 kickoff 会议
Story kick off,指的是启动一个 Stroy 进入开发阶段。Story kick off的时候,通常需要三种角色参与:BA、QA 以及开发 Story 的 Dev。
会议内容:
- DEV 自己先完整地过一遍 Story 的描述;
- DEV 给 BA 和 QA 去讲这个 Story 的功能以及 AC;
- 要能够清晰的讲出来,并且三者达成一致,如果有疑惑,需要当场得到解决;
- DEV 开始开发 Story,并自行将 Story 拆分成多个子任务列表,开工。
Desk Check / Shoulder Check
Desk Check 发生在开发人员确信自己已经完成了故事卡,需要另外的人(BA、QA、Dev)坐到旁边一起帮忙验证是否确实完成。
Desk checklist:
参与者:BA、QA、Dev 或者其他感兴趣的人
- 故事卡测试覆盖率够吗?
- 自己本地测过功能么?
- 自己本地跑过全量的测试么?
- 所有的 AC 是否都满足了?
- 做故事卡的过程中是否遇到了理解上的问题?
Defect Prevention Using Agile Techniques
Tech Huddle
开发人员聚到一起就项目中遇到的技术展开讨论
前提条件:
会议之前确定并逐条列出会议讨论的技术主题
产出物:
- 一个或多个行动方案,合并到用户故事
- 方案的优缺点
- 方案的责任人
迭代回顾会议
在每个迭代结束时,Scrum Master会主持该会议,目的是为了确定哪些改变可以提升下个迭代的生产效率。在这个会议当中,每个人都可以开诚布公地提出自己的建议,有利于管理者从反馈中获取团队的现状。
最高指导原则 (Prime Directive):
Regardless of what we discover, we understand and truly believe that everyone did the best job he or she could, given what was known at the time, his or her skills and abilities, the resources available, and the situation at hand.
无论我们发现了什么,考虑到当时的已知情况、个人的技术水平和能力、可用的资源,以及手上的状况,我们理解并坚信:每个人对自己的工作都已全力以赴。
内容包含:
- 上个迭代中做的好的有哪些?
- 上个迭代中做的不好的有哪些?
- 我们可以做些什么改善哪些不好的地方?(改善建议)
参与者包含:
- Product Owner
- Scrum Master
- 开发团队
- 其他干系人
迭代review会议 (Showcase)
Showcase 就是开发团队把开发好的功能给客户的 Product Owner(以下简称PO)等业务相关人员演示,以获取他们对所开发系统的反馈,是敏捷开发流程中的一个实践,一般的频率是一个迭代一次,也可以根据项目具体情况做调整。
内容包含:
用户故事
故事基础
用户故事是指在软件开发和项目管理中用日常语言或商务用语写成的句子。这个句子反映一个用户在其工作职责的范围内要达到的某个目的, 以及此目的所需要的功能。
例子
为了避免遗漏附件延误工作
作为邮件发送者
我希望邮件系统能够在我忘记带附件的时候提醒我
格式
|
|
要点
重点描述商业的价值不只是功能
- 帮助团队了解需求背后的意图,利于开发团队协同客户、业务部门设计出更好的解决方案
用业务语言描述
- 利于客户、业务部门理解并区分优先级
利益相关者不仅包含用户
- 系统拥有者的角度也是需求的重要来源
CCC 组件 (The three ‘C’s)
Card (卡片)
- 业务价值 remainder
- 做计划和沟通业务时的 token
Conversation (交流)
- 用于在做计划和沟通业务时引发沟通,制造共同话题(收敛)
- 需求文档的生成方式
Confirmation (确认)
- 将细节以验收测试的方式检测功能的完整性和准确性
划分原则 INVEST
Independent (独立性)
故事和故事之间尽量保持独立,互相依赖的故事对于估算工作量、确定优先级和安排计划都带来很多不便。通常我们可以通过组合和拆分的方式减少依赖性(去除重复)。
独立性更多的指的是实现要完整。前后端拆分通常不是很好的拆分方法
Negotiated (可协商的)
一个故事是可以协商的,故事卡不是合同,它只是包含对一个需求的简短描述。具体的细节在沟通阶段产出,以验收测试的方式。如果带有太多的细节,反而限制了和用户的沟通。
Valuable (有价值的)
每个故事都必须对客户有价值(无论是用户还是客户)。一个让用户故事有价值的好办法就是客户写下它们。
Estimable (可评估的)
开发团队需要去估计一个用户故事以便确定优先级,工作量,安排计划。如果难以估计故事的时间, 意味着:
- 领域知识的缺乏: 这种情况下需要更多的沟通
- 技术实现的模糊: 这种情况下要做试验, 做原型
- 或者故事太大了: 这时需要把故事切分成小些的
- 还有对其它团队的依赖…
Small (短小的)
一个好的故事在工作量上应该是尽量短小的,至少确保能在一个迭代或 Sprint 中能够完成。用户故事越大,在估算、计划安排等方面的风险就越大。
Testable (可测试的)
一个用户的故事必须是可被测试的,以便它是可以完成的。如果一个故事无法测试,那么就无法知道它何时可以完成。一个不可测试的例子:为了节省时间,作为用户,我希望软件是易用的。
判断标准 (经验准则)
- 可以写在 Release Notes 里
- 值得讲给其它行业的人听
- 可以写在市场宣传材料中
验收条件
捕获预期的行为
一般验收条件都会在开发之前准备好,用于捕获预期的系统行为,同时作为故事卡业务描述的一部分,定义了故事卡的 DoD(Definition of Done)。
格式
- Given前置条件,告诉我们在进行操作之前,需要设置和完成什么;
- When触发结果的操作
- Then操作之后的预期结果
最佳实践模式
1. 可读的
我们希望业务人员审阅和修正验收条件,如果写的内容只有开发人员能懂,我们就失去了获得反馈的机会。使用上述书写格式,可以提高可读性。
|
|
2. 可测试的
反模式1 - 模棱两可的陈述
|
|
这里的 reasonable period of time 就是不可测试的,因为没有人可以决定什么才是 reasonable.
合理的改法是:
|
|
5ms 之内,这是一个标量,完全可以衡量。
反模式2 - 非系统输出
|
|
这里的 I can see my account preferences 是无法进行断言的,因为这是系统无关的,说得极端些,假如用户闭上眼睛,这个功能就没法通过验收了。
合理的改法是:
|
|
这个时候,我可以检查系统展示了我的用户页面。
反模式3 - when 隐藏到 given
|
|
基本上,这是团队编写 AC 时最容易犯的错误,操作出现在前置条件中, when反而不是系统行为了。
合理的改法是:
|
|
3. 实现无关的
验收条件应该是实现无关的,它和用户故事一样,是给业务和开发人员提供交流凭证的一种工具,所以它应该聚焦于功能,而不是功能的展现形式。
|
|
这里已经框死了必须要使用 text box 实现展示功能,而实际上其背后真正的意图是通过属性字段进行搜索,隐藏了业务含义的验收条件是不可取的。
合理的改法是:
|
|
换句话说,验收条件本身不应该关注于展现形式,当然,为了便于理解,wireframe 是提供直观素材的更好的方式。
代码评审
代码评审是团队针对今天一天 提交过的代码的评审会议。这样做的目的是去除代码的坏味道,减少累积的技术债。
团队成员使用版本控制工具,轮流展示自己一天的编码成果并且说明代码的用途。这期间,其他的成员不仅要评审代码的逻辑是否正确(满足验收条件),还必须思考是否有更优雅的方式实现这段功能。大家可以随意表达自己对这些代码的好恶,代码所有者也可以据理力争,所有者一旦被说服就必须无条件地按意见修改。为防止遗忘,可以使用便笺纸记录下来。
结对编程
通俗地讲,结对编程就是两个人同时工作在同一个 Story 上,一起讨论 Story 的解决方案,并编写代码实现功能,一个人敲键盘,一个人看屏幕,穿插着进行。Pair的双方在快速敲击键盘的时候会伴随一些交流。能力相当的两人,可以一人写测试,一人写实现代码。
测试驱动开发 (TDD)
TDD,即测试驱动开发,强调的是测试先行,跟我们先实现功能代码后添加测试的过程恰恰相反。测试驱动开发是一种驱动代码实现和设计的过程。我们说要先有测试,再去实现;保证实现功能的前提下,重构代码以达到较好的设计。整个过程就好比演绎推理,测试就是其中的证明步骤,而最终实现的功能则是证明的结果。
实践方法:
- Given:初始状态或前置条件
- When:行为发生
- Then:断言结果
单元测试和测试驱动开发
测试的价值表现在两个方面:
- 防止已有的功能被破坏
- 驱动出功能实现
从测试金字塔描述的层级来看,单元测试是基座。这类测试数量应该是非常多的,而且还有一个显著特征——运行速度特别快。一般违反快速这个标准的,基本上可以肯定这不是单元测试。
基于以往带团队的经验,我给出一些写单元测试时常犯的错误和好的实践:
常犯的错误
- 覆盖率100%,却没有任何断言
- 使用Java反射setAccessible(true)访问私有属性
- 依赖某些地方产生的数据才能运行
- 单元测试运行速度超级慢
- 开始测试之前,准备巨量的数据
- 测试之间必须以特定顺序运行
- 上帝类(God Object)导致数以千行的测试代码
好的实践
- 绝对不测试私有的方法
- 当方法没有返回值,可以通过测试异常或者其委托对象的方式 121. @Test(expected = RuntimeException.class)2.Mockito.verify(mockObj).doSomething(args…)
3. 静态方法调用是强耦合的信号。需要重构待测类依赖新接口,新接口定义的方法由子类实现,实现方法中包装这些静态方法。
什么时候写单元测试?
- 新的需求,首先写单元测试(保证功能实现)
- 已经存在的代码,补充单元测试 (保证功能不被破坏)
- 发现Bug,首先加测试(保证缺陷不再出现)
自动化测试
测试金字塔和 DOT(Depth of Test)
持续集成/持续交付
再次强调一下,持续集成是一项团队务必遵守的实践。
开发过程中,我们鼓励小步提交的代码控制方式。即当开发人员对story的部分功能编码完成之后,如果 确认可以提交到代码仓库(如果是分布式的Git,可以考虑提交到本地仓库),应该尽早提交。开发人员的每日工作可以遵循7步提交法,如下:
- 更新代码;
- 本地编码;
- 本地构建(使用
mvn test
); - 再次更新代码;
- 本地构建;
- 提交到代码仓库;
- CI上构建。
既然是实践(规矩),就有几项有效的纪律需要遵守:
- CI红的时候不能提交代码
- CI红着不能过夜(不能等到第二天才去修复)
- 任何人提交后CI失败,无论原因,都有责任跟进修复
- 在提交代码前运行所有测试
- 不注释掉或删除失败的测试
这也就意味着开发人员需要密切关注CI的状态。CI持续反馈着软件的可工作状态,所以团队应该把CI的健康状况列为项目的最高优先级,甚至高于开发新特性。