Early in my career, I treated engineering like craft. There was a "best" way to structure things, a beautiful way to model a domain, the cleanest abstraction, and the most elegant code. That mindset genuinely sharpened my craft. The code got cleaner, the technical decisions got better. But craft alone was not helping the company survive or move forward. There's a time for both, and I will defend the value of well-made software. At early stage though, overoptimizing on craft can literally kill the business before it has a chance to find out if anyone wants what you're building.
Then I started spending more time in early-stage companies, where the rules are different. Most companies do not die because the code is inelegant. They die because they run out of time, run out of money, or never find product-market fit. (If you're in a later-stage org, the constraints change. You're often optimizing for coordination, consistency, and organizational scale. It is a different game with a different playbook.)
That's when I learned the lesson a lot of engineers learn the hard way: elegance is not the same thing as impact.

This article is about one practical manifestation of that shift: early-stage focus. When you're pre-product-market-fit (PMF), time is your scarcest resource. You have more to do than you have weeks to do it. In that environment, every "interesting" technical detour has an opportunity cost.
To be clear, running multiple products amplifies this pain, but it is not required for the argument. Even one product is enough to drown you in decision tax.
So instead of treating the stack like a personal expression, treat it like a constraint system: pick a default that removes decisions, reduces re-learning, and keeps you shipping. (For the broader frame on choosing under uncertainty, see how I make technical decisions when there's no data.)
The best external example of this mindset is Spotify's "Golden Paths": an opinionated default meant to reduce fragmentation1. If you want a metrics lens, DORA's delivery metrics are about shipping changes quickly and safely, and fragmentation pushes both in the wrong direction2. If you want leverage, you need a default. And if you're a serial founder or operating multiple bets at once, this becomes non-negotiable: every unnecessary choice shows up as lost time, lost focus, and slower iteration across everything you're trying to ship.
The novelty tax compounds (especially pre-PMF)
Novelty feels like progress: new framework, new database, new deployment approach, new arguments. In an early-stage company, novelty is rarely "free." It is time you could have spent on customer learning, distribution, reliability, or revenue.
Stack experimentation can be the right move when the goal is explicit R&D, but most early-stage teams aren't short on ideas. They're short on time. So stack diversity does not just add cost. It multiplies it.
- Hiring slows down because every stack creates a new hiring funnel. You end up asking candidates to match a rotating list of tastes instead of a stable set of fundamentals.
- Onboarding slows down because each product becomes a new apprenticeship. "Learn our domain" becomes "learn our domain and our stack and our deployment approach and our conventions."
- Review bandwidth collapses because fewer people can confidently review changes without rebuilding context first.
- Tooling fragments because CI, observability, local dev, and quality checks drift per repo.
- Agencies get harder to use because outsourcing works best when you can hire generalists quickly. If you're on the latest framework or an unusual stack, you pay a premium for scarce talent, spend more time onboarding, and have fewer people who can safely maintain the work once the contract ends.
- On-call risk increases because when something breaks, you're debugging unfamiliar failure modes under time pressure. The person who knows Product A is not the person who can safely touch Product B at 2 a.m.
None of this is theoretical. It shows up as longer cycle times, more hidden work, and a team that feels busy while shipping less.
What "a boring stack" is really buying you
For me, "boring and repeatable" looks like (full appendix at the bottom):
- Laravel
- React
- Inertia.js
- Tailwind
- PostgreSQL
- Stripe
- Vite
But the list is not the point, and it's not the pitch. The point is what a default stack buys an early-stage team: fewer decisions, fewer new failure modes, and less context switching. In practice, "boring" usually means batteries included. It is an ecosystem where the common problems (queues, email, storage, realtime) are well-trodden, well-documented, and predictable. It is not a minimalist setup that turns every foundational need into a bespoke journey.
It also buys you reusable judgment. When the stack is consistent, your team accumulates it.
- A developer who shipped a feature in Product A can review a PR in Product B the same day.
- Patterns repeat, so mistakes stop repeating.
- You can build a real component library and a real accessibility baseline instead of reinventing the same decisions.
- Your deployment pipeline and quality gates become a product, not a per-repo snowflake.
- Incidents become procedural rather than mystical.
You do not need to be a large company to benefit from the underlying principle. Small teams often benefit even more, because they do not have extra headcount to spend on translation and re-learning.
"Your stack isn't a personality trait. It's an operating decision."
The honest tradeoffs and how to manage them
A boring stack is not a claim that one stack is best for all problems. It is a claim that defaults matter, and that exceptions should be deliberate. It is also a claim about what kind of engineer you are choosing to be. Some engineers optimize primarily for novelty and craftsmanship. Some optimize for leverage and outcomes. In real companies, the job is to balance both, but to price them correctly for the stage you are in.
1) Hammer and nail risk
If you only have one hammer, everything looks like a nail.
Countermeasure: define explicit exception criteria. Here are a few that are usually legitimate:
- hard realtime constraints
- extremely high throughput ingestion
- ML training or heavy compute workloads
- offline-first and edge-heavy requirements
- non-negotiable regulatory or data residency constraints
- non-negotiable ecosystem or go-to-market constraints (a required integration surface, a required deployment environment, enterprise procurement requirements)
- correctness and risk constraints where failure is existential (payments, security, privacy, safety-critical behavior)
- extreme team constraints (you are standardizing on what your team, your hires, or your agency bench can reliably ship and support)
If a product meets those criteria, deviate. Otherwise, default.
2) Innovation debt
Standardization can become stagnation if you never re-evaluate.
Countermeasure: schedule stack reviews on a cadence and write down the decision. Twice a year is often enough. Treat it like any other business decision: costs, risks, and upside.
3) Monoculture risk
Yes, a shared stack means shared blast radius.
Countermeasure: treat the stack like critical infrastructure.
- dependency scanning and patch cadence
- good observability and incident response muscle
- drills and runbooks that assume failures will happen
The risk here is real. The mitigation is also real. The goal is not "avoid all shared risk." The goal is "manage shared risk with discipline."
A simple decision framework: when boring is the right move
Boring, repeatable stacks are usually the right call when:
- You're early-stage and still earning product-market fit.
- Your team is small (or functionally small because everyone is context-switched).
- Your product is mostly CRUD and workflows (most SaaS is).
- You need iteration speed more than theoretical flexibility.
- You want to reduce the amount of engineering time spent on "supporting the engineering."
If this describes you, the best stack is often the one you can ship and operate consistently, not the one that wins an argument on a podcast.
When you should break the rule
There are real cases where the "boring default" should lose.
- ML and compute-heavy workloads
- high-throughput realtime systems
- specialized edge or offline-first constraints
The key is to make the tradeoff explicitly and budget for it.
When you take on a new stack, you are taking on:
- an onboarding curve
- a new set of production failure modes
- a new observability and debugging workflow
- a new hiring funnel
- a set of unknown unknowns (the issues you won't discover until you ship, scale, and get paged)
That can still be worth it. Just do not pretend it is free.
Anticipating the pushback
Skeptics are right to challenge standardization. Here are the objections I hear most, and the answers I think hold up.
"Isn't this anti-innovation?"
No. It is pro-shipping.
The argument is not "never use new tools." The argument is "spend your innovation budget where it creates customer value." If you are rebuilding your foundation every quarter, you are not innovating. You are cycling.
"Best tool for the job matters."
Agreed. The question is what the "job" is.
If the job is building one standalone product, the best tool might be a highly specialized choice.
If the job is proving you have a viable business that is still alive in year three, the "best tool" is often the one that keeps you shipping, learning, and recovering quickly when things break.
And if you are operating a portfolio of products with a small team, the same logic applies: the best tool might be the one your entire team can reason about, deploy, and debug across contexts.
"A single vulnerability could hit everything."
True. That is a reason to mature your discipline, not a reason to avoid defaults.
Your security posture should not depend on diversity by accident. It should depend on patching, monitoring, and incident response.
"This sounds like founder cope."
Maybe. It can sound like a rationalization if you assume the "boring" choice is just the one someone already knows.
In my case, it was not. I had not written PHP in almost 15 years, and if I were optimizing for personal enjoyment, I would have defaulted to full-stack React.
The reason I moved back toward a boring, batteries-included stack was the opposite of comfort. I kept running into fights that did not move the company forward. Every new project turned into a new journey just to get foundational pieces in place: queues, realtime, background work, email, and storage. When you're pre-PMF, those detours compound.
LLM help gets worse in ecosystems that are fragmented and fast-moving. When there are constant breaking changes and 30 "normal" ways to solve the same problem, prompts become harder to ground, answers vary wildly, and you spend more time validating than building. The more novelty you add, the harder it is to get consistent help, consistent debugging, and consistent progress.
So yes, this can be founder cope. The alternative, though, is paying the novelty tax forever. The claim is simple: defaults reduce organizational drag, and that drag is one of the biggest invisible killers of velocity.
Takeaway
If you're early-stage, your biggest risk is not that your code won't scale. Your biggest risk is that the company won't. Pick defaults that keep you shipping, keep you learning, and keep you alive, and make exceptions rare, explicit, and funded.
Your stack isn't a personality trait. It's a survival decision.
Appendix: My current "boring" starter stack
This is the concrete setup I use to start new products quickly. It is intentionally simple: one repo, two apps, coordinated tooling, and shared docs.
A quick caveat: a lot of these categories have multiple "correct" answers. The goal is not to argue that these tools are objectively best. The goal is to show what it looks like to pick defaults, use them consistently, and stop paying the decision tax on every new project.
Platform (apps/platform/)
- Laravel + Inertia + React
- Tailwind
- NPM, Herd (local TLS), Vite
- Pint, PHPStan, ESLint, Prettier
- Deployed via Laravel Forge to DigitalOcean
- Nightwatch/Sentry
Marketing (apps/marketing/)
- Astro + React
- Tailwind
- Biome
- Deployed to Cloudflare Pages
Shared
- Monorepo coordinated via Overmind (
Procfile.dev) - GitHub Actions with path-filtered CI/CD per app
- Canonical docs in
/docs
Services (typical defaults)
- PostgreSQL (primary database)
- Cloudflare R2 (object storage)
- Cloudflare (domains, DNS, edge)
- Redis (queues, cache)
- Reverb (realtime)
- Postmark (transactional email)
- MailerLite (marketing email)
- PostHog (product analytics + session replay)