I’ve been messing around with computers for long enough that I can remember when getting a Windows game running on Linux felt like some sort of dark art.

I started playing with Ubuntu 4.10 not long after it appeared in 2004. Linux felt completely open in a way I hadn’t experienced before. You could use GNOME, KDE, replace half the operating system because you didn’t like it, and a few years later set your desktop on fire with Compiz Fusion for absolutely no good reason.

It was brilliant. It was also frequently an absolute pain in the arse.

Wine was already incredibly capable, but getting games running usually involved a lot of configuration and hoping reality agreed with you. Today we’ve got Proton, Bottles and a Linux gaming ecosystem that would have seemed ridiculous back then.

There was just something deeply satisfying about being told something probably wouldn’t work and then making it work anyway.

The rabbit hole properly started with World of Warcraft

Around 2005 I got completely obsessed with World of Warcraft, which led me into the private-server community around MaNGOS and the Unified Database project.

I started by messing with the database and submitting the occasional patch. That turned into C++, Lua, scripting encounters and running more things on Linux servers.

That was probably the point where computers stopped being something I enjoyed using and became something I wanted to understand.

Once you start asking why something works rather than just how to use it, you’re basically fucked. There’s no way back.

When programming became the job

At Fairfax School I originally worked mostly on systems and infrastructure: networks, servers, hardware, Windows, Linux, whatever needed doing.

Programming gradually became a bigger part of the role. I built internal web systems and desktop applications, including DigitalSignage and ProxySwitch.

When the MacBook Air came out, work bought me one specifically so I could do development wherever I happened to be. I remember thinking: oh shit, this is actually my job.

The code from that period is now old enough that looking at some of it causes physical discomfort, but people used those systems. ProxySwitch ended up in several local schools.

Software became real when somebody depended on something I had written.

Good tooling should remove friction

A lot of my career since then has involved getting annoyed at unnecessarily painful ways of working and eventually replacing them.

Just seeing the words Subversion or SVN still creates an immediate response in my brain:

Fuck this. We’re changing this.

I helped move development from FTP/SVN to Git at both Tomorrow People and later Instiller. That same instinct eventually led into CI, automation and infrastructure as code.

At Instiller I introduced Ansible into the infrastructure we run ourselves. Describing our bare-metal Rocky Linux systems in code beats relying on somebody remembering which magic SSH commands they ran three years ago.

Find repetitive or fragile work. Automate it. Keep the interesting bit.

The interesting bit is still solving the problem

The part of programming I love most hasn’t changed. It’s getting stuck on something for hours, reading the same code twenty times and twice convincing yourself you’ve fixed it before discovering that, no, apparently computers just hate you personally.

Then one thing clicks and suddenly you can see the way forward.

I’ve got ADHD, which probably helps explain why I can disappear into that kind of problem once I get the first bit of progress.

It’s probably also why I got so addicted to Brazilian jiu-jitsu. BJJ is basically problem solving except the problem is another human being who keeps changing the problem while you’re trying to solve it.

At least a compiler has the decency to keep the rules the same. Usually.

I do make a distinction between programming and engineering. Programming is sitting down and writing the thing. Engineering is pulling the camera back: how does it integrate, what does it break, what happens in production, and who has to maintain it later?

Most interesting work involves both, but if you gave me a choice between spending an afternoon talking about software and spending one actually writing some, I’m probably opening Zed.

AI has to earn trust

It took me a while to trust AI tools. I’m like that with anything I bring into the way I work: I want to know where it fails before I depend on it.

I use AI heavily now. OpenCode is my main coding harness, I’ve built tooling around agent workflows, and I use models for investigation, planning, implementation, review and learning.

I also still use Zed with the AI functionality turned off.

Sometimes I want an agent to do the work. Sometimes I know exactly what I want to write and I want to write it myself.

When I do use an agent, I treat it more like another developer than an autocomplete box. We inspect the system, argue about the approach, work out what we’re likely to break, then build it.

What I don’t like is handing an agent a paragraph, disappearing for an hour and treating whatever comes back as finished software.

It might work. It might even pass the tests. But I still want to know what the fuck it did.

That’s also why I’m sceptical of blanket claims that AI makes every developer two, three or ten times more productive. Sometimes it saves me hours. Other times I could have written the thing faster than I can prove its version is safe.

Caduceus is probably where that thinking ended up

Caduceus probably best represents how I think about AI development at the moment.

I can label a GitHub issue and have an AI worker investigate the codebase, implement the change and produce a pull request. The interesting part to me is everything around the agent.

The Caduceus emblem: a winged staff with two snakes, circuit diagrams, and the Caduceus wordmark.
Caduceus, a Unix daemon and Hermes plugin.

Caduceus itself is a Rust daemon. It handles the boring deterministic bits: polling GitHub, claiming work, creating isolated worktrees, tracking state, enforcing timeouts, dealing with Git and producing the pull request.

The model gets the messy bit: understand the code and work out what to change.

That boundary is deliberate. I’m happy letting a model reason about software. I’m much less interested in asking one whether a lock exists or a process timed out when normal code can answer that perfectly well.

Barkley Assistant does a lot of the implementation work on Caduceus itself, which makes the whole thing slightly recursive. I still architect the system, read what it produces and send it back when it’s wrong.

It’s working very well at the moment, but I’m still sceptical about how it holds up long term. There will be mistakes I miss. The workflow should make those easier to catch, but it isn’t magic.

Open is still my default

I’ve always leaned heavily towards open source, so I tend to prefer open-weight models when they’re good enough for the job.

That doesn’t mean software should be free. People need to get paid and companies need to make money. I just like there being an option I can inspect, run and modify myself.

It’s one of the reasons Linux appealed to me in the first place and one of the reasons open models interest me now. I want there to be a choice.

Maybe twenty years of Linux has poisoned my brain. I’m fine with that.

The bit that worries me

What worries me about AI development is that we’ve massively lowered the barrier to producing code without lowering the barrier to understanding software.

Express.js is a good example of the underlying problem, even though AI wasn’t involved. Socket documented what happened: a Git and GitHub tutorial used the real Express repository to demonstrate opening a pull request, and viewers followed along until hundreds of meaningless PRs landed in the project.

The barrier to participating had dropped below the level of understanding needed to realise what that participation was doing.

AI lowers it further. You don’t need to understand Git, know the codebase or even be able to write the change yourself before you can produce something that looks plausible.

That’s fantastic for learning. It’s less fantastic when the output becomes somebody else’s maintenance or security problem.

The same applies to products. If a vibe-coded application handles real customer data, somebody still needs to understand the authentication, permissions, tenancy boundaries, backups and failure modes.

The problem isn’t that AI wrote the code. The problem is when nobody involved understands whether the code is correct.

AI is also one of the best learning tools I’ve ever had. I’ve used it constantly while learning Rust, especially for asking why something works or what assumption I’ve dragged across from another language.

That’s the split for me. AI can make somebody who understands what they’re doing much more capable. It can also let somebody who doesn’t understand what they’re doing produce a frightening amount of plausible software.

I still like writing the code

The tools are almost unrecognisable compared with the ones I was using when I was changing World of Warcraft database records, scripting boss fights and fighting with Linux games. AI is obviously going to change them again.

That’s fine. I’ve spent most of my career adopting tools that remove repetitive work and make the process better.

I just don’t particularly want to automate away the bit that made me obsessed with this stuff in the first place.

I still like understanding how things work. I still like getting stuck on a problem and eventually cracking it. I still like seeing something go from an idea to a thing that actually works.

After all these years, I still really like writing the code.