November 30th 2016

Live Demo #

You’re looking at it :)

Overview #

TedY.io is my personal website, which I began working on sporadically mid summer 2016, and finished in December.

Objectives #

I could have quickly launched a website using Wordpress or just written a blog using Medium. But I wanted to meet several objectives, that were impossible with an off the shelf solution.

I wanted the site to be:

  1. Testable: I had to be able to write unit tests for at least some of the code.
  2. Fast: The site had to be extremely fast, even under load.
  3. Fault Tolerant: If I decided to host a blog here in the future or some popular file, I did not want the site to crash.
  4. Dynamic: I wanted at least some level of interaction to be possible. For example, I did not want to use a “mailto:” link for email, instead preferring a form which would send mail to me directly.
  5. Customizable: I did not want to be stuck with a CompanyName.com/MyName domain name.
  6. Inexpensive: I did not want to pay a fortune for hosting.
  7. Effective: I wanted my site to make an impression and showcase my skillset.

Tech #

Technologies I Considered #

  1. Isomorphic MERN Stack: The first version of this website did use the MERN stack. However, it was too tedious to develop all the React components, and I didn’t like running a NodeJS server or sending a large React Javascript file client side. Furthermore, configuring autoscaling groups of EC2 instances on AWS to make the site fault tolerant would be complicated and expensive. Perhaps this would have been ideal for a larger project.

  2. Wordpress / Medium / Github / etc: Using an online blogging platform would immediately get me speed and fault tolerance, and possibly also the dynamic email form that I wanted. However, there was no opportunity to write unit tests (to practice my TDD skills), some didn’t allow custom domain names, nor did any offer the same level of customization as building form scratch. They were thus overall less effective for my needs.

  3. NodeJS + Handlebars + CouchDB: This was a great stack for the work I did with my friend James for EvokeOne. However, I’d learn nothing new from building another site with the same stack, and I’d run into the same issue with speed and fault tolerance inherent in running a NodeJS server.

Tech Used #

Eventually, I decided the best way to go would be to build a static website that would be rendered on my computer. Initially, I planned to build such a generator myself using NodeJS, until a friend tipped me off about Hexo. This is the full tech stack I used:

  • Hexo: Hexo is a static page generator written with NodeJS that allows one to build a fully fleshed website from a set of templates. Since it’s written with NodeJS, it’s possible (though not easy right away) to add code and extend its capabilities, which I did extensively.

  • NodeJS/Javascript: I used NodeJS and Javascript extensively to modify the Hexo generation process as well as to implement a number of client page effects. Almost all the code needed to modify the generation process was in the form of some “get” statement that would have resulted in lots of duplicate code. Thus, I compiled and wrote tests for all these in the getters.js file in the themes/teoman/scripts directory.

  • Mocha/Chai: Mocha and Chai are a great set of testing tools for Javascript. Yes, I’ve read why I should use Tape instead of these, but I still like the expressive nature of Chai assertions (expect(var).to.be.a... etc) as well as the pretty output from running Mocha.

  • Bootstrap: I wanted a clean, professional layout, but I didn’t want to have to make it from scratch as I’m not a designer. Buying a Bootstrap template and extensively tweaking it turned out to be the best option.

  • AWS: S3 + Cloudfront: S3 is Amazon’s simple file storage service. It’s extremely cheap and fast. Cloudfront is the Amazon CDN, which takes the site from fast to hyperspeed. Once I set up S3 and Cloudfront, I was able to use a plugin for Hexo to deploy my site in one step. Thus, I’ve cheaply hosted a website that can withstand front page of Reddit traffic without buckling (though I will pay extra if that happens, which I’m okay with).

  • AWS: Lambda + API Gateway / Mailgun: Getting an email form to work on my site without any server side code was such a royal pain that I’m going to write a separate entry and release the code used to do so :) In short, the form on my homepage sends a request to API Gateway at Amazon, which forwards the request to a Lambda function I’ve set up, which then passes it on to Mailgun.

  • Pug (formerly Jade): By default, Hexo uses EJS templates, though it supports many others. I settled on Pug for two reasons: first, it lets me use Javascript in my templates as needed. Second, the syntax is simple and elegant.

I did eventually factor out any complicated model code from my view templates, but I still needed a way to request needed information. If I used a template engine that didn’t support this (like Handlebars), I would’ve had to dig deeper in Hexo to get the needed data on my behalf. Perhaps this is a better solution long term, but in the short run I preferred to have the templates just ask for the information they needed. For an example:

//- tag-stub.pug

mixin tagStub(tag)
    tag = _get().tags.metadata(tag, theme.tags);
    tag.target = '#tag-info';

  a.tag-stub&attributes(_get().attribute({tagStub: tag}, attributes))=tag.name

I get metadata for a tag from the model by calling _get().tags.metadata, passing in the tag argument (from Hexo) and theme.tags, which is information about all the tags. I will explain the design decisions here longer in a blog post, but this is just one example.

Playing With the Code #

If you want to play with the code for my site, check out the Github Repo. Clone it and make sure you have Hexo installed (if not, run npm i or sudo npm i -g hexo && npm i depending whether you want to use it globally or not).

To generate and run the site, run hexo clean && hexo s --generate and navigate to localhost:4000. Note, the site can’t be deployed from the Github repo as I’ve obviously not included my AWS keys in the public code :)

The structure is as follows:

  • source: this is where all of my posts, assets, images, and so forth go. This content gets rendered using the information in the themes/teoman directory and is converted into a working site in the public directory. I will probably eventually set up my repo to pull the source directory dynamically from another S3 bucket (in order to create greater separation between site content and site code), but for now I am uploading it to Github.
  • themes/teoman: This is where the custom Hexo theme I built lives. It has several subdirectories including:
    • layout: all the Pug templates and mixins for the site
    • scripts: Javascript files run by Hexo at generation time. The client does not receive these files, but they allow me to create better separation between presentation and data model.
    • source: images, javascript files, and LESS templates used by the site as a whole (as opposed to specific content for projects.) e.g. particlesJS, banner images, and so on.
    • tests: Unit Tests. Right now, I’ve only included tests for the “server side” (i.e. generation) scripts, as the client side work is much more UI oriented. I test the UI visually, and it would be tedious and wasteful for me to try to think out the best expected value for a visual effect, rather than just trying it to see how it works. If you want to run the test script, run mocha test_generation.js

That’s it! I will write up a blog post at some point that expands on some of these points, but this writeup is good enough for now.

NOTE: I am allowing the code for the site to be downloaded only as a showcase / example. Please do not reconfigure and reuse the theme, as I am not licensed to do so.