A long time ago, in a web stack far, far away...
Around 2005, the IT landscape looked completely different.
At least in the web services world I was working in, we were all something we call these days "full-stack" developers. Working on a web service meant messing with Apache or nginx configurations, tuning Perl or PHP, writing the backend code, and then jumping straight into frontend JavaScript. Sure, some people specialized more than others, but generally speaking, we did everything. And in some ways, it was easier back then.
The software ecosystem was considerably smaller. There were fewer dependencies, frameworks, layers of abstraction and so on. I could actually understand a substantial part of the stack I was working with - from the very bottom (including motherboard and RAM type and I was a person who assembled that manually) to the very top Javascript functions on the browser side. One nice consequence was that we had a pretty good understanding of what our code was doing and where the performance costs were hiding - including hardware.
Of course, performance was a complicated subject even then. On the backend, the costs of database queries, disk access, network communication, interpreter execution and script compilation could easily outweigh small code-level optimizations. There was no point optimizing a few instructions while ignoring a slow database query. And don't get me even started on the possible hardware optimizations. And Facebook's HipHop for PHP, which would later transform PHP into optimized C++ and compile it into native code, was still five years away, so we were mostly in a dynamic-typed language world.
But on the JavaScript side - oh man! Browsers weren't very fast (haha, much faster then now!), and even relatively simple operations could become expensive (JS V8 engine was also years away!). Every unnecessary DOM update was something worth thinking about. Really! I specifically remember working on one of our first approaches to asynchronous communication back in... 2005!
I didn't reinvent the wheel. I basically stole the idea from Gmail, which had just introduced this rather impressive experience of updating parts of the interface without reloading the entire page. Remember how revolutionary that felt?
Today we'd call it AJAX. My implementation used a simple FIFO (first-in, first-out) JavaScript queue. Requests were added to a list by one function, while another function continuously processed them:
var req = [];
function queueRequest(url) {
req.push(url);
}
function processNext() {
if (!req.length) return;
var url = req.shift(); // FIFO
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
// Process the response
processNext();
}
};
xhr.open("GET", url, true);
xhr.send(null);
}Nothing particularly sophisticated by today's standards, right? Just an array with two functions and XMLHttpRequest. no async/await, frameworks, promises etc. But it was enough and it worked.
You might ask "Hey, why didn't you just use a JavaScript library?" Well, because it was 2005! There were already some early libraries and frameworks, such as Prototype and script.aculo.us. But the ecosystem was young and very fragmented, and nowhere near what we're used to today. jQuery wouldn't arrive until 2006 😄
I remember that our own codebase didn't import a single external JavaScript or CSS library. Everything was written by us, maintained by us, and, at least at some point, tested for performance by us.
BUT.
It wasn't for free - the true cost was our time. We weren't always writing what we'd call clean code today. Some principles stayed - understandable structure, avoiding unnecessary duplication, some naming conventions etc - those made sense. Others were deliberately sacrificed when a more direct implementation performed better.
Sometimes fewer abstractions meant less overhead. Like caching a DOM reference instead of repeatedly looking it up mattered more than making the surrounding code elegant. Generally we made plenty of tradeoffs - the simplest code wasn't the prettiest code. C'est la vie.
BUT (again): the economics of those trade-offs might be changing now.
Fast-forward to 2026.
These days, I'm having a surprisingly hard time answering what should be a fairly basic question - Should I even read LLM-generated code?
It may sound ridiculous, right (still sound like it to me; just near my desk I have 2 books opened, which I'm going through for the 3th and 5th time: Rust in Action by Tim McNamara and Learning Go by Joe bodner - so how could I NOT read a code?). But bear with me. I've been thinking about the idea that LLM-driven software engineering might represent a shift in the abstraction layer we work with.
Years ago, programmers moved from writing machine instructions and assembly language toward higher-level languages. Compilers took over increasingly large parts of the translation between human intent and machine execution. What if something similar is happening again?
Instead of implementing every detail ourselves, perhaps we're moving toward describing what we want, establishing constraints, designing systems, and letting agents handle more of the implementation. I believe Linus Torvalds recently even spoke about this particular view of this shift!
Anyways, I decided to try something - I stopped looking at the code for a while. Not entirely, and not forever, but during the implementation process, I deliberately stepped away from reading it for a one feature. I designed this feature and refined its requirements, and gave the implementation task to a group of agents using different models.
One model acted as the implementer. Another acted as the reviewer, both received the original design. First, they discussed the proposed implementation. Each round used a fresh agent with a new context, and the conversation continued until they reached an agreement.
Then the implementer wrote the code. And again, the agents reviewed it, discussed the design choices, suggested improvements, and iterated until both were satisfied.
Once that was done, the implementation was submitted as a GitHub pull request. GitHub Copilot picked it up and performed another review. And yes - the agents went through those review comments too. Eventually, the PR was accepted and only then did I look at the code myself. Was it perfect? Nope.
There were decisions I wouldn't have made. Some were just different design preferences, while others involved abstractions and clean-code choices that I personally wouldn't have introduced. But the implementation passed the established checks, was shipped, and has worked correctly in its intended use so far.
So... was this a success? In that particular experiment, yes. Did it prove that I no longer need to read code? Absolutely not.
The agents agreed with each other, but agreement isn't proof of correctness. They could share the same blind spots. A successful implementation doesn't tell me much about what happens six months later, when the requirements change or an unexpected failure appears. And yet, the whole experience made me question something I'd previously taken for granted.
Are we moving to another abstraction layer?
I still don't know whether this is the right way to build software. And I suspect many of us don't. Maybe we're watching the beginning of a fundamental change in software engineering. Or maybe we're simply going through an exceptionally productive phase of automation that will eventually settle into something less revolutionary.
There are plenty of unanswered questions. How reliable will these agents become? How much human supervision will still be necessary? Who is responsible for understanding the implementation when something goes wrong?
And the economics. The current cost of generating, reviewing, and regenerating enormous amounts of code might not be representative of what we'll pay in the future. Model providers are investing heavily in infrastructure, business models are still evolving, and the long-term economics of agent-driven development are far from settled.
Today's relatively cheap automated review might become more expensive. Or model efficiency could improve enough to make it dramatically cheaper. We simply don't know.
BUT.
There's another question here. One that we can start investigating without waiting for the entire AI industry to figure itself out. And it takes me straight back to 2005.
What's the actual price of clean code?
I think I've been asking myself this question for most of my career, although I rarely phrased it that way. What's the true cost of clean code?
For a long time, the answer seemed obvious enough. Writing understandable, maintainable code was necessary if you wanted to survive in a large codebase. Other people had to read it, modify it and quite often, that other person was simply you, six months later, wondering what on earth you'd been thinking (c'mon, I know you know that feeling, when you look at git blame and you see your name there!).
Clean code wasn't free, but the investment usually made sense. Now consider a slightly different situation. What if agents write most of the code? What if agents also review most of it? And what if, eventually, agents become the primary maintainers of that code? Does human readability still have the same value?
I'm not suggesting that we should abandon meaningful names, modularity, or good software design. Nor am I suggesting that LLMs magically understand terrible code better than humans do. They don't necessarily. But perhaps we should reconsider which abstractions are actually worth their cost.
Take polymorphism, for example. It's a useful tool for organizing software, separating responsibilities, and making systems extensible. But depending on the language, compiler, and implementation, dynamic dispatch can introduce additional runtime costs or prevent certain optimizations. A direct switch or match might be faster in some situations.
Or it might not!
And the polymorphic version might be easier to extend. Or perhaps a simple switch would be easier for an agent to understand and modify. That's the kind of trade-off I'm interested in. If a design pattern improves readability for humans but introduces measurable runtime overhead, should we still use it when agents are doing most of the maintenance?
And how would we measure the cost of not using it?
Because runtime performance is only one part of the equation. There's also the cost of generating code, reviewing it, debugging failures, applying changes, and keeping the whole system correct over time.
Maybe the right question isn't whether we should stop writing clean code. Maybe it's whether our current definition of clean code still optimizes for the right things?
The first experiment
At this point of philosophical monologue, I decided to start measuring something to just see, if there is a light in the tunnel. My first pilot compares polymorphism with switch/match-based dispatch in Go and Rust (simply because I have those two mentioned books here!).
The benchmarks use equivalent workloads in both languages, together with pointer-based switch kernels and correctness checks. The idea is to separate the cost of dispatch from effects introduced by memory layout and data representation. Of course, this doesn't measure the total cost of clean code. It measures one particular trade-off, under specific conditions, using specific compilers and workloads. But it's a starting point.
And perhaps that's how we should approach the whole question: not by assuming that clean code is always worth the price, but by figuring out what we're actually paying for.
That's it for now. But here's a sneak peek at the first results! With 16,384 same-type objects, polymorphism was 3.50× slower in Go and 6.61× slower in Rust than contiguous switch/match. Quite a difference, right?
BUT. With roughly one million randomly mixed objects, that gap shrank to just 1.09× and 1.11×. And when compared against a pointer-based switch/match implementation, polymorphism was actually faster in both languages!
So, getting rid of polymorphism doesn't necessarily make code faster. Memory layout and workload type matters. And switching programming languages doesn't guarantee anything either. And that's just runtime performance. We haven't even touched agent efficiency, maintainability, or the cost of future changes.
If we can't put a fixed price on a single abstraction, how are we supposed to price clean code as a whole? More on that in the next part!
Comments