Skip to main content

You do not have to trust the agent to use it

·12 mins

Robert C. Martin, the man most of us know as Uncle Bob, once held that “compilers were for weenies” . He had written assembly for roughly 15 years and knew the machine down to how every flag changed, so he had the standing to say it. Then he adopted C, read the assembly his compiler produced, and was horrified by it. He stayed with C anyway, and used everything he knew about assembly to write programs that corrected what the compiler emitted. He is now telling engineers to run agents the same way. You do not have to trust the generator. You have to own the check.

You have been here before, more than once #

Dismissing a new layer because you are fluent in the one below is older than any of us. “This attitude is extremely old,” Uncle Bob says, and he takes it back to the beginning. In 1951 and 1952, Grace Hopper built the A-0 system for the UNIVAC I, and he says she called the idea automatic programming.

Hopper’s own account of how that landed, quoted by IEEE Spectrum , is that she had “a running compiler, and nobody would touch it,” because “they carefully told me computers could only do arithmetic.”

Uncle Bob watched the same shape repeat at the object-oriented revolution and then the Java revolution, where each time a group felt they were about to be disenfranchised.

That feeling was accurate about the discomfort and wrong about the outcome. Each layer arrived, and the expertise underneath it did not become worthless. It became the thing that let you judge what came out. The programmers who knew the machine best were the ones who could tell whether a compiler had produced something sane. That is the position Uncle Bob found himself in a couple of decades later.

Why this particular reversal is worth something #

If you have ever split a class because it was doing too much, or argued in a review that a module should depend on an interface instead of a concrete type, you have been shaped by Robert C. Martin. He is one of the 17 authors of the Agile Manifesto , he wrote Clean Code, and he articulated the five design principles that Michael Feathers later named SOLID .

That matters here because he got this one wrong in public and said so. He was certain nothing like this was coming, and two years ago he was writing articles arguing the industry had plateaued. He had been making that case since at least 2017, when he wrote “Living on the Plateau” . On the How Many CTOs? podcast, talking with hosts Brad Hefta-Gaub and Scott Porad, he says, “I can’t imagine how wrong I was.” He now teaches Clean AI: Agentic Discipline with Justin Martin, on the position that “the greatest danger of using AI agents is to use an undisciplined approach.”

This is not enthusiasm from someone with a position to defend. It is a man who argued the opposite in print and then changed his mind where everyone could see it.

Two objections, and only one of them is technical #

Two objections came back at him, and only one is about the code. Uncle Bob said he does not read the code anymore, which set off the internet. His answer to the reaction is narrower than the surrender it sounded like.

Some looked at agent output and were horror struck. He says they are right to be, if they are shipping it unchecked. That is a technical objection with evidence behind it. It is roughly where you are standing if you gave an agent a task, read what came back, and would have sent it to the bin in review.

The others, he says, are having an identity crisis. Their identity is writing code. They hear that it no longer matters, and they reject the whole idea.

I do not read that as an insult and I would not use it as one. It is the most honest description of the resistance I have seen written down. If you spent 15 years getting good at something and someone tells you a machine does it now, the thing that arrives is not primarily an engineering judgment. It shows up dressed as one, because that is the language we have for it at work. Naming it does not make it foolish. The engineers who felt disenfranchised by objects and then by Java felt something like it too, and they were not wrong about what their skill was worth.

He read what his compiler produced, and he wrote a tool #

He committed to C before he knew what it would emit, and that order is the whole argument. He read Kernighan and Ritchie , saw that C was an assembly language and a better one, and moved over rapidly. Only then did he look at what the compiler emitted, and he was, in his word, horrified. He was among the most qualified people alive to make that judgment. He would have written it better by hand, and I think he was right.

The tooling of that era could also be outright wrong. The episode turns to compiler bugs and quirks. The same section of C, moved to a different place in the program, could compile into different machine code that did not work. Rare now, he says, and far more common in the 1970s.

If bad output is a reason to back out, that should have ended it for him. It did not. He wrote programs that corrected the assembly coming out of the compiler. A deterministic tool, his phrase for it. He ties it straight to his work today. “Just what I’m doing now,” he says.

The episode raises the objection that matters, which is that a compiler is deterministic and a model is not. I am not going to argue that away. But predictable and correct are not the same thing, and a compiler reproduces its wrong answers as faithfully as its right ones, which is what those 1970s bugs were. Uncle Bob never waited for his to become trustworthy.

Notice what 15 years of assembly turned into. They are what let him read the compiler’s output and know it was bad, and then write the thing that corrected it. The expertise moved up a layer with him.

What you build instead of a better prompt #

His position now is a check the agent has to pass. The tool changed shape between then and now. The old one corrected the compiler’s output for him. The new one refuses the agent’s work until the agent fixes it itself. Both are deterministic, and both are his rather than the generator’s.

The new one is tools it must execute that come back yes or no, encoding what he would otherwise repeat and it would otherwise forget. He names method size, duplication, module size, and the direction dependencies are allowed to point . Repeating rules in a prompt does not work, because an agent complies for about ten minutes and then loses the instruction as its context fills.

His own example is worth stealing outright. He runs a metric called CRAP , invented in 2007, which combines a function’s cyclomatic complexity with its test coverage. At full coverage the score is the complexity alone. With no coverage it skyrockets, on the theory that a complicated untested function is a crappy one. He holds his agents to a ceiling of 6 . They will happily write twelves and twenties and eighty-fours, and the tool tells them no until they write tests and break the function apart.

The number is the point. An agent can grind against 6. It cannot grind against “keep your method small.”

What that looks like if you write Ruby #

Here is what his four checks look like in the stack I run. The shape transfers to any language. The tool names may not.

Most of this already ships in your Gemfile. RuboCop has Metrics cops for method length, class length, and cyclomatic complexity. I keep them off in the Rails codebases where I run agents, because I find arguing method by method is the wrong altitude.

RubyCritic is the better gate. It wraps Reek , Flay , and Flog into one score for the whole codebase, and .rubycritic.yml takes a minimum_score. Mine sits at 95. That is the Ruby answer to his ceiling of 6.

Flay is worth calling out on its own, because he is specific about where agents break. They are good at eliminating duplication and bad at finding it. Flay compares structure instead of text, ignoring names, literals, whitespace, and style, so it catches the copy an agent renamed its way out of. The tool finds, the agent fixes.

His fourth check, the direction dependencies are allowed to point, is the one RuboCop does not ship. You write that one. A custom cop subclasses RuboCop::Cop::Base and loads through the require directive in .rubocop.yml. Mine hold an allowlist of sanctioned top-level app/ directories and refuse a second class defined inline at the bottom of a file. Each cop has its own spec, because the gate is code and code gets tested.

One rule outranks all of them. Exclusion lists only shrink. He warns that agents will change tests that used to work so the next thing passes, and the config version of that is suppressing a smell rather than fixing it. Taking an entry out of the ignore file is free. Putting one in takes a human and a ticket.

If your objection is that you have no time to build any of this, the episode has an answer. “The agents will build those tools and they’ll build them in an hour.”

What this does not fix #

Three things, and Uncle Bob names the first himself. He walks the language progression, C to C++ to Java and on to Ruby and Python, calls that a natural progression, and then says this one is not. So the man whose history I am using is telling you this shift is unlike the ones before it.

He is right, and it does not cost me the argument. I am not claiming this shift is the same size or the same kind as the ones before it. What repeats is not the technology. It is what happens to a person whose skill sits in the layer being automated, and what that person should do next. Hopper’s programmers, the engineers who fought objects, and the engineer standing in front of an agent this morning are doing the same thing with different tools.

The second is that checking generated assembly was tractable in a way checking generated behavior is not. A gate catches the classes of error you thought to encode and is silent on the ones you did not. Agents still do fairly dumb things that can go invisible, he says, and the episode adds that the tools do not catch them.

The third I have no answer for. Uncle Bob knew his compiler was producing bad assembly because he had written assembly for 15 years. If reading the generated layer becomes rare, it is not obvious where the next generation gets that judgment. He calls bringing new people into this industry an interesting problem for the next 5 years, and nobody has solved it.

What I would do Monday morning #

Take one instruction you keep repeating to an agent, the one you have typed into a prompt more times than you want to count, and turn it into a tool it has to pass. Writing it into a rules document will not hold, because that is the thing it forgets. You want a check that returns yes or no and blocks on no. Then stop repeating it, and watch what the agent does.

You do not have to trust the generator to use it. Uncle Bob did not trust his. He moved up a level anyway, and the 15 years he had spent below that level are what let him build the thing that caught what it got wrong. Your years are not what is being taken from you. They are the qualification for the job that is left.

Will AI agents make senior engineers obsolete?

Not in Uncle Bob’s account of it. His position is that “the worst thing you could do would be to insert a human into the syntax loop, because that’s the thing we’re bad at.” He steps back half a step instead, reading the output of his tools, the behavior of the system, and the coverage numbers, and he directs the fixes himself. Expertise in the layer below is what qualifies you to judge the layer above, the way his 15 years of assembly let him see his C compiler was producing bad output.

Has software been through an AI-like transition before?

Repeatedly. Uncle Bob points to the arrival of compilers in the 1950s, the object-oriented revolution, and the move to Java. Each time a group of engineers felt the skill they had built was about to be devalued. He says they were wrong about that, though he grants that is how it felt.

Is AI-generated code different from compiler output?

It is less predictable. A fixed compiler version on a fixed platform reproduces its output, including its mistakes, and a model does not. But predictable is not the same as correct. Uncle Bob’s answer to compiler output he did not trust was to write a deterministic tool that corrected it rather than to wait for the generator to improve.

What should I put in place before letting agents write production code?

A check you own that returns yes or no on the properties you care about, and blocks on no. Tests, quality gates, dependency-direction rules, and complexity ceilings all qualify. Rules written in a prompt do not, because an agent loses them as its context fills.

Practical AI #

I co-contribute to Practical AI , Damian Galarza’s newsletter for builders working with AI.

It covers the patterns, tradeoffs, and lessons that show up when AI moves from prototype to real work.