Minha opinião sobre a reescrita do Bun em Rust
(en.liujiacai.net)- A reescrita do Bun em Rust está mais próxima de uma decisão de migrar para uma stack tecnológica mais mainstream, mantendo a arquitetura e as estruturas de dados existentes feitas em Zig
- O PR da reescrita teve 6.755 commits e foi aberto em 8 de maio e mesclado em 14 de maio, o que deixa dúvidas sobre a profundidade da revisão de uma mudança em um runtime de nível de produção
- Passar nos testes valida apenas caminhos conhecidos, e dificilmente garante até invariantes globais como caminhos de erro, concorrência e condições extremas de memória
- O ponto central não é um fracasso do Zig, mas uma incompatibilidade estrutural entre a cultura da equipe do Bun, que prioriza lançamentos rápidos, e o custo do gerenciamento manual de memória
- A aposta real não é Zig versus Rust, mas se código gerado por IA e sem revisão suficiente pode ser mantido no longo prazo
A base do Bun criada com Zig
- Antes de olhar o PR da reescrita do Bun em Rust, vale lembrar que o papel do Zig foi importante para o Bun chegar onde está hoje
- Jarred não escolheu Zig porque ele era “legal”, mas porque ele permitia que uma equipe pequena prototipasse rapidamente um runtime JS de alto desempenho sem garbage collection
- O baixo atrito do Zig, a manipulação direta de memória e a interoperabilidade simples com C possibilitaram o desempenho inicial do Bun e o tamanho reduzido da equipe
- Como o próprio Jarred disse que “a arquitetura não muda, e as estruturas de dados também não mudam”, a reescrita em Rust está mais próxima de herdar o esqueleto construído em Zig
- Construir a base com Zig, lançar o produto, captar investimento e depois, com a empresa adquirida e maior, migrar para uma stack mais mainstream pode ser visto como uma decisão de negócios normal
- Ainda assim, é difícil apresentar isso como uma inadequação do próprio Zig
O risco de uma reescrita massiva mesclada em 6 dias
- O PR da reescrita registrou 6.755 commits, com o nome de branch
claude/phase-a-port, aberto em 8 de maio e mesclado em 14 de maio - O ponto central é que a reescrita completa de um runtime JS de nível de produção foi mesclada em apenas 6 dias
- Um princípio básico da engenharia de software é que “código que você não entende não deveria rodar em produção”
- Esse princípio não existe porque o código necessariamente terá bugs, mas porque, quando um bug acontece, você deixa de saber por onde começar a olhar, o que vira uma linha de base para manutenção
- Na lista de revisores do PR estavam
coderabbitai[bot]eclaude[bot], e o único revisor humano,alii, estava em estado “Awaiting requested review” - Um ciclo fechado em que o Claude escreve e o Claude revisa não é algo logicamente impossível, mas na prática significa que nenhum humano leu de fato toda a base de código até o fim
O que passar nos testes não consegue garantir
- Mesmo que a suíte de testes passe em todas as plataformas, isso valida apenas a correção de comportamentos conhecidos e caminhos conhecidos
- Uma suíte de testes dificilmente consegue garantir bem as áreas abaixo
- se os caminhos de erro são tratados corretamente
- como o sistema se comporta em condições-limite sob estresse
- se a consistência de estado é mantida em cenários de concorrência
- se o modelo de memória corresponde à intenção em condições extremas
- O próprio Jarred resumiu que problemas de memória ao reentrar na fronteira do JS não podem ser tratados pelo compilador Rust e ainda dependem de humanos
- O problema é que justamente essa parte que depende de humanos não foi revisada por humanos
- A tradução de código por IA tende mais a uma equivalência semântica local, em que cada função é ajustada para se comportar como o original de forma isolada
- Invariantes globais entre funções e restrições de projeto que não estão nos testes, mas só na cabeça do autor original, são difíceis de garantir apenas com tradução por IA
- Essas restrições podem não aparecer nos testes atuais e só surgir 6 meses depois como falhas difíceis de explicar sob uma carga específica de produção
- Isso não é um problema exclusivo do Claude, mas algo que também vale para qualquer ferramenta ou programador humano que traduza código sem revisão suficiente
- Em uma escala de 6.755 commits, esse risco é muito ampliado
Quem passou a assumir o risco após a aquisição
- No começo, o Bun era um projeto em que Jarred apostava em si mesmo, e o uso de Zig, as iterações rápidas e a aceitação de dívida técnica podiam ser entendidos pela lógica de uma startup que assume o próprio risco
- Agora que o Bun foi adquirido por uma grande empresa e tem uma base de usuários que o utiliza em sistemas reais de produção, quem assume o risco da reescrita já não é Jarred, mas os engenheiros que executam Bun em produção e os usuários por trás deles
- Jarred disse que essa versão ainda está em canary e que ainda faltam otimizações e trabalho de limpeza antes do lançamento oficial
- Canary é uma linha de defesa, mas não substitui revisão humana
- Otimização e limpeza são questões de qualidade de código; elas não resolvem o problema de saber se os mantenedores realmente entenderam o código
- Uma base de código que ninguém da equipe leu completamente continua sendo uma caixa-preta para quem dá manutenção, mesmo com testes abrangentes ou um período longo em canary
- Esse problema pode se transformar em dor real no futuro, quando surgir a necessidade de depurar um bug sério
O erro no diagnóstico do problema do Zig
- A justificativa anterior apresentada por Jarred era que a base em Zig tinha muitos use-after-free, double-free e vazamentos de memória em caminhos de erro
- Esse diagnóstico em si está correto, mas é difícil concluir a partir disso que “o Zig não funciona”
- Um diagnóstico mais preciso seria dizer que, em um projeto comercial que prioriza iteração rápida, o custo cognitivo do gerenciamento manual de memória ultrapassou o orçamento da equipe
- Isso não é um bug do Zig, mas uma incompatibilidade estrutural entre os objetivos de projeto do Zig e o modelo de negócios do Bun
- O público-alvo do Zig são programadores de sistemas que sabem o que estão fazendo e estão dispostos a pagar o custo pelo controle final
- O TigerBeetle escreveu um banco de dados em Zig com praticamente nenhum bug de memória, o que pode ser resumido pelo fato de a cultura da equipe e a natureza do projeto combinarem com a filosofia do Zig
- A cultura da equipe do Bun está mais próxima de iteração rápida, lançamento rápido e correção rápida de bugs, e isso está em tensão fundamental com a disciplina rigorosa de memória exigida pelo Zig
- Interpretar “nossa equipe comete muitos erros com esta ferramenta” como “esta ferramenta é inadequada” é algo próximo de um erro de atribuição
- Foi usada a analogia de que, só porque o martelo não serviu, isso não significa que o martelo em si esteja errado
Perspectiva de curto prazo e risco de longo prazo
- No curto prazo, é bem possível que a versão reescrita fique, em geral, OK
- Os principais caminhos são cobertos por testes, problemas óbvios aparecem na fase canary, e as garantias do compilador Rust eliminam um tipo de bug de memória
- Na superfície, tudo pode parecer normal
- No longo prazo, 6.755 commits que nenhum humano leu por completo continuam sendo um risco estrutural
- Se daqui a 6 meses surgir um bug estranho de concorrência, ou se uma condição-limite sob uma carga específica provocar um comportamento anômalo, o engenheiro encarregado da depuração vai se ver diante de um sistema que ninguém jamais entendeu de verdade
- Dizer que é um sistema nunca entendido não significa que ele não tenha bugs, mas que, quando os bugs aparecerem, ninguém vai saber por quê
- A verdadeira aposta técnica dessa reescrita não é Zig versus Rust, mas se código gerado por IA e não revisado pode ser mantido no longo prazo em ambiente de produção
- Essa pergunta é mais complexa do que “todos os testes passam” e mais profunda do que “segurança de memória do Rust”
- A conclusão é resumida na metáfora de que “o Zig construiu a base, o Claude ergueu o prédio, e o revisor humano ainda está a caminho”
- O quão habitável esse prédio será ao longo do tempo depende de, quando aparecer o primeiro vazamento, alguém ainda conseguir ler a planta
1 comentários
Comentários no Lobste.rs
Antes de discutir “vamos reescrever o Bun em Rust”, é preciso dizer que o Bun chegou onde chegou graças ao Zig
O criador do Bun, Jarred também diz isso. Ele diz que o Zig tornou o Bun possível e teve grande mérito no fato de um projeto codado sozinho por um ano em um quarto fedorento em Oakland ter crescido até se tornar uma das ferramentas mais usadas do ecossistema JavaScript
Também diz que o Zig é uma linguagem excelente e que o Bun deve muito do seu sucesso a ela, mas que outros projetos como Ghostty e Tigerbeetle não passaram pelos problemas de estabilidade que o Bun enfrentou
Normalmente, bancos de dados têm desafios de estabilidade muito maiores do que runtimes de linguagem
Este texto tem muito cheiro de texto escrito por LLM
Então essa impressão também pode ser um traço de tradução automática
Se o usuário-alvo do Zig é “um programador de sistemas que sabe o que está fazendo e está disposto a pagar o preço pelo controle máximo”, isso parece uma reaparição da autoconfiança estilo C/C++ no contexto de linguagens que vieram depois de Rust e Swift
Se o Jarred disse que está migrando porque “havia muitos use-after-free, double-free e vazamentos de memória em caminhos de erro no codebase em Zig”, isso vira um ponto de dados sobre se é possível tornar uma linguagem sem segurança de memória efetivamente segura só fazendo a LLM caçar bugs de memória com empenho
Nem mesmo as empresas de LLM seguiram esse caminho aqui
A frase “código escrito pelo Claude, revisado pelo Claude, então o ciclo fechado não é logicamente impossível, mas isso significa que nenhum humano jamais leu de fato esse codebase inteiro” claramente não parece correta
Se um codebase em Zig escrito por humanos foi traduzido mecanicamente para Rust, quem entende o código original também pode entender o código novo. Não é como portar para APL; ambas são linguagens procedurais na tradição sintática do C
O ponto de que, se você deixar LLMs rodarem sem supervisão daqui para frente, o código pode se afastar cada vez mais da intuição humana continua em aberto, mas o codebase neste momento ainda parece compreensível tanto quanto se poderia esperar de um runtime JavaScript de um milhão de linhas e de uma ferramenta tudo-em-um de binário único
A frase “a IA só acerta equivalência semântica local no nível de função e não entende invariantes globais entre funções” soa estranha tanto numa leitura favorável quanto crítica às LLMs
LLMs não são determinísticas o bastante para garantir que cada função se comporte igual ao original. Para esse tipo de garantia seriam necessárias ferramentas como
c2rust. Uma LLM pode traduzir código que parece com o original, mas o que impede que((abc & 45) << 3) == 360vire((abc & 45) << 30) == 360é o compilador, a suíte de testes e talvez a detecção probabilística de um code review com LLMPor outro lado, se existisse um tradutor que, como o
c2rust, garantisse a equivalência de cada função e, como uma LLM, também preservasse comentários e estrutura, então isso seria um tradutor perfeito e daria para portar automaticamente até um codebase de um milhão de linhas. Compiladores também podem ser vistos como um caso especial disso, e o Clang, embora não seja livre de bugs, chega perto o bastante para que as pessoas confiem nele. Se LLMs conseguissem traduzir Zig ou C++ para Rust com confiabilidade no nível do Clang, o Chrome já teria virado Rust puro até o fim deste mêsAlém disso, codificar invariantes entre funções é justamente o cerne de um sistema de tipos. Um dos motivos para reescrever o Bun em Rust também é que o sistema de tipos do Rust consegue expressar melhor invariantes complexos. Não é como se a Anthropic tivesse compilado tudo para assembly e depois queimado o código-fonte
O Bun já vinha sendo vibe coded muito antes da migração para Rust. Adotou IA bem cedo e, depois da aquisição pela Anthropic, quase todos os commits passaram a ser escritos por bots. O Jarred também tuitou que deixou a IA escrever funcionalidades durante o fim de semana e que várias features foram adicionadas assim
HTML Parsers in Portland analisa várias portas de um parser HTML em Python e mostra que um dos algoritmos centrais foi implementado de maneiras muito diferentes em cada porta. Em todos os casos, uma portabilidade mecânica era possível, mas na prática foi isso que não aconteceu
Claro, é um caso do começo do ano e o processo também foi diferente, mas alguns trechos que vi no codebase do Bun depois da migração parecem sofrer de problemas parecidos
Mas o ponto de que “LLMs não são determinísticas o bastante para garantir que cada função se comporte igual ao original” leva no fim à conclusão de que nenhum humano realmente leu o codebase inteiro
Um exemplo de “todos os testes passam”: https://github.com/oven-sh/bun/…
Como várias pessoas em outros fóruns também linkaram esse mesmo commit, parece que viram esse link em algum lugar, acharam que batia com a expectativa e depois não conferiram se de fato tinha relevância. Acho que deveríamos aplicar um padrão mais alto a nós mesmos
Primeiro commit: “await process exit / JSON-parse-retry instead of fixed sleeps”
Reversão: “test: revert proc.exited change in spawn.test.ts, keep isDebug iteration count”
Tenho pensado bastante ultimamente no princípio de que “você não deve executar em produção código que não entende”
Minha carreira já avançou bastante, mas evitei de propósito ir para gestão, e minha trajetória em programação também foi cada vez mais em direção às camadas mais baixas da stack. É porque eu gosto de entender profundamente o programa em si. Lançar funcionalidades e melhorar a vida dos usuários também é muito importante, mas uma das grandes recompensas internas da programação é a sensação de aprender fatos verdadeiros sobre sistemas reais
Para alguém como eu, a ideia de que humanos precisam entender cada linha de um programa parece quase um axioma. Mas, no organograma, meu gerente é responsável pelo programa que eu mantenho. Ele é um ótimo gerente e não faz microgerenciamento, então obviamente não entende cada linha do software que a equipe entrega. Na verdade, nem sei se ele quase chegou a ler o código. Nesse caso, então ele estaria violando esse princípio
Estou pensando se existe uma diferença significativa entre “código escrito por subordinados que eu não entendo” e “código escrito pela minha IA que eu não entendo”
O caso 1 parece aceitável, mas o caso 2 incomoda. Será que a diferença é só se existe um humano responsável por aquele código? Se existe alguém a quem se possa atribuir a culpa? Ainda não sei se isso basta para justificar a sensação moral forte que tenho
Tenho receio de que essa emoção forte não seja essencial para uma boa engenharia e esteja mais próxima de uma preferência estética pessoal. Cada um pode ter seus gostos, mas, se for só isso, então talvez seja preciso esperar que esse aspecto do trabalho fique menos prazeroso daqui para frente. No fim, seria como ser empurrado para a gestão, só que meus subordinados seriam bots
Dizem que “o Zig permitiu que uma equipe pequena prototipasse rapidamente um runtime JS de alto desempenho sem coletor de lixo e sem runtime pesado”, mas outros runtimes importantes, Node e Deno, também fazem a mesma coisa
Ambos envolvem engines JS em linguagens sem coletor de lixo, C++ e Rust. Não sei ao certo se o V8 ou o próprio JSC de fato distribuem um coletor de lixo junto, mas isso foge do ponto principal
O repositório inteiro do Bun parece uma distopia. Bots conversando entre si e abrindo PRs sem sentido
Ex.: https://github.com/oven-sh/bun/issues/30766
A velocidade com que isso foi considerado “concluído” é bem chocante, mas não parece um desastre ferroviário em movimento como o texto descreve sem evidência sólida
A frase “daqui a 6 meses aparece um bug estranho de concorrência, e quando uma condição de borda sob certa carga causar um comportamento anômalo, o engenheiro depurando isso vai se deparar com um sistema que ninguém jamais entendeu de verdade” é pura especulação, e nenhum fato foi apresentado
Portar linguagens é uma área adequada para LLMs, e o código de base já era entendido por muitos humanos. Não vejo por que uma simples portabilidade de linguagem de repente destruiria de forma irreversível a diagnosticabilidade