I pick boring stacks on purpose.
Not because I cannot set up Kubernetes. Not because I have not read the Hacker News thread about the new framework that abolishes boilerplate. Because on a 2–4 week MVP, every "interesting" infrastructure choice is a tax on the only thing that matters: whether a real user can complete the core loop.
People hear "Next.js, managed auth, Postgres, Vercel" and think laziness. That reading is wrong. Choosing a boring stack is not avoiding hard engineering. It is refusing to spend hard engineering on problems your users did not ask you to solve.
The stack is a product decision. Treat it like one.
Your users do not care that your auth is custom
They care that they can sign in, reset a password, and not lose their session on mobile Safari.
They do not care whether you built authentication yourself. They care that it never becomes their problem.
Every custom authentication system becomes your responsibility forever. Password resets, OAuth edge cases, account recovery, session invalidation, abuse prevention, compliance. None of those differentiate your product, but every one can wake you up at 2am when something breaks.
The same applies to infrastructure more broadly. Users are not impressed that you wrote a bespoke deployment pipeline or split your backend into six services. They care that bugs get fixed quickly, that the product feels reliable, and that the next engineer can navigate the codebase without a guided tour.
Custom infrastructure is invisible when it works and existential when it does not. Early products already have enough existential risk. Do not invent more.
What "boring" means here
Boring is not "bad." Boring is known failure modes. And the reverse is just as concrete: clever choices are usually a specific tax you are choosing to pay before revenue tells you to.
| Layer | Boring default I reach for | Product benefit | What the "clever" alternative usually costs |
|---|---|---|---|
| App | Next.js + TypeScript + Tailwind | One repo, typed boundaries, ship UI and API together | Distributed debugging before product clarity |
| Auth | Clerk / Auth.js / similar managed provider | Sessions, resets, OAuth without becoming an IAM team | Security footguns and password-reset hell |
| Data | Postgres (Neon or similar) + Drizzle | A real database with migrations you can reason about | Novelty where reliability was already free |
| Hosting | Vercel (or equivalent git-push deploys) | Preview URLs, rollbacks, less deploy theater | Deploy scripts nobody trusts at 2am |
| UI system | Existing component library | Five stable screens instead of a design system project | Design system work instead of empty states |
| Email / pay | Resend / Stripe | Battle-tested edge cases you will not invent yourself | Reinventing infrastructure that already exists |
| Errors | Sentry or equivalent | "What broke?" without SSH folklore | Flying blind when production fails |
This is not the only valid stack. It is the stack that stops the conversation early so we can talk about the product.
If your discovery call spends forty minutes debating frameworks, you are already behind. The bet is the workflow, not the renderer.
Founders and engineers sometimes pick tools to impress other founders and engineers: microservices before revenue, event buses before a second consumer exists, a custom design system before five screens are stable. That stack photographs well in an architecture review. It photographs badly in a week-three demo where half the sprint went to glue. I have watched teams spend a month looking enterprise-ready and still not know if anyone wants the product. That is cosplay with CI, not engineering maturity.
Boring is how you buy change
A short sprint is never a straight line. Requirements move. The founder changes the onboarding flow. A user interview kills a feature. A payment provider rejects a checkout flow. The schema grows an awkward field on Thursday afternoon.
Boring stacks make those changes cheap.
This is the pattern I keep seeing: a customer finds a bug the morning of a demo, someone reproduces it, ships a fix, and the customer refreshes the page before lunch. Nobody spends the day chasing session cookies across three services or wondering which deployment script drifted. The time goes into improving the product instead of repairing the platform.
That is what you are buying.
- One deployable unit means changing a form does not involve coordinating three services.
- Managed auth means the pricing page matters more than cookie flags.
- Typed Postgres with migrations lets the model evolve without folklore SQL on production.
- Preview deployments let stakeholders review a URL instead of a Loom recording from localhost.
Speed is not keystrokes per hour. Speed is how many product corrections you can absorb before the calendar runs out. That is quality.
I wrote about the shape of a production-ready MVP in The Anatomy of a Production-Ready MVP. The stack defaults are simply how you make that anatomy affordable.
What I actually optimize for
When I choose tools for a sprint, I optimize for five things.
- Time to a production URL with the core loop working.
- Time to a safe change after the first real user finds a bug.
- Hireability of the codebase for the next engineer.
- Reversibility of decisions that might be wrong.
- Operational silence so nobody is babysitting servers during customer calls.
Next.js wins a lot of that for SaaS MVPs because the product surface is mostly web, the team is small, and the boundary between UI and server stays deliberately simple. Managed auth wins because authentication is a requirement and a liability, not a differentiator. Postgres wins because your data model is the product, and fighting your database halfway through a sprint is a self-own.
If a tool fails those five tests, I do not care how elegant it looks on a whiteboard.
"But we might need X later"
You might.
You might also need a different customer, a different pricing model, and a different core loop. Building for hypothetical requirements is how MVPs arrive late and still guess wrong.
The adult version of future-proofing is:
- Clear module boundaries so you can extract a service later.
- A real schema so you can migrate later.
- Managed vendors with export paths so you can leave later.
- Zero custom platforms for problems you have not actually felt yet.
I will take a slightly ugly Next.js app that ships in three weeks over a pristine distributed system that ships a login form in three weeks.
Later-you, with revenue and a growing team, can afford interesting problems. Early-you cannot afford fake ones.
When boring is the wrong call
Strong opinions still need edges.
Skip the default stack when:
- The product is the infrastructure.
- A hard constraint forbids it (enterprise procurement, data residency, or an existing platform you must extend).
- The team already has deep production expertise elsewhere and switching would burn the sprint.
- Latency, offline capability, or hardware constraints make a web-first MVP dishonest.
Those are product reasons.
"I'm bored of Next.js" is not.
"A Twitter thread said SSR is dead" is not.
"Our competitor uses Rust" is definitely not.
The conversation I want on kickoff
I do not want a bake-off.
I want this:
- We will use a boring full-stack default unless a real constraint forbids it.
- Authentication, email, payments, and hosting are bought, not rebuilt.
- We spend our novelty budget on the core loop, not the plumbing.
- Anything clever must justify itself in product outcomes, not engineer dopamine.
Then we can argue about the workflow, the cut list, and what "done" means. That is the work. Framework theology is procrastination with syntax highlighting.
If you want the upstream discipline for what not to build, read Scope You Kill on Purpose. Stack defaults are the same instinct applied to tools: kill the optional complexity before it kills the sprint.
You are not paid to make software impressive. You are paid to make it useful. Choose the stack that buys product time, then spend every saved hour learning whether the product deserves to exist.
Boring stack. Sharp product. That is the trade.
Related: The Anatomy of a Production-Ready MVP · Scope You Kill on Purpose · How to Brief an Engineer for a 2–4 Week MVP