下面是一些应对之策:
下面是一个合适的规则集:
你甚至还能为团队争取到一些王冠饰品呢。:o)
总体来说,很多团队都会过高评价同时实现多个用户故事的效果。这样做从开发速度上说感觉好像不错,但却只是一个幻象;因为它将有风险和消耗实践的编码工作推到了最后——包括合并、集成与测试等工作。
这就是Scrum团队应该保持小规模的原因(少于9个人)——这样他们就可以紧密协作而且将注意力集中于自己的工作成果之上。如果任何人都在独立开发自己负责的用户故事,那大概就不会有多少协作的情形发生了。当然可以有人为将来做计划,为下一个故事做准备并做一些实现工作。但是在任何时候,团队的主要工作努力都要放在优先级最高的故事上。
多个团队就是不同的情况了。如果很想并行实现多个故事,那就不妨创建多个团队。我们不久后就会看到如何具体操作。首先我想讨论下关于回归测试,以及分叉代码的相关话题。
当一个故事“完成”后,我们会把它移入到“完成”一栏中,并将相关内容从工作分支拷贝到主干中。主干必须一直保持可发布状态。此处有一个重要的暗示。
规则:任何接触到主干的人,必须保证整个主干保持可发布状态——包括之前的全部功能!
实际上,这条规则意味着:对故事A的测试,同样包括运行之前实现的故事的全部相关回归测试。如果上传代码后,故事A没有问题,但是之前故事的测试却通不过了,这是不行的。
稍等。这是不是有点不合理啊?每完成一个故事就要运行所有的回归测试?
嗯,首先我没有说运行所有的回顾测试。我是说所有相关的回归测试。我们已经有了一个干净而且可发布的主干作为基础,现在只是要添加一个故事而已!这是一个很小的增量变更。如果回归测试可以自动化完成,我们就可以全部运行了。要是有需要手工完成的回归测试,那我们就要有选择性了。
最后还是归结到了对风险vs成本的权衡之上。对于每一个手动的回归测试,我们应该评估运行它的成本(比如需要多少工作量来完成测试),同时评估发现任何重要缺陷的可能性,对二者进行权衡。当然还要加入自动化该测试的成本。:o)
假设我正在兴高采烈地编写调用Widget类的代码,我却不知道团队成员Jim在一个小时之前进行重构时移除了Widget类。现在我们就有了< b>分叉代码。在花费更多时间编写其他调用Widget类的代码前,我希望尽早发现类似问题。
规则:持续不断地(等同于尽早)将你的代码同步到工作分支中。
这种同步是双向的。从工作分支中取得并合并最新的代码,然后检入你的代码。第一步可以叫做“跟上”(等同于我要知道其他人检入了哪些内容)。第二步可以叫做“公开”(等同于我希望我所做的更新可以让团队其他人都知道)。
每小时同步一次是好习惯,基本上在进行任务切换或者没有处于某项工作进行中时,就可以进行同步。这不只是关于“我要尽快知道别人的代码是否与我的相冲突”,还包括“我希望其他人尽快知道我的代码是否与他们的冲突”。要记得不要违反工作分支的方针(通过单元测试等等)。
该规则听起来很浅显,但是请允许我不断重申。我希望大家都能对其一清二楚,因为涉及到多个团队协作时,我们会再次使用类似的思维方式。
假设有团队A和团队B。他们都是跨职能团队,并且一起开发一个航班订票系统。团队A的注意力放在开发订票流程之上,而团队B主要负责开发后台相关功能。
假设他们现在要开始一个sprint,每个团队有两个用户故事要开发(通常一个sprint中会有更多的故事)。
由于每个团队都要在发布代码到主干前进行测试,因此他们有各自的工作分支。
现在我们遇到了一个有趣的问题。假定我在团队A中,而且有我们自己的工作分支。变更可能先在主干中发生,而不会先出现在我的工作分支中!为什么?恩,因为有另一个团队啊,他们每完成一个故事就会将其发布到主干中!
所以在任何给定的时刻,在主干上都可能有我不知道的新代码。而且这些代码可能(但愿不会如此)会与我的代码冲突!也许团队B中的某人会重新命名Widget类,可我已经在代码中调用了它而且……呃……等一下,我们不是刚讨论过这个话题了么?
没错,是同样的问题。解决方案也相同。但是范围有一点点不同。
规则:每天都从主干向你的工作分支中合并代码。
每天开始工作时,我所在团队中的某人负责将代码从主干中合并到我们的团队工作分支中(等同于“跟上”主干中发生的变化)。
如果我的团队(团队A)发现一个代码冲突,我们会马上解决它——这是优先级最高的事情!如果我们需要团队B的帮助(或者是开发与我们发生冲突的代码的负责人),找他们过来,一起工作,把问题解决掉。重要之处在于我的团队要负责解决问题,而且我们要在自己的工作分支上(而不是在主干上)完成。
规则:在最不稳定的分支上解决冲突。
当然,如果人们不经常向主干发布代码,那从主干合并代码就是浪费时间了。除非有人发布工作到主干上,团队A和团队B之间的任何分歧都会是不可见的。所以接下来的规则如下:
规则:经常从工作分支向主干合并代码,例如每完成一个故事之后。不要等到sprint结束后再做这个工作!
注意这里有一个有趣的副作用:
副作用:先检入代码者为王!
如果两个团队正在开发的代码互相冲突,后一个检入的团队必须负责解决冲突问题。这是一个好的副作用,因为它可以鼓励团队尽早检入代码。:o)
下面是一个完整Sprint的示例图:
两个团队进行了一次6天的sprint。团队A准备实现“预订”和“取消”。团部B准备实现“发票”和“黑名单”。让我们看看发生了什么。
Sprint完成了!除“黑名单”之外,所有的故事都完成了。但是没关系,我们还是可以发布的!因为我们是以增量的方式完成合并与集成的工作。如果我们等到sprint结束再做,任何分叉的代码就会在错误的时刻发现——此时我们能够用来修复问题的时间最少。
假定我们完成了sprint 1并发布了系统的1.0.0版本。现在,我们在进行sprint 2之中的工作时,有人报告说之前发布的版本中发现一个严重的缺陷!不!我们该怎么办?
最简单的方式是:在主干上修复该问题,并发布1.1.0版本。这就是说在sprint 2中任何新实现的故事都会包括在新发布版本中。理论上来说,这样做没有问题;因为主干是“完成”分支,而“完成”的定义就是“可发布的”。所以主干上的内容无论何时都是我们要发布的东西。
不过还是会有一些原因让我们不想马上发布新故事。例如:
我们具体该如何做呢?
注意我们在发布1.0.0版本时没有必要创建“发布1.0”分支,可以等到问题出现时再做。这样以来,除非真的需要建立某个分支以让我们对其做些什么,我们就不必创建额外的分支。
好,现在我已经给出了如何使用该模式的详细范例。让我们往后站一点儿,看看整个的图景是什么样子吧。
在主线模型中,一个分支被称为一条代码线(实际上,分支可以被人物是一条代码线的实现)。有时这些被成为流。
一条代码线的上级(也就是该代码线的起源代码线)被称为它的基线。主线是没有基线的代码线。
所以在上面的例子中,我们可以总结出:
下面是一个更复杂的例子:
这个图告诉我们:
每条代码线有一个相对其基线的坚固水平,也就是说每条代码线要不就比其基线更坚固,要不就不及其基线坚固(更柔软)。
在绘制代码线时,坚固的代码线分支向上,而柔软的代码线分支向下。观察上图,我们可以总结出:
使用上述描述绘制图表,对于展示分支历史来说很有用;但是如果同时有很多分支存在,就可能带来混乱。下面是一个更清晰的格式,仅展示了当前存在的代码线以及它们的衍生出处。
我建议以此格式绘制你的分支图,而且可以将其挂到团队所在房间的墙上。在讨论集成问题时参考此图真的很有帮助。
所有的变更都应沿着所在的线索发展,这是一条很重要的规则!所以不能直接从团队A的工作分支向团队B的工作分支中合并代码,这会导致很多混乱。实际上,在团队A工作分支中发生的变更应该流回到主线中,再向下进入到团队B的工作分支中。
任何位于主线之上的代码线都可以称为发布代码线,意味着一个比主线更坚固的代码线。
任何位于主线之下的代码线都可以称为开发中代码线(或工作代码线),意味着一个比主线更柔软的代码线。
协作黄金规则:
-总是接受稳定的变更。
-绝不强制使用会导致不稳定的变更。
那么这对于不同类型的代码线意味着什么呢?
上图以彩色的方式说明了:
无论变更何时应用到代码线及其基线,有些合并是必须要做的。因为代码合并是一个很容易犯错误的操作,我们希望在两条代码线中稍柔软的一条上进行。一旦合并完成而且通过检验,我们就可以将合并的代码复制回更坚固的代码线。
将坚固代码线比柔软代码线绘制得更高,使用这个惯例,我们可以推出一个简单的规则:
规则: 向下合并,向上复制
示例:团队A注意到主线已经更新了。他们将变更向下合并到自己的开发代码线,并修复任何冲突。只要团队A的开发代码线达到稳定的一个点,他们就可以将代码复制回主线。当然,他们必须检查同时主线上没有发生任何变更。
“版本控制模式”一节描述了一个如何实施主线模型的范例。
“大图景”一节以更通用的方式描述了主线模型。
本节中,我将针对如何实施该模式提出一些典型的变种。
先确定“完成”一词的任何定义,然后确保有一个分支可以容纳根据该定义已经“完成”的故事。不过还是要注意别遗漏重要的内容。如果集成测试没有包含在“完成”中,那什么时候进行集成测试呢?
这个模式需要一条主线才能进行下去,不过不必是主干(虽然在大多数情况下,使用主干是很自然的选择)。
当然可以有多个团队共享同一分支,甚至直接在主线上展开工作。只要保证遵循分支的方针即可。
通常,团队希望有自己的分支,以避免未完成的故事在多个团队之间造成干扰。
可以使用同样的发布分支,而不是在每个sprint结束后都创建新分支。那个分支可以称为“最新生产系统”或其他类似的名字。如果在生产系统中总是保持只有一个版本,这当然是很好的模型。
可以在每个故事完成后进行发布。或者每三个sprint完成后发布一次。确定你自己的步伐。
如果在你的环境中,回归测试或集成确实很难实际操作,那么可以在sprint接近尾声时进行。这样就可以针对一批故事进行测试和集成的工作。你自己要承担相关风险。如果回归测试和集成属于“完成”的定义,这可能意味着你在sprint末尾时可能会遇到问题,导致没有任何故事完成的风险。
CI服务器应该针对哪个分支运行?这要根据具体情况,不过下面的叙述是一个好的起点。
假定主干的方针是“完成而且可发布”,而每个工作分支的方针是“通过单元测试”:
不确定。我知道Perforce是可以的,我想subversion应该也没有问题,但是对于CVS我不敢打包票。欢迎任何新的建议。
不过要记得一个重要的事情——切换工具的成本要比不能有效协作产生的成本低得多!所以搞清楚你想怎么做,然后找到合适的工具来支持。
不是所有的代码变更都必须与某个用户故事相关的,在例子中,我只是为了描述的清晰才这样做。无论检入何种类型的代码(或文档之类),同样的模型也是可用的。
恭喜,你得了合并恐惧症——对于合并代码的非理性恐惧!合并让你觉得麻烦,是因为做的太少了。合并得越多,痛苦就越少。:o)
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/14780914/viewspace-410107/ ,如需转载,请注明出处,否则将追究法律责任。