
在现代软件研发中发布速度、发布质量和开发者体验直接影响团队的工程效率。本文以一家海外电商平台公司的软件发布文化为例介绍其如何通过 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》《自动部署实践》