如果你还没有经历过不能回退系统的痛苦,那么如果继续玩火,不停地迅速修复系统,迟早会遇到这种问题。不要用应用太复杂或者代码发布太频繁作为借口,拒不加入回退代码的功能。一个明智的飞行员,如果没有具备让飞机着陆的能力,就不会让飞机起飞。一个明智的程序员,如果不能让系统在紧急情况下回退代码,就不应该发布代码。
为了给接下来要讲的原则制造气氛,我们应该在深夜围坐在一堆篝火周围讲恐怖故事。我们要讲的是经典的恐怖故事,就是人们在房子里听到了恐怖的声音但并不逃跑的事。那些忽视所有警告信号的傻瓜就是我们。作为程序员的上司和CTO,我们收到过几平每个经理架构师和程序员的汇报:应用太复杂了,不能进行回退。我们自己对此也确信无疑。代码发布后曾经出现过几次中断或问题,先是疯狂地迅速修复,之后在同一天中得到一个热修复补丁,以便完全恢复服务。我们忍受了这种小小的不便,因为我们认为这个应用太复杂了,不能进行回退。
和之前发布的所有版本一样,一个主要的基础设施的版本发布后,也不能进行回退。这次发布简直是场灾难。凌晨时,一切看起来都很好但当天亮了以后,流量激增,这个站点就招架不住了。如果回退,只会让几个用户不高兴,给自己留点儿小伤痕,但不会有更糟的事情了。但我们却不能回退,所以只好花费一整天的时间,为这个站点做点儿增加容量、限制流量等工作,以便在得到修复补丁之前保持一切仍然运行。那天晚上我们推出了一个补丁,当时站点并没有流量,所以我们认为问题已经修复了。第二天早晨,当流量增加后,这个站点又开始有问题了。只好又在晚上打补丁…这种模式持续了一周多。
接连折腾了这么多天,到那周结束时,所有人都已精疲力竭。最后,我们打了一个补丁,完全忽略之前的所有修改,终于使站点稳定了。虽然从这次事故(包括指挥失误)中可以学到很多东西,但与本条原则关系最紧密的是,无论我们还是客户所经历的所有痛苦都是可以通过回退代码避免的。
事后总结经验教训,我们确定日后不允许再发布没有回退功能的版本。当时除了发出这个布告外,我们别无选择,客户无法容忍这种性质的问题,每个程序员也都理解了这种要求的必要性。六周后,当下一个版本准备好时,我们已经能够进行回退了。我们曾经都认为难以克服的困难变得相当简单了。
下面列出了要具备回退功能需要注意的几个关键点。是的,回退的主要难点在于数据库。通过仔细检査应用,一一排除那些明显的问题,然后坚持几个简单的原则,所有团队都能够进行回退。
口数据库修改只能是増量的。在下一个废除了列之间的依赖关系的版本发布前,只能添加数据库中的列或表,不能删除。一旦实施了这些标准,每个版本都应该有一部分代码专门用于清除上一个版本遗留的不再需要的数据。
口DDL和DML必须脚本化且测试过。每个版本中对数据库的修改必须通过脚本实现,而不能手动进行。其中应该包括回退脚本。这样做的原因有两点:1)团队需要在QA或某个阶段测试回退操作,以便验证什么都没有漏掉,不会妨碍回退;2)需要在一定负载的条件下测试脚本,确保在应用程序使用数据库时,它仍然能够执行。
口对应用中的SQL查询进行约束。开发团队需要消除所有SQL语句中的歧义,删除所有 SELECT*查询,并且给 UPDATE语句加上要更新的列的名字。
口数据的语义修改。在发布版本中,开发团队不能修改数据的定义。举个例子。票务表中的一列用于存放状态信号,其中有三个值assigned、fixed和 closed。在应用的新版本中,如果没有发布处理新状态的代码,就不能添加第四个状态。
口Wire On/ire Off。应该让应用结构化,使其能根据外部配置,让有些用户能够访问某个代码路径和功能,而有的用户则不能访问。这种设置可以存放在配置文件中,也可以存放在数据库表中既能够根据角色赋予访问权限,也能够根据随机百分比分配权限。有了这种结构,就能够让有限的用户对新功能进行beta测试,而且能够迅速地删除主要bug的代码路径,从而不必回退整个代码。我们得到的教训虽然惨痛,但是很有网站建设价值,有了这次教训,我们再也不会发布不能回退的代码了。即使以后和其他团队一起工作,我们也会这样要求自己的。可见,这些原则并不复杂,而是相当简单,任何团队都能够应用它们,都能具备回退的能力。
本文地址://www.gogoparty.cc//article/3492.html