---
title: "First Make It Work"
date: "2026-04-10T16:30:12+01:00"
url: "https://invis.net/first-make-it-work/"
author: "invisnet"
license: "CC BY-ND 4.0"
license_url: "https://creativecommons.org/licenses/by-nd/4.0/"
site: "invis.net"
copyright: "Copyright 2024-2026 Charles Lecklider. All rights reserved."
disclaimer: "Personal website. Opinions are my own."
categories:
  - "Article"
tags:
---

# First Make It Work

If I ever get a sign made for my office, that's what it would say.

The principle is older than AI, older than agile, older than most of the people currently explaining AI to me on LinkedIn. I learned it as a sysadmin in the nineties. In those days servers were physical, telnet was typical, a single ping made screens blue, and you had to fax South Africa to get an SSL cert. It was hard enough to make something *work*. If you simultaneously tried to make it secure, you achieved neither.

It turns out this applies to AI agents almost exactly. But I didn't start there.

I started with "prompt engineering". We all want working, idiomatic, well-commented code, so that's what we tell the agent we want. We craft detailed specs that include all the code quality requirements — naming conventions, error handling, idiomatic style, comprehensive comments, architecture guidelines, testing guidelines, formatting guidelines. When the results disappoint, we refine the prompt. We add examples. We restructure the instructions. We give talks to each other about how best to prompt the agent. How to run multiple agents. How to *loop* multiple agents. And we call that "engineering".

At some point we stopped trying to understand how to get the best out of the tool and started treating it like a foreigner — speaking ever louder and slower.

The agent understood you the first time.

It tried to produce what we wanted, but the idiomatic pressure pushed it into patterns that merely *looked* right, the narrative pressure produced *justifications* for questionable decisions, and the difficult parts got swept under a carpet of passing tests.

It produced bad code because we demanded that it not only did *what* we wanted, but did it *how* we wanted.

## One Step at a Time

So I went back to what I knew.

1. **Make it work.** The tests pass. The output is correct. Nothing else matters. Not naming conventions, not code style, not comments, not elegance, not architecture — nothing. The sole criterion is: does it do what the spec says?
The agent produces *working* code.

2. **Make it idiomatic.** Clean it up. Follow the language's conventions. Fix naming. Improve error handling. Restructure — *add* structure. But don't break anything — the tests from pass one are the spec.
Now the agent produces *idiomatic* working code.

3. **Comment it.** Every exported function, every non-obvious decision, every place where a future reader might ask "why?" — answer it. Add your house style.
Now the agent produces *commented* idiomatic working code.

And none of this is one-shot. Breaking the work into passes makes it easier to steer, but each pass is still iterative. Early on, it takes a few rounds for the spec and the code to line up. Then you get better at both: better at stating what “working” means, and better at getting each pass there. You stop speaking louder and slower and learn the language.

## Nowhere to Hide

The spec matters more than the prompt. If your success criterion is "produce good code," the agent will produce something that *looks like* good code. If your success criterion is "these specific inputs produce these specific outputs," the agent will either succeed or visibly fail. There is no partial credit. There is nowhere to hide.

But — and this is where I'll lose some people — that means *fewer* tests in pass one, not more. It especially doesn't mean TDD, where every internal function has its own test and every test encodes assumptions about the shape of the code.

Every unit test adds mass. More mass needs a bigger impulse for the same change in direction. The less inertia you carry into pass two, the better.

Pass one gets only black-box tests. Given this input, expect this output. The implementation is a black box. When pass two arrives, the tests *are* the spec; you only need to refactor the implementation. And if the new structure needs internal tests, this is when you write them — against code that now has the right shape.

First make it work. The rest follows.

---
Copyright 2024-2026 Charles Lecklider. All rights reserved.
Personal website. Opinions are my own.
