↓Skip to main content

You do not have to trust the agent to use it

·11 mins

Robert C. Martin, the man many 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 felt 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.

This has happened 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 traces it back to the 1950s. 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 least twice more, at the object-oriented revolution and then the Java revolution. Each time, a group of engineers felt they were about to be disenfranchised.

That feeling was accurate about the discomfort. Uncle Bob says it was 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 after Hopper.

Why this particular reversal is worth something #

Robert C. Martin has spent decades teaching programmers how to structure code. He is one of the 17 authors of the Agile Manifesto , and he wrote Clean Code.

That matters here because he got this one wrong in public and said so. He did not see it coming. He says he was still writing articles arguing the industry had plateaued as recently as two years ago, and 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 #

Uncle Bob 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 own word, horrified. His 15 years of assembly are what let him 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 compiler to become trustworthy.

Notice what 15 years of assembly turned into. Those years 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 tools are deterministic, and both are his rather than the generator’s.

The new one is a set of checks the agent has to run, each coming back yes or no. They encode what he would otherwise repeat and the agent 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, on dependency direction, 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. My cops 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 resisted 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 check it has to pass. Writing it into a rules document will not hold, because that is the thing it forgets. You want one 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.