原文:
annas-archive.org/md5/49af0d85218130f9ed5d5bc40351ac10
译者:飞龙
第四章:提升团队协作与 GitHub
恭喜你来到了我们旅程中的这一关键章节!现在,是时候更深入地探索了,超越将 GitHub 仅视为一个代码托管平台的认知。GitHub 是全球开发者构建软件的地方,是开发流程简化的舞台,也是 DevOps 精神真正展现的地方。
在本章中,我们将深入探讨使用 GitHub 的实践内容,将理论转化为实际操作。我们的重点将是 GitHub 中对于团队合作和 DevOps 环境中协作至关重要的具体功能。每一节都旨在提升你的理解和技能,确保从传统系统到现代 DevOps 实践的平稳过渡。让我们与 GitHub 一起,踏上这段激动人心的旅程,GitHub 是 DevOps 工具包中的强大助手。
本章将涵盖以下主要内容:
-
开始使用 GitHub
-
问题 – GitHub 上的协作卓越
-
拉取请求卓越
-
最大化利用 GitHub
-
GitHub 仓库卓越
我们将从与 GitHub 的实践体验开始。如果你想再次了解 GitHub 的全貌,请回到 第一章 了解 GitHub 的全部内容。
技术要求
继续本节所需的配置说明可以在以下仓库中找到。请确保已安装 Git 和 SSH 工具。对于 Windows 用户,推荐使用 PowerShell。我还建议你查阅最新的有关不同命令和环境的信息:github.com/PacktPublishing/DevOps-Unleashed-with-Git-and-GitHub
。
开始使用 GitHub
现在,让我们开始 GitHub 的体验。本节将介绍如何开始使用 GitHub,并涵盖基本的仓库操作。你还将学习如何使用 Git 与远程仓库进行交互,在这里完成 Git 基础的学习之旅!
设置你的 GitHub 账户
开始你的 GitHub 之旅首先需要一个简单的注册过程。访问 GitHub 网站,提供用户名、电子邮件和密码进行注册。这一步是进入协作开发世界的入口,GitHub 提供了一套完整的功能来支持团队协作和项目管理。
注册后,你就可以创建仓库并将代码推送到 GitHub,标志着你的 Git 之旅的开始。GitHub 不仅是一个工具,它还是你 DevOps 旅程的基石,促进协作并推动创新。如果你已经有 GitHub 账户,随时可以跳过注册,进入下一步。
要注册,请访问 github.com/
并点击页面右上角的 注册 按钮:
图 4.1 – github.com 首页
输入你的电子邮件,选择一个密码,并选择一个独特的用户名。在完成几个确认步骤后,你将收到一封确认邮件。使用提供的验证码来完成你的 GitHub 账户设置:
图 4.2 – GitHub.com 上的注册页面和确认邮件
通过这些步骤,你的 GitHub 账户就准备好了,为创建你的第一个仓库铺平了道路。账户创建过程是当前撰写时的情况,未来可能会有所变化,但基本上这是一个直接的方式。
创建你的第一个 GitHub 仓库
当登录 GitHub 时,创建新仓库的菜单会根据你的用户身份有所不同。新用户会直接看到创建仓库按钮,而现有用户则会在他们的仓库列表中找到新建按钮:
图 4.3 – 创建新仓库的按钮
然后,仓库创建设置页面会出现:
图 4.4 – 创建新仓库
创建仓库涉及几个关键决策:
-
仓库位置:选择仓库的所有者(你自己或你所在的组织),并为选定所有者下的仓库提供一个独特的名称。
-
可见性:决定你的仓库是公开的还是私有的。使用公开仓库,任何人都可以访问你的项目,使其成为开源社区的一部分,鼓励共享和协作。另一方面,私有仓库是为了那些你希望保密或者只与你选择的人分享的工作,提供了保密性。对于在 GitHub 企业计划下的组织成员,还有一个选项可以创建内部仓库。这些仓库非常适合内部源(InnerSource)项目,因为它们仅在你的企业内部可见,提供了一个安全的协作环境。
-
README.md
、.gitignore
或LICENSE
文件。包含一个README.md
文件就像是为你的项目提供了一个欢迎指南;这是你解释项目内容、使用方法以及访客需要知道的其他重要信息的地方。如果你的项目是开源的,选择合适的许可证尤其重要。它为他人如何使用和贡献你的项目设定了规则。选择这些选项意味着 GitHub 会自动运行git init
命令。如果你打算将现有项目推送到 GitHub,可以不勾选这些选项。
对于这个例子,保持所有初始化复选框未选中,结果是一个空的仓库。这为接下来的步骤上传本地文件做好了准备。填写完仓库详细信息后,点击创建仓库按钮。此操作将使你的新仓库正式创建:
图 4.5 – 创建的仓库
恭喜你创建了新的 GitHub 仓库!这一里程碑标志着你作为开发者旅程中的一个重要步骤,为你在 DevOps 和开源开发的世界中打开了合作、创新和成长的大门。
接下来,我们将把你的本地仓库连接到远程仓库,其中包括为 GitHub 生成并注册 SSH 密钥。设置 SSH 连接的细节将在接下来的部分中非常重要,因此务必注意 SSH URL 字符串。点击SSH按钮,然后点击复制图标,将值复制如下截图所示:
图 4.6 – SSH 连接信息
注册你的 SSH 密钥
现在,是时候设置你的 SSH 密钥了。为此,请通过点击 GitHub 右上角的菜单按钮进入设置:
图 4.7 – 打开菜单
从这个菜单栏,你可以访问 GitHub 账户的各个部分。你可以查看个人资料并管理自己参与的仓库和组织。现在,选择菜单栏中的设置:
图 4.8 – 在菜单栏点击设置打开设置
一旦进入设置部分,在左侧菜单中查找SSH 和 GPG 密钥。在这里,你可以创建一个新的 SSH 密钥:
图 4.9 – 设置
点击新建 SSH 密钥按钮后,系统会提示你输入标题并选择密钥类型。此时,你应该选择认证密钥。然后,可以将 SSH 密钥粘贴到指定的字段中。
现在,你需要你的 SSH 密钥进行注册!接下来我们来看看如何生成一个。如果你已经注册过,可以跳过此过程。
创建你的 SSH 密钥是设置一个安全环境以管理 GitHub 上代码的重要步骤。如果你对 SSH 密钥不熟悉或尚未拥有密钥,这一步尤其重要。你可以通过在终端中进入 ~/.ssh
目录来检查是否已有密钥。该目录通常包含你的 SSH 配置文件和密钥。如果你没有现有的密钥或想为 GitHub 专门创建一个新密钥,那么我们来创建一个吧。
首先,打开你的终端并进入 SSH 目录:
$ cd ~/.ssh
然后,使用 ssh-keygen
命令生成新的 SSH 密钥。你将使用 RSA 密钥来实现这一目的:
$ ssh-keygen -t rsa
该命令启动了密钥生成过程,并显示如下信息:
Generating public/private rsa key pair.
在提示时,输入保存密钥的文件名。以下是一个示例:
Enter file in which to save the key
(/Users/username/.ssh/id_rsa): [Your Key Name]
在这种情况下,我将使用 git_key
作为密钥名称。
接下来,系统会要求你输入一个密码短语。这为你的密钥增加了一层额外的安全保护:
Enter passphrase (empty for no passphrase): [Your Passphrase]
Enter same passphrase again: [Repeat Your Passphrase]
为了进一步增强安全性,为你的 SSH 密钥添加一个密码短语是一个关键步骤。如果有人未经授权获得了你的私钥,他们仍然无法使用它,因为没有密码短语。这可以防止未经授权的使用。每次使用密钥时,你都需要输入密码短语,确保只有同时拥有密钥和密码短语的人才能访问。这个额外的安全层使你的 SSH 连接更安全,建议用于保护重要信息和访问权限。完成这些步骤后,你将看到一个确认消息,表明你的身份(私钥)和公钥已保存。同时,还会显示一个唯一的密钥指纹和一张随机艺术图像。
现在,你需要检查并复制你的新公有 SSH 密钥:
$ cat ~/.ssh/git_rsa.pub
复制显示的 SSH 密钥(以 ssh-rsa
开头):
ssh-rsa AAAAB3NzaC1yc2EAA...x4CWuT2U= a1b2@c3d4.e5f6
现在,让我们将这个 SSH 密钥添加到你的 GitHub 账户中。再次进入 GitHub 设置,找到SSH 和 GPG 密钥部分,然后将你的密钥粘贴并保存到那里:
图 4.10 – 添加一个新的 SSH 密钥
要设置或修改你的 SSH 连接,首先使用 touch
命令创建一个配置文件,该命令会创建一个新文件或更新现有文件的最后修改时间:
$ touch config
现在,配置文件准备好了,是时候为 GitHub SSH 连接添加特定的配置了。确保将 git_rsa
替换为你的私有 SSH 密钥文件的名称。如果你按照之前的步骤创建了新密钥,那么你的文件名应该是 git_rsa
。如果你是一个有经验的 Git 用户,可能已经在这个文件中有一些 SSH 配置。在这种情况下,你应该更新或替换现有的配置。对于那些添加新配置的人,输入以下行:
Host github github.com
HostName github.com
IdentityFile ~/.ssh/git_rsa # Your key file name
User git
输入这些行后,保存并关闭文件。此配置指示你的系统在 GitHub 上使用哪个 SSH 密钥,并且是在哪个用户账户下。
还建议验证你与 GitHub 的 SSH 连接,以确保一切设置正确。你可以通过运行 ssh -T git@github.com
命令来完成此操作。此步骤有助于确认你的系统能够成功地使用配置中指定的 SSH 密钥与 GitHub 通信。
git remote – 连接本地和远程仓库
现在,是时候将本地开发与 GitHub 世界连接起来了。如果你正在继续之前章节中的现有仓库,只需使用 cd
命令进入该目录,如下所示:
$ cd path/to/your/repository
对于那些想要创建新项目的人,设置一个新的仓库是非常简单的。首先创建一个新目录,初始化一个 Git 仓库,并准备一个 README
文件——这是任何新项目的标志:
$ mkdir new-project
$ cd new-project
$ git init
$ echo "# README" >> README.md
$ git add README.md
$ git commit -m "Initial commit"
记得使用你在上一节中记录的 SSH URL。
将本地仓库连接到 GitHub 上的远程仓库涉及添加一个远程 URL。这种链接允许您将本地更改推送到 GitHub。使用 git remote add
命令建立这种连接,确保用您的 GitHub 用户名和仓库名替换 [Username]
和 [Repository]
:
$ git remote add origin git@github.com:[Username]/[Repository].git
对于已经有远程 URL 但需要更新的情况,git remote set-url
是首选命令。该命令将您的 Git 配置更新为新的远程仓库 URL:
$ git remote set-url origin git@github.com:[Username]/[Repository].git
通过这些步骤,您已成功将本地和远程仓库连接起来。这一连接是管理项目的关键点,确保您的本地开发在 GitHub 上得到进一步的推进和协作。
随着我们的进展,下一步将是将您的本地代码推送到 GitHub 上。
git push – 让您的代码生效
最后,是时候将您的本地提交推送到远程仓库了。此步骤将会用您本地所做的更改更新远程仓库:
$ git push -u origin main
当您使用 -u
或 --set-upstream
与 git push
一起使用时,实际上是在将当前分支在本地仓库的上游设置为 main
远程。
通过指定此上游,您简化了与远程仓库的未来交互。一旦设置了上游,您可以直接使用 git push
推送到远程仓库的同一个分支,而无需额外的参数。这意味着随后的 git push
操作将自动知道将提交推送到 main
远程。
在 GitHub 上检查代码
现在,让我们来查看在 GitHub 上推送的代码是什么样子的。
当您访问 GitHub 仓库时,迎接您的第一件事是仓库的主页面。在这里,您可以看到最近更新的文件、README.md
文件(如果有)、以及各种仓库细节。此视图提供了项目内容和目的的快速快照:
图 4.11 – 推送代码后的 GitHub 仓库页面
在 GitHub 上编辑代码
其中一个关键功能是直接与 GitHub 上的代码进行交互。您可以使用界面本身添加或编辑文件,这对于小改动或快速修复特别有用。以下是如何操作:
- 单击右上角的 编辑 按钮进入编辑模式:
图 4.12 – 进入编辑模式
- 接下来,您可以编辑并提交您的更改。请记住,这将直接提交到您的代码库:
图 4.13 – GitHub 上的编辑页面
- 这个操作会将你的更改直接提交到远程仓库。不过,你也可以灵活选择目标和分支来进行提交。虽然默认情况下通常是你当前工作的分支,但你也可以选择同时创建一个新分支,这在开始新的贡献时特别有用。如果你选择创建一个新分支,之后仍然可以将其合并到主分支:
图 4.14 – 提交更改
- 现在,你应该能看到更改的反映,如下图所示:
图 4.15 – 提交后,你将返回到代码界面
在 GitHub 上审查代码和更改
现在,让我们通过刚刚更新的代码查看 GitHub。为了更仔细地查看代码,GitHub 提供了几种文件浏览器视图:
- 预览模式是默认模式,适用于某些文件类型(如 Markdown),并显示文件在格式化状态下的样子:
图 4.16 – 预览模式
- 代码模式显示文件的最新提交内容,并具有美观的语法高亮:
图 4.17 – 代码模式
- blame模式特别有用,因为它按行显示文件,展示了每一行最后由谁修改,并在何次提交中进行修改。这对于理解代码的演变和跟踪更改非常有价值:
图 4.18 – blame 模式
当你不想在本地获取代码并进行审查,而是希望先查看 GitHub 上的代码时,这非常有用。在 GitHub 仓库界面中,你可以探索项目的提交历史。这个功能允许你深入了解每个提交的具体内容,查看所做的更改:
图 4.19 – GitHub 上的更改历史
此外,GitHub 提供了对这些更改进行评论的功能,使你能够参与讨论或对仓库中的具体修改提供反馈:
图 4.20 – GitHub 提交视图
在 GitHub 上管理分支
在 GitHub 上管理分支非常简单。以下是 GitHub 上分支管理的简要概述。
首先,在仓库中不同分支之间切换是一个常见任务。在 GitHub 上,你可以通过分支下拉菜单轻松切换分支,通常该菜单位于仓库页面的顶部。这个功能允许你快速从一个分支切换到另一个分支,帮助你高效地审查项目的不同版本或阶段:
图 4.21 – 在 GitHub 上切换分支
对于拥有多个分支的仓库,查找特定分支可能会变得有些困难。GitHub 提供了一个分支下拉菜单中的搜索功能。这个功能允许你快速筛选并找到你需要的分支,节省时间并提高工作效率:
图 4.22 – 在 GitHub 上搜索分支
启动一个新的开发线通常是通过创建一个新的分支来完成的。GitHub 简化了创建新分支的过程。你可以命名新分支,并基于现有分支,这使得为新特性或实验创建分支变得非常简单:
图 4.23 – 在 GitHub 上创建分支
GitHub 的分支管理功能提供了一种无缝高效的方式来处理一个仓库内的多个开发线。无论你是切换查看不同的项目状态,搜索特定的分支,还是为开发创建一个新的分支,GitHub 界面都使这些任务直观且易于访问。这种精简的分支管理方法对于保持一个有序且高效的开发环境至关重要。
git pull – 桥接本地和远程工作
现在我们已经看过 GitHub 如何管理我们推送的代码,让我们回到命令行。获取本地仓库中新做的更改。我们已经在 GitHub 上编辑了 README.md
,但我们如何将这些更改带到本地环境呢?答案就在 git pull
命令中。
在使用 Git 进行版本控制的过程中,保持与远程仓库最新更改的同步对无缝的协作和开发至关重要。git pull
命令就是专门为这个目的设计的工具。它充当了一个桥梁,将远程平台(如 GitHub)上做出的更改带入到你本地的工作目录中。
使用 git pull
在协作环境中尤为重要。假设你的团队成员正在向 GitHub 上的共享仓库提交更改,在这种情况下,定期拉取这些更改可以确保每个人的工作保持一致,减少冲突或不一致的可能性。
要使用 git pull
,请在命令行或终端中进入你的仓库目录,并输入以下命令:
$ git pull origin main
这是一个简单却强大的命令,能够保持你合作工作的和谐,并确保你的本地仓库与最新的开发保持同步。
当你执行 git pull
时,实际上发生的是一个两步的过程。首先,Git 从远程仓库获取更新——这包括自上次更新以来所有的提交和分支。然后,它将这些更新合并到你的本地仓库中。这次合并至关重要,因为它将远程更改与本地工作整合,确保你的本地仓库与远程仓库保持同步。
乍看之下,git pull
命令似乎很简单,但实际上它是两个基本 Git 命令的结合:git fetch
和 git merge
。这种双重性质使得 git pull
成为 Git 工具中的一把利器。
接下来,让我们深入了解这个过程的第一个组成部分:git fetch
。这个命令是版本控制谜题中的一个关键部分,允许你查看别人正在做的工作,但还没有将这些变更集成到你的工作中。
git fetch – 同步而不中断
git fetch
在开发者与远程仓库互动中起到至关重要的作用。git fetch
的核心功能是安全且高效地将远程仓库的变更更新到你的本地仓库。
当你运行 git fetch
时,Git 会联系指定的远程仓库并下载你尚未拥有的所有数据。这包括新的提交、分支和标签。git fetch
的美妙之处在于,它在不更改你的工作文件的情况下执行这些操作。就像偷偷看别人在做什么,而不会实际将他们的变更集成到你的工作中。这个特性使它成为一个非破坏性操作,确保你当前的开发工作不受影响。
拉取的数据被存储在你的本地仓库中,但与你的实际项目文件分开。要将这些拉取的变更合并到你的工作中,你通常会跟进一个 git merge
命令,将拉取的分支合并到你当前的分支中。没错——git fetch
在与 git merge
结合使用时可以显示其真正的威力。
fetch 与 pull
让我们稍微深入了解一下 git pull
。当你运行 git pull
时,它首先启动一个 git fetch
操作。这一部分的过程会访问远程仓库并拉取所有新数据。这些数据包括自上次拉取以来在远程仓库中更新的提交、文件和引用。这是一个至关重要的步骤,确保你获取到远程仓库的所有最新信息,但它并不会自动将这些变更集成到你的工作文件中。
git pull
命令的第二部分是 git merge
的作用。在拉取更新后,git merge
将这些新下载的引用并入你的本地仓库。这个合并过程实际上是将远程仓库的变更更新到你当前的工作文件中。这是一种无缝集成远程变更与本地工作的方法,保持你的仓库与远程仓库完美同步。
理解 git pull
作为获取和合并操作的双重性质,揭示了它在协作环境中管理和同步代码变更的真正威力。
此外,了解git fetch
与git pull
之间的区别非常重要。它可以让你更准确地控制何时以及如何将远程仓库的更改合并到本地工作中。清晰地理解这点对于顺利的协作和高效的仓库管理至关重要,因为它让你可以战略性地决定是仅仅查看更改还是完全合并它们。
git clone – 将 GitHub 仓库带到你的工作空间
说到克隆和下载,这些选项对于每个仓库都是随时可用的。只需进入仓库页面并点击**< > 代码**按钮,你就能看到这些选项。克隆会在你的机器上创建一个仓库的本地副本,让你能够离线工作,而下载则提供一个项目的 ZIP 文件,便于备份或审查:
图 4.24 – git clone
执行git clone
命令是一个简单的过程,使任何想要参与 GitHub 上托管项目的人都能轻松接触到。要克隆一个仓库,你只需要该仓库的 URL:
$ git clone [Your SSH URL]
git clone
是一个核心命令,使你能够创建现有仓库的精确本地副本。这个过程不仅仅是复制当前文件;它完整地复制了整个仓库,包括所有文件版本、完整的提交历史和所有分支。通过使用git clone
,你可以将项目的完整、功能齐全的版本带到本地机器上。这不仅让你可以离线工作,还为你提供了项目开发历史的全面视图,有助于理解和高效贡献。
Fork – 不只是复制代码
除了git clone
,还有另一种方法可以在 GitHub 上复制一个仓库。这对于开源开发尤其有用。在 Git 中,特别是在像 GitHub 这样的平台注册上,**Fork(分叉)**的概念是协作和开源开发的基石。Fork 一个仓库意味着在你的账户下创建一个属于你自己的副本:
图 4.25 – 在 GitHub 上 Fork
当你 Fork 一个仓库时,你就在自己的 GitHub 账户内创建了一个属于你个人的副本。虽然这个副本最初与原仓库是镜像关系,但它独立运行,这意味着你可以进行修改、添加或实验,而不会影响原项目。然而,理解这种独立性是有局限的也很重要。例如,如果原始仓库被删除或其可见性发生变化,可能会影响分叉的状态。尽管存在这些依赖关系,Fork 仍然是开源开发中的重要实践,它使开发者可以通过拉取请求进行贡献,而无需对源仓库拥有直接的写入权限。
Fork 操作在开源世界中尤其重要。它允许开发者通过在自己的分支上进行更改,然后通过称为pull request的过程向原始项目提出这些更改,从而为项目作出贡献。这就是你如何在没有直接写入源代码库权限的情况下,参与到项目中的方式。当你进行 Fork 时,会在你的新环境中创建一个副本,如下图所示:
图 4.26 – 被 Fork 的代码库在你的环境中进行管理
Fork 提供了一个独特的平台,任何人,无论与原始项目维护者的关系如何,或是信任程度如何,都可以自由地进行实验并做出贡献。这种方式显著降低了协作编码的门槛。
通过 Fork 一个代码库,你可以创建一个环境,在其中为项目添加你的创意、增强功能或修复问题,而不影响上游的代码库。这对那些尚未获得项目维护者信任、无法直接访问主代码库的新贡献者尤为重要。它让他们能够在一个独立的个人空间中展示他们的能力和贡献。
Fork 操作的这种独立而又相互关联的特性至关重要。它使得贡献过程分为两个阶段:首先,在你自己的 Fork 中自由地进行实验和更改;其次,通过 pull request,提出将这些更改合并到原始项目中。这种工作流促进了开放协作的文化,大家可以自由地交流创意和贡献,最好的创意将无缝地融入项目中。
本质上,Fork 不仅仅是复制一个代码库;它更是参与到一个更大社区中的方式。无论你是在为一个现有项目做贡献,基于他人的工作启动一个新项目,还是仅仅进行实验,Fork 都是使用 Git 和 GitHub 时不可或缺的一部分。
你已经学会了 Git 命令的基础知识,并且了解了如何在单独的和协作的远程环境中管理 Git 代码库。现在,你准备好深入了解 GitHub Issues 和 pull requests,它们是 GitHub 上沟通的关键工具。这些功能虽然看起来很简单,但在开源项目的成功中起着至关重要的作用。它们作为创意生成、讨论和审查的平台,促进了共同开发和创新的文化。接下来,我们将探讨这些功能如何增强沟通与协作,助力 GitHub 上的开发。
Issues – GitHub 上的协作卓越
GitHub Issues 作为 GitHub 生态系统中的一个多面工具,必不可少于协调协作努力。它不仅仅是一个报告问题的地方,它是一个全面的系统,用于跟踪与项目相关的各种任务和活动。这包括管理 bugs、提出改进建议以及监控 GitHub 仓库中的其他重要任务。
在 DevOps 环境中,GitHub Issues 扮演着至关重要的角色,它通过促进持续反馈和无缝协作,提供了一个透明且高效的平台,开发人员可以标记问题,团队成员可以建议新功能,而利益相关者可以参与关于潜在改进的有意义讨论。这一功能与 DevOps 的核心原则高度契合,DevOps 强调打破组织障碍,促进开放沟通,并培养持续改进和适应的文化。
通过利用 GitHub Issues,团队可以创建一个共享、可访问的空间,促进协作,并确保项目中的每个成员都在同一页上。这不仅仅是为了追踪问题;它是关于构建一个动态、响应迅速的环境,在这个环境中,创意能够蓬勃发展并被高效管理。
GitHub Issues 的独特之处
现在让我们来看一下 GitHub Issues 在促进透明度和提升开发者体验中的独特作用。
GitHub Issues 是软件开发领域中的一个独特工具,尤其是在透明度和协作方面。作为 GitHub 的一部分,它重新定义了开发团队,甚至更广泛的开源社区,在项目中沟通和协作的方式。GitHub Issues 的重要性不仅体现在它作为 bug 跟踪或功能请求工具的功能上,还体现在它在培养一种开放、透明且由社区驱动的软件开发方法中的作用,这种方法与开源运动的精神相呼应。
开发中的透明度重要性
软件开发中的透明度是指让所有相关方(从开发人员到最终用户)能够看到并理解软件的创建、修改和维护过程。这种透明度至关重要,原因如下:
-
改进的协作:当项目的各个方面都对所有人可见时,团队成员能够更有效地协作。每个人都能访问相同的信息,从而做出更好的决策,并对目标和挑战有共同的理解。
-
提高的责任感:透明度使责任分配更加清晰。当团队成员的工作对他人可见时,他们会对自己的工作更加负责,从而培养出更强的责任感和承诺感。
-
增强的质量与创新:项目数据的开放访问使得更多的人可以参与项目的审视,从而带来更多的反馈、创意和批评。这种集体的审查不仅提升了质量,还激发了创新。
-
建立信任:透明性建立了团队成员之间以及与外部利益相关者(包括用户和客户)之间的信任。信任对长期项目的成功至关重要,并且对建立可靠的、以用户为中心的软件至关重要。
GitHub Issues – 透明协作的催化剂
GitHub Issues 体现了这种透明的做法。与允许详细分层权限的工具不同,GitHub Issues 通常采用更为开放的访问模型。每个问题、其讨论线程以及作出的决策对所有团队成员可见,并且在开源项目中通常对公众开放。这种开放性避免了信息孤岛,鼓励了一种自下而上的文化,任何级别的组织或社区成员都可以提出想法和反馈。
这种方法与开源开发的精神完全契合,开源开发强调社区贡献、共同责任和开放对话。通过在内部采用类似的模型,公司可以获得开源方法的好处,打破组织壁垒,培养团队内部的社区氛围。这鼓励开发者主动提出想法,参与健康的、建设性的辩论。
开源作为内部协作的模型
开源的工作方式,借助像 GitHub Issues 这样的工具,成为提升开发者体验的绝佳策略。它将开源社区的协作性、透明性带入组织的内部运作。当开发者能够看到自己工作的影响,并参与到超出自己直接任务的讨论时,他们会感到更加投入和被重视。这种开放的环境培养了社区感,提升了士气,并能显著提升创新力和生产力。
此外,GitHub Issues 和开源模型所促进的透明性和开放性提供了宝贵的学习机会。开发者可以相互学习,从不同的角度获得见解,并通过接触各种挑战和解决方案来成长。这种环境有利于个人和职业发展,对于留住人才、保持团队的动力和生产力至关重要。
GitHub Issues 是协作的催化剂
总之,GitHub Issues 在促进透明度和社区驱动的软件开发方法中发挥着关键作用。从开源实践中汲取灵感有助于打破组织障碍,培养协作和透明的工作文化,并显著提升开发者体验。在软件开发日益注重社区和协作的时代,GitHub Issues 成为了一个灯塔,引导团队走向一种更加开放、包容和高效的工作方式。
让我们从这个角度来看一下一个问题。
制作一个问题 – 构建一个结构良好的问题的基本要素
在 GitHub 上创建你的第一个问题,起初可能会觉得挑战性很大,因为它的简单性,但掌握这一技能对有效的协作至关重要。一个结构良好的问题是关键:它应该清晰、简洁并具有可操作性。目标是提供足够的上下文,使你的观点易于理解,同时避免用过多的信息轰炸合作者。首先要清楚地识别问题,解释其重要性,并概述期望的结果。
这一过程从你的 GitHub 仓库开始。如果你已经有了一个现有仓库或刚刚创建了一个仓库,你会在仓库菜单中找到Issues选项卡。在这里,你可以创建一个新的问题:
图 4.27 – 新问题
创建问题是简单的。界面展示了标题和主要描述的字段,以及分配者和标签等元数据选项。重点应该放在问题的内容上。GitHub 支持 Markdown 进行文档格式化,因此熟悉 Markdown 语法是很有益的。然而,请记住,简洁是关键——Markdown 的功能不如 Microsoft Word 丰富,但它非常适合创建干净、简洁的文档:
图 4.28 – 在 GitHub 上创建问题
在一个问题中,你可以将其分配给团队成员,还可以在内容中直接提到个人或团队以发送通知:
图 4.29 – 将问题分配给其他用户
任务标记(例如,作为 bug、文档或增强功能)也是可能的。虽然可以创建自定义标签,但建议先从默认标签开始,并逐步增强它们。过度使用标签可能导致混乱和分类上的挑战。如果你的团队或组织有特定的标签标准,最好遵循这些标准。
在 GitHub 上使用问题的方法并非自上而下,而是鼓励一种社区驱动的、自下而上的风格。从一开始就强制执行严格的规则可能会限制促进开放、灵活和协作文化所需的自由。平衡非常重要;随着你和你的团队越来越习惯 GitHub 的工作流程,你可以相应地调整方法:
图 4.30 – 可以应用多种标签
最后,你会看到提交的问题:
图 4.31 – 提交问题后
如你所见,管理 GitHub 上的问题非常简单。关键在于促进协作和沟通。在接下来的部分,我们将深入探讨有效的沟通技巧,并探索如何通过 GitHub 增强组织内的协作。
有效沟通
现在,构建一个优秀问题有几个要素:标题、描述的写作方式以及回复的基本方式。即使我们努力清晰地写作,在忙碌的工作日中,我们也容易写得杂乱无章。这种语境上的缺失会导致沟通不畅。通过遵循将要介绍的一些规则,你将能够与同事更好地沟通。
编写有效标题——清晰度和影响力的关键原则
问题的标题是合作者首先看到的内容,因此它对吸引注意力和传达问题的本质至关重要。目标是创建一个简洁而富有描述性的标题。避免使用模糊的标题,如“问题”或“功能请求”——要具体。例如,“修复 README 中的断链”要比“问题”更具信息性。但为什么清晰性如此重要呢?
在组织内部工作时,你通常会参与多个代码库,每个库中可能包含大量未解决的问题。为了有效地管理并引起人们对这些问题的关注,标题起着至关重要的作用。它应当具有吸引力且准确无误,清晰但不夸张。
下面是一些确保你的问题标题既突出又易于理解的指南:
-
简洁明了的描述:确保标题简洁而富有描述性,使用简单的语言,避免使用技术术语。这种清晰性有助于团队成员快速理解问题。
-
以类别关键字开始:使用“修复”、“增强”或“优化”等术语,以便立即传达问题的性质。
-
概括核心问题:标题应简明扼要地概括主要问题或请求,避免冗余细节。
-
中立且具体:聚焦于问题本身,而不是谁报告或将解决它。如果适用,包含受影响的特定组件或功能,以便更好地分类。
-
清晰优于优先级:避免在标题中使用表示紧急或严重的措辞。相反,使用标签或问题内容来传达这一点,确保标题保持明确且聚焦。
当然,也存在一些例外情况,这些指南应根据每个项目的需要进行定制,但在 GitHub 问题中,加速问题分类,通过快速排序和识别问题,提高可发现性,便于轻松查找问题,并且促进团队间清晰沟通,确保每个人都能一目了然地理解问题。这种简化的方法是高效管理代码库的关键。
提供描述中的上下文——清晰简洁沟通的策略
有效的问题描述是 DevOps 协作解决问题的基石。要写出引人注目且清晰的问题描述,首先概述当前的情况,为理解打下基础。接着简洁地陈述你所提出的问题或改进。最后,阐明你希望达到的结果或解决方案。这种有条理的方法确保了理解并为建设性的对话方向提供保障。
背景在问题描述中至关重要。它能为你的团队成员提供对你的观点和问题产生或特性需求出现时的背景的理解。如果可能,提供可以复现问题的步骤。这不仅仅是解释一个问题,而是将你的协作者带入你的经验中,使他们能够有效地评估问题。
你还可以利用 Markdown 来提升问题的可读性和互动性。这种简单的标记语言允许你通过标题来构建问题,用项目符号或编号列表来组织要点,并使用代码块和语法高亮来包含代码片段。这些元素不仅使问题更易于导航,而且更加吸引人且便于互动。
以下是确保你的问题描述清晰、简洁并有效传达你所处理的问题或改进的指南:
-
背景和清晰度:从清晰的当前情况背景开始,并用具体的术语定义问题或改进点。这为理解问题打下基础。
-
结果和复现步骤:描述期望的解决结果,并在报告 bug 时提供详细的复现步骤。这种清晰度有助于其他人形象化解决方案并理解问题的范围。
-
美丽的 Markdown 和表情符号:使用 Markdown 和表情符号来组织你的描述,使其通过标题、列表和代码块变得更易读。
-
视觉辅助和解决方案:包括截图或链接提供额外的上下文。建议潜在的解决方案或替代方法也能帮助启动问题解决过程。
-
聚焦与协作:确保描述聚焦于问题本身,避免涉及无关话题。鼓励反馈并愿意接受建议,促进解决问题的协作方法。
良好的问题描述提升理解,通过提供清晰的理解帮助团队成员快速把握问题,促进高效问题解决,通过快速评估和解决方案的制定,加强协作,通过邀请建设性反馈和共同寻找解决方案。这种简洁的方法简化了项目进展和团队合作。
问题回复促进协作文化
GitHub Issues 不仅仅是一个跟踪 bug 的工具;它们是协作和文档的中心。当你回复一个问题时,你不仅仅是在解决一个特定的问题或疑问,而是在为项目的被动文档做出贡献。
回复问题很简单。只需在 Markdown 中写下你的回复并提交即可。但即便如此,你也需要有一个哲学:
图 4.32 – 评论问题
在 GitHub 上,评论一个问题非常重要,不仅仅是邮件回复。我们来看看以下几个视角:
-
被动文档:被动文档是一个来自 InnerSource 背景的概念,文档并非主动创建,而是通过仓库内的互动自然演变。在 GitHub 中,每一个问题、拉取请求和讨论线程都成为这种被动文档的一部分。这个过程是有机的,自下而上的;当团队成员参与讨论、请求功能、解决问题或实施解决方案时,他们的互动会被记录下来。这形成了一个全面、不断发展的记录,记录了决策、讨论和变化,成为一个活文档,捕捉项目的历史和理由。
-
集中沟通以提高透明度:在协作环境中,沟通通常通过多个渠道进行,如 Slack、Teams、Jira 和 GitHub。然而,将与项目相关的讨论集中在 GitHub 上具有显著的优势。它不仅创造了有价值的文档,还促进了透明的文化。所有的决策过程和协作对所有团队成员都是可见和可访问的,促进了包容性和理解。当讨论发生在 Slack 等其他平台时,将这些讨论引导回 GitHub,确保关键信息和决策得以记录,并在整个团队中共享。这种方法避免了信息孤岛,确保每个人,无论何时加入项目,都能访问到相同的信息。
-
拥抱积极和包容性的沟通:有效的协作建立在积极和包容性的沟通基础上。在回复问题时,重要的是要肯定并表扬良好的行为,感谢团队成员的贡献,并保持尊重和支持的语气。这不仅鼓励健康的团队氛围,还能提升士气,培养归属感和感激之情。包容性沟通意味着确保每个人的声音都被听到和重视,他们的贡献得到认可。这种方法不仅加强了团队凝聚力,还推动了更好的问题解决和创新。
在 GitHub 中,回复问题不仅仅是一个回应;它是构建被动文档、促进团队透明度并鼓励积极包容性沟通的重要组成部分。通过将问题回复视为对一个活文档的贡献,我们创造了项目演变的丰富历史。将沟通集中在 GitHub 上,确保所有团队成员都能平等地获取信息,有助于透明的决策制定。最后,采取积极的沟通风格可以增强团队凝聚力,确保每个人都感到被重视和倾听。对问题回复的这种全面方法,不仅仅是解决问题,更是建立强大、包容和透明的团队文化。
现在,让我们看一下下一节的拉取请求。这与 GitHub 问题的界面非常相似,主要的区别在于它除了评论外,还涉及到实现。
现在,让我们深入探讨工程协作的核心。
拉取请求的卓越
GitHub 的拉取请求功能是软件开发领域的一项关键创新,可以说它对开源软件(OSS)运动的形成起到了重要作用。它的引入标志着一个转折点,重新定义了在软件项目,尤其是开源软件项目中如何进行协作、代码集成和质量保证(QA)。
GitHub 中的拉取请求不仅仅是一个功能;它是软件开发领域协作的基础机制。拉取请求本质上是一个请求,将一组更改从仓库的一个分支合并到另一个分支,通常是从功能分支或主题分支合并到主分支或主干分支。但拉取请求的意义远不止于代码合并;它们是讨论、审查和完善代码的枢纽,尤其是在协作项目中。
什么使拉取请求与众不同?
拉取请求模型改变了协作编码的方式。它将焦点从单独提交的代码补丁转移到更加互动和社区驱动的方式。开发者现在不仅可以提交更改,还可以直接在 GitHub 平台上参与关于这些更改的讨论。这促进了协作审查和持续反馈的文化,这对于维持高质量的代码和确保贡献与项目目标一致至关重要。拉取请求引入了一种结构化、透明的代码审查方法。代码更改现在可以在拉取请求的上下文中轻松查看和讨论,从而允许更详细和高效的反馈。这个过程确保了更改经过充分审查,从而提高了代码质量和软件项目的健壮性。
拉取请求不仅革新了代码审查和合并的方式,还显著简化了 GitHub 中的开发工作流程。通过与 GitHub 原生功能和大量第三方工具无缝集成,拉取请求支持并增强了持续集成(CI)和持续部署(CD)实践。这一集成改变了开发过程,使其更加高效、可靠和自动化。
一个关于拉取请求的有趣发展是预发布测试的集成,这一过程发生在拉取请求的讨论线程中。这一进展促成了一种更全面、更紧密的方式来确保代码质量和项目管理。每当开发者发起拉取请求时,它会触发一系列自动化检查和测试。这些测试可以涵盖从代码质量评估和安全漏洞扫描到性能测试等多个方面。这些测试的结果会直接显示在拉取请求中,提供即时且可操作的反馈。
超越命令行
在讨论 GitHub 拉取请求的历史背景和演变时,值得注意的是,拉取请求这一术语本身源于 Git 中的 git request-pull
命令。这一联系可能使一些人认为 GitHub 的贡献主要是为现有概念提供了用户界面,而不是发明了一种全新的东西。然而,深入探讨拉取请求的历史和发展,会发现它具有更为深远的影响。
GitHub 将一个基本的命令行功能转变为其网页界面中的一个丰富、互动且协作的功能。这一转变不仅仅是增加了一个用户界面层;更是重新构想了围绕代码更改进行协作的方式。
创建拉取请求
首先,在 GitHub 上选择或创建一个仓库。为了演示,我们将重点关注 GitHub 的用户界面,而非命令行界面。考虑一个仅包含 README.md
文件的仓库作为起点。目标是向该文件中添加详细内容并提交一个拉取请求进行合并:
图 4.33 – 仓库页面
第一步是创建一个名为 adding-details-to-readme
的分支。这个分支是你在将更改合并到主分支之前进行编辑的地方。虽然你可以使用 Git 命令创建并推送新分支,GitHub 也提供了一个直观的界面来实现这一功能。使用左上角的分支下拉按钮从主分支创建一个新分支:
图 4.34 – 通过下拉菜单创建新分支
一旦创建了分支,你可以在任何分支上进行更改,而不仅仅是主分支或主干分支:
图 4.35 – 分支创建通知
然后,让我们编辑README.md
。在开始编辑之前,请通过检查左上角的下拉菜单确保您位于正确的分支上。接着,点击右上角的编辑按钮开始编辑README
文件:
图 4.36 – 点击编辑按钮
README.md
文件是您仓库的“面孔”和“门户”,欢迎团队成员和新贡献者。在这里,发挥您的想象力,撰写一份引人注目的README
:
图 4.37 – 编辑 README.md 文件
好的,现在我们已经准备就绪。让我们开始提交您的更改。既然您已经在编辑分支上,您可以直接提交您的编辑内容。不过,如果您在main
分支上打开编辑窗口并提交,您仍然可以选择创建一个新的分支。但最好在开始时有意识地创建一个分支并提交。另外,GitHub 上有分支保护策略配置,防止您直接提交到团队共享环境中,比如main
、production
和release
分支,关于这部分内容将在本章后续讲解。
标题会成为 Git 提交消息,因此在阅读前几章并进行提交时,请仔细考虑这一点:
图 4.38 – 提交更改
一旦更改提交,您可能会在仓库主页看到创建拉取请求的通知。如果没有出现通知,或者您希望选择不同的分支,您也可以通过点击新建拉取请求按钮来创建拉取请求:
图 4.39 – 拉取请求通知
如果您没有看到此通知,或者想选择不同的分支,可以通过点击拉取请求标签下的新建拉取请求按钮来开始相同的流程:
图 4.40 – 点击新建拉取请求按钮
在浏览界面时,您可能会发现自己进入了拉取请求页面,无论是通过跟随通知还是点击新建拉取请求按钮。无论您如何到达,这都是您查看更改历史记录的地方。
在 GitHub 中,更改的展示方式可能有所不同。如截图所示,差异(或 diff)可以以内联方式展示,交错在每行代码中:
图 4.41 – 统一模式下查看更改页面
或者,GitHub 提供了分屏视图,显示原始代码和修改代码的差异。这种分裂式显示方式可以让您更清楚地对比原始代码和修改后的代码:
图 4.42 – 分屏模式下查看更改页面
当你满意时,开始编写拉取请求。一个好的拉取请求包括清晰的标题、详细的描述和指定的评审者。至于评审者,你可以指定个人或团队。这里的内容有点像你在问题中会写的内容:
图 4.43 – 你可以查看提交之间的差异
现在,你的写作技巧再次受到考验。写作时需要关注的内容与我们在上一节中关于问题时所关注的内容相同。写作时使用易读的 Markdown 格式。然而,在这种情况下,理解这些工具的不同目标至关重要。拉取请求的主要目的是合并,通常是针对有权批准的特定个人。相反,问题可以面向更广泛且未定义的受众。因此,编写这个拉取请求的描述时,考虑谁将进行评审以及如何写作是非常重要的。
如果评审是由特定人员进行的,可能会有一些共享的理解或背景,这可以减少评审时需要大量评论的情况。基本的技巧和处理问题时一样,但要根据你的目标受众调整写作方式。
你可能会注意到在创建拉取请求时有两个可用选项,可以通过右侧的下拉按钮访问。你可以选择创建标准拉取请求或选择草稿拉取请求。根据你的计划,草稿拉取请求可能不适用于私有仓库,但任何人都可以在公共仓库中尝试使用。我鼓励你查看:
图 4.44 – 另一个选项:创建草稿拉取请求
草稿拉取请求和常规拉取请求在用户界面上没有太大区别。然而,草稿拉取请求用于表示该拉取请求尚未完成,或者整体工作仍在进行中、未完成,或者处于开发的特定阶段。
它允许进行中期评审,表明该拉取请求尚不打算进行正式评审。评审者通常会欣赏收到中期结果或报告。早期评审可以提供宝贵的见解,并与团队分享进展,这对项目有益。
在草稿拉取请求的情况下,不会因错误而立即进行合并,如下所示:
图 4.45 – 草稿拉取请求明确表示尚未准备好进行评审
在极端情况下,你甚至可以通过git empty commit
命令创建一个拉取请求,而不写一行代码。这在开发的早期阶段非常有用,可以让团队成员或配对编程的伙伴提前审查代码,帮助尽早发现错误。因此,强烈建议同时利用草稿拉取请求。
在 Git 中,空提交是指不包含任何更改的提交;就像发送了一条消息但没有修改任何文件。这在开发的早期阶段尤其有用,可以在不需要实际代码更改的情况下发起讨论。要创建一个空提交,可以使用git commit --allow-empty -m "Your message"
命令,这样就可以进行一次提交,包含消息但不包含任何内容更改。这个功能对于创建作为未来代码审查占位符的拉取请求或标记开发过程中特定的里程碑非常有用。建议你在使用草稿拉取请求时记住这个空提交。
现在,一旦你进行了适当的沟通并完成了审查,就到了合并的时候。这里 GitHub 的伟大之处并不是命令行界面,而是能够在用户界面上进行合并。你可以直接进行合并,但在执行之前,让我们先看看可选的选项:
图 4.46 – 可以通过网页用户界面进行合并
在检查合并下拉菜单时,你会发现三个选项。这些选项分别是创建一个合并提交,然后将提交合并为一个并执行合并,最后是执行变基合并。虽然这些合并技术在之前的章节中已经讲解过,并在这里不会详细展开,但 GitHub 的美妙之处在于它提供的灵活性,允许你选择每个选项来管理你的 Git 历史记录。如果你想保留贡献历史,首先需要合并提交,确保尽可能多地保留变更历史。另一方面,如果你更倾向于一个干净的历史,像合并并压缩和变基合并等选项也是可以考虑的。
然而,重要的是要注意 GitHub 的git rebase
命令。GitHub 上的变基合并实际上是将特性分支上的每个提交单独重新应用到基础分支上,而不创建合并提交。这可以保持线性的历史,同时保留提交的时间顺序。无论你是希望保留详细的贡献记录,还是更倾向于简化的历史,GitHub 都提供了满足你项目需求的工具:
图 4.47 – 合并选项
这次,我们选择了创建一个合并提交。创建合并提交很简单;只需写一个标题和描述。然后,点击确认合并按钮进行合并:
图 4.48 – 创建合并提交
然后,你将确认在 GitHub 平台上是否已成功完成合并:
图 4.49 – 检查合并是否成功
你已经观察到在 GitHub 上管理拉取请求的简便性。这个平台展示了传统上通过命令行执行的复杂操作和审查,如何在一个统一的平台中高效进行,并通过精简的用户界面得到增强。
接下来,我们将深入探讨熟练的拉取请求管理技巧。我们的重点将放在如何在以 DevOps 为中心的沟通框架中巧妙地利用这些功能,从而推动项目或产品的成功。此外,我们还将讨论需要你关注和考虑的关键方面。
拉取请求审查基础
首先,掌握如何在 GitHub 上进行审查至关重要,然后再探索值得关注的最佳实践。当你开始学习 GitHub 时,可能不会立即进行全面的代码审查。然而,了解审查应该如何进行,可以影响你的期望以及在编写拉取请求时所包含的内容。
请记住——这些都是可以逐步改进的实践,最终应该针对你的团队进行优化。
审查基础
在 GitHub 上访问审查界面非常简单。从拉取请求页面,打开更改的文件标签,查看哪些文件已被修改:
图 4.50 – 审查拉取请求的标签
一旦你打开后,-
表示删除,而 +
表示添加:
图 4.51 – 在 GitHub 上查看逐行差异
若要评论特定行的更改,使用左侧的**+**按钮。这也允许选择多行,便于高效启动讨论:
图 4.52 – 审查更改
在审查时,你可能想要提出具体的更改。这可以通过点击屏幕上的建议按钮来完成,按钮会复制相关行的内容。你可以在评论中编辑这些内容,但请注意,虽然可以对已删除的行添加评论,但不能对已删除的行提出建议:
图 4.53 – 使用建议按钮提出更改
如果你认为某个建议不错,可以按下添加单条评论按钮进行评论。如果有多个评论建议,请使用开始审查按钮,这在有很多修改时非常方便。评论建议会同时出现在拉取请求线程中,并与代码差异交错显示,使得代码审查更加直接:
图 4.54 – 建议作为评论出现
完成审查后,你会看到提交建议和将建议添加到批次按钮。GitHub 的亮点在于它允许通过用户界面直接提交这些更改,而无需通过命令行界面或编辑器进行提交。提交建议会提交单个更改,而将建议添加到批次则能将多个更改记录为一次提交。这可以防止当你的拉取请求收到许多修改时产生不必要的提交。按下提交建议按钮后,会弹出一个界面,用于输入提交信息和描述,之后该更改会提交到拉取请求分支:
图 4.55 – 提交更改
一旦审查完成,就该进行审核了。请注意,批准审查并不会自动合并拉取请求,但它会标记为你已批准。在这里,你可以选择提交、批准和请求更改。评论选项仅用于发表评论,批准表示批准,请求更改用于建议必要的修改:
图 4.56 – 审查的三种选项
如果你批准了,评论和批准状态将像这样发布在线程中:
图 4.57 – 审查后的状态
因此,GitHub 简化了审查过程,并通过其图形用户界面(GUI)启用了许多功能。代码更改与相关评论的结合丰富了上下文,使得审查者、被审查者以及当前或未来需要查看历史背景的团队成员能够有效理解和互动这些信息。
掌握此功能可以实现团队内部高度透明的沟通。
让我们来看看在审查或被审查时需要考虑的事项。
审查最佳实践
在软件开发的动态环境中,GitHub 审查作为连接个人努力与集体卓越的关键环节。这些审查不仅仅是对代码的检查,更是促进协作与学习丰富环境的途径。掌握 GitHub 审查对于寻求高质量开发流程的团队至关重要。以下是一些实现这一目标的指导原则:
-
培养审阅者的心态:GitHub 审阅的有效性始于审阅者的心态。强调及时反馈是关键,因为它能保持开发的动力。然而,速度需要与彻底性相平衡,认识到代码是团队的共同资产。这种心态促进了一种建设性且没有自我的审阅方式,将审阅视为一个有意义的对话和文档记录的机会,而不仅仅是一个任务,服务于当前和未来的团队成员。
-
增强审阅中的沟通:有效的沟通是 GitHub 审阅的核心。利用 Markdown 提供清晰且结构化的反馈,确保审阅内容易于访问和理解。在评论中具体而简洁地表述,避免混淆,加快审阅过程。积极的强化和建设性的反馈,常常辅以表情符号,营造出一个欢迎且心理上安全的审阅环境,鼓励快速且积极地采纳变更。
-
在审阅中促进多样性和作者权:审阅过程中的多样性引入了不同的视角,丰富了审阅内容并防止盲点。鼓励开发者根据反馈修改代码能够增强其自主性和技能发展。此外,即便在像 squash 合并这样的做法中,保持作者权也对保持个人动力和责任感至关重要。
-
在紧急情况下保持标准:即便在紧急情况下,如热修复,审阅的质量和彻底性也不应妥协。在压力下保持标准至关重要,因为每一次审阅都对团队的整体工作态度和代码质量有所贡献。
-
整合自动化以提高效率:通过自动化测试、语法检查和 linter 规则将自动化融入审阅过程,有助于简化审阅流程。这使得人工审阅者可以将注意力集中在代码的更复杂方面。采用审阅模板也能确保一致性和彻底性,维持高质量的审阅标准。
总结来说,GitHub 上的审阅不仅仅是代码质量保证的机制;它是促进团队成长、学习和协作的关键部分。通过采用这些最佳实践,团队不仅提升了代码质量,还改善了工作环境,促进了相互尊重、持续改进和协作精神。一次执行良好的审阅,确实是朝着更具凝聚力、高效性和韧性的开发过程迈进的一步。
我们现在已经回顾了拉取请求的概述、基本用法和最佳实践。到目前为止,看来你已经对如何在 GitHub 上协作有了不错的理解。从这里开始,我们将探讨 GitHub 的其他功能,帮助你更好地进行协作。
发挥 GitHub 的最大效能
探索 GitHub 中的其他功能可以大大提升你的仓库管理体验。尽管每个功能的详细探索可能会非常庞大,但简要介绍每个功能可以突出它们的重要性。
GitHub Projects – 在一个地方管理你的问题和拉取请求
GitHub Projects 是一个工具,可以让你在一个地方管理问题、问题草稿和拉取请求。
GitHub Projects 将提供比仅仅管理 GitHub 问题和拉取请求更灵活的管理方式。它代表了开发团队在 GitHub 上组织、跟踪和管理工作的一次重要进化。与问题和拉取请求的线性且有些受限的范围不同,GitHub Projects 提供了多方面的项目管理方法,与现代软件开发的协作和迭代特性无缝对接:
图 4.58 – GitHub Projects 表格视图
GitHub Projects 的核心是看板(Kanban board),这是一种高度可视化的工具,可以增强工作流的可视化。这个看板允许团队创建自定义的列,以反映他们的工作流阶段——从待办到进行中再到完成。拖放问题和拉取请求到这些列的简便操作显著提高了工作流程的透明度,并帮助更直观地跟踪任务的进展:
图 4.59 – GitHub Projects 看板视图
通过指派人和里程碑等工具,协作进一步得到增强。指派人确保责任明确,而里程碑有助于跟踪关键目标或截止日期的进展。这种结构化的协作方式确保每个团队成员都与项目目标保持一致,并理解自己在实现这些目标中的角色。
集成项目报告是 GitHub Projects 的另一大特色,它提供了项目进展的全面概览。DevOps 指标对评估团队表现和识别改进领域至关重要。这种基于数据的项目管理方法使团队能够做出明智的决策,并优化工作流程:
图 4.60 – GitHub Projects 状态图
GitHub Projects 还提供了一系列项目模板——从预定义的常见项目类型(如错误追踪器或看板)到能够创建自定义模板,以适应特定团队工作流程的能力。这些模板节省时间,并提供一致的结构,团队可以依赖它们。
此外,项目板中包含问题和拉取请求的筛选器,增强了有效管理任务的能力。团队可以按标签、指派人、里程碑等进行筛选,使他们能够快速找到并专注于最重要的任务。
GitHub 项目最显著的优势之一是其能够在单一项目中管理来自多个代码库的问题和拉取请求。这个功能对于跨多个代码库的大型项目尤为有利,它提供了一个整体视图和连贯的管理体验。
GitHub 项目超越了传统的问题跟踪和拉取请求管理,提供了一个全面、灵活且高度协作的平台来进行项目管理。通过使用其功能,开发团队可以提高生产力、改善协作,并推动项目走向成功,所有这一切都在熟悉的 GitHub 生态系统中进行。与 DevOps 文化的原则——优先考虑协作、效率和透明度——保持一致,使得 GitHub 项目成为现代软件开发领域中的一款优秀工具。
GitHub Codespaces —— 利用基于云的环境改变开发工作流
GitHub Codespaces 通过在 GitHub 内直接提供完全可配置的基于云的开发环境,彻底改变了开发者的工作方式。这标志着开发工作流演变的一个重要步骤,使团队更容易协作并简化其 DevOps 实践。
GitHub Codespaces 通过关键功能简化开发:
-
快速启动和统一工作空间:实现基于云的环境的即时设置,协调团队努力并减少设置复杂性
-
协作与拉取请求集成:促进在 Codespaces 环境内更轻松的代码审查和讨论,加强跨地点的团队合作
-
安全与隔离的环境:为每个用户提供安全的、容器化的工作空间,保护项目的完整性和数据安全
作为一种可以在浏览器中使用的 Visual Studio Code 实现,GitHub Codespaces 提供了与 VS Code 中相同的功能和扩展。虽然在 Codespaces 版本中可用的扩展存在一些限制,但这种熟悉感是一个巨大的优势。它使开发者能够利用这个功能强大且广受欢迎的开发工具,同时享受云端可访问性和集成的额外好处:
图 4.61 – GitHub Codespaces
启动一个新的 codespace 非常简单。开发者只需点击几下,就可以启动一个自动配置好仓库 .devcontainer
配置中指定的设置和工具的开发环境。这确保了开发环境的一致性,这是 DevOps 实践中的一个关键方面,强调可复现性并减少了“在我的机器上可用”的问题。
GitHub Codespaces 的一个重要好处是无需克隆大型文件并在本地设置复杂的开发环境。这不仅节省了新工程师的入职时间,还简化了在不同项目间切换的过程,每个项目都有其独特的环境需求。通过将开发环境托管在云端,Codespaces 大大减少了管理本地开发环境的开销。
性能和定制化是 GitHub Codespaces 的关键特点。开发者可以为他们的 Codespaces 选择硬件规格,确保他们拥有完成任务所需的资源。定制化还扩展到开发环境本身,支持 Visual Studio Code 扩展和个性化的编辑器设置,进一步强化了 DevOps 对开发者体验的关注。从管理角度来看,GitHub Codespaces 允许组织领导者设定与开发环境相关的特定政策,包括对用户选择机器类型的限制。这一功能确保开发环境与组织的标准和资源管理策略相符,在公司指导方针内提供了成本与性能的平衡方法。
图 4.62 – 限制机器类型的访问
此外,它提供了出色的协作与安全性平衡,为现代开发团队提供了完美的支持。将拉取请求直接集成到 Codespaces 环境中,显著提升了协作过程,使团队能够更高效地提出、讨论和合并更改,无论其物理位置如何。这种简化的代码审查和协作工作流程与强大的安全优势相结合。Codespaces 减少了员工将整个源代码数据下载到本地所带来的风险,解决了本地文件存储和管理所引发的常见安全问题。每个用户的工作区都被安全隔离,进一步缓解了与本地开发实践相关的潜在安全问题。Codespaces 中协作与安全性的深思熟虑的结合,不仅提高了生产力,还加强了项目数据的保护,提供了一个安全且协作的开发解决方案。
GitHub Codespaces 不仅仅是一个开发环境;它是提升 DevOps 实践的催化剂。通过提供一个灵活、协作和集成的平台,它使团队能够更快速地创新、更有效地协作,并交付更高质量的软件,所有这一切都在 GitHub 生态系统内进行。这使得 GitHub Codespaces 成为现代软件开发和 DevOps 领域中不可或缺的工具。
GitHub 讨论区 – 促进社区和协作
GitHub Discussions 作为一个重要的平台,在 GitHub 仓库内构建和培养社区方面发挥着重要作用。它代表了开发者、贡献者和用户之间互动与协作方式的重大进步。这个功能超越了传统的问题和拉取请求通信,提供了一个专门的空间,用于提问、想法和讨论。GitHub Discussions 创造了一个环境,使更广泛的社区——包括那些可能未直接参与代码贡献的人——能够互动、分享见解并提供反馈:
图 4.63 – GitHub 讨论线程
GitHub Discussions 的一个关键特点是其高效组织沟通的能力。讨论可以按不同类型进行分类,如问答、公告或一般讨论。这种分类有助于保持清晰性和重点,使用户更容易找到相关的对话并有意义地参与其中:
图 4.64 – GitHub 讨论类别
GitHub Discussions 在知识共享和保存方面也起着至关重要的作用。与即时通信渠道(如聊天室)不同,讨论是持久的并且可以搜索,这使得它们成为当前和未来社区成员的宝贵资源。这种档案化特性确保了在讨论中共享的知识,在对话结束后仍能继续造福社区。
总之,GitHub Discussions 不仅是促进社区参与和协作的强大工具,而且在维护 GitHub 问题的组织性和清晰度方面起着至关重要的作用。随着项目的增长和发展,问题的数量和多样性可能变得压倒性,导致杂乱无章和混乱。GitHub Discussions 作为沟通的催化剂,使团队能够将各种话题和对话引导到更有结构和更合适的空间。
GitHub Discussions 将广泛的一般性讨论与通常位于 GitHub Issues 中的更具体、技术性的对话区分开来的能力极为宝贵。它有助于保持Issues部分的专注和可管理性,减少噪音,并使团队更容易跟踪和处理特定的与代码相关的任务。
此外,通过为广泛的讨论提供专门的平台,GitHub Discussions 在多方面促进了协作的顺利进行。它不仅确保了宝贵的见解和想法得到共享和保存,还避免了在Issues部分中技术讨论的稀释。这种将一般讨论和特定问题区分开的做法对于维持高效的工作流程并与 DevOps 实践保持一致至关重要,后者强调简化的流程和清晰的沟通。
因此,GitHub Discussions 不仅仅是 GitHub 生态系统中的一个附加功能;它是一个增强项目管理整体功能和有效性的核心组成部分。它有助于保持 GitHub 问题的简洁和专注,同时为更广泛的社区驱动对话提供了一个强大的环境,从而促进协作并推动项目成功。
GitHub 仓库卓越性
GitHub 仓库中有许多配置可以促进协作。这些设置可以减少对失败的恐惧,并使审查过程更加规范化。我们不会在这里涵盖所有配置,但其中一些主要设置尤其重要。
仓库规则 – 简化工作流程并确保代码质量
分支规则对于以平衡安全性和协作的方式管理仓库至关重要。虽然 GitHub 允许为仓库或特定功能设置权限,但并未提供基于代码的权限。仅依赖写权限可能会限制参与并阻碍开放沟通。分支规则允许仓库管理员对分支执行特定政策,尤其是对于主分支或主分支等关键分支。这些政策包括拉取请求审查要求、状态检查和对谁可以推送到该分支的限制。通过设置这些规则,团队可以确保合并到重要分支的代码符合预定的质量标准,并且流程与团队的工作流程保持一致。
规则集允许多种设置。这些设置可以仅应用于特定分支,或者管理员可以授权绕过这些规则:
图 4.65 – 在 GitHub 中设置分支规则
分支规则的主要好处之一是强制执行代码审查。通过要求拉取请求在合并之前获得一定数量的批准,团队可以确保每个变更都经过审查和验证。这个同行审查过程不仅提高了代码质量,还促进了团队成员之间的知识共享和协作:
图 4.66 – 分支规则中的代码审查要求
状态检查是分支规则中的另一个关键部分。这些检查可以包括自动化测试、代码检查结果或任何其他验证代码质量和功能性的自动化过程。通过要求这些检查在合并之前通过,团队可以防止错误和问题进入主代码库,从而保持高标准的代码质量:
图 4.67 – 分支规则中的状态检查
分支规则是 GitHub 中保护代码质量和执行工作流纪律的基本配置。它们使团队能够自动化和强制执行开发过程中的关键方面,符合 DevOps 的原则。通过使用分支规则,团队可以确保其代码库保持稳定、安全和一致,从而支持在协作环境中交付高质量的软件。这使得分支规则成为现代软件开发中的一个核心元素,也是 DevOps 工具包中宝贵的资产。
CODEOWNERS – 精简审查和所有权管理
GitHub 中的CODEOWNERS
文件是一个简单而强大的工具,用于自动分配拉取请求的审阅者,并明确特定代码区域的所有权。该文件放置在根目录、docs/
目录或.github/
目录中,列出个人或团队以及文件模式。当对匹配这些模式的文件进行更改时,会请求指定的代码所有者进行审阅:
图 4.68 – CODEOWNERS 文件示例
使用CODEOWNERS
文件的好处包括:
-
自动化审阅者分配:通过自动分配合适的审阅者来简化拉取请求流程,确保变更由相关领域的专家进行审查
-
明确所有权:明确谁负责代码库的特定部分,有助于更快的决策和更高效的维护
CODEOWNERS
这一部分特别重要,因为它直接关系到部署安全。在 CI/CD 是常态的环境中,确保只有经过充分审查和批准的代码被部署至关重要。这为部署过程增加了一层安全性和效率,体现了有效的 DevOps 实践中的预防性和主动性原则。
问题和拉取请求模板
GitHub 中的模板增强了协作并保持项目管理各个方面的一致性:
图 4.69 – 问题模板菜单
问题模板帮助贡献者创建详细且结构化的问题报告。通过为不同类型的问题(如漏洞报告、功能请求等)提供特定模板,可以确保所有必要的信息都被包含在内,从而促进更易理解和更快的解决:
图 4.70 – 问题模板示例
拉取请求模板确保每个拉取请求都符合项目的标准和要求。这些模板通常包括检查清单、描述变更的部分、引用相关问题和任何附加备注。通过标准化,简化了审查过程并提高了贡献的质量。
这些组件中的每一个都在构建一个井然有序、可访问且对贡献者友好的 GitHub 仓库中发挥着至关重要的作用。通过实施这些标准的基础文档和模板,你为协作和项目管理奠定了坚实的基础,与 DevOps 和开源文化中的最佳实践相呼应。
总结
在这一章中,你已经获得了关于在 GitHub 上协作的基本知识,为 DevOps 实践打下了坚实的基础。虽然我们已经探讨了与 Git、GitHub 和 DevOps 相关的各种主题,但本章特别集中在如何有效使用 GitHub 进行协作,这是 DevOps 世界中的一项关键技能。
这些知识不仅是理论性的,而且具有很高的实用性,为我们旅程的下一阶段奠定了基础——在实际的 DevOps 发布过程中应用这些 Git 技巧和 GitHub 技能。当我们过渡到下一章时,我们将基于这个基础,深入探讨这些技能和实践如何在 DevOps 工作流中直接应用和利用,最终提升软件开发和部署过程的效率与效果。
准备好开始下一阶段了吗?让我们进入下一章,在这里我们将看到这些原则的实际应用,见证 Git 和 GitHub 在 DevOps 领域的变革性影响。
第五章:通过 GitHub 推动 CI/CD
本章旨在剖析 GitHub Actions 的各个层面,从核心概念到高级部署策略。您将详细了解 GitHub Actions 的功能、结构和最佳实践。我们将深入探讨工作流、任务、步骤和动作的本质,逐一解析它们在自动化中的重要性。关于减少冗余、管理密钥和变量、调试技巧等实用见解,将提升您的工作流效率和安全性。权限和审批流程也被覆盖,确保您能够精准掌控您的持续集成/持续交付 (CI/CD) 流水线。
部署策略清晰展开,呈现了蓝绿部署、滚动部署和金丝雀部署,每种部署策略都有其步骤、实际应用和切换方法。功能发布策略的世界也被揭示,为您提供了详细的功能标志和发布列车的解释。
本章将涵盖以下主要内容:
-
GitHub Actions – 精通工作流自动化
-
部署策略
-
功能发布策略
GitHub Actions – 精通工作流自动化
GitHub Actions 代表了软件开发和部署过程自动化的变革性转变。作为 GitHub 的原生功能,这一世界级的 CI/CD 平台使得在您的 GitHub 仓库中直接创建、管理和执行工作流成为可能。GitHub Actions 提供了简单明了的工作流,以下截图展示了这一点:
图 5.1 – GitHub Actions 内置可视化
本章深入探讨 GitHub Actions 的复杂性,提供对其功能、实现和最佳实践的详细理解。
GitHub Actions 综合概述
GitHub Actions 提供了广泛的优势,以增强您的软件开发工作流程。以下是它为您带来的具体内容:
-
自动化:GitHub Actions 自动化了诸如代码构建、测试和部署等重复任务。它响应诸如代码推送和问题创建等事件,减少了人工干预的需求。
-
多功能性:它可以用于多种用途,包括构建容器化应用程序、部署 Web 服务、管理依赖关系等。这种灵活性使其能够适应不同的开发需求。
-
CI/CD 自动化:GitHub Actions 简化了 CI 和 CD 流程。它自动构建并测试代码变更,确保代码质量,简化部署管道,从而加速并提高发布的可靠性。
-
改进协作:它自动化代码审查流程,执行自动化测试,并根据工作流结果发送通知。这促进了团队成员之间更好的协作,因为他们可以专注于解决关键问题和改进。
-
自定义:GitHub Actions 允许通过 YAML 配置文件进行工作流的自定义。开发人员可以根据自己的特定需求调整工作流,并无缝集成第三方工具和服务以扩展功能。
创建工作流只需将一个 YAML 文件放入仓库的 .github/workflows
目录中,如下图所示。这一设置使得 CI 任务、拉取请求中的反馈集成以及拉取请求合并条件得以定义:
图 5.2 – 使用 YAML 轻松的开发体验
为了展示 GitHub 工作流的强大功能和灵活性,我们以一个典型的 Node.js 应用程序的 CI 工作流为例。每当推送到 main
分支或提交拉取请求时,这个工作流会被触发,可能包括 构建、测试 和 部署 等任务。每个任务都有其特定的目的——分别是构建项目、运行测试和部署应用程序。在这些任务中,步骤和动作被精心编排,确保 CI 过程中的每个环节都能顺利执行。从检出代码、设置 Node.js 环境到运行测试和部署应用程序,每个步骤和动作都扮演着至关重要的角色。接下来,我们将详细探讨其结构。
以下是一个典型的 GitHub Actions 工作流示例。该工作流是用 YAML 编写的,可能看起来有些困难。但如果你逐行查看,它其实非常简单且易于理解。请注意,这个示例是用来构建 Node.js 应用程序的,如果你只是直接粘贴此文件,它是无法正常工作的。如果你想尝试,可以从 Packt 仓库复制整个项目 (github.com/PacktPublishing/DevOps-Unleashed-with-Git-and-GitHub/tree/main/chapter5
):
name: Node.js CI
on:
push:
branches: [ "main" ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [16.x, 18.x]
steps:
- uses: actions/checkout@v3
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- run: npm ci
- run: tsc
- run: node dist/test
深入剖析 GitHub 工作流结构
GitHub 工作流代表了现代软件开发自动化的典范,这一功能从根本上改变了开发者处理 CI/CD 流程的方式。本章节将深入探讨 GitHub 工作流的复杂性,解析其结构和功能,帮助理解它们如何革新软件开发实践。
当你看到 YAML 文件时,可能会觉得它很难理解。没错,如果能在 GUI 中调整它,可能会更直观。但 GitHub Actions 工作流其实非常简单。
工作流可以分解为以下几个元素:
-
工作流:自动化的骨架,定义整个过程
-
任务:在相同环境中执行的一组步骤
-
步骤:工作流中的最小单元,每个步骤代表一个独立任务
-
动作:可重用的单元,组合成步骤以执行特定任务
如果我们将这些看作以下模块,它会更容易理解:
图 5.3 – GitHub Actions 工作流结构
我们将在以下子节中详细介绍这些元素。
工作流 – 自动化框架
GitHub Actions 的核心是 GitHub 仓库中 .github/workflows
目录的概念,这是你的自动化策略的蓝图。正是在这里,魔法开始了——每当发生 GitHub 事件(如推送或拉取请求)时,这些工作流就会启动,推动自动化的齿轮运转。
GitHub Actions 工作流可以与各种事件类型进行交互,每种事件都会触发在 YAML 文件中定义的不同自动化序列。以下子节将详细解释可以用来触发工作流的事件类型。
Webhook 事件触发
有多种触发器可以使用,但我们将在此提及一些典型的触发器:
-
main
分支。 -
问题操作:与拉取请求类似,工作流可以对问题活动(如创建、分配、标签和关闭)做出反应。这对于自动问题响应模板、优先级排序和问题分类非常理想。
-
推送事件及其他事件:推送事件可能是最常见的触发器,当提交被推送到仓库时会触发。由推送事件启动的工作流可以处理许多任务,包括代码扫描、构建、测试和应用程序部署。
以下示例会在推送到 main
时触发事件:
on:
push:
branches: [ "main" ]
定时事件触发
工作流可以使用 Cron 语法设置为在特定时间运行。这个功能类似于一个闹钟,它会在预定的时间间隔触发你的工作流,非常适合夜间构建、定期清理或周期性同步任务。在此示例中,工作流每天在 UTC 时间 5:30 和 17:30 触发:
on:
schedule:
- cron: '30 5,17 * * *'
手动事件触发
workflow_dispatch
允许通过 GitHub UI 或 API 手动触发工作流。这提供了按需运行工作流的灵活性,特别适用于临时任务,如手动部署、数据导入或任何在执行前需要人工监督的操作。
以下是手动操作设置的示例:
on: workflow_dispatch
任务 – 执行单元
在每个工作流中,我们会遇到 任务。可以将任务看作是一个包含多个步骤的连贯组,这些步骤在同一个 runner 上一起执行。GitHub Actions 中的 runner 是安装了 GitHub Actions runner 应用程序的服务器。它本质上是执行你任务的地方。每个 runner 可以看作是一个干净、隔离的 虚拟机(VM),它为每个任务或工作流运行提供资源。GitHub 工作流中任务的魅力在于,除非明确指定彼此依赖,否则它们可以在不同的 runner 上并行运行。每个任务可以在各种 GitHub 托管的虚拟机上运行,这些虚拟机支持不同的操作系统,或者甚至在自托管的机器上运行,提供了前所未有的灵活性。
在以下示例中,build
作业是工作流过程中的一个基本组件,通常负责编译代码或生成构建产物。build
这个名称可以替换为任何你喜欢的作业名称。在你的配置中,runs-on
键指定了以下示例作业的执行环境,即 ubuntu-latest
运行器。这个选择指示 GitHub Actions 在平台上执行 build
作业,使用的是最新版本的 Ubuntu Linux。除此之外,你还可以选择 macOS 和 Windows。此外,你的本地计算机或内部部署的计算机可以注册为自托管运行器,使你能够在多种环境中使用它们,包括那些有严格网络要求或更大工作负载的环境:
jobs:
build:
runs-on: ubuntu-latest
GitHub Actions 中的 strategy
功能通过使用 matrix
选项简化了跨多个环境的测试。这个选项允许你定义一组不同的配置(如操作系统、编程语言版本或依赖项),并指定你的工作流要在哪些配置上运行。无需为每个配置手动设置多个作业,matrix
选项会动态生成每个你指定的配置组合的作业。这种方法避免了手动设置的冗余,并确保你的代码在不同版本的依赖项(如 Node.js)上都能正常工作。这是一种主动发现兼容性问题的方式,确保你的应用在不同环境中的稳定性。虽然这是可选的,但值得牢记:
jobs:
build:
strategy:
matrix:
node-version: [16.x, 18.x]
此外,可以按如下方式进行容器设置。如果你需要特定的容器环境,可以使用它来在灵活的环境中实现 CI/CD:
jobs:
your-container-job:
container:
image: node:18
你还可以按照如下方式指定作业运行的条件:
jobs:
production-deploy:
if: github.repository == 'georgehattori/hello-world'
步骤 – 构建模块
深入来看,每个作业包含 步骤,即基本任务或操作。每个作业中的步骤按顺序执行,确保任务有序进行。这些步骤可以是简单的脚本或命令执行,也可以是执行一系列操作的复杂过程。步骤是工作流的构建模块,将自动化的大图拼接在一起:
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
操作 – 定制的任务执行器
这些步骤中最细粒度的元素是 操作。操作是 GitHub 工作流的核心,封装了可重用的特定功能。从为特定任务量身定制的自定义操作到 GitHub 市场上丰富的现成操作,可能性是无限的。操作有多种形式,包括用于在运行器上直接执行的 JavaScript 操作、用于容器化操作的 Docker 容器操作,以及组合运行步骤操作,它们结合了多个运行命令。这种多样性使得高度定制成为可能,开发者可以根据自身的开发需求精确地制定工作流。
以下是 GitHub Actions 工作流中的一个代码片段,专门为 Node.js 项目定制,利用一系列命令和操作来准备运行时环境并构建应用程序:
- uses: actions/checkout@v3
- name: Use Node.js 14
uses: actions/setup-node@v3
with:
node-version: '14'
cache: 'npm'
- run: npm ci
- run: npm run build --if-present
- run: npm test
让我们更仔细地看看 GitHub Actions 的具体内容:
-
name
:这本质上是一个标签,用于提高工作流文件的可读性。它帮助你和任何阅读工作流的人理解该步骤的目的。这里,它指定该步骤将设置 Node.js 版本 14。 -
uses
:这个键指定要使用的操作。操作是可重用的代码块,可以执行复杂的任务。actions/setup-node@v3
指的是setup-node
操作的第三个主版本,它用于配置 Node.js 环境。当你通过uses
指定一个操作时,GitHub Actions 会获取该代码并作为步骤的一部分执行。 -
with
:该步骤的这一部分向uses
指定的操作传递输入参数。它自定义了操作的行为。对于setup-node
,你提供了两项信息:Node.js 版本(node-version: '14'
)和包管理器缓存设置(cache: 'npm'
)。cache
选项告诉操作缓存依赖项,这可以加速未来作业的执行。这是操作中的一个预定义参数,通常在市场中可用,或者是开源的。你可以创建自己的操作并分发给全世界,或者仅为内部使用创建并供内部用户使用。 -
run
:这是一个运行命令行脚本的指令,它是你可以执行 shell 命令的地方。在这种情况下,npm ci
是一个命令,它会根据你的package-lock.json
文件安装项目的所有依赖项。之后,它会构建并测试项目。
GitHub Actions 起初可能看起来很复杂,但一旦掌握了它,其实很简单。它本质上是一种控制脚本流的机制。
此外,GitHub 工作流不仅仅是执行任务;它们还简化和优化了开发过程。能够在不同的上下文中重用工作流的部分内容,例如在拉取请求验证中重用设置和测试步骤,体现了该平台对效率和可重用性的重视。在如今敏捷开发和快速迭代为常态的时代,这一方面尤为重要。
现在,让我们开始使用 GitHub Actions。由于有大量现成的模板可供选择,最好的方式是直接开始尝试它们。这些模板涵盖了广泛的自动化需求,从简化重复任务、在 CI 中自动化代码测试和构建,到在不同环境之间进行代码部署、实施关键的安全检查和措施。GitHub 上可以轻松访问大量的 GitHub Actions 模板,如下图所示:
图 5.4 – GitHub Actions 的广泛模板
通过利用这些模板,你将能够高效、有效地发挥 GitHub Actions 的全部潜力。拥抱这一强大的工具,观察它如何改变你的开发工作流,使其更加简洁、安全和强大。
GitHub Actions 最佳实践
有几个值得讨论的 GitHub Actions 最佳实践。它们包括减少冗余,这有助于通过消除不必要的重复来简化工作流。秘密和变量管理对于保持动作的安全性和效率至关重要。调试 GitHub Actions 工作流是一个必要技能,能够快速识别和修复问题。有效管理权限确保了保持正确的访问级别,而通过环境实施审批流程有助于控制部署流程并维护不同部署阶段的完整性。学习并应用这些最佳实践可以显著提高你在 GitHub Actions 中的熟练度。
现在,让我们一一查看它们。
减少冗余
高效地利用 GitHub Actions 的力量需要专注于减少冗余,这是工作流自动化中的一个关键最佳实践。这包括三个主要选项:
-
利用 GitHub Marketplace:在这里,你可以访问由社区创建的各种现有动作,适用于广泛的常见自动化任务。这种方式节省了时间,并利用了其他开发者的集体智慧。
-
创建自定义动作:对于 Marketplace 动作无法覆盖的独特需求,自定义动作提供了量身定制的解决方案。它们使你能够精准地解决项目的具体需求。
-
实现可重用的工作流:开发可以在多个项目中应用的工作流。这不仅简化了工作流设置过程,还确保了一致性和更容易的维护。
通过集成这些内容,你可以优化 GitHub Actions 的使用,从而实现更高效、一致和可维护的自动化流程,在你的软件开发生命周期(SDLC)中。以下是作为开源项目提供的各种动作:
图 5.5 – GitHub Marketplace 中的动作
自定义动作和可重用工作流是读者接下来需要学习的内容。我们这里不深入探讨,但对于 GitHub Actions 的扩展使用,章节末尾的进一步阅读部分提供了详细资源的链接。
秘密和变量管理
在 GitHub Actions 中,管理秘密和变量对安全性和工作流定制至关重要:
-
${{ secrets.NAME }}
,确保它们不会暴露在日志中或对未经授权的用户开放。一旦注册了一个秘密,它无法被检索,且注册值不会出现在 GitHub Actions 的控制台输出中。 -
${{ env.My_Variable }}
。您可以直接在工作流程文件中设置环境变量,每个作业或步骤可以有不同的设置。
以下是在 GitHub Actions 中使用注册的秘密的示例,允许您以灵活的方式从工作流程外部注入值:
steps:
- shell: bash
env:
MY_SECRET: ${{ secrets.MySecret }}
run: |
sample-command "$MY_SECRET"
正确管理秘密和变量对于维护 GitHub Actions 工作流程的安全性和效率至关重要。可以通过 GitHub 配置页面如下设置秘密和变量:
图 5.6 – GitHub Actions 的秘密和变量配置
调试 GitHub Actions 工作流程
在 GitHub Actions 中进行调试是一种重要的实践,以确保您的工作流程按预期运行,并识别和解决可能出现的任何问题。以下是一些关键点,帮助您有效地调试 GitHub Actions 工作流程:
-
ACTIONS_STEP_DEBUG
的值为true
。这将在执行操作期间为您提供更详细的信息,帮助您更容易地定位问题发生的位置和原因。 -
运行日志访问:GitHub Actions 自动为每次运行生成日志。这些日志可以在您的 GitHub 仓库的操作选项卡中访问。它们详细记录了工作流程的每个步骤中发生的情况。
-
act
(github.com/nektos/act
) 允许您在本地机器上运行工作流程。这对于测试复杂的工作流程而无需推送更改到您的仓库非常有帮助。它可以加快修复和优化操作的迭代周期。 -
在您的工作流程文件中使用
workflow_dispatch
事件触发器可以手动触发工作流程。这对于在推送或拉取请求事件上触发工作流程之外进行更改测试非常有用。
创建 GitHub Actions 工作流程时需要牢记的其他事项包括以下内容:
-
简化工作流程:将复杂的工作流程分解为更小、更易管理的部分,以便隔离和识别问题。
-
利用操作输出:使用操作的输出进行诊断目的。这些输出可以揭示不同步骤的行为。
-
迭代方法:在对工作流程进行更改时,逐步进行。在实施下一个更改之前,彻底测试每个更改。这种方法有助于隔离每个更改的影响,使调试变得更容易。
有效地调试 GitHub Actions 工作流程需要结合使用详细日志、本地测试工作流程、简化复杂设置以及手动触发工作流程。这些实践有助于快速识别和解决问题,确保您的 CI/CD 过程健壮可靠。
权限
在 GitHub Actions 中管理权限对于维护工作流过程的安全性和完整性至关重要。本节内容将重点讲解如何为 GitHub 工作流中的操作设置和管理适当的权限:
-
GITHUB_TOKEN
,一个由 GitHub 生成的特殊令牌,供工作流使用。默认情况下,这个令牌的作用范围限于包含工作流的仓库,确保操作不会无意中影响其他仓库或敏感数据。 -
在工作流文件中使用
GITHUB_TOKEN
来为特定任务或整个工作流提供令牌。这允许你根据单个任务的需求,限制或扩展令牌的功能,增强安全性并降低风险。权限在工作流文件中使用 permissions 键进行设置。你可以为各种 GitHub API 范围指定权限,如内容、问题、部署等。以下是此类设置的示例:# Sets permissions of the GITHUB_TOKEN permissions: contents: read pages: write id-token: write
-
最小权限:遵循最小权限原则(PoLP),只授予执行特定操作或任务所需的最低权限。除非绝对必要,否则避免授予广泛的或管理员级别的权限。
通过细致地管理 GitHub Actions 中的权限,你可以保护工作流免受未经授权的访问、错误和潜在的安全漏洞。这涉及到设置精确的令牌权限、安全管理机密以及定期审查访问级别,以符合 PoLP。实施这些做法将显著增强你的 CI/CD 流水线的安全性。
环境中的审批流程
在软件部署和持续集成(CI)的动态世界中,GitHub Actions 通过其环境功能带来了至关重要的控制和监督元素。这一创新功能显著提升了团队处理部署工作流的方式,尤其是在审批流程和遵循特定操作条件方面。
GitHub Actions 中的环境作为工作流中的专用空间,每个环境都针对特定阶段,如测试、预发布或生产进行定制。这些环境的特点在于它们可以通过独特的规则和访问控制进行定制。它们不仅仅是工作流的部分,而是被控制的领域,每个领域都有其独特的权限和秘密。这确保了敏感信息和操作仅限于最相关的区域,从而增强了整个过程的安全性。
环境的核心价值在于它们在促进审批工作流中的作用。假设有一个场景,任何部署到生产环境的操作都需要经过仔细审查。环境通过集成手动审批流程使这一点成为可能。当部署到达生产阶段时,它会触发工作流的暂停,等待授权人员的批准。这种暂停不仅仅是操作的中止;它是一个关卡,确保每个更改在影响生产环境之前都经过审查。带有环境的审批流程无缝集成到 GitHub Actions 的体验中,如下图所示:
图 5.7 – 环境中的审批
除了单纯的审批外,GitHub Actions 中的环境还允许进行复杂的条件设置。环境中的作业可以配置为仅在特定情况下运行,例如在某些分支上或响应特定事件时。这样的控制细节确保了工作流遵循项目的精确操作标准,避免了任何意外的部署或操作。
在这些环境中引入保护规则进一步强化了工作流的安全性。通过设置诸如强制审核者等要求,组织可以强制执行合规性并维持高安全标准。这不仅仅是为了确保工作流的安全性;更是为了将其与组织的协议和最佳实践对齐。
因此,环境不仅仅是 GitHub Actions 的一个附加功能;它是在管理部署工作流方面的一个范式转变。它引入了一个安全性、控制性和合规性层面,这在现代软件开发中是不可或缺的。环境确保了 CI/CD 流水线中的每一步,特别是涉及敏感阶段(如生产环境)的步骤,都会在严格审查和定义的操作指南范围内执行。这不仅仅是 GitHub Actions 促进自动化,而是在关注安全性、可靠性和组织完整性的同时进行自动化。从环境配置中,你可以添加保护规则设置,例如添加审批人:
图 5.8 – 环境配置
GitHub Actions 作为一个多功能工具,适应广泛的开发场景,使其成为现代软件开发团队不可或缺的资产。无论是自动化常规任务、确保代码质量还是部署应用程序,GitHub Actions 提供了高质量软件交付所需的工具和灵活性。
现在,让我们来看一些使用 GitHub Actions 部署的应用实践。
部署策略
在 DevOps 中,重要的是要将失败视为系统的一部分。在 DevOps 的动态世界中,拥抱失败不仅是推荐的做法,而是至关重要的。与传统观点中将失败视为挫折不同,在 DevOps 中,失败承担着转变的角色。它不仅仅是一个事件或意外结果,它是 DevOps 不可或缺的一部分。
在传统的部署方法中,广泛的检查和平衡是常态,旨在尽量减少故障的发生。然而,现代开发的快速节奏,如敏捷开发,显著改变了这一方法。在现代开发中,环境不断变化,服务也在不断更新。如果这些变化不能迅速有效地进行,可能会导致服务退化或故障。因此,构建一个具有韧性、能适应这些快速变化的系统变得至关重要。
在这种背景下,目标不是完全避免失败,而是创造一个可以管理失败并最小化其影响的环境。这涉及到采用预见性策略,实施机制,在必要时迅速恢复或回滚变更。关键是将健壮性和韧性融入系统中,确保系统能够承受并从失败中恢复,而不仅仅是追求无故障的运行。本节将深入探讨如何将失败作为 DevOps 中的系统性必要性加以接受。
传统上,基础设施和应用程序部署的领域是分开的。然而,DevOps 的出现带来了一个时代,打破了这些边界,形成了一个统一的环境。如今,基础设施配置也通过基础设施即代码(IaC)来定义。这意味着基础设施和应用程序可以同时发布。在 DevOps 的背景下,你需要在这两个方面都稳定地部署基础设施和应用程序。
让我们来看一些在部署服务时特别常见的实践。
蓝绿部署
蓝绿部署是一种软件部署策略,通过维护两个相同的生产环境来最小化停机时间和风险。这种方法允许在软件版本之间进行安全和平滑的过渡。如下面的图示所示,负载均衡器或平台即服务(PaaS)服务的功能切换流量:
图 5.9 – 蓝绿部署
在这种策略中,蓝色环境承载当前的在线版本,而绿色环境用于新的版本。一旦绿色环境中的新版本完全测试并准备好,流量就会从蓝色切换到绿色。
这种方法的关键好处包括以下几点:
-
轻松回滚:如果在绿色环境上线后发现问题,可以快速将流量重新引导回稳定的蓝色环境。
-
最小化停机时间:蓝绿部署使您能够在没有任何停机时间的情况下发布新版本的软件。新版本与旧版本并行部署,且切换瞬时完成。
-
简便测试:开发人员和质量保证(QA)团队可以在类似生产的环境中彻底测试新版本,而不会影响最终用户。
蓝绿的术语常常被视为类似于诸如预发布、质量保证和生产等环境的同义词,但它并不完全相同。与传统的预发布到生产的升级方式不同,这种方法中的蓝色和绿色环境都是生产级别的。
尽管有其优点,但也存在一些显著的挑战:
-
需要两个生产环境:这可能会消耗大量资源并且成本较高。即使在云计算环境中,维护两个完整的生产环境也可能是昂贵的。
-
不可逆更改的挑战:如果某些更改(如数据库架构更新)不向后兼容,回滚可能会变得复杂。
-
有状态应用程序的考虑:在不同环境之间同步有状态数据可能会比较复杂。
常见步骤
蓝绿部署在 Web 应用程序中的一个实际示例,其中当前实时版本为 1.3,在蓝色环境中,目标是部署版本 1.4,具体步骤如下:
-
部署:首先将新版本 1.4 部署到绿色环境。该环境是现有蓝色环境的副本,但托管的是新版本。
-
验证:在绿色环境中进行全面测试。此步骤至关重要,以确保新版本正常运行并满足所有必要的要求。
-
流量切换:验证成功后,下一步是切换用户流量。这是通过更新负载均衡器设置,将流量从蓝色环境(版本 1.3)重定向到绿色环境(版本 1.4)。
-
监控:流量切换后,密切监控绿色环境中的任何操作问题或异常行为。这个监控阶段对于迅速识别和解决部署后可能出现的任何问题至关重要。
-
最终确认:如果绿色环境中的版本 1.4 运行顺利且没有发现重大问题,则视为部署成功,绿色环境将成为新的生产环境。然而,如果出现严重问题,您可以选择及时将流量切换回稳定的蓝色环境(版本 1.3),以最小化干扰和风险。
这种系统化的方法确保了应用版本之间的平稳过渡,在整个部署过程中保持系统的稳定性和可用性。
实际场景
实现成功的蓝绿部署策略需要对基础设施和平台内可用的工具有全面的了解。实施方法可能会根据你使用的是基础设施即服务(IaaS)与虚拟机(VM)还是带有负载均衡器的 PaaS 而有所不同。
这里是不同方法的详细介绍:
-
DNS 负载均衡器:在这种方法中,蓝绿环境之间的切换是通过 DNS 重写来管理的。该方法通常用于环境托管在不同的服务器或集群上时。
-
第四层网络负载均衡器:这些负载均衡器通过网络地址转换(NAT)进行切换。这是一种适合在同一网络基础设施中切换环境的选项。
-
第七层应用负载均衡器:在这里,不同应用版本的路由是在应用层进行管理的。该方法提供了更细粒度的流量分配控制,通常用于复杂的部署中。
许多 PaaS 解决方案自带负载均衡功能,简化了蓝绿部署过程。在这些设置中,负载均衡器可以轻松配置以在蓝绿环境之间切换流量。在这种情况下,用户可以仅通过配置来设置切换,而无需意识到负载均衡器的存在。
在更先进的环境中,如Kubernetes(k8s),网络资源作为基础设施即代码(IaC)进行管理,提供动态分配的能力。这使得通过修改 IaC 配置来实现蓝绿部署成为可能,通常通过编辑 YAML 文件来实现。尽管许多 PaaS 解决方案会将网络配置的复杂性抽象化,某些平台,如 Kubernetes,仍然在单一集群内维护网络配置,从而使得同样的蓝绿部署方法可以在集群内无缝应用。
切换方法
那么,我们如何切换环境呢?有几种可能的方式,让我们来看一下:
-
单独的脚本或图形用户界面(GUI)用于负载均衡器配置:一些团队倾向于将切换过程与主要的部署脚本分开处理。这可以通过专用脚本或通过 GUI 完成,从而在切换过程中提供更多的控制和监督。
-
在部署脚本中配置负载均衡:这种方法涉及在部署脚本中直接编写负载均衡器设置的变化。这是一种简化的方式,可以将切换过程作为部署管道的一部分进行自动化。
-
自动化:自动化包括对新版本进行自动化测试,确保只有在新版本成功运行时才会进行切换。这涉及为发布设置特定条件,通常依赖于端到端(E2E)测试和对新环境中的错误进行监控。
需要注意的是,切换时机和现有连接的处理可能会根据所使用的系统和服务有所不同。每种方法都有其独特之处,需要精心规划,以确保平稳过渡,并尽量减少对用户的干扰。
滚动部署
滚动部署是一种用于分布式系统的部署策略,通过逐步在集群的节点上应用更新。该方法减少了停机时间,并确保在更新应用程序或系统时保持高可用性(HA)。
它用于像 Kubernetes 这样的集群中,进行稳定版本发布且不产生停机时间。滚动部署涉及逐步发布,如下图所示:
图 5.10 – 滚动部署
通过这种方式,我们可以安全地逐个更新每个版本。
典型步骤
集群滚动部署的过程通常包括以下步骤:
-
准备工作:在开始更新之前,确保系统准备好进行升级。这包括对关键数据进行备份,并准备好要部署的新版本软件或配置。
-
更新第一个节点:通过选择一个单独的节点开始滚动更新。在应用更新之前,不是将该节点下线,而是在节点仍在服务时应用更新。一个带有更新配置或软件的新实例会与现有实例一起启动。
-
健康检查:新实例启动并运行后,进行彻底的健康检查。这一步骤至关重要,以确保新实例配置正确,能够顺利处理预期的工作负载且没有问题。
-
流量切换:在确认新实例健康后,逐步将流量从旧实例切换到新实例。这可能涉及调整负载均衡器设置或服务发现配置,以指向新实例。
-
移除旧实例:一旦新实例成功处理流量,旧实例就可以安全地从服务中移除。这可以确保没有停机时间,因为服务在更新过程中持续不中断。
-
渐进式发布:继续进行滚动更新,通过逐个节点地重复此过程。逐一更新每个节点,确保每个新实例在停用旧实例之前都能健康运行并处理流量。
-
监控:在更新过程中,持续监控系统是否存在问题。特别关注性能指标和错误日志,以便快速识别和解决任何问题。
-
完成操作:在所有节点更新并删除旧实例后,滚动更新完成。进行最后审核以确保整个系统稳定,按预期运行,并且所有实例都在运行新版本。
实际应用场景
在实际应用场景中,集群滚动部署在连续可用性至关重要的环境中尤为重要。此方法通常用于云计算服务、数据中心和大型网络应用程序。例如,更新 Kubernetes 集群或数据库集群经常使用此策略,以避免停机和服务中断。此外,请注意滚动升级包括应用上下文和基础设施上下文。在 Kubernetes 的情况下,除了集群端升级外,还存在应用升级的相应过程。
可通过在服务运行时进行现场升级完成,但在金融服务等关键服务中,可能与其他发布策略结合使用。这类似于云负载均衡器前面的蓝绿部署和分布式集群本身。
尽管某些情况下确实存在失去滚动升级优势的论述,但这意味着开源项目和服务供应商通常认为的最佳配置实践未必与实际可采用的配置相匹配。
切换方法
滚动部署的方法难易程度因系统是否支持作为特性而异。在诸如 Kubernetes 之类的集群编排器中,可以使用自动编排器推出更新节点并进行健康检查的流程管理器,同时也存在通过手工传统实现滚动部署的情况。
例如,如果单租户服务托管在为个别公司提供隔离环境的环境中,并且每个环境都需要更新,则将客户分层部署并逐步进行更新。虽然这也是一种滚动部署类型,但明确引用 DevOps 实践通常会涉及自动化实践。
集群滚动部署对于在升级或维护过程中维护分布式系统的可靠性和稳定性至关重要。通过顺序更新节点,此策略最小化了系统范围故障的风险,并确保在整个过程中服务仍然对用户可用。
金丝雀部署
金丝雀部署是一种技术,通过逐步向用户的一小部分推出新软件版本,然后再向整个用户群体部署。此方法允许开发人员在实际环境中以最小风险测试新版本。
在金丝雀部署中,新的版本不会一次性发布给所有用户,而是首先向一小部分用户——金丝雀——推出。这个小组充当潜在问题的早期指示器,因此得名金丝雀,类似于过去矿井中用来检测有害气体的金丝雀。通过监控这些初始用户的软件行为和表现,开发人员可以在全面发布前识别并解决任何问题。如图所示,切换是逐步进行的,一旦确认稳定性,范围就会扩大:
图 5.11 – 金丝雀部署
这本质上与滚动部署相同,但在持续时间以及与之相关的活动和目标上有所不同。滚动部署的目的是通过完全替换应用运行基础设施,逐步用新版本替换旧版本,并在短时间内执行健康检查并完全替换。在大多数情况下,这通常在几分钟到几小时内完成。对于大型系统,可能需要几天时间。
另一方面,金丝雀发布需要较长的时间来观察系统如何响应用户行为,并利用这些反馈进行稳定版本的发布。
尽管金丝雀发布有时会与面向用户子集的测试版发布混淆,但需要注意的是,金丝雀发布显然是一种以发布稳定性为主要目标的实践。面向用户子集的测试版发布方法将在功能 标志部分中详细介绍。
金丝雀部署的关键好处包括以下几点:
-
风险缓解:通过限制新版本的曝光,任何负面影响都能被控制,且只影响少部分用户群体。
-
真实世界测试:提供一个观察新版本在真实环境中表现的机会。
-
用户观察:从一部分用户那里进行早期观察,对于在更广泛发布前进行调整至关重要。
-
逐步发布:提供一种受控方式来管理发布过程,降低广泛问题发生的可能性。
然而,它也有其挑战:
-
分组:决定哪些用户应该属于金丝雀组可能具有挑战性。
-
监控复杂性:需要强大的监控工具来有效跟踪新版本的性能。
-
一致的用户体验:确保所有用户都能获得一致的体验,无论他们使用的是哪个版本,这可能很困难。例如,当开发一个新的后端服务时,必须确保它的发布不会影响当前服务的正常运行。
典型步骤
让我们来看看这些步骤:
-
部署:最初,将新版本部署到基础设施的少部分部分,这部分将服务于你的一小部分用户。
-
监控和分析:密切观察应用程序的行为。使用度量指标、日志和用户反馈来评估性能并识别问题。
-
扩展:如果初始部署成功,逐步增加访问新版本的用户比例。继续监控并分析用户反馈和系统性能。
-
全面发布:一旦确认新版本稳定且获得良好反馈,即可进行全量发布,推向所有用户。
-
必要时回滚:如果在金丝雀阶段发现重大问题,迅速回滚更改以最小化影响。
现实世界场景
金丝雀发布将根据用户数量和要部署的服务进行不同的应用。
例如,当微软应用金丝雀发布来部署 Azure 功能时,金丝雀可以从特定的数据中心或数据中心集群中选择。这是因为它能够清晰地缩小受影响的用户范围,最小化影响。这个清晰的用户焦点的另一个好处是可以持续观察情况。当通过部署到特定集群或数据中心来确认稳定性时,下一步是将部署范围扩展到更广泛的目标。
在无法针对特定数据中心或集群进行定位的情况下,金丝雀发布通常以更随机的方式执行,利用云平台的能力。例如,考虑将一部分用户(例如 5%)随机引导到一个新配置的环境中。这种方法采用基于权重的分配策略,最初选择一个小比例(如 5%),然后逐步增加到 10%、25%,最终达到 100%,随着对新环境的信心逐步增强。在此过程中,负载均衡器配置为将定义的用户请求比例分配到暂存环境或新环境中。许多云负载均衡器使用这种基于权重的方法。
为了提高用户体验的一致性,一些服务使用 cookies 来确保来自同一客户端的后续请求被引导到相同的实例。然而,在这种模型下,流量分配的主要方法仍然是随机的。例如,在 5%的用户被引导到新环境且新版本存在错误的情况下,用户遇到错误的可能性是 5%,但所有用户都有这种可能性。
或者,一种更确定性的方法是使用基于头部的路由。这种技术涉及将特定信息插入请求头中,然后利用这些信息来引导流量。通过为特定用户或用户群体进行定向试验,这种方法减少了随机性。这种方法的可行性取决于所使用云服务的功能。
这两种方法都提供了逐步曝光新更新或功能的机制,但每种方法的控制程度不同。例如,DNS 负载均衡器无法读取头部信息,因此它们基本上只是分配权重,但可能能够按国家分配。而七层应用负载均衡器则可以读取头部信息和其他信息。
金丝雀选择方法
在金丝雀部署中,如何管理用户接触新版本的方式至关重要。可以使用多种方法来控制这种曝光:
-
基于权重的方法:这种方法涉及将一定比例的流量分配给新版本。例如,最初,5%的用户可能会被导向新的环境,随着对新版本信心的增加,逐步增加到 25%、50%甚至最终 100%。负载均衡器通常会根据预定义的权重来处理这种流量分配。
-
基于头部路由:这种技术利用添加到请求头中的特定信息,将流量导向适当的版本。这种方法比基于权重的路由更少随机,允许进行更具针对性的测试,例如根据头部标准将新版本暴露给特定的用户组或个人。
-
基于 Cookie 的路由:一些服务使用 Cookie 来确保来自同一客户端的后续请求始终定向到相同的版本(无论是新版本还是旧版本)。这种方法有助于保持一致的用户体验,特别适用于会话持久性。
-
基于地理的分布:在某些情况下,可以根据用户的地理位置分配流量。例如,一些 DNS 负载均衡器可以根据国家或地区不同来路由流量,这对于希望先在特定区域推出更新的全球服务特别有用。
选择正确的切换方法是金丝雀部署中的一个关键决策,因为它直接影响着有效测试新版本的能力,以及在初始发布阶段可能出现的任何问题的应对方式。
金丝雀部署是一种软件发布的战略方法,提供了风险缓解与现实世界测试的平衡。通过逐步引入变化,它使软件部署能够采取更有衡量和信息支持的方法。
我们已经了解了如何部署应用程序以及如何无缝、安全地发布它们。现在,我们已经看到了部署的策略,接下来我们将看看如何在更具功能性的层面进行发布。
功能发布策略
到目前为止,我们主要关注发布基础设施和服务整体,但现在,我们将重点放在发布服务内的功能上。对于功能发布,我们引入了一种发布风格,叫做功能标志和发布列车。
功能标志
功能标志部署是一种在软件开发和交付中使用的技术,涉及在不部署新代码的情况下切换应用程序的某些功能。这种方法允许对功能发布、测试和回滚进行更精细的控制,使软件管理变得更加动态和灵活。
使用功能标志的主要缺点是,功能标志很容易成为技术负担。未使用的功能标志可能会让代码库变得杂乱无章。功能标志为特定的用户或群体启用功能,如下图所示:
图 5.12 – 功能标志
暗启动通常发布给不知道自己正在被测试的用户群体,并且这些用户完全不知道新功能。另一方面,这样的优势在于,它可以应用于任何希望进行此操作的用户群体,而不仅仅是特定的用户类别。
典型步骤
功能标志的实现通常包括以下步骤:
-
功能标志在代码中的实现:开发人员为新功能编写条件代码,由功能标志进行控制。这些标志可以切换开启或关闭,决定功能是否激活。
-
与功能管理系统的集成:功能标志随后与功能管理系统集成,使得能够在不修改代码库的情况下控制和更改标志。
-
测试:在向所有用户启用功能之前,通常会先在有限的用户群体中进行测试,这类似于金丝雀发布。这些测试可以根据用户群体、地理位置或其他标准进行定向。
-
逐步推出:在初步测试后,功能可以逐步推广给更多用户,从而仔细监控其影响和表现。
-
监控与反馈:持续监控对于快速识别问题至关重要。在推出阶段,用户的反馈对于进一步的改进也非常宝贵。
-
全面推出或回滚:根据反馈和表现,功能可以完全推广给所有用户,或者通过简单地关闭功能标志进行回滚。
现实世界场景
功能标志是现代软件团队的重要工具,使他们能够更高效、更有控制力地交付功能。这种方法降低了部署新功能时的风险,并允许更加敏捷的产品开发和迭代。它们的应用遍及软件开发的各个方面,包括 A/B 测试和暗启动。
以下小节展示了这些应用的现实世界示例。
A/B 测试
假设一家电商公司的软件团队可能会使用功能标志来测试两种不同的结账页面设计。他们将创建两个变体:设计 A(当前设计)和设计 B(新设计)。通过使用功能标志,他们可以将设计 B 展示给一小部分随机用户,同时其他用户继续看到设计 A。然后,团队监控关键绩效指标(KPI),如转化率、平均订单价值(AOV)和用户反馈。这些数据帮助他们了解哪种设计在用户参与度和销售转化方面表现更好。根据结果,他们可以决定将设计 B 推广到所有用户,继续改进,或者恢复设计 A,所有这些都不会对整体用户体验造成太大干扰。
暗启动
一个社交媒体平台可能会使用暗启动来测试一个新功能,比如增强版的图像识别算法。他们将该功能暗中发布,意味着它已经上线但对用户不可见。平台随后收集关于新算法在准确性和速度方面与旧算法相比的表现数据,而用户并未意识到这一变化。
这种方法使平台能够收集关于该功能在真实环境中的表现数据,并在将其公开给用户之前解决任何问题。如果新算法表现不如预期,他们可以在不影响用户体验的情况下进行调整或回滚。
功能标志部署使团队能够更有信心和控制地测试和发布功能。通过将部署与发布解耦,它允许更多的灵活性、更快的迭代,并降低软件开发过程中的风险。
发布列车
发布列车是软件开发中的一个概念,强调定期、按计划交付新功能、增强和修复。它协调多个团队和流程,确保发布周期的协调性和可预测性,从而显著提高软件部署的效率和可靠性。如下面的图示所示,发布列车只会发布那些满足截止日期的内容:
图 5.13 – 发布列车
典型步骤
发布列车方法通常包括以下步骤:
-
规划与协调:建立所有团队都必须遵守的发布日程。该日程包括固定的时间间隔(例如,每两周一次或每月一次),在这些时间点发布新功能和更新。
-
开发与测试:团队在定义的时间表内工作,开发各自的功能和修复。采用 CI 和持续测试(CT)来确保代码质量和兼容性。
-
集成与预发布:所有功能、增强和修复都被集成到预发布环境中。这个阶段对于识别任何集成问题至关重要,并确保所有组件能够无缝协作。
-
发布评审:与所有利益相关者进行评审会议,确保发布准备好部署。会议评估发布的质量、功能和潜在影响。
-
部署:一旦获得批准,按照计划将发布部署到生产环境中。部署过程通常是自动化的,以减少人为错误并提高效率。
-
监控与反馈:发布后,持续的监控至关重要,能够快速识别并解决任何部署后的问题。收集用户和利益相关者的反馈,以便进行未来的改进。
现实场景
在大型软件组织中,发布火车方法可以显著简化部署过程。例如,一家开发一系列商业应用程序的公司可能会有多个团队分别处理财务、人力资源和运营等不同模块。通过采用发布火车模型,组织确保所有模块的更新都能够协调一致地发布。每个月,各团队的新功能和更新被整合、测试,然后同时部署。这种同步避免了不同团队独立部署更新时可能出现的复杂性和冲突。
此外,这种方法为客户提供了可预测的更新计划,使他们能够为新功能和更新的采用做出规划。它还允许组织内部更高效地分配资源,因为各团队可以根据发布计划安排工作量。
本质上,发布火车模型促进了一个有纪律、可预测且高效的发布过程,既有利于开发团队,也有利于最终用户。
总结
本章为你提供了将开发工作流程转化为自动化卓越模型所需的知识和技能。通过本章的学习,你从 GitHub Actions 的基础元素,到处于现代软件实践前沿的战略部署与发布技术都有了深刻的了解。要进一步深入,你需要加强对测试与并发的理解。这些不仅仅与管道的构建方式相关,还与测试和集成的哲学息息相关。希望你能继续深入学习。
通过接受本书中所阐述的实践和策略,你现在已经具备了构建高效、强大、安全且符合最高标准的工作流程的能力。希望本章能成为你在不断创新和卓越的过程中始终的伙伴。在接下来的章节中,我们将深入探讨 DevOps 指标、DevSecOps 以及如何扩大协作,探索这些概念如何在提升软件开发与部署效率和安全性方面发挥关键作用。
进一步阅读
-
关于自定义 操作:
docs.github.com/en/actions/creating-actions/about-custom-actions
-
复用 工作流:
docs.github.com/en/actions/using-workflows/reusing-workflows
第三部分:超越 DevOps
本部分详细分析了 DevOps,强调了度量标准的作用,DevSecOps 实践的融入,以及在组织中扩展协作的策略。接着,焦点转向 AI 在软件开发中的集成,探讨了如 GitHub Copilot 等工具,以及使用 AI 辅助编程的最佳实践,包括提示词编写和适应 AI 的编程原则。最后,回顾了 Git、GitHub、DevOps 和 AI 等技术在软件开发中的变革性影响,思考了 AI 对软件工程实践未来的影响。
本部分包含以下章节:
-
第六章*, 丰富 DevOps 实施*
-
第七章*, 利用 AI 加速生产力*
-
第八章*, 反思与总结*
第六章:丰富 DevOps 实施
在本章中,我们采取全面的方法来探讨 DevOps,研究衡量开发过程的关键作用,通过 DevSecOps 融入安全实践的过程,以及如何有效扩展组织内的协作。我们将深入探讨工具(如 GitHub)如何推动可见性和性能,探讨如何将安全无缝融入开发生命周期,并讨论如何培养基于信任的协作文化,强调 InnerSource 原则以有效扩展 DevOps 实践。本章提供了对现代 DevOps 多面性本质的深入理解。
本章将讨论以下主要内容:
-
在 DevOps 中利用指标
-
DevSecOps – 安全作为一个持续问题
-
扩展协作
在 DevOps 中利用指标
在 DevOps 中衡量指标意味着什么?在 DevOps 中衡量指标是指量化软件开发和交付过程的各个方面,以提高效率、质量和协作。指标提供了一个基于数据的方法来评估性能、识别瓶颈并支持决策过程。它们对于持续改进至关重要,使团队能够跟踪目标进展,并使努力与组织目标保持一致。
在 DevOps 中,指标可以分为不同类型。性能指标,如部署频率和变更交付时间,衡量部署管道的效率。质量指标,如缺陷率和平均恢复时间,评估软件的稳定性和可靠性。过程指标则关注开发过程的效率,而人员指标则聚焦于团队的表现和满意度。
工具和自动化对于有效衡量这些指标至关重要。持续集成/持续部署 (CI/CD) 工具,如 GitHub Actions、问题跟踪系统和应用性能监控工具,通常用于收集和分析数据。接下来我们将讨论一些著名的指标测量方法和理念。
四个关键指标 – DORA 指标
DevOps 研究与评估 (DORA) 指标,在 Nicole Forsgren、Jez Humble 和 Gene Kim 合著的影响力书籍《加速》中首次提出,已成为软件开发中的一个关键框架。这些指标,通常被称为四个关键指标,不仅仅是简单的指标;它们提供了一个全面的方法来评估和改进 DevOps 实践。
以下列表有助于理解 DORA 指标:
-
部署频率:这是衡量你成功发布到生产环境的频率。目标是进行定期且较小规模的部署。这种方法能够减少风险,并促进持续反馈和改进的文化。
-
变更的前置时间:此指标跟踪从代码提交到生产部署的时间。通过简化开发和质量保证流程来缩短这一时间,可以提升团队的效率和适应市场变化的能力。
-
变更失败率:此指标评估生产中失败的部署占比。通过改进测试、质量保证和监控,可以减少这些失败,从而提高发布的质量。
-
恢复平均时间(MTTR):此指标衡量从生产故障中恢复的平均时间。制定强健的事件响应策略并投资自动化,可以帮助缩短这一时间,确保系统的可靠性。
以下是许多团队喜爱并使用这些指标的原因:
-
客观洞察:DORA 指标为您的团队表现提供清晰、量化的洞察,支持数据驱动的决策。
-
改进的路径:通过这些指标识别优点和缺点,帮助在 DevOps 实践中针对性地进行改进。
-
行业基准:这些指标使您能够将自己的实践与行业标准进行比较,旨在达到并保持最佳实践。
然而,正如任何指标一样,这些指标只是衡量手段,而非目标本身。请注意以下几点:
-
背景很重要:始终根据您组织的独特背景和目标来解读这些指标。
-
超越数字:记住要平衡这些定量衡量指标与团队士气和客户满意度等定性因素。
-
保持适应性:随着您的组织和行业的变化,您的指标应用方法也应随之调整。
团队通常会根据这些指标被分类为四个表现水平——精英、高、一般和低。最终目标是达到 精英 水平,这代表着一个高效、敏捷且高性能的 DevOps 环境。
DORA 指标为查看和提升您的 DevOps 实践提供了一个宝贵的视角。深思熟虑地应用这些指标可以带来更高效、注重质量且有效的软件开发过程,使您的组织能够在不断变化的技术环境中脱颖而出。
SPACE 框架
虽然 DORA 指标聚焦于 DevOps 的具体操作方面,但由 Nicole Forsgren 博士及其同事提出的 SPACE 框架通过扩展视角,涵盖了 开发者体验(DevEx),为此提供了补充。这种双重方法使得组织能够在追求技术卓越的同时,深入考虑驱动成功软件开发的人为因素。
SPACE 框架由 Nicole Forsgren 博士及其合作者开发,提供了一个多维度的软件开发视角,考虑了超越传统 DevOps 范畴的方面。SPACE是满意度与福祉、绩效、活动、沟通与协作、效率与流畅性的缩写,提供了一种全面的策略,旨在改善开发团队的技术和人文方面。
以下是 SPACE 框架的元素:
-
满意度与福祉:优先关注开发者的心理和情感健康,认识到他们的满意度与福祉直接影响生产力、创新和效率。
-
绩效:重新定义传统的绩效指标,不仅包括速度和产出,还要考虑质量及其对项目和组织目标的整体影响。
-
活动:专注于优化开发者的日常工作流程和任务,使其与项目目标和个人职业抱负相一致。
-
沟通与协作:鼓励开放的沟通和有效的协作对于打破信息孤岛、促进开发项目中的凝聚性进展至关重要。
-
效率与流畅性:这意味着在工作流程中追求一种“心流”状态,让开发者能够顺畅工作,从而提高创造力和高质量产出。
SPACE 框架通过强调技术效率和成功无法完全实现,除非考虑到团队的福祉和满意度,为 DevOps 策略增添了一个至关重要的维度。这一全面的视角是领先组织的关键,它承认积极的开发者体验对于可持续增长和创新的重要性。
以下是将 SPACE 框架集成到 DevOps 中的好处:
-
综合开发策略:通过包括以人为本的方面,SPACE 框架确保了软件开发更加平衡和深入的方法。
-
增强团队动态:关注满意度、沟通和协作可以促进团队更加积极、凝聚力强且高效。
-
多维度基准测试:SPACE 框架使组织能够不仅对其技术表现进行基准测试,还能对其文化和以人为本的实践进行行业标准对标。
将 SPACE 框架与 DORA 指标结合使用,可以更全面地展示一个组织的 DevOps 实践。这确保了在实现操作效率和技术基准的同时,开发团队成功与满意度的关键人文因素也得到了应有的关注。这种双重方法为更成功、更全面和更可持续的 DevOps 战略铺平了道路。
GitHub 的度量标准
在 DevOps 的多维领域中,存在着广泛的指标,每个指标提供不同的方式和见解。本文件专注于 Git 和 GitHub 的协作维度,强调对理解和增强这些平台内团队协作至关重要的指标。掌握这些特定指标的细微差别及其功能对于在 DevOps 环境中有效利用 Git 和 GitHub 至关重要,以确保更加紧密和高效的协作。
GitHub Insights
GitHub 提供了一系列功能,深入分析软件开发的各个方面,帮助团队提升开发效率。它跟踪各个领域的变化,包括贡献、代码频率和仓库健康等。这些功能在理解项目动态、团队协作和社区参与方面发挥着至关重要的作用。
GitHub 指标可以追踪仓库之间、贡献者和团队之间的协作健康状况。此外,它还提供了关于安全性的库依赖关系的鸟瞰图。
以下是 GitHub Insights 功能的简要概述:
- Pulse:Pulse 是一个功能,能够快速概览一个仓库在特定时间段内的活动情况。它帮助团队追踪项目的进展,显示已完成和待处理的事项,包括合并的拉取请求、提议的更改以及已开启或关闭的问题。Pulse 对于提供项目健康状况和动力的快照视图至关重要:
图 6.1 – GitHub Insights 中的 Pulse
- 贡献者:贡献者部分提供了关于谁在为项目贡献以及如何贡献的见解。它追踪个人的贡献,如提交、拉取请求和创建的问题。这个功能对于表彰团队成员的努力和理解团队内工作分配非常重要:
图 6.2 – GitHub Insights 中的贡献者
- 社区:本节通过追踪用户参与度和贡献者活跃度等方面,评估项目社区的健康状况。它鼓励开源最佳实践,帮助确保项目是开放和包容的,这对于培养一个充满活力和可持续的社区至关重要:
图 6.3 – GitHub Insights 中的社区
- 社区标准:本节提供了一个检查清单,帮助维护一个健康和欢迎贡献者的环境。这包括行为规范、贡献指南、问题和拉取请求模板等。遵守这些标准文档是为了建立一个尊重和协作的社区:
图 6.4 – GitHub Insights 中的社区标准
- 流量:流量分析提供了有关有多少人查看和互动仓库的数据。它包括关于克隆、浏览量、访客和来源网站的信息。理解流量有助于评估项目的受欢迎程度和影响范围,为未来的增长和参与策略提供依据:
图 6.5 – GitHub Insights 中的流量
- 提交记录:提交记录功能提供了项目更改的详细历史记录。它允许团队追踪进展、审查更改并理解代码库随着时间的演变。这个功能对于维护一个全面且可追溯的开发过程记录至关重要:
图 6.6 – GitHub Insights 中的提交记录
- 代码频率:该图显示了随着时间推移,代码库中添加和删除的次数。这个可视化图表帮助团队理解编码模式和高活动期:
图 6.7 – GitHub Insights 中的代码频率
- 依赖关系图:依赖关系标签展示了仓库的依赖项以及依赖于它的项目。这对于管理第三方库、理解潜在的安全漏洞以及确保代码的完整性和可靠性至关重要:
图 6.8 – GitHub Insights 中的依赖关系图
- 网络:网络功能可视化了仓库的分支网络,展示了从原始仓库衍生出来的分支和分叉。这可以揭示更改是如何被合并回主项目的,以及社区如何进行贡献和分支:
图 6.9 – GitHub Insights 中的网络
- 分支:分支功能指示了仓库被其他用户分叉的次数。这个指标是衡量项目在 GitHub 社区中影响力和传播范围的重要标志。它可以反映积极的参与度和潜在的协作领域:
图 6.10 – GitHub Insights 中的分支
通过利用这些功能,团队和项目维护者可以全面了解项目的表现、社区参与度和协作动态。这些洞察对于做出明智决策、促进社区成长以及推动开发团队的持续改进至关重要。
这些配置对于社区驱动的开发项目(如开源)以及在企业环境中融入社区文化的内源化(InnerSource)工作尤为有效。可视化贡献提供了一种实际方法,以便了解谁在贡献以及贡献的方式。GitHub 上文档的平衡对于促进团队合作至关重要,强调了管理文档过多或过少的关键作用。然而,需要注意的是,这些见解可能并不适用于所有项目。
根据需求,整合方法论,如四个关键点或结合使用 SPACE 框架,可以提供量身定制的方法,以提高项目成果。
问题指标
测量问题指标是维护健康高效软件开发环境的关键方面。这些指标提供了关于团队解决问题的速度和效率的宝贵见解,这是整体开发者体验(DevEx)的关键组成部分。通过跟踪开放问题的数量、解决问题的时间和问题积压,团队可以评估他们的响应能力和解决问题的能力。这反过来直接影响团队的生产力、满意度以及他们所生产软件的质量。
GitHub Action issue-metrics
,位于 github/issue-metrics
(github.com/github/issue-metrics
),作为一个强大的示例,展示了团队如何自动化并简化跟踪问题相关指标的过程。
以下 GitHub Action 通过在仓库中搜索问题、拉取请求和讨论来衡量几个关键指标:
图 6.11 – issue-metrics 与 GitHub Actions 一起工作
它生成一个以 GitHub 问题形式呈现的综合报告,提供数据的清晰和有序视图。可以使用搜索查询过滤要分析的项目,使该工具适应不同的需求和背景。
以下是可用的指标:
-
首次响应时间:衡量从问题创建到首次评论或审查的时间,提供有关团队对新问题初步响应的洞察。
-
关闭时间:跟踪从问题创建到其关闭的时间,表示问题解决的整体速度。
-
答复时间(讨论):专门针对讨论,这个指标衡量从创建到回答的时间,反映了团队在讨论中的参与度。
-
必须设置
LABELS_TO_MEASURE
环境变量。
利用如issue-metrics
这样的工具可以显著提升团队监控和改进问题处理流程的能力。这不仅提高了开发周期的效率,还在维护积极的开发者体验和健康的团队动态方面起着至关重要的作用。从 GitHub 中可以获取大量数据。让我们来了解一下哪些对你的团队有效。
拉取请求指标
拉取请求指标对于理解代码审查的动态以及团队适应和整合新变更的能力至关重要。诸如合并拉取请求所需时间和已开与已关闭拉取请求的比例等指标为代码审查过程的效率、团队的响应能力以及协作有效性提供了宝贵的洞察。这些指标对于识别瓶颈并推动协作和代码质量的改进尤其有用。由于问题的关闭通常与拉取请求的关闭同步进行,因此在衡量时,你不仅要测量问题,还要测量拉取请求。你还可以使用issue-metrics
GitHub Action 来衡量拉取请求。
在 InnerSource 的背景下,正如在第四章中提到的,InnerSource 指的是组织内采纳开源实践,这些指标具有额外的重要性。InnerSource 指标关注诸如跨团队协作、跨项目代码重用以及不同团队的贡献率等方面。在 GitHub 组织中,团队的概念允许定义属于特定功能或组织区域的成员组。通过分析拉取请求,组织可以衡量在这些定义的团队之外发生的协作程度。这对于评估 InnerSource 计划的有效性尤其重要,因为它反映了跨团队互动和分布式贡献模型。
量化超越团队边界的协作数量是推动 InnerSource 和分布式贡献模型的组织的关键指标。它突出了组织中不同部门如何接受开放协作实践,并可以指出需要更多努力来打破孤岛的领域。
总之,在 DevOps 中利用指标不仅仅是跟踪数字。它是通过数据推动更好的软件交付实践,增强团队协作,并持续改进开发流程的整体健康状况。通过仔细选择和分析这些指标,组织可以创造一个更高效、更响应、更具协作性的开发环境。
DevSecOps – 安全作为持续的议题
在传统的软件开发方法中,安全性通常被 relegated(降级)为最后一步,通常由专门的安全部门处理或外包给外部供应商。虽然这种方法是标准做法,但它导致了安全考虑与核心开发过程之间存在一定的隔阂。安全评估通常在特定的检查点进行,往往会在开发周期的后期识别出漏洞,而此时这些漏洞更难以修复且成本更高。
然而,软件开发和安全领域已经发生了重大变革。在当今这个快速发展的、不断变化的数字环境中,将安全性视为事后考虑已经不再可行。安全问题需要贯穿整个开发过程,而不是在最后阶段才进行补充。这种视角和实践的转变催生了 DevSecOps 的概念。
DevSecOps 代表着在软件开发中对安全性认知和实施的文化与技术上的根本变化。它是一种将安全实践融入 DevOps 流程的方法,使得安全性成为一个持续关注的领域,而非一个独立或最终的阶段。在 DevSecOps 中,安全性不仅仅是一个单独团队的责任,而是一个共享的责任,从项目生命周期的起始阶段起就深深融入其中。
安全性 Shift-left
Shift-left的概念早于 DevSecOps 出现,但在 DevSecOps 框架中得到了显著的接受和完善。传统上,安全性常常是一个独立的关注点,由专门的团队来管理,通常在开发周期的后期处理。然而,在 DevSecOps 时代,这一范式发生了显著变化。安全性不再是专门安全部门的独立责任,而是成为开发团队工作流程中共享和不可或缺的一部分。
在开发周期的早期集成安全性不仅仅是一个程序性变化;它是对如何处理安全性进行的根本性重新定义。Shift-left 的积极姿态在今天的快速发展环境中至关重要,因为它使团队能够更早地发现和解决安全问题。这种方法通常比在部署后修复安全问题更加具有成本效益且复杂度较低。这种积极的策略有几个重要原因:
-
早期发现漏洞:从一开始就集成安全实践,使得团队能够尽早发现并缓解潜在的漏洞。这不仅减少了安全漏洞的风险,还减少了在开发周期后期修复问题的复杂性。
-
成本效益的安全管理:在开发后期或部署后解决安全问题可能会变得非常昂贵。早期集成通过防止安全漏洞升级为更严重的问题,帮助最大程度地减少这些成本。
-
开发实践中的文化转变:Shift-left 提倡文化变革,在这种文化中,安全是整个开发团队的集体责任,而不仅仅是安全专家的关切。这有助于在开发过程中培养更全面的安全观念。
DevSecOps 在实现 Shift-left 方法中起着至关重要的作用。它涉及将安全工具和实践集成到 CI/CD 管道中,确保安全检查和控制成为开发工作流的核心部分。这包括自动化的安全测试、定期的代码审查以发现安全漏洞以及对生产中软件的持续监控。
GitHub 中的安全功能
至于 GitHub 的安全功能,有几个功能因其提高软件开发安全性和完整性方面的有效性而脱颖而出。这些功能包括Dependabot、代码扫描、秘密扫描(包括推送保护)和依赖项审查。这些功能中的每一项都在加强 GitHub 管理项目的安全性方面发挥着关键作用。
值得注意的是,尽管代码扫描、秘密扫描(包括推送保护)和依赖项审查是GitHub 高级安全套件的一部分,但在撰写本文时,它们对于公共代码库是免费的。这种可访问性凸显了 GitHub 致力于为广泛的开发者和组织提供强大安全工具的承诺。通过利用这些工具,团队可以显著增强其安全实践,确保及时发现和解决漏洞。
让我们来看一看每个功能的主要安全特性。
Dependabot——简化依赖管理与安全性
在当今快速发展的软件开发生态系统中,开源软件被广泛使用,确保依赖关系保持最新且安全变得越来越具有挑战性。由于开源世界中更新的数量和频率极为庞大,手动跟踪每个依赖项的更新或漏洞已经变得不可行。这就是 GitHub 集成的 Dependabot 变得不可或缺的地方。Dependabot 作为 GitHub 集成的一个功能,在自动化软件维护方面起着至关重要的作用,确保项目保持安全并且及时更新,且最小化人工干预。它自动化了依赖项的监控和更新,这对于维护应用程序的安全性和完整性至关重要,解决了现代软件开发中快速和持续部署成为常态的一个关键需求。
以下是 Dependabot 如何增强开发工作流以保持依赖项更新:
-
自动扫描漏洞:Dependabot 持续监控代码库中的依赖项,检查是否存在已知漏洞。这不仅包括项目的直接依赖项,还包括传递依赖项(依赖项的依赖项)。
-
自动化拉取请求更新:当检测到过时或存在漏洞的依赖项时,Dependabot 不仅仅是通知你。它更进一步,自动创建拉取请求,将依赖项更新到更新的、更安全的版本。
-
可定制的配置:开发人员可以控制 Dependabot 在其项目中的操作方式。你可以配置检查的频率、包括或排除哪些依赖项,以及如何处理版本更新。
配置 Dependabot 以实现最佳性能
Dependabot 是一个确保项目依赖安全和最新的关键工具。为了最大限度地提高其效率,理解并配置其各种设置非常重要:
-
Dependabot 警报:设置警报,以便及时通知你依赖项中的漏洞。你可以手动生成 Dependabot 拉取请求来解决这些漏洞。配置警报通知确保你能够及时了解潜在的安全问题。
-
Dependabot 规则:此功能允许你管理警报的处理方式。你可以查看并调整这些设置,确保 Dependabot 以与你项目需求相符的方式响应安全问题。
-
Dependabot 安全更新:启用此选项后,Dependabot 会自动尝试为每个具有可用补丁的警报创建拉取请求。为了更精细的控制,你可以禁用此选项,并通过 Dependabot 规则指定你的偏好设置。
-
分组安全更新(Beta):此设置将所有解决 Dependabot 警报的更新按包管理器和目录分组,每个组生成一个拉取请求。对于有效管理多个更新非常有用。
-
dependabot.yml
文件,你可以在其中指定频率、检查的目录和使用的包管理工具等参数。
这是 Dependabot 的配置菜单。你可以启用或禁用每个设置项:
图 6.11 – Dependabot 配置
正确配置 Dependabot 不仅有助于维护项目的安全性,还能确保你的依赖管理过程顺畅,并与项目的工作流程保持一致。
最佳实践以确保最佳采用
在自动化依赖管理的背景下,Dependabot 的作用不仅仅是更新包。为了充分发挥其潜力,团队需要采纳一些关键的互补实践和配置:
-
与测试自动化的集成:Dependabot 自动更新包依赖需要一套健全的自动化测试。这一点至关重要,因为每次依赖更新,尽管可能修复了漏洞,但也可能带来新问题或不兼容性。全面的自动化测试集确保依赖更新引入的任何变化不会破坏现有功能。因此,高测试覆盖率不仅有益,而且是有效利用 Dependabot 的基本要求。
-
高效的通知管理:另一个重要方面是高效配置 Dependabot 通知。如果管理不当,Dependabot 可能会生成大量警报,导致团队成员产生通知疲劳。这种情况常常导致重要的更新被忽视或忽略,从而削弱工具的有效性;为避免这种情况,必须设置合适的通知配置,在保持信息更新和避免过多警报之间取得平衡。
-
培养响应文化:最后,培养一个文化至关重要,在这个文化中,Dependabot 警报能得到及时关注。忽视警报可能导致漏洞累积,从而使自动化更新失效。团队应该优先处理 Dependabot 的拉取请求,或设置与其处理更新能力相匹配的配置。这可能涉及安排可管理频率的更新,或按更新的严重性对其进行分类,从而有效地优先处理。
总之,虽然 Dependabot 显著简化了依赖管理,但其有效性依赖于支持性做法,如高测试覆盖率、高效的通知管理和积极响应的文化。这些元素相互配合,确保自动化依赖更新能够增强而非破坏软件开发过程。
代码扫描 – 实施和利用自动化安全分析
将代码扫描纳入软件开发过程是提升代码库安全性和质量的积极步骤。GitHub 提供了一种有效且用户友好的方式,通过其代码扫描功能实现这一点。此过程涉及自动化分析代码中的潜在漏洞和编码错误,并在早期发现和解决安全问题中发挥至关重要的作用。
开始使用代码扫描
启用代码扫描非常简单。第一步是在 GitHub 仓库设置中启用代码扫描。该设置位于仓库设置中的安全选项卡下:
图 6.12 – 代码扫描配置
代码扫描可以通过 GitHub Actions 执行,或者通过集成第三方工具。GitHub Actions 提供了一种无缝的方式来在 GitHub 环境中自动化代码扫描。对于特定需求或偏好,也可以将第三方工具集成到工作流中。
点击探索工作流,你将进入市场页面并可以查找 GitHub Actions:
图 6.13 – 探索工作流
对于默认配置,点击代码扫描配置菜单中的设置,图 6.12,然后选择默认将显示基本设置界面。你可以配置语言设置并启用 CodeQL(截至目前,支持 JavaScript、TypeScript 和 Python):
图 6.14 – CodeQL 默认配置
设置完成后,将执行初步分析。你可以查看 CodeQL 分析执行的进度,如下所示:
图 6.15 – CodeQL 设置
这将作为 GitHub Actions 运行,但在 .github/workflows/
目录下不会有 YAML 文件,后端会处理并应用设置:
图 6.16 – CodeQL 设置完成
如果扫描结果没有安全问题,代码扫描会在安全选项卡下的页面上显示健康状态:
图 6.17 – 所有工具按预期工作时
如果发现任何漏洞,代码扫描会向你显示详细信息:
图 6.18 – 安全漏洞通知
你可以直接从扫描结果页面创建问题。要在 GitHub 中解除代码扫描结果,首先选择你要处理的警报。然后,选择适当的解除理由:如果警报无效,选择误报;如果警报与未用于生产的代码相关,选择用于测试;如果警报与你的项目无关,选择不修复。这个过程有助于精炼代码扫描的重点,聚焦于相关且可操作的安全警报。
图 6.19 – 安全警报内容
如果你想更详细地配置这些设置,可以在配置页面选择高级而非默认,并使用 YAML 进行自定义设置。如果需要构建分析的语言, 无论用户希望设置多详细,都需要使用高级设置。你将在 GitHub Actions 中看到相同的界面或设置,在那里你可以配置触发器和其他设置,就像为典型的 GitHub Actions 配置一样,并且具有处理更复杂工作流的灵活性:
图 6.20 – CodeQL 高级配置
代码扫描的好处
在 GitHub 中开始使用代码扫描,涉及到在你的仓库中启用此功能并配置必要的工具,可以通过 GitHub Actions 或第三方集成来实现。实施代码扫描的好处是显著的,涵盖了从早期检测漏洞到与开发过程无缝集成,最终帮助建立一个更加安全和健壮的代码库。
代码扫描的好处如下:
-
漏洞和错误扫描:代码扫描的主要功能是系统地分析代码库中的已知漏洞和编码错误。这包括扫描诸如 SQL 注入、跨站脚本(XSS)以及其他常见的安全漏洞等问题。
-
CodeQL 用于语义分析:GitHub 代码扫描功能的核心组成部分是 CodeQL,这是一个强大的语义代码分析工具。CodeQL 将代码解释为一个数据库,使得可以进行更复杂、更彻底的查询,发现简单扫描方法可能忽略的漏洞。
-
与 GitHub 工作流的集成:在 GitHub 工作流中集成代码扫描工具,确保安全检查成为开发过程的无缝一部分,不会导致重大干扰或延迟。
秘密扫描和推送保护
在当今云为中心的软件开发环境中,保护敏感信息(如 API 密钥、令牌或凭证)的重要性不容小觑。GitHub 认识到这一需求,并通过其秘密扫描和推送保护功能,加强了对仓库安全的保障。
在代码中硬编码秘密是一项风险极高的行为。随着开发者越来越多地使用云环境,错误地将关键的秘密信息包含进仓库代码的几率也越来越高。尽管大家都保持警惕,仍然会出现敏感信息错误地上传到仓库的情况。在 Git 的世界中,其核心功能是维护代码历史记录而不是修改它,一旦秘密信息被提交,删除它可能是非常具有挑战性的,有时几乎是不可能的。这样的暴露对安全性的影响重大,可能会导致安全漏洞和敏感数据的泄露。
这就是需要诸如秘密扫描和推送保护等预防措施的显著原因:
图 6.21 – 秘密扫描配置
秘密扫描 – 主动检测暴露的秘密
秘密扫描是一项主动扫描你的仓库以识别暴露的秘密的功能。这个功能可以通过你的仓库设置轻松启用。一旦启用,它会持续监控你的代码库,并在检测到暴露的秘密时,及时向你发出警报。这一即时通知系统使得开发人员和仓库管理员能够迅速采取行动,保护仓库和相关服务,从而防止潜在的安全漏洞。
推送保护 – 防止意外泄露
与秘密扫描相辅相成的是推送保护功能。该功能为你的仓库安全措施增加了一层至关重要的预防措施。当在你的仓库设置中启用时,该功能会在推送操作期间生效。它并不会对所有代码进行全面检查,而是仔细审查被推送的更改,是否包含潜在的秘密或敏感数据。如果检测到此类风险,推送将被阻止,从而防止意外地将漏洞引入到你的仓库中。
秘密扫描和推送保护共同构成了你仓库的全面防御机制。秘密扫描会警惕地识别并提醒暴露的秘密,而推送保护则作为一个防护措施,防止敏感信息在代码库中无意间被包含。这些功能对于维护软件项目的完整性和安全性至关重要,确保你的代码免受不经意的漏洞影响。
依赖审查 – 确保安全且知情的依赖管理
依赖审查旨在检测代码依赖中的安全风险,这些依赖在清单文件中进行了定义。它支持多种编程语言,其中 JavaScript 的 package.json
和 Python 的 requirements.txt
是比较典型的例子。
这一点在拉取请求过程中尤为重要。当对清单文件进行更改时,依赖审查会自动检查这些更改是否引入了任何安全风险。如果检测到任何风险,依赖审查关联的 CI 任务将失败,提示可能的安全问题。将其集成到拉取请求工作流中,可以自动化和积极地确保对依赖项的更新或新增不会危及项目的安全。
拉取请求中的依赖分析
查看依赖审查非常直观;你可以在拉取请求中使用差异模式,通过在编辑过的包清单文件上启用丰富差异设置,直观地验证差异:
图 6.22 – 在丰富差异模式下的依赖审查
此外,依赖审查已无缝集成到 GitHub 的拉取请求流程中。你可以使用 actions/dependency-review-action
(github.com/actions/dependency-review-action
)来实现这一集成:
图 6.23 – 使用 GitHub Actions 进行依赖审查
这种集成使团队能够执行以下操作:
-
审查依赖变更:当一个拉取请求涉及添加或更新依赖项时,依赖审查会提供这些变更的清晰且详细的视图。这包括有关新依赖或更新依赖的包、版本信息,以及这些变更对项目的影响。
-
分析安全影响:该工具的一大优势是能够分析依赖变更如何影响项目的安全性。它会标记出新依赖或更新依赖所引入的潜在漏洞,从而使团队能够主动评估和解决安全风险。
-
做出明智的决策:通过提供对依赖变更及其影响的全面洞察,依赖审查使团队能够做出明智的决策。团队可以评估将新的或更新的依赖项纳入项目的必要性、益处以及潜在风险。
将依赖审查集成到拉取请求流程中,作为一种主动措施,确保所有依赖变更在合并到代码库之前都经过安全性审查。这种主动的做法对于维护应用程序的安全完整性至关重要,特别是在依赖项往往是漏洞来源的环境中。
扩展协作
随着组织的发展,扩展 DevOps 的范围已变得越来越迫切。本节讨论了“扩展协作”的关键概念,这是超越传统 DevOps 实践并采用更具包容性的组织方法的核心要素。
到目前为止,我们已探索了 Git、GitHub、GitHub Actions 和 DevSecOps 世界观,重点关注它们各自和共同的度量标准。下一步是扩展这些协作框架。扩展协作不仅意味着增强 Dev 和 Ops 团队内部的沟通,还意味着将协作的影响力扩展到整个组织范围。这意味着培养一种致力于在所有组织层级做出积极贡献的思维方式。如果我们能够与不同的团队合作,而不仅仅是 Dev 和 Ops 团队,那将是非常棒的。
为什么扩展协作至关重要
在 DevEx 的背景下,随着云架构中微服务的逐步采用,扩展协作的需求愈加明显,这与 DevOps 密切相关。当不同的人相互依赖时,他们的工作可能会被他人的开发进度所影响。我们已经进入了一个需要重新定义如何与拥有不同开发风格和优先级的人进行良好协作,以及如何与团队中的其他成员建立关系的时代。对高级协作策略的需求达到了顶峰。此外,平台工程的兴起突显了技术基础设施管理的复杂性日益增加,进一步加重了团队的认知负担。在这种管理庞大代码库和持续协作的环境中,需要转变观念,将代码管理视为一个共享的组织资源,并且要做到良好的协作。
在缺乏可持续的代码库管理的情况下,组织面临着几个挑战:
-
增加的代码负担:将代码维护的责任集中在少数人身上可能会导致倦怠并产生关键瓶颈。代码维护有时会被忽视。通常情况下,在代码发布后,人力资源减少,维护负担越来越依赖少数几个人。
-
代码重复:缺乏持续的代码管理往往导致错失利用过去工作成果的机会,导致不必要的重复发明解决方案。
-
代码作为负担:忽视代码管理会使代码变成沉重的负担,表现为维护困难,未维护的代码和杂音增多。
在这种情况下,采纳分布式贡献模型至关重要。该模型拓宽了协作的范围,并扩大了其在组织中的影响力。
现在,你需要培养一种协作的组织文化。扩展协作等同于培养更健康的组织文化。当团队成员普遍利用像 GitHub 这样的平台进行开放、透明的协作时,它自然地培养出一种高度协作的氛围。这种文化转型对于使组织更加敏捷、创新和高效至关重要。
总之,在 DevOps 领域中扩展协作不仅仅是为了增强沟通;它是为了拥抱分布式贡献模型并培养开放协作的文化。这种方法是打破壁垒、激发创新以及确保响应迅速和高效的开发环境的关键,为更加统一和高效的组织结构铺平道路。
InnerSource – 分布式协作模型
InnerSource 这个术语由 Tim O’Reilly 于 2000 年提出,代表了公司在软件开发方式上的一个重大转变。它是在组织内部利用开源开发方法论,进行文化转型,向类似开源的共享经济模型过渡。这种方法是一个逐步打破组织壁垒的过程,同时尊重企业独特的文化和内部限制。InnerSource 的目标是打破封闭的组织结构,推动高度透明的协作。它使组织内的工程师能够更轻松、更愉快地工作,而横向的协作则能在组织之间创造协同效应。这种协同效应最终将导致创新和组织的强大。通过 InnerSource,我们所能实现的,不仅仅是打破开发团队的壁垒,更是超越这一点。
正如在 第一章 中讨论的,InnerSource 基于几个关键原则:
-
开放性
-
透明度
-
优先的导师制度
-
自愿代码贡献
这些原则在 DevOps 的背景下有时可能被忽视,但它们本质上已经融入了许多现有的实践中。例如,极限编程 (XP) 涉及到 集体代码所有权(也称为 团队代码所有权 和 共享代码)。平台工程是另一个关键领域,假设避免重复发明轮子。重要的是,深入平台工程通常需要 InnerSource 风格的协作,因为它关注的是分布式贡献,而非单一团队承担所有责任。
但究竟什么才是 InnerSource?一个常见的误解是仅仅使用 GitHub 就等同于实践 InnerSource。事实上,GitHub 是供团队共享代码的工具,使用问题和拉取请求功能使其像开源开发那样运作。然而,正如 InnerSource Commons 的创始成员 Silona Bonewald 指出的,挑战不仅仅是通过 GitHub 增加代码的透明度。真正的任务是要在公司内部找到并培养开源的源泉,将其发展成一个以社区为中心的事业。
这里引用了一本著名的关于 InnerSource 的书中的话。这个引用代表了 InnerSource 的本质。
认为 GitHub 就是实现 InnerSource 所需的全部工具是我们每天要反对的一个观念。大多数人并没有意识到,找到、创建并发展开源社区需要的不仅仅是 GitHub。是社区创造了软件,而不是相反,但大多数大型公司往往缺乏整体社区意识。
– 理解 InnerSource 清单,Siona Bonewald。
使用 GitHub 并不会自动导致分布式协作模式。首先,InnerSource 的概念早于 GitHub,它在这样的平台尚未成为常态的时期就已经出现。InnerSource 从根本上讲是一种文化;它是一种超越工具和平台的思维与协作方式。
有效的 InnerSource 所需的关键仓库功能
这些是启用 InnerSource 协作并促进合作环境的必要仓库功能:
-
可发现性:这意味着使代码库、文档和相关材料易于查找和浏览。首先,这意味着能够搜索,此外,还需要能够发现其价值。不论一个库有多优秀,如果搜索者不知道如何使用它,那它就毫无意义。
-
可组合性:这意味着允许在不同的环境中快速轻松地使用源代码。它需要足够灵活,可以被集成到许多不同的东西中。相比于庞大的单体仓库,简单、功能专一的仓库更受欢迎。当然,如果很多人会使用它,它可以很大,我并不是说单体仓库无法采用 InnerSource。然而,如果每个组件都是松散设计的、易于使用和管理,那会更好。
-
CONTRIBUTING.md
文件。联系某个仓库的所有者或维护者的门槛应尽量低。创建一个热情友好的氛围非常重要。 -
可维护性:这意味着确保代码能够持续维护且至关重要。正如许多人不愿意使用那些已经停止更新好几年的开源仓库,InnerSource 也是如此。人们不会使用那些不再维护的仓库。换句话说,InnerSource 不适合于某些项目类型,即那些有明确截止日期和交接时间、并且明确指出何时停止更新的项目。仓库需要持续维护。如果你在这样的项目中发现了一个有用的 InnerSource 种子,应该找到一种方法,在组织层面继续维护它,而不是停止开发。这些被认为是成功的必要条件。这也与团队文化紧密相关。我鼓励你考虑什么对你的团队来说最合适。
代码所有权
在 InnerSource 中,源代码被视为属于组织。代码属于所有人。然而,这并不一定意味着放弃所有的所有权。
存在一种弱代码所有权的概念。在这种所有权方式下,代码被分配给所有者,但开发者可以更改他人拥有的代码。如果有人想修改别人写的代码,首先应该与代码的所有者进行沟通。
除此之外,还有集体代码所有权的概念,即完全放弃对代码的所有权,但“每个人都拥有代码”的含义是, 同时,“没有人拥有代码”。这可能导致责任和维护连续性的问题。在许多情况下,InnerSource 组织采用弱代码所有权并促进主团队和客团队之间的协作。
一种异步工作方式
使异步协作成为可能是推动组织内部 InnerSource 采用的必要条件。这源自开源开发等情境,在这些情境中,团队分布在世界各地,跨时区的人们协作,但也有许多其他积极的方面。
组织内产生孤岛的原因有很多,其中最突出的可能是内部政治和其他隐藏信息的尝试。
采用 InnerSource 为组织带来了透明性,但完全透明性是通过异步工作方式实现的。事实上,在同步流程中很难确保透明度。那些无法参加会议的人如何看到会议内容并参与决策过程?他们如何在没有记录的情况下后来了解所做决策的背景?
在开源中,大多数对话可以在 GitHub 的问题和拉取请求中找到,并且这些讨论可以追溯到过去。这降低了那些希望参与者的参与门槛,这要归功于文档文化。积累的对话作为一种文档形式,通常被称为被动文档。这样的对话历史非常有价值,因为它们代表了信任的积累。InnerSource 仓库的用户和贡献者有不同的需求、优先事项和参与方式。随着这些人们的合作,拥抱异步协作变得重要,以确保每个人都能参与其中。
InnerSource 项目办公室
在组织层面实施 InnerSource 通常需要一个InnerSource 项目办公室(ISPO),该办公室可能会从传统的 OSPO 角色发展而来,或是开发技术团队的一部分。其职责是无缝地将分布式贡献模型整合到整个组织中。
ISPO 确保以下事项:
-
共享 InnerSource 政策
-
进行辅导和培训
-
提供 InnerSource 战略咨询
-
开发激励模型
-
组织支持活动
-
确保适当的工具支持
评估 InnerSource 的关键指标包括具有来自不同团队的多名贡献者的仓库数量、CONTRIBUTING.md
和README.md
文件的存在,以及分叉和跨团队拉取请求的数量。
InnerSource 模式
内部开源模式(patterns.innersourcecommons.org/
)本质上是内部开源的最佳实践的编码化,以特定格式结构化,便于在不同背景下理解、评估和应用。这些模式详尽地记录在这本书中,代表了由内部开源共同体识别和收集的最成熟和有效的实践。它们作为实施内部开源方法论的宝贵指南,为协作软件开发中常见挑战提供了实用和经过验证的解决方案。
图 6.24 – 内部开源模式
采纳内部开源模式可以显著增加组织内部开源的采纳率和有效性。为了有效地使用这些模式,请执行以下操作:
-
识别相关模式:评估你的组织在内部开源中面临的挑战,并识别解决这些特定问题的模式
-
适应你的上下文:定制模式,以适应组织的独特环境和文化
-
实施和评估:应用模式中提出的解决方案,并持续评估其有效性,根据需要进行调整
有效协作的关键内部开源模式
截至 2024 年 1 月,涵盖了 24 个成熟模式,并在其发展过程中继续增加。内部开源模式提供了解决协作开发中常见挑战的结构化方法。我不会在这里列出它们所有,但我会介绍五种在许多不同组织和情境中被证明有效的典型模式。
这些是我喜欢的,并且是你了解这些模式的良好入口点:
-
信任的提交者:对于接收到持续外部反馈和贡献的项目,这种模式非常有用。它涉及识别和奖励外部合作者的贡献,超越个人贡献,将其指定为“信任的提交者”。该角色包括导师和质量控制等职责,进一步将贡献者融入项目中。
-
内部开源许可证:当同一组织内的不同法律实体希望共享源代码时,可能会涉及法律和会计方面的问题。内部开源许可证为这种共享提供了法律框架,澄清了权利和义务,并促进了组织内合作的新形式。
-
内部开源门户:为了便于发现内部开源项目,可以建立一个内部开源门户。这个企业内网站列出了所有可用的内部开源项目,使潜在的贡献者更容易找到感兴趣的项目,同时也方便项目所有者吸引外部参与。
-
README.md
、CONTRIBUTING.md
和COMMUNICATION.md
使新贡献者能够自主找到常见问题的答案。 -
30 天保修期:这一模式解决了团队在接受外部贡献时可能存在的犹豫问题。通过提供 30 天的保修期,贡献团队承诺在代码集成后修复任何漏洞。这增强了信任,也使得接受外部贡献的可能性更大。
这些 InnerSource 模式提供了促进协作、简化贡献流程和建立信任的实际解决方案。通过实施这些模式,组织可以创造一个更加开放、高效和合作的开发环境。
总结来说,InnerSource 不仅仅是一种方法论,它更是一种文化范式,强调在组织内部进行开放、透明和以社区为中心的软件开发。其成功的实施不仅依赖于 GitHub 等工具,更取决于广泛接受开源原则和实践的文化氛围。
设置 GitHub 以实现可扩展的协作
创建一个支持分布式贡献模型的环境至关重要。这对于 DevOps 和 InnerSource 方法都是如此。然而,优化 GitHub 配置是实现这一协作模式的关键。若配置不当,可能会极大地限制开发工作,尤其是在那些过于强调安全性的企业中,过于严格的配置会妨碍协作。主要的挑战在于找到安全性与协作之间的平衡。项目应当结构化地促进参与,需要合理管理可见性和访问权限。这意味着需要实施一个 GitHub 配置,提供受控的可见性,保护敏感代码,同时促进知识共享与协作。
组织需要在安全优先的视角和协作优先的视角之间找到细微的平衡。这涉及到在管理敏感代码的风险的同时,也要鼓励内部代码共享,以提高生产力和创新。不同的 InnerSource 项目对可见性的需求可能不同,理解这些细微差别对于有效的协作至关重要。
选择正确的配置
在配置 GitHub 时,GitHub 提供了三个配置层次。每个层次分别是 企业级、组织级 和 代码库级。理解这些层次如何相互作用至关重要:
-
企业级:在这一层级上,配置需要小心谨慎。过于严格的配置可能会对开发人员造成摩擦,导致影子 IT 的出现。默认设置的提示往往比完全的限制更有效。
-
组织级别:授权代码库所有者管理可见性和权限往往比从上层强加广泛的政策更为有效。这种方法避免了过于狭隘政策的陷阱,并能够适应不断变化的组织需求。
-
仓库级别:对于敏感的仓库,考虑在某些组织内实施更严格的控制,而不是在所有企业代码中应用统一的限制。
选择适当的基础仓库权限至关重要。诸如读取(Read)权限允许每个企业成员查看每个仓库,这在某些情况下可能是合适的。然而,无政策(No Policy)或无权限(No Permissions)提供了更多的灵活性,允许企业内的不同组织选择自己的基础权限。
内部协作中的分叉与分支
在分布式协作模型中,分叉和分支都是代码开发的有价值方法,各自具有不同的优点。分叉使得不需要仓库所有者许可即可进行快速的、一次性的贡献,适合进行小的修改或临时的贡献。这种方法对于 InnerSource 特别有益,因为它比基于分支的开发提供了一种低摩擦的方式来进行小的贡献。
在分叉(forking)和分支(branching)之间的选择通常取决于项目的性质和 GitHub 实例的配置——内部的或面向公众的。在内部实例中,分叉的风险较低,但在面向公众的实例中,尤其是涉及合规性和监管问题时,分叉可能需要加以限制。重要的是将配置与架构结合,以使分叉成为一种选项,而不增加意外泄露的风险。
GitHub 中的基础权限
GitHub 中的基础权限至关重要,因为它们定义了组织内部的协作环境,并可以在不同的级别进行设置。
-
企业级别:在企业级别,基础权限适用于所有仓库,并定义所有企业成员的默认访问权限。读取权限授予每个成员对每个仓库的读取访问,这在某些环境中是合适的。然而,无政策通常为每个组织提供更多的灵活性,允许企业内的每个组织根据其需求定制仓库权限。谨慎选择这些设置至关重要,以避免过于限制的政策阻碍协作和创新。无政策或读取是常见的选择,允许不同的组织设置从广泛到狭窄的内部共享权限。
-
组织级别:在组织级别,没有无政策选项,选择范围从读取到无权限不等。选择读取可以在组织内进行广泛共享,而无权限则提供更为严格的访问控制,适应组织内不同的共享需求。基础权限的目标是支持低摩擦的仓库创建和大规模读取访问,同时允许仓库所有者在共享其仓库时做出明智的选择。他们应具备灵活性,选择适合仓库中代码类型的共享级别,而不被迫根据 GitHub 组织的典型使用方式来选择广泛或有限的共享。
设置基础权限的最终目标是创建一个支持轻松创建和访问仓库的环境,并赋予仓库所有者自主决定共享级别的权利。这种方法确保仓库的共享方式适应其特定需求和代码类型,而不是受限于广泛的组织政策。
总结
本章首先探讨了 DevOps 指标,并提到了 DevSecOps。诸如 InnerSource 之类的概念,以其与 DevOps 的高度兼容性和能够促进跨团队合作并推动整个组织卓越的能力为人所知。
DevOps 本质上是一种文化,可以通过采纳个体实践、重新评估工具使用情况和适当地设置指标来加以改进。这个改进过程是持续进行的。市面上有各种书籍,建议您进一步学习,找到最适合您团队的 DevOps 形式。
进一步阅读
-
Accelerate: The Science of Lean Software and DevOps: Building and Scaling High Performing Technology Organizations 由 Nicole Forsgren、Jez Humble 和 Gene Kim 编写 (
en.wikipedia.org/wiki/Accelerate_(book)
) -
The SPACE of Developer Productivity 由 Nicole Forsgren、Margaret-Anne Storey、Thomas Zimmermann、Brian Houck 和 Jenna Butler 编写 (
queue.acm.org/detail.cfm?id=3454124
) -
Getting Started with InnerSource 由 Andy Oram 编写 (
www.oreilly.com/radar/getting-started-with-innersource/
) -
Understanding the InnerSource Checklist 由 Silona Bonewald 编写 (
innersourcecommons.org/learn/books/understanding-the-innersource-checklist/
) -
InnerSource Patterns 由 InnerSource Commons Community 提供 (
patterns.innersourcecommons.org/
)
第七章:利用 AI 加速生产力
在本章中,我们将开始探索充满激动人心的 AI 驱动软件开发领域。实际上,尽管大型语言模型在编程中的应用逐渐变得越来越明显,但它仍然主要处于研发阶段。虽然到目前为止我们侧重于实践内容,但本章将把我们的注意力转向有助于与 AI 进行出色协作的理论。我们的目标是提供帮助你正确理解 AI 在开发中的背景,并掌握其使用的洞察力。有了这个基础,让我们一起探索 AI 驱动编程的世界。
AI 在编程中的角色本质上归结于古老的观点——如何编写优质代码,而这一点又依赖于知识、技能和经验。如果你在寻找一种通用的魔法技巧,让 AI 编写出卓越的代码,你可能会发现,实际上这样的技巧可能并不存在。此外,即使是在提到特定产品功能时,也需要意识到这个领域快速发展的步伐可能很快使新获得的技能变得过时。
本书始终关注协作主题。在这个背景下,我们继续保持这一焦点,旨在理解如何与 AI 进行有效的合作。所有沟通的入口都始于了解你的对方。通过正确理解 AI、设定合理的期望,并专注于从 AI 中提取正确的信息,你将能够优化与 AI 工具的互动,无论这些工具如何发展。作为作者,我认为我们应该旨在从根本上理解 AI,而不是单纯关注个别技巧和窍门。
我们将探索与 AI 工具互动的最佳实践,重点关注使用 AI 协助编程的细微差别。AI 驱动的编程代表了一个令人兴奋的前沿领域,充满了许多未知的领域。我们鼓励你掌握基础,并与 AI 一起开始编程。
本章将涵盖以下主要内容:
-
编程中的 AI 创新
-
探索 AI 在编程中的能力与互动
-
最大化 AI 效率的策略
编程中的 AI 创新
OpenAI 推出的 LLM(大型语言模型)标志着软件开发演变中的一个关键时刻。我们将深入探讨这一具有突破性创新的后续影响,探索它如何重塑编程领域。
LLM 对编程的影响
我们可以说,LLM 的出现从根本上改变了编程的方式和执行过程。凭借理解和生成类人文本的能力,这些模型为编码开辟了新的领域,使其更加高效且易于访问。LLM 显著加快了编写代码的过程。开发者现在可以利用 AI 快速生成代码片段,减少在常规或重复编程任务上花费的时间。LLM 的引入使得开发者能够更具创造性地解决编程挑战。通过提供建议和替代方案,这些模型已成为程序员问题解决工具箱中的宝贵工具。
LLM 引入了 AI 驱动开发的新领域,开发者与 AI 工具合作,提升他们的编码工作流。这种合作涵盖了从生成代码片段到为复杂的编码问题提供见解。对于初学者来说,AI 作为一种教育工具,帮助他们学习编码模式和最佳实践。这降低了编程的入门门槛,使其对初学者更加亲近。此外,对于经验丰富的开发者来说,这种 AI 集成是一个强大的催化剂,通过高级代码建议、自动化常规任务和提供更深层次的代码优化和问题解决见解,帮助他们取得更大的成就。经验丰富的开发者的专业知识与 AI 的高效性相结合,创造了一种协同效应,推动了软件开发领域的边界。
现代 LLM 的引入彻底改变了编码,将其从纯粹的手工工作转变为更加协作、高效和创新的过程。这一变化不仅加速了开发,还为软件工程领域的创造力和问题解决开辟了新的可能性。此外,这一领域目前正扩展到各种任务,不仅限于编码,还包括审查和文档编写。
理解 LLM – 基本介绍
在 AI 驱动的编程背景下,LLM 作为一个关键创新浮出水面,重塑了我们对软件开发的方式。那么,LLM 到底是什么呢?让我们先了解一下它。
LLM 是先进的 AI 模型,旨在理解、解释和生成类人文本。这些模型不仅在规模上庞大(通常包含数十亿个参数),而且在训练数据的范围和能力上也极为广泛。
像生成预训练变换器(GPT)这样的 LLMs,是在包含广泛互联网文本的大型数据集上进行训练的。这种训练使它们能够根据接收到的输入预测并生成文本,从而在语言理解和生成方面表现出极高的灵活性。LLMs 背后的核心技术涉及神经网络架构,特别是变换器模型,它们彻底改变了自然语言处理(NLP)领域。这些网络擅长处理序列数据,使它们在语言任务中具有理想的应用前景。
为了充分发挥它们的潜力,理解 LLMs 的基本设计目标和它们的局限性是至关重要的。虽然人工智能看起来像魔法,但它更像是一面反映你自己输入的镜子;它并不是解决所有问题的灵丹妙药。你必须以正确的期望来面对它,正确引导它,并巧妙地提取其中的价值。现在,让我们来看看 LLMs 的几个基本特征:
-
词预测引擎:从本质上讲,LLMs 是设计用来预测序列中下一个单词的复杂引擎。这种预测能力基于它们从大量数据集中获得的广泛训练,使它们能够生成上下文相关且连贯的文本。
-
概率性,而非确定性:与那些对给定输入始终产生相同输出的确定性模型不同,LLMs 是概率模型。这意味着它们基于各种可能的延续的概率来预测接下来会发生什么,从而使得相同的输入可能产生不同的输出。这一特点强调了 LLMs 固有的随机性,突显出相同的“上下文”或输入可能会根据概率性判断产生不同的结果。
-
不是谷歌搜索的替代品:需要特别注意的是,LLMs(大语言模型)并不是谷歌等搜索引擎的替代品。它们不像传统意义上的学习,或者说,它们不会为未来的输出保留信息。每次由典型 LLM 生成的回应,都是基于当时提供的输入,而没有对过去互动的记忆。
-
生成,而非检索:LLMs 的工作方式是每次生成响应,而非从存储的信息中检索。这意味着它们的输出是基于它们在训练过程中学到的模式重新生成的。
LLMs 在编程方面具有重要作用,主要得益于它们预测下一个字符或单词的能力。这些模型不仅能处理像英语这样的自然语言,还能扩展到各种编程语言。事实上,LLMs 在编程语言中的应用,是它们效果最显著且最为人们期待的领域。
LLM 对不同编程语言的适应性来源于其在多样化数据集上的训练,这些数据集不仅包括自然语言文本,还包括大量的代码库。这使得它们能够理解各种编程语言的语法和语义,从而在代码补全、修复 bug,甚至生成完整的功能代码块等任务中变得非常有用。
此外,LLM 还可以帮助开发者将需求转化为代码,提供基于最佳实践的建议,甚至为复杂的编程挑战提供创造性解决方案。它们的预测能力确保能够推荐最相关的代码片段,简化编码过程,显著提高编码效率和准确性。
除了关键能力之外,理解局限性和处理误解也是非常重要的:
-
并非万无一失:LLM 的准确性并非绝对。尽管它们能够生成非常相关和复杂的输出,但有时它们的预测也可能不准确。它们有时会生成看似合理但实际上不准确或毫无意义的输出,这种现象有时被称为幻觉。
-
需要人类监督:这种错误的潜力凸显了人类监督的重要性。LLM 的用户应保持警觉和敏锐,能够识别和纠正模型输出中可能存在的误导或错误。
-
适当使用和设定期望:理解这些局限性是设定现实期望并找到 LLM 最有效使用场景的关键。它们应被视为增强和辅助任务的工具,比如编码或文本生成,而不是作为具有完全自主性和准确性的独立解决方案。
本质上,LLM 是一项强大的创新,在文本生成和语言理解方面提供了显著的能力。然而,它们的有效使用需要意识到其局限性,并认识到人类监督在引导其输出中的关键作用。
LLM 在编码中的应用
LLM 在编码领域的集成最显著的应用之一是 AI 驱动的编码工具,如 GitHub Copilot。本节探讨了 AI 如何重新定义编码体验。
旨在帮助开发者编写代码的 AI 工具利用 LLM 的强大功能提供实时代码建议,自动化一些编码过程,并提高整体生产力。这些工具在大量代码库的训练基础上,能够解读当前编码环境中的上下文,并提供下一行代码、函数实现,甚至是整个类和模块的建议。
这些人工智能工具的基本功能是作为插件工作在编辑器中,如 Visual Studio Code,在你编码时提供人工智能协助。这些工具背后的愿景包括将人工智能融入软件开发周期的所有阶段,特别强调其作为主要功能集成到编辑器中的作用。这种方法代表了一个更广泛的努力,旨在利用人工智能提升软件开发过程,目标是使编码更加高效,并让各个技能层次的开发人员都能更轻松地进行开发。
转变编码过程
基于人工智能的工具通过简化各种编码任务来提升开发过程。传统的编码包括研究、阅读文档以及确保代码的正确性。这些工具帮助在几个关键领域优化这些活动:
-
提高速度和效率:开发人员可以借助这些工具加快编码过程。它们帮助减少重复代码模式所花费的时间,并提供快速的解决方案和建议,从而解放开发人员,让他们有更多时间处理更复杂和创新的工作。
-
促进学习和探索:对于新手或正在学习新编程语言或框架的人来说,这些人工智能工具充当了教育辅助工具。它们提供语法正确的代码片段,并展示最佳实践的实际操作。
-
减轻认知负担:人工智能工具处理编码中更为常规的部分,减轻了开发人员面临的心理负担。这种认知负担的减少使开发人员能够将精力集中在更复杂和具有挑战性的问题上。
-
拓展可能性:通过提供建议,这些工具不仅协助代码完成,还激发了创意思维。它们为开发人员介绍了替代的解决问题方法,并展示了他们可能以前未曾遇到或考虑过的新编码模式和实践。这种可能性的扩展可以促使更具创新性的解决方案,并扩展开发人员的技能。
通过减少频繁查找信息的需要,并提供相关的代码建议,这些人工智能工具支持更加专注和高效的工作流程。这不仅提高了代码质量,还增强了开发人员的生产力,使这些工具成为现代软件开发中不可或缺的组件。
协作式代码创建
在比较代码补全工具与基于聊天的工具时,可以清楚地看到每种工具为开发人员提供了不同的功能。以下是主要的区别:
- 代码补全体验:具备代码补全功能的工具能够在编辑器中直接预测下一个词语或代码块。它们提供逐步的建议,用户可以快速接受或拒绝,从而简化编码过程。这有点像和一位经验丰富的工程师一起进行集体编程,或者和他人进行配对编程并实时共享屏幕,这种互动性和动态性促进了编码环境的活跃:
图 7.1 – GitHub Copilot 中的代码补全体验
- 聊天体验:与此相对,聊天体验类似于通过像 Slack 或 Teams 这样的平台与高级工程师咨询,甚至是委派实现任务。一些工具还具有聊天界面,开发者可以通过这些界面获得直接的代码帮助和对话式指导:
图 7.2 – GitHub Copilot 中的聊天体验
GitHub Copilot,这款以开发者为中心设计的工具,支持聊天和代码补全两种体验。以多功能性著称的 ChatGPT,要求用户精心设计提示语来引导其响应。相比之下,人工智能驱动的开发者工具则通过增强开发者在编辑器中的体验脱颖而出,专注于工程师如何将当前的工作背景与人工智能帮助无缝结合。
它们旨在通过直观地理解开发者工作的背景,支持开发者,并使得开发者可以通过更少的提示语将这些背景信息更容易地传达给人工智能。开发者编写的代码越多,这些工具就越能根据开发者的目标和工作背景的具体情况,量身定制其帮助。
提示语与背景
提示语的概念随着生成性人工智能技术的出现而引起了广泛关注。在你可能遇到的各种术语中,“提示语工程”作为一个特别常见的术语脱颖而出。然而,什么是提示语工程呢?提示语工程是为人工智能模型设计输入或提示语,以生成期望的输出。它是通过设计问题或陈述的方式,来引导人工智能理解并以特定的方式作出响应。这一点至关重要,因为人工智能输出的质量和相关性高度依赖于提示语的结构。然而,另一方面,确实也存在对这一领域过高的期望,早期它甚至被当作一个流行词来使用。
对我来说,提示语工程这个术语似乎是多种事物的结合体。我将在这里解释它们。
两种类型的提示语工程
在人工智能和机器学习的发展过程中,提示工程已成为一门重要的学科,影响着我们与 AI 模型互动以及从中提取价值的方式。提示工程有两种类型。虽然这不是一个学术分类,但我将在这里称之为可重复使用的提示工程和一次性提示工程,它们适用于不同的应用和需求。认识到它们之间的区别,并在日常与 AI 互动中了解并使用这些目标,至关重要。
可重复使用的提示工程
可重复使用的提示工程是为那些在相似情境中反复使用的提示设计的。这在面向消费者的 AI 应用、自动化系统以及 AI 与机器的互动中较为常见。这里的目标是创建能够始终如一地从 AI 中获取准确且相关回应的提示,不管输入或上下文中有多小的变化。
在可重复使用的提示工程中,接近完美的准确性是必不可少的。这一点在机器消费者中尤其重要,因为 AI 的回应会触发其他功能或过程。同样,在 B2C 应用中,高准确性对保持用户参与度、防止用户沮丧或困惑至关重要。
这种类型的提示工程面临的主要挑战是保持 AI 回应的稳定性和一致性。这通常需要深入理解 AI 模型的能力和局限性。构建提示的工程师还必须考虑用户输入和上下文的变化,确保 AI 能够处理这些变化,而不会显著降低准确性。
在可重复使用的提示工程中,重点主要是如何——设计提示以确保可靠且准确的回应。这一重点至关重要,因为什么和为什么往往是不可预测的,尤其是在涉及广泛多样用户群体的情境中。提示必须设计得能够处理来自不特定用户的大量输入,每个用户有其独特的需求和与 AI 系统互动的方式。
在这个世界上,提示工程这个术语在狭义上通常是这样使用的。它是关于如何完善向 AI 提供的指令,以便从中提取高度准确的信息。然而,在实际的开发领域中,并不需要花费时间去完善只使用一次的提示。开发中的提示工程需要的是不同的语境。
一次性提示工程
一次性提示工程的特点是创建一次性使用的提示。这些提示通常由开发者或用户为特定的、往往是独特的情境设计。在这种情况下,重点从广泛适用性和一致性转向了具体性和即时相关性。
这种类型的工程涉及高度的创造性和适应性。开发者会即时创建提示,根据具体任务或问题进行调整。这要求开发者深入理解上下文和目标(即“为什么”和“做什么”),并采取灵活的方法与 AI 互动。
在一次性提示工程中,上下文是关键。提示通常是为了应对特定问题或生成独特输出而设计的。因此,工程师必须为 AI 提供足够的上下文,以便其理解并适当响应当前任务。在开发的背景下,每当你在开发任务中做些创造性工作时,你都需要为 AI 提供新的指令。毕竟,你会发现,当你在开发中使用 AI 时,你几乎不需要掌握每一种类型的提示工程。更重要的是上下文。
总结来说,需要认识到,掌握复杂的提示工程技术并非每个目标都能高效实现。再说一次,对于那些需要新思路并且本质上是一次性需求的日常任务,花大量时间去完善提示可能并非必要。相反,对于像开发一个注入了 AI 的应用程序这样的项目,提示的质量至关重要。在这些情况下,不断优化提示,以确保与 AI 组件的最佳互动是非常有益的。这个过程通常涉及反复试验,并且需要对 AI 模型如何理解不同提示有深刻的认识。
上下文的重要性
在 AI 驱动的开发中,特别是在编程中,一段代码所处的上下文至关重要。上下文包括代码的周围信息和环境,超出了直接的代码库,涵盖了项目规范、编码标准和预期功能。AI 的有效性依赖于其解读和响应这种上下文的能力。
提供给 AI 系统的上下文决定了其回应和建议的相关性与准确性。如果没有足够的上下文,AI 工具可能会生成技术上正确但与项目目标或需求不符的输出。
随着开发者将 AI 整合到工作流程中,理解它们在提供清晰和相关上下文中的角色变得至关重要。这个责任意味着要理解,尽管 AI 强大,但它并非无所不知或无所不能。它需要准确反映当前问题和期望结果的输入。
开发者应将 AI 视为协作工具,带领其通过明确的上下文,以确保其贡献与项目目标一致。这需要对 AI 的建议进行批判性评估,并根据项目的具体细微差别调整这些建议。
以下是需要包括的上下文内容:
-
提供详细注释:在代码中加入全面的注释,不仅解释代码的功能,还要阐明其背后的目的。这有助于 AI 工具理解代码的意图。
-
使用描述性的命名规范:选择能够清晰指示其目的和用法的变量和函数名称。这有助于 AI 生成更相关且可读的代码。
-
彻底文档化代码:确保代码库有良好的文档记录,概述更广泛的项目目标、编码标准以及具体功能。
-
清晰地框定问题:寻求 AI 帮助时,尽可能具体地定义问题。这包括陈述期望的结果以及任何相关的限制或考虑因素。
有效地利用 AI 进行编程需要一种平衡的方法,认识到 AI 是一个强大的助手,但并非完全替代人类专业知识。通过提供清晰、详细的语境并保持关键的监督,开发者可以最大化 AI 工具的效益。这种方法确保 AI 作为提升软件开发生产力和创造力的催化剂,补充人类能力,而非试图取而代之。
在 AI 驱动的编程中,语境的重要性不可过分强调。随着 AI 的不断发展,它解释和利用语境的能力将决定其对软件开发的影响程度。
为了让开发者真正发挥 AI 在编程中的潜力,必须具备深厚的编程和技术理解。尽管提示工程大大提高了生产力,但它并非独立解决方案。提供清晰、详细的语境给 AI 是与开发者技术专长相辅相成的技能。最终,AI 工具在增强开发工作中的有效性取决于开发者在编码方面的基础知识和经验。这种技术精通与巧妙提示工程的结合是最大化 AI 在软件开发中效益的关键。
探索 AI 在编码中的能力与互动
本节致力于为开发者提供深入的见解和策略,帮助他们在编码项目中有效利用 AI,重点介绍其功能和交互动态。无论是将 AI 用于常规的编码任务,还是用于更复杂和富有创意的编程方面,本节旨在为如何使 AI 成为你编码实践中的变革性元素提供全面的指导。
让我们探索 AI 在编码中的广泛能力,并学习与 AI 工具交互的最佳实践,不仅提高项目的生产力和质量,还提升你作为开发者的整体体验。
代码补全 —— AI 驱动编码的基础
与需要在每次交互中提供完整上下文的 ChatGPT 不同,编程环境中的代码补全与代码编辑器深度集成。AI 驱动的编码工具会动态收集你正在编写的代码中的必要数据,并与后端的大型语言模型(LLM)无缝通信。这种集成提供了类似于与 AI 合作的结对编程或集体编程的体验。
典型的 AI 工具会持续分析编辑器中的代码,理解当前的上下文,从而提供相关的建议。这种上下文意识是代码补全有效性的关键。在 AI 工具中,上下文很重要,最重要的是如何从编辑器中收集上下文。有时,人们倾向于关注其背后模型的准确性。这并不算错误;AI 越智能,效果越好。然而,随着 AI 技术的发展,任何工具都能执行某些任务。在这种情况下,真正突出的是它作为数据收集工具的优越性。因此,在代码补全中,了解 AI 驱动的编码助手工具如何从编辑器中收集信息并判断是否应该考虑这些信息,显得尤为重要。
当开发者输入代码时,AI 驱动的编码工具会建议可能的代码片段,以补全或扩展代码。通常,这种功能不仅仅是加快输入过程;更重要的是,它能够提供智能的、上下文相关的建议,从而提高代码质量和工作效率。
这是代码自动补全的一个示例。我们来创建一个文件calc.js
,并在其中写下以下 JavaScript 代码:
function calculateSum(a, b) {
// AI Suggestion Here}
例如,AI 代码补全会补充函数内容,如下所示:
function calculateSum(a, b) {
const sum = a + b;
return sum;
}
代码补全背后的 AI 模型已经接触了大量的代码,但需要澄清的是,这种接触意味着它是“在数据上训练”的,而不是以传统意义上“学习”的方式。实际上,它通过分析这些庞大的代码库,变得擅长于识别编程中独特的模式。通过使用 LLM,模型能够辨别模式、最佳实践和常见的编程范式,从而生成建议。作为预测代码中下一个单词或序列的引擎,它的建议质量直接受输入代码质量的影响。本质上,输出质量反映了它所训练的代码数据的质量,这表明它提供相关建议的能力依赖于识别训练数据中的模式。
代码生成——AI 驱动的编码工具能够理解并响应自然语言的能力——非常了不起。AI 的突破在于它能够理解自然语言的呈现方式,并根据这种理解提供代码建议,而不像传统的非 AI 代码补全工具(如 IntelliSense)那样依赖静态分析。开发人员可以通过注释详细描述代码段的功能、参数和预期结果,从而指导 AI 生成相关代码。
当我们谈论“代码生成”时,它包括了前面部分提到的代码补全,但这里我们讨论的是更广泛的概念,重点是从自然语言和各种信息生成代码。
这个功能的有效性取决于提供的指令的精确性和清晰度。明确定义和具体的注释能够使 AI 驱动的编码工具生成更准确、更合适的代码响应。
这时,你的知识、经验以及构建提示的方式变得尤为重要。必须利用你的批判性思维和逻辑写作技巧,以便 AI 能够理解。
例如,让我们创建一个名为calc.js
的 JavaScript 文件,如下所示,并写入你想实现的注释:
// Function name: calculateAverage
// Function arguments: numbers (array)
// Return type of the function: number
如果 AI 驱动的工具能够生成代码,它将如下所示:
function calculateAverage(numbers: number[]): number {
// calculate the average of the array
const sum = numbers.reduce((a, b) => a + b);
return sum / numbers.length;
}
在代码的其他部分或注释中提供示例是非常有帮助的。此外,AI 驱动的编码工具的一个优势在于它们能够根据结构化的示例生成代码。这一功能在例如从给定数据示例中开发模型等情境中尤为有用。
比如,考虑以下注释:
# [{"id": "A1", "name": "Yoshi Yamashita"}]
然后,AI 可以生成以下模型。如果你在user.py
文件中写 Python,下面的示例将输出 Python,但同样,如果你写入特定语言,如user.js
或user.rb
,则会为不同语言执行相同的实现:
users = json.loads(json_data)
class User:
def __init__(self, id, name):
self.id = id
self.name = name
这表明实现定义与实现本身之间的距离正在变得非常接近。
在定义方面,表定义可以转化为数据库表的 SQL 查询,云基础设施定义可以转化为 Terraform 的 YAML 文件,等等。
例如,从给定的 JSON 示例中,可以生成用于创建 Ruby on Rails 模型的命令。
以下是给定的 JSON 示例:
{
"name": "Yoshi Yamashita",
"age": 48,
"description": "Hello from Tokyo",
"country": "Japan",
"title": "Software Engineer",
"email": yoshiyamashita@example.com
}
你只需提供提示“生成一个 rails 命令来创建一个新用户”作为示例,AI 就会创建一个准备好使用的命令,输出如下:
rails g model User name:string age:integer description:text country:string title:string email:string
代码生成能力代表了 AI 驱动编码的一个重要进步。通过解释描述性注释和结构化示例,AI 可以生成准确且有效的代码,从而减少手动编码的工作量,提升开发过程的效率。
代码解释
AI 具有分析现有代码并自动生成解释的能力,从而阐明代码的功能。这一特性对于理解没有充分文档说明的代码或为其他开发者解密复杂算法非常有价值。这些生成的解释有助于将复杂代码简化为更易理解的部分,从而帮助他人更好地理解代码背后的逻辑和意图。
这种能力对于减少新团队成员的入职时间,加快首次查看代码的人员对代码的理解,以及在规范与实际代码之间存在明显差距的情况下都非常有用。此外,它通过为之前未维护或模糊不清的代码提供解释,从而提升了代码的价值,使其功能更加清晰。
下面是一个通过创建文件eratosthenes.py
并特别考虑生成埃拉托斯特尼筛法算法的解释的 Python 算法编写示例,该算法最初是在没有解释的情况下提出的:
def eratosthenes_sieve(n):
primes = []
sieve = [True] * (n + 1)
for p in range(2, n + 1):
if sieve[p]:
primes.append(p)
for i in range(p * p, n + 1, p):
sieve[i] = False
return primes
如果你在行首写下注释,AI 会识别出你在每一新行写注释,并会通过简单地换行来自动完成其余部分。AI 可以生成以下注释:
# This is a function that calculates prime numbers less than n
def eratosthenes_sieve(n):
# Primes will hold all the prime numbers found
primes = []
# sieve is a list used to track prime numbers
sieve = [True] * (n + 1)
# loop through each number in the range 2 to n
for p in range(2, n + 1):
# If the number is marked as True in the sieve list
if sieve[p]:
# It is a prime number, so add it to the primes list
primes.append(p)
# Mark all multiples of this prime number as False
for i in range(p * p, n + 1, p):
sieve[i] = False
# return the list of prime numbers
return primes
在极端情况下,你不需要任何特定的提示。有时,在使用 GitHub Copilot 时,只需按Tab和Enter即可。然而,如果进行了更多格式化,它可以超越简单的解释,具有各种可能性,例如从代码中推导文档,或者例如从数据库迁移实现中逆向工程表定义。
AI 对代码的解释显著提高了代码的整体质量,通过增强其可读性和可理解性。这一特性在缩小复杂代码与全面文档之间的差距方面发挥着至关重要的作用,提供了一种自动化解决方案,使代码对广泛的开发者更具可访问性和可理解性。通过节省时间并促进更好的代码维护与协作,这一能力突显了 AI 在编码过程中的变革性影响。
最大化 AI 效率的策略
本节内容将深入探讨旨在提升你在编程领域内掌握 AI 工具的策略。通过采纳一种强调具体性、上下文意识和一致性的方法,你会发现与 AI 的互动得到显著改善,从而简化编码流程并提高输出质量。具体来说,提供清晰、详细的指令可以提高 AI 工具的效率,使其更好地与预期目标对齐。深入理解并传达工作背景能够使 AI 生成更精确且适用的建议。此外,保持统一的编码风格并遵循既定的命名约定,会极大地帮助 AI 理解你的代码,最终获得更高质量的成果。这些策略将共同优化你与 AI 的合作,使其成为编程中更高效、更有效的伙伴。
此外,我还想谈谈与 AI 互动的迭代过程。这个过程包括以下内容:
-
请求 AI 提供建议
-
批判性地审查结果
-
决定接受、拒绝或手动调整建议
-
应用变更或反馈进行持续改进(kaizen)
通过在每个与 AI 互动的阶段都牢记这三个原则,你将促进更高效、更和谐的合作。这些做法将传统的软件工程原则与 AI 的创新能力结合起来,确保你的代码既对人类友好,又能优化 AI 的辅助作用。
具体明确
指令的清晰性和具体性在工具的有效性中起着至关重要的作用。
基于 AI 的编码工具旨在响应开发者提供的指令细节。其生成有用且准确代码的能力,在提示或评论具体且清晰时得到极大提升。指令越详细,AI 就越能理解预期结果。这种理解直接影响 AI 工具提供的代码建议的相关性和准确性。
在模糊提示的例子中,开发者可能会用“对这个列表进行排序”这样的语句来指示 AI。这个提示不明确,因为它没有指定列表的内容或排序的方式。面对这种模糊性,AI 可能会很难提供准确的解决方案。然而,当指令更具体时,例如“按升序排序这个整数列表”,就变得更加清晰了。这个具体的提示向 AI 提供了关于列表中数据类型(整数)以及排序标准(升序)的精确信息。有了这些细节,AI 能够更好地生成一段更准确、更相关的代码,符合开发者的意图。
以下两点可以帮助获得更好的结果:
-
根据任务定制提示:在使用 AI 工具时,重要的是根据当前任务定制提示。这包括指定数据类型、期望结果、约束条件以及任何可能影响代码生成的相关细节。
-
避免歧义:具体的指令有助于避免歧义,确保 AI 工具不会误解任务或提供无关的代码片段。
在与 AI 工具合作进行软件开发时,具体指令是一个关键的最佳实践。详细的提示使得这些工具能够提供更准确和有用的代码建议,从而提高开发过程的效率和效果。通过在与 AI 互动时专注于清晰和精准,开发人员可以充分发挥这些工具的潜力,从而带来更高效、更成功的编程体验。
关注上下文
接受上下文意识至关重要。这种方法不仅提高了工具使用的效率,还改善了向 AI 传递信息的准确性。在软件设计中,上下文意识意味着要意识到划定工作、系统和过程的边界。
在考虑到人类和 AI 固有的局限性时,认识到这些边界的重要性尤为突出。简单来说,这强调了两个实体在处理信息时都有有限的能力,必须在适当的上下文中运行才能有效发挥作用。
-
人类的局限性:人类有一个认知阈值。当信息过载时,选择相关信息变得困难,从而导致所谓的认知过载。通过关注当前上下文并在有限的上下文内处理信息,人类可以更有效地管理信息。人类不能向 AI 提供无限的信息,也不能有效地筛选从 AI 收到的大量信息。
-
AI 的局限性:同样,AI 在识别上也有其局限,主要由当前模型的令牌限制定义。令牌是 AI 识别的最小单位,如字符或单词,在撰写时 AI 模型中有其数字限制。虽然 AI 可以继续生成符合上下文的相关信息,但生成最终必须终止,以确保输出的准确性和预期效果,因此需要意识到 AI 的性能边界。
在这里,我提供了一份在与 AI 互动时使用的实用清单。这份清单对于确保与 AI 的有效协作至关重要,重点关注为开发工作提供正确的上下文。养成在每次与 AI 互动时都考虑这些因素的习惯,将有助于你进行更好的互动。
每次与 AI 互动的清单:
-
AI 知道吗?——明确的上下文提供:检查 AI 是否已经熟悉你任务的上下文。如果你的任务超出了 AI 的既有知识范围,提供额外的、详细的上下文以弥补这一差距。
-
AI 能力如何?——评估 AI 的限制:验证你的期望是否符合 AI,如 GPT-4,能够现实实现的范围。了解其能力和局限性,特别是在标记数和上下文扩展性方面,是至关重要的。
-
使用
#file
和#editor
来指定相关的上下文,利用代理特性如@workspace
来扩展上下文,可以提高准确性。请验证你方法的准确性。具体工具的实现不在此讨论,但对于 GitHub Copilot,请参考进一步阅读部分中的最新实现文档。 -
如何优化它?——质量管理:评估并调整发送给 AI 的文本、字符和数据的量。目标是优化信息的数量——增加必要的部分,减少不必要的部分——以确保质量和效率。
上下文感知的重要性不容忽视——它意味着提供恰到好处的信息,并利用提示和编码技术以精确传达意图。AI 驱动的开发工具,如 GitHub Copilot,是工程师的辅助工具,帮助将丰富的上下文传递给 AI,从而提升工具的效用和效果。
反思这一点,不难发现,将这些理念应用于架构和编程并不是一个新概念。这一原则与长期以来已经在实践中的方法论相契合。通过采用领域驱动开发的方法,可以进行上下文感知设计。此外,架构中的松散耦合原则,在不同场景中已有广泛探索,它从特定语言的领域分离演变为面向服务架构,进一步发展为微服务架构。
总结来说,将上下文感知的方法融入到 AI 驱动的编码中,可以视为在 AI 软件开发时代的一种良好策略。归根结底,这种方法的核心是采用良好的、现有的架构实践,这些实践松散耦合、关注边界,并且对人类友好。通过聚焦于综合上下文的整合,使得 AI 工具能够更深入地理解项目,开发者可以提升 AI 生成建议的精准性和实用性。这不仅使开发者能更有效地操作 AI,还使任何人都能更轻松地导航并充分利用 AI 的能力。
保持一致性
在 AI 增强的编程环境中,保持一致的编码风格并采用 AI 可读的命名约定至关重要。本节探讨了这些做法如何增强与 AI 驱动的编码工具的交互,并促进更好的代码质量。
一致的编码风格,涵盖缩进、命名约定和注释写作等方面,在软件开发中至关重要。它不仅确保了代码对人类开发者的可读性,还在 AI 驱动的编码工具如何有效地解释和建议代码方面发挥了重要作用。
例如,以下 Python 代码可以被认为是一种一致的代码:
def calculate_area(length, width):
return length * width
这个例子展示了清晰一致地使用snake_case
命名法和简洁的函数命名,便于人类理解并有助于 AI 解读。
相比之下,下面的代码可能是 AI 的一个不良示例:
def calcSomething(l, w):
# code goes here
在这个例子中,不一致的命名和缺乏清晰度可能导致 AI 给出的建议效果不佳。如果尝试通过自动补全来完成这个内容,AI 可能会针对这样一个简单的例子给出准确的答案,但如果在代码库中散布着无数这样的随机且无意义的标记,就可能会出现错误。
AI 能够理解自然语言和编程语言,表明它不仅在技术语法上阅读代码,还将其视作一种自然语言。这凸显了编程中清晰且有意义的命名约定的重要性。通过以易于理解的方式命名变量和函数,开发者不仅帮助人类理解,还增强了 AI 模型准确辨识代码目的和上下文的能力。
例如,AI 可读的有效命名约定涉及使用特定且具描述性的名称来命名变量和函数。这一做法不仅帮助人类协作者,还使得 AI 工具能够更准确地解读代码。命名中的清晰性有助于减少模糊性,否则这些模糊性可能会导致 AI 系统给出不准确或不相关的代码建议。
具体性和上下文至关重要。避免使用通用名称,并努力提供清晰的上下文,这可以通过类型提示或添加解释性注释等方法实现。这些做法显著提升了 AI 生成建议的准确性,从而导致更相关和更有功能性的代码输出。
到目前为止,很明显,AI 容易理解的代码本身对人类来说也更加易于理解。从本质上讲,AI 在编码中的出现并不总是需要重新发明软件工程中的最佳实践。像 O’Reilly 的 可读代码的艺术:编写更好代码的简单实用技巧 这样的权威资源中列出的原则,在 AI 时代仍然是相关且适用的。保持这些经过验证的实践,确保代码对人类合作者和 AI 工具都保持可访问性和可理解性。
总结
AI 可以帮助你编写代码。然而,你可能已经注意到,无论 AI 有多先进,编码的方法其实变化不大。你所需要做的只是保持你一直以来优秀的工程师身份。另外,善用 AI 将帮助你提升技能。
如果你以好奇心去面对事物,AI 将会做得比你预期的更多,所以让我们与 AI 一起创造美好的未来,我希望这一章能给你一些启发。
深入阅读
-
GitHub Copilot 优化:通过提示设计和上下文 设置(
code.visualstudio.com/docs/copilot/prompt-crafting
) -
可读代码的艺术:编写更好代码的简单实用技巧,作者:达斯汀·博斯威尔(Dustin Boswell)和特雷弗·福舍尔(Trevor Foucher)(
www.oreilly.com/library/view/the-art-of/9781449318482/
)