Engineering 13 min read

The stack I reach for — and why it works for me, not for everyone

Google Cloud, Firebase, Cloudflare, Neon, Astro, React, Swift and Kotlin. The stack I build solo apps on, why I chose each piece, and why it suits my situation — not yours by default.

Every few months someone asks me what I build on, and I give them the list — Google Cloud and Firebase, Cloudflare, Neon when I need SQL, Astro and React on the web, Swift and Kotlin on mobile — and then I immediately start adding caveats, because the list on its own is misleading. A stack isn’t a recommendation. It’s a set of trades, and the trades only make sense once you know who’s making them.

So before any of the names: this is the stack of a platform engineer who ships solo apps in the evenings. A long background in cloud infrastructure, platform engineering and Linux administration — which matters more than it first looks, because of a trap it sets. On a side project there is no platform team. I’m it. And if I let myself, I will happily disappear into being the platform engineer for my own side project — building the Terraform, the tooling, the perfect pipeline — and never ship the product I actually wanted to build. So the discipline isn’t doing less infrastructure because I don’t understand it; it’s doing deliberately less of it because I understand it too well and know how far it can swallow my evenings. Reaching for managed services is how I stop myself from platform-engineering instead of shipping. If your situation is different, the right stack is different. What might transfer is the reasoning, so that’s what I’ll spend the words on.

The gravity well: Google Cloud and Firebase

Almost everything starts in Google Cloud, and the honest reason is a combination of two things that are easy to say and hard to feel until you’ve lived both sides: the free tier is genuinely generous, and the unit economics hold up as you scale. A side project can sit at essentially zero cost for a long time, and the curve from there is predictable rather than punishing.

The deeper reason is who the platform is aimed at. Google’s services are built for a developer to use directly, not for an infrastructure team to operate on a developer’s behalf. That’s the opposite of how a lot of cloud tooling is shaped, and for a team of one it’s the whole game — I can stand up auth, a database, functions, scheduled jobs and analytics without first building the platform team that would normally sit between me and those things. The services are scalable and, more importantly, scalable without me thinking about it.

There’s a real catch, and I want to name it sharply because it’s the part that bites people: ease compounds cost. The same frictionlessness that lets you wire up a service in minutes lets you wire up a service that quietly bills you for a configuration you forgot about. The defence isn’t what an infrastructure person’s instinct says it is. My instinct says infrastructure-as-code, and yes, I do that — but for a solo project IaC is not the thing that saves you from a four-figure surprise. Billing alerts and a hard kill-switch are. I care far more that a budget alert fires, and that there’s something ready to actually act on it, than that every resource is perfectly Terraformed.

That’s literally why I built the-slashinator: a guard that makes sure a missed configuration can’t bill me an extortionate amount before I’ve noticed. It exists because “the cloud is easy now” and “the cloud can quietly ruin your month” are the same sentence, and the only way I’m comfortable enjoying the first half is having automated the second.

The other thing Google quietly gives a generalist is data and AI without specialising in either. I’m not a data engineer or an ML person, but BigQuery, Vertex AI and Gemini are approachable enough that I can do real analytics and small-scale AI integration without that being a project of its own. Gemini has a genuinely free tier to prototype against via Google AI Studio, and Vertex AI — a billed, enterprise-grade platform rather than a free one — is very cost-efficient for the small-scale work I throw at it. Tooling like the Agent Development Kit and the other lightweight frameworks around it (worth poking at for simple builds) means an AI feature can be a feature, not a research programme. For someone whose specialism is elsewhere, that accessibility is worth as much as any single capability.

Firebase as a backend — and where its edges are

When an app genuinely needs a backend — shared state, multi-user data, something server-authoritative — Firebase is what I reach for, because it’s the easiest “real backend” I know that doesn’t ask me to run anything. And it’s got materially more flexible over the years; the gap between “Firebase toy” and “Firebase product” has narrowed a lot.

But it has edges, and they’re structural rather than cosmetic:

  • Dev versus prod takes deliberate thought. There’s no free “environment” abstraction handed to you; you decide how that works.
  • Auth across projects is awkward if you want a single auth source — an Auth0-style identity provider that several apps share. Firebase Auth is happy inside its own project; making one identity span many is swimming upstream.
  • You’re mostly confined to the logical boundary of a project. There are ways around it, but they’re work, and the natural grain of the platform is one project equals one thing.

My response to that last one is the bit people find surprising: I stop fighting it. The boundary is effectively 1:1, projects are free, so why contort everything into one mega-project? I run one Firebase project per logical app. It keeps blast radius small, IAM legible, and dev/prod honest, at the cost of no shared monolith — which I’ve decided I don’t want anyway. To make “many projects” cheap to live with, I’ve got a simple, repeatable way to bootstrap a new project to the same baseline — an upfront tax you pay once so spinning up the next one isn’t a decision. The one thing to know going in: you’ll eventually hit the default per-account project quota (it’s not large — think dozens), at which point you’re filing a quota-increase request. Worth knowing before it surprises you, not a reason to avoid the pattern.

The single, deliberate exception is data. Data is the one thing I do centralise: rather than letting analytics pool in a dozen projects where it’s useless, I run data pipelines that feed every project into one central data and analytics project. Everything else is deliberately scattered; consolidation lives in exactly one place, on purpose.

And there’s a quieter answer that often beats all of this: for a single-user app, the right amount of Firebase backend is none. I rebuilt SteelRep from a Firebase backend to fully backendless on native storage, and the cleanest backend turned out to be the one I deleted. That’s its own story — the point here is just that “Firebase the backend” and “no backend” are both live options I weigh, not a default I reach for on reflex.

The edge: Cloudflare for hosting and bindings

For hosting, I keep landing on Cloudflare Pages and Workers, and it’s not a knock on Firebase — Firebase Hosting and App Hosting are genuinely good. It’s that Cloudflare’s model fits how I want to work. The CI/CD is trivial to set up, the DX is built around speed and simplicity, and the end state is your thing sitting on a fast global edge by default. That last bit is a small advantage rather than a decisive one, but small advantages with no downside are exactly the ones worth taking.

What’s made Cloudflare more than a CDN for me is the bindings. Inside Pages Functions and Workers you’ve got KV, D1 (databases), R2 (storage), image hosting and AI services right there. They range from very mature to merely fine, and the practical effect is that “do this on the edge, or do it in Google?” becomes a real per-feature decision rather than a platform-wide commitment.

Which is the part I want to stress: none of this is mutually exclusive. A single app of mine might keep a KV store on Cloudflare’s edge while its primary database is in Firebase or Neon. I’m not picking a team. I’m putting each piece of state where that piece of state is cheapest and simplest to serve, and the two platforms compose far more happily than the “pick one cloud” framing suggests.

For the static end of this — marketing sites, blogs, this site — the colophon goes into the specifics of why it’s Astro building to static files on Cloudflare with no server in sight.

SQL only when I need it: Neon

Sometimes the data model genuinely wants relational SQL, and when it does I reach for Neon — for one dominant reason: cost, via scale-to-zero. An idle Neon database costs essentially nothing. For a side project that might see traffic in bursts, or might not see it for weeks, that’s the difference between a database being free to keep around and being a line item I resent.

It gives me the things a hosted SQL provider is supposed to: managed Postgres, backups, branching, the lot. The one caveat worth knowing is that it’s hosted on AWS, so if I’m sitting in Google Cloud the cross-cloud network hop is real. In a true low-latency environment that would matter — but with a normal database cache in front of it, it almost never shows in practice. I keep it filed under “know it’s there,” not “worry about it.”

The honest framing is that Neon is the early answer. It scales relatively well, but if a project actually takes off, that’s the moment to do the unit-economics maths and work out where a cutover to an always-on CloudSQL or Spanner starts to make sense. Scale-to-zero is a brilliant deal at low volume and a worse one at high steady volume; the skill is knowing roughly where the lines cross and not cutting over a day before you have to.

The web: Astro and React, because I’m a weak frontend dev

I’ll say the quiet part: I’m a bad frontend developer. That’s not false modesty, it’s a load-bearing fact in this decision. I want frameworks with vast documentation and a huge corpus, because that’s exactly where AI assistance is strongest — and for the front end I lean on that help heavily. Astro and React both clear that bar enormously, which is most of why they’re my two.

I split them by what the page actually is:

Astro — content-shaped sites. Marketing pages, blogs, anything where the job is to render content fast and rank well.

  • Benefits: ships zero JavaScript by default, so it’s fast and SEO-friendly out of the box; content collections and Markdown make a blog pleasant; islands let you add interactivity only where you need it; the docs are excellent and AI handles it well. I built this site, SteelRep’s web presence and LastLift’s in it with very little effort.
  • Drawbacks: it’s not built for app-like, state-heavy interactivity. The moment a page wants lots of shared client state across components, you’re working against the grain, and the islands model has its own learning curve.

React — interactive product UIs. Dashboards, builders, anything that’s really an application wearing a web page.

  • Benefits: the default for rich interactive UIs, an enormous ecosystem, and AI knows it cold. When a page is genuinely an app — like the coach and athlete surfaces in IronWolf — the component-and-state model is the right tool.
  • Drawbacks: it ships a runtime and more build weight; it’s easy to over-engineer something that should’ve been a static page; and you own more of the performance story yourself rather than getting it for free.

The rule I actually use: if it’s content, Astro; if it’s an application, React. Static brochure-and-blog work goes to Astro and lands on Cloudflare’s edge; an authenticated, interactive product gets React. You can see both choices sitting side by side across the things I keep.

Mobile: native Swift and Kotlin, by choice

On mobile I write Swift for iOS and Kotlin for Android, natively, and this one is the most deliberate reversal in the whole stack. I spent a long time in React Native and Expo, genuinely productively, and I chose to leave it. Not because native is cool — though it is — but for reasons that kept compounding:

  • Early access to platform APIs. New OS capabilities show up natively first; the cross-platform layer is always catching up, and the features I wanted most were exactly the platform-native ones.
  • Mature first-party SDKs for the things I actually use, instead of a community shim of varying quality wrapping them.
  • No version-mismatch churn. No more afternoons lost to which library version maps to which framework version, or an SDK upgrade detonating the native build underneath me.

I wrote the full version of this — the migration, what flipped the trade, and what I got wrong about shipping Android too early — in why I rebuilt from React Native and Firebase to native and backendless. The short version is that the cross-platform tax stopped buying me the cross-platform benefit, so I stopped paying it.

The actual lesson: match the stack to your situation

Read back over all of that and you’ll notice none of the choices are “this is the best technology.” They’re “this is the best technology for a platform engineer building solo apps with no team and no patience for undifferentiated heavy lifting.” Generous free tiers matter because there’s no budget. Developer-aimed services matter because I am the platform team, and the only way to ship products as well is to refuse to spend my evenings being it. Scale-to-zero matters because traffic is bursty and uncertain. Native matters because I’m building for the long term on platforms I want to use fully.

Change any of those facts and the stack should change with them. A funded team with a dedicated infra group might rightly choose more powerful, more granular tools and happily pay the complexity. A product whose entire value lives in one capability another platform does better should follow that capability.

So this isn’t the golden path, and I’d be suspicious of anyone who told you they had one. It’s a path that fits my feet. If you’re in a similar spot — a strong generalist shipping things solo, allergic to running servers you don’t need — then maybe enough of this rhymes with your situation to be worth stealing. Take the reasoning, pressure-test it against your own constraints, and let that pick your stack. The list is just where I happened to land — and if you’d like a second pair of eyes on where yours should, that’s the kind of architecture and planning work I do through Thoughtgears.

FAQ

Why Google Cloud over AWS?

Developer ergonomics, unit economics, and honestly a decade of habit. I've built on Google since App Engine went GA — 10+ years — so I'm fast and fluent there, and the services are aimed at a developer rather than an infrastructure team, with a generous free tier and Firebase, Vertex AI and BigQuery that wire together easily solo. AWS is powerful and is genuinely catching up on ergonomics; this is as much personal preference as a technical verdict.

Do you really run a separate Firebase project per app?

Yes. Projects are free and the logical boundary is effectively 1:1 with an app anyway, so I stop fighting it and treat one project as one app. I keep a simple, repeatable way to bootstrap each new project to the same baseline so it's never a decision. Two things to know: it's an upfront tax you pay once per project, and you'll eventually hit the default per-account project quota (it's only in the dozens) and have to request an increase. The single deliberate exception is data — I pipe analytics from every project into one central data and analytics project rather than scattering it.

Why Neon instead of CloudSQL, Spanner or Firestore?

Cost, and only when I actually need SQL. Neon scales to zero, so an idle database costs nothing until there's real traffic. It's hosted on AWS, so in a genuine low-latency setting the extra network hop matters — but with a normal database cache it rarely shows. When the unit economics flip, that's the signal to cut over to an always-on CloudSQL or Spanner.

Why native Swift and Kotlin instead of React Native or Flutter?

By choice, after years on React Native and Expo. Native gets me early access to platform APIs, mature first-party SDKs for what I actually use, and no version-mismatch churn between a cross-platform layer and the native build underneath. I wrote up the full migration story separately.

Is this the stack you'd recommend to someone starting out?

Not blindly. It fits a platform engineer building solo who has to ration how much platform work goes into a side project. If you have a team, a different risk profile, or a product that lives or dies on one capability another platform does better, copy the reasoning, not the list. If you'd like a second pair of eyes on the architecture or the plan, that's the kind of thing I help with through Thoughtgears.

#stack #google-cloud #firebase #cloudflare #neon #astro #swift

Also published on Medium . The version here is canonical.