Node.js para Bun: transição com benchmark real, compatibilidade e rollback
Voltar para blog

Node.js para Bun: transição com benchmark real, compatibilidade e rollback

07/06/2026 · 10 min · Desenvolvimento

Migrar um workload em produção de Node.js para Bun sem um método de engenharia rigoroso é uma receita para trocar gargalos de performance por incidentes graves de compatibilidade. O Bun promete velocidades de execução extraordinárias, consumo reduzido de memória e carregamento instantâneo, mas sua fundação no motor JavaScriptCore (JSC) do Safari - ao contrário do motor V8 do Chrome usado pelo Node.js - introduz divergências comportamentais e incompatibilidades sutis em dependências de baixo nível.

Abaixo, detalho o runbook completo para realizar a transição de forma incremental, cobrindo validação de dependências nativas, suporte a TypeScript, benchmarks reais com monitoramento SRE, testes de regressão, segurança, pipelines de CI/CD e uma estratégia robusta de rollback automático para mitigação de riscos em produção.

Critério TécnicoNode.js (V8)Bun (JavaScriptCore - JSC)Impacto na Transição
Versão Mínima18+ (LTS recomendado)1.0+ (estável)Pré-requisito de estabilidade
Inicialização (Startup)Média (carregamento V8 mais lento)Instantânea (JSC otimizado)Redução de latência em Serverless/Cold Starts
Instalação de DependênciasNPM / Yarn / PNPM (velocidade variada)Nativo (bun install, extremamente rápido)Redução drástica de tempo de build em CI/CD
Suporte a TypeScriptExige transpilação externa (tsc / esbuild)Suporte nativo out-of-the-boxElimina etapas e ferramentas extras de build
API de WebSocketsDepende de pacotes externos (ws, socket.io)Nativa, de alta performance (Bun.serve)Redução no uso de CPU e overhead de pacotes
Variáveis de AmbienteRequer bibliotecas (dotenv)Suporte nativo a arquivos .envSimplificação no bootstrap do projeto

1. Versões mínimas requeridas e compatibilidade de runtimes#

Antes de iniciar qualquer alteração, é mandatório estabelecer o baseline de versões dos runtimes. A migração só é tecnicamente viável e estável se o ecossistema estiver atualizado.

Execute os seguintes comandos para verificar as versões locais do ambiente:

# Verificar a versão ativa do Node.js
node --version

# Verificar a versão instalada do Bun
bun --version

Atualização e auditoria de dependências#

Antes da transição, use o gerenciador do Node para mapear pacotes obsoletos que possam gerar conflitos no novo runtime:

# Listar dependências desatualizadas no ambiente Node.js
npm outdated

Pós-migração para o Bun, para atualizar as dependências mapeadas no arquivo de lockfile do Bun, execute:

# Atualizar as dependências do projeto sob o gerenciador do Bun
bun update
AçãoComando Node.js (NPM)Comando Bun
Instalar Dependênciasnpm installbun install
Adicionar Pacotenpm install <package>bun add <package>
Remover Pacotenpm uninstall <package>bun remove <package>
Executar Scriptnpm run <script>bun run <script>
Executar Binário Localnpx <command>bunx <command>

2. Seleção de serviço candidato e auditoria de dependências#

Não inicie a migração por serviços críticos monolíticos que processem pagamentos, autenticação ou fluxos de dados sensíveis. O ideal é selecionar microsserviços simples, workers em lote (batch queue consumers) ou APIs stateless que possuam alta cobertura de testes e isolamento via containers.

Auditoria de dependências nativas (node-gyp)#

O maior obstáculo de compatibilidade são as bibliotecas que dependem do node-gyp para compilar código C/C++ nativo na instalação. Embora o Bun possua uma camada de compatibilidade para a API de C++ do Node (N-API), dependências complexas frequentemente falham no processo de compilação ou sofrem crash silencioso em runtime.

Use o jq para analisar o seu package.json e identificar dependências nativas críticas:

# Escanear dependências que comumente usam compilação nativa
cat package.json | jq '.dependencies | to_entries[] | select(.key | contains("native") or contains("gyp") or contains("sqlite3") or contains("bcrypt") or contains("canvas") or contains("sharp"))'

Alternativamente, execute uma busca recursiva no diretório node_modules para localizar arquivos e referências ao compilador node-gyp:

# Identificar pacotes que usam node-gyp para build de bindings nativos
grep -r "node-gyp" package.json node_modules/*/package.json 2>/dev/null

Auditoria de lockfiles#

O Bun utiliza um formato binário de alta performance para o seu lockfile (bun.lockb). Verifique a presença dos arquivos de controle do projeto:

# Inspecionar os lockfiles de dependências do repositório
ls -la bun.lockb package-lock.json yarn.lock 2>/dev/null

Se você estiver migrando um projeto, o bun install lerá o package-lock.json ou yarn.lock existente para replicar a árvore exata de versões, gerando em seguida o bun.lockb.


3. Suporte a typescript sem transpilação externa#

Ao contrário do Node.js, que exige ferramentas como ts-node, tsx, ou uma etapa separada de transpilação via tsc/esbuild antes de rodar o código em produção, o Bun executa arquivos .ts e .tsx diretamente. Ele possui um transpiltador nativo integrado no motor JavaScriptCore que analisa a sintaxe do TypeScript e a executa de forma transparente, sem overhead perceptível de startup.

Verificação do tsconfig.json#

Embora o Bun ignore a maior parte das configurações de compilação do tsconfig.json (pois ele executa o código diretamente sem gerar arquivos intermediários), as diretivas de resolução de caminhos (paths) e imports devem estar alinhadas. Valide o arquivo de configuração:

# Verificar se o arquivo de configuração do TypeScript existe
ls -la tsconfig.json 2>/dev/null

Validação de tipos e build#

Como o Bun não realiza checagem de tipos estática em runtime (ele apenas remove as anotações de tipo para executar o JavaScript), erros de tipagem podem passar despercebidos. Para garantir a integridade do código em nível estático, utilize o compilador oficial do TypeScript para validação sem emitir código:

# Executar validação estática de tipos do TypeScript sem gerar arquivos
npx tsc --noEmit

Para testar o empacotamento ou build final otimizado do projeto definido nos scripts do seu package.json, execute:

# Testar a rotina de build do projeto sob o Bun
bun run build

4. Monitoramento e benchmarks em ambientes reais#

Para evitar o autoengano de benchmarks em laboratórios artificiais, as comparações de performance devem ocorrer no mesmo host físico ou container sob limites rígidos de CPU e memória.

Testes de carga com autocannon#

Utilize o autocannon para disparar carga controlada contra a sua aplicação e comparar métricas fundamentais como throughput (requisições por segundo) e latência de resposta:

# Executar teste de carga de 30 segundos com 100 conexões simultâneas
npx autocannon -c 100 -d 30 http://localhost:3000/health

Para extrair apenas a latência p99 para relatórios SRE, processe a saída com jq:

# Filtrar o relatório para exibir a latência de percentil p99 em milissegundos
npx autocannon -c 100 -d 30 http://localhost:3000/health --json | jq '.latency.p99'

Monitoramento de recursos de sistema#

Durante a execução do benchmark sob carga, capture a memória residente real (RSS) e o consumo de CPU do runtime do Bun em tempo real:

# Monitorar PID, RSS (em KB), VSZ e uso de CPU do processo Bun ativo
ps -p $(pgrep -f "bun run") -o pid,rss,vsz,%cpu,cmd

Validação de código de retorno HTTP#

Certifique-se de que a migração não está gerando erros HTTP silenciosos (5xx ou crashes repentinos). Teste o endpoint de health check:

# Verificar se o código de status HTTP retornado é exatamente 200 OK
curl -s http://localhost:3000/health -w "%{http_code}" -o /dev/null

Auditoria de logs de erros#

Monitore ativamente os logs do sistema em busca de exceções não capturadas, segmentation faults ou pânicos de runtime:

# Monitorar logs em tempo real em busca de crashes ou falhas críticas de runtime
tail -f /var/log/app/error.log | grep -iE "error|crash|panic|segfault"

Tabela comparativa de performance típica#

MétricaNode.js BaselineBun CandidateVeredito
Throughput (req/sec)12.50028.900Bun (+131%)
Latência p9518 ms6 msBun (3x mais rápido)
Latência p9942 ms11 msBun (Melhor estabilidade)
Uso de Memória (Startup)78 MB31 MBBun (-60%)
Uso de Memória (Sob Carga)180 MB92 MBBun (-48%)
Tempo de Cold Start180 ms15 msBun (Melhor p/ Serverless)
Taxa de Erro HTTP 5xx0.00%0.00%Equivalente

5. Gestão avançada de variáveis de ambiente#

O Bun elimina a necessidade de carregar bibliotecas externas como dotenv ou dotenv-expand para ler arquivos .env. O runtime faz o parse desses arquivos automaticamente no bootstrap da aplicação.

Por padrão, o Bun busca e injeta variáveis dos seguintes arquivos na ordem de precedência decrescente:

  1. .env.local
  2. .env.production ou .env.development (baseado em NODE_ENV)
  3. .env

Para forçar explicitamente o carregamento de um arquivo .env específico, utilize a flag --env-file:

# Executar a aplicação forçando a injeção de variáveis de um arquivo customizado
bun run --env-file=.env.production src/server.ts

Alternativamente, ao utilizar scripts definidos no package.json, você pode anexar a flag diretamente ao executável:

# Executar script start injetando variáveis de ambiente customizadas
bun --env-file=.env run start

No código JavaScript/TypeScript, a recuperação das variáveis é retrocompatível com o Node.js (process.env.VARIAVEL), mas o Bun também oferece o atalho nativo e mais rápido import.meta.env.VARIAVEL.


6. WebSockets nativos: alta performance vs bibliotecas do node#

No ecossistema Node.js, a implementação de WebSockets de alta performance requer o uso de dependências externas como ws ou socket.io. No Bun, o protocolo WebSocket é suportado nativamente na engine do servidor HTTP através de Bun.serve(), implementado diretamente em C++ sobre a biblioteca uWebSockets.

Varredura de código por dependências WebSockets#

Para identificar se o projeto atual faz uso de bibliotecas externas de WebSockets, execute:

# Pesquisar o uso de bibliotecas de WebSockets no código e configurações
grep -r "WebSocket\|socket.io\|ws" package.json src/

Exemplo prático de servidor WebSocket com Bun#

Abaixo está o exemplo funcional de um servidor que trata conexões WebSocket de forma nativa e otimizada:

// src/websocket-server.ts
Bun.serve({
  port: 3000,
  fetch(req, server) {
    // Fazer upgrade da requisição HTTP regular para uma conexão WebSocket
    const success = server.upgrade(req);
    if (success) {
      return undefined; // Sucesso no upgrade
    }
    return new Response("Upgrade falhou", { status: 400 });
  },
  websocket: {
    open(ws) {
      console.log(`Cliente conectado: ${ws.remoteAddress}`);
      ws.subscribe("canal-global");
    },
    message(ws, message) {
      console.log(`Mensagem recebida: ${message}`);
      // Echo da mensagem para todos os inscritos no canal
      ws.publish("canal-global", `Echo: ${message}`);
    },
    close(ws, code, message) {
      console.log(`Cliente desconectado. Código: ${code}`);
      ws.unsubscribe("canal-global");
    }
  }
});
console.log("Servidor WebSocket nativo rodando na porta 3000");

Testando a conexão WebSocket#

Utilize o utilitário wscat para validar interativamente o funcionamento do servidor de WebSockets:

# Conectar ao servidor WebSocket local para testar mensageria
npx wscat -c ws://localhost:3000/ws

7. Suporte a HTTP/2 nativo#

O suporte a HTTP/2 no Bun é nativo e integrado diretamente à API Bun.serve. Ao configurar chaves de segurança TLS (certificados SSL), o servidor negocia automaticamente conexões HTTP/2 via ALPN sem necessidade de módulos complexos de terceiros como http2 ou spdy, comuns no Node.js.

Varredura de módulos HTTP/2 legados#

Identifique se a aplicação Node.js legada depende de bibliotecas obsoletas ou específicas de HTTP/2:

# Buscar referências a pacotes ou módulos http2/spdy
grep -r "http2\|spdy" package.json src/

Validando HTTP/2 em produção#

Após subir o serviço com Bun, valide se o handshake e a multiplexação HTTP/2 estão ativos através do curl:

# Inspecionar cabeçalhos de resposta HTTP validando o protocolo HTTP/2
curl --http2 -I https://localhost:3000/

8. Testes de regressão e cobertura de código#

O Bun inclui um executor de testes integrado e extremamente rápido (bun test) compatível com as principais APIs do Jest e Vitest. Isso elimina a necessidade de instalar e configurar frameworks complexos de testes em JavaScript.

Execução de testes#

Para rodar toda a suíte de testes do projeto utilizando o Bun:

# Executar todos os arquivos de teste correspondentes (*.test.ts, *.spec.js, etc.)
bun test

Filtragem de testes#

Se você deseja executar apenas uma suíte ou testes específicos baseados em padrões de nome, utilize a flag --filter:

# Executar apenas testes que correspondam à string "auth"
bun test --filter "auth"

Geração de relatórios de cobertura#

Para auditar a cobertura do código de forma nativa e sem necessidade de ferramentas como Istanbul/c8:

# Executar testes gerando relatório de cobertura direto no terminal
bun test --coverage

Comparação com Node.js test runner#

O Node.js 18+ também introduziu um test runner nativo (node --test), mas ele não possui asserções embutidas (exigindo importação do módulo node:assert) nem transpilação nativa de TypeScript.

# Executar testes nativos no Node.js (requer transpilação prévia para TypeScript)
node --test

9. Integração CI/CD completa (GitHub actions)#

A migração de runtime deve ser validada a cada commit e pull request para evitar regressões comportamentais ou quebras de build. O exemplo abaixo apresenta um pipeline completo e otimizado para o GitHub Actions, utilizando a action oficial do Bun.

Crie ou atualize o arquivo .github/workflows/migrate-to-bun.yml:

name: Pipeline de CI/CD - Validação Bun

on:
  push:
    branches: [ main, master ]
  pull_request:
    branches: [ main, master ]

jobs:
  validate-and-build:
    runs-on: ubuntu-latest

    steps:
      - name: Checkout do Código
        uses: actions/checkout@v4

      - name: Configurar Ecossistema Bun
        uses: oven-sh/setup-bun@v1
        with:
          bun-version: latest # Ou travar na versão estável exata, ex: 1.1.0

      - name: Instalação de Dependências
        run: bun install --frozen-lockfile

      - name: Validação Estática de Tipos (TypeScript)
        run: npx tsc --noEmit

      - name: Execução da Suíte de Testes
        run: bun test --coverage

      - name: Compilação de Produção
        run: bun run build

10. Segurança e auditoria de dependências#

O gerenciador de pacotes do Bun possui um utilitário nativo de auditoria de segurança extremamente rápido para mapear vulnerabilidades conhecidas na árvore de dependências (CVEs).

Auditoria de vulnerabilidades#

Para executar uma varredura de segurança contra o banco de dados oficial do npm sob o Bun:

# Auditar dependências em busca de falhas de segurança conhecidas
bun audit

Se precisar comparar ou rodar de forma clássica com o npm do Node.js:

# Executar auditoria de vulnerabilidades via NPM clássico
npm audit

Auditoria de licenças e pacotes suspeitos#

Para listar todas as dependências instaladas, incluindo dependências indiretas, facilitando a identificação de pacotes maliciosos (malwares), typo-squatting ou licenças restritivas incompatíveis com as diretrizes do seu negócio:

# Listar todas as dependências da árvore com informações detalhadas
bun pm ls --all

Você pode filtrar a saída para validar licenças ou dependências suspeitas:

# Filtrar dependências instaladas em busca de termos de licença
bun pm ls --all | grep -i "license"

Estratégia de rollout, rollback e dockerfile hardened#

Para garantir que a transição ocorra de forma segura, o deploy deve ser feito em containers isolados utilizando boas práticas de segurança (usuário não-root e bloqueio de dependências).

Dockerfile otimizado para produção (Bun)#

Este Dockerfile multi-stage garante uma imagem leve, com cache otimizado e rodando sob o usuário de sistema restrito bun:

# Estágio 1: Instalação de Dependências
FROM oven/bun:1.1-alpine AS base
WORKDIR /usr/src/app

# Copiar arquivos de dependências
COPY package.json bun.lockb ./
RUN bun install --frozen-lockfile

# Estágio 2: Build da Aplicação
FROM base AS builder
WORKDIR /usr/src/app
COPY . .
RUN bun run build

# Estágio 3: Execução da Imagem de Produção (Sem privilégios de root)
FROM oven/bun:1.1-alpine AS release
WORKDIR /usr/src/app

# Copiar apenas os artefatos necessários
COPY --from=builder /usr/src/app/package.json ./
COPY --from=builder /usr/src/app/node_modules ./node_modules
COPY --from=builder /usr/src/app/src ./src

# Expor porta do serviço
EXPOSE 3000

# Executar com usuário de privilégios mínimos
USER bun
ENTRYPOINT ["bun", "run", "src/server.ts"]

Configuração de Docker compose com estratégia de rollback#

Para permitir o retorno imediato ao Node.js em caso de anomalias detectadas no runtime do Bun em produção, configure seu docker-compose.yml mantendo o contêiner Node inativo, mas pronto para ser promovido instantaneamente via proxy reverso ou DNS:

version: "3.8"

services:
  # Serviço Primário executando no Bun
  app-bun:
    build:
      context: .
      dockerfile: Dockerfile
    image: minha-app:bun-latest
    container_name: app-prod-bun
    ports:
      - "127.0.0.1:3000:3000"
    environment:
      - NODE_ENV=production
    restart: always
    healthcheck:
      test: ["CMD", "curl", "-f", "http://localhost:3000/health"]
      interval: 10s
      timeout: 5s
      retries: 3

  # Serviço Secundário de Rollback executando no Node.js
  app-node:
    image: node:18-alpine
    container_name: app-prod-node
    volumes:
      - .:/app
    working_dir: /app
    command: ["node", "src/server.js"]
    ports:
      - "127.0.0.1:3001:3000"
    environment:
      - NODE_ENV=production
    restart: no
    profiles:
      - rollback

Se o SLO de latência ou taxa de erro do Bun degradar, execute o comando de rollback imediato sem recompilar código no momento de crise:

# Parar o container Bun e subir a imagem estável de segurança do Node
docker compose stop app-bun && docker compose --profile rollback up -d app-node

Checklist operacional de migração#

Siga rigorosamente as etapas abaixo antes, durante e após a promoção do Bun para produção:

Fase 1: Pré-migração#

Fase 2: Instalação e testes locais#

Fase 3: Benchmark e validação de carga#

Fase 4: Integração de CI/CD e infraestrutura#

Fase 5: Rollout e pós-deploy#

Este artigo foi útil?

Deixe uma reação rápida para apoiar o conteúdo:

CC BY-NC

Este post está licenciado sob CC BY-NC.

Comentários

Participe da discussão abaixo.

0 comentários