Linus 的名言要改了:Talk is cheap, show me the Spec

本文永久链接 – https://tonybai.com/2025/12/12/talk-is-cheap-show-me-the-spec

大家好,我是Tony Bai。

在 IT 行业,有一句被奉为圭臬的名言,出自 Linux 之父 Linus Torvalds:

“Talk is cheap, show me the code.”
(废话少说,放码过来。)

在过去的三十年里,这句话是绝对正确的。因为将人类的自然语言逻辑翻译成机器能运行的 C/C++/Go/Java 代码,是一项高难度、高成本的脑力劳动。代码,就是程序员的军功章,是能力的终极证明。

但是,站在 2025 年末的今天,当我们看着 Claude Code 或 Cursor 在几秒钟内生成了数百行逻辑严密、注释清晰的代码时,你是否感觉到了一种微妙的价值观崩塌

如果生产代码变得像呼吸一样简单,那么“Show me the code”还足以证明你的价值吗?

我认为,是时候修正这句话了。在 AI 原生开发时代,新的法则应该是:

“Talk is cheap, show me the Spec.”
(空谈廉价,请给我看你的规范说明书。)

价值倒置:代码的通货膨胀

为什么说 Code 变得 cheap(廉价)了?这符合基本的经济学规律:供需关系

  • 前 AI 时代: 代码的供给受限于程序员的打字速度和脑力带宽。优质代码是稀缺资源。
  • AI 时代: LLM 使得代码的供给趋近于无限。只要你给指令,AI 可以不知疲倦地生成无限吨位的代码。

当一个东西的供给变得无限时,它的价值就会无限趋近于零。

现在,你随便找个实习生,配上 AI 工具,他也能给你 Show 出一大堆 Code。但这些 Code 是垃圾还是黄金?能不能跑?符不符合业务需求?

这取决于“指令”的质量,而不是“代码”本身。

权力的转移:从“实现” 到 “定义”

在传统的软件工程中,权重最高的是 Implementation(实现)。我们推崇那些能搞定复杂算法、能手写汇编的大神。

但在 SDD (Spec-Driven Development) 兴起的当下,权力中心正在向 Definition(定义) 转移。

什么是 Spec (Specification)?

在 AI 时代,请务必注意:Spec 不再是那个单薄的 requirements.txt 或 README.md,它是广义的“全景蓝图(Blueprint)”。

参考业界前沿的 SDD 规范(如 spec-kit / openspec),一个合格的、能让 AI 准确执行的 Spec,通常包含 “SDD 三件套”

  1. The Context (语境/需求) —— spec.md

    • 定义 “What & Why”:业务逻辑是什么?输入输出接口定义(Interface)是什么?
    • 核心要素:用户故事、API 契约、非功能性约束(性能/安全)、领域知识(Domain Context)。
  2. The Strategy (策略/架构) —— plan.md

    • 定义 “How”:为了实现上述需求,我们需要修改哪些文件?数据流怎么走?
    • 核心要素:技术栈选择、文件变更拓扑图、伪代码(Pseudocode)、架构决策记录(ADR)。
  3. The Execution (执行/进度) —— tasks.md

    • 定义 “Steps”:将大象装进冰箱分几步?
    • 核心要素:原子化的任务清单(Atomic Checklist)。AI 每做完一步,就勾选一项。这能极大地降低 AI 的“遗忘率”和“幻觉率”。

Code 只是这套 Spec 的“编译产物”。

这就好比建筑行业:当砌砖机器人都普及了,“砌砖”这个动作就不值钱了。值钱的是包含效果图(Spec)、结构图(Plan)和施工进度表(Tasks)在内的完整蓝图

我们可以用一张架构图来展示这个“广义 Spec”的结构:

当你对 AI 说 “Show me the Spec” 时,你是在要求这三者的完整交付。 只有这样,AI 才能从一个“只会聊天的机器人”变成一个“使命必达的工程师”。

新的开发范式:Show me your Spec

让我们对比一下两种开发者的对话模式:

旧模式(Code-Centric):
* A: “这个功能怎么做?”
* B: “你看我这几行代码(Show Code),我用了一个递归……”
* 痛点: 陷入细节泥潭,难以维护,AI 容易写偏。

新模式(Spec-Centric):
* A: “这个功能怎么做?”
* B: “你看我的 Spec 文档(Show Spec)。我定义了数据结构,约束了异常处理流程,并列出了 5 个测试用例。然后我让 AI 实现了它。”
* 优势: 逻辑闭环,架构清晰,AI 能够一次做对(One-shot Success)。

在这个模式下,程序员的核心竞争力,从“How to implement”(怎么写代码),变成了“How to define”(怎么定义问题)。

你能写出多么严谨、清晰、无歧义的 Spec,AI 就能给你多完美的代码。

Spec 的精度,决定了系统的质量。

小结:做架构师,别做打字员

Linus 说 “Talk is cheap”,是因为当年的 Talk 无法直接转化为生产力。

但现在的 “Spec” 不是 cheap talk,它是可执行的指令(Executable Instructions)

在 AI 时代,请不要再沉迷于堆砌代码行数。

把那些繁琐的实现交给 AI。

你应该去思考架构,去定义边界,去编写 Spec。

下一次,当有人还在炫耀他手写了多少代码时,请淡定地告诉他:

“Code is cheap. Talk is cheap. Show me your Spec.”


如何写出让 AI 听话的 Spec?

道理大家都懂,但真到了实战中,很多人发现自己写的 Spec,AI 根本看不懂,或者生成的代码依然是一坨浆糊。

Spec 也是一门编程语言,只不过它的编译器是 LLM。

如果你想掌握这门“面向 AI 的编程语言”,学会如何编写高质量的 Prompt 和 Spec 文档,构建一套基于 Claude Code 的自动化流水线,欢迎关注我的极客时间专栏《AI 原生开发工作流实战》

在本专栏中,我将:

  • 定义标准: 公开我经过实战验证的 SDD (Spec-Driven Development) 文档模板。
  • 实战演示: 展示如何用一份详实完备的Spec,指挥 AI 完成一个项目模块的开发、测试与部署。
  • 思维升级: 帮你完成从 Coder 到 Architect 的关键跃迁。

别让你的才华浪费在廉价的代码实现上。扫描下方卡片,掌握定义未来的能力。


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

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

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

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

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


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

Jepsen 报告震动 Go 社区:NATS JetStream 会丢失已确认写入

本文永久链接 – https://tonybai.com/2025/12/11/jepsen-report-nats-jetstream-data-loss-acknowledged-writes

大家好,我是Tony Bai。

近日,一则重磅消息在 Go 社区引发了不小的震动。分布式系统领域的“终极拷问者”——Jepsen——发布了一份针对 Go 生态中流砥柱级消息系统 NATS 及其子系统 JetStream 的深度分析报告

报告的结论是严峻的,甚至可以说是颠覆性的:在特定的、可复现的故障模式下,NATS JetStream 可能会丢失已经被服务器确认 (acknowledged) 并声称“已成功持久化”的数据

对于一个以持久化和可靠性为核心卖点的系统而言,这无异于一声惊雷。这份报告,对于所有正在使用或考虑使用 NATS JetStream 的 Go 开发者来说,都是一份必读的“警示录”。它深刻地揭示了在一个分布式系统中,“持久化”的承诺与现实之间的微妙鸿沟。

背景科普:NATS 与 JetStream 是什么?

在深入 Jepsen 的发现之前,让我们先快速了解一下今天的主角。

  • NATS:是一个用 Go 语言编写的、开源、高性能的消息中间件。它以其极致的性能、简单的 API 和轻量级的设计,在 Go 社区乃至整个云原生领域享有盛誉。其核心(有时被称为 “Core NATS”)提供的是一种“尽力而为” (best-effort) 的消息传递,速度飞快,但不保证消息的持久性或送达。

  • NATS JetStream:这是 NATS 的一个内置子系统,旨在为需要更高可靠性的场景提供解决方案。通过引入 Raft 共识算法,JetStream 在 NATS 的核心之上,构建了一个持久化的、可复制的日志(流)。它向用户承诺提供“至少一次” (at-least-once) 的消息传递保证——即已被确认的消息,不应丢失。

正是 JetStream 的这份“不丢失”的承诺,成为了 Jepsen 本次“拷问”的核心目标。

核心发现:“懒惰”的 fsync 默认策略

Jepsen 报告中最核心、也最具普遍警示意义的发现,在于 NATS JetStream 的默认 fsync 策略

问题根源
NATS JetStream 宣称,一旦客户端的 publish 请求被服务器确认,该消息就“已成功持久化”。然而,Jepsen 的测试发现,这并不完全准确。

默认情况下,NATS JetStream 每两分钟才调用一次 fsync 将数据从操作系统的页面缓存 (page cache) 刷入物理磁盘。

这意味着,在任何两次 fsync 之间,都存在一个长达两分钟的窗口期。在这段时间内,所有被服务器立即确认的写入,实际上只存在于内存中

后果是什么?
如果在这两分钟的窗口期内,发生协调性的断电内核崩溃、或多个节点快速连续地重启,那些仅仅存在于内存中的、已经被确认为“持久化”的数据,将永久丢失

Jepsen 的测试通过一个名为 LazyFS 的工具,精确地模拟了这种“断电即失忆”的场景,并成功复现了数据丢失:在一个测试运行中,NATS 丢失了大约 30 秒的写入,共计 131,418 条已被确认的消息。

与 Raft 理论的背离
这实际上与 Raft 论文的建议相悖。Raft 明确指出,节点在响应客户端之前,必须“将新的日志条目刷入 (flush) 它们的磁盘”。MongoDB, etcd, TiDB, Zookeeper 等其他基于共识的系统,都遵循了这一“先落盘,再确认”的原则。

NATS 的选择,是一种典型的性能与持久性之间的权衡。通过异步 fsync,它获得了极高的写入吞吐量,但牺牲了对“灾难性事件”的防护能力。

NATS 团队的回应
NATS 团队已经意识到了这个问题,并在文档中补充说明了这一风险。他们建议,对于需要更强持久性保证的用户,可以将 sync_interval 设置为 always,但这会将吞吐量降低到每秒几百条消息。

更深层次的风险:文件损坏与脑裂

除了 fsync 的问题,Jepsen 还发现了几个在文件损坏场景下,可能导致更严重后果的漏洞。

数据块 (.blk) 文件损坏导致大量数据丢失

Jepsen 发现,即使只是在一个 5 节点的集群中的少数节点上,对 JetStream 的数据块文件 (.blk) 引入单个比特位的错误或截断,也可能导致集群丢失大量已确认的写入,甚至出现数据分歧(脑裂)——不同的节点返回不同的消息集,整个流的数据变得像“瑞士奶酪”一样千疮百孔。

在一个测试中,对两个节点的文件进行比特翻转,最终导致三个节点丢失了高达 78% 的已确认消息。

快照 (snapshot) 文件损坏导致流被删除

更令人不安的是,当快照文件损坏时,一个节点可能会错误地认为某个流已经“孤立”(orphaned),并做出删除该流所有数据的决定。在 Jepsen 的测试中,一个数据已损坏的节点,竟然成功地成为了集群的领导者,并立即删除了包含所有测试消息的流,导致了数据的完全丢失

这暴露了 NATS 在面对数据损坏时,其领导者选举和恢复机制的潜在脆弱性。

一个单一的 OS 崩溃也可能导致数据丢失和脑裂

Jepsen 还设计了一个精巧的实验,证明在异步网络环境下,仅仅一次单节点的操作系统崩溃(模拟断电),就可能导致已提交写入的丢失和持久性的脑裂

场景复现

  1. Leader 节点将一次写入复制给了 Follower A,并收到了确认。此时,写入在 Leader 和 Follower A 的内存中被认为是“已提交”的。
  2. Leader 节点在将这次写入刷入磁盘之前,也还未成功复制给 Follower B 的时候,突然发生了 OS 崩溃。
  3. Leader 节点重启后,它内存中那份“已提交”的写入已经丢失
  4. 此时,集群中存在两个“干净”的节点(重启后的 Leader 和从未收到写入的 Follower B)。它们可以组成新的多数派,选举出新的领导者,并继续处理请求。
  5. 从这个新的多数派的视角看,那次丢失的写入仿佛从未发生过

Jepsen 的测试成功地在 NATS 2.12.1 中复现了这一理论场景,并导致了持久性的副本分歧(脑裂)。

Go 开发者的核心启示

这份报告,并非对 NATS 的“死刑判决”,而是一次深刻的、关于分布式系统复杂性的现实教育。对于 Go 社区的开发者,它至少带来了三点核心启示:

  1. 魔鬼在默认配置中:永远不要盲目相信软件的默认配置。NATS JetStream 默认的sync_interval,是为了性能而优化的,而非持久性。你需要根据你的业务场景(是能容忍丢失少量近期数据,还是要求金融级别的“绝不丢失”),来审慎地做出权衡和配置。

  2. “已确认”不等于“已落盘”:在与任何分布式存储系统交互时,请仔细阅读其文档,搞清楚一个“成功的”写入响应,其背后的持久性承诺到底是什么级别的。是“已写入 Leader 内存”、“已写入多数派内存”,还是“已在多数派节点上 fsync 到磁盘”?这三者之间,差之毫厘,谬以千里。

  3. 拥抱混沌工程:Jepsen 的工作方法,正是混沌工程思想的极致体现。它告诉我们,仅仅通过单元测试和集成测试,永远无法发现分布式系统在真实世界故障模式下的脆弱性。我们需要引入更复杂的、模拟真实世界混乱(网络分区、进程暂停、磁盘错误)的测试手段。

小结

NATS 依然是一个出色、高性能的 Go 原生消息系统。Jepsen 的这份报告,如同一次严苛的“体检”,指出了它在追求极致性能的过程中,所做出的一些高风险权衡。对于我们 Gopher 而言,这不仅是一次了解 NATS 内部工作原理的机会,更是一堂关于如何批判性地思考、审慎地选择和配置我们所依赖的基础设施的必修课。

资料链接:https://jepsen.io/analyses/nats-2.12.1


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

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

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


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

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

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

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

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


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

如发现本站页面被黑,比如:挂载广告、挖矿等恶意代码,请朋友们及时联系我。十分感谢! Go语言第一课 Go语言进阶课 AI原生开发工作流实战 Go语言精进之路1 Go语言精进之路2 Go语言第一课 Go语言编程指南
商务合作请联系bigwhite.cn AT aliyun.com

欢迎使用邮件订阅我的博客

输入邮箱订阅本站,只要有新文章发布,就会第一时间发送邮件通知你哦!

这里是 Tony Bai的个人Blog,欢迎访问、订阅和留言! 订阅Feed请点击上面图片

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

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

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

比特币:

以太币:

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


View Tony Bai's profile on LinkedIn
DigitalOcean Referral Badge

文章

评论

  • 正在加载...

分类

标签

归档



View My Stats