“你装了 Go 1.26,却写不了 Go 1.26 的代码?”——复盘 go mod init 的降级风波

本文永久链接 – https://tonybai.com/2026/02/22/go-1-26-go-mod-init-downgrade-collision-review

大家好,我是Tony Bai。

2026年2月,Go 1.26 带着众多瞩目的新特性(如期待已久的 new(expr) 语法糖全面启用的 Green Tea GC)正式发布。你兴奋地更新了本地的工具链,迫不及待地打开终端,想要体验一把用 new(42) 直接初始化指针的快感。

你熟练地敲下:

$ mkdir test && cd test
$ go mod init mytest
$ cat <<EOF > main.go
package main
import "fmt"
func main() {
    fmt.Println(new(42))
}
EOF
$ go build

你期待着编译成功,然而,迎接你的却是迎头一棒的编译错误:

./main.go:5:14: new(42) requires go1.26 or later (-lang was set to go1.25; check go.mod)

注:go run不会有问题。go run 主要用于快速运行 Go 程序,它将直接使用当前 Go 工具链版本(比如Go 1.26.0)来执行代码,不会对 go.mod 中的版本声明进行验证。

“什么情况?我用的明明是最新的 Go 1.26 工具链!”

你满脸疑惑地打开刚刚生成的 go.mod 文件,赫然发现里面写着:

module mytest

go 1.25.0

你没有看错。在 Go 1.26 中,go mod init 默认生成的不再是你当前正在使用的工具链版本(1.N),而是退回了一个大版本(1.N-1)。 如果你使用的是 RC 预览版,它甚至会退回两个版本(1.N-2)。

要想使用新特性,你必须手动去修改 go.mod,或者再多敲一行命令:go get go@1.26.0。

这个打破了所有 Go 开发者十年肌肉记忆的改动,迅速在 GitHub 上引爆了争议。在 Issue #77653 中,社区与 Go 核心团队展开了一场火药味十足的“大辩论”。

官方视角的“良苦用心”:为了生态的平滑演进

要理解这个“反直觉”的改动,我们必须先带入 Go 核心团队(特别是那些维护庞大开源生态和基础设施的工程师)的视角。

这个改动源自 Go 1.26 开发周期中的 Issue #74748。Go 官方团队成员 dmitshur 提出了这个修改建议,并得到了 mvdan 等资深贡献者的强烈支持。

他们的核心论点是:不假思索地要求最新版本,是一种对下游极其“不友好”的行为。

遵循“支持两个最新大版本”的官方承诺

Go 官方的维护策略是始终支持最近的两个主要版本(在 1.26 发布时,受支持的是 1.26 和 1.25)。

dmitshur 认为,如果一个开发者在 1.26 发布的第二天就用 go mod init 创建并发布了一个开源库,默认的 go 1.26 会导致所有尚未升级(仍在使用合法的、受支持的 1.25 版本)的下游企业用户无法直接编译这个库。

“新的默认值永远不会切断任何一个当前受官方支持的 Go 工具链。” —— dmitshur

倒逼开发者做出“有意识的选择”

go.mod 中的 go 1.x 指令不仅控制着语法特性(Language Version),还控制着 GODEBUG 的默认行为。

官方团队认为,放弃兼容旧版本,应该是一个“有意识的(Conscious)”决定。

mvdan 在辩论中直言不讳:“我们不应该鼓励新的 Go 用户在新语言特性一出现时就立即使用它们。因为使用了新特性而破坏对旧版本用户的兼容性,这应该是一个深思熟虑的选择。”

站在上帝视角,Go 官方希望把 go mod init 变成一种“刹车机制”:默认让你兼容更多人,除非你真的、确实、迫切需要最新特性,那你再去手动升级。

社区的全面反弹:被傲慢牺牲的“开发者体验”

官方的“爹味”说教并没有说服社区。Issue #77653 的发起者 willfaught 以及众多开发者列举了连串的反驳,直指这一决策在逻辑上的“千疮百孔”。

违背“最小惊讶原则”

软件设计的铁律是“所见即所得”。用户下载了 Go 1.26,理所当然地认为开箱即用的就是 1.26 的全部能力。

现在,官方文档、发布博客、社区媒体都在铺天盖地地宣传 1.26 的新语法,但新手按照官方教程敲下 go mod init 后,新语法却全部报错。这种认知断层对新手极度不友好,增加了无谓的挫败感。

“所有代码都是公共库”的虚假前提

官方论点的核心基石是“保护下游调用者”。但社区一针见血地指出:世界上 99% 的 go mod init 都是为了创建私有项目、业务微服务、一次性脚本或个人玩具。

“公共模块的维护者确实需要考虑兼容性,但为什么要让数以百万计的普通应用开发者,去为那几十个核心开源库作者的便利买单?”

如果是写业务代码或自己跑着玩,开发者唯一的诉求就是用最新的工具写最爽的代码。强迫这 99% 的人每次都要手动 go mod edit -go=1.26,是典型的“为了 1% 的特例惩罚 99% 的大众”。

GOTOOLCHAIN 让这种担忧变得多余

社区还指出,官方的担忧在 Go 1.21 引入了向前兼容的工具链下载机制(GOTOOLCHAIN=auto)后就已经不复存在了。

如果一个库要求 go 1.26,而下游用户使用的是 Go 1.25,Go 1.25 的工具链会自动、透明地在后台下载 1.26 编译器来完成构建。

既然工具链已经足够智能地解决了版本不匹配问题,为什么还要在 go.mod 初始化时进行人为的降级限制?

虚假的安全感

开发者 rittneje 提出了一个致命的逻辑漏洞:go 1.25 只能阻挡语法级别的新特性。如果开发者在一个 go 1.25 的模块中使用了 Go 1.26 标准库中新增的函数,这并不会触发编译器的版本阻拦,但下游的 1.25 用户拉取代码后依然会编译失败。

这意味着,官方强推的 N-1 降级策略,连他们自己宣称的“保护兼容性”的目的都无法严密达成。

程序的傲慢与僵化的治理

在这场辩论中,比技术分歧更让人感到不安的,是 Go 核心团队在开源治理上的态度。

当社区列出了如此详尽、逻辑严密的反对意见时,Go 核心成员 Ian Lance Taylor 的回复却像一盆冷水浇灭了讨论的希望:

“大家都知道,我们决策的准则之一是:一旦我们做出了决定,除非有新的信息,否则我们不会重新审视它。否则我们将陷入无休止地重新考虑旧决定的循环中。恕我直言,我没有看到任何会导致我们重新审视此决定的新信息。”

这段冷酷的回复引发了强烈的不满。开发者们指出,最初导致这个改变的提案(#74748)甚至没有走标准的 Go 提案审查流程(Proposal Process)。它作为一个普通的 Feature Request 被 Go 内部人员提出,并在极小范围内的几个人赞同后,就被直接合并进了 1.26 版本。

“新信息就是:大多数开发者在 1.26 发布后才感知到这个隐蔽的改动,并认为这是一个糟糕的默认体验。” 开发者愤怒地反驳道。

当官方以“没有新信息”为由拒绝倾听社区关于“开发者体验”的反馈时,Go 团队长期以来被诟病的“Google 工程师的傲慢(Google knows best)”似乎再次上演。

哲学的分歧:我们在为谁设计语言?

纵观整场风波,它不仅仅是一个 go mod init 默认输出什么字符串的技术细节,它本质上是一场关于“工具链默认行为到底应该为谁服务”的哲学碰撞。

  • Go 核心团队(精英维护者视角):他们站在整个生态系统的塔尖,每天看到的是版本碎片化、库冲突、向下兼容等宏观问题。对他们而言,保守、稳定、克制、不破坏是最高的优先级。因此,他们倾向于将“默认设置”作为一种教育手段,强迫开发者不要走得太快。
  • 广大 Gopher(一线开发者视角):他们身处业务交付的一线,面临的是业务迭代的压力。对他们而言,直觉、效率、无缝的开发者体验才是最高的优先级。当他们更新了最新版的编译器,他们想要的就是立刻获得最新的能力,而不是被工具链“按着头”讲兼容性的大道理。

在 Rust 社区,工具链(Cargo)总是鼓励你使用最新的 Edition;在 Node.js/Python 社区,大家习惯了追逐最新版本。而 Go,似乎正在一条更加“爹系”的道路上越走越远。

小结:如何应对 1.26 的新常态?

就目前的情况来看,Go 团队大概率不会在短时间内撤回这个决定。对于广大的 Gopher 来说,我们需要适应这个略显尴尬的新常态。

如果你是一名应用开发者,希望在每个新项目中无缝使用最新的 Go 特性,你可以采取以下两种策略:

  1. 修改肌肉记忆:以后创建新项目时,不要只敲 go mod init,养成敲连招的习惯:
    bash
    go mod init mymodule && go get go@latest
  2. 设置 Shell 别名:在你的 .zshrc 或 .bashrc 中写一个 alias 来覆盖默认行为:
    bash
    alias gomodinit='f() { go mod init "$1" && go mod edit -go=$(go env GOVERSION | sed "s/go//") ; }; f'

Go 1.26 无疑是一个性能卓越、充满亮点的优秀版本,但 go mod init 的这一小段“降级”插曲,或许会在很长一段时间内,成为社区茶余饭后的吐槽谈资。

技术工具的演进,永远在“严谨的安全网”与“极致的自由度”之间走钢丝。只是这一次,Go 似乎为了 1% 的开源生态理想,让 99% 的普通开发者感到了一丝被背叛的错愕。

你对 Go 1.26 的这个默认行为改动怎么看?是支持官方的保守克制,还是支持社区的痛批?欢迎在评论区留下你的观点!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


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

当“安全性”遭遇“交付速度”:2026 年,我为什么告别了 Rust

本文永久链接 – 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 的心路历程。这篇文章也引发了一场关于“为了极致的安全性,我们是否值得牺牲过多的交付速度?”的深刻辩论。

缘起:一个 C/C++ 老兵的“安全梦”

Dmitry 绝非那些被即时编译(JIT)宠坏的脚本小子。相反,他的技术底色是硬核的 C/C++。

早在高中时代,他就沉迷于指针的魔力,痴迷于手动管理内存的掌控感。他写过 3D 渲染器、IRC 机器人,甚至操作系统内核。然而,由于第一份工作是 PHP Web 开发,他被迫进入了动态语言的世界。虽然 PHP、Python 和 Ruby 带来了 Web 开发的极速体验,但在内心深处,他始终怀念 C 语言那种“压榨硬件每一滴性能”的快感,同时也痛恨 C 语言中防不胜防的内存安全漏洞。

直到 Rust 横空出世。

对于像 Dmitry 这样的工程师来说,Rust 简直就是“鱼与熊掌兼得”的梦想:

  • 低级控制力:像 C 一样精确控制内存布局。
  • 安全性:编译器在编译阶段就能消除一整类内存错误。
  • 现代体验:拥有像 Cargo 这样优秀的包管理工具。

于是,他做了一个所有热血工程师都会做的决定:为了追求极致的质量与安全,用 Rust 从零构建一个商业 Web 应用。

起初,一切都很完美。他在 2023 年底成功上线了项目,甚至因此受邀在两个技术大会上发表演讲。但随着时间的推移,业务逻辑日益复杂,“安全性”的红利开始被“交付速度”的损耗所抵消。到了 2026 年初,为了项目的生存,他不得不做出了那个艰难的决定:告别 Rust

深度复盘:Rust 在 Web 交付中的“五大减速带”

Dmitry 的文章之所以珍贵,是因为他用亲身经历证明了:在 Web 开发的特定场景下,Rust 引以为傲的“安全性”机制,如何一步步变成了拖慢“交付速度”的罪魁祸首。

1. 模板与视图:类型安全 vs. 迭代速度

在后端逻辑中,Rust 的类型系统坚不可摧。但当数据流向前端(HTML/Email 模板)时,这种为了安全而设计的严格性,变成了修改 UI 时的噩梦。

  • 安全性的代价:为了保证编译时的类型安全,Rust 社区诞生了 Maud 或 Askama 这样的编译时模板库。它们通过宏(Macro)在编译期检查 HTML 模板中的每一个变量引用。这听起来很棒,意味着你永远不会渲染出错误的变量。
  • 速度的牺牲:但这带来的副作用是,每次修改 HTML 哪怕一个标点符号,都会触发漫长的重新编译。在 Web 前端开发这种需要“所见即所得”的高频迭代场景下,这种等待是毁灭性的。
  • 对比 Node.js:TypeScript 配合 JSX/TSX 提供了全链路的类型安全,同时保持了极快的热重载(Hot Reload)速度。重构一个字段,VS Code 会立即标红所有受影响的视图组件,修改后毫秒级生效。这种“安全且快”的体验,是 Rust 目前无法提供的。

2. 国际化(i18n):生态缺失带来的效率黑洞

对于商业应用,支持多语言是刚需。

虽然 Mozilla 开发了 Project Fluent,但 Rust 生态中缺乏成熟的、开箱即用的 i18n 解决方案。你往往需要为了“正确性”而去处理繁琐的加载逻辑和类型绑定,编写大量的胶水代码。而Node.js生态中的i18next 等库不仅极其成熟,还能配合 TypeScript 提供键值级别的类型安全。Node.js 原生内置了完整的 ICU 标准(Intl API),处理货币、日期、复数格式化信手拈来。在这一点上,Rust 开发者需要花费数倍的时间来实现同样的功能,严重拖慢了产品推向全球市场的速度。

3. “动态”业务 vs. “静态”约束

Web 业务充满了动态性:用户提交的 JSON 结构可能是不确定的,筛选条件的组合可能是无穷的。Rust 试图用静态类型系统去约束这些动态行为,结果就是开发效率的暴跌。

  • 序列化之痛:serde 是 Rust 的瑰宝,但在处理复杂的、充满 Option 的业务数据时,为了安全地取出一个嵌套字段,你不得不编写大量的 match 或 unwrap 处理代码。为了优雅地处理错误,Dmitry 定义了十几个自定义错误枚举。虽然代码很健壮,但写起来太慢了。
  • SQL 的僵局:sqlx 提供了极其强大的编译时 SQL 检查,这在静态查询时非常棒。但是,一旦你需要根据用户输入动态构建查询(例如:用户选了 A 筛选条件就加个 WHERE 子句),Rust 的强类型系统就变成了噩梦。你无法像在 Node.js 中使用 Kysely 或 Prisma 那样,流畅地拼接查询片段。为了“安全”地构建 SQL,你付出了巨大的代码复杂度成本。

4. 编译时间:CI/CD 的隐形杀手

这是最让 Dmitry 崩溃的一点,也是“交付速度”最直观的体现。

  • Rust 的等待:随着依赖增多(尤其是使用了大量宏的 Web 框架),编译时间呈指数级增长。Dmitry 的 CI 流程需要 12-14 分钟 才能完成部署。“每次我在 Sentry 上看到一个简单的 Bug,想到修复它需要等待 15 分钟的构建流程,我就失去了修复的动力。”
  • Node.js 的极速:迁移到Node.js后,完整的 CI 流程(含 Lint 和测试)仅需 5 分钟。部署速度提升了 3 倍。这意味着“发现 Bug -> 修复 -> 上线”的反馈闭环被大大缩短了。在商业竞争中,修复速度往往比绝对的“无 Bug”更重要。

5. 生态成熟度:造轮子的时间成本

Rust 的 Web 生态虽然在成长,但面对长尾需求时仍显稚嫩。

  • 场景:你需要集成一个冷门的第三方支付网关,或者处理一个特定的 Webhook 签名验证。
  • Rust 的困境:官方 SDK?没有。社区库?两年前就不更新了。为了安全,你不得不对着 API 文档,自己手写 HTTP 请求、自己实现加密验签逻辑。这占用了大量本该用于开发业务核心功能的时间。
  • Node.js 的便利:npm install 通常能解决一切。几乎所有 SaaS 服务商都会提供第一方的 Node.js SDK。“拿来主义”是提升交付速度的最佳捷径。

总结与反思:我们到底为了什么而编程?

Dmitry 的文章并没有否定 Rust 的价值。相反,他依然热爱 Rust,依然怀念那些与编译器“斗智斗勇”并最终获得完美代码的日子。

他的结论非常客观,为所有正在做技术选型的团队提供了一把衡量“安全”与“速度”的标尺:

  1. 资源占用 vs. 开发效率的账本
    Rust 版本的应用内存占用仅 60-80MB,而 Node.js 版本约为 117MB。
    Rust 确实更省资源。但对于业务应用来说,这 50MB 的内存差异,在云服务器几美元一个月的成本面前不值一提。然而,为了节省这 50MB 内存,开发者付出了几倍的开发时间、调试精力以及心智负担。这笔账,在商业逻辑上是划不来的。

  2. 技术选型的“黄金法则”

    • 何时拥抱“安全性”(选 Rust):如果你在构建数据库内核、搜索引擎、高频交易系统、嵌入式设备固件,或者像 Lambda 这样对冷启动时间极度敏感的 Serverless 函数。在这些场景下,性能和稳定性是核心竞争力,为了安全牺牲开发速度是值得的。
    • 何时拥抱“交付速度”(选 Node.js/Go/Python):如果你在构建 CRUD 后端、SaaS 业务逻辑、内部管理工具,或者处于需要快速试错、频繁变更需求的初创阶段。在这些场景下,迭代速度(Velocity)才是核心竞争力。
  3. 给 Go 开发者的启示
    有趣的是,Dmitry 在注脚中提到了 Go:“Yes, there is Go. But I never really had the chance to like Go.”
    这其实是一个非常有意思的信号。在 Rust 的“极致安全”和 Node.js 的“极致速度”之间,Go 恰恰占据了那个“黄金平衡点”

    • 它有静态编译和类型系统,比 Node.js 更安全、性能更好。
    • 它有极快的编译速度和简单的语法,比 Rust 的心智负担低得多。
    • 它有极其成熟的中间件和微服务生态。

    对于那些厌倦了 Node.js 运行时错误,又被 Rust 借用检查器拖慢脚步的 Web 开发者来说,Go 依然是当下最务实的选择。

小结

技术选型从来没有绝对的优劣,只有“最适合当下约束条件的工具”。

Dmitry 的故事提醒我们:不要因为手里拿着“安全性”这把锤子(Rust),就无视了“交付速度”这个钉子。在商业软件的世界里,有时候,为了让产品活下去,为了让用户更快用上新功能,“足够好”且“跑得快”的代码,往往比“完美但迟到”的代码更有价值。

Rust 是系统编程的未来,但这并不意味着它是所有 Web 业务的终点。对于独立开发者或初创团队而言,“快”,本身就是一种极其重要的功能。

资料链接:https://yieldcode.blog/post/farewell-rust/


你会为了“安全”放弃“速度”吗?

软件工程永远是权衡的艺术。在你的项目中,你是否也曾为了追求某种“先进特性”,而导致项目进度失控?如果给你 50MB 的内存节省,你愿意多等 10 分钟的编译时间吗?

欢迎在评论区分享你的选型纠结!


还在为“复制粘贴喂AI”而烦恼?我的新专栏 AI原生开发工作流实战 将带你:

  • 告别低效,重塑开发范式
  • 驾驭AI Agent(Claude Code),实现工作流自动化
  • 从“AI使用者”进化为规范驱动开发的“工作流指挥家”

扫描下方二维码,开启你的AI原生开发之旅。


你的Go技能,是否也卡在了“熟练”到“精通”的瓶颈期?

  • 想写出更地道、更健壮的Go代码,却总在细节上踩坑?
  • 渴望提升软件设计能力,驾驭复杂Go项目却缺乏章法?
  • 想打造生产级的Go服务,却在工程化实践中屡屡受挫?

继《Go语言第一课》后,我的《Go语言进阶课》终于在极客时间与大家见面了!

我的全新极客时间专栏 《Tony Bai·Go语言进阶课》就是为这样的你量身打造!30+讲硬核内容,带你夯实语法认知,提升设计思维,锻造工程实践能力,更有实战项目串讲。

目标只有一个:助你完成从“Go熟练工”到“Go专家”的蜕变! 现在就加入,让你的Go技能再上一个新台阶!


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

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 AI原生开发工作流实战 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com
这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

如果您觉得这里的文章对您有帮助,请扫描上方二维码进行捐赠 ,加油后的Tony Bai将会为您呈现更多精彩的文章,谢谢!

如果您希望通过微信捐赠,请用微信客户端扫描下方赞赏码:

如果您希望通过比特币或以太币捐赠,可以扫描下方二维码:

比特币:

以太币:

如果您喜欢通过微信浏览本站内容,可以扫描下方二维码,订阅本站官方微信订阅号“iamtonybai”;点击二维码,可直达本人官方微博主页^_^:
本站Powered by Digital Ocean VPS。
选择Digital Ocean VPS主机,即可获得10美元现金充值,可 免费使用两个月哟! 著名主机提供商Linode 10$优惠码:linode10,在 这里注册即可免费获 得。阿里云推荐码: 1WFZ0V立享9折!


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats