Our default stack is boring. We've shipped with TypeScript, Postgres, and Cloudflare Workers/Pages for years now, and we keep coming back to it after experimenting with newer options. This post is the case for boring — and the trade-offs we accept by being conservative.
Why boring
"Boring" in tech is a compliment. A boring stack means:
- The bugs are known and documented.
- Stack Overflow has the answer to your question already.
- Hiring is straightforward — most senior engineers have shipped this stack.
- The library you depend on will still be maintained in five years.
Compare that to bleeding-edge stacks where you become the discoverer of bugs, every error is novel, and the framework's API changes between versions. The novelty is fun for prototypes. It's expensive in production.
The stack
TypeScript everywhere
TypeScript on the frontend, TypeScript on the backend, shared types between them. The single biggest productivity multiplier we've found. Refactoring becomes trivial because the compiler tells you what broke. API contracts between client and server become a compile-time check instead of a runtime hope.
We use tsx or bun for dev, tsc or esbuild for build. We avoid any like the plague. We use zod at every system boundary.
Postgres for everything
Relational data? Postgres. Document data? Postgres with jsonb. Full-text search? Postgres with tsvector. Time-series? Postgres with TimescaleDB. Queue? Postgres with SKIP LOCKED. Cache? Often Postgres, sometimes Redis.
It's not that Postgres is the best at any one of these. It's that running one database in production is dramatically easier than running five. Until you have a real reason to add another data store — and "I read about it in a blog post" doesn't count — Postgres is enough.
Cloudflare for hosting
Workers and Pages for compute, R2 for object storage, KV for low-latency reads, D1 or Hyperdrive-fronted Postgres for primary data. The edge network gives us sub-100ms TTFB globally without any thought. The bill is small. The DX is good.
We've shipped on Vercel, AWS, Fly.io, and Railway. They're all fine. Cloudflare won because the edge model is the right primitive for the kinds of apps we ship most often.
Astro or Next.js for the app shell
Content-heavy: Astro. App-heavy: Next.js. We have a whole post on the decision. The point here is that both are boring choices in 2026. Either way you're using React (Next.js) or HTML + targeted React/Vue components (Astro), and the patterns are well-understood.
Tailwind for styling
Yes, the class lists get long. Yes, we've heard the criticisms. The trade-off is that the styling lives next to the markup, the design system maps cleanly onto utility classes, and our refactoring story is much simpler than with CSS-in-JS or BEM.
The trade-offs we accept
Picking a boring stack means giving things up:
- We don't get the newest framework features on day one. By the time we adopt something, it's been battle-tested for a year. We're fine with that.
- We're occasionally slower than competitors using more specialised tools. A site running on Vite + custom React might be marginally faster than ours. The difference rarely matters to the user.
- We sometimes pick more code over less magic. We'd rather write five lines of explicit SQL than one line of an ORM doing magical joins behind our back.
Where we'd consider breaking the rules
Specialised workloads sometimes justify specialised tools:
- Real-time multiplayer or collaborative editing. Yjs, Liveblocks, or PartyKit are genuinely better than rolling your own.
- ML inference at scale. Modal, Replicate, or self-hosted Triton outperform shoehorning ML into a web stack.
- Hard real-time data. Sometimes Postgres genuinely isn't enough and you need ClickHouse, Materialize, or similar.
Boring isn't a virtue in itself. Boring is a virtue when it lets you spend your novelty budget on the parts that actually matter — usually the product, not the stack.
The case for boring, in one sentence
The best stack is the one where the team can ship on a Friday afternoon without anxiety. For us, that's a boring stack. For you, it might be different. The point isn't the specific choices — it's the principle of optimising for confidence, not novelty.