早些年开发软件,一个版本发布上线的时间周期是以“月”甚至“年”为单位计的,但是现在随着敏捷开发的推行和普及,版本上线的周期变成了“周”为单位,甚至更短。周期缩短,并不意味着要牺牲质量,而是一样会有完善的开发流程来保障质量,比如设计、开发、自动化测试和手工测试。但是当缩短开发周期的时候,可能原本运行好好的开发流程就会出问题,软件质量下降,需要去重新调整开发流程,以重新做到高效和稳定。

在这里将向您分享一下我所在团队的经历,我们从两周一个发布周期,到每周一个发布周期,都遇到了什么问题和挑战,最终如何克服和解决的。希望能对您有所帮助启发。

在两周一个版本的迭代中,我们是进行项目开发的?

开发流程

在这里先简单介绍一下,我们两周一个Sprint的开发流程是什么样的。整个开发流程如图所示:
从两周发布上线到一周发布上线,如何做到高效稳定?-LMLPHP

Sprint计划和部署上一版本到生产环境
每个Sprint开始前,会先有一个计划会议,规划好当前Sprint 要完成的新功能和要修复的Bug,这些任务都在Jira上用一条条Ticket记录和跟踪。

编码和分支管理
在计划好了后,紧接着是一整周的开发编码,这期间会对计划好的Tickets进行编码。我们是基于分支开发的,在开发一个Ticket之前会创建一个新的分支,开发完成后将分支代码提交PR,代码审核通过以及自动化测试完成后合并到git的master分支。

测试
在一周的开发编码完成后,就会基于master的最新代码创建tag,并发布到测试环境。QA人员会对发布的功能进行自动化测试和手工测试,发现的bug会提交ticket进行跟踪。开发人员会对报的bug进行修复,修复bug的代码会继续合并到master。

代码冻结
由于我们要部署的代码都在master上,如果master持续的合并代码会不太稳定,所以在Sprint最后两天会对master的代码冻结,除非重要的bug修复,否则新的PR不合并,等到下一个Sprint开始再统一合并。

部署生产环境
在测试结束后,我们会根据版本创建release tag,这样根据tag就可以部署对应的版本到生产环境。

两周Sprint的优缺点

这样两周一个Sprint的周期在执行的时候还算比较顺利,上线的版本由于测试较为充分,所以比较稳定。但缺点就是两周才能发布一个新的版本,响应需求的速度较慢;两周一个版本,由于更新的代码较多,在上线后出现问题也不容易定位。

其实上面的开发流程还有一个问题,只是由于两周一个迭代,并没有暴露出来。这个稍后再讨论。

在一周一个版本的迭代中,我们是进行项目开发的?

正是基于两周Sprint的一些问题,我们决定改成一周一个Sprint,这样就可以更高频率的发布新版本;发布的版本变更较小,有问题也能及时定位和发现。

在最开始的时候,一周一个Sprint的开发流程和之前两周一个Sprint的开发模式类似,只是把相应的开发和测试时间缩短了。如图所示:
从两周发布上线到一周发布上线,如何做到高效稳定?-LMLPHP

改成一周一个Sprint后遇到的问题和挑战

但在变成一个周一个Sprint后,我们很快发现版本质量下降了,经常在上线后发现严重的问题而需要回滚或者打补丁。

于是在一次Retro会议(项目回顾会议)上,我们的QA提出了一个建议:“Avoid last minute change”。背景是我们的服务刚刚有过一次生产环境故障,故障的原因是因为开发在上线前有过一些改动,原以为改动很小,不会有问题,Code Review没发现问题,部署到测试环境简单测试后,也没发现问题。结果部署上生产环境后,出现严重的问题。
从两周发布上线到一周发布上线,如何做到高效稳定?-LMLPHP

这是个很好的建议,然而我们能避免上线前临时修改吗?避免了上线前的临时修改服务就会稳定吗?

在接下来的时间里,我们努力避免上线前的临时修改,上线前的修改需要更严格的代码审查流程,然而想完全避免上线前的临时修改是几乎不可能的,总有一些上线前才发现的问题需要修复。

那么在减少了上线前的临时修改后,我们的服务更稳定了吗?

有一点改善,但并没有完全解决,服务还是不太稳定,而且很多服务器故障并不是由于上线前的临时修改导致的!

这就说明上线前的临时修改并不是导致服务不稳定的根本原因,那么到底是什么原因导致的服务不稳定?

于是我们又尝试了一些探索和改进,比如说增加更多的自动化测试、上线后对主要功能做一些手动的检查。这些方法都有一点效果,但都属于治标不治本的措施,服务还是不算稳定。

是什么原因让服务不稳定

对于这个问题我一直没有答案,直到几个月后,又一年的“Holiday Readiness”,也就是美国的购物季,从万圣节开始一直持续到新年,商家有各种促销打折活动,民众也是各种买买买。这期间对于我们这种线上消费类网站来说,稳定性要求特别高,当机一点点时间都可能造成巨大损失。

如何保证服务的稳定呢?根据我们过去几年的血泪教训中总结出来的经验,保证服务稳定的最简单有效的措施就是:节日期间不更新,除非必要的小更新和补丁!

所以在消费季,我们会实行“Soft/Hard Moratorium”,也就是“Holiday Readiness”期间,我们不上线新功能,只做必要的补丁更新以修复一些严重的线上故障。并且上线需要有严格的审批流程。

为了应对公司的“Soft/Hard Moratorium”策略,我们组也做了一些调整:

  • 首先我们创建了一个holiday的branch分支,这个分支只修复生产环境的Bug或者必须的新功能更新。其他常规的开发依然放在master主分支。

  • 然后每次holiday分支的修改在部署生产环境前,都预先在测试环境测试一周左右时间,测试没问题后再部署生产环境。

这样实施下来,在“Holiday Readiness”结束的时候,我发现我们的服务异常的稳定,虽然有过数次更新,但是一次故障都没有发生。这给我很多启示,让我意识到之前服务之所以一直不稳定,有两个主要原因:

原因一:没有一个稳定的可随时发布的分支

首先,我们的版本发布是基于master分支发布的,新功能和Bug修复的PR都会合并到master,这就意味着master分支一直是不稳定的。

在以前每两周一个Sprint的时候,这个问题就存在,只是当时因为测试时间更长,所以没有暴露出来。当改成每周一个Sprint后,这个问题就很严重了,导致了很多上线前的临时修改。

在“Holiday Readiness”期间,我们有一个稳定的holiday分支,这个分支只有bug修复,几乎没有新功能的代码,所以相对要稳定很多。

原因二:测试时间不够充分

在以前每两周一个Sprint的时候,我们有3-5天的时间测试,有大的问题问题基本上能在测试环境发现,当改成每周一个Sprint后,留给测试的时间只有1-2天,这点时间是很难充分测试的,所以很多问题要在上线后才暴露出来。

在“Holiday Readiness”期间,在每次代码修改后发布前,在测试环境都会有一周左右的测试时间,这样Bug就能得到充分的暴露,而不至于到生产环境才发现,而导致回滚或者补丁。

怎么改善发布流程?

既然已经发现了问题所在,怎么去改进就相对容易了。所以我针对当前流程提出了一些改进的建议。

每个Sprint对应一个稳定的分支

对于没有稳定分支的问题,很好解决,那就是在每次Sprint完成,我们都创建一个对应的Release分支,Release分支创建好后,类似于我们之前在holiday分支做的那样,只做Bug修复,不增加新功能代码。

对于前面说到的上线前临时修改,如果是紧急Bug更新,那么放到Release分支,如果是其他的,则只合并到master分支,不会影响到release分支。

给测试留足时间

对于测试时间不够的问题,一个简单可行的方案就是回到两周一个Sprint的开发方式。但是大家已经习惯了一周一个Sprint的节奏,尤其是产品经理,希望新功能能尽早上线,更喜欢保持一周一个Sprint。

那么怎样在一周一个Sprint的情况下,保证有充足的时间测试呢?

于是我提出了一个简单可行的方案:在“Holiday Readiness”结束后,推迟第一个Sprint的上线时间一周。

具体做法是:我们的第一个Sprint完成后,不上线生产环境,在测试环境保留一周,同时开始第二个Sprint的开发,等到第二个Sprint开发完成后,上线第一个Sprint的版本,第二个Sprint的版本发布到测试环境测试一周,同时开始第三个Sprint的开发。

如图所示
从两周发布上线到一周发布上线,如何做到高效稳定?-LMLPHP

这就意味着我们每一个Sprint开发完成,有完整的一周时间进行测试和Bug修复,但我们还是可以每周一个版本部署生产环境。

经过这样的调整后,我们的服务马上就稳定下来了,基本上不用担心发布后会有严重的质量问题,也极少需要上线后回滚或补丁。

当然这样调整后,也带来一些小的问题:

问题一:有两个Sprint在并行

在当前Sprint开发的同时,还要对上一个Sprint的Bug进行修复。但一般Bug的修复都比较简单,这样并行并没有带来太多的问题,我们的开发人员对这种模式适应的很好。

问题二:多分支管理

由于每个Sprint都会创建一个分支,那么意味着修复一个Bug,有可能要将修改同时合并到多个分支。

举例来说,在生产环境发现一个问题需要打补丁,那么这个PR要合并到生产环境版本对应的分支,还要合并到测试环境版本对应的分支,最后还要合并到master;如果在测试环境发现的Bug,需要同时合并到测试环境版本对应的分支和master。

不过相对于稳定性带来的提升,这一点不便还是完全可以接受的。

问题三:开发过程不易理解

这个开发模式在我们组内部运行的很好,但是对于组外的人来说,不是很容易理解,对于他们来说,最关心的是:我的需求或者PR什么时候能部署到测试环境,什么时候能部署到生产环境。

于是我们开发了几个工具,可以直观的知道当前开发中的是在哪个版本,测试环境是哪个版本,生产环境是哪个版本。
从两周发布上线到一周发布上线,如何做到高效稳定?-LMLPHP
(电视投影)
从两周发布上线到一周发布上线,如何做到高效稳定?-LMLPHP

配合这样的小工具,无论是组内还是组外,都可以很直观的了解当前的开发状态了。

总的来说,新的开发流程实行后,虽然存在一些小的麻烦,但是运行的很不错,服务的稳定性大幅提升。release分支让我们有一个随时可以发布的稳定版本;一周的测试时间可以很好的保证质量;同时每周一个版本的发布频率也可以让我们及时响应需求,有问题能及时发现。

Chrome的开发流程

无独有偶,后来我发现Chrome的开发流程,和我们现在的这套开发流程很类似,它是6周的开发流程,其中上一个版本的测试和当前版本的开发也是重叠的。
从两周发布上线到一周发布上线,如何做到高效稳定?-LMLPHP
Chrome Release Cycle

总结

软件工程中没有银弹,不可能有一种开发模式适用于所有的软件项目。当项目发生变化时,以前一些运行的很好的开发模式可能渐渐的不适合了,这时候就需要先找出问题产生深层次的原因,然后对症下药,探索出适合于当前项目特点的开发模式。

08-24 15:36