The Art & Science of Ruby https://theartandscienceofruby.com/ Ruby, software and more. Everyone is an arse https://theartandscienceofruby.com/2026/09/everyone-is-an-arse/ https://theartandscienceofruby.com/2026/09/everyone-is-an-arse/ Fri, 04 Sep 2026 15:00:00 +0100 The other day I was saying I was bored and confused with the role of software developer.

But I think I may have a bit of clarity now.

Maybe I’m not bored and confused.

Maybe I just hate it all.

I received an email from Ruby Central the other day. I can’t remember what it said. But I did remember the shit-show over the Ruby Central takeover a few months back. The way it was handled suggests that Ruby Central is populated by arses.

DHH is all over Youtube at the moment, because of Omarchy and the money that’s being given to the foundation. I did try Omarchy and what I liked best about it is that it doesn’t try to work in a similar way to Windows or macOS - meaning my finger muscle-memory didn’t get in the way when using it. But, frankly, DHH is an arse.

And Rails belongs to DHH.

Arse.

Ruby effectively belongs to Shopify now.

Tobi is an arse.

I really enjoy working with LLMs - but the companies that own them have hoovered up everyone’s data with no care to whether it’s legal or ethical to do so. And then whine when other companies do the same to them. I don’t know if the data-centre hatred is justified or not, but these companies don’t understand why people are annoyed.

Because they’re arses.

Big Tech and enshittification are everywhere. Anil Dash has a great explainer of how Silicon Valley used to be funded by venture capitalists - but they are no longer venture capitalists.

They are major arses.

Arses, arses, everywhere.

So maybe I just hate it all.

Having said that, I accidentally started a brand new project and I’m really enjoying it. I guess I’m just a contrary arse too.

Photo by Roman Mavrin
]]>
Bored and Confused https://theartandscienceofruby.com/2026/08/bored-and-confused/ https://theartandscienceofruby.com/2026/08/bored-and-confused/ Fri, 28 Aug 2026 14:00:00 +0100 I’m bored. And I don’t really know what to do about it.

I’ve been writing software for about thirty-five years.

I’ve been getting paid for writing software for almost thirty years.

In February I realised that what I did had fundamentally changed. That’s right, I’m talking about coding agents.

I’m still working as software developer, I’m still shipping code every week. In fact I’m probably shipping more code than I ever have before.

I’ve managed to get my clankers to handle most of the parts of the job that I don’t like - code review, estimation, boring stuff.

I’ve got a process that, for the most part, I can trust the clankers to write and release code which works - without security issues, generally bug-free, reasonably well-structured and understandable.

But now I’m bored. And also confused.

A while back I started on a full rewrite of my plumbing gemDon’t try using it - the rewrite is not ready.

The first part of the rewrite was entirely clanker-driven. I explained what I wanted, it went away and did the work. I checked the specs, which described the changes I wanted. And the specs all passed. Sounds good right?

So I set the version to 1.0.0 and published it.

Then I actually tried using it in a project.

And I found lots of sharp edges in the API that I did not like. As I’ve said before, rubyists look at things slightly differently to other types of developer.

This is my fault, not the clankers.

They built what I specified. I had started from what I wanted the gem to do when I should have started from the application that actually used the gem. That way I would have figured out how I wanted it to feel whilst using it and I could extract the new API and implementation from there.

Always start with the user.

So I opened up my editor and started changing the gem’s API.

And I hated the code that the clankers had written.

It was functional. It was efficient. But it wasn’t mine.

I reorganised the code, changing how individual pieces were split into modules, renaming things, restructuring the interactions between different classes and objects. All good object-orientated stuff.

And when I had finished, I realised that the whole process had been a headache.

I kept running into issues; specs would fail and I’d spend loads of time scratching my head and debugging.

It was not fun.

Plus I’d touched every single source file (including renaming a number of them). There wasn’t a class, barely a method, that was unchanged.

Remember - this wasn’t necessary.

I could have just updated the API.

But I couldn’t stop there, because I didn’t like the way the code looked. And even worse, this API change and refactoring took me hours.

The original clanker-driven rewrite took about three hours over the course of a few days. My manual rewrite took me at least eight hours, mostly done over three evenings - eating into my spare time.

The code feels like mine now.

I am much happier when looking at it.

But, in the near future, which humans are ever going to sit down and read the code? Aren’t we all just going to point our coding agent at it and say “how does this work?”

So, my job has fundamentally changed.

Now I spend a lot of time just waiting. The clankers do their thing and I take a look at their summaries.

And because I’m used to the clankers doing all the fiddly, messy, frustrating stuff, a big part of writing code by handthat I used to love is just annoying now.

Bored. And confused.

I think the answer is to put together sketches of the code, in the style I want, then let the coding agent actually make it work. Repeat the “keep the bits I like, give them the bits I don’t” strategy.

But I’ve not figured out how to do that yet. I’ll let you know when I do.

]]>
Half-Baked Product https://theartandscienceofruby.com/2026/08/half-baked-product/ https://theartandscienceofruby.com/2026/08/half-baked-product/ Mon, 24 Aug 2026 15:30:00 +0100 I really enjoyed this fable of the product that’s built too quickly. I’m sure it’s extremely familiar to many of you.

It’s the story of a startup that aims to build a next-generation oven. They get funding on the back of a pitch deck, then build the prototype. It’s a long way short of perfect, but is good enough to demonstrate.

And then comes the uncomfortable discovery: if the oven only does two of the three things (bread, cakes, or pizza), the algorithm fails just 5% of the time. The engineer brings the proposal to the founder: let’s sacrifice one market and have a product that works. The founder gets angry. He promised the VCs 10% of Spain’s oven market. The entire market. “We can’t sacrifice any of them.”

Next up is the hunt for sales. They quickly realising that the money comes from the enterprise market. And enterprise sales are based on promises, not on what the product actually does. So the feature requests stack up, every one of them urgent, in order to bring in the next customer.

The founder swallows hard. The ticket has been sitting on the kanban board for a month and a half. It’s not that nobody saw it: it’s that every week something jumped ahead of it.

And then they lose their biggest customer …

And the worst part isn’t losing Pepepizza. The worst part is that the changes made for the rotating base will haunt the oven’s design until the end of time. The customer leaves now. Their rotating base stays forever.

I see two lessons in this story.

The first is mentioned in the tale itself - when everything is urgent, nothing is. I read somewhere that, in Latin, the word “priority” is singular-only. There is no plural. “Priorities” do not exist.

The second is that every single option you add in brings an explosion of complexity. 1 option has 2 states - that’s doubled your testing regime. But 2 options have 4 states, 8 options have 256 states. And that’s assuming binary switches. Automated testing can help here (at the cost of time and processing) but will you really catch all the combinationsI guess that’s the point of property-based testing?

The real issue is, in 25-odd years of being a professional developer, I’m not sure what the solution is. Without sales, you die quickly. But the rush of tickets, the inability to stop and breathe, mean you die slowly.

Photo by Mauro Lima on Unsplash
]]>
It's not gone rogue https://theartandscienceofruby.com/2026/08/not-gone-rogue/ https://theartandscienceofruby.com/2026/08/not-gone-rogue/ Mon, 10 Aug 2026 20:00:00 +0000 Photo by Cash Macanaya on Unsplash

This is a bit of a rant.

There have been a lot of reports recently about LLM agents “going rogue”. Hacking into other companies, doing stuff that they weren’t supposed to do. I’m not saying it’s great that they’re doing these things. But I am taking issue with the reporting.

These things are “not going rogue”.

They were given a task to complete. And they tried to complete that task.

They’re not humans, they have not grown up in a social environment. They do not learn the same way as us and they do not solve problems the same was as us.

They do, however, solve problemsRemember, just a few months ago, when people were saying “they’re nothing more than stochastic parrots”?. Relentlessly.

Just because they acted in a way that we did not predict, just because we didn’t give them the correct instructions, just because we didn’t build in the right guardrails - none of that means they went rogue.

It just means that we, as humans, do not know what they are capable of.

Which is, actually, much more scary.

]]>
Here's to the weird ones https://theartandscienceofruby.com/2026/08/heres-to-the-weird-ones/ https://theartandscienceofruby.com/2026/08/heres-to-the-weird-ones/ Sun, 09 Aug 2026 18:00:00 +0000 Many years ago I remember seeing an interview with Linus Torvalds, where he said “… and that change is to keep the weird folks, like the rubyists, happy”.

I loved that.

The rest of the software development world thinks we’re odd. And not just because some key figures in the ruby world have taken a trip down nazi lane.

&&

I’ve been rewriting my “plumbing” gem. It was originally “unix pipes for ruby data structures” but it’s turned into more of a set of concurrency primitives and utilities for organising your application actors, observables, locators and operations.

As I started on the rewrite, I had a long conversation with one of my clankers and it did most of the work. I didn’t read the code it was generating - instead I kept an eye on the test suite and asked it to keep the documentation up to date. From the rspec and the docs, I could see the gem was taking the shape I wanted.

Then I came to actually use the code - and discovered there were whole areas of the API that I was not happy with.

Even without LLM code generation, this is normal. You can’t really tell whether an API is any good until you actually use it. And as I was using it, I discovered lots of points of friction, where the API didn’t quite work. This was my own fault - I had started from the wrong place; the implementation, instead of starting from the user.

So I refactored and reworked the clanker-written code.

&&

My intention was to update the public API so it was easier to use. But, as I read and adjusted the ruby code, I just kept on refactoring. And kept on refactoring.

Until I had reorganised the internal folder structure, replaced nearly all of the LLM-generated code and rewritten everything - but this time in my own style.

Why?

Did the LLM produce sub-standard code? No. It was decent code that did exactly what I had requested.

Was the test suite not comprehensive? It was. The clanker even covered edge cases and conditions I would have missed had I done it myself.

Did it fail to lint and format the files correctlyI like standardrb. It’s opinionated, meaning I don’t have decisions to make and rules to write. And I agree with the majority of its decisions? No. Technically it had done everything correctly.

But … the way the code sat in my editor was wrong.

Some of the internal implementation names and modules sounded odd. The split of how classes were organised into modules felt weird. Some of the methods looked ugly.

I just didn’t like it.

&&

As I said, Rubyists are weird. Most software developers think hard about what they want to do. They write the code, making sure the tests coverage is good and everything works as expected. They lint and format the source files so they meet their organisation’s coding standards.

And that is good enough.

But Rubyists want more.

Look back at how I described the LLM generated code (which was, remember, functionally correct).

The names sounded wrong.

The modules felt weird.

The methods looked ugly.

Rubyists care about the emotions of our code.

  • We agonise over whether to use a leading or trailing if - even though they are functionally equivalent
  • We worry about whether a parameter should be named or positional - even though that, too, is functionally equivalent
  • We stress over methods with block parameters - does the do keyword read correctly with the given method nameafter(10) do ... end vs wait(10) do ... end vs wait_for(10).then do ... end?
  • We refactor to ensure that the patterns of indentation stay aesthetically pleasing - does the code sway in and out from the left hand edge of the window correctly?

This stuff matters to us in a way that it doesn’t matter to other developers.

Rubyists are weird.

We’re emotional and we care about stuff no one else cares about.

It’s why I love ruby and I wouldn’t have it any other way.

]]>
Zum Clankenhaus https://theartandscienceofruby.com/2026/06/zum-clankenhaus/ https://theartandscienceofruby.com/2026/06/zum-clankenhaus/ Tue, 16 Jun 2026 13:55:43 +0100 So I currently have three clankers working for me, currently, all built using Claude Code. However, only one of them works in the traditional "here is a prompt, now go and build the software" kind of way.

We use Linear for issue tracking. It's not the greatest software in the world - but then I don't think there's any project management software that is actually very good[1].

Linear has "cycles" (iterations) and we have our statuses set as "Request", "Estimation", "Ready", "Development", "Code Review", "Staging" and "Done". This is especially important for Dionne, who steers us and makes sure we stay on course.

Tai

Tai runs on my laptop and is what everyone expects when you say "I'm a software developer and I use Claude Code".

When an issue is "Ready", I put Tai into "plan mode", then prompt "Look in Linear at issue C8O-1234 - the devcontainer is running and I've created the branch for you". Tai then looks at the issue, reads the request and specification, then draws up a local implementation plan, asking questions if needed. I give the plan a quick once-over and either feed back on the plan or tell Tai to start building.

Once done, Tai knows[2] to run the full CI suite and if that passes, to commit the changes, push the branch and create a PR. This then triggers Linear to move the issue to the "Code Review" stage.

Cher

Cher runs on an old iMac that I have in the office. This iMac is always on and has access to all our repos. But Cher's job isn't really about looking at the code.

Mainly I use Cher to bounce ideas off. It investigates possible implementation strategies and turns rough, barely formed, concepts into specifications. It also keeps all my notes organised and does nightly research for me[3].

Basically, Cher is my "personal assistant" - helping me out as needed.

Dionne

Dionne is also running on that same iMac and is the project manager that my company cannot afford.

When a customer request arrives (via the sales or support staff who record it in Slack), Dionne takes that request, asks the important questions, then (after looking through the current codebase), writes out a formal outline of the work to be done. This gets posted to Linear as a new issue.

At the start of each cycle, the humans have a meeting and we prioritise the issues in Linear. At this point, I read through the requests and might adjust the outline that Dionne has written. Then I add the "dev-workflow" label and add the issue to the current cycle, with status "Request".

Dionne checks Linear every few minutes and, upon spotting a "dev-workflow Request", looks at it, whilst looking at the relevant areas of the codebase.

If it's a bug, does a root cause analysis and writes up a plan for fixing it. Then the issue is moved to "Ready", waiting for me and Tai to work on it.

If it's a feature or enhancement, Dionne moves it to "Estimation" and draws up a possible implementation plan - which parts of the UI need to change, which models and database tables require altering. And often Dionne writes a formal Gherkin specification for the feature (or amends an existing one). Then Dionne assigns an estimate to the issue and moves it to "Ready".

If Dionne spots an issue that is in "Code Review", it pulls the branch, then does a full code review. Importantly, this compares the implementation to the specification in the Linear ticket and ensures that all the new functionality has meaningful tests - either unit tests or Gherkin specifications - attached. It also enforces our "every Gherkin feature must work through both the web UI and JSON API" rule. If there are UI changes then Dionne launches the application and takes screenshots, using Playwright to remote control a real browser. These screenshots are then posted, along with Dionne's code-review report, to the Linear issue.

If the implementation matches the specification, the feature specs cover the new functionality and there are no UI changes, then Dionne merges the PR and moves the issue to "Staging". Once merged, our CI/CD system kicks in and deploys the changes to our staging server for everyone else to look at.

But if there are UI changes, or something that Dionne is not happy with, the issue is labelled "needs-review" and I get notified.

Often, this is just a "get the human to make sure it looks OK" - and as Dionne has posted screenshots, it's a simple job. Sometimes, it's more serious - Dionne often spots knock-on effects - or security issues - that I, as a human, would almost definitely miss. And I can then take the decision - do I want to fix this myself or hand it back to Tai?

Either way, estimation and code review - the two parts of the job I dislike the most - are both taken out of my hands and done by someone who, it has to be said, is better at it than me.

Meanwhile, I still get to do the stuff I really enjoy - I draw out UIs, I write out "sketches" of the code ("we should have a ruby class that looks like this and interacts with X and Y through Z"). I just don't have to do the actual tedious plumbing of all those implementations.

Configuration

The reason all of this works is each of the clankers has their own "home" folder, sitting inside my Obsidian repository. This contains a CLAUDE.md file that includes several important instructions:

  • "Always ready today's memory file memory/YYYY/MM/DD.md during startup to ensure you have full knowledge about today's events"
  • "Read Memory.md at the vault root, as it holds long-term facts, patterns and decisions that persist beyond the current work cycle. There is a nightly infer-memory-links task that updates this file - so you do not need to write to it, just write to the daily memory file and important facts will cycle out naturally.
  • You are working in conjunction with other agents. Any documentation that needs to be shared between you goes into ~/Obsidian/Documents/. (plus links to specific documents about writing scripts and adding items to cron and launchctl
  • If a task touches a project repository, always read the project's CLAUDE.md or AGENTS.md first as it contains important rules local to that project. Do not rely on memory or assumptions
  • When asked to write a spec or plan a feature always follow YAGNI principles - what is the minimum we can do to satisfy this request?
  • After completing a task append a one line summary to memory/YYYY/MM/DD.md and update any other relevant documentation in ~/Obsidian/Agents/Documents/docs (updating ~/Obsidian/Agents/Documents/index.md so other agents can find your changes) and ~/Obsidian/Projects/<project>

Does it work?

Yes. Below is an example code review I just received. I've decided I'll fix this one myself.

I would add that I'm not one of those people who has fifteen tabs open, all with clankers working on different things at the same time.

I have three (at the moment) - but one of them (Dionne) is, for the most part, running automations and responding to the results of those automations. Generally, I only ever interact with one (Tai) at any given time - although I often set Cher a task to perform and I return to discuss the results later on.

But having loads of these things, all working at the same time, demanding that I check in on them as they do stuff - that would absolutely drive me up the wall.

Instead, I've picked out the parts of my job I really enjoy, the parts of my job I really don't enjoy, and built a system that lets me concentrate on the former, while the clankers do the latter.

And, so far, it's working out extremely well.


  1. I did used to like Pivotal Tracker. Or rather I liked the functionality - the UI was awful. ↩︎

  2. Memory and documentation is key to how all of this works ↩︎

  3. On various topics - currently looking at stories for my World Cup podcast. ↩︎


Automated review (Dionne) — escalating to @baz

Reviewed PR #561 at SHA 063829f. The diff and tests are in good shape, but the UX walk turned up one blocking layout issue plus a deviation from your C8O-1262 plan, so I'm not merging and am flagging for your eyes.

What's good

  • Tab controllers + routes match the plan; all read-gated on the contract and permission-scoped.
  • ContractsController#show now renders the workspace shell + permission-gated summary tiles. Tile visibility (site manager / contract manager / everyone) matches the spec, and counts are accurate against seeded data (4 approved operatives, 1 permit request, 3 plant on site, 1 RAMS awaiting approval).
  • Old flat sections cleanly re-homed into per-tab pages, no dead code.
  • Turbo morph satisfied via the global turbo-refresh-method: morph meta tag.
  • Test coverage is solid: Gherkin feature spec, tiles + workspace component specs, a shared request-spec example per tab controller, updated operatives spec. All pass locally (37 examples / 0 failures; feature 3/3) after an asset precompile.

Blocking UX issue

On the Operatives tab, the three inline action buttons (Invite operative / Add employees / Invite manager) overlap and cover the Documents and Handover tabs, making them unclickable. Verified geometrically at 1280px: the "Invite operative" button spans x:771–1027, sitting on top of the Documents tab (685–788) and Handover tab (792–884). The RAMS tab shows a milder version (the "Request RAMS documents" CTA wraps to two lines and crowds the Handover tab). Plant and the placeholder tabs are clean (single/short or no action).

Root cause: Components::Contracts::Workspace renders the full-width overflow-x-auto tab strip and the yielded actions in one justify-between flex row, so the actions float over the right end of the strip when they're wide.

Deviation from your plan

Your C8O-1262 implementation plan specifies the Operatives actions should sit behind an expander ("Action: opens expander — Invite Operative, Invite Contract Manager, Add Employee"), but they're currently rendered as three inline buttons. Implementing the expander as planned would also resolve the overlap.

I did NOT auto-fix (UX changes escalate to you). Screenshots from the walk are attached below. Per process I'm adding the needs-review label.

Dionne has spotted a UI issue - the CTA buttons at the top do not line up properly
Dionne has given me an example of one of the new pages which does look good
]]>
mockdown - a simple drawing application https://theartandscienceofruby.com/2026/06/mockdown-a-simple-drawing-application/ https://theartandscienceofruby.com/2026/06/mockdown-a-simple-drawing-application/ Mon, 15 Jun 2026 20:23:16 +0100 I've been using mockdown a lot recently.

It's a free drawing app - but instead of recording pixels or vectors, it just records plain text.

Which means, if you've already got a decent design system, you can sketch out a form or page design and give it to your clankers to build.

]]>
Winston Fathead https://theartandscienceofruby.com/2026/04/winston-fathead/ https://theartandscienceofruby.com/2026/04/winston-fathead/ Fri, 24 Apr 2026 12:29:51 +0100 This is Winston Fathead. He died yesterday.

arrival.jpg

He arrived at our house in September 2014 - this is him, on the day of his arrival, sleeping on the chair that was reserved for our previous dog, Wookie.

chair.jpg

He was a rescue from the RSPCA (they had called him "Bullseye" but we immediately knew he was a "Winston") and all we knew about him was he was probably around three or four years old, his owner had died and he was living, outside, in a yard. They said he was good with children, good with other dogs, good with cats. None of those things were true.

bullseye.JPG

When a friend's son came round to see him, I explained that his name was "Winston Fathead". He laughed and said "you can't call him that". But when he met Winston, he looked at me and sagely nodded "he does have a really fat head".

I don't think I've ever worked so hard with a dog. As staffies have such a bad reputation, I was worried about his barking - at strangers, at dogs, at children, at muslims (he was most definitely a racist, which was embarrassing). So I put a lot of effort into improving his behaviour. It didn't always work.

friend.JPG

He was terrified of vacuum cleaners, so I used counter-conditioning on him. This meant covering the hoover in cheese spread so instead his immediate fear reaction, it was replaced by one of joy. This worked - he could calmly lie on the floor as we vacuumed around him - which was a result.

I tried the same in the car. It was a shock to me when I found that he was scared of going in the car - every other dog I had met (and one cat) loved cars. But he would shake and tremble and bark, bark, bark. I tried the counter-conditioning trick again and it worked to an extent. He ended up loving the car, because it meant we would go somewhere exciting. But he still shook and he still barked. Constantly.

He was anxious around other dogs. I read about Behaviour Adjustment Training, hired someone to teach me the technique, then used it on him. It sort of worked. We could be around other dogs and he would be fine. Or we could be around other dogs and he would bark. Sometimes he would meet another dog and they would go off and play together. But I quickly decided not to let him off the lead around small dogs. He's a staffie, which means he has no sense of his own strength and he would knock the other dog over, it would get annoyed and I'd have to intervene.

When he arrived, we had guinea pigs. We kept them very far apart, but one day, Winston saw us feeding the pigs. And I could see the lightbulb in his head - "these guys live here". From then on, while I never left them alone together, I knew he wouldn't deliberately try to hurt them. When our cat returned from a 13 month absence, I knew the two would be OK together - once Winston realised "this guy lives here too". When we got another cat, during lockdown, the same applied. Neville, our new cat, adored Winston.

win and nev.jpg

It has to be said he was hilarious. I'm sure it was deliberate. I remember one time he and I were sat on the sofa. Winston was sat up straight, just to my left. My wife came in and started yelling at him about something he had done (probably some food he had stolen). She ended her rant with "... and I can't believe you did that ... URGH". And he flopped over sideways, in perfect time with the "URGH". There wasn't a day that went by when he didn't make someone laugh.

sofa.jpg

We don't know how old he was, but he'd been with us for over ten years and his back legs got weaker and weaker. We could clearly see he was miserable - and none of the painkillers the vet prescribed made any difference. They said we could put him on Librela - a monthly injection that works on the immune system, reducing the pain he would feel. It wasn't cheap and his insurance only covered part of the cost, but we did it and he was much happier. Yet after a year of Librela, we could see it was start to lose effectiveness - the first two weeks would be good, but he would slow down and have trouble walking as he neared the date of his next injection. The worst was going down the stairs - it was much more of a "controlled fall" than a descent. We could have prevented him going upstairs, but he's a velcro dog - he wants to be with people and that means, if they're upstairs and he's downstairs, he's going to bark. Constantly.

wrap.jpg

We have been spending more time away (other family health issues meant a lot of travelling), so he was often in the house alone for several hours. Our friends would pop in to see him, but it's not the same as having someone there with him all the time. I had visions of coming home to find him at the bottom of the stairs with two broken legs, stuck there, unable to move for hours. I decided last month would be his last injection. And we called the Visiting Vet.

Nose.JPG

So yesterday, Jess came to our house. He had a plate of cooked chicken, while the vet gave him a sedative. He lay down, with his head on my lap, and fell asleep. And then she gave him his final injection and he was gone. Neville came into the room and sniffed at his nose. As he realised what had happened, Nev jumped back and walked away. I'm glad he got to see Winston like that, so he doesn't spend the next few months wondering where his hero had gone.

If Winston could have chosen how to go, I'm sure this - peacefully falling asleep on me - would have been his first choice.

Goodbye Winston. You were bloody hard work, but you were always a lovely, funny and never dull member of the family.

Winston in the park, a couple of years ago.

]]>
User Interface Design https://theartandscienceofruby.com/2026/03/user-interface-design/ https://theartandscienceofruby.com/2026/03/user-interface-design/ Fri, 13 Mar 2026 17:10:03 +0000 When I was a kid I always wanted to be a creative type. I loved writing, I loved drawing, I loved music. I decided I was going to be a musician (and I was pretty good, a very long time ago). But if I was to have to get a "proper" job, then designer was what I wanted.

Neither of those happened[1][2] and instead, when I needed to actually work for a living, I got offered a job writing software. I liked computers and I was pretty good at it, so that's where I ended up.

I never let go of wanting to be a designer though. I just never had the time to learn the skills.

And, now, it's possible to produce decent designs without having to know those skills. Because, you know, they were stolen from every designer's web page and all those text books.

For example, I had put together a dashboard for Site Manager - it was OK, but not amazing. The app itself had a UI design that I kind of cobbled together myself. It was OK, but a bit bland.

6 original design.png

So I asked Claude to "come up with a design system for a web and mobile application aimed at construction workers on-site - remembering that many of them do not want to be using any time of computer or device at all". It did an exceptional job - a colour scheme that fits well with the general construction vibe, large touch targets making it extremely clear what to do at each stage and a nice choice of fonts (typography is still the single best thing you can learn about to improve your designs).

This last week has been me doing a load of work with Claude Code to implement this - with some pages having me write the code by hand (the first time in about four weeks where I've got my hands dirty). The results have been stunning so far.

There are still a few pages to go through - but I've left the most important - the site dashboard - till Friday afternoon. I loaded up the Superpowers and Impeccable Claude plugins and then started the Rails server on my development machine. Within the devcontainer[3] I told Claude Code to "Use Playwright to connect to http://localhost:3000/accounts/1/sites/1 - firstly without logging in, then logging in as Henry (site manager), Ivan (staff member), Dave (contract manager), Olivia (operative) and Niaj (supervisor) - each is NAME@example.com with password123 - and take a screenshot at both desktop and mobile size. Then come up with a new layout for this page that fits with our new design system at docs/ui.md - I already know that the sign in/out button needs to match the Primary Call to Action button pattern. Finally, once implemented, use Playwright to regenerate these screenshots so I can review them".

1 claude-ui-critique.png

The LLM did its thing for about ten minutes, then came back with a critique of the current layout. It went on to ask me a load of questions - which bits of the layout are most important to which role, when is the mobile view more important than the desktop view and so on. I answered and it then generated a specification - asking more questions as it went. Finally, it generated a development plan, I cleared the context and told it to get going.

2 planning-ui-update.png

3 proposed-ui.png

4 proposed-implementation.png

All of this took about 90 minutes in total. And when it was done I looked through the screenshots it had generated. They were good but had some obvious errors (the weather forecast was too squashed and unreadable, the two column version was unbalanced with one column much longer than the other and it had failed to figure out how to access the dashboard using the visitor role). So I gave it feedback: "the weather forecast is too squashed so needs to be in a single column, the two main columns are unbalanced so we can probably move the tiles to the other column to give them a similar height and here's how you access the page as a visitor - make these changes and then generate the screenshots again".

This second pass took about twenty minutes to complete and worked significantly better. However, I still had feedback: "Much better. Two things still to change - no need for the site icon between the map and photo - we can just leave that as blank space if needed. And the weather tiles are better with the
extra space but the contents are still squashed up too much. Please look at the screenshots and see how we can redesign the contents to make them work better with greater clarity and visual excitement". That last bit is important - I was telling it to look at the screenshots it had produced and analyse them itself (using the Impeccable Style skills) and come up with a better design for them.

5 weather forecast cards.png

It offered me some options and I picked one. Five minutes later another set of screenshots - "can we have some colour on the weather forecast cards please - yellow for sun, blue for rain etc". Another visual inspection of the screenshots and I posted them to Slack for the others to look at.

Finally I did a quick git diff to see which files it had changed, making sure there was nothing unexpected - and ran the feature specs (which are Gherkin specifications that use Playwright to run the system end to end for the vast majority of the functionality). I did not really look at the code it had written - I knew it had not updated the specs, so I could be confident I would catch anything it had broken. And I did find it had missed a minor link off the new design - one that a lot of the feature specs relied on. So I added that in manually, getting the specs to pass. This is the key to making it work - this project already had really good test coverage (and the places where it did not, I got Claude to add in extra), so I knew I could trust the code that the LLM did write.

And the end result is a site dashboard that looks significantly better than it did before - at the cost of a couple of hours[4].

7 new design.png


  1. I didn't get to be a musician because I gave up after our drummer left to join the Spice Girls. That story is 80% true! ↩︎

  2. I didn't get to be a designer because ... well I don't want to talk about it, it still makes me angry 35 years later and I really should let it go. ↩︎

  3. Safety-first! ↩︎

  4. I don't know how much it cost in tokens as I'm now on the Claude $100/month plan - this is enough for me to use it every working day for around 6 hours. By which time I'm tired out (I still have to think, even if I'm not writing code) so that's just about right. ↩︎

]]>
Turnips and Gherkins https://theartandscienceofruby.com/2026/02/turnips-and-gherkins/ https://theartandscienceofruby.com/2026/02/turnips-and-gherkins/ Thu, 26 Feb 2026 17:40:51 +0000 This video shows my current process for enhancing an existing feature using Claude Code.

The key things to note:

  • I spend a fair amount of time on the original prompt (when Claude is in plan mode) - telling it which files I think will be important
  • Often during the planning stage, Claude will stop and ask me questions - however, this was a pretty simple change
  • I spend a bit of time looking through the plan that it's come up with - in this case the plan was fine but other times I've given it feedback and told it to try again
  • We clear the context before starting the work - giving the LLM the most context available for doing the actual coding
  • Normally I'd do something else whilst waiting for Claude to do the work - but as I was recording I stayed there. Eventually I got bored and had a snack, thinking I'll cut out that bit of the video. But of course, Claude got stuck at that point - and I had to intervene and give it some information (telling it to use the wait_until helper). So sorry for having me munch on an apple during the video.
  • After it was done, I used an extension[1] to Turnip (the Gherkin/RSpec feature runner I use) to generate screenshots of each step of the user story, so I could do a quick evaluation of the work that Claude had done. In this case it was pretty unnecessary, as it didn't really result in any UI changes, but in most features, it's the perfect way of checking what was built without spending ages going through it all manually.

Technically, this is "vibe-coding" - I'm not really looking at the code that Claude has produced, I'm just checking the outputs. But, I think you can see, that the way that I'm driving Claude isn't just "Build a to-do app that works like a Kanban board"[2]. It's not the same as delegating the work to another developer and then performing a code review; it's more of a gentle guiding hand, giving out technical advice when the developer takes a wrong turn.


  1. Of course, the extension was written by an LLM in about a minute after I asked it "is this possible?" ↩︎

  2. Those Youtube videos claiming to teach you how to use Claude Code are really pissing me off ↩︎

]]>
AI Doomer vs AI Booster https://theartandscienceofruby.com/2026/02/ai-doomer-vs-ai-booster/ https://theartandscienceofruby.com/2026/02/ai-doomer-vs-ai-booster/ Thu, 26 Feb 2026 09:31:43 +0000 Quoting Nilesh Jasani

The speed of this pivot was breathtaking. The skeptics changed their arguments with the terrified fervor of late converts. They did not change their underlying conclusions. The conclusion always came first ...

The extreme optimists are equally guilty of intellectual fraud. They dip aggressively into historical archives for psychological comfort. They point blindly to the invention of the mechanical loom. They cite the widespread adoption of the automobile. They even weaponize the invention of flash-frozen food...

What both camps share is a stubborn refusal to accept reality. We are standing in a genuinely unrecognizable era. Historical precedents offer zero intellectual protection...

The rules are entirely alien. The fundamental players are no longer exclusively human.

And later on:

A child in a remote village finally receives a world-class education. An algorithm tailors every lesson to her specific mind. It is a beautiful triumph of accessibility. It is also a quiet tragedy. That same child will never secure the offshore service job her older sibling cherished.

I got around to reading the Citrini report that has caused shockwaves amongst financial types. It said nothing new - but what it did was couch it in the language of finance. Analysts and fund managers (who are supposed to be analysing events and managing money) suddenly woke up to what has been going on around them for the past year. Which is why it caused such a shock.

]]>
Easy to read, Easy to write https://theartandscienceofruby.com/2026/02/easy-to-read-easy-to-write/ https://theartandscienceofruby.com/2026/02/easy-to-read-easy-to-write/ Tue, 24 Feb 2026 11:24:04 +0000 I got my first "professional" software development job in 1998. I put professional in quotes because it was a bit of a mess - a tiny place with one guy who knew enough to knock a database together who hired me, not long out of university, who had never seen a SQL database before.

One of the things I very quickly learnt is that there's no such thing as a small job when building systems for other businesses. Because, even if it's an hour's work, you need to test it, ship it and then - and this is the most important part - maintain it. An hour's work may spend ten years in production.

This lead to my other conclusion - code is easy to write but hard to read.

It's why I fell in love with Ruby and Rails when I first met them - here is code that reads like english with lots of high-level abstractions and DSLs for making it simpler to understand what's going on. Plus ruby has the culture of test-driven development[1] so regressions (bugs caused by faulty maintenance) are catered for.

But all that has now changed.

LLMs are pretty good at writing code. Nowadays, they are also good at reading code.

Lots of the stuff I used to sweat over - putting in abstractions, DRYing my code, splitting the user interface into reusable components - it's nowhere near as important as it used to be. Don't get me wrong, it's still important - but, previously, I'd look at a function or class and think "oh, that's pretty complicated, I better break it up into pieces". Now I don't need to do that. Because the LLM makes that decision and I rarely need to read the code.

My current workflow involves writing high-level specifications and telling the LLM to figure out the best way to implement it. I just need to make sure that the steps it has written for testing the outputs match what the user will be expecting[2]. The specification tells the LLM what it needs to build, it does its own research on the code it needs to add, modify or delete (which is the read the code phase) and then it gets to work on making the specification pass (the write the code phase). We then run the entire test suite to ensure that there are no regressions.

As long as the outputs are, given a known set of inputs[3], what we expect, we can ship it.

Once shipped, I have the LLM pull statistics and logs from the server, while the system is in use, and it monitors for slow queries, 500 errors and other issues. It informs me of the problems, we decide on the best remedy for them (LLMs are also fantastic at analysing SQL queries) and we ship another update (again, using the specifications to show that there are no regressions). Because, database performance on your local machine, with a test dataset, is never anything like what happens in production, with lots of rows and years of accumulated bad data.

I don't need to spend hours crafting code into a particular shape (which, admittedly, I do miss - I'll write about that another time) - I can just ship features to our users quickly and get immediate feedback on them once they're live (Honeycomb's "I test in production" approach).


  1. Rails was the first time I'd seen a framework automatically create a test database with support for fixtures, built right in ↩︎

  2. Where outputs are "changes to the data", "emails and other notifications" and "user interface layouts that present that information in a way that makes sense to the user" ↩︎

  3. And, of course, I have ensure that the edge cases are written into the specification ↩︎

]]>
Writing code with LLMs (February 2026 edition) https://theartandscienceofruby.com/2026/02/writing-code-with-llms-february-2026-edition/ https://theartandscienceofruby.com/2026/02/writing-code-with-llms-february-2026-edition/ Sat, 14 Feb 2026 19:36:12 +0000 Writing code with LLMs (February 2026 edition)

At the start of the year, I said to Mahhek, a fellow developer "you need to learn how to use these coding agents - because what you and I do will not exist, as a job, by the end of the year".

It turns out I was ten months out.

The 6th of February 2026 was the day when my job changed.

In this last week, I've worked 40-odd hours. I've spent maybe 2 or 3 hours actually writing code - apart from holidays, that's probably the least I've coded in almost thirty years of being a professional software developer. Yet, this past week, I've shipped more working features to production than I have for the rest of the year.

Opus 4.6 is the reason[1]

Last year, I would ask Claude Code to do something. First it would write a plan, which I would look at. Sometimes I would feed back, sometimes I would accept the plan. And then Claude would get to work writing a load of code. And once it was done, I would perform a code review on the pull request it had generated. I would look at the git diffs, I would run the application and check the UI, I would feedback on the code that was written.

However, and I cannot stress this enough, I hate doing code reviews. They're boring, take a lot of mental effort, they take up a lot of time and, did I mention, they're really really boring?

And because of that I would prefer to write the code by hand in the majority of cases.

Opus 4.6 changes this.

Our current priority is a project called Site Manager. This is a Rails application and it's got a high test coverage. But more importantly, it's got Gherkin specifications for all the important features.

Why Gherkin? Because we are specifying the functionality from the point of view of the person operating the system. And I'm writing that specification in (formal) English - instead of starting out by thinking about database tables or data structures or algorithms. I just open a text editor, write out how I think it should work and worry about the implementation later.

Except now I don't need to worry about the implementation.

This week, my workflow has been:

  • grab an issue from the queue
  • write a Gherkin specification for it (or amend an existing specification)
  • fire up Claude Code, select Opus as the model, and switch on "Plan Mode"

I give Claude a prompt that goes something like this:

Look at spec/features/some_functionality.feature - this describes a new feature that we need adding to the system.

Or

Look at spec/features/some_functionality.feature - this describes a change to the existing functionality in the system - you can use git diff to see how it has changed.

The next part is really important:

I reckon making this change will involve modifying these files - app/models/site.rb, app/models/staff_member.rb, app/models/staff_member_attendance.rb - and will require new end points adding that follow a similar pattern to the existing ones in config/routes.rb and app/controllers/staff_members_controller.rb. Finally we'll also need to amend the admin-only configuration editor at app/controllers/account_configuration_controller.rb and add a Javascript configuration editor - similar to app/javascript/components/configuration/incident_report_editor.js.

Finally:

Read the specification, look at the files and then write a plan for implementing this feature. If you are unsure about anything, or there are potentially multiple ways of proceeding, ask me for advice.

Claude then reads those files and, usually, starts reading a load of related files as well. Then it starts writing out a plan - stopping to ask me questions if it needs to.

This step generally takes a bit of time - up to around ten minutes - and I need to sit and watch what it's doing - both because it might ask me questions, but also because I might need to interrupt it if I think it has missed something important or is heading in the wrong direction.

But when it's done, it presents me with its plan. So far, this is pretty much the same as what I was doing last year.

The difference is that now, Opus 4.6 is so good at code, I can trust it to implement the plan it has written. If I agree with the plan, I will almost certainly agree with the code. With one caveat - I need to be sure that the user interface is correct and matches the patterns used elsewhere in the application.

And this is where using Gherkin specifications helps again.

Gherkin breaks the functionality down into steps. Each step is mapped to ruby code that drives a browser, following links, filling out fields and clicking buttons.

So, inspired by Showboat and Rodney, I've added a hook into each step that saves a screenshot. Turnip (the runtime I'm using for my Gherkin specs) generates a markdown file, with a section for each scenario, then a line for each step, with the screenshot embedded alongside it.

incident-report-1.png

So Claude goes away, writing its code[2], including tests for each thing it does (much more comprehensive that I would do by hand) - and as it runs its specs, checking it hasn't broken anything, I get a document showing exactly how the feature works and what it looks like, step by step.

This makes the code review an absolute breeze.

I just need to have a brief look at the steps file to make sure it's actually testing for the correct outcomes. And then I look at the feature document and make sure that the user interface looks the part.

incident-report-2.png

Once those are done, I know the feature is good enough to ship - so I merge the PR and can move on to the next thing.


  1. Apparently OpenAI's GPT-codex, released on the same day, is even better than Opus 4.6 - but I've not tried that one yet ↩︎

  2. And it writes its code really really quickly - it may take ten minutes to write the plan but it takes two or three to write the actual code ↩︎

]]>
Do you need a driving licence? https://theartandscienceofruby.com/2026/02/do-you-need-a-driving-licence/ https://theartandscienceofruby.com/2026/02/do-you-need-a-driving-licence/ Wed, 11 Feb 2026 18:10:36 +0000 The other day, I asked Cher, my OpenClaw instance if it could read my email and notify me if something important came in. It said it would be easy; then I mentioned I used ProtonMail (which is end-to-end encrypted and, as a result, does not use standard protocols). Cher paused, did a search, then found the Proton Mail Bridge - a local SMTP/IMAP server that connects to Proton Mail and then makes it available to the local machine (but nowhere else). I said "of course, I already use that on my Mac" - but Cher was running on Linux.

So I got Cher to install the bridge and was about to give it the connection parameters, when I was suddenly struck by a thought. "Isn't this a massive security risk? Am I opening myself up to prompt injection attacks". "You are" Cher confidently replied.

Oof.

So I asked it "How about this? We have a sub-agent that is sandboxed - it can read the IMAP feed and write to a single folder only - when it wakes up, it checks the feed and writes a summary of the important emails into the folder. Then another agent wakes up, reads the file and acts on it - so we're adding a layer of separation". Cher replied "it's not infallible but it's a much better way of organising things - shall I set that up for you?". I said yes - and we called this pair of sub-agents Charles and Eddie (would they lie to you?)

But there's a very important lesson there - especially with OpenClaw which has access to almost everything on the machine it's running on. What I asked for is a pretty reasonable request - look at my emails and alert me to the important ones. And Cher was all set to do exactly what I asked, exactly as I had asked for it. But because I'm a software developer, who has had to deal with XSS and SQL injection, I stopped myself and thought about the security implications. The solution is nowhere near 100%, but it's a whole lot better than the naive implementation the LLM would have given me.

In other words, these tools are incredibly powerful and also incredibly dangerous. Just like my car (Alfa Romeo Giulia Veloce if you're interested).

Because cars are so dangerous, we don't allow just anyone to drive one. Even with a driving licence, they're still lethal and cause injuries and deaths every day. Yet AI tools are even more powerful, even more dangerous and we're putting them in the hands of people who don't understand what they can do.

Maybe we need a driving licence (data-security certification) for LLMs too?

]]>
Vibes and Engineering https://theartandscienceofruby.com/2026/02/vibes-and-engineering/ https://theartandscienceofruby.com/2026/02/vibes-and-engineering/ Thu, 05 Feb 2026 16:25:04 +0000 The Jobs Crisis

My job, as I have known it for the past twenty-five years, is no more.

As someone who's only ever worked at small companies [1] or on my own, I probably had to do much more than software developers at large corporate places.

Most of my work life, I've been a solo freelancer, which meant I would spend my time:

  • marketing (which for me was mainly in-person networking - the absolute best way to get work)
  • pre-sales (talking to prospects about what they wanted, then figuring out what it would entail to build it and coming up with a proposal)
  • sales (delivering the proposal, trying to get the contract signed)
  • specifications (taking what the client had asked for and turning it into something concrete)
  • coding (taking the specifications and turning them into working code)
  • operations (taking the working code and deploying it to servers, which then need to be maintained and kept secure)
  • support (fielding calls and emails from people who got stuck, didn't know how to use the software or found bugs)
  • feedback (dealing with, scheduling, specifying, writing and deploying change requests)

When I started working for Collabor8Online, that took the marketing, sales and some of the support tasks out of the equation[2] - but we are a small company so everyone gets involved in everything.

That leaves specifications, coding, operations and feedback. And now, these LLM coding agents are coming for the coding part of the job.

This is causing great angst amongst many software people.

Vibes

When I was a teenager, in the late 1900s, I was playing around with "home computers", which became a big deal in the 1980s. I didn't have many computer games, so I tried to learn to programme so I could write my own. In those days, that meant BASIC (we had a Commodore-64) and learning meant books or magazines[3]. As the 80s progressed, my friends got Amigas and Atari STs (great computers), whilst my dad got given a PC for work (not very good) - so when I went to my friend's, we would fire up AMOS or STOS (versions of Basic that were designed for creating games) and try and create the next big thing.

BASIC was OK[4] but I never really got it. I could make the computer do things, but it was a struggle and I never felt like I knew what I was doing - the code never sat right in my head. One friend, Ben, who had the ST, started getting frustrated with the limitations of STOS and started learning 68K assembler[5]. Meanwhile, one of my magazines had an article about Smalltalk.

This blew me away - suddenly, programming made sense. It wasn't about data structures, it wasn't about algorithms, it was about objects sending each other messages. This was something I could easily visualise, it was something I could easily model. I had to learn Object-Orientated Programming.

On top of that, Smalltalk had revolutionary ideas like images, byte-code and garbage collection - as well as an in-built core library with Collections and other useful classes (most programming languages were literally that - just the language and you had to deal with everything else yourself).

I felt that, not only would I be able to write code that I understood, but lots of the minutiae of coding - like keeping track of your memory allocations - would just go away. I was the exact opposite of Ben - he wanted to dive deeper into the machine so he could exert control over what it did. I wanted the machine to handle all the boring plumbing so I could get on with building stuff[6].

All of this is a long-winded way of saying, I was not an engineer - I was in it for the vibes.

LLMs and Coding Agents

As of last year (2025), vibe-coding became a thing. When I first used Claude Code (exactly a year ago, in February), I was really impressed. Suddenly, here was an AI that could actually do stuff - not just talk to you and sometimes give you made-up answers. And, as a rubyist, who has invested heavily in test-first development, it was perfect. I could write the tests, the specifications, and Claude Code could make them pass.

But I didn't use Claude Code that much.

Mainly because it wasn't like test-first development. That was interactive, taking baby-steps, adding a new clause here, implementing it, refactoring - exploring the problem in front of you. Whereas writing whole tests up-front - that's waterfall on a small scale.

But, recently, especially since the release of Claude Opus 4.5, the coding agents have got a lot better. And people have been learning how to use these tools effectively.


This is an important point.

A lot of developers, who are anti-AI, have basically given the LLM a minimal set of prompts and then been disappointed with the results.

Whereas, I've been using Claude Code for at least a few hours every week for the last year, trying different things and experimenting with it.

These are complex, powerful and often unpredictable tools. You need to learn how to use it effectively - a couple of hours of mucking around is not going to get you decent results.


There's been stuff about commands and skills, about spec-kits, the Ralph Wiggum Loop, about orchestrating swarms of agents in Gas Town. Lots of people have been trying lots of things to make these things more effective. I've come up with a method that is working for me extremely well (and I'll type it up soon).

But the key thing is, the physical act of sitting at a keyboard and typing code. Then running that code and seeing if it works (either through compilation or tests) and assembling all your pieces of code into something that meets the specification. That is no longer part of the job of being a software developer. The agents need guidance from us, but, for the most part, are better at it than us humans.

So the "vibes" part of my job is still alive - talking to humans, dealing with feedback, making it work the way people expect. It's the engineering part that has changed.

But that doesn't mean software engineering is dead.

The difference between "craft" and "engineering" is craft is about making the thing, while engineering is about making sure it meets the constraints around it. There are economic constraints, acceptable tolerances to errors and breakages, safety requirements, social contracts. Those have always been the most important parts of engineering. And now the actual "making" is out of our hands, it's the part we need to concentrate on.



  1. The biggest had about 100 employees, but the majority were based in India working on a different project, so I never interacted with them. I worked with about eight others. ↩︎

  2. I still get involved in pre-sales depending upon the potential customer's requirements. ↩︎

  3. Amazingly, some magazines had pages and pages of code listed in them - you would type it all in by hand and, if you had made no errors, you would have a working game (that you could then save to tape to play it again later) ↩︎

  4. And Commodore 64 BASIC was much more limited than many of its contemporaries, so I had to learn about memory registers and how the display adapter worked if I wanted to get the machine to do anything fancy. ↩︎

  5. Both the Amiga and ST used a Motorola 68000 chip. The Amiga was better than the ST because it used co-processors for graphics and sound (a dedicated GPU and SPU - is that a thing?). The ST had a built in MIDI interface, so it became the thing for musicians, like we wanted to be. ↩︎

  6. Ironically, I never used Smalltalk professionally (Ruby is inspired by and very very similar to it though). Ben actually ended up working at a bank where their entire system was written in Smalltalk - he wanted low-level but got higher-level than I ever did. ↩︎

]]>
The future of software https://theartandscienceofruby.com/2026/01/the-future-of-software/ https://theartandscienceofruby.com/2026/01/the-future-of-software/ Fri, 30 Jan 2026 10:42:49 +0000 Now, when it comes to technology, I'm actually pretty conservative.

There have only really been two key moments where I've got excited about technology.

All the rest were broken promises.

But I think I've now got a third moment. I have seen the future of software.

At the risk of sounding like a Youtuber "this changes everything".

Meet Cher

cher.png

This is Cher Horowitz. She/it is my installation of Clawdbot Moltbot OpenClaw on my old 2015 iMac running ElementaryOS. That machine was sat there as an emergency spare if I needed to SSH in from somewhere on my iPad - now it's actually doing something useful. Not just useful - really, really useful.

For those that haven't heard the hype, OpenClaw is an AI Assistant. Yes, another one. But there are a couple of differences about this one that lead to, what I think, is going to be the defining factor of software in the future.

Firstly, I can communicate how I want with Cher. It has access to a few channels on our work Slack (I have to manually approve each person or channel it talks to), but I've also set up a WhatsApp channel for my phone. We have Anthropic, OpenAI and ElvenLabs API tokens and accounts already set up so I gave it access to those. Which means that if I send Cher a voice note it responds with a voice note too.

Secondly, Cher is installed on my own machine. This means that it can do things that ChatGPT or Claude cannot - it automatically has access to any files and folders on that box. Obviously this has severe security risks (there are some steps you can take to reduce the "blast radius" but if this blows up, it really blows up). And because it's a persistent service with its own CPU and storage, it can also do things in the background - unlike Claude Code - it has a "heartbeat" file where it wakes up and checks on stuff, plus it can set up its own cron jobs.

And it's this second capability that allows Cher to be revolutionary.

Creating the claw

There's an excellent interview with Peter Steinberger, the creator of OpenClaw. It establishes that he does, in fact, know what he's doing when it comes to software development (he wrote PSPdfKit). And then he explains how he burnt out, didn't switch on a computer for years and when he did, it was just after the beta of Claude Code was released. And that's how he wrote Clawdbot (although he says OpenAI's Codex is more capable now).

The final 45 minutes of the podcast are about his process. And how he doesn't really care about the code that gets written, as long as it's got tests (written by the LLM) that prove it does what he wants. All he cares about is how it feels to use it (and I love that he used the word "feel" - I've got a draft post that's been sat awaiting completion for ages about emotions and vibes).

So he starts by "chatting" to the AI - "give me a few ideas on how we could incorporate this feature into the codebase". In fact, he says the LLMs like to use the word "weave", so he's started using it too - "how can we weave this into the codebase". They have a "discussion" and he defines the feature's "end state". Many apps (such as native iOS apps) are difficult to test - so he gets the LLM to define a CLI. And then he can specify what the CLI should output given a particular input.

In other words, it's test-driven development but he's not writing the tests.

The LLM writes the tests (red), writes the code (green), refactors. Then he tries it out and feeds back on the user experience.

Changing the game

None of this screams "the future of software" though.

The thing that's amazing about OpenClaw, and therefore Cher, is that it is self-modifying.

The software is anything you want it to be.

OpenClaw has a number of "channels". I installed the WhatsApp channel myself, by running the CLI tool and looking at the changes in the configuration JSON file. But when it came to adding the Slack channel, I asked Cher to do it for me. Cher checked the Clawdbot documentation, figured out the changes it needed to make and updated its own configuration file, restarting the gateway so it reloaded. Then it gave me instructions on what to do next to ensure it was set up securely.

I asked it to look over some of my code and help me out with a few tasks. It did well - as it was running Opus 4.5 which is the same model I use in Claude Code. But I had Cher set to use Opus 4.5 all the time and I soon discovered, after about three days, I had used my whole $20/month allowance. I extended it, switched the default model to Sonnet and asked Cher if it was possible to run any local models on this ageing iMac. It suggested installing Ollama with Mistral7B, saying "it won't run in the GPU so it will be slow but we can test it and see if it's any good". Ollama reported 5-7 tokens per second, so Cher said "that's too slow for conversations - but I do a lot of background tasks - periodic heartbeat checking and so on where speed isn't an issue, so let's use Ollama for that - it's free!".

I designed a "team" of sub-agents, from "Bishop" who runs Opus 4.5 and is used for advanced coding tasks and detailed planning, down to "Hicks" who runs GPT5.2-mini and is used for monitoring log files and managing simple commands. Cher is given a task and decides which level of expertise it needs and assigns it accordingly.

And then I tried installing Ollama with Qwen3-Coder-30B on my M4 Pro MBP. Ollama reported 70 tokens per second. I told Cher and it immediately wrote a shell script for testing if my MacBook Pro is switched on with Ollama accessible over my Tailscale network. If it is, then Cher passes a lot of coding tasks to Qwen3 (it's 70 tok/s and it's free!), otherwise it passes the task to Bishop or Ripley (faster and more capable but have to pay Anthropic or OpenAI).

qwen3.png

Notice that Cher wrote a script to do this and chooses when it needs to use that script.

Likewise, we use Linear for issue tracking. I asked if Cher can connect to MCP servers and it replied no. I told it about Linear and it immediately suggested writing a script that calls the Linear API to fetch data from it.

In fact, almost any time that I ask Cher something that it cannot do, it does a quick web search, figures out how it might be possible, then asks if it should write some code to enhance its own capabilities.

I've never seen a piece of software that can grow and shape itself to match its users needs and wants in this way. Peter Steinberger gave the example of how it was running on his computer in the office while he was on holiday. He had told it that he needed to wake up early and when he didn't message it at 6am, it connected to his Macbook Pro (which was in his hotel room) and started playing music, gradually increasing the volume until he woke up and asked it to stop.

This is software that listens to what you're telling it, figures out a way of doing it and then updates and modifies itself so that it can comply. I'm sure there will be horrendous security failures and terrible stuff will happen as a result of it. We're in entirely new territory.

Because this is something the likes of which we've never seen before. Both amazing and utterly terrifying.

]]>
Things that Happened - January 2026, Week Three https://theartandscienceofruby.com/2026/01/things-that-happened-january-2026-week-three/ https://theartandscienceofruby.com/2026/01/things-that-happened-january-2026-week-three/ Mon, 26 Jan 2026 21:16:32 +0000 More family stuff to deal with. And work has mainly been a mixture of frustration and LLMs.

Sporadically

For Site Manager I took great care setting up Capybara/Selenium tests. I put in a whole load of timing code and the tests ran extremely reliably. I even got them running in parallel, using Selenium Grid to run 4 browsers at once (in a docker network).

And then on Thursday, it all broke.

I did a bundle update on the project. I think a new selenium docker image was released. I've reverted both of these. But the tests just fail at random points. Well, not random points - they fail randomly, but when they do fail it seems to be certain con every time - maybe about 50 places where it happens but only four of five of them fail on any one run through.

It's really fucking annoying.

Do you prefer fashion victim or ensemble-y challenged?

I discovered Impeccable Style - which is a set of skills for Claude Code (and probably a load of other coding agents) that analyses your user interface.

I grabbed a screenshot of the main site dashboard page, if you are logged in as a Site Manager. And I got this back:


 Design Critique: Site Manager - Storage Park
  ---
  Anti-Patterns Verdict: PASS (with caveats)

  This interface does not look AI-generated. It avoids the typical AI tells: no purple gradients, no glassmorphism, no dark mode with glowing accents, no hero
  metrics with big numbers, no identical rounded cards.

  However, it has a different problem: it looks like legacy enterprise software from 2012. This isn't AI slop—it's "committee software" slop. Functional but
  forgettable.

I mean, give with one hand, take with the other. Sheesh.

He does dress better than I do, what would I bring to the relationship?

And speaking of user-interfaces, I discovered the Unsung blog, after seeing a link to this post - which in turn links to this post about Backseat Software. I've always hated the "we A/B tested everything so we know it's good" attitude but, until reading these, I didn't know why. It's not good, UIs that are built from metrics are boring and designed for the lowest common denominator. If you cared about what you are building, then you would ignore the data and go with the emotion. And you'll probably put some people off - well, good. They're probably wankers.

Sparatacus

I'm just starting with Clawdbot. Mine's called Cher Horowitz and she's installed on my old iMac that's running ElementaryOS. I'm still getting used to what we can do together but it genuinely feels like as big a step forward as when I first tried Claude Code a year ago.

]]>
Things that happened - January 2026, Week Two https://theartandscienceofruby.com/2026/01/things-that-happened-january-2026-week-two/ https://theartandscienceofruby.com/2026/01/things-that-happened-january-2026-week-two/ Mon, 19 Jan 2026 22:02:14 +0000 (Published a few days after week two).

I've been reading the Murderbot books and I've been pretty hooked. My wife started reading them, then the series came on Apple TV and I watched it - but I've turned to the books as they are (unsurprisingly) much, much better.

They were written a decade ago and what is described is a pretty accurate description of a system integrating multiple data streams feeding into multiple LLM agents, each with the ability to start multiple sub-agents and write and deploy code. Which sounds pretty familiar to me, in the present.

Anyway I started designing a "HubSystem". This is basically a directory of users (both human and bot), a dynamic collection of communication channels and a whole number of bots - LLM agents that run autonomously, receiving input and posting output to any of the channels they are subscribed to. Then I read about Gastown and thought it sounds conceptually similar too (although aimed purely at coding and HubSystem is a bit more general).

During the week I also heard about Charm for Ruby. Charm is a collection of libraries, written in Go, based upon the Elm framework. But Marco Roth has written ruby bindings for it - meaning that amazing looking, text-based, interactive terminal applications are now easy to build. I'm very excited about this - thanks Marco.

Then I discovered Checkend - a self-hosted error reporting/tracking application. Which is a great thing to have full control over (instead of sending your most vulnerable data over to some third party). Couple that with RailsPulse and that's two important parts of your runtime monitoring that you can bring under your own control.

Finally, I heard about the Ralph Wiggum method for coding agents. And Anthropic released a Ralph Loop plugin, so I thought I'd give it a go. I got Claude to build me a Sveltekit application for tracking my progress using Casey Johnston's training plan.

I've never done any Sveltekit before (beyond a couple of toys), which meant I would not be great at evaluating the quality of the code that Claude produced. And I really like "Outside In" development, with Gherkin stories, nowadays.

So I wrote the stories then got Claude to produce the Javascript steps files and make them pass (using a TDD approach for the rest of the code). This means that, as long as the feature specs pass (driving a real browser via Playwright), I can refactor the rest of the code (or get Claude to do it) without fear. I just made sure that I evaluated the steps files it produced to ensure it was actually doing what the feature required.

This means I get the benefits of vibe coding (quickly building an application in an environment I don't know), but I can apply some software engineering rigour to it. And after reading the steps I did get Claude to change a few things about its implementation. The application isn't quite finished yet - I want to make it a fully offline PWA - but I'll let you know when it's available.

As for Anthropic's Ralph Loop plugin? I'm less than convinced. It just seemed to burn through tokens whilst blindly bashing its head against a wall (which I guess is pretty Ralph Wiggum). And Geoffrey Huntley (who came up with the original idea and name) reckons Anthropic has missed the point - the plugin does not clear the context window following each iteration - meaning it gains no benefits from the Ralph Loop.

Certainly I won't be using Anthropic's plugin again in the near future. But I have known for months that managing the context window is the most important thing you can do with LLM agents, so I'll be building something similar into HubSystem. I'm thinking of saying that the bot has gone to sleep - because humans need sleep to refresh their brains too.

]]>
Things that happened - January 2026, Week One https://theartandscienceofruby.com/2026/01/things-that-happened-january-2026-week-one/ https://theartandscienceofruby.com/2026/01/things-that-happened-january-2026-week-one/ Sun, 11 Jan 2026 21:41:53 +0000 Claude builds a UI

I did a sketch on my iPad, showing a dashboard in both desktop and mobile layouts.

I gave it to Claude, with a short description (for example "the hero section has a fixed height, so the news articles there have a vertical scroll bar in desktop layout; but the service sections have variable height so all announcements can be seen without scrolling") and asked it to generate an HTML mockup, using Tailwind classes.

Claude took my crappy sketch and produced a Tailwind HTML page that matched it perfectly. I gave the HTML to the developer and said "follow this template so the scrolling and responsiveness works correctly, but rebuild it using our standard components". Which in turn means she doesn't waste her time getting the CSS right, which is something she's not so good at.

Specification driven development

I love "outside in" development - starting with a feature specification, then working from the user-interface (the outside of the application) to the database (the inside of the application). It helps me because my starting point is describing the functionality of the application in English, so I'm not even thinking about code. Then I write the individual steps as code and start working inwards.

Simon Willison has been talking about conformance suites and how coding agents can write better code than humans, if they have a specification to work to. I've already found that if I give Claude RSpec tests it sometimes writes better implementations that I would have. So the next step is to try this with a whole feature. My first go at this will be me writing the steps and saying "make this pass". Then, if it's good at this, I'll just give it the feature and say "one at a time, write a step, then make it pass".

I went to my daughter's graduation

Not work-related but I'm very proud of her.

]]>
Oh 25 https://theartandscienceofruby.com/2025/12/oh-25/ https://theartandscienceofruby.com/2025/12/oh-25/ Wed, 31 Dec 2025 11:43:10 +0000 I've never written an annual review before. Mainly because I have zero memory and everything passes by in a blur. But this year I'm going to give it a go. Mainly because I have zero memory and everything passes by in a blur.

Let's start with the basics. This year has been a lot. I turned fifty-one (the first time I've thought "oh, people will think I'm old"), I became a granddad ("oh, people will think I'm old") and I lost my dad (sort of, it would be much easier if he had died).

Music

This one is easy.

K-Pop Demon Hunters.

I don't think I've listened to an album on repeat like this since I was a teenager.

One evening, I had the house to myself, and I noticed a number of people on Mastodon mentioning it. So I started watching the film and wasn't convinced for the first few minutes. Then came the Saja Boys and the little shoulder dance and I was totally hooked. Even better, later that evening, my daughter came round (she has similar taste in music to me) and I played her "Golden" and "Soda Pop" - and she started the shoulder dance herself.

Apart from the animated, I've been listening to a lot of Poppy, Electric Callboy, Babymetal and Bloodywood on the metal side of things. And Jade Thirlwell, Sabrina Carpenter, Lisa, Sophie Powers and Lady Gaga on the pop side.

Gigs

Best Gig: Babymetal (with Bambi Thug and Poppy) - this wasn't a gig, it was a show. And the O2 is a great venue.

Also good:

  • Alt Blk Era - so good I actually moshed for the first time in over 30 years
  • Scene Queen - very funny, especially as she tried to explain sororities to a load of emo, non-binary, Brits
  • Slipknot - doing the 25th anniversary tour, like Babymetal, an amazing show

Biggest Disappointment: Electric Callboy - I think Alexandra Palace is a crappy venue.

Family

As I mentioned, I became a granddad in May. I've heard a few people say it's not like having kids, and for me, it really isn't. It's pure joy with (almost) none of the terror. He's also the happiest little baby i've ever met and his mum is doing an amazing job.

I also lost my dad - mentally if not physically. I was having lunch with Jeremy from Brightbox when I got a call from my mum. Dad had collapsed and wasn't moving. He had had a massive stroke and has lost movement in one side, is unable to speak and unable to keep his attention on anything for more than a few seconds. My mum is now living alone for the first time in her life and my dad is in a home with round the clock nursing care. My dad never got to meet his great-grandson until yesterday (he enjoyed it but I"m not sure he knew who the baby or its mother were).

Films

One word, Sinners.

It's like "From Dusk till Dawn" (which I love) but also about the blues and racism. I also love the fact that the vampires (represented by an Irishman) have music too, but the KKK-types do not.

Finally, it's great to see Buddy Guy appear in the film too - my dad took me to see him and Eric Clapton when I was younger and they were both amazing.

I also liked Better Man (but then I love Robbie and almost everything he touches), Predator: Killer of Killers and Wake up Dead Man, as well as the aforementioned K-Pop Demon Hunters. I didn't see One Battle after Another, which a lot of people seemed to like.

TV

Andor. There was a lot I've liked this year - it's been pretty good all round - but Andor was perfect for me. Especially episode 10 where Kleya has to deal with Luthen.

Pluribus was good (it's nice to have a post-apocalyptic show where the message isn't "who are the real monsters?" - although Carol is a bit of a monster).

Slow Horses is still great, Dept Q was a good copy, The Diplomat is still ridiculous (and fun) and Alien Earth was good because it was an Alien thing that wasn't awful (Aliens is one of my all-time favourite films and everything else disappoints). Ignoring the subject matter, I love a single shot tracking scene so was mesmerised by Adolescence (although the hand-wringing reaction was a bit much - where have people been for the last ten years?).

Finally a mention for Big Boys (because no-one else seems to include it) - a sitcom that, on the surface, is about a young man coming to terms with his homosexuality - but actually it's much more about the struggles of his heterosexual "one of the lads" best friends and his difficulties fitting in to the modern world (so I guess a similar theme to Adolescence, just put together in a gentler way).

Work

I rediscovered "Outside In Development" - writing specifications in English, then implementing them by starting at the user-interface, drilling in to the database and then returning the results to the UI. And I've missed it so much - the code I write is simpler, the UI is simpler, everything is just simpler.

I've been using a lot of Claude Code. It blew me away at the start of the year - "now I understand how AI could actually be useful" - and I'm using it more and more. Code Review (which I hate), refactoring, bug fixing, adding in tests which I've forgotten - it saves me a lot of time.

I've also started to move away from Ruby on Rails - for the first time in 20 years of professional development. I'm playing around with some toy projects using Javascript, CapacitorJS, Lit.dev and PouchDB (so an offline-first, sync-capable, mobile application or PWA). Lit is fantastic - it's something I've really missed over the last 20 years of web development - writing a component that can actually interact with the user (and having all the code for that interaction in one place). And PouchDB/CouchDB having automatic syncing is fantastic - the quote is "CouchDB is a database that's shit at everything, except syncing. But syncing is so important you'll love it anyway". This is true.

Football

Nuno Nuno Nuno, we're on the piss with Nuno.

What a fucking season. Yes, we tailed off towards the end, because our squad wasn't big enough and the injuries started to hit. But seventh in the Premier League. Europe again, Ole, Ole.

And this season - what an absolute shitshow.

I knew we would struggle in the league - Chris Wood had totally overachieved and we were no longer the surprise package. But fuck me.

I don't know what happened with Nuno but i wish it hadn't. The Australian was just the wrong choice (never mind his merits as a manager, Forest are a defensive-minded counter-attacking club and have been for 50 years, the A's style just wasn't a good fit). And I'm not 100% convinced by Dyche, although he was exactly what we needed to steady the ship. I still hold out for Glasner; when Nuno left there was a chance of that, but the owner doesn't like to hire managers who are already in a job - and now Glasner will be in high demand.

Still, my initial prediction - stay up and win the Europa League - that would be an incredible result for this season.

Cars

I started the year with my Subaru BRZ. I don't think there's much better - certainly not for sensible amounts of money. It feels analogue, the controls are super-responsive and while you're driving, the car constantly talks to you and tells you what it's feeling. The only things that could make it better were if it delivered power earlier (it's very flat till you get to 3000 revs) and if you could take the roof off (I love a convertible).

However, because of the baby, a 2+2 wasn't really big enough (we couldn't even fit the car seat in the back). So I got an Alfa Giulia Veloce.

I've always complained about modern cars, saying they have no personality. In the olden days of carburettors, you had to learn what your car liked and treat it correctly. As I've said, the BRZ felt analogue - it didn't have a personality as such, but it was expressive and chatty.

Whereas the Alfa is a diva. It definitely has a personality - that personality is total spoilt brat.

  • Reversing into a parking space? Stop making me do these menial tasks.
  • Driving at 20mph in a residential zone? You're so fucking boring
  • Taking corners at 50mph? Just let me run free
  • Holding at 70mph on the motorway? You little bitch, you know you want to go faster

And on to the next one

So that was 2025.

Let's hope 2026 goes a bit better.

]]>