Sacar un producto pequeño casi nunca se traba por “cuál framework es el mejor”. Se traba porque te comes una semana armando el proyecto: monorepo, API tipada, auth, ORM, lint, package manager, deploy. Cada día que pasas re-decidiendo eso es un día en el que no estás probando si el producto importa.
Mi atajo para no caer ahí otra vez es Better-T-Stack (create-better-t-stack). No es religión. Es un scaffold que me deja un monorepo TypeScript type-safe en un punto donde puedo shippear la primera feature real el mismo día.
Este post no es un tour de todas las opciones del CLI. Es lo que yo de verdad elijo cuando arranco (o reinicio) un proyecto indie — y cómo se juntan esas piezas cuando el scaffold ya cumplió su trabajo.
Para qué sirve un scaffold
Un buen starter hace tres cosas:
- Te quita el trabajo aburrido que no quieres repetir — workspaces, TS compartido, scripts, carpetas
apps/ypackages/. - Deja los tipos alineados desde el día uno — web, server y DB no se pelean entre ellos a la semana.
- Se puede borrar sin drama — si una decisión salió mal, la sacas sin reescribir el producto.
Better-T-Stack es interactivo (o todo por flags). Eliges frontend, backend, runtime, base de datos, ORM, API, auth, pagos y addons. La salida es un monorepo de verdad, no una landing con un // TODO.
En esos repos dejo un bts.jsonc. Es una foto de lo que elegí al crear el proyecto. Se puede borrar; me sirve para recordar por qué el árbol se ve así.
Qué suelo elegir
Los combos cambian por producto. Los patrones se repiten.
Package manager y monorepo: Bun + Turborepo
En la mayoría de mis scaffolds recientes uso Bun (a veces pnpm). Turborepo entra casi siempre que hay más de una app en el mismo repo: web, server, a veces native, paquetes compartidos.
Realidad indie: una persona, varias superficies. Un monorepo con task graph le gana a cinco carpetas a medio sincronizar.
Backend: Hono (casi siempre en Bun o Workers)
Hono es lo que uso cuando quiero un backend pequeño y explícito. En unos proyectos corre en Bun; en otros, en Cloudflare Workers por el path del stack. No es “edge porque suena cool”. Es HTTP delgado sin pelearte con un framework el día uno.
Datos: Postgres + Drizzle (o SQLite/D1 si el deploy lo pide)
Cómo lo pienso:
| Si necesito… | Suelo ir por… |
|---|---|
| Una DB relacional de verdad en local | Postgres + Drizzle, casi siempre con Docker |
| Deploy con cara Cloudflare / edge | SQLite + D1 + Drizzle, cuando calza con el target |
Drizzle se queda porque el schema vive en TypeScript y las migraciones se pueden revisar. Solo en el producto no quiero una capa mágica de “models” que no entiendo.
Contrato de API: oRPC
Cuando el scaffold ofrece RPC tipado, casi siempre tomo oRPC. Compartir tipos entre client y server mata una clase entera de bugs del tipo “la API devolvió otra cosa” — sobre todo si web y server viven en el mismo monorepo.
No todo proyecto necesita RPC el día uno. Si la superficie es simple, rutas Hono planas bastan. Cuando el producto crece y hay varios call sites, los tipos a través del cable valen la pena.
Auth: Better Auth cuando hay cuentas
Better Auth lo activo cuando el producto tiene usuarios de verdad (no todo MVP los tiene). En el scaffold es opcional; dejo auth: none hasta que el login sea un requisito real. Auth muy temprano te distrae. Auth muy tarde es un rewrite.
Pagos: Polar cuando el producto vende
Cuando el stack trae pagos, he usado Polar con Better Auth si esa integración es el punto. Solo cuando el dinero está en juego. Montar toda la parafernalia de Stripe/Polar antes de que alguien quiera pagar es una forma clásica de matar un side project por configuración.
Frontend: depende del producto
Better-T-Stack no es “un solo frontend”. Lo que he scaffolded de verdad:
- TanStack Start — React full-stack cuando quiero la app web y el routing en un solo lugar
- TanStack Router — web SPA cuando el server es otra app
- Next.js — cuando el producto se beneficia de ese ecosistema (o ya vive ahí)
- React Native (NativeWind / UniWind) — cuando mobile es parte de la apuesta
El truco no es seguir la tendencia de la semana en Twitter. Es alinear el frontend con cómo va a encontrar el producto la gente.
Lint y agentes: Biome / Ultracite / Oxlint, Ruler, Skills
Addons que se me repiten:
- Biome o Ultracite (a veces Oxlint) — lint/format rápido sin un ESLint de siete plugins
- Ruler — mismas reglas de editor/agentes en todo el monorepo
- Skills (cuando el scaffold las trae) — contexto del proyecto para que los agentes no inventen otra arquitectura
No colecciono linters por deporte. Quiero un check de un solo comando y que la IA lea las mismas convenciones que yo.
Cómo se juntan las piezas
create-better-t-stack
│
▼
monorepo (Turborepo)
├── apps/web (TanStack / Next / …)
├── apps/server (Hono)
├── apps/native (opcional)
└── packages/* (db, auth, tipos compartidos)
│
├── schema Drizzle → Postgres / D1
├── oRPC o rutas HTTP
├── Better Auth (cuando hace falta)
└── Polar (cuando el dinero es real)
El scaffold dibuja ese diagrama en archivos. Después el trabajo mío es producto: un flujo que duela, un camino a plata o a aprendizaje — no arquitectura infinita.
Lo que no meto en todo proyecto
- Auth el día cero si el MVP es single-player o un waitlist.
- Pagos antes de que alguien pida pagar.
- Native porque “tal vez algún día app”.
- Todos los addons del CLI. Turborepo + un lint + tipos alcanza para v0.
- Un segundo ORM “por si acaso”. Drizzle (o ninguno) y a moverse.
El error es tratar Better-T-Stack como carrito de compras. Es una configuración de partida, no un ranking de qué tan moderno te ves.
Cuándo no lo usaría
- Un sitio de contenido (este blog es AstroPaper, no un monorepo BTS).
- Un script de un archivo o un CLI sin web.
- Un proyecto de cliente con stack impuesto por otro.
- Cuando solo estoy tanteando una idea de API y un
index.tssolo es más honesto.
Los scaffolds ayudan cuando vas a vivir meses en el repo. Sobran cuando la idea muere en un fin de semana — y está bien. No hay que romantizar ninguno de los dos finales.
Receta de arranque (sin sobrepensar)
Si mañana levantara otro SaaS pequeño, esto escribiría sin pensarlo mucho:
- Better-T-Stack interactivo (o el comando reproducible que imprime)
- Bun + Turborepo
- Backend Hono
- Drizzle + Postgres (Docker en local) o D1 si el deploy es Cloudflare-first
- oRPC si client y server comparten monorepo
- TanStack Start o TanStack Router en web (Next solo con razón concreta)
- Better Auth solo si hay cuentas de verdad
- Biome / Ultracite u Oxlint — un solo camino de lint
- Shippea un solo loop visible para el usuario antes de Polar, native o una segunda app
Después borra lo del scaffold que no entiendas. Si no puedes explicar un paquete en una frase, no va en v0.
La frase para recordar
Arranca con defaults type-safe. Quédate con lo que te hace más rápido y más honesto. El resto vuelve cuando el producto lo pida — no antes.
Better-T-Stack es donde arranco la mayoría de mis apps indie en TypeScript. Las herramientas de adentro —Hono, Drizzle, oRPC, Better Auth, Turborepo, Bun, TanStack, lint— solo se quedan si el siguiente commit sigue siendo más fácil con ellas.
Builder: better-t-stack.dev · CLI: create-better-t-stack · GitHub