35

从两周发布上线到一周发布上线,如何做到高效稳定?

 3 years ago
source link: https://zhuanlan.zhihu.com/p/196083453
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

从两周发布上线到一周发布上线,如何做到高效稳定?

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

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

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

开发流程

在这里先简单介绍一下,我们两周一个Sprint的开发流程是什么样的。整个开发流程如图所示:

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的开发模式类似,只是把相应的开发和测试时间缩短了。如图所示:

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

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

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

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

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

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

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

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

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

是什么原因让服务不稳定

对于这个问题我一直没有答案,直到几个月后,又一年的“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的开发。

如图所示:

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

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

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

问题一:有两个Sprint在并行

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

问题二:多分支管理

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

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

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

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

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

于是我们开发了几个工具,可以直观的知道当前开发中的是在哪个版本,测试环境是哪个版本,生产环境是哪个版本。

(电视投影)显示各个环境部署的版本信息

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

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

Chrome的开发流程

无独有偶,后来我发现Chrome的开发流程,和我们现在的这套开发流程很类似,它是6周的开发流程,其中上一个版本的测试和当前版本的开发也是重叠的。



Chrome Release Cycle​chromium.googlesource.com

总结

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


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK