One Boring Stack, Three Products
Shashank Manjunath
Quit Log, Frictionwell, and a small internal tool I built to manage my own subscriber emails all run on the exact same stack: a single Postgres database, a plain server-rendered backend, and just enough client-side JavaScript to make the parts that need to feel instant actually feel instant. I chose this stack for Quit Log four years ago, for reasons that had nothing to do with foresight, and I have not seriously reconsidered it since — not because nothing better has come along, but because "has something better come along" turns out to be the wrong question for someone building alone.
The cost nobody puts on the roadmap
Every framework or database migration I've watched other indie builders undertake carries the same hidden cost, and it's not the migration itself — it's the six to eight weeks afterward where you're debugging problems you don't have intuition for yet, because your intuition was built on the old stack and the new one breaks in unfamiliar ways. I know this because I made this mistake once, early on, moving Quit Log's backend to a newer framework that promised meaningfully better performance. The performance gain was real and, at Quit Log's actual scale, completely irrelevant — a few hundred users generate load that any reasonable stack from the last decade could handle without breaking a sweat. What wasn't irrelevant was the two months I spent relearning how my own error logs read, why a deploy that used to take ninety seconds now took four minutes, and where a specific class of subtle data-race bug I'd never had to think about before was now quietly possible.
"The new framework was better on a benchmark I'd never actually hit. It cost me two months of debugging problems my old stack had already taught me not to make."
What staying boring actually buys
The practical upside of running three products on one stack shows up constantly, in ways that are individually small and collectively the entire reason I can build alone at all. A retention fix I built for Quit Log's cohort logic ported to Frictionwell in an afternoon, because the underlying query pattern was identical:
select
date_trunc('week', signed_up_at) as cohort_week,
count(*) filter (where last_active_at >= now() - interval '30 days') as retained,
count(*) as total
from users
group by 1
order by 1;That query, or a close variant of it, has now shipped in some form on all three products, and I didn't have to relearn a new ORM's syntax or a new database's quirks to write it each time. When something breaks in production at 11pm — and something always eventually breaks in production at 11pm — I'm debugging a system I've now spent four years building instincts for, not a system I picked up eight months ago because a blog post convinced me it was the modern choice.
- One stack means one set of debugging instincts, compounding — every bug I've fixed on any of the three products has made me faster at fixing the next bug on any of the others, because the failure modes rhyme.
- Boring means well-documented, which means fewer dead ends — a stack that's been stable for years has had its edge cases written up by someone else already; a stack six months old has edge cases only you are discovering, in production, alone.
- Code genuinely ports between products — the cohort query above, and a dozen things like it, moved between codebases in an afternoon instead of a rewrite, because the underlying primitives never changed.
- The opportunity cost is a real product, not a hypothetical one — every week I don't spend evaluating a new framework is a week spent on a retention fix, a support reply, or a feature a real user asked for.
The one exception, and why it stayed an exception
I have made exactly one deliberate deviation from this rule in four years, and I think it's instructive precisely because I can point to the specific, narrow reason for it. Frictionwell's mobile client uses a cross-platform framework the other two products don't touch at all, because the alternative — building and maintaining genuinely separate native iOS and Android codebases, alone, on top of everything else — was a cost I could name precisely and couldn't justify at this stage of the business. That's the bar I now hold any deviation to: not "this is theoretically better," but "I can name the specific constraint this solves that my existing stack cannot solve at all," which is a much higher bar than most stack-comparison content on the internet ever asks you to clear.
Even within that one exception, I kept the deviation as narrow as I could manage. The mobile client talks to the same Postgres-backed API the server-rendered products use, through the same authentication and the same query patterns, so the actual surface area of "new stack I have to build fresh instincts for" is limited to the client rendering layer and nothing underneath it. A full migration would have meant relearning my own backend logic in a new language, on top of relearning a new frontend framework, and I don't think I would have had the bandwidth to ship anything else for the better part of a year if I'd taken that path instead.
"The one exception I made, I made narrowly, on purpose, and I kept everything underneath it exactly the same. That's the difference between a deliberate deviation and just chasing whatever's new."
The trade I'm actually making
I don't think this is a claim that the stack I picked four years ago is objectively the best one available today — it almost certainly isn't, on some dimension a framework comparison post would care about. The claim is narrower: for someone building alone, across multiple small products, the compounding value of familiarity reliably beats the marginal technical advantage of whatever's newest, up to a scale I am nowhere near approaching. If Frictionwell ever needs genuinely different infrastructure to handle real load, I'll make that change deliberately, with a clear reason tied to an actual constraint I'm hitting. Until then, boring is not a compromise I'm making. It's the actual strategy, and three products running on one stack is the evidence that it's working.
Shashank Manjunath
Small & Deliberate · Editor & sole writer
An Indian builder-operator writing about AI, teams, and the cross-cultural patterns shaping tech — read from Asia outward, with the West as the contrast class. This is a one-person publication; reply to any email and it reaches me directly.