No compilers. No bundlers. Just a generator.
This site is now a static site using Eleventy. It used to be a server-rendered Next.js app. Now, there’s no TypeScript, no PostCSS, no module bundler, no frameworks, and no server. The mean response time went down from 1.7 seconds to 130ms. And yet, when I fix a typo or publish a post, it’s updated pretty much instantly, as if it were a server-rendered app.
And rewriting it was so much fun!
Tinkering was fun until it wasn’t anymore
I was getting sick of taming dependencies, especially with the increasing amount of CVEs over the last months. Next.js updates were constantly breaking something. To make the site fast, I had to use experimental features, which of course kept breaking with updates. But not updating meant I couldn’t apply new performance improvements. Not mentioning that not updating sometimes meant dealing with security issues. And despite using all the performance optimization tricks, the site still felt slow. Because it was slow!
I spent more and more time configuring tools, updating dependencies, and fixing things that broke, instead of actually doing some fun tinkering. At some point, it just wasn’t fun anymore.
So I decided to rewrite it with Eleventy and get rid of pretty much everything I didn’t actually need and didn’t enjoy.
I’m now writing and shipping vanilla CSS and JavaScript. No compilation, no bundling, no PostCSS. I simply concatenate all the source files into a single CSS file and a single JavaScript file. I don’t even minify them, because it’s fine without minifying.
The little progressive enhancements I have are totally fine to maintain in plain JavaScript. I’m using swup for seamless page transitions, and Eleventy’s image plugin for image optimization and dynamic OpenGraph images.
I didn’t even install a linter or formatter. My editor already does linting and formatting automatically, so why even bother?
It’s a breath of fresh air to not care about configuring lots of different tools, installing dozens of dependencies, or figuring out which experimental feature broke this time. Instead, I can just write code.
But what about instant deployments?
One thing I really liked about the old site, and was a hard requirement for me, was how quickly I could publish changes. When I fixed a typo or published a post, it took just a few seconds for the changes to be live. I didn’t want to wait minutes for a new build just because I fixed a typo.
And I thought that wasn’t really possible with a static site generator. After all, the entire point is to generate the whole site without a server ahead of time, right?
Turns out, it works just as well.
I have a small stateful standalone service running that uses webhook to receive a GitHub webhook and call a bash script. The script pulls the latest content repo, runs an Eleventy build, and uploads the static site to a self-hosted git-pages instance.
I’ve added a small cache to Eleventy so it only rebuilds changed files, and git-pages only uploads files that actually changed. There’s no complicated deployment pipeline, no ephemeral CI/CD that needs to spin up and download some caches, just a stateful service and a bash script.
By the time I click “save” in my CMS (or push to git) and switch tabs to see the changes on the site, it’s already finished deploying.
So I still get the same near-instant publishing experience I had before, just without a server-rendered application running all the time.
Everything is replaceable
One decision from the old site that really paid off was keeping all the content as Markdown files with MDX in a separate git repo. When I originally made that decision, my intention was to be able to completely rewrite the site without having to change any of the content files.
And that’s exactly what happened. I’m still using the exact same Markdown files, in the same repo, without having to change the source content at all.
And still, visually, you couldn’t even tell that something had changed, even though all the code and the whole architecture had changed.
I’m also still using Keystatic as my CMS, which I now deploy as a small self-contained service. But the CMS isn’t a requirement for the site to work. Neither is my deployment setup.
If I wanted to change how the site is built and deployed, I could just delete the deployer/ folder and not touch anything else. I could have a cron running every minute that pulls changes and rebuilds the site if it needs to, without a webhook. Everything else would work exactly the same way.
If I didn’t want to use a CMS anymore, I could delete the cms/ folder. I could still write content in a text editor and push changes to git.
That’s something I really like about the new setup. Everything is just a small, independent piece that I can replace or remove without having to rewrite everything else.
For this, simple is enough
Don’t get me wrong, I still like frameworks and all the tooling that comes with them.
For larger production apps, I’d of course still choose a web framework. I’d still use Tailwind CSS because I like it, and I prefer writing reusable components over reusable classes. I’d still use TypeScript if I had to, though lately I’ve been enjoying Phoenix and Ash a lot because they make it so easy to write large, high-quality systems.
I’m not saying that all compilers, bundlers, frameworks, or TypeScript are inherently bad. They can be worth it.
But this is a blog. It doesn’t need all of that, obviously.
And I know my previous codebase was intentionally overengineered as fuck. Because overengineering was part of the fun. I wrote it into its readme, so curious people who want to learn don’t think all of this is necessary for a blog. I enjoyed experimenting with new tools, learning techniques that I’d also apply to larger production apps, and building something much more complicated that it needed to be. Until I didn’t enjoy it anymore.
Now I’m having fun writing code again, without constantly thinking about the dozens of dependencies around it. The site is faster, publishing is just as quick as before, and there are fewer things that can break.
No compilers. No bundlers. Just a generator. And for this site, that’s more than enough.
…and last but not least, to address the elephant in the room: Rewriting a blog is, like always, a great motivator to write more content again.