I could have worked through all that on my own, but having the AI as a rubber duck to talk to - plus the edge cases it was able to point out - was really helpful.
I wonder if a refactor is even necessary.
Concepts like KISS, DRY, etc. are largely human-centric. They're meant to make code easier to write, understand, and maintain - for people.
Duplicating code was traditionally "bad" because it increased maintenance costs, risked divergence, and added cognitive overhead for human developers.
But with coding agents, some of those tradeoffs start to change. An agent can find and update duplicate implementations in seconds. It doesn't need to maintain the same mental map of a codebase that a human does.
And if we have good specifications and comprehensive tests, behavioral divergence should be detectable and correctable. In fact, duplication can offer an advantage over shared abstractions: isolation. Changes to one implementation don't risk breaking unrelated consumers of shared code, and each implementation can evolve independently.
Maybe we won't need as many high-level abstractions, or eventually even some of the architectural patterns we consider best practices today.
If the cost of writing and maintaining code approaches zero, perhaps we should optimize less for human readability and more for runtime performance, memory efficiency, isolation, and correctness.
We might even regain some CPU and memory efficiency by eliminating layers of indirection, virtual calls, and abstractions that primarily existed to make code manageable for humans.
Perhaps the optimal architecture for AI-maintained software is fundamentally different from the optimal architecture for human-maintained software.
A lot of our software engineering best practices are based on the economics/ergonomics of human maintenance. What happens when those fundamentally change?
Until proven wrong by coding agents, this is still the case even for agents. Not sure why you are assuming this doesn't apply for the machine
I cannot read a 50K LOC code base at once. I mean, obviously, I can physically do it, but I don't have the patience, etc. I need mental shortcuts like abstractions layers to hold even parts of the code base in my head. LLMs don't. They can read that in seconds and hold the whole thing in context.
The models don't know that they're not human when they estimate. It's worth telling them that all estimates need to be made in the context of an agentic AI doing the work.
Now, I wonder if it actually agreed, or if it sensed that I preferred the robust solution and was trained to make me happy.
Same with any other acronyms - if you don't set up guardrails on when given principle should apply you'll get code that you can only complain about.
Oftentimes there are two broad ways to fix a gap in the logic. You can eliminate the source of it, which often involves refactoring but tends to result in less code and cleaner, more comprehensible logic. Or you can leave the gap there, but attempt to render it safe by adding extra logic to detect and handle the error condition. This reduces changes to existing code, but makes the overall system more complicated.
You can’t just indiscriminately favor one approach. Which one of those is best depends on the situation. The deciding factor is often something like a time constraint that you can’t know just by reading the code. You might need to talk to your team before deciding.
But LLMs don’t do that. They always choose option B: slap a bandage on it. Keep on adding more epicycles. In the 2000s we learned that, over time, always choosing this approach inexorably leads to codebases that are brittle, bloated and expensive to maintain.
So the problem, from my perspective, isn’t that we shouldn’t be coding defensively. It’s that LLMs encourage us to do it in a way that we’ve already learned is unsustainable.
I don't think this is anything new, nor LLMs are to blame here. Although - LLMs want to make you happy, or maybe they don't think about what user actually needs, just how to accomplish the goal that was stated; they do stuff without asking questions (unless you constrain them or plan first). I've actively put in a hook for my agent to not make decisions on their own and ask me when it detects it's about to make a decision.
I think where a lot of people run into problems with the KISS principle is they conflate simple and easy. More often than not, simple is hard. Which is maybe a soft reason why agentic coding, which is all about trying to make every task easy, leads so consistently to complexity and bloat.
If you write your prompt as a request, and not carefully as an idea or suggestion, the bot can blindly agree to 'help'. And if you are stubborn about it, you can lose hours on something that cannot work, or make the code infinitely worse.
However, it's also the path of getting better at programming. To learn to take a step back, to ask yourself if it's really sensible. Just that now it also involves questioning the bot's help, as well as writing your prompts to prevent it.
Not coding for situations that “do not happen” is exactly what creates vulnerabilities. But I also don’t think that such code is the main source of complexity in LLM-generated code.
Having it in the lower layer is correct, a proper optimizing compiler should be able to detect there's a single code path to the function and that the checks have already been done, skipping the second check.
[1]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...
That’s why I think code needs to document all its assumptions, and in a way that is checked by the compiler to the extent possible. If you document that the values have restricted range, then the AI should be taking it into account.
My main point still stands = LLMs code defensively catering for unknowns that are not loaded into context.
Yet, I removed 2/3s of the code, because it triple-checked that every file really existed and was valid, that the input form was less than a million bytes in size, that there were propper HTTP status codes for everything, that every possible error from reading data was eagerly checked and caught, and so on.
Like come on, three people will ever look at it. So keep it small and tidy so I can understand it better!
i am going to quote that a lot...
We don’t have effective objective measures of what would constitute KISS.
Impossible for you, me, and other humans maybe. But not hard to understand for LLMs
> I want to see an LLM reduce a complex multi-million line codebase to 1/10th of its size or less without breaking functionality. That would be impressive!
They can and do, if you ask them to.
If you have tests to validate functionality, it can easily reduce and refactory.
But, why are we caring about LOC again?
To me KISS is basically about how you’d write the code, the architecture, which is something that - at least currently - humans need to oversee when writing new code with LLMs.
But what does it have to do with AI discovering a ton of bugs in a lot of (open-source) software?
Maybe, when they build a pomodoro timer, people will consider the possibility that the entirety of a browser engine as well as 30 000 javascript dependencies from random unvetted sources introduces unnecessary complexity and a practically infinitely fractal attack surface.
Someone not following KISS, might install Windows Server + tons of bloated applications, in order to serve a .asp server, which serves JSON that never changes, or something similarly stupid.
Meanwhile, the KISS developer would put static .json files on a CDN or self-hosted Varnish or whatever, and call it a day.
The first opened up a whole can of potential venues for exploitation, while the second might only have opened up one potential venue for exploitation.
That doesn't mean every simple system solves the problems we intend for it to solve. It means that all else being equal, we should prefer parsimony.