by lelanthran | 2025-09-16 12:12:59+00:00
> How does writing a few lines manually change anything?

I'm still trying to figure out where he says the LLM wrote any code. You're still working on the assumption that he said the LLM wrote code, and he changed a few lines. To me it reads like, I rubber duck specs/plans with the LLM, and then I write the code).

It doesn't seem ambiguous to me

> On the other hand i am almost willing to bet, he writes worst code I do by just prompting LLM

I dunno; the LLM, by definition alone, is going to produce median quality code.

After all, this thread was started by me complaining that it's been very difficult for me to get any LLM, even SOTA ones, to produce C code in a convention that minimises resource errors (use-after-free, memleaks, etc).

It does it for one function. Then for the next one I have to remind it again that it must use single cleanup point.

Putting it in the prompt works sometimes, not all the time, but for it to work then all my prompts have a code example of how to structure C code to ensure a single cleanup point which returns the returned value (which could or could not be an error).

So, no, I've not been getting spectacular results using CC and similar tools for C. Maybe it works better for C++?

What I have been getting good results on is SQL (have to specify the variant, though).

I sometimes think that the LLM cannot tell human-readable code from unreadable messes; hence I get these almost comically shortened C snippets sometimes when (for example) a static lookup table, though longer, would be more readable.

I think the misunderstanding here is what metric we are using for "Quality". I am using "Is this the most readable way to solve the problem?"

Just this morning I tried to get it to write code to dispatch different serialisation functions based on a provider, it gave me a large-LoC monster of structs with vtables for dispatch[1], and some functions to perform the initialisation, then more functions for the dispatching properly.

I asked it to make it simpler; it give me a simpler, but very long, jump table using switch/case on the `type` field in the `struct`. Easier, but still it means that an unintended fallthrough, or a missing `case` is a bug I have to look for when visually inspecting the code for correctness.

I gave up and wrote this (some details changed), which is much easier for me to visually verify that no cases have been missed, no accidental fallthrough, etc:

typedef bool (provider_serialise_func_t) (const some_t *src_some, some_http_t **dst_rqst); typedef bool (provider_deserialise_func_t) (const some_http_t *src_resp, some_t *dst_some); struct some_provider_t { enum some_provider_type_t type; char *name; provider_serialise_func_t *serialise_fptr; provider_deserialise_func_t *deserialise_fptr; }; // Serialisation/deserialisation functions, defined at the end of the file static bool serialise_all(const some_t *some, some_http_t **http_request); static bool deserialise_all(const some_http_t *http_response, some_t *some); static bool serialise_prov1(const some_t *some, some_http_t **http_request); static bool deserialise_prov1(const some_http_t *http_response, some_t *some); static bool serialise_prov2(const some_t *some, some_http_t **http_request); static bool deserialise_prov2(const some_http_t *http_response, some_t *some); static bool serialise_prov3(const some_t *some, some_http_t **http_request); static bool deserialise_prov3(const some_http_t *http_response, some_t *some); static bool serialise_prov4(const some_t *some, some_http_t **http_request); static bool deserialise_prov4(const some_http_t *http_response, some_t *some); static bool serialise_prov5(const some_t *some, some_http_t **http_request); static bool deserialise_prov5(const some_http_t *http_response, some_t *some); static bool serialise_custom(const some_t *some, some_http_t **http_request); static bool deserialise_custom(const some_http_t *http_response, some_t *some); static const struct some_provider_t g_some_providers[] = { // NOTE: Order is important, don't switch the order around { SOME_PROVIDER_ALL, "all", serialise_all, deserialise_all }, { SOME_PROVIDER_PROV1, "prov1", serialise_prov1, deserialise_prov1 }, { SOME_PROVIDER_PROV2, "prov2", serialise_prov2, deserialise_prov2 }, { SOME_PROVIDER_PROV3, "prov3", serialise_prov3, deserialise_prov3 }, { SOME_PROVIDER_PROV4, "prov4", serialise_prov4, deserialise_prov4 }, { SOME_PROVIDER_PROV5, "prov5", serialise_prov5, deserialise_prov5 }, { SOME_PROVIDER_CUSTOM, "custom", serialise_custom, deserialise_custom }, }; ... // Usage in an actual function const struct some_provider_t *provider = &g_some_providers[src_some->provider];

And now there is not `switch` statement to verify (it's in the struct, one on a line, easier to read), there's no confusion via multiple indirect derefences to a vtable, etc.

So if your metric for quality is "it works and has no bugs", then sure - you would feel that the LLM is providing high-quality code.

If your metric is "How easy is this to code-review, when it's midnight and I have been working all day long at my day job?" then things look a little different.

===========================

[1] Obviously it trained on OO designed C code, like the Linux kernel