Farewell, Middleman

Four years ago, I wrote a post about porting this website from Jekyll (the great-grandparent of all static site generators) to Middleman (a Ruby-based alternative that takes the best features from Jekyll and bakes them into a more powerful, more flexible framework). However, after four years of spending more time updating/maintaining the Gemfile than actually writing posts, I decided to make yet another change to the way this site gets generated.

What I Actually Wanted

Jekyll and Middleman already offered a ton of features that I didn't want to give up—static content servable via HTTP, markdown support (with "frontmatter"), a lightweight templating language, etc—but I had a few items on my wishlist when I embarked on this search.

Near-zero boilerplate

I wanted to focus on maintaining only the text and templates necessary to render the site, and very little else. Moving to Middleman certainly helped with this (since there are a ton of extensions available as pluginable gems), but there were still a few large pieces of code that had to be committed to the repo—the Gemfile and Gemfile.lock, a server config (config.ru), and a plugin for embedding tweets, among a few other odds & ends.

I had also set up a Dockerfile that allowed me to render the site from my Windows PC, and I wanted to do away with this in favor of something that was easier to run natively on macOS, Windows, and Linux.

Faster setup times

Whenever I revisit this website, the first thing I do is reinstall/update its dependencies and deploy those changes. This means that when I'm motivated to write something, I often end up staring at the bundle install output, or waiting for a new version of Ruby to build (via rbenv). It's a small thing, but it can quickly stamp out whatever spark of inspiration I might've had.

Built-in shortcode support

One thing that had been hard to set up in Jekyll and Middleman (not impossible, just difficult) was the idea of a "shortcode"—something that can be embedded natively in the templating language supported by the content engine. In my case, I had been using a "tweet" shortcode to embed a tweet in this post.

Rendering that in-line looked like this:

{% tweet https://twitter.com/danwrong/status/171681426297729025 %}

However, I had been maintaining a 100+ line tweet_tag.rb file that was in charge of hitting the Twitter API, caching the tweet's content, rendering a non-JS version of the tweet, and wrapping it in the JS required to fetch and render the Twitter-enhanced version of the Tweet.

I wanted something that could more natively support shortcodes for any number of content types—tweets, YouTube videos, charts & figures, GitHub gists, you name it. And if the shortcode wasn't built-in, I wanted to make sure I could easily define a more declarative template without writing custom code, as I had done the Ruby-based solutions.

Enter: Hugo, Zola, and Gatsby

Since I knew I wanted something that could run natively on a wide range of platforms with ease, I started first by looking at languages known for solid cross-platform support. The top three that came to mind were Go and Rust, but I also considered JS frameworks since Node's cross-platform support has gotten much better over the years.

This led me to look at the three top static site generators for those platforms: Hugo, Zola (formerly Gutenberg), and Gatsby.

Ruling out Gatsby

While I was certainly impressed with Gatsby's sophisticated vision for the JAMStack architecture, I quickly realized that it violated my number one rule: reduce boilerplate to zero. You see, even though the project structure is relatively free from excess, the majority of the site and templates must be built as React components. As such, for even simple pages I'd end up writing and maintaining files that looked more like code than like templates.

If I were building an application with real interactivity (and real business value), I'd count this as a strong positive. But since this site is mostly focused on very minimal content, I decided against this high-horsepower approach in favor of something with a far more concise templating language. Especially since Gatsby's build performance is relatively slow, as compared to its Go and Rust competitors.

Hugo, or Not Hugo, that is the question

Next up was Hugo: "the world's fastest framework for building websites"

Right out of the box, this Go-based product certainly ticks the "faster setup times" box. Plus, it supports shortcodes, and since Hugo ships entirely as a binary, it runs natively on most platforms, while also cutting down on the boilerplate.

With a much narrower focus on static site generation (and fewer buzzwords like "JAMStack"), Hugo appealed strongly to my sense of finding the right tool for the job. However, there was a deal-breaker:

I hated the templating language.

While I'm sure it's perfectly capable, the Go-like syntax felt, at best, like overkill, and I suspected that it would result in far more mental overhead than I was willing to take on. So I kept looking. And that's when I found...


No, not the wedding planning website.

I mean, I just used that other Zola for my own wedding registry, so I wasn't particularly excited about this static site generator's name. But looking past that, I found something that resembles Hugo (written in Rust instead of Go), but with a much more intuitive templating language.

From a performance standpoint, Zola's build times are roughly equivalent to Hugo's (give or take), and everything else (boilerplate, shortcodes, cross-platform support) is functionally equivalent. To top it all off, I've been learning Rust in my free time, so supporting something built by the Rust community also felt satisfying.

So, this site is now running on Zola. The fact that you wouldn't be able to tell speaks to the power of static site generation in general -- I've been able to transition the content and layout across three different products, and each time has been easier and has resulted in a more concise and content-focused repo. Here's the obligatory git diff:

Git Stats

It's a small thing, but I find it satisfying.