不再需要运维团队手动构建和配置环境,而是可以使用自动化的方式完成以下操作:
复制虚拟化环境(如 VMware 虚拟机镜像、执行 Vagrant 脚本,以及启动 Amazon EC2 虚
拟机镜像文件);
构建“裸金属物理机”的自动化环境搭建流程(例如,使用 PXE 方式通过基线镜像进行
安装);
使用“基础设施即代码”的配置管理工具(例如 Puppet、Chef、Ansible、SaltStack、
CFEngine 等);
使用操作系统自动化配置工具(例如 Solaris Jumpstart、Red Hat Kickstart 和 Debian preseed);
使用一组虚拟镜像或容器(例如 Vagrant 和 Docker)搭建环境;
在公有云(例如 Amazon Web Services、Google App Engine 和 Microsoft Azure)、私有云或其他 PaaS(平台即服务,如 OpenShift 和 Cloud Foundry 等)中创建新环境。
自动化的环境搭建过程确保了一致性,减少了繁琐、易出错的手动操作,运维人员能从这种快速搭建新环境的能力中获益。开发人员也得到了好处:他们在工作站上就能再现生产环境的必要部分,并在这个环境中构建、运行和测试代码。
为了确保即使在发生灾难性事故时,也可以重复且精确地(最好还能快速地)恢复生产环境必须把下列资源也纳入版本控制系统:
应用的所有代码和依赖项(例如库、静态内容等);
任何用于创建数据库模式的脚本、应用的参考数据等;
上一节描述的所有用于搭建环境的工具和工件(例如 VMware 或 AMI 虚拟机模板、Puppet或 Chef 配置模块等);
任何构建容器所使用的文件(例如 Docker 或 Rocket 的定义文件和 compose 文件等);
所有支持自动化测试和手动测试的脚本;
任何支持代码打包、部署、数据库迁移和环境置备的脚本;
所有项目工件(例如需求文档、部署过程、发布说明等);
所有云平台配置文件(例如 AWS CloudFormation 模板、Microsoft Azure Stack DSC 文件,以及 OpenStack HEAT 模板文件);
创建支持多种基础设施服务(例如企业服务总线、数据库管理系统、DNS 区域文件、防火墙配置规则和其他网络设备)所需的任何其他脚本或配置信息。
仅能重现生产环境之前的状态是不够的,还必须能够重现整个预生产环境和构建过程。因此,需要把构建过程所依赖的一切也都纳入版本控制系统,这包括所用工具(例如编译器和测试工具)及其所依赖的环境。
版本控制还为价值流中的所有人提供了有效的沟通方式,让开发人员、QA 人员、信息安全人员和运维人员都能够看到彼此所做的变更。这有助于减少信息不对称,并帮助建立和增强信任
为了确保环境的一致性,所有对生产环境的变更(配置变更、打补丁、升级等)都需要被复制到所有的预生产环境以及新搭建的环境中。
必须确保所有变更都能自动地被复制到所有环境中并被版本控制系统记录,而不需要手动登录服务器进行变更操作。
可以依靠自动化配置管理系统保证一致性(例如 Puppet、Chef、Ansible、Salt、Bosh 等),也可以通过自动化构建机制,创建新的虚拟机或容器,将其部署到生产环境,再销毁或移除旧资源。
为了杜绝不受控制的配置差异,可以禁止远程登录生产服务器②,或定期删除和替换生产环境中的实例,从而确保移除手动变更。这会促使所有人都通过版本控制系统用正确的方式进行变更。
“完成”不是指开发人员认为已经完工,而是指可以成功地构建和部署应用,并且确定它在类生产环境中按照预期运行
需要确保任何人都能按需获得类生产环境。通过让开发人员在软件项目的最初阶段就使用类生产环境,可以显著降低生产环境出现问题的风险。这
通过把所有生产工件纳入版本控制系统,我们有了“唯一的事实来源”,这使我们能够用快速、可重复和文档化的方式重新搭建整个生产环境,并在运维工作中采用和开发工作一致的实践。
部署流水线确保所有检入版本控制系统的代码都是自动化构建的,并在类生产环境中测试过。这样一来,当开发人员提交代码变更后,立即就能获得关于构建、测试或集成错误的反馈,从而使开发人员能够立刻修复这些错误。
每次代码变更之后,部署流水线都会确认代码已经成功地集成到类生产环境中。通过部署流水线这个平台,测试人员进行验收测试和可用性测试,以及自动化性能测试和安全性验证。
部署流水线的第一个环节是提交阶段,这个阶段完成代码构建和打包,运行自动化单元测试,并执行其他各种验证,如静态代码分析、测试覆盖率分析、重复代码检查以及代码风格检查等。
部署流水线的目的是给价值流中的所有成员(特别是开发人员)提供尽可能快速的反馈,帮助他们及时识别可能让代码偏离可部署状态的变更,
每当有新的变更检入版本控制系统时,就需要在构建和测试环境中运行快速的自动化测试。立刻发现和解决所有集成问题。这样就能维持较小的代码集成量,并保证代码始终处于可部署状态。自动化测试从快到慢分为如下几类。
单元测试:通常独立测试每个方法、类或函数。它的目的是确保代码按照开发人员的设计运行。
验收测试:通常整体测试应用,确保各个功能模块按照设计正常工作(例如符合用户故事的业务验收标准,API 能正确调用),而且没有引入回归错误(即没有破坏以前正常的功能)。单元测试和验收测试的区别在于:“单元测试的目的是证明应用的某一部分符合程序员的预期……验收测试的目的则是证明应用能满足客户的愿望”在构建的版本通过单元测试后,部署流水线就对其执行验收测试。任何通过验收测试的构建版本通常都可用于手动测试(例如探索性测试、用户界面测试等)和集成测试。
集成测试:保证应用能与生产环境中的其他应用和服务正确地交互,而不再调用打桩的接口。
当面对项目最后期限的压力时,可能不再在日常工作中编写单元测试。为了发现并杜绝这种情况,需要度量测试覆盖率还要把度量结果可视化,甚至可以在测试覆盖率低于一定水平时使测试套件的验证结果显示失败。
要在执行那些耗时的自动化测试(如验收测试和集成测试)之前,执行完速度更快的自动化测试(如单元测试)。这两种测试都要先于手动测试执行。
每当验收测试或集成测试发现一个错误,就应该编写相应的单元测试,以便更快、更早、更廉价地识别这个错误。“理想的测试金字塔”这一概念,即使用单元测试捕获大部分错误,
如果编写和维护单元测试或验收测试既困难又昂贵,说明架构可能过于耦合,即各个模块之间不再有(或者从来就没有)明显的边界。在这种情况下,需要构建更松散耦合的系统,使模块可以不依赖于集成环境进行独立测试。
任何通过所有自动化测试的构建版本都可用于探索性测试以及其他形式的手动测试或资源密集型测试(如性能测试)。
任何测试人员(包括所有开发人员)都应该使用通过了所有自动化测试的最新版本。
要确保自动化测试可靠,最有效的一个方法是通过测试驱动开发和验收测试驱动开发,在对系统做任何变更时,都要先编写一个自动化测试用例,执行并确保测试失败,然后再编写实现功能的代码,并且让代码通过测试。
自动化测试套件和程序代码一同被检入版本控制系统,以提供一套可用且最新的系统规范。如果开发人员想了解如何使用系统,可以查看测试套件,找到演示如何调用系统 API 的示例。
应该从少量可靠的自动化测试开始,并随着时间的推移不断增加。这样一来,系统的保障级别随之提高,并能快速检测出所有让代码偏离可部署状态的变更。
编写和执行自动化性能测试的目标是验证整个应用栈(代码、数据库、存储、网络、虚拟化等)的性能,并把它作为部署流水线的一部分,这样才能尽早发现问题,并以最低的成本和最快的速度解决问题。
如果能了解应用和环境在类生产负载下的表现,就可以做出更好的容量规划,以及检测出如下情况:
数据库查询时间非线性增加(例如忘记为数据库创建索引,导致页面加载时间从 100 毫秒增加为 30 秒);
代码变更导致数据库调用次数、存储空间使用量或者网络流量增加数倍。
为了能尽早发现性能问题,应该记录所有性能测试结果,并对比上一次结果,评估各项性能指标。例如,如果性能测试结果与上一次的偏差超过 2%,则可判定本次性能测试失败。
非功能性需求,包括可用性、可扩展性、容量以及安全性等。
当部署流水线失败时,至少要告知整个团队。所有人要么都去一同解决问题,要么回滚代码,甚至可以把版本控制系统配置为拒绝后续的代码提交,直到部署流水线的第一阶段(即构建和单元测试)恢复成绿色状态。
为了更容易发现失败的自动化测试,应该安装非常直观的指示器,以便团队的所有成员都能看到。很多团队在墙上安装了直观的灯光装置。
如果不拉下安灯绳,也不立即解决部署流水线的问题,就会导致应用和环境更难恢复到可部署状态。
为了更好地促进工作,需要一个可以由开发人员或运维人员来执行的代码发布流程,并且在理想情况下,应该不需要任何手动操作或工作交接。这个流程的步骤如下。
构建:部署流水线必须基于版本控制系统构建可部署到任何环境(包括生产环境)的软件包。
测试:任何人都应该能够在他们的工作站上或测试系统中运行任何一个自动化测试套件。
部署:任何人都应该能够将这些软件包部署到具有访问权限的任何环境,通过执行(已提交到版本控制系统中的)脚本来完成部署。
自动化部署必须具备如下能力:
保证在持续集成阶段构建的软件包可以部署到生产环境中;
使生产环境的就绪情况一目了然;
为能在生产环境中部署的任何代码,建立一键式和自助式的发布机制;
自动记录审计和合规管理所需的相关内容,包括在哪台机器上运行了命令,运行了什么命令,是谁授权的,以及结果如何;
通过冒烟测试验证系统正常工作,并且数据库连接字符串等配置正确;
为开发人员快速提供反馈,使他们能够尽快了解部署结果(例如部署是否成功,应用是否能在生产环境中正常运行,等等)。
在开发人员提交代码之前,他们就在自己的工作站上执行了 4500 多个单元测试,而这些测试仅需要不到一分钟的时间。对外部系统(如数据库)的所有调用都已经打桩了。
在代码变更被提交至主干以后,持续集成服务器上会立即执行 7000 多个自动化测试用例。
如果所有测试都按顺序执行,那么“执行 7000 多个测试用例将需要大约半个小时。因此,我们把测试分成了几组,使它们并行地运行在由 10 台服务器组成的Jenkins[持续集成]服务器集群上。这样做让我们实现了 11 分钟的目标”。
“部署”和“发布”这两个词是不同的动作,并且有着截然不同的目标,
部署是指在特定的环境中安装指定版本的软件(例如,将代码部署到集成测试环境中或生产环境中)。
发布是指把一个特性(或者一组特性)提供给所有客户或者一部分客户(例如,向 5%的客户群开放特性)。
按需部署,那么何时向客户发布新特性,就成了业务和市场决策,而不再是技术决策。通常使用的发布模式有以下两种。
基于环境的发布模式:在两个或更多的环境中部署系统,但实际上只有一个环境处理客户流量(例如,通过配置负载均衡器切换流量)。将新的代码部署到非生产环境中,然后再把生产流量切换到这个环境。这种模式非常强大,因为一般只需要对应用做很少的改变,或者几乎不用改变。这种模式包括蓝绿部署、金丝雀发布和集群免疫系统。
基于应用的发布模式:对应用进行修改,从而通过细微的配置变更,选择性地发布或开放应用特性。例如,可以通过特性开关逐渐地开放新特性——先开放给开发团队,再开放给所有内部员工,然后开放给 1%的客户;或者在确认特性完全符合设计后,直接发布给全体客户。这就是所谓的黑启动技术——在生产环境里将所有特性都部署完毕,并在发布前用生产环境的流量做测试。例如,在发布前的几周里,用生产环境的流量来测试新特性,以便在正式发布之前发现和解决所有问题。
蓝绿部署模式
我们有两个生产环境:蓝环境和绿环境。在任一时刻,只有其中的一个环境处理客户流量。
在发布新版本的服务时,先把它部署到非在线环境,以便在不影响用户体验的情况下执行测试。在确信一切都正常以后,再把客户流量切换到蓝环境,用这种方式来交付新版本。之后,蓝环境就变成了生产环境,绿环境则变为预生产环境。通过把客户流量再重定向回绿环境,还可以
实现回滚。
金丝雀发布模式
金丝雀发布这个术语来自于煤矿工人把笼养的金丝雀带入矿井的传统。矿工通过金丝雀来了解矿井中一氧化碳的浓度。如果一氧化碳的浓度过高,金丝雀就会中毒,从而使矿工知道应该立刻撤离。
在金丝雀发布模式下,我们会监控软件在每个环境中的运行情况。一旦出现问题,就回滚;否则就在下一个环境中进行部署。
图 12-6 显示了 Facebook 为了采用这种发布模式而创建的运行环境组。
A1 组:仅向内部员工提供服务的生产环境服务器。
A2 组:仅向一小部分客户提供服务的生产环境服务器,在软件达到某些验收标准后部署(自动化部署或手动部署均可)。
A3 组:其余的生产环境服务器,软件在 A2 组中达到某些验收标准后再部署。
上一小节介绍了基于环境的发布模式。它的特点是,通过使用多个环境并在其间切换流量,实现部署与发布的解耦。
本小节将介绍基于应用的发布模式,通过代码来更灵活、更安全地向客户发布新特性,代码里实现的,所以需要开发团队的参与。
特性开关还具有如下优势。
轻松地回滚:只需更改特性开关的设置,就可以在生产环境中,快速安全地禁用出了问题或造成服务中断的特性。在非频繁部署的情况下,它的价值更大——关掉某一个特性通常要比回滚整个版本容易得多。
缓解性能压力:当服务遭遇极高的负载时,通常需要扩容系统;更糟糕的是,可能导致生产环境中的服务中断。不过,可以使用特性开关来缓解系统的性能压力。换句话说,可以通过减少可用的特性,来支持更多的用户(例如,减少使用某特性的客户数量,禁用推荐等 CPU 密集型特性,等等)。
采用面向服务架构提高恢复能力:即便某个特性依赖于还没有上线的服务,仍然可以将这个特性部署到生产环境中,然后用特性开关把它先隐藏起来。当它所依赖的服务上线后,就可以启用这个特性。同样,当所依赖的服务中断时,也可以关闭该特性,这样做不但可以和下游的故障服务隔离,同时还可以保持应用的其余部分正常运行。
特性开关将代码部署与特性发布解耦。
此外,在发布特性时,可以采用渐进的方式将其开放给一小部分客户。一旦出现任何问题,就中止发布。
通过这种方式,我们再也不用等到大规模的发布以后才能验证客户对产品的满意度。相反,在宣布进行重大发布时,我们已经完成了对业务假设的验证,并且在真实的客户中进行了无数次的改良实验,这些措施有助于提高产品和客户需求的匹配度。
单体架构的本质并不坏。事实上,在产品生命周期的早期阶段,单体架构通常是最佳的选择。
没有一个可以适用于所有产品和规模的完美架构。任何架构都能满足特定的一组目标,或一系列需求和条件
适用于创业公司的单体架构(例如,需要为新特性快速创建原型,或者公司的战略目标可能出现重大改变)完全不同于拥有数百个开发团队的公司所采用的架构,后者的每一个团队都要能够独立地向客户交付价值。
在第三部分中,我们实现了使工作从开发到运维快速流动的架构和技术实践,从而快速、安全地向客户交付价值。
在第三部分中,我们描述了如何建立从开发到运维的快速流动的工作流,以及它所需要的架构及技术实践。在第四部分中,创建从运维到开发的快速且持续的反馈。需要探索和落实如下工作 :
1、建立能定位和解决故障的遥测系统 ;
2、使用监控更好地预测故障,感知业务目标的达成情况 ;
3、将用户研究和反馈融入到研发团队的工作中 ;
4、为开发和运维提供反馈,让他们能安全地部署应用 ;
5、用同行评审和结对编程的反馈方式,提高工作质量。
遥测被广泛定义为“一个自动化的通信过程,先在远程采集点上收集度量数据,然后传输给与之对应的接收端用于监控”。目标是在应用及其环境中建立遥测,包括生产环境、预生产环境和部署流水线。
该体系架构具有以下组成部分。
在业务逻辑、应用程序和环境层收集数据: 在每一层中,建立以事件、日志和指标为对象的监控。将所有日志发送到公共日志服务中,这样更利于集中、轮换和清除。如 Linux 的 syslog、Windows 的事件日志等。所有层次中收集指标,能更好地了解系统的活动状态。
负责存储和转发事件和指标的事件路由器: 此功能支持监控可视化、趋势分析、告警、异常检测等。
除了要对生产服务和环境进行监控采集以外,在发生重大事件时,还必须从部署流水线中采集数据。例如,自动化测试是否通过,以及什么时候在环境中执行了部署。我们还要采集构建执行和测试所消耗的时间长短。这样就可以发现一些预示潜在问题的情况。例如,如果性能测试或构建花费的时间是正常情况的两倍,那么在进入生产环境之前,我们就能发现这个错误并修复它。
我们必须确保对所有正在构建和运维的应用都建立充分的遥测要实现这一点,开发和运维工程师必须把建设生产遥测作为日常工作的一部分,
NASA 每次发射火箭时,都用数百万的自动传感器来报告这一宝贵资产每个组成部分的状态。2014 年,我们每天产生了 10亿多个遥测事件,监控着 10 万多行的代码。
价值流中的所有成员都将以各种方式使用遥测。例如,开发人员可以在应用程序中临时创建更多的遥测,从而更好地在他们的工作站上诊断问题,而运维工程师可以使用遥测来诊断生产问题。此外,信息安全和审计师可以通过遥测确认系统所需控制的有效性,产品经理可以用它们来
跟踪业务成果、功能利用程度和转化率。
日志还要具有不同的级别,其中的一些可能也会触发告警,
调试级别:此级别的信息是相关应用程序中发生的所有事件,最常用于调试的时候。通常,调试日志在生产中是禁用的,但在故障诊断期间要暂时启用。
信息级别:此级别的信息包括用户触发的或系统特定的操作(例如“开始信用卡交易”)。
警告级别:此级别的信息告诉我们可能的出错情况(例如,调用数据库花费的时间超过某个特定时长)。可能会因此而触发报警和故障诊断过程,而其他日志消息会有助于更好地理解事件的原因。
错误级别:此级别的信息侧重于错误状况(例如,API 调用失败,内部出错)。
致命级别:此级别的信息告诉我们何时发生了中断情况(例如,网络守护进程无法绑定网络套接字)。
我们应该保证所有潜在的重大应用事件都生成日志记录条目,编写的以下清单:
认证/授权的结果(包括退出);
系统和数据的访问;
系统和应用程序的变更(特别是特权变更);
数据的变更,例如增加、修改或删除数据;
无效输入(可能的恶意注入、威胁等);
资源(内存、磁盘、中央处理器、带宽或其他任何具有硬/软限制的资源);
健康度和可用性;
启动和关闭;
故障和错误;
断路器跳闸;
延迟;
备份成功/失败。
为了更容易地解释和给出所有日志条目的意义,(理想情况下)我们应该对日志记录进行分级和分类,比如对非功能属性(如性能、安全性)和功能属性(如搜索、排行)。
我们需要在所有环境中,在应用程序栈的每个层级中,以及支持它们的部署流水线中,建立充分而完整的遥测。我们需要以下层级的度量指标。
业务级别:示例包括交易订单的数量、产生的营业额、用户注册数、流失率、A/B 测试的结果等。
应用程序级别:示例包括事务处理时间、用户响应时间、应用程序故障等。
基础架构级别(如数据库、操作系统、网络、存储):示例包括 Web 服务器吞吐量、CPU负载、磁盘使用率等。
客户端软件级别(如客户端浏览器上的 JavaScript、移动应用程序):示例包括应用程序的出错和崩溃、用户端的事务处理时间等。
部署流水线级别:示例包括构建流水线状态(例如:各种自动化测试套件的红色或绿色状态)、变更部署前置时间、部署频率、测试环境上线状态和环境状态。
通过利用遥测完整地覆盖以上领域,
不管服务多么简单或者多么复杂,将业务指标与应用程序和基础架构指标聚合在一起显示,会有助于发现故障。例如,可能会看到新客户注册数下降到日平均值的 20%,然后还立即发现所有数据库查询花费的时间是正常情况的 5 倍,这使我们能够把精力集中在解决问题上。
无论是在应用程序、数据库还是生产环境中,通过使服务的所有组成部分都发送遥测信息以便进行分析,可以早在故障导致灾难之前,甚至是早在客户注意到问题之前,就发现并解决问题。
分析生产环境度量指标最简单的一种统计技术是计算均值(或平均数)和标准差。通过这种方式,我们可以创建一个过滤器,用来检测度量指标与正常值显著不同的情况,甚至配置告警,
假设现在要分析每天未经授权的登录次数,收集到的数据呈高斯分布(即呈正态分布或钟形曲线分布),如图 15-1 所示。钟形曲线中间的垂直线是均值,由其他垂直线表示的第一、第二和第三标准差分别包含 68%、95%和 99.7%的数据。
标准差的常见用途是,定期检查数据集的某个度量,如果与均值有显著差异就告警。例如,当每天未经授权的登录次数比均值大三个标准差时就告警。即使只是一种简单的统计分析,它也是有价值的,因为再也不必设置静态阈值
如果 NGINX Web 服务器停止对请求做出响应,我们会查看主要指标,它们可能已提前警告我们,我们已开始偏离标准操作。例如:
应用级别——网页加载时间正在增加等;
操作系统级别——服务器闲置内存不足、磁盘空间不足等;
数据库级别——数据库事务处理时间超出正常值等;
网络级别——负载均衡器背后运行的服务器数量下降等。
上述所有指标都是生产环境事故的潜在征兆。对于每一个这样的指标,我们将设置告警。
Netflix 利用了这样一个事实:其消费者的浏览模式有着惊人的一致性和可预测性,尽管未呈高斯分布。图 15-4 反映的是在整个工作周内每秒的客户请求数,展示了从周一到周五客户浏览模式是规律和一致的。
Scryer 先使用异常检测组合抛出伪数据点,然后使用诸如快速傅里叶变换(FFT)和线性回归等技术使数据更加平滑,同时保留在其数据中重现的合法流量峰值。结果是Netflix 可以以惊人的准确性预测流量需求。
平滑的统计技术,它对于时间序列数据特别适用,这意味着每个数据点都有一个时间戳(例如,下载事件、已完成的事务处理事件等)。平滑技术通常涉及使用移动平均数(或滚动平均数),它利用每个点与滑动窗口中的所有其他数据的平均值,来转换数据。这样做有助于抑制短期波动,突出长期趋势和周期。
15-6 中展示了这种平滑技术的效果。黑线表示原始数据,而灰线表示 30 天的移动平均数(即 30 天的平均值轨迹)。
可以预期,与用户有关的大量遥测数据将具有周期性/季节性的相似性——网络流量、零售交易、电影观看以及许多其他用户行为,其每日、每周和每年的模式都是惊人地规律和可预测。这让我们可以检测出与历史规律有差异的情况,例如,周二下午的订单交易率降至周平均数的 50%。
我们确保在任何人执行生产部署时,都积极主动地监控生产环境的度量指标,这可以让部署人员(无论是开发人员还是运维人员)在新版本在生产环境中运行以后,快速地确认功能是不是按预期正常运行。
我们在部署过程中积极地监控软件功能相关的度量指标,以确保没有无意中破坏自己的服务,或者更糟糕的是,破坏了另一项服务。
还是存在着检测不到的错误,这就要依靠生产环境的遥测来快速恢复服务了。我们可以使用特性开关关闭出错的功能(这通常是最简单且风险最小的选择,因为它不涉及生产部署),或者前向修复这个错误(即为了修复缺陷而修改代码,然后通过部署流水线将代码变更部署到生产环境),或者回退(例如,使用特性开关切换回之前的旧版本,或通过蓝绿部署、金丝雀发布等模式,将出错的服务器做离线处理)。
让开发人员、开发经理和架构师轮流和运维团队共同值班,这确保了价值流中的所有人能够就他们所做的上游架构和编码决策得到直接的反馈。这样,运维人员不再独自、孤立地解决生产环境中的代码缺陷;
以上实践还帮助开发管理人员意识到,每一项产品功能都标记为“完成”并不代表业务目标已经实现。相反,所有功能都在生产环境中按照设计正常地运行,没有引起重大的故障,也没有引发计划外的运维或开发工作,才是真的“完成”。
开发人员希望跟踪他们的工作对下游的影响——通过亲见客户所面临的困难,他们会在日常开发工作中做出更好和更明智的决策。
在现代用户体验实践中,最常用的 A/B 测试技术是,在一个网站上,给访问者随机地展示一个页面的两个版本之一,即控制组(A)或实验组(B)。基于对这两组用户后续行为的统计分析,可以判断这两者的结果是否存在显著差异,从而找出实验组(例如,功能的变化、设计元素、背景颜色)和结果(例如,转化率、平均订单大小)之间的因果联系。
利用特性开关将软件的多个版本同时交付给多个用户群,可以进行快速、迭代的 A/B 测试。要实现这一点,需要在应用程序栈的各个层次上进行全面的生产环境遥测。
通过勾选特性开关中的选项,可以控制能看到实验组版本的用户比例。例如,可以让一半的客户成为实验组,向其显示“与购物车中失效商品相似商品的链接”。在实验中,我们对比控制组(无选择)和实验组(有选择)的用户行为,可能是衡量在此期间的购买数。
实验的目的是做出明智的决策,确保向数百万的会员推出可用的功能。我们经常在一些功能上投入了大量时间,而且不得不维护,但没有证据表明它们是成功的或者受到了用户的欢迎。A/B 测试让我们可以在开发过程中就判断一项功能是否值得继续投入。
必须确保产品经理将每个功能都视为一个假设,并基于在生产环境中实际的用户实验结果来证明或反驳这些假设。在功能开发中如何通过如下形式构建假设:
我们相信,增大预订页面上酒店图片的大小将会提升客户的参与度和转化率如果在 48 小时内查看酒店图片并预订房间的客户增加了 5%,我们将有信心进行这个改变。
采用实验的方法进行产品开发,不但需要将工作分解成更小的单元(故事或需求),而且还要验证每个工作单元是否能够实现预期的结果。如果没有达到预期,就用替代方案修改工作路线图,并最终实现业务成果。
成功不但需要快速地部署和发布软件,还要在实验方面超越竞争对手。A/B 测试能够安全、轻松地进行用户实验,从而让员工发挥出创造力和创新能力,并进行组织性学习。
代码评审,但它同样适用于对应用程序或环境(包括服务器、网络和数据库)进行的任何变更。同行评审的目标是通过工程师同事的仔细核查来减少变更错误,这种形式的评审不仅提高了变更的质量,还相当于进行了交叉培训,
进行同行评审的合理时机,是将代码向版本控制系统中的主干提交时,但是对于风险更高的领域,例如数据库变更,或者在自动化测试覆盖率不高的情况下对业务的关键组件进行变更,可能就需要领域专家(例如信息安全工程师、数据库工程师)做进一步的审查
变更的批量与整合这个变更的潜在风险之间存在着非线性的关系——从 10 行代码的变更到 100 行代码的变更,发生错误的风险不止高出 10 倍,
随着变更尺寸的增加,我们对代码变更进行有意义的评判的能力也随之下降。请程序员来审查 10 行代码,他会找到 10 个问题。请他审查 500行代码,他会说看起来都不错。
代码评审的指导原则如下。
每个人在将代码提交到主干以前,必须要有同行来评审他们的变更(例如代码、环境等)。
每个人都应该持续关注其他成员的提交活动,以便识别和审查出潜在的冲突。
定义哪些变更属于高风险的变更,从而决定是否需要请领域专家(例如数据库变更、安全性敏感的身份验证模块等)来进行审查。
如果提交的变更尺寸太大了,以至于让人很难理解——换句话说,阅读了几遍代码还无法理解,或者需要提交者进行解释——那么这个变更就需要分解成多个较小的变更来提交,使之一目了然。
代码评审有如下几种形式。
结对编程:程序员结对地在一起工作(见下节)。
“肩并肩”:在一名程序员编写了一段代码后,评审程序员接着就逐行阅读他的代码。
电子邮件送审:在代码被签入到代码管理系统中后,系统就立刻自动向评审者们邮寄一份代码。
工具辅助评审:编码者和审阅者都使用专门用于代码评审的工具(例如,Gerrit、GitHub的 Pull Request 等)或由源代码仓库(例如,GitHub、Mercurial、Subversion,以及 Gerrit、Atlassian Stash 和 Atlassian Crucible 等其他平台)提供的类似功能。
结对模式中,一名工程师扮演驾驶者角色,他是实际上编写代码的人,而另一名工程师则作为导航员、观察者或督导者,他会检查驾驶者正在进行的工作。在检查的过程中,观察者也可以根据工作的战略方向,提出改进的思路以及将来可能遇到的问题。当两人具有不同的特长时,他们可以互相取长补短,
另一种结对编程的模式是通过测试驱动开发进行的,这是指一名工程师编写自动化测试,另外一名工程师编写代码。结对编程的优势是及时性:评审者就坐你旁边时,你是不可能忽略他的。
结对编程还能有益于知识在组织内的传播,以及信息在团队内部的流动。让经验丰富的工程师在经验不足的工程师实现代码时同步地评审,这也是一种有效的教学和学习的方式。