Back to blog

The Case for Boring Stacks: Why I Keep Reaching for Next.js and SQLite

Every product I have shipped in the last two years runs on the same boring stack. That is not a failure of curiosity. That is the point.

6 min read
Gagan Deep Singh

Gagan Deep Singh

Founder | GLINR Studios


I keep track of the stack each of my products runs on. Looking at the list this week, fifteen of the last sixteen use some version of the same four choices. Next.js on the frontend. SQLite or Postgres for data. Tailwind for styling. Vercel for hosting. The outlier is a Rust CLI.

Two years ago I would have called that a lack of imagination. I have changed my mind.

What boring actually buys you

Every hour you spend learning a new framework is an hour you did not spend writing the thing your users wanted. That trade works when the new framework solves a real problem. The trade gets expensive when it solves a problem you do not have yet.

Boring stacks buy three things:

Muscle memory. I can open a new Next.js project and have auth, routing, a database connection, and a deploy pipeline running in an hour. Not because I am fast. Because I have done it thirty times. The second hour goes straight to the product. The new-framework version of that hour goes to reading docs.

Debugging speed. When something breaks in a stack you have used for two years, you recognize the shape of the error before you finish reading it. When it breaks in a new framework, you are paying attention to two things at once: the bug, and the semantics of a system you barely know. That split attention doubles the time to fix.

Predictable edge cases. I know what Next.js does on cold starts. I know how SQLite behaves under concurrent writes. I know which Tailwind utility classes render differently between browsers. These are small facts that together save me an hour every week.

The cost of chasing

I have chased. Deno 2, Bun, Qwik, Astro islands, Remix at different points, a brief fling with SolidJS. Some were fine. Each cost me weeks of ramp-up. In every case the thing I would have shipped on my boring stack would have reached users faster.

The chase makes sense when the new thing genuinely unlocks something the old thing cannot. A faster dev loop is not that. Syntactic sugar is not that. Being trendy on Twitter is especially not that.

What would qualify: a new constraint the old stack cannot meet. Real-time collaboration with thousands of concurrent users is one example. Sub-hundred-millisecond cold starts for an edge-rendered app is another. Neither applies to fifteen of my sixteen products.

Why SQLite runs all my small things

For anything under a thousand users, SQLite is not a compromise. SQLite is the correct answer.

SQLite gives you:

  • A real relational database with transactions, foreign keys, and indexes
  • Full-text search via FTS5 that is fast enough for most search use cases
  • Zero setup, zero ops overhead, one file you can back up with cp
  • Deploy-to-Fly or deploy-to-Railway volume mount, done in five minutes
  • better-sqlite3 in Node gives you synchronous calls, which simplify your code in ways async Postgres never will

The threshold at which you should switch to Postgres is not traffic. The real trigger: when you need several write nodes or horizontal read replicas. For most solo products, that moment never arrives.

The cultural pressure to use Postgres from day one is real and, for small projects, wrong. Postgres is a correct answer to a problem you do not have yet. Solving problems you do not have is another form of overthinking.

Why Next.js over the alternatives

People often ask why not Astro, why not Remix, why not pure React with Vite. Here is the honest answer: I have shipped on Next.js more times than I have shipped on anything else, and the ability to open a new project and move is worth more to me than a cleaner router API or a slightly faster build.

Next.js's rough edges are ones I know how to route around. I know when to use Server Components and when to bail to Client Components. I know which data-fetching patterns work with Vercel's caching and which do not. I know which Tailwind arbitrary values cause hydration warnings. None of that is universal knowledge. Every bit of it is mine, and trading it for a new framework's knowledge carries a real cost.

Astro might be better for static-heavy sites. Remix might be cleaner for form-heavy apps. I am not going to switch for small wins when my existing stack ships.

The unlock of one stack across many products

Running fifteen products on similar infrastructure has a compounding benefit: the tooling I build for one ends up useful for five others.

Example. For theSVG I wrote a small script that generates MDX files from a directory of SVGs. That same script, lightly modified, now builds the icon catalog for Glinui. Another near-copy generates the brand detection logic in FeaturedDrop. Cross-pollination is cheap when the substrate is the same.

If every product ran on a different stack, I would have no shared utilities. I would have eight slightly different versions of the same thing, and every one would need maintenance independently. Variety kills compounding.

What I would switch for

Not nothing. Three things would move me.

  • A database primitive that gives me SQLite-style simplicity with live multi-node replication. Not polling, not eventual consistency, real synchronous replicated state. If this shows up and the implementation reaches production-grade, I switch.
  • A React-compatible framework that lets me ship without a build step. Some partial hydration experiments are getting close. None are there yet.
  • A non-chromium browser-automation primitive that reliably handles auth. I keep having to fall back on full headless browsers, which is overkill.

Until something like these shows up, I am staying boring. The output tells me the strategy is working.

The meta-point

The boring stack is not about being lazy. The point is allocating the scarce resource. The scarce resource for a solo builder is not compute. The scarce resource is attention. Every context-switch to learn a new tool is a withdrawal from a bank account that never gets refilled. Boring stacks let me keep the attention in my account and spend it on the product.

If your output is going up year over year, your stack is probably right for you, no matter how boring it looks from outside. If your output is going down and you keep rebuilding your stack, you might be rearranging furniture in a house nobody lives in yet.

Build the house. Decorate later.


Contact