I decided to write a blog. All the cool kids have a blog, and they regularly get featured on HN. I am not creative enough to be YouTuber, and not attractive enough to be an influencer, so blogging it is.
There are several ways to start a blog. You can either:
- Create an account on Medium or dev.to,
- Set up a WordPress blog, or
- Use a simple static site generator like Jekyll or Hugo.
All the above options allow you to get set up in O(minutes), and you can start cranking out posts. After spending hours reading thousands of reviews for all these options (the first act of bike-shedding), I decided to go for the third option.
I installed Jekyll, and read through its "Getting Started" docs. And then it hit me -- this is just a markdown parser and templating engine (along with a bunch of minor stuff). How hard could it be to just do it myself? So I set about writing yet another static site generator (or "yass").
I have mostly worked with object-oriented languages all through my career --- mostly Java and C++, with some odd bits and pieces of Python and Typescript. I decided I should utilize this opportunity and learn Go. It has a nice in-built template engine, a good dependency management system, and produces a single binary I can just copy anywhere.
It turns out, I suck at writing non-OOP code. Shocking, I know! I dearly missed composition, inheritance, polymorphism, dependency injection, and other patterns I have come to rely on. I was trying to learn how to do simple stuff in Go and how to structure my code properly, in addition to implementing the solution to the problem at hand.
I worked on it for around two weeks diligently, and then kind of gave up. "yass" was destined to join its ancestors in my abandoned side projects directory. Or was it!?
A lot of stuff happened. My team at work was hit with layoffs. I got really busy leading a project with tight deadlines. I wrote a utility to show pretty diffs in the browser (hopefully it'll see the light of day someday). And then, I again thought of starting a blog.
I took a fresh look at the "yass" code. The basics were in place. It could generate a website similar to this one. But the code structure was ugly, and future changes were going to be a pain unless I did a major refactoring.
Going against all conventional wisdom, I decided to rewrite it, in C++ (oh! the horror!). I already use C++ at work daily, and I am pretty comfortable with it. "yass-go" (formerly "yass"), already had a nice convention-based blog processing system, and a dummy blog for testing. And the task was still pretty simple, parse markdown and integrate it with a templating engine (and a few other minor things). So, I started writing "yass-cpp" (now "yass"), and surprisingly pretty much finished it in a day.
Raw input files to "yass" look pretty similar to those in Jekyll andHugo. There's a frontmatter at the top, and then Markdown. For parsing markdown, I had already chosen md4c library. Now I had to decide how to parse the frontmatter.
The obvious way was to just use a regex. It's a pretty simple regular grammar, and regex is an obvious choice. In fact, "yass-go" used regex to parse it in just a couple of lines. This was the obvious way to go, so I decided not to use regex.
In my college days, we had a class where we had to create bespoke state machines to parse given regular expressions. It was quite a bit of fun, but I had never written code for that. Now I had a simple regular expression, no one to review my code, and all the time in the world. I was going to write a state machine-based parser for the frontmatter. It had the benefit that I could provide better error messages in case I messed up (and I did mess up; a lot). I had also recently read Rob Pike's take on using regex in lexing and parsing, and that might have also influenced me to go with this.
It turned out to be good enough for my needs (barring a few edge cases which I avoid), and it gave me good error messages in case the parsing failed. This yak was finally shaved.
C++ now has a bazillion build systems (make, GNU automake, cmake, bazel, etc). I used blaze (an internal version of bazel at work), and it works beautifully in the monorepo. I was not so sure how good it is with code outside Google. I decided to go with simple Makefiles.
I need to confess something here. I have never properly learned how to write a Makefile. I just picked up bits and pieces here and there, and resort to Google for anything more complex than just compiling a bunch of files to objects and objects to an executable.
I put together a half-good Makefile, and it worked well enough that I finished almost the entirety of my work relying on it. When "yass-cpp" (now "yass") was almost done, I took a look at my Makefile, and thought, I can do better.
I decided to use GNU Automake. I had never written a "configure.ac" file in my life before today. So I looked for a tutorial and started following it. Halfway through it, I found that it doesn't do half the stuff I want. Finally, I started browsing the official docs. They are pretty well written, I was able to get it to compile "yass". As an added benefit, it somehow also halved the compiled binary size, so I'd consider this yak to be well-shaven and nicely groomed.
Apart from the above two major yak shaves, I think I also spent a lot of time looking for the perfect style for the blog. I went through several blogs which are featured regularly on HN and evaluated their style. I always wanted a minimal, but modern look, and I am pretty pleased with the aesthetic this website has right now. And all this happened before I had a single idea for a blog post (apart from one describing how to use "yass" that I wrote for testing).
This was one of the rare side projects I saw to completion. I still have a lot of improvements in mind for "yass", but it works well enough for my use case right now. And I can finally focus on writing something.
At the moment, no. I plan to release the "yass" source pretty soon with a permissive open-source license, once I clean it up a bit to make it presentable, and ask the lawyers at work for permission.