Thursday 4 Dec 2025
My website is built with a home-cooked static site generator (don’t look, it’s a mess). I wrote a half-baked markdown parser I haven’t touched in a year. Until now!
It’s time to under-engineer Hmmarkdown!
De-sync
I coded my markdown parser to be “optimistically async”. That’s not a fancy programming term, I mean literally every function was async. I had a vague notion of parallelised rendering for performance, despite knowing Promise.all isn’t magically faster. In fact, it can be slower than a for loop, as I learned.
I did have success with a [s…
Thursday 4 Dec 2025
My website is built with a home-cooked static site generator (don’t look, it’s a mess). I wrote a half-baked markdown parser I haven’t touched in a year. Until now!
It’s time to under-engineer Hmmarkdown!
De-sync
I coded my markdown parser to be “optimistically async”. That’s not a fancy programming term, I mean literally every function was async. I had a vague notion of parallelised rendering for performance, despite knowing Promise.all isn’t magically faster. In fact, it can be slower than a for loop, as I learned.
I did have success with a streamable implementation. It can parse a ReadableStream line-by-line and yield partial HTML. This doesn’t require the rest of the library to be async. And it’s only useful if I’m streaming markdown and need to serve it rendered in chunks. And who does that?
In reality I realised it was quicker to read all markdown into memory. For my website around 500 files is only a few megabytes. Each file is parsed synchronously in milliseconds or less. Using async and streams in the markdown library was slower.
By making the code synchronous I saved 50ms. Which doesn’t sound a lot but that was like a 15% speed up. It takes less than half a second for the entire website. So more blazingly fast than it already was.
Cache
Syntax highlighting with Shiki is a bigger bottleneck than markdown. Shiki alone took ~1500ms to render every code snippet on my blog. I attempted to multi-thread this with Web Workers. The worker overhead was slower than a synchronous approach. I shaved of more milliseconds by simplifying the code.
What really saved time was skipping Shiki entirely. My code snippets rarely change, why repeat the work? I started caching the highlighted HTML using a quick hash as a reference.
Maintainable
I can now build my 1000-ish page website in under 4 seconds. Slightly faster than the “around four seconds” it took before. This is all a bit pointless because random fluctuations in CPU and I/O usage can plus or minus a second. macOS has become increasingly aggressive in hijacking my computer for its own background tasks.
What matters is that I’ve basically just deleted a ton of code. Less code is easier to understand and maintain. And I’m not done yet! I plan to keep deleting code until something breaks!