On it being a "mess": When writing applications, I only devoted at most 2 brain cells to installing dependencies even before Claude existed, and that was enough for npm, uv, or cargo to just work. Never used any of these flags. Meanwhile pip etc demand way more attention to not ruin things.
Yeah, this is when it really matters that they wrote it in a CPU-performant language. There have been times I pointed uv at a random pip-managed GitHub project to rescue it because the author forgot to specify some versions and entire deps in requirements.txt. It even took uv a bit of chugging to find an overlap. Also wow, those packages had a lot of pointless breaking changes.
I don't do Python all that often, but I got sold on uv when a year or so ago I wanted to try to fix a small bug in a relatively niche Python open source I was trying to use, and it used a packaging tool I hadn't even heard of before (pdm). I was able to run `uv tool run pdm <whatever other arguments I needed>` without having to know anything about how pdm worked or worry about how to properly install yet another python tool and figure out whether I needed to be concerned about whether it might try to use system (or user local) versions of packages I had installed or get pointed at a magic hidden directory that I had to create first. When I realized I might never have to care about that ever again for any Python tool, I was hooked.
Even with that, uv took some time at 100% CPU during dep resolution, which I imagine would've been much slower if in pure Python instead. Unless pip's backtracking is already in C.
I don't think it's true at all that "dropping features" makes uv fast. As an author of uv, I think that particular section of the article is way off base.
I don't find that to be a particularly strong argument that a Python package manager would be possible to be in the same ballpark of performance as a Rust one. There's quite a lot of it that either doesn't support the idea of Python being capable of the same level of performance or actively supports the opposite.
> PEP 658 went live on PyPI in May 2023. uv launched in February 2024. uv could be fast because the ecosystem finally had the infrastructure to support it. A tool like uv couldn’t have shipped in 2020. The standards weren’t there yet.
> Other ecosystems figured this out earlier. Cargo has had static metadata from the start. npm’s package.json is declarative. Python’s packaging standards finally bring it to parity.
Are there any tools written in Python since then that are anywhere as close to as fast as uv when operating on packages that use this newer format? I've yet to hear of one.
> No .egg support. Eggs were the pre-wheel binary format. pip still handles them; uv doesn’t even try. The format has been obsolete for over a decade.
It seems dubious that adding support for egg would prevent uv from being as fast on packages that don't use that format.
> Virtual environments required. pip lets you install into system Python by default. uv inverts this, refusing to touch system Python without explicit flags. This removes a whole category of permission checks and safety code.
Passing `--user` to `pip install` doesn't seem to make things noticeably faster in most cases.
> Parallel downloads. pip downloads packages one at a time. uv downloads many at once. Any language can do this.
Any language with a global interpreter lock certainly can't do that as effectively as a language without one.
> Python-free resolution. pip needs Python running to do anything, and invokes build backends as subprocesses to get metadata from legacy packages. uv parses TOML and wheel metadata natively, only spawning Python when it hits a setup.py-only package that has no other option.
This one is pretty self-explanatory.
The section at the end somewhat overlaps with the parts I called out, and I recognize that the author of that post is almost certainly more familiar with the specifics of uv and Python package management than me, but with a lack of concrete example of a Python package manager that's anywhere close to the level of performance of a Rust one, I can't help but feel like pip would probably be quite noticeably slower than a Rust alternative written with an identical feature set (whether that feature set is "what pip currently supports" or "the minimal set of features described here"). I could imagine it being something like, pip could maybe be optimized from being 50x slower than uv to only 5x, but if that's the case, I think "Rust isn't the main reason it's fast" is a bit of an oversimplification when the discussion is about comparisons to alternatives that are all written in Python.
All I know is internally they were recommending people use Gemini CLI until they switched to recommending Jetski/Windsurf CLI. Even before that, I could tell Jetski was way ahead, but still people from Gemini CLI were trying to convince me to use it.
Jetski/Antigravity is a better piece of software. The Gemini CLI codebase looks like someone tried to vibe-code a Claude code clone in nodeJS, as it's simply not maintainable.
That being said, until recently Gemini CLI was better. It had support for persistent policies on what code could run without asking and had good extension hooks to allow you write extensions that influence policy (to perform complex logic like rewriting tool calls before they are executed).
Antimatter/Jetski only recently added support for remembering what commands are "always allowed" between sessions, the extension framework (excuse me, "plugins") has fewer features, and hooks have much less power than with Gemini CLI (and can't come bundled with extensions).
Maybe I'm just a cranky old greybeard, but to me, the fact that these CLI tools are written in Node, React, etc. says a lot about the type of devs that are building them
I don't mind if they use nodejs for this, but the Ink/React stuff is annoying. It wastes space and even makes it hard to copy-paste. Wish it could open a few pipes for raw in/out as an alternative.
There's nothing wrong with writing CLI stuff in Node. It has interfaces to work with terminals built-in that aren't unlike what you'd find in any other language. I say this as someone who has worked with ncurses in C a decent amount (and there is a reason I try to avoid doing so anymore!)
React, however, I do find questionable, even having read about and understanding the idea behind Ink.
Gemini CLI seemed more extensible, but the internal Gemini CLI didn't come with whatever it needed to deal with Google's internal tooling, and Jetski did. You were supposed to install those yourself in Gemini CLI, which I did, and it worked until it broke later, then I was like nah I'm not interested in keeping up with this. Which is of course not applicable to external users, but it suggested they were putting more eggs in the Jetski basket.
Also, Gemini CLI takes like 30-60s to start up, which is unacceptable when I'm starting it frequently in separate terminals and Jetski and Claude both start sorta instantly. I thought it must've been a dogfooding issue, but the external Gemini CLI seems just as slow. They're all similar stacks afaik, nodejs + react + ink.
Well, the first thing the "new" antigravity did was f^&* up all of my existing projects by replicating them (with one replica per conversation within each project). That's really bad.
I now have 45+ projects pointing at a the same 5-7 folders (the actual projects). Can I delete those extra projects? The warnings are sure telling me not to.
Also it doesn't even matter because the real way to use both uv and npm is to switch everything to = and only update manually, rather than trusting non-major updates not to break anything
The distinction here is on application vs library, IMO. I basically agree that applications, as a default, `==`'ing everything makes sense.
For libraries, having loose bounds might mean that users upgrade and hit issues due to a lack of an upper bound. But given how lightly maintained most projects are, the risk of upper bounds simply getting in the way are higher IMO.
(Put an upper bound if you know of an issue, of course!)
It's a bit tricky though. Django deps in particular tend to want to explicitly check support for newer versions, but the more I think about it the more I ask myself if this is the right strategy
The lockfile does more than just pin the versions of your immediate deps, so one might reset it for some other reason. Or you might want to update individual packages without caring about the specific commands for that, so you edit the package file, delete lockfile, reinstall.
But if I use uv sync and the package I want I don't ever need to toss the whole file right. In rust I'd never sign off on a mr that just randomly updated lots of deps with no reason tied to the issue they were resolving
non major updates in the npm ecosystem are pretty reliable in my experience; my much more limited python experience suggests that semver is much less respected on that side of the fence
You can write C++ in a way that's similar to C if you want and run into some of the same UB. Normally I don't like the "C/C++" thing, but in this context it makes sense.
reply