← All posts

The Lean Stack: Why We Went Back to HTML, CSS, and Fundamentals

The Lean Stack: Why We Went Back to HTML, CSS, and Fundamentals

For the past year, everything new we shipped — including this very website — has been built on what I call the lean stack: pure HTML, CSS, and JavaScript fundamentals, with just enough tooling on top to stay productive.

No React. No Next.js. No bundler. No build step.

And honestly? We ship faster than we ever did with the "modern" stack.

The frontend: jQuery and Bootstrap, evolved

If you were building for the web fifteen years ago, you remember how simple it was: jQuery for interactivity, Bootstrap for layout, one HTML file, hit refresh. That simplicity never stopped working — we just buried it under webpack configs and hydration errors.

The lean stack is that same idea, matured:

  • Alpine.js — reactive behavior declared right in your markup. It is the spiritual successor of jQuery, minus the spaghetti.
  • htmx — the server sends HTML, the page updates. What AJAX should have become before we decided every website needed to be a JavaScript application.
  • Bulma CSS — a clean, classless-friendly framework that plays the role Bootstrap used to, without dragging its own JavaScript along.

Together they weigh a few dozen kilobytes. The browser does what browsers have been optimized to do for thirty years: render HTML. There is nothing to compile, nothing to hydrate, and when something breaks, the error is right there in the file you wrote.

The backend: Bun + Hono + TypeScript

On the server, we pair this with Bun and Hono in TypeScript. One binary, instant startup, SQLite built in, and a router so small you can read its entire source over coffee. A full production website — server-rendered pages, analytics, admin dashboard — fits in a handful of files that a single person can hold in their head.

That last part matters more than any benchmark. In the era of the one-person dev team, the constraint is no longer typing speed. It is how much of the system one mind (and its AI agents) can fully comprehend. A lean stack maximizes exactly that.

Same philosophy, all the way down to the metal

This fundamentals-first mindset doesn't stop at the web. In our embedded work — ESP32 sensor nodes, single-board computers running on factory floors — we apply the same principle with Rust: no runtime, no garbage collector, no mystery layers between your code and the hardware. The compiler is strict, which is precisely why AI agents write it so well — the feedback loop is immediate and honest.

That stack deserves its own article, so I wrote one: Rust on ESP32 and Single-Board Computers: Our Embedded Stack.

Leaving the cloud, reclaiming governance

The second half of this story is where the lean stack runs: on our own hardware.

After a decade of "cloud first", the bill came due — not just the monthly invoice, but the governance bill. Your data sits in someone else's region. Your uptime depends on someone else's status page. Your AI features pipe your customers' information through someone else's API, under terms that change quarterly.

Self-hosting is not nostalgia. It is the foundation of any serious local AI strategy. If you want models working on your company's real data — contracts, financials, operations — the sane place for that is infrastructure you govern. As I argued in Technology is Real Power: who owns the data, who can access the model, who can shut it down — those questions decide everything. Self-hosting is how you make sure the answer to all three is you.

And here is the part nobody tells you: a Bun/Hono monolith on a modest self-hosted box, behind a tunnel, serves more traffic than most startups will ever see. The lean stack is what makes leaving the cloud practical.

Efficiency is the feature nobody budgets for

The cloud taught a whole generation of engineers a terrible habit: when the system is slow, buy more of it. Add instances. Bump the tier. Turn on autoscaling and let the invoice absorb the problem.

Throwing money at servers is not engineering. It is renting a bigger carpet to sweep inefficiency under.

Most of the workloads I see burning four-figure monthly cloud bills would run comfortably on a single modest machine — if anyone had spent a week making them efficient. An N+1 query hidden behind an ORM. A 2MB JavaScript bundle re-hydrating a page that could have been static HTML. A queue of microservices serializing JSON to each other to do what one function call used to do. Autoscaling doesn't fix any of that; it just multiplies it by the hourly rate.

The lean stack forces the honest conversation. When you self-host, hardware stops being an abstract line item and becomes a physical fact: this box, this much RAM, this many cores. You feel waste, so you fix it — a proper index, a cached render, HTML instead of a SPA payload. And modern fundamentals repay the effort absurdly well: Bun serving rendered pages with SQLite on local disk handles more requests per second than an entire fleet of containerized Node services passing traffic through load balancers to reach a managed database three network hops away.

Efficiency compounds exactly where it matters most in the AI era, too. GPUs and tokens are the expensive resources now. A team trained to reach for "more compute" as the answer will drown in AI infrastructure costs; a team trained to squeeze the workload first will run local models on hardware they already own.

The mindset shift tech management needs

The hardest migration is not technical. It is managerial.

An entire generation of tech leadership learned to equate "professional" with the React/Next.js pipeline: the SPA, the component library, the build farm, the five-figure Vercel bill, the team of specialists needed to change a button. We normalized frontend hell and called it best practice.

In my assessment, most of that complexity was never serving the product. It was serving the org chart — and the hiring pipeline that fed it.

AI broke that equilibrium. When agents can generate and maintain code, the winning move is to give them the simplest possible target: standard HTML, standard CSS, small server functions. Every layer of framework abstraction is now pure overhead — for the human and for the machine. Managers who keep measuring seriousness by the size of the toolchain will keep paying 10x for software that ships 10x slower.

The teams that internalize this — fundamentals first, frameworks only when they earn their keep — are the ones quietly outrunning everyone else.

Mobile without the toll booth

Apply the same fundamentals to mobile and something liberating happens: for most commercial apps, you don't need the app stores at all.

A PWA built on this exact stack — installable, offline-capable, push-enabled — covers the real needs of the overwhelming majority of business applications: dashboards, ordering, booking, field operations, internal tools. Same codebase, same team, same deploy pipeline as your website.

What you skip is everything else: the 30% platform tax, the week-long review queues, the arbitrary rejections, the annual SDK-churn treadmill, and the quiet reality that a platform gatekeeper can remove your app — your business — from the market at any moment. For games and heavy native integrations, fine, the stores earn their cut. For the ordinary commercial app? The open web is the distribution channel nobody can revoke.

Closing thought

None of this is retro. Alpine, htmx, Bun, Rust, local models on your own GPUs — this is the current frontier, not the past. What we abandoned was not old technology; it was the discipline of building on fundamentals we actually control.

The lean stack gives that control back. Own your markup, own your server, own your data, own your distribution. In the AI era, that ownership is the whole game. 🚀