Why I Default to Boring Tech
Every project tempts you to try the new thing. Here's why I keep reaching for Postgres, Next.js, and a Dockerfile instead — and when boring is the wrong call.
There's a familiar feeling at the start of any new project: you're staring at a blank repo, and every decision is open. Database? Framework? Deploy target? Auth? Suddenly the new thing on Hacker News looks tempting. Edge runtime, vector-native database, the framework that just hit 1.0 last week. The greenfield is the only window where you can pick anything, so why not pick the interesting one?
I've learned to push back on that instinct. Not always — sometimes the new thing is genuinely the right call — but as a default, I reach for boring tech. Postgres. Next.js. A Dockerfile. A managed runtime. The stuff that's been around long enough that the failure modes are documented and the StackOverflow answers are five years old and still correct.
This post is the reasoning, not the rule. The rule "use boring tech" without the reasoning turns into cargo culting just as fast as "always try the new thing".
Boring tech has cheaper failure modes
Every piece of tech you adopt has a failure surface. Some of it is obvious — bugs, outages, performance cliffs. Some of it is subtle — the maintainer burns out, the API churns, the docs go stale, the community moves on, the hosting provider deprecates the runtime.
Boring tech has had decades for those failure modes to surface and get patched. When Postgres has a weird edge case, somebody else has already hit it, written about it, and shipped a fix. When Next.js does something unexpected on Vercel, there's a thread. When your Dockerfile build breaks, the error message is googleable in three languages.
New tech doesn't have that yet. Not because it's bad — because nobody's stress-tested it in your weird specific situation. You become the person who finds the edge case at 2am on a Saturday, with no Stack Overflow answer and no thread to follow.
That's fine when you're learning. It's expensive when you're shipping.
The interesting parts of your project aren't the stack
Here's the trap: you tell yourself the new tech will let you build something more ambitious. The vector database will unlock semantic search. The edge runtime will make it faster. The framework that just hit 1.0 will be more elegant.
Sometimes that's true. Mostly it's not. Mostly the interesting part of your project — the thing your users actually care about — is the domain logic, the UX, the content, the integrations. The stack is just the substrate underneath.
If your project is interesting because of its domain, picking boring tech lets you spend your attention budget on the domain. If your project is interesting because of its tech, you don't have a project — you have a tech demo.
I've shipped a lot of side projects, and the ones that stuck weren't the ones with the cleverest stack. They were the ones where the stack got out of the way fast enough that I could spend my evenings on the actual problem.
What "boring" actually means
Boring isn't the same as old. It's not the same as ugly. It means:
- Documented: there's an official tutorial that's still accurate.
- Stable API surface: upgrades don't routinely require rewrites.
- Big install base: lots of people are running this in production.
- Predictable performance: you can reason about it without instrumenting everything.
- Well-understood ops story: backups, monitoring, scaling are solved problems.
By that definition, Next.js is boring. So is Postgres. So is Tailwind. So is React. None of them feel old, and yet all of them satisfy the criteria. The point isn't to use COBOL — it's to use tools that have already crossed the chasm where the failure modes are known.
My actual default stack
For a typical solo full-stack web project, the answer is almost always:
- Frontend + backend: Next.js (App Router, TypeScript)
- Database: Postgres (managed if I'm in a hurry, self-hosted if I'm cost-sensitive)
- ORM: Prisma
- State: Zustand for client state, server components for everything else
- Styling: Tailwind
- Validation: Zod at the API boundaries
- Auth: depends, but usually email + a session cookie, no third-party until I need SSO
- Deploy: Dockerfile to GHCR, redeploy webhook to a small VPS via Dokploy. Vercel for previews
- Tests: Vitest, plus Playwright if there's enough UI to justify it
This isn't because I think it's optimal for every project. It's because it's optimal for most of my projects, and starting from a known-good base means I can focus the first week on the domain instead of yak-shaving the platform.
When a project genuinely needs something different — heavy ML inference, real-time multiplayer, embedded device targets — I deviate. But "this will be a CRUD app with a bit of AI sprinkled on top" almost never needs to deviate.
When boring is the wrong call
I'm not arguing you should never adopt new tech. There are real reasons to break the default:
- You're learning. Side projects are a legitimate place to play with new things. The cost of failure is your weekend, and the upside is you actually understand the new tool when it shows up at work.
- The new tech solves a problem you actually have. If you're building real-time collaboration and you reach for
socket.iobecause that's boring, butliveblockswould let you ship in a tenth the time, the boring choice is the expensive one. - The boring choice has a structural ceiling you'll hit. If your domain genuinely needs vector search at scale and you try to fake it in Postgres, you'll spend more time fighting that than you would have learning a real vector DB.
The thing all three have in common: there's a specific reason for the deviation, named in advance. Not "this seems cooler". A specific problem the boring choice can't solve.
The judgment call, not the rule
Boring tech is a default, not a doctrine. The point isn't to refuse to learn anything new — it's to make sure that when you do reach for the new thing, you're paying the learning cost on purpose, with eyes open about what it'll cost you in debugging time, doc gaps, and 2am wake-ups.
Most of the time, the answer to "what should I use for this project" is "the same thing I used last time, unless I have a specific reason to switch". That's not a boring answer because the project is boring. It's a boring answer because it's the right one most of the time, and saving the novelty budget for the parts that actually need it is how you ship.
