Skip to content
🥷
Content at Scale · MigrationLive in production

Knowledge Chop

19,415 posts, no database, one very stubborn serverless bundle

knowledgechop.com

Knowledge Chop is a content archive: visual guides, single-line facts, and conversation starters, plus a game mode built on top of the question set. It was a WordPress blog with nearly twenty thousand posts and the hosting bill and page speed that implies. The rebuild kept every post and removed the database entirely.

19,415
Posts migrated
0
Databases
10
Page routes
6.2K
Lines of source

The problem

Twenty thousand short posts is an awkward scale. It is far too many to hand-manage, but the content is small and static enough that paying for a database and an ORM to serve it is pure overhead — every post is a title, a body and a category, and none of it ever changes.

The interesting constraint was discovery. With that much content and no accounts, the site needed to feel like it was surfacing something worth reading without a user profile, a session store, or anything to log into.

What we built

A scripted migration off the WordPress REST API

A one-time scraper walked the WordPress REST API and split the archive into per-category JSON: 12,686 visual guides, 5,105 facts, 1,570 conversation questions and a small uncategorised remainder. The result is committed data, so the rebuild has no runtime dependency on the WordPress install that produced it.

Recommendations that live in the browser

A recency-decay engagement model runs entirely in localStorage — it tracks what a visitor has looked at, decays older interest, and biases what surfaces next. It is a real recommendation system with no backend, no cookie, and nothing to leak, because there is nowhere to store anything.

Reproducible randomness

Shuffling uses a mulberry32 PRNG with Fisher-Yates rather than Math.random. Seeding means a shared link resolves to the same set for the person who receives it, and a bug in content selection can actually be reproduced from a seed instead of being chased.

Engineering highlights

The parts that were genuinely hard — and how they were solved.

The static site that outgrew static files

The genuinely hard part was deployment. Next.js does not trace JSON that is read at runtime rather than imported, so the serverless function shipped without its own content and 404'd in production while working perfectly in development. The fix was explicit output file tracing plus a loader that probes the serverless bundle root directly — a real production gotcha that only appears past a certain data size.

A 21MB file is a design decision

The largest category file is 21MB, and roughly 34MB of JSON has to reach the Lambda. Splitting by category rather than shipping one combined blob keeps each route's working set to what it actually needs, with the legacy combined file retained only as a fallback path.

No accounts, therefore no account problems

There is no auth, no user table, no password reset, and no personal data at rest. For a content archive, that removes an entire category of maintenance and liability in exchange for nothing the product actually needed.

Where it stands

  • Live at knowledgechop.com serving the full 19,415-post archive.
  • No database, no CMS, and no per-request backend cost for content delivery.
  • Search, related-content and category APIs all read from the same committed JSON shards.

The stack

Framework
Next.js 14React 18TypeScript 5App Router
Content
Static JSONPer-category shardingWordPress REST migration
Client logic
localStorage engagement modelmulberry32 PRNGFisher-Yates
Styling
Tailwind CSS 3Typography plugin
Infrastructure
VercelOutput file tracingStatic generation