Hire Me

The software I could never justify building — and why LLMs changed that

Mickey · · 11 min read

I recently made the mistake of asking GitHub and GitLab what I had worked on during the previous twelve months.

The answer was ridiculous: sixty GitHub repositories with my commits, forty-two GitLab project IDs with activity, and enough overlap, mirrors, experiments, libraries and product work to make the raw list almost meaningless. Some of it was paid work. Some of it maintained software I would have maintained anyway. Neither category says much about artificial intelligence.

The interesting part was the remainder: software that made no economic sense at all.

A command that tells me which process is making a sound. A proper lsusb for macOS. A CAN frame generator that notices when the controller is lying. A bridge that gives the iOS Simulator Bluetooth, cameras and NFC hardware. An Amiga-style tracker with a real-time playback engine. A userspace driver stack for music hardware that its manufacturer has long since moved on from. A family chat that does not need a server. A weather app with no ads, no account and no business plan.

These are not startup ideas. Most will never earn enough to pay for the signing certificates, never mind the time. I built them anyway — and, more importantly, I built them to a point where somebody else can use them.

Without LLMs, I probably would not have.

The economics, not the intelligence

The usual argument about AI and programming is about speed: how many lines it can write, how much faster a ticket closes, whether one developer can now do the work of two. I understand why companies care about that. It is also the least interesting part of the change for me.

I have never had a shortage of ideas, and the interesting kernel of a small tool is rarely what kills it. The first version of lsaudio can ask CoreAudio for its process objects and print a table. The first version of mcangen can open a SocketCAN socket and send frames. The first version of a camera bridge can move a buffer through a Unix domain socket.

Then comes the other 90 percent: error handling, cancellation, reconnects, tests, a second platform, a native interface, accessibility, documentation, examples, packaging, Homebrew, signing, notarisation, screenshots, release automation and all the small paths only another person will take.

For commercial software, that work can be weighed against revenue. For a personal tool whose total addressable market may be twelve people with the same USB adapter, the rational decision is not to begin. The opportunity cost is too high. The idea stays in a notes file, or survives as a brittle script that works on one machine.

LLMs changed that calculation. They did not make the work disappear. They made the long, cross-disciplinary tail cheap enough that finishing became reasonable.

That is why I am excited.

Private crutches can become public tools

The cleanest examples are lsaudio and lsusd, which I wrote about in “Two questions macOS should answer”.

Both began as annoyances. What process is playing audio? What serial device did I just plug in? macOS knows the answers, but makes me dig through Activity Monitor, System Information, ioreg and shell pipelines to find them.

Without AI, I would have solved each irritation once. There would have been a script in ~/bin, a few hard-coded assumptions and no reason to show it to anybody. Instead, both became real command-line tools. They handle awkward system cases, produce machine-readable output, have tests and man pages, install through Homebrew and now have native menu-bar companions built on the same core libraries.

The idea and the platform knowledge were mine. So were the product decisions: which process is the source of truth, when killing it is appropriate, what belongs in the menu-bar panel and what belongs in the full window. The LLM supplied persistence at the parts where my enthusiasm would normally have evaporated. It kept moving through Linux compatibility, stale devices, authorisation, package metadata and documentation while I kept deciding whether the result was correct.

That distinction matters. AI did not invent a need. It let a need survive contact with everything required to make it useful.

Small tools now pass that threshold all the time. Soxmate makes SoX friendlier. swift-repl launches a Swift REPL with modules from local packages. canconf turns a pile of SocketCAN incantations into configuration, health monitoring and an interactive ECU terminal. None deserves a quarterly roadmap. All deserve to exist.

Hardware powers for a simulator

The more dramatic example is Simsalabim.

The iOS Simulator has no useful Bluetooth radio, camera or NFC reader, and a fresh simulator has none of the personal data that makes a real phone look lived-in. I wanted simulated apps to keep using CoreBluetooth, AVFoundation, Core NFC, Contacts and EventKit while a Mac-side provider supplied the missing world.

That became four projects:

Simsalabim brings them together in one menu-bar suite. A shared package handles sockets, provider ownership and the shell around them.

No sensible solo developer looks at that list — API interception, binary streaming, Apple framework emulation, USB readers, menu-bar UI, test fixtures, packaging and documentation — and says, “Yes, this is a small unpaid side project.” Each individual bridge is plausible. Turning four of them into a coherent product family is where the arithmetic stops working.

LLMs made the breadth possible. I could stay at the architectural boundary — the application must keep using the honest Apple API; fixtures configure the world outside it — while delegating enough scaffolding, protocol glue, repetitive UI and test construction to keep momentum. I still had to test the hardware, spot bad abstractions and reject implementations that were merely plausible. But I no longer had to personally type every unsurprising line between one hard problem and the next.

The bench became conversational

My automotive bench produced another cluster of economically unreasonable tools: mcangen, mcandump, canconf, a J2534 bridge and a Windows DLL tester called Jester.

They exist because the standard tools were always almost right for the question in front of me. I wanted a generator that could produce a deterministic million-frame run, survive BUS-OFF, and imitate the traffic shape of an ECU flash. I wanted a logger that could verify sequence integrity and remain pleasant during a long interactive session. I wanted old 32-bit PassThru DLLs isolated from the process using them. Each wish is useful; none is a business.

The loop closed with mcanbus and socketcan-mcp. Instead of making an agent drive candump and parse terminal text, I gave it typed SocketCAN and ISO-TP tools. I then watched it read a VIN from a real Scania ECU from inside a chat, first by performing ISO-TP flow control itself and then through a purpose-built primitive. That changed the texture of bench work for me: the model was no longer autocomplete attached to an editor, but a colleague inside the investigative loop.

The amusing recursion is that AI helped me build tools that make AI better at helping me. Every structured boundary replaces a page of terminal transcript with a typed result. The model spends less context on plumbing and more on the actual diagnostic question. Better tools make the agent sharper; the sharper agent makes the next tool cheaper to build.

Hobbies can be technically unreasonable again

The projects that delight me most are the ones nobody can defend in a planning meeting.

Paulinche is a native macOS Amiga-style tracker with real-time MIDI recording, sample and wavetable instruments, a playback engine, classic OCS export and cycle-budget tracing against the original hardware.

Paulinche OCS showcase running in FS-UAE, with a pixel-art cornucopia and Boing Ball above four-channel playback telemetry
Not merely Amiga-inspired: Paulinche exports a real OCS player. This FS-UAE capture belongs to its visual-parity and raster-budget test workflow.

CompleteControl is a Swift/libusb stack for discontinued Native Instruments keyboards and controllers: privileged daemon, displays, LEDs, encoders, MIDI, simulators, the lot. It also gives Paulinche a physical control surface rather more ambitious than the project has any economic right to possess.

Paulinche's pattern editor and cell editor running on the two displays of a Native Instruments Komplete Kontrol keyboard
Paulinche on the real hardware: its pattern and cell editors rendered on the two Komplete Kontrol displays through CompleteControl.

A separate hardware lab grew from reverse engineering an unsuitable controller into a C++20 Stream Deck driver, renderer and simulator. CompleteControl got its own virtual keyboard as well, so the whole surface can be exercised without occupying the instrument.

CompleteControl simulator showing a full virtual keyboard, buttons, encoders and Paulinche's dual-display tracker interface
The hardware-free CompleteControl simulator exercises the same screens, encoders, buttons, lights and key events as the physical surface.

These projects are full of the kind of work that traditionally makes a hobby collapse under its own ambition. Audio, USB, real-time constraints, pixel formats, old hardware, modern Swift concurrency, C and C++, app UI, daemons and hardware-free test rigs all meet in the same repository tree. The expected revenue is approximately irrelevant. The personal value is enormous.

The same is true of the revived Retroplayer ecosystem, a Space Taxi-inspired game, a SID studio and a blink(1) LED that somehow became a library, CLI and menu-bar app with an audio VU meter and an on-air mode. I am not building a company around any of them. I am learning, preserving old machines, making tools I want and enjoying the craft.

Before LLMs, I rationed that kind of ambition. I chose one stack, one platform and one hard problem, because every additional surface multiplied the maintenance. Now I can follow the shape of the idea further before the carrying cost wins.

That feels less like automation and more like permission.

Software no longer needs a business case

Some projects are valuable because their audience is exactly one family.

I built a private peer-to-peer travel chat where every device, including the watch, is a proper member. I split my recipe collection into a real app with iCloud, a watch shopping list, multiple stores and suggestions learned from what we actually buy. I started writing and illustrating books with automated typesetting. I finally built the weather app I had resisted for twenty years and made it free, without accounts, advertising or tracking.

None of those ideas needed a pitch deck. What they needed was for implementation cost to stop dominating personal value.

This may be the most important change LLMs bring to software: more software can be local, specific, strange and humane. It can fit one workshop, one instrument, one family or one person’s way of thinking. It does not have to grow until venture capital can see it. It does not even have to become a subscription.

The web once did something similar for publishing. Suddenly a text did not need a printing press and a viable print run to deserve an audience. LLM-assisted development lowers a comparable threshold for software. Not to zero — software still has consequences — but low enough that many more ideas can cross it.

What the model does not provide

Enthusiasm is not abdication.

An LLM does not know which product should exist. It does not own the taste that says a failed station change should leave the old music playing, or that a simulator bridge must preserve the production framework API. It does not feel a CAN controller go bus-off, hear an audio click, notice that a menu-bar panel is awkward, or understand why a particular shortcut matters after twenty years with trackers.

It can also be confidently wrong. It invents APIs, chooses abstractions that compile but do not belong, misses real-time constraints and declares victory at the edge of the simulator. Hardware has been an excellent antidote to AI credulity: either the LED lights, the frame reaches the wire and the audio plays, or it does not.

My job has moved upward and outward. I spend more time defining contracts, choosing boundaries, constructing tests, reviewing behaviour and deciding what “finished” means. I spend less time writing boilerplate I already understand or translating the same model through a fifth layer by hand. That is not less engineering. It is a different allocation of engineering attention.

And responsibility does not move at all. I may accept a patch from a model, but I still ship it under my name.

The measure that matters

I do not know how to turn the repository inventory into a productivity percentage. Commit counts are a terrible unit of thought, generated lines are worse, and “hours saved” assumes the alternative was doing the same project more slowly.

Often the real alternative was not doing it.

That is the counterfactual hidden by every benchmark. lsaudio would have remained a private command. Simsalabim would have been one Bluetooth proof of concept. The CAN tools would have stayed shell fragments. The Amiga tracker, hardware drivers and family software would still be notes headed “someday”.

The output I care about is not more code. It is more ideas allowed to exist — especially the useful, joyful, economically irrational ones.

That is why I am so excited about LLMs.