在现代软件研发中,发布速度、发布质量和开发者体验,直接影响团队的工程效率。本文以一家海外电商平台公司的软件发布文化为例,介绍其如何通过 CI/CD、合并队列、金丝雀发布和自动化部署,帮助开发者更安全、更高效地将代码发布到生产环境。
我们一直在探索,如何帮助开发者更顺畅地达成目标。我们希望通过工具打造良好的开发体验,让开发者感到高效,并尽最大努力让代码发布成为一种享受,而不是一项苦差事。

海外某电商平台公司工程团队
本次活动录像以及更多问答,现已发布在本文末尾的“虚拟活动:发布文化”部分。
作者:该公司工程团队成员
去年年底,我们在一篇关于“如何成功整合 1000 多名开发者工作成果”的文章中介绍了合并队列 v2。我们经常被问到一个问题:“为什么你们选择自己开发这个工具?”简而言之,是因为我们找不到任何现成方案能够完全满足我们的需求。更具体地说,对我们而言,打造一种符合开发者工作习惯的优化体验至关重要;与此同时,我们也需要不断围绕自身的“发布文化”改进工具和流程。
这家海外电商平台公司对文化的定义是:
“公司所有员工信念与行为的总和。”
我们看待软件发布文化时,也秉持同样的态度。我们的确有一些重要目标,例如确保有问题的变更不会进入生产环境并影响用户;同时,也要确保变更能够在不牺牲安全性的前提下顺利部署到生产环境。但实现这些目标的方式有很多种,对于“应该如何做”,也往往不止一种正确答案。
作为一个团队,我们努力寻找能够帮助开发者实现目标的方式。我们希望通过工具创造良好的体验,让开发者感到高效,并尽最大努力让代码发布成为一种享受,而不是一项苦差事。
如何衡量软件发布文化
当我们谈论“衡量文化”时,关注的是以下几个问题:
开发者希望如何工作?
对他们而言,什么最重要?
他们希望所使用的发布工具如何帮助他们?
他们希望了解多少工具背后的运行机制和底层原理?
这些问题通常没有唯一答案。尤其是在一家大型技术组织中,每天都有大量背景各异的开发者部署代码。我们会通过一些主动和被动的方式,了解围绕代码发布形成的文化。每一种方式都很重要;综合来看,它们能够帮助我们更清楚地理解工具使用者的真实体验。
被动式与主动式衡量方法
我们采用的被动式方法,并不需要团队投入大量额外精力,主要是对收到的信息进行管理和汇总。开发者满意度调查是公司每半年开展一次的开发者调查。调查会请开发者围绕多个问题进行自我反馈,例如他们对所使用工具的满意度,以及他们认为自己的时间主要浪费在了哪些方面。
此外,我们还设有专门用于代码发布的内部沟通频道,任何人都可以加入。用户可以在这里获得来自我们团队或其他用户的支持,也可以报告遇到的问题。我们的团队会积极参与这些频道,营造社区氛围,鼓励开发者分享经验;不过,我们通常不会直接通过这些频道主动征求反馈。
在实际落地中,发布文化并不只依赖某一个工具,而是依赖任务、项目、文档和沟通机制之间的顺畅协同。对于需要统一管理跨团队协作的组织来说,Worktile 这类通用项目协作系统,可以承接任务、项目、文档、即时沟通、目标、日历、甘特图、工时和审批等协作场景,帮助团队把发布过程中的沟通与执行沉淀下来。
话虽如此,我们确实希望主动发现痛点。我们也知道,不能过度依赖用户来告诉我们方向。因此,我们还会主动采取一些措施,确保自己能够聚焦最重要的问题。
首先是内部试用。和公司的其他开发者一样,我们团队每天也会使用自己构建和维护的工具来发布代码。这有助于我们发现服务中的不足,并在问题出现时更好地理解用户的感受。
我们的内部支持团队也是一项宝贵资源。他们负责帮助用户,并支持我们不断演进的内部工具体系。他们会诊断问题,帮助用户找到合适的团队来解答疑问。在识别当前工作流程中的常见痛点,以及发现概念设计和原型中的潜在缺陷方面,他们发挥着不可或缺的作用。我们非常感谢他们。
最后,尤其是在添加新功能或调整现有工作流程时,我们会在整个过程中开展用户体验研究:
一方面,我们希望更好地理解用户行为和用户期望;
另一方面,我们也会在开发过程中测试概念和原型。
我们会全程观察开发者提交 PR 的过程,了解他们在做决策时还会考虑哪些因素。我们会与设计师和文案撰写人员交流;在其他公司,他们也许并不负责提交代码,但在这家公司,他们经常需要这样做。我们会请他们详细介绍自己的工作流程,以及他们如何学习使用常用工具。我们还会邀请实习生和新员工测试原型,以获得新鲜视角,并挑战我们原有的假设。
所有这些做法结合在一起,确保我们在产品开发和发布流程中,能够持续从真实用户那里获得反馈,并不断改进产品。
反馈是一份礼物
在这家公司,我们常说“反馈是一份礼物”。不过,这句话并不能完全消除用户在表达不满时的顾虑,也不会让我们在面对问题时天然变得轻松。
我们开展各种衡量工作的目标,是建立一个良性的反馈循环:一方面,让用户能够安心地谈论哪些地方做得不好,因为他们知道我们重视这些反馈,并会努力改进;另一方面,我们也希望从用户反馈中获得力量和灵感,而不是感到沮丧或挫败。
我们希望用户明白,他们的反馈对我们至关重要,能够帮助我们为所有人打造更友好的发布工具和发布文化。
软件发布流程是什么样的
接下来,让我们看看这家公司实际的软件发布流程是什么样的,以及团队正在如何持续改进它。
正常情况下,发布路径大致如下:从拉取请求(PR)开始,经过持续集成(CI)和合并,再进入金丝雀发布,最终部署到生产环境。
发布流程从 PR 和 /shipit 命令开始。
开发者首先创建 PR,并在准备发布时输入 /shipit 命令。随后,合并队列系统会尝试将该 PR 与主干分支合并。
当合并队列判定该变更可以成功集成时,PR 会被合并到主干分支,并部署到 Canary 基础设施。Canary 环境会随机接收全部传入请求中的 5%。
开发者可以使用相关工具,在 Canary 环境中对变更进行 10 分钟测试。如果没有人工干预,并且自动化的 Canary 分析没有触发任何警报,该变更就会被部署到生产环境。
对于希望借鉴这类实践的研发团队来说,关键不只是建立 CI/CD 流水线,还要让目标、客户反馈、需求清理、评审排期、开发、测试、发布上线和知识沉淀形成完整闭环。PingCode 这类智能化研发管理工具,能够覆盖研发全生命周期管理,并打通研发生态链中的多类工具,让发布流程中的数据更顺畅地流转起来。
信任:让开发者掌控发布流程
开发者希望被信任,也希望对自己的工作拥有自主权。开发者应当能够掌控自己提交的 PR 的整个发布流程。
整个流程由开发者负责。没有发布经理,没有额外审批环节,也没有限定开发者只能在某些特定时间窗口发布代码。
遗憾的是,系统有时确实会出错,但这并没有关系。我们已经构建了完善的基础设施,用来限制错误变更的影响范围。更重要的是,我们相信每位开发者都会对自己的变更负责,并在变更导致问题时承担恢复责任。
一旦修复方案准备就绪,无论是向前修复还是回滚,开发者只需使用一条 /shipit --emergency 命令,就能快速将修复方案优先推送出去。为了帮助开发者快速决策,我们没有设置多种恢复协议,而是只提供一个紧急恢复功能,由它选择最快的恢复路径。
速度:让代码更快进入生产环境
开发者希望尽快发布代码。
对这类大型互联网应用而言,发布速度至关重要。开发者每天会多次发布代码,并让代码迅速触达最终用户,这极大提升了他们的工作效率。更重要的是,快速发布流程也让团队能够快速恢复。
为了真正加快发布流程,我们愿意在成本上做出权衡。除了拥有专门的基础设施团队外,我们还管理着自己的持续集成集群;在高峰期,该集群每天会运行数千个节点。
自动化部署:减少重复劳动
开发者不希望执行重复性任务。在某些方面,计算机仍然比人类更擅长,因此我们会尽可能实现自动化。我们在持续部署和金丝雀测试等环节都使用了自动化技术。
例如,开发者不需要手动点击“部署”按钮。我们会自动、持续地将版本部署到 Canary 和生产环境。
不过,对开发者而言,能够手动控制部署机制仍然非常重要。例如,在紧急情况下,开发者可以锁定自动部署,并改为手动部署。
虚拟活动:发布文化
2020 年 4 月 20 日,团队举办了一场关于发布文化的线上问答活动,嘉宾包括该公司工程团队的几位成员。活动讨论了公司的代码发布文化,并回答了大家提出的问题。由于活动期间未能回答所有问题,我们已将未解答问题的答案整理如下。
自动化和高速发布如何保障正常运行时间?
自动化有助于在变更发布过程中维持一定的质量水平。例如,自动化的 Canary 分析可以确保变更达到进入生产环境所需的质量标准,从而帮助维持正常运行时间。
快速发布同样有助于保障正常运行时间,因为它可以缩短故障持续时间。当故障发生时,快速发布意味着问题能够更快得到修复。
高并发合并场景下,如何识别有问题的变更?
对于一个全天有大量合并操作的活跃单体应用来说,Canary 环境的部署频率必须非常高。如果 Canary 环境最近合并了很多变更,你们如何识别“有问题”的合并?又如何确保当 Canary 环境中存在“有问题”的合并时,不会阻碍其他合并继续发布?
我们目前的流程还不够成熟,因此故障分类仍然需要人工参与。高效的开发速度有助于控制变更列表的规模,从而简化故障分类流程。
至于如何降低错误变更带来的影响,请参阅我们关于合并队列的文章。合并队列可以帮助我们避免在出现错误变更时整个发布流程完全停滞。
如何应对工具蔓延,并平衡赋能与控制?
作为一个工具开发组织,你们如何应对工具蔓延?如何平衡赋能与控制?也就是说,团队是否可以选择自己的语言和框架,还是只能使用一套既定工具和框架?
总体而言,我们在技术选择上相对谨慎。这主要是因为我们希望有策略地选择所使用的技术。因此,我们乐于尝试新技术,但公司也会推荐一些经过实践检验的技术作为默认选项,例如 Ruby 和 Rails。
一篇关于“React Native 成为移动端未来方向”的文章很有意思,介绍了团队最近进行的一项技术变革。
在引入 Shipit 之前,你们如何管理发布流程?
在引入 Shipit 之前,你们使用的是什么软件?迁移过程是怎样的?你们如何衡量迁移是否成功?
一篇关于“如何成功整合 1000 多名开发者工作成果”的文章,讲述了团队如何构建当前 /shipit 系统的故事。
我们用两类指标来衡量这一过程是否成功:一类是开发者满意度调查中的反馈,另一类是平均 PR 上线时间等量化指标。
CI/CD 流水线由哪些工具组成?
CI/CD 流水线由多少种不同工具组成?这些工具都是公司内部开发的吗?它们由某个特定团队负责,还是所有人都可以参与开发?
我们与许多外部服务商合作。其中两个重要合作方分别用于持续集成调度和代码托管;后者也是我们构建开发工作流的基础。你也可以在一些海外技术栈信息平台上找到更多关于我们工具栈的信息。
我们自行构建的工具由开发者加速团队负责开发和维护,但任何人都可以自由贡献代码,我们也一直收到大量内部贡献。我们的持续交付系统 Shipit 实际上也是开源的,我们也经常看到社区成员为它贡献代码。
功能发布后,如何监控生产环境性能?
通常情况下,各团队会自行制定监控策略并负责监控。性能对我们至关重要,因此我们设有内部仪表盘,帮助团队跟踪各自的性能指标。我们相信每个团队都会认真对待产品中的这一部分。
除此之外,生产工程团队也设有监控器和仪表盘,用于观察整个系统层面的性能指标。
如何进入开发者工具领域?
你是如何开始为公司内部团队开发开发者工具的?对于对此感兴趣的人,你会推荐学习哪些语言或系统?
注:我把这个问题理解为一个关于职业发展的问题。
就我个人而言,我一直更偏爱软件开发中偏“元”的部分,也就是关注如何提升长期生产力,以及如何让过去的项目在未来仍然易于维护。因此,全职从事开发者工具工作,对我来说几乎是水到渠成的选择。
我认为,要在这个领域取得成功,关键能力是适应力:既要适应新技术,也要适应新理念。Ruby 和 Python 这类语言能让你更专注于代码背后的思想,在这方面很有帮助。与此同时,Docker 和 Kubernetes 相关知识在这个领域也同样宝贵。
特性分支和特性开关如何配合使用?
开发是在特性分支上完成,并将整个功能一次性合并到主干分支?还是会将部分完成或尚未完成的功能合并到主干分支,但通过特性开关加以保护?
这是个好问题。我认为不同团队、不同功能的做法会略有差异,但通常情况下,我们会通过特性开关来发布新功能。在我们的系统中,这类开关被称为“Beta 开关”。借助它们,我们可以按店铺,或按一定比例的店铺,逐步推出变更。
你们使用 Crystalball 吗?
我们把这个问题转给了测试基础设施团队。他们的回复是:我们没有使用 Crystalball。团队曾进行过一些简单探索,但它的速度还不足以跟上我们的代码库规模;此外,我们主单体应用中的测试套件是用 minitest 编写的。
附加信息
Shipit-engine —— 某海外公司开源的 Shipit 项目
《成功整合 1000 多名开发者的工作成果》
《大规模发布拉取请求:如何借助合并队列完成代码发布》—— 某海外开发者大会演讲
《如何在没有专门质量保证团队的情况下将研发规模扩展到数千人》—— 某海外技术大会演讲
《合并队列简介》
《隆重推出 Shipit》
《自动部署实践》
文章包含AI辅助创作:软件发布文化:CI/CD、合并队列与金丝雀发布实践,发布者:shang,转载请注明出处:https://worktile.com/kb/p/3979179
微信扫一扫
支付宝扫一扫