Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'm sympathetic to the viewpoint that GPT-4 is prone to mistakes when writing code. Unfortunately, the analysis in this paper is pretty bad and doesn't support that conclusion.

The authors assume that for any given method under consideration, it must only occur within a particular pattern of other method calls and control flow instructions. But the templates they have chosen are clearly only applicable in certain situations.

For example, they claim that I/O operations are "wrong" unless they are wrapped in exception handlers that log any errors:

    try {
        ...
    } catch (IOException e) {
        e.printStackTrace();
    }
But of course, this will cause execution to continue as though the I/O was successful, which might be exactly the wrong thing to do! In many cases, you want the exception to propagate, so that the caller can decide how to handle the failure. (And even if you do want to report the error somehow, writing it to stderr might not be correct; it's pointless in a GUI app.)

Similarly, the authors assume that every time you create a file or directory, you always want to call .exists() first (even though doing so has an inherent race condition); that Map.get() must always be followed by an "if" block; that List.get() must always be guarded by an explicit bounds check; that after doing a database query, you always want to close the connection; and so on. None of those rules are universally applicable.

I would expect the real problem with LLM-generated code to be semantic bugs and "misunderstandings" of the requirements, which would not be caught by superficial checks like this.



I see humans do this all the time, especially abuse of exception handling, even among “Senior” developers. They don’t have a semantic understanding of what they are doing or why they are creating a race conditions or creating perverse control flow logic N layers down in the stack.

The fact that researchers get it wrong is, well, unsurprising. LLMs might actually be an improvement.


Well, there's no "correct" answer. Depending on the context, the "correct" thing might be 1) to log and swallow the exception and move on; 2) Let the exception percolate up to caller, who can handle and recover; 3) implement your own recovery and handling; 4) kill the process. To know which one to do, one needs to understand the context. Logging and swallowing is not the worst default, but it's also likely not the best.


But this is because almost everyone gets taught this badly. When you ask how to do exception handling best practices etc, even from teachers/college profs etc you get wildly different answers from every single one of them. So people either learn themselves or from colleagues or, most likely, not at all.


What’s a “senior” developer these days? 3-4 years experience on average?


Knows how to build a feature or small system without handholding.


Hi, do you have a recommended read for those of us who might inadvertently create race conditions?


Now hopefully I won't get horribly dinged for mistakes and poor advice here. What I am trying to say is that I too read "even senior devs don't understand the race conditions they create downstream." And I thought - oh God, don't I.

But five minutes thought can help you walk through most issues. For most applications most of the time you can reason your way through without fear, and when you do encounter gnarly problems they often can go away by redesigning your application! Often the problems you encounter you caused. retrace your steps and find an easier path. Save the hard thinking for genuine problems / value creation.

So race conditions are simply when two processes / threads are likely to affect a single resource. In this case it's a file - and the problem is test if a file exists, then if it does not, create it and then write to it.

If two threads do this, say a log file, the first one creates the file and logs it's important stuff, the second then creates it again wiping out the first log data.

Solutions in this area include

- create as append file (the concept is basically deviates old because this is a decades old problem)

- avoid sharing resources. for logging log to per thread locations. Not always possible but you sure as hell can minimise this to one or two resources you must share.

- hand off creating files to a seperate part of the application. There is a balance between "scripting" and "application" and using small little library functions to do in one line indirectly something that also takes one line using the methods shown in the docs.

- handing off batons / mutexes etc etc. This gets wildly complex. Honestly given a world of async libraries, Erlang, and distributed computing, if you find yourself having to use multi-threading think very carefully if this is the right approach.


For the example in question one approach which might work well is to realize the test is superfluous. Just open the file with the right flags.

That is, the flag for create if not already existing and flaf for deny sharing so any other process/thread will fail to concurrently open it.

Then you just handle the case when the open fails.

Of course this relies on those flags being available and working. For example shared files might not respect the sharing lock.

Many cases of avoiding race conditions boil down to something similar: avoid the problem by not doing extra work.


Or, sidestep it by creating an empty log file (if it doesn't exist) during initialization before creating multiple threads.


Yeah that's more or less "don't do simple one liners in your application code - write a simple library that does the simple thing, but wraps the simple thing in lots of checks"

There probably is a "design pattern" for that but darned if I can draw it in UML


For Java, Effective Java, 3rd Edition, chapter 11. For a more detailed treatment, Java Concurrency in Practice is the book to read.


I have only glanced at a few examples in the paper so far but I would rate some of the cases listed as being better responses from the LLM than their exemplars.

If you ask someone how to open a can of soda you do not want them to tell you how to check to see if the can has been shaken and what to do if it has. You want the instructions to the question you asked.

If anything I would a LLM to produce code that does the fundamental operation, possibly I might like it to offer a more robust framing of the operation as an extended example but I suspect even that would get annoying after a while.

I absolutely would not want The most technically correct but harder to read response to a question of "how do I X?"


The paper actually explains that it targets at the API misuse problem, not the semantic alignment or bugs. Semantic bugs are difficult to detect, which is already a consensus in software engineering field and still many ongoing work on it. And when we say 'semantic' in this special problem it means more than 'semantic' in programs but also how developers express their semantics, which is definitely a problem bigger than checking the code itself. The API usage patterns are created to help check the code snippets given by LLMs, while the race condition you mentioned could exist but not checkable unless given other components of the programs. If adding the bugs you mentioned, the buggy code generated by LLMs could even exceed the number claimed in the paper.


But what is "misuse", then, if not something that causes a bug?

"Misuse" is not a formally defined thing in Java, and the authors never define what they mean by it.

> If adding the bugs you mentioned, the buggy code generated by LLMs could even exceed the number claimed in the paper.

Or it could be much fewer. Aside from a few scarce examples, the paper gives no evidence that most of the patterns that were selected are actually associated with "misuse". They just declare it to be so. (The 2018 paper they cite for their dataset also provides little such evidence. It just says that the authors reviewed the patterns, reviewed documentation, and decided which ones they considered to be poor code quality.)

It is easy to come up with valid situations where a piece of code violates those patterns, but behaves as intended and is not misusing the API. So why should I assign any meaning to the fact that some percentage of code snippets violate the patterns?

I could make a list of adjectives that frequently appear in comments near buggy code, and then count how many LLM outputs contain those outputs, and then say that means the LLM output is buggy. But I would not be saying anything meaningful about the LLM's quality by doing so.


> But of course, this will cause execution to continue as though the I/O was successful, which might be exactly the wrong thing to do!

Almost always is the wrong thing to do when I encounter this antipattern. GPT-4 beats these researches in code quality is my takeaway here.


If you take the point of view that LLM's have been trained on all the code that humans write, then that sort of things is probably 'correct' to the model.

For coding it seems like there almost needs to be a weighting to certain 'correct and orthodox' handling of events if you're going to give over control of code generation to an LLM.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: