本文最初发表于 谷歌的开发者网站,由 InfoQ 中文站翻译分享。
近年来,软件工程经历了一次深刻而根本性转变:以前我们的大多数代码都是手工编写的,而现在我们要求 AI 编码助手和智能体为我们生成大量的代码。但是,AI 需要监督,所以必须由我们人类来阅读它们生成的代码、清理它,并验证它是否按照我们的意愿工作。由于 AI 对其生成的代码所必须运行的更大上下文的认识有限,我们必须定义系统架构、设计服务之间的边界,并确保生产环境的整体安全性和可靠性。
在这个范式中,我们对开发工具的需求也在发生转变。
历史上,开发者通常根据编程语言易于编写代码的程度来衡量其生产力。但当编码智能体可以在几秒内生成数百行语法有效的代码时,人类编写代码的速度就不那么重要了。现在真正重要的是审查、验证和维护那些已经写好的代码。
换句话说,AI 越来越像你的队友,它有点像独行侠,但毕竟是你的队友。最重要的是我们如何作为一个团队进行合作。
促使 Rob Pike、Robert Griesemer 和 Ken Thompson 在 20 多年前在谷歌创建 Go 编程语言的原因恰好是考虑到了团队驱动开发。当其他语言迅速增加特性并试图扩展表达程序逻辑的方式数量时,Go 专注于一个更宏大的愿景,那句是 为软件工程服务的语言设计。
软件工程与编程不同。编程是通过编写代码并运行它来解决问题,而软件工程则是与他人协作来设计和实现一个随时间演进的、持久性的系统的行为。编程是软件工程的一部分,但仅仅是一部分而已。
面向软件工程的语言设计不仅需要一门语言,还需要一个横跨整个软件开发生命周期的端到端平台,以及围绕它的工具。这需要具备带有一定倾向性的简洁性,以便整个团队能够以相同的方式组织、格式化和测试他们的代码。这需要强大的兼容性保证,以便你今天编写的代码不仅在十年后还能工作,而且在十年后仍然是好代码。这需要一个强大的生态系统,拥有全局的依赖管理系统,可以随着你的团队而扩展。这需要通过合理、稳健的安全考量和工具贯穿始终来完成所有这一切。
这些元素的结合是可扩展、长期团队合作的基础,使我们能够构建在原作者离开多年后仍然可维护的系统。现在,AI 成为了团队的一部分,这个基础比以往任何时候都更重要。
Go 最突出的特点之一是它不仅仅是一种语言,而是一个平台。从一开始,Go 就配备了一个健壮的、端到端的工具链,涵盖软件开发生命周期的各个阶段。Go 平台提供了开箱即用、内置的 格式化工具、测试框架、依赖管理 和高级安全工具,所有这些都可以直接从标准工具链访问。这个平台与一个综合的标准库结合在一起,消除了复杂的外部框架的需要,提供了无与伦比的一致性基线。


Go 是一个平台,在整个软件开发生命周期中都有开发者的接触点。
这些功能和工具最初是为了增强人类的能力而构建的,但事实证明 AI 和人类有着惊人相似的需求。当 AI 智能体被要求在没有外部验证的情况下迭代重构代码时,其性能可能会迅速降低,就像人类手工重构一样。第一遍可能有 95% 的正确性,但后续的交互会复合错误率并污染上下文窗口,降低准确性同时增加 token 成本。但使用 Go,AI 模型可以利用平台的端到端工具链来更快、更便宜、更可靠地操作 Go 代码,生成更高质量、更安全、更正确的代码。
这种集成工具还有第二个不太明显的优势:生态系统范围内的一致性。因为绝大多数 Go 开发者都使用相同的核心工具,整个社区统一地一起前进,无缝地采用主要的语言增强,这是跨运行时、IDE 和包生态系统的。这种统一的方法因 Go 的标准库而得到加强,标准库通过减少程序逻辑的差异和促进重复的、可预测的习语来创造项目间的进一步的一致性,这些习语开发者和 AI 都能更快地理解。这种结构一致性不仅能够帮助人类团队维护大型的代码库,而且为 LLM 创建更干净、更标准化的训练数据。
Go 另一个突出的特征是它优先考虑 可读性而不是易写性。Rob、Robert 和 Ken 认识到开发者花在阅读现有代码上的时间远多于他们敲代码的时间。在仅有人类编码的世界中,这种设计理念表现为一种文化,它重视 简洁而不是聪慧,并明确拒绝其他语言所推崇的语法魔法。Gophers 经常谈到他们非常喜欢无法分辨团队中谁写了某一段代码的感觉,因为这些代码看起来都是一样的。
在 AI 驱动开发的时代,这种可读性优先的哲学变成了一个力量倍增器。在历史上,个人开发者可能倾向于通过简洁的语法、隐式类型和巧妙的快捷方式来加速原型制作,而 智能体人体工程学 以及相应的人工验证循环,则需要完全相反的东西:可预测性、明确性和严格的结构。在使用 AI 时,软件开发生命周期的速度限制瓶颈完全从生成过程转变成了验证过程。如果一种语言提供了十几种不同的方式来表达相同的逻辑,AI 模型必然会生成一个支离破碎、杂乱无章的语法混合体。对于人类审查者来说,验证代码变成了一项令人筋疲力尽的解读意图的任务。
Go 通过严格的一致性解决了这个问题。通过内置的 gofmt 工具确保唯一的、标准化的格式,并提供了有意限制复杂抽象的语言设计,Go 确保所有代码(无论是由资深工程师、初级贡献者还是 LLM 编写的)看起来都是一样的。当语法完全可预测时,人类开发者可以更快地发现虚构的 API 调用、逻辑缺陷或安全漏洞。而且,因为这种标准化扩展到了 Go 语言的开源生态系统,模型在标准化数据上进行训练,使它们能够更好地以更少的尝试生成正确的且符合习惯用语的 Go 代码。
最终,对人类清晰的语言对 AI 模型来说本质上也是清晰的。当 AI 继续加速我们生产的代码量时,Go 对可读性的承诺确保我们可以扩展自己的系统,而不会失去理解、验证和安全维护它们的能力。
但是,可读性和开发者生产力只是战斗的一半。一种语言可能像我们希望的那样易读和高效,但如果生成的应用程序很脆弱、不安全或在负载下表现不可预测,那么它在生产中就没有用武之地。
在 Go 语言中,第一道防线是 Go 的静态类型系统,它对智能体代码起着自动安全网的作用。LLM 经常在文件间的结构边界和类型一致性上无能为力,这会导致虚构的属性和沉默的、定时炸弹式的缺陷。在 Python 等动态类型语言中,这些虚构的属性通常会通过基本的语法检查,只在运行时特定的生产工作负载下才会导致系统崩溃。在 Go 中,编译器会立即拒绝这些错误。如果 AI 智能体尝试使用不存在的方法、传递不正确的类型或留下未初始化的变量,代码根本无法编译。与 Go 的快速编译速度相结合(比 Java、C#、Rust 和其他编译的、生产级语言快好几个数量级),智能体可以 在高效的自我纠正循环中 迭代化地细化和修复自己的语法和类型错误,在人类队友审查它之前就能交付语法正确的代码。
除了编译器之外,Go 的“内置电池(batteries-included)”理念(指的是其标准库极其强大且功能完备的核心理念——译注)解决了 AI 生成代码内在的一个关键安全风险:软件供应链。当被要求实现一个功能时,LLM 依赖于它们的训练数据,这经常导致它们建议陈旧的、不维护的,甚至恶意的第三方依赖。Go 的综合标准库自然会引导 AI 模型使用优化的、安全的、官方维护的包,而不是拉入外部依赖。这大大减少了供应链漏洞的攻击面,并保持了代码库的精简和可维护性。


Go 的漏洞管理系统通过仅展示实际代码调用函数中的漏洞来减少噪音。
当需要外部依赖时,Go 的平台基础设施保证了完整性。每个曾经导入到 Go 程序中的模块的校验和(checksum)和缓存副本都被记录在 Go 的校验和数据库和模块镜像 中,防止了中间人攻击并消除了依赖消失或无声更改的风险。此外,Go 的漏洞数据库和集成漏洞扫描工具 govulncheck 会跟踪这些依赖中已知的漏洞,并标记调用易受攻击的符号的代码。这为人类审查者和 AI 提供了低噪音、高度可操作的反馈,他们可以用精确的方式修补漏洞。


模糊测试是一种自动化测试,持续操纵程序的输入来查找缺陷。
最后,Go 的内置测试框架和 原生模糊测试 工具为持续验证提供了一个标准化、严格的沙盒。Go 开发者及其 AI 队友不必依赖于拼凑外部测试工具和框架,而可以使用原生工具链来编写和运行健壮的测试。通过运行模糊测试来暴露隐藏的边界场景下的缺陷,AI 可以迭代式地加强自己的逻辑以对抗随机的、不可预测的输入。其结果就是能够形成一个高度可靠的软件开发生命周期,其中代码在投入生产之前得到了彻底的强化。
虽然可读的代码能够让你将其投入生产环境,可靠的代码能够让你在今天使其保持流畅地运行,但软件系统真正的衡量标准是它在第二天及以后的可维护性。代码库是活的系统。它们会自然衰退、积累技术债务,并且必须要 不断适应变化的需求。当人类开发者是软件的唯一作者时,这种维护负担是你运维成本中可预测的组成部分。但当自主 AI 智能体可以生成数百个 pull request 并随意重构整个服务时,代码库演进的速率和架构漂移的可能性大幅提升。
Go 对这种加速的主要答案在于它著名的 兼容性承诺。在 Go 中,兼容性不仅仅是便利,它是一个关键的安全和运维要求。因为兼容性承诺,为 Go 1.0 编写的 15 年前的代码 将在最新的 Go 工具链上编译和运行,无需任何更改。而且,因为 Go 致力于永不破坏 向后兼容性(永远不会有 Go 2.0!),Go 代码永远不会遭到破坏。相反,当 Go 编译器和运行时变得更好时,你的代码也会变得更好,无需任何更改:只需升级、重新编译,并享受它的收益即可。
这种长期持久性在与 Go 的运维可移植性搭配时效果更好。Go 直接编译成单个静态二进制文件,零系统依赖。当自主的 AI 智能体越来越多地作为系统管理员运行(启动微服务、执行脚本和通过命令行接口与环境交互)时,这种自包含的设计变得比以往任何时候都更重要。而且,因为 Go 编译器可以跨操作系统和系统架构进行交叉编译,这些 AI 智能体可以根据需要轻松地为所有可能的目标构建二进制文件,而无需复杂的构建系统。


Go 提供了数十种预置的现代化器(modernizers),通过将旧版代码模式确定性地更新为最新的惯用写法和语言特性,以保持代码的一致性。
为了应对架构漂移问题,Go 提供了一套内置的、确定性的工具,其目的是大规模地重构和现代化代码库,乃至整个 Go 生态系统。这其中包括 Go 官方语言服务器 gopls,以及全新重构的 go fix,现在则引入了 “现代化器”(modernizer)的概念。现代化器 通过将旧版代码模式确定性地更新为最新的惯用写法和语言特性,以保持代码的一致性。在大规模应用中,这种机制不仅推动了代码的演进,也带动了整个 Go 生态的向前发展,从而在各类库、开源项目以及第三方代码库之间维持了高度的统一性。此外,由于这些工具是标准化的,并且直接内置于 Go 平台中,AI 智能体可以利用它们来安全地重构包结构、管理依赖项以及清理技术债务,而不会破坏代码库的稳定性。
最后,Go 通过内置的可观测性和性能调优工具,确保这种可维护性能够直接延伸到生产环境中。Go 运行时开箱即用地内置了性能分析(profiling)和执行追踪功能,让开发者能够深入洞察应用程序在负载下的行为表现。同时,编译器也原生支持基于配置文件的引导优化(profile-guided optimization),即利用真实的生产环境配置数据来编译出高度优化的二进制文件。当这种能力与 AI 编排的部署流水线相结合时,就形成了一个高度精密的闭环优化周期:生产数据可以被自动反馈给编译器,用于重新构建和优化系统。
随着开发者编写的代码越来越少,编程语言的选择反而变得比以往任何时候都更加重要,这似乎有些违反直觉。然而,当代码生成被外包给 AI 时,软件工程的主要瓶颈已完全从“编写速度”转移到了“审查、验证和维护的严谨性”上。那些历史上优先考虑松散原型设计和巧妙隐式捷径的语言,如今在应对碎片化的 AI 智能体输出时,往往难以保持系统的稳定性。相比之下,Go 从诞生之初就是为了解决大规模、长期协作的挑战而设计的。其“阅读优先”的清晰度、生产就绪的特性以及全平台的一致性,提供了极其精确的确定性护栏,使其能够在不牺牲可靠性、可维护性或系统完整性的前提下,完美吸收 AI 队友的高频输出。
归根结底,AI 是你最新的队友,它是一个需要强大护栏才能发挥作用的超级高产贡献者。当你基于 Go 进行构建时,你不仅仅是在编写代码,你是在建立一个稳健的、能够自我纠错的平台,让人类和 AI 能够在这个平台上安全地协作,并对生产系统进行迭代。
开始体验
准备好尝试了吗?你可以按以下步骤开始:
- 前往 go.dev,按照 安装说明下载最新版本的 Go。
- 如果你使用的是基于 Visual Studio Code 的 IDE(如 Antigravity),请安装 官方的 Go 扩展插件。
- 明确指示你的 AI 智能体使用 Go 工具链,或者通过预加载的技能(例如,这个 热门社区仓库中提供的 Skill)来实现。
- 让你的 AI 智能体用 Go 为编写一个新应用吧!
来源:
https://developers.googleblog.com/why-go-is-an-ideal-language-for-ai-assisted-software-engineering








