Weeknotes 2025 W44: Ernestina

October 27​–​November 2, 2025

Quick bits:


I am back to health, but still feeling the residual effects of what must’ve been a severe cold.

Being back to health, I was looking to resume our TTRPG sessions, but alas — someone else in our group now fell sick, and so it’ll have to be delayed for a while longer. There sure is something going around. It has been more than a month ago that we scoundrels pulled off a score in the darkness of the city of Doskvol.


I pushed a Nanoc patch release with primarily some runtime performance improvements. These certainly are micro-optimizations, but they’re worth it nonetheless.

Take a look at this:

def direct_predecessors_of(to)
  @to_graph.fetch(to, Set.new)
end

Rather than creating an empty Set over and over again, it’s faster to reuse an empty one:1

EMPTY_SET = Set.new.freeze

def direct_predecessors_of(to)
  @to_graph.fetch(to, EMPTY_SET)
end

Or take a look at this:

@vertices[v] = @next_vertex_idx.tap do
  @next_vertex_idx += 1
end

Removing #tap makes it faster:2

@vertices[v] = @next_vertex_idx
@next_vertex_idx += 1

In the past, I’ve done optimizations such as replacing loop do … end with a while true … end. The latter is distinctly non-idiomatic Ruby, but if it has significantly better runtime, then I think it worth it.

I’ve also removed fibers from Nanoc. Fibers bring quite some complexity with them, and I don’t think the complexity is worth the gain.

Additionally, I’ve removed some threading that wasn’t pulling its weight. In Ruby, the global interpreter lock prevents more than one thread from running at the same time anyway. Removing threading, therefore, not only simplifies the code but even improves performance by removing the overhead of context switching.

Ahh, how nice it is to make code faster by simplifying it.

With some changes to my personal web site, my entire web site now compiles in about 8s, which is good enough. For now.


One particular reason why I like Zig: you can put the tests right next to the code.3 Like this:

pub fn addWrite(
    self: *Self,
    allocator: std.mem.Allocator,
    data: []const u8,
) Error!void {
    // [snip]
}

test "addWrite() simple" {
    // [snip]

    try assembler.addWrite(
        std.testing.allocator,
        "bloop"
    );

    // [snip]
}

// [snip]

I’ve always admired the Ruby community for having such a strong focus on automated testing. But this week, I found myself annoyed at having the code and its tests so far apart from each other. Code and tests live in separate top-level directories; they couldn’t be further apart from each other:

lib/nanoc/core/compiler.rb
spec/nanoc/core/compiler_spec.rb

I’m now pondering whether it would be possible (and reasonable) to put specs in the lib/ directory, too. Highly unconventional, I know. But wouldn’t it be nice?

lib/nanoc/core/compiler.rb
lib/nanoc/core/compiler_spec.rb

I miss the Zig/Go/Rust approach. I love having tests that live in the same directory — or even in the same file!


My fiction writing sure has taken a back seat lately. I’ve not published anything in over a year. Oops!

Since it’s November, and November used to be National Novel Writing Month (in the U.S.), I’ve been thinking of deliberately making more time for fiction writing.

I’ve got plenty of drafts lying around, written on loose sheets of paper with various colors of fountain pen ink. There’s enough material to create more than a few cohesive stories.

Maybe I’ll continue with the Alphabet Superset? I’ve missed the (soft) deadline by, erm, years. Not that a deadline matters, though.


It’s not just my fiction writing that has taken a back seat. I’ve not done too much technical writing either. I’m a bit surprised by that myself, as I’ve had quite a bit of time, being unemployed and all that.

I’ve got a few drafts of articles, but none of them are even close to completion. In all likelihood, 2025 will be the first year where I don’t publish any articles at all, breaking my streak since I began in 2020.

I still have time to write something, of course — but I’m not going to write something just for the sake of writing something.

My interpreter book is on ice, in large part because the existing implementation uses Ruby and the book is written with a Ruby audience in mind, but at this point I think that Ruby is just not the right choice of language anymore. Also, given the existence of the excellent Crafting Interpreters book and the Writing An Interpreter In Go book, I don’t think I have enough of a niche.

I’m tempted to start drafting an article on how to implement a fast templating language in Zig. After all, that’s what I’ve been doing with Deng, and it’d be good to share what I learned.


After several months, I’ve given LLM coding tools another try. Friends, it did not go well.

GenAI coding tools struggle to generate even syntactically valid code. When they do, they use deprecated APIs, or even non-existent ones. It is nigh impossible to have these tools generate code that even compiles.

And before I can get the output to a point where it is somewhat reasonable, I’m hitting the daily rate limits of these tools.

GenAI is so unbelievably bad that it is worse than useless. It is a waste of my time; I spend my time undoing the utter stupidity that these tools spit out, over and over again. It even fucks up existing code that was working perfectly fine. Over and over again, I have to rush to the agent’s STOP button to prevent it from bringing ruin to my codebase.

Genuine question: what the actual fuck.

I had expected there to have been progress in the last half year or so since I last used these tools. On the contrary; it seems like these tools have regressed.

Granted, I am using the free version of these tools. But free tiers, demos and trials are supposed to pull you in, right? I have just about zero interest in paying for these tools, given how much of a giant stinking pile of garbage the free versions are.

My experience with AI coding tools is vastly different from the advantages that many people on the Internet claim. I cannot reconcile these two worlds. Am I living in a parallel reality?


Entertainment:


Links:

Alasdair Beckett-King links:

Tech links:


  1. This is only correct if the returned Set is not modified, Which it isn’t, in my case, and I’m using #freeze to ensure that. ↩︎

  2. These two pieces of code aren’t strictly equivalent: the former evaluates to the original @next_vertex_idx, while the latter evaluates to the new @next_vertex_idx. In my case, I didn’t use the return value, so that distinction didn’t matter. ↩︎

  3. It’s not just Zig, of course! Go and Rust do something similar. ↩︎

  4. No Other Land, directed by Yuval Abraham, Basel Adra, Hamdan Ballal and Rachel Szor (Yabayay Media, Antipode Films, 2024). ↩︎

  5. A Bigger Splash, directed by Luca Guadagnino (Frenesy Film Company, Cota Film, StudioCanal, 2016). ↩︎

  6. Swimming Pool, directed by François Ozon, written by François Ozon, Emmanuèle Bernheim and Sionann O’Neill (Fidélité Productions, France 2 Cinéma, Gimages, 2003). ↩︎

  7. Avernum 4: Greed and Glory (Spiderweb Software, 2025), published by Spiderweb Software. ↩︎

You can reply to this weeknotes entry by email. I’d love to hear your thoughts!
If you like what I write, stick your email address below and subscribe. I send out my weeknotes every Sunday morning. Alternatively, subscribe to the web feed.