Rye-to-uv migrations go wrong when teams treat them like a brand swap. The hard part is not replacing one Rust binary with another. The hard part is deciding whether your Python workflow is ready to accept uv's fuller contract: more of the project state lives in standard pyproject.toml fields, the authoritative lockfile becomes uv.lock, Python installation and tool execution move under one surface, and a few bits of Rye convenience still do not map one-to-one.[1][2][3][4]
That is also why the move is easier than it first appears. Astral's own Rye migration page is explicit: Rye is no longer developed, users are encouraged to migrate to uv, and if a project does not rely on [tool.rye] sections it should usually work in uv with no further changes.[1] The same page also says uv supports almost all of Rye's features and keeps a similar philosophy and CLI feel.[1] So the real migration question is narrower than "Should we relearn Python packaging?" A better question is: which parts of our Rye workflow were actually standards-aligned already, and which parts were convenience glue we now have to replace on purpose?[1][3][4]
As of 2026-04-10T00:04:37Z UTC, the GitHub API reports that astral-sh/uv has 82,953 stars, 2,926 forks, 2,729 open issues, and a most recent push at 2026-04-09T22:02:03Z.[5] The release feed shows 0.11.6 published on 2026-04-09, following 0.11.5 and 0.11.4 on 2026-04-08.[6] astral-sh/rye, by contrast, is now marked archived by GitHub, shows a most recent push at 2026-02-05T00:39:05Z, and its latest release remains 0.44.0 from 2025-02-26.[7] Those dates matter because they turn the migration argument into an operational one. This is no longer a contest between two equally live tools. It is a cutover from a frozen predecessor into the actively shipping line.
Image context: the cover uses a real GitHub portrait of Charlie Marsh rather than a packaging diagram or terminal screenshot. That choice fits because this migration is not mainly about dependency graph aesthetics. It is about maintainership continuity combined with a changed workflow contract: the same builder lineage, but a broader tool surface and a different set of defaults.[8]
1. The clean part of the migration is broader than most teams expect
The official migration guide starts with the best news first. If your project has no [tool.rye] sections, uv should usually run it without special conversion work.[1] Even where Rye-specific configuration exists, the same guide says the fix is often mostly mechanical: rename [tool.rye] to [tool.uv], move tool.rye.dev-dependencies to standardized dependency-groups.dev, invert a few booleans, and translate index configuration into uv's own tables.[1]
That recommendation makes more sense once you read uv's feature map. uv is not only a package installer. The docs describe one tool that can install Python versions, manage standalone scripts, create and sync full projects, run and install user tools, and still expose a lower-level uv pip interface for legacy package-management workflows.[2] In other words, the successor is not a narrow replacement for one Rye command. It is a wider packaging and environment surface that happens to preserve enough of Rye's philosophy that the first week usually feels familiar.[1][2]
This is why the migration tends to feel smooth for teams that already let standards do most of the work. If dependencies already live in project.dependencies, if requires-python is written down, if dev-only tools are easy to separate into groups, and if local path or Git sources are deliberate rather than magical, uv mostly asks for cleanup, not reinvention.[3][4]
2. The real cutover is a metadata cutover
The sharper boundary appears when you stop translating commands and start translating project intent. Rye's migration page calls out several places where the names or meanings change in subtle but important ways: tool.rye.virtual maps to tool.uv.package with the sense inverted, tool.rye.lock-with-sources becomes tool.uv.no-sources with the boolean inverted again, and Rye's dev-dependency section should move to the newer dependency-groups.dev form.[1]
Those are not cosmetic renames. uv's configuration docs say the presence of a [build-system] table determines whether the current project itself is built and installed into the environment; if no build system is defined, uv installs only dependencies, while a defined build system causes uv to build and install the project package too.[4] The same docs also allow overriding that behavior with tool.uv.package, which is exactly why the old Rye virtual toggle matters during migration.[1][4]
The dependency model is similarly explicit. uv's dependency docs split project state across project.dependencies, project.optional-dependencies, dependency-groups, and tool.uv.sources.[3] That is powerful, but it comes with a boundary that migration plans should say out loud: tool.uv.sources are only respected by uv itself, and dependency-groups are described as recently standardized and not yet supported by every other tool.[3] So the biggest metadata risk is not that uv cannot express your old state. It usually can. The risk is that mixed-tool estates may discover that some formerly implicit Rye behavior now lives in uv-specific tables that downstream tooling will ignore.[3]
This is the point where an engineering manager should slow down. If your workflow really depends on other tools interpreting the same development metadata exactly the same way, you need to separate which fields are standards-level and which are uv-only. Teams that skip that audit tend to blame the migration later for what was actually a tooling-boundary problem from the start.[3][4]
3. uv.lock is not just a new filename
Rye's documentation trained users to think in requirements.lock terms. uv's migration guide says that model is over: uv uses its own lockfile format, uv.lock, and tells users to generate a requirements.txt-style artifact with uv export only when another tool truly requires it.[1] The same page goes further and recommends using uv directly in production and Docker workflows with uv sync during build steps and uv run at runtime, rather than preserving Rye's older lockfile-install pattern through pip install or uv pip install.[1]
That is a real architectural shift. A Rye estate could treat the lockfile as a compatibility object for many tools. uv is pushing teams toward a world in which the lockfile is part of uv's own environment contract first, and only exported outward when necessary.[1] That is usually the right long-term move, but it does change rollout planning. If your CI, Dockerfiles, or deployment scripts still assume that a plain requirements-format lockfile is the primary artifact, you do not yet have a completed migration. You have a new local developer tool with old deployment semantics attached.
uv's environment configuration docs sharpen the same point from the other direction. They say UV_PROJECT_ENVIRONMENT can redirect the project environment path, even toward a system prefix, but they also warn that uv sync removes extraneous packages and can leave a system environment broken.[4] That warning is useful because it shows how opinionated uv's environment reconciliation really is. This is not a tool that wants to politely coexist with drift forever. It wants an authoritative environment and will prune toward it.[4]
4. The missing parity still matters, but it is narrow
Most of the migration story is positive. The parts that still need deliberate replacement are also clear. The official Rye migration guide says that, as of the guide's own uv 0.8 / July 2025 target, uv did not yet provide a built-in task runner comparable to [tool.rye.scripts], and it points users to a still-open feature request plus third-party alternatives used through uv run.[1] That is an important limitation precisely because it has nothing to do with dependency resolution. Teams that used Rye as a lightweight task orchestration layer will need to pick a new habit rather than expect automatic parity.[1]
The Python-command story is similarly close, but not identical. The migration guide says Rye offered a global Python shim that could redirect python and python3 to managed interpreters, while uv's uv python install --default installs direct python and python3 commands rather than a project-aware shim.[1] The same page recommends uv run python for a project shell and uvx python for an isolated interpreter.[1] That is a sensible design, but it means teams should retire any mental model in which the global python command automatically picks up the current project's dependency context.
Rye's shorthand commands for linting, formatting, and tests also do not carry forward as built-in aliases. The guide's replacement is straightforward, uv add --dev ruff, uv add --dev pytest, then uv run ruff check, uv run ruff format, and uv run pytest, but it is still a replacement, not parity.[1] For disciplined teams, that is acceptable. For teams that quietly relied on Rye's shortcuts as social defaults, it is worth writing down before the first migration ticket closes.
5. A rollout shape that usually keeps the migration honest
The highest-signal first month is usually:
- Pick one Rye project with the smallest amount of custom
[tool.rye]state and convert only the metadata tables that the migration guide explicitly maps.[1] - Set or confirm
project.requires-python, because uv's config docs make it part of both syntax expectations and dependency selection.[4] - Decide whether the project should actually be packaged, then check
[build-system]andtool.uv.packagerather than assuming Rye's oldvirtualmeaning survives unchanged.[1][4] - Replace any Rye task shortcuts with explicit
uv run ...,uv tool ..., or a separate task layer, instead of waiting for hidden convenience gaps to surface later.[1][2] - Update CI and container builds to treat
uv.lockanduv syncas the primary install path, exporting requirements-style locks only where another tool truly forces it.[1]
That sequence gives you a real answer quickly. If the project already lived mostly in standards-shaped metadata, the migration will feel nearly boring. If the project depended on Rye-specific convenience state, Docker-era requirements.lock assumptions, or task-runner shortcuts that nobody had named, those problems will show up immediately and in the right order.
Bottom line
Rye-to-uv is a good migration when you read it as a packaging-contract reset, not as an enthusiasm contest. The upstream story is clear: Rye is frozen, uv is the maintained successor, and the easy wins come from the fact that both tools already tried to stay close to Python standards.[1][5][6][7]
The real work sits in three places: translating the remaining [tool.rye] metadata into uv's tables, accepting uv.lock as the primary environment artifact, and replacing the narrow slice of Rye convenience that still lacks built-in one-to-one parity.[1][3][4] Teams that handle those three pieces deliberately will usually find that the migration is smaller than expected and more structural than dramatic.
Sources
- Rye docs, "Migrating to uv": deprecation status, feature-parity guidance, config-key mapping, task-runner gap, Python-shim differences, and
uv.lockguidance. - uv docs, "Features": Python installation, projects, tools,
uv pip, lockfiles, and the broader feature surface. - uv docs, "Managing dependencies":
dependency-groups,tool.uv.sources, source-table boundaries, and standards-level dependency fields. - uv docs, "Configuring projects":
requires-python, build-system behavior,tool.uv.package, project environment paths, anduv syncenvironment boundaries. - GitHub API snapshot for
astral-sh/uv: stars, forks, open issues, and recent push timestamp. - GitHub API release listing for
astral-sh/uv: current release tags and publication timestamps, including0.11.6,0.11.5, and0.11.4. - GitHub API snapshot for
astral-sh/rye: archived status, stars, forks, open issues, and recent push timestamp. - GitHub API profile for Charlie Marsh, including the avatar source used for the article image.