Shipping small products is rarely blocked by “which framework is best.” It’s blocked by setup tax: monorepo layout, typed API, auth, ORM, lint, package manager, deploy shape. Every week you spend re-deciding those is a week you’re not testing whether the product matters.
My default answer to that tax is Better-T-Stack (create-better-t-stack). It isn’t a religion. It’s a scaffold that gets a type-safe TypeScript monorepo to a place where I can ship the first real feature the same day.
This post is not a feature tour of every CLI option. It’s the subset I actually pick when I start (or restart) indie projects—and how those tools fit together once the scaffold is gone.
The job of a scaffold
A good starter does three things:
- Removes irreversible busywork — workspace layout, shared TS config, package scripts, a place for
apps/andpackages/. - Locks end-to-end types early — so the web app, the server, and the DB layer don’t drift into three different stories.
- Leaves room to delete — if a choice is wrong, you can rip it out without rewriting the whole product.
Better-T-Stack is interactive (or fully flag-driven). You choose frontend, backend, runtime, database, ORM, API layer, auth, payments, and addons. The output is a real monorepo, not a demo page with a TODO comment.
I keep a bts.jsonc in those repos. It’s a snapshot of the choices at scaffold time. Safe to delete; useful as a breadcrumb of why the tree looks the way it does.
What I usually pick
Exact combos change by product. Patterns repeat.
Package manager and monorepo: Bun + Turborepo
Most of my recent scaffolds land on Bun as package manager (sometimes pnpm). Turborepo shows up almost every time I care about more than one app in the same repo: web, server, sometimes native, shared packages.
Indie reality: one person, several surfaces. A monorepo with a task graph beats five half-synced folders.
Backend: Hono (often on Bun or Workers)
Hono is the API surface I reach for when I want something small and explicit. In some projects the runtime is Bun; in others it’s Cloudflare Workers via the stack’s Workers path. The point isn’t “edge for its own sake.” It’s a thin HTTP layer that doesn’t force a framework theology on day one.
Data: Postgres + Drizzle (or SQLite/D1 when the deploy story wants it)
Default mental model:
| Need | Choice I tend to make |
|---|---|
| Local product with a real relational DB | Postgres + Drizzle, often with Docker for local DB |
| Edge / Cloudflare-shaped deploy | SQLite + D1 + Drizzle when that matches the deploy target |
Drizzle stays because the schema lives in TypeScript and migrations stay reviewable. I don’t want a black-box “magic models” layer when I’m alone on the product.
API contract: oRPC
When the scaffold offers a typed RPC path, I often take oRPC. Shared input/output types between client and server cut a whole class of “the API returned something else” bugs—especially when the same monorepo owns web and server.
Not every project needs RPC on day one. When the surface is simple, plain Hono routes are fine. When the product grows multiple client call sites, types across the wire pay rent.
Auth: Better Auth when I need accounts
Better Auth is the default I enable when the product has users (not every MVP does). It’s optional in the scaffold; I leave auth: none until login is a real requirement. Auth too early is a distraction. Auth too late is a rewrite.
Payments: Polar when the product sells
On stacks that include payments, I’ve used Polar with Better Auth where that integration is the point. Again: only when money is in scope. Scaffolding Stripe/Polar ceremony before the first paid use case is how side projects die of configuration.
Frontend: depends on the product
Better-T-Stack is not “one frontend.” What I’ve actually scaffolded:
- TanStack Start — full-stack React when I want the web app and routing story in one place
- TanStack Router — SPA/router-first web when the server is a separate app
- Next.js — when the product benefits from that ecosystem (or already lives there)
- React Native (NativeWind / UniWind variants) — when mobile is part of the bet
The skill is matching the frontend to the distribution of the product, not to Twitter trends that week.
Lint and agent hygiene: Biome / Ultracite / Oxlint, Ruler, Skills
Addons I keep seeing in my trees:
- Biome or Ultracite (and sometimes Oxlint) — fast lint/format without a seven-plugin ESLint novel
- Ruler — keep agent/editor rules consistent in the monorepo
- Skills (when offered) — agent-facing project knowledge so coding agents don’t invent a second architecture
I’m not collecting linters for sport. I want check to be one command and I want AI tools to read the same conventions I do.
How the pieces fit (one mental model)
create-better-t-stack
│
▼
monorepo (Turborepo)
├── apps/web (TanStack / Next / …)
├── apps/server (Hono)
├── apps/native (optional)
└── packages/* (db, auth, shared types)
│
├── Drizzle schema → Postgres / D1
├── oRPC or HTTP routes
├── Better Auth (when needed)
└── Polar (when money is real)
The scaffold’s job is to draw that diagram in files. My job after that is product: one painful workflow, one path to money or learning, not infinite architecture.
What I deliberately don’t put in every project
- Auth on day zero if the MVP is a single-player tool or a waitlist.
- Payments before someone has asked to pay.
- Native because “we might need an app someday.”
- Every addon the CLI offers. Turborepo + one lint story + types is enough for v0.
- A second ORM “just in case.” Pick Drizzle (or none) and move.
The anti-pattern is treating Better-T-Stack as a shopping cart. It’s a starting configuration, not a scorecard of how modern you are.
When I wouldn’t use it
- A pure content site (this blog is AstroPaper, not a BTS monorepo).
- A one-file script or CLI with no web surface.
- A client project that already mandates a stack I don’t control.
- When I’m only exploring an API idea and a single
index.tsis honest.
Scaffolds are leverage when you’ll live in the repo for months. They’re overhead when the idea dies in a weekend—and that’s fine; just don’t romanticize either outcome.
A practical starting recipe
If I were spinning up another small SaaS-shaped product tomorrow, the defaults I’d type without overthinking:
- Better-T-Stack interactive (or the reproducible command it prints)
- Bun + Turborepo
- Hono backend
- Drizzle + Postgres (Docker locally) or D1 if the deploy story is Cloudflare-first
- oRPC if client/server share the monorepo
- TanStack Start or TanStack Router for web (Next only if I have a concrete reason)
- Better Auth only when accounts are required
- Biome/Ultracite or Oxlint — one lint path
- Ship a single user-visible loop before adding Polar, native, or a second app
Then delete anything the scaffold added that I don’t understand. If I can’t explain a package in one sentence, it doesn’t belong in v0.
The portable rule
Pick a scaffold that encodes type-safe defaults. Keep the tools that protect speed and honesty. Drop the rest until the product forces them back in.
Better-T-Stack is where I start most TypeScript indie apps. The tools inside it—Hono, Drizzle, oRPC, Better Auth, Turborepo, Bun, TanStack, lint addons—only earn a permanent seat if they still make the next commit easier.
If you want the builder itself: better-t-stack.dev · CLI: create-better-t-stack · GitHub