Writing

A year of building, then unbuilding

2026-04-10

From an HTMX meal-planning idea to Stario—adding layers, then stripping them until the core stayed honest.

It started when I wanted to build an AI-powered meal-planning app. As a backend engineer I had almost no experience shipping anything substantial on the frontend, and I briefly considered learning React—luckily I didn’t.

I stumbled on HTMX, built a few smaller things, and realized that as a backend developer I could already do far more in the browser than I’d assumed. That felt like a good direction.

I sketched a simple outline of the app and, with SSE and HTMX, got it to a proof-of-concept that actually ran.

Then people from Datastar showed up and argued I could do the same thing with less code than HTMX.

I was skeptical, tried it anyway, and that was probably a “mistake” in the best sense: it changed the path completely.

I started with FastAPI and the Datastar SDK, rebuilt the PoC, and it already felt clearer and smaller. The deeper I went, though, the more I ended up fighting frameworks I had assumed were “just how things are.”

FastAPI sits on Starlette, so I wondered what a Datastar-native framework would look like if I folded in the pieces I actually wanted—something new, not a pile of adapters.

That’s where work on Stario 1.0 began: dependency injection plus Datastar as an integrated layer on top of Starlette, so I could keep a familiar surface from years of FastAPI and still build the way I wanted.

I built it, it worked, and I shipped it—roughly September 2025.

As soon as I tried to build something bigger, the friction showed up. Improving the experience meant pushing against Starlette more often than not.

In late November I decided to drop that stack and rewrite from scratch. Taking uvicorn and Sanic as inspiration for the server side, I put an HTTP/1.1 server on top of Python’s asyncio protocols. It worked.

Then another round of decisions. Dependency injection had become harder to explain than to use—especially in docs—and Go’s style of explicitness was a big inspiration. I borrowed a lot of that mindset and shipped Stario 2.0 toward the end of January: tighter core, clearer handling, overall smaller.

The pattern repeated. Pieces I had treated as essential started to grate; I wanted them gone. Stario 3 is that pass: a deliberately bare, small core—still borrowing from Go and elsewhere—doing exactly what it needs to and not much else.

It’s been about a year from the first experiments to now.

This is hard work. Explicit over implicit sounds easy until you implement it, use it, document it, and watch the clever seams split. You build something neat, lean on it in real code, and the edge cases whisper back.

The whole journey left me appreciating simple software: simple in the guts, simple in the sense that I can see what’s going on, and simple enough that I’m not hunting through layers of “magic” to remember how a feature behaves.

My taste in code shifted too. I used to reach for the shortest, cleverest version; now I care more that the file in front of me says what it does, without a shadow stack of indirect behaviour and corner cases I have to rehearse every time.

It’s tough—and I’ve learned a hell of a lot.

Where it’s going: keep shrinking the core until there’s nothing left to remove for the use cases I care about, and keep the surface honest enough that “how it works” and “how you use it” stay the same story.

← All writing