No palco do Rails World deste ano, o DHH disse uma frase que eu não esperava ouvir do criador do Rails:

“I don’t know any Rust at all. I consider that a feature […]”

Aos 35:40 do keynote, ele completa que avalia o código Rust como uma caixa preta, de fora, como faria qualquer dono de negócio que contrata programadores.

No começo de outubro veio a tabela. O Campfire, o app de chat da 37signals, reescrito por agentes em sete linguagens, com o Rails lá embaixo.

Gráfico de barras do Campfire, room page em requisições por segundo: Django 1,507; Laravel 3,872; Rails 4,101 (em destaque); Elixir 5,350; Express 42,636; Go 53,060; Rust 106,494; C 137,524.
Room page do Campfire, em requisições por segundo. Fonte: README do once-campfire-rust.

A timeline fez a conta sozinha: se o criador do Rails não precisa saber Rust pra entregar um Campfire 26x mais rápido, a linguagem virou formato de saída. Escolhe a mais rápida e segue.

Essa leitura está meio certa. A parte certa é o Rust. A parte errada é achar que o feito é do Rust.

1. A velocidade não é a novidade

Os números são reais, e não surpreendem ninguém. Rust é compilado, Ruby é interpretado. Um binário Rust servindo HTTP vai ser muito mais rápido que um Rails, com ou sem agente.

E se ninguém vai ler o código, faz todo sentido escolher a linguagem mais rápida. Rust é uma linguagem boa. Tratada como caixa preta, entrega performance sem cobrar de você o preço de escrever Rust. É o que o DHH está fazendo com o backend do HEY.

NOTA: entre os outros ports a conta é menos limpa. Hoje o once-campfire-rust tem 437 commits, o de Go tem 40 e o de Elixir, 45. Parte da diferença entre Rust, Go e Elixir é rodada de ajuste, não linguagem. Já a distância entre Rust e Rails é linguagem mesmo.

A novidade é outra. Alguém que diz não saber nada de Rust entregou um Campfire em Rust que, segundo o próprio repositório, é indistinguível do original, pixel por pixel.

2. O que o Rust faz no Campfire

O Campfire é o chat em grupo da 37signals: salas, mensagens diretas, anexos, busca. Ele é vendido no modelo ONCE - você paga uma vez e roda no seu servidor. O código é Rails, aberto, com licença MIT.

Uma instalação dessas roda vários processos. Ruby com Puma servindo HTTP, Redis, Resque pros jobs em background e o Thruster na frente, cuidando de TLS e cache.

O port em Rust troca tudo isso por um executável só. O frontend continua sendo o do Rails, com poucos ajustes. O Rust é o servidor inteiro: HTTP, WebSockets compatíveis com o Action Cable, jobs, push, busca.

E tem uma exigência que muda o tamanho do problema. O README promete que ele usa o mesmo banco SQLite, o mesmo layout de arquivos e os mesmos cookies assinados e criptografados, “so existing installs can upgrade without migrating data or signing everyone out”.

Ou seja, o Rust tem que se comportar exatamente como o Rails, inclusive nos detalhes que ninguém vê.

Como alguém que não sabe Rust garante isso?

3. O código mais importante do port foi escrito em Ruby

O repositório responde. Antes de qualquer linha de Rust, tem um plano em plans/rust-conversion.md, e o objetivo dele é paridade, não velocidade: um Campfire em Rust que “can’t be told apart from the Rails app”.

Pra provar isso, montaram um harness em duas metades:

  • Captura visual, em TypeScript com Playwright. Compara HTML, DOM, árvore de acessibilidade e screenshots dos dois apps.
  • Golden vectors, em Ruby. Cookies assinados, cookies criptografados, CSRF, signed IDs, nomes de stream do Turbo.

A primeira metade é uma caixa preta olhando outra pelo navegador. A linguagem ali tanto faz.

A segunda é o gabarito, e a regra pra ele está no cabeçalho de reference-tools/rails_compat_vectors.rb:

“Every expected value comes from the reference app itself (cookie jar, session store, controllers, verifiers), never from a reimplementation.”

E o plano é mais seco ainda: “We never port one from our reading of the docs.”

Dava pra escrever esse gabarito em Rust? Em parte, sim. Você sobe o Rails, loga por HTTP, captura o Set-Cookie e salva. Isso funciona de qualquer linguagem.

Só que muito do que precisa bater não aparece por HTTP. Como a chave é derivada do secret_key_base. Qual digest assina o cookie. Como uma chave antiga, rotacionada, continua valendo. Pra conferir isso de fora, você teria que reimplementar esses pedaços do Rails no próprio harness - e aí é Rust conferindo Rust. Os dois concordam, podem estar errados juntos, e aí todo usuário existente é deslogado no deploy.

O port já tem essa reimplementação, inclusive. É um crate chamado rails_compat. O que faltava era um gabarito que não saísse da mesma cabeça.

Em Ruby, o script roda dentro do app de referência e pergunta direto pro cookie jar e pros verifiers do Rails. É o caminho mais curto até a resposta certa.

Diagrama do harness de paridade do Campfire. À esquerda, o Campfire em Rails como oráculo; scripts Ruby perguntam pro Rails e geram golden vectors (cookies assinados, CSRF, signed IDs, Turbo streams), que conferem o Campfire em Rust escrito por agentes. Um laço do Rust conferindo o próprio Rust aparece riscado com um X vermelho. Embaixo, o Playwright em TypeScript compara HTML, DOM e screenshots dos dois apps.

Pra escrever esses vetores, alguém precisou saber quais contratos existem. Que o digest de assinatura do cookie é separado do digest de derivação da chave, e pode ser SHA1. Que o nome de uma variante de imagem é o hash de um dump de Marshal do Ruby. Que o Rails tem que conseguir subir em cima do banco que o Rust escreveu, porque rollback tem que funcionar.

É isso que segura o port inteiro, e nada disso aparece em req/s.

4. O que transferiu foi o modelo do problema

Os commits do port têm Co-Authored-By: Claude na maioria. Quem digitou o plano importa pouco. Ele codifica um conhecimento profundo do Rails e do Campfire - e é esse conhecimento que deixa alguém dirigir agentes numa linguagem que não conhece.

Que nem um cozinheiro experiente numa cozinha estranha. Ele não sabe em que gaveta fica a concha, nem como esquenta aquele fogão. Mas sabe o gosto que o molho tem que ter. Prova, corrige, prova de novo.

O DHH não sabe onde ficam as panelas do Rust. Mas sabe como o Campfire tem que se comportar melhor que qualquer pessoa no planeta.

Isso funciona bem com agente porque LLM é muito boa em reproduzir - foi treinada assim. “Olha esse texto e, quando eu pedir, gera um parecido.” Com uma referência boa do lado, ela acerta muito mais.

O Campfire em Rails era essa referência. Cada tela, cada cookie, cada resposta tinha um original pra comparar.

Acredito que no Omarchy seja a mesma coisa. O DHH sabe o que quer fazer e sabe conferir o resultado contra uma referência. O Rust ali é escolha do artefato, não o que faz o projeto andar.

Por isso, sempre que eu vou começar um projeto, a primeira coisa que faço é procurar referências: projetos parecidos, código de quem já resolveu algo próximo do que eu quero. Isso ajuda muito a dar direção pro agente.

5. E isso não é privilégio do DHH

Aqui é onde eu discordo do tom de milagre.

Qualquer pessoa sênior carrega modelos de problemas que já viu em outras linguagens. Quando você vai resolver alguma coisa, já tem uma imagem de como a solução vai ser. A IA fecha o buraco da sintaxe. O resto você trouxe.

Eu fiz isso duas vezes este ano.

O BEThoven, o bolão da Copa que a gente jogou pelo terminal via SSH, é em Go. Eu sabia de Go a sintaxe e um pedaço da biblioteca padrão. Só isso. Escolhi Go porque precisava de uma TUI boa servida por SSH, num binário só, e o ecossistema do Charm (Bubble Tea, Wish) resolve exatamente isso. O bolão aguentou a Copa inteira.

Em Rust eu nunca tinha escrito uma linha. Mesmo assim tenho patches mergeados no ai-memory, que é um projeto Rust de bom tamanho. Mandei cinco PRs, três entraram.

Nenhum dos dois casos me faz especialista em Go ou em Rust. Eu não precisei ser.

6. Mas tem teto

“Qualquer sênior faz” tem limite, e vale saber onde ele fica.

Critério na linguagem não transfere. Eu sei dizer se um patch em Rust está certo: o comportamento bate, o teste passa, o desenho faz sentido. Não sei dizer se é Rust idiomático. Essa palavra final fica com quem mantém o projeto e vive na linguagem.

Mas dá pra encurtar essa distância, e de novo com harness. Arquivo de contexto dizendo como o projeto escreve, linter bem configurado, CI que reprova warning. O próprio once-campfire-rust faz isso: unsafe_code = "deny" no Cargo.toml, um AGENTS.md mandando manter o clippy limpo, e o CI rodando clippy com -D warnings.

O critério de quem vive na linguagem vira regra escrita. E regra escrita o agente segue.

Domínio pesa mais que linguagem. O DHH falou no keynote que o backend do HEY está virando um servidor de email em Rust. O difícil de um servidor de email é SMTP, entregabilidade e segurança. Rust é a parte barata. O modelo do problema só transfere se você tem o modelo.

Banda de revisão é o teto de verdade. O DHH disse no palco que escreveu 150 mil linhas em agosto. Nenhum sênior revisa isso. Com essa vazão, o harness deixa de ser detalhe e vira a única coisa entre você e produção. No próprio relatório da madrugada do port tem um agente que mandou o email do usuário no User-Agent de uma requisição pro crates.io. Foi pequeno, e só dá pra saber porque o relatório registrou.

Operar não transfere na velocidade que escrever transfere. Profiling, um panic às três da manhã, saber qual crate o ecossistema já abandonou. Isso leva tempo, com ou sem agente.

7. “Retired from being a professional programmer”

No mesmo keynote, aos 39:21, o DHH disse que se aposentou de ser programador profissional, uns quatro ou cinco meses antes.

Eu não tenho problema nenhum com isso. Na verdade acho que todo mundo vai largar a programação do jeito que a gente fazia. Eu inclusive.

Mas olha o que ficou no lugar dela, no repositório dele mesmo: plano revisado por outro modelo, contratos listados um por um, vetores gerados do app de referência e rollback testado antes da primeira feature.

Isso é o trabalho de sênior que sobrou quando a digitação saiu da mesa.

O Akita resume bem, num post recente:

“QA virou a habilidade mais valiosa do programador, muito acima de ‘saber a sintaxe de Rust’.”

A tabela vende a linha errada. O que deveria viralizar é o reference-tools/.

8. A linguagem é cada vez mais o que ela se propõe a ser

Sempre foi assim: cada linguagem se propõe a resolver um tipo de problema, e quem escolhe é o artefato.

O que atrapalhava era o custo de chegar lá. Aprender a sintaxe, digitar, errar, até ficar produtivo. Por isso muita gente acabava usando a linguagem que já sabia, não a que o problema pedia.

Agora que digitar ficou barato, esse atrito caiu. E a escolha fica cada vez mais com o artefato.

O DHH está fazendo o Omarchy, que é um sistema operacional. Rust faz todo o sentido ali. Quem distribui binário - CLI, agente, servidor que precisa caber num container pequeno - tem bons motivos pra Go ou Rust. Eu fui de Go porque queria uma TUI via SSH num binário só.

E pra um web app novo? O próprio Akita, no mesmo post:

“Exemplo pessoal: web app novo, eu abro Ruby on Rails sem pensar duas vezes.”

Toda linguagem tem seu lugar, e agora dá pra ir até ela sem pagar o pedágio da sintaxe.

O que não dá é pular o pedágio do problema.

Fechando

“I consider that a feature.”

É feature mesmo. Mas a feature não é o Rust. É conhecer o Campfire tão bem que não precisa conhecer Rust - saber o que quer e ter com o que comparar.

E essa feature qualquer sênior pode construir. Só não vem de graça e não aparece em tabela de req/s.

Por hoje é só.