$ cat ~/projects/lancangkuning.mdx
LancangKuning.com
Rebuilt a legacy Laravel 5 news portal on a Go + Nuxt.js + Redis stack, cutting page load from ~1 minute to under 1 second.
- Go
- Nuxt.js
- MySQL
- Redis
Context
LancangKuning.com was a regional Indonesian news portal that had accumulated years of organic growth on a Laravel 5 stack. By the time I joined, the home page was taking 45–60 seconds to render under load, and the editorial team couldn't publish a breaking news story without watching the entire site fall over.
What I built
The rebuild was a near-total backend replacement, keeping the existing content:
- Go HTTP server with a thin, request-scoped middleware chain (auth, request ID, cache lookup, fallback to handler). The routing table is small — maybe a dozen handlers — but every one of them is cache-aware.
- Normalized MySQL schema — the legacy database was denormalized to a degree that made every article list query a 7-table JOIN with a
LIKE '%keyword%'on a TEXT column. The new schema has proper article/category/tag join tables and full-text indexes where they make sense. - Redis caching layered in two tiers: page-level (60s TTL on article pages) and fragment-level (longer TTL on the sidebar and footer). The invalidation story is "purge by article ID on publish" — no global flushes.
- Nuxt.js frontend doing SSR so the first paint is server-rendered HTML, with hydration for the interactive bits.
Outcome
Page load went from 45–60 seconds to under 1 second. The editorial team could publish during traffic spikes without coordinating with engineering. The bigger win was operational: the new stack was small enough that one engineer could hold the whole system in their head, which made on-call dramatically less painful.
What I'd do differently: I'd build the cache invalidation primitives first, then the cache layer, then the handlers. We did it handler-by-handler and the first version of the invalidation logic was a mess of "publish this, purge that" inline. Centralizing it in a small set of event-driven purges would have been cleaner.