$ cat ~/projects/lancangkuning.mdx
LancangKuning.com
Rebuilt a slow Laravel 5 news portal with Go, Nuxt.js, MySQL, and Redis. Page load dropped from 45–60 seconds to under 1 second.
- Go
- Nuxt.js
- MySQL
- Redis
Context
LancangKuning.com was a regional Indonesian news portal that had grown for years on Laravel 5. Under load, the home page took 45–60 seconds to render. Publishing a breaking story could make the rest of the site unusable.
What I built
I replaced most of the backend and kept 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. Editors could publish during traffic spikes without calling engineering. The new stack was also small enough to understand end to end, which made on-call easier.
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.