利用“抽象分支”做增量式大规模软件改造

很多开发团队通常严重依赖于版本控制系统的分支功能。分布式版本控制系统让分支操作更加方便。然而,在《持续交付》一书中描述的很多非常规言论中,就有一条是:“使用分支,你就无法做持续集成”。根据定义,如果你有代码在某个分支上,那就没有集成。有一种很常见的情况,会让人很自然地想到利用版本控制工具的分支功能:那就是“对应用程序进行大规模改造时”。然而,还有一种替代这种真实分支的做法,技术上叫做“抽象分支(Branch by Abstraction)”。

抽象分支:在主干上进行以增量方式对软件进行大规模改造的一种模式。

Paul Hammant在2007年就提到过用这种方式把OR mapping的方案从Hibernate切换到iBatis(详见这里)。同样,我曾经工作的一款商业产品(持续集成与敏捷发布管理平台 Go)则从iBatis改到了Hibernate,这已经是两年前做的事情了。我们也把产品的UI层慢慢地从“使用Velocity和JsTemplate”转移到了“JRuby on Rails”上。

这两种变化都是慢慢地增量式地完成的,在改变的同时,也做新功能的开发,但这并不妨碍我们每天向Mercurail的版本库主干上提交数次代码,甚至在切换过程中做了数次正式发布。我们是如何做到的呢? Continue reading 利用“抽象分支”做增量式大规模软件改造

持续交付:价值主张​

过去十年中,一个划时代的改变就是:基于Web的业务模式对传统企业业务模式的冲击。亚马逊就是历史最长,也最明显的例子之一,而越来越多的公司(从航空到金融服务)开始依赖软件打造其竞争优势了。

​依靠软件来运行的业务有两个关键组件:一是你想如何改变世界的愿景,二是尽早收集用户的反馈。精益创业运动特别强调反馈的重要性,这不仅仅体现在创业公司。像亚马逊、NetFlix、和脸谱这样的公司也持续不断地对其网站进行小步改进,从而增加收入,并改善网站用户的体验。

​什么是持续交付?

想在用户与项目团队(包括客户或者Product Owner)之间建立紧密的反馈环,依赖于你是否有这样的能力,即:通过持续交付新的软件版本,验证新的想法和软件的改动,并能衡量这些改动对收入的影响。

对于很多需要几个月时间才能发布新版本的公司来说,在一天之内发布数次似乎是天方夜谭。然而,遵循《持续交付》一书中的原则与实践,一些原来一年才能发布几次新版本的公司,也已经能在一个月内发布数次,或者更频繁了。这是一种巨大的竞争优势,而且意味着,在时间和精力方面减少了大量的浪费。

实际上,持续交付有两个至关重要的业务收益:

  • 第一,它让你能更快地验证新业务方案的结果,并根据真实的用户反馈进行调整。尤其是当业务方案有根本性缺陷时,你一定希望能尽早地发现,而不是花数月或数年的时间和大量的金钱来实施计划后,才知道存在问题。
  • 第二,与项目结束后的一次性大版本发布相比,它能提供一种大幅降低交付风险的交付流程,而且成本的投入也更加具有可预见性。

另外,对于IT管理来说,它还提供另外两种好处:

  • 第一,项目经理们能看到项目的真实进度,因为,此时的“完成”是指:运行于真实生产环境中的可工作的软件已经为用户带来价值。
  • 第二,通过规律性增量发布,大大减少了每次发布的风险。

实现持续交付

为了能够成功实现持续交付,需要依赖于两个基础,一是技术基础,二是组织基础。而这两个基础有三大支柱:(1)全面的配置管理;(2)敏捷测试;(3)部署流水线。

配置管理:

就配置管理而言,为了实现持续交付,有四个关键点:

  • 第一,要能以全自动化的方式,构建、测试并部署你的应用。为了做到这一点,源代码、测试脚本、构建与部署脚本、数据库迁移脚本等统统要纳入到版本管理。
  • 第二,应用程序的部署时及运行时配置项需要以某种统一且集中地方式管理起来。
  • 第三,要能用全自动化的流程在每种环境上创建或者进行配置变更。
  • 第四,所有的开发工作必须在主干上完成,而且,能够对较大的特性或重构做增量实现,以便应用程序一直保持在可工作状态。如果必要的话,没有开发完的特性可以利用配置开关,使用户无法通过界面或公共接口使用它。

同时,有效的配置及发布管理也依赖于整个交付过程中开发团队与运维团队(包括DBA)之间的紧密合作。为了确保运维需求被纳入项目考虑范围,并让应用程序在开发初期就能部署到类生产环境中进行单一功能或跨功能测试,运维人员应在项目开始阶段就成为交付团队的一员。这种协作也正是DevOps运动所倡导的重要的文化转变之一。

敏捷测试

持续交付依赖的第二个支柱是敏捷测试方式。当然,它也同样包含技术和组织两方面。从工程角度来看,各个层次上的自动化测试是必不可少的,包括单元级别、组件级别,系统级别(功能与非功能测试)。要确保做到:如果自动化组合套件全部成功通过了,那软件本身就达到了可发布质量,即:在生产环境中没有回归缺陷,满足业务需要——包括容量和可用性方面的要求。

敏捷测试要求在整个交付过程中的质量内嵌。也就是说,测试不再是一个“阶段”,而应该是在整个交付过程中持续发生的一种活动。同样,软件的质量不再只是测试人 员或QA的责任,而是整个交付团队的责任。测试人员与分析人员应该在一起,共同创建可测试的验收条件。作为开发过程的一部分,测试人员与开发人员应该合作创建自动化的验收测试,用于验证所开发的内容满足验证标准,这叫做“验证测试驱动的开发(acceptance test-driven development)。开发人员要负责维护验收测试,并确保它们一直可以成功通过。

部署流水线

持续交付的第三个支柱是部署流水线。从本质上讲,部署流水线是现有交付过程的一个模型,是整个价值流的一部分,即从提交到发布的那一段价值流。可以用像Go这 样的工具对其进行建模。对于应用程序的任何修改(包括配置)都要从头到尾通过这个部署流水线,即先对系统进行构建,并基于该构建产物运行自动化测试,然后放到某处,以便后续部署到测试环境和生产环境。可以把部署流水线看做是持续集成的一个延伸,即:它将持续集成从开发团队扩展到了测试和运维团队。

用来建模和管理部署流水线的工具会记录每一次构建历史,它会记录每个构建所对应的版本控制库中的版本号,谁把它部署到了哪个环境,什么时候部署的,部署的结果是“成功”,还是“失败”。这个部署流水线应该强制你的部署工作流(包括认证和授权),并能对整个交付流程进行审计。由于这个流水线是对你的开发及部署过程进行建模,所以,它为发现流程瓶颈,持续改进交付过程提供了丰富的数据支持。

通过持续交付来管理项目

毫无疑问,对于刚刚接触持续交付的团队来说,对其所要求的纪律性会感到吃惊。很多直觉上应该如何管理项目的方式(包括团队成员之间如何互动)都需要改变。对团队来说,当实施持续交付时,和所有的敏捷方法一样,最重要的事情是通过回顾,持续反省他们正在做的事情,并对工作方式和过程做增量式的改变,并不断地改进它。

重新调整你的直觉

每个项目天生都有一种节奏和风格。传统的交付项目在项目开始和中期,总会有一个漂亮而体面的进度报告。然而,当到了某个时间点后,客户和管理层常常会有一些令其不悦的发现:尽管大部分内容都“开发完成”了,但在集成阶段,部署到具有现实负载的类生产环境中的应用程序总是不能工作得很好。

此时,这个项目就进行了所谓的“两难境地”,或叫“死亡行军”,人们在很大的压力上加班工作,试图修复整个系统。这令每个人都极不满意,而事实上, 在某些领域,这种失控模式已经成为一种家常便饭。

在那些实践持续交付的项目中,开始时进展可能比较慢。因为项目一开始要建立基础设施,对构建、测试和部署做自动化,并在得到成功构建的产品后,在类生产环境中执行验收测试。这让团队成员,尤其是刚接触它的管理者,感到不舒服。然而,这正说明它起作用了。持续交付的影响表现在它把痛苦提前了,从而让交付过程更加平稳、真实且可度量。

你原来体会到的是“开发完成“,与“真正完成”不一样。现实中,真正的“完成”是指发布给用户,或者至少在集成环境上严格地进行验收和回归测试后,在类生产环境中向客户演示。在新功能开发完后,其实还有很多工作要做:交付过程中的这部分被叫做最后一哩。持续交付坚持认为:“只有过了这一哩”,工作才算完成,根本就没有“完成了百分之五十”这个说法,所以,这让整个交付进度看上去比较慢。​

然 而,通过持续交付,你能得到可持续性。首先,因为持续交付意味着软件一直处于可发布状态,所以可以把原来的测试与集成阶段移掉,这就会大大降低项目风险。在这两个阶段,经常会发现严重问题,甚至是整个架构的缺陷。而修复这些问题的时间则很难预期。通过持续交付,这些问题在交付过程的早期就会被发现,些修复成本往往比较低。

​其次,对于新开发的特性来说,能更快地从客户和用户那里得到反馈。通过增量开发和持续发布给用户,你能在特性创作过程中就得到早期反馈,并不断调整。这样,软件的演进可以更快速地满足用户的需求。

设法进行增量开发

在主干上增量开发时,为了能够将大的特性拆分成小的、有价值的、可测试且相对独立的用户故事,需要分析人员、测试人员及开发团队的紧密合作。这么做,有几个好处。

可能最重要的好处就是,它促使团队将一个功能里,关键的部分和非关键部分区分开来,这让客户能在更小的粒度上做决策。将一个“必须做”的特性分解成多个小的、可增量开发的小需求,能更有效地区分出哪些是该功能的核心,哪些是外围需求。

然后,你可以将功能的核心部分(最初的最直接的实现)给用户进行测试,并找出用户接下来想做什么。这样可以给客户一些具体的数据,让他们更好地对该功能的剩余工作进行优先级的排序。当然,也很可能在他们看过以后,客户希望将后续的时间花在其它功能上了。而且,假如客户看过以后。认为这个特性没有什么价值,你可以马上停下来,不再浪费精力在这上面了。

增量开发依赖于每个人每天都能够提交一次,这样,应用程序才可以持续集成。对于那些习惯于在长生命周期分支上开发新特性或每个分支对应一类客户的团队来说,这看上去是不可能的。好消息是,在主干上开发是可行的,对于很大的团队也是可行的。坏消息是,它要求应用程序由松耦合的组件构成,并有良好的架构。它还要求,可以通过配置项在UI上能控制特性的开与关,这样,你才能可以灵活地控制,当功能准备好后,再打开开关。即使一些新功能只开发到一半,你也能持续地发布你的软件。

然而,这样做的收益也很大:你能持续不断地把已经做好的功能交付给客户,而不用绞尽脑汁地猜测哪个功能有价值。频繁地发布有价值的新功能是提高收入的 最好方式。

我怎么知道我是不是真的在持续交付?

持续交付是否需要一个像“Nokia 测试”那样的checklist呢?实际上,没有那个必要,因为有一个非常简单的方法来验证,你是不是真的在持续交付:你的软件是不是一直处于产品可发布状态。你只要按个回车键就可以把 它发布给用户。如果你的发布过程很痛苦,而且不太频繁,并且在发布之前还有一个充满风险的集成阶段,那么你就没有在做持续交付。

持续交付中最重要的度量是周期时间(cycle time):从决定实现某个想法开始,到将其发布给用户为止这段时间长度。有几个原因让它成为一个很重要的度量:

  • 第一,这是一个全局度量指标,用于度量整个过程的有效性。
  • 第二,这是一个团队度量指标,而不是一个个人度量指标——只整个团队关注于改进交付过程,才有可能改变周期时间。

部署流水线提供了部分答案:部署流水线的实现会给你提供一些你需要的数据,主要是从提交到发布这段时间的。那么,只要你有一种方式将提交与特性关联起来,你就可以看到某个特性的周期时间。如果现在的工具不能让你收集这些数据,那就找一个去吧。

这样,你就可以制定一个持续改进流程,应用约束理念 ,或者使用看板,在交付过程中找到瓶颈,并想办法移除它们。持续交付可以也应该同样使用持续改进的方式减少成本,提高收入的增量地方式实现。

 

原文刊登于InfoIT

主干开发,“关上”未完成的功能

软件开发中,“配置开关”根本算不上是一个“新”概念。无论是通过启动时加载配置,还是应用程序运行时动态刷新配置,都可以用来决定或改变应用程序的某种具体行为。比如:

  • 通过配置文件,使用应用程序连接不同的数据库
  • 通过模板配置,达到应用程序“换肤”的目的
  • 通过系统管理员的配置,使不同的用户具有不同的权限

所有这些“开关”都被认为是理所当然的,因为用户的需求就是这样的,“我需要通过xx开关来控制yy”。因此,做为交付团队,也会老老实实地把这些需求实现了。

然而,现在的很多团队,为了能够尽早发布软件,使用主干开发方式,并在程序中加入了另外一种“对用户透明”的开关。这种开关通常有两种使用场景是:

  1. 某些高级(或创新)的特性已经开发完毕,但业务人员根据市场情况,认为不需要投放市场。
  2. 市场需要某些已开发完成的特性,但还有一些特性尚未完成,仍旧处于开发中。

无论哪种情况,都是要求将某些特性进行隐藏后再发布。这种开关常常被认为是不可取的,但现在很多需要“持续交付”的公司都在使用。

(有的同学会说:“这完全可以通过按特性拉分支的方式来解决”。关于这个问题,我会在下一篇中讨论,本次仅限于讨论在使用“主干开发”的情况下,如何正确使用开关。)

开关最容易的地方就是应用程序的用户界面。当为已上线的应用程序重新设计了全新交互界面后,团队无法在一次性在同一发布周期中将其修改完成时,可以 先保留原有页面不变,同时每次仅替换少数的新页面。令所有未开发完成的页面对用户不可见,例如:原有页面的URL是 http://xx.xx.xx.xx/create_user,那么对应的新页面使用http://xx.xx.xx.xx/new_UI /create_user(要对后台逻辑的修改应能够同时响应新旧两个页面的请求)。当新页面完成后,将原来创建用户的页面链接重新指向新页面就可以了。 这样,就能在不破坏原有功能的前提下,做到持续发布。

开关实现形式

另外,对于那些无法在一个发布周期内实现的新功能,我们可以使用下面形式的“开关”:

  • 配置文件,通常在启动时加载

<features>
<feature name=”hq.remote_panel_load” />
<feature name=”el.enable_asset_library” />
…etc…
</features>

  • 在代码中实现

public function isFeatureOn(featureName:String):Boolean {
var nodes:XMLList = xml.features.feature.
(@name==featureName);
return null != nodes && 0 < nodes.length();
}

  • 设计成API

${core-url}/accounts/featureBits?userUid=&orgUid=&

而在编码中,可以使用不同的实现方式:

  • 最简单的检查

if( registry.config.isFeatureOn( featureName ) ) {
// new implementation …
} else {
// the old way …

}

  • 使用策略模式

if( registry.config.isFeatureOn(“ct.analyzer.v2”) ) {
service.analyzer = new Analyzer_v2();
} else {
service.analyzer = new Analyzer();
}

  • 使用工厂模式

public function createMainDisplay():DisplayObject {
if( registry.config.isFeatureOn( “service.panel.v2” ) ) {
return new panel2(); // which extends panel
} else {
return new panel(); // which extends DisplayObject
}
}

  • 使用责任链方式

if( registry.config.isFeatureOn(“hq.trickle_reporting”) ) {
userActionLogger = userActionLogger.setNext(new       TrickleReportNotifier() );
}

注意事项:

  • 提前评估是否需要开关,需要的话提前设计,不应该随意在代码中添加。
  • 定义一个开关命名规则,并在团队中达成共识。
  • 尽可能在设计时考虑在哪些层次上加开关。

“开关”在使用中的反模式:

  • 代码中到处都是开关,个数太多;
  • 在开关完成其使命后,未及时清理,使代码变烂。

现在,很多公司的产品都使用这种开关方式。进一步阅读,请参见:

相关工具:

代码示例来自于:Erik Sowa,”Feature Bits” on slideshare.net

本文版权归作者乔梁所有,转载请包含作者签名和出处,不得用于商业用途,作者将保留“追究法律责任”的权利!

持续交付的8条原则

  1. 软件的发布或部署过程必须是可重复且可靠的。这就引出了下一条…
  2. 所有操作的自动化!我很难相信“手工操作是可重复且可靠的”这种说法。所以一定要将所有重复性的操作变成自动化的,从而变得可靠。
  3. 如果某件事情做起来很困难或者让你觉得很痛苦,那么就尽早且尽可能频繁地去做。乍一看上去,这么做太蠢了,因为人的直觉反应是:应该推迟这件事。然而,实际上,这句话是说:如果做某件事很痛苦,一旦要求自己更频繁地做,你就会有动力想出各种办法,来解决这个痛苦,很可能把它变成了自动化的,最终会把它变成一件简单容易的事情。就拿更新数据库结构来说吧。一般来说,没人想频繁地修改它,所以就会尽可能推迟或少做,比如一个月做一次更新,或者更长。然而,你真正需要做的却是改进数据库结构调整的流程,让它变成更容易,更频繁。甚至如果必要的话,可以一天做一次。
  4. 对所有内容进行版本控制。当今软件行业还在强调这种要求,你可能会觉得奇怪,谁现在还没有用版本控制呢?但是,我指的不仅仅是源代码哟,还包括环境、配置、数据等等。
  5. 完成意味着“已发布”。也就是说,项目的“完成”是指把它交到用户手中,并且可以正常工作。而不是“我已经提交了,后面的我不管了”,或者“我已经提测啦”,或者“我测试完了,没有问题。”
  6. 内建质量。在质量度量方面花一点儿精力。从长期维护的角度来讲,具有良好质量度量目标的项目(如单元测试覆盖、代码风格、复杂度等等) 要比没有这些度量的项目更容易一些。
  7. 每个人都要对交付过程负责。在开发人员机器上运行的程序不会为公司带来收益。没有部署的项目也一样。开发人员也应该时刻想着如何部署手中的软件。项目经理也应该关注什么时间部署。测试人员也应该进行部署测试。
  8. 持续改进。软件开发如“逆水行舟”,不进则退。持续改进意味着,你的系统需要一直改进,这样当需要时,才能很容易修改。

原文:http://www.dzone.com/links/r/the_8_principles_of_continuous_delivery.html

配置与发布管理成熟度模型1.0版

在过去多年的持续集成咨询经历中,最常听到的一个问题是:如何来评估企业在配置与发布管理方面做得到底怎么样?还需要在哪些地方进行改进或提高?

两年前,CITCON的几个参与者给出了一个持续集成成熟度模型,聚焦于“持续集成实践”,而在《持续交付》一书中,Jez与Farley也结合ThoughtWorks在这方面的实践经验,总结了“配置与发布管理成熟度模型”,并在多个项目中进行了实际应用,效果不错。

由于这是通用模型,所以各等级中多为定性描述,在拿到具体企业使用时,可以根据实际情况,对其中一些定性描述进行量化,比如“提交不频繁”“定期沟通”等。

如何使用这个成熟度模型呢?

  1. 找出你所在团队在各维度上所处的级别。
  2. 找出你所在团队需要改进的维度,以及在该维度上的改进目标。
  3. 根据目标与现状,制定改进行动计划并实施。
  4. 重新评估,再制定下一个目标。

任何流程都是可以改进的,所以没有最好,只有更好。所以最高一级是“持续优化”。

模型内容可从以下地址下载:

http://download.csdn.net/source/3454593