<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Tony Bai &#187; 技术志</title>
	<atom:link href="http://tonybai.com/category/technical-notes/feed/" rel="self" type="application/rss+xml" />
	<link>https://tonybai.com</link>
	<description>一个程序员的心路历程</description>
	<lastBuildDate>Sun, 12 Apr 2026 22:30:28 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>别再无脑 go func() 了！Go 资深布道师 Dave Cheney 的 Goroutine 管理哲学</title>
		<link>https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy/</link>
		<comments>https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy/#comments</comments>
		<pubDate>Sun, 12 Apr 2026 22:29:12 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ConcurrencyModel]]></category>
		<category><![CDATA[ConcurrentProgramming]]></category>
		<category><![CDATA[ContextManagement]]></category>
		<category><![CDATA[CooperativeCancellation]]></category>
		<category><![CDATA[DaveCheney]]></category>
		<category><![CDATA[deadlock]]></category>
		<category><![CDATA[errgroup]]></category>
		<category><![CDATA[GCRoots]]></category>
		<category><![CDATA[GC根节点]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[goroutine]]></category>
		<category><![CDATA[GracefulShutdown]]></category>
		<category><![CDATA[HighAvailabilityArchitecture]]></category>
		<category><![CDATA[MemoryLeak]]></category>
		<category><![CDATA[pkg/group]]></category>
		<category><![CDATA[RaceCondition]]></category>
		<category><![CDATA[ServiceGovernance]]></category>
		<category><![CDATA[StructuredConcurrency]]></category>
		<category><![CDATA[SyncWaitGroup]]></category>
		<category><![CDATA[Tomb]]></category>
		<category><![CDATA[上下文管理]]></category>
		<category><![CDATA[优雅关闭]]></category>
		<category><![CDATA[内存泄漏]]></category>
		<category><![CDATA[协同退出]]></category>
		<category><![CDATA[并发模型]]></category>
		<category><![CDATA[并发编程]]></category>
		<category><![CDATA[服务治理]]></category>
		<category><![CDATA[死锁]]></category>
		<category><![CDATA[竞态条件]]></category>
		<category><![CDATA[结构化并发]]></category>
		<category><![CDATA[高可用架构]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6178</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy 大家好，我是Tony Bai。 在 Go 语言的江湖里，go func() 就像一把绝世好剑。它轻灵、锋利，只需几个字符，就能让你瞬间拥有“分身术”，并发地处理海量任务。Go 团队曾自豪地告诉我们：Goroutine 很廉价，你可以随手启动成千上万个。 于是，我们习惯了在代码里肆意挥洒： HTTP 请求来了？go handle()。 要写日志？go log()。 要发通知？go notify()。 &#8230; &#8230; 我们以为自己掌握了并发的捷径。 但就在去年的 GopherCon Singapore 技术大会上，Go 社区的资深布道师 Dave Cheney，却用一场充满哲学思考的演说，给所有 Gopher 敲响了警钟。 他的核心论点很明确：Goroutine 绝非免费的午餐，它是一种需要付出代价的“有限资源”。如果你只管启动（Start）而不懂如何停止（Stop），你并没有在写高效的并发程序，你只是在为系统埋下慢性自杀的伏笔。 今天，我们就来深度拆解 Dave Cheney 的这场重要演讲，梳理出他在 AI 大模型和微服务时代，为我们总结的 “Goroutine 声明周期管理四大哲学”以及他最终给出的Goroutine管理方案。 哲学一：内存是有价的，而 Goroutine 是“内存之根” Dave Cheney 在演讲开头提出了一个极其硬核的观点：内存不是无限的，它是和数据库连接、文件句柄一样的有限资源。 在 Java 或 C++ 中，我们要时刻担心内存泄漏。但在 Go 里，我们觉得有 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy">本文永久链接</a> &#8211; https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy</p>
<p>大家好，我是Tony Bai。</p>
<p>在 Go 语言的江湖里，go func() 就像一把绝世好剑。它轻灵、锋利，只需几个字符，就能让你瞬间拥有“分身术”，并发地处理海量任务。Go 团队曾自豪地告诉我们：Goroutine 很廉价，你可以随手启动成千上万个。</p>
<p>于是，我们习惯了在代码里肆意挥洒：</p>
<ul>
<li>HTTP 请求来了？go handle()。</li>
<li>要写日志？go log()。</li>
<li>要发通知？go notify()。</li>
<li>&#8230; &#8230;</li>
</ul>
<p>我们以为自己掌握了并发的捷径。</p>
<p>但就在去年的 GopherCon Singapore 技术大会上，Go 社区的资深布道师 Dave Cheney，却用<a href="https://www.youtube.com/watch?v=eJLVT157BSs">一场充满哲学思考的演说</a>，给所有 Gopher 敲响了警钟。</p>
<p>他的核心论点很明确：Goroutine 绝非免费的午餐，它是一种需要付出代价的“有限资源”。如果你只管启动（Start）而不懂如何停止（Stop），你并没有在写高效的并发程序，你只是在为系统埋下慢性自杀的伏笔。</p>
<p>今天，我们就来深度拆解 Dave Cheney 的这场重要演讲，梳理出他在 AI 大模型和微服务时代，为我们总结的 “Goroutine 声明周期管理四大哲学”以及他最终给出的Goroutine管理方案。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-qr.png" alt="" /></p>
<h2>哲学一：内存是有价的，而 Goroutine 是“内存之根”</h2>
<p>Dave Cheney 在演讲开头提出了一个极其硬核的观点：<strong>内存不是无限的，它是和数据库连接、文件句柄一样的有限资源。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-2.png" alt="" /></p>
<p>在 Java 或 C++ 中，我们要时刻担心内存泄漏。但在 Go 里，我们觉得有 <a href="https://tonybai.com/2026/04/07/garbage-collectors-deep-dive">GC（垃圾回收器）</a>在，一切无忧。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-3.png" alt="" /></p>
<p>然而，Dave 指出了一个被 99% 的人忽略的真相：<strong>在 Go 的世界里，每一个正在运行的 Goroutine，都是一个“GC 根节点（GC Root）”。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-4.png" alt="" /></p>
<p><strong>什么意思？</strong></p>
<p>只要一个 Goroutine 还在运行，它所引用的所有内存、它栈上的所有变量、它指向的所有堆对象，GC 都<strong>绝对不敢</strong>回收。</p>
<blockquote>
<p>“你可以关闭一个文件，可以解锁一个互斥锁。但你如何‘回收’一个失控的 Goroutine？”</p>
</blockquote>
<p>如果你启动了一个 Goroutine 后失去了对它的追踪，它就变成了一个永远无法回收的“内存僵尸”。它不仅自己霸占着 2KB 以上的栈空间，更可能死死拽着几个 GB 的业务对象不撒手。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-5.png" alt="" /></p>
<h2>哲学二：永远不要启动一个你不知道如何停止的 Goroutine</h2>
<p>这是 Dave Cheney 演讲中最核心的一句军规：<strong>Never start a goroutine without knowing how it will stop.</strong></p>
<p>为了证明“野 Goroutine”的破坏力，Dave 在现场演示了一个极其经典的血泪 Demo。</p>
<p>他写了一个 HTTP 服务器，为了让请求秒回，他把日志记录放到了后台：go logRequest(r)。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-7.png" alt="" /></p>
<p>接着，他通过重定向标准输出模拟了下游日志系统网络拥堵、写入被阻塞的场景。</p>
<p><strong>恐怖的一幕发生了：</strong></p>
<p>服务器内存开始疯狂飙升，每秒钟都有成百上千个新的 Goroutine 被创建，但因为输出被阻塞，它们全都卡在写入的那一行，一个都死不掉。<br />
不到一分钟，整个程序因为 OOM（内存溢出）当场暴毙。</p>
<p><strong>Dave 的结论非常冷酷：</strong></p>
<p>启动一个 Goroutine 只需要 1 微秒，但如果不考虑它的“死法”，这个 Goroutine 最终会成为杀掉你整个集群的凶手。</p>
<h2>哲学三：不要强迫它停，要“优雅地求它停”</h2>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-8.png" alt="" /></p>
<p>在 Java 中，曾经有一个 thread.stop() 方法，后来被禁用了，因为它会引发不可控的资源损坏。Go 语言聪明地避开了这个坑：<strong>Go 没有任何一种方式，能让一个 Goroutine 强行停止另一个。</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-6.png" alt="" /></p>
<p>你只能通过 <strong>“协同（Cooperation）”</strong>。</p>
<p>Dave 强调，defer 是 Goroutine 的“临终遗言”。所有的资源释放（文件关闭、锁解除）都必须放在 defer 里。</p>
<p>而管理这一切的唯一“生死符”，就是 <strong>Context</strong>。</p>
<p>在 Dave 的哲学里，一个合格的后台服务函数，必须长成这样：</p>
<pre><code class="go">func (s *Service) Run(ctx context.Context) error {
    // 1. 临终遗言：无论如何，最后一定要清理战场
    defer s.cleanup() 

    for {
        select {
        case &lt;-ctx.Done():
            // 2. 收到“生死符”，优雅退出
            return ctx.Err()
        case task := &lt;-s.taskChan:
            s.process(task)
        }
    }
}
</code></pre>
<p>你必须给 Goroutine 一个“想得开”的机会，让它在收到 ctx.Done() 时，带着所有的 defer 体面地离开。</p>
<h2>哲学四：把并发权留给调用者，而不是库</h2>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-9.png" alt="" /></p>
<p>这是 Dave Cheney 给库开发者（Library Authors）提出的最高阶要求。</p>
<p>他引用了另一位大神 Peter Bourgon 的话：<strong>“Leave concurrency to the caller.”</strong></p>
<p><strong>一个设计糟糕的库：</strong> 在你调用 NewProvider() 的时候，悄悄在后台启动了一个 Goroutine 去跑心跳，却没给你返回任何停止它的句柄。这种库是不可靠的。</p>
<p><strong>一个具有“管理哲学”的库：</strong> 即使它需要后台运行，它也应该把那个 Run 函数暴露给用户，让用户自己决定：</p>
<ul>
<li>是开一个 Goroutine 去跑它？</li>
<li>还是把它扔进一个 errgroup 里集中管控？</li>
<li>还是干脆同步运行它？</li>
</ul>
<p>只有这样，作为顶层架构师的你，才能真正实现所有子系统的 <strong>“同生共死”</strong>。</p>
<h2>历史的挣扎：从 Tomb 到 Errgroup，我们与“失控”的斗争</h2>
<p>事实上，Go 社区与“Goroutine 管理”这个恶魔的斗争，从 2012 年就开始了。Dave带着我们一起回顾了一下社区的方案，虽然每个方案都不完美！</p>
<p><strong>第一代武器：Tomb (坟墓)</strong></p>
<p>来自 Canonical（Ubuntu 母公司）的 Juju 项目，发明了 tomb 包。它通过一个 t.Go() 方法来启动 Goroutine，并用一个 t.Wait() 来等待它们全部结束。但它的缺点是，如何通知这些 Goroutine“你们该停了”，依然需要开发者手动传来传去。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-10.png" alt="" /></p>
<p><strong>第二代武器：Errgroup</strong></p>
<p>由 Go 社区大神 Brad Fitzpatrick 编写的 errgroup，极大地简化了“并发执行一组任务，并收集第一个错误”的场景。但它同样没有解决“如何优雅地通知所有任务提前中止”的问题。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-11.png" alt="" /></p>
<p><strong>第三代武器：OK Log 的 group 包</strong></p>
<p>由 Peter Bourgon 设计的 group 包，首次引入了一个极其优雅的范式。它要求你在添加一个任务时，必须同时提供两个函数：一个 execute 函数（如何启动），和一个 interrupt 函数（如何打断）。</p>
<p>这是一种“契约式”的设计，强制开发者在启动一个 Goroutine 的时候，就必须想好如何杀死它。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-12.png" alt="" /></p>
<h2>Dave Cheney 的Goroutine管理方案</h2>
<p>在吸收了上述哲学以及社区尝试后，Dave 给出了一个现代 Go 微服务的“标准起手式”，当然也是他自己的Goroutine管理方案：pkg/group。</p>
<p>在吸收了社区十几年来的所有经验和教训之后，Dave Cheney 在演讲的最后，亮出了他自己多年来在无数个项目中沉淀下来的“终极武器”——一个同样名为 group 的、集大成的 Goroutine 管理库：pkg/group，也可以认为是一个现代 Go 微服务的“标准起手式”：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-15.png" alt="" /></p>
<p>在 Dave Cheney 的 group 里，你添加的每一个任务，都必须是一个接受 context.Context 作为参数的函数。</p>
<pre><code class="go">g.Add(func(ctx context.Context) error {
    // ...
})
</code></pre>
<p>Context 成了所有 Goroutine 唯一的“生死符”。无论是超时、是上游请求被取消、还是整个服务收到了 SIGTERM 信号准备关闭，都会通过 ctx.Done() 这个唯一的通道，通知到每一个角落。</p>
<p>在 Dave Cheney 的 group 中，任何一个子 Goroutine 发生的 panic，都不会导致整个进程崩溃。它会被 recover 住，转化为一个 error，然后触发整个 group 的优雅关闭流程。</p>
<p>pkg/group的使用典型示例如下：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-13.png" alt="" /><br />
<img src="https://tonybai.com/wp-content/uploads/2026/dave-cheney-goroutine-management-philosophy-14.png" alt="" /></p>
<p>在这段代码里，所有的后台服务被捆绑成了一个“命运共同体”。任何一个服务失败，或者 k8s 发来关闭 Pod 的信号，都会导致所有服务一起进入优雅关闭流程，确保数据不丢失、连接被妥善断开。</p>
<h2>小结</h2>
<p>从“启动”到“坟墓”，Dave Cheney 为我们揭示了并发编程的下半场：<strong>Goroutine管理</strong></p>
<p>go func() 赋予了我们随手创造并发的权力，但真正体现架构师功力的，是你管理这些并发生命周期的责任感。</p>
<p>下一次，当你在键盘上敲下那几个字符时，请停顿一秒。</p>
<p>想一想：<strong>这把剑挥出去，你还能收回来吗？</strong></p>
<p>资料链接：https://www.youtube.com/watch?v=eJLVT157BSs</p>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>在你的项目中，是否曾遇到过 Goroutine 泄漏导致的内存灾难？你是如何定位出那个“失踪”的 Goroutine 的？</p>
<p>欢迎在评论区分享你的避坑经验!</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/13/dave-cheney-goroutine-management-philosophy/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AI 时代，敏捷宣言已死？听听 Martin Fowler 和 Kent Beck 怎么说</title>
		<link>https://tonybai.com/2026/04/12/agile-manifesto-dead-in-ai-era-martin-fowler-kent-beck/</link>
		<comments>https://tonybai.com/2026/04/12/agile-manifesto-dead-in-ai-era-martin-fowler-kent-beck/#comments</comments>
		<pubDate>Sat, 11 Apr 2026 22:56:06 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[AgileDevelopment]]></category>
		<category><![CDATA[AgileManifesto]]></category>
		<category><![CDATA[AIAssistedProgramming]]></category>
		<category><![CDATA[AI辅助编程]]></category>
		<category><![CDATA[ArtificialIntelligence]]></category>
		<category><![CDATA[Codereview]]></category>
		<category><![CDATA[CognitiveAwakening]]></category>
		<category><![CDATA[ExtremeProgramming]]></category>
		<category><![CDATA[FullstackEngineering]]></category>
		<category><![CDATA[HumanAIInteraction]]></category>
		<category><![CDATA[HumanCollaboration]]></category>
		<category><![CDATA[KentBeck]]></category>
		<category><![CDATA[MartinFowler]]></category>
		<category><![CDATA[ProgrammingParadigm]]></category>
		<category><![CDATA[Refactoring]]></category>
		<category><![CDATA[SoftwareComplexity]]></category>
		<category><![CDATA[SoftwareEngineering]]></category>
		<category><![CDATA[SystemDesign]]></category>
		<category><![CDATA[TrustDebt]]></category>
		<category><![CDATA[VibeCoding]]></category>
		<category><![CDATA[人工智能]]></category>
		<category><![CDATA[人机交互]]></category>
		<category><![CDATA[人类协作]]></category>
		<category><![CDATA[代码审查]]></category>
		<category><![CDATA[信任债务]]></category>
		<category><![CDATA[全栈工程]]></category>
		<category><![CDATA[敏捷宣言]]></category>
		<category><![CDATA[敏捷开发]]></category>
		<category><![CDATA[智能体]]></category>
		<category><![CDATA[极限编程]]></category>
		<category><![CDATA[氛围编程]]></category>
		<category><![CDATA[系统设计]]></category>
		<category><![CDATA[编程范式]]></category>
		<category><![CDATA[认知觉醒]]></category>
		<category><![CDATA[软件复杂度]]></category>
		<category><![CDATA[软件工程]]></category>
		<category><![CDATA[重构]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6173</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/12/agile-manifesto-dead-in-ai-era-martin-fowler-kent-beck 大家好，我是Tony Bai。 25 年前，在美国犹他州的一间滑雪小屋里，17 位当时最顶尖的软件开发者聚集一堂，共同签署了一份将彻底改变未来二十年软件工程形态的纲领——《敏捷软件开发宣言》。 在这 17 位“上古大神”中，有两个名字，如同北极星一般，指引了一代又一代程序员的成长：一位是《重构》的作者 Martin Fowler，另一位则是“极限编程（XP）”之父、敏捷宣言的发起人 Kent Beck。 25 年后的今天，当生成式 AI 的海啸席卷全球，当“敏捷迭代”被 AI 的“瞬间生成”无情碾压时，我们不禁要问：敏捷已死吗？我们曾经信奉的那些工程哲学，还剩下什么？ 就在前几天，在一个汇聚了硅谷最火热 AI 创业者的闭门活动上，这两位白发苍苍的“活化石”出人意料地并肩坐到了一起，进行了一场关于 AI 时代的世纪对话。 他们没有去鼓吹 AI 带来了多高的效率，反而用一种极其深刻、甚至有些悲观的视角，对当下这场“AI 狂欢”提出了终极拷问。这场对话，值得我们每一个身处其中的技术人，暂停手中飞速生成的代码，静下心来，一字一句地读完。 历史的轮回：AI，不过是又一个“微处理器” 面对台下年轻开发者对 AI 的狂热与恐慌，Kent Beck 的开场异常平静。他把时间拉回到了自己还是个孩子的时候。 “在微处理器（Microprocessor）诞生之前，电脑是一个你根本搬不动的庞然大物。当英特尔 4004 芯片问世时，我们突然意识到，‘等等，这也是一台电脑！’ 突然之间，你能做的事情的想象空间被无限放大了。” Kent Beck 认为，今天的 AI，在本质上与当年的微处理器、后来的面向对象、再后来的互联网浪潮并无不同。它们都是“想象力的放大器”。 他坦言自己现在正在用 AI 去做一些“极其离谱的、野心勃勃的项目”，比如用 Rust 写库级别的高质量代码。“很多都会失败，但这没关系，这就是探索的一部分。” 而 Martin Fowler 则补充了他对技术浪潮的“二阶思考”： “你必须在‘怀疑主义’和‘好奇心’之间找到完美的平衡。我对区块链就极其怀疑。但我的怀疑主义必须是绝对的——这意味着，我必须连我自己的怀疑本身，都保持怀疑。” 他坦言，自己一开始对 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/agile-manifesto-dead-in-ai-era-martin-fowler-kent-beck-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/12/agile-manifesto-dead-in-ai-era-martin-fowler-kent-beck">本文永久链接</a> &#8211; https://tonybai.com/2026/04/12/agile-manifesto-dead-in-ai-era-martin-fowler-kent-beck</p>
<p>大家好，我是Tony Bai。</p>
<p>25 年前，在美国犹他州的一间滑雪小屋里，17 位当时最顶尖的软件开发者聚集一堂，共同签署了一份将彻底改变未来二十年软件工程形态的纲领——《<a href="https://agilemanifesto.org/">敏捷软件开发宣言</a>》。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agile-manifesto-dead-in-ai-era-martin-fowler-kent-beck-2.png" alt="" /></p>
<p>在这 17 位“上古大神”中，有两个名字，如同北极星一般，指引了一代又一代程序员的成长：一位是《重构》的作者 Martin Fowler，另一位则是“极限编程（XP）”之父、敏捷宣言的发起人 Kent Beck。</p>
<p>25 年后的今天，当生成式 AI 的海啸席卷全球，当“敏捷迭代”被 AI 的“瞬间生成”无情碾压时，我们不禁要问：<strong>敏捷已死吗？我们曾经信奉的那些工程哲学，还剩下什么？</strong></p>
<p>就在前几天，在一个汇聚了硅谷最火热 AI 创业者的闭门活动上，这两位白发苍苍的“活化石”出人意料地并肩坐到了一起，进行了<a href="https://www.youtube.com/watch?v=CZs8J1ZD0CE">一场关于 AI 时代的世纪对话</a>。</p>
<p>他们没有去鼓吹 AI 带来了多高的效率，反而用一种极其深刻、甚至有些悲观的视角，对当下这场“AI 狂欢”提出了终极拷问。这场对话，值得我们每一个身处其中的技术人，暂停手中飞速生成的代码，静下心来，一字一句地读完。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-software-engineering-qr.png" alt="" /></p>
<h2>历史的轮回：AI，不过是又一个“微处理器”</h2>
<p>面对台下年轻开发者对 AI 的狂热与恐慌，Kent Beck 的开场异常平静。他把时间拉回到了自己还是个孩子的时候。</p>
<blockquote>
<p>“在微处理器（Microprocessor）诞生之前，电脑是一个你根本搬不动的庞然大物。当英特尔 4004 芯片问世时，我们突然意识到，‘等等，这也是一台电脑！’ 突然之间，你能做的事情的想象空间被无限放大了。”</p>
</blockquote>
<p>Kent Beck 认为，今天的 AI，在本质上与当年的微处理器、后来的面向对象、再后来的互联网浪潮并无不同。它们都是“想象力的放大器”。</p>
<p>他坦言自己现在正在用 AI 去做一些“极其离谱的、野心勃勃的项目”，比如用 Rust 写库级别的高质量代码。“很多都会失败，但这没关系，这就是探索的一部分。”</p>
<p>而 Martin Fowler 则补充了他对技术浪潮的“二阶思考”：</p>
<blockquote>
<p>“你必须在‘怀疑主义’和‘好奇心’之间找到完美的平衡。我对区块链就极其怀疑。但我的怀疑主义必须是绝对的——这意味着，我必须连我自己的怀疑本身，都保持怀疑。”</p>
</blockquote>
<p>他坦言，自己一开始对 Copilot 这种东西也极度不屑，觉得它生成的都是垃圾。直到他读了 Simon Willison 的博客，才意识到：要用好一个工具，你必须先学会如何用好它。这和当年很多人嘲笑“面向对象”没用，但其实只是他们自己没有用对，是同一个道理。</p>
<h2>戳破幻觉：“敏捷”的敌人，从来不是瀑布开发</h2>
<p>当被问及“AI 承诺的‘更快、更好、更便宜’，与 25 年前敏捷宣言的初衷是否一致”时，Kent Beck 抛出了一个极其扎心的观点：</p>
<blockquote>
<p>“事实证明，企业根本不想要更快、更好、更便宜。在一个公司内部，各种激励机制的错位，导致他们会惩罚那些真正追求效率的人。”</p>
</blockquote>
<p>Martin Fowler 对此深有同感。他认为，AI 与敏捷最大的不同在于，当年他们需要费尽口舌去说服企业“敏捷有多重要”，而今天，<strong>没有任何一家公司敢对 AI 的重要性视而不见。</strong></p>
<p>但这恰恰是最大的陷阱。</p>
<p>当年的“敏捷转型”，在无数企业中最终都演变成了一场“形式主义的灾难”，催生了庞大的“敏捷工业复合体”。</p>
<p>而今天，同样的剧本正在 AI 身上重演。无数根本不懂技术的咨询公司，正在兜售着各种“AI 转型”的灵丹妙药。</p>
<p><strong>AI 正在成为新的“蛇油（Snake Oil）”。</strong></p>
<blockquote>
<p>注：“蛇油”是19 世纪的美国民间骗局，有人贩卖一种据说能治百病的“蛇油”之类的神药。其核心特征是用夸张的疗效宣传、用故事/神秘疗法包装、同时缺乏科学依据，最后你花钱买到的往往是没用甚至有害的东西。</p>
</blockquote>
<h2>架构师的终极拷问：AI 正在摧毁程序员的“社交”</h2>
<p>如果说对“蛇油”的警惕还只是宏观层面的担忧，那么 Kent Beck 接下来提出的观点，则直接刺向了每一个正在享受 AI 编码便利的开发者。</p>
<p>他认为，AI 正在让软件开发<strong>“重新孤岛化（Re-soloing of programming）”</strong>。</p>
<blockquote>
<p>“极限编程（XP）很大一部分工作，是为那些天生不善社交的程序员，创造一个安全的社交环境。在一个 XP 团队里，人们每天花几个小时进行结对编程、激烈讨论，并乐在其中。”</p>
<p>“但我现在看到的是什么？‘我是一个程序员，我手下有 6 个 Agent，所以我是一个小团队的管理者。’ <strong>不，你不是。你只是在同时使用 6 个工具。</strong>”</p>
</blockquote>
<p>在过去，我们把程序员从一个个封闭的办公室里解放出来，让他们围坐在一起，通过“混乱、复杂、充满人味儿”的社交过程，去创造伟大的软件。</p>
<p>而现在，我们似乎又在主动退回那个“把程序员关进小黑屋，从门缝底下塞披萨”的时代。只不过，这次陪伴你的，是几个冰冷的 AI 机器人。</p>
<p>Martin Fowler 也表达了同样的担忧：</p>
<blockquote>
<p>“未来的团队，到底是‘一个披萨的团队’（因为 Agent 不吃披萨），还是一个‘两个披萨的团队，但效率翻倍’？<strong>我赌后者。</strong>”</p>
</blockquote>
<p>他认为，<strong>“两个人类 + N 个 AI”</strong> 的结对编程模式，可能是未来的答案。因为两个人类可以更好地控制 AI 的方向，同时保留了宝贵的人类交互。</p>
<p>有趣的是，Kent Beck 甚至觉得现在的 AI 有点“太快了”。</p>
<blockquote>
<p>“当 AI 需要 3 分钟才能返回结果时，我们正好可以利用这段时间，去讨论一下变量命名的哲学，或者下一步的架构方向。但如果它 15 秒就返回了，我们就失去了交流的时间。”</p>
</blockquote>
<h2>手艺人的黄昏：当 AI 剥夺了“重构的快感”</h2>
<p>在对话的最后，当被问及“AI 时代，程序员该如何自处”时，Kent Beck 的一段独白，充满了“手艺人”的失落与悲情，足以让每一个热爱编码的资深开发者瞬间破防。</p>
<blockquote>
<p>“我过去在编程中获得的一种‘强迫症’般的享受，正在消失。那种把一个文件从一坨屎山，通过无数个微小、安全的步骤，最终重构成一件艺术品的快感，再也没有了。”</p>
<p>“我依然可以从宏观上理解我正在做什么。但我需要把我的关注点，从享受‘雕琢程序本身’，转移到享受‘理解业务领域’上。因为在‘雕琢程序’这件事上，我们已经失去了杠杆。”</p>
</blockquote>
<p>Martin Fowler 则给出了更具操作性的建议：</p>
<blockquote>
<p>“一个有趣的现象是：开发者体验（Developer Experience）和智能体体验（Agent Experience）的维恩图，是一个完美的圆。对 Agent 友好的代码，对人类也友好。”</p>
</blockquote>
<p>他认为，拥有良好模块化、清晰接口和完备测试的代码，AI 处理起来会更得心应手。我们过去几十年积累的那些“手艺”，并没有过时，它们只是从“指导人类”变成了“指导 AI”。</p>
<h2>小结：在不确定的浪潮中，抓住不变的礁石</h2>
<p>这场持续了一个多小时的对话，没有给出任何关于“如何写 Prompt”、“用哪个模型”的答案。</p>
<p>但这两位穿越了数个技术周期的智者，用他们的人生经验，为我们指明了在 AI 这场史无前例的巨浪中，唯一能抓住的几块礁石：</p>
<ol>
<li>保持绝对的怀疑，包括对怀疑本身的怀疑。</li>
<li>学会设计最小化的实验，亲自去验证那些天花乱坠的说法。</li>
<li>不要放弃与人交流，那才是创造力的真正源泉。</li>
<li>把你的代码写得更清晰、更模块化、测试更完备。这不仅是为了你自己，更是为了你未来的 AI 同事。</li>
</ol>
<p>最后，Kent Beck 给出了一个极其悲壮的建议：<strong>或许，我们是时候放弃享受“雕琢代码”的乐趣，而去享受“理解世界”的乐趣了。</strong></p>
<p>这或许是对 AI 时代，我们这些“数字手艺人”最深刻、也最无奈的宿命注解。</p>
<p>资料链接：https://www.youtube.com/watch?v=CZs8J1ZD0CE</p>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>在使用 AI 编程后，你是否也像 Kent Beck 一样，感觉失去了那种“重构屎山”的快感？在 AI 时代，你认为“结对编程”是会消亡，还是会变得更加重要？</p>
<p>欢迎在评论区分享你的看法！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p><strong>原「Gopher部落」已重装升级为「Go &amp; AI 精进营」知识星球，快来加入星球，开启你的技术跃迁之旅吧！</strong></p>
<p>我们致力于打造一个高品质的 <strong>Go 语言深度学习</strong> 与 <strong>AI 应用探索</strong> 平台。在这里，你将获得：</p>
<ul>
<li><strong>体系化 Go 核心进阶内容:</strong> 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏，夯实你的 Go 内功。</li>
<li><strong>前沿 Go+AI 实战赋能:</strong> 紧跟时代步伐，学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等，掌握 AI 时代新技能。 </li>
<li><strong>星主 Tony Bai 亲自答疑:</strong> 遇到难题？星主第一时间为你深度解析，扫清学习障碍。</li>
<li><strong>高活跃 Gopher 交流圈:</strong> 与众多优秀 Gopher 分享心得、讨论技术，碰撞思想火花。</li>
<li><strong>独家资源与内容首发:</strong> 技术文章、课程更新、精选资源，第一时间触达。</li>
</ul>
<p>衷心希望「Go &amp; AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚，享受技术精进的快乐！欢迎你的加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-and-ai-tribe-zsxq-small-card.jpg" alt="img{512x368}" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/12/agile-manifesto-dead-in-ai-era-martin-fowler-kent-beck/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Go Command 工作组成立：这几个用了十年的命令可能要被废！</title>
		<link>https://tonybai.com/2026/04/11/go-command-working-group-formed-legacy-commands-deprecated/</link>
		<comments>https://tonybai.com/2026/04/11/go-command-working-group-formed-legacy-commands-deprecated/#comments</comments>
		<pubDate>Sat, 11 Apr 2026 00:14:44 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[BackwardCompatibility]]></category>
		<category><![CDATA[CI/CD流水线]]></category>
		<category><![CDATA[CIDDPipeline]]></category>
		<category><![CDATA[DependencyHell]]></category>
		<category><![CDATA[DependencyManagement]]></category>
		<category><![CDATA[EngineeringPhilosophy]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[go1.26]]></category>
		<category><![CDATA[GO111MODULE]]></category>
		<category><![CDATA[GoCommand]]></category>
		<category><![CDATA[GoCommandWorkingGroup]]></category>
		<category><![CDATA[GoCommand工作组]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[golist]]></category>
		<category><![CDATA[GoModules]]></category>
		<category><![CDATA[GOPATH]]></category>
		<category><![CDATA[go命令]]></category>
		<category><![CDATA[ModuleGraphPruning]]></category>
		<category><![CDATA[ModuleMode]]></category>
		<category><![CDATA[proposal]]></category>
		<category><![CDATA[StaticBuild]]></category>
		<category><![CDATA[toolchain]]></category>
		<category><![CDATA[依赖地狱]]></category>
		<category><![CDATA[依赖管理]]></category>
		<category><![CDATA[向后兼容性]]></category>
		<category><![CDATA[工具链]]></category>
		<category><![CDATA[工程哲学]]></category>
		<category><![CDATA[提案]]></category>
		<category><![CDATA[模块图剪枝]]></category>
		<category><![CDATA[模块模式]]></category>
		<category><![CDATA[静态构建]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6169</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/11/go-command-working-group-formed-legacy-commands-deprecated 大家好，我是Tony Bai。 在这个技术浪潮汹涌的时代，Go 语言以其惊人的稳定性和向后兼容性著称。但稳定，并不代表停滞。 就在最近，Go 核心团队内部悄然发生了一件大事：他们正式成立了一个全新的 “Go Command 工作组（Go Command Working Group）”。 这个工作组汇聚了 Go 工具链领域最核心的大神们（如 Cherry Mui、Matloob、ThePudds 等）。他们的使命非常明确：对 go 命令集中那些最古老、最含糊、最容易引发开发者困惑的“历史遗留问题”，进行一次彻底的“清理门户”。 就在前几天，这个“指挥部”的前两次闭门会议纪要，以及随之而来的两份重磅提案（Issue #78350 和 #78387被公之于众。 当我读完这些提案和讨论后，我意识到，一场关于 Go 语言未来的“静默革命”已经打响。今天，就让我们来拆解这场顶级大佬的闭门会议，看看我们用了十年的几个“祖传命令”，为什么即将面临被废除的命运。 第一刀：砍向 go list &#8230;，这个“万能匹配”为何成了大坑？ 如果你写过稍微复杂一点的 Go 项目，甚至只是写过一些 Makefile，你大概率见过 go list &#8230;。 在早期，go list &#8230;中的这三个点的省略号 &#8230; 意味着“匹配所有（Everything）”。 但在 Go Modules 时代，这条命令成了一个彻头彻尾的“陷阱”。 在最新的 Issue #78387 提案中，工作组负责人 Matloob [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/go-command-working-group-formed-legacy-commands-deprecated-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/11/go-command-working-group-formed-legacy-commands-deprecated">本文永久链接</a> &#8211; https://tonybai.com/2026/04/11/go-command-working-group-formed-legacy-commands-deprecated</p>
<p>大家好，我是Tony Bai。</p>
<p>在这个技术浪潮汹涌的时代，Go 语言以其惊人的稳定性和向后兼容性著称。但稳定，并不代表停滞。</p>
<p>就在最近，Go 核心团队内部悄然发生了一件大事：他们正式成立了一个全新的 “<a href="https://github.com/golang/go/issues/78474">Go Command 工作组（Go Command Working Group）</a>”。</p>
<p>这个工作组汇聚了 Go 工具链领域最核心的大神们（如 Cherry Mui、Matloob、ThePudds 等）。他们的使命非常明确：<strong>对 go 命令集中那些最古老、最含糊、最容易引发开发者困惑的“历史遗留问题”，进行一次彻底的“清理门户”。</strong></p>
<p>就在前几天，这个“指挥部”的前两次闭门会议纪要，以及随之而来的两份重磅提案（<a href="https://go.dev/issue/78350">Issue #78350</a> 和 <a href="https://go.dev/issue/78350">#78387</a>被公之于众。</p>
<p>当我读完这些提案和讨论后，我意识到，一场关于 Go 语言未来的“静默革命”已经打响。今天，就让我们来拆解这场顶级大佬的闭门会议，看看我们用了十年的几个“祖传命令”，为什么即将面临被废除的命运。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/the-ultimate-guide-to-go-module-qr.png" alt="" /></p>
<h2>第一刀：砍向 go list &#8230;，这个“万能匹配”为何成了大坑？</h2>
<p>如果你写过稍微复杂一点的 Go 项目，甚至只是写过一些 Makefile，你大概率见过 go list &#8230;。</p>
<p>在早期，go list &#8230;中的这三个点的省略号 &#8230; 意味着“匹配所有（Everything）”。</p>
<p>但在 Go Modules 时代，这条命令成了一个彻头彻尾的“陷阱”。</p>
<p>在最新的 Issue #78387 提案中，工作组负责人 Matloob 毫不客气地指出：</p>
<blockquote>
<p>“在Go 模块模式下，go list &#8230; 几乎永远做不出用户期望它做的事！”</p>
</blockquote>
<p><strong>大佬辩论现场还原：</strong></p>
<ul>
<li><strong>Matloob（主刀人）</strong>：它试图列出构建列表中所有模块的所有包，这会导致解析一大堆根本不需要的依赖。如果直接在模块下运行，它甚至会因为找不到工作区依赖而直接抛出莫名其妙的错误。</li>
<li><strong>PJ Weinberger</strong>：强烈支持（废弃）！</li>
<li><strong>ThePudds</strong>：<a href="https://tonybai.com/2021/08/19/go-module-changes-in-go-1-17">模块图剪枝（Pruning）在Go 1.17引入后</a>，匹配模式的含义变得非常复杂，连文档都没完全跟上。大家越来越搞不懂 &#8230; 到底代表什么了。</li>
</ul>
<p><strong>为什么必须砍掉它？</strong></p>
<p>在旧的 GOPATH 时代，go list &#8230; 能简单粗暴地列出 $GOPATH/src 下的所有包。但在 Modules 时代，你想要的其实是当前项目的所有包，也就是 go list ./&#8230;（注意前面的 ./）。</p>
<p>直接用 &#8230; 会引发漫长且无意义的全局依赖解析，甚至导致构建失败。</p>
<p>更有意思的是，核心成员 Sean Liao (seankhliao) 用 GitHub 搜索了一下，发现有将近 6700 个 Makefile 或脚本里还写着 &#8230;。但经过抽查发现，这些代码大多是从几年前的旧教程里复制粘贴过来的，实际上在现在的模块模式下，它们本来就已经跑不通了。</p>
<p>经过讨论，工作组达成初步共识：在模块模式下，直接使用 go list &#8230; 将会报错并被禁用。系统会提示你改用 ./&#8230; 或者 work 模式。如果你公司的古老 CI 脚本里还有这个写法，赶紧改！</p>
<h2>第二刀：GO111MODULE=auto 的黄昏，彻底关上 GOPATH 的大门</h2>
<p>GO111MODULE 这个环境变量，是无数 Gopher 从 GOPATH 时代痛苦过渡到 Modules 时代的“阵痛记忆”。</p>
<p>它有三个值：on（强制开启模块）、off（强制关闭）、以及 auto（自动检测）。</p>
<p>在 <strong>Issue #78350</strong> 提案中，工作组决定对 auto 下达最终的“死亡通知书”。</p>
<p><strong>大佬辩论现场还原：</strong></p>
<ul>
<li><strong>Matloob</strong>：我们提议，将 GO111MODULE=auto 的行为直接等同于 on。实际上这就是把它给“移除”了。</li>
<li><strong>Cherry Mui（安全与数据派）</strong>：我们应该现在就开启遥测（Telemetry），看看到底还有多少人在用 auto。我们无法预测什么时候会需要这些数据。</li>
<li><strong>ThePudds（社区观察家）</strong>：确实还有少数人，比如只想在命令行随手编译一个单文件脚本，不想建 go.mod 的人，还在享受 GOPATH 模式。</li>
</ul>
<p><strong>为什么必须砍掉 auto？</strong></p>
<p>auto 的逻辑是：如果当前或上层目录有 go.mod，就用模块模式；否则就回退到 GOPATH 模式。</p>
<p>这种“左右摇摆”的行为在十年前是伟大的过渡方案，但在今天却成了巨大的累赘。</p>
<p>Go 的工具链在启动时，每次都要去猜自己到底在什么模式下运行。如果彻底砍掉 auto（即默认全局 on），编译器可以做大量的架构简化。</p>
<p>更有趣的是，在提案的评论区，有开发者表示他们为了在旧 GOPATH 项目和新 Modules 项目间切换，在全局环境变量里写死了 GO111MODULE=auto。</p>
<p>但 Go 团队的决心是坚定的：到了 2026 年，如果你真的还在维护古老的 GOPATH 项目，你应该显式地在那个目录下设置 GO111MODULE=off。默认情况下，大门已经向 GOPATH 彻底关闭。</p>
<h2>第三刀：终结 go.mod 里的版本号“无意义内卷”</h2>
<p>除了上述两个直接废弃的命令，会议纪要中还透露了一个极具前瞻性、也最能体现 Go 团队“工程哲学”的重磅提议：<strong>关于 go.mod 文件中 Go 版本号的简化。</strong></p>
<p>如果你现在运行 go mod init my-module，生成的 go.mod 文件里会包含一个精确到补丁号（Patch version）的版本，比如 go 1.26.2。</p>
<p>这引发了一个极其无聊，却又在开源界反复上演的“内卷”：</p>
<p>每次 Go 发布一个新的小补丁版本，Github Dependabot 这种自动化机器人就会疯狂地给全世界的开源项目提 PR，要求把 go.mod 里的版本号也跟着升上去。</p>
<p><strong>大佬辩论现场还原：</strong></p>
<ul>
<li><strong>ThePudds</strong>：这种为了升级而升级的行为，带来了巨大的“噪音（Noise）”，却没有相应的收益。我们应该倡导一个最佳实践：<strong>默认情况下，go mod init 应该只生成主次版本号（如 go 1.26），补丁号应该是可选的且不推荐设置！</strong></li>
<li><strong>Cherry Mui（安全视角）</strong>：等一下，这需要跟安全团队确认。如果某个补丁修复了严重的安全漏洞，漏扫工具会不会因为开发者没写补丁号而漏报？</li>
<li><strong>ThePudds</strong>：每个开发者都有自己本地的构建工具链决策权。仅仅因为 Go 出了个补丁，并不意味着世界上每一个开源库都需要立刻被 Dependabot 强行更新一次 go.mod 文件。</li>
</ul>
<p>go.mod 里的 go 指令，核心作用是<strong>“启用语言的语法特性”</strong>。只要你的代码没用新语法，写 1.26 就足够了。至于构建时到底用 1.26.3 还是 1.26.8 的编译器来保证安全，那是执行构建动作的人（或者 CI 系统）该操心的事，而不是由成千上万个基础库的 go.mod 文件来反向绑架。</p>
<p>这项提议一旦落地，将彻底终结无意义的 PR 轰炸，让开源维护者重新获得清净。</p>
<h2>小结：一场“静默的革命”</h2>
<p>Go Command 工作组的这两次会议，没有像泛型那样引入任何惊天动地的新语法。</p>
<p>但它对 Go 语言生态的影响，可能比任何一个新特性都要深远。</p>
<p>它像一个经验丰富的老园丁，正在小心翼翼但又果断地修剪 Go 这棵大树上那些已经枯萎、或者长歪了的枝桠。</p>
<ul>
<li>砍掉 go list &#8230;，是为了让模块查询的逻辑更清晰。</li>
<li>砍掉 GO111MODULE=auto，是为了让构建环境更具确定性。</li>
<li>简化 go.mod 的补丁号，是为了让整个生态的协作更高效。</li>
</ul>
<p>在这场“静默的革命”背后，我们看到的，是 Go 团队对<strong>“简单性、确定性、工程效率”</strong>这三大工程哲学一以贯之的坚守。</p>
<p>Go 语言的伟大，不在于它有多么强大的功能，而在于它在过去十几年里，<strong>拒绝了多少看似“合理”的坏品味</strong>。而这场“清理门户”，才刚刚开始。</p>
<p>资料链接：https://github.com/golang/go/issues/78474</p>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>在日常开发中，你被 Go 命令行的哪些“反直觉”行为坑过？对于废弃 go list &#8230; 和 GO111MODULE=auto，你是拍手叫好还是觉得会影响你的老项目？</p>
<p>欢迎在评论区分享你的看法！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/11/go-command-working-group-formed-legacy-commands-deprecated/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Ruby on Rails 之父最新访谈：AI 正在推高顶尖程序员的身价</title>
		<link>https://tonybai.com/2026/04/10/rails-father-dhh-on-ai-and-programmer-value/</link>
		<comments>https://tonybai.com/2026/04/10/rails-father-dhh-on-ai-and-programmer-value/#comments</comments>
		<pubDate>Thu, 09 Apr 2026 23:36:06 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Agent]]></category>
		<category><![CDATA[AgentFirst]]></category>
		<category><![CDATA[AICollaboration]]></category>
		<category><![CDATA[AIProgramming]]></category>
		<category><![CDATA[AI协作]]></category>
		<category><![CDATA[AI编程]]></category>
		<category><![CDATA[Architect]]></category>
		<category><![CDATA[Automation]]></category>
		<category><![CDATA[CodeAutocomplete]]></category>
		<category><![CDATA[CodingAgent]]></category>
		<category><![CDATA[Collaboration]]></category>
		<category><![CDATA[DavidHeinemeierHansson]]></category>
		<category><![CDATA[DeliverySpeed]]></category>
		<category><![CDATA[DHH]]></category>
		<category><![CDATA[DimensionalityReduction]]></category>
		<category><![CDATA[Industrialization]]></category>
		<category><![CDATA[Productivity]]></category>
		<category><![CDATA[RoleEvolution]]></category>
		<category><![CDATA[RubyOnRails]]></category>
		<category><![CDATA[SoftwareCraftsmanship]]></category>
		<category><![CDATA[SoftwareEngineering]]></category>
		<category><![CDATA[TechnicalValue]]></category>
		<category><![CDATA[交付速度]]></category>
		<category><![CDATA[代码补全]]></category>
		<category><![CDATA[协作]]></category>
		<category><![CDATA[工业化]]></category>
		<category><![CDATA[技术价值]]></category>
		<category><![CDATA[智能体]]></category>
		<category><![CDATA[架构师]]></category>
		<category><![CDATA[生产力]]></category>
		<category><![CDATA[自动化]]></category>
		<category><![CDATA[角色演进]]></category>
		<category><![CDATA[软件匠艺]]></category>
		<category><![CDATA[软件工程]]></category>
		<category><![CDATA[降维打击]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6164</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/10/rails-father-dhh-on-ai-and-programmer-value 大家好，我是Tony Bai。 在这个由 AI 主导的、充满不确定性的 2026 年，整个软件行业似乎都被一种集体性的焦虑所笼罩。我们每天都在讨论：当 AI 能在一分钟内写完我们一周的代码时，我们这些“人类程序员”的价值还剩下多少？ 就在所有人都在悲观地预测“程序员即将贬值”时，一位以“毒舌”和“极简主义”著称的硅谷大神，却逆着人潮，抛出了一个极其震撼的“反共识”暴论： “我们可能已经见证了‘普通程序员’薪资的顶峰。但对于那些顶尖的、真正懂行的开发者来说，AI 正在让他们变得比以往任何时候都更值钱、更有价值。” 说出这句话的，正是 David Heinemeier Hansson (DHH)——Ruby on Rails 框架之父、37signals (Basecamp &#38; HEY) 的联合创始人兼 CTO。 就在几个月前，DHH 还是 AI 编程最坚定的“喷子”之一。他曾公开嘲讽 Copilot 像个烦人的实习生，打断他的思路，生成的代码全是垃圾。 但在一场最新的深度访谈中，他却上演了一场惊天动地的“自我推翻”。他不仅承认自己已经“彻底投降”，更是将他现在的工作流形容为 “Agent First on Everything”（万物皆以智能体为先）。 这场 180 度的惊天逆转背后，到底发生了什么？在这场信息量爆炸的对话中，DHH 不仅详细复盘了让他“觉醒”的那个“aha moment”，更对 AI 时代的程序员价值、团队协作、以及“软件匠艺”的未来，给出了极其深刻、甚至有些残酷的终极洞见。 从“令人作呕”到“欲罢不能”：DHH 的“觉醒”之路 DHH 坦言，在 Copilot 和早期 Cursor 的“代码补全（Autocomplete）”时代，他对此类工具的厌恶达到了顶峰。 “我感到无比愤怒。它总是在我还没想清楚的时候就试图猜我想写什么。‘你是想写这个吗？’‘你是想写那个吗？’ [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/rails-father-dhh-on-ai-and-programmer-value-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/10/rails-father-dhh-on-ai-and-programmer-value">本文永久链接</a> &#8211; https://tonybai.com/2026/04/10/rails-father-dhh-on-ai-and-programmer-value</p>
<p>大家好，我是Tony Bai。</p>
<p>在这个由 AI 主导的、充满不确定性的 2026 年，整个软件行业似乎都被一种集体性的焦虑所笼罩。我们每天都在讨论：当 AI 能在一分钟内写完我们一周的代码时，我们这些“人类程序员”的价值还剩下多少？</p>
<p>就在所有人都在悲观地预测“程序员即将贬值”时，一位以“毒舌”和“极简主义”著称的硅谷大神，却逆着人潮，抛出了一个极其震撼的“反共识”暴论：</p>
<p><strong>“我们可能已经见证了‘普通程序员’薪资的顶峰。但对于那些顶尖的、真正懂行的开发者来说，AI 正在让他们变得比以往任何时候都更值钱、更有价值。”</strong></p>
<p>说出这句话的，正是 <strong>David Heinemeier Hansson (DHH)</strong>——Ruby on Rails 框架之父、37signals (Basecamp &amp; HEY) 的联合创始人兼 CTO。</p>
<p>就在几个月前，DHH 还是 AI 编程最坚定的“喷子”之一。他曾公开嘲讽 Copilot 像个烦人的实习生，打断他的思路，生成的代码全是垃圾。</p>
<p>但在<a href="https://www.youtube.com/watch?v=JiWgKRgdgpI">一场最新的深度访谈</a>中，他却上演了一场惊天动地的“自我推翻”。他不仅承认自己已经“彻底投降”，更是将他现在的工作流形容为 <strong>“Agent First on Everything”（万物皆以智能体为先）</strong>。</p>
<p>这场 180 度的惊天逆转背后，到底发生了什么？在这场信息量爆炸的对话中，DHH 不仅详细复盘了让他“觉醒”的那个“aha moment”，更对 AI 时代的程序员价值、团队协作、以及“软件匠艺”的未来，给出了极其深刻、甚至有些残酷的终极洞见。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-software-engineering-qr.png" alt="" /></p>
<h2>从“令人作呕”到“欲罢不能”：DHH 的“觉醒”之路</h2>
<p>DHH 坦言，在 Copilot 和早期 Cursor 的“代码补全（Autocomplete）”时代，他对此类工具的厌恶达到了顶峰。</p>
<blockquote>
<p>“我感到无比愤怒。它总是在我还没想清楚的时候就试图猜我想写什么。‘你是想写这个吗？’‘你是想写那个吗？’ <strong>闭嘴！让我自己把话说完！</strong>”</p>
</blockquote>
<p>他甚至一度悲观地认为，整个行业将走向一个由“Tab 键”驱动的、毫无思想的愚蠢未来，并开玩笑说自己可能要去丹麦种土豆了。</p>
<p>转折点发生在 2025 年的冬天。两个关键变量，彻底改变了游戏规则：</p>
<ol>
<li><strong>模型的质变</strong>：Anthropic 的 Claude Opus 4.5 模型发布。DHH 发现，这个模型生成的代码质量，第一次持续地、稳定地震惊到了他。它产出的代码，在很多时候，是他自己也愿意合并的。</li>
<li><strong>交互范式的革命</strong>：以 Open Code 和 <a href="http://gk.link/a/12EPd">Claude Code</a> 为代表的 Agent Harnesses出现。AI 不再是那个烦人的“代码补全机”，而是变成了一个可以独立使用工具（Bash、网络）、拥有自己终端的“数字同事”。</li>
</ol>
<p>DHH 形容，当这两个变量结合在一起时，他迎来了职业生涯的“第二次启蒙”——上一次，是 2000 年初他第一次发现 Ruby 语言的优雅。</p>
<blockquote>
<p>“我不再是那个在键盘上打字的人，我感觉自己像是穿上了一套超级机甲。我突然长出了 12 只手，可以同时操作 7 个屏幕。<strong>我作为程序员的能力，被极度放大了。</strong>”</p>
</blockquote>
<h2>我们可能已经度过了“程序员薪资的顶峰”</h2>
<p>当被问及 AI 是否会取代程序员时，DHH 毫不避讳地抛出了一个极其冷酷的观点：</p>
<p><strong>我们很可能已经见证了“程序员（作为一种普通职业）”的黄金时代顶峰。</strong></p>
<p>他认为，在过去，程序员之所以能获得极高的薪资，是因为他们是生产软件的“瓶颈资源”。产品经理想出一个绝妙的点子，必须排队等待昂贵的程序员花几周时间才能实现。</p>
<p>但现在，瓶颈正在快速转移。</p>
<blockquote>
<p>“当产品经理自己就能用 AI 生成可用的代码时，事情就要变天了。在任何一个软件开发被视为‘成本中心’（而这恰恰是世界上绝大多数的软件开发场景）的公司，降薪和裁员的压力将是不可避免的。”</p>
</blockquote>
<p>但这是否意味着所有程序员都会被淘汰？</p>
<p>恰恰相反。DHH 认为，AI 正在引发一场剧烈的<strong>“价值两极分化”</strong>。</p>
<ul>
<li><strong>中间层的崩溃</strong>：那些只会“把需求翻译成代码”的普通程序员，其价值正在被无限稀释。因为 AI 做这件事更快、更便宜。</li>
<li><strong>顶尖人才的价值飙升</strong>：那些具备极高<strong>“品味（Taste）”、“审美（Aesthetics）”和“架构判断力”</strong>的资深工程师，他们的价值正在被 AI 放大 10 倍甚至 100 倍。</li>
</ul>
<p>因为他们是那个能够判断“AI 生成的东西是对是错、是美是丑”的最终把关人。他们从“体力劳动者”，进化为了“艺术总监”。</p>
<h2>当 AI 能写所有代码，我们还剩下什么？</h2>
<p>在这场对话中，DHH 反复强调一个词：<strong>Aesthetics is truth（美学就是真理）。</strong></p>
<p>他认为，无论是在数学、物理学还是软件工程中，一个优美的解决方案，往往也正是那个正确的方案。</p>
<blockquote>
<p>“乔布斯之所以关心 Mac 电脑机箱内部的走线，是因为他凭直觉知道，<strong>只有那些在乎印刷电路板布局的人，才会去死磕用户界面的每一个像素。</strong>”</p>
</blockquote>
<p>在 AI 时代，这种对“美”的追求，不仅没有过时，反而变得空前重要。</p>
<p>因为当你拥有了无限的“算力（AI）”时，唯一稀缺的，就是<strong>“品味（Taste）”</strong>。</p>
<p>DHH 认为，未来顶尖的软件工程师，其核心竞争力将不再是“知道多少种排序算法”，而是：</p>
<ol>
<li><strong>产品感</strong>：深刻理解“我们应该做什么，不应该做什么”。</li>
<li><strong>系统设计能力</strong>：将模糊的业务需求，抽象为清晰、优美的架构。</li>
<li><strong>极高的审美标准</strong>：能够引导 AI 生成不仅能工作、而且看起来赏心悦目、易于维护的代码。</li>
</ol>
<p>代码的实现，正在变得廉价；而代码的“品味”，正在变得无价。</p>
<h2>大神的日常：我是如何指挥 AI “军团”的？</h2>
<p>DHH 详细分享了他现在的“Agent-First”工作流，堪称教科书级：</p>
<p>他使用 tmux 在终端里创建了一个三分屏布局：</p>
<ul>
<li>左侧是 Neovim 编辑器。</li>
<li>右上是跑着 Google Gemini 的 Open Code。</li>
<li>右下是跑着 Claude Opus 的 Claude Code。</li>
</ul>
<blockquote>
<p>“我几乎所有的工作都从其中一个 Agent 开始。我给它一个模糊的指令，然后看着它生成初稿。然后我把初稿扔给另一个 Agent，让它去批判和重构。我让它们俩来回‘吵架’。最后，我再跳到 Neovim 里，做那个最终的‘裁判’。”</p>
</blockquote>
<p>他分享了一个让他自己都感到震惊的案例：</p>
<p>37signals 的 Linux 发行版 Omarchy 积压了 250 个无人处理的 PR。他花了 <strong>90 分钟</strong>，让 Claude 帮他审完了其中 100 个。</p>
<ul>
<li>10% 直接合并。</li>
<li>20% Claude 觉得思路对，但实现太烂，直接帮他重写了一版。</li>
<li>剩下的大部分，要么被他判定为“不需要”，要么被 Claude 识别为“实现太差且没有好思路”，直接关闭。</li>
</ul>
<blockquote>
<p>“这在以前至少是一周的工作量。更重要的是，其中一半的 PR 涉及我不懂的领域，Claude 在那些领域，是比我更聪明、更优秀的审查者。”</p>
</blockquote>
<h2>野心的爆炸：探索一个直觉的成本，已被降低一千倍</h2>
<p>DHH 在访谈中提到了一个极具启发性的概念：<strong>AI 正在让“雄心（Ambition）”变得廉价。</strong></p>
<p>他举例，他让 Agent 在几天内，为一个搁置已久的需求（为 Omarchy 实现 Windows 双系统启动）制定了一套完整的、可执行的方案。而在过去，他连花 4 个小时去调研的意愿都没有。因为这件事“重要但不紧急”，而且“非常麻烦”。</p>
<blockquote>
<p>“探索一个直觉的成本，已经被降低了一千倍。我们现在可以去挑战那些过去连想都不敢想的项目。”</p>
</blockquote>
<p>他分享了 37signals 内部的一个真实案例：一位名叫 Jeremy 的工程师，利用 AI 发起了一个名为“P1 优化”的疯狂项目。他要去优化系统中那最快的 1% 的请求，让它们变得更快。</p>
<p>这在传统性能优化的世界里，简直是“吃饱了撑的”。</p>
<p>但 Jeremy 仅用了几天时间，通过让 Agent 疯狂分析和重构，提交了 12 个 PR，硬生生把这 1% 请求的延迟从 4ms 压缩到了 0.5ms 以下，实现了 10 倍的性能提升。</p>
<p>当探索的成本趋近于零时，过去那些被视为“无用功”的边缘优化，将共同汇聚成压倒性的产品优势。</p>
<h2>小结：这是一场关于“手艺”的文艺复兴</h2>
<p>在访谈的结尾，DHH 表达了他对未来的极度乐观。</p>
<p>他认为，AI 并没有让编程变得无趣，反而让他找回了自 2000 年初发现 Ruby 以来最大的快感。</p>
<p>DHH 的这场“觉醒”，不仅仅是一个技术大佬对新工具的拥抱。它更像一个宣言：</p>
<p><strong>在 AI 时代，软件工程的“手艺（Craft）”并没有消亡，它只是从“雕琢代码”的微观层面，升维到了“塑造品味”与“驾驭系统”的宏观层面。</strong></p>
<p>AI 正在无情地淘汰那些只会“拧螺丝”的码农，但同时，它也为那些真正热爱创造、拥有极高审美和品味的“工匠”，递上了一把前所未有的神兵利器。</p>
<p>你，准备好拿起它了吗？</p>
<p>资料链接：https://www.youtube.com/watch?v=JiWgKRgdgpI</p>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>在使用 AI 编程后，你是否也像 DHH 一样，感觉自己的“野心”被放大了，敢于去挑战更复杂的项目？在你的工作中，AI 是更多地扮演“体力外包”，还是“创意伙伴”的角色？</p>
<p>欢迎在评论区分享你的真实感受！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p><strong>原「Gopher部落」已重装升级为「Go &amp; AI 精进营」知识星球，快来加入星球，开启你的技术跃迁之旅吧！</strong></p>
<p>我们致力于打造一个高品质的 <strong>Go 语言深度学习</strong> 与 <strong>AI 应用探索</strong> 平台。在这里，你将获得：</p>
<ul>
<li><strong>体系化 Go 核心进阶内容:</strong> 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏，夯实你的 Go 内功。</li>
<li><strong>前沿 Go+AI 实战赋能:</strong> 紧跟时代步伐，学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等，掌握 AI 时代新技能。 </li>
<li><strong>星主 Tony Bai 亲自答疑:</strong> 遇到难题？星主第一时间为你深度解析，扫清学习障碍。</li>
<li><strong>高活跃 Gopher 交流圈:</strong> 与众多优秀 Gopher 分享心得、讨论技术，碰撞思想火花。</li>
<li><strong>独家资源与内容首发:</strong> 技术文章、课程更新、精选资源，第一时间触达。</li>
</ul>
<p>衷心希望「Go &amp; AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚，享受技术精进的快乐！欢迎你的加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-and-ai-tribe-zsxq-small-card.jpg" alt="img{512x368}" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/10/rails-father-dhh-on-ai-and-programmer-value/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>别搞“小而美”了！Rust 开发者请愿：求求标准库学学 Go 吧</title>
		<link>https://tonybai.com/2026/04/09/stop-being-small-and-beautiful-rust-petition-to-learn-from-go/</link>
		<comments>https://tonybai.com/2026/04/09/stop-being-small-and-beautiful-rust-petition-to-learn-from-go/#comments</comments>
		<pubDate>Thu, 09 Apr 2026 00:20:15 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[BackwardCompatibility]]></category>
		<category><![CDATA[BatteriesIncluded]]></category>
		<category><![CDATA[cargo]]></category>
		<category><![CDATA[CodeAudit]]></category>
		<category><![CDATA[crates]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GoLanguage]]></category>
		<category><![CDATA[Go语言]]></category>
		<category><![CDATA[Maintainability]]></category>
		<category><![CDATA[Modularity]]></category>
		<category><![CDATA[npm]]></category>
		<category><![CDATA[OpenSourceCommunity]]></category>
		<category><![CDATA[PackageManager]]></category>
		<category><![CDATA[Rust]]></category>
		<category><![CDATA[safety]]></category>
		<category><![CDATA[standardlibrary]]></category>
		<category><![CDATA[SupplyChainSecurity]]></category>
		<category><![CDATA[SwordOfDamocles]]></category>
		<category><![CDATA[ThirdPartyDependencies]]></category>
		<category><![CDATA[ZerocostAbstraction]]></category>
		<category><![CDATA[代码审计]]></category>
		<category><![CDATA[供应链安全]]></category>
		<category><![CDATA[可维护性]]></category>
		<category><![CDATA[向后兼容性]]></category>
		<category><![CDATA[安全性]]></category>
		<category><![CDATA[开源社区]]></category>
		<category><![CDATA[标准库]]></category>
		<category><![CDATA[模块化]]></category>
		<category><![CDATA[电池内置]]></category>
		<category><![CDATA[第三方依赖]]></category>
		<category><![CDATA[软件包管理]]></category>
		<category><![CDATA[达摩克利斯之剑]]></category>
		<category><![CDATA[零成本抽象]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6161</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/09/stop-being-small-and-beautiful-rust-petition-to-learn-from-go 大家好，我是Tony Bai。 如果你之前经常听 Go 社区最火的播客 GoTime(很遗憾，该播客2024年末因平台原因停播了)，你一定会熟悉每期节目最后的那个经典环节——“Unpopular Opinion”（非主流观点）。在这个环节，嘉宾们会分享一些看似离经叛道、却往往一针见血的“暴论”。 但就在前几天，这个流行于 Go 社区的“梗”，却被隔壁的 Rust 社区“偷”了过去，并掀起了一场史诗级的“路线之争”。 一位 Rust 开发者，在 r/rust 论坛上发了一篇帖子，标题就叫：《Unpopular opinion: Rust should have a larger standard library》（非主流观点：Rust 应该有一个更大的标准库）。 他在这篇帖子中发出了灵魂拷问： “我不想为了写一个程序，被迫去拉几百个我根本没时间、也没人去审计的第三方依赖包。看看隔壁的 Go 是怎么做标准库的，你几乎可以不依赖任何三方包就构建出复杂的系统！” 这篇帖子瞬间引爆了 Rust 社区。短短一天，帖子收获了近 700 的高赞和近 300 条激烈辩论。 这看起来像是一场简单的“库多库少”之争，但本质上，它背后是 Rust 这门以“零成本抽象、极致安全”著称的语言，在面对日益猖獗的供应链安全威胁和 Go 语言“开箱即用”的降维打击时，所爆发的一场深刻的身份危机与哲学反思。 “小而美”的代价：悬在每个 Rust 项目头顶的达摩克利斯之剑 长期以来，Rust 社区一直为自己“小核心、强生态”的模式感到自豪。Rust 的标准库（std）极其精简，只提供最基础、最核心的功能。任何稍微高级一点的需求，比如随机数生成、异步运行时、序列化，官方都鼓励你去 crates.io 上找社区“钦定”的“明星库”（Blessed Crates）。 这套模式在早期极大地促进了生态的繁荣。但随着 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/stop-being-small-and-beautiful-rust-petition-to-learn-from-go-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/09/stop-being-small-and-beautiful-rust-petition-to-learn-from-go">本文永久链接</a> &#8211; https://tonybai.com/2026/04/09/stop-being-small-and-beautiful-rust-petition-to-learn-from-go</p>
<p>大家好，我是Tony Bai。</p>
<p>如果你之前经常听 Go 社区最火的播客 <a href="https://changelog.com/gotime/">GoTime</a>(很遗憾，该播客2024年末因平台原因停播了)，你一定会熟悉每期节目最后的那个经典环节——“Unpopular Opinion”（非主流观点）。在这个环节，嘉宾们会分享一些看似离经叛道、却往往一针见血的“暴论”。</p>
<p>但就在前几天，这个流行于 Go 社区的“梗”，却被隔壁的 Rust 社区“偷”了过去，并掀起了一场史诗级的“路线之争”。</p>
<p>一位 Rust 开发者，在 r/rust 论坛上发了一篇帖子，标题就叫：《<a href="https://www.reddit.com/r/rust/comments/1seu7p2/unpopular_opinion_rust_should_have_a_larger/">Unpopular opinion: Rust should have a larger standard library</a>》（非主流观点：Rust 应该有一个更大的标准库）。</p>
<p>他在这篇帖子中发出了灵魂拷问：</p>
<blockquote>
<p>“我不想为了写一个程序，被迫去拉几百个我根本没时间、也没人去审计的第三方依赖包。看看隔壁的 Go 是怎么做标准库的，你几乎可以不依赖任何三方包就构建出复杂的系统！”</p>
</blockquote>
<p>这篇帖子瞬间引爆了 Rust 社区。短短一天，帖子收获了近 700 的高赞和近 300 条激烈辩论。</p>
<p>这看起来像是一场简单的“库多库少”之争，但本质上，它背后是 Rust 这门以“零成本抽象、极致安全”著称的语言，在面对日益猖獗的<a href="https://tonybai.com/2026/02/25/govulncheck-high-signal-to-noise-ratio-security-workflow">供应链安全威胁</a>和 Go 语言“开箱即用”的降维打击时，所爆发的一场深刻的身份危机与哲学反思。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-qr.png" alt="" /></p>
<h2>“小而美”的代价：悬在每个 Rust 项目头顶的达摩克利斯之剑</h2>
<p>长期以来，Rust 社区一直为自己“小核心、强生态”的模式感到自豪。Rust 的标准库（std）极其精简，只提供最基础、最核心的功能。任何稍微高级一点的需求，比如随机数生成、异步运行时、序列化，官方都鼓励你去 crates.io 上找社区“钦定”的“明星库”（Blessed Crates）。</p>
<p>这套模式在早期极大地促进了生态的繁荣。但随着 npm left-pad 事件和各种开源投毒攻击的阴影笼罩全球，这套模式的代价也变得越来越难以承受。</p>
<p>原帖作者一针见血地指出了所有人的噩梦：</p>
<blockquote>
<p>“是的，你可以采取各种缓解措施。但等你发现某个藏在你依赖树第三层的、不起眼的包被植入了恶意软件时，你的服务器密钥可能早就被偷光了！”</p>
</blockquote>
<p>评论区里的一位开发者用一句话概括了所有人的痛点：</p>
<blockquote>
<p>“我完全同意。有时候 std 里就是缺了那么一点至关重要的东西。我能理解这背后的原因，但为了生成一个随机数就要去装一个第三方包，这实在有点小题大做了。”</p>
</blockquote>
<p>这正是 Rust 开发者面临的尴尬：当你只是想生成一个 UUID，或者发起一个 HTTP 请求时，你被迫要对 rand、reqwest、tokio 这些由社区个人或小团体维护的库，付出与 Rust 官方核心团队同等级别的“信任”。</p>
<p>而这种信任，正在变得越来越昂贵和危险。</p>
<h2>隔壁的诱惑：Go 语言的“大一统”模式</h2>
<p>在这场大讨论中，一个名字被反复提及，它就是 Go 语言。</p>
<p>Go 从诞生之初，就选择了与 Rust 截然相反的“自带电池（Batteries Included）”哲学。</p>
<ul>
<li>你想做 Web 开发？net/http 原生支持，性能强大到可以直接裸奔在生产环境。</li>
<li>你想做 JSON/XML 解析？encoding/json(以及实验性的encoding/json/v2)、encoding/xml 是标配。</li>
<li>你想做并发？goroutine 和 channel 是语言级原生特性。</li>
<li>你想生成随机数？math/rand、crypto/rand 随便用。</li>
</ul>
<p>评论区里，一位 Rust 开发者的对比极其扎心：</p>
<blockquote>
<p>“把恶意代码偷偷塞进一个（流行的）Crate 的第四层依赖里，比把它塞进 Rust 的 std 里要容易得多。”</p>
</blockquote>
<p>Go 语言通过一个庞大、稳定、由官方核心团队直接维护的标准库，为开发者提供了一道坚固的“安全护城河”。你可以在不引入任何一个第三方依赖的情况下，构建出一个功能极其完备、性能强大的高并发网络服务。</p>
<p>这种“开箱即用”的安全感和便利性，对于那些深受<a href="https://tonybai.com/2025/05/21/go-crypto-audit">供应链安全审计</a>折磨的企业开发者来说，是致命的诱惑。</p>
<h2>社区的挣扎：当“保守”成为“瓶颈”</h2>
<p>面对社区的“呐喊”，Rust 核心团队的成员和社区大佬们也纷纷下场，给出了极其理性和深刻的解释。他们的回复，揭示了 Rust 在标准库扩张上，面临的“三重枷锁”。</p>
<p><strong>枷锁一：向后兼容性的“诅咒”</strong></p>
<p>一位核心成员引用了 Python 社区的一句名言：</p>
<blockquote>
<p><strong>“标准库，是模块最终的坟场（The standard library is where modules go to die）。”</strong></p>
</blockquote>
<p>一旦一个 API 进入了 std，它就必须背上永不破坏向后兼容的沉重承诺。哪怕 10 年后发现这个设计有缺陷，也只能眼睁睁地看着它腐烂，或者推出一个 urllib2、urllib3 这样极其丑陋的补丁。</p>
<p>Rust 团队宁愿让这些库在社区里自由进化、大浪淘沙，等到它们的设计真正成熟、稳定到可以“永恒”时，再考虑纳入 std。比如 once_cell 和最新的 rand（目前在 nightly 版本中）。</p>
<p><strong>枷锁二：无休止的“维护地狱”</strong></p>
<p>另外一名核心成员指出，将一个库纳入 std，意味着它的维护成本将全部转移到人数本就捉襟见肘的官方维护者身上。而在社区，每个 Crate 都有自己专门的维护者。这是两种完全不同的成本模型。</p>
<p><strong>枷锁三：设计的“过早僵化”</strong></p>
<p>最典型的例子就是异步。原帖作者提议：“Rust 能不能偷一下 Zig 的 IO 思想，这样我们就不需要在 Tokio 和 non-Tokio 生态之间分裂了？”</p>
<p>一位社区大佬立刻反驳：Zig 没有 Rust 的 Send/Sync 标记，两者的异步模型有本质区别。Rust 的异步生态之所以看起来“分裂”，恰恰是语言给了开发者在不同场景下做最优选择的自由。如果过早地在 std 里统一一个官方运行时，反而会扼杀创新。</p>
<h2>破局之路：从“大一统”到“邦联制”</h2>
<p>在这场激烈的辩论中，一些极具建设性的“折中方案”也开始浮现。这或许预示着 Rust 未来的演进方向。</p>
<p><strong>方案一：官方背书的“准标准库（Semi-official）”</strong></p>
<p>一位开发者提出，Rust 项目组可以借鉴 C++ Boost 库的模式，官方接管 serde、rand、tokio 这些“钦定”的明星库，将它们纳入一个统一的 extd (extended) 命名空间下。</p>
<pre><code class="rust">use extd::regex::Regex;
use extd::rand;
</code></pre>
<p>这并不会增加 std 的体积，但给了这些库一个“官方认证”的金字招牌，极大地解决了开发者的信任和审计问题。</p>
<p><strong>方案二：引入“孵化期（Incubation Phase）”</strong></p>
<p>一位开发者建议，应该有一个更明确的孵化流程，让那些有潜力进入 std 的库，先在一个类似 Go golang.org/x 的“实验场”里进行检验，而不是直接从某个个人开发者仓库里一步登天。</p>
<p><strong>方案三：强化 Cargo 的安全审计能力</strong></p>
<p>一些核心成员则认为，问题的根源不在于 std 的大小，而在于 crates.io 的分发机制不够安全。与其“因噎废食”地把所有东西都塞进 std，不如去建立更强大的包安全审计机制，比如：</p>
<ul>
<li><strong>发布隔离期</strong>：新发布的包必须经过 72 小时自动化扫描才能被下载。</li>
<li><strong>签名与信任链</strong>：通过 cargo 增强包签名和审计者签名，让企业可以选择只使用“可信审计者”批准的依赖列表。</li>
</ul>
<h2>小结：一场关于“灵魂”的拷问</h2>
<p>这场由“非主流观点(Unpopular Opinion)”引发的大讨论，表面上是在争论标准库的大小，但其核心，却是一场关于 Rust 与 Go 两种截然不同建国哲学的灵魂拷问。</p>
<ul>
<li><strong>Go 语言</strong>，像一个大一统的、中央集权的帝国。它为你提供了从道路、货币到度量衡的一切基础设施。你享受着极高的安全感和便利性，代价是必须忍受它某些时候的“独裁”与“不灵活”。</li>
<li><strong>Rust 语言</strong>，则更像一个松散的、充满活力的城邦联盟。官方只提供最基础的法律和军队，剩下的一切都交给各个城邦（Crates）自由发展。你拥有无与伦比的自由和选择权，代价是你必须自己承担选择的风险，并时刻提防“外敌入侵”（供应链攻击）。</li>
</ul>
<p>这两种哲学没有绝对的优劣，只有不同场景下的取舍。</p>
<p>但 Rust 社区的这场“请愿”，无疑为我们所有技术人敲响了警钟：<strong>在软件供应链日益脆弱的今天，一个强大、可靠、由顶级专家背书的“官方基础设施”，其价值正在被无限放大。</strong></p>
<p>或许，Rust 的未来，真的需要在“自由”与“安全”之间，找到一个新的平衡点。而隔壁 Go 的作业，他们可能真的需要抄一抄了。</p>
<p>资料链接：https://www.reddit.com/r/rust/comments/1seu7p2/unpopular_opinion_rust_should_have_a_larger/</p>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>在你的日常开发中，你是更喜欢 Go 这种“自带电池”的大标准库模式，还是 Rust 这种“小核心+强生态”的自由模式？你是否也曾因为“拉了一堆三方库”而感到安全焦虑？</p>
<p>欢迎在评论区分享你的看法!</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/09/stop-being-small-and-beautiful-rust-petition-to-learn-from-go/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>倒计时 33 个月？Go 前安全负责人：量子计算机将“摧毁”互联网</title>
		<link>https://tonybai.com/2026/04/08/perspective-on-quantum-computing-timeline/</link>
		<comments>https://tonybai.com/2026/04/08/perspective-on-quantum-computing-timeline/#comments</comments>
		<pubDate>Wed, 08 Apr 2026 00:15:14 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[DigitalSignatures]]></category>
		<category><![CDATA[ECC]]></category>
		<category><![CDATA[FilippoValsorda]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GoLanguage]]></category>
		<category><![CDATA[Go语言]]></category>
		<category><![CDATA[infrastructure]]></category>
		<category><![CDATA[KeyExchange]]></category>
		<category><![CDATA[kyber]]></category>
		<category><![CDATA[MLDSA]]></category>
		<category><![CDATA[mlkem]]></category>
		<category><![CDATA[PostQuantumCryptography]]></category>
		<category><![CDATA[PQC]]></category>
		<category><![CDATA[QDay]]></category>
		<category><![CDATA[QuantumComputing]]></category>
		<category><![CDATA[RSA]]></category>
		<category><![CDATA[SecurityAudit]]></category>
		<category><![CDATA[ShorAlgorithm]]></category>
		<category><![CDATA[Shor算法]]></category>
		<category><![CDATA[SNDL]]></category>
		<category><![CDATA[StoreNowDecryptLater]]></category>
		<category><![CDATA[TLS]]></category>
		<category><![CDATA[x509]]></category>
		<category><![CDATA[先收集后破解]]></category>
		<category><![CDATA[基础设施]]></category>
		<category><![CDATA[安全审计]]></category>
		<category><![CDATA[密钥交换]]></category>
		<category><![CDATA[抗量子密码学]]></category>
		<category><![CDATA[数字签名]]></category>
		<category><![CDATA[椭圆曲线]]></category>
		<category><![CDATA[量子末日]]></category>
		<category><![CDATA[量子计算机]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6158</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/08/perspective-on-quantum-computing-timelines 大家好，我是Tony Bai。 过去三十年，我们一直活在一个笑话里：“能够破解 RSA 加密的量子计算机，永远在十年之后。” 作为一名软件工程师，我曾和你们中的大多数人一样，对所谓的“量子末日（Q-Day）”嗤之以鼻。我们觉得，在有生之年，我们赖以生存的 RSA、ECC 加密体系坚不可摧，那一天遥遥无期。 但就在昨天（2026年4月6日），这个笑话，似乎被终结了。 Go 语言前核心团队安全负责人、Go密码学专家之一 Filippo Valsorda，在他的个人博客上发表了一篇极其沉重的文章。 在文章的开头，他用一种近乎“忏悔”的口吻写道： “在推出抗量子密码学的紧迫性上，我的立场与几个月前相比，已经发生了改变。……是时候公开表明并解释我改变想法的原因了。” 在这篇长文中，Filippo 引用了 Google 和 Oratomic 上周刚刚发表的两篇重磅论文，以及多位顶级物理学家的最新警告，最终得出了一个令整个软件工程界脊背发凉的结论：我们不再拥有十年。具备破解当前所有主流加密算法能力的量子计算机（CRQC），其到来的时间线，已经被一些顶级专家压缩到了一个令人绝望的数字——2029 年。 是的，只剩下 33 个月。 今天，就让我们跟随这位曾经的“官方守护者”的视角，看看这场已经兵临城下的“加密世界末日”，到底是怎么回事，以及我们作为普通的后端开发者，该如何在这场史无前例的迁移中生存下来。 冰山撞来：Google 的论文与被撕碎的幻想 Filippo 指出，在过去的一周里，两篇论文彻底改变了游戏规则。 第一篇，来自 Google。 这篇论文极大地修正了破解 256 位椭圆曲线（如我们每天都在用的 HTTPS 证书、比特币签名 secp256k1）所需的逻辑量子比特（qubits）和门电路数量。结论是：在超导量子比特这种高速架构上，攻击可以在几分钟内完成。 第二篇，来自 Oratomic。 这篇论文更加激进。它指出，如果拥有非局部连接能力（如中性原子方案），破解 256 位椭圆曲线甚至只需要 10,000 个物理量子比特。这种攻击虽然更慢，但哪怕一个月只能破解一个密钥，也足以引发灾难。 那张出现在论文第二页、堪称“末日倒计时”的图表，清晰地展示了攻破 RSA-2048 和 ECC-256 所需的物理量子比特数，正在以肉眼可见的速度急剧下降。 Filippo 坦言：“我不是物理学家，我看不懂论文里所有的物理学原理。但我的工作是风险评估。我知道的是，至少有一部分真正的专家正在告诉我们：硬件在变好，算法在变便宜，纠错要求在降低。一切都在加速。” [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/perspective-on-quantum-computing-timeline-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/08/perspective-on-quantum-computing-timeline">本文永久链接</a> &#8211; https://tonybai.com/2026/04/08/perspective-on-quantum-computing-timelines</p>
<p>大家好，我是Tony Bai。</p>
<p>过去三十年，我们一直活在一个笑话里：<strong>“能够破解 RSA 加密的量子计算机，永远在十年之后。”</strong></p>
<p>作为一名软件工程师，我曾和你们中的大多数人一样，对所谓的“量子末日（Q-Day）”嗤之以鼻。我们觉得，在有生之年，我们赖以生存的 RSA、ECC 加密体系坚不可摧，那一天遥遥无期。</p>
<p><strong>但就在昨天（2026年4月6日），这个笑话，似乎被终结了。</strong></p>
<p>Go 语言前核心团队安全负责人、Go密码学专家之一 <strong>Filippo Valsorda</strong>，在他的个人博客上发表了<a href="https://words.filippo.io/crqc-timeline/">一篇极其沉重的文章</a>。</p>
<p>在文章的开头，他用一种近乎“忏悔”的口吻写道：</p>
<blockquote>
<p>“在推出抗量子密码学的紧迫性上，我的立场与几个月前相比，已经发生了改变。……是时候公开表明并解释我改变想法的原因了。”</p>
</blockquote>
<p>在这篇长文中，Filippo 引用了 Google 和 Oratomic 上周刚刚发表的两篇重磅论文，以及多位顶级物理学家的最新警告，最终得出了一个令整个软件工程界脊背发凉的结论：我们不再拥有十年。具备破解当前所有主流加密算法能力的量子计算机（CRQC），其到来的时间线，已经被一些顶级专家压缩到了一个令人绝望的数字——2029 年。</p>
<p><strong>是的，只剩下 33 个月。</strong></p>
<p>今天，就让我们跟随这位曾经的“官方守护者”的视角，看看这场已经兵临城下的“加密世界末日”，到底是怎么回事，以及我们作为普通的后端开发者，该如何在这场史无前例的迁移中生存下来。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/paid/go-crypto-101-qr.png" alt="" /></p>
<h2>冰山撞来：Google 的论文与被撕碎的幻想</h2>
<p>Filippo 指出，在过去的一周里，两篇论文彻底改变了游戏规则。</p>
<p><strong><a href="https://research.google/blog/safeguarding-cryptocurrency-by-disclosing-quantum-vulnerabilities-responsibly/">第一篇</a>，来自 Google。</strong> 这篇论文极大地修正了破解 256 位椭圆曲线（如我们每天都在用的 HTTPS 证书、比特币签名 secp256k1）所需的逻辑量子比特（qubits）和门电路数量。结论是：<strong>在超导量子比特这种高速架构上，攻击可以在几分钟内完成。</strong></p>
<p><strong><a href="https://arxiv.org/abs/2603.28627">第二篇</a>，来自 Oratomic。</strong> 这篇论文更加激进。它指出，如果拥有非局部连接能力（如中性原子方案），破解 256 位椭圆曲线甚至只需要 <strong>10,000 个物理量子比特</strong>。这种攻击虽然更慢，但哪怕一个月只能破解一个密钥，也足以引发灾难。</p>
<p>那张出现在论文第二页、堪称“末日倒计时”的图表，清晰地展示了攻破 RSA-2048 和 ECC-256 所需的物理量子比特数，正在以肉眼可见的速度急剧下降。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/perspective-on-quantum-computing-timeline-2.png" alt="" /></p>
<p>Filippo 坦言：“我不是物理学家，我看不懂论文里所有的物理学原理。但我的工作是风险评估。我知道的是，至少有一部分真正的专家正在告诉我们：<strong>硬件在变好，算法在变便宜，纠错要求在降低。一切都在加速。</strong>”</p>
<h2>精英的警告：当你还在嘲笑，他们已经开始行动</h2>
<p>更让 Filippo 感到警惕的，不是冷冰冰的论文，而是行业顶尖精英们近乎“反常”的表态。</p>
<ul>
<li>Google 的安全总监 Heather Adkins 和 Sophie Schmieg 公开宣布，他们的<strong>最后期限是 2029 年</strong>。Filippo 强调：“在此之前，从没有人给出过如此激进的时间表。”</li>
<li>著名的量子计算理论家 Scott Aaronson，将当前的状态比作 <strong>1939 年到 1940 年之间</strong>——那段时间，关于核裂变研究的公开发表突然在全球范围内“神秘消失”了。</li>
<li>Scott Aaronson 更是抛出了一个灵魂拷问，戳破了所有人的侥幸心理：<br />
> “一旦你理解了量子容错，再问‘你什么时候能用 Shor 算法分解 35？’，就好像在 1943 年问曼哈顿计划的物理学家‘你什么时候能搞出一次小小的核爆？’一样可笑。”</li>
</ul>
<p>Filippo 写道，如果你还在想“这事儿可能成，也可能不成”，那你已经输了。</p>
<p>现在的赌注不是“你 100% 确定 2030 年会有量子计算机吗？”，而是<strong>“你 100% 确定 2030 年绝对不会有吗？你敢拿你所有用户的身家性命去赌那不到 1% 的可能性吗？”</strong></p>
<h2>SNDL 攻击：你的数据，正在被黑客“先存后破”</h2>
<p>为什么我们必须立刻行动？因为黑客们正在疯狂执行一种极其阴险的战略：<strong>“Store Now, Decrypt Later”（先收集，后破解，SNDL）。</strong></p>
<p>他们把现在通过网络截获的、由 RSA/ECC 加密的、看似安全的核心机密数据（比如你的银行交易、公司的商业合同、国家的敏感情报）全部存储在巨大的硬盘阵列里。</p>
<p>等几年后量子计算机成熟，他们就能在一瞬间，把这些尘封的历史数据全部解开，一览无余。</p>
<p>这意味着，我们今天发送的每一封加密邮件，每一次 HTTPS 访问，都在为未来的某一次“数字考古”提供素材。</p>
<h2>行动指南：我们必须立刻开始“造船”</h2>
<p>面对这场已经兵临城下的风暴，Filippo 作为Go 密码学界的专家，给出了极其具体、甚至有些“痛苦”的行动指南。</p>
<p><strong>1. 密钥交换（Key Exchange）：立即迁移到 ML-KEM</strong></p>
<p>这是抵御 SNDL 攻击的第一道防线。Filippo 强调，任何非抗量子的密钥交换（如经典的 ECDH）都应被视为潜在的“主动破解”行为，并像 OpenSSH 那样向用户发出明确警告。</p>
<p><strong>2. 数字签名（Digital Signatures）：硬着头皮上 ML-DSA，放弃幻想</strong></p>
<p>这是最痛苦的部分。Filippo 不无遗憾地承认，他原本希望我们能有更多时间去设计更优雅的、适应大签名体积的协议。但现在，没时间了。</p>
<p>我们必须接受 <strong>ML-DSA 签名体积巨大</strong>（几千字节，而 ECDSA 只有几十字节）的残酷现实，并开始在为小签名设计的协议（如 X.509 证书）中强行塞入这些“肥胖”的签名。</p>
<p>他甚至激进地提出：<strong>应该彻底放弃“混合签名”（经典+后量子）的过渡方案</strong>，直接一步到位使用纯 ML-DSA-44。因为混合签名带来的复杂性和性能开销，已经超过了它能提供的那点微不足道的“对冲”收益。</p>
<p><strong>3. 对 Go 开发者意味着什么？</strong></p>
<p>Filippo 直言不讳：</p>
<blockquote>
<p>“在我的世界里，我们必须开始思考，<strong>Go 标准库中一半的密码学包突然变得不安全意味着什么</strong>。……这是我们职业生涯中从未遇到过的事情：从 SHA-1 到 SHA-256 的迁移，远没有这次这么具有破坏性。”</p>
</blockquote>
<p>这意味着，我们很快就要在 Go 的 crypto/tls, crypto/x509, x/crypto/ssh 中看到翻天覆地的变化。</p>
<h2>小结： weird, but it is what it is</h2>
<p>在文章的结尾，Filippo 提到，他本周刚刚开始在博洛尼亚大学教授一门密码学博士课程。他告诉学生，RSA、ECDSA 这些我们曾经引以为傲的算法，<strong>现在只能作为“遗留算法（Legacy Algorithms）”来介绍了</strong>。</p>
<p>他写道：<strong>“我知道，这感觉很奇怪。但，现实就是如此（it is what it is）。”</strong></p>
<p>Filippo 的这声叹息，既是对一个技术时代的告别，也是对我们所有软件工程师拉响的高级别警报。</p>
<p>当 Go 语言前核心团队的安全负责人、一个以极度严谨和保守著称的密码学专家，都开始用如此紧迫的口吻催促我们行动时，我们没有理由再把头埋在沙子里，假装危机还很遥远。</p>
<p><strong>那艘名为“量子计算”的巨轮，已经出现在了海平面上。现在不是争论它会不会撞上来的时候，现在是立刻开始造救生艇的时候。</strong></p>
<p>资料链接：</p>
<ul>
<li>https://words.filippo.io/crqc-timeline/</li>
<li>https://research.google/blog/safeguarding-cryptocurrency-by-disclosing-quantum-vulnerabilities-responsibly/</li>
<li>https://arxiv.org/abs/2603.28627</li>
</ul>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>在你的公司或个人项目中，有哪些核心数据是绝对不能在 5-10 年后被解密的？面对这场迫在眉睫的密码学大迁移，你觉得我们应该从哪个环节开始着手准备？</p>
<p>欢迎在评论区分享你的看法和焦虑！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/08/perspective-on-quantum-computing-timeline/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>从 1960 到 2026：一文看透 Java、Go、Python 垃圾回收器的原理与演进</title>
		<link>https://tonybai.com/2026/04/07/garbage-collectors-deep-dive/</link>
		<comments>https://tonybai.com/2026/04/07/garbage-collectors-deep-dive/#comments</comments>
		<pubDate>Tue, 07 Apr 2026 00:17:15 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ColoredPointers]]></category>
		<category><![CDATA[ConcurrentMarking]]></category>
		<category><![CDATA[CopyingCollector]]></category>
		<category><![CDATA[CPython]]></category>
		<category><![CDATA[Cycles]]></category>
		<category><![CDATA[EscapeAnalysis]]></category>
		<category><![CDATA[G1GC]]></category>
		<category><![CDATA[GarbageCollection]]></category>
		<category><![CDATA[GC]]></category>
		<category><![CDATA[GenerationalHypothesis]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[HybridWriteBarrier]]></category>
		<category><![CDATA[Java]]></category>
		<category><![CDATA[JVM]]></category>
		<category><![CDATA[latency]]></category>
		<category><![CDATA[MarkAndSweep]]></category>
		<category><![CDATA[MemoryManagement]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[ReferenceCounting]]></category>
		<category><![CDATA[StopTheWorld]]></category>
		<category><![CDATA[STW]]></category>
		<category><![CDATA[Throughput]]></category>
		<category><![CDATA[TriColorMarking]]></category>
		<category><![CDATA[WriteBarrier]]></category>
		<category><![CDATA[ZGC]]></category>
		<category><![CDATA[三色标记]]></category>
		<category><![CDATA[全线停顿]]></category>
		<category><![CDATA[内存管理]]></category>
		<category><![CDATA[写屏障]]></category>
		<category><![CDATA[分代假说]]></category>
		<category><![CDATA[吞吐量]]></category>
		<category><![CDATA[垃圾回收]]></category>
		<category><![CDATA[复制算法]]></category>
		<category><![CDATA[并发标记]]></category>
		<category><![CDATA[延迟]]></category>
		<category><![CDATA[引用计数]]></category>
		<category><![CDATA[循环引用]]></category>
		<category><![CDATA[有色指针]]></category>
		<category><![CDATA[标记清除]]></category>
		<category><![CDATA[混合写屏障]]></category>
		<category><![CDATA[逃逸分析]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6154</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/07/garbage-collectors-deep-dive 大家好，我是Tony Bai。 为什么 Java 的 G1GC 需要设置停顿目标？Go 的混合写屏障是如何消除栈重扫的？Python 又是如何解决引用计数无法处理的循环引用？ 垃圾回收（GC）不仅是语言运行时的核心，更是理解高性能系统绕不开的坎。 本文翻译自Shubham Raizada的文章《Garbage Collection: From First Principles to Modern Collectors in Java, Go and Python》。 此文通过对历史经典论文的溯源和对现代主流语言底层实现的拆解，构建了一套完整的 GC 知识体系。 文章涵盖了从基础的标记-清除、复制与整理算法，到复杂的三色标记抽象、写屏障机制以及有色指针技术。 无论你是想调优 JVM 性能，还是试图理解 Go 并发垃圾收集的吞吐成本，这篇文章都将为你提供从理论支撑到代码实现的全景视角。 以下是译文全文： 在过去的几年里，我的技术栈经历了从 Java 到 Go，再到 Rust，现在又回到了 Java 的过程。 在这些语言之间切换时，一直绕不开的一个话题就是垃圾回收（Garbage Collection, GC）。Java 和 Go 有 GC，而 Rust 没有。 在基准测试、延迟讨论以及“为什么这个服务变慢了”的对话中，GC 总会出现在某个角落。我经常听到关于 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/07/garbage-collectors-deep-dive">本文永久链接</a> &#8211; https://tonybai.com/2026/04/07/garbage-collectors-deep-dive</p>
<p>大家好，我是Tony Bai。</p>
<p>为什么 Java 的 G1GC 需要设置停顿目标？Go 的混合写屏障是如何消除栈重扫的？Python 又是如何解决引用计数无法处理的循环引用？</p>
<p>垃圾回收（GC）不仅是语言运行时的核心，更是理解高性能系统绕不开的坎。</p>
<p>本文翻译自Shubham Raizada的文章《<a href="https://shbhmrzd.github.io/systems/garbage-collection/memory-management/2026/04/01/garbage-collectors-deep-dive.html">Garbage Collection: From First Principles to Modern Collectors in Java, Go and Python</a>》。</p>
<p>此文通过对历史经典论文的溯源和对现代主流语言底层实现的拆解，构建了一套完整的 GC 知识体系。</p>
<p>文章涵盖了从基础的标记-清除、复制与整理算法，到复杂的三色标记抽象、写屏障机制以及有色指针技术。</p>
<p>无论你是想调优 JVM 性能，还是试图理解 Go 并发垃圾收集的吞吐成本，这篇文章都将为你提供从理论支撑到代码实现的全景视角。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-qr.png" alt="" /></p>
<p>以下是译文全文：</p>
<hr />
<p>在过去的几年里，我的技术栈经历了从 Java 到 Go，再到 Rust，现在又回到了 Java 的过程。</p>
<p>在这些语言之间切换时，一直绕不开的一个话题就是垃圾回收（Garbage Collection, GC）。Java 和 Go 有 GC，而 Rust 没有。</p>
<p>在基准测试、延迟讨论以及“为什么这个服务变慢了”的对话中，GC 总会出现在某个角落。我经常听到关于 GC pauses（GC 停顿）、throughput overhead（吞吐量开销）和 write barriers（写屏障）的讨论，但我并不完全理解底层发生了什么。</p>
<p>在追溯起源时，我读到了 McCarthy 1960 年的论文，这篇论文因引入 Lisp 而闻名，但它也是首次描述 mark-and-sweep（标记-清除）的地方。</p>
<p>这又引导我阅读了 Wilson 1992 年的综述《Uniprocessor Garbage Collection Techniques》，该文将随后的所有发展组织成了一个清晰的分类学。</p>
<p>阅读这两篇文献让我更容易理解现代垃圾收集器，因为 G1GC、ZGC、Go 的并发收集器以及 CPython 的混合方案全都是这些论文所描述思想的变体。我还用 Go 编写了一个简单的玩具级 GC，以便亲自观察其机制。</p>
<p>以下是我在这一过程中的笔记。</p>
<h2>起源论文</h2>
<h3>McCarthy (1960): <a href="https://dl.acm.org/doi/10.1145/367177.367199">Recursive Functions of Symbolic Expressions and Their Computation by Machine</a></h3>
<p>这篇论文因引入 Lisp 而闻名，但垃圾回收器几乎是作为实现细节被埋藏在其中的。McCarthy 需要一种方法来管理符号表达式的内存。Lisp 程序操作的是嵌套的列表（lists of lists of lists），这种递归结构使得要求程序员手动释放内存变得不切实际。因此，他描述了一种自动执行此操作的机制。</p>
<p>该机制分为两个阶段。首先，从程序正在活跃使用的 root（根）变量开始，遍历它们引用的每一个对象，将每个对象标记为 reachable（可达）。其次，扫描所有内存。任何未被标记的对象都是垃圾。将它们重新添加回 free list（空闲列表）。</p>
<p>这就是 mark-and-sweep（标记-清除）。它能自然地处理 cycles（循环引用，因为不可达的循环永远不会被标记），不需要逐个对象的簿记工作，并让程序员可以完全忽略内存管理。</p>
<p>其代价是程序在收集器运行时必须完全停止。每一次分配、每一次计算，所有一切都会冻结，直到标记和清除完成。对于 McCarthy 在 1960 年编写的程序来说，这完全是合理的。</p>
<p>随着程序规模变大并进入对延迟敏感的环境（如处理每秒数千次请求的 Web 服务器），stop-the-world（全线停顿）成了一个难以接受的权衡。现代 GC 研究产生的大部分成果都是为了回答一个问题：如何在不停止世界的情况下进行垃圾内存回收？</p>
<h3>Wilson (1992): <a href="https://dl.acm.org/doi/10.5555/645648.664824">Uniprocessor Garbage Collection Techniques</a></h3>
<p>到 1992 年，三十年的 GC 研究已经产生了许多想法，但缺乏统一的词汇。Wilson 的综述论文将这一切组织了起来。它不是一种新算法，而是一个分类学，为散落在几十年论文中的思想赋予了名称和结构。</p>
<p>Wilson 正式确立了所有后续算法构建其上的三种经典算法。</p>
<p>第一种是 <strong>mark-and-sweep</strong>（标记-清除），即 McCarthy 的原始算法。从 roots 开始，遍历对象图，标记你能触达的所有内容，然后扫过堆并释放任何未标记的内容。它自然处理循环引用，实现简单。缺点是经过足够多的分配和回收循环后，堆会变得 fragmented（碎片化）。存活对象最终散落在各处，中间夹杂着细小的空闲间隙，分配器(allocator)必须更费力地寻找空间。</p>
<p>第二种是 <strong>copying</strong>（复制算法），有时被称为 semi-space（半空间）。其想法是将堆分成两个相等的部分。你在其中一半进行分配，当它填满时，将所有存活对象拷贝到另一半，然后将第一半完全丢弃。碎片消失了，因为存活对象在拷贝过程中被紧密排列在一起。分配速度很快，因为你只需移动一个 bump pointer（碰撞指针）。代价是有一半的内存始终处于空闲状态，等待成为下一次拷贝的目标。</p>
<p>第三种是 <strong>reference counting</strong>（引用计数）。每个对象都记录有多少个指针指向它。当创建一个新引用时，计数增加；当移除一个引用时，计数减少。当计数归零时，对象立即被释放。没有追踪过程，没有停顿，销毁是确定性的。问题在于 cycles（循环引用）。如果两个对象相互指向，即使程序中没有任何其他部分可以触达它们，它们的计数也至少为 1。仅靠引用计数，它们永远不会被释放。</p>
<p>除了这三种算法，Wilson 还探讨了现代垃圾回收器赖以生存的两个观察结果。</p>
<p>第一个是 <strong>generational hypothesis</strong>（分代假说）：大多数对象死得早。在实践中，程序分配的临时对象（中间值、请求作用域的缓冲区、循环变量）往往很快变成垃圾，而只有一小部分对象会贯穿整个程序生命周期。如果你频繁回收年轻对象，偶尔回收老对象，你就能将大部分工作集中在堆中主要是垃圾的部分，这比每次都扫描所有内容的代价要小得多。</p>
<p>第二个是 <strong>tricolor marking</strong>（三色标记），这是一种用于增量和并发收集的抽象。你不再简单地将对象标记为已访问或未访问，而是使用三种颜色：white（白色，尚未见到）、grey（灰色，已见到但子节点尚未扫描）和 black（黑色，已完全处理）。收集器一次处理一个灰色对象。结束时，白色对象即为垃圾。这种抽象使得收集器和应用程序可以同时运行，而不会破坏彼此对堆的视图。Go 的并发 mark-and-sweep 和 ZGC 的并发标记都是这一思想的直接后裔。</p>
<p>本文“现代 GC”部分中的所有内容都可以映射回 Wilson 的分类。工程实现已经变得更加复杂，但底层结构依然如故。</p>
<h2>两种基本方法</h2>
<p>几乎所有的垃圾回收器要么是 reference counting（引用计数），要么是 tracing（追踪），或者是两者的某种结合。Wilson 的论文围绕这一划分进行组织，三十年后依然成立。</p>
<h3>Reference Counting (引用计数)</h3>
<p>每个对象维护一个指向它的引用计数。当引用创建时，计数增加。当引用移除时，计数减少。当计数归零时，对象立即被释放。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-2.png" alt="" /></p>
<p>这是 CPython 所使用的其主要机制。它很简单，并能提供确定性的销毁。当指向文件句柄的最后一个引用消失时，<strong>del</strong> 运行，文件当场关闭，而不是在以后的某个 GC cycle中。</p>
<p>有两个问题使得引用计数无法独立胜任。</p>
<p><strong>Cycles (循环引用)。</strong> 如果对象 A 指向对象 B，且对象 B 指向 A，那么即使程序中没有任何其他部分能触达它们，两者的计数也至少为 1。两者都不会被释放。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-3.png" alt="" /></p>
<p>这并非理论上的边缘案例。循环引用在链表数据结构、父子关系、观察者模式和缓存中自然出现。稍后在介绍 CPython 的 GC 时，我将讨论 Python 如何处理这个问题。</p>
<p><strong>Per-mutation overhead (每次修改的开销)。</strong> 每次指针赋值都需要更新引用计数。在多线程程序中，这些必须是 atomic（原子）操作，成本昂贵得多。每当你将对象传递给函数、返回它或将其赋值给字段时，你都要支付这种代价。</p>
<h3>Tracing (追踪式，即 Mark-and-Sweep)</h3>
<p>追踪式收集器不跟踪单个引用，而是从一组已知的存活引用（称为 root set，根集合）开始，遍历整个对象图。它能触达的每个对象都被标记为存活。其他所有对象都被释放。</p>
<p>Root set 是起点，因此什么算作 root（根）至关重要。不同语言的答案是相同的：root 是 runtime（运行时）无需追踪就能找到的任何引用。这些指针锚定在程序当前的执行状态中，是在任何遍历开始之前你就知道是存活的东西。</p>
<p>在实践中，roots 分为以下几类。</p>
<p>每个活跃 stack frame（栈帧）中的 <strong>local variables</strong>（局部变量）和函数参数都是 roots。程序正在活跃地运行这些函数，因此它们引用的任何内容定义上都是在使用中的。</p>
<p><strong>Global and static variables</strong>（全局变量和静态变量）是 roots，因为它们在程序的整个生命周期内都存在。</p>
<p><strong>CPU registers</strong>（CPU 寄存器）是 roots。因为当 JIT 编译器优化一个热点方法时，它可能会将频繁访问的对象引用保留在 CPU 寄存器中，而不是写回栈。如果 GC 此时运行，寄存器保存着该对象的唯一存活引用。如果 GC 不扫描寄存器，它就会释放一个仍在使用中的对象。为了防止这种情况，运行时在代码中定义了 safe points（安全点），GC 只能在这些点发生，并且在这些点，它会快照寄存器状态以寻找持有的任何引用。</p>
<p><strong>Runtime（运行时）本身</strong>也持有与用户代码无关的 roots。在 JVM 中，class loaders 是 roots：你加载的每个类都由其类加载器引用，只要类加载器存活，它加载的每个类（包括它们的静态字段）就保持存活。Interned strings（常量池字符串）是 roots，因为 String.intern() 将字符串存储在 JVM 维护的共享池中。JNI handles 是 roots，因为当原生 C 或 C++ 代码通过 Java Native Interface 持有 Java 对象的引用时，该引用存在于 Java 堆外的句柄表中，GC 必须扫描它。每个活跃线程都是一个 root，其整个调用栈帧都是 root set 的一部分。</p>
<p>Go 的运行时遵循同样的原则。每个 goroutine 都有自己的栈，必须扫描所有 goroutine 栈以寻找 roots。运行时还跟踪自己的内部数据结构，例如 finalizer 队列，作为 root set 的一部分。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-4.png" alt="" /></p>
<p>核心见解是：roots 是由运行时在无需追踪的情况下就已经知道是存活的东西定义的。其他所有东西必须通过从 root 可达来证明自己的生存权。这就是为什么这个概念是与语言无关的。Java、Go 和 Python 之间的具体 roots 集合有所不同，但原则是一样的：从你知道是存活的地方开始，向外追踪，并回收其余部分。</p>
<p>循环引用被自然处理。如果 A 和 B 相互指向，但都无法从任何 root 到达，则标记阶段永远不会访问它们。它们保持未标记状态并被清除。</p>
<p>代价：朴素的 mark-and-sweep 必须在追踪堆时暂停整个程序。这种 stop-the-world（全线停顿）是早期垃圾回收器的核心问题，也是现代 GC 几十年来工程化改进的重点。</p>
<h3>为什么大多数现代 GC 都是追踪式的</h3>
<p>在具有高分配速率的服务器工作负载中，引用计数的逐次修改成本会积少成多。每次指针写入都会增减计数。在多线程程序中，这些更新必须是原子的，而原子操作很昂贵。在数十个线程中每秒进行数千次分配时，这种开销变得可衡量。此外，循环引用问题无论如何都需要一个补充的追踪步骤。而且追踪式收集器可以做成并发的，在应用程序运行的同时运行，只有简短的停顿。</p>
<p>Java 和 Go 使用追踪式收集器。Python 是一个显著的例外，它以引用计数为基础，并在此之上增加了一层用于追踪循环引用的检测器。</p>
<h2>追踪式的变体</h2>
<p>Wilson 的论文描述了实现追踪的四种方式，每种方式都有不同的权衡。</p>
<h3>Mark-Sweep (标记-清除)</h3>
<p>最简单的追踪式收集器。分为两个阶段：</p>
<ol>
<li><strong>Mark (标记)：</strong> 从 roots 开始，遍历对象图并在每个可达对象上设置标记位。</li>
<li><strong>Sweep (清除)：</strong> 遍历整个堆。任何没有标记位的对象都是垃圾。释放它并将内存添加回空闲列表。</li>
</ol>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-5.png" alt="" /></p>
<p>Mark-sweep 的主要问题是 fragmentation（碎片化）。经过足够的回收周期后，堆看起来就像瑞士奶酪：存活对象散布其间，中间有很小的空闲间隙。你总共可能有 100MB 空闲内存，但没有一个连续的块大到足以满足一次新分配。分配器必须维护一个 free list 并搜索合适的空间，随着堆变得碎片化，这会变慢。</p>
<h3>Copying (Semi-Space，复制算法/半空间)</h3>
<p>堆被分成两个相等的一半：from-space（源空间）和 to-space（目标空间）。分配发生在 from-space，使用简单的 bump pointer（碰撞指针）。当 from-space 填满时，收集器将所有存活对象拷贝到 to-space，更新所有指针，然后交换两者的角色。旧的 from-space 被完全丢弃。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-6.png" alt="" /></p>
<p>分配速度极快，因为它只是一个指针移动。Compaction（压缩）自然发生。代价是任何时候只有一半的堆可用。</p>
<h3>Mark-Compact (标记-整理)</h3>
<p>标记阶段与 mark-sweep 相同，但收集器不是简单地释放未标记的对象，而是将所有存活对象滑动到堆的一端。这消除了碎片，且没有复制算法 50% 的内存开销。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-7.png" alt="" /></p>
<p>缺点是整理需要对堆进行多次扫描：一次标记，一次计算新地址，一次更新所有指针，一次移动对象。</p>
<h3>The Generational Hypothesis (分代假说)</h3>
<p>Wilson 论文中最具影响力的观察之一是弱分代假说：大多数对象死得早。</p>
<p>在典型的 Web 服务器中，每个请求都会创建临时对象（解析器、中间字符串、响应构建器），它们只存活几毫秒。配置对象、连接池和缓存则贯穿整个应用程序生命周期。</p>
<p>分代收集器利用这一点，将堆划分为 generations（代）。新对象进入 young generation（年轻代）。如果它们在几次回收中幸存下来，就会被提升到 old generation（老年代）。年轻代回收频繁且速度快，因为那里的大多数对象已经死了。老年代回收较少发生。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-8.png" alt="" /></p>
<p><strong>Eden</strong> 是所有新对象出生的地方。每一个 new Object() 都去这里。它很快就会填满，因为大多数程序分配速率很高。</p>
<p><strong>S0 和 S1</strong> 是两个较小的 survivor spaces（幸存者空间）。当 Eden 填满并运行 minor GC（次要回收）时，收集器将 Eden 中的每个存活对象拷贝到其中一个空间（比如 S0）。下一次回收时，来自 Eden 和 S0 的幸存者被拷贝到 S1。再下一次，回到 S0。它们在每个周期轮换。这是年轻代中的复制算法：没有碎片，没有空闲列表，只有两半空间轮流充当目标。代价是你需要两个幸存者空间，但它们保持得很小，因为到回收运行时，Eden 中的大多数对象都已经死了。</p>
<p><strong>Promotion to old generation (提升到老年代)。</strong> 在对象在 S0 和 S1 之间反弹足够多次之后（JVM 中的默认阈值是 15 次），收集器认定它已赢得了一席之地，并将其提升到老年代。老年代回收频率低得多，并且使用更重的算法（标记-整理而非复制），因为那里的对象庞大且长寿。</p>
<p>关键的实现挑战是跟踪从老对象到新对象的引用。如果一个老对象指向一个年轻对象，即使没有年轻代 root 指向它，该年轻对象也绝不能被回收。这通过 write barrier（写屏障）解决，即在每次指针写入时注入的一小段代码，用于在 remembered set（记录集）中记录跨代引用。</p>
<h2>用 Go 构建一个玩具级 Mark-and-Sweep GC</h2>
<p>我写了一个极简的 mark-and-sweep 收集器来使这些概念具体化。它大约有 70 行代码，演示了完整循环：分配对象、构建对象图、从 roots 标记以及清除不可达对象。</p>
<pre><code>package main

import "fmt"

// Object 代表一个在堆上分配的对象。
type Object struct {
    name     string
    marked   bool
    children []*Object
}

// VM 是一个带有垃圾回收器的微型虚拟机。
type VM struct {
    heap  []*Object
    roots []*Object // 模拟栈变量和全局变量
}

// NewObject 在 VM 的堆上分配一个对象。
func (vm *VM) NewObject(name string) *Object {
    obj := &amp;Object{name: name}
    vm.heap = append(vm.heap, obj)
    return obj
}

// mark 从每个 root 开始遍历并标记所有可达对象。
func (vm *VM) mark() {
    for _, root := range vm.roots {
        vm.markObject(root)
    }
}

func (vm *VM) markObject(obj *Object) {
    if obj == nil || obj.marked {
        return
    }
    obj.marked = true
    for _, child := range obj.children {
        vm.markObject(child)
    }
}

// sweep 释放未标记的对象并重置幸存者的标记。
func (vm *VM) sweep() {
    alive := []*Object{}
    for _, obj := range vm.heap {
        if obj.marked {
            obj.marked = false // 为下一个 GC 周期重置
            alive = append(alive, obj)
        } else {
            fmt.Printf("  collected: %s\n", obj.name)
        }
    }
    vm.heap = alive
}

// GC 运行一次完整的 mark-and-sweep 回收。
func (vm *VM) GC() {
    fmt.Printf("gc: heap has %d objects\n", len(vm.heap))
    vm.mark()
    vm.sweep()
    fmt.Printf("gc: %d objects remain\n\n", len(vm.heap))
}

func main() {
    vm := &amp;VM{}

    a := vm.NewObject("A")
    b := vm.NewObject("B")
    c := vm.NewObject("C")
    _ = vm.NewObject("D") // 已分配但从未链接到任何东西

    // 构建图: A -&gt; B -&gt; C
    a.children = append(a.children, b)
    b.children = append(b.children, c)

    // 只有 A 是 root
    vm.roots = append(vm.roots, a)

    fmt.Println("=== GC #1: D is unreachable ===")
    vm.GC()

    // 创建循环: C -&gt; A, 然后移除所有 roots
    c.children = append(c.children, a)
    vm.roots = nil

    fmt.Println("=== GC #2: A-&gt;B-&gt;C-&gt;A cycle, no roots ===")
    vm.GC()
}
</code></pre>
<p>运行结果：</p>
<pre><code>=== GC #1: D is unreachable ===
gc: heap has 4 objects
  collected: D
gc: 3 objects remain

=== GC #2: A-&gt;B-&gt;C-&gt;A cycle, no roots ===
gc: heap has 3 objects
  collected: A
  collected: B
  collected: C
gc: 0 objects remain
</code></pre>
<p>第一次回收：A、B 和 C 通过 root A 可达。D 没有任何 root 路径，因此被回收。</p>
<p>第二次回收：A、B 和 C 形成了一个循环（A->B->C->A），但没有 roots。标记阶段从未访问过它们中的任何一个。所有三个都被清除了。这正是击败引用计数的场景。循环中的每个对象都有非零的引用计数，但没有一个能从 root 到达。</p>
<p><strong>追踪式 GC 不关心循环。它们只关心从 roots 开始的可达性。</strong></p>
<p>有一点需要注意：markObject 函数使用了递归，这在深层对象图上会耗尽栈空间。真实的垃圾回收器使用显式的 worklist（工作列表）而不是调用栈。</p>
<h2>现代 GC 实现</h2>
<p>上面的玩具收集器为了整个标记和清除过程停止了世界。现代 GC 已经进化到在应用程序持续运行的同时并发完成大部分工作。</p>
<h3>Go: 三色并发标记-清除 (Tri-Color Concurrent Mark-and-Sweep)</h3>
<p>Go 的垃圾回收器是非分代的、非整理的且并发的。它不按年龄区分对象，也不在内存中移动对象。其重点是保持低停顿时间。</p>
<p>收集器使用三色抽象（tri-color abstraction）进行并发标记。每个对象处于三种状态之一：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-9.png" alt="" /></p>
<ul>
<li><strong>White (白色)</strong>: 尚未访问。标记结束时仍为白色的任何东西都是垃圾。</li>
<li><strong>Grey (灰色)</strong>: 已访问，但其子节点尚未全部扫描。遍历的前沿（frontier）。</li>
<li><strong>Black (黑色)</strong>: 已访问，所有子节点已扫描。确定存活。</li>
</ul>
<p>收集器开始时将所有对象设为白色，然后将 roots 设为灰色，并处理灰色对象直到不再剩余。所有仍为白色的内容都被清除。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-10.png" alt="" /></p>
<pre><code>开始: 所有对象为白色，roots 为灰色

步骤 1: 选取一个灰色对象，扫描其子节点
        - 将子节点标为灰色
        - 将扫描过的对象标为黑色

步骤 2: 重复直到没有灰色对象剩余

步骤 3: 所有白色对象都是垃圾

示例:

  Roots: [A]

  开始:      A(grey) --&gt; B(white) --&gt; D(white)
             A(grey) --&gt; C(white)

  扫描 A:    A(black) --&gt; B(grey) --&gt; D(white)
             A(black) --&gt; C(grey)

  扫描 B:    A(black) --&gt; B(black) --&gt; D(grey)
             A(black) --&gt; C(grey)

  扫描 C:    A(black) --&gt; B(black) --&gt; D(grey)
             A(black) --&gt; C(black)

  扫描 D:    A(black) --&gt; B(black) --&gt; D(black)
             A(black) --&gt; C(black)

  结果: 任何剩余的白色对象都是垃圾并被释放
</code></pre>
<p>难点在于应用程序在收集器遍历时持续运行并修改指针。这造成了一个需要仔细处理的正确性问题。</p>
<p>收集器认为黑色对象已完成。一旦对象变黑，收集器就不会再扫描它。它的所有子节点都已被访问并设为灰色。但是，如果应用程序在收集器仍在运行时，将一个指向白色对象的指针写入黑色对象，收集器就有麻烦了。黑色对象已经处理完了。该白色对象也无法从任何灰色对象触达。当标记阶段结束并清除运行时，该白色对象将被释放，即便有一个存活的黑色对象指向它。</p>
<p>这被称为 <strong>tricolor invariant</strong>（三色不变性）：黑色对象绝不能直接指向白色对象。如果发生了这种情况，白色对象对收集器是不可见的，会被错误释放。write barrier（写屏障）的存在专门用于在并发标记期间应用程序修改对象图时维护这一不变性。</p>
<p>Go 通过 <strong>hybrid write barrier</strong>（混合写屏障，Go 1.8 引入）解决了这个问题。要理解它为什么有效，看看它结合的两种旧屏障会有所帮助。</p>
<p><strong>Dijkstra’s 插入屏障 (1978)</strong>：每当一个指针被写入对象时，将新的被引用者设为灰色。如果一个黑色对象存储了对白色对象的引用，该白色对象会在收集器错过它之前变灰。这维护了三色不变性。</p>
<p>问题在于 goroutine 栈与堆对象不同。编译器在堆指针写入处注入写屏障，例如写入结构体字段或切片元素。栈写入是局部变量赋值，编译器对其分别处理。在每一个局部变量赋值上放屏障会使函数调用和基本操作变得极其昂贵，所以屏障不覆盖它们。这意味着在并发标记期间，goroutine 可以自由地将指向白色对象的指针写入局部变量，而没有屏障触发。收集器不知道发生了这事。</p>
<p>为了修复这一点，在并发标记结束时，Go 曾经必须停止世界并从头重新扫描每个 goroutine 的整个栈。重新扫描时发现的任何指向白色对象的指针都会变灰，防止它们被错误释放。此步骤的停顿时间随着 goroutine 数量和其栈大小而增加。拥有成千上万个 goroutine 的程序可能会看到数毫秒的 STW 停顿，仅仅是为了这次重新扫描。这是 Go 1.8 之前主要的 STW 停顿来源。</p>
<p><strong>Yuasa’s 删除屏障 (1990)</strong> 采取相反的方法：每当一个指针即将被覆盖时，在旧引用消失前将其变灰。这确保了在标记开始时可达的任何东西直到结束都保持可达，即便应用程序在标记期间丢弃了它的引用。缺点是标记期间死亡的一些对象会存活到下一个周期（floating garbage，浮动垃圾），因为屏障保守地让它们活着。</p>
<p><strong>Go 的混合屏障</strong>结合了两者。在堆写入时，它同时应用两种屏障：将旧引用变灰（Yuasa）并将新引用变灰（Dijkstra）。在栈写入时，不运行屏障，但栈上新分配的对象开始时就是黑色而不是白色。这种组合赋予了收集器足够强的不变性，使其在标记结束时永远不需要重新扫描栈。STW 停顿从几十毫秒降到了不到一毫秒。</p>
<pre><code>// 混合屏障在堆指针写入时的逻辑:
// *slot = new_ptr

shade(*slot)   // 将旧引用变灰 (Yuasa: 不要丢掉之前在那里的内容)
shade(new_ptr) // 将新引用变灰 (Dijkstra: 不要错过新到来的内容)
*slot = new_ptr
</code></pre>
<p>这就是并发垃圾回收的吞吐量成本：标记阶段的每一次堆指针写入都要运行此 shade 逻辑。单次操作开销虽小，但在高分配速率下会累积。权衡的结果是你获得了亚毫秒级的 STW 停顿，而不是几十毫秒。</p>
<p>Go 仅简短地停止世界以扫描 goroutine 栈并切换写屏障的开关。实际的标记和清除与应用程序并发进行。</p>
<p><strong>No compaction (无整理)。</strong> Go 在分配后不移动对象。相反，Go 使用 tcmalloc 风格的分配器，将内存划分为 size classes（大小类），并从每个处理器的缓存（per-processor caches）中分配。对象被分组为固定的大小类（8 字节、16 字节、32 字节，最高达 32 KB）。分配时从空闲列表中选取合适大小的槽。这减少了碎片而无需移动对象，但并不能完全消除碎片。</p>
<p><strong>No generational collection (无分代收集)。</strong> Go 团队的理由是，考虑到 Go 典型的带有 goroutine 和并发工作负载的分配模式，分代 GC 增加的复杂性（用于跟踪老到新指针的写屏障、提升逻辑、分代大小调优）带来的收益是不确定的。Go 通过使其并发标记器足够快来补偿，从而使额外的回收频率变得可以接受。</p>
<p><strong>关键里程碑：</strong></p>
<ul>
<li>Go 1.5 (2015)：引入并发 GC。在此之前，Go 使用全停顿收集器，停顿时间达 10-100ms 或更多。此版本使 Go 能够胜任延迟敏感型服务。</li>
<li>Go 1.8 (2017)：混合写屏障。降低了在并发标记期间维护三色不变性的开销。</li>
<li>Go 1.19 (2022)：GOMEMLIMIT。使 Go 程序能在容器环境的内存预算内工作。</li>
</ul>
<p><strong>GOGC 调节旋钮。</strong> Go 提供了一个主要的调优参数：GOGC。它控制在下一次 GC 触发之前堆可以增长多少。默认值是 100，意味着当堆自上次回收以来翻倍时触发 GC。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-11.png" alt="" /></p>
<pre><code>GOGC=100 (默认):
  GC 后，存活堆 = 500MB
  下次 GC 触发点: 500MB * (1 + 100/100) = 1000MB

GOGC=50 (更激进):
  GC 后，存活堆 = 500MB
  下次 GC 触发点: 500MB * (1 + 50/100) = 750MB

GOGC=200 (较保守):
  GC 后，存活堆 = 500MB
  下次 GC 触发点: 500MB * (1 + 200/100) = 1500MB
</code></pre>
<p>更低的 GOGC 意味着更频繁的回收（更低的内存占用，更高的 CPU 开销）。更高的 GOGC 意味着较少的回收（更高的内存占用，更低的 CPU 开销）。</p>
<p>Go 1.19 增加了 GOMEMLIMIT，这是一个软内存限制。在具有硬性内存预算的容器环境中，GOMEMLIMIT 告诉 GC pacer（步调算法）在内存使用接近限制时变得更加激进。</p>
<p><strong>亲自尝试：</strong></p>
<pre><code>package main

import (
    "fmt"
    "runtime"
    "time"
)

var longLived []*[1024 * 1024]byte

func main() {
    fmt.Println("Go version:", runtime.Version())

    for round := 0; round &lt; 50; round++ {
        // 短寿对象: 分配小对象，让它们死亡
        for i := 0; i &lt; 5000; i++ {
            _ = make([]byte, 1024)
        }

        // 长寿对象: 每 10 轮保留一个
        if round%10 == 0 {
            arr := new([1024 * 1024]byte)
            longLived = append(longLived, arr)
        }

        time.Sleep(50 * time.Millisecond)
    }

    var stats runtime.MemStats
    runtime.ReadMemStats(&amp;stats)
    fmt.Printf("Total GC cycles: %d\n", stats.NumGC)
    fmt.Printf("Total STW pause: %v\n", time.Duration(stats.PauseTotalNs))
    fmt.Printf("Long-lived objects: %d\n", len(longLived))
}
</code></pre>
<p>运行并开启 GC 追踪：</p>
<pre><code>GODEBUG=gctrace=1 go run gcdemo.go
</code></pre>
<p>观察输出内容：</p>
<pre><code>gc 1 @0.011s 1%: 0.044+0.56+0.13 ms clock, 0.62+0.21/0.57/0+1.8 ms cpu, 3-&gt;4-&gt;0 MB, 4 MB goal, 0 MB stacks, 0 MB globals, 14 P
</code></pre>
<p>从左到右阅读：</p>
<ul>
<li>gc 1: GC 周期编号</li>
<li>@0.011s: 自程序启动的时间</li>
<li>1%: 到目前为止 GC 消耗的 CPU 百分比</li>
<li>
<p>0.044+0.56+0.13 ms clock: GC 周期的三个阶段：STW 标记开始 (0.044ms) + 并发标记和扫描 (0.56ms) + STW 标记结束 (0.13ms)。STW 停顿是 clock 字段中的第一个和第三个数字。在此例中，应用程序被冻结的总墙钟时间是 0.044 + 0.13 = 0.174ms。中间的 0.56ms 是并发的：你的应用程序一直在运行。在 Go 中，STW 停顿通常在 1ms 以下，往往远低于 0.1ms。</p>
</li>
<li>
<p>0.62+0.21/0.57/0+1.8 ms cpu: CPU 时间细目。格式为：STW-开始 + 辅助/背景/空闲 + STW-结束。每个数字代表：</p>
<ul>
<li>0.62ms — STW 标记开始时所有核心的 CPU 总时间。高于墙钟时间 (0.044ms)，因为 Go 会在多个核心上并行化初始栈扫描。</li>
<li>0.21ms — 应用程序 goroutine 执行 mutator assists（赋值器辅助）所花费的 CPU 时间。当某个 goroutine 分配速度超过 GC 处理速度时，它会被“征税”，必须在允许其分配之前自己做一些标记工作。</li>
<li>0.57ms — 专用背景 GC 工作 goroutine 执行并发标记所使用的 CPU 时间。</li>
<li>0 — 空闲 GC 工作者的 CPU 时间（仅在调度器没有其他任务运行时才领取 GC 任务的 goroutine）。此处为零意味着专用工作者处理了所有事情。</li>
<li>1.8ms — STW 标记结束时所有核心的 CPU 总时间。高于墙钟 (0.13ms)，因为多个核心并行工作以排空剩余任务并禁用写屏障。</li>
</ul>
</li>
</ul>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-12.png" alt="" /></p>
<p>当多个核心并行工作时，CPU 时间可以超过墙钟时间。并发阶段的 CPU 时间可能少于墙钟时间，因为 GC 与你的应用程序共享核心。</p>
<ul>
<li>3->4->0 MB: GC 开始时的堆大小、GC 触发点的堆大小、GC 完成后的存活堆大小</li>
<li>4 MB goal: 下次 GC 触发前的目标堆大小（基于 GOGC 和当前存活堆）</li>
<li>0 MB stacks: goroutine 栈使用的内存</li>
<li>0 MB globals: 标记期间扫描的全局变量使用的内存</li>
<li>14 P: 逻辑处理器数量 (GOMAXPROCS)</li>
</ul>
<h3>Java: G1GC (Garbage First Collector)</h3>
<p>G1GC 自 JDK 9 以来一直是 Java 的默认垃圾回收器。它是一个分代的、基于区域（region）的收集器。它进行追踪、标记和整理，但它是增量式进行的，而不是一次性完成。</p>
<p><strong>Region layout (区域布局)。</strong> G1 将堆划分为大小相等的区域，通常每个区域为 1MB 到 32MB，取决于堆的大小。每个区域在任何时候扮演四种角色之一：Eden（伊甸园）、Survivor（幸存者）、Old（老年代）或 Humongous（巨型对象，用于超过半个区域大小的对象）。区域的角色可以在不同回收周期之间改变。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-13.png" alt="" /></p>
<p><strong>Young collection (次要 GC)。</strong> Eden 区域填满。G1 停止世界，使用并行多线程标记器标记 Eden 和 Survivor 区域中的存活对象，将幸存者拷贝到新的 Survivor 区域或提升到 Old 区域，并完全丢弃旧的 Eden 区域。这是一个并行的 STW 停顿，但很短，因为年轻代区域较小且年轻对象大多已死。</p>
<p><strong>Mixed collection (混合回收)。</strong> G1 周期性地运行并发标记周期，以找出哪些老年代区域包含的垃圾最多。然后运行混合回收：同时疏散（evacuating）年轻代区域和最具“盈利价值”的老年代区域。这就是“Garbage First”名称的由来。G1 总是优先选取垃圾密度最高的老年代区域，从而在单位停顿时间内实现最大的回收量。</p>
<p><strong>SATB (Snapshot-At-The-Beginning，起始快照)。</strong> 在并发标记期间，应用程序持续运行并修改对象图。G1 使用 SATB 维护正确性。在标记开始时，G1 对哪些对象存活进行逻辑快照。该快照中存活的对象在此周期被视为存活，即使应用程序在标记期间丢弃了它们。写屏障将修改字段的旧值记录到 SATB 队列中。这种做法是保守的（一些垃圾会存活到下个周期），但是正确的。</p>
<pre><code>并发标记正在运行。应用程序执行：
  obj.field = null   (原本指向 X)

没有 SATB: X 可能没有其他引用，未被标记，在使用中被释放。
有 SATB:    写屏障记录“此处曾有 X”，将 X 标为灰色。安全。
</code></pre>
<p><strong>Pause target (停顿目标)。</strong> 你可以通过 -XX:MaxGCPauseMillis 配置 G1 的目标最大停顿时间。默认值是 200ms。G1 通过调整区域数量、回收集合大小和时机，尝试将停顿保持在目标范围内。它并不总是能成功，特别是在 Full GC 期间，但它是主要的调优旋钮。</p>
<p><strong>亲自尝试：</strong></p>
<pre><code>import java.util.ArrayList;
import java.util.List;

public class GCDemo {
  static List&lt;byte[]&gt; longLived = new ArrayList&lt;&gt;();

  public static void main(String[] args) throws InterruptedException {
    System.out.println("Starting GC demo...");

    for (int round = 0; round &lt; 50; round++) {
      // 短寿对象：创建并立即丢弃
      for (int i = 0; i &lt; 1000; i++) {
        byte[] tmp = new byte[10 * 1024]; // 每个 10KB
      }

      // 长寿对象：保留一些对象以构建老年代
      if (round % 5 == 0) {
        longLived.add(new byte[1024 * 1024]); // 1MB
      }

      Thread.sleep(50);
    }

    System.out.println("Done. Long-lived objects: " + longLived.size());
  }
}
</code></pre>
<p>使用 G1GC 日志运行：</p>
<pre><code># 编译
javac GCDemo.java

# 使用 G1GC (Java 9+ 默认) 并开启 GC 日志
java -Xmx256m \
     -XX:+UseG1GC \
     "-Xlog:gc*:file=gc_g1.log:time,uptime,level,tags" \
     GCDemo

# 或者，使用简洁的一行输出
java -Xmx256m -Xlog:gc GCDemo
</code></pre>
<p>观察日志：</p>
<pre><code>[0.005s][info][gc] Using G1
[0.135s][info][gc] GC(0) Pause Young (Normal) (G1 Evacuation Pause) 26M-&gt;3M(256M) 0.644ms
[0.812s][info][gc] GC(1) Pause Young (Normal) (G1 Evacuation Pause) 132M-&gt;7M(256M) 0.707ms
[1.710s][info][gc] GC(2) Pause Young (Normal) (G1 Evacuation Pause) 165M-&gt;13M(256M) 1.019ms
[2.528s][info][gc] GC(3) Pause Young (Normal) (G1 Evacuation Pause) 171M-&gt;19M(256M) 0.964ms
</code></pre>
<p>阅读日志：</p>
<ul>
<li>Using G1: 确认 G1GC 是活跃收集器</li>
<li>Pause Young (Normal): 回收 Eden 和 Survivor 区域的次要 GC</li>
<li>G1 Evacuation Pause: G1 正在将存活对象从回收区域拷贝（疏散）到新区域</li>
<li>26M->3M(256M) 0.644ms: 堆之前是 26MB，之后是 3MB，总堆容量 256MB，停顿耗时 0.644ms</li>
<li>在 2.5 秒的运行时中进行了四个 GC 周期，每个周期在 1.1ms 内完成。大多数分配的对象都是短寿的，并在年轻代被回收。</li>
</ul>
<h3>Java: ZGC (Z Garbage Collector)</h3>
<p>ZGC 自 Java 11 起可用，并在 Java 15 中达到生产就绪状态。扩展了分代收集的 Generational ZGC 在 Java 21 中引入。ZGC 的目标是无论堆大小如何（包括数百 GB 的堆），停顿时间均保持在亚毫秒级。</p>
<p>G1 在年轻代回收时停顿较短，但随着堆的增长，在并发标记设置和混合回收期间会有更长的停顿。ZGC 的方法不同：它几乎将所有工作（标记、重定位、引用处理）并发进行，将 STW 工作降至最低。</p>
<p><strong>Colored pointers (有色指针)。</strong> ZGC 直接在指针位中编码 GC 元数据。在 64 位平台上，指针宽度为 64 位，但你实际上并不需要所有 64 位来寻址内存。2^42 就能给你 4TB 的可寻址空间，这超出了大多数应用程序的使用范围。这使得每个指针中留有超过 20 位空闲。ZGC 重新利用其中一些空闲位，直接在指针内部存储 GC 状态。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-14.png" alt="" /></p>
<p>每个元数据位都有特定用途：</p>
<ul>
<li>
<p><strong>M0 和 M1 (标记位)：</strong> 用于跟踪对象是否已被标记为存活。ZGC 在每个 GC 周期中交替使用 M0 和 M1。在周期 1，收集器对每个可达对象设置 M0。在周期 2，它改用 M1。这样收集器就能区分“本周期标记”和“上个周期标记”，而无需在周期之间清除所有标记位。</p>
</li>
<li>
<p><strong>Remap (R，重映射)：</strong> 此位跟踪在对象重定位（relocated）后指针是否已更新。在并发重定位期间，ZGC 将对象移动到新地址，但并不立即更新堆中的每一个指针。相反，它保留旧指针，并使 remap 位处于未设置状态。当应用程序加载这些过时指针之一时，load barrier（读屏障/加载屏障）会注意到 remap 位未设置，并对其进行修正。</p>
</li>
<li>
<p><strong>Finalizable (F)：</strong> 表示该对象具有一个需要在释放前运行的 finalizer。</p>
</li>
</ul>
<p>巧妙之处在于元数据随指针移动。GC 不需要一个单独的侧表来查找对象的 GC 状态。每个指针都已经携带了它。</p>
<p><strong>Load barriers (加载屏障)。</strong> 每次应用程序从堆加载引用时，ZGC 都会插入一个加载屏障。屏障检查指针的颜色位，如果它们不处于预期状态，则采取行动。</p>
<p>以下是实际操作中的情况。假设收集器在并发重定位阶段将一个对象从地址 0&#215;1000 移动到了 0&#215;2000。应用程序仍然持有一个地址为 0&#215;1000 且 remap 位未设置的指针。</p>
<pre><code>应用程序代码:
  Object x = obj.field;

实际执行的内容:
  raw_ptr = load obj.field           // raw_ptr = 0x1000, remap bit = 0
  if (raw_ptr.color != expected) {   // remap bit 为 0, expected 为 1 → 进入 slow path
      new_addr = forwarding_table[0x1000]  // 查找: 对象已移动到 0x2000
      raw_ptr = set_address(raw_ptr, 0x2000)
      raw_ptr = set_remap_bit(raw_ptr)
      obj.field = raw_ptr            // 就地修正指针，以便下次使用
  }
  x = raw_ptr                       // x 现在指向 0x2000
</code></pre>
<p>下次任何线程加载 obj.field 时，remap 位已经设置好了。屏障检查通过 fast path，没有额外工作。过时指针在第一次访问时被惰性修正。</p>
<p>这是关键机制。与其像 G1 在疏散期间那样让 GC 停止世界以一次性更新所有指向重定位对象的指针，ZGC 让应用程序在遇到指针时逐个修正。代价是每次指针加载都要支付屏障检查的开销，即便没有任何东西被重定位。在实践中，fast path（检查几位）执行代价足够小，与避免 STW 重定位停顿带来的收益相比，开销很小。</p>
<p><strong>Concurrent relocation (并发重定位)。</strong> G1 停止世界以将存活对象从回收区域中疏散。ZGC 在应用程序运行的同时重定位对象。它能做到这一点是因为加载屏障处理了指针修正。在启动和结束每个阶段（标记开始、标记结束、重定位开始）时有简短的 STW 停顿，但这些通常远低于 1ms。拷贝对象和修正指针的实际工作是并发发生的。</p>
<p><strong>Generational ZGC (Java 21+)。</strong> 最初的 ZGC 不按年龄划分堆。分代 ZGC 增加了年轻代和老年代，同时保留了亚毫秒级停顿的保证。它更频繁地回收年轻区域（垃圾最多的地方），较少回收老年代区域。加载屏障和有色指针机制被扩展以处理分代写屏障。</p>
<p><strong>何时使用 ZGC vs G1：</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-15.png" alt="" /></p>
<p><strong>亲自尝试：</strong></p>
<pre><code># 使用 ZGC 运行
java -Xmx256m \
     -XX:+UseZGC \
     "-Xlog:gc*:file=gc_zgc.log:time,uptime,level,tags" \
     GCDemo

# 使用分代 ZGC (Java 21+)
java -Xmx256m \
     -XX:+UseZGC -XX:+ZGenerational \
     -Xlog:gc \
     GCDemo
</code></pre>
<p>观察日志：</p>
<pre><code>[0.318s] GC(0) Garbage Collection (Warmup) 28M(11%)-&gt;12M(5%)
[0.321s] GC(0) Pause Mark Start 0.023ms
[0.489s] GC(0) Concurrent Mark 168.123ms
[0.491s] GC(0) Pause Mark End 0.019ms
[0.492s] GC(0) Concurrent Select Relocation Set 1.234ms
[0.502s] GC(0) Concurrent Relocate 10.456ms
</code></pre>
<p>STW 停顿是标记为“Pause”的行。其他所有内容都是并发的。将此处的停顿持续时间与 G1 的输出进行对比。</p>
<h3>Python: 引用计数加循环 GC</h3>
<p>CPython（Python 的参考实现）是“追踪式收集器占主导”模式的主要例外。它使用引用计数作为主要机制，并在之上增加了一层用于追踪循环引用的检测器。</p>
<p><strong>CPython 中的引用计数。</strong></p>
<p>每个 Python 对象都有一个 ob_refcnt 字段。Python 的 C API 在 Py_INCREF 时增加，在 Py_DECREF 时减少。当计数归零时，对象在 _Py_Dealloc 中被立即释放。这赋予了 Python 确定性的销毁：<strong>del</strong> 方法和上下文管理器的 <strong>exit</strong> 调用在最后一个引用掉落的那一刻发生。</p>
<pre><code>import sys

x = []
print(sys.getrefcount(x))  # 2: 1个来自x，1个来自getrefcount参数本身的临时引用

y = x
print(sys.getrefcount(x))  # 3: 1个x, 1个y, 1个getrefcount参数

del y
print(sys.getrefcount(x))  # 2: 回到1个x, 1个getrefcount参数
</code></pre>
<p><strong>循环引用问题。</strong> 仅靠引用计数无法回收循环垃圾。</p>
<pre><code>import gc

# 创建循环引用
class Node:
    def __init__(self, name):
        self.name = name
        self.ref = None

a = Node("A")
b = Node("B")
a.ref = b
b.ref = a   # cycle: A -&gt; B -&gt; A

# a 和 b 的计数都 &gt;= 1（由于相互引用）。
# 仅靠引用计数，两者都不会被释放。

del a
del b
# a 和 b 依然存活！Refcount: A 为 1 (来自 b.ref), B 为 1 (来自 a.ref)

# 显式触发循环检测器
collected = gc.collect()
print(f"Collected {collected} objects")  # 收集了 4 个对象 (2个node + 2个dict)
</code></pre>
<p>引用计数处理了常见情况，但它无法收集循环引用。CPython 的答案是运行在引用计数系统之上的独立循环检测器。其实现在 Modules/gcmodule.c 中。</p>
<p>循环检测器是一个追踪式收集器，但它并不追踪整个堆。它仅跟踪能够参与循环引用的对象：如列表、字典、集合及用户自定义类实例等容器对象。字符串和整数无法持有对其他对象的引用，因此无需跟踪它们。</p>
<p>与 Java 的收集器一样，循环检测器使用分代方法。共有三代，编号为 0、1 和 2。思路与我们之前讨论的分代假说相同：大多数对象死得早，所以经常检查年轻对象，少打扰老对象。默认阈值硬编码在 CPython 的 <a href="https://github.com/python/cpython/blob/v3.9.6/Modules/gcmodule.c#L137">Modules/gcmodule.c</a> 中：</p>
<pre><code>struct gc_generation generations[NUM_GENERATIONS] = {
    /* PyGC_Head,                                    threshold,    count */
    { {(uintptr_t)_GEN_HEAD(0), (uintptr_t)_GEN_HEAD(0)},   700,        0},
    { {(uintptr_t)_GEN_HEAD(1), (uintptr_t)_GEN_HEAD(1)},   10,         0},
    { {(uintptr_t)_GEN_HEAD(2), (uintptr_t)_GEN_HEAD(2)},   10,         0},
};
</code></pre>
<p>你可以验证你的运行时实际使用的是什么：</p>
<pre><code>python3 -c "import gc; print(gc.get_threshold())"
# (700, 10, 10)
</code></pre>
<p>请注意，某些框架和发行版会在启动时通过 gc.set_threshold() 覆盖这些默认值，因此你的环境可能显示不同的值。</p>
<p>第 0 代持有新分配的容器对象。当自上次回收以来的新分配数量超过阈值（默认 700）时，回收第 0 代。幸存的对象被提升到第 1 代。在第 0 代被回收 10 次后，第 1 代被回收一次。幸存者移至第 2 代。在第 1 代被回收 10 次后，第 2 代被回收一次。</p>
<p>效果是第 0 代大约每 700 次分配回收一次，第 1 代大约每 7,000 次，第 2 代大约每 70,000 次。进入第 2 代的长寿对象几乎永远不会被打扰。检测器将其大部分时间花在最年轻的对象上，这些对象最有可能最近变成了垃圾。</p>
<p>你可以看到这些计数：</p>
<pre><code>import gc

# 当前各代阈值
print(gc.get_threshold())  # (700, 10, 10)

# 当前分配计数: (gen0分配, 自上次gen1回收以来的gen0回收数, 自上次gen2回收以来的gen1回收数)
print(gc.get_count())  # 例如 (342, 8, 2)

# 强制进行全量回收
gc.collect()

# 完全禁用循环检测器 (如果你确定代码中没有循环引用)
gc.disable()
</code></pre>
<p>当检测器在某一代码代上运行时，它需要找出哪些对象仅被循环引用保持存活。通过一个例子更容易理解算法。</p>
<p>假设检测器正在查看三个被跟踪的对象：X、Y 和 Z。X 指向 Y 和 Z。Y 指回 X。还有一个局部变量持有对 X 的引用。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-16.png" alt="" /></p>
<p>步骤 1：拷贝引用计数。X=2, Y=1, Z=1。</p>
<p>步骤 2：减去内部引用。Y 指向 X，所以从 X 的副本中减 1 (X 从 2 变为 1)。X 指向 Y，所以从 Y 的副本中减 1 (Y 从 1 变为 0)。X 指向 Z，所以从 Z 的副本中减 1 (Z 从 1 变为 0)。</p>
<p>步骤 3：检查剩余部分。X 的调整后计数为 1。被跟踪集合之外的某些东西（局部变量）仍然指向它。X 存活。Y 和 Z 虽然调整后计数为 0，但它们可以从 X 到达，因此它们也幸存下来。</p>
<p>现在想象局部变量消失了。X 的引用计数掉到 1 (只有 Y 指向它)。运行相同算法：拷贝 X=1, Y=1, Z=1。减去内部引用：X 变为 0, Y 变为 0, Z 变为 0。每个调整后的计数都是零。被跟踪集合之外没有任何东西指向它们。它们仅因彼此而存在。三者都是垃圾。</p>
<p>这就是核心思想。算法寻找那些存在的唯一理由是同一集合中其他对象的目标。</p>
<p>有一个边缘案例困扰了多年：finalizers（终结器）。</p>
<p>终结器是运行时在对象被销毁前调用的方法，给予其清理外部资源（如文件句柄或网络连接）的机会。在 Python 中，这就是 <strong>del</strong> 方法。</p>
<p>假设 A 和 B 处于循环中，且两者都有 <strong>del</strong> 方法。检测器知道它们是垃圾，但要释放它们，它需要打破循环。问题是：哪个 <strong>del</strong> 先运行？如果你先运行 A 的终结器，而它尝试使用 B，但 B 已经正在被销毁，你就会崩溃。如果你先运行 B 的，而它使用 A，同样的问题。没有安全的顺序。</p>
<p>在 Python 3.4 之前，CPython 直接放弃处理这些对象。它将它们放入名为 gc.garbage 的列表中，且永远不释放它们。如果你的代码创建了带有 <strong>del</strong> 的循环引用，你就会有一个静默的内存泄漏。PEP 442 通过在打破任何引用之前调用终结器修复了这个问题。当 A 和 B 的 <strong>del</strong> 运行时，两者都保持完整。只有在所有终结器执行完毕后，检测器才会打破循环并释放对象。</p>
<p>关于 CPython 的内存模型还有一件事值得了解。每当 Python 执行类似 x = some_object 的操作时，它会增加 some_object 的引用计数（C 语言中的 Py_INCREF）。每当变量超出作用域时，它减少计数 (Py_DECREF)。在 C 中这些是普通的整数操作：refcount += 1, refcount -= 1。没有锁，没有原子指令。</p>
<p>在多线程程序中，这是一个问题。两个线程可能同时增加同一个对象的引用计数。如果没有同步，一个增加操作会丢失（经典的竞态条件），之后该对象可能会在有人仍在使用时被释放。</p>
<p>GIL (全局解释器锁) 防止了这种情况。一次只有一个线程执行 Python 字节码，因此两个线程永远不会同时修改同一个引用计数。GIL 免费使所有引用计数操作变得安全，而无需任何原子指令。</p>
<p>这也是移除 GIL 如此困难的原因。如果拿掉它，整个代码库中的每一个 Py_INCREF 和 Py_DECREF 都需要变成原子操作。原子操作比普通整数增量要昂贵得多。Python 3.13 开始附带实验性的 free-threaded 模式，它使用 biased reference counting（偏向引用计数）来降低这种成本：创建对象的线程可以对引用计数进行廉价的非原子更新，只有访问该对象的其他线程才支付原子操作的代价。</p>
<h2>映射回 Wilson：全景图</h2>
<p>每一种现代垃圾回收器都可以映射回 Wilson 在 1992 年描述的两个家族。它们之间的区别在于关于如何最小化停顿、处理并发以及高效管理内存的工程决策。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/garbage-collectors-deep-dive-17.png" alt="" /></p>
<p>从这一对比中可以观察到几点：</p>
<p><strong>Wilson 的追踪式家族在服务器运行时占据主导地位。</strong> 引用计数用于 Swift、Python 和 Rust 的 Arc，但对于具有高分配速率的托管运行时，追踪式收集器是标准做法。循环引用问题无论如何都需要补充追踪步骤，这增加了复杂性，且无法消除每次修改时的引用计数开销。</p>
<p><strong>分代收集除 Go 以外随处可见。</strong> Java 重度利用了分代假说。Python 的循环检测器使用了三代。Go 最初选择不使用分代收集，因为跨代指针写屏障的开销对 Go 的典型工作负载来说不划算。这种情况可能正在改变：最近的 Go 版本中已经开发了实验性的分代支持。</p>
<p><strong>Compaction (整理) vs No compaction 是一个真正的设计分歧点。</strong> Java 收集器进行整理，这允许 bump-pointer 分配（非常快）并消除碎片。Go 不整理，这意味着它永远不需要更新指向已移动对象的指针（更简单的写屏障，无需读屏障以保证正确性）。Go 通过大小类分配器（size-class allocator）来补偿。这是经典的 Wilson 权衡：拷贝和整理收集器以内存开销和指针更新成本换取分配速度和碎片消除。</p>
<p><strong>ZGC 的有色指针是 Wilson 指针标记 (pointer-tagging) 思想的现代实现。</strong> Wilson 提到过在指针中使用位来存储 GC 元数据。ZGC 将此进一步发展，将标记状态、重映射状态和终结状态直接嵌入 64 位指针。在每次指针加载时检查这些位的加载屏障是 ZGC 为亚毫秒级停顿支付的代价。</p>
<p><strong>基本问题从未改变。</strong> 从 roots 开始追踪，标记存活内容，回收其余部分。自 1960 年以来的所有发展都是对 McCarthy 原始洞察的工程改进。</p>
<h2>参考资料</h2>
<ul>
<li><a href="https://dl.acm.org/doi/10.1145/367177.367199">McCarthy, J. (1960). Recursive functions of symbolic expressions and their computation by machine, Part I</a></li>
<li><a href="https://www.cs.rice.edu/~javaplt/311/Readings/wilson92uniprocessor.pdf">Wilson, P. R. (1992). Uniprocessor Garbage Collection Techniques. IWMM ‘92</a></li>
<li><a href="https://tip.golang.org/doc/gc-guide">A Guide to the Go Garbage Collector</a></li>
<li><a href="https://go.dev/blog/ismmkeynote">Getting to Go: The Journey of Go’s Garbage Collector</a></li>
<li><a href="https://github.com/golang/proposal/blob/master/design/17503-eliminate-rescan.md">Proposal: Eliminate STW stack re-scanning &#8211; Austin Clements (2016)</a></li>
<li><a href="https://www.oracle.com/technetwork/tutorials/tutorials-1876574.html">Java Garbage Collection: The G1 Garbage Collector</a></li>
<li><a href="https://openjdk.org/jeps/333">ZGC: The Z Garbage Collector &#8211; JEP 333</a></li>
<li><a href="https://openjdk.org/jeps/439">Generational ZGC &#8211; JEP 439</a></li>
<li><a href="https://peps.python.org/pep-0442/">PEP 442: Safe object finalization</a></li>
</ul>
<hr />
<p><strong>你的“停顿”时刻</strong></p>
<p>GC 的艺术在于平衡。在你的开发生涯中，是否遇到过因为 GC 停顿导致的生产事故？你是倾向于 Go 的极致低延迟，还是 Java G1GC 的高吞吐？<br />
欢迎在评论区分享你的调优经历或吐槽！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p><strong>原「Gopher部落」已重装升级为「Go &amp; AI 精进营」知识星球，快来加入星球，开启你的技术跃迁之旅吧！</strong></p>
<p>我们致力于打造一个高品质的 <strong>Go 语言深度学习</strong> 与 <strong>AI 应用探索</strong> 平台。在这里，你将获得：</p>
<ul>
<li><strong>体系化 Go 核心进阶内容:</strong> 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏，夯实你的 Go 内功。</li>
<li><strong>前沿 Go+AI 实战赋能:</strong> 紧跟时代步伐，学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等，掌握 AI 时代新技能。 </li>
<li><strong>星主 Tony Bai 亲自答疑:</strong> 遇到难题？星主第一时间为你深度解析，扫清学习障碍。</li>
<li><strong>高活跃 Gopher 交流圈:</strong> 与众多优秀 Gopher 分享心得、讨论技术，碰撞思想火花。</li>
<li><strong>独家资源与内容首发:</strong> 技术文章、课程更新、精选资源，第一时间触达。</li>
</ul>
<p>衷心希望「Go &amp; AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚，享受技术精进的快乐！欢迎你的加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-and-ai-tribe-zsxq-small-card.jpg" alt="img{512x368}" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/07/garbage-collectors-deep-dive/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>AI 编程时代，我挖出了一本 1999 年的“删库跑路”指南</title>
		<link>https://tonybai.com/2026/04/06/how-to-write-unmaintainable-code/</link>
		<comments>https://tonybai.com/2026/04/06/how-to-write-unmaintainable-code/#comments</comments>
		<pubDate>Mon, 06 Apr 2026 00:27:24 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[AIProgramming]]></category>
		<category><![CDATA[AI编程]]></category>
		<category><![CDATA[ArchitectureDesign]]></category>
		<category><![CDATA[ClaudeCode]]></category>
		<category><![CDATA[CodeAudit]]></category>
		<category><![CDATA[CodeReuse]]></category>
		<category><![CDATA[Codereview]]></category>
		<category><![CDATA[CodeStandards]]></category>
		<category><![CDATA[Codex]]></category>
		<category><![CDATA[DefensiveProgramming]]></category>
		<category><![CDATA[EmptyInterface]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[gofmt]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[GoLanguage]]></category>
		<category><![CDATA[Go语言]]></category>
		<category><![CDATA[InterfaceDesign]]></category>
		<category><![CDATA[LegacyCode]]></category>
		<category><![CDATA[ReverseEngineering]]></category>
		<category><![CDATA[SoftwareEngineering]]></category>
		<category><![CDATA[TechnicalDebt]]></category>
		<category><![CDATA[TypeSafety]]></category>
		<category><![CDATA[UnmaintainableCode]]></category>
		<category><![CDATA[不可维护代码]]></category>
		<category><![CDATA[代码复用]]></category>
		<category><![CDATA[代码审查]]></category>
		<category><![CDATA[代码审计]]></category>
		<category><![CDATA[代码规范]]></category>
		<category><![CDATA[反向编程]]></category>
		<category><![CDATA[屎山]]></category>
		<category><![CDATA[技术债务]]></category>
		<category><![CDATA[接口设计]]></category>
		<category><![CDATA[架构设计]]></category>
		<category><![CDATA[空接口]]></category>
		<category><![CDATA[类型安全]]></category>
		<category><![CDATA[软件工程]]></category>
		<category><![CDATA[防御性编程]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6150</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/06/how-to-write-unmaintainable-code 大家好，我是Tony Bai。 在这个由 Claude、GPT、Gemini等大模型定义的 2026 年，我们似乎已经习惯了 AI 那种近乎“洁癖”的编码风格：优雅的接口设计、滴水不漏的错误处理、以及永远对齐的工整格式。 AI 正在用它那冰冷的、毫无感情的逻辑，将软件工程推向一个前所未有的标准化时代。 但就在前几天，我在一个尘封的互联网角落里，挖出了一本写于 1999 年的上古奇文——《How To Write Unmaintainable Code》（如何编写不可维护的代码）。 这篇文章的作者 Roedy Green，怀着一种极其黑色幽默的极客精神，手把手教导当年的 Java 程序员们，如何写出能让“接盘侠”当场崩溃、从而保证自己“终身就业”的屎山代码。 当我用 AI 时代的眼光，去重新审视这本 27 年前的“反向圣经”时，我感到既荒谬又亲切。它就像一面镜子，照出了在没有 gofmt、没有 AI、没有 Claude Code 的“古法编程”时代，我们曾如何野蛮生长，以及 Go 语言在设计之初，就已经用多么前瞻性的眼光，封印了那些曾经肆虐一时的“魔鬼”。 今天，就让我们开启一场技术考古之旅，用现代 Go 语言，来“复刻”一下这些差点失传的“防御性”编程之术。 底层哲学：把你的同事想象成一个“管中窥豹”的傻子 Roedy Green 在开篇就点明了核心：维护者看代码，就像通过一个卫生纸筒的中心在看世界，视野极其狭窄。 你的任务，就是让他永远无法拼凑出完整的画面。 古法复刻：让同事“提刀来见”的 骚操作 命名之罪 用 l 冒充 1，用 O 冒充 0：利用字体的模糊性，制造视觉混乱。 [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/how-to-write-unmaintainable-code-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/06/how-to-write-unmaintainable-code">本文永久链接</a> &#8211; https://tonybai.com/2026/04/06/how-to-write-unmaintainable-code</p>
<p>大家好，我是Tony Bai。</p>
<p>在这个由 Claude、GPT、Gemini等大模型定义的 2026 年，我们似乎已经习惯了 AI 那种近乎“洁癖”的编码风格：优雅的接口设计、滴水不漏的错误处理、以及永远对齐的工整格式。</p>
<p>AI 正在用它那冰冷的、毫无感情的逻辑，将软件工程推向一个前所未有的标准化时代。</p>
<p>但就在前几天，我在一个尘封的互联网角落里，挖出了一本写于 1999 年的上古奇文——<strong>《<a href="https://www.doc.ic.ac.uk/%7Esusan/475/unmain.html">How To Write Unmaintainable Code</a>》（如何编写不可维护的代码）</strong>。</p>
<p>这篇文章的作者 Roedy Green，怀着一种极其黑色幽默的极客精神，手把手教导当年的 Java 程序员们，如何写出能让“接盘侠”当场崩溃、从而保证自己“终身就业”的屎山代码。</p>
<p>当我用 AI 时代的眼光，去重新审视这本 27 年前的“反向圣经”时，我感到既荒谬又亲切。它就像一面镜子，照出了在没有 gofmt、没有 AI、没有 Claude Code 的“古法编程”时代，我们曾如何野蛮生长，以及 Go 语言在设计之初，就已经用多么前瞻性的眼光，封印了那些曾经肆虐一时的“魔鬼”。</p>
<p>今天，就让我们开启一场技术考古之旅，用现代 Go 语言，来“复刻”一下这些差点失传的“防御性”编程之术。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-software-engineering-qr.png" alt="" /></p>
<h2>底层哲学：把你的同事想象成一个“管中窥豹”的傻子</h2>
<p>Roedy Green 在开篇就点明了核心：维护者看代码，就像通过一个卫生纸筒的中心在看世界，视野极其狭窄。</p>
<p>你的任务，就是让他永远无法拼凑出完整的画面。</p>
<h2>古法复刻：让同事“提刀来见”的 骚操作</h2>
<h3>命名之罪</h3>
<ol>
<li><strong>用 l 冒充 1，用 O 冒充 0</strong>：利用字体的模糊性，制造视觉混乱。<br />
<code>go<br />
// 古法复刻<br />
var l int64 = 11 // 这是 11 还是 1l？<br />
var speed int = O1 // 这是 O1 还是 01？</code></li>
<li><strong>创造极其相似的变量名</strong>：仅通过大小写或一个不显眼的字母进行区分。<br />
<code>go<br />
// 古法复刻<br />
var swimmer, swimner string // 99% 的 Code Review 都会看走眼<br />
var hashTable, HashTable *map[string]int</code></li>
<li><strong>滥用缩写，且不保持一致</strong>：在不同的地方使用同一个单词的不同缩写，让全局搜索彻底失效。<br />
<code>go<br />
// 古法复刻<br />
func GetUserAuth(...) {}<br />
func GetUsrAuthorization(...) {}<br />
var athnClient *Client</code></li>
<li><strong>使用与业务逻辑无关的变量名</strong>：比如，在屏幕上显示“Postal Code”(邮政编码)，但在代码里把它命名为 zip。</li>
<li><strong>在函数名中使用极其抽象的词汇</strong>：比如 HandleIt, ProcessData, DoStuff。让调用者永远猜不透这个函数到底干了什么。</li>
</ol>
<h3>注释之罪</h3>
<ol>
<li><strong>在注释里撒谎</strong>：最简单的一招，改了代码，但不更新注释。</li>
<li><strong>写废话注释</strong>：为每一行显而易见的代码配上同样显而易见的注释，用大量的噪音淹没真正有价值的信息。<br />
<code>go<br />
// 古法复刻<br />
i++ // i plus 1</code></li>
<li><strong>永远不要注释一个变量</strong>：它的单位、取值范围、边界条件，全让维护者自己去猜。</li>
</ol>
<h3>结构之罪</h3>
<ol>
<li><strong>极限压行</strong>：在一行里塞进尽可能多的逻辑，挑战显示器的宽度极限。</li>
<li><strong>深度嵌套</strong>：以能嵌套 10 层以上的 if-else 为荣，坚决不使用 early return或happy path。</li>
<li>
<p><strong>滥用全局变量</strong>：永远不要使用局部变量，把一切都提升为包级变量，让并发的 Goroutine 们去为了争夺它而自相残杀。</p>
<pre><code class="go">// 古法复刻
var tempResult string // 提升为包级变量

func HandleRequestA() {
    tempResult = "result_from_A"
    // ...
}

func HandleRequestB() {
    tempResult = "result_from_B"
    // ...
}
// 当这两个函数并发执行时，一场血案即将发生。
</code></pre>
</li>
<li><strong>复制-粘贴-修改</strong>：当有相似功能时，坚决不抽象，直接复制粘贴。在一个代码库里埋下 5 份只有细微差别的一模一样的代码，等待日后引爆。</li>
<li><strong>一个函数只做一件事？不，一个函数必须干三件事！</strong> 让一个名为 IsValid() 的函数，在校验的同时，偷偷地把数据写入数据库。</li>
</ol>
<h3>Go 语言的反击</h3>
<p>当然原文中，Roedy Green的“骚操作”不止这些。</p>
<p>但其中的一些“防御”手段对今天的Go语言来说，并不生效。</p>
<p>你会发现 Go 语言在设计之初，就已经对这些“手段”进行了“免疫”，比如：</p>
<ul>
<li><strong>关于缩进与格式</strong>：Roedy Green 痛斥当年程序员通过“故意错位”的缩进来制造 if-else 匹配的视觉陷阱。
<ul>
<li><strong>Go 的反击</strong>：对不起，我们有 gofmt。在 Go 的世界里，关于代码格式的“圣战”在第一天就结束了。无论你的代码写得多乱，Ctrl+S 的瞬间，一切都会变得整齐划一。</li>
</ul>
</li>
<li><strong>关于花括号</strong>：原文建议省略非必须的 {}。
<ul>
<li><strong>Go 的反击</strong>：Go 语言强制要求 if, for 后面必须跟 {}，从语法层面彻底消灭了这种的“防御”写法。</li>
</ul>
</li>
</ul>
<h2>现代化的“魔鬼”：用 Go 复刻那些更高级的骚操作</h2>
<p>当然，Go 也不是万能的。很多源自 Java/C++ 时代的“高级骚操作”，在 Go 里依然可以“继续存在”。</p>
<ol>
<li><strong>伪装成构造函数</strong>：<br />
<code>go<br />
// 古法复刻：这个函数名和类型名完全一样，但它不是构造函数！<br />
type User struct{ name string }<br />
func User(name string) { /* ... do something evil */ }</code></li>
<li><strong>滥用 interface{}</strong>：把所有的函数参数都定义成 interface{}，然后在函数内部进行大量的类型断言（Type Assertion）。这能完美地把编译时错误，转化为运行时 panic。</li>
<li><strong>颠倒参数顺序</strong>：定义一个 DrawRectangle(height, width int) 函数。在几个版本之后，神不知鬼不觉地把它改成 DrawRectangle(width, height int)，但函数名保持不变。</li>
<li><strong>魔数（Magic Numbers）</strong>：在代码里硬编码大量的数字 100，但就是不定义一个常量。更高级的玩法是，偶尔用 99 代替 100-1，用 50*2 代替 100。<br />
<code>go<br />
// 古法复刻<br />
if len(users) &gt; 99 { // 这里是 &gt; 99<br />
    // ...<br />
}<br />
for i := 0; i &lt; 100; i++ { // 这里是 &lt; 100<br />
    // ...<br />
}</code></li>
<li><strong>迷惑性的函数重载（Go 版本）</strong>：Go 没有函数重载，但我们可以用“接口”来模拟。<br />
<code>go<br />
// 古法复刻<br />
func Process(input interface{}) {<br />
    switch v := input.(type) {<br />
    case string: // 处理字符串<br />
    case int:    // 处理整数，但逻辑和 string 完全不同<br />
    // ...<br />
    }<br />
}</code><br />
当你的同事传入一个他以为是数字的字符串 “123&#8243; 时，他将收获一个意想不到的结果。</li>
</ol>
<h2>小结：在 AI 时代，我们为什么要回顾“屎山”？</h2>
<p>重温这本 27 年前的“反向圣经”，在今天这个 AI 编程时代，显得格外有意义。</p>
<p>AI 的出现，正在把“编写可维护代码”的门槛，拉到前所未有的低点。一个初级程序员，在 AI 的辅助下，也能写出格式工整、变量命名规范的代码。</p>
<p><strong>但这是否意味着“屎山”将成为历史？</strong></p>
<p>恰恰相反。AI 在解放我们生产力的同时，也正在<strong>“批量化”和“隐蔽化”</strong>地制造着“新时代的屎山”。AI 可能会生成一段逻辑上看似完美，但在高并发下会引发严重数据竞争的代码；它也可能会为了实现一个简单功能，引入一个庞大且带有安全漏洞的第三方库。</p>
<p>这本古老的指南提醒我们：<strong>技术的进步可以消灭“语法层面”的丑陋，但永远无法替代人类工程师在“架构层面”的审美与抉择。</strong></p>
<p>在 AI 时代，我们不再需要像 Roedy Green 那样，靠着“加密代码”来保住饭碗。但我们比以往任何时候，都更需要理解那些“不可维护代码”背后的设计缺陷，从而在 Code Review 中，扮演好 AI 的“最终质检员”角色。</p>
<p>代码的整洁与混乱，终究是一场关于“责任心”的博弈。无论时代如何变迁，这，或许是软件工程永恒的真理。</p>
<p>当然，如果你真的想了解古法编程时代的更多“骚操作”，可以看看Roedy Green的原文：https://www.doc.ic.ac.uk/%7Esusan/475/unmain.html</p>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>在你见过的 Go 项目中，遇到过哪些让你拍案叫绝、或者让你想“提刀来见”的“屎山代码”骚操作？</p>
<p>欢迎在评论区分享你的“开眼”经历！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p>你的Go技能，是否也卡在了“熟练”到“精通”的瓶颈期？</p>
<ul>
<li>想写出更地道、更健壮的Go代码，却总在细节上踩坑？</li>
<li>渴望提升软件设计能力，驾驭复杂Go项目却缺乏章法？</li>
<li>想打造生产级的Go服务，却在工程化实践中屡屡受挫？</li>
</ul>
<p>继《<a href="http://gk.link/a/10AVZ">Go语言第一课</a>》后，我的《<a href="http://gk.link/a/12yGY">Go语言进阶课</a>》终于在极客时间与大家见面了！</p>
<p>我的全新极客时间专栏 《<a href="http://gk.link/a/12yGY">Tony Bai·Go语言进阶课</a>》就是为这样的你量身打造！30+讲硬核内容，带你夯实语法认知，提升设计思维，锻造工程实践能力，更有实战项目串讲。</p>
<p>目标只有一个：助你完成从“Go熟练工”到“Go专家”的蜕变！ 现在就加入，让你的Go技能再上一个新台阶！</p>
<p><img src="https://tonybai.com/wp-content/uploads/course-card/iamtonybai-banner-2.gif" alt="" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/06/how-to-write-unmaintainable-code/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>当AI 榨干了编程所有的乐趣：我不再是程序员，而是“Claude Code”的项目经理</title>
		<link>https://tonybai.com/2026/04/04/the-death-of-coding-joy-in-the-age-of-ai-agents/</link>
		<comments>https://tonybai.com/2026/04/04/the-death-of-coding-joy-in-the-age-of-ai-agents/#comments</comments>
		<pubDate>Sat, 04 Apr 2026 00:45:30 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[Agency]]></category>
		<category><![CDATA[Agents]]></category>
		<category><![CDATA[AIProgramming]]></category>
		<category><![CDATA[AI编程]]></category>
		<category><![CDATA[AutonomousProgramming]]></category>
		<category><![CDATA[CodeGeneration]]></category>
		<category><![CDATA[CodeQuality]]></category>
		<category><![CDATA[Codereview]]></category>
		<category><![CDATA[CodingAgent]]></category>
		<category><![CDATA[DunningKrugerEffect]]></category>
		<category><![CDATA[HandcraftedCode]]></category>
		<category><![CDATA[IdentityDegradation]]></category>
		<category><![CDATA[SoftwareEngineering]]></category>
		<category><![CDATA[SystemArchitecture]]></category>
		<category><![CDATA[TechDividend]]></category>
		<category><![CDATA[TechnicalDebt]]></category>
		<category><![CDATA[VibeCoding]]></category>
		<category><![CDATA[人类主观能动性]]></category>
		<category><![CDATA[代码审查]]></category>
		<category><![CDATA[代码生成]]></category>
		<category><![CDATA[代码质量]]></category>
		<category><![CDATA[手动编码]]></category>
		<category><![CDATA[技术债务]]></category>
		<category><![CDATA[技术红利]]></category>
		<category><![CDATA[智能体]]></category>
		<category><![CDATA[氛围编程]]></category>
		<category><![CDATA[系统架构]]></category>
		<category><![CDATA[自动编程]]></category>
		<category><![CDATA[身份降维]]></category>
		<category><![CDATA[软件工程]]></category>
		<category><![CDATA[达克效应]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6146</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/04/the-death-of-coding-joy-in-the-age-of-ai-agents 大家好，我是Tony Bai。 过去的两年，我们见证了 AI 编程工具从“玩具”到“神器”的进化。从 Copilot 的代码补全，到 Claude Code 的“一句话建站”，再到各种Coding Agent 的“自主开发”，我们写代码的效率被史无前例地拉满了。 但在这场效率的狂欢之下，一股难以言喻的“失落感”和“空虚感”，正在资深程序员群体中悄然蔓延。 就在几天前，Reddit 的 r/webdev 社区（一个拥有 66 万开发者的顶级论坛）上，一位拥有 20 年经验的资深后端工程师发了一篇帖子，标题极其刺眼：《AI has sucked all the fun out of programming》（AI 榨干了编程所有的乐趣）。 他写道： “我曾对自己解决难题、深挖源码的能力无比自豪。但自从 Claude Code 变得越来越强，我感觉自己不再是一个程序员，更像是一个项目经理，每天管理着一个叫 Claude Code 的中高级外包。我交付功能的速度比以往任何时候都快，但内心却无比空虚。这些没有灵魂的特性，我无法再把它们看作是我的创造。” “AI 让我变得极度懒惰，它彻底摧毁了我作为一个优秀工程师、甚至一个人类的价值。我希望它从未被发明过。” 这篇充满“怨气”的帖子，像一块巨石砸入了平静的湖面，瞬间引爆了整个社区。短短一天，帖子收获了 1500+ 的高赞和近 400 条评论。无数开发者涌入评论区，分享着自己在 AI 时代相似的困惑、挣扎与幻灭。 今天，我们就来扒开这场顶级社区的“赛博哀悼会”，看看当 AI 剥夺了编程最后的“手艺活”时，我们这些“数字工匠”，到底失去了什么？ 身份的剥夺：从“创造者”到“代码审查员” 在评论区，点赞最高的一条回复，只用了一句话，就说出了所有人的心声： [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/the-death-of-coding-joy-in-the-age-of-ai-agents-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/04/the-death-of-coding-joy-in-the-age-of-ai-agents">本文永久链接</a> &#8211; https://tonybai.com/2026/04/04/the-death-of-coding-joy-in-the-age-of-ai-agents</p>
<p>大家好，我是Tony Bai。</p>
<p>过去的两年，我们见证了 AI 编程工具从“玩具”到“神器”的进化。从 Copilot 的代码补全，到 <a href="https://mp.weixin.qq.com/s/NWYyq6LV3WV08lJloK0OZg">Claude Code</a> 的“一句话建站”，再到各种Coding Agent 的“自主开发”，我们写代码的效率被史无前例地拉满了。</p>
<p><strong>但在这场效率的狂欢之下，一股难以言喻的“失落感”和“空虚感”，正在资深程序员群体中悄然蔓延。</strong></p>
<p>就在几天前，Reddit 的 r/webdev 社区（一个拥有 66 万开发者的顶级论坛）上，一位拥有 20 年经验的资深后端工程师发了一篇帖子，标题极其刺眼：<strong>《<a href="https://www.reddit.com/r/webdev/comments/1s6mtt7/ai_has_sucked_all_the_fun_out_of_programming/">AI has sucked all the fun out of programming</a>》（AI 榨干了编程所有的乐趣）。</strong></p>
<p>他写道：</p>
<blockquote>
<p>“我曾对自己解决难题、深挖源码的能力无比自豪。但自从 Claude Code 变得越来越强，我感觉自己不再是一个程序员，更像是一个项目经理，每天管理着一个叫 Claude Code 的中高级外包。我交付功能的速度比以往任何时候都快，但内心却无比空虚。这些没有灵魂的特性，我无法再把它们看作是我的创造。”</p>
<p>“AI 让我变得极度懒惰，它彻底摧毁了我作为一个优秀工程师、甚至一个人类的价值。我希望它从未被发明过。”</p>
</blockquote>
<p>这篇充满“怨气”的帖子，像一块巨石砸入了平静的湖面，瞬间引爆了整个社区。短短一天，帖子收获了 1500+ 的高赞和近 400 条评论。无数开发者涌入评论区，分享着自己在 AI 时代相似的困惑、挣扎与幻灭。</p>
<p>今天，我们就来扒开这场顶级社区的“赛博哀悼会”，看看当 AI 剥夺了编程最后的“手艺活”时，我们这些“数字工匠”，到底失去了什么？</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-qr.png" alt="" /></p>
<h2>身份的剥夺：从“创造者”到“代码审查员”</h2>
<p>在评论区，点赞最高的一条回复，只用了一句话，就说出了所有人的心声：</p>
<blockquote>
<p>“是的，是的，是的。而且，审查那些由 AI 生成的、过度工程化的垃圾 PR，简直让人精疲力竭。”</p>
</blockquote>
<p>这精准地概括了资深程序员们失落感的第一个根源：<strong>身份的降维</strong>。</p>
<p>在没有 AI 的时代，我们是“创造者”。我们享受的是从零开始，将复杂的业务逻辑，通过一行行精巧的代码，构建成一个优雅系统的过程。那种“庖丁解牛”般的掌控感和心流体验，是支撑我们熬过无数个加班夜的精神支柱。</p>
<p>而现在呢？</p>
<p>AI 成了那个大刀阔斧的“创造者”，它可以在几分钟内生成 10 个文件、成千上万行代码。而我们，这些曾经的“建筑师”，<strong>被迫降级成了一个卑微的“代码审查员（Code Reviewer）”</strong>。</p>
<p>我们的日常工作，不再是“如何巧妙地设计一个接口”，而是“如何在这堆由 AI 生成的、看似完美却隐藏着无数暗雷的代码里，找出那个该死的 Bug”。</p>
<p>一位开发者形容这种感觉就像“吃屎”：</p>
<blockquote>
<p>“重构一小段代码，就像在菜里加点盐，很有趣。但如果让你 9 点到 5 点都在重构 AI 生成的屎山，那就完全是另一回事了。”</p>
</blockquote>
<h2>学习的终结：当“挣扎”的权利被剥夺</h2>
<p>除了身份的降维，更让开发者们感到恐惧的，是<strong>“学习感的丧失”</strong>。</p>
<p>一位只有 2 年经验的前端开发者的评论获得了 123 个高赞：</p>
<blockquote>
<p>“AI 确实让我变快了。但有时我感觉，我跳过了那些本该挣扎和学习的部分。而正是那些挣扎，才让知识真正刻进我的脑子里。”</p>
</blockquote>
<p>这说出了一个残酷的真相：<strong>人类的学习，本质上是一个伴随着痛苦和摩擦的过程。</strong></p>
<p>当你为了一个 Bug 熬了三个小时，翻遍了 Stack Overflow，最后在某个不起眼的角落找到解决方案时，你对这个 Bug 的理解是刻骨铭心的。</p>
<p>但现在，你只需要把报错信息扔给 Claude Code，它会在 3 秒钟内给你正确答案。</p>
<p>效率是提升了，但你的大脑也失去了构建深度知识模型（Mental Models）的机会。你成了知识的“搬运工”，而不是“内化者”。</p>
<p>更可怕的是，这种趋势正在从个人蔓延到团队，甚至威胁到新人的成长路径。</p>
<p>评论区的一位开发者也分享了他的遭遇：</p>
<blockquote>
<p>“我们是做嵌入式开发的，AI 很多时候根本不懂底层。但我们的经理对 AI 产生了宗教般的狂热，他强迫我们必须用 AI。如果我们不用，他就会 visibly upset（肉眼可见地不爽）；如果我们用了，然后报告 AI 出了问题，他会立刻假定是我们用错了，而不是 AI 的问题。”</p>
</blockquote>
<p>这种来自管理层的“AI 迷信”，正在让那些真正懂技术的专家感到心寒。当你的老板拿着 ChatGPT 的一段胡言乱语来质疑你的专业判断时，技术尊严的丧失，远比失去乐趣更令人痛苦。</p>
<h2>正在分裂的社区：效率派 vs 手艺派</h2>
<p>当然，也并非所有人都对 AI 感到悲观。评论区同样出现了鲜明的“效率派”阵营。</p>
<p>一位拥有 27 年经验的资深开发者表达了截然不同的看法：</p>
<blockquote>
<p>“我反而觉得 AI 增强了我的能力。我依然负责掌控项目的整体架构，AI 只是帮我处理那些烦人的、重复的体力活。这就像我有了一个能完美听懂我话的初级开发人员，而且他永远不会抱怨。”</p>
</blockquote>
<p>另一位开发者则将这种转变描述为角色的升维：</p>
<blockquote>
<p>“乐趣转移了，但没有消失。我们团队的人类现在负责所有的架构决策，AI 负责具体的实现。创造性的工作依然存在——它从‘如何写好这个函数’，变成了‘如何设计好这个系统’。我们从‘砖瓦工’，变成了‘建筑师’。”</p>
</blockquote>
<p>这两种截然不同的声音，揭示了 AI 时代开发者社区正在经历的一场剧烈的身份分化：</p>
<ul>
<li><strong>手艺派</strong>：他们热爱编码本身，享受那种与代码“人剑合一”的创造快感。对他们而言，AI 剥夺了这个过程。</li>
<li><strong>效率派（或架构派）</strong>：他们更享受从宏观层面掌控系统的乐趣，将编码视为一种实现目标的手段，而非目的。对他们而言，AI 是解放他们生产力的“外骨骼”。</li>
</ul>
<p>这两种观点没有对错，它仅仅反映了不同性格的开发者，在面对一场史无前例的生产关系变革时，所产生的自然分化。</p>
<h2>出路何在？：夺回“原子化”的掌控力</h2>
<p>在这场关于“乐趣与灵魂”的大讨论中，我们依然能找到一些极具建设性的生存法则。</p>
<p><strong>第一，坚决捍卫“人类最终解释权”。</strong></p>
<p>AI 可以生成，但你必须成为那个拥有“一票否决权”的最终审计者。正如一位开发者所言：</p>
<blockquote>
<p>“我正在无视所有关于‘再不拥抱 AI 就会被淘汰’的末日预言。我只把它当成一个强化版的搜索引擎。如果未来真的只需要一批不懂底层原理的‘提示词操作员’，那我的工作反正也变得毫无意义了。但如果未来依然需要懂底层的人，那我的处境绝对比那些‘氛围编码’了好几年的人强得多。”</p>
</blockquote>
<p><strong>第二，主动创造“无 AI 日（Zero AI Day）”。</strong></p>
<p>另外一位开发者的建议获得了很多人的认同：</p>
<blockquote>
<p>“为了对抗这种侵蚀，我每周会选定一天作为我的‘无 AI 日’。在那一天，我禁止自己使用任何 AI 工具。这种感觉非常自由。”</p>
</blockquote>
<p>这就像健身中的“欺骗餐”，它能让你重新找回对代码最原始的“手感”。</p>
<p><strong>第三，把 AI 当作“副驾驶”，而不是“自动驾驶”。</strong></p>
<p>真正的老司机，绝不会在高速上双手离开方向盘。他们会利用 AI 去处理那些最耗时、最没有创造性的部分：写单元测试、生成 OpenAPI 文档、转换数据格式。</p>
<p>而在核心的业务逻辑和架构设计上，<strong>亲手去写，去感受系统的“摩擦力（Friction）”，去构建你脑海中那张独一无二的架构蓝图。</strong></p>
<h2>小结：从“多巴胺”到“内啡肽”</h2>
<p>AI 的出现，极大地满足了我们对“即时反馈”的多巴胺式快感：敲一句话，代码就出来了。</p>
<p>但真正的编程乐趣，那种来自于深度思考、解决难题后获得的、持久而宁静的成就感，属于“内啡肽”。</p>
<p>AI 正在用廉价的多巴胺，稀释我们获取内啡肽的能力。</p>
<p>我们不必为此感到绝望。正如工业革命没有消灭所有手工艺人，反而催生了更昂贵的“高级定制”一样。</p>
<p>当 AI 能够批量生产千篇一律的“预制菜”代码时，那些依然能够亲手雕琢出艺术品级架构的“米其林大厨”，其价值将不降反升。</p>
<p>问题的关键在于，在这场大浪淘沙中，你，是选择成为流水线上一颗随时可被替换的螺丝钉，还是那个手握最终菜谱的顶级大厨？</p>
<hr />
<p><strong>今日互动探讨：</strong></p>
<p>在使用 AI 编程后，你的“编程乐趣”是增加了还是减少了？你觉得 AI 帮你完成的最有价值和最没有价值的工作分别是什么？</p>
<p>欢迎在评论区分享你的真实感受！</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p><strong>原「Gopher部落」已重装升级为「Go &amp; AI 精进营」知识星球，快来加入星球，开启你的技术跃迁之旅吧！</strong></p>
<p>我们致力于打造一个高品质的 <strong>Go 语言深度学习</strong> 与 <strong>AI 应用探索</strong> 平台。在这里，你将获得：</p>
<ul>
<li><strong>体系化 Go 核心进阶内容:</strong> 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏，夯实你的 Go 内功。</li>
<li><strong>前沿 Go+AI 实战赋能:</strong> 紧跟时代步伐，学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等，掌握 AI 时代新技能。 </li>
<li><strong>星主 Tony Bai 亲自答疑:</strong> 遇到难题？星主第一时间为你深度解析，扫清学习障碍。</li>
<li><strong>高活跃 Gopher 交流圈:</strong> 与众多优秀 Gopher 分享心得、讨论技术，碰撞思想火花。</li>
<li><strong>独家资源与内容首发:</strong> 技术文章、课程更新、精选资源，第一时间触达。</li>
</ul>
<p>衷心希望「Go &amp; AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚，享受技术精进的快乐！欢迎你的加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-and-ai-tribe-zsxq-small-card.jpg" alt="img{512x368}" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/04/the-death-of-coding-joy-in-the-age-of-ai-agents/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>REST 已老，AI 时代的智能体需要怎样的 API？</title>
		<link>https://tonybai.com/2026/04/03/agentic-api-in-action/</link>
		<comments>https://tonybai.com/2026/04/03/agentic-api-in-action/#comments</comments>
		<pubDate>Fri, 03 Apr 2026 00:32:35 +0000</pubDate>
		<dc:creator>bigwhite</dc:creator>
				<category><![CDATA[技术志]]></category>
		<category><![CDATA[ActionDriven]]></category>
		<category><![CDATA[AgentExperience]]></category>
		<category><![CDATA[AgenticAPI]]></category>
		<category><![CDATA[AIAgent]]></category>
		<category><![CDATA[APIAggregation]]></category>
		<category><![CDATA[APIDesign]]></category>
		<category><![CDATA[API聚合]]></category>
		<category><![CDATA[API设计]]></category>
		<category><![CDATA[AX]]></category>
		<category><![CDATA[BusinessLogic]]></category>
		<category><![CDATA[CRUD]]></category>
		<category><![CDATA[FineGrainedPermissions]]></category>
		<category><![CDATA[Go]]></category>
		<category><![CDATA[Golang]]></category>
		<category><![CDATA[HCI]]></category>
		<category><![CDATA[HeadlessFirst]]></category>
		<category><![CDATA[IntentDriven]]></category>
		<category><![CDATA[json]]></category>
		<category><![CDATA[LLM]]></category>
		<category><![CDATA[M2M]]></category>
		<category><![CDATA[MCP]]></category>
		<category><![CDATA[Microservices]]></category>
		<category><![CDATA[OpenAPI]]></category>
		<category><![CDATA[RESTFUL]]></category>
		<category><![CDATA[softwarearchitecture]]></category>
		<category><![CDATA[StateConsistency]]></category>
		<category><![CDATA[ToolCalling]]></category>
		<category><![CDATA[业务逻辑]]></category>
		<category><![CDATA[人机交互]]></category>
		<category><![CDATA[动作驱动]]></category>
		<category><![CDATA[大语言模型]]></category>
		<category><![CDATA[工具调用]]></category>
		<category><![CDATA[微服务]]></category>
		<category><![CDATA[意图驱动]]></category>
		<category><![CDATA[无头模式]]></category>
		<category><![CDATA[智能体]]></category>
		<category><![CDATA[智能体体验]]></category>
		<category><![CDATA[机机交互]]></category>
		<category><![CDATA[模型上下文协议]]></category>
		<category><![CDATA[状态一致性]]></category>
		<category><![CDATA[细粒度权限]]></category>
		<category><![CDATA[软件架构]]></category>

		<guid isPermaLink="false">https://tonybai.com/?p=6139</guid>
		<description><![CDATA[本文永久链接 &#8211; https://tonybai.com/2026/04/03/agentic-api-in-action 大家好，我是Tony Bai。 在过去的十几年里，如果你问任何一位后端工程师：“我们应该如何设计 API？”得到的答案几乎是统一的：RESTful。 我们将世界抽象为一个个“资源（Resources）”，用名词来命名 URI（比如 /users, /orders），用 HTTP 动词（GET, POST, PUT, DELETE）来表达对这些资源的操作。这套基于 CRUD（创建、读取、更新、删除）的法则，优雅地统治了移动互联网和微服务时代。 然而，时代变了。 当我们步入 AI 时代，尤其是当各种大语言模型（LLM）驱动的智能体（AI Agent）开始接管我们的软件系统时，一个尖锐的矛盾浮出水面：这群“硅基同事”在面对我们精心设计的 REST API 时，表现得像个无所适从的笨蛋。 今天，作为本专栏的开篇，我想和你探讨一个极其现实的工程问题：为什么在 AI 时代，统治后端十年的 REST 架构正在失效？我们又该如何为 AI 智能体设计下一代接口——Agentic API？ AI 智能体的“认知障碍”：REST API 的三大罪状 为了理解 REST 的局限性，我们不妨先来做个角色互换。 假设你现在不是一个人类工程师，而是一个被赋予了任务的 AI Agent。你的主人对你说：“帮我把昨天那个发错的订单取消掉，并给客户退款。” 作为 Agent，你拥有一个极其强大的大脑（比如 GPT-5.x 或 DeepSeek-V3.x），并且你被授权访问公司内部的订单系统 API。你兴冲冲地查看了该系统的 Swagger 文档，看到了以下几个端点： GET /orders/{id} (获取订单) PUT [...]]]></description>
			<content:encoded><![CDATA[<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-1.png" alt="" /></p>
<p><a href="https://tonybai.com/2026/04/03/agentic-api-in-action">本文永久链接</a> &#8211; https://tonybai.com/2026/04/03/agentic-api-in-action</p>
<p>大家好，我是Tony Bai。</p>
<p>在过去的十几年里，如果你问任何一位后端工程师：“我们应该如何设计 API？”得到的答案几乎是统一的：<strong>RESTful</strong>。</p>
<p>我们将世界抽象为一个个“资源（Resources）”，用名词来命名 URI（比如 /users, /orders），用 HTTP 动词（GET, POST, PUT, DELETE）来表达对这些资源的操作。这套基于 CRUD（创建、读取、更新、删除）的法则，优雅地统治了移动互联网和微服务时代。</p>
<p>然而，时代变了。</p>
<p>当我们步入 AI 时代，尤其是当各种大语言模型（LLM）驱动的<strong>智能体（AI Agent）</strong>开始接管我们的软件系统时，一个尖锐的矛盾浮出水面：<strong>这群“硅基同事”在面对我们精心设计的 REST API 时，表现得像个无所适从的笨蛋。</strong></p>
<p>今天，作为本专栏的开篇，我想和你探讨一个极其现实的工程问题：<strong>为什么在 AI 时代，统治后端十年的 REST 架构正在失效？我们又该如何为 AI 智能体设计下一代接口——Agentic API？</strong></p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-qr.png" alt="" /></p>
<h2>AI 智能体的“认知障碍”：REST API 的三大罪状</h2>
<p>为了理解 REST 的局限性，我们不妨先来做个角色互换。</p>
<p>假设你现在不是一个人类工程师，而是一个被赋予了任务的 AI Agent。你的主人对你说：“帮我把昨天那个发错的订单取消掉，并给客户退款。”</p>
<p>作为 Agent，你拥有一个极其强大的大脑（比如 GPT-5.x 或 DeepSeek-V3.x），并且你被授权访问公司内部的订单系统 API。你兴冲冲地查看了该系统的 Swagger 文档，看到了以下几个端点：</p>
<ul>
<li>GET /orders/{id} (获取订单)</li>
<li>PUT /orders/{id} (更新订单)</li>
<li>DELETE /orders/{id} (删除订单)</li>
<li>POST /refunds (创建退款)</li>
</ul>
<p>这时候，你的“认知障碍”出现了。</p>
<h3>罪状一：意图的丢失</h3>
<p>你要“取消订单”，但在 REST 的世界里，并没有一个叫“取消”的操作。</p>
<p>你应该调用 DELETE /orders/{id} 吗？如果你真的这么做了，你可能就把这条订单的物理记录从数据库里抹掉了，这在真实的电商系统中是灾难性的（通常我们需要软删除或者状态流转）。</p>
<p>还是说，你应该调用 PUT /orders/{id}，并在 JSON Payload 里传一个 {“status”: “CANCELLED”}？这听起来合理一些，但如果你传的是 {“status”: “DELETED”} 呢？API 会报错吗？</p>
<p><strong>REST API 强迫调用者去猜测后端的业务逻辑映射。</strong> 对于人类开发者，我们可以通过阅读厚厚的 API 接入文档，或者直接去问写这个接口的同事来澄清。但对于 AI Agent，它只能基于常识去“猜”。当 AI 开始猜你的系统设计时，就是灾难的开始。</p>
<h3>罪状二：原子性与编排的噩梦</h3>
<p>更糟糕的是，主人的任务是“取消订单并退款”。</p>
<p>在 REST 架构下，订单资源（/orders）和退款资源（/refunds）通常是分离的。AI Agent 必须自己完成以下编排：</p>
<ol>
<li>调用 PUT /orders/{id} 将状态改为 CANCELLED。</li>
<li>解析步骤 1 的响应，确认成功。</li>
<li>调用 POST /refunds，并小心翼翼地把订单的金额、支付流水号等信息拼装到 Payload 中。</li>
</ol>
<p>如果步骤 1 成功了，但步骤 2 因为网络超时失败了怎么办？AI Agent 需要具备复杂的错误恢复机制和分布式事务处理能力（比如发起撤销操作）。我们把极其复杂的系统状态一致性问题，推给了客户端（AI）。</p>
<h3>罪状三：权限的过度宽泛</h3>
<p>为了让 AI 能够完成上述操作，你需要给它分配什么样的权限？</p>
<p>在传统的 OAuth 2.0 体系中，你可能不得不给它 order:write 和 refund:write 权限。这意味着，这个 AI Agent 不仅能取消订单，它还能修改订单金额，甚至能随意发起退款！</p>
<p><strong>REST API 以“资源”为粒度划分权限，这对于非确定性的 AI 来说，权限敞口太大了。</strong> 我们真正想给 AI 的权限是“仅限取消特定状态的订单”，但这在传统的 REST 模型中极难优雅地表达。</p>
<h2>破局之道：从“面向资源”到“面向任务”</h2>
<p>面对上述痛点，业界最近非常流行一种解决方案：<strong>让 AI 使用工具（Tool Calling / Function Calling）。</strong></p>
<p>比如 Anthropic 推出的 MCP（Model Context Protocol）协议，它的核心思想是：在 AI 和系统之间架设一个中间件（MCP Server），将系统的能力包装成一个个具体的 Tool（工具）暴露给 AI。</p>
<p>这确实缓解了部分问题，AI 可以直接调用名为 cancel_order_and_refund 的工具了。<strong>但请注意，这治标不治本。</strong></p>
<p>这相当于我们在后端依然写着糟糕的、极难编排的 REST API，然后派人写了一堆中间层胶水代码（Glue Code）来适配 AI。随着系统变得复杂，维护这些“胶水工具”的成本将呈指数级上升，状态同步和权限控制的难题依然存在。</p>
<p><strong>我们真正需要的，是一场后端架构范式的革命：从源头上设计对 AI 友好的 API。</strong></p>
<p>这就是本微专栏要向你隆重介绍的 <strong>Agentic API</strong> 理念。</p>
<p>Agentic API 的核心思想是：<strong>放弃将世界强行扭曲为“资源（名词）”，回归人类和 AI 最自然的交流方式——“任务与意图（动词）”。</strong></p>
<p>我们来看一个对比。</p>
<p><strong>传统 REST API 的思维模式：</strong></p>
<blockquote>
<p>“这里有一个 Order 资源。你可以对它执行 POST, GET, PUT, DELETE。”</p>
</blockquote>
<p><strong>Agentic API 的思维模式：</strong></p>
<blockquote>
<p>“这里有一个业务系统。你可以执行 CANCEL（取消订单）, REFUND（发起退款）, NOTIFY（发送通知）等明确的任务。”</p>
</blockquote>
<p>我们用一张简单的时序图来对比一下这两种模式下，AI Agent 完成“取消并退款”任务的复杂度差异：</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-2.png" alt="" /></p>
<p>在 Agentic API 模式下：</p>
<ol>
<li>意图极其明确：API 端点本身就是一个清晰的动词（或动词组合），AI 不需要猜测 PUT 到底意味着什么。</li>
<li>后端掌控状态：复杂的编排逻辑（改状态、调退款接口、处理分布式事务）被收敛到了后端。后端永远是状态的最终防线。</li>
<li>权限精准控制：我们可以给 AI 颁发一个名为 action:cancel_and_refund 的细粒度 Token，即使 AI 产生幻觉想去改订单金额，也会被 API 网关直接拦截。</li>
</ol>
<h2>实战演练：用 Go 构建你的第一个 Agentic API</h2>
<p>光说不练假把式。接下来，我们将用 Go 语言，从零开始将一个传统的 REST 接口改造为 Agentic API。</p>
<p>假设我们正在开发一个博客系统，我们需要一个接口让 AI 帮我们<strong>“总结一篇文章的核心观点”</strong>。</p>
<h3>项目目录准备</h3>
<p>请确保你已安装 Go 1.21 或以上版本。我们将使用标准库 net/http 来保持代码最简。</p>
<p>创建一个新目录并初始化模块：</p>
<pre><code class="bash">mkdir agentic-api-demo
cd agentic-api-demo
go mod init agentic-demo
touch main.go
</code></pre>
<h3>传统的 RESTful 实现 (反模式)</h3>
<p>在传统的 CRUD 思维下，很多开发者可能会这么设计：</p>
<p>让客户端发送一个 POST /documents/{id}/summary 请求，或者使用一个万能的 PATCH /documents/{id}，带上一个 action=summarize 的字段。</p>
<p>这虽然能工作，但语义不够清晰，扩展性极差（如果明天需要翻译、提取关键字呢？）。</p>
<h3>Agentic API 的实现思路</h3>
<p>在 Agentic API 的设计中，我们提倡使用<strong>明确的动词驱动路由</strong>。针对这种数据处理类的任务，我们可以定义一个 COMPUTE 或 ANALYZE 大类。</p>
<p>让我们在 main.go 中写下这段优雅的代码：</p>
<pre><code class="go">// ch01/agentic-api-demo/main.go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strings"
)

// AgenticRequest 代表了 AI 智能体发来的标准任务请求
type AgenticRequest struct {
    // 明确的意图动作
    Action string json:"action"
    // 动作的目标上下文 (例如文档ID)
    ContextID string json:"context_id"
    // 动作需要的特定参数
    Parameters map[string]interface{} json:"parameters,omitempty"
}

// AgenticResponse 代表了返回给 AI 的标准结构化响应
type AgenticResponse struct {
    Status  string      json:"status" // SUCCESS, FAILED, REQUIRE_CONFIRM
    Result  interface{} json:"result,omitempty"
    Message string      json:"message,omitempty"
}

func main() {
    // 定义一个面向动作的路由前缀
    http.HandleFunc("/api/v1/actions", actionHandler)

    fmt.Println("Agentic API Server started on :8080")
    log.Fatal(http.ListenAndServe(":8080", nil))
}

// actionHandler 充当了“任务调度中心”
func actionHandler(w http.ResponseWriter, r *http.Request) {
    if r.Method != http.MethodPost {
        http.Error(w, "Only POST is allowed for actions", http.StatusMethodNotAllowed)
        return
    }

    var req AgenticRequest
    if err := json.NewDecoder(r.Body).Decode(&amp;req); err != nil {
        sendResponse(w, http.StatusBadRequest, "FAILED", nil, "Invalid JSON payload")
        return
    }

    // 核心：基于 Action (动词) 进行路由分发，而不是基于资源名词
    switch strings.ToUpper(req.Action) {
    case "SUMMARIZE":
        handleSummarize(w, req)
    case "TRANSLATE":
        // handleTranslate(w, req)
        sendResponse(w, http.StatusNotImplemented, "FAILED", nil, "Action TRANSLATE not implemented yet")
    default:
        sendResponse(w, http.StatusBadRequest, "FAILED", nil, fmt.Sprintf("Unknown action: %s", req.Action))
    }
}

// handleSummarize 处理具体的总结任务
func handleSummarize(w http.ResponseWriter, req AgenticRequest) {
    docID := req.ContextID
    if docID == "" {
        sendResponse(w, http.StatusBadRequest, "FAILED", nil, "context_id (Document ID) is required")
        return
    }

    // 解析可选参数 (Agentic API 应该允许 AI 传入控制参数)
    maxLength := 100 // 默认值
    if ml, ok := req.Parameters["max_length"].(float64); ok {
        maxLength = int(ml)
    }

    // 模拟从数据库获取文档并进行总结的复杂逻辑
    log.Printf("Executing SUMMARIZE for doc: %s, max length: %d\n", docID, maxLength)

    // 模拟生成的摘要
    mockSummary := fmt.Sprintf("这是关于文档 %s 的核心总结，长度被限制在 %d 字以内：Agentic API 是未来的趋势。", docID, maxLength)

    // 返回标准化响应
    sendResponse(w, http.StatusOK, "SUCCESS", mockSummary, "Document summarized successfully")
}

// 统一的响应封装助手
func sendResponse(w http.ResponseWriter, statusCode int, status string, result interface{}, message string) {
    w.Header().Set("Content-Type", "application/json")
    w.WriteHeader(statusCode)
    resp := AgenticResponse{
        Status:  status,
        Result:  result,
        Message: message,
    }
    json.NewEncoder(w).Encode(resp)
}
</code></pre>
<h3>运行与验证</h3>
<p>在终端运行该代码：</p>
<pre><code class="bash">go run main.go
</code></pre>
<p>现在，假设你是一个 AI Agent，你决定执行“总结文章”的任务，你可以构造如下清晰的 Payload 发送给后端：</p>
<pre><code class="bash">curl -X POST http://localhost:8080/api/v1/actions \
-H "Content-Type: application/json" \
-d '{
  "action": "SUMMARIZE",
  "context_id": "doc_9527",
  "parameters": {
    "max_length": 50
  }
}'
</code></pre>
<p>你会得到一个标准化的、极易解析的响应：</p>
<pre><code class="json">{
  "status": "SUCCESS",
  "result": "这是关于文档 doc_9527 的核心总结，长度被限制在 50 字以内：Agentic API 是未来的趋势。",
  "message": "Document summarized successfully"
}
</code></pre>
<p><strong>看出区别了吗？</strong></p>
<p>我们建立了一个统一的 /actions 门户。AI 只需要指明它<strong>想做什么（Action: SUMMARIZE）</strong>，针对<strong>什么目标（ContextID: doc_9527）</strong>，以及<strong>有何要求（Parameters）</strong>。</p>
<p>后端完全掌控了路由分发、参数校验和复杂的业务实现。如果你明天需要增加一个“翻译”功能，对于 AI 来说，只是换了一个动词（TRANSLATE），它的交互模式（Schema）<strong>没有任何改变</strong>。这种一致性极大地降低了 AI 的试错成本和代码生成复杂度。</p>
<h2>专栏剧透：我们将如何系统性地驯服 AI 智能体？</h2>
<p>刚才的实战只是开胃菜。要让你的整个微服务集群、成百上千个接口都变成“Agent-Ready（AI 就绪）”，我们需要一套完整的架构方法论。</p>
<p>这就引出了我们这个《<a href="https://mp.weixin.qq.com/mp/appmsgalbum?__biz=MzIyNzM0MDk0Mg==&amp;action=getalbum&amp;album_id=4455138001052172294#wechat_redirect">Agentic API 实战：为 AI 智能体设计下一代接口</a>》微专栏，以及后续的安排。</p>
<p>在接下来的 5 讲中，我将摒弃那些空洞的 AI 概念，从一名后端架构师的视角出发，带你一步步把这套理念落地为真实的生产级能力。所有核心模式都会配备详实的 Go 语言可运行代码。</p>
<p>以下是我们接下来的“作战路线图”：</p>
<ul>
<li><strong>第 02 讲 | 重新定义动作：掌握 ACTION 接口分类法</strong><br />
我们会深入探讨 Agentic API 的“六大核心动词”（获取、计算、交易、集成、编排、通知）。你会学到如何彻底抛弃 CRUD 思维，用 AI 最容易理解的意图来重塑你的路由设计。</li>
<li><strong>第 03 讲 | 语义可发现性：让 AI 自己“读懂”你的系统能力</strong><br />
当你有 100 个接口时，把文档全部喂给 AI 是愚蠢且昂贵的。我们将用 Go 实现一个动态的 DISCOVER 端点，让 AI 能够像人类查字典一样，按需、动态地探索你系统的能力边界和前置条件。</li>
<li><strong>第 04 讲 | OpenAPI 进化：用 Agentic 扩展赋能机器阅读</strong><br />
我们不需要推翻现有的基础设施。这一讲，我将教你如何利用 OpenAPI (Swagger) 的 x- 自定义扩展机制，把“不可逆风险”、“副作用”等业务约束“藏”进标准文档里，让死文档变成 AI 的“行动护栏”。</li>
<li><strong>第 05 讲 | 复杂任务编排：链式调用 (Chaining) 与测试模式 (Dry Run)</strong><br />
这是保证 AI 绝对安全的核心！当 AI 需要连续调用三个接口完成扣款时，如何在网络抖动中保全业务状态？我们将设计基于后端的链式调用，并引入价值连城的“Dry Run（安全演习）”模式，把 AI 犯错的成本降到最低。</li>
<li><strong>第 06 讲 | 演进与落地：如何将现有系统平滑升级为 Agentic API？</strong><br />
现实是骨感的，你的公司里堆满了 5 年前写的陈旧 REST 接口。大结局中，我将演示一种优雅的“代理与适配器（Proxy &amp; Adapter）”架构，教你在不修改任何一行老代码的前提下，为遗留系统穿上“AI 外骨骼”。</li>
</ul>
<p>这是一次从思维方式到工程实现的全面升级。如果你准备好了迎接 AI 带来的自动化红利，并且希望成为团队里那个“最懂如何让机器调接口”的架构师，那么，请扫描下方二维码，紧跟我的步伐。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2026/agentic-api-in-action-qr.png" alt="" /></p>
<h2>本讲小结</h2>
<p>今天，我们站在了一个新时代的起点。</p>
<ol>
<li><strong>认知翻新：</strong> 传统的 RESTful API 围绕着“静态资源（名词）”展开，要求调用方（无论人还是机器）了解系统的内在状态流转。这在 AI 时代变成了沉重的认知负担，导致意图丢失、编排复杂、权限泛滥。</li>
<li><strong>范式转移：</strong> Agentic API 提倡转向“任务驱动（动词）”。API 端点应该直接表达操作意图（如 SUMMARIZE, CANCEL_ORDER），由后端收敛复杂的业务编排和状态管理。</li>
<li><strong>实战起航：</strong> 我们用 Go 构建了一个极简的动作分发网关，展示了如何用统一的结构（Action, Context, Parameters）来响应 AI 智能体的请求。</li>
</ol>
<p>这仅仅是冰山一角。在接下来的专栏中，我们将深入探讨 Agentic API 的骨架：<strong>六大核心 ACTION 分类法</strong>。我们将学习如何让 AI 自动“发现”你的系统能力，如何通过扩展 OpenAPI 规范生成完美的智能体说明书，以及如何设计让 AI 执行复杂连锁任务的“沙盒测试模式”。</p>
<p>世界正在不可逆转地走向自动化，懂 AI 调用的 API 架构师，将成为下个十年最稀缺的资源。我们下一讲见！</p>
<p>本讲涉及的示例代码和脚本可以在<a href="https://github.com/bigwhite/publication/tree/master/micro-column/agentic-api-in-action/ch01">这里</a>下载。</p>
<h2>思考题</h2>
<p>在你的日常开发中，有没有遇到过一个传统的 REST API（比如一个修改用户状态的 PUT 接口），它的内部逻辑其实非常复杂（包含了发邮件、写审计日志、调起其他微服务等操作）？</p>
<p>如果让你用 Agentic API 的思维（动词驱动）重新设计这个接口的访问方式，你会怎么命名这个 Action？它的 Payload 结构会是什么样的？</p>
<p>欢迎在评论区留下你的思考和设计，我会和你一起讨论。</p>
<hr />
<p>还在为“复制粘贴喂AI”而烦恼？我的新专栏 <strong>《<a href="http://gk.link/a/12EPd">AI原生开发工作流实战</a>》</strong> 将带你：</p>
<ul>
<li>告别低效，重塑开发范式</li>
<li>驾驭AI Agent(Claude Code)，实现工作流自动化</li>
<li>从“AI使用者”进化为规范驱动开发的“工作流指挥家”</li>
</ul>
<p>扫描下方二维码，开启你的AI原生开发之旅。</p>
<p><img src="https://tonybai.com/wp-content/uploads/2025/ai-native-dev-workflow-qr.png" alt="" /></p>
<hr />
<p><strong>原「Gopher部落」已重装升级为「Go &amp; AI 精进营」知识星球，快来加入星球，开启你的技术跃迁之旅吧！</strong></p>
<p>我们致力于打造一个高品质的 <strong>Go 语言深度学习</strong> 与 <strong>AI 应用探索</strong> 平台。在这里，你将获得：</p>
<ul>
<li><strong>体系化 Go 核心进阶内容:</strong> 深入「Go原理课」、「Go进阶课」、「Go避坑课」等独家深度专栏，夯实你的 Go 内功。</li>
<li><strong>前沿 Go+AI 实战赋能:</strong> 紧跟时代步伐，学习「Go+AI应用实战」、「Agent开发实战课」、「Agentic软件工程课」、「Claude Code开发工作流实战课」、「OpenClaw实战分享」等，掌握 AI 时代新技能。 </li>
<li><strong>星主 Tony Bai 亲自答疑:</strong> 遇到难题？星主第一时间为你深度解析，扫清学习障碍。</li>
<li><strong>高活跃 Gopher 交流圈:</strong> 与众多优秀 Gopher 分享心得、讨论技术，碰撞思想火花。</li>
<li><strong>独家资源与内容首发:</strong> 技术文章、课程更新、精选资源，第一时间触达。</li>
</ul>
<p>衷心希望「Go &amp; AI 精进营」能成为你学习、进步、交流的港湾。让我们在此相聚，享受技术精进的快乐！欢迎你的加入！</p>
<p><img src="http://image.tonybai.com/img/tonybai/gopher-and-ai-tribe-zsxq-small-card.jpg" alt="img{512x368}" /></p>
<hr />
<p>商务合作方式：撰稿、出书、培训、在线课程、合伙创业、咨询、广告合作。如有需求，请扫描下方公众号二维码，与我私信联系。</p>
<p><img src="http://image.tonybai.com/img/tonybai/iamtonybai-wechat-qr.png" alt="" /></p>
<p style='text-align:left'>&copy; 2026, <a href='https://tonybai.com'>bigwhite</a>. 版权所有. </p>
]]></content:encoded>
			<wfw:commentRss>https://tonybai.com/2026/04/03/agentic-api-in-action/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>
