EGCA EGCA Engineering Handbook
Internal reference · v1
Home Onboarding Git Review Prototype ↔ Prod
Home 1. Stack Selection

01 — Stack Selection

Pick the stack from the table, not from Twitter.

By app type

App typeDefault stackWhy
Internal tool / admin dashboardNext.js (App Router) + Postgres + Vercel + Drizzle + Auth.js with Entra SSO restricted to @egca.io + Entra emailEveryone 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 / MVPNext.js monolith on Vercel + Prisma + Clerk + ResendClerk and Resend remove weeks of auth/email work. Prisma over Drizzle here because junior onboarding is faster.
Client web — large prodNext.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 serviceHono (TS) or FastAPI (Python) on Azure Web Apps or Docker VPSBoth are small, fast, and have first-class OpenAPI. Pick the language the consuming team already uses.
Data / ML / AIPython + Postgres + pgvectorStays boring until you measure you need a dedicated vector DB. See playbooks/data-ml-ai.
MobileExpo + React Native + TypeScriptShared JS talent, OTA updates via EAS, native modules available when needed.
CLI / scriptTypeScript with tsx + commander, OR Python with typerNo framework. If it grows past one file with 500 lines, split it.

Hosting picker

ChooseWhen
VercelAnything Next.js, client web apps, anything where zero ops matters more than cost.
Azure Web AppsMicrosoft-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.