You don't want to handle errors in all the leaves of the system the way AIs have a tendency to, because you very rarely have the right context that deep in the stack to actually handle the error in an intelligent way. So what they end up doing (IMO) is actually hiding problems deep in the stack, in this effort to avoid a visible crash.

I think it's very similar to the tendency to write too much from scratch and reuse too little, in both cases what is necessary is a broader view of how the whole system fits together, rather than only the specific method / file / module being written.

You don't dismiss me, so I'd like to respond to your comments within the bounds of my own knowledge, even though I can't compare to a programmer as skilled as you.

I don't think that's entirely wrong. But human code has the same problem, just in the opposite direction — because humans trust too much. The issue arises from the assumption that 'the other side will handle it.'

For example, good API design says you should only send as much data as needed, but in practice, programmers like me can't do that. Because three different companies, all on the lowest bid, are trusting each other's domains, so in API design they lay out the entire dataset and tell the frontend to filter it. On the other hand, if you design a lean API layout with just what's truly needed and submit it, the frontend company gets angry. So what do I do then? I document everything precisely. I write down that I designed the API this way, but the other company did it that way, and I create documentation and error codes to shift the responsibility over to them, stating that they should handle the filtering on their side

So while there are good programming practices and conventions, in reality we're under pressure from low bids and tight deadlines.

AI code doesn't have a full system map, so it's hard for it to decide how far to propagate errors and where to stop, but I think that part can just be pruned by AI anyway.

Usually in error handling, we use Result<T> a lot, right? For libraries or frameworks, Result<T, E> is common. You centralize your error policy and usually design programs with policy and error policies built in. You create an error policy table with about 7 or 8 types like ValidationError, NotFoundError, ExternalApiError, and within that, you only take responsibility for your own scope.

At the design stage, if you have a clear initial vision, maybe it works. But in practice, the PM changes things mid-project. So in the end, your ideal code approach is correct in theory, but for practical delivery survival, the GPT approach ends up being more realistic. The reason is simple: you can't trust the other side at all, so you create evidence in case of contractual risk.

Because our domains are different. Programmers at service companies aim for long-term maintenance, so their domain boundaries are clear. But the companies that come to me are often in a dirty state where such clear domain separation is impossible. That's where I think the difference lies.

So while I understand your point, I suspect we are optimizing under very different constraints.

AI code usually creates fake 'cohesion.' It looks good on the surface. But in reality, it's often just optimized for the moment, weak to change. After reading Code Complete 4 or 5 times, I became obsessed with the idea that I need to balance cohesion and coupling. AI code has strong local cohesion, but when you look at the overall cohesion, it's weak.

True cohesion is usually about 'things that change for the same reason are grouped together.' But the fake cohesion that AI creates is usually this: 'Neatly organize the given requirements for now.'

On the surface, it just repeats obvious hexagonal or clean architecture patterns like Service, Manager, Handler, Validator, Repository. But the problem is that human code does the same thing. Honestly, I don't trust most people on HN who claim they're different. Even the enterprise code I've bought and the real big-company code I've seen don't have perfectly beautiful separation.

And that's natural. Modeling is always unstable. A single word from a PM saying 'we need to add a coupon' can break a beautifully designed domain.

AI often puts UserService and UserValidator into its structures, but in reality, the reasons for change aren't just one. They bundle multiple reasons together. There's just some flawed modeling.

But what matters is something else. It fits the 'current' input well. When you start digging deeper into the prompt, AI ends up turning the code into enterprise patterns based on the depth of meaning it parsed. And then this problem arises.

Human programmers usually don't have uniform code quality. Of course not. You and I are only deep in our own areas of expertise; outside of that, we're terribly shallow. But AI tries to fix other areas based on the deepest part. That results in verbose and cumbersome code. Small, elegant code becomes verbose, flat, and turns into the patterns we've all seen before.

But I don't think that's necessarily a bad thing. Why? Because realistically, I think it's better in the long run. The uniform enterprise patterns that AI produces are ultimately predictable and searchable.

Top-quality code deviates from the average. That makes it hard to predict. But that's not my level. So I think that when genius programmers contribute to the world through libraries and frameworks, people like me, who aren't talented, build things with them. And for that, predictable code is more than enough.

AI code is easier for AI to read and fix later. Human code is harder to predict. That makes it harder for me to maintain. In the garden of open source, people obsess over 'good code' quality, but for me, if I fail, the work stops coming.

The difference between you and me is that you're a better programmer than I am, and I'm just a beginner who's more indifferent to that performance gap. We value different things. And I think your perspective is more 'programmer-like.' I respect it.