Matz Acabou de Dropar um Compilador que Transforma Ruby em Binários Nativos
Yukihiro “Matz” Matsumoto, o criador do Ruby, largou um repositório no GitHub que ninguém esperava: um compilador AOT (ahead-of-time) que pega código Ruby e cospe executáveis nativos. Sem dependência do interpretador. Sem runtime pesado. Só um binário que roda direto no sistema operacional.
O projeto se chama Spinel, e os benchmarks mostram speedups de até 86.7x em tarefas computacionais pesadas. Eu fiquei cético quando vi o número — até olhar os detalhes.
Ah, e tem um detalhe que fez o Hacker News pirar: Matz usou o Claude como assistente de programação para construir o compilador. Em aproximadamente um mês. Um compilador auto-hospedado. Escrito em Ruby. Que compila a si mesmo.
Vamos destrinchar isso.
O Que o Spinel Faz (E O Que Ele Não Faz)
O Spinel implementa um pipeline de compilação em quatro estágios:
- Parsing — usa o Prism (libprism), o parser oficial do Ruby, para gerar uma AST (árvore sintática abstrata)
- Inferência de tipos — analisa o programa inteiro e deduz os tipos de cada variável e expressão
- Geração de código C — transforma a AST tipada em código C otimizado
- Compilação nativa — um compilador C padrão (gcc/clang) gera o binário final
O resultado? Um executável standalone que depende apenas de libc e libm. Nada de Ruby instalado na máquina. Nada de gems. Nada de bundler. Só o binário.
# Compilar um programa Ruby
./spinel meu_programa.rb
# Resultado: um executável nativo
./meu_programa
Parece simples demais, né? É porque por baixo tem 21 mil linhas de código fazendo o trabalho pesado.
Os Benchmarks que Chamaram Atenção
Eu não costumo confiar em benchmarks de README — todo mundo cherry-picka os números bonitos. Mas o Spinel roda contra 28 benchmarks diferentes, e a média geométrica dá ~11.6x mais rápido que o miniruby (Ruby 4.1.0dev).
Os destaques:
| Benchmark | Speedup vs miniruby | O que faz |
|---|---|---|
| Game of Life (Conway) | 86.7x | Simulação com arrays e loops |
| Ackermann | 74.8x | Recursão profunda |
| Red-black tree | 22.6x | Estrutura de dados com inserção/busca |
| JSON parsing | 10.1x | Parse de strings e manipulação |
| Ray tracing | 8.0x | Cálculos de ponto flutuante |
| Lista encadeada | 2.9x | Alocação de objetos |
O baseline é o miniruby do Ruby 4.1.0dev — que já é consideravelmente mais rápido que o Ruby 3.x que a maioria roda em produção. Então o speedup real comparado ao Ruby de “mercado” é ainda maior.
Os casos com ganhos enormes (86x, 74x) são computação pura: loops apertados, recursão, manipulação de arrays. O compilador brilha quando consegue eliminar todo o overhead de dispatch dinâmico e garbage collection.
Onde o ganho é menor (2.9x na lista encadeada) é onde a alocação de objetos ainda domina o tempo de execução.
A Arquitetura: Um Compilador que Compila a Si Mesmo
A parte mais elegante do Spinel é que ele é self-hosting. O backend do compilador (spinel_codegen.rb) é escrito em Ruby — um subconjunto de Ruby que o próprio Spinel consegue compilar.
Traduzindo: o Spinel compila o Spinel. O compilador gera um binário de si mesmo.
Isso não é só um truque técnico bonito. Self-hosting é um marco clássico em design de compiladores. Se o seu compilador consegue compilar a si mesmo, significa que ele é expressivo o suficiente para implementar um sistema complexo. É o equivalente a um organismo que se reproduz.
A história de desenvolvimento também é interessante:
- Versão 1: Implementado em C puro (18 mil linhas)
- Versão 2: Reescrito em Ruby
- Versão 3 (atual): Reescrito novamente em um subconjunto de Ruby que o Spinel compila
Cada reescrita foi um teste: “meu compilador consegue expressar a próxima versão de si mesmo?”
Os componentes:
spinel → Shell wrapper
spinel_parse.c → Frontend (Prism/libprism) — 1.061 linhas
spinel_codegen.rb → Backend (inferência + codegen) — 21.109 linhas
lib/sp_runtime.h → Runtime — 581 linhas
lib/sp_bigint.c → Suporte a inteiros grandes — 5.394 linhas
lib/regexp/ → Engine de regex — 1.759 linhas
O backend sozinho tem 21 mil linhas em um único arquivo Ruby. Sim, isso levantou discussões sobre manutenibilidade — e é aqui que entra a parte da IA.
O Papel do Claude na Construção
Nos comentários do Hacker News, veio à tona que Matz usou o Claude como assistente de programação durante o desenvolvimento do Spinel. A comunidade teve reações mistas, mas o consenso foi surpreendentemente positivo.
O argumento mais repetido: IA não substituiu o Matz — amplificou ele. Um programador que já tem décadas de experiência em design de linguagens e compiladores conseguiu usar IA para acelerar a implementação de algo que ele já sabia como construir.
Como um comentarista colocou: “transforma programadores 10x em programadores 100x.”
Isso levanta uma questão que eu acho mais interessante que o próprio compilador: qual é o papel da IA quando o humano que usa ela é literalmente o criador da linguagem? Matz não estava pedindo para a IA “inventar” um compilador. Ele estava delegando tarefas de implementação para uma ferramenta que executa rápido o que ele já projetou na cabeça.
É a diferença entre “vibe coding” (jogar prompts e ver o que sai) e usar IA como um par de mãos extras guiado por expertise profunda.
As Otimizações Que Fazem a Mágica
O Spinel não é rápido por acidente. Ele implementa um conjunto de otimizações que atacam diretamente os gargalos clássicos do Ruby:
1. Alocação de structs na stack
Classes pequenas (até 8 campos escalares) viram structs C alocadas na stack, não no heap. Zero pressão no garbage collector.
# No CRuby: aloca no heap, GC precisa rastrear
class Point
attr_accessor :x, :y
end
# No Spinel: vira struct C na stack
# typedef struct { int64_t x; int64_t y; } Point;
2. Flattening de concatenação de strings
Expressões como a + b + c + d que no CRuby geram 3 alocações intermediárias, no Spinel colapsam em uma única alocação com o tamanho total pré-calculado.
3. Loop-invariant code motion
Cálculos de .length em arrays e strings dentro de loops são movidos para fora do loop quando o compilador prova que o valor não muda.
# Antes da otimização
while i < arr.length # .length recalculado a cada iteração
# ...
end
# Depois da otimização
len = arr.length # calculado uma vez
while i < len
# ...
end
4. Inlining de métodos
Métodos curtos recebem a diretiva static inline no código C gerado, permitindo que o compilador C os insira diretamente no call site.
5. Interning de symbols em tempo de compilação
Symbols literais (:nome, :status) são resolvidos em tempo de compilação. Interning dinâmico só é incluído no binário quando efetivamente usado — dead code elimination remove o resto.
6. Propagação de constantes
Valores literais são inlined diretamente nos pontos de uso, eliminando lookups em tabelas de constantes.
Essas otimizações combinadas explicam por que o Game of Life roda 86x mais rápido: é um programa que aloca muitos objetos pequenos, faz loops intensivos e usa operações de array repetidamente. Exatamente o perfil que o Spinel otimiza melhor.
O Que o Spinel NÃO Suporta
Aqui é onde a realidade bate. O Spinel não compila Ruby “de verdade” — ele compila um subconjunto de Ruby. E as exclusões são significativas:
Não funciona:
eval,instance_eval,class_eval— avaliação dinâmica de códigosend,method_missing,define_method— metaprogramação runtimeThread,Mutex— multi-threading- Encoding além de UTF-8/ASCII
- Patterns complexos de lambda calculus
Pra quem programa Ruby no dia a dia, essa lista dói. method_missing é a base de inúmeras gems. eval alimenta DSLs inteiras. send está em todo framework de testes.
Um dos comentários mais contundentes no HN resumiu assim: “Unless this gets back eval, metaprogramming and threads this isn’t all that interesting as an actual language.”
E ele tem razão — se você pretende compilar uma aplicação Rails ou rodar o RSpec, esqueça. Basicamente nenhuma gem popular roda sob as restrições do Spinel.
Mas eu acho que essa crítica erra o alvo. O Spinel não está tentando substituir o CRuby. Ele está criando uma nova categoria: Ruby para sistemas.
Ruby Para Sistemas: O Nicho Que Ninguém Ocupava
Pense nos casos de uso onde o Spinel brilha:
- Ferramentas de CLI — um binário único, sem dependências, que roda em qualquer Linux
- Scripts de infraestrutura — substituir bash por algo com tipos e performance
- Ferramentas de build — bootstrapping sem precisar do Ruby instalado
- Microserviços leves — processos com startup instantâneo
- Computação científica — loops numéricos intensivos
Hoje, se você quer um binário nativo a partir de uma linguagem com sintaxe elegante, suas opções são:
| Linguagem | Trade-off |
|---|---|
| Go | Verboso, sem expressividade |
| Rust | Curva de aprendizado brutal |
| Crystal | Parecido com Ruby, mas é outra linguagem |
| Nim | Ecossistema pequeno |
| Spinel | Subconjunto de Ruby, sem metaprogramação |
O Crystal sempre foi a comparação óbvia. Mas o Crystal exige tipos estáticos explícitos e tem divergências significativas da sintaxe Ruby. O Spinel preserva a sensação dinâmica do Ruby enquanto faz inferência de tipos por debaixo dos panos.
Self-Hosting como Prova de Conceito
O fato do Spinel compilar a si mesmo é mais do que um truque legal — é uma demonstração de que o subconjunto suportado é genuinamente útil.
O backend do compilador (spinel_codegen.rb) usa:
- Classes com herança
- Mixins via
include attr_accessor- Pattern matching (
case/in) - Operador de navegação segura (
&.) - Iteradores (
each,map,select,reduce) - Tratamento de exceções (
begin/rescue) - Fibers para concorrência cooperativa
- Regex via engine NFA embutida
- Inteiros de precisão arbitrária
- I/O de arquivos
21 mil linhas de código real, não um toy example. Se esse subconjunto é suficiente para implementar um compilador completo, é suficiente para a maioria das ferramentas de linha de comando e scripts de sistema.
Como Se Compara ao CRuby 4.1
O CRuby não parou no tempo. O Ruby 4.1 (dev) que serve de baseline para os benchmarks já inclui:
- YJIT — JIT compiler que melhora performance de código orientado a objetos
- Prism parser — o mesmo parser que o Spinel usa
- Melhorias contínuas no garbage collector
E mesmo assim o Spinel é 11.6x mais rápido na média. Por quê?
A resposta é simples: compilação AOT com inferência de tipos global elimina overhead que nenhum JIT consegue remover. O YJIT otimiza hot paths em runtime, mas ainda carrega todo o peso de um interpretador completo, dispatch dinâmico e garbage collection generacional.
O Spinel sabe, em tempo de compilação, exatamente quais tipos cada variável vai ter. Não precisa de guards, deoptimization ou warmup. O binário sai otimizado desde a primeira instrução.
O Elefante na Sala: 21 Mil Linhas em Um Arquivo
Vou ser direto: um arquivo de 21 mil linhas com 15 níveis de nesting é um code smell monumental. Vários desenvolvedores apontaram isso no HN, questionando se o projeto é mantível sem assistência de IA.
A preocupação é legítima. Se o Spinel depende de “jogar o arquivo inteiro no contexto de um LLM” para fazer modificações, ele tem um problema de sustentabilidade. LLMs são ótimos para gerar código, mas a complexidade não desaparece só porque uma IA consegue navegar nela.
Por outro lado, compiladores historicamente são sistemas complexos com arquivos enormes. O GCC tem arquivos de 15 mil linhas. O V8 do Chrome também. Não é ideal, mas é a realidade de certos domínios.
O mais provável é que o Spinel evolua com refatoração gradual se ganhar tração na comunidade. Ou morra como um experimento fascinante — o que também é um resultado válido.
Implicações Para o Ecossistema Ruby
O Ruby vive um momento interessante. Depois de anos sendo “a linguagem do Rails” e perdendo mindshare para Python, Go e Rust, vários projetos estão tentando reinventar o que Ruby pode ser:
- YJIT (Shopify) — JIT compiler integrado ao CRuby
- Prism — parser unificado para o ecossistema
- Spinel — compilação AOT nativa
- rv — toolchain unificada para Ruby (em desenvolvimento)
O Spinel pode nunca substituir o CRuby para aplicações web. Mas ele abre uma porta que estava fechada: usar Ruby onde hoje você usaria Go ou Rust por necessidade, não por preferência.
Se você é um Rubyist que já escreveu scripts em bash porque “não vale a pena instalar Ruby no servidor de CI” ou “preciso de um binário único pra distribuir” — o Spinel é para você.
Instalação e Primeiros Passos
O Spinel roda em Linux e macOS. Você precisa de:
- Ruby (CRuby) para bootstrap inicial
- GCC ou Clang
- libprism
# Clonar o repositório
git clone https://github.com/matz/spinel.git
cd spinel
# Bootstrap: compilar o Spinel usando CRuby
make bootstrap
# Agora o Spinel compila a si mesmo
make self-host
# Testar com um programa
echo 'puts "Hello from Spinel!"' > hello.rb
./spinel hello.rb
./hello
O binário gerado não precisa de nada além do sistema operacional. Copie pra outro servidor e rode.
O Que Esperar Daqui Pra Frente
O Spinel é um projeto jovem. Se Matz mantiver o desenvolvimento ativo (e considerando que ele é Matz, isso não é garantido — ele tem o Ruby inteiro pra cuidar), os próximos passos lógicos seriam:
- Suporte a mais da stdlib — File, Net, JSON nativo
- Concorrência — Fibers já funcionam, mas threads seriam um game-changer
- Melhor interop com C — FFI simplificado para usar bibliotecas existentes
- Tooling — LSP, debugger, profiler
Mas honestamente? Mesmo que o Spinel pare no estado atual, ele já provou um ponto: Ruby pode ser compilado para código nativo competitivo. E isso muda a conversa sobre o futuro da linguagem.
O repositório está em github.com/matz/spinel — e pelo nível de atividade nos últimos dias, a comunidade está prestando atenção.
Fonte de inspiração: Spinel: Ruby AOT Native Compiler — Hacker News, 268 pontos













