Software Essay

One File, One Vendor

A site that takes money, stores data, and sends email — running as one JavaScript file on one company's infrastructure.

Software | Essay
Back to writing

I built darthvader.mba a few months ago. It's a satirical leaderboard for companies that change the terms after you've bought in — you nominate one for $20, the public votes at a dollar a vote, and half of every fee goes to the EFF.

The joke is the product. But the checkout is real, and that means the thing has to actually work.

It takes payments. It stores nominations and votes in a database. It sends me email when a nomination lands. It has a private admin page where I approve or reject, and rejections get refunded. It renders a live leaderboard, a page per nominee, a sitemap, and a terms page nobody will read.

All of that is one JavaScript file. Sixteen files in the repository, and one of them is the site.

What it runs on

Everything is Cloudflare except Stripe and GitHub.

Stripe is the only third party in the request path, and only on the payment step. If Stripe goes down, the leaderboard and the nominee pages keep serving. GitHub holds the source but isn't in the path at all — I deploy with wrangler deploy from my own machine, not through a pipeline.

There is no framework. No build step. No bundler, no transpiler, no CI. I edit worker.js, type one command, and about twenty seconds later it's live in every Cloudflare data center.

Why this is possible now

Five years ago this same site would have been a static host, a separate API server, a managed Postgres somewhere, a transactional email vendor, and a deploy pipeline to tie them together. Five services, five bills, five sets of credentials, and a build that takes four minutes.

What changed is that the edge runtime got a database and a file server attached to it. SQLite at the edge is the piece that mattered. Once your data lives next to your compute and both live at the CDN, the middle tier stops having a reason to exist.

I've been doing this a long time. I don't often get to delete four layers.

What it costs

This is the part the enthusiastic version of this essay leaves out.

It's total lock-in. Not "we use some Cloudflare products." Every layer — registrar, DNS, compute, database, storage, email — is one company. Moving off isn't a migration, it's a rewrite. I chose that with my eyes open, but I chose it.

One outage takes everything. Not degraded. Gone. The site, the database, the admin page, and the email that would tell me something's wrong all fail together, and they fail through the same DNS I'd need to route around them. There is no partial failure mode because there are no parts.

D1 is young. It's SQLite with a good story around it, and the story is still being written. I'm storing votes people paid for. That's a real bet on a product that hasn't been through many years of other people's disasters yet.

No build step means no safety net. No types, no test harness, nothing between me and production. I have shipped bugs to this site that took real money and did nothing with it — a checkout that carried no metadata, so people's paid votes were charged and never reached the leaderboard. I found it because I looked, not because anything told me. On a site handling other people's payments, "I found it because I looked" is not a methodology.

The HTML lives in template literals. A thousand lines of it. At this size it's fine and I'd argue it's clearer than the alternative. At ten times this size it would be indefensible.

Where I'd draw the line

For a site this size the trade is right. The lock-in is real, but the alternative is a build pipeline I'd spend my time maintaining instead of writing the thing the site is actually for. Every hour on infrastructure is an hour not on the work, and on a one-person project that ratio is the whole game.

I'd change my mind at three points. If someone else had to maintain it, it needs types. If the money got large enough that a day of downtime mattered, it needs a second vendor somewhere in the stack. And if the file passed a few thousand lines, the template literals have to go.

None of those are true yet. When one becomes true, I'll do the work then rather than paying for it now against a future that may not arrive.

That's the whole argument. Build the smallest thing that's actually real, know exactly what you traded away, and write the threshold down so you notice when you cross it.