upvote
My complaint is that LLMs’ approach to making code robust is something we used to pejoratively describe as “enterprisey”.

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.

reply
Agree. It should fall on to developers to decide what to do = whether tackling tech debt is viable solution (whether making decision on their own or talking to people that can make such decision) or not. If, however, you're told "here is a codebase that you're seeing for first time, fix this error" and you simply cannot make the decision, even prior to LLMs you'd put a bandaid on it. Or at least - that's the standard approach = fix now, refactor later, where later in my experience never happens (sorry for this train of thought).

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.

reply
Agreed, it is not at all a new problem. The enterprisey codebases I’ve encountered are ones that have chronically had no clear owner, or constant ownership churn. I have been in that position before, and I also decided to make the code even more bloated for the same reason: I did not and was not going to have the time to learn the system well enough to safely do a better job.

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.

reply
So true! And it goes hand-in-hand with a different problem: You being able to try and brute-force solutions.

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.

reply
Very well stated
reply
> coding for situations that either do not happen or are unlikely to happen.

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.

reply
An example to what I'm talking about: I have application that takes two inputs from request. I've put in validation on request values to deny values that are greater than 100. In my code I'm adding these values. LLM can say "cater for addition of these two numbers, because they might go over MAX_INT and result in overflow". This is a situation that won't happen. LLM can recommend - add assertions. That's unnecessary.
reply
And the day you add a way to batch import request through another code path, you may forget the validation.

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.

reply
Alternatively, the code could be written in the style of parse, don't validate[1]. However I've never had a coding LLM preemptively suggest that.

[1]: https://lexi-lambda.github.io/blog/2019/11/05/parse-don-t-va...

reply
Well - this would be an example of defensive programming and by that logic catering for this "what if" is the way to go. LLMs were correct all along!
reply
Arguably it’s only unnecessary when the value range is enforced by the programming language (types). The structure of who calls what in the code can change, and suddenly larger values can appear. You don’t want to have to perform whole-program analysis on every change to check whether all assumptions made in the past still hold. That’s also a source of vulnerabilities: assumptions that held when the code was written, but stopped holding sometime later.

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.

reply
Types is an option. Another option would be tests (and in this case mutation testing).

My main point still stands = LLMs code defensively catering for unknowns that are not loaded into context.

reply
Idk I routinely see the LLMs repeat validations that already happen in another layer or it imagines things that don't exist
reply
I vibe-slopped a small and local forms application for my family. I explicitly mentioned that fact to the LLM.

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!

reply