The JavaScript ecosystem has cycled through npm, then yarn, then pnpm, and now bun across barely a decade. Python went from pip to pipenv to poetry to pdm to hatch and now uv, each iteration carrying a new lock file format and a fresh promise to fix what the previous tool got wrong. Simon Willison captured the frustration cleanly in a recent piece: package managers need to cool down.
The frustration is real and well-documented. But there is a specific mechanism worth unpacking, because the churn is not just inconvenient. It is actively undermining some of the foundational guarantees that package managers are supposed to provide.
What a Lock File Actually Promises
A lock file is a reproducibility contract. When you commit yarn.lock or poetry.lock or uv.lock to your repository, you are encoding a specific resolution of your dependency graph so that every developer and every CI runner gets the same tree. The promise is: this worked, and you will get exactly this.
That promise holds only as long as the tool reading the lock file stays stable. When yarn 2 (Berry) landed in 2020 with its Plug’n’Play system, it introduced a fundamentally different resolution model and a new lock file format incompatible with yarn 1. Teams that upgraded mid-project had to re-resolve their entire dependency graph and retrofit their tooling. The reproducibility guarantee did not transfer.
pnpm has gone through similar evolution. Its pnpm-lock.yaml format changed in breaking ways across major versions, and its use of a content-addressable store with hard links, while genuinely clever for disk savings, means a corrupted or misconfigured store produces failures that are opaque to developers unfamiliar with the architecture.
uv, the Rust-based Python package manager from Astral, is fast. Genuinely, impressively fast. But it has been iterating on lock file semantics, workspace handling, and resolution behavior at a pace that makes pinning a specific uv version in CI a non-optional practice. That is fine for a young tool, but it matters when teams reach for it to replace pip in production environments because someone showed them a benchmark.
The Supply Chain Problem Is Not a Package Manager Problem
A persistent misconception is that a faster or stricter package manager will reduce supply chain risk. It will not, because almost every significant supply chain incident has had nothing to do with the package manager’s feature set.
The event-stream incident in 2018 happened because a popular npm package’s author transferred ownership to an unknown contributor, who added a targeted payload aimed at a specific bitcoin wallet. npm’s resolution algorithm and install speed were irrelevant. The vector was trust delegation.
The xz-utils backdoor discovered in March 2024 (CVE-2024-3094) was a two-year social engineering campaign. The attacker, operating under the name Jia Tan, contributed legitimate patches over years, built credibility with maintainers, and eventually merged a backdoor into a compression library used in SSH authentication across major Linux distributions. No package manager could have prevented it, because the attack worked through the same mechanisms as legitimate open source collaboration.
The dependency confusion attacks documented by Alex Birsan in 2021 exploited how package managers resolve private versus public packages when both share a name. The fix was a configuration and namespace convention change, not a new tool.
What all of these have in common: the vulnerability was in the ecosystem’s trust model, not the resolver’s algorithm. A new package manager written in Rust that installs packages ten times faster is still executing the same postinstall scripts from the same package authors with the same level of cryptographic verification, which in most ecosystems means none at runtime.
The Python Situation
Python’s packaging story deserves specific attention. For years the running complaint was that Python had too many packaging tools and no consensus. pip was slow and had no native lock file support. pipenv showed promise then stalled in maintenance. poetry worked well for many cases but had its own quirks around dependency resolution and editable installs.
The pyproject.toml standard (PEP 518, accepted 2016) tried to unify project metadata, but tool fragmentation continued for years after because different tools interpreted the same standard differently or added proprietary extensions.
uv is genuinely good. Its resolver is fast, its error messages are readable, and its compatibility with the existing pip and PyPI ecosystem is a serious engineering achievement. Astral, the company behind it, also maintains ruff, which has become a widely adopted Python linter, so there is precedent for them shipping tools that land and stay.
But Astral is a VC-backed company, and uv is their path to relevance and eventual monetization in the Python ecosystem. That is not a reason to distrust the tool, but it is a reason to think carefully about what “stable” means for a commercial product versus for a community standard. pip is governed by the Python Packaging Authority and moves conservatively by design. uv’s development velocity is driven partly by competitive positioning and a funded engineering team, which produces faster feature shipping but also more frequent behavioral changes in the years before an API freeze.
A developer asking how to manage Python dependencies in 2026 will still encounter conflicting advice across documentation written in 2018, 2021, and last month, each reflecting a different tool in a different state of community adoption. The Python Packaging User Guide has been working to consolidate this, but the surface area of outdated tutorials across the internet is effectively permanent.
What Cargo Got Right
Cargo, Rust’s package manager, is the useful counterexample here. Its lock file format has been remarkably stable across years of development. Breaking changes to resolution behavior have been rare and well-telegraphed through edition mechanisms. The Cargo.toml manifest format evolves deliberately, with new fields added behind explicit feature gates. When Cargo does change something, the migration path is documented and typically automated.
Cargo benefits from being developed alongside a language that treats stability as a first-class commitment, as evidenced by Rust’s edition system which allows the language to evolve without breaking existing code. But the attitude is transferable. It reflects a belief that the people using your tool have built real things on top of it, and those things deserve to keep working.
npm, for all the chaos of its early years, has moved in this direction over time. After the disruption of package-lock.json arriving in npm 5 (2017), subsequent major versions maintained compatibility rather than replacing it. The format has grown but not fractured.
The Meta-Dependency Problem
Every package manager you adopt is itself an unversioned dependency in your stack. It runs with your user’s permissions, executes arbitrary lifecycle scripts, and has network access to the registries it talks to. You are trusting it to behave deterministically across versions, to not change resolution behavior under you between CI runs, and to remain actively maintained.
When you swap to a new package manager because it is faster, you are trading known (if slow) behavior for unknown behavior, faster. That trade can be worth it, but it should be made consciously, with an understanding of what is still moving and what has stabilized.
The part that gets missed in the benchmark-driven adoption pattern is that the old tool’s weirdness is at least documented. Someone has already hit your edge case with pipenv or yarn classic and written about it. With a tool in rapid development, you are on your own when you find the resolution behavior that the changelog did not mention.
What Slowing Down Looks Like
“Cool down” does not mean stop. It means stabilize the interfaces before expanding the feature surface.
For package managers, the interfaces that matter most are: the lock file format, the configuration schema, and the install behavior given identical inputs. When those change in breaking ways, the cost is distributed across every team running the tool in CI, every getting-started tutorial that now has outdated instructions, and every project that gets an inexplicable install failure because someone upgraded the tool without reading the migration notes.
The signal worth watching is whether a tool publishes a compatibility policy, not just a changelog. Does it distinguish between patch-safe and potentially-breaking changes in resolution? Does it provide a formal definition of what the lock file format guarantees? Does it commit to a period of stability before the next major version? These are the commitments that let teams build on top of a tool rather than just chasing it.
The ecosystem keeps cycling through new tools because each one genuinely solves a real problem with the previous one. The pattern is not irrational. But at some point the cumulative cost of the cycle, the re-learning, the CI changes, the outdated tutorials, the breakage, exceeds the incremental gains from the latest resolver algorithm. Figuring out where that point is, and building tools that acknowledge it, is the part the ecosystem has not gotten to yet.