12 怎样制定发布计划, 处理固定价格的合同
一次只计划一个sprint的事情会显得提前量不足, 提前做计划是个好习惯;
尤其是签了固定价格的合同之后, 不得不预先计划好, 防止无法按期交付的危险情况;
在制定release plan的时候尝试回答:"最晚到什么时候可以交付新系统的1.0版本?"
定义验收标准
除了产品backlog外, PO会定义一系列的验收标准, 从合同的角度将产品backlog中重要性级别的含义进行简单分类;
e.g.
1) 所有重要性>=100的条目都必须在1.0版本发布;
2) 所有重要性在50~99的条目都应该在1.0版本发布, 不过也许可以在紧接着的一个快速发布版中完成一些; [patch, 补丁]
3) 重要性在25~49之间的条目也是需要的, 不过可以在1.1版本发布;
4) 重要性<25的条目是不确定的, 也许永远用不到;
>红色表示必须在1.0版本发布 黄色应该在1.0版本发布; 绿色表示可以以后再做;
应当尽量在最后期限之前能够发布红和黄的所有条目;
对最重要的条目进行时间估算
为了制定发布计划, PO需要进行时间估算, 特别是合同中包含的故事; 估算也应该是PO和团队一起完成的 -- 团队估算, PO描述内容, 回答问题;
如果时间估算和最后的结果有很大偏差, 那它的价值就打了很大的折扣;
1) 让团队来做估算;
2) 不要花太多时间;
3) 确保时间估算只是粗略估算, 不是承诺;
通常PO会把团队聚到一个房间, 提供食品饮料, 告知团队目标是得出产品backlog上前20个US的估算; [agenda] PO先描述一边所有的故事, 然后团队开始工作, 而PO就在房间里回答问题, 解释需求范围, 使用"如何做演示"的方式描述有助于减少发生误解的情况;
会议的时间要严格控制, 防止团队把时间花费在少数几个故事上; 如果PO想花更多时间, 可以在安排会议; [grooming] 团队必须保证PO认识到这些会议对当前sprint的影响[消耗了当前sprint的时间], 让PO理解时间估算这个动作本身也是有代价的;
e.g.时间估算结果:
估算生产率
现在对重要的US有了粗略的估算, 下一步是估算每个sprint的平均生产率;
确定投入程度: 投入程度表示"团队有多少时间可以放在当前承诺的US上" 这个数字不可能为100% [除非机器人], 因为团队会把时间用于完成未经计划的条目, 切换环境. 帮助其他团队, 检查邮件, 修复电脑, 休息娱乐...
假定团队的投入程度是50%(一般为70%), sprint长度3周(15个工作日), 团队6人;
这样每个sprint是90人/天, 能交付45人/天的故事, 估算生产率为45个故事点; 如果每个US的估算都是5天[纯理想情况, 基本不可能], 团队差不多能在一个sprint完成9个US;
统计一切因素, 生成发布计划
有了时间估算和生产率(45), 可以很容易把产品backlog放到sprint中:
在不超出估算生产率(45)的前提下, 尽可能把每个sprint填满US;
>大约要3个sprint来完成所有"必须"和"应该"完成的US; 3个sprint = 9周 = 2个月; 是否是要向客户许诺的最后期限, 要看合同情况, 范围限制的严格程度而定; 通常的做法是增加相当多的时间缓冲, 避免糟糕的时间估算, 未预期的问题和未预期的特性造成影响; 这种情况下, 可能会把发布日定在三个月之后;
每隔三周就可以给客户演示一些有用的东西, 在过程中邀请他们提出/更改需求(按照合同而定);
调整发布计划
每个sprint之后, 都要看一下这个sprint的实际生产率; 如果实际生产率和估算生产率差距很大, 就会给下面的sprint调整生产率, 更新发布计划;
如果不能简单得调整发布计划, PO就需要和客户谈判; 或者检查是否能在不违反合同的情况下调整范围; 或者和团队一起找出方法, 消除某些在sprint中发现的严重障碍, 提高生产率或投入速度 [1)加人, 2)加班 不过都是效率低下的办法]
告知客户实际情况, 讨论完成的范围或者时间(在发布后的后续版本加入暂时去除掉的特性), 至少这样诚实的态度对客户负责 [不知道在国内的情况是不是直接糊弄客户...], 尽早地给客户提供了选择: 应该准时发布最重要的功能, 还是推延一段时间, 发布所有的功能;
---发布计划 End---
13 怎样组合使用Scrum和XP
Scrum注重的是管理和组织实践, 而XP关注的是实际的编程实践; 所以他们可以很好的协同工作--他们解决的是不同领域的问题, 可以相互补充;
有些XP实践直接被Scrum替代了: 整体团队, 坐在一起, 故事, 计划游戏;
结对编程
-提高代码质量 [互相等于在review]
-让团队精力更集中 [成员会互相提醒, code, 需求以及日常会议...]
-很多编程开发人员没有尝试过, 实践后大多数会喜欢这种形式; [大多数情况是提高效率的]
-结对编程令人精疲力竭, 不能全天都这样做; [给我点私人时间看看新闻, 玩玩手机...休息一下]
-常常更换结对组合是有好处的; [不同水平的人在一起工作的方式不同]
-结对编程可以增进团间的知识传播; [互相之间会学到很多, 例如怎样方便使用IDE, 怎样查找问题, 这些只有坐在一起才能看到]
-有些人还是会不习惯结对编程; [要尽量安排优秀的开发人员适应结对编程]
-可以把代码审查作为结对编程的替代方案;
-"领航员"(不用键盘, 而是坐在旁边的那位) 应该自己有一台机器, 不是写业务代码, 而是在需要的时候做一些试验, 当"司机"(使用键盘写code的那位)遇到难题的时候可以查看文档等;
-不要强制结对编程, 鼓励程序员, 并提供合适的工具, 让他们按照自己的节奏去尝试;
测试驱动开发TDD
测试驱动开发意味着要先写一个自动测试, 然后编写恰好够用的代码, 让它通过这个测试, 接着对代码进行重构, 主要是提高可读性和消除重复; 整理好以后继续;
对测试驱动开发的看法:
- TDD很难; 开发需要花一定时间才能掌握; 往往问题不在于你用了多少精力去教学, 辅导和演示, 多数情况下, 开发掌握它的唯一方式就是跟一个熟悉TDD的人结对编程, 一旦掌握之后, 就会受到彻底的影响, 习惯于这种方式;
- TDD对系统设计的正面影响很大;
- 新产品中, 需要过一段时间, TDD才能开始应用并有效运行, 尤其是黑盒集成测试; 但是回报来的会很快;
- 投入足够时间, 保证大家可以容易地编写测试; 这意味着有合适的工具, 有经验的人, 提供合适的工具类或基类, 等等;
测试驱动开发使用到的工具:
- jUint/httpUnit/jWebUnit, TestNG, Selenium;
- HSQLDB用作嵌入式的内存数据库, 测试中使用;
- Jetty用作嵌入式的Web容器, 测试中使用;
- Cobertura用来度量测试覆盖率;
- Spring框架用来织入不同类型的测试装置(带mock, 不带mock, 带有外部数据库或带有内部数据库等)
在成熟的产品中(TDD的角度), 都有自动化的黑盒验收测试; 这些测试会在内存中启动整个系统, 包括数据库和Web服务器, 然后通过系统的公共接口进行访问(例如HTTP); 这样能把开发-构建-测试这三者构成的循环变的很快, 同时充当一张安全网, 让开发人员有足够的信心频繁重构, 随着系统增长, 可以帮助设计保持整洁简单;
新代码上进行TDD
在全新的开发过程中使用TDD, 即使这会在开始时延长项目配置时间(需要更多工具, 为测试装备提供支持), 但TDD会带来更多好处;
在旧代码上进行TDD
TDD很难, 在一开始没有用TDD进行构建的代码库上实施TDD更难;
e.g.
我们曾花了大量时间在一个比较复杂的系统上进行自动化集成测试, 它的代码库已经存在很长时间, 处于极度混乱的状态, 没有测试代码; 每次发布之前, 都有一个由专门的测试人员组成的团队来进行大批量的, 复杂的回归测试[regression]和性能测试; 这些测试大多是手工进行; 开发和发布周期被严重延误; 我们的目标是将这些测试自动化, 但是几个月的煎熬之后没有取得多少进展;
之后我们改变方式, 首先承认已经陷入了手工回归测是的泥潭, 然后想办法怎样让手工回归测试消耗的时间更少; 当时开发的是一个赌博系统, 测试团队在非常琐碎的配置任务上花费了大量的时间; 例如浏览后台, 创建牌局测试, 或者等待一个安排好的牌局启动; 所以我们创建了一些实用工具; 快捷方式和脚本都很小, 使用方便, 它们可以完成那些琐碎麻烦的工作, 让测试人员专注真正的测试;
Note 如果你深陷手工回归测试, 打算让它自动化执行, 最好还是放弃; 首先应该想办法简化手工回归测试, 然后在考虑将真正的测试变成自动化执行;
增量设计
一开始就应该保持设计简单化, 然后不断进行改进, 而不是一开始努力保证它的正确性, 然后就冻结它, 不再改变;
需要有大量的时间重构, 改进设计, 而不是大量的预先设计; 有时候也会出错, 例如允许一个不稳定的设计"陷入"太深, 以至于代码重构成了大问题;
持续的设计改进, 在很大程度上是TDD自动带来的成果; [不用TDD也是可以达到Iteration进步, 重构和改进的工作方式的, TDD会更自然些]
持续集成 [CI]
e.g. Maven, QuickBuild; 成熟的持续集成节省大量时间, 对于"它在我的电脑上没有问题"这样的老生常谈, CI是终极解决方案;
要判断所有代码库的健康状况, 可以用持续构建服务器充当"法官"或是参考点; 每次有人向版本控制系统check in东西, 持续构建服务器就会醒来, 在一个共享服务器上从头构建一切, 运行所有测是; 如果出现问题, 它就会向整个团队发送邮件告诉大家构建失败, 在邮件中会包括有哪些代码的变化导致build失败的细节, 指向测试报告的链接等;
每天晚上, CI服务器会从头构建产品, 向内部文档门户上发布二进制文件(ear, war...[Java build]), 文档, 测试报告, 测试覆盖率报告和依赖性分析报告等等; 产品也会被自动部署到测试环境中;
把这一切搭建起来需要大量工作[公司甚至需要组建一支团队来做这些事情], 但付出的每一分钟都值得;
代码集体所有权
不是所有的团队都采取这种方式; [Scrum鼓励这种互相cover的方式]
在结对的编程中频繁交换结对, 会自动把代码集体所有权提到一个很高的级别; 如果团队拥有高度的代码集体所有权, 团队会变得健壮, 不会因为关键人物缺席就造成sprint失败;
充满信息的工作空间
利用白板可墙壁空间; 墙上会贴满各种关于产品和项目的信息; 最大的问题是旧的作废的信息也会堆积在墙上, 或许应该在团队中引入一个"管家角色" [可以在每个sprint开始前大家一起整理白板]
使用任务板, 有效又直观;
代码标准
从简单的版本开始入手, 慢慢增加, 需要写的是非常识性的, 特定的条目, 尽可能加上对外部资料的链接;
大多数程序员有自己特定的编程风格; 例如如何处理异常, 注释代码, 返回null等; 有时候这些差异没问题, 但在某些情况下, 系统设计因此出现不一致的现象, 情况严重, 代码不宜看懂; 这时代码标准的用处就会明显;
e.g.
- 可以打破任一个规则, 不过一定要有理由, 并且记录下来;
- 默认使用Sun的代码惯例 http://www.oracle.com/technetwork/java/codeconvtoc-136057.html
- 永远不要在没有记录堆栈跟踪信息stack trace或是重新抛出异常的情况下捕获异常; 可以用log.debug(), 只要不丢失堆栈跟踪信息;
- 使用基于setter方法的依赖注入 Dependence Injection [控制反转 Inversion of Control]来将类与类解耦 (除非特殊情况需要紧耦合)
- 避免缩写, 除了熟知的和公认的, 例如DAO;
- 需要返回Collections或者和数组的方法不应该返回null; 应该返回空的容器或数组, 而不是null;
可持续的开发速度/精力充沛的工作
很多有关敏捷软件开发的资料声称: 加班工作在软件开发中会降低生产率; [疲劳降低效率]
e.g. 一年前, 一个大团队疯狂加班; 现存的代码库质量惨不忍睹, 他们不得不投入大多数的时间来救火; 测试团队没有时间来认真做质量保证工作, 用户很生气, 后果很严重; 几个月后, 我们成功把工作时间缩短到了适当范围; 团队正常上下班(除了在项目关键时期加班); 令人惊异的是, 生产率和质量都得到显著提高;
减少工作时长不是带来改进的唯一因素, 但他的影响很大; [适当的工作时间, 合理的娱乐和休息调整, 快乐的团队会是高生产率的团队]
---组合XP End---