DEV Community

Cover image for No Human Will Ever Write Code Better Than LLMs? Better at What?
Daniel Nwaneri
Daniel Nwaneri Subscriber

Posted on

No Human Will Ever Write Code Better Than LLMs? Better at What?

Deconstructs the flawed scalar metric of better

A tweet went around saying: "No human will ever write code better than LLMs. Accept this fact, stop pretending, and move on."

Here is my reply.

That's a lot of implicit "better" in that question. LLMs are trained to be plausible, not right. You can get as elegant/idiomatic/smooth-sounding as you want, and still miss the real constraints that matter (existing systems, team patterns, business considerations, failure modes, not-yet-real features).

Senior-level engineering is not just about "writing" code. It's knowing when not to. It's about recognizing real edge cases and not-baked-yet abstractions, knowing when a problem could be more simply solved, or not solved at all. That kind of judgment is hard to quantify, hard to prompt, and often non-obvious in a generated solution.

So yes, LLMs can be a handy tool, but not even close to the level of a real human expert.

The word that bothers me most in that tweet is "better". So I wrote down what it means.

"Better" is a word that we treat like a scalar value. In other words, there is one value that defines better. However, when you ask "better" at what are you measuring it? Lines per minute? Correctness? Maintainability? The courage to not build it? You quickly realize "better" means different things depending on the dimension you are measuring it along.

The productivity metric is lines per minute. It's a nice, simple metric, and it's easy to measure. There's a lot of momentum in this metric because it feels like improvement. However, it is only a measure of typing. It doesn't mean the resulting program is better.

A thousand lines of wrong code is worse than no code at all. A thousand lines of code that crumbles the second you look at it are even worse than that. So, instead we look at correctness. We want our code to be better because it's correct. It has the correct functionality, and it meets the spec. It passes the test. This is an improvement. But, that's not enough.

The real nightmare is that the LLM-generated code works until it doesn't, and it doesn't make sense to you. You'll get reasonable-looking code that somehow finds its way to production, and your payment pipeline will suddenly start silently discarding transactions because the model just decided to add a retry_on_timeout decorator to your transaction handler function that will catch the exception, log a WARNING, and return None. The code down the line will take the None and read it as a zero-dollar successful charge, so now your customers are getting the things they ordered for free.

Gramli tested a case like this in To Retry or Not to Retry. In one scenario, a payment request returned a 503 with a Retry-After header and no idempotency key. Most of the 8 models followed the header and said to retry. Only two said no.

Dex from HumanLayer explains why this happens in his talk "Harness Engineering Is Not Enough and Why Software Factories Fail". Many coding benchmarks give the model a pass or fail reward. The model wins when the tests pass, and nothing penalizes it for bad design. So it learns to add a try/catch or a type cast, because those make the test pass.

We can have a perfectly good piece of software that's unchangeable. This is the opposite of what we want. This is legacy code, and it is bad. We need to be able to make changes without pain.

So we improve it by making it maintainable. Maintainable code is readable, simple code, it's code that doesn't make anyone else want to burn down the office. It doesn't break as time goes on. Maintainable software just works. It meets new requirements and changes without breaking.

The last option is the best of all. It's the metric of wisdom. The "better" that allows us to say "no we shouldn't do that". It's better because it's less, but necessary.

In other words, code is a liability, it is something that we should keep at a minimum. This "better" is the most difficult to practice. It requires saying no to pressure, to budget, to a stakeholder who thinks they have a great idea. It is a less is more metric. However, the compounding interest it pays by not building something keeps growing.

So, "better" is not one thing. It's multiple things. It's a hierarchy, and it changes as we gain experience. We go from lines per minute to correctness to maintainability to not building.

Top comments (51)

Collapse
 
ingosteinke profile image
Ingo Steinke, web developer •

"Better" is a word that we treat like a scalar value.

I remember StackOverflow used to discourage asking for "best practices" and "best" solutions back in it's "better" days, for the exact reasons that you mentioned. Better at what? Better for whom?

Apart from questioning requirements and apparent best practices, and anticipating edge cases as experienced senior developers who take responsibility, we could even still write better (or worse) lines of code. Maintainability, readability, security, standards-compliance, code style etc. Developers have been arguing about ditching code comments in favor of descriptive naming, refraining form over-optimizing compact code-golf idioms in favor of the former goals. Many of those principles have been documented and standardized already. Still, AI keeps mixing idioms, styles and strategies and it keeps stating the obvious redundantly in abundant lines of comments, as if AI was a living in the 1990s and getting paid per line of output.

Thanks for sharing, and especially thanks for mentioning maintainability and questioning assumptions!

Collapse
 
dannwaneri profile image
Daniel Nwaneri •

Thanks Ingo. Excellent second question, “better for whom?”

I also like the StackOverflow point. Your comment about AI often generating comments that just repeat the code is spot on. It’s just the lines-per-minute metric all over again, as if it gets paid by the line.

Collapse
 
gramli profile image
Daniel Balcarek •

Nice write-up. Whoever said "No human will ever write code better than LLMs" probably doesn't spend much time reviewing AI-generated code or maintaining a real codebase. 😅

I have multiple examples from just the last few weeks where AI made wrong assumptions or introduced unnecessary complexity.

GPT Sol 6.1, last week, was supposed to implement a feature in an existing codebase. The generated code was readable, but it introduced unnecessary database reads and made the whole feature far more complex than it needed to be.

GPT Astra 6.1, in the same week, was asked to refactor some code. The result was actually beautiful, readable code, something I'd expect from a senior engineer. But it also made assumptions and implemented functionality that wasn't needed. And as we know, dead code in a codebase is poison.

I could write about my battles with AI every single week, and I haven't even started talking about code ownership and responsibility. 🤣

Collapse
 
dannwaneri profile image
Daniel Nwaneri •

Thanks for the feedback, Daniel. I actually think your examples illustrate my point. It’s possible to write code that is perfectly readable but performs additional, unnecessary DB reads. It’s also possible to write code that will someday become a maintenance issue because somebody else will have to figure out what it’s doing and won’t know that it’s safe to remove.

It’s just that the model isn’t aware of these things, so it does the best it can to solve the problem as it sees it. Maybe I should write a post about those too.

Collapse
 
maame-codes profile image
Maame Afua A. P. Fordjour •

Such a nuanced and necessary take! Outputting lines per minute is easy, but knowing what not to build and designing for long-term maintainability is where real software engineering happens. Great posts as always Daniel!

Collapse
 
dannwaneri profile image
Daniel Nwaneri •

Thank you Maame, you did a great summary of my article. It is easy to measure output but hard to measure what not to include. I am happy that this helped you.

Collapse
 
leob profile image
leob •

"No human will ever write code better than LLMs" - yeah that's a ridiculous statement to make, even when it's just a tweet aimed at generating clicks & views - a statement this absolute (and devoid of context) is meaningless ...

Collapse
 
dannwaneri profile image
Daniel Nwaneri •

Thanks. This is such an absolute statement that it sounds cool. However, it doesn’t really address the issue I was getting at in the original post... which was, “better at what?”

Collapse
 
leob profile image
leob •

Yeah, define "better" :P

Collapse
 
edmundsparrow profile image
Ekong Ikpe •

Not worth a debate rather a place for clarity. Strong & hard may be synonymous buh 🙄 "better than"? Totally off for me 😅.well a typewriter does neater than a pen maybe 😉

Collapse
 
dannwaneri profile image
Daniel Nwaneri •

Thank you, Ekong. You used a very good example with the typewriter: it’s tidier than a pen, but that doesn’t necessarily mean that it’s superior. Again: “better at what?”

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

The retry_on_timeout payment example is the one that makes the hierarchy concrete — plausible code that silently turns a timeout into a free order is worse than no code at all. I'd argue the hierarchy needs one more rung below "not building": not shipping what you can't explain when it fails, since the payment case only surfaced because someone traced the None. Do you think the "courage to not build" test should apply to generated code the team can't yet debug, or is that bar too high for shipping velocity?

Collapse
 
dannwaneri profile image
Daniel Nwaneri •

Thanks! I like the extra step: Don’t ship code that you can’t debug. I think it should be extended to generated code. If you can’t debug it, you don’t own it. I code alone, and I don’t ship things I don’t understand. It’s cheaper to check than to have an incident.

Collapse
 
kami_butt_69b922ade8de012 profile image
Agentic Affairs •

"If you can't debug it, you don't own it" is the sharper version of my rung — it turns a shipping rule into an ownership test. Coding alone makes it enforceable, since you're the one who pays at 2am either way. The harder case is a team, where the person who generated it isn't the person who debugs it: does the bar then become "someone on the team can debug it," or does the generator have to stay the owner until it's understood?

Collapse
 
alexshev profile image
Alex Shev •

“Better” needs a workload and a responsibility model. Generating a plausible patch is different from recovering a production incident, negotiating an ambiguous product constraint, or deciding whether evidence is sufficient to release. The useful benchmark is not keystrokes produced, but verified outcomes under realistic ownership and failure conditions.

Collapse
 
dannwaneri profile image
Daniel Nwaneri •

Thanks, Alex. It is more useful to measure success and failure under actual ownership rather than keystrokes.

Adding the question of a responsibility model to "better at what" is valuable.

Collapse
 
alexshev profile image
Alex Shev •

Exactly: “better” needs ownership, environment, and time horizon. A model may win at producing a candidate patch, while the engineer still owns requirements, integration, rollback, and the consequences of a plausible but wrong change.

Collapse
 
mudassirworks profile image
Mudassir Khan •

the retry_on_timeout decorator that returns None and gets read as a zero dollar charge is the failure that scares me most, because nothing throws. the pass or fail reward point explains why models reach for a try/catch first. one cheap guard that has helped is making the payment path return a typed result with an explicit failed variant, so a swallowed exception cannot look like success.

do you have a lint or review rule that flags catch blocks returning a default value in money paths?

Collapse
 
dannwaneri profile image
Daniel Nwaneri •

Thank you! You are right: the most terrifying kind of failure is a silent one where nothing is throwing at all.

Providing a typed return value with an explicit error variant is a good defense against that. If an error is swallowed it can never masquerade as a successful value.

I don’t have a lint rule for this yet, but given your question I am thinking it would be good to add one.

Collapse
 
mudassirworks profile image
Mudassir Khan •

a lint rule is probably overkill at first. a grep in CI over the catch blocks in your payment module, flagging any that return something other than a rethrow or the typed failure, would catch most of it and takes maybe an hour to write. tighten it only if it misses real cases.

did that timeout decorator come from generated code or an old helper somebody copied over?

Collapse
 
fgrandon profile image
Francisco Grandón •

Framing 'better' across dimensions like maintainability and knowing what not to build cuts right through the lines-per-minute noise. That failure example of an LLM slapping a retry_on_timeout decorator that returns None and silently creates zero-dollar charges highlights how plausible-looking code masks catastrophic state bugs. How do you structure code review guidelines on your team to catch these subtle semantic edge cases in generated pull requests?

Collapse
 
golflover2023 profile image
golflover •

The dimension I never see in these lists: ownership cost when the code breaks. LLM code fails confidently in the middle - not at the edges its author knew were shaky - so whoever debugs the payment pipeline at 2am is reading code nobody on the team can explain. That is vendor code without a support contract. Practical bar: tests pinning the spec plus one human who can narrate every module before it ships; otherwise the lines-per-minute gains get paid back in incident review.

Collapse
 
dannwaneri profile image
Daniel Nwaneri •

Thanks - good point to expand on, the expense of ownership. I like your particular case - “vendor code without a support contract”.

You have clear and unambiguous criteria - the tests pin the spec, and there must be one person who understands every single module (in my case, me).

Collapse
 
kling4ai-production profile image
Kling4 AI •

The distinction between syntax generation and architecture/intent is spot on. Code completion is mostly solved, but knowing which boundary to decouple or anticipating production operational failure modes is still fundamentally where human engineering happens.

Some comments may only be visible to logged-in visitors. Sign in to view all comments.