为什么需要 CI

编写软件是一个耗时耗力的过程。如果你开发软件是为了消遣,那么 only 花费你的业余时间。如果你为公司工作(无论是作为承包商还是全职员工),时间就更加宝贵,因为你可以从中获得报酬。事实上,公司都希望降低成本,因此他们不希望在一项功能上花费比必要的更多的钱。

我们日常工作的很大一部分就是修复缺陷。交付无缺陷软件可能是所有开发人员都希望实现的目标。我们不是故意犯错,但错误总是会发生。不过,有一些方法可以减少错误带来的损失。

bug的成本

错误的成本相当高,因为它不会给产品带来任何价值。因此,我们的目标是尽早发现这些错误,越早发现,成本就越低。下面的截图形象地说明了在开发过程中,错误出现得越晚,修复成本就越高:

image 2023 11 12 16 05 47 986
Figure 1. Figure 11.1: Estimated relative costs of fixing a bug based on the time of its detection

但是,随着时间的推移,成本大幅增加的原因是什么?虫子为什么会花钱?

在早期阶段,成本主要来自解决问题所需的时间。举例来说,如果一个错误本可以通过更好的需求来避免,那么所需的工作量就会减少,因为它是在人工测试过程中发现的。如果在生产过程中发现了错误,则需要许多人参与修复:首先,服务台员工需要确认客户报告的错误,并将其转交给质量保证(QA)工程师,由其复制错误并写出正确的错误单。

然后,该单子会分配给产品经理,产品经理在花时间重现和验证缺陷后,会将其列入下一个冲刺计划。该单子最终会分配给一名开发人员,他需要一些时间来重现和修复缺陷。但这并没有结束,因为缺陷修复可能需要另一位开发人员进行代码审查,并由产品经理或质量保证工程师进行双重检查,然后才能最终发布。

一旦 "逃离" 开发人员的本地环境,所有这些开销都会大大增加缺陷的成本。此外,如果缺陷已经进入生产阶段,就会导致客户不再愿意使用产品,因为他们对产品不再满意。这就是所谓的客户流失。

即使你不是在开发商业产品,而是在开发开源项目,这一概念也可以转化为时间或精力。一个错误会导致一份问题报告,你首先需要阅读和理解这份报告,可能还要再问一些问题,并等待勾选作者的回复。如果你的软件错误太多,人们就会减少使用它,你之前的努力可能就会白费。

如何防止bug

幸运的是,我们现在有了一个工具箱,它可以帮助我们在别人之前发现代码中的错误。我们只需要使用它—​这已经是个问题了,因为我们开发人员通常都很懒惰。

当然,你也可以在每次部署前手动运行所有工具。比方说,你想将一些代码部署到生产环境中。在将代码合并到主分支后,应执行以下步骤,以确保不会有错误的代码交付到生产环境中:

  1. 使用 PHP 内核程序确保代码的语法正确性

  2. 执行代码样式检查程序和修复程序,使代码样式保持一致

  3. 使用静态代码分析查找潜在问题

  4. 执行所有自动化测试套件,确保代码仍然有效

  5. 为使用的代码质量指标创建报告

  6. 清理构建文件夹并创建代码归档以进行部署

这是一份需要牢记的清单。没有人会在较长时间内不犯错误,因此,你自然会开始编写脚本,帮助你一次性执行这些步骤。这已经是一个很好的改进,我们还将在本章中进一步利用它。

持续集成简介

在本地环境中运行上一节中的所有步骤将需要一些时间,并且在检查运行期间,你几乎无法执行其它任何操作,因此必须等到它们完成。 那么,为什么不将整个工作流程加载到另一个专用服务器上呢?

这正是 CI 的作用:它描述了将应用程序的所有必要组件组合在一起形成可交付成果的自动化过程,以便可以将其部署到所需的环境中。 在此过程中,自动检查将确保代码的整体质量。 请务必记住,如果其中一项检查失败,则整个构建将被视为失败。

有许多 CI 工具可用,例如 Jenkins,它通常是自托管的(即由你或你的团队或公司中的某人操作)。 或者,你可以选择付费服务,例如 GitHub Actions、GitLab CI、Bitbucket Pipelines 或 CircleCI。

你会经常看到缩写 CI/CD,我们也将在本书中使用它。CD 代表持续交付,我们将在本章末尾介绍这个概念。不过,目前你无需关心它。

设置这些工具之一听起来需要大量工作,但它也有一些很大的好处,例如:

  • 可扩展性:如果你在一个团队中工作,使用本地设置很快就会出现问题。对构建流程的任何更改都需要在每个开发人员的电脑上完成。虽然构建脚本是版本库的一部分,但在部署之前,人们可能会忘记从版本库中提取最新的更改,或者出现其它问题。

  • 速度: 自动测试或静态代码分析是一项相当耗费资源的工作。虽然现在的计算机功能强大,但它们必须完成大量的协同任务,而你并不想在本地系统上额外运行一个构建管道。CI/CD 服务器只做这一项工作,而且速度通常很快。即使速度慢,它们也能分担本地系统的负载。

  • 无阻塞:你需要一个构建环境来运行所有工具并检查你的代码。在构建过程中,使用本地开发环境只会阻塞构建环境,尤其是在使用集成或端到端(E2E)测试等较慢的测试类型时。不建议在本地系统上运行两个环境,一个用于开发,另一个用于 CI/CD,因为你很快就会陷入配置地狱(想想封锁网络服务器端口数据库就知道了)。

  • 监控:使用专用的 CI/CD 服务器可以让你全面了解谁在何时部署了什么。试想一下,如果你的生产系统突然发生故障—​使用 CI/CD 服务器,你可以立即查看最近的更改,只需点击几下就能部署上一版本的应用程序。此外,CI/CD 工具还能让你随时了解最新情况,例如通过电子邮件或你最喜欢的信使应用程序通知你任何构建和部署活动。

  • 处理:手写的部署脚本肯定能完成工作,但要让它像现代 CI/CD 解决方案一样舒适灵活,需要花费大量时间。此外,如果你采用业务标准,团队中的其它开发人员更有可能已经掌握了相关经验。

以上几点希望能让你对使用 CI 的好处有所了解。每个 CI/CD 工具的一个组成部分就是所谓的构建管道,我们将在下一节详细介绍。