2026-02-10 12:11:49

本文永久链接 – https://tonybai.com/2026/02/10/ai-agent-realizes-ultimate-dream-software-factory
大家好,我是Tony Bai。
在计算机科学与软件工程的历史长河中,始终存在着一个令人魂牵梦绕、却又屡屡受挫的终极梦想——“软件工厂(Software Factory)”。
早在 20 世纪 60 年代,日本的大型科技企业(如日立、东芝)就开始尝试引入制造业的流水线理念来生产软件。80 年代,CASE(计算机辅助软件工程)工具试图实现全流程自动化;21 世纪初,MDA(模型驱动架构)试图通过 UML 图直接生成代码。
然而,这些尝试无一例外都未能成为主流。
为什么?因为软件开发与硬件制造有着本质的不同。硬件是标准化的,而软件需求充满了不确定性(Ambiguity)、非标准化(Non-standard)和创造性(Creativity)。传统的刚性流水线无法处理这种“软”的复杂性。
但这一次,不一样。
随着以 GPT-5.2、Claude 4.5、Gemini Pro 3.0 等为代表的大语言模型(LLM)能力的爆发,以Claude Code、Gemini Cli等编码智能体的快速演进,以及Agentic Workflow(智能体工作流)的成熟,我们第一次拥有了能够理解“非标需求”并将其转化为“标准代码”的通用推理引擎。
特斯拉前 AI 总监 Andrej Karpathy 将这一刻定义为 Software 3.0 的黎明。在这个新时代,那个尘封已久的“软件工厂”蓝图,正在从幻想变成触手可及的现实。
今天,我们就来深度剖析这座正在崛起的 AI 软件工厂,看看它将如何重塑我们的行业、生态与职业。

要理解软件工厂的本质,我们需要先理解 Karpathy 提出的软件演进三阶段论。这是一次技术的迭代,更是编程范式的根本性迁移。

这是我们最熟悉的时代。程序员使用 Go、Python、C++、Java、TypeScript 等语言,编写显式的逻辑规则。
深度学习的兴起带来了 2.0 时代。程序员不再编写规则,而是编写目标(损失函数)和准备数据,由优化器(Optimizer)在神经网络的权重空间中搜索出最优解。这是一个黑盒。虽然它能解决图像识别等 1.0 很难解决的问题,但它缺乏逻辑的可解释性。
现在,我们进入了 3.0 时代。LLM 成为了一个新的、通用的可编程实体。
正是 Software 3.0 的出现,让“输入模糊需求,输出精确系统”成为了可能。
想象一下,未来的软件交付不再是一个团队几周的冲刺,而是一个工厂几分钟的运转。这座工厂不再由传送带和机械臂组成,而是由运行在云端的 Agent Swarm(智能体集群)构成。
这是一座柔性制造的超级工厂,其运作流程如下:
你不需要编写代码,甚至不需要编写格式严格的 PRD 文档。
工厂的原材料可以是极其粗糙的:
需求进入工厂后,会被一个Orchestrator(编排器)接管,并分发给不同的“职能车间”。这些车间由专精不同领域的 Agent 组成:
设计车间 (Architect Agent):
它首先分析需求,进行系统拆解。它会输出:
制造车间 (Coder Agent):
这是工厂的主力军。它会裂变出多个子 Agent 并行工作:
质检车间 (QA Agent):
这是保证“良品率”的关键。QA Agent 不会等到代码写完才介入,而是采用 TDD(测试驱动开发)模式。
装配车间 (DevOps Agent):
代码通过测试后,DevOps Agent 上场。它编写 Terraform 或 Dockerfile,调用 AWS/Aliyun/Cloudflare的 API,自动配置云端环境,进行部署。
工厂的传送带末端,输出的不是一堆冷冰冰的代码文件,而是一个可访问的 URL,一个已经配置好的 Admin 后台,以及一套完善的系统监控仪表盘。
这就是 Software 3.0 的终极形态:Prompt in, System out.

为什么我们要强调这是“柔性”工厂?因为它解决的是传统 CI/CD 流水线最大的痛点——刚性。
传统的流水线是线性的(Build -> Test -> Deploy)。一旦 Test 挂了,流水线就停了,红灯亮起,必须等待人类工程师介入修 Bug。
但 AI 软件工厂是有生命、会呼吸的。
它是基于 Agentic Workflow 的动态有向无环图(DAG),甚至是包含循环的图。
这是一条会思考的流水线。它不仅生产代码,还生产基础设施(IaC)。它与云厂商深度集成,实现了真正的 Serverless——作为用户,你连 Server 都不用感知,你只感知 Service。
当这种“输入需求,输出系统”的工厂模式普及后,软件行业的格局将发生天翻地覆的变化。
传统的软件工程理论(Agile, Scrum, 看板)很大程度上是为了解决“人与人协作”中的摩擦——信息不对称、理解偏差、情绪波动。
但在 AI 工厂里,协作变成了 A2A (Agent-to-Agent) 的协议交互。
未来的软件工程,将从管理“人”,转向管理“协议”和“标准”。协作的重心将聚焦于“人与工厂”的交互——即如何更精准、更高效地向工厂下达指令(Prompt Engineering / Spec Writing)。
在 Software 1.0 时代,开源项目(如 React, Spring, Django)是给人用的库(Library)。我们需要学习它的文档,理解它的 API。
在 Software 3.0 时代,开源项目将变成工厂的“模具”。
我们可能不再直接引用库,而是告诉工厂:“用 React 的模具生产前端”。源代码(Source Code)本身可能会变成像汇编语言一样的中间产物/表示——只有 AI 读写它们,而人类只会面向Spec。
Software is ephemeral. Spec is eternal.(软件是瞬态的,规格是永恒的。)
这是最残酷但也最充满机遇的转变。软件公司的人才结构将呈现极端的两极分化:
我们正处于软件行业“手工作坊”向“机器大工业”过渡的前夜,就像 1760 年代瓦特改良蒸汽机的前夜。
AI 软件工厂 不是科幻小说,它正在此时此刻发生。
Claude Code的Agent Team、针对编码智能体编排的Gas Town等,很可能都是这座工厂雏形的组件。
Karpathy 说的 “The hottest new programming language is English” 并不是一句玩笑。它意味着编程的门槛被无限降低,但构建系统的门槛被无限拔高。
无论你是想做“订货人”还是“厂长”,现在开始学习驾驭 AI Agent,学习如何构建和管理这些“数字员工”,是你拿到新时代船票的唯一方式。
你的“厂长”初体验
“软件工厂”的时代正在加速到来,我们每个人都将面临从“码农”到“订货人”或“厂长”的转型。想象一下,如果你现在拥有一座 24 小时不停工的“AI 软件工厂”,你最想让它为你生产一个什么样的系统?你认为在“机机协作”的未来,人类程序员最后的护城河在哪里?
欢迎在评论区分享你的脑洞与思考!让我们一起在这场软件工业革命的前夜寻找坐标。
如果这篇文章为你揭示了软件工程的未来,别忘了点个【赞】和【在看】,并转发给你的架构师朋友,大家一起未雨绸缪!
亲手搭建你的“微型工厂”
虽然完全自动化的“软件工厂”还在建设中,但其中的核心技术——Agent 编排、Spec 驱动开发——已经触手可及。
在我的极客时间专栏《AI 原生开发工作流实战》中,我将带你从零开始,利用 Claude Code,构建一个微型的 AI 软件流水线。
扫描下方二维码,开启你的 AI 架构师之旅。

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

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

© 2026, bigwhite. 版权所有.
2026-02-10 08:43:33

本文永久链接 – https://tonybai.com/2026/02/10/goodbye-flaky-tests-go-testing-nettest-proposal
大家好,我是Tony Bai。
在 Go 语言的测试哲学中,我们一直追求快速、稳定和可重复。然而,一旦测试涉及到 net 包——无论是 HTTP 服务、RPC 框架还是自定义协议——这种追求往往就会撞上现实的墙壁。
我们通常面临两种选择:要么在 localhost 上监听真实端口,但这会导致测试并发时的端口冲突、防火墙干扰以及操作系统层面的不确定性;要么使用 net.Pipe,但它那“同步、无缓冲”的特性与真实的 TCP 连接大相径庭,常常导致生产环境运行良好的代码在测试中死锁。
为了彻底解决这一“最后一公里”的测试难题,Go 团队的 Damien Neil 提议引入 testing/nettest。这是一个完全在内存中运行,但行为上高度仿真真实网络栈(支持缓冲、异步、错误注入)的实现。
本文将和你一起剖析该提案的背景、设计细节以及它将如何改变我们编写网络测试的方式。

要理解 nettest 的价值,我们首先需要审视现状。目前的 Go 标准库在网络测试辅助方面,存在显著的“中间地带真空”。
net.Pipe() 是目前标准库提供的唯一内存网络模拟工具。但它本质上是一个同步内存管道。
使用回环地址(Loopback)是另一种常见做法,但它带来了“外部依赖”:
Go 1.24 引入了实验性的 testing/synctest 包,旨在通过虚拟时钟解决并发测试中的时间依赖问题。然而,synctest 难以接管真实的系统网络调用。为了让 synctest 发挥最大威力,Go 需要一个完全由用户态代码控制、不依赖操作系统内核的网络实现。nettest 正是这块关键的拼图。

testing/nettest 的目标非常明确:提供 net.Listener、net.Conn 和 net.PacketConn 的内存实现,使其行为尽可能接近真实的 TCP/UDP,同时暴露极强的控制力。

这是 nettest 与 net.Pipe 最大的区别。nettest.Conn 内置了缓冲区。
// 创建一对连接
client, server := nettest.NewConnPair()
// 模拟一个拥塞的连接,缓冲区仅为 1 字节
server.SetReadBufferSize(1)
// 此时写入大量数据,client.Write 将会阻塞,直到 server 端读取
go func() {
client.Write([]byte("hello world"))
}()
在真实网络中,我们可以通过 IP 地址来区分连接来源。nettest 通过 netip.AddrPort 模拟了这一点。
更妙的是 Listener.NewConnConfig 方法,它允许我们在 Server Accept 之前,对“即将到来”的连接进行修改。
实战场景:测试 IP 白名单中间件
以往测试 IP 白名单,你可能需要复杂的 Mock 或者真的去配置网卡。现在:
l := nettest.NewListener()
defer l.Close()
// 模拟一个来自特定 IP 的恶意连接
go func() {
conn := l.NewConnConfig(func(c *nettest.Conn) {
// 伪造源 IP
c.SetLocalAddr(netip.MustParseAddrPort("192.168.1.100:12345"))
})
conn.Close()
}()
conn, _ := l.Accept()
// 在这里断言你的中间件是否正确拒绝了该 IP
网络编程中最难测试的不是“连通”,而是“断连”、“超时”和“读写错误”。nettest 将错误注入标准化了。
它提供了一系列 Set*Error 方法:
你可以通过 SetReadError 模拟连接在中途突然 Reset,验证你的客户端是否会按预期进行重试。这些注入的错误会被自动包装在 *net.OpError 中,以保持与真实网络行为的一致性。
我们在测试中经常需要断言“连接是否已关闭”或者“是否有数据可读”。在标准 net 包中,这通常需要发起一个阻塞的 Read 调用,如果超时则认为无数据。这种基于时间的断言是 Flaky Test 的温床。
nettest 提供了非阻塞的状态查询方法:
配合 synctest,这将允许我们编写出逻辑极其严密、不依赖 time.Sleep 的确定性测试。
除了面向流(Stream)的 TCP 模拟,提案还照顾到了面向报文(Packet)的 UDP。
由于 UDP 没有“连接”的概念,不能像 TCP 那样简单返回一对 Conn。nettest 引入了 PacketNet 的概念,它就像一个微型的内存交换机。
// 创建一个虚拟的 UDP 网络环境
pn := nettest.NewPacketNet()
// 在这个网络中创建两个端点
c1, _ := pn.NewConn(addr1)
c2, _ := pn.NewConn(addr2)
// c1 发送给 c2
c1.WriteTo([]byte("ping"), addr2)
// c2 收到数据
buf := make([]byte, 1024)
n, src, _ := c2.ReadFrom(buf)
这使得测试基于 UDP 的自定义协议(如 QUIC 的某些握手流程、或是自定义的游戏协议)变得轻而易举,且完全隔离于宿主机网络。
在提案的讨论中,Damien Neil 非常清晰地界定了 nettest 的边界。理解它“不做”什么,和理解它“做”什么同样重要。
该提案一经发布,立即引起了 Go 社区资深开发者的强烈共鸣。
下一步是什么?
考虑到 API 表面积较大,Go 团队计划遵循“实验先行”的原则。nettest 将首先在 golang.org/x/exp/testing/nettest 中落地。这意味着我们很快就能在项目中引入并尝鲜了。待经过充分的社区验证和 API 打磨后,它最终将进入标准库,成为 testing 包下的一员猛将。
testing/nettest 的提案,看似只是增加了一个测试工具,实则反映了 Go 团队在工程效能上的深层思考。它试图消除测试中的“不确定性”,让网络测试回归逻辑的本质,而不是与操作系统和网络协议栈的噪声做斗争。
对于我们每一位 Gopher 而言,这意味着未来的测试代码将更少依赖 time.Sleep,更少处理端口冲突,运行速度更快,且更加稳定。让我们拭目以待,并准备好在 x/exp 发布的第一时间去拥抱它。
资料链接:https://github.com/golang/go/issues/77362
聊聊你的测试难题
网络测试中的“随机失败”曾让你抓狂吗?你是否也曾为了避开 net.Pipe 的坑而被迫在测试里撒满 time.Sleep?对于即将到来的 nettest,你最期待它的哪个功能?
欢迎在评论区分享你的测试心得或吐槽!让我们一起期待测试变得更简单、更稳健。
还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

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

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

© 2026, bigwhite. 版权所有.
2026-02-09 08:25:53

本文永久链接 – https://tonybai.com/2026/02/09/amp-kills-vscode-plugin-human-ai-pair-programming-is-dead
大家好,我是Tony Bai。
如果一家 AI 编程工具公司,宣布砍掉它最受欢迎、用户量最大的产品入口,你会怎么想?
这听起来像是商业自杀,但这正是 AMP(从 Sourcegraph 孵化出来的 AI 编程 Agent)刚刚做出的决定。
在 2026 年 2 月的一期播客中,AMP 的创始人 Thorsten 和 Quinn 宣布:将在 60 天后,彻底关停 AMP 的 VS Code 插件和 Cursor 扩展。
要知道,在过去的两年里(2024-2025),IDE 侧边栏(Sidebar)几乎定义了 AI 编程的标准形态。无论是 GitHub Copilot、Cursor 还是早期的 AMP,我们都习惯了在编辑器里写代码,在侧边栏里和 AI “乒乓球”式地对话。
但 AMP 团队认为:这个时代结束了。
“你看着代码,AI 在侧边栏看着你,你们一来一回地对话……这种模式不是未来。对于那 1% 想要活在未来的开发者来说,侧边栏不仅不是助力,反而是枷锁。”
为什么他们敢于“烧掉桥梁”?因为一种全新的开发范式——“AI软件工厂模式(The Factory)”,正在随着 GPT-5.2 和 Claude Opus 4.5的成熟以及新版本编程大模型的发布而全面爆发。
今天,我们深度解读这份极具前瞻性的访谈,看看为什么 IDE 侧边栏必死,以及未来的软件工厂究竟长什么样。

要理解为什么要砍掉侧边栏,首先要理解模型能力的质变。
在 2025 年之前,主流模型(如 Claude 3.5 Sonnet)的特点是“聪明但急躁”。它们非常适合 Smart Mode:你问一个问题,它秒回一段代码;你报错,它秒回修正。这是一种高频的、实时的“结对编程”体验。
但随着 GPT-5.2 Codex 的发布,情况变了。
AMP 推出了一个新的模式:Deep Mode(深度模式)。
“侧边栏”完全无法承载这种体验。
想象一下,如果你在 IDE 侧边栏里发了一个指令,然后 AI 转了 45 分钟圈圈,期间你不敢关窗口,不敢切分支,这是一种多么糟糕的体验?
结论 1:
当 AI 的能力从“秒级补全”进化到“小时级任务”时,它必须脱离 IDE,进入后台,成为一个独立的Worker,而不是依附于编辑器的 Assistant。
访谈中透露了一个令人细思极恐的细节,揭示了 AI 原生开发时代的价值观重构。
AMP 团队为了优化内部的开发效率,重写了他们的构建工具。
他们用 Zig 语言重写了 svelte-check,将其命名为 zvelt-check。这样做的目的是为了让 Agent 跑得更快,且输出的日志更结构化(便于 Agent 解析)。不过,这个新工具也破坏了 VS Code 对 Svelte 的原生支持(Human DX 下降)。人类开发者在编辑器里看到的错误提示变差了,甚至失去了一些高亮功能。
在“人类体验(Human DX)”和“智能体体验(Agent DX)”发生冲突时,AMP 选择了后者。
甚至有一半使用 NeoVim 的员工表示:“我不在乎 VS Code 体验变差,只要 Agent 跑得快就行。”
这是一个标志性的时刻。
长久以来,所有的开发者工具(CLI、Linter、Log)都是为了“让人类读懂”而设计的。我们需要漂亮的颜色、进度条、友好的报错提示。
但在 AI 时代,90% 的工具调用者将是 Agent。Agent 不需要颜色,不需要进度条,它们需要的是极致的速度、结构化的 JSON 输出、幂等的执行逻辑。
结论 2:
未来的工具链,将优先为 AI 优化。如果一个工具对人类不友好但对 AI 友好,它依然会被采用。我们正在主动劣化人类的开发体验,以换取 AI 生产力的十倍跃迁。
访谈中提到了一个名为 “The Melting of Software(软件的消融)” 的概念。这不仅影响开发工具,更影响我们构建产品的方式。
案例 A:Ryan Florence 的健身教练
Ryan 没有使用任何健身 App。他只是打开了 ChatGPT 的语音模式,说:“我在家里的健身房,指导我锻炼。”
AI 说:“做一组深蹲,好了叫我。”
Ryan 做完说:“好了。”
AI 说:“休息 60 秒。”
没有 UI,没有按钮,没有 App。软件消失了,只剩下服务。
案例 B:购物清单的回归
Torston 本想用 Agent 自动化管理 Todoist(一个著名的待办事项 App)。
但他突然意识到:“我为什么要用 Todoist?我的购物清单只有 15 项。Agent 可以直接在一个纯文本文件里管理它。”
如果 Agent 能读懂文本,能实时更新状态,能通过 CLI 提醒我,那我为什么还需要一个复杂的 SaaS 软件?
这指向了一个终极问题:当 Agent 能够理解非结构化数据,并能通过原子化工具(如Skills)操作一切时,传统的“应用软件”是否会大量消亡?
未来的软件,可能不再是精心设计的 GUI,而是一组 Skills(能力) + Context(上下文文件)。
结论 3:
软件正在退化为 API 和数据,中间的“交互层”正在被 Agent 接管。
既然侧边栏死了,我们靠什么来通过 AI 开发?
答案是:CLI + Skills。
AMP 团队展示了他们如何在内部大量使用 Skills。
Skills 是“经验的固化”。
当你教会 Agent 解决一个问题后,让它把过程总结成一个 Skill。下次,它(以及团队里的其他 Agent)就不会再犯错。
这比在 Chat 窗口里一遍遍写 Prompt 要高效得多。
为什么 AMP 敢于砍掉 VS Code 插件?这源于他们独特的公司哲学。
“我们就像一个艺术装置(Art Installation),随时准备自我毁灭和重建。”
在这个技术每 3 个月就迭代一代的疯狂时代,“护城河”是最大的陷阱。
AMP 的 CEO 说:“如果我们因为‘用户习惯’而保留旧功能,我们就会变成哪怕是最好的‘落伍者’。我们必须每 3 个月重新赢得我们的客户。”
“Run towards the fire.”(向着炮火前进。)
如果你看到某个技术趋势正在颠覆你,不要躲避,不要观望,加入它,甚至成为颠覆自己的人。
这篇文章可能让大家感到不安。
你习惯了 VS Code,习惯了 Copilot 的自动补全,习惯了掌控一切。
但在 2026 年的视野里,“人机结对”只是一个过渡形态。
真正的未来属于 Agentic System(智能体系统),属于 Factory(软件工厂)。
在那个未来里:
对于那 1% 愿意走出舒适区、拥抱“Factory Mode”的开发者来说,你们的生产力将不再是线性的增长,而是指数级的爆发。
侧边栏已死,工厂万岁。
资料链接:https://www.youtube.com/watch?v=4rx36wc9ugw
你愿意为效率牺牲体验吗?
AMP 为了 Agent 效率主动劣化人类开发体验(Agent DX > Human DX),这一决定让你感到兴奋还是不安?如果一个工具能让你效率提升 10 倍,但代价是你再也看不清语法高亮,你会接受吗?
欢迎在评论区分享你对“AI 软件工厂”的看法!
提前布局你的“软件工厂”
虽然我们还不能完全抛弃编辑器,但 AMP 倡导的 Agent-Native 开发流,现在就可以开始实践。
在我的极客时间专栏《AI 原生开发工作流实战》中,我们将深度对齐这种前沿理念:
不要等了。扫描下方二维码,现在就构建你的未来开发流。

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

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

© 2026, bigwhite. 版权所有.
2026-02-09 08:23:26

本文永久链接 – https://tonybai.com/2026/02/09/go-immutable-types-8-year-dormant-proposal-awakened
大家好,我是Tony Bai。
2026 年 2 月 4 日,在 Go 语言规范团队的最新一次“语言变更评审会议”纪要中,一个尘封已久的 Issue 赫然在列:proposal: spec: immutable type qualifier #27975。

这个提案最初提交于 2018 年,那是“Towards Go 2”口号喊得最响亮的年代。当时的 Go 社区正沉浸在对泛型、错误处理和不可变性的热烈讨论中。然而,随着泛型的落地,关于不可变性的声音似乎逐渐微弱。
如今,这个提案被重新摆上台面,是否意味着 Go 语言在完成泛型这一宏大叙事后,终于要向“数据竞争”和“防御性编程”这两个顽疾开刀了?
今天,我们就来看看复盘这份长达 8 年的提案,剖析一下“不可变性”对 Go 意味着什么,以及它面临的巨大挑战。

在 Go 1.x 的世界里,我们为了保证数据的安全性,往往需要付出高昂的代价。
假设你有一个包含敏感配置的结构体,你想把它暴露给其他包,但又不希望它被修改:
type Config struct {
Servers []string
// ...
}
// 现在的做法:为了安全,必须返回拷贝
func (c *Config) GetServers() []string {
out := make([]string, len(c.Servers))
copy(out, c.Servers)
return out
}
这种“防御性拷贝”带来了两个严重问题:
正如提案作者 romshark 所言:“我们现在的做法,要么是不安全的(直接返回指针),要么是低效的(防御性拷贝)。”
而不可变类型(Immutable Types)的引入,旨在提供第三种选择:既安全,又高效。
NO.27975 提案的核心思想非常直接:引入一个新的类型限定符(最初建议重载 const,后倾向于引入immut ),让编译器来强制执行“只读”契约。
想象一下这样的 Go 代码:
// 定义一个只读的切片类型
func ProcessData(data immut []byte) {
// 读取是 OK 的
fmt.Println(data[0])
// 修改是编译错误的!
// data[0] = 'X' // Compile Error: cannot assign to immutable type
}
在这个愿景中,不可变性是类型系统的一部分。
这看起来很像 Rust 的 & (immutable reference) 和 &mut (mutable reference),或者 C++ 的 const。但 Go 社区的讨论,揭示了这背后远比想象中复杂的工程难题。
这份提案下的讨论区,堪称 Go 语言设计哲学的“修罗场”。Ian Lance Taylor, Rob Pike 等核心大佬纷纷下场,与社区开发者展开了长达数年的拉锯战。
这是 Ian Lance Taylor 最担心的问题。如果你把一个底层函数的参数标记为 immut,那么所有调用它的上层函数,为了传递这个参数,往往也需要把自己的变量标记为 immut。
这种“传染性”会导致代码库中充斥着 immut 关键字。更糟糕的是,如果你以后需要修改底层函数,让它对数据进行一点点修改,你需要修改整个调用链上的类型签名。这在 C++ 中被称为“const correctness”的噩梦。
bcmills 提出了一个极其尖锐的兼容性问题:现有的 io.Writer 接口定义是 Write(p []byte)。
这似乎陷入了一个死循环:要么破坏所有现有代码,要么新特性无法与标准库兼容。
jimmyfrasche 指出了一个微妙的语义陷阱。
在 C++ 中,const T& 只是意味着“我不可以通过这个引用去修改它”(Read-only View),并不意味着“这个数据本身不会变”。因为可能还有另一个非 const 的指针指向同一块内存,并且正在修改它。
如果是前者(只读视图),它无法解决并发安全问题(数据竞争依然存在)。如果是后者(真正的内容不可变),那么 Go 必须引入一套类似 Rust 的所有权(Ownership)系统来保证“没有其他人在写”。这对于 Go 来说,改动太大了。
既然困难重重,为何在 2026 年的今天,这个提案又被翻出来了?
我认为有几个关键因素:
首先,泛型的“降维打击”。以权限泛型(Permission Genericity)化解兼容性死结。
前面提到了,在 Go 1.18 泛型落地之前,不可变性提案面临着一个被称为“io.Writer 陷阱”的致命矛盾:如果将 io.Writer.Write(p []byte) 改为接受 immut []byte,将导致全世界现有的实现代码因签名不匹配而崩溃;如果不改,只读数据又无法直接传入。
泛型的引入为这一难题提供了全新的解题思路。通过类型约束中的联合类型(Union Types),我们可以实现所谓的“权限泛型性”。这意味着 mutability(可变性)不再是一个硬编码的死结,而可以作为一个类型参数(Type Parameter)来处理。
想象一下,我们可以利用泛型约束定义一个覆盖“可变”与“不可变”两种状态的超集:~[]byte | ~immut []byte。下面是在这种模式下的一个泛型化的Writer接口:
// 这是一个设想中的“权限泛型”接口
type Writer[T ~[]byte | ~immut []byte] interface {
Write(p T) (n int, err error)
}
泛型化的 Write[T ~[]byte | ~immut []byte](p T) 方法,在逻辑上可以产生如下影响:
其次,性能压力的倒逼。
随着 Go 在高性能领域的应用越来越深(如数据库、AI 推理),对于“零拷贝”的需求越来越强烈。能够安全地共享内存,是提升性能的关键。
最后是安全性需求。
在并发编程中,数据竞争依然是 Go 程序的头号杀手。go vet 和 race detector 虽然好用,但它们是运行时的、滞后的。社区渴望一种编译期的保证。
虽然完全的“不可变类型”可能依然很难落地,但我们可以期待一些更温和的替代方案:
NO.27975 提案的“复活”,是一个信号。它表明 Go 团队并没有满足于现状,依然在探索如何在保持“简单”这一核心价值观的同时,赋予语言更强的表达力和安全性。
无论最终结果如何,这都是 Go 语言演进史上值得铭记的一笔。它提醒我们:在软件工程中,没有免费的午餐,每一个简单的特性背后,都是无数次复杂的权衡。
你支持引入 immut 吗?
面对“性能”与“简单”的博弈,你是否愿意为了消除数据竞争而接受 immut 带来的“类型传染”?在你的项目中,是否也曾深受“防御性”的性能困扰?
欢迎在评论区分享你的看法,或者聊聊你最期待的 Go 演进方向!
还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

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

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

© 2026, bigwhite. 版权所有.
2026-02-08 07:39:52

本文永久链接 – https://tonybai.com/2026/02/08/go-boilerplate-code-vs-rust-data-refutes-stereotypes
大家好,我是Tony Bai。
在编程语言的鄙视链中,Go 语言常常因为其“繁琐”而饱受诟病。
“if err != nil 写断手”、“缺乏语法糖”、“到处都是重复的样板代码”…… 这些似乎已经成为了 Go 的标签。
相比之下,Rust 往往被视为“表达力”的代表,拥有强大的宏、模式匹配和类型系统,能够用更少的代码做更多的事。
然而,Ben Boyter 最近的一项硬核研究,通过分析 GitHub 上各语言 Top 100 仓库(总计约 4 亿行代码),得出了一个令编程语言社区大跌眼镜的结论:
在代码重复率和“样板代码”密度上,Go 和 Rust 几乎处于同一水平线。

传统的 SLOC(源代码行数)往往无法真实反映项目的复杂度和冗余度。Ben Boyter 使用了他开发的工具 scc 中的一个特殊指标:ULOC (Unique Lines of Code,唯一代码行数)。
ULOC 指标并非简单的“全量去重”,而是通过剥离“结构性噪音”来更精准地衡量系统的真实复杂度。其计算逻辑如下:
通过这种方式计算出的 Dryness(干度),代表了剔除“语法支架”和“版权模板”后,真正的业务逻辑与注释在代码中的占比。百分比越高,说明重复代码越少,信息密度越高;百分比越低,说明“样板代码”或重复结构越多。
让我们直接看数据(数据来源:GitHub Top 100 仓库分析,2026年2月):

发现了吗?Rust (60.5%) 和 Go (58.78%) 的差距微乎其微,甚至可以说在统计学上是等价的。
Ben Boyter 在文章中坦言,他之前也持有“Go 的样板代码比 Rust 多得多”的刻板印象。但数据表明,虽然两者的“啰嗦”方式不同,但结果是一样的:
正如作者所总结的:
Go 狂热者:“Go 很简单!” -> “是的,简单到你需要把同一件事写很多遍。”
Rust 狂热者:“Rust 完美表达!” -> “是的,但你花了 40% 的时间在写 setup 代码和 trait 实现。”
除了 Go 和 Rust 的“握手言和”,这份报告还有几个极具冲击力的发现:
Clojure 以 77.91% 的惊人密度位居榜首。Haskell 紧随其后。
这验证了一个古老的观点:如果你想要最高的“人类思想 vs 击键次数”比率,Lisp 和函数式语言依然是王者。它们几乎每一行代码都是纯粹的业务逻辑。
Java 的得分为 65.72%,显著高于 Go、Rust 和 C#。
这听起来反直觉,毕竟 Java 以 PublicStaticVoidMain 这种冗长著称。但这可能说明:
Shell Script 的密度极高(72.24%),但这主要是因为 Shell 脚本通常很短且高度定制化(Bespoke),很难复用,因此“唯一性”很高。
这个研究告诉我们一个道理:语言特性(Features)并不一定能消除复杂度,它往往只是转移了复杂度。
Go 选择了少量的特性,导致逻辑必须通过显式的重复代码来表达;Rust 选择了丰富的特性(宏、泛型、Trait),导致开发者必须编写大量的结构性代码来支撑这些特性。
对于 Gopher 来说,这或许是一种宽慰:别再为 if err != nil 感到羞愧了。隔壁写 Rust 的兄弟,虽然代码看起来很酷,但他们为了让编译器开心而敲击键盘的次数,并不比你少。
毕竟,软件工程没有银弹,只有取舍。
资料链接:https://boyter.org/posts/boilerplate-tax-ranking-popular-languages-by-density/
聊聊你的“啰嗦”体验
看完这个数据,你是感到“意料之中”还是“大吃一惊”?在你的实际开发中,你觉得 Go 的 if err != nil 更磨人,还是 Rust 的类型体操和 Trait 实现更让你头大?你认同“复杂度守恒”这个观点吗?
欢迎在评论区留下你的看法,让我们来一场理性的“语言之争”!
还在为“复制粘贴喂AI”而烦恼?我的新专栏 《AI原生开发工作流实战》 将带你:
扫描下方二维码,开启你的AI原生开发之旅。

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

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

© 2026, bigwhite. 版权所有.