← Back to posts

Removing a special cause of variation in small steps

The next annoying variation in my blog writing workflow is adding images to a post. I wanted to add drag and drop. I let a coding agent generate something that almost worked, and then did the finishing touches by hand, simplifying the UX and code as I went, and learning more about writing tests and debugging in Emacs Lisp as I went.

Adding images is something I want to do in the flow, while looking at images. Thinking about where to store them is accidental friction during the writing and edting process.

In the previous post I made images visible (to me) while editing blog posts. Adding said images was still as annoying as it was in other static sites I write in. Including the QWAN slides repository. Images are not next to blog posts. I repeated that mistake here when creating Firehose. Fixing that is a bigger step. I wanted a small step, and even when images are next to blog posts, I still have to go into the terminal and copy files around. Often I want to add a screenshot, so I find the screenshot, then remember (aka look up) where images are stored, cp the image in the right place, and then make the link in markdown.

Screenshot of Emacs, with a dropped image in a blog post in the left pane. Screenshot of working environment. Blog post with dropped image on the left, printed debug messages at the bottom, commit history on the right.

I used Pi.dev to rummage through the scrapheap of Emacs lisp code on my machine. Programming by similarity is something LLMs can do. Since I found out recently that I can drop an image on an org-mode file and link it ("Attaching" org-mode calls that), I wanted the same in markdown. So I asked how that could work, and then had a function generated and wired into markdown-mode through a 'hook'. It almost worked, but not quite.

DeepseekV4-flash had generated a function with the usual LLM 'fallbacks'. What if the image does not exist? what if the directory does not exist?

I applied the usual 'fail fast' principle and deleted the fallbacks, replacing some of them with a call to error. I am upskilling in Emacs Lisp as I go - I found a call to error, looked up what it does: it throws an exception. I also wrote some tests, like in the previous blog post. More to get going. But as usual, it helped me simplify. The act of writing a test forces me to be very precise in what I want.

The generated code was, like much 'normal' Emacs Lisp from libraries very tied to the UI. I extracted a function to determine if something was an image. This was written as two variable assignments. Having worked with Haskell and Clojure, I would write code like that sometimes, and then extract to a function or a pipe later, once I understood the dependencies between the steps better. Having been on the road for the last week and now teaching others to work with coding agents in small steps, I am moving from knowing what good looks like to knowing what good feels like. Intuition, judgement, guided by (bad ;-)) experience.

I won't bore you with the details of my code here. Suffice it to say, I have one excuse less to not publish posts. Screenshots are easily made, now they are also easy to publish. A small step etc.