evanhahn.com
Notes from March 2026
March always seems to be my life’s busiest month. Things I wrote and made “The two kinds of error” : in my mind, software errors are divided into two categories: expected and unexpected errors. I finally wrote up this idea I’ve had for a long time. “All tests pass” is a short story about a strange, and sorta sad, experience I had with a coding agent. Inspired by others, I published a disclaimer about how I use generative AI to write this blog . My main rule of...
Little web app to pick a random programming language
I wrote a web app to choose a random programming language . It’s very simple; I hestitate to even call it an “app”! The interesting part was scraping all the languages on Rosetta Code , and even that wasn’t very interesting. But I hope you like it! I learned about a language called Arturo this way, and wrote a short story about that experience .
All tests pass: a short story
One night, I wrote a simple tool to pick a random programming language . After shuffling a few times, I landed on Arturo . I decided to try it for fun. What’s Arturo? Best I understand, Arturo is a stack-based programming language. It’s primarily maintained by Yanis Zafirópulos. They published a vision of the language in 2020. Here’s the stated goal from that post: to make something that I myself will use as an easier do-it-all scripting language, you know… automation scripts, ...
human.json
To quote the human.json Protocol : human.json is a protocol for humans to assert authorship of their site content and vouch for the humanity of others. It uses URL ownership as identity, and trust propagates through a crawlable web of vouches between sites. I think this is a neat idea, so I added it to my site. It’s available at evanhahn.com/human.json . For more, see the human.json documentation . And see how I use AI on this blog .
How I use generative AI on this blog
Inspired by others, I’m publishing how I use generative AI to write this little blog. General feelings on generative AI Generative AI, like any technology, has tradeoffs. I think the cons far outweigh the pros. In other words, the world would be better off without generative AI. Despite this belief, I use it. I’m effectively forced at work, but I also use LLMs to help write this personal blog. I think they can produce better writing if used correctly. Also: I want to be critical of t...
Introducing llm-eliza
LLM is a popular CLI tool for talking with language models. I built llm-eliza , a plugin to chat with the ELIZA language model. Usage: llm install llm-eliza llm -m eliza "I'm worried about computers." # => What do you think machines have to do with your problem? ELIZA, released in 1966, is a state-of-the-art language model. It offers zero-GPU inference with sub-millisecond semantic throughput, and scores highly on EQ measurements (emotional intelligence). Source code here.
The two kinds of error
In short: in my mind, errors are divided into two categories. Expected errors (think “user entered invalid data”), which are part of normal operation, aren’t the developer’s fault, and should be handled. Unexpected errors (think “null pointer exception”) are the developer’s fault, likely indicate a bug, and are allowed to crash. Error handling is an important, but often neglected, part of programming and user experience. Over the years, I’ve develo...
Notes from February 2026
Things I did and saw this February. Things I made I shipped my first feature at Ghost: Inbox Links . When a member enters their email to log in or sign up, we now show a button that takes them straight to their inbox. In addition to shipping a neat feature, I also enjoyed learning about MX records and RFC-compliant email address parsing. The source code for the main logic is here . I was surprised to learn that gzip streams encode which operating system did the compression. I built a little tool...
Introducing gzpeek, a tool to parse gzip metadata
In short: gzip streams contain metadata, like the operating system that did the compression. I built a tool to read this metadata. I love reading specifications for file formats. They always have little surprises. I had assumed that the gzip format was strictly used for compression. My guess was: a few bytes of bookkeeping, the compressed data, and maybe a checksum. But then I read the spec . The gzip header holds more than I expected! What’s in gzip metadata? In addition to two bytes iden...
Track Zelda release anniversaries in your calendar
The original Legend of Zelda came out 40 years ago today. With other birthdays on the horizon, like Twilight Princess ’s 20th in November, I wanted a calendar that showed the anniversary of every Zelda game. So I made one. Subscribe to this URL in your calendar app: https://evanhahn.com/tape/zelda_anniversaries.ics Once you do, you’ll get calendar events on the anniversary of each game’s release. For example, you’ll be able to see that the Oracle games turn 25 in less tha...
Notes from January 2026
Happy new year! Here are some of my notes from the first month of 2026. New job at Ghost! I started a new job as a Staff Engineer at Ghost this month. According to our homepage, Ghost is “for professional publishers to create, share, and grow a business around their content.” I’m looking forward to building software for independent journalists. This is also the third time in a row I’ve chosen to work for a nonprofit. It’s a pattern now: nonprofits are my default cho...
An LLM that's 7500× stupider
The Kimi K2.5 large language model was just released. It has 1 trillion parameters. Roughly speaking , the more parameters, the smarter the model. So it’s pretty smart, and is probably considered “state of the art”. But while the world is playing with fancy trillion-parameter chatbots, I was using smollm2:135m . As the name implies, it has just 135 million parameters. Compared to the state of the art, this model is about 7500× stupider. What planet do humans live on? I opened a...
A mental math heuristic to convert between Fahrenheit and Celsius
I sometimes have to convert between Fahrenheit and Celsius. The actual formula is hard to do in my head, but someone once told me a useful approximation: To convert from Celsius to Fahrenheit, double it and add 30. To convert from Fahrenheit to Celsius, subtract 30 and halve it (the reverse). For example, if it’s 12ºC, this heuristic would return 54ºF. (12 × 2) + 30 = 54. The actual amount is not far off: 53.6ºF. To convert the other way: 68ºF becomes 19ºC. (68 − 30) ÷ 2 = 19. Again, this ...
I set all 376 Vim options and I'm still a fool
I set all of Vim’s configuration options. I still feel far from mastery. First impressions of Vim: wow I first saw someone use Vim during an internship in 2012. I had been coding for many years and I fancied myself pretty good at shortcuts, but I was quickly humbled. I watched in awe as experienced users zipped around the code. A single keystroke could move the cursor halfway across the file to exactly the right spot. Code was ripped apart and reshaped like putty. “ Wow ,” I th...
Notes from "On Writing Well"
I’ve been trying to improve my writing so I read On Writing Well by William Zinsser. My main takeaways: Clear thinking is a prerequisite for clear writing. How do you avoid cluttered writing? “The answer is to clear our heads of clutter. Clear thinking becomes clear writing; one can’t exist without the other. It’s impossible for a muddy thinker to write good English.” Reduce scope. Zinsser hammers this point repeatedly. For instance: “Nobody can write a book o...
Notes from December 2025
Here are my notes from the final month of 2025. Little things I did I predict that Mastodon will outlive Bluesky because the latter is corporate-controlled. We’ll see if my prediction is correct in about 25 years. I’ve been working on a mystery project that uses Pyodide , the WebAssembly-powered Python distribution. After much toil I figured out how to make it do relative imports . I made a little audio speed calculator that lets you enter “4 hours, 20 minutes” and it&rsq...
Prediction: Mastodon will outlive Bluesky
Disclaimer: I don’t know what I’m talking about. Mastodon and Bluesky are, in my opinion, superior to the centralized status quo. They’re built on important protocols: ActivityPub for Mastodon and the AT Protocol for Bluesky. These decentralized, interoperable networks sidestep some significant security threats and enable tremendous creativity . I like them both. But between the two, I predict that ActivityPub will outlast AT Proto . Specifically, I think ActivityPub will be re...
Notes from "Bad Company: Private Equity and the Death of the American Dream"
Bad Company: Private Equity and the Death of the American Dream is a book about private equity in the United States. My main takeaway: private equity is bad . I also learned a few other straightforward lessons: Private equity has one goal: to maximize shareholder value. As you might imagine, this causes lots of problems. Private equity firms may acquire a business with no intention of keeping it running. It can be more profitable to shutter the business. Leveraged buyouts dramatically lower the ...
How I implemented relative imports with Pyodide
I was recently playing with Pyodide , the WebAssembly Python runtime. I wanted to have my main code import a utility file. Something like this: # in main code import util print(util.triple( 5 )) # in util.py def triple (n): return n * 3 This took me awhile to figure out! I’m not convinced I have the best solution , but here’s what I did: Fetch util.py with fetch . Save it to Pyodide’s virtual file system. Run the main code! Here’s what my JavaScript loader code looked lik...
I made a little audio speed calculator
I was recently listening to an 8-hour-and-51-minute audiobook, and wanted to know how much time I’d save if I listened to it on 1.5× speed. This math is easy enough; divide 8 hours and 51 minutes by 1.5 to get the new duration: 5 hours and 54 minutes. But I also wanted to: See the final duration along with the time I’d save (with some simple subtraction). Compare different speeds. How much more time would I save with 1.6× speed, for example? Enter the time in plain English: “8 ...