The language you choose now decides how much of the output you have to check
AI-assisted programming, or vibe coding, is not a fad. It is here to stay. The days of spending long hours staring at a couple of lines of code trying to work out why they do not do what they obviously should are almost gone, and almost anyone can write code now.
Seasoned programmers get more out of these tools rather than less, which is not the way the story usually gets told. Experience is what lets you drive the model to the answer you want, and it is what lets you look at the result and tell whether it is any good.
The genuinely new part is that you no longer need to know the language you are writing in. I never learnt Java. I never liked the language. But I can read it well enough to review code and approve pull requests, and nothing now stops me from using coding tools to write an entire application in it. Or in Rust, or R, or whatever else the problem seems to call for.
The question is whether I should.
Because the model is not equally good at all of them, and the gap is wider than most teams assume when they pick a language. It does not show up as the model refusing. It shows up as output that looks right, takes longer to verify, and costs you review cycles you did not plan for.
Four things make a model stronger in one language than another
None of them is about how good the language actually is.
Training data volume sets the floor
Public repositories are overwhelmingly Python, JavaScript and Java. GitHub, Stack Overflow, every tutorial site you have ever landed on from a search. A model trained on billions of tokens of Python has seen every idiom, every awkward edge case and every bad idea somebody committed at 2am on a Friday.
Feed it a language with a few million tokens in the corpus and it is working from a much thinner sample. You still get output. It just shifts from confident to plausible looking, and telling those two apart is now your problem.
Strict compilers punish near misses, which distorts the leaderboards
Benchmarks score whether the code executes, not whether the intent was sound. Python and JavaScript will cheerfully run something with minor structural flaws. Rust, Haskell and C++ reject the whole file over a lifetime annotation or a missing trait bound, even when the algorithm underneath is mathematically correct.
The benchmark records a hard failure where a human reviewer would have said "nearly there, fix line 12". Worth keeping in mind the next time you look at a leaderboard and conclude that a model cannot write Rust. It can. It just does not get partial credit.
Paradigm bias means functional languages come out wearing the wrong shape
Public code is mostly procedural and object oriented, so that is the shape these architectures absorb best. Pure immutability, pattern matching and monadic composition are all badly underrepresented by comparison.
You see the result the moment you ask for idiomatic Haskell or Elixir and get imperative logic wearing functional syntax. It compiles, sometimes. It is rarely what somebody fluent in the language would have written, and it is usually harder to maintain than what you would have written yourself.
API churn is where the hallucinated functions come from
Mainstream ecosystems rely on stable, standard libraries. requests, express and numpy have looked broadly the same for years, so the model holds a consistent picture of them.
Niche languages tend to have fragmented libraries that move fast, and when the model reaches for something that is not there, it fills the gap with a function that sounds exactly like the one that ought to exist. That is where most API hallucinations come from. Not a missing fact, but a plausible pattern extended one step too far.
Put the four together and you get the practical rule: treat generated code in mainstream languages as a first draft you review, and generated code in anything niche as a suggestion you verify line by line, starting with whether the functions it called are real.
Four tiers, and only the first one is a first draft
Roughly, models are strongest in Python, JavaScript/TypeScript and Java, competent in C++, C# and Go, and drop off sharply in Rust, the functional languages and the lower-resource scripting tools. The tiers below are my own reading of that, informed by multilingual benchmarks such as McEval, which covers 40 languages and publishes a live leaderboard. They are a judgement about review effort, not a benchmark result.
| Tier | Languages | What the output tends to look like |
|---|---|---|
| 1. High | Python, JavaScript, TypeScript, Java | Idiomatic code, standard libraries handled seamlessly, edge cases usually accounted for. Review it the way you would review a competent colleague's pull request. |
| 2. Moderate | C++, C#, Go, PHP | Core algorithmic logic is strong. Error rates climb around pointer arithmetic, memory management and framework-specific boilerplate, so that is where the review time goes. |
| 3. Strict execution | Rust, Swift, Kotlin | Logic is generally sound but benchmark execution rates are low, because the compiler rejects the file outright over things like a lifetime mismatch. Expect iteration, not rewriting. |
| 4. Low | Haskell, Lua, Julia, Racket, Assembly | Frequent hallucinated APIs, synthetic syntax that is not quite the language, non-idiomatic patterns, and high failure rates on anything functionally complex. |
The tier boundaries move as models improve, and they move faster for languages that are gaining public code than for languages that are not. Treat this as a snapshot with a direction of travel, not a fixed ranking.
What this changes is the rewrite maths, not the language you love
Rust has been sitting on my TODO list for years. I have never learnt it properly, though I admire it from a distance. It is fast, close enough to C that the difference rarely matters, and there is no shortage of reports from peers and other companies about the money they saved by rewriting hot paths in it. The case for Rust is real, and nothing above is an argument against it.
But if you are budgeting for a rewrite of your Python application, add a line for this: the LLM you have standardised on is probably weaker at Rust than it is at the Python you are leaving behind. The savings are still there. They just arrive after more review cycles than the pitch deck assumed.
Plan for that, or you will discover it the expensive way, usually at the point where the migration is half done and the schedule is already public.
Where to start
- Work out which tier your target language sits in before the language decision is final, not after. It is a review-cost input, alongside performance and hiring.
- If the answer is tier 3 or 4, budget the review cycles explicitly in the plan rather than absorbing them as a surprise.
- Make dependency verification a standing review step for anything below tier 1. Check the functions exist before you check the logic.
- Do not read a low benchmark score as "the model cannot do this language". Check whether the failure mode is compiler strictness or genuine confusion, because those need different responses.
- Keep the choice under review. The gap narrows unevenly, and a decision made on last year's tiers may not hold.
None of this argues for staying on the language you are on. It argues for pricing the move honestly.
I, for one, welcome our new robot overlords.




