One stack for everything I build from now on
I have a bad habit. I read a blog post that says some framework is ten times faster, or some language is the future, and I start doubting what I am using. This year I changed the stack of one project three times. Each change made sense on the day I made it. Each one also cost me weeks. At some point you have to admit that you keep losing time because you never settled on a stack.
So I decided. One set of tools for the product apps I start from now on. Blogs, simple websites, and games can have different needs. I want to write it down, partly so you can steal it, mostly so I stop reopening the decision.
I am one person, sometimes part of a very small team. I have more product ideas than time. So I want tools that are maintained by big communities or real companies, that I expect to still exist in ten years, that let me import things instead of writing them, and that are cheap to run. I want to ship without spending every evening maintaining the tools.
Backend: Go. I looked hard at C# with ASP.NET, at Rust, at Python with Django, and at going back to TypeScript. All of them are fast enough. I doubt anyone would notice the difference in request speed on the kind of products I build. What I do notice is the hosting bill. In one project, my Node deployment used around 1.5 GB of memory. After the Go rewrite, it used around 200 MB. Those are numbers from that project, not a general benchmark of the two languages. I pay for memory every hour, whether anyone visits or not. Go compiles to one binary, has had a backward compatibility promise since Go 1, and is great for command line tools and background workers too. It has fewer batteries than ASP.NET or Django, so I keep one small kit of libraries I trust and copy it from project to project. That kit is my framework.
Web frontend: React. I have used it in most of my projects, and the ecosystem is a big reason I keep coming back. Svelte reads beautifully. Vue is lovely. But when I need a date picker, a data grid, a chart, or a form library for React, I can usually find a maintained option that has been tested on more browsers and devices than I will ever own. I tried the other road once with Ember. I liked the framework and then spent my time writing things that already existed for React. I learned my lesson.
Components: MUI. I want a library I can import and update as a dependency, rather than components I copy into my repo and maintain myself. I have used shadcn and enjoyed it, but I prefer that maintenance model. MUI has a company behind it, and I can theme it so it looks like my product and not like a Google product.
Mobile: native. Swift on iOS, Kotlin on Android. I want to work directly with the tools and APIs of each platform, so I am choosing that over Expo or React Native. I want the apps to feel right on each platform. I still have two apps to test and release. I am accepting that work because I want direct control over each platform.
The glue: one protobuf schema with ConnectRPC. From that schema, I generate the Go server interfaces and the clients for TypeScript, Swift, and Kotlin. I still write the server logic, but I write the contract once and every client gets types generated from it. For my app interfaces, I prefer a React client using the same API as the mobile apps. Server-rendered pages could use that API too, but I do not want to maintain server rendering as well. One caveat is that Connect currently labels its Kotlin implementation as beta, while Swift is stable. I need to check that the Kotlin client covers what an app needs before relying on it.
Hosting: Railway for the Go services and the database, with Cloudflare in front for caching and protection. I thought about Cloudflare Workers for the web. I do not expect enough savings at my size to justify splitting the hosting yet. Railway also offers private networking between services. A web server can use that to forward requests to the Go API, but browser requests still need a public endpoint. Hosting React on Railway does not give the browser access to that private network. The web app has no Cloudflare-specific code in it, so I can move it later if the bill says so.
If I had made this decision five years ago, I would have picked TypeScript everywhere: Node on the server, React on the web, Expo on the phones. One language, one mental model, because switching languages was expensive for one person. That cost has dropped a lot for me. With AI agents doing a large part of the typing, I can work in Go, TypeScript, Swift and Kotlin in the same week without the old friction. The agents help with the idioms of each, though I still have to understand and review the code. I feel less pressure to pick one language for everything, so I can choose the tools I want for each layer.