Some rather intense keyboard clacking over the past years has resulted in me having to give my hands some time off work.
As a software developer, I pride myself on being able to implement complex systems from scratch. I can analyze the problem, choose the right tools, and design and implement solutions. I can maintain and grow the implementation as the business grows over a long period.
The right tool
Choosing the right tools can mean a lot of things here. Some of the criteria I use for selecting frameworks and libraries often are as follows:
- Is it easy to solve my problem using this tool?
- How easy is it to get started with it?
- Does the tool grow with my application, and as my application becomes more complex, does the tool still provide answers?
- How large is the ecosystem and community, what does it integrate with, and do I get answers to my questions?
- What is the quality of documentation?
- Are there escape hatches when I need to add something I make myself?
- Are there a lot of companies solving similar problems using this?
Here’s an example of a good tool that solves my problems in web application development: Django. We will look at it through the lens of the above criteria. Django boasts being able to solve a wide variety of problems that developers typically encounter throughout the lifecycle of application development, ranging wide into topics like security, scalability, and extendability. The Django tutorial gives a good overview of what Django can do and how to get started. And, as my applications written in Django grow over time, I can pick from many ready-made libraries that help me solve my problems.
The Django community is vast, there are annual conferences, and a quick look at Stackoverflow reveals hundreds of thousands of answers to problems that developers have encountered so far. The documentation is superb, and for any detailed questions, the source code itself is well-structured and legible.
When I encountered problems that required a custom approach, I was always able to write libraries to fix these issues quickly. And many companies could grow their products using Django, even if nowadays they use a wide variety of frameworks and libraries to continue their development. Instagram and Pinterest are prominent examples of companies using Django, which is confidence-inspiring.
Since I believe in the importance of enjoyment in my work, I think a lot about whether using something is fun. Fun can be many things, be it an innovative approach to solving a problem or easy debug-ability when encountering problems. But of course, I would rather have it so that I don’t experience issues in the first place. Complicated problems require me to dive into hour-long debugging sessions.
Going in the wrong direction
Then, unfortunately, comes the darker side of why we choose the things we use. We choose tools because they look cool, are hip, increase someone’s clout, and so on. Many will say they choose tools based on their technical merit, but even I am often guilty of using something simply because of the number of upvotes it has received on Hacker News. Making fun of databases that stray from the path of the relational has become a favorite pastime for some, and I will not join the mob here. I will also not mention the successor of Google’s Borg: a tool many accuse of being the result of a top-down decision in favor of being able to scale their product for a future that is far away indeed.
When you choose something that falls short of some of the above criteria, one of the things that will always suffer is ergonomics. Ergonomics can mean a lot of things, but I often encounter issues with the following:
- Is using this tool verbose? Are there things I repeatedly have to write, despite being considered a solved issue, i.e., boilerplate?
- How many lines of code do I need to write to be able to implement my solution using this tool?
- Will this spontaneously break when transient dependency X is updated, and how long do I have to spend updating it?
- Will this break in unexpected ways when using it on a different OS or architecture?
Here is where it went wrong for me at the end of 2022: my website and blog broke terribly. I have spent a few weeks migrating my site from Jekyll to Hakyll. Hakyll drew me in because it allows you to Pandoc more directly without endlessly spawning Pandoc processes inside Jekyll, which was relatively slow.
In the beginning, it worked well. Hakyll uses Haskell, and it has you effectively write your site generator instead of Jekyll’s path and configuration-driven approach, with some light plugin writing here and there. The ability to write a fully static site generator meant that I was able to dive deep into Pandoc and implement markdown filters for graphviz and mscgen based diagrams.
That felt so elegant and cool! And Haskell having a sophisticated typing system means that your website could be correct by construction. Building skills in Haskell programming will create transferable skills for working with other programming languages. My website will be robust, and I will learn a lot. And using Pandoc and XeLaTeX would allow me to generate PDFs of my articles, and with Graphviz and Mscgen, I could generate SVG for the web and EPS for PDF. It sounded elegant. I am very familiar with TeX from my university days, so this aura of formal correctness has always appealed to me.
The price I had to pay turned out to be enormous. First, wrangling with the type system took a lot of work. And as time passed and transient dependencies got updated, it became increasingly frustrating. Part of it might also have been my lack of familiarity with Stack – the Haskell build tool and package manager. Until this day, I don’t fully understand how exactly GHC, Cabal, and Stack divide their roles and how they interact with each other. Yes, there is always another page of documentation that you can read to further your understanding of these concepts. But at some point, you want to get on with it and start writing blog posts.
Upgrading to M2
At the end of 2022, I was trying to get my site to build again on my M2 MacBook Air. Before, I would run the site generator on my x86 Debian machine or an old and clunky 2013 MacBook Air. The compile times have always been maddeningly slow on that 2013 MacBook Air, as Stack insists on compiling 100s of modules from scratch, which is a surprising amount for a static site generator. A tool that essentially has to write some HTML files, and then you FTP them onto your web server. And yes, there are a lot of features in a static site generator, like asset minification, sitemap generation, RSS feeds, i18n, and so on. But Hakyll didn’t even have these features out of the box, meaning I would have had to implement them from scratch, use a library/follow someone else’s tutorial, and then discover it doesn’t work for my particular use case.
The final straw was realizing that to compile my static site generator written in Hakyll on my M2, I would have to switch to a much newer version of Stack than the one I used on my previous systems. That version of Stack would pull in a new Hakyll and Pandoc. A lot of Pandoc’s API has changed in minute ways. And, since the resulting number of necessary changes in my codebase was so large, I faced a lot of work. And seeing the Haskell compiler giving you an endless to-do list in the form of tens of new type errors is frustrating. Each type error requires you to dig into the Hakyll and Pandoc documentation and, even worse, through library code. Given my intermediate Haskell skills, reading library code would often be challenging, as it constantly uses advanced Haskell typing concepts.
For some reason, my particular configuration of Stack refused to accept overloadable Haskell strings, so in every location where the compiler would morph strings into the correct form for me, I would now have to manually adjust them and pass them into the appropriate type wrangling functions myself. So, why does something that has worked so well previously punish me so hard by breaking exactly when I need it just because I update the environment? Yes, I am using these projects free of charge, and countless people volunteer their precious time to maintain them. And I appreciate the amount of work I have been able to do so far using these tools. At some point, though, I need to move on.
Repetitive strain injury, or RSI, is a group of injuries that affect the skeletal and nervous systems and can manifest in many ways that I do not feel qualified to discuss. If you have any issues while you use your computer or other work tools, it doesn’t hurt to get it checked out sooner rather than later.
In my case, I have had oversensitized nerves in my right hand’s fingertips. Copious amounts of typing caused this on keyboards that are too hard and with a typing intensity that is not sustainable for long periods. The amount of strain made typing an extremely unpleasant experience. Not long after, I would evaluate any minuscule code I had to write with a “Do I want to inflict pain on myself this time to get it done?”
The cure, as with many hand injuries, among others, is rest and plenty of rest. That means I have to type much less, predominantly with my left hand. Automatically, that influences how I choose tools for the coming rehabilitation. I want to use something that requires considerably fewer keystrokes and would allow me to have more rest in between and still give me the feeling that I am achieving something. In this case, development ergonomics means: Will working with this tool lay waste on my body?
Writing a blog post itself usually takes a few hours. By the time I could superficially fix most of the issues, I must have been working on it for hours already. And realizing it is nighttime and not having written the blog post itself because your static site generator keeps breaking is demotivating and heartbreaking. As someone who thinks of himself as an able developer, not being able to turn a Markdown file into an HTML file effectively is a humiliating experience.
I have used Hugo effectively for my corporate website. Since it is a small site, I could only partially evaluate whether Hugo will grow with the scope of the project, which, as I have mentioned above, Django does quite well. Reading again through the Hugo documentation recently, I was surprised to see that it has support for i18n, deeply structured content hierarchies, asset minification, live reloading, and most importantly, it’s a binary that you can easily install, and it’s fast. That gives me hope that Hugo will grow together with the project.
I decided to give Hugo a spin. And in the beginning, the resistance I felt was high. How do I use Graphviz? How do I generate PDFs? It’s written in Go! And again, I thought long and hard and realized: My blog has featured 4 or 5 diagrams made in Graphviz. And yet, I must have spent two days implementing the Graphviz feature. I suspect no one ever reads the PDFs, yet I have spent a very long time trying to get CJK fonts to work well and ensure that embedded figures render alright. Go, the language used to implement Hugo feels clunky to me, but who cares? I don’t need to work with Go myself. Hugo works very well for me, and as I slowly tried porting my site to use Hugo, I was swallowing my pride bit by bit, and in the end, I arrived at a new build that works well enough in Hugo.
“Well enough” is the criterion I should use more often to evaluate whether it works for me. Well enough means that while I might not be able to do everything I want (like easily integrating external tools), I can still do 90 % of the things I ever will do. And in the end, all I want is to publish some Markdown on my website and move on with my life. The goal is to communicate my ideas and thoughts with other people over the internet. If I ever want to reinvent the way we do web publishing, I will gladly do that as a separate project. And for the foreseeable future, every time inspiration hits me, I can jump into a text editor and promptly publish it to a broad audience anytime I want to, without a hitch.