2026-02-21 07:58:47

本文永久链接 – https://tonybai.com/2026/02/21/safety-vs-delivery-speed-why-farewell-rust-in-2026
大家好,我是Tony Bai。
在软件工程的铁三角中,Rust 占据了“安全性”与“性能”的绝对高地。凭借借用检查器(Borrow Checker)和极其严格的类型系统,它向开发者承诺了一个没有内存错误、没有空指针崩溃的完美世界。
然而,在商业软件开发的战场上,还有一个至关重要的维度往往被技术纯粹主义者忽视,那就是——交付速度(Delivery Speed)。
近日,资深工程师 Dmitry Kudryavtsev 发表了长文《Farewell, Rust》,详述了他为何忍痛将一个运行了多年、已盈利的 Rust 项目全盘重写为 Node.js 的心路历程。这篇文章也引发了一场关于“为了极致的安全性,我们是否值得牺牲过多的交付速度?”的深刻辩论。

Dmitry 绝非那些被即时编译(JIT)宠坏的脚本小子。相反,他的技术底色是硬核的 C/C++。
早在高中时代,他就沉迷于指针的魔力,痴迷于手动管理内存的掌控感。他写过 3D 渲染器、IRC 机器人,甚至操作系统内核。然而,由于第一份工作是 PHP Web 开发,他被迫进入了动态语言的世界。虽然 PHP、Python 和 Ruby 带来了 Web 开发的极速体验,但在内心深处,他始终怀念 C 语言那种“压榨硬件每一滴性能”的快感,同时也痛恨 C 语言中防不胜防的内存安全漏洞。
直到 Rust 横空出世。
对于像 Dmitry 这样的工程师来说,Rust 简直就是“鱼与熊掌兼得”的梦想:
于是,他做了一个所有热血工程师都会做的决定:为了追求极致的质量与安全,用 Rust 从零构建一个商业 Web 应用。
起初,一切都很完美。他在 2023 年底成功上线了项目,甚至因此受邀在两个技术大会上发表演讲。但随着时间的推移,业务逻辑日益复杂,“安全性”的红利开始被“交付速度”的损耗所抵消。到了 2026 年初,为了项目的生存,他不得不做出了那个艰难的决定:告别 Rust。
Dmitry 的文章之所以珍贵,是因为他用亲身经历证明了:在 Web 开发的特定场景下,Rust 引以为傲的“安全性”机制,如何一步步变成了拖慢“交付速度”的罪魁祸首。
在后端逻辑中,Rust 的类型系统坚不可摧。但当数据流向前端(HTML/Email 模板)时,这种为了安全而设计的严格性,变成了修改 UI 时的噩梦。
对于商业应用,支持多语言是刚需。
虽然 Mozilla 开发了 Project Fluent,但 Rust 生态中缺乏成熟的、开箱即用的 i18n 解决方案。你往往需要为了“正确性”而去处理繁琐的加载逻辑和类型绑定,编写大量的胶水代码。而Node.js生态中的i18next 等库不仅极其成熟,还能配合 TypeScript 提供键值级别的类型安全。Node.js 原生内置了完整的 ICU 标准(Intl API),处理货币、日期、复数格式化信手拈来。在这一点上,Rust 开发者需要花费数倍的时间来实现同样的功能,严重拖慢了产品推向全球市场的速度。
Web 业务充满了动态性:用户提交的 JSON 结构可能是不确定的,筛选条件的组合可能是无穷的。Rust 试图用静态类型系统去约束这些动态行为,结果就是开发效率的暴跌。
这是最让 Dmitry 崩溃的一点,也是“交付速度”最直观的体现。
Rust 的 Web 生态虽然在成长,但面对长尾需求时仍显稚嫩。
Dmitry 的文章并没有否定 Rust 的价值。相反,他依然热爱 Rust,依然怀念那些与编译器“斗智斗勇”并最终获得完美代码的日子。
他的结论非常客观,为所有正在做技术选型的团队提供了一把衡量“安全”与“速度”的标尺:
资源占用 vs. 开发效率的账本
Rust 版本的应用内存占用仅 60-80MB,而 Node.js 版本约为 117MB。
Rust 确实更省资源。但对于业务应用来说,这 50MB 的内存差异,在云服务器几美元一个月的成本面前不值一提。然而,为了节省这 50MB 内存,开发者付出了几倍的开发时间、调试精力以及心智负担。这笔账,在商业逻辑上是划不来的。
技术选型的“黄金法则”
给 Go 开发者的启示
有趣的是,Dmitry 在注脚中提到了 Go:“Yes, there is Go. But I never really had the chance to like Go.”
这其实是一个非常有意思的信号。在 Rust 的“极致安全”和 Node.js 的“极致速度”之间,Go 恰恰占据了那个“黄金平衡点”:
对于那些厌倦了 Node.js 运行时错误,又被 Rust 借用检查器拖慢脚步的 Web 开发者来说,Go 依然是当下最务实的选择。
技术选型从来没有绝对的优劣,只有“最适合当下约束条件的工具”。
Dmitry 的故事提醒我们:不要因为手里拿着“安全性”这把锤子(Rust),就无视了“交付速度”这个钉子。在商业软件的世界里,有时候,为了让产品活下去,为了让用户更快用上新功能,“足够好”且“跑得快”的代码,往往比“完美但迟到”的代码更有价值。
Rust 是系统编程的未来,但这并不意味着它是所有 Web 业务的终点。对于独立开发者或初创团队而言,“快”,本身就是一种极其重要的功能。
资料链接:https://yieldcode.blog/post/farewell-rust/
你会为了“安全”放弃“速度”吗?
软件工程永远是权衡的艺术。在你的项目中,你是否也曾为了追求某种“先进特性”,而导致项目进度失控?如果给你 50MB 的内存节省,你愿意多等 10 分钟的编译时间吗?
欢迎在评论区分享你的选型纠结!
还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.
2026-02-21 07:56:10

本文永久链接 – https://tonybai.com/2026/02/21/compound-engineering-ai-native-software-development-philosophy
大家好,我是Tony Bai。
在 2024 年和 2025 年,开发者们经历了一场狂欢。从 GitHub Copilot 到 Cursor,再到 Claude Code,我们习惯了通过自然语言生成代码。然而,随着项目规模的扩大,许多团队发现了一个尴尬的现象:AI 带来的加速度开始衰减。
为什么?因为传统的软件开发是线性的。你解决了一个 Bug,写了一个功能,代码库变大了,但也变得更复杂了。复杂度是软件工程的头号杀手。在传统模式下,新功能的开发往往是对旧代码的妥协。
而在 AI 辅助开发中,如果我们只是把 AI 当作一个更快的打字机,我们只是在以更快的速度制造“遗留代码(Legacy Code)”。每一次对话结束后,AI 对项目的理解(Context)往往随着会话窗口的关闭而重置。下一次,你不仅要重新解释需求,还要面对上次 AI 生成的、你可能都没完全读懂的代码。
Every.to 发布的最新报告《Compound Engineering: The AI-native engineering philosophy》中提出了 “复利工程(Compound Engineering)”,正是为了解决这个问题而生。这是一种哲学——如何让每一次开发迭代,都成为系统智慧的积累,而非技术债务的堆叠。它的核心理念颠覆了我们对软件资产的定义:代码本身不再是最重要的资产,系统对业务逻辑、设计规范和架构决策的“记忆”才是。
本文将深度拆解这一理念,探讨如何构建一个能够“自我进化”的 AI 开发系统。

复利工程是一种 AI 原生的工程哲学。它要求我们将开发过程视为一个闭环系统,每一次迭代不仅要交付功能,更要沉淀知识。
在这个体系中,软件开发不再是简单的“编写代码”,而是由四个步骤组成的无限循环:
Plan(规划)-> Work(执行)-> Review(审查)-> Compound(复利/沉淀)
这看似普通的四个词,在 Agentic AI(智能体式 AI)的加持下,拥有了全新的含义。
在传统开发中,工程师是全能工匠。而在复利工程中,工程师晋升为“系统架构师”和“智能体指挥官”。
这种转变要求我们放弃对“手写每一行代码”的执念,转而专注于如何教 AI 学会我们的品味(Taste)和规范。
让我们深入技术细节,看看这四个步骤是如何在实际的 AI 原生工作流中落地的。
在 AI 开发中,模糊是最大的敌人。如果需求描述不清,AI 会用幻觉填补空白,导致灾难性的后果。
复利工程要求在写第一行代码前,进行极度详尽的规划。但这不再需要工程师耗费数小时。
通过 workflows:brainstorm 和 workflows:plan 等指令,我们可以让 AI:
这个阶段的产出物不是代码,而是决策。工程师的职责是在这个阶段介入,确认 AI 的路径是否正确。只要 Plan 是对的,执行只是算力问题。
这是复利工程最“暴力美学”的部分。
传统的开发者一次只能写一个文件,修一个 Bug。但在 Agent 原生架构中,我们可以利用 Git Worktree 或分支技术,实现并发执行。
通过 workflows:work,系统可以:
这彻底改变了“速度”的定义。速度不再取决于你的打字速度,而取决于你能同时指挥多少个 Agent 并行工作。
这是质量控制的核心。在复利工程中,Review 不再仅仅依赖疲惫的同事,而是由一个专门训练的 Agent 审查委员会先行把关。
Every 的实践中,workflows:review 会唤起 14 个以上的专业 Agent,每个 Agent 佩戴不同的“透镜”:
这些 Agent 不会疲倦,不会因为人情世故而放水。它们会输出一份包含 P1 (Critical) 到 P3 (Nit) 的详细报告。工程师只需要做最后的“法官”,决定是否合并。
这是大多数 AI 工作流缺失的一环,也是“复利工程”得名的原因。
仅仅完成任务是不够的,我们必须让系统变得更聪明。
在 workflows:compound 阶段,系统会执行以下操作:
随着时间的推移,你的 AI 队友越来越懂你。它不再会犯同样的错误,不再需要你重复解释“我们团队使用 Kebab-case 而不是 CamelCase”。系统随着开发而生长,这就是复利。

要实施复利工程,技术栈的升级只是表象,更难的是工程师思维模式的转变。报告中列举了我们需要“遗忘”和“采纳”的信念。
复利工程的威力不仅限于后端开发,它正在渗透到软件生产的每一个环节。
对于前端和设计领域,报告提出了 “The Baby App Approach”(婴儿应用法)。
与其在庞大的生产库中小心翼翼地修改 UI,不如让 Agent 快速生成一个独立的、抛弃型的原型应用(Baby App)。
在这个沙盒中,你可以通过自然语言极速迭代设计(Vibe Coding),直到满意为止。然后,通过 Design Agents 提取其中的设计系统(颜色、间距、组件模式),再将其应用回主代码库。
这解决了“在屎山上雕花”的风险,让创新变得零成本。
传统的用户研究耗时耗力。在复利工程中,我们可以创建 Persona Agents(角色智能体)。
将用户访谈记录、通过 Descript 转录的文本喂给 AI,构建出代表典型用户的 Agent(如“忙碌的营销经理 Sarah”)。
在开发新功能时,先让 Sarah Agent 试用并反馈:“这个仪表盘数据太多了,我早上只有 5 分钟时间看,这对我没用。”
这缩短了反馈循环,从几周(等待用户测试)缩短到几分钟。
Copywriting Agent 可以学习你过往所有的博文和文档,掌握你的语调(Voice)。
Changelog Agent 可以监控 Git Commit,自动生成发布说明。
这些都不是简单的生成,而是基于“复利”——它们知道哪些功能是用户关心的,哪些只是底层重构,从而写出真正有价值的文档。
实施复利工程不可能一蹴而就。Every 提出了一个 0 到 5 的成熟度模型,你可以对照自查:
你的目标,应该是尽快从 Stage 2 跨越到 Stage 3。
复利工程的核心不在于某个具体的 Prompt 或工具,而在于“积累”。
在传统开发中,随着项目老化,开发速度必然下降(熵增定律)。
但在复利工程中,随着 Skill 的积累、CLAUDE.md 的完善、测试覆盖率的提升,开发速度是加速的。你的系统越庞大,AI 可用的“积木”越多,它构建新功能就越快。
这是一种反直觉的体验,也是 AI Native 时代最大的红利。
正如报告中所说:“Ship more value. Type less code.”(交付更多价值,少敲代码。)
这不仅是愿景,更是每一位工程师在这个时代保持竞争力的必经之路。
开始行动吧,别让你的代码库只有“债务”,没有“复利”。
资料链接:https://every.to/guides/compound-engineering
你处在哪个 Stage?
对照文中的“成熟度模型”,你目前的 AI 协作处于第几阶段?在你的开发流中,是否已经开始尝试利用 CLAUDE.md 或自定义 Skill 来实现“知识沉淀”?你认为“代码不再是最重要资产”这一观点激进吗?
欢迎在评论区分享你的 2026 进化心得!
还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.
2026-02-20 07:25:31

本文永久链接 – https://tonybai.com/2026/02/20/why-we-need-new-go-module-review-mechanism
大家好,我是Tony Bai。
你以为你在 GitHub 上看到的代码,就是你的 Go 程序编译时使用的代码吗?答案可能令你背脊发凉。
在 Go 语言的生态系统中,我们一直引以为傲的是其卓越的包管理和安全性。Go Checksum Database(校验和数据库)被公认为现代编程语言中最强大的完整性保障机制之一。然而,前 Go 安全团队负责人、著名的密码学家 Filippo Valsorda 在最近的一篇文章中揭示了一个令人不安的真相:虽然 Go 的工具链是安全的,但我们人类审查代码的方式却存在巨大的安全漏洞。
本文将深入探讨这一安全隐患的成因,剖析著名的“虚假 BoltDB”攻击案例,并介绍 Filippo 及其团队 Geomys 推出的解决方案——pkg.geomys.dev,一个致力于填补这一信任缺口的源码查看服务。

在深入探讨漏洞之前,我们有必要先回顾一下 Go 语言为何被誉为拥有“无可争议的最佳包完整性故事”。这主要归功于 Go Checksum Database (SumDB)。
Go 模块的获取本质上是去中心化的。你可以直接从 GitHub、GitLab 或任何 Git 托管服务上拉取代码。例如,当你运行 go get github.com/example/[email protected] 时,Go 工具链(在 GOPROXY=direct 模式下)会直接克隆对应的 Git 仓库并检出 v1.2.3 标签。
这种去中心化虽然灵活,但带来了巨大的安全风险:如果代码托管方(如 GitHub)被入侵,或者作者遭受胁迫修改了代码,亦或是作者恶意 Force-push(强制推送)覆盖了标签,下游用户该如何察觉?
SumDB 应运而生。它的工作原理如下:
这种机制比传统的 PGP 签名或作者管理私钥要实用得多,同时提供了极高的安全性保障。
既然 SumDB 如此完美,漏洞从何而来?
Filippo 指出,漏洞不在于机器,而在于人。
每当我们直接在代码托管平台(如 GitHub)上阅读代码时,我们就引入了一个薄弱环节。
Go 工具链验证的是下载到本地缓存中的 ZIP 包的哈希值。而我们在浏览器中打开 https://github.com/example/mod/blob/v1.2.3/exp.go 时,看到的是 GitHub 当前展示的 v1.2.3 标签对应的内容。
关键问题在于:Git 标签是可变的(Mutable)。GitHub 允许维护者强制推送标签。一个恶意的维护者(或攻击者)可以这样做:
这种“狸猫换太子”的攻击方式,利用了 Web 界面(GitHub)与构建工具(Go Toolchain)之间的数据源不一致。
这并非理论上的恐慌,而是已经发生的现实。
去年,Go 生态系统遭受了一次经典的域名抢注(Typosquatting)攻击。攻击者发布了一个名为“BoltDB”的虚假模块(利用了大小写或相似名称的混淆)。为了掩人耳目,攻击者利用了上述机制:
当时,一些评论员错误地将此归咎于 Go Module Mirror 的缓存机制。但 Filippo 一针见血地指出:这本质上是利用了 GitHub Web 界面天然缺乏验证机制的漏洞。GitHub 展示的代码,并不是 Go 工具链正在使用的、经过 SumDB 验证的“真实”代码。
既然 GitHub 不可信,作为开发者,我们该如何确保自己在审查“正确”的代码?
最安全的方法是将 Go 工具链实际使用的代码下载到本地进行审查。Filippo 给出了一个基于命令行的解决方案:
cd $(go mod download -json filippo.io/[email protected] | jq -r .Dir)
这条命令做了三件事:
在这个目录中看到的代码,才是绝对真实、不可抵赖的代码。此外,Go 团队也正在开发 go mod verify -tag 命令(预计将在Go 1.27版本落地),用于验证本地 Git 仓库的内容是否与 SumDB 匹配,这将进一步简化本地审查流程。
虽然本地审查最安全,但不得不承认,在浏览器中点击 pkg.go.dev 的链接查看源码实在是太方便了。为了在“便利性”和“安全性”之间取得平衡,Filippo Valsorda 开发了一个全新的服务:pkg.geomys.dev。
这是一个类似于 go-mod-viewer 的源码查看器,但它在设计上完全针对安全性与现代体验进行了优化。它的核心价值在于:展示经 Go Proxy 和 SumDB 确认的、真实的 ZIP 包内容,而非 GitHub 上的 Git 仓库内容。
其核心特性包括:
它是如何工作的呢?
这个服务的实现非常精妙,充分利用了现代 Web 技术:
Filippo 的这项工作(以及背后的 Geomys 组织)不仅仅是造了一个轮子,它向整个软件供应链安全领域提出了一个严肃的问题:我们所依赖的基础设施,是否能够支撑“代码即法律”的信任?
长期以来,我们将 GitHub 视为代码的“真理之源”。但在现代包管理机制下,真理之源已经转移到了不可篡改的构件(Artifacts)和透明日志上。Go 语言通过 SumDB 先行一步,而工具链的配套设施(如 IDE、代码浏览器)也必须跟上这一步伐。
此外,Geomys 组织的运作模式也值得关注。它是由 Ava Labs、Teleport、Tailscale 和 Sentry 等知名科技公司资助的专业维护者组织。这种通过商业公司联合资助关键开源基础设施维护者的模式,或许是解决开源可持续性问题的一条新出路。
作为一名负责任的 Go 开发者,我们应当意识到“便利”背后的代价。为了防止下一个“虚假 BoltDB”事件发生在你的项目中,我们建议:
安全,往往隐藏在这些看似微不足道的细节之中。
参考链接:
你会怎么审代码?
习惯了在网页上“指点江山”的我们,可能都忽略了 ZIP 归档才是唯一的真理。在你的开发流程中,是否也曾遇到过 GitHub 源码与本地代码不一致的“灵异事件”?你会为了安全而安装那个将链接重定向到 pkg.geomys.dev 的插件吗?
欢迎在评论区分享你的安全观!
还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.
2026-02-19 08:27:56

本文永久链接 – https://tonybai.com/2026/02/19/using-go-fix-to-modernize-go-code
大家好,我是Tony Bai。
2026年2月,Go 1.26 正式发布。除了语言层面的新特性(如 new(expr))和运行时的性能提升(如 Green Tea GC)之外,工具链迎来了一次史诗级的升级:go fix 命令被彻底重写。
在过去,go fix 更多是用来解决破坏性变更的“补救工具”(例如 Go 1.4 到 Go 1.5 的迁移)。但在 Go 1.26 中,它华丽转身,成为了一个代码现代化(Modernization)的利器。它不再仅仅是修复错误,而是主动帮助你将代码升级到 Go 的最新惯用法(Idioms)。
本文将基于 Alan Donovan 的官方博文,深度解析新版 go fix 的工作原理、核心特性——Modernizers(现代化器),以及其背后的分析框架架构。旨在帮助你彻底掌握这一新工具,让你的 Go 代码库焕发新生。

随着 Go 语言进入“后泛型时代”(Post-Go 1.18),语言特性的演进速度明显加快。从 strings.Cut 到 min/max 内置函数,再到 range-over-func,每一个版本都在引入更简洁、更高效的表达方式。
然而,现实是残酷的:代码库具有巨大的惯性。
大多数现存的 Go 代码依然停留在几年前的写法上。更糟糕的是,随着 LLM(大语言模型)编程助手的普及,AI 正在基于海量的旧代码进行训练。这就导致了一个恶性循环:AI 学习了旧的写法,生成了旧的写法,开发者接受了旧的写法,进一步污染了语料库。
Go 团队意识到了这一点。为了打破这个循环,确保未来的模型和新加入的开发者能够掌握最新的 Go 习惯用法,Go 1.26 推出了全新的 go fix。它利用了一套复杂的静态分析算法,自动识别并重构代码,使其拥抱现代化的 Go。

新版的 go fix 在使用体验上向 go build 和 go vet 看齐。它接受标准的包模式(Package Patterns)。
要“修复”当前目录及其子目录下的所有包,只需运行:
$ go fix ./...
如果运行成功,它会静默地直接修改你的源文件。
注意:go fix 会自动忽略生成的文件(Generated Files),因为对生成文件的修复应该在生成器本身中进行,而不是在产物中。
由于 go fix 可能会瞬间修改成百上千个文件,直接运行可能让人心惊肉跳。Go 团队贴心地提供了 -diff 标志,让你在应用变更前先进行预览:
$ go fix -diff ./...
--- dir/file.go (old)
+++ dir/file.go (new)
- eq := strings.IndexByte(pair, '=')
- result[pair[:eq]] = pair[1+eq:]
+ before, after, _ := strings.Cut(pair, "=")
+ result[before] = after
...
因此,我们强烈建议每次升级 Go 工具链版本后,都对项目运行一次 go fix。在运行前,请确保 Git 工作区是干净的,这样你可以清晰地查看 go fix 带来的改动,并方便同事进行 Code Review。
默认情况下,go fix 会运行所有注册的分析器。但在大型项目中,为了减轻 Code Review 的负担,你可能希望一次只应用一种类型的修复。
你可以通过 go tool fix help 查看所有可用的分析器:
$go tool fix help
fix is a tool for static analysis of Go programs.
fix examines Go source code and reports diagnostics for
suspicious constructs or opportunities for improvement.
Diagnostics may include suggested fixes.
An example of a suspicious construct is a Printf call whose arguments
do not align with the format string. Analyzers may use heuristics that
do not guarantee all reports are genuine problems, but can find
mistakes not caught by the compiler.
An example of an opportunity for improvement is a loop over
strings.Split(doc, "\n"), which may be replaced by a loop over the
strings.SplitSeq iterator, avoiding an array allocation.
Diagnostics in such cases may report non-problems,
but should carry fixes that may be safely applied.
For analyzers of the first kind, use "go vet -vettool=PROGRAM"
to run the tool and report diagnostics.
For analyzers of the second kind, use "go fix -fixtool=PROGRAM"
to run the tool and apply the fixes it suggests.
Registered analyzers:
any replace interface{} with any
buildtag check //go:build and // +build directives
fmtappendf replace []byte(fmt.Sprintf) with fmt.Appendf
forvar remove redundant re-declaration of loop variables
hostport check format of addresses passed to net.Dial
inline apply fixes based on 'go:fix inline' comment directives
mapsloop replace explicit loops over maps with calls to maps package
minmax replace if/else statements with calls to min or max
newexpr simplify code by using go1.26's new(expr)
omitzero suggest replacing omitempty with omitzero for struct fields
plusbuild remove obsolete //+build comments
rangeint replace 3-clause for loops with for-range over integers
reflecttypefor replace reflect.TypeOf(x) with TypeFor[T]()
slicescontains replace loops with slices.Contains or slices.ContainsFunc
slicessort replace sort.Slice with slices.Sort for basic types
stditerators use iterators instead of Len/At-style APIs
stringsbuilder replace += with strings.Builder
stringscut replace strings.Index etc. with strings.Cut
stringscutprefix replace HasPrefix/TrimPrefix with CutPrefix
stringsseq replace ranging over Split/Fields with SplitSeq/FieldsSeq
testingcontext replace context.WithCancel with t.Context in tests
waitgroup replace wg.Add(1)/go/wg.Done() with wg.Go
By default all analyzers are run.
... ...
要查看特定分析器的文档:
$ go tool fix help forvar
forvar: remove redundant re-declaration of loop variables
The forvar analyzer removes unnecessary shadowing of loop variables.
Before Go 1.22, it was common to write for _, x := range s { x := x ... }
to create a fresh variable for each iteration. Go 1.22 changed the semantics
of for loops, making this pattern redundant. This analyzer removes the
unnecessary x := x statement.
This fix only applies to range loops.
要单独运行某个分析器(例如 any),可以使用对应的标志:
$ go fix -any ./...
反之,如果你想运行除了 any 之外的所有分析器,可以将其禁用:
$ go fix -any=false ./...
和 go vet 一样,go fix 也是基于特定的构建配置(Build Configuration)进行分析的。如果你的项目包含大量特定于平台的文件(例如 _linux.go, _windows.go),建议针对不同的 GOOS 和 GOARCH 多次运行:
$ GOOS=linux GOARCH=amd64 go fix ./...
$ GOOS=darwin GOARCH=arm64 go fix ./...
$ GOOS=windows GOARCH=amd64 go fix ./...
Go 1.26 引入了一个新概念:Modernizers。它们是一组特殊的分析器,专门用于将旧的习惯用法替换为利用新语言特性或新标准库 API 的写法。
以下是几个最具代表性的 Modernizers 示例,展示了它们如何简化代码:
在 Go 1.21 之前,计算最小值/最大值通常需要写冗长的 if/else 语句。
旧代码:
x := f()
if x < 0 {
x = 0
}
if x > 100 {
x = 100
}
minmax 修复后可能的样子:
x := min(max(f(), 0), 100)
代码意图一目了然,且消除了分支跳转,可能带来微小的性能提升。
Go 1.22 引入了对整数的 range 支持。
旧代码:
for i := 0; i < n; i++ {
f()
}
rangeint 修复后:
for range n {
f()
}
如果你不需要索引 i,新的写法极其清爽。
Go 1.18 引入的 strings.Cut 是处理“按分隔符切分”场景的神器,它比 Index + Slicing 更高效且不易出错。
旧代码:
i := strings.Index(s, ":")
if i >= 0 {
return s[:i]
}
stringscut 修复后:
before, _, ok := strings.Cut(s, ":")
if ok {
return before
}
这是 Go 1.26 刚刚引入的语言变动:new() 函数现在支持传入表达式,直接初始化变量。这在处理 Protobuf 或 JSON 的可选字段(Pointer 类型)时非常有用。
旧代码(通常需要辅助函数):
func newInt(x int) *int { return &x }
data, err := json.Marshal(&RequestJSON{
URL: url,
Attempts: newInt(10), // 需要定义辅助函数或临时变量
})
newexpr 修复后:
data, err := json.Marshal(&RequestJSON{
URL: url,
Attempts: new(10), // Go 1.26 原生支持!
})
newexpr 这样的 Modernizer 非常智能。它会检查你的 go.mod 文件中的 go 指令或文件的 //go:build 标签。只有当你的项目明确声明支持 Go 1.26 或更高版本时,它才会建议由于 new(expr) 带来的修改。这确保了 go fix 不会引入破坏向后兼容性的代码。
go fix 的强大之处在于它是迭代式的。应用一个修复可能会触发另一个修复。
考虑一个经典的性能陷阱:在循环中拼接字符串。
初始代码:
s := ""
for _, b := range bytes {
s += fmt.Sprintf("%02x", b) // O(N^2) 复杂度!
}
use(s)
第一轮 go fix (stringsbuilder):
分析器识别出这是低效的字符串拼接,将其重构为 strings.Builder。
var s strings.Builder
for _, b := range bytes {
s.WriteString(fmt.Sprintf("%02x", b))
}
use(s.String())
第二轮 go fix (fmtappendf):
一旦代码变成了 WriteString(Sprintf(…)),另一个分析器(源自 staticcheck 的 QF1012)就会识别出这可以优化为 fmt.Fprintf,不仅更简洁,而且直接写入 Buffer,减少了中间内存分配。
var s strings.Builder
for _, b := range bytes {
fmt.Fprintf(&s, "%02x", b)
}
use(s.String())
因此,对于大型重构,建议运行多次 go fix,直到代码达到稳定态(Fixed Point)。
go fix 可能会在同一文件的不同位置应用几十个修复。它内部使用了一个简单的三路合并算法(Three-way Merge)来协调这些修改。如果两个修复在语法上冲突(例如修改了同一行),工具会丢弃其中一个,并提示用户重新运行。
但还有一种更棘手的语义冲突(Semantic Conflict)。
例如,修复 A 删除了变量 x 的一次使用,修复 B 删除了 x 的另一次使用。两个修复单独看都没问题,但合在一起后,变量 x 变成了“未使用的变量”,导致编译错误。
go fix 的解决方案很务实:它在所有修复应用完毕后,会运行一个最终的清理 Pass,自动删除那些因重构而变得多余的 import 语句。对于未使用的变量,通常会留给编译器报错,由开发者手动删除(或者等待未来的 deadcode 消除器)。
新版 go fix 的核心动力来自于 Go Analysis Framework。
早在 2017 年,Go 团队将 go vet 的核心逻辑拆分成了两部分:
这种分离架构带来了极大的灵活性。同一个分析器(比如 printf 检查)可以运行在多种场景下:
Go 1.26 的里程碑意义在于:go fix 和 go vet 在底层实现上终于完全统一了。 它们的区别仅在于目标:vet 侧重于报告错误(低误报率),fix 侧重于自动修改(无回退,保全正确性)。
为了让 go fix 能在大型代码库上秒级运行,Go 团队引入了多项基础设施优化:
Inspector 与 Cursor:
分析器通常需要遍历语法树(AST)。inspector 包预先计算了遍历索引,使得分析器可以快速跳过不关心的节点。新增的 Cursor 类型更是允许在 AST 上进行类似 DOM 的灵活导航(父节点、兄弟节点)。
Facts(事实)与跨包推断:
分析框架支持跨包的“事实”传递。例如,printf 检查器可以分析 log.Printf 的函数体,得出一个“Fact”:log.Printf 是 fmt.Printf 的包装器。这个 Fact 会被序列化并传递给导入了 log 包的其他包,从而实现跨包的格式化字符串检查。
TypeIndex(类型索引):
很多分析器需要查找“所有对 fmt.Printf 的调用”。与其遍历整个 AST,typeindex 预先构建了符号引用索引。这使得查找特定符号的开销从“与代码量成正比”降低为“与调用次数成正比”,对于查找冷门符号(如 net.Dial)的分析器,性能提升可达 1000 倍。
Alan Donovan 在博文中提出了一个令人兴奋的愿景:Self-Service Paradigm(自助式范式)。
目前的 Modernizers 大多是针对 Go 标准库的。但第三方库的作者呢?如果你维护了一个流行的 ORM 或 Web 框架,当你升级 API 时,如何帮助你的用户自动迁移?
你不可能把你的迁移逻辑塞进 Go 官方的 go fix 里。
Go 1.26 迈出了“自助服务”的第一步:基于注解的内联器(Annotation-driven Inliner)。
库作者可以在即将废弃的函数上添加一行特殊的注释:
// Deprecated: Use Pow(x, 2) instead.
//go:fix inline
func Square(x int) int { return Pow(x, 2) }
当用户运行 go fix 时,分析器会识别这个指令,并自动将用户代码中的 Square(x) 替换为 Pow(x, 2)。
动态加载分析器:
未来,Go 可能会支持从模块源代码树中动态加载分析器并安全执行。这意味着 sql 包可以自带一个检查器来防止 SQL 注入,或者你的公司内部框架可以自带一套 go fix 规则来强制执行内部编码规范。
声明式控制流检查:
许多检查逻辑都遵循“做完 Y 之后别忘了 X”的模式(例如:打开文件后别忘了 Close,获取锁后别忘了 Unlock)。Go 团队计划探索一种通用的方式,让开发者只需简单的注解就能定义这种检查,而无需编写复杂的 Go 代码来分析控制流。
Go 1.26 的 go fix 不仅仅是一个工具的更新,它代表了 Go 工程化能力的一次跃迁。
它告诉我们:维护代码不仅是修修补补,更是持续的进化。 通过将最佳实践固化为代码(Analyzers),并赋予工具自动执行的能力(Fixers),Go 正在构建一个更加健康、更具韧性的生态系统。
对于每一位 Gopher 来说,现在的任务很简单:升级到 Go 1.26(记得将go.mod的go版本升级为go 1.26.0或后续版本),在你的项目中运行 go fix ./…,然后享受代码变得更现代、更高效的快感吧。
参考资料:https://go.dev/blog/gofix
你的“现代化”阻碍是什么?
自动重构工具虽然强大,但老代码库的惯性依然巨大。在你目前的项目中,有哪些“旧习惯”最让你难以割舍?你是否尝试过用 go fix 来升级你的代码?
欢迎在评论区分享你的重构经历或对新工具的看法!
还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.
2026-02-18 07:43:31

本文永久链接 – https://tonybai.com/2026/02/18/why-we-chose-go-over-python-for-llm-gateways
大家好,我是Tony Bai。
在 2026 年的今天,人工智能早已走出了实验室,成为企业级应用的核心驱动力。Python,凭借其在机器学习领域的绝对统治地位——拥有 PyTorch、TensorFlow、Hugging Face 等无可匹敌的生态系统——长期以来被视为 AI 开发的“默认语言”。
然而,随着 AI 应用从模型训练(Training)走向推理服务(Inference)和应用编排(Orchestration),工程重心发生了微妙的转移。当我们谈论模型本身时,Python 是王者;但当我们谈论承载模型流量的基础设施——网关、代理、路由器时,Python 还是最佳选择吗?
近日,开源 LLM 网关项目 Bifrost 的维护者在 Reddit 上分享了一篇题为《Why we chose Go over Python for building an LLM gateway》的技术复盘,引发了社区的强烈反响。他们放弃了拥有 LiteLLM 等成熟竞品的 Python 生态,转而使用 Go 重写了核心网关。结果令人咋舌:延迟降低了约 700 倍,内存占用降低了 68%,吞吐量提升了 3 倍。
这场技术选型的背后,折射出的是 AI 工程化进入深水区后,对并发模型、资源效率与部署架构的重新审视。

在项目的初期,选择 Python 似乎是理所当然的。
1. 生态惯性与“胶水”优势
绝大多数 AI 工程师都是 Python Native。从 LangChain 到 LlamaIndex,几乎所有的 Agent 开发框架都优先支持 Python。使用 Python 构建网关,意味着可以直接复用现有的库,甚至可以直接挂载一些轻量级的 Python 逻辑来处理 Embeddings 或 RAG(检索增强生成)流程。FastAPI 的易用性更是让开发者能在几分钟内搭建起一个服务。
2. 遭遇瓶颈:网关的本质是 I/O
然而,LLM 网关的业务属性决定了它的性能痛点。与计算密集型(CPU-bound)的模型推理不同,网关是典型的 I/O 密集型应用。它的核心职责是:
在这个过程中,网关绝大部分时间都在“等待”。
3. Python 的并发痛点
Bifrost 团队在测试中发现,当并发请求数达到 500-1000 RPS(每秒请求数)时,Python 的瓶颈开始显现。
Bifrost 团队最终选择了 Go。这一决定并非出于对语言的偏好,而是基于冷冰冰的 Benchmark 数据。让我们深入分析他们披露的核心指标。

数据对比:
* Bifrost (Go): ~11 微秒 (0.011ms) / 请求
* LiteLLM (Python): ~8 毫秒 / 请求
这是一个惊人的 700 倍 差距。
虽然 8 毫秒在人类感知中似乎微不足道,但在高并发架构中,这被称为“开销放大”。
此外,Go 的 net/http 标准库在处理 HTTP 请求时经过了极致优化。Go 不需要像 Python 那样依赖 ASGI/WSGI 服务器(如 Uvicorn),其原生的 HTTP 处理能力配合 Goroutine,使得每个请求的内存分配和 CPU 周期都降到了最低。
架构对比:
* Go: 10,000 个 Goroutines,每个仅占用 ~2KB 栈空间。
* Python: 受限于 OS 线程开销或 Event Loop 的单核瓶颈。
LLM 网关的特殊性在于长连接。LLM 的流式输出可能持续数秒甚至更久。这意味着网关必须同时维护成千上万个活跃连接。
Go 的 GMP(Goroutine-Machine-Processor)调度模型天生适合这种场景。成千上万个 Goroutine 可以复用少量的系统线程,上下文切换由 Go Runtime 在用户态极速完成,几乎不消耗系统内核资源。
相比之下,Python 即使使用了 uvloop,在面对海量并发连接的数据搬运时,其解释器的开销依然是一个沉重的包袱。
数据对比:
* Go: 内存占用降低 ~68%。
* 生产环境: Go 跑在 t3.medium (2 vCPU, 4GB) 上即可;Python 则需要 t3.xlarge。
对于大规模部署 AI 服务的企业来说,这意味着基础设施成本直接减半。
Python 的动态类型系统和垃圾回收机制导致其对象内存占用较大。而 Go 的结构体布局紧凑,且编译器能进行逃逸分析(Escape Analysis),将大量对象分配在栈上而非堆上,从而显著降低了 GC 压力和内存占用。
这篇帖子在 r/golang 引发了极高质量的讨论,评论区揭示了行业内更深层次的趋势。
过去,Python 的一大优势是“开发效率高”。写 Python 代码通常比写 Go 或 Rust 快。
但在 2026 年,“Agentic Coding”(即利用 AI Coding Agent 辅助编程)已经成为主流。
有开发者指出:“LLM 让编写 Rust 和 Go 变得非常高效,你完全可以享受到高性能语言的红利,而不用支付编写它们的‘学习成本’。”
这是一个极其深刻的洞察。
当“编写代码”不再是瓶颈时,“运行时性能”和“稳定性”的权重就被无限放大了。这进一步削弱了 Python 在后端基础设施层的竞争力。
既然要高性能,为什么不直接上 Rust?
评论区对此展开了激辩。虽然 Rust 在理论上拥有比 Go 更高的性能上限和内存安全性(无 GC),但 Go 在“开发效率”与“运行效率”之间找到了完美的平衡点。
对于大多数 AI 网关场景,Go 是性价比最高的选择。
这是否意味着 Python 将被淘汰?绝不。
社区共识非常明确:Python 的护城河在于 ML 生态。
但在生产环境部署(Production Serving)阶段,架构正在发生分离:
Bifrost 的案例只是冰山一角。我们正在目睹 Go 语言在 AI 领域的“新基建”运动。
Deployment simplicity 是作者提到的另一个关键点。
部署 Python 应用通常意味着:配置 Docker -> 安装 Python -> pip install requirements.txt -> 解决依赖冲突 -> 虚拟环境管理。
而部署 Go 应用:COPY bifrost /usr/local/bin/ -> Run。
在容器化和 K8s 盛行的今天,Go 的静态链接二进制文件极大地简化了 CI/CD 流程,减小了镜像体积,提升了冷启动速度(这对于 Serverless AI 推理尤为重要)。
虽然 Go 在 Tensor 操作库上不如 Python 丰富,但在应用层工具上正在迅速补齐。
如果你正在构建一个 AI 应用平台:
Bifrost 从 Python 到 Go 的迁移,不仅仅是一次代码重写,更是一次架构理念的升级。它证明了在 AI 浪潮中,基础设施的性能与模型的智能同等重要。
随着 LLM 应用规模的爆发式增长,计算成本和响应延迟将成为企业关注的焦点。Go 语言凭借其高效的并发模型、极低的资源占用和极简的部署体验,正在成为 AI 基础设施层的“事实标准”。
对于 Gopher 而言,这是一个最好的时代。我们不需要成为算法专家,只需要发挥 Go 语言最擅长的能力——构建高性能、高可靠的管道,就能在 AI 时代占据不可或缺的一席之地。
资料链接:https://www.reddit.com/r/golang/comments/1r27pqx/why_we_chose_go_over_python_for_building_an_llm/
你认为 Python 会被“边缘化”吗?
随着 Agentic Coding 的普及,高性能语言的入门门槛正在消失。在你的 AI 实践中,是否也感受到了 Python 在生产部署时的无奈?你认为 Go 在 AI 领域还会攻下哪些阵地?
欢迎在评论区分享你的看法!
还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?
继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!
我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。
目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!

商务合作方式:撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求,请扫描下方公众号二维码,与我私信联系。

© 2026, bigwhite. 版权所有.