I agree with the idea that boringly predictable should be what is preferred but anecdotally my experience in using Go with LLMs is that they trip up a lot on the races and locking from go’s thread model. I haven’t seen the same problem in rust which is now why I’m doing all my LLM work for tooling in rust.
The parallelism issue in particular was also not something I noticed agent struggling with in JavaScript, although JavaScript concurrency model is clearly fundamentally different.
The concurrency issues that I saw LMM‘s face was one reason why I created freelang which uses a very boring and audible concurrency model of OS processes that use the file system to talk instead of IPC, shared state, or anything like that. Higher overhead, lower throughput, but more boring and hopefully less bugs: https://github.com/DO-SAY-GO/freelang
I disagree. "Boring" languages leave a lot of assumptions in code, which will start to compound the more changes model (and programmers) make to the code.
The more assumptions I can move to compile time the better models are at dealing with emerging complexity.
I would go the other way with LLMs and I wish for liquid types and effects in Rust to make type specifications even more strict.
P.S. effects and liquid types and type specifications in general add a lot of busywork, but models have higher level of tolerance to busywork compared to developers.
Rather than "boring", this seems to be reaching for something like the concept of a "pit of success", or https://haskellforall.com/2016/04/worst-practices-should-be-... . I don't think the fact that the most common pitfalls in Go are well known should be taken as a sign that it doesn't have more esoteric pitfalls as well; it's just that the common cases (like nil) are the ones that everyone sees all the time.
This is an interesting idea, but I'd want to see something solid before acting on it.
From what I can tell, LLMs know/use patterns above the syntax and idioms of specific languages and the syntax and idioms of specific languages and how to apply the former to the latter.
The bottleneck isn't what languages the LLM can handle, but what I can handle coming out of the LLM. The general advice, then, is to use the language (and related setup/environment) you're familiar with.
Contradictory anecdote: there’s basically only one way to write Elm, as it is a very trend-resistant language with minimal updates over long timespans, but most agents in my experience will throw Haskell syntax and Prelude functions into their Elm output. Compiler or LSP will often set them right but they still try it initially
I program in two languages: Swift (my main language), for client work, and PHP, for backend work. It’s overwhelmingly Swift.
In the last year or so, I have been using LLMs, to assist my work, with generally, excellent results.
I have noticed that the LLM delivers much better PHP, than Swift. I seldom need to rewrite or correct, the PHP code I get from it, and am constantly correcting the Swift. Part of the reason, may be that I am a much better Swift programmer, than PHP programmer, and there’s just a lot more Swift code. I haven’t really taken the time to analyze it.
I have my theories, as to why, but it’s not something I’m really into researching. I’ve just noted the trend.
I haven’t had an issue using Python with LLMs where I have to decide “Should one use pip, poetry, or uv?” Since there is enough training data using pip or just choose that since it is the most boring solution and many of the commands map to uv since uv has a superset of features. Not that go is a bad solution honestly I would just say use what you know best.
I wonder if the training data for some languages has higher quality code. I can imagine some niche languages having a higher standard than, for example Python, which surely has a bunch of random buggy scripts in the mix.
On the other hand, even if that were true, I don’t know how important it would actually be since LLMs can generalise across languages well.
It might be best to pick languages where it’s just harder to screw up, the canonical example being to prefer typescript over JavaScript.
> Languages and ecosystems with low variance in their training corpus are represented better and executed more reliably by coding agents.
So I think the author is saying that go is a simple language that tends to have less solutions to the same problem. I personally agree to that to a degree.
What I don't agree on is that we can choose what "low variance" is. There is a lot of go code out there, it's shape may have little "noise", but the variance is massive.
Try to apply first principles to LLM coding:
* Chances are that fewer people (maybe even none) will look at the code when it's LLM-generated
* Amount of code being written isn't all that critical anymore
* Keeping patches small isn't that big of a deal anymore (because it's now the LLM's job to maintain it, not the human's)
All of this implies: boilerplate isn't a good reason to avoid a language anymore. (I hate this conclusion, because I hate boilerplate).
Then the question is: what kind of language can you use that buys safety with boilerplate? Probably a statically typed one, possibly with lots of asserts... Eiffel? I don't know if there's enough Eiffel code around the Internet to train LLMs, so maybe a more popular one would be better.
Maybe Java or C#? Haskell? OCaml?
The article suggests golang, and I think there are use cases where golang would be a good candidate.
It would be quite interesting to run an experiment: give separate instances of the same LLM coding agent the task to implement a specific application, and use different languages. Then compare quality, code size, runtime performance and token cost. Ideal would be a multi-stage development that better simulates a real development workflow (bug reports and new feature requests come in over time).
I was a little surprised to find when I gave an LLM REPL access to the running program, it readily started using it during development and debugging.
Rust, Elixir, and Go are the way to go for LLMs in my testing and experience, for this and other reasons
Also usie boring languages without LLMs
> From a model’s standpoint, there are simply too many ways to write any of this
They seem quite good at figuring this out in my experience
What??
Python __is__ a boring language (it is mature and well supported) with a somewhat convoluted package manager that has gotten a lot better since that xkcd came out.
Yeah, I get it, Go is better for distributing your code-- just one binary you can copy. But what does that have to do with "boring"?
[flagged]
> Python is the same story but sung in a different key. Asking a simple question like “which package manager are you using?”
This is annoying but only needs to be solved once at the start, either by the LLM or the human guiding it. A single prompt of "Set up a uv project in this directory with Python 3.13" is enough that it's never an issue again for that repo.
> Goroutines are a far more tractable primitive for coding agents than threads, callbacks, async/await, or any of the colored-function regimes that dominate elsewhere.
I disagree with this. Goroutines, along with threads, callbacks, and traditional async, are all in the same category: spaghetti of unbounded background tasks. Structured concurrency [1] on the other hand is dramatically easier to reason about. Python has support for this (in Trio and asyncio.TaskGroup) as do other languages like Kotlin and Swift. Function colouring a red herring; if anything, it's useful because it highlights the scheduling/cancellation points in your code.
[1] https://vorpus.org/blog/notes-on-structured-concurrency-or-g...
-----
This really does read as "Go is my favourite language". In fairness, that's a good reason to choose a language to use with an LLM (so long as it's powerful enough and not too obscure). But let's not pretend it's the best language for everyone.