Lobste.rs 关于 Spinel 的讨论

Lobsters Hottest 新闻

摘要

一篇博客文章认为,Rails 的约定对于 AI 代理和编译器而言是理想之选,并引用提前编译的 Ruby 编译器 Spinel 作为例子,指出该编译器可能消除应用在扩展后需要重写的需求。

<p><a href="https://lobste.rs/s/0yc3r0/lobste_rs_on_spinel">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/07/29 05:51

# 上市之后会发生什么? 来源:https://intertwingly.net/blog/2026/07/28/What-Happens-After-You-IPO.html ### 上市之后会发生什么? --- 2026-07-28T23:47:33Z 现在去看看 rubyonrails.org (https://rubyonrails.org/)。第一眼看到的是: > **通过约定优于配置来加速你的智能体。** > **Ruby on Rails 从 PROMPT 扩展到 IPO。** > 对 token 高效的代码,智能体容易编写,人类审查时也赏心悦目。 我想对此稍作停留,因为我认为它比表面看起来更有趣。 ## Rails 在推销什么属性 主页上的论点是,约定使 Rails 对机器友好。智能体无需发现东西放在哪里,无需推断命名方案,也无需读取你的配置来找出你这周决定了什么。名为 `Story` 的模型对应名为 `stories` 的表,名为 `StoriesController` 的控制器,以及 `app/views/stories` 下的视图。你不用看就知道。智能体也一样。这就是"token 高效"的含义——约定携带了本需要明确写出的信息。 关键在于:这正是一个编译器所需要的属性。当结构可预测时,静态分析很容易;否则很难。Rails 施加的每个约定都是分析器免费获得的事实,而每个配置逃生口都是它必须证明的事实。Rails 花了二十年时间构建了一个本质上适合转译器的理想输入语言,现在已将其作为一项功能放在主页上——出于不同的原因,面向不同的消费者。智能体和编译器想要同样的东西。 主页的另一半无论如何都成立:对人类审查而言依然美观,因为没有人需要扭曲源代码使其对机器可读。约定早已存在。 ## 箭头之后的部分 "从 PROMPT 扩展到 IPO" 是一个好句子,Rails 确实赢得了它的前半部分。没有什么能比它更快地将一个想法变成运行中的应用程序。那么:上市之后会发生什么? 历史上的答案是你离开。你会遇到一堵墙——吞吐量、延迟、基础设施成本、花在服务器上的人力——解决方案是用某种编译型语言重写。这个故事已经上演了足够多次,成为一种体裁。重写成本高昂,耗时数年,丢失了最初让你快速前进的约定,而且新东西用起来更糟糕。我不认为这种权衡是必要的,而这个项目的目标比"让 Rails 变快"更狭窄、更具体: **你不应该需要更改你的应用程序。** 不是移植它。不是注释它。不是为了适应某个工具而重组它,不是采用一个子集,不是维护一个并行版本。你已经写好的 Rails——你的团队熟悉的那一套,你的智能体擅长写的那一套——应该就是输入。如果编译器需要应用程序没有提供的东西,那是编译器的问题,不是你的问题。这个约束正是使这件事困难的原因,也是这个想法唯一值得追求的版本。一个要求你重写应用的转译器只是重新发明了重写。 ## 博客,以及一个已关闭的章节 三个月前,在 matz/spinel (https://github.com/matz/spinel) 的一个关于四个类型推断缺口的问题中,我表达了一个希望:一个运行在 Spinel(Matz 的提前编译 Ruby 编译器)之上的 Rails 博客演示,将成为现有演示中一个有价值的补充。在 issue #83 (https://github.com/matz/spinel/issues/83) 中的回答: > 将 Rails 博客作为输入的演示是一个很好的推动力,其输入形状与当今问题追踪器中驱动大部分工作的小型复现不同,因此是的,一个由 Spinel 驱动的 Rails 博客将与现有演示一起受到欢迎。 同样的回复设定了工作方法,并且至今仍在使用:将每个缺口作为一个独立的最小复现提交,按症状而非机制命名标题,每个问题一个具体的失败案例。小型、自包含的复现程序易于分类;修复通常在一两个提交内完成。 三个月后,博客被转译、编译成本机二进制文件并运行。十二天前,它在 RubyConf 的主舞台上,Matz 在场,演讲及其演示 (https://intertwingly.net/blog/2026/07/16/There-Is-No-Server.html) 已上线。一个推动力完成了它的工作,然后就不再有趣了。生成的博客是一个脚手架——它具有 Rails 意图的形状,而不是应用程序获得的形状。关闭这个章节意味着选择一个更困难的输入。 ## 两个 Lobsters,有目的地测量 Lobsters (https://lobste.rs/) 是一个真实的、由社区运营的 Rails 应用程序:一个链接聚合网站,而不是脚手架。它携带了真实应用程序所携带的东西——手写的 scope、concern、回调、视图助手、没有生成器能生成的查询模式。它也是已知的量,因为 Shopify 的 Ruby 和 Rails 基础设施团队将其变成了 YJIT (https://railsatscale.com/2023-08-25-we-turned-lobsters-into-a-rails-benchmark-for-yjit/) 的基准测试,正是出于这个原因:它像微基准测试无法做到的那样,给真实的 Rails 请求施加压力。 将 Lobsters 移植到 Spinel 是一项大工程。与其将其视为一次推动,不如将其分为两个较小的部分,测量不同的东西——而且,更有用的是,让它们互相发现谎言。 **基准测试通道** 运行捕获冻结在 ruby/ruby-bench (https://github.com/ruby/ruby-bench) 中的内容,锁定到固定提交。基准测试必须冻结其输入,否则数字无法在运行之间比较。代价是它描述了快照时的应用状态。 **合规性通道** 回答另一个问题:它是否能处理他们*现在*拥有的代码?它跟踪上游 HEAD,记录它运行的提交而不是锁定它,并针对同一检出目录的转译运行 Lobsters 自己的 RSpec 套件。 第二个通道是测试实际论点的那个。他们的测试,他们的应用,未经修改——如果输出通过了原始测试通过的套件,那么"你不必更改你的应用程序"就是一个可测量的陈述,而不仅仅是一个口号。而测试套件比返回 200 的路由要精细得多:每个失败都命名了一个尚未建模的具体构造,这将套件变成了一个工作项生成器。没有保真度的速度是对错误程序的基准测试。没有速度的保真度是一个更慢的 Rails。你需要两个通道,并且需要分别报告它们,因为这类后续文章的标准撒谎方式是证明一个并暗示另一个。 ## 实际位置 今天两个通道都运行了,针对 Lobsters `4227239d` 和 Roundhouse `f0b3ab24`。以下数字来自这些运行。 **合规性。** 104 个 spec 文件中有 25 个文件中的 354 个示例——模型 spec,它们最密集地测试编译器表面;请求、特性、邮箱和路由 spec 尚未运行。**39 通过。315 失败。** 这不是一个好分数,我宁愿发布它而不是四舍五入。使它值得发布的是形状:两个原因占了 315 个中的 211 个。`Keystore.upsert` 未实现(133 个示例),并且 `SearchParser` 类输出为空,因为 parslet DSL 被丢弃了(78 个)。其余排名列表为 24、20、16、11、5、5——一个 Symbol 被用在预期整数索引的地方,十六个 `saved_change_to_?` 方法未合成,nil 进入整数算术。每一个都命名了具体的东西。没有一个说"Rails 太动态"。转译本身产生了 743 个文件,零错误,2361 个警告。警告是诚实的部分:建模债务,逐条列出,在准确的代码行上。 **基准测试。** Ruby 输出——Rails 语义编译成普通 Ruby,仍在 CRuby 上运行——用 200 服务了 26 个路由中的 26 个,其中 21 个渲染了 Rails 渲染的内容。在 YJIT 下,每次迭代 133.3 毫秒对比 Rails 的 491.3 毫秒,快了 3.69 倍;没有 JIT 时,167.2 对比 821.0,即 4.91 倍。这些数字是可靠的,并且*不是* Spinel 的故事——这是在任何编译器介入之前,去除框架开销所获得的。Spinel 通道,即实际目标,服务了 **26 个路由中的 10 个**。16 个返回 500。基准测试页面对其自己的计时这样说: > 此页面上的性能数字不可信。 它这么说是因为 AOT 通道在计时运行期间失败了 72 次访问,这使得测量以无法通过眯眼恢复的方式变得乐观。一个在三分之二路由上死掉仍然发布时间的通道,发布的是没有死掉的路由的时间。如果你点击并找到不同的数字——或者一个根本不运行的通道——那就是为什么提交与它们一起发布。两个通道都坐在其下移动的上游,该页面是实时记录。 这是一个下午的快照。一个不对称性影响了上述每个数字。Lobsters 通过 `Rails.cache` 缓存其最重的工作——用户树一天,首页 45 秒,单个故事一分钟。CRuby 通道后面有一个真实的内存存储,足够接近 ActiveSupport 的 `MemoryStore` 以像它一样运行。Spinel 通道有一个空操作:每个 `fetch` 通过其块重新计算。答案仍然正确,所有工作都重做,这使其更像一个缺失的优化,而不是一个与 Rails 运行的程序不同的程序——并且意味着编译后的通道做的比与之对比的通道严格更多的工作。 为什么只有 CRuby 有它,是这个项目的缩影。一个真实的存储需要 Marshal、Mutex 和基于时间的驱逐——正是共享运行时的类型屏障排除的动态类型形状——所以它存在于 CRuby 覆盖层中,而每个其他目标在轮到它之前都保留空操作。这种权衡反复出现:在 Ruby 中很容易,而使其对编译器可用意味着要么正确建模它,要么以书面形式承认它目前是目标特定的。上面的差距中有些是尚未完成的工作,而不是无法完成的工作。 ## 方法 没有哪个版本是下一个提交就能完成它的。取而代之的是:定期运行评估,收割任何便宜的东西,保持上游问题队列的加载,并发布账本,无论它是否讨好。今天的通过是一个有代表性的日子。 我重新测量了当前 Lobsters 对 Spinel 的编译,并一次一个地剥离了一个阻塞因素——在我停止计数之前有三十三个不同的原因,其中二十九个是我的。两个是 Spinel 的缺口,带有小型复现,按照 Matz 在四月要求的形式提交——#3414 (https://github.com/matz/spinel/issues/3414) 和 #3415 (https://github.com/matz/spinel/issues/3415),两者都在几小时内在上游修复,每个都在我报告的问题后面发现了第二个缺陷。还有四个便宜到可以当场修复。那个比例——二十九个我的,两个上游的——是这个工作的诚实形状,也是为什么评估会重新运行而不是假设。 其中一个是为什么两个通道胜过单个通道的好例子。一个生成的视图引用了一个被其自身模块命名空间遮蔽的类:在 `module Views; module Stats` 内部,一个非限定的 `Stats` 解析为视图模块,而不是模型。严格提前编译的通道在编译时拒绝它。CRuby 通道一直愉快地交付它,并在请求时引发错误。无法运行代码的通道找到了可以运行代码的通道中的错误。 你可以实时阅读所有这些。Lobsters 页面 (https://rubys.github.io/roundhouse/apps/lobsters.html) 链接了类型推断、降级、基准测试结果和合规性结果。在 IDE 中打开应用程序,每个未建模的构造在准确的代码行上都是一个波浪线。那些波浪线是关键点:覆盖缺口变得可读,而不是一个写着"大多数东西都能工作"的 README。 这确实还很早。快照服务器仅在其少数路由上服务真实页面,第一个合规性测试通过,基准测试页面拒绝为其自己的计时背书。这三件事同时为真,并且所有这三件事都是朝着唯一重要问题——你已有的应用程序能否与你同行——取得的进展。 --- *Roundhouse (https://github.com/rubys/roundhouse) 是开源的:双许可 MIT / Apache-2.0。欢迎提交问题和讨论。*

相似文章

Lisp 对 Ruby 的影响

Hacker News Top

一篇技术博客文章,探讨 Ruby 的设计(包括闭包、一等函数、符号和方法命名约定)如何受到 Lisp 的影响。

从Rust到Ruby

Hacker News Top

开发人员描述使用LLM将一个15,000行的Rust Web应用转换为Ruby on Rails,发现Ruby版本明显更短,并评估了开发速度、安全性和可测试性方面的权衡。

lobste.rs 现已运行在 SQLite 上

Lobsters Hottest

Lobste.rs 已将其数据库从 MariaDB 迁移到 SQLite,从而降低了 CPU 和内存使用率,减少了成本,并提升了网站性能。本文详细介绍了迁移过程和背景。

遵从性 vs 理解

Lobsters Hottest

作者回顾了他在软件标准和开源领域的职业生涯,然后描述了使用 Claude 构建一个名为 Roundhouse 的编译器,该编译器将 Rails 应用程序转换为静态类型语言,如 Rust、Crystal 和 TypeScript。