The Startup Tech Stack I Keep Coming Back To After Eight Years of Rebuilding the Same Things
The startup tech stack question is usually asked too late.
By the time someone is comparing databases on a Friday afternoon, they have often already committed to the expensive part: a product idea with no clear boundary, a deadline invented to force momentum, and a pile of features that all sound foundational. The stack becomes a safe thing to debate because it has named options and screenshots.
I have spent eight years building production software with TypeScript, React, Next.js, Node.js, PostgreSQL, AWS, and Tailwind. That does not make this a universal stack. It does make it the stack I can move through without stopping every hour to look something up, which is a more valuable property for a solo founder than whatever topped Hacker News this morning.
Here is what I would choose for a new web product today, and where I would deliberately stop adding things.
What is the best tech stack for a startup?
For a conventional SaaS or web product: TypeScript, Next.js, PostgreSQL, a managed deployment platform, an authentication provider, a payment provider, and a small set of services you can replace later.
That reads like an uninteresting answer because it is. Uninteresting is a feature.
Your early product has two technical jobs. It needs to get a customer through the core action without breaking. And it needs to stay understandable while you learn what the core action actually is. A stack that gives you those two things beats a theoretically superior stack that turns every small change into a week of infrastructure work.
The version I return to looks like this:
| Layer | Default choice | Why it earns the default | | --- | --- | --- | | Language | TypeScript | Types follow the work from UI to server and make refactors cheaper | | Web app | Next.js and React | One codebase can handle public pages, the app, server rendering, and API work | | Styling | Tailwind CSS | Fast to iterate, no context-switching into a separate styling system | | Database | PostgreSQL | Relational data stays straightforward, and the ecosystem is everywhere | | ORM or query layer | A typed, boring query layer | Schema changes and database calls should be visible in the codebase | | Authentication | Clerk, Better Auth, or a comparable provider | Do not hand-roll password resets and session security for version one | | Billing | Stripe | Subscriptions, invoices, tax, and recovery already exist as boring solved problems | | Deployment | Vercel, AWS, or another managed host you know | Deployment should not become the product | | Analytics | One privacy-conscious product analytics tool plus server logs | You need enough signal to make decisions, not a command center |
These are defaults, not a shopping list. If your product is native mobile, hardware, an offline desktop app, or a real-time multiplayer system, the shape changes. The mistake is treating every project as a special case before the users have proved it is one.
Why TypeScript is the right default for a startup stack
TypeScript is not faster than JavaScript in the sense people mean when they ask about speed. It is faster when you change a customer-facing concept and want the compiler to show every place you forgot.
Early products change names, data shapes, permissions, pricing plans, and onboarding flows constantly. You could test each path manually. Or you could let the type system refuse a build when the old plan name still lives in a webhook handler you forgot existed.
The real advantage is shared vocabulary. The object your React component renders can be the object your route handler validates and the object your database query returns, with intentional translation at the edges. That does not prevent bad decisions. It prevents a fair number of boring mismatches.
The warning is not to turn types into a second product. A one-week argument over a generic abstraction has never helped an early customer. Use clear domain types, validate external input, and keep the rest plain.
Why use Next.js for a startup?
I use Next.js when the product needs both a public site and an authenticated application, which is most SaaS products.
The useful thing is not that Next.js can do everything. It is that it lets you put marketing pages, documentation, server-rendered content, dashboard screens, and server-side work in one repository while the product is still small. That makes the seams visible. You do not need to coordinate a frontend deployment, an API deployment, and a content system just to change an onboarding sentence.
It is particularly good for a content-led product. Server-rendered pages are readable by search crawlers, including the AI crawlers that are far less reliable at executing JavaScript. That matters here because the site is a tools directory first, with the blog feeding discovery. The page a person sees needs to exist in the initial HTML, not arrive after a loading spinner.
Next.js is a poor fit if you have already established a separate backend team, a mobile-first product, or a long-lived application protocol that deserves its own boundary. That is not where most solo founders start. Most start with a browser and a subscription page.
If you are evaluating it, the Next.js listing is a useful starting point. Just do not confuse a framework decision with evidence that the product should exist.
Why PostgreSQL keeps winning the database decision
Early SaaS data looks relational even when you want it not to. Users belong to organizations. Organizations have members. Members have roles. Plans enable features. Payments create invoices. A report belongs to something and was generated by someone.
PostgreSQL handles that shape directly. Its reliability model is well documented, and the ecosystem around migrations, backups, query inspection, and managed hosting means you do not need to invent database operations while trying to find customers. The PostgreSQL documentation describes the write-ahead log machinery that protects committed transactions through crashes. You should still take backups. You should not need to explain your database choice to every new contractor.
Use Postgres until a measured problem tells you not to. That could be a search workload that wants a dedicated index, a time-series system with particular retention needs, or a graph problem where relationships are the actual product. It is almost never because someone on X called SQL old.
For an ordinary product database, the less exotic choice gives you more time to build the thing people pay for.
What should a startup use for authentication and billing?
Buy both at the beginning.
Authentication is an attack surface with an annoying amount of edge behavior: email verification, password resets, sessions, rate limiting, OAuth callbacks, account linking, organization roles, and the person who no longer has access to their work email. A service such as Clerk or an established library such as Better Auth saves time exactly where a solo product does not need a novel implementation.
Billing has the same shape. The happy path is one checkout button. The real path includes failed renewals, proration, invoices, tax, refunds, subscription status, entitlement changes, and someone whose card expires three months after you forgot about the feature.
Stripe's SaaS integration guide is worth reading before you write the first line. It separates product modelling, entitlement, pricing display, recovery, tax, and testing because those are separate jobs. The point is not to implement every part of it on day one. The point is to understand that a checkout success page is not the moment someone becomes entitled to use your product. Your webhook handling is.
Start with one paid plan if that matches the product. Add tiers only when a real pricing difference appears. Usage billing is a proper product decision with metering and customer communication attached, not a way to make a pricing page feel more sophisticated.
What not to add to your startup stack
The starter kits and architecture diagrams usually omit this section because subtraction does not sell well.
Do not add microservices because you have three routes. Do not add a queue because an email can be sent synchronously for the first fifty users. Do not add event sourcing because you read a good post about it. Do not add an analytics warehouse before you have a question the product analytics screen cannot answer.
You may need every one of those things. Later. The test is whether a concrete failure or constraint has made the next component necessary.
This is also why buying a boilerplate has a limit. A good boilerplate can save you from rebuilding authentication, deployments, and billing. It cannot choose the product boundary or earn the first user. I wrote about the tradeoff in what seven launches taught me about SaaS boilerplates. The parts it skips are useful. The part it cannot skip is the one you should think about before opening the repo.
A stack is only good if you can remove parts of it
This is the test I care about most.
Can you change the email provider without rewriting user accounts? Can you move the database host without changing every query? Can you turn off an analytics vendor without breaking the checkout? Can a second person understand where a subscription changes a customer's access?
You do not get this by adding abstract layers for every vendor. You get it by keeping vendor calls at the boundaries and naming things by what the product does. Send an email. Charge a subscription. Store a project. Do not let a provider's internal vocabulary become the shape of the whole application.
That is why the stack above stays boring. It is not about future-proofing, which is usually a flattering way to say planning for imaginary problems. It is about leaving yourself room to learn.
Pick the stack you can ship with. Use managed services for the jobs that are not your differentiation. Keep the data model clear. Then get back to the uncomfortable work of finding out whether anyone needs the result.
A solo full-stack developer and product builder with 8 years of experience shipping production software and 2 years as an indie hacker.
alexcloudstar.com