ADR-0035 Runtime e toolchain
Padrão da empresa para runtime e toolchain — o Bun como runtime, gestor de pacotes, workspaces e executor de testes, com preferência pelas APIs built-in do Bun em vez de pacotes; Oxlint, Oxfmt e Vite ficam para o que o Bun não faz.
- Decisão
- Bun, built-ins primeiroEntregar e operar
- Estado
- Aceite
- Data
- Âmbito
- Predefinição da empresa para todas as apps e ferramentas
Ligações
Apoia-se em
Contexto
Seção intitulada “Contexto”O ADR-0006 escolheu Node LTS, pnpm e Vite+, e rejeitou o Bun por “não ser o runtime de referência”. Desde então foi lançado o Bun 1.4, que já cobre várias coisas para as quais instalamos pacotes:
- Um só binário executa a app, instala dependências, corre scripts do workspace e corre testes
- Clientes built-in: um cliente PostgreSQL (
Bun.SQL), um cliente S3 (Bun.S3Client), um cliente Redis, hashing de palavras-passe (Bun.password, argon2id), carregamento de.env, uma shell (Bun.$), globbing e hashing - Workspaces e catálogos no
package.jsonda raiz, instalações isoladas por predefinição nos novos workspaces, e lifecycle scripts bloqueados a menos que o pacote esteja emtrustedDependencies - O ecossistema que usamos corre nele: o Bun documenta o TanStack Start (o preset
bundo Nitro) e o Drizzle, o Drizzle v1 tem um driverbun-sql, o Better Auth corre no Bun, o BullMQ lista o Bun, e o Testcontainers corre um smoke test com Bun no CI - Uma imagem distroless oficial,
oven/bun:<version>-distroless, sem shell e sem gestor de pacotes
Cada pacote que o Bun dispensa é uma dependência a menos para atualizar e auditar. As lacunas do Bun também são reais, e são elas que decidem onde param os built-ins.
Opções consideradas
Seção intitulada “Opções consideradas”1. Manter Node LTS + pnpm + Vite+ (ADR-0006)
Seção intitulada “1. Manter Node LTS + pnpm + Vite+ (ADR-0006)”Prós: o runtime de referência; a auto-instrumentação do OpenTelemetry funciona; o modo de browser real do Vitest; uma linha LTS. Contras: cada driver e cada cliente é um pacote; três ferramentas para instalar e fixar (Node, pnpm, Vite+); o Vite+ é jovem e a sua CLI é mais uma coisa para aprender.
2. O Bun só como gestor de pacotes, sobre o Node
Seção intitulada “2. O Bun só como gestor de pacotes, sobre o Node”Prós: instalações rápidas, mantém a compatibilidade do Node. Contras: outra vez duas ferramentas, e nenhum dos built-ins do runtime — perde-se a maior parte do ganho.
3. O Bun para runtime, pacotes, workspaces e testes
Seção intitulada “3. O Bun para runtime, pacotes, workspaces e testes”Prós
- Uma só ferramenta, do portátil à imagem de produção
- As APIs built-in de Postgres, S3, Redis e palavras-passe substituem pacotes de drivers e de SDKs
- O
bun.locké texto e permite diffs; os lifecycle scripts são opt-in por pacote - Uma imagem de runtime distroless oficial
Contras
- Pouca auto-instrumentação do OpenTelemetry. Desde a 1.4, as instrumentações
httpefsdo OTel funcionam no Bun paranode:httpenode:fs. Mas o servidor do Start corre emBun.serve, não emnode:http; oBun.SQLe ofetchdo Bun não são bibliotecas em que alguma instrumentação faça patch; e o Bun tratamodule.register, o hook de loader ESM, como um no-op. O tracing nativo (Bun.otel) é um pull request em aberto (oven-sh/bun#39965). Os spans de pedidos, de queries e defetchvêm de três pequenos wrappers nossos até chegar (ADR-0015) - Sem modo de testes em browser real. O
bun testcorre os componentes em happy-dom; o comportamento de foco, teclado e ponteiro passa para o Playwright (ADR-0014) - Sem linter, formatador nem verificador de tipos: Oxlint, Oxfmt e
tscficam, como simples dev dependencies - Não substitui o Vite: o TanStack Start é construído sobre o Vite, pelo que o
vite buildfica, executado pelo Bun - Sem linha LTS: uma só linha de releases, pelo que a versão é fixada exatamente e atualizada de forma deliberada
- Lacunas de compatibilidade com o Node: hooks de
node:module, partes denode:async_hooks, algumas opções denode:worker_threads. O Playwright corre no Bun desde a 1.4, mas o próprio projeto do Playwright lista o suporte ao Bun como não planeado
4. Deno
Seção intitulada “4. Deno”Prós: permissões seguras por predefinição, tooling built-in. Contras: sem configuração documentada do TanStack Start, e a compatibilidade com o npm é um modo e não a predefinição.
Decisão
Seção intitulada “Decisão”O Bun é o runtime, o gestor de pacotes, a ferramenta de workspaces e o executor de testes da empresa, em desenvolvimento local, no CI e na imagem de produção (ADR-0003). Quando o Bun tem um built-in para alguma coisa, usamo-lo em vez de um pacote — exceto quando o built-in não consegue cumprir o requisito de outro ADR; essas exceções estão em “Onde um pacote fica”.
Em concreto:
-
Versão: Bun 1.4.x (1.4.4 à data de escrita), fixado exatamente no
package.jsonda raiz como"packageManager": "bun@1.4.4". O CI instala essa versão, e as imagens Docker usam a mesma versão, por digest — a imagem de runtime éoven/bun:<version>-distroless, executada com um utilizador não-root (ADR-0016). O Renovate atualiza o campo e as tags das imagens em conjunto -
Pacotes:
bun install; obun.locké o único lockfile e entra no commit; o CI e o Docker corrembun ci, que falha quando o lockfile está desatualizado. Nunca npm, pnpm nem yarn -
Workspaces:
workspaces.packageseworkspaces.catalognopackage.jsonda raiz (ADR-0026), com o linker isolado.bun run --filtertem como alvo um só pacote -
Os lifecycle scripts continuam bloqueados.
trustedDependenciesestá vazio por predefinição; acrescentar lá um pacote é revisto como uma dependência nova. Os pacotes pré-1.0 acrescentam-se combun add --exact -
As tarefas são scripts do
package.json, corridos combun run:Script Corre devbun --bun vite devbuildbun --bun vite buildcheckoxfmt --check . && oxlint && tsc --noEmitfixoxfmt . && oxlint --fixtestbun test(ADR-0014) -
Built-ins que usamos em vez de pacotes:
Necessidade Built-in Substitui Cliente Postgres Bun.SQL, através dedrizzle-orm/bun-sql(ADR-0008)pgArmazenamento compatível com S3 Bun.S3Client(ADR-0030)@aws-sdk/client-s3, presignerHashing de palavras-passe Bun.password, argon2id (ADR-0009)o scrypt predefinido do Better Auth Testes, mocks, cobertura bun test(ADR-0014)Vitest, @vitest/coverage-v8Ficheiros .envem desenvolvimentocarregamento automático dotenvScripts, ficheiros, globs, hashes Bun.$,Bun.file,Bun.Glob,Bun.hashexeca,fs-extra,glob -
Onde um pacote fica, e porquê:
ioredis— orate-limiter-flexiblee o@better-auth/redis-storagesó aceitam os clientes que listam, e o cliente Redis do Bun não é um deles; um só cliente para o Valkey vale mais do que dois (ADR-0010)- Vite — o TanStack Start é um plugin do Vite (ADR-0002)
- Oxlint, Oxfmt, TypeScript — o Bun não tem linter, formatador nem verificador de tipos
- Nodemailer e pino — o Bun não tem cliente SMTP nem logger (ADR-0029, ADR-0015)
- O pipeline de Markdown — o
Bun.markdownestá marcado como instável (ADR-0021) - O
createCsrfMiddleware()do Start — verificaSec-Fetch-SiteeOriginsem tokens; oBun.CSRFemite tokens, um desenho diferente (ADR-0013) - Playwright — o
Bun.WebViewcontrola um browser headless, mas não tem executor de testes, traces, execuções cross-browser nem integração com o axe; o Playwright corre no Bun desde a 1.4 (ADR-0014)
-
O código de servidor importa de
bunsó dentro desrc/server/(ADR-0026), pelo que o build do browser nunca o vê
Os fatores decisivos: uma ferramenta em vez de três, menos pacotes para manter atualizados, clientes built-in de Postgres e S3, lifecycle scripts bloqueados por predefinição e uma imagem distroless oficial. Aceitamos manter os nossos próprios spans de pedidos, de queries e de fetch até o tracing nativo do Bun chegar, testes de componentes num DOM simulado e um runtime sem linha LTS.
Consequências
Seção intitulada “Consequências”Mais fácil:
- Um portátil, um runner de CI e a imagem precisam de um só binário, numa só versão fixada
- Menos dependências: sem driver de Postgres, sem AWS SDK, sem executor de testes, sem
dotenv - As instalações são rápidas, e nenhum pacote corre scripts de instalação a menos que o tenhamos listado
Mais difícil:
- Os spans de pedidos, de queries e de
fetchsão wrappers nossos até oBun.otelchegar (ADR-0015); o Valkey, onode:http, os jobs, as chamadas de IA e os logs usam instrumentação mantida. Rever quando chegar - Uma lacuna de compatibilidade com o Node numa dependência fica por nossa conta, para contornar ou reportar
- O Bun evolui depressa: ler as notas de lançamento em cada atualização e correr a suite de testes completa antes de fazer merge
- Os exemplos online das nossas bibliotecas assumem quase sempre Node e pnpm
Do que abdicamos: do estatuto de runtime de referência do Node e da instrumentação OpenTelemetry mantida para o servidor HTTP, o cliente Postgres e o fetch, do modo de browser real do Vitest e do comando único vp do Vite+. Aceite — um runtime com clientes built-in é mais simples de operar e de proteger do que um runtime mais os pacotes que lhe faltam.

