Clean code was never about speed. It was about discipline nobody could keep.
Build it now, fix it later stopped being the expensive trade, because a machine has no Friday. The catch is that it enforces whatever rules you hand it, perfectly, without ever asking whether a rule is a good one - and one famous rule costs twelve years of hardware progress.

“Build it now, fix it later.” Every engineer has said it. Very few have done the second half.
The trade was always real. Ship fast and carry the debt, or build it properly and lose the window. And the debt mostly went unpaid, because paying it meant going back into working code with nothing new to show at the end of the sprint. So “later” became a place things were sent to disappear.
That trade has changed, and not because anyone got more disciplined.
Machines write most of the code now, and a machine has no Friday. It does not get tired at hour nine, does not decide this file is an exception, does not skip the docstring because the ticket is already late. Hand it a rule and it applies the rule in the first file and in the four hundredth, at the same standard. Building it right from the beginning stopped being the expensive option.
There is a catch, and it is the entire subject of this article. The machine will enforce whatever rules you give it, perfectly, without ever asking whether a rule is a good one. So the value has moved from following rules to knowing which rules to hand over. That requires a system, and the system has to be right about what it enforces.
Here is a rule almost all of us absorbed without testing: replace the branch with polymorphism. Do not write a switch on the shape type; give every shape an Area() method and let the language dispatch. It is in the book, in the interview question, in the review comment you left last week. It is also exactly the kind of rule an agent will apply everywhere without being asked twice.
In February 2023 Casey Muratori took that example, from that literature, and measured it.
summing the area of an array of shapes cycles per shape
polymorphic hierarchy, the "clean" version ~35
switch over a flat struct ~24 1.5x faster
table-driven dispatch 3.0-3.5 10x faster

Ten times. His framing lands harder than the number: a 10x speedup is roughly the distance between an average CPU today and an average CPU from 2010. Apply the rule in a hot loop and you hand back twelve years of hardware progress. Add one more property to the shapes and the table version pulls to about 15x, which is fourteen years.
So the rule is not free, and it is not free in a way that compounds: it is now being applied by something that never gets tired of applying it.
The processor is not slow at arithmetic
The instinct behind the rule was formed when computation was the bottleneck. An extra indirection was a rounding error next to the work being done.
That world is gone. A modern core is starved, not busy: main memory sits hundreds of cycles away, and a cache miss costs more than most of the arithmetic in your function. The bottleneck moved from computing to fetching, and every textbook that treats indirection as free is quietly billing the hardware.
Two things break at once when you put objects behind an interface.

The data scatters. An array of pointers to polymorphic objects is not an array of anything the prefetcher understands. It cannot guess where the next object lives, so it stops helping. Each object also carries a hidden pointer to its method table, which inflates it and pushes real data out of the cache line you did fetch.
And the compiler goes blind. This is the larger cost and the less obvious one. It cannot see the body of a virtual call, so it cannot inline it, and everything downstream of inlining disappears with it: constant folding, unrolling, strength reduction.
A benchmark by Jim Preiss makes the mechanism visible rather than just the penalty:
plain loop with arithmetic the compiler can simplify
concrete call 2.85 2.86
virtual call 4.98 11.52
Read the rows, not the columns. The concrete version did not get slower when the work got harder, because the compiler replaced a division with a shift. The virtual version doubled, because the compiler could not see what it was optimising. The gap widens from 1.7x to 4x, and none of that is the cost of the call itself. It is the cost of the optimisations that never happened.
The academic side agrees. Wingqvist, Wickström and Memeti, at IEEE GEM 2022, rebuilt the same game application twice, once object-oriented and once data-oriented, and measured up to 13.25x in favour of laying data out contiguously instead of wrapping it in objects.
So why keep the rules at all
Here is where most articles on this topic stop, having concluded that clean code is a tax. That conclusion is as unexamined as the rule it attacks.
These rules were never sold as a performance technique. They are a legibility technique. The promise was that a stranger, or you in two years, could open the file and change it without breaking three other things. Nothing in the measurements above touches that promise, and Muratori is not claiming otherwise.
The real problem with the rules was never that they were wrong. It was that under deadline they were unenforceable. The naming convention holds for six months, then someone in a hurry breaks it, and nobody wants to be the person blocking a hotfix over a variable name. Discipline decayed exactly where pressure was highest, which is exactly where the code mattered most. Forty years of methodology has largely been an attempt to fix that with willpower.
That part is genuinely solved now, and it is worth being precise about what changed: not the rules, and not human nature. Only who executes them.
And that is where it gets dangerous
An agent learned to write from books, from public repositories, from every accepted answer where the polymorphic version won the upvotes. So it writes clean code by default. Small functions, interfaces, hidden internals, no switch on type.
And it does not measure. It has never run a profiler on its own output, never compared two memory layouts, never looked at a cache-miss counter. It reproduces the shape its training data marked as correct, at machine speed, in every file.
So the thing Muratori described has not aged away in three years. It scaled. A rule that a human applied inconsistently, and occasionally stopped to question, is now applied perfectly and instantly across an entire codebase by something that cannot tell the difference between a rule that helps and a rule that costs twelve years of hardware.
The system enforcing your standards is only as good as the standards. Consistency was the hard problem and it is now cheap; correctness of the rule itself was the easy problem and it just became the only one that matters.
This is the same failure that runs through everything else we write about: a confident answer and a correct answer look identical until somebody measures. It just happens to be code here instead of data.
Which brings up who reads it next
There is a second habit worth breaking, and it is more consequential than dispatch.
A great deal of code is now written with an assistant, shipped, and never documented, because documentation feels like a courtesy to a colleague who may never arrive. Two years later something breaks, and the question is who works out why.
It will not be a person reading it fresh. It will be an agent. It will read the repository, and what it finds is what it has to work with: the code, whatever comments survived, whatever decisions were written down, and nothing else. Every “why” that lived only in someone’s head at the time is gone.
That reframes documentation completely. It is no longer a nicety for a hypothetical future colleague. It is the input to the system that will maintain this. A roadmap, an architecture note, a paragraph explaining why the ugly branch in the hot path is ugly on purpose, are not politeness. They are the difference between an agent that fixes the bug and an agent that helpfully refactors your hand-tuned loop back into a clean polymorphic hierarchy, because nothing in the repository told it not to.
Write the comment that says: this is deliberate, it was measured, here is the number, do not tidy it.
What we actually do
Two honest admissions first, because a piece like this reads as a sermon otherwise.
We did not build our platform to a standard of cleanliness anyone would hold up as an example. Chasing that, we would not have finished, and on some things we would not have started. There are modules written to a deadline, decisions made because a requirement said so rather than because the shape was right, and corners that exist because shipping mattered more that week.
And we run almost nothing that is genuinely CPU-bound. Our load is dominated by network calls and data movement, so the twelve years of hardware in Muratori’s benchmark are, for most of our code, twelve years we were never going to use.
What we do run is the discipline. Every change is claimed on a board, built in an isolated branch, reviewed by agents from competing vendors chosen because they were trained differently and fail differently, and nothing merges with an open finding. We wrote about that machinery in the piece on the Dev Factory, and this is the same argument from the other end: the rules that people could not hold consistently are held by the system instead.
The practical version, for anyone deciding what to do on Monday:

Split by latency requirement, not by taste. Ordinary business logic: keep the rules, religiously, because the constraint there is comprehension and not cycles. The hot path your profiler actually found: different laws apply, and data layout beats elegance.
Do not swap polymorphism for switch on reflex. Much of the penalty is recoverable by the compiler when you help it, through final, link-time optimisation, CRTP, variant. Reach for those before rewriting an architecture.
Profile before you optimise, and read the right numbers. perf stat and the tail of the distribution, not the average. Averages hide the case your users complain about.
Tell the next reader why. Especially when the next reader is a machine that will otherwise clean up after you, in the worst sense.
Design like a cathedral, optimise like a mechanic in a scrapyard, and know which room you are standing in. That last part is the engineering.
Sources, all figures verified against the originals: Casey Muratori, “Clean” Code, Horrible Performance, February 2023. The Muratori and Martin discussion, which is a better argument than most things written about it since. Jim Preiss, Virtual functions: still slow, 2015. Wingqvist, Wickström and Memeti, Evaluating the performance of object-oriented and data-oriented design with multi-threading in game development, IEEE GEM 2022.
