The problem
I really want to highlight something most people don’t discuss in depth when picking what technology stacks to use for their static website, something I’ve had to deal with frequently when writing for this site, and something that I found that Hugo was the only reasonable solution to.
Dependencies.
st=>start: Site
para=>parallel: Site Generator
op1=>operation: Dependency 1
op2=>operation: Dependency 2
op3=>operation: Dependency 3
st->para(path1,bottom)->op1->op3
st->para(path2,right)->op2->op3
Here’s a real-life scenario for you:
You set up your site, meaning to update it frequently… alas, life gets in the way (as it does) and you end up updating it only a few times per year. Oops.
Also, you switched laptops and computers several times in-between this and have to reinstall all your dependencies so you can re-build your site… but.. uh oh! The theme you use is not compatible with the new version of your tech stack and it looks like some of the old library versions are no longer available and the new ones aren’t compatible with your other old libraries!
You’ve now entered dependency hell. Enjoy your stay.
st=>start: Site
para=>parallel: Site Generator
op1=>operation: Old Dependency 1 (needs Dependency 3 ver 1.2)
op2=>operation: New Dependency 2 (needs Dependency 3 ver 1.3)
op3=>operation: Dependency 3 (version conflict)|current
st->para(path1,bottom)->op1->op3
st->para(path2,right)->op2->op3
This tale should ring familiar to most developers. Just thinking about it brings back bad memories.
So what are the possible solutions?
“Solution” one: Never touch anything (Freezing)
There is an art to flying, or rather a knack. The knack lies in learning how to throw yourself at the ground and miss. … Clearly, it is this second part, the missing, that presents the difficulties. - The Hitchhiker’s Guide to the Galaxy by Douglas Adams
One alternative is freezing everything and trying your best to not touch anything or breathe too hard. You could for example create a docker image with a specific kernel version, frozen libraries and frozen system-wide software to ensure nothing changes. If you store the docker image somewhere you control as well you can also ensure it’ll never change or disappear.
However, freezing dependencies only works for so long and this is usually due to a layer 8 issue (humans being the weak link). It may be that your needs change over time and you find yourself longingly gazing at the newer versions, or for some inexplicable reason an old frozen library stops working due to changing APIs on any level in your stack. The latter is what happened to me, for some unknown reason stuff just stopped working (my suspicion is that for some reason one of the dependencies was not properly frozen for one reason or another). There were cryptic error messages abound I could have used to debug the issue but the literal last thing I want to do when I just want to get an article written is sift through thousands of lines of source code in an already outdated library to hopefully find a temporary solution.
That said, to its credit it does kind of work because you can always spool up a working container. The biggest blocker to really using this came down the line when I expanded the number of devices I was working from due to moving around a lot.
See, I don’t always work from the same device. I have a multitude of laptops, VMs, and computers for a good number of reasons, and sometimes I’m working from one and somethings I’m working from another.
I don’t want to and can’t install docker (or a hypervisor) on all of these since if I’m e.g. writing from a work laptop I may not even have permission to install random things like that. And if I don’t want to do that I’m right back in dependency hell again.
Similarly, if you’re stuck with the old versions you may have to end up making modifications to the libraries yourself if you want to support modern web standards down the line… Not touching isn’t always so easy.
Still a better problem to have than actual genuine dependency hell, but there is a better solution…
“Solution” two: Use a third-party host
You could of course always just write on Medium or host your website somewhere like Wix that are full-service platforms. Though I love Medium and actually pay for it myself, I personally do not feel comfortable keeping all my content on a third party service. Content creators get screwed over constantly on services like Youtube, Instagram, and Tiktok… and who’s to say this same pattern won’t be the future for Medium as well?
I can’t tell you how annoyed I’d be if 5 years down the line Medium suddenly decided cybersecurity articles are no longer allowed and I had to painstakingly migrate potentially dozens if not a hundred articles over to another host, and that’s assuming they would even let me access them anymore.
Unfortunately, I don’t think this is a good solution, since you’re effectively giving the power of your voice over to someone else which can really bite you in the butt if you ever want to talk about anything sensitive. And you are writing to make your voice heard, right? Who knows what you might want to talk about down the line.
The real solution: Reduce your dependencies
Yep, just stop using things that depend on millions of other things. I considered titling this article ‘why you should be using Hugo’ since, seriously, it’s just about the only piece of mature modern software that asks almost no dependencies for building static websites. Gatsby depends on node, which has a great (but messy) hierarchical dependency management system, but you’re still dependent on modules not disappearing off the face of the internet suddenly.
Hugo is almost entirely hermetic. It’s built in Go and is a single binary with separate versions for Linux, Windows, and Mac. That means you can just download the binaries into your Git repo and keep them around forever with no worries about them suddenly breaking (at least until we move onto some weird future x128 ABI).
Now when I move over to a new install literally the only thing I have to do is download the Git repo for my website and run the OS-appropriate binary to build my website. There are no other files to download in any way whatsoever.
3 files, that’s it.
And that’s only because I want to have binaries for Mac, Linux and Windows. The one downside is that the files are big. Like more than 100 mb big, so zipping is mandatory if you want to store them in GitHub (which has a 100mb file limit), but last I checked 7z managed a 80% compression ratio, turning the 3 files into a single 42 MB file, so I personally don’t see it as a big deal. I don’t use GitHub for my site, so I personally don’t bother zipping them.
A word of warning: if you update your static site generator binaries a lot, you’ll be storing every new and old version of it in the Git repo history which can quickly add up. The best way to combat this is to occasionally squash (technically rebasing + pruning + garbage collecting) down your repository to the last X number of commits since you usually don’t need every single old commit stored, which will delete any old versions in the history that are no longer in use. However, updating isn’t mandatory and you can just be lazy and keep using the same binaries for as long as the features they support are sufficient.
This also made it ridiculously trivial to set up an automatic build system on Google Cloud Platform (disclaimer: I work for Google so I’m biased) because all I have to do is clone the Git repo and have the build system call the binary that’s already included and then upload the files from the public folder into the GCS bucket that hosts my website. Forget worrying about whether there are docker images available for your chosen static site generator – you don’t need them anymore.
But seriously, try it.
I can not overstate what a difference it makes. I can locally host a server on my local machine to preview it using the binary included in the repo without ever having to worry about a single dependency (beyond having Git on the system). And since it was so easy to set up the cloud build system now all I need to do to publish new articles is to make a Git commit and push.
The only downside is that I had to migrate my articles over from another static site generator (Jekyll), but since both of them support articles written in markdown it honestly took me no longer than maybe an hour in total to fix the links and give them a quick read-through to make sure nothing was broken. I spent most of the afternoon playing with the various themes available for Hugo (there’s quite a few good ones).
I clocked it, the time to go from zero to writing is about 10 minutes on a completely brand new machine. Now that’s exactly what I want, because I personally can’t be bothered to write articles if I know I’ll have to spend several hours debugging dependencies every time I want to publish something. It was actually only slightly slower to completely migrate over to Hugo than try to fix that mess again.
So there you have it, if you’re considering which static website generator to pick I strongly suggest Hugo if what you want is a pain-free and moderately future-proof writing experience, especially if you plan on only updating your site sometimes. I really wish someone had told me this a couple of years ago so I could have saved myself a lot of pain, but hey, now that this article is written there’s a chance someone out there might not make the same mistake I did.