Voltar para o blog
BunJavaScriptNode.jsArquitetura

Bun em projeto longo: a lição da cidade de Minecraft

04 de outubro de 2026·6 min de leitura·Diego Horvatti

Mike Tomlin, técnico de NFL, passou 12 anos construindo uma cidade no Minecraft. Doze anos. Bloco por bloco. A história saiu no The Athletic e eu li pensando numa coisa que não tem nada a ver com futebol americano: o jeito como a gente adota ferramentas como o Bun em projetos que vão viver muito tempo.

Parece forçado? Um pouco. Mas fica comigo.

O que um técnico de NFL tem a ver com o seu backend

Ninguém constrói uma cidade de 12 anos num fim de semana. Você coloca uma rua. Depois uma casa. Depois percebe que a rua ficou torta e refaz só aquele trecho. A cidade nunca para de funcionar enquanto você mexe nela.

Projeto de software que dura é igualzinho. O sistema que você mantém hoje provavelmente tem código de três ou quatro "eras" diferentes. Tem um pedaço em CommonJS, outro em ESM. Tem Jest, tem um script em shell que ninguém lembra quem escreveu. E tem gente usando aquilo em produção agora, enquanto você lê isto.

É nesse cenário que aparece a vontade de trocar tudo pelo Bun. Ele é rápido, roda TypeScript direto, já vem com test runner, bundler, gerenciador de pacotes, SQLite e cliente de Postgres. A tentação de fazer um "big bang" é enorme.

Não faça.

Por que reescrever tudo para o Bun é a pior estratégia

A reescrita completa tem um problema conhecido: durante semanas você tem dois sistemas, nenhum terminado. O antigo continua recebendo correção. O novo fica sempre atrás. Quando finalmente vira a chave, aparece aquele bug que só acontecia numa combinação esquisita de dependência nativa com variável de ambiente.

E o Bun tem um detalhe que pesa aqui: a compatibilidade com Node é muito boa, mas não é 100%. Pacotes com addon nativo, coisas que dependem de comportamento específico do node: interno, ferramentas que leem process.versions e decidem caminho a partir disso. Na maioria dos projetos não dá nada. Mas "na maioria" não é "no seu".

Ninguém constrói uma cidade inteira de uma vez. Nem migra um backend.

O jeito sensato é o do Tomlin: um bloco de cada vez, com a cidade funcionando o tempo todo.

Como adotar o Bun bloco por bloco

Esta é a ordem que eu uso e recomendo. Cada passo é reversível. Se der problema, você volta um passo e segue a vida.

Bloco 1: só o gerenciador de pacotes

O passo mais barato. Você troca npm install por bun install e mantém o Node como runtime.

rm -rf node_modules package-lock.json
bun install

O Bun gera um bun.lock em texto, que dá para revisar em PR sem sofrimento. No CI, o install costuma cair de forma bem perceptível, principalmente em monorepo com muita dependência. Seu código não mudou uma linha. Se algo der errado, você restaura o lockfile antigo e pronto.

Bloco 2: os testes

O bun test aceita a API no estilo Jest: describe, it, expect, mock. Em boa parte dos casos, os testes rodam sem alteração.

import { describe, it, expect } from 'bun:test'
import { calcularFrete } from './frete'

describe('calcularFrete', () => {
  it('zera o frete acima de 300 reais', () => {
    expect(calcularFrete({ total: 350, cep: '01001000' })).toBe(0)
  })
})

Aqui você já sente a diferença no dia a dia. Teste que demora para subir é teste que ninguém roda antes do commit. Teste que roda rápido vira hábito.

Uma dica: comece por uma pasta só. Rode bun test src/utils e veja o que quebra. Normalmente é mock de módulo ou algum jest.useFakeTimers mais exótico. Resolve e expande.

Bloco 3: scripts e ferramentas internas

Sabe aquele script de seed do banco? O que migra dados? O que gera relatório? Esses são candidatos perfeitos. Ninguém acessa de fora, falha não derruba produção, e o ganho de rodar .ts direto sem ts-node nem build é imediato.

// scripts/seed.ts
import { sql } from 'bun'

const usuarios = await Bun.file('./fixtures/usuarios.json').json()

for (const u of usuarios) {
  await sql`insert into usuarios (nome, email) values (${u.nome}, ${u.email})`
}

console.log(`${usuarios.length} usuários inseridos`)

Roda com bun scripts/seed.ts. Sem config. Sem dependência nova.

Bloco 4: um serviço novo, pequeno

Só agora o Bun vira runtime de algo em produção. E não é o seu monólito. É um serviço novo, de escopo fechado: um webhook, um worker de fila, uma API interna.

Bun.serve({
  port: 3000,
  routes: {
    '/health': new Response('ok'),
    '/webhook': {
      POST: async (req) => {
        const evento = await req.json()
        await processar(evento)
        return new Response(null, { status: 204 })
      },
    },
  },
})

Se esse serviço passar alguns meses sem surpresa, aí sim você tem dado real para discutir trocar o runtime do resto.

Bloco 5: o resto, se fizer sentido

Repare no "se". Tem projeto que para no bloco 2 e está ótimo. Instalação rápida e teste rápido já pagam o esforço. Não existe medalha por usar o Bun em tudo.

O que observar antes de colocar o Bun em produção

Alguns pontos que eu checo antes de subir um serviço com Bun:

  • Dependências nativas. Rode bun install e veja se algum pacote reclama de build. bcrypt, sharp e drivers de banco antigos são os suspeitos de sempre. Para hash de senha, o Bun.password resolve sem pacote extra.
  • Imagem Docker. Use a imagem oficial oven/bun e fixe a versão. "latest" em produção é pedir para ter surpresa numa sexta à tarde.
  • Observabilidade. Confira se o seu APM e o seu logger funcionam no Bun. Alguns agentes fazem instrumentação no nível do runtime do Node e simplesmente não enxergam nada.
  • Plataforma de deploy. Hoje várias plataformas já aceitam Bun como runtime. Mesmo assim, teste o deploy num ambiente de preview antes.

Nada disso é motivo para não usar. É só o equivalente a olhar o terreno antes de colocar o primeiro bloco.

E a lição de 12 anos de Minecraft?

Imagino que a cidade do Tomlin tenha bairros feitos em épocas diferentes. Uma parte com o estilo de quando ele começou, outra com as técnicas que aprendeu depois. E está tudo bem. A cidade funciona como um todo justamente porque ele nunca tentou demolir tudo para começar de novo.

Repositório é a mesma coisa. Ter um serviço em Bun ao lado de outro em Node não é bagunça. É uma cidade em construção. O problema só aparece quando ninguém sabe o porquê de cada bloco estar ali. Por isso vale deixar um README curto ou um ADR explicando a decisão: "este worker roda em Bun porque X, e o resto continua em Node porque Y".

E tem um bônus humano. Mudança pequena é mais fácil de revisar, mais fácil de ensinar para o time e mais fácil de defender numa reunião. Ninguém aprova "vamos trocar o runtime de tudo". Quase todo mundo aprova "vamos deixar o CI mais rápido trocando o install".

Minha opinião sobre o Bun hoje

O Bun já passou da fase de brinquedo. Uso no dia a dia e não volto atrás para install e testes. Como runtime de produção, uso em serviço novo sem medo, e em sistema legado só depois de medir.

A minha opinião forte é esta: o maior risco do Bun não é técnico, é a empolgação. Ferramenta rápida dá vontade de sair reescrevendo tudo. E reescrita movida a empolgação costuma terminar com dois sistemas pela metade.

Faz como o técnico: um bloco por vez, a cidade sempre de pé. Daqui a alguns anos você olha para trás e percebe que migrou quase tudo sem nenhuma noite perdida. Se quiser ver como eu venho aplicando isso nos meus projetos, dá uma olhada no que estou construindo.

Resumo para o LinkedIn

Um técnico da NFL levou 12 anos para construir uma cidade no Minecraft. Bloco por bloco, sem nunca demolir tudo.

Pensei nisso quando vi a vontade que bate em todo time de reescrever o backend inteiro no Bun.

O Bun é rápido e já vem com test runner, bundler e SQLite. Justamente por isso a empolgação é o maior risco.

Comigo funciona nesta ordem: primeiro o bun install, depois os testes, depois os scripts internos, e só então um serviço novo e pequeno em produção.

Cada passo dá para desfazer. E tem projeto que para no segundo e já está ótimo.

Escrevi o passo a passo completo no blog, com o checklist que eu uso antes de subir para produção. Se você está pensando em migrar, vale a leitura.

#Bun #JavaScript #TypeScript #NodeJS #DesenvolvimentoDeSoftware