Nuno Ferreira wrote a 200 line tool that turns a folder of invoices into one summary sheet, and it worked. He sent it to a colleague in the same office, who ran it and got an error on line 4 about a function that did not exist. The code was identical. The versions of one library underneath it were not, and the whole of the second day went on that.
The syntax of the language took Nuno a weekend. The part that cost him a day is not syntax at all, and it is the same part in every language on this topic: how a project says what it depends on, and how a second machine is made to agree.
What a language actually asks you to learn
A tutorial covers the loops, the functions, the way the language spells a list. That material is real and it is also the small half. Underneath it sits a package manager, a lockfile, a build step, a test runner, a formatter and a way of publishing, and none of those appear in the tutorial because none of them are the language. They are the reason a working program stops working on somebody else's computer, which is the event that actually ends most people's first project.
The useful thing is that the shape underneath is the same everywhere. Python's uv, JavaScript's npm and Rust's cargo are the same tool with different names: each reads a file listing what the project needs, resolves that into exact versions, and writes those exact versions down. Learning the shape once means the second language costs a fraction of the first, and it is why what the choice actually depends on, and what it does not matters less than people expect.
The four facts that end the second day
Nuno's error came from a version. The library he used had published a new release between his install and his colleague's, and a function had moved. The mechanism that prevents this is written down in three characters of punctuation.
Semantic versioning: 2.4.1 means major 2, minor 4, patch 1. The rule the publisher promises to follow is that a patch fixes something, a minor adds something without breaking what was there, and a major is allowed to break. So the number is not decoration. It is a claim about what will happen to your code if you accept it.
What a project file usually records is a range rather than a version, and the punctuation carries the range. A caret in ^2.4.1 accepts 2.9.0 and refuses 3.0.0. That is the setting Nuno had, and it is the right default: it takes fixes and stays on the side of the promise. It also means that installing on a Tuesday and installing on a Thursday can genuinely give two different libraries, which is exactly what happened.
The resolution is the second file, the one nobody reads. A lockfile pins the exact versions so a second machine gets the same ones. It is generated rather than written, it belongs in the repository, and its whole job is to record the resolution of every range at one moment so that it can be reproduced. Nuno's project did not have one committed. With uv.lock in the repository his colleague's install would have been the same install, and the second day would have been an hour.
uv add pandaswrites the range into the project file and the exact version into the lockfileuv syncon the second machine installs from the lockfile rather than resolving againgit add uv.lockis the step that was missing
How much the language does for you, and how much you assemble
Languages differ enormously in how much arrives in the box, and the difference is countable. Writing the same trivial server that returns one line of text, a Go project installs 0 packages, because the server is in the standard library. The Rust version installs 4 and the Python version installs 2. The JavaScript version installs 49, which is why the only language a browser runs is also the one with the largest package registry and the deepest dependency trees. Neither is better, and the difference shows up as a number Nuno had never looked at. His invoice tool declares four dependencies and installs thirty one, because those four bring their own, and uv tree is the command that prints the whole thing on one screen. A Go version of the same tool would have declared none. The four are not carelessness; they are the reading of a spreadsheet format he was never going to write himself.
This is the axis worth checking before starting a language rather than after. It predicts what the first week feels like, what the tooling arguments will be about, and how much of the code you ship will be code somebody else wrote.
Where people stall
The stall is the second language, and it is caused by the first one going well. Somebody learns readable, everywhere, and the usual first language, gets comfortable, and concludes that the comfort was fluency in programming. Then they open memory safety without a garbage collector and meet a compiler that refuses code they cannot see anything wrong with, and read the refusal as a verdict on themselves. It is not. The first language taught syntax and one ecosystem; the second one is charging for the assumptions the first one let them keep.
The other stall is smaller and more common. People try to learn a language by reading about the language, and never install anything, because the install is the boring part. The install is the subject.
What to open, in 2026
For Python, uv is the current answer and it replaced a stack of three: pip for installing, virtualenv for isolation and pyenv for runtime versions. It does all three, it is fast enough that the speed is a genuine feature, and it writes the lockfile Nuno needed. For JavaScript, npm ships with Node and remains the default, having taken over from copying a folder of libraries into the project by hand. For Rust, cargo arrives with the language and there has never been a second option, which is the quiet argument for the languages that decided this early.
A coding assistant will write correct code in a language you have never opened, and it is at its most confident exactly where the ecosystem differs from the one you know. It will not tell you that the lockfile was not committed.
What none of this decides is which of the twelve below to spend the next six months on, and the honest answer depends on where the code has to run rather than on the language, which is the argument made across the rest of programming, from the first variable to a deployed system.