I've just started scoping a new side project, and the instinct after almost a decade of building production systems is to reach for infrastructure first — pick the stack, set up the monorepo, wire up auth, stand up a database. At a company, with existing users and existing traffic, that instinct usually serves you well: the risk isn't whether the idea works, it's whether the system holds up. A brand-new side project has the opposite risk profile. There's no traffic to hold up yet, and the thing most likely to kill it isn't a scaling problem — it's being wrong about the one idea the whole thing depends on.
Find the assumption that would kill it
Every new project has exactly one assumption that matters more than the rest — the thing that, if false, means nothing else you build is worth building. For this one, it's a specific algorithmic bet: does the core logic actually hold up against real-world data, independent of any pipeline around it? So that's the only thing I'm building first — a plain, isolated module, tested against real fixture data, with no server, no database, no UI attached to it at all. If it doesn't hold up here, in the cheapest, fastest environment possible, no amount of good infrastructure around it would have saved the idea.
Define "proven" before you start
It's easy to convince yourself a spike is going well just because you're busy. So before touching the core logic, I wrote down what "it works" actually means — specific, checkable outcomes, not a feeling. In this case: it needs to correctly catch the handful of real cases I already know it should catch, and stay quiet on the normal variation it shouldn't flag. Deciding that in advance matters more than it sounds like it should, because it's the only thing standing between "I proved the idea" and "I got used to looking at the output and decided it seemed fine."
Defer everything that isn't that
No queue, no multi-service split, no managed infrastructure with usage-based billing I can't predict yet. It's tempting to treat this kind of setup work as progress, because it's concrete and it feels productive — but if the core idea doesn't hold up, none of it would have mattered anyway. And if it does hold up, most of those decisions are additive later rather than something I'd need to unwind now.
Costs you can predict, not costs that scale
One deferral I'm being deliberate about even before there's real traffic: avoiding infrastructure that bills based on usage I can't yet estimate. At zero revenue, a flat, predictable cost is worth more than a "scales infinitely" promise I don't need yet — the failure mode of guessing wrong about usage-based pricing is a surprise bill, not a graceful upgrade path. Flat-rate infrastructure I can move off later, once there's an actual reason to, beats an elastic system I have to trust blindly today.
Build for the migration, not the rewrite
The one thing I am doing early is keeping internal boundaries clean — clear functions with defined inputs and outputs — even while everything runs as a single process. That costs almost nothing now and means that if this does need to split into separate services later, it's an infrastructure change, not a rewrite.
Validation isn't only a code problem
The riskiest assumption isn't always technical. Alongside proving the core logic, I'm also trying to get real signal on whether anyone besides me cares about the problem at all — talking to people, watching how they react to the pitch, not just watching test fixtures pass. A technically correct solution to a problem nobody has is still a dead end, and that's a cheaper thing to find out now than after the infrastructure exists.
Whether this particular idea works out or not, that sequencing — prove the riskiest part cheaply, decide what "proven" means up front, defer the rest, keep the seams clean, and check for real signal outside the code too — is the part I'd reuse regardless.