We recently announced the redesign of our website and blog. So far, it has been a great success. The site is a lot faster, SEO is better than ever (signaled by the growth of organic traffic), user bounce rates are down, the amount of visited pages went up as did the duration of the sessions. Hurray! :-)
However, the change was much deeper than just a visual revamp. We decided to revise the tech stack that has been powering the site for more than 6 years. It was a good opportunity to create the site we always wanted, and learn in the process.
In this post and the next ones, we will talk about the process we went through and try to answer some questions:
git push origin masteris enough?
In what follows, we will focus on the first 3 questions.
Every iteration of our website was built with Django and its server-side templates. We had models for most of the content (such as team members, customers, testimonials, etc.), so it could be edited through the Django Admin interface.
Our stack was:
Our first blog was built using a Django app called Zinnia. It did what it was supposed to, but required us to invest developer time to do basically anything. For the second iteration of our blog (in 2014), we switched to Wordpress since it was much better supported by the community and had plugins available for nearly everything.
We had this running for several years, but there were several things that never left us quite satisfied with this stack:
Consequences of having servers:
sshand see what was going on if the site was down for some reason. Only a handful of developers in the team had permissions to do this and they were generally busy with other tasks.
The overhead of Wordpress:
There is a saying that the shoemaker's son always goes barefoot. We have configured servers hundreds of times, set up automated alarms with CloudWatch, Datadog, Pingdom and plenty others. When you are serious about an app/site, they are a must.
The fact is:
We wanted to avoid servers altogether and not need to configure any alarms for anything related to our website/blog.
We have mostly static content; we are not really an interactive application, so what is the real advantage of using Django for the backend?
I could imagine a scenario in which our site is just a bunch of
.html files in a folder somewhere, just like in the good old days. Could we build a 100% static website?
The idea really appealed to all the developers in the team. Be able to write in Markdown, commit to git (you get version control for free!) and have some system that auto-deploys when stuff is pushed to a certain branch. Could we convince the few non-techies that we have that this was the way to go? ;)
Everything was making sense, so it was time to start working. However, there was this fear that in the middle of the development of the new site and blog, we would find out that some feature was unsupported or very difficult to achieve with the static site generator of our choice. If this happened, we would either end up migrating our code to another generator (adding to our time and costs) or, with great sadness, we would have to reconsider our decision to go static.
To avoid the unfortunate scenario, the smart thing to do was to write down the requirements for the new site and blog. This way we could detect potentially problematic stuff in advance.
In our previous site, the URL of the blog was /blog. Using the same domain instead of the blog subdomain is (arguably) better for SEO.
We want to have pretty urls and nothing "nasty" ending in
The main website should have sections such as Our Work, Team, What We Do, etc. No big deal, but for building the site we have to keep in mind that some of the sections in the website make reference to the blog:
Apart from the standard style of blog with the main page listing all posts in chronological order and paginated, we had the following requirements:
/blogprefix. For example:
/blog/YYYY/MM/dd/post-slug/. If possible, we would like to keep the format of the urls of the Wordpress site.
Having seen several statically-generated blogs (like, every single site in GitHub pages) we knew many things could be done already. What we were not so sure about were the following requirements:
It was time to dig a little into how we would handle these.
Because of its big ecosystem, Jekyll is where we started looking.
We looked for sites generated with Jekyll to see if we could find something similar enough to what we were going to build. From our findings, the pagination seemed to be the biggest problem. In particular, the pagination by author/tag/category. With Jekyll you can do tags and categories but these were pages that listed them all, instead of separate pages for tags/categories with pagination support.
We were glad to find out that the StackExchange blog is open source and built with Jekyll, so we looked here. It made clear that Jekyll can pull off some pretty complex sites; and this seemed very close to what we needed. It has tag and author pages with pagination! How did they pull it off? With the +400 lines of Ruby code of the custom pagination plugin they built. We didn't like this, modifying this plugin to adapt it to our reality was a bit too much.
Looked like Jekyll might not be the best fit. Maybe there was some other static generator with built in support for this?
When I first found out about Hugo, I was impressed and hoping that some day I would find an excuse to try it out. We are talking about a static generator that is shipped as single executable (built in Go), which needs no runtimes and no plugins -- contrast to Jekyll which depends on the Ruby ecosystem. Cross-platform, fast (~1 ms build time per page), with live reload for easy development. Hopefully the features are on a par with this.
We decided we had to give Hugo a go (pun intended).
The first thing we noticed was that, contrary to Jekyll, Hugo has native support for the notion of taxonomies. These taxonomies give us a way to classify content however we like. For example, we can add a taxonomy for a series of related posts.
The default taxonomies are categories and tags, which is just what we needed. Could we also build a taxonomy for author and also have posts grouped by author? Turns out it is trivial to do. Provided the correct templates exist, Hugo will automatically create pages for all the content under each taxonomy, and these will be paginated. This killed requirements 1 and 2.
For requirement 3 we needed to store the authors pages with a profile picture, a small biography, and some other data. Reading the documentation, it looked like it might be a good fit for data files where we can have each author as a
toml file. There is even a GitHub issue open as of Hugo 0.16 in order to standardize this, so we should have even better support in the future. Ok, requirement 3 was officially killed.
There was only one thing left before actually starting an implementation: how do we handle the Search functionality in static sites? We found there were two options:
We would leave this choice for later; at this stage we only needed to know whether it was possible. The answer was yes: requirement 4 was not an issue anymore.
In hindsight, the decision to use Hugo has proved very successful. The entire site builds in under 400 ms, and every developer in the office can have Hugo properly installed within a few seconds for local development.
In the next post we will dig deeper into how the site is actually generated, and how we solved some of the problems that came up. We will present a modern workflow for a static site, using Hugo and Webpack. If you don't want to miss it, make sure you subscribe to our newsletter.