01 — Stack Selection
Pick the stack from the table, not from Twitter.
By app type
| App type | Default stack | Why |
|---|---|---|
| Internal tool / admin dashboard | Next.js (App Router) + Postgres + Vercel + Drizzle + Auth.js with Entra SSO restricted to @egca.io + Entra email | Everyone already knows it, Vercel preview URLs make demos trivial, staff sign in with company accounts (offboarding via Entra), mail comes from the company domain without extra infra. |
| Client web — small / MVP | Next.js monolith on Vercel + Prisma + Clerk + Resend | Clerk and Resend remove weeks of auth/email work. Prisma over Drizzle here because junior onboarding is faster. |
| Client web — large prod | Next.js frontend + 1–3 split backend services (semi-microservices) | Next.js does SSR and the thin API; heavy/async work goes in a service. See 02-architecture. |
| Backend service | Hono (TS) or FastAPI (Python) on Azure Web Apps or Docker VPS | Both are small, fast, and have first-class OpenAPI. Pick the language the consuming team already uses. |
| Data / ML / AI | Python + Postgres + pgvector | Stays boring until you measure you need a dedicated vector DB. See playbooks/data-ml-ai. |
| Mobile | Expo + React Native + TypeScript | Shared JS talent, OTA updates via EAS, native modules available when needed. |
| CLI / script | TypeScript with tsx + commander, OR Python with typer | No framework. If it grows past one file with 500 lines, split it. |
Hosting picker
| Choose | When |
|---|---|
| Vercel | Anything Next.js, client web apps, anything where zero ops matters more than cost. |
| Azure Web Apps | Microsoft-shop apps that need Entra SSO, Key Vault, Graph API — or where the client lives in Azure. |
| Docker + VPS (Hetzner / DO) | Long-running workers, stateful services, cost-sensitive bulk compute, anything Vercel’s request-time limits don’t fit. |
Default to Vercel for anything that is fundamentally a web app. Reach for Azure or VPS when Vercel’s model (request-scoped, serverless) genuinely doesn’t fit — not because someone read a blog post about cost.
Repo layout
- One app, one service → polyrepo. Simpler.
- Multiple apps sharing code (shared UI, shared types, shared SDK) → monorepo with pnpm workspaces + Turborepo.
- Multiple unrelated apps, no shared code → polyrepo. A monorepo without shared code is just a directory with extra steps.
Language defaults
- TypeScript by default everywhere JS runs.
strict: true. No new JavaScript files. - Python for ML/data, scripts where the ecosystem wins (pandas, scikit, ML libs), or when the team consuming the code is Python.
- No other languages without a lead’s sign-off. We are a small team; one Go service that nobody else can maintain is a liability.
What we explicitly do NOT default to
- No Remix / SvelteKit / Nuxt in new projects. Next.js or bust, unless a client mandates otherwise.
- No GraphQL unless there’s a real client-driven reason (multiple frontends with different shapes). REST + Zod or tRPC solves 95% of cases with less infra.
- No Kubernetes. Docker + VPS or Azure Web Apps until you measurably outgrow them.
- No NoSQL / Firebase / Firestore as a primary store. Postgres always — local/dev on Docker, preview on Neon or Supabase, production on Azure Postgres. See 02-architecture § Database.