Our own What it is page has a section called “What you give up,” and comments were the first thing on the list. That was honest when we wrote it. Comments are the classic reason a static site stops being static. You want readers to talk back, so you add a database, then an API in front of it, then a server to run the API, then a spam service, and now the cheapest part of your site is the article and the most expensive part is the box underneath it.
Or you hand the whole thing to a third-party embed, and now somebody else's script runs on every page, tracks every reader, and holds every word your community ever wrote in a database you can't open.
We didn't want either one. So we built comments the way the rest of Quickieweb is built: as files.
How it actually works
A comment is a small JSON file in the site's own bucket, under sites/<your-site>/_comments/<article>/. The file name starts with a timestamp, so S3's alphabetical order is also time order. That's the whole database.
Reading. When a reader opens an article, their browser asks the CDN for the list of comment files for that article. The tiny edge function that already checks every page view rewrites the request into an S3 listing and nothing else. The CDN caches the answer. No Lambda, no database, no server.
Posting. The browser uploads the comment straight to S3 as an ordinary form POST. It doesn't go through us. S3 accepts it because the form carries a signed upload policy (we call it a ticket), and S3 itself enforces what's in that policy: the content type, a 10 KB size cap, the exact folder, and the author's identity stamped on the file as metadata. If an upload breaks any of those rules, S3 refuses it. Nobody has to trust our JavaScript, because the rules aren't in our JavaScript.
The one function that wakes up. Getting a ticket requires a signed-in reader, and that's the same Lambda that already handles sign-in. It runs about once an hour per active commenter, when the ticket expires. Each time it re-checks that the person still belongs on the site and that comments are still on. Moderation (deleting a comment, or approving one from the pending queue on sites that require approval) also goes through it. Moderation is the CMS's job, so it costs a CMS call. Reading and posting never do.
The scorecard
The Flat Stack Manifesto has ten rules. The Scale-to-Zero Report grades other people against them, so it's only fair to grade ourselves.
- Rule 1 — Do not wake the Beast: A. There is no Beast. No database, no comment server. A thread is an S3 listing behind a CDN cache, and the edge function doing the rewrite was already running for the page view.
- Rule 2 — Keep your center of gravity low: A. Your comments are plain JSON files in your bucket. You can list them with
aws s3 ls, back them up with a copy, and leave with them any day you like. No vendor holds the only door. - Rule 3 — The balance sheet is the product owner: A−. At rest, a quiet comment section costs nothing. Posting a comment is one S3 upload, about half a thousandth of a cent. The minus: S3 listings cost about ten times what a plain file read does, so the CDN cache is doing real work there, not decoration.
- Rule 4 — Security, availability, resilience: B+. The upload limits are enforced by S3, not by code an attacker can skip. The honest part: anyone holding a valid ticket can still upload whatever bytes they like within those limits. So every reader's browser checks every comment on the way in (file name, author against the signed metadata, timestamp against S3's own clock, schema, lengths, characters) and drops anything that fails. Text reaches the page only as text, never as HTML. Reads go out with nosniff and a sandbox content security policy. And if you remove someone from a site, it takes effect when their current ticket expires, which is up to an hour, not instantly.
- Rule 5 — Every dependency is a decision: A. The comment library has no SDK and no framework. It's plain functions, and the code we test is the code readers get, rebuilt from the same source, so there's no second copy to drift.
- Rule 6 — Stateless, scale to zero: A. Nothing is warm between readers. The only Lambda wakes about once an hour per active commenter and goes straight back to sleep.
- Rule 7 — No shiny objects: A. A browser form posting to S3 under a signed policy is a feature S3 has had for well over a decade. JSON files in a folder are older than that. Nothing here is new, which is the point.
- Rule 8 — Modernization baked in: B. Too young to grade fairly. Ask us in a year.
- Rule 9 — Document your schemas: A. Comment files are versioned (
"v": 1) and the format is written down: comments, plus resolve, reopen and retract events that point at a comment. Nothing is overwritten. Edits are new files, so the history is the data.
Overall: A−. The architecture is about as flat as a comment system gets. The grades we didn't give ourselves are about what flat costs, and that deserves its own section.
What we gave up
There's always something given up. An article that pretends otherwise is marketing.
- No server-side spam filter. Nothing reads a comment before it lands, because nothing is running. If a signed-in reader posts junk that's shaped correctly, it shows up. The answer is the approval setting: comments land in a pending folder the CDN never serves, and they become visible only when an editor approves them.
- Revocation isn't instant. Removing a commenter, or turning comments off, takes effect within one ticket lifetime: an hour at most.
- It isn't live. You see new comments when the page loads, not as they're typed. No sockets, no presence, no “someone is typing.”
- Threads have a ceiling. The reader library stops at 20,000 entries per thread and says so. If an article ever gets there, we'll have a very different problem.
- The reader's browser does the checking. Validation runs on the reader's device instead of on our server. That's a few milliseconds of their phone instead of a machine of ours running all day. We think that's the right trade, but it is a trade.
The point
Comments were supposed to be the feature that proved a static site needs a server. It turns out what they needed was storage that enforces its own rules, a CDN that can list a folder, and a browser that doesn't trust what it downloads.
Our What it is page is out of date now. Good.
The principle: The comment section is the part of a website that is always awake. Make the storage enforce the rules, make the reader verify what it reads, and nothing has to be.