Winding Down Artichoke Ruby
Farewell to Artichoke Ruby
On November 3, 2025, I officially archived Artichoke Ruby. Throughout the remainder of that month, I followed suit by archiving the majority of the other repositories housed within the @artichoke GitHub organization.
This decision was long overdue. I fought against it for a while because closing the project felt like validating a specific narrative I'd seen in Hacker News comments:
"Most alternative Ruby VMs eventually run out of steam."
I simply wasn't ready to let Artichoke become just another statistic in that trend.
The Genesis of Artichoke
Artichoke didn't begin as a grand mission to "save" or "fix" the Ruby language; it started as a toy. I wanted to construct a digital Rube Goldberg machine with the following flow:
To implement this, I utilized the mrusty crate to get bindings for mruby. While those bindings functioned, they were somewhat unstable and prone to crashing (though, to be fair, they were tackling a very difficult problem).
Instead of finishing the UI toy, I became obsessed with the underlying engine. I began "strangler-figging" mruby—a process of incrementally replacing its components with Rust implementations.
The "Strangler Fig" pattern, as described by Martin Fowler, involves gradually replacing specific pieces of a system until the new system eventually replaces the old one entirely.
I never envisioned Artichoke as a direct competitor to giants like CRuby, JRuby, or TruffleRuby. The original goals are still preserved in the project's VISION.md. The project felt "real" to me for the first time when I implemented Regexp (a feature mruby lacked) and presented the work at RubyConf 2019.
Technical Milestones
Over six years, Artichoke evolved into something far more substantial than I had anticipated.
1. A Pure-Rust, Spec-Compliant String
I developed a String implementation that was 100% Rust and fully compliant with the spec, leveraging the bstr crate. This work actually contributed back to the Rust community; I was honored to be mentioned in Andrew Gallant’s announcement for bstr.
Building this required a deep dive into:
- Ruby's encoding semantics.
- The distinction between byte and character boundaries.
- Grapheme assumptions.
- The idiosyncratic edge cases of MRI.
2. Modular VM Architecture
I utilized Cargo features and crate modularity to create a capability-oriented architecture. This is detailed in ARCHITECTURE.md and was inspired by matklad's approach to repository documentation.
| Crate Prefix | Purpose |
|---|---|
artichoke-* | VM-level concerns |
spinoso-* | Core data structures |
mezzaluna-* | VM internals and glue code |
scolapasta-* | General purpose utilities |
3. The Hard Lesson of the GIL
One of the most significant learning curves involved correcting an early architectural mistake: wrapping the interpreter state in .Rc<RefCell<_>>
- PR #442: An attempt to break down the monolithic
Statestruct. It failed to compile and triggered a cascade of refactors. - PR #670: The final removal of
Rc<RefCell<_>>, shifting the interpreter toward explicitmutborrowing.
This migration forced nearly every API to adopt mut self. It became clear why YARV and CPython utilize a Global Interpreter Lock (GIL): managing shared mutable state and aliasing in a dynamic runtime is an absolute nightmare.
I realized that Ruby is fundamentally entwined with C and POSIX assumptions. Trying to over-optimize core structures (similar to how Scala handles maps) felt wrong for a Ruby VM. Artichoke eventually stopped fighting this reality and embraced it.
The Educational Value
Artichoke served as my personal bootcamp for mastering Rust. I gained deep expertise in:
unsafecode and FFI boundaries.- Pointer lifetimes and trait design.
- Runtime architecture.
- Borrow semantics under extreme pressure.
The language that made Artichoke possible.
Why I'm Stepping Away
There is no tragedy here; I simply lack the time. As I've grown older, my priorities have shifted toward my family and my professional life. Working at OpenAI is incredibly demanding (in a positive way), and the opportunity cost of maintaining a language runtime became unsustainable.
The original drive for Artichoke was the desire to learn Rust "the hard way." Having achieved that, the "spark" of discovery has faded. The endless cycle of updating spec versions and chasing behavioral edge cases is a burden I can no longer carry without a dedicated user base.
I knew the end had arrived when I looked at my remaining checklist:
- Implement
String - Implement
HashUnfinished for three years
Final Notes
For those interested in the project's history, my old tweets are still available at @artichokeruby.
⚠️ Warning: If you are using Artichoke in a production environment, please migrate to CRuby or another maintained implementation immediately. Artichoke will receive:
- No security patches.
- No new features.
- No compatibility updates.
It is "complete" only in the sense that it fulfilled my personal goals.
fn main() {
println!("Thank you for believing in a Rust-backed Ruby!");
}
I am deeply grateful to everyone who contributed patches, filed issues, or asked questions. A special shout-out to my co-maintainers:
If you take one thing away from this story, let it be this: Build things.