Git-和-Github-DevOps-潜能释放-全-
Git 和 Github DevOps 潜能释放(全)
原文:
annas-archive.org/md5/49af0d85218130f9ed5d5bc40351ac10译者:飞龙
前言
让我们通过这本必备指南,深入了解 DevOps 的世界。本书专为开发人员、运维专业人员和渴望精通 Git 和 GitHub 的 DevOps 爱好者而编写。这本书不仅仅是理论和教程内容,它是实践智慧和现实应用的宝藏,引导你走上掌握这些现代软件开发和运维工具的道路。
本书的特色:
-
Git 基础揭秘:从扎实的 Git 基础开始你的旅程。了解它在版本控制和协作软件开发中的关键作用,这是任何有志于成为 DevOps 专业人士的基础步骤。
-
探索 GitHub 的高级功能:深入了解 GitHub 的复杂功能。学习它如何促进持续集成/交付(CI/CD)并简化工作流程,弥合开发与运维之间的差距。
-
实践 DevOps 策略:超越基础,结合实际见解和现实场景。见证 Git 和 GitHub 在各个 DevOps 过程中发挥作用,从增强协作到将安全实践无缝集成到你的开发周期中。
《Git 和 GitHub 在 DevOps 中的应用》不仅仅是一本书,它是一个全面的工具包。书中包含了专家技巧、实践练习和有见地的案例研究,是任何努力在动态的 DevOps 世界中脱颖而出的人们的终极指南。
你是否曾经历过这样一种情况,心里想着,“哎呀,我犯了个错误!这太尴尬了”?即使并不是真的很尴尬,难道它没有让你感到有些低落或紧张吗?
尽管我们被建议从错误中学习,但我们都希望尽可能避免错误。
好消息?你失败的次数越多,你学到的东西也就越多。没有失败就没有学习。现在,给你一个坏消息。你将学习的 Git 和 GitHub,本质上是用于记录变化的系统。它们并不是为了轻松隐藏你的错误而设计的。在某些圈子里,精通 Git 是理所当然的,甚至不能使用 GitHub 可能会让你在工程师资格上受到质疑。更糟糕的是,自动化 DevOps 流水线中的错误可能直接导致生产故障。一个小小的失误,可能是更大错误的开端。在一个以协作和快速发展为特征的技术生态系统中,这些工具和方法支撑着现代工程实践。你对 Git、GitHub 和 DevOps 的全面理解是非常值得的。
而这本书正是满足你这种需求的书籍。它赋能开发团队成员在团队内有效沟通,教授现代团队开发的基础,不断改进你的产品。最终,它将显著提升你的开发者体验。
但让我们明确一点,完美是一个神话。然而,卓越无疑是可以实现的。从这个角度来看,这不仅仅是一本关于避免错误的书,更是一本关于拥抱错误并建立一个允许错误发生的流程、工作流和文化的书。它的核心是理解在错误不可避免地发生时,如何反应、适应并成长。这就是一个充满活力的 DevOps 文化的本质。
重要的是,你要开放心态,从自己的失误中学习,与团队公开沟通这些失误,并找到集体的解决方案。你每犯的一个错误并从中学习,都会让你离成为一名不仅仅是合格工程师,而且是无价的团队成员更近一步。
我希望你不仅能将这本书视为一本掌握 Git、GitHub 和 DevOps 的指南,还能视为你职业成长的导师。在翻阅这些页面时,你将遇到真实的场景、实用的练习和深刻的见解,这些将照亮前进的道路。你将学会如何应对团队协作工具的复杂性,如何在共享环境中管理代码,并简化开发过程。
欢迎来到一个持续学习、改进和协作的旅程。欢迎来到 Git、GitHub 和 DevOps 精通的世界。
本书适合的人群
本书是一本为进入 DevOps 动态世界的人们提供的全面指南,特别针对行业内不同角色的需求。书籍的目标读者包括以下群体:
-
即将成为 DevOps 专业人士的你:如果你是第一次参与 DevOps 项目的工程师,这本书就是为你量身定做的。我们理解你对犯错的恐惧。这本指南将帮助你掌握技能,增强信心,熟练地使用 Git 并将其无缝集成到你的团队中。
-
IT 管理员和基础设施工程师:随着行业逐步通过 Git 管理云配置,本书是为习惯于手动管理的基础设施工程师准备的重要资源。它提供了适应这一新范式所需的指导,将你传统的 IT 和基础设施技能与必要的编码和云计算知识相结合。
-
产品经理和产品负责人:这一部分面向的是那些在产品开发中扮演重要角色的非编码专业人士。如果你正在使用 GitHub 进行团队沟通,但对 Git 和 GitHub 的操作知识不足,这本书将为你解开这些工具的神秘面纱。
本书内容
第一章,DevOps 与开发者体验——进入现代开发的世界,介绍了 DevOps 和开发者体验,突出它们在现代软件开发中的重要性。它提供了这些概念的基础知识,包括 Git 和 GitHub 等工具,以及它们在提升开发流程中的作用。
第二章,Git 入门,提供了 Git 的实用介绍,强调了其基本用法以及团队开发中至关重要的沟通方面。内容包括文件管理、分支以及在 Git 驱动的工程环境中的协作原则。
第三章,团队协作的高级 Git 使用,聚焦于高级协作技巧。本章讲解如何管理提交历史、处理复杂分支并解决合并冲突,强调有效的代码库管理策略,以提高团队生产力。
第四章,通过 GitHub 提升团队协作,探讨了 GitHub 在 DevOps 中的作用,超越了其作为代码托管平台的身份。内容涵盖了对于团队协作至关重要的 GitHub 功能,以及从传统系统过渡到现代 DevOps 实践的过程。
第五章,通过 GitHub 推动 CI/CD,深入探讨了 GitHub Actions。本章介绍了其核心概念、工作流优化以及如蓝绿部署和金丝雀部署等高级部署策略,以及功能发布策略。
第六章,丰富 DevOps 实施,全面探讨了 DevOps,讨论了度量指标的重要性、安全实践的集成(DevSecOps),以及在组织内扩展协作的策略。
第七章,通过 AI 加速生产力,聚焦于 AI 在软件开发中的应用。本章深入探讨了 GitHub Copilot 等工具,并介绍了如何借助 AI 协助编程的最佳实践,包括有效的提示语设计和 AI 友好的编程原则。
第八章,反思与总结,反思了 Git、GitHub、DevOps 和 AI 等技术在软件开发中的变革,并考虑了 AI 对软件工程实践未来的影响。
为了从本书中获得最大的收获
本书将涉及在终端中使用命令,以更深入地理解 Git。最好具备初级的终端命令理解能力。
| 本书涵盖的软硬件 | 操作系统要求 |
|---|---|
| Git | Windows、macOS 或 Linux |
| GitHub | Windows、macOS 或 Linux |
你需要一个最新版本的 Git 和 GitHub 账户的工作环境,才能不仅阅读本书,还能真正体验它。
安装 Git 的说明可以在本书的 Git 仓库中找到(下一节中会提供链接)。
如果你使用的是本书的数字版本,我们建议你自己输入代码或通过本书的 GitHub 仓库获取代码(下节会提供链接)。这样可以帮助你避免与复制和粘贴代码相关的潜在错误。
下载示例代码文件
你可以从 GitHub 下载本书的示例代码文件,链接地址为github.com/PacktPublishing/DevOps-Unleashed-with-Git-and-GitHub。如果代码有更新,将会在 GitHub 仓库中同步更新。
我们还提供了来自丰富书籍和视频目录中的其他代码包,网址为github.com/PacktPublishing/。快来看看吧!
使用的约定
本书中使用了多种文本约定。
文本中的代码:表示文本中的代码、数据库表名、文件夹名、文件名、文件扩展名、路径名、虚拟 URL、用户输入和 Twitter 用户名。举个例子:“为了准备发布,会从开发分支创建一个发布分支。”
代码块设置如下:
name: Node.js CI
on:
push:
branches: [ "main" ]
jobs:
build:
当我们希望你注意到代码块中的特定部分时,相关行或项目会以粗体显示:
on:
push:
branches: [ "main" ]
所有命令行输入或输出如下所示:
$ git checkout main
$ git merge --no-ff add-feature
粗体:表示新术语、重要词汇或你在屏幕上看到的词汇。例如,菜单或对话框中的词汇会以粗体显示。举个例子:“通过点击探索工作流,你将进入市场页面并可以查找 GitHub Actions。”
提示或重要说明
以这种方式显示。
联系我们
我们始终欢迎读者的反馈。
一般反馈:如果你对本书的任何部分有疑问,请通过电子邮件联系我们:customercare@packtpub.com,并在邮件主题中注明书名。
勘误:虽然我们已尽一切努力确保内容的准确性,但错误仍然会发生。如果你发现本书中的错误,我们将非常感激你向我们报告。请访问www.packtpub.com/support/errata并填写表格。
盗版:如果你在互联网上遇到我们作品的任何非法复制形式,我们将非常感激你提供相关的地址或网站名称。请通过 copyright@packt.com 与我们联系,并提供相关资料的链接。
如果你有兴趣成为作者:如果你在某个领域有专业知识,并且有兴趣写书或参与书籍的编写,请访问authors.packtpub.com。
分享你的想法
一旦你阅读完DevOps Unleashed with Git and GitHub,我们希望听到你的想法!请点击这里直接进入 Amazon 书评页面并分享你的反馈。
你的评论对我们以及技术社区非常重要,它将帮助我们确保提供高质量的内容。
下载此书的免费 PDF 副本
感谢购买本书!
你喜欢随时随地阅读,但又不能将纸质书籍带到各个地方吗?
你的电子书购买无法在你选择的设备上使用吗?
别担心,现在购买每本 Packt 书籍,你都可以免费获得该书的无 DRM 保护 PDF 版本。
随时随地,在任何设备上阅读。直接从你喜爱的技术书籍中搜索、复制并粘贴代码到你的应用程序中。
好处不仅仅是这些,你还能独享折扣、新闻通讯以及每天送到你邮箱的精彩免费内容
按照以下简单步骤来获取福利:
- 扫描二维码或访问下面的链接

packt.link/free-ebook/9781835463710
-
提交你的购买证明
-
就是这样!我们将直接通过邮件发送免费的 PDF 文件及其他福利
第一部分:Git、GitHub 与 DevOps 基础
这部分关于现代软件开发的内容从介绍 DevOps 和开发者体验开始,强调它们在当今开发领域中的重要性,并提供基础知识和理论。接着,内容提供了 Git 的实操介绍,涵盖了其基本功能,并重点讲解了团队协作中至关重要的沟通方面,包括文件管理和分支管理。最后,讨论深入到 Git 的高级方法,专门为团队协作量身定制,如管理提交历史、处理复杂分支以及解决合并冲突,所有这些旨在提升团队生产力和有效的代码库管理。
本部分包含以下章节:
-
第一章,DevOps 与开发者体验——进入现代开发的世界
-
第二章,Git 入门
-
第三章,面向团队协作的高级 Git 使用技巧
第一章:DevOps 和开发者体验 – 进入现代开发的世界
本章将涵盖两个主要主题。首先,我们将讨论 DevOps 的定义,这是本书的核心主题。接下来,我们将深入探讨开发者体验,这是一种在组织内保持并持续取得 DevOps 成功的战略。在这个背景下,我们还将介绍 Git 和 GitHub 工具。
本章作为指南,帮助即将开始学习 DevOps、工具和 DevOps 协作实践的人们。它将建立关于这些概念和实践的基础知识,并阐明它们如何提升你的开发过程。
本章将涵盖以下主要内容:
-
DevOps – 通过减少摩擦加速开发周期
-
开发者体验 – 一种追求开发者卓越的战略
-
Git – 代码协作的起点
-
GitHub – 人工智能驱动的开发者平台
DevOps – 通过减少摩擦加速开发周期
让我们从学习 DevOps 基础知识开始。
技术领域有很多术语。有时,这些术语可能比较抽象,人们往往对特定框架有不同的理解。DevOps 也不例外。此外,在谈到 DevOps 时,从个人、团队和整个组织的角度来看,如何看待这些组件的相互关系,有时会让人感到模糊。
我们将回顾 DevOps 的基础知识,澄清一些误解,并简要讨论其典型的实践。在深入探讨 DevOps 之前,让我们先聊聊过去的情况。
DevOps 背景
在过去,软件基本上是你安装的东西。因此,对于一个产品开发团队来说,所需的只是能够开发和测试它的工程师。说到测试,一切都集中在发布前消除漏洞。想象一下那时的情况,它有点像早期电子游戏的发布,类似于任天堂娱乐系统(NES)时代。游戏一旦通过卡带发布,就无法进行更新或添加新特性。游戏中存在的漏洞会一直陪伴消费者。然而,随着 Apache HTTP 服务器的出现以及向网络通信的转变,IT 运维部门需要管理这种新范式。
在许多情况下,IT 运维团队专注于 IT 操作。他们的工具、文化,甚至目标,都与开发团队不同。开发团队想要添加新特性,而 IT 团队则把系统稳定性放在首位。这是一个矛盾。
此外,在这两个团队分裂的世界里,当涉及到部署时,就像是将代码扔过墙一样。开发人员将一个可以部署的完美工件抛给墙另一边的 IT 运维团队。然而,出于某些原因,IT 运维团队收到的却是一个不完整、不能在生产环境中正常工作的版本,导致他们在尽力部署和维护的过程中精疲力尽:

图 1.1 – 工程师将代码扔过墙
近年来,技术发展取得了显著进展,其影响已经渗透到软件领域之外,几乎影响了每个行业。为什么会这样?这是因为无论公司规模如何,软件开发在现代企业管理的战略中扮演着至关重要的角色。受到这股技术浪潮影响的公司发现自己处于一个竞争激烈的市场中,焦点集中在为客户提供快速、尖端和可靠的产品。
尽管市场条件和客户需求在不到一年的时间里可能发生显著变化,但一年只发布几次软件显然是不够的。我们不能因为内部孤岛之间的摩擦而延误软件发布。孤岛指的是缺乏与其他部门或系统的集成或沟通的隔离系统、流程或部门,这可能导致效率低下。我们必须找到一种方式来简化从产品开发到运维的整个过程。这就是全新的开发方法论、组织结构和文化的作用所在。
DevOps 作为一种方法论应运而生,正是从这种情境中诞生的。它代表了开发和运维的融合,开发和运维团队作为一个统一的实体协作,共同培养共享文化、优化流程、部署工具,所有这些都旨在加速发布、提升整体产品质量、收集客户反馈,最终为客户提供更好、更快速的服务。这反过来又促进了更有效的市场表现。
什么是 DevOps?
DevOps 这个术语是由 Development(开发)和 Operations(运维)两个词融合而成的。
那么,DevOps 仅仅是促进开发和运维团队之间的协作吗?
虽然这只是故事的一部分,现实往往更为复杂。这两个团队之间通常存在断层,各自有独特的优先事项和目标。仅仅将它们聚集在一起可能会在某种程度上增强它们的关系和相互理解,但这往往不足以实现有效的协作。实质上,DevOps 不仅仅是整合角色或团队;它代表了组织内部更广泛的文化变革。它关乎对齐优先事项、简化工作流程,并最终打破阻碍有效沟通和进展的壁垒。
简而言之,DevOps 是人、流程和工具的结合,旨在持续为最终用户提供价值,并且本质上是一种文化转变:

图 1.2 – DevOps 是人、流程和工具的结合
“我明白了。我理解了。DevOps 是关于人、流程和工具的。”
很多人以为他们已经理解了 DevOps,并开始着手为 DevOps 实施自动化管道。但是,等等。仅仅替换这些元素并不能确保 DevOps 的成功。为了让 DevOps 成功,你需要更多地了解它是什么,它不是啥,以及为什么它存在。
事实上,DevOps 的定义相当广泛。因此,作为 DevOps 扩展形式的 DevOps* 概念,近年来迅速扩展。各种衍生方法,如 DevSecOps 和 BizDevOps,已被提出,导致了对 DevOps 包含内容的多种不同看法。
不幸的是,DevOps 并没有一个被普遍接受的精确定义,如何实施 DevOps 的问题,不同的人有不同的回答。因此,关于 DevOps 的误解和不准确的解释很常见,往往会导致争论。有时,尽管 DevOps 很重要,当谈到实施 DevOps 时,人们却不愿意讨论。

图 1.3 – DevOps 就像房间里的一头大象,常常被忽视
什么是不是 DevOps?
通常,在定义 DevOps 时,与其尝试明确 DevOps 的真正含义,不如指出什么是不是 DevOps。让我们在这里澄清一个典型的误解。
DevOps 并不仅仅是一个工具、技术或产品
市场上有许多可以应用于 DevOps 的工具。许多云服务商和工具供应商通过说“使用这个工具,你可以构建一个 DevOps 工作流”或“这个工具对一个 DevOps 团队至关重要”来推广它们。
当很多人听到 DevOps 这个词时,他们中的一些人会想到云技术,如 Amazon Web Services(AWS)、Azure 和 Google Cloud Platform(GCP),或者像 Docker 和 Kubernetes 这样的技术。此外,Kubernetes 生态系统还包括许多组件,如 Istio、Flux、Helm、Envoy 和 Prometheus。还有一些平台,包括 GitHub Actions、CircleCI 和 Jenkins,促进了持续、快速和频繁的发布。监控工具的世界同样多样化。如果你还不熟悉这些名字,可能需要一些时间来理解它们的细微差别和好处。
然而,即使你掌握了这些工具,并且遵循了著名的架构或成功案例,这也不意味着你已经实现了 DevOps。因为工具背后始终有着人和流程,单纯改变工具并不会改变它们。
实际上,即使使用了以 DevOps 名义品牌化的工具,仍然常见瀑布式开发的情况。通过将多步骤的审批流程插入自动化工作流中,尊重现有复杂的公司规则,限制客户发布为每三个月一次以避免更改会计或安全检查,或让基础设施团队管理由应用团队使用现代编排系统 Kubernetes 构建的平台创建的容器——做这些事情只会增加运营负担和混乱,而无法创造业务价值。
最终,实现显著的商业目标和转型需要在人员、组织和流程上进行改变。那么,DevOps 是否意味着转型个人和组织?
DevOps 不仅仅是特定的个人、团队或角色
DevOps 是现实,但这个术语本身有些像流行词。公司为这些新活动招聘工程师,职称为 DevOps 工程师或 DevOps 架构师,甚至将其团队称为 DevOps 团队。那些认为自己无法做 DevOps 的人,甚至可能犯下试图将 DevOps 外包给 DevOps 合作伙伴的错误。
当你看待这种情况时,可能会觉得 DevOps 指的是特定的个人、团队或角色,但事实并非如此。在许多情况下,这些术语仅仅是指一些角色,如超级基础设施工程师、超级开发人员或只是云工程师。
公司之所以这么做,是因为为了快速响应不断变化的业务需求并实现快速发布,它们需要新的技术和自动化。此外,它们通常还需要管理复杂的平台,这些平台包括现有组件和新组件。这就包括充分利用 GitHub Actions,本书中将详细介绍这一部分。曾经需要手动安装的基础设施领域、通过界面配置或 CLI 操作的部分,现在需要使用 Git 进行版本控制,并对自动化工作流进行配置和管理。
然而,在实践中,DevOps 处理的事务更为复杂。它不仅仅是掌握这些工具和技术,或为它们配置角色。实际上,它需要强有力的领导者,能够在现有系统和组织中引领变革。通常,他们的行动超出了 DevOps 宽泛框架的范围。我所见过的那些真正实现了 DevOps 的人,往往在追求更大的目标,而不仅仅是 实施 DevOps。
DevOps 本质上是一个变革之旅,旨在转型处理各种复杂技术和产品的组织,它并非仅仅是为现有团队增添拥有新技能的人员,或改变团队和职位的定义。
那么,如果 DevOps 不仅仅是工具,也不仅仅关乎人员和组织,那么是否存在一个专门称为 DevOps 的过程呢?
DevOps 不仅仅是一个过程
DevOps 团队通常会在 2-3 周的短周期内进行迭代开发,通常称为冲刺,并每天早晨举行站立会议。这是真的,采用 DevOps 后,你可能会融入符合敏捷流程和 Scrum 最佳实践的流程。这些方法论是在软件开发中将每项功能分成更小的部分,并通过多个短周期推动开发。
那么,DevOps 是敏捷或 Scrum 的进化概念,还是它们的包容性概念?这个问题的答案既是是,也是不是。
DevOps 拓宽了范围,不仅仅关注像敏捷那样的软件开发,还扩展到软件的发布、反馈收集和改进。这些原则和哲学适用于软件开发生命周期(SDLC)的整个过程。团队关注的是产品的整个生命周期,而不仅仅是开发新特性或设计网页组件:

图 1.4 – DevOps 生命周期的八个阶段
然而,DevOps 的最终目标并不是引入一个新的过程本身。
DevOps 有着广泛的实践。具体的实践将在第五章中详细阐述,但并不是所有这些实践都能平等地应用于每个团队。例如,像 Microsoft 这样的环境需要将应用程序部署到多个服务器上,这与将一个小型应用部署到 EC2 实例的情况是不同的。需要应用的实践在用户数为 100 万的服务和用户数为 1000 的服务之间也有所不同。
让我们考虑一下敏捷,引用 Andy Hunt 博客中的内容:
敏捷方法要求从业者思考,坦白说,这很难做到。遵循给定的规则,并声称“按书面要求做”要轻松得多。这既简单又安全,不会受到嘲笑或责备;你不会因此被解雇。尽管我们可能会公开谴责一套规则的狭隘性,但在其中却有安全感和舒适感。但当然,要想敏捷或高效,并非是关于舒适感。
这个思路同样适用于 DevOps。为了能够频繁且迅速地发布以客户为中心的开发,提供可靠的服务,并最终对业务产生重大影响,你需要从比工具、人员和流程更广阔的视角来解决问题。
DevOps 是一种文化
所以,DevOps 不仅仅是开发和运维的融合,具体的角色、流程、工具、技术或产品的组合。人们常常简化它为“人员、流程和工具”。
那么,DevOps 到底是什么呢?
让我们听听 DevOps 社区的杰出人物 Patrick Debois 的话,以求更清晰的理解:
我当前对 Dev*Ops 的定义是:你所做的一切,旨在克服由孤岛效应带来的摩擦……其余的就是普通的工程工作。
在今天的云技术时代,无论是尖端公司、传统企业还是初创公司,都可以轻松访问相同的环境。甚至人工智能驱动的工具也以实惠的价格提供。
最终,阻碍团队和业务发展的正是摩擦,而这种摩擦通常出现在涵盖人、流程和工具的孤岛中。
DevOps 本质上代表了一种文化转变和一种旨在消除组织内部孤岛间摩擦的方法。它意味着思维方式、习惯和文化的转变。
DevOps 原则
那么,DevOps 文化究竟由什么构成呢?让我们深入探讨 DevOps 的核心原则,全面理解这种文化的真正内涵。
以客户为中心
每一个过程中的每一个行动都应该以为客户提供价值为目标。
在构建和维护软件时专注于客户,使得特性交付更快且更符合实际需求。短反馈循环有助于将产品微调以与用户需求对齐,从而降低风险,并以最低成本最大化价值。通过关注客户需求,最终目标变成了高效解决现实问题,这使得任务优先级更容易设定,积压任务得到更好的管理,资源分配更为高效,运营更加顺畅且具成本效益。最终,这为长期的商业成功奠定了基础,更好地满足市场需求和用户期望。
在 DevOps 中,以客户为中心不仅仅是一个口号;它是必不可少的。
从最终目标出发进行创造
理解客户需求并解决现实问题应优先于基于假设的操作。这提倡一种整体战略,团队同步进行开发和运营任务,以满足客户需求。这包括掌握产品的整个生命周期,从产品的诞生、开发到部署和持续支持,确保最终交付物真正能够造福最终用户。
从一开始就忽视客户需求可能导致一个在技术上完美无缺的产品,但在用户满意度和解决问题的能力上却未能达标。这一原则不断提醒我们,将所有技术努力与商业目标和用户期望对齐,确保最终产品不仅具备功能性,而且对用户具有价值和相关性。
跨职能自主团队
成功实施 DevOps 的核心在于形成自治、跨职能团队的不可或缺的策略。与传统的工程设置不同,开发人员、运维和质量保证(QA)专家在独立的隔间中操作,DevOps 倡导打破这些障碍。这里的关键点不仅在于形成多样化的团队,而是使这些团队自主——能够从产品或功能的概念阶段到交付全程负责。
为什么这么重要?答案在于敏捷性和效率。当团队具备从编码、测试到部署和设计甚至对业务有细致理解的一系列技能时,决策速度加快。这取代了层级系统中固有的迟缓和繁琐的命令链,转而形成了及时负责、行动迅速的文化。
消除这些组织瓶颈不仅加快了工作流程,还培养了拥有权和责任感的文化。团队不必等待外部部门或高层做出决策;他们拥有集体技能和自主权来直面挑战。
通过消除组织内常常引起摩擦的隔离,自治、跨职能团队成为顺利实施 DevOps 运营的关键。其结果是简化的流程,便于对变更快速响应,并促进了协作和责任文化的形成。这不仅是现代工程学的一个不可或缺的特性,更是任何企业成功实施 DevOps 所需的基础策略。
持续改进
持续改进是 DevOps 的基石,为现代软件开发和运营提供不可或缺的技术和文化优势。在技术上,通过持续分析性能指标和利用自动化工作流程,它使软件交付过程的可靠性、适应性和效率得以增强。这些特性不仅使最终产品更加健壮,还有助于资源优化和更快的功能交付。从文化上讲,持续改进促进了协作环境,确保责任制,鼓励学习文化,并最终有助于打破组织的隔离。它还战略上与改善开发人员体验相一致。
从反馈循环的角度来看,持续改进的重要性变得更加明确,反馈循环在 DevOps 生命周期中充当着神经系统的角色。这些循环使得实时监控成为可能,并提供可操作的指标,直接为持续改进计划提供数据支持。通过定期评估系统性能、用户参与度以及其他关键绩效指标(KPI),组织能够迅速调整其战略,确保长期成功。在今天快速发展的技术环境中,这种动态的数据驱动方法至关重要,使得持续改进不仅仅是最佳实践,而是竞争生存的基本要求。
自动化
在传统的开发模型中,开发和运维通常是两个独立的部门。开发人员专注于编写代码和构建应用,而运维专注于部署和维护。这种割裂的方式常常导致延误、低效,以及两队之间的摩擦。
持续集成/持续交付(CI/CD)成为连接这两个世界的关键桥梁。通过 CI,来自多个贡献者的代码更改会频繁地合并到一个共享代码库中,自动化测试会迅速运行,以检测错误和不一致之处。这促进了一个更加协作的环境,使得在开发周期的早期就能更容易地发现问题。CD 确保代码始终准备好进行部署,消除了由于发布待定而无法添加新功能的漫长冻结期。
手动流程不仅容易出错,还会大大拖慢速度和效率,这是 DevOps 旨在解决的核心问题。在自动化广泛采用之前,系统管理员需要手动配置服务器,这是一个繁琐且容易出错的过程。此外,开发人员常常发现很难复制操作环境进行测试,导致了臭名昭著的“在我的 机器上能运行”现象。
在这个背景下,自动化不是奢侈品,而是必需品。自动化脚本处理从代码测试到部署的任务,确保过程尽可能标准化,从而消除了许多与手动干预相关的错误和延误。
在 DevOps 实践中飞跃至卓越
到目前为止,DevOps 的基本概念已经被涵盖。事实上,DevOps 的概念一直在不断发展。一些概念被定位为本应最初包括的内容或最初包括但尚未讨论的内容,这些可能会被贴上不同于 DevOps 的标签。
让我们看看这个背景下特别重要的一些理念。这些是有助于将 DevOps 文化融入其中的有益概念。
DevSecOps
DevSecOps 是一种将安全元素整合到 DevOps 框架中的方法。在这种方法论中,安全被认为是开发初期的关键因素。其目标是在及时交付高效且安全的软件的同时,确保软件的安全性。
传统上,安全通常由独立的专门团队处理,通常是在开发周期的最后阶段进行处理。许多情况下,漏洞在开发后才被发现,导致软件发布的延迟。通过采用左移(shift-left)方法来处理安全问题,可以显著降低修复成本。在 DevSecOps 的背景下,左移(shift-left)指的是在软件开发生命周期(SDLC)的早期阶段(理想情况下是在设计和开发阶段)就将安全措施整合进去的做法。通过从一开始就关注安全问题,团队旨在更高效地识别和解决漏洞,从而减少与后期修复相关的成本和风险。左移方法强调主动的安全措施,而非反应式的安全,确保应用程序从设计阶段起就是安全的。在运营阶段出现的安全问题的修复成本可能极高。此外,这种晚期的关注会把客户数据置于风险之中,还可能损害公司的声誉。
根据国家标准与技术研究院(NIST)的数据,在生产阶段解决缺陷的成本可能是 30 倍,而处理与安全相关的缺陷时,这个成本可能会增加到 60 倍:

图 1.5 – 根据检测时间修复漏洞的相对成本图,数据来源:NIST
在 DevSecOps 中突出的一点是将安全视为一个持续状态的概念,而不是一个终点。例如,在过去,通常会在发布或技术采纳时进行静态代码分析,或在安全清单中勾选安全项。然而,随着发布周期的加速和技术的快速发展,每次手动检查所有安全事项变得不再切实可行。DevSecOps 通过使用自动化工具解决了这个问题,确保安全处于持续的状态。这使得在发现新漏洞时能够快速响应。
从这个意义上讲,DevSecOps 可以定义为将安全流程和工具整合到 DevOps 流程中,创造一种文化,在这种文化中,开发人员也能考虑安全,将安全视为一种状态,而非在特定时间点的一个产物,并且创建一个环境,使得流程、环境和数据始终得到保护,以便安全和创新能够兼容。
DevSecOps 对于使用开源软件(OSS)的公司尤其重要。如今,大多数企业都在积极使用某种形式的开源软件,这些软件可能包含未知的漏洞。通过在工作流程中每周检查这些漏洞并融入 DevSecOps 原则,有可能及早发现这些漏洞并迅速进行修正:

图 1.6 – DevSecOps 强调在 DevOps 生命周期中的安全性
总体来说,DevSecOps 的目标是将安全性贯穿于从开发到运营的整个生命周期,促进更安全、更高效的软件开发。这种集成方法使得业务与安全能够并存。
基础设施即代码
基础设施即代码(IaC)是一种通过代码管理和配置系统基础设施(包括网络配置和服务器设置)的方法。这是通过专门的配置管理软件自动化的。传统上,像服务器设置和网络配置这样的任务是由人工手动执行的,遵循程序文件。这种手动方法存在几个问题,包括任务的复杂性和耗时性、人为错误的高风险以及程序文件和实际环境之间可能存在的不一致性。尤其在大规模系统中,这些问题变得尤为严重,使得手动管理不可持续。
采用基础设施即代码(IaC)显著缓解了这些挑战。在 IaC 环境中,基础设施的状态通过代码定义,并由配置管理工具自动应用。这消除了繁琐的手动工作,确保了高度的可重复性和一致性。此外,由于这些工具负责基础设施的自动构建和操作,过程变得更加高效并且可以自动化,从而实现了更可靠、更可扩展的系统管理。
可观察性
可观察性在管理现代软件系统中起着至关重要的作用,特别是在旨在消除摩擦、促进跨组织沟通的 DevOps 框架内。可观察性通常被视为监控的演变形式,但两者各自有其独特的目的。
在传统监控中,重点是观察系统中预定义的元素。从本质上讲,监控是基于规则的。它涉及设置预定的度量标准和基准,当这些标准被突破时,会触发警报。监控关注的是已知问题——它是一个问“我的系统是否按预期工作?”的方法。然而,监控的范围通常仅限于发现问题发生的位置,而不是发生的原因。这可能只是冰山一角。要全面了解问题,可能需要很长时间:

图 1.7 – 监控中可见的区域只是冰山一角
然而,可观察性提供了一种更细致的方法。它不仅仅包含监控,还超越了监控,提供了对系统整体健康状况的深入洞察。在这个模型中,重点不再是监控预定义的问题,而是深入了解系统内部发生了什么。可观察性让你不仅仅停留在表面,帮助你问自己:“系统当前的状态是什么,为什么它会处于 这个状态?”
相反,可观察性依赖于指标、日志和追踪,通常称为遥测数据,提供系统性能的全面且相互关联的图景。在 DevOps 领域,监控不仅仅是运维团队的独立任务;它是一个集体责任,涉及到运维和开发专业人员。随着系统变得更加复杂,尤其是在云原生技术和微服务的兴起下,可观察性的重要性也随之增加。它提供了一种更全面的方式来理解系统不同组件之间的交互,从而更容易发现瓶颈、调试问题和优化性能:

图 1.8 – 可观察性的三大支柱是追踪、指标和日志
传统的监控工具通常是为了满足特定组织结构和需求而创建的,导致解决方案的格局有些碎片化。然而,云原生环境需要一种更集成的方法。可观察性工具旨在提供这种集成,提供跨多个环境的系统状态的统一视图。这种全面的理解使 DevOps 团队能够简化流程、降低风险,并更有效地为组织的目标做出贡献。
下一个挑战
你现在已经理解了 DevOps 的基本概念。本节内容涵盖了它是什么以及它不是什么,同时也讨论了 DevOps 文化。你还回顾了构成 DevOps 的各个领域,例如 DevSecOps、基础设施即代码(IaC)和可观察性。
那么,假设你已经在 DevOps 方面取得了成功,或者你正在朝着成功的道路前进并为此感到自豪。那么,你的下一个挑战是什么呢?也就是说,“DevOps 本身如何在组织中继续取得成功并发展?”
可能会发生这样的情况:“我们有一个很棒的 DevOps 团队。它还不完美,但文化已经发生了很大的变化,我们的合作非常顺畅!” 就在你以为一切都会变得完美时,成长起来的工程师们却纷纷离开,他们在寻找更好的工作环境。
在许多情况下,团队中最有技能、知识面最广的工程师往往在幕后做着最复杂的操作。最有价值的工程师可能最终会去做那些他们可能并不真正想做的琐碎任务。
事实上,大多数组织并没有为开发者提供一个能够高效且愉快工作的环境。可能是工具的问题,或者是大量无关紧要的任务和过多的工具。
这些仅仅是一些例子,但为了保持 DevOps 的最佳状态并进一步发展,一个组织需要从长远来看考虑其开发人员。无论你是个体贡献者还是经理,你都有责任确保你的团队成员对他们的工作感到满意,这样你才能拥有最优秀的团队。如果你体现了良好的开发体验,你的团队成员也会给你带来良好的体验。
回到这里的第一个问题:“DevOps 本身如何才能在组织内持续成功并发展?”
一个答案是让开发者感到快乐——换句话说,创造一个开发者能够做出最佳工作的环境。这被称为开发者体验。
开发者体验——推动开发者卓越的战略
开发者体验 这个术语超越了单纯的用户体验,涵盖了 DevOps 团队中的开发者如何在工作中保持高效并获得满意感。它也作为一种组织策略,帮助 DevOps 成功。如果工程师们感到愉快,组织内没有孤岛,沟通流畅,你的 DevOps 就会成功。在这方面,GitHub 基本上是一个最大化开发者体验的平台,而 Git 则是实现这一目标的工具。

图 1.9 – 开发者体验是成功 DevOps 的关键策略和基础
当你从 DevOps 领域中的各个工具来看,它们可能显得很小。然而,从维持和实现 DevOps 成功的角度来看,开发者体验与如何使用 Git 和 GitHub 之间的联系会变得显著强烈。因为正是在那里,开发者之间的核心沟通正在发生。
开发者体验是一种策略
开发者体验本质上是创造一个开发者能够做出最佳工作的环境。如果从 DevOps 的角度来定义,开发者体验可以被解读为一个策略,用于创造、发展和维持作为一个组织的最佳 DevOps 文化。DevOps 本质上关乎文化。如果你的开发团队不满,别指望会有繁荣的文化。没有繁荣的文化,DevOps 是不会成功的。
根据 GitHub 的说法,开发者体验的概念可以通过以下公式表示。这个公式可以在文章《开发者体验:它是什么,为什么你应该关心?》中找到 (github.blog/2023-06-08-developer-experience-what-is-it-and-why-should-you-care/):

图 1.10 – GitHub 的开发者体验公式:GitHub 上开发者体验的表现方式
公式中的每个元素可以解释如下。
-
开发者生产力:这代表了效率和速度。换句话说,它反映了开发者完成任务的效率和速度。
-
开发者影响力:这包括影响力、实施代码更改和从想法到生产的转变。它展示了开发者的影响力有多大,以及他们能多快将一个想法转变为真实的产品或服务。
-
开发者满意度:这意味着在工作环境、工作流程和工具中实现高影响力、低摩擦。本质上,它衡量的是开发者对自己工作的满意度,他们的工作流程有多顺畅,以及他们使用的工具有多有效。
此外,这些元素会通过协作得到放大。团队内合作和沟通越好,开发者的生产力、影响力和满意度水平就越高。现在,让我们来探讨这些元素如何作用于 DevOps 基础。
开发者生产力
提升开发者生产力是一项复杂的挑战。DevOps 作为一种催化剂,有助于平滑组织内的低效现象,并提升开发团队的输出。
当今的开发者肩负着繁重的责任:编写代码、进行代码审查、架构规划以及偶尔的基础设施部署。其中一些任务发生在组织或团队层面,而另一些则是个人努力。DevOps 显然强调合作,但同样需要关注提升个人表现——这是一个需要精心监控的动态。开发者体验也涵盖了这一部分内容。
目前,许多企业由于缺乏熟练的工程师而面临困难。但请考虑一下:在一个有 100 名开发者的组织中,如果每个开发者每天能节省 10 分钟时间,那就相当于增加了将近 17 个小时。这几乎相当于增加了两个额外成员。
然而,衡量单个工程师的生产力是一项具有挑战性的工作。尽管诸如代码行数或提交次数等量化指标看似可靠,但它们往往未能提供全面的视角。有时候,一行经过深思熟虑的代码可能比 10 行匆忙写就的代码更有价值。像研究和架构设计这样的任务通常没有被衡量,这进一步加剧了评估的复杂性。单纯依赖这些狭隘的指标可能无意间培养出微观管理的文化。当这些指标成为评估标准时,就可能促使一些行为侧重于噪音而非实际价值。这些指标在第六章中有所介绍。
在开发者生产力的背景下,一个视角可能是将 AI 应用到各种开发场景中。将 AI 融入开发流程标志着一个变革性的时代。像 GitHub Copilot 这样的工具作为 AI 驱动的编程助手,支持代码编写过程,从而降低了时间和成本。自从 2022 年 11 月 ChatGPT 发布以来,AI 在开发中的影响力正在以前所未有的速度扩展。
开发者的影响
在 DevOps 领域,关注点通常集中在团队如何影响客户和整体业务。该领域的度量指标通常从产品或客户入手,比如衡量部署的频率。与此相对,开发者体验聚焦于开发者,强调他们对代码库以及最终投入生产的想法的影响。
那么,开发者生产力和开发者影响力之间的区别是什么呢?对于生产力,成功的衡量标准是用更短的时间做更多的工作。另一方面,理解如何衡量影响以及应当衡量什么是非常困难的。而且,不同的团队对影响的定义各不相同。
最容易识别的事情是能够量化为对最终产品价值的贡献。例如,如果一个团队正在开发一个新功能,那么其影响将体现在新功能带来的用户数量和收入。此外,其他例子还可以包括下载量、请求、服务级别协议(SLA)等。
我相信,当创造出良好的 DevOps 文化时,工程师会变得更加快乐。然而,有些人可能会想:“归根结底,这一切不就是为了钱吗?” 事实上,衡量开发者的影响力确实与钱有关。
如果工程师的影响没有被正确衡量,且薪酬未能与其市场价值相匹配,那么工程师对工作的满意度将会下降。接下来的情景应该不难想象。许多组织正变得越来越复杂,影响是间接的,但即使是现在,也没有很多公司能够衡量这种间接影响。
这最终与 DevOps 的原则非常契合。毕竟,如果要衡量开发者的影响力,就需要了解客户和客户在产品中的状态,并以“从目标出发,创建”的心态来创造产品。
衡量开发者影响力的指标有很多,但最容易获取的包括开发者关闭拉取请求所需的时间,以及从收到客户反馈到实际实施之间的时间跨度。
开发者满意度
最重要的是,在这个背景下,开发者生产力及其影响并不是让管理者去管理并指出个别开发者的生产力,而是帮助开发者以更加积极的方式加速其开发进程。
组织需要确保通过提供最佳的环境、工作流和工具,让工程师通过开发和为客户交付价值来感到满意。
在更广泛的组织层面上,在整体优化的背景下,有必要确保环境能够广泛地为所有开发者的生产力提升做好准备;而在更狭窄的组织层面上,则需要根据团队、角色和经验制定提高开发者生产力的策略。这包括通过 DevOps 实践减少团队瓶颈,从而最小化周期时间,同时为开发者提供必要的工具,以便他们能最大化地发挥潜力。
协作
在加强开发者体验以实现 DevOps 成功时,协作是关键。它不仅仅是将开发团队和基础设施团队整合并共享一些责任;更重要的是创造一个能够让开发者蓬勃发展、贡献和感受到拥有感和参与感的环境。理想的状态是透明的协作,在这种状态下,组织之间的壁垒不仅仅是减少,而是完全消除。这种思想、最佳实践和建设性批评的自由流动,创造了一个有利于开发者体验的工作环境。
例如,有人说 DevOps 不仅仅是工具和实践;它是一种文化、一种哲学,一种旨在实现商业和组织成功的理念。在这种背景下,Git 和 GitHub 经常被边缘化为 仅仅是工具 或 沟通手段。这种观点有时是对的,但往往目光短浅。采纳这种观点可能会限制你对 DevOps 中 Git 和 GitHub 的理解,可能仅仅局限于如何使用 GitHub Actions 实现 CI/CD 或者如何在学习 Git 和 GitHub 时使用 Git 和 Git 分支策略。
在组织或团队中工作时,团队由各个贡献者组成,例如开发者。尤其是当开发者在团队或组织中工作时,沟通变得极其复杂。成功的对话最终是提升团队或个人生产力的关键。Git 和 GitHub 不仅仅是工具,它们是共享代码、获取反馈、进行整合和成长的平台。在这种环境中良好的沟通能力对 DevOps 的成功至关重要。
促进这种协作的最强大范式之一就是 InnerSource。借鉴开源协作模型,InnerSource 赋能组织内部的开发者参与其他团队的项目,即使他们并非该团队的正式成员。就像开源开发一样,InnerSource 鼓励开放沟通、代码共享和集体问题解决,有效减少了常常困扰组织的孤岛现象。
开源协作模型本身就是打破障碍后能够实现的成果的证明。通过将代码库开放给公众,开源项目能够接触到全球开发者社区,每个开发者都为项目带来了独特的视角和专业知识。个人成长和集体进步之间存在一种互利的关系,这是 DevOps 团队应当在内部努力实现的共生关系。这个开放模型带来了透明文档、同行评审和民主化问题解决的核心理念,极大丰富了开发者体验。
透明度是这种协作的关键。当从初级开发者到团队负责人,所有人都能访问讨论、查看代码库,并参与决策时,角色和部门之间的壁垒开始崩塌。这种缺乏孤岛的状态自然会导致问题的更快识别、更高效的解决方案以及更统一的愿景。
放大 DevOps 和开发者体验的元素
有一些理念可以帮助你的 DevOps 更加成功。其中之一就是采用去中心化的贡献模型和 平台即产品(PaaP)的概念。没错,正是 InnerSource 和平台工程。这些都会提升开发者体验。
InnerSource – 去中心化贡献模型
InnerSource 从开源世界中汲取灵感,但它旨在通过在组织边界内应用开源实践来解决内部工程挑战。它允许跨团队合作与技术专长的共享,最重要的是,它旨在创造一个更透明和包容的文化。这种方法与 DevOps 的哲学紧密契合,旨在消除组织壁垒,促进合作和透明的文化。InnerSource 还旨在推动文化转型,这种转型对提升开发者体验至关重要。
定义 InnerSource
本质上,InnerSource 是一种在单一组织内部进行的软件项目协作方式。这个概念由像 PayPal 这样的公司推广,旨在解决大型组织中常见的孤岛效应和边界问题。InnerSource 鼓励一种开放的文化,任何人都可以为任何项目贡献代码,且流程和决策过程都是透明的。
InnerSource 的四大支柱
让我们深入探讨四个基础原则——开放性、透明性、优先指导和自愿代码贡献:
-
开源仓库中的
README.md和CONTRIBUTING.md,使得项目更加易于发现和访问。这种开放性提高了开发者的体验,通过减少摩擦,使得工程师可以轻松地切换上下文或团队。例如,即使在 DevOps 环境中有大量微服务,拥有这种文档文化也有助于促进协作。 -
透明性:当我们谈论 InnerSource 中的透明性时,指的是项目决策过程中的开放性。例如,问题讨论和拉取请求评审都是透明进行的,通常会被记录并且易于访问。这为我们提供了关于为什么做出某些决策、是谁做出的决策以及决策背景的洞察。透明性不仅提高了项目的质量,还通过创造一种归属感和参与感,显著提升了开发者的体验。
-
优先指导:在 InnerSource 中,指导并非附带任务,而是优先事项。在 InnerSource 中,仓库维护者的角色也被称为可信承诺者,这考虑到了与内部限制相关的活动差异。
可信承诺者的角色在这里至关重要。可信承诺者不仅仅是代码贡献者;他们还是项目的倡导者,专注于代码的质量和可持续性,并且指导新贡献者。他们对仓库和组织有深刻的理解,充当两者之间的桥梁。可信承诺者的心态是开放的、包容的,并致力于提升他人。这个角色对于通过确保高质量的贡献和培养持续学习的文化来维持良好的开发者体验至关重要。
-
自愿代码贡献:自愿代码贡献的原则强调贡献是自愿的,促使工程师对他们所参与的项目产生归属感和责任感。这是一种自下而上的方法,与自上而下的方法不同,能够营造一个更加自然、协作的环境。随着工程师自愿参与,文化变得自我调节,既有助于项目本身,也促进了组织整体文化的发展。这种参与型的环境显著提升了开发者的体验,为个人和职业成长提供了机会。它与 DevOps 文化中集体所有权的理念相似,贡献的目标是改善整个系统,而不仅仅是单个组件。
InnerSource 与 DevOps 的互补关系
通过将 InnerSource 与 DevOps 实践相结合,组织可以创建一个有利于客户导向和开发者导向的全面环境。InnerSource 通过专注于文化特征——开放性、透明性、指导和自愿贡献——提供工具来改善开发者体验,使工程师的日常工作更加有趣、有意义和令人满足。
平台工程
目前有许多云服务可用,尤其是来自主要公共云提供商的服务。特别是在云原生计算基金会(CNCF)的核心,围绕 Kubernetes 的生态系统发生了巨大的变化,涵盖了超过 1000 个项目。工具的激增增加了开发团队的认知负担。
平台工程是一种新兴的技术行业方法,专注于优化开发者体验和运营效率。其目标是实现可复用的工具和自助功能,自动化基础设施操作,从而提升开发者的体验和生产力。平台团队的“客户”是开发团队,特别强调满足他们的需求。
从根本上讲,平台工程围绕内部开发者平台(IDP)的构建和管理展开。这些平台整合了各种工具、服务和自动化工作流,提供自助服务选项。它们本质上为开发者提供了一个黄金路径,帮助他们从开发到部署的过程,不会因基础设施的复杂性而陷入困境。
平台团队通常会提供开发者门户网站,例如 Spotify 的开源 Backstage,进一步强调了 PaaP 的概念。最重要的心态是将平台视为一个产品,而不仅仅是工具,面向的是内部开发者——即“客户”。
平台团队的角色是多方面的,从创建 IDP 到建立内部 SLA。他们监控团队绩效指标,并监督一个安全高效的交付过程。平台团队工具箱中的一个重要元素是 IaC(基础设施即代码),以及强大的 CI/CD 管道。这些设置作为代码开发和部署的中枢神经系统,自动化了从搭建基础设施到构建、测试和推送代码到不同环境的所有操作。这使得平台团队能够专注于更有价值的任务和客户价值,而不是被繁琐且容易出错的手动操作所拖累。
从本质上讲,平台工程旨在加强 DevOps 范式,改善开发者体验,并优化软件交付的运营方面。该方法旨在消除大型组织中常见的障碍,加速从代码到客户的旅程。在此过程中,平台工程填补了现代 DevOps 领域中的一个关键空白,既确保了运营效率,又促进了协作文化和共享责任。
Git —— 代码协作的起点
管理代码变更是一个复杂的挑战。不同的团队成员不断地为统一的代码库做出贡献,而这个代码库必须始终保持功能正常。
Git 改变了软件开发团队的协作方式。通过分布式架构并提供创建分支的能力,Git 已经成为 DevOps 工具箱中的一项重要资产。分支本质上是一个独立的开发线路,像一个平行宇宙,在这里你可以在不影响主项目的情况下工作于新特性或修复问题。这使得团队能够同时专注于多个方面,提高了开发过程的效率,并且能够快速适应快速变化和快速交付软件的需求,这是 DevOps 环境中的关键要求。
Git 不仅仅是一个跟踪代码变化的工具——它还是组织内变革的催化剂。通过提供一种可靠的协作方式,Git 促进了团队之间更好的沟通。这与 DevOps 的核心目标高度契合,即打破组织内部的壁垒,从而消除摩擦,使得构建和部署软件的过程更加紧密和高效。
想象没有版本控制的世界
想象一下一个没有版本控制的环境。开发者将难以管理文件的更改,没有系统的方式来跟踪历史。每一次更改都必须手动记录,导致评论海量,且掩盖了代码的真正目的。在这种情况下,像 DevOps 中看到的快速交付几乎是不可能实现的。缺乏版本控制会为协作过程带来摩擦。而且,文件冲突会成为常见的头痛问题。试想一下,正在开发一个功能时,发现别人用他们的更改覆盖了你的文件——这种冲突可能会中断开发进程。如果没有版本控制,你还不得不采用一些奇怪的命名规范,比如 v1、v2 或 backup-foobar-20230930.py,仅仅是为了保留旧版本的文件备份。
Git 的历史
Git 由 Linus Torvalds 于 2005 年创建,至今已成为全球最受欢迎的 分布式版本控制系统 (**DVCS*)。
那么,Git 为什么会被创建呢?简单的答案是因为需求。Linus Torvalds 需要一个能够很好地完成多项任务的 版本控制系统 (**VCS*)。首先,它必须快速高效,让开发者工作时不受拖慢。其次,它需要允许多个开发者在同一项目上工作而不互相干扰。第三,它必须能够毫不费力地管理大型代码库。此外,Torvalds 希望拥有一个能够保持整个项目可靠历史的系统。他还寻求一种支持非线性开发方法的灵活性,这种方法能够有效管理多个分支并合并。在响应这些需求时,Git 被设计为简单但功能强大的系统。
什么是 VCS?
那么,什么是 VCS 呢?VCS 是一个用于监控 SDLC 中代码修改的系统。在有多个贡献者的项目中,跟踪谁修改了什么以及何时修改——以及由此产生的任何 bug——是至关重要的。VCS 高效地协调了这一跟踪过程。
VCS 技术通常分为两大类——集中式和分布式:
-
集中式:在这种设置下,单个远程仓库存储项目数据,所有团队成员都可以访问。SVN 和 CVS 就是这种类型的例子。
-
分布式:在像 Git 这样的分布式模型中,每个开发者都使用本地仓库的副本,进行更改,然后再将更改同步到中央远程仓库。
下图展示了在使用 Git 时,文件是如何分发和传递的。Git 本质上允许你拥有多个远程仓库和灵活的分布式开发结构,但通常,人们使用像 GitHub 这样的开发平台创建一个远程仓库,开发者通常与远程和本地仓库进行交互:

图 1.11 – Git 中的版本控制是以分布式方式进行的,允许多人同时协作
像 Git 这样的分布式模型的突出优势在于它为开发者提供的自主性。这种去中心化的方法能够更流畅地进行工作,减少了贡献者之间代码冲突的风险。
安全性与完整性
对于 Git 来说,确保源代码的完整性是首要任务。在 Git 中,每次提交都会分配一个唯一的哈希值。该哈希值是基于提交的代码内容及其元数据生成的。因此,如果代码被修改,哈希值也会发生变化,这使得篡改历史记录变得极为困难。影响分支或标签的操作,如合并或回退,也会作为变更历史的一部分被保存。
适应性
Git 具有很强的适应性,能够适应各种开发工作流。无论是小规模项目还是企业级应用程序,Git 都能以多种方式适应,追踪变更并促进团队成员之间的协作。

图 1.12 – Git 高效地使用分支、提交和合并来管理代码
Git 在现代开发中不可或缺
在 Git 引入之前,集中式系统如 SVN 和 CVS 占据主导地位。然而,Git 的兴起促使了个体开发者和组织在版本控制系统(VCS)方面的重大转变,向分布式选项倾斜。
随着云技术的兴起,应用程序和基础设施之间的界限正在变得模糊,Git 不再仅仅是为编写代码的开发者所使用,它也被那些从事基础设施工作的人员所采用。
在 DevOps 的世界里,顺畅的协作与沟通至关重要,Git 成为开发者体验的支柱,不仅支持代码管理,还支持旨在提高协作的工作流策略。
在当代软件开发领域,版本控制不是可选的,它是必不可少的。
我们将在第二章和第三章中介绍所有关于 Git 的知识。
GitHub – AI 驱动的开发者平台
GitHub 是一个 AI 驱动的开发者平台,用于构建、扩展和交付安全软件。
GitHub 常被认为只是一个代码仓库服务,或者有时作为一个附带 CI/CD 功能的代码仓库服务。然而,GitHub 现在提供了一个涵盖整个开发生命周期的平台,从编写应用程序到构建和发布。GitHub 作为 DevOps 协作和自动化的基础平台,通过开发者体验的视角,它是 InnerSource 项目的核心,并且托管着平台工程所必需的代码。此外,这一切都得到了 AI 的支持。无论你是在编写代码还是在审查代码,GitHub 的 AI 都能在多种场景中为你提供支持。
GitHub 的功能可以分为五个主要类别:由 AI 驱动、协作、生产力、安全 和 规模。让我们逐一看一下每个类别。
由 AI 驱动
由于 生成式 AI(GenAI)由 大语言模型(LLMs)驱动的进展,世界正在发生变革。这股变化的浪潮也正在影响工程师,促使开发人员借助 AI 编写代码。AI 不仅能从现有的代码中推断,还能直接将自然语言命令转化为代码,甚至能够用自然语言提供代码解释。当 AI 革命刚刚开始时,这些功能仅限于在编辑器中进行简单的代码生成。然而,作为一个平台,GitHub 的作用远不止代码生成,它在整个开发生命周期中提供了全面的支持。
GitHub Copilot – 你的 AI 编程搭档
GitHub Copilot 是 GitHub 提供的一个 AI 支持的服务。截至 2023 年,它已经成为 Stack Overflow 开发者调查中备受喜爱的服务之一。在 AI 的背景下,生产力提升意味着什么?显而易见,开发人员能够更快地编写代码,并且准确性不断提高。然而,工程师的工作不仅限于编码;例如,研究也是一个至关重要的环节。工程师经常在编辑器和浏览器之间切换,或者在编辑器和 Slack 之间切换,常常进行多任务处理。AI 工具的强大不仅体现在代码生成上,还在研究和文档工作中大放异彩。这些工具减少了工程师的多任务需求,帮助他们保持工作流。换句话说,它们帮助维持 专注模式。
AI 的多功能性
AI 不仅帮助编写代码,还协助 CI/CD 配置,并根据特定格式实现 YAML 文件。Shell 命令的实现也是这些 AI 工具能够轻松完成的任务。它们不仅对应用程序开发人员非常有用,也对平台工程师大有裨益,能够提高个人和团队层面的生产力。
AI 服务的发展速度极快,最新的信息很快就会过时。因此,本书不会深入探讨 GitHub Copilot 的具体服务和功能,但 第八章 将提供关于使用 LLM 的编码技巧的一般性建议。
协作
GitHub 是一个强大的平台,旨在为开发团队提供卓越的协作体验。
新成员的快速融入得益于GitHub Projects、GitHub Issues 和 GitHub 代码搜索的帮助。具体来说,GitHub Projects 通过看板、路线图和表格视图,使任务及其进展的可视化组织变得更加便捷,而 GitHub Issues 则清晰地列出了具体的问题或漏洞。此外,通过给每个问题添加标签,新成员可以迅速了解从哪里开始。这种透明性和方法鼓励了自然的协作。此外,GitHub 代码搜索使得过去的项目、讨论和代码能够快速检索,从而帮助新成员迅速访问现有的知识和项目历史。
为确保代码质量,拉取请求和合并队列非常有用。代码更改通过拉取请求进行明确的审查。通过集成的评论和代码视图,这一过程变得更加清晰和高效。利用合并队列可以确保已审查的更改高效且安全地合并到生产环境中。
从企业文化的角度来看,拉取请求、GitHub Issues、GitHub Discussions 和 GitHub Enterprise 内部仓库等功能有效地在组织内实现了通过 InnerSource 消除信息孤岛。通过利用这些功能,跨团队的透明沟通得到了促进,开发文化逐渐变得更加开放和自下而上。这也能够防止重复发明轮子。采用这种 InnerSource 方法,能够减少工作中的冗余,大大提升整体的员工满意度。
生产力
GitHub 提供了多种工具和功能来提高生产力。
首先,GitHub 具有多种自动化流程和高效的 DevOps 功能。通过使用 GitHub Actions,您可以自动化 CI/CD 过程。这使得您能够在保持代码质量的同时,快速地对产品进行稳定的更改。GitHub Actions 包括自托管运行器和大型运行器,允许您适应受限环境和高规格的机器需求。这种灵活性使您能够更快地将软件更改推向市场,从而有助于提高市场响应时间(TTM)。
此外,GitHub Projects 的自动化功能通过自动化一系列任务提高了工作效率。例如,你可以通过内置的工作流自动化问题项的处理流程或问题标签的分配。当然,复杂的自动化任务也可以通过 GitHub Actions 完成。GitHub Codespaces 提供了一个在线开发环境,允许你随时随地进行编码。这使得开发团队能够高效地远程协作,并显著减少了新成员环境配置所需的时间和资源。通过使用npm和 GitHub Packages,包管理变得更加简便。通过整理依赖关系,你可以高效地共享和重用代码,从而加快开发周期。
GitHub 还提供了 GitHub Copilot,一个 AI 配对编程助手。它具备由大型语言模型(LLM)支持的高性能代码补全功能,帮助开发人员更加高效地编写代码。这不仅提高了代码质量,还显著增加了开发人员的生产力。
这些功能减少了开发人员的工作负担,让他们能够专注于创造内在价值并促进收入增长的任务。
GitHub 可以简化开发过程,并通过加速代码质量和发布速度,更快速地将符合客户需求的产品推向市场,从而实现客户满意度的提高。
安全性
GitHub 是广泛应用于软件开发的一个平台,但它在安全领域也提供了先进的体验。随着维护应用程序高安全性的重要性日益增加,GitHub 的功能变得更加至关重要。
例如,高级安全功能会自动检测代码中的漏洞,帮助公司和开发人员通过及时解决这些问题来降低风险。
该平台还提供了无缝的安全问题管理功能。具体而言,安全概览仪表板让你能够轻松查看所有安全警告和设置,将管理过程集中化。
此外,GitHub 的秘密扫描功能会自动扫描是否有任何密钥或 API 密钥错误地上传到 GitHub,并对其发出警告。对于支持的平台,GitHub 还会进一步采取措施,自动撤销错误上传到公共仓库的密钥,并能够验证密钥的活动状态。仅仅检测到秘密往往无法满足全面的 DevSecOps 要求。这种主动的做法不仅可以早期识别潜在问题,还能显著降低风险。此外,推送保护功能作为一个保障,防止敏感信息被意外上传到 GitHub。
在 GitHub 上,安全政策的创建和执行过程也非常简单。通过设置安全策略并在仓库中强制执行 GitHub 高级安全性,开发者更容易维护一个安全的编码环境。这使得整个组织或企业能够实施更一致的安全措施。
最后,让我们谈谈供应链。如今,几乎每家公司都在某种形式上使用开源技术。许多项目有数百个开源依赖项,这些依赖项可能会引入安全风险。如果其中一个依赖项发现了漏洞,会发生什么呢?这就是 Dependabot 发挥作用的地方。它自动识别依赖项中的漏洞并提议更新,帮助减少供应链中的安全风险。
总的来说,GitHub 涵盖了多个安全方面,为开发者和公司提供了有用的工具,可以在更短的时间内创建和管理更安全的代码。
规模
要实现业务规模化,一个可扩展的平台如 GitHub 至关重要。可靠性、全球访问和持续创新是不可或缺的。拥有比任何其他平台都多的开发者——确切来说超过 1 亿人——GitHub 作为其高度可信和优质的见证,展示了其信任度和质量。
接下来,作为开源的家园,GitHub 实现了全球覆盖。开发者和团队可以公开共享他们的代码,并与全球其他开发者协作。GitHub 的开放式方法是进入多元化市场、工程师和产品的关键。
此外,使用 GitHub 不仅允许用户开发自己的软件,还提供了利用其他项目和开源代码生成新创意和解决方案的机会。这表明 GitHub 不仅仅是一个代码存储工具;它是全球范围内创新和协作的催化剂。
我们将在第四章到第八章中介绍所有你需要了解的 GitHub 知识。
总结
本章开始了一段探索之旅,了解 DevOps 如何彻底改变我们开发软件的方式。我们讨论了开发者体验的重要性,作为为 DevOps 团队提供最佳工作环境的策略。我们还深入探讨了 Git 和 GitHub,作为 DevOps 环境下协作的基础。
一切都是相互联系的,并且是有原因的。DevOps 的需求、使用 Git 和 GitHub 的原因以及文化在塑造组织的人员、流程和工具中的重要性——这些元素相互关联。它们共同塑造了开发者体验策略的形式,使 DevOps 团队中的开发者能够发挥最佳水平。你所做的一切最终都会回馈给你,丰富你作为开发者的成长。
DevOps 应该被设计为允许在快速开发过程中犯错。每一次失误都是通向精通 DevOps、提升技能并为团队和组织做出有意义贡献的步伐。从本质上讲,你在 DevOps 的旅程就是我们在这一章中讨论的所有内容的体现。
所以,让我们带着新获得的理解,准备动手实践。接下来,我们将深入探讨 Git 的基础用法,Git 是作为 DevOps 文化和协作的支柱的版本控制系统(VCS)。
进一步阅读
-
改善开发者体验的秘密公式 揭晓!: https://www.youtube.com/watch?v=mRqoVlhtVzA
-
DevEx: 实际驱动 生产力的因素:
queue.acm.org/detail.cfm?id=3595878 -
GitHub 入门指南 针对创业公司的使用:
www.youtube.com/watch?v=K5zhNxnrVW8
第二章:Git 入门
让我们深入学习 Git。本章将介绍 Git 的基本用法。然而,正如上一章所解释的,这本书不仅仅是介绍概念和方法。它强调沟通和协作。因此,尽管您无疑能够掌握 Git 的基本用法,但您还将了解背后的沟通方面,以及 Git 如何在团队开发中使用。
在本章中,我们将通过快速实践文件管理和分支的基本操作,首先让您了解 Git 的基础知识,然后我们将介绍 Git 的工作原理。接着,您将了解与 Git 合作时作为工程师需要注意的协作原则。
本章将涵盖以下主要主题:
-
Git 入门
-
Git 的基本构造——适合初学者的 Git 工作原理解释
-
成为 Git 通信的高手
技术要求
继续进行此部分的配置说明可以在以下 GitHub 仓库链接中找到。请确保已安装 Git 和 ssh 工具。对于 Windows 用户,建议使用 PowerShell。我还建议您获取有关不同命令和环境的最新信息:
github.com/PacktPublishing/DevOps-Unleashed-with-Git-and-GitHub
Git 入门
在这一部分,我们将假设您在个人环境中工作,并仅仅是在建立历史记录,来进行 Git 的使用介绍。
Git 基础——从实践开始
在深入细节之前,让我们先进行一些实践体验。通过动手操作来理解概念通常比仅仅阅读更容易。
git config——向 Git 介绍自己
现在,在开始项目之前,您需要做一件事。向 Git 介绍自己。要向 Git 介绍自己,使用git config命令:
$ git config --global user.name "John Doe"
$ git config --global user.email "john@example.com"
git config是用于设置 Git 配置的命令,适用于系统、用户和仓库级别。system级别适用于所有用户和所有仓库。global级别适用于特定用户的所有仓库。local级别仅适用于单个仓库。
要验证您的介绍是否已注册,可以使用git congif --``list命令检查:
$ git config --list
user.name=Your Name
user.email=Your Email
现在,第一个任务完成了!让我们快速进入 Git 的基本操作。
git init——您的代码旅程从这里开始
就像每一段伟大的旅程都有其起点一样,在 Git 的世界里,你代码的旅程从 git init 命令开始。此命令用于初始化一个新的 Git 仓库,并开始跟踪现有的目录。当你运行此命令时,它会设置一个包含版本控制所需所有文件的 .git 目录。完成这些后,你就可以开始使用 Git 的各种命令来跟踪和更新你的项目:
# Make a directory
$ mkdir handson-repo
# Change the current working directory
$ cd handson-repo/
$ git init
重要提示
另一种方法是将目录名作为参数传递,如 git init handson-repo;这将创建该目录,因此你不需要运行 mkdir 命令。
现在,.git/ 目录已创建,文件的更改保存在该目录下,但 Git 并不会像近期的 Microsoft Office 产品那样自动保存文件。
在 Git 中,保存是通过执行 git add 命令完成的,该命令有意识地从已编辑、已添加或已删除的文件中选择要保存的文件,接着 git commit 命令将这些更改注册到历史中。
接下来,让我们往仓库中添加一些代码。
git add – 为你的代码准备亮相
git add 命令是你在工作目录中进行更改与准备将这些更改永久存储到 Git 仓库之间的桥梁。当你对文件进行修改时,Git 会识别到文件已被更改,但这些更改不会自动成为历史的一部分。这时,git add 就发挥作用了。可以把它理解为保存一个文档的过程。
首先,在你的 handson-repo 目录中创建一个新文件:
# Making a README.md file
$ echo "# README" > README.md
git status 命令显示仓库的当前状态,显示哪些文件的更改是已跟踪的,哪些是未跟踪的。当你看到 Untracked files 消息时,Git 就是在告诉你有一个文件它还没有被告知要监视。在我们的例子中,README.md 文件是 Git 新增的文件,并未被注册,因此它被标记为未跟踪:
# Checking how Git recognizes the new file
$ git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
README.md
新添加的文件是项目的一部分,但 Git 尚未跟踪它们。要将它们从未跟踪状态转移到已跟踪状态,你需要使用 git add 命令:
$ git add README.md
$ git status
On branch main
No commits yet
Changes to be committed:
(use "git rm --cached <file>..." to unstage)
new file: README.md
现在,Git 已经识别 README.md 为一个新文件,并且它现在已被跟踪。git add 命令所保存的状态就是通过 git add 命令将工作树中的 README.md 文件标记为已跟踪。
重要提示
git add 还有其他选项。你可以使用 git add . 来包括所有文件,或者像 git add file1.md file2.md file3.md 这样包含多个文件,或者使用通配符如 git add *.md 来添加所有 .md 扩展名的文件。
一切就绪;现在是时候将你的修改记录到历史中了。
git commit – 锁定你的开发进度
git commit 命令将你通过 git add 暂存的更改记录到仓库历史中。这使你能够随着时间跟踪更改。
想象一下您在玩一款具有挑战性的视频游戏。随着您的进展,您经常会保存游戏以锁定成就。类似地,在开发软件时,您将使用git commit保存工作。每个提交都是一个保存点,以后如果需要可以返回到该点。
要提交更改,通常可以执行以下操作:
$ git commit -m "Initial commit with README.md"
在这里,-m标志后面跟着一个简短的描述性消息,捕捉您所做更改的精髓。编写良好的提交消息是一门艺术,因为它有助于理解更改的历史和意图。
现在,让我们再次使用git status命令查看当前工作目录中的所有更改是否已保存:
$ git status
On branch main
nothing to commit, working tree clean
如果出现消息 nothing to commit,则表示您的更改已经合并。
好了,就这些;在 Git 中保存文件非常容易。让我们在这里回顾一下。编辑、暂存和提交流程始终保持不变,无论您的项目有多复杂:
- 编辑文件:对文件进行必要的更改。例如,在这个示例中,已编辑两个现有文件进行删除和修改,并添加了另一个文件:

图 2.1 – 编辑文件
- 暂存更改:决定要提交的文件或特定更改,并将它们暂存。例如,在这个示例中,三次编辑中只有删除和修改被暂存:

图 2.2 – 暂存更改
- 提交更改:在满意暂存的更改后,执行提交以注册它们。请记住,每次提交都会生成一个唯一的提交 ID:

图 2.3 – 提交更改
git log – 遍历提交树
一旦您进行了几次提交,您可能希望回顾并查看存储库中所做更改的历史记录。这就是git log命令派上用场的地方。该命令按时间顺序反向显示存储库中进行的提交列表,即最近的提交首先显示。
要尝试此操作,请使用以下命令:
$ git log
commit a16e562c4cb1e4cc014220ec62f1182b3928935c (HEAD -> main)
Author: John Doe <john@example.com>
Date: Thu Sep 28 16:30:00 2023 +0900
Initial commit with README.md
这将显示所有提交的列表,每个提交都包含以下内容:
-
独特的 SHA-1 标识符:这充当提交的签名,并可以在各种 Git 命令中用来引用特定的提交
-
提交者的详细信息:显示执行提交的个人的姓名和电子邮件地址
-
提交的时间戳:显示提交的时间
-
提交消息:捕捉提交中改动的要点的简短和信息丰富的注释
除了基本的git log命令外,还有许多选项可以让您根据需要定制输出:
-
git log -p: 这个选项显示每个提交引入的差异(即补丁)。 -
git log --stat: 这提供了每个提交的简略统计信息 -
git log --oneline: 这提供更紧凑的输出,将每个提交显示为单行 -
git log --graph:该命令以 ASCII 图形布局可视化分支和合并历史。 -
git log --author="John Doe":此命令筛选提交,仅显示由特定人员(在本例中为“John Doe”)进行的提交。
例如,它还可以改善外观,如下所示:
$ git log --graph --pretty=format:'%x09 %h %ar ("%an") %s'
输出结果如下所示:

图 2.4 – 美化后的 git log
现在让我们尝试使用git log命令。首先,更新README.md文件,并创建一个新的CONTRIBUTING.md文件:
$ echo "# CONTRIBUTING" > CONTRIBUTING.md
$ echo "# README\n\nWelcome to the project" > README.md
$ git add .
$ git commit -m "Set up the repository base documentation"
完成后,添加一个示例 Python 代码:
$ echo "print('Hello World')" > main.py
$ git add .
$ git commit –m "Add main.py"
当在日志中确认已正确记录时,任务完成:
$ git log --oneline
344a02a (HEAD -> main) Add main.py
b641640 Set up the repository base documentation
a16e562 Initial commit with README.md
从本质上讲,git log命令是任何开发者都必备的重要工具。它帮助你轻松浏览代码的历史,无论是查找特定的更改,还是仅仅回顾之前的工作。
现在我们已经回顾了到目前为止关于git log功能的内容。
使用分支 – 协作的基石
虽然前面的部分已经为你提供了如何初始化和管理 Git 仓库的深入理解,但分支的概念将这一过程提升到了一个新层次。虽然仅使用git commit会创建一个线性历史,git branch可以用来创建一个并行环境的历史。然后,你可以将这些多个环境合并为一个,这使得多人可以共同开发,同时为你提供了灵活性,能够在不影响主代码库的情况下尝试新功能、修复 bug,甚至是完全的前卫创意。
git 分支 – 理解 Git 分支的基础
当你初始化一个 Git 仓库时,它会自动创建一个默认分支,通常叫做main(之前称为 master)。当你运行git branch命令时,它会显示仓库中所有分支的列表,并高亮当前分支:
$ git branch
* main
直观地可以将线性主分支视作如下图所示:

图 2.5 – git 分支
你可以使用git branch <分支名称>命令创建一个新分支。此命令从当前分支创建一个新分支:
# Create a new branch named 'feature/new-feature'
$ git branch feature/new-feature
如果你创建一个新分支,你可以建立一条具有不同历史的分支,并向该分支添加提交,示例如下:

图 2.6 – 创建新分支
分支命名规范对于沟通非常重要。常用的标准是将分支名称前缀为feature/、bugfix/或hotfix/,后跟简短描述。这使得任何人都能一目了然地理解分支的目的。
你还可以从一个特定的分支或提交创建分支,与你当前所在的分支不同。这在你需要创建一个特性分支或修复 bug 的分支时特别有用,这些分支应该从指定的开发或暂存分支而不是从当前工作分支创建:
# Create a branch from a specific branch
$ git branch <new-branch-name> <base-branch-name>
# Create a branch from a specific commit
$ git branch <new-branch-name> <commit-hash>
git checkout/git switch – 在分支之间切换
在日常工作流中,你将经常需要从一个分支切换到另一个分支,特别是在同时处理多个特性或修复 bug 时。当你已经在多个分支上开始工作时,了解你当前所处的分支变得尤为重要。在 Git 中,HEAD是指你当前正在处理的分支的最新提交。
更改当前工作分支被称为切换分支。git checkout命令可以实现这一操作:
# Switch to 'feature/new-feature' branch
$ git checkout feature/new-feature
这个操作将 HEAD 位置,即分支的最新提交,切换到名为feature/new-feature的分支:

图 2.7 – 切换分支
git checkout命令将当前的位置切换到feature/new-feature分支的最新提交,即 HEAD。
Git 的最新版本还提供了git switch命令,它提供了一种更直观的方式来切换分支:
# Switch to 'feature/new-feature' branch
$ git switch feature/new-feature
有时,你可能会发现创建一个新分支并立即切换到它更为高效。Git 提供了一个简化命令,结合了git branch和git checkout或git switch的功能。
若要在一步操作中创建并切换到一个新分支,可以使用git checkout -b命令:
# Create and switch to a new branch
$ git checkout -b feature/another-new-feature
这相当于执行以下命令:
$ git branch feature/another-new-feature
$ git checkout feature/another-new-feature
在 Git 的最新版本中,你也可以通过使用-c选项和git switch实现相同的操作:
# Create and switch to a new branch
$ git switch -c feature/another-new-feature
现在,你不仅可以通过git commit线性保存更改,还可以通过git branch创建平行世界,并通过git checkout在平行世界之间自由切换。现在,是时候合并这两个世界了。
git merge <分支名称> – 合并分支
一旦你在某个分支上做出了更改并彻底测试过,你可能希望将这些更改合并回主分支或其他分支。这个操作被称为合并:
# First, switch to the branch you want to merge into
$ git checkout main
# Now, merge your feature branch
$ git merge feature/new-feature
合并允许你将有不同历史的代码行合并在一起,如下图所示:

图 2.8 – 合并分支
合并操作可能很简单,但如果分支之间存在冲突,操作会变得复杂。在这种情况下,Git 需要手动干预以解决冲突。冲突解决将在下一章讨论。
git branch –d – 删除一个分支
一旦一个分支成功合并并且不再需要,它可以被删除,以保持仓库的整洁:
# Delete a local branch
$ git branch -d feature/new-feature
因此,到目前为止,在实践教程中,你应该已经掌握了 Git 的基础,并对其有了一定的了解。你现在应该清楚你的项目中发生了什么,并学会了如何使用基本命令。
另一方面,你仍然需要学习如何灵活地在团队中协作。未来,你将学习这些方法,但同样有必要理解 Git 的实际工作原理,才能从根本上理解这些操作。
了解 Git 的机制可以加深你对命令本质作用的理解,不仅能提升你在 Git 操作中的熟练度,还能改善你在 Git 和 GitHub 中的沟通,从而促进 DevOps 的发展。
让我们来看看 Git 是如何工作的。
Git 的结构 – 一个对初学者友好的解释,讲解 Git 是如何工作的
Git 非常强大,尤其是在项目变得更加复杂时。到目前为止,我们的重点是直观的历史记录。然而,Git 真正的优势在于其处理大型项目的能力,尤其是当有大量贡献者参与并且能够无缝地管理团队中动态发展的代码时。我们通过直观的方式,像使用命令一样操作 Git。现在是时候深入探讨了。虽然对 Git 有直观的了解是有帮助的,但通过理解 Git 在幕后是如何运作的,我们可以充分发挥它的潜力。
Git 中的文件生命周期
在 Git 中,我们在上一节中学到,保存更改是一个两步操作,即暂存和提交,但 Git 实际上将文件处理为四种状态。
你项目中的每个文件都可以处于四种状态之一:
-
未跟踪:这些是存在于你的目录中的文件,但尚未被 Git 控制。它们是新文件,或是 Git 被明确告知忽略的文件。
-
未修改:这些是之前已经被添加到 Git 中,并且自上次提交以来没有任何变化的文件。它们安静地存在,被 Git 监控,但不需要立即采取任何行动。换句话说,它们是已提交的。
-
已修改:一旦你对已跟踪的文件进行更改,Git 就会将其标记为已修改。此时,该文件自上次提交以来已经被更改,但尚未为下一次提交做好准备(或暂存)。
-
git add命令会暂存你的修改。虽然这些更改已被标记,但在你提交之前,它们并未被保存到仓库中。
下图显示了这些状态的转换。文件在这些状态之间来回变化:

图 2.9 – Git 中文件状态的四种类型
当你创建或引入新文件到项目中时,使用 git add 命令后,它们会过渡到未修改状态,表示它们现在在 Git 的监控之下。
对这些已跟踪文件的后续更改会将它们置于已修改状态。在它们被提交之前,这些更改需要被暂存,从而将文件移动到已暂存状态。暂存让你预览即将提交的更改。可以把它想象成将物品放入箱子(暂存区)并准备运送(提交)。你决定哪些物品(或更改)将放入那个箱子。
在暂存之后,你可以使用 git commit 命令提交这些更改。当你通过 Visual Studio Code 等界面进行暂存更改时,通常只需点击一个按钮。一旦你对暂存的更改感到满意,就可以提交它们,永久地将它们保存到你的项目历史中。
提交后,这些文件将恢复为 未修改 状态,等待将来的编辑或更改。换句话说,在此阶段,可以说其状态已经变为 已提交。在 Git 中,你所做的每个提交都记录了项目在特定时间点的当前状态。这个记录机制是 Git 能力的基础,确保每个更改都被文档化。这使得开发者可以回到任何特定的提交,提供了回顾或恢复到以前版本的灵活性。
此外,如果一个被跟踪的文件从目录中被删除,Git 会将其视为 未跟踪,直到它被明确地从仓库中删除。
这个生命周期为开发者提供了对项目更改的精确控制,允许进行战略性的提交,并确保清晰、有序的版本历史。
幕后 – Git 的架构
首先,Git 到底是什么?从本质上讲,Git 就是一个 .git 目录。这个隐藏目录包含了你的代码历史记录——提交、分支、配置文件等。你可能记得在 Git 学习初期,在执行 git init 命令时,会创建一个 .git 目录。
深入探索 – 探索 .git 目录
通过执行 ls 命令,你可以看到多个子目录和配置文件。其中,objects 目录与我们当前的讨论最为相关。它是 Git 键值存储的核心,存放着 blob(实际文件内容)、树对象(目录结构)和提交记录:
$ ls .git
COMMIT_EDITMSG hooks objects
HEAD index refs
config info
description logs
现在,让我们来看看 objects 文件夹。这就是键值存储所在的地方。文件夹的名称由两个字母数字字符组成,代表提交 ID 的前两个字符:
$ ls .git/objects
2f 7e b1 e3 info
34 a1 b6 e6 pack
4b af df ea
在 Git 中,每个提交或数据块都通过一个键(SHA-1 哈希)来唯一标识。这个哈希值是一个由 40 个字母数字字符组成的字符串,类似于 b641640413035d84b272600d3419cad3b0352d70。这个每个提交的唯一标识符是 Git 根据提交内容生成的。你在执行 git log 命令时看到的这些 ID 对应着你迄今为止所做的更改:
$ git log
commit 344a02a99ce836b696c4eee0ee747c1055ab846b (HEAD -> main)
Author: John Doe <john@example.com>
Date: Thu Sep 28 18:41:41 2023 +0900
Add main.py
commit b641640413035d84b272600d3419cad3b0352d70
Author: John Doe <john@example.com>
Date: Thu Sep 28 18:41:18 2023 +0900
Set up the repository base documentation
commit a16e562c4cb1e4cc014220ec62f1182b3928935c
Author: John Doe <john@example.com>
Date: Thu Sep 28 16:35:31 2023 +0900
Initial commit with README.md
如果我们打开 b6 目录,我们会认出键值存储的结构,其中提交 ID 作为文件名或键。但这些文件里到底有什么呢?为了弄清楚,我们接下来可以使用 git cat-file 命令来查看其中的内容。
重要提示
在本书中,Hash 的前两个字符是 b6,但在你的环境中会显示不同的列表。让我们选择一个合适的哈希值并执行 ls 命令:
$ ls .git/objects/b6/
41640413035d84b272600d3419cad3b0352d70
git cat-file – 解剖 Git 的内部工作原理
要检查键值存储中值的内容,可以使用 git cat-file 命令。当传递提交 ID 的前七个字符作为参数时,我们得到的结果展示了树对象和父对象,它们分别引用了父提交的 ID:
# Passing the first seven letters of the Commit Id as an argument
$ git cat-file -p b641640
tree af4fca92a8fbe20ab911b8c0339ed6610b089e73
parent a16e562c4cb1e4cc014220ec62f1182b3928935c
author John Doe <john@example.com> 1695894078 +0900
committer John Doe <john@example.com> 1695894078 +0900
Set up the repository base documentation
重要提示
在 Git 命令中处理哈希时,不需要传递完整的 40 个字符;可以省略。这个例子中传递了前七个字符作为参数,但最少要求是四个字符。虽然具体情况取决于项目的大小,但为了避免键冲突,建议至少指定七个字符。
在 Git 中,主要管理和使用四种对象:
-
commit对象:引用了 tree 对象 -
tree对象:包含对 blob 和/或其他 tree 对象的引用 -
blob对象:包含数据(如文件内容) -
tag对象:包含有关注释标签的信息
提交引用结构中嵌入了 parent 提交的 ID。但实际的提交文件去了哪里呢?在输出中,我们看到标有 tree 的 ID 和 parent。看起来这个 tree 对象也有一个 SHA-1 哈希值,因此让我们使用 git 的 cat-file 命令来检查其值:
# Passing the first seven letters of the Tree Id as an argument
$ git cat-file -p af4fca9
100644 blob b1b003a2...a277 CONTRIBUTING.md
100644 blob ea90ab4d...79ca README.md
100644 blob e69de29b...5391 main.py
在调用 git cat-file 命令查看与 tree 标签关联的 ID 时,我们得到的结果展示了一种名为 blob 的文件类型。让我们通过 git cat-file 命令引用 README.md 的 blob ID。这样可以显示文件内容,表明存储在键值存储中的 blob 类型数据实际上是文件本身。这些观察结果让我们对 Git 的架构有了更清晰的了解:
$ git cat-file -p ea90ab4
# README
Welcome to the project
现在你已经知道 Git 如何在键值存储中存储值。你应该理解,Git 并不是一个黑匣子;它是一个通过 SHA-1 哈希键来管理历史记录的系统。
git show – 在日常使用中更易于操作
之前我们使用了 git cat-file 命令来了解 Git 的工作原理,但有一个类似的命令是 git show。这两个命令都是强大的 Git 工具,但它们的作用有所不同,输出也不同。git cat-file 是一个低级工具,主要用于检查 Git 对象,如 blobs、trees、commits 和 tags。它可以显示对象的类型、大小,甚至是原始内容。而 git show 更加用户友好;该命令以可读的方式展示各种 Git 对象的内容。默认情况下,它展示提交的日志信息和文本差异。然而,它也足够灵活,可以以易于阅读的格式展示其他对象类型,如 blobs、trees 和 tags:
$ git show b641640
commit b641640413035d84b272600d3419cad3b0352d70
Author: John Doe <john@example.com>
Date: Thu Sep 28 18:41:18 2023 +0900
Set up the repository base documentation
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 0000000..b1b003a
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1 @@
+"# CONTRIBUTING"
diff --git a/README.md b/README.md
index 7e59600..ea90ab4 100644
--- a/README.md
+++ b/README.md
@@ -1 +1,3 @@
# README
+
+Welcome to the project
diff --git a/main.py b/main.py
new file mode 100644
如果你是开发人员或 Git 用户,想查看某个提交引入的更改或查看特定版本的文件内容,git show 是一个更直观的选择。相比之下,git cat-file 更深入地探讨了 Git 的内部结构,允许用户直接与原始 Git 对象进行交互和检查。对于那些深入参与 Git 内部工作或开发与 Git 核心系统接口工具的人来说,git cat-file 提供了一个详细的层次。然而,对于大多数日常任务,以及那些刚刚开始接触 Git 和 GitHub 的用户,git show 提供了一种更友好的方式来查看更改和内容,无需深入了解 Git 对象数据库的复杂性。
Git 树结构
现在,我们知道 Git 本质上是一个键值存储。接下来,让我们看看每个对象是如何连接和管理的,以便作为历史数据的一致性存储。在上一节中,我们看到过关键字 tree 和 parent,但它们到底是什么呢?现在,我们将探索提交与这些关键字所链接的对象之间的关系。
提交、树和数据块
在 Git 中,树结构的概念在维护仓库状态方面起着至关重要的作用。每个提交不仅仅是一组更改。
每个关键字的解释:
-
提交:Git 中的每个提交都有一个唯一的 SHA-1 哈希值。它通过引用一个树对象携带着仓库状态的快照。
-
树:Git 中的树像目录一样工作。它们可以引用其他树(子目录)和数据块(文件)。每个树都有其独特的 SHA-1 哈希值。表示仓库顶级目录的主树被称为根树。
-
数据块:数据块表示仓库中文件的内容。像提交和树一样,每个数据块都有其独特的 SHA-1 哈希值。
父与子
仓库历史的传承和进展通过提交之间的父子关系得以记录。
大多数 Git 提交都引用单个父提交,表示仓库时间线中的直接前驱。
如前一页所解释的,提交持有其父提交的 ID,建立了引用关系。在许多提交的可视化表示中,箭头通常表示这种关系。值得注意的是,这些箭头的方向通常与提交的顺序相反。每个提交都具有图中所示的关系。

图 2.10 – Git 提交关系
重要提示
提交有时会有多个父级,特别是在两个分支合并时。这种双重父级标志着两个独立开发线的合并。
Git 如何存储树和数据块?
Git 高效性的奇妙之处在于它如何存储其树和数据块。
让我们通过以下方式说明这些关系:

图 2.11 – Git 树结构
Git 中的每个提交都对应一个树,表示在特定时刻仓库中文件和目录的状态。要更深入地了解,可以参考所提供的图表。标签为 fb36640 的提交包含了对树 d6f50a2 的引用。这个树反映了该提交时仓库的根目录状态。
当我们遍历这棵树(d6f50a2)时,我们会遇到各种指针。有些指向 blobs,有些指向 trees。像 2d69956 这样的 blob 对应一个文件——在这种情况下是 LICENSE。而像 1d0f85d 这样的 tree 则代表一个名为 contents 的子目录。这个子目录树可以进一步指向它自己的一组 blobs 和 trees。
这种复杂的关联构建了一个类似传统文件系统的层级结构。这个层级的每一层代表着你仓库中不同的文件和目录。Git 设计哲学的核心是高效。通过以这种层级结构存储数据,Git 能够快速跟踪文件和目录的变化,而无需冗余存储。例如,不变的文件在不同的提交中会指向相同的 blob,从而优化了存储和检索:

图 2.12 – Git 中的高效文件管理
理解 Git 的树结构以及它与 blobs 和 commits 之间的关系是每个开发者的基础知识。这不仅仅是使用 Git 命令;更重要的是理解其背后的巧妙架构,确保代码的历史得到高效且准确的保存。当你在 Git 的旅程中不断前进时,这些知识将帮助你充分利用 Git 的功能。
到目前为止,你已经学会了 Git 如何识别每个文件的状态,并将其记录在一个键值存储中。你还了解到,这个键值存储采用树形结构,具有父子关系。如果你深入 .git/ 目录,你会发现它有一个非常简单的结构,正是因为这个简单,Git 才能高效管理复杂的项目。
如果你已经理解了这些,那么你已经为掌握 Git 之旅做好了充分准备。尽管本书无法覆盖每个 Git 命令,但理解这些基础将确保你在未来遇到不熟悉的 Git 命令时也能应对自如。你已经具备了理解它们的技术基础。
现在,让我们学习另一个内容,以结束你在本章中对 Git 之旅的准备工作。那就是 Git 之旅的思维方式。这个思维方式不仅适用于使用 Git,还直接与 DevOps 中的协作相关。
成为 Git 通信的专家
让我们稍作停顿。尽管大多数资源可能会催促你快速学习 Git 的基本命令、冲突解决、合并类型和 Git 工作流,但我们选择暂时休息一下,专注于问题的核心。
那么,为什么 Git 一开始会被开发出来呢?从本质上来说,Git 是为了简化复杂开发项目中的沟通而创建的。由于本书的目的是提升你在 DevOps 团队中的角色,理解 Git 在沟通方面的力量至关重要。毕竟,DevOps 不仅仅是技术问题;它关乎于改善协作、打破壁垒和促进更顺畅的工作流程。
当你在 Git 命令和仓库中导航时,要记住,你不仅仅是在分享代码;你还在与团队进行沟通。你的提交、拉取请求和合并应该被视为在一个更广泛对话中的部分,旨在共同创造出某个宏伟的作品。
因此,在学习 Git 的过程中,专注于磨练你的思维方式。一个经过精心调整的 Git 方法不仅仅是掌握命令,它还能让你成为一名无价的团队成员,与 DevOps 环境中的整体目标保持一致。
记住:欲速则不达。花时间深入理解 Git 的基础知识,你不仅能成为一名熟练的编码员,还能成为你 DevOps 团队中的杰出合作者。
git commit – 重新审视最重要的命令
如果你问我,Git 中最重要的命令是 git commit。如果这个命令做得完美,其他的一切都是次要的。这个命令界定了你所有编码活动的范围,并巩固了你的输出,决定了你工作的质量。一次提交就像是一个沟通单元。如果这个沟通单元出错,所有后续的沟通都会变得混乱。
你有没有用乐高积木做过什么东西?乐高的精髓不仅仅在于按照说明书操作,更在于激发创造力并打造独特的作品。有时,你还需要和朋友一起合作,像一起建造一个城堡。每个人可能都有一个角色:有人负责做城门,有人负责做地基,还有人负责做塔楼。你们的成功在于作为一个团队共同打造出一个宏伟的城堡。但考虑一下:即使你个人做了一部分很棒的作品,如果它不符合其他人设想的效果,或者和他们做的部分不匹配,它可能不会得到认可。确保各个部分的尺寸匹配并经常检查其兼容性是至关重要的。换句话说,你需要在正确的时机进行沟通,并不断调整建造的方式和各个部件。小时候,你可能做过一些奇怪的作品,虽然缺乏完美的沟通,但由于富有创意还是会受到赞扬。然而现在情况不同了。如果你正在阅读这本书,你很可能在一个组织中工作,创造某种产品,可能有工资收入,并且承担责任。这意味着,这些考虑因素不仅仅是可有可无的,它们是必须要有的。
让我们回到编程。当你在团队中编程时,不仅你自己,所有团队成员(包括过去和现在的)都会审查你在 Git 中的代码和操作。即使你一个人编程,你的未来自我和过去自我也是你的合作伙伴。我曾多次因为不理解自己以前写的代码或忘记自己当时想要实现什么,通过运行 git log 命令后还是毫无头绪。Git 操作管理得如何,将直接反映在代码管理和生产流程中。最好有一个你可以记住的原则来使用 Git。
控制质量和数量,成为一个优秀的沟通者
关于如何使用 Git,存在很多良好的实践。然而,许多内容可以归类于下图的四个框中的某个位置:

图 2.13 – Git 提交的卓越实践
早期和频繁提交 —— 采用 DevOps 成功的核心原则
过去几十年,现代软件开发的格局发生了巨大的变化。传统的瀑布式开发方法已被敏捷方法取代,进一步演变为如今许多组织所采用的 DevOps 文化。这个演变的核心概念就是 持续集成和持续交付 (CI/CD)。支持这一方法论的基础实践之一就是早期和频繁提交代码的理念。本节将深入探讨这一实践的重要性,尤其是在 DevOps 的 Git 和 GitHub 上的应用。
传统范式
在过去,开发人员通常会花费几天甚至几周的时间来开发一个功能或修复,然后在最后将其合并到主分支。这通常会导致合并冲突、错误和大量手动干预以修复问题。这种模式不可扩展,与今天快速发展的、以客户为中心的技术环境需求背道而驰。
向早期提交的转变
尽早提交意味着,当你完成一部分逻辑性的工作时,你就应该提交。这并不一定意味着整个功能已经完成,可能只是某个函数或类。为什么这样做有益?
-
更小的更改:较小的更改更容易审查。它们更易于理解,使得代码审查过程更高效、更有效。
-
减少合并冲突:通过尽早提交和推送你的更改,你可以减少遇到合并冲突的机会,因为你会频繁地将分支与主分支同步。
-
更快的反馈循环:你越早提交和推送更改,自动化测试就能越早运行,你就能越早获得关于代码的反馈。这有助于更快地迭代和更快地交付功能和修复。
经常提交的好处
经常提交与尽早提交是相辅相成的。你提交的频率越高,以下几点就越容易做到:
-
更容易找到问题:如果出现 bug,通过筛选一个小的提交要比筛选一个大的提交要简单得多。
-
更容易回滚:如果一个提交引起了意外问题,回滚到之前的稳定状态非常简单。这种安全网在生产环境中可能是一个救命稻草。
了不起的提交信息 - 为您的代码打造清晰、信息丰富和简洁的叙述
在软件开发领域,我们编写的代码不仅仅是机器的一组指令;它也是与同行交流的一种形式。然而,代码展示了如何做到,而提交信息则阐明了为什么这样做。一个精心制作的提交信息是同行开发者的灯塔,提供上下文、清晰度和代码变更的历史记录。
提交信息的目的
在其核心,提交信息具有几个关键功能:
-
历史记录:它提供了变更的按时间排序的账户,使开发人员能够理解代码库的演变。
-
上下文:它提供了变更的理由,提供了单单代码本身无法传达的洞察力。
-
文档:除了内联注释和外部文档之外,提交信息还充当一种文档形式,可以解释决策和权衡。
优秀提交信息的特征
一个好的提交信息与一个了不起的提交信息有何不同?以下是出色提交信息的标志:
-
简洁的主题行:以简洁直接的主题行开头,理想情况下不超过 50 个字符,捕捉提交的核心内容。
-
git commit -m "subject-line" -m "``description-body"命令。 -
使用主动语态:像“添加功能”或“修复错误”这样的短语比“添加了功能”或“修复了错误”更清晰、更直接。
-
引用问题:如果提交与跟踪系统中的特定问题或任务相关,请引用其 ID 或链接,有助于可追溯性。
DevOps 的联系
对于现代的 DevOps 工程师,提交信息不仅仅是一个附带的想法;它们是 DevOps 哲学的核心组成部分。
在当今的软件领域中,应用程序开发和基础设施之间的界线变得日益模糊。精通基础设施的工程师可能会发现自己阅读应用程序代码,反之亦然。更重要的是,现在两个领域的工具都与 Git 集成,使专业人士能够在各种平台上查看提交信息以及 Git 哈希值。
鉴于这种集成,提交信息不再局限于其仓库或团队。它开始跨越不同的组织边界和平台。这一过渡突显了精心编写的提交信息作为一种通用语言的价值,它可以被软件开发和交付流程中的不同利益相关者理解和欣赏。在打破组织孤岛并改善开发者体验的过程中,你的优秀提交信息将助力你的旅程。
以下是一些优秀提交信息的建议:
-
透明性至关重要:清晰的提交信息使得从开发到运营的团队能够理解代码变更,减少摩擦并促进协作。
-
持续交付依赖历史:随着组织推动更频繁的发布,快速理解和验证变更的能力变得至关重要。信息丰富的提交信息是这一过程中的重要工具。
提交信息示例
这是一个提交信息的示例。它是基础性的,你可以在此基础上添加你自己的上下文,但不应过长:
| 类别 | 示例 提交信息 |
|---|---|
| 简单变更 | 添加README.md |
| 更新许可证到期日期 | |
| 移除已废弃的方法 XYZ | |
| 功能添加/更新 | 实现用户认证流程 |
| 为主页添加搜索功能 | |
| 扩展 API 以支持版本控制 | |
| 错误修复 | 修复导致会话超时的登录错误 |
| 解决数据处理模块中的内存泄漏 | |
| 修正用户注册表单中的拼写错误 | |
| 重构和代码 质量改进 | 重构数据库连接逻辑以支持连接池 |
| 优化图片加载以加速页面渲染 | |
| 改进支付网关中的错误处理 | |
| 文档 和注释 | 记录 XYZ 模块中的主要算法 |
| 更新 X 函数中的注释以提高清晰度 | |
| 修订 API 文档以包含新端点 | |
| 恢复变更 | 恢复“添加实验性功能 X” |
| 回滚到缓存层更新之前的稳定状态 | |
| 依赖项和 外部集成 | 升级到 ABC 库的 v2.1.3 版本 |
| 集成 XYZ 框架的最新安全补丁 | |
| 带有问题/任务 跟踪引用 | 修复#1234:处理订单结账中的边缘情况 |
| 功能#5678:添加多语言支持 | |
| 合并操作 | 合并分支‘feature/user-profiles’ |
| 解决 main.css 中的合并冲突 | |
| 与测试相关的变更 | 为工具函数添加单元测试 |
| 重构集成测试以使用模拟数据 | |
| 修复用户注册流程中的不稳定测试 |
表 2.1 – 提交信息示例
单一功能代码
在不断发展的软件开发领域中,敏捷和 DevOps 实践倡导快速迭代和强有力的协作,单一功能代码的原则变得愈发重要。它的影响力不仅限于代码结构的领域,甚至渗透到开发者体验和 Git 通信的核心。让我们深入探讨这些概念之间深刻的联系。
与敏捷原则的契合
敏捷方法论的核心理念是促进适应性、持续改进,并以小而可管理的增量交付价值。单一功能代码与这些原则无缝契合:
-
增量开发:就像敏捷将特性拆解为更小的用户故事或任务一样,单一功能代码鼓励将软件组件拆分成专注的、可管理的模块。
-
适应性:单一功能组件更易于修改或替换,符合敏捷方法论对变化的拥抱。
提升 DevOps 流水线
DevOps 强调软件的持续集成和交付,架起了开发与运维之间的桥梁。此时,单一功能代码的优势便显现出来:
-
精简的 CI/CD:由于代码组件专注且独立,集成过程中一个模块意外影响另一个模块的几率降低,从而使得 CI/CD 流水线更加顺畅。
-
更好的监控和日志记录:当组件具有单一责任时,监控其行为和记录相关事件变得更加容易。任何异常都可以直接追溯到特定功能。
提升开发者体验
开发者体验(DX)的概念围绕着让开发者的工作更轻松、提高生产力和减少摩擦展开。单一功能代码在这一过程中扮演了至关重要的角色:
-
直观的入职体验:当代码库由清晰且专注的组件组成时,新团队成员能够更快理解并做出贡献。
-
高效的调试:由于每个组件只做一件事,识别和解决问题变得更加高效。
与 Git 通信的深厚联系
如前所述,单一功能代码与 Git 之间有着深刻的协同作用:
-
清晰的提交信息:编写单一功能代码能生成精确的提交信息。对单一功能或模块的更改可以在 Git 中简洁地描述,从而促进透明度和沟通清晰度。
-
简化的代码审查:在 GitHub 等平台上,若 Pull 请求围绕着专注的变更进行,审查过程将变得更加直接。审阅者更容易理解意图并验证实现,从而带来更有意义的反馈和协作。
完整的代码:在精确性和进度之间找到平衡
在软件开发领域,完美与进步之间的长期矛盾始终存在。开发人员常常会遇到这样的问题:我的代码什么时候才算准备好了?在本文中,我们旨在阐明“完整代码”这一概念,这一理念强调在不陷入对完美的难以实现追求中,提供稳健、完全实现的解决方案。
完整代码的本质
完整代码背后的理念既简单又深刻:任何编写的代码都应在意图和执行上是完整的。这意味着:
-
没有半途而废:无论是在实现功能还是修复 bug 时,代码都应该完全达成目标,而不是部分地或表面地实现。
-
准备审查:代码应该达到足够的质量,准备接受同伴审查。这不仅意味着功能正常,还意味着符合团队或组织的编码标准和最佳实践。
-
附带测试:在适用的情况下,代码应附带测试,确保其不仅现在能按预期工作,而且随着软件的演进,依旧能够继续保持这种效果。
完成比完美更好
虽然重点放在完整性上,但必须认识到,追求完美可能是一个适得其反的行为。软件本质上是迭代的,等待完美的解决方案可能会拖慢进展。口号“完成比完美更好”提醒我们以下几点:
-
迭代改进是关键:即使一开始解决方案不是最优的,也没有关系。它需要能工作,之后可以随着时间不断改进。
-
反馈推动完美:通常,发布代码并收集反馈比无休止的内部迭代能得出更好的解决方案。
测试在完整代码中的角色
测试是完整代码哲学中的关键:
-
验证:测试验证代码是否按预期执行,提供防止回归的安全网。
-
文档:精心编写的测试也充当文档的角色,提供关于代码预期行为的洞察。
-
自信:测试能增强信心。当开发人员编写有测试保障的完整代码时,他们可以在修改或添加功能时确信,如果代码出现问题,自己能及时发现。
现代开发实践的重要性
在敏捷和 DevOps 主导的时代,CI/CD 管道自动化软件交付,完整代码的重要性愈加凸显:
-
简化的管道:CI/CD 管道的执行考虑到集成的代码可能是不完整的。但这并不意味着代码随时都可以不完整,若失败过多,你团队的测试统计数据将变为红色。不完整的代码会破坏管道,造成瓶颈和低效,并且是你团队代码质量的体现。
-
协作效率:在团队合作中,当开发者提交完整且可以审查的代码时,能够促进更顺畅的协作。审查者花费更少的时间指出基础问题,更多的时间可以用来深入讨论架构或逻辑问题。
现在你已经理解了如何在 Git 中保留历史记录。编写提交信息是件让人头疼的事。你需要在仅仅几十个字符内描述你的工作。然而,在 Git 协作中,关注这些细节是非常重要的。
因为你花了几个小时来进行提交(可能是几十个小时),如果因为一些小细节的疏忽而破坏了合作的质量,那真是太可惜了。只需要多花几秒钟或几分钟的时间关注细节,就能让协作变得更好。
卓越藏于细节。让我们练习并成为真正的大师!
总结
在结束本章时,你应该对 Git 的基本功能、底层机制以及最重要的协作哲学更加熟悉了。理解这些基础知识通常比死记 Git 命令或存储快捷代码片段更为重要。
如果你已经内化了本章的洞见,放心,你已经牢牢掌握了 Git 的基础知识。你不仅准备好提交代码,还准备好在团队中成为一个协作的力量。
当你第一次加入一个由 Git 管理的项目时,最初的挑战通常不是解决复杂的冲突或处理混乱的分支;而是如何编写有意义的提交,并证明自己是一个不可或缺的团队成员。每一次经过深思熟虑的提交,不仅有助于项目的发展,还能巩固你在团队中的地位。
现在,让我们继续深入学习更高级的 Git 使用方法,帮助你在团队中大放异彩。
第三章:高级 Git 用法用于团队协作
现在,我们进入实际协作的部分。在本章中,我们将深入探讨你希望采用的各种协作实践。你将学习如何组织提交历史、管理复杂的分支,并在合并时解决冲突。本章的目标是帮助你能够很好地控制这种分支流的团队协作。
在这里,重点不仅仅是完成工作,而是以一种能增强团队合作的方式来完成。要认识到,管理整个代码库背后有一套深思熟虑的策略,旨在最大化团队的生产力和影响力。根据产品或项目的类型、团队的规模及其成熟度,会采用不同的策略。在我们深入 Git 命令之前,理解这个基础策略——分支策略,是至关重要的。
那么,让我们开始让你的协作变得更加顺畅高效吧。
本章将涵盖以下主题:
-
团队协作的分支策略
-
在分支上应用变更的方法
-
处理冲突
-
掌握更好的协作
技术要求
继续本部分的配置说明可以在以下 GitHub 仓库链接中找到。请确保已安装 Git 和 SSH 工具。对于 Windows 用户,建议使用 PowerShell。我也鼓励你获取关于不同命令和环境的最新信息。
github.com/PacktPublishing/DevOps-Unleashed-with-Git-and-GitHub
团队协作的分支策略
在团队协作的领域中,提交是至关重要的构建块。这些提交链接在一起,形成一个按时间顺序排列的历史记录,记录了你的项目的演变。这一历史记录通过分支进行组织和维护。
那么,工程师和团队如何将这些历史记录编织成一个连贯有意义的叙事呢?分支策略就是这个问题的答案。分支策略是一个有效管理 Git 中分支的开发策略,它能够促进顺畅的协作和服务交付。
为什么分支策略很重要
一个精心设计的分支策略不仅仅是锦上添花;它在团队开发环境中至关重要。你的分支策略会对你的 DevOps 流程产生连锁反应,影响部署单元和工作流效率。顺利协作的能力不仅依赖于团队内部的良好沟通,还会影响你的产品发展速度。这与我们在前几章中覆盖的所有元素相关,例如 CI/CD 测试的频率和方法论。本质上,你的分支策略在消除组织摩擦方面至关重要,而这正是 DevOps 的最终目标。
以下是为什么深思熟虑的分支策略是不可妥协的原因:
-
变更隔离:它允许单独的团队成员在不干扰彼此工作的情况下,处理不同的功能或缺陷。
-
风险缓解:分支策略可以保护主分支(通常称为 master 分支)不被未经测试或不稳定的代码破坏。
-
促进协作:通过良好的分支策略,多个团队成员可以并行在不同的分支上工作,从而提高团队的整体效率。
在 DevOps 的背景下,分支策略还具有以下功能:
-
自动化测试集成:分支策略可以设计为在不同阶段触发自动化测试。这样可以确保只有经过充分测试的代码才能合并回主分支,从而支持持续集成。
-
简化部署:一个组织良好的分支结构可以简化部署过程,使得代码更容易从开发环境移至暂存环境,最终到生产环境。
-
增强开发者体验:通过提高协作的透明度和效率,它改善了整体的开发者体验(DX),这是成功 DevOps 的关键策略。
-
特定环境的分支:为特定环境(开发、暂存、生产等)设置分支,有助于实现更加平滑和可控的部署。
-
增强安全性:通过在分支之间建立清晰的边界,分支策略可以通过控制对敏感代码的访问,并确保在合并到关键环境之前,所有更改都经过适当的审查和批准,从而增强安全性。
分支策略与分支政策
在软件开发和 DevOps 领域,分支策略和分支政策这两个术语可能看起来是同义的。虽然它们经常被混淆,但必须认识到,分支策略是一个更广泛的概念,涵盖了分支政策。
分支策略是一个全面的计划,概述了如何在开发工作流中管理、创建和集成分支。它不仅仅涉及处理分支的技术方面,还包括组织的规模、团队文化以及项目或产品的具体需求等背景变量。
分支策略,另一方面,是一组更为具体的规则或指南,用于分支管理。这些规则通常构成分支策略的骨架,作为模板,根据具体需求进行定制。有时,像Git Flow或GitHub Flow这样的策略名称(我们将在本章中介绍)被用作开发策略的名称。它们应该被视为分支策略的一种类型。著名的软件思想领袖马丁·福勒在他的文章《源代码分支模式管理》(Patterns for Managing Source Code Branches)中并未将这些视为策略本身,而是在观察一些分支策略部分进行讨论,具体内容请参见他的文章(martinfowler.com/articles/branching-patterns.html)。
因此,在建立分支方法时,选择一个作为基础的分支策略至关重要。这个策略应根据组织的独特需求和目标进行定制,以有效减少开发过程中的摩擦并加速软件发布。这种定制化的方法确保了分支策略不仅优化了工作流程,还与团队的文化和组织方面无缝融合。
较小且频繁的修改 vs 较大且不频繁的修改
目前存在许多分支策略。公司通常会创造特定的分支策略名称,并将其发布为最佳实践,如GitHub Flow。从根本上讲,所有的分支策略都可以映射到两个原则之一:频繁地进行较小的修改,或者偶尔进行较大的修改。
对于小型团队来说,集成变更和快速发布的摩擦自然较小。但在大型组织中,尤其是涉及大型产品或冗长审批过程时,摩擦不可避免地增加。更多的冲突出现,需要更多的检查来防止这些冲突。
然而,允许这些挑战拖慢开发过程可能会对产品或项目发布的及时性产生不利影响,最终影响业务的成功。
随着时间的推移,许多公司设计了各种分支策略来缓解这些问题。它们中的大多数都是现有实践的扩展,旨在减少组织或产品限制内的摩擦,目标是加速发布。
需要理解的是,没有任何一种分支策略是适用于所有情况的“万能解”。基础策略通常是根据团队的组成和文化来选择的,然后根据具体需求进行定制。
本节介绍了基于主干的开发、Git Flow 和 GitHub Flow 这三种分支策略,它们的映射如下:

图 3.1 – 分支策略映射
基于主干的开发以将较小、频繁的更改直接集成到主分支中而闻名。与此相对,Git Flow 因其较少频繁地集成较大更改的策略而著名。两者各有优缺点,选择其中之一通常取决于团队的具体需求和工作流程。
对于那些希望更频繁地发布更多更改并减少摩擦的大型组织,GitHub Flow 是一个很好的代表。这些策略基本上是将频繁小规模更改的核心原则适应大型企业的复杂性和规模。
像 GitHub Flow 这样的策略是在 Git Flow 的影响下构建的,但现在许多公司使用 GitHub Flow 作为模板,在此基础上构建他们自己的定制化分支策略。
分支策略的类型
现在,让我们来看一个典型的分支策略。
本节介绍三种典型的分支策略:
-
基于主干的开发
-
Git Flow
-
GitHub Flow
考虑根据您的团队或组织需要发布的频率以及您的产品和项目的规模来分析每种情况。
基于主干的开发
基于主干的开发(TBD)是一种软件开发方法,在这种方法中,开发人员在短生命周期的分支上工作,通常不超过一天,或者直接从一个名为主干或主线的单一分支上工作。其关键原则是尽量缩短分支的生命周期,以促进频繁的集成,并避免长期存在的特性分支所带来的问题,如合并冲突和代码库分歧。
在 TBD 中,主干始终保持工作状态并且健康,应该始终处于可部署状态。开发人员会选择一个小的功能或任务块进行开发,并尽快将其合并回主干。如果某个功能尚未准备好发布,可以使用功能标志来隐藏这些功能,直到它们完成为止,这样代码就可以合并而不影响最终用户。像功能标志这样的发布实践将在第五章中讨论。
如图所示,在 TBD 中,创建了许多短生命周期的分支,并将它们合并到主线中。

图 3.2 – 基于主干的开发
由于集成发生得很频繁,因此必须拥有一个强大的自动化测试套件,在每次代码合并到主干时进行运行。这确保了代码库始终保持稳定和可部署。持续集成(CI)工具通常与 TBD 一起使用,以自动化测试和构建过程,确保主干始终保持良好状态。
为了处理提供紧急修复的 hotfix,开发者可能会创建短生命周期的分支,这些分支在完成后立即合并回主干。这确保了能够迅速解决关键问题,而不会危及主干的稳定性。
TBD 的主要优点之一是它的简单性和专注于生成清晰、可部署的代码库。鼓励频繁的合并减少了合并冲突的可能性,并使所有开发者保持与最新版本代码的一致性。这在优先考虑快速迭代和快速交付的 DevOps 文化中尤为重要。
以下是基于主干开发的优缺点。
优点:
-
频繁集成:由于代码频繁合并,合并冲突的可能性较小,且更容易解决
-
快速反馈循环:频繁集成变更有助于在开发过程中尽早发现问题
-
简化的工作流程:没有大量长生命周期的功能分支,开发工作流程得以简化,管理起来更容易
缺点:
-
不稳定的风险:如果没有经过适当的测试,频繁的合并可能会导致不稳定的代码进入主干
-
不适合大型功能:对于非常大的或破坏性更强的变更,这种方法可能会导致问题,因为这些变更可能会使主干在较长时间内不稳定
总结来说,TBD(基于主干开发)注重快速集成,始终保持主干可部署,并通过自动化测试保持代码质量。它与敏捷开发和 DevOps 方法论非常契合,旨在通过简化开发过程来消除摩擦并改善开发者体验。
Git Flow
Git Flow 是一种主要针对健壮的项目版本控制的分支策略,特别适合有定期发布周期的项目。它引入了一种结构化的方法,涉及多种类型的分支,包括 feature、release、develop 和 hotfix,以及 main(或 master)分支。

图 3.3 – Git Flow
在 Git Flow 中,开发工作从 main 分支创建一个 develop 分支开始。develop 分支作为功能集成的分支,是所有开发者的分支合并的地方。当你准备开发一个新功能或修复一个 bug 时,你会从 develop 分支创建一个 feature 分支。这个隔离的环境使你能够在不影响整个代码库的情况下进行工作。
随着功能的进展,增量更改会提交到这个 feature 分支。一旦功能完成并经过测试,它会被合并回 develop 分支。为了准备发布,会从 develop 分支创建一个 release 分支。这个分支用于进行最后的小错误修复或文档更新。一切准备就绪后,release 分支将合并到 main 并标记版本号。同时,它也应该被合并回 develop,以确保未来的发布也包含这些更改。对于紧急的修复,可以直接从主分支创建一个 hotfix 分支。
Git Flow 提供了一个严格的结构,对于需要平衡稳定性和新特性的多个开发者的大型项目非常有益。它确保了开发过程是分开的但平行进行,使项目历史更易于理解和回退。
下面是 Git Flow 的优缺点。
优点:
-
结构化工作流程:适用于有计划发布周期的项目
-
隔离:功能分支允许开发者在隔离的环境中工作,使得管理复杂功能变得更加容易
-
热修复支持:专用的热修复分支使得快速修复生产版本变得更加容易
缺点:
-
复杂性:对于较小的团队或项目,Git Flow 可能引入不必要的复杂性
-
延迟集成:由于功能分支生命周期较长,这可能导致合并冲突或在开发过程中发现的错误
总结来说,Git Flow 为更复杂的项目提供了一种模型,确保代码库保持有序,并且发布管理得当。它特别适用于大型团队,其中协调和发布规划至关重要。
GitHub Flow
GitHub Flow 是一种简化的工作流程,鼓励持续交付实践。它只包括主线和短期存在的功能分支。其主要原则是分支、开发新功能、提交拉取请求、审核代码后再进行部署。拉取请求是 GitHub 的发明,是开发者通知团队成员他们已完成一个功能或修复的方式,随后该功能或修复会被审核和讨论,合并到代码库的主分支中。详细内容请参考 第四章。
GitHub Flow 很简单,但其背景不仅仅限于 Git。注意,图中包含了 GitHub 流程,如拉取请求和批准:

图 3.4 – GitHub Flow
它从创建一个新的描述性分支开始,该分支从默认的代码库分支派生,为修改提供一个安全的环境,不会影响主代码库。所有修改都提交并推送到这个远程分支。当准备好时,创建一个详细的拉取请求供团队审查,通常会与相关问题链接,以提供上下文。审查可能包括问题、建议或特定行的评论。针对反馈的后续提交会自动添加到拉取请求中。批准后,拉取请求会被合并到默认分支,将你的贡献并入主代码库。根据设置,可能需要解决合并冲突或满足批准标准。合并后,工作分支会被删除,但提交历史和讨论仍然可以访问,供日后参考。
本质上,GitHub Flow 促进了协作、透明性和增量开发,为团队项目提供了一种灵活而结构化的方式。
以下是 GitHub Flow 的优缺点。
优点:
-
简洁性:GitHub Flow 提供了一种简单的方式,只有一个主分支和短生命周期的特性分支,即使是新手也容易上手和管理。这种简洁性简化了开发过程,让开发者可以专注于提高生产力,而不是复杂的分支策略。
-
更快的部署:通过鼓励持续集成和交付,GitHub Flow 使团队能够更频繁地发布更新。这种快速的部署周期能让团队及时获得反馈并进行更快的迭代,减少从开发到市场的时间。
-
增强的协作性:GitHub Flow 中心的拉取请求机制促进了透明的代码审查和协作。它允许每个团队成员参与讨论,确保代码质量并共同拥有项目。
缺点:
-
平台兼容性考虑:采用 GitHub Flow 可增强协作与效率,特别是在 GitHub 上完全支持的情况下。然而,在与不同平台集成时,可能需要额外的工具或调整,以充分利用其潜力,确保在多种环境下无缝的项目管理。
-
适应复杂项目的能力:尽管 GitHub Flow 提供了一种简化、直接的方式,适合快速部署和持续交付,但在多团队协作、处理复杂项目时可能会遇到挑战。这种工作流注重简洁性和单一主分支开发,有时会限制对多个同时进行的开发任务或不同项目时间表的精细控制。例如,对于需要广泛集成测试或多个子团队之间协调的项目,可能需要额外的分支管理策略或更强大的发布规划机制。
分支命名规范 – 了解 Git 中命名分支的最佳实践
在 Git 和 DevOps 的领域中,命名是有效团队协作和代码管理的关键因素。当你在一堆分支中浏览时,一个清晰且具有描述性的名称能带来天壤之别,帮助你理解它们的具体功能、归属以及生命周期状态。建立一致的分支命名规范是有效分支策略的一个重要组成部分。
让我们深入探讨在 Git 中命名分支的最佳实践,旨在减少组织内的摩擦并加速发布。一个明确定义的命名规范使得工程师可以立刻理解一个分支的目的,无论它是为了某个功能、修复漏洞、热修复,还是一个实验性的尝试。在团队需要筛选数十个甚至数百个分支时,这种清晰度至关重要。命名规范为更透明、高效和精简的工作流程奠定了基础,使 DevOps 团队的每个人都能更轻松地进行协作。
分支命名规范和示例
以下是为每个主题命名的主要规范和示例。请注意,这些只是示例,每个团队的命名规范可能会有所不同。
这里有一些通用的指导原则:
-
使用破折号 (
-)、下划线 (_) 或斜杠 (/) 来分隔单词。斜杠尤其常用于分隔热修复和功能等主题。 -
小写名称:虽然 Git 区分大小写,但坚持使用小写字母有助于保持一致性并避免混淆。
-
简洁但富有描述性:分支名称应该能够立刻让人理解该分支的内容,同时尽可能简洁。
以下是每个分支名称的示例:
-
feature/,后跟简短描述。一个例子是feature/user authentication。 -
bugfix/,后跟简短描述。例如,bugfix/login-error。 -
hotfix/。例如,hotfix/xyz-security-vulnerability。 -
release/作为前缀。一个例子是release/v1.2。
上下文命名
尽管这些分类提供了一个很好的起点,你也可以考虑在分支名称中加入更多的上下文信息。例如,你可以在末尾附加问题编号(如feature/123-user-authentication),或者包括负责该分支的人员姓名(如feature/teamxyz-authentication)。
在本节中,我们认识到,一个稳固的分支策略是任何协作开发项目的支柱。我们已经探讨了分支策略在维持稳定的代码库同时促进持续集成和交付方面的重要性。无论是采纳 TBD 的小而频繁提交、Git Flow 的结构化角色,还是 GitHub Flow 的简易性,正确的策略对于团队的成功至关重要。
记住,这些约定应该与您更广泛的分支策略和政策保持一致,并根据您的团队规模、项目以及组织的独特约束和目标进行调整。一个精心选择的命名约定将有助于强化分支政策的有效性,帮助减少摩擦并加速发布。通过遵循明确的命名约定,您能够让团队更加高效地工作,并促进清晰和责任感的文化。
在接下来的章节中,您将学习如何将不同的开发线合并在一起,同时保持代码的完整性和历史记录。让我们带着信心继续前进,将分支的知识与 Git 合并的实际技能相结合。
如何在分支上应用更改
现在,您已经深入了解了 DevOps 中分支管理和工作流的复杂性,可能已经开始看到全局。您已理解您的个人提交如何为整体开发流做出贡献。接下来的步骤是连接各个点——更具体地说,考虑您编写的代码如何被合并到主干中。
代码库是一个充满活力的协作环境,记录了各种团队成员的贡献历史。在快速发展的环境中,我们可能会因为赶进度而急于提交或一次性推送大量的更改。然而,在合并时,必须考虑到这些更改如何有助于维持一致、可理解且稳定的共享环境。特别是在 DevOps 文化中,这一点尤为重要,因为目标不仅仅是快速部署,还包括无摩擦的协作。
在接下来的章节中,我们将探讨在 Git 中执行成功合并的各种策略和最佳实践,特别是针对 DevOps 环境中出现的需求和挑战。
合并与重基(Merging vs rebasing)
Git 提供了两种主要的技术来集成这些变化:合并(merging)和重基(rebasing)。虽然它们的最终目的是相同的——将不同的代码分支合并在一起——但它们在操作上有明显的差异。在深入了解实际命令之前,让我们先区分合并和重基。
合并(Merging)
合并会将源分支的内容与目标分支集成在一起。这个新提交将拥有两个父提交,保留被合并分支的独立历史。合并可能会显得繁琐,因为它保留了各个分支的历史,但在多人同时参与项目时,它在集成过程中提供了灵活性。主要有两种合并方式:非快进合并(non-fast-forward merge),这种方式会创建一个新的合并提交来记录合并过程;以及快进合并(fast-forward merge),这种方式则不创建合并提交。还有一个叫做 squash 的功能——它可以将多个提交压缩成一个提交进行合并。
在 GitHub 等平台上,默认设置是像下面的图示一样进行合并提交:

图 3.5 – 合并示例(非快进)
以下是合并的优缺点。
优点:
-
历史保存:合并保留了两个分支的历史,提供了详细的日志
-
简单性:通常初学者更容易理解
-
分支隔离:各个分支可以继续进行独立的更改,而不影响合并
缺点:
-
复杂的日志:虽然保留了历史记录,但合并可能导致日志历史复杂且杂乱
-
缺乏线性:项目历史变得非线性,难以浏览提交历史
变基
变基是将一系列提交移动或合并到新的基础提交的过程。它本质上是将功能分支中所做的更改重新播放到另一个分支之上。与合并不同,变基不会创建新的提交,而是重写提交历史,生成一个线性的提交顺序。变基的优点是其历史的线性。回顾历史时,很容易跟随主分支发生的变化,这对于修复 bug 非常有利。
一旦掌握变基,你可以为历史记录做出无噪音的贡献:

图 3.6 – 变基
以下是变基的优缺点。
优点:
-
更清晰的历史:变基会生成一个更清晰、线性的项目历史
-
消除噪音:它移除了在执行 git merge 时出现的不必要的合并提交
-
更简便的调试:有了更清晰的历史记录,调试变得更容易
缺点:
-
共享分支风险:变基可能会破坏历史,特别是在共享分支上,因为它会重写提交历史
-
复杂性:变基可能更难理解和正确执行
两种技术都有其优缺点。合并保留了原始分支历史,但可能导致日志复杂。变基提供了更清晰、更线性的项目历史,但存在风险,尤其是在共享分支上工作时。
探索 Git 中不同的合并方式
现在让我们深入实际步骤。我们将探索合并两个分支的过程,提供实践经验帮助你更好地理解每种方法。在本节中,我们将介绍一些最常用的分支合并方法,帮助你根据项目需求做出明智的决策。
git merge --ff – 保持直线
现在让我们深入探讨实际应用。本节将聚焦于 Git 合并中的一个常见默认行为,称为 --ff 命令选项。我们将逐步讲解,确保你理解它的工作原理以及何时使用它。
快速前进合并是 Git 中集成分支的最简单方法之一。本质上,快速前进合并将目标分支的末端移动到源分支的最新提交。
使用快速前进合并时,在查看历史记录时,实际上什么也没有改变,如下图所示。这就是快速前进合并的好处。HEAD 持续移动,合并可以顺利进行:

图 3.7 – 快速前进合并
在 Git 中,当基础(或目标)分支在特性分支创建后没有新的提交时,就可以进行快速前进合并。本质上,它消除了需要新提交来连接分支的需求,从而保持项目历史的线性。
快速前进合并的实际步骤
在本指南的上下文中,假设你有一个 main 分支和一个 add-description 分支。add-description 分支是从 main 分支派生出来的,你打算将特性合并回 master:
# Initialize a new repository
$ mkdir try-fast-forward-merge
$ cd try-fast-forward-merge
$ git init
# Add and commit initial README.md to master
$ echo "# My Project" > README.md
$ git add README.md
$ git commit -m "Initial commit on master"
# Create and switch to a new branch 'add-description'
$ git checkout -b add-description
# Make changes to add a description, add and commit changes
$ echo "This project is an example of how to use Git." >> README.md
$ git add README.md
$ git commit -m "Add project description to README.md"
此时,你的仓库结构应该类似于下图:

图 3.8 – git merge –ff (1)
现在,让我们通过切换回 main 分支来合并这些分支:
# Switch back to 'main' and perform a Fast-Forward merge
$ git checkout main
$ git merge add-description
# View the linear history
$ git log --graph --oneline
* 26d90bf (HEAD -> main, add-description) Add project description to README.md
* 37ecd54 Add project description to README.md
* a1164b9 Initial commit on master
现在,你的仓库历史应该如下所示:

图 3.9 – git merge –ff (2)
为什么在 DevOps 和团队协作中更倾向于使用快速前进合并
在后台,快速前进合并只是将 HEAD(指针)移动到最新的提交。而且,快速前进合并不会创建新的合并提交,保持 Git 历史的清晰和线性。这使得它成为一种简单且高效的操作。
快速前进合并在团队协作中通常更受欢迎,原因有几个:
-
简洁性:它们保持 Git 历史的线性,使得更容易跟踪
-
透明性:通过清晰的历史记录,更容易追踪变更、调试问题,并理解功能集成的顺序
-
效率:快速前进合并消除了额外合并提交的需求,从而简化了代码审查
然而,请记住,快速前进合并并非总是可行的。当你单独开发或进行简单开发时,可以使用这种方式,但在大多数开发中,通常有很多事情并行进行。当主分支和特性分支同时发生变更时,可能需要非快速前进合并,有时也称为三方合并。
git merge --no-ff – 保留分支历史
非快进合并,通常通过--no-ff标志来调用,提供了一种与之前讨论的快进合并不同的合并策略。与快进合并不同,快进合并将目标分支的指针移动到源分支的最新提交,而非快进合并会生成一个新的合并提交。这个新的提交有两个父提交:一个来自源分支,一个来自目标分支。
非快进合并可以将上下文嵌入到合并提交中,以便以后回顾时查看为什么进行此合并。

图 3.10 – 非快进合并
这种方法可以追踪特性分支合并到主分支的事实,保留了过去提交时的上下文。
非快进合并的实际步骤
假设你正在处理main分支和add-feature分支。以下是执行非快进合并的步骤:
# Initialize a new repository
$ mkdir try-no-fast-forward-merge
$ cd try-no-fast-forward-merge
$ git init
# Add and commit initial README.md to master
$ echo "# My Project" > README.md
$ git add README.md
$ git commit -m "Initial commit on main"
# Create and switch to a new branch 'add-feature'
$ git checkout -b add-feature
# Make changes, add and commit them
$ echo "Adding a new feature..." >> README.md
$ git add README.md
$ git commit -m "Implement a new feature"
现在commit日志如图 3.11所示。到目前为止,你所做的与在git merge --ff部分所做的相同。

图 3.11 – git merge --no-ff (1)
然后,让我们切换回main并执行非快进合并:
$ git checkout main
$ git merge --no-ff add-feature
以下编辑消息将出现在终端中。编辑提交信息并保存:
git merge branch 'add-feature'
编辑完成后,我们来看看当前的日志:
# View the history
$ git log --graph --oneline
* f58977f (HEAD -> main) Merge branch 'add-feature'
|\
| * a48c0a9 (add-feature) Implement new feature
|/
* fe93feb Initial commit on main
你的仓库历史将显示一个新的合并提交,指示add-feature分支是如何集成到main分支中的。

图 3.12 – git merge --no-ff (2)
为什么在 DevOps 和团队协作中使用非快进合并?
非快进合并提供的好处在各种 DevOps 和团队协作场景中都可能非常有价值:
-
上下文保留:在合并过程中生成一个新的提交,不仅保留了代码,还保留了历史和上下文。这种清晰的集成记录使得理解不同分支的更改何时以及如何被合并变得更加容易。
-
--no-ff提供了无价的透明性,清晰地记录了谁在何时做了什么更改,以及为什么做这些更改。这在较大的团队和复杂项目中尤为重要,能够帮助理解贡献的流动。
虽然合并提交可以提供丰富的上下文和历史记录,但如果过度使用或文档记录不充分,它们也可能会使 Git 历史记录变得杂乱。团队在选择合并策略时必须谨慎,并努力保持清洁的共享仓库。
git merge --squash – 压缩复杂性
git merge --squash 选项提供了一种不同的合并技术,它兼具清晰性和整洁性。虽然快进和非快进合并非常适合跟踪分支历史,但可能会有一些情况需要在合并之前将 feature 分支的更改浓缩为一个提交。这时,git merge --squash 就发挥了作用。
在压缩合并中,源(或功能)分支上的所有更改都会合并为目标(或主)分支上的一个单一提交。此操作有效地将功能分支的历史浓缩为一个提交,同时进行合并,提供了一个干净、易于跟随的 Git 历史。这使得更改处于未提交状态,允许你在最终提交之前修改差异。
尽管团队致力于保持干净的共享代码库,个别开发分支通常会因为各种尝试和错误而变得杂乱无章。压缩合并有助于通过防止这些杂乱的实验性日志进入生产历史,从而保持主代码库的整洁。
如下图所示,压缩合并在某些方面是最干净的合并方式,并且有很多好处。然而,请记住,它是一种合并更改的方式,丢失了之前的更改历史以及其他公司的提交历史。稍后将在本节中提到这一点。

图 3.13 – 压缩合并
压缩合并的实际步骤
假设你有一个main分支和一个add-multiple-features分支。要执行压缩合并,请执行以下操作:
# Initialize a new repository
$ mkdir try-squash-merge
$ cd try-squash-merge
$ git init
# Add and commit initial README.md to main
$ echo "# My Project" > README.md
$ git add README.md
$ git commit -m "Initial commit on main"
# Create and switch to a new branch 'add-multiple-files
$ git checkout -b add-basic-files
# Make some changes, add and commit them
$ echo "# HOW TO CONTRIBUTE" >> CONTRIBUTING.md
$ git add CONTRIBUTING.md
$ git commit -m "Add CONTRIBUTING.md"
$ echo "# LICENSE" >> LICENSE.txt
$ git add LICENSE.txt
$ git commit -m "Add LICENSE.txt"
现在,分支应该像以下图所示:

图 3.14 – git merge --squash (1)
让我们切换回主线并执行一个 squash 合并:
# Switch back to 'main' and perform a squash merge
$ git checkout main
$ git merge --squash add-basic-files
然后,这些提交将被压缩并成为一个单一的提交:

图 3.15 – git merge --squash (2)
然后,Git 会将未提交的更改添加到 main 分支。此时是完成合并过程的时候了。要完成合并,你需要提交这些未提交的更改:
$ git add .
$ git commit -m "Add repository standard docs"
$ git log --graph --oneline
* 6eb6df3 (HEAD -> main) Add repository standard docs
* ffc2ed5 Add CONTRIBUTING.md
* 2c5ad11 Initial commit on main
这将把 add-multiple-features 分支的所有更改合并为 main 分支上的一个新提交:

图 3.16 – git merge --squash (3)
为什么在 DevOps 和团队协作中使用压缩合并?
压缩合并为 DevOps 和协作开发提供了一套独特的好处:
-
原子性更改:压缩合并会创建一个包含所有功能更改的单一提交,这样在需要回滚时会更加方便。
-
减少噪音:压缩合并消除了来自主分支的许多小的、可能是实验性的提交的杂乱无章。这使得日志历史更加清晰,便于阅读和理解。
-
战略性提交信息:squash 使你能够创建一条综合性的提交信息,比一系列小的提交信息更有效地概括功能的目的和影响。
然而,值得注意的是,虽然 squash 合并可以简化历史,但它也可能会使历史变得模糊。来自功能分支的单独提交会在主分支中丢失,这使得理解每个单独更改的开发背景变得困难。在使用这种合并策略时,要谨慎并了解它对开发历史的影响。
squash 合并的伦理问题与陷阱
squash 其他人的提交有时可能会带来问题。
git merge --squash 命令是一个强大的工具,可以将多个提交合并成一个。虽然此功能能保持提交历史的清晰和可管理性,但当用于其他人提交时,它会引发伦理和实际问题。
在团队协作中,了解以下问题非常重要:
-
作者归属错误:默认情况下,执行 squash 的人将成为合并提交的作者,从而有效地抹去原始贡献者的历史。这可能会使团队成员感到沮丧,因为他们的贡献没有得到认可。
-
历史篡改:该命令会改变提交历史,这可能会被认为是不尊重原作者工作的行为。
如果保持单独提交的完整性很重要,考虑使用标准合并。这可能会导致历史更为复杂,但它能保留所有贡献者的工作和认可。
此外,git rebase 命令提供了对提交历史的更多控制,这对于清理或重新安排自己的工作而不影响他人非常有用。
接下来让我们探索这个命令。
git rebase – 以便清晰的方式重写
Rebase 是 Git 中另一种强大的技巧,它与合并有显著的不同。合并和 rebase 之间的主要区别在于它们整合更改的方式。git merge 将一个分支的更改合并到另一个分支,而 git rebase 则是将一系列提交移动或合并到新的基础提交上。
在团队协作的背景下,rebase 用于保持线性的项目历史,这可以简化调试并使代码审查变得更容易。然而,rebase 也有其复杂性和陷阱,通常最好在特定情况下使用。
在深入实践教程之前,让我们先大致了解一下 git rebase 的工作原理。rebase 的主要用途是将 feature 分支的更改放到另一个分支的顶部。
例如,考虑以下分支:

图 3.17 – Rebase (1)
在将 feature 分支 rebase 到 main 后,你的分支可能看起来像这样:

图 3.18 – Rebase (2)
最后,你可以合并回主分支,此时可以进行快速合并:

图 3.19 – Rebasing (3)
git rebase 的实际步骤
让我们以一个main分支和new-feature分支为例,来看一下如何进行 rebase:
# Initialize a new repository
$ mkdir try-git-rebase
$ cd try-git-rebase
$ git init
# Add and commit initial README.md to main
$ echo "# My Rebase Project" > README.md
$ git add README.md
$ git commit -m "Initial commit on main"
# Create and switch to a new branch 'new-feature'
$ git checkout -b new-feature
# Make some changes, add and commit them
$ echo "This is a new feature." >> NewFeature.md
$ git add NewFeature.md
$ git commit -m "Add new feature"
到此为止,你的分支历史可能会像这样:

图 3.20 – git rebase (1)
现在,假设在你工作在new-feature时,main上添加了新的提交:
# Switch back to 'main' and add new commits
$ git checkout main
$ echo "Updates to the project." >> Updates.md
$ git add Updates.md
$ git commit -m "Update main"
你的提交图现在已经分叉:

图 3.21 – git rebase (2)
现在,将new-feature分支 rebase 到main分支:
# Switch to 'new-feature' and rebase onto main
git checkout new-feature
git rebase main
让我们看看现在的情况:
$ git log --graph --online
* 43ea59e (HEAD -> new-feature) Add new feature
* 16e1878 (main) Update main
* 3021494 Initial commit on main
之后,你的分支将如下所示:

图 3.22 – git rebase (3)
现在是时候合并并完成git rebase过程了:
# Switch to 'main' and perform fast-forward-merge
$ git checkout main
$ git merge new-feature
当进行快进合并时,main和new-feature分支的 HEAD 将是相应的提交,如下所示:

图 3.23 – git rebase (4)
为什么在 DevOps 和团队协作中,rebase 是强大的
在 DevOps 文化中,rebase 的主要优势是它比合并产生更干净的项目历史。更清晰的历史记录更容易调试,更易理解,并且对于后期加入项目的开发者来说更具逻辑性。
这里有一些好处:
-
线性历史:比 git merge 创建的非线性历史更容易理解。
-
简化调试:历史记录更干净后,追踪某个特定 bug 何时被引入变得更容易。
-
代码整洁:rebase 鼓励你合并修复提交或拆分较大的提交,使得你的修改比其他开发者的更容易理解。
git rebase 的注意事项和陷阱
有一些黄金规则需要遵循:不要 rebase 公共(团队)分支。其中一条基本规则是避免对公共分支进行 rebase。rebase 对于清理功能分支是一个非常好的工具,但在公共分支上使用时,它可能会变成灾难。
使用git rebase时需要考虑的事项:
-
团队协作中的冲突:假设你已经 rebase 了一个公共分支并推送了更改。其他已经拉取了该分支旧版本的开发者,现在会有一个分叉的历史。当他们尝试推送他们的更改时,Git 会拒绝推送,迫使他们合并历史。这会增加额外的工作量并增加合并冲突的可能性。
-
复杂的合并:在公共分支已经 rebase 并且历史被修改后,将其与其他分支合并可能会变得非常具有挑战性。因为 Git 使用提交历史来确定如何整合更改,修改历史可能会使合并变得比需要的复杂得多。
-
上下文丢失:Rebase 可能会将提交合并在一起或更改它们的顺序,这可能导致这些更改的上下文丢失。这会使调试变得更加困难,并且可能使理解导致当前代码库的开发过程变得复杂。
Rebase 可能会很复杂且有风险,特别是对于缺乏经验的开发者来说。在最坏的情况下,你可能需要解决许多冲突,如果不小心处理,可能会导致错误和 bug。
通过理解并使用 git merge 和 git rebase,你可以处理几乎所有需要合并不同开发线的情况。每个命令在 Git 中都有其适用的场合,理解何时使用哪个命令是保持代码库干净且易于理解的关键——这在 DevOps 的世界中非常宝贵。
git cherry-pick – 选择特定的提交
Git 命令中最灵活的工具之一就是 git cherry-pick 命令。虽然之前的合并方法主要用于集成整个分支,git cherry-pick 允许你选择一个分支中的特定提交并将其应用到另一个分支。此方法在你只需要应用几个特定更改而不是将另一个分支的所有修改都带入时非常有用。
假设你有两个分支,main 和 feature。你意识到 feature 分支中的一个或两个提交应该被移动到 main,但你还不准备合并整个分支。git cherry-pick 命令正好允许你做到这一点:

图 3.24 – Cherry-picking(1)
你可以将 feature 分支中某个特定提交的更改 cherry-pick 到 main 分支上。此操作将在 main 分支上创建一个新的提交:

图 3.25 – Cherry-picking(2)
Cherry-picking 的实际步骤
现在让我们来看看 cherry-pick 更改并将它们合并到分支中的实际步骤。每个提交都会向每个分支添加一个文件。我们将这些提交中的一部分合并到 main。首先,我们添加这些文件:
# Initialize a new repository
$ mkdir try-cherry-pick
$ cd try-cherry-pick
$ git init
# Add and commit initial README.md to main
$ echo "# My Project" > README.md
$ git add README.md
$ git commit -m "Initial commit"
# Create and switch to a new branch 'add-base-documents'
$ git checkout -b add-base-documents
# Make changes and commit
# Add CONTRIBUTING.md
$ echo "# CONTRIBUTING" >> CONTRIBUTING.md
$ git add CONTRIBUTING.md
$ git commit -m "Add CONTRIBUTING.md"
# Add LICENSE.txt
$ echo "LICENSE" >> LICENSE.txt
$ git add LICENSE.txt
$ git commit -m "Add LICENSE.txt"
# Take a look at the 'add-base-documents' branch log
$ git log add-base-documents --graph --oneline
* 02ee2b4 (HEAD -> add-base-documents) Add LICENSE.txt
* a80e8ad Add CONTRIBUTING.md
* cfb060a (main) Initial commit
现在这些分支看起来像下图所示:

图 3.26 – git cherry-pick(1)
现在,让我们只选择 a80e8ad 提交并将其放到 main 分支上。请在你的环境中替换哈希值:
# Now switch back to the 'main' branch and cherry-pick the commit
$ git checkout main
$ git cherry-pick a80e8ad
[main 9a36741] Add CONTRIBUTING.md
Date: Sun Oct 29 16:04:56 2023 +0900
1 file changed, 1 insertion(+)
create mode 100644 CONTRIBUTING.md
# Let's check the 'main' branch log
$ git log --graph --oneline
* 9a36741 (HEAD -> main) Add CONTRIBUTING.md
* cfb060a Initial commit
在你成功 cherry-pick 该提交后,一个新的提交将被添加到你当前的分支(在此示例中为 main),并且该提交将包含 cherry-pick 提交中的更改。请注意,新的提交包含相同的更改,但有不同的提交哈希值:

图 3.27 – git cherry-pick(2)
为什么 cherry-pick 在 DevOps 和团队协作中很有用
团队开发需要灵活的开发方式。git cherry-pick 是一个在协作编码环境中非常有用的命令,允许团队选择性地整合变更并保持代码的完整性。
让我们看看 cherry-pick 可以提供什么价值:
-
选择性集成:它允许特定的 bug 修复或功能被移入生产环境,而不需要将所有开发分支的变更一起移动。
-
轻松回退:如果出现问题,你只需要回退一个小的变更,而不是整个分支合并。
-
清理历史:通过仅包含相关的提交,保持 Git 历史的整洁,使其更易于阅读和理解。
git cherry-pick 提供了高精度的分支间变更集成方式。它允许你精确选择要包含的提交,提供对项目历史的细粒度控制。这使得它成为任何 DevOps 工程师在追求灵活且高效的版本控制工作流时的宝贵工具。
在探索了 Git 中的各种合并策略之后,你可能会问自己:“我应该使用哪种方法?” 答案就像大多数工程问题一样,“这取决于情况。” 有多个因素在决定最适合你需求的 Git 合并策略时起作用。此外,值得注意的是,你不必坚持使用单一方法;你可以根据情况调整你的方法。理解这些因素可以帮助你做出明智的决定。
让我们检查以下因素,看看你应该如何选择:
-
项目复杂性:具有多个贡献者和并行开发线的复杂项目可能需要更一致的合并方式,以减少冲突。像 GitHub 这样的平台允许你为项目设置合并策略。
-
git merge可以减少错误发生的几率。然而,如果大多数团队成员都有经验,你可能会想利用rebase的优势,以保持更清晰的 Git 历史,并促进更顺畅的沟通。 -
Git 历史的期望清晰度:如果你非常重视清晰、线性的 Git 历史,那么选择快进合并或 rebase 可能是最好的方法。另一方面,如果你更看重项目开发过程的详细记录,那么非快进合并会更适合,能更好地保留变更的全面记录。
随着你前进,让这一节中的知识帮助你选择适合你项目的合并方法。通过实际场景练习来增强信心。记住,在 Git 中没有“一刀切”的答案——灵活性和适应性是你最好的盟友。
解决冲突
在一个协作开发环境中,冲突不仅仅是可能的——它们是不可避免的。当多个开发者在相同的代码库上工作,甚至是在相同的文件上进行修改时,很有可能会发生变化重叠,导致冲突。处理和解决这些冲突对于保持顺畅和高效的 DevOps 工作流程至关重要。
为什么会发生冲突
冲突通常发生在两个分支对文件的同一行或部分进行更改,然后尝试进行合并时。尽管 Git 非常强大,但它无法判断哪个更改应该优先。高效解决这些冲突的关键是理解冲突发生的原因,并尽可能主动预防它们。
如何解决 Git 中的合并冲突
让我们从基础开始。Git 中的冲突解决通常需要手动干预。以下是如何进行处理的步骤:
-
识别冲突。使用
git status查看哪些文件发生了冲突。 -
检查发生冲突的文件。打开文件并查找冲突标记(
<<<<<<<、=======和>>>>>>>)。这些标记区分了来自不同分支的更改:<<<<<<< HEAD Someone's change is here ======= Your change is here >>>>>>> branch-you-want-to-merge -
解决冲突。选择保留哪些更改。你可以保留某个分支的更改,混合两者的更改,甚至添加全新的内容。
-
提交已解决的文件。解决冲突后,需要将文件添加到暂存区并提交。
如何解决合并冲突
在协作开发环境中,合并冲突是不可避免的。关键在于知道如何高效地解决它们。根据冲突的性质,你可以遵循几种不同的解决模式。
当有一个明确的版本需要保留时
如果你已经合并了两个分支,并希望完全接受其中一个版本而忽略另一个版本,可以选择使用 git checkout --ours 或 git checkout --theirs:
-
checkout --ours:当发生合并冲突时,使用此命令保留你分支的文件:checkout --theirs: This command will retain the files from the merged branch, discarding the ones in your current branch:$ git checkout --theirs --
在运行这些命令之一后,你需要将更新后的文件添加到暂存区并提交。
当你需要评估两个版本时
如果不清楚应该优先采用哪个版本,则需要更细致的处理方法。
-
审查代码:在文本编辑器中打开冲突文件,手动检查冲突的代码行。决定保留哪些部分并相应地编辑文件。
-
沟通:如果有必要,与你的团队成员协商,决定保留哪些更改。可以通过面对面的讨论、虚拟会议或代码审查工具进行此操作。
-
运行测试:一旦你手动解决了冲突,运行测试是非常重要的,以确保代码库仍然稳定。
-
提交更改:在成功测试后,将已解决的文件暂存并提交到你的代码库。
通过小心处理合并冲突,你可以帮助保持干净的代码库,并促进团队内部更好的沟通。
支持合并活动的有用命令
合并是一个充满挑战的活动。拥有合适的工具和命令可以让这个过程更加顺利,减少出错的机会。在这一节中,我们将介绍一些有用的 Git 命令,帮助你有效管理合并。
git diff – 查找差异
git diff 命令是一个用于识别两组代码差异的关键工具。它帮助你查看两者之间究竟发生了什么更改,使得在冲突出现时更容易解决问题。
你可以通过以下方式将当前分支与目标分支进行比较:
$ git diff feature-branch..main
该命令逐行显示 feature-branch 和 main 之间的更改对比。你也可以专注于特定的文件,甚至是特定的代码行,这使它成为一个灵活的工具,可以在不同的粒度下发现差异。
尽管 Git 没有内置 git merge 的 dry-run 选项,你可以模拟一次合并以查看会发生什么:
$ git merge --no-commit --no-ff feature-branch
$ git diff --cached
该命令序列尝试将 develop 的更改合并到当前分支,但会在提交之前停止。你可以使用 git diff --cached 查看已暂存的更改。如果合并结果不符合预期,你可以简单地中止:
$ git merge --abort
git mergetool – 简化的工具指南,用于可视化冲突解决
当你遇到一个难以手动解决的合并冲突,或者你更喜欢图形界面时,Git 内置的 git mergetool 可以帮助你解决问题。
可以设置以下项:
-
kdiff3,meld,和vimdiff。 -
全局配置:使用以下命令将你选择的工具设置为所有 Git 仓库的默认工具:
# For example, if you chose vimdiff, you would run: $ git config --global merge.tool vimdiff $ git mergetool该命令打开你选择的图形工具,并并排显示冲突的更改。这个界面简化了理解冲突的过程,并帮助你决定保留哪些更改。这个设置允许你指定不仅是命令行工具,还可以指定像 Visual Studio Code 这样的现代工具,以图形化方式解决合并。
通过遵循这些简单的步骤,你可以以更直观的方式解决复杂的合并冲突,使这个过程对于所有技能水平的团队成员都变得更加可访问。
总的来说,冲突解决是任何工程师必备的技能。尽管冲突可能复杂且具有挑战性,但知道如何高效地解决冲突,可以让你的开发工作流程更加顺畅。这不仅仅是关于解决冲突本身,而是理解导致冲突的根本问题。这种细致的方法对促进团队之间更好的沟通和合作至关重要。
随着团队开发的推进,项目变得越来越复杂。冲突是不可避免的。冲突是一个很好的机会,帮助团队提升协作能力。你不应该害怕冲突,应该学会如何通过逐一解决它们来帮助团队进行更好的沟通。
精通更好的协作
到目前为止,我们已经讨论了如何在 Git 中处理合并并解决冲突。我们已经看到,你可以使用 git merge --squash 清理本地更改,或者使用 git rebase 调整提交历史。虽然保持工作区整洁是很好的,但理想的情况是保持代码库的干净,尤其是在推送到共享环境时。接下来,让我们讨论一下可以帮助你成为一个优秀协作者的命令,无论你是个人贡献者还是管理主共享分支的团队领导。
回滚时间
在任何协作项目中,错误是不可避免的。即使不是错误,很多时候你也会想要回退并回到过去。无论是一个破坏构建的错误提交,还是一个未能按预期实现的功能,回滚更改的能力至关重要。两个不可或缺的命令就是 git reset 和 git revert。
git reset – 回滚更改
git reset 命令允许你回退 Git 历史,本质上是将 HEAD 和可选的工作目录移动到某个特定的提交。这是一个非常强大的功能,但需要谨慎使用。有几种不同的方式可以使用 git reset。了解它们,以便你能有效地组织你的环境:
-
软重置:这会保持工作目录和索引不变,但会移动 HEAD。此命令用于当你只想撤销提交,而不影响索引工作树时:
git reset --soft command:

图 3.28 – git reset --soft
-
git add和git commit。文件更改将保持不变;这是在git reset未指定任何选项时的默认行为:git reset --mixed command:

图 3.29 – git reset --mixed
-
硬重置:这会重置索引和工作目录,永久删除未提交的更改。它会删除所有提交、更改和文件,因此所有更改将不再保留。如果你想删除所有内容,这就是方法:
git reset --hard command:

图 3.30 – git reset --hard
git revert – 在不重写历史的情况下撤销更改
与 git reset 不同,后者会更改提交历史,git revert 创建一个新的提交来撤销之前提交所做的更改。这在共享环境中非常有用,因为重写历史是不被鼓励的。
git revert 会创建一个相反的提交,如下所示:

图 3.31 – git-revert 创建一个提交来取消该提交
此命令将撤销指定哈希的提交所做的更改,并创建一个新的提交来记录此操作:
$ git revert <commit_hash>
以下是可以使用 git revert 的情况:
-
git revert不会重写历史,因此在共享分支上使用是安全的 -
git revert是一种干净的删除方法 -
git revert操作作为回滚策略的一部分
让我们了解一些额外的高级git revert选项:
-
回退多个提交:你可以通过指定提交范围来回退一系列提交:
-n or --no-commit flags:git revert -n <commit_hash>
This will apply the revert changes to your working directory but will not commit them. You can then make further changes and commit manually.
注意
注意插入符号(^)。这意味着回退范围内的最旧提交也会被包括在内。
掌握git revert对任何开发者或 DevOps 专业人员来说都至关重要。它提供了一种安全的机制来撤销更改,促进更好的协作和更可靠的代码。
git checkout – 不只是切换分支
在之前的讨论中,我们主要在切换分支的上下文中提到了git checkout命令。虽然这是它的主要功能之一,但理解git checkout是一个多用途工具也至关重要,它不仅可以操作分支,还能在单个文件或目录的粒度上进行操作。在本节中,让我们扩展对git checkout的理解,看看它如何在高效协作和错误修正中发挥重要作用。
这里是切换分支的基本命令回顾:
$ git checkout <branch_name>
那如果你只需要恢复一个文件到先前的状态呢?git checkout同样能帮你解决。如果你对一个文件进行了更改,但还没有提交,并且决定想要撤销这些更改,你可以这样操作:
$ git checkout -- <file_name>
这将丢弃你工作目录中的更改,并将文件恢复到最后一次提交的状态。有时候,你可能只想将另一个分支的部分更改应用到当前工作分支。git checkout也能做到这一点:
$ git checkout <branch_name> -- <file_name>
这个命令会将另一个分支中的特定文件检出到你当前的工作分支,让你根据需要混合和匹配代码。
它在团队协作中提供了多种灵活性:
-
快速回滚:如果生产环境出现问题,你可以从稳定的分支快速检出特定的文件。
-
选择性功能测试:在合并新功能之前,你可以只检出与该功能相关的文件进行测试。
-
轻松错误修正:错误是常有的事。能够单独检出文件使得更正错误变得更加容易,而不会影响到代码库中的其他部分。
注意
在文件上使用git checkout将会丢弃更改。确保这是你打算执行的操作。如果你正在进行实验,建议经常提交更改。这样,如果需要,你可以轻松回退到某个特定的提交。
理解git checkout的完整功能可以显著提升你的工作流程和协作效率。无论你是单独工作还是作为团队的一员,能够操作的不仅仅是分支,还有单个文件,这赋予了你更高的控制力和适应性。
组织你的工作环境
在团队开发中,你的个人工作区就像是一个实验室——一个可以自由创新、调试和测试的空间,而不会影响到整个项目。有效管理这个空间至关重要,Git 提供了一组强大的命令,帮助你高效管理。在本节中,我们将探索三种基本的 Git 命令——git clean、git stash和.gitignore——这些命令可以轻松保持你的工作区干净。
git clean – 清爽的开始
git clean命令提供了一种快速清理工作目录中未跟踪文件和目录的方法,基本上提供了一个全新的起点。在执行合并之前或之后,或者当你想清除不需要版本控制的杂物时,这个命令特别有用。
# Remove untracked files and directories
$ git clean -fd
git stash – 轻松暂停和恢复工作
git stash是一个非常有价值的工具,用于暂时保存你已做的更改,而这些更改还没有准备好提交。
作为开发人员,多任务处理往往是日常工作的一部分。无论是被紧急的 bug 修复打断,还是需要暂时切换上下文,git stash都能派上用场。此命令让你保存当前的更改,而无需进行正式提交,从而让你有自由切换任务,之后可以轻松回到之前的工作状态:
# Stash changes with a description
$ git stash save "WIP: New Feature"
# Reapply the stashed changes
$ git stash apply stash@{0}
此外,这里列出了一些常用的git stash命令,这些命令可以显著提升你的工作区管理:
-
git stash:此命令将你的更改保存到 stash,使你的工作目录保持干净。未跟踪的文件不会被 stash。 -
git stash save "Your Comment":此命令将你的更改保存到 stash,并允许你附加评论。这对于稍后使用git stash list识别 stash 非常有用。 -
git stash list:此命令显示你所有已保存的更改的列表。如果你使用了git stash save,你将在此看到你的评论,这使得识别每个 stash 变得更加容易。 -
git stash apply:此命令将最近保存的更改恢复到你的工作目录。stash 仍会保留在git stash list中。 -
git stash apply [stash@{n}]:此命令根据索引号恢复特定的 stash,你可以通过git stash list找到该索引号。 -
git stash drop:此命令删除 stash 列表中的特定 stash。 -
git stash drop [stash@{n}]:此命令根据索引号删除特定的 stash。 -
git stash clear:此命令删除所有 stash,清空你的stash list。
.gitignore – 自定义共享内容
在处理复杂项目时,你的本地环境可能会生成日志文件或包含个人配置设置——这些内容你不希望与团队共享。.gitignore文件允许你指定在git add期间应该忽略哪些文件和文件夹,从而确保它们只存在于你的本地环境中。
这是一个.gitignore文件的示例:
# Ignore all log files
*.log
# Ignore personal config files
config/personal/
谁做了什么?帮助你调试的绝妙方法
Git 有几种典型的方法来分析过去的情况。git blame 和 git bisect 是非常实用的工具,记住它们很重要,因为它们易于使用,可以帮助追溯并调试是谁做了什么修改。
git blame – 谁做了什么?
在共享代码库中工作时,可能会有需要了解特定代码行历史的情况。git blame 命令提供了一个文件的分解,注释每一行,显示最后修改它的人员以及该行属于哪个提交。这有助于识别负责特定更改的人员,这对调试、重构或简单理解某段代码存在的原因非常有用。
$ git blame file.txt
-L 选项允许你指定输出的行数:
$ git blame -L 5,10 README.md
$ git blame -L 5,+5 README.md
必须强调的是,git blame 的目的是不是为了将错误或可疑决策归咎于个人。在任何协作环境中——尤其是在团队合作至关重要的 DevOps 中——必须记住,错误是集体的责任。每个人都可能犯错;重要的是团队如何合作解决这些错误。从心理安全的角度来看,使用 git blame 应该视为增强团队沟通和识别改进领域的一种方式,而不是用来归咎他人的机制。
git bisect – 在提交范围内高效地寻找 bug
调试常常感觉像是在大海捞针,尤其是在拥有大量提交历史的大型代码库中。在 DevOps 领域,快速部署周期是常态,而在无数变更中引入的 bug 可能会造成严重后果。此时,git bisect 就发挥了作用,成为定位引入 bug 的特定提交的强大工具。
git bisect 命令通过在提交历史中执行二分查找来找到引入 bug 的提交。该过程从标记一个已知良好的提交和一个已知有问题的提交开始:
$ git bisect start
$ git bisect bad # If the current version is bad
$ git bisect good <Last known good version commit hash>
Git 然后会自动检查良好提交和有问题提交之间的一个中间提交。你需要测试这个提交,然后将其标记为良好或有问题:
$ git bisect good # or
$ git bisect bad
Git 将继续这一过程,逐步缩小提交范围,直到识别出罪魁祸首。一旦找到有问题的提交,就能更容易地理解出了什么问题并制定修复方案:
# Exit bisect mode and return to your branch
$ git bisect reset
在 DevOps 中,识别和解决问题的速度对保持卓越的运营至关重要,git bisect 成为了一种无价的工具。它能够很好地融入 DevOps 工具链,支持自动化测试,并促进更快速的回滚和修补。通过高效地定位错误,它增强了团队合作解决方案的能力,强调了快速反馈和持续改进这两个 DevOps 的基本原则。
版本控制的卓越性
在 DevOps 生态系统中,CI/CD使一切都变得增量化。然而,拥有具体版本的软件作为里程碑仍然至关重要。这些版本不仅是时间的标记;它们表明了代码的稳定性、新功能以及整体健康状态。它们还促进了更顺畅的回滚,并使隔离问题变得更加容易。
什么是语义化版本控制
语义化版本控制是一种版本控制方案,旨在传达每次新发布所涉及的具体变化含义。其格式由三个数字组成,数字之间用点分隔(例如:2.4.4)。每个数字具有特定的意义,与向后兼容性和引入的更改类型相关。
Git 中的标记
在 DevOps 环境中,标记成为了有效版本控制的基石。通过标记代码库历史中的特定点,您可以创建作为稳定发布点或重要里程碑的锚点。这些标记版本为您的 CI/CD 流水线奠定了基础,确保了持续一致和可靠的部署。
为了使标签更具信息性,可以考虑为其添加有用的元数据和上下文:
$ git tag -a v1.0 -m "Initial stable release"
您标记的提交通常应该代表稳定的发布点或关键的里程碑。这些是所有自动化测试通过、功能完成并经过同行评审的提交。从本质上讲,它们是您的生产就绪提交。
要标记特定的提交,可以使用以下命令:
$ git tag v2.4.4 32de0b2
Git 标记和语义化版本控制不仅是技术操作,还是战略性措施。它们确保每个人都能理解当前部署的内容以及它能执行的功能。
在本节中,我们已经掌握了如何在代码库中回溯时间的知识,了解何时以及如何使用git reset、git revert和git checkout。这些强大的命令确保我们能够优雅地撤销更改、纠正方向并保持清晰的项目历史记录——这些对于协作环境至关重要。通过git clean、git stash和正确的.gitignore设置来组织工作环境,为您提供一个干净的工作区,在这里,专注和清晰取代了杂乱和混乱。这个干净的工作区不仅仅是个人偏好的体现;它是为团队设定标准,确保只有有意的更改才能进入共享的代码库。
将这些技巧融入到您的日常工作中,不仅能提升个人表现,还能增强整个团队的协作能力。
概述
在本章中,我们深入探讨了分支策略的细节、合并和变基的复杂性以及如何处理冲突的艺术,目标是掌握团队内部更好的协作。我们从理解为什么分支策略至关重要,到将分支命名与组织目标对齐,为您奠定了构建稳健工作流程的基础。
我们探索了各种分支模型,如基于主干的开发、Git Flow 和 GitHub Flow,强调了它们各自独特的优势以及适合的应用场景。关于合并方法的讨论,提供了关于维护干净且富有信息的项目历史的工具,并解决了有时令人头疼的冲突解决问题。
当你完成本章内容时,请记住,掌握 Git 的道路是不断前行的。鼓励自己去探索更多,并通过自信地在开发工作中练习这些技能。拥抱挑战及其带来的学习机会,继续成长为一名开发者。Git 不仅仅是一个工具,它是你在 DevOps 旅程中的伙伴,通过消除摩擦并促进协作文化,提升开发者的体验。
第二部分:GitHub 卓越性与 CI/CD 基础
本部分强调了 GitHub 在 DevOps 中的作用,扩展了其用途,不仅仅是代码托管。它讨论了 GitHub 的功能,这些功能增强了团队协作,并促进了向现代 DevOps 实践的过渡。此外,还深入探讨了 GitHub Actions,重点关注工作流优化、先进的部署方法,如蓝绿部署和金丝雀发布,以及功能发布策略。这为深入理解 GitHub 在持续集成和部署中的能力提供了帮助。
本部分包含以下章节:
-
第四章,通过 GitHub 提升团队协作
-
第五章,通过 GitHub 驱动 CI/CD
第四章:提升团队协作与 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/)
第八章:反思与结论
让我们结束这一段通过 Git、GitHub、DevOps 和日益兴起的 AI 在软件开发中的世界的旅程。本章不仅回顾了这些技术带给开发者体验的深刻变革,还展望了未来,思考 AI 在重塑我们对软件工程方法的影响和潜力。
让我们一起回顾并庆祝在本书中学到的知识,为下一步前进打下基础。
本章将涵盖以下主要内容:
-
回顾 Git、GitHub 和 DevOps 的历程 —— 提升开发者体验
-
拥抱 AI 在开发中的应用 —— 软件工程中的下一步进化
-
结语
回顾 Git、GitHub 和 DevOps 的历程 —— 提升开发者体验
当我们停下来反思我们的旅程时,很明显,我们在 Git、GitHub 和 DevOps 领域的历程是具有变革性的。这段旅程不仅仅是技术概念中的一系列步骤,更是深入探讨了开发者体验的精髓。它关乎理解开发者如何工作,提升他们的影响力,并增加他们的工作满意度。让我们花点时间将这些关键要素联系起来,揭示每个要素如何为软件开发和团队协作的宏大蓝图贡献力量:
-
Git:不仅仅是一个工具,Git 代表着我们管理和互动代码方式的根本性转变。它不仅仅是追踪变化,更是创造一个流畅、协作的环境,在这个环境中,错误可以被撤销,分歧的想法可以共存,最终无缝合并。这种版本控制的基础至关重要,因为它为随后的更高级的实践和方法论奠定了基础。
-
GitHub:GitHub 作为一个枢纽,将编码的孤独行为转变为协作的努力。在这里,焦点不仅仅是管理代码,而是管理代码背后的人和流程。作为一个协作平台,GitHub 引入了一个生态系统,在这个生态系统中,代码审查、问题跟踪和文档管理汇聚,创造出一个更加协调和透明的工作流程。这里是开源和 InnerSource 思维真正生动体现的地方,促进了社区通过集体努力推动创新。
-
DevOps:DevOps 不仅仅是一组实践,它是一种文化转变。其引入强调了对持续集成和交付的承诺,目标是在确保软件质量的同时缩短开发生命周期。DevOps 鼓励共享责任的心态,自动化流程和频繁、可靠的发布,所有这些都建立在我们在 Git 和 GitHub 中学到的原则基础上。
-
开发者体验:虽然许多关于 DevOps 的书籍在此处结束讨论,但我们的关注点延伸到组织环境中开发者体验的更广泛、更加整体的层面。这个层面至关重要。改善开发者体验是关于创造一个让开发者感到被重视、能够高效工作、并且有自主权做出重要决策的环境。这包括提升有形和无形的方面,从简化工作流程和有效工具到培养开放沟通和欣赏个人专业知识的文化。开发者体验与 Git、GitHub 和 DevOps 的工具和实践紧密相连。在 Git 中,它是关于赋予开发者控制和灵活性。在 GitHub 中,它是关于促进合作和社区。在 DevOps 中,它是确保从编码到部署的整个软件生命周期顺畅高效。
回顾过去,很明显,穿越 Git、GitHub 和 DevOps 的旅程不仅仅是掌握工具或技术。它是关于拥抱一种将开发者体验置于首位的哲学。在这里,每一个元素——版本控制、协作和操作效率——都扮演着关键角色,当这些元素和谐统一时,它们会带来一个更高效、更令人满意且更具影响力的开发环境。因此,这段旅程不仅仅是技术的进步,更是文化的演变。
在开发中拥抱人工智能——软件工程中的下一个进化步骤
现在让我们把注意力转向一个正在重塑行业的变革性力量:人工智能(AI)。这一进入人工智能领域的转变标志着一个范式的变化,不仅仅是我们如何开发软件的方式发生了变化,更是我们如何看待和与技术互动的方式发生了变化。现在,让我们一起来思考人工智能是如何彻底改变开发过程,推动我们拥抱学习和创新,最终引领我们走向一个技术与人类创造力融合的未来。
人工智能不仅仅是开发者工具箱中的另一个工具;它代表了开发过程中的根本性变化。随着人工智能具备生成代码的能力,开发领域正在经历一场巨变。在我看来,这场革命并不是取代人类开发者,而是增强和扩展他们的能力。人工智能使开发者能够集中精力进行创造性问题解决和战略思考,将一些常规编码任务交给人工智能来完成。
人工智能在开发中的出现是对持续学习和适应的呼唤。如果好奇心一直是人类进步的引擎,那么在人工智能时代,它依然是驱动力。拥抱人工智能开发不仅仅是学习新技术;它是培养探索和创新的思维方式。随着人工智能的发展,可能性边界不断扩展,给那些愿意探索和学习的人提供了无尽的机会。
技术历史充满了紧张与兴奋的时刻,从互联网革命到开源的崛起,再到云计算的出现。这些里程碑事件都曾带来关于工作岗位被取代的担忧,但它们实际上为创新和发展开辟了新的视野。正因如此,工作岗位的数量并没有减少,反而增加了。人工智能,像它的前辈们一样,不一定是工作的过时预兆,而是进化的灯塔。
这种进展或许反映了我们人类固有的不断前进的欲望,拒绝停滞不前。在资本主义框架下,这种不断创新和改进的驱动力几乎是不可避免的。我们集体选择了创新与进化。然而,关于人工智能的讨论不仅仅局限于技术层面的影响,还涉及到伦理和监管的考量。随着人工智能的日益普及,关于监管的呼声也越来越高。未来的道路或许充满不确定性,但这是我们作为一个集体社会共同开辟的道路。
将人工智能融入开发者的工具包提供了无数提高效率、自动化繁琐任务并促进创新的机会,用以创造新的软件解决方案。对于开发者来说,人工智能可以简化编码过程,更快速地识别漏洞,并个性化开发体验,从而提高生产力和工作满意度。此外,开发者通过与人工智能互动,可以加速学习曲线,这使得人工智能不仅仅是提升生产力的工具,更是学习中的强大盟友。当开发者掌握人工智能时,他们不仅适应了不断变化的技术环境,也在塑造这个环境中发挥着关键作用。这一转变强调了提升技能和拥抱新工具与方法的重要性,确保开发者始终站在创新的前沿。
当我们站在这一交汇点上,信息明确——人工智能不仅仅是一个技术现象,它是一个转型的力量,我们必须集体拥抱它。让我们带着乐观和决心迈入这个新时代,认识到人工智能是一个增强我们能力并扩展我们潜力的工具。是时候与人工智能作为盟友一起探索、创新和进化了。这不仅仅是技术进步的旅程,更是人类成长与发展的旅程。让我们共同踏上这条激动人心的道路,迎接人工智能带来的挑战和机遇,在此过程中重新定义软件开发的未来!
结束语
当我们走到本书的结尾时,我衷心感谢你与我一起踏上这段旅程。从 Git 和 GitHub 的基础知识到广阔的 DevOps 世界,再到开创性的人工智能领域,我们的探索跨越了现代软件开发的多元化领域。
本书的核心目标之一是阐明现代开发方法,如 DevOps,如何无缝地融入开发者体验的更广泛背景。这不仅仅是对生产力或满意度的讨论;这是邀请你将开发视为一个整体、相互联系的过程,其中管理、协作和技术熟练度相互交汇。
本书的一个重要主题是在 DevOps 背景下对失败的重新定义。失败,作为一种挑战,不再是阻碍,而是成长、学习和改进的机会。它是关于为安全实验建立护栏,拥抱早期的失败,并培养一种协作和创造力至上的文化。这一理念不仅限于代码,还包括文档和知识共享,利用诸如 Git 等工具作为集体智慧的仓库。
再次提醒,展望由 AI 塑造的未来,让我们以乐观的心态和创新的愿望来迎接它,而不是恐惧。AI 不是威胁,而是我们通过技术让世界变得更美好的旅程中的伙伴。本书鼓励你放下恐惧,拥抱一个协作、创造力和技术交汇的未来,从而提高满意度,增强我们的工作对社会的影响力。
总之,我鼓励你将本书作为克服恐惧的工具包之一——对失败的恐惧和对尴尬的恐惧。拥抱 Git、自动化和 AI 辅助协作等技术,拓宽视野,重新发现工作中的乐趣。通过这样做,你会发现自己有更多时间投入到那些重要且富有创造性的任务中。
请记住,归根结底,一切都是关于协作的。很少能单独完成事情;大多数成就都是多人协作的结果。虽然本书可能让你接触到各种技术,但它也是一本关于最佳协作实践的指南。无论是与同事合作,还是与尚未见面的开源社区成员合作,利用这些知识来增强和深化你的协作努力。
一起,让我们拥抱变化,探索新的可能性,享受开发和协作的艺术!
八代幸


浙公网安备 33010602011771号