AI, interface quality, and the cost of errors
2025-08-22
I've come up with a consulting-style matrix for knowing whether or not I want to use AI on a piece of a software system.
Interface Quality
┌───────────────┬───────────────┐
Cost of errors │ Good │ Bad │
┌─────────────────┼───────────────┼───────────────┤
│ High │ Write by │ Use AI, │
│ │ hand │ verify │
├─────────────────┼───────────────┼───────────────┤
│ Low │ Who cares │ Vibe code │
│ │ │ │
└─────────────────┴───────────────┴───────────────┘
The two axes are interface quality, i.e., how effective an interface is at letting a human work the component underneath it, and cost of errors, i.e., how bad it would be to have errors in the underlying component.
Good interface, high cost of errors
The database is at the heart of the applications I write. The back end logic that manages access to said database is only slightly behind the database in terms of importance. It's important that the back end is correct.
I've recently been learning Clojure, which has been a joy to work with. I think it's a wonderful interface for working with data, which is primarily what back end development is about. I have no trouble writing Clojure with my own hands, which is good, because I get to pay attention to the overall design of the back end.
Bad interface, high cost of errors
I love relational database management systems. I do not love SQL. SQL is a bad interface to relational databases, but it's the only real interface relational databases have.
In my recent projects, I've been using AI to write SQL, which makes me several times faster at writing features than I would otherwise be. However, you're hosed if you get SQL wrong, so I've had to take great care to verify that the SQL produced by AI is correct.
This SQL is not at all vibe coded, because the term "vibe coding" implies a certain disregard for the resultant artifact. I absolutely care about the integrity of the stuff that touches my database.
I find this category particularly interesting because it means that I can eliminate things such as ORMs from my stack. The only reason I would use an ORM in the past is because it felt like a nicer interface than raw SQL, so much so that it was sometimes worth the cost. Now that I can "write" SQL with AI, I no longer need to use ORMs.
Good interface, low cost of errors
I'm less opinionated about this category than the others, since I'm not actually confident about what fits here. Maybe using Ruby to build personal shell scripts? If the interface is good/enjoyable and if the underlying task isn't that dangerous to get wrong, I shouldn't really care about how you do it. And I don't.
Bad interface, low cost of errors
Most of my projects need some sort of user interface. Unfortunately for me, to make UIs, I need to use some technologies I dislike. I rejected JavaScript years ago, mostly due to the culture of its ecosystem. I also find HTML and CSS miserable to work with, even with preprocessors.
Until the rise of LLMs, I thought I'd have to constrain myself to the back end. This made it pretty difficult to write full projects. Now that we have LLMs, as long as I write a high-quality set of JSON endpoints, I can throw out a terrible AI front end in a mere two minutes. It won't matter at all if it's bad. I can just re-create it if it's bad. What's the cost of getting a user interface wrong in the early days of a prototype? Near zero.
This is vibe coding. I have zero attachment to the artifacts produced by this process.