Migrating to uv from pip + venv + requirements.txt: a pragmatic path
You don't have to commit to a full migration to benefit from uv. The pip compatibility layer is a zero-effort speedup. Move source-of-truth to pyproject.toml only when it earns its keep.
Most migration guides assume you want to swap your build system on Monday morning. This one doesn’t. A working pip + venv + requirements.txt project is not broken; it’s just slow and a little brittle at the edges. uv lets you trade off those problems one at a time.
The order matters. Day one buys you speed for free. Week one buys you a real lockfile. Month one — if and only if you’d actually use them — buys you pyproject.toml-native workflows.
Same requirements.txt. Same packages. Same resolved versions. 10–100× faster, depending on cache state. CI gets faster on the next push; local installs feel instant.
uv pip is a drop-in for the pip CLI surface that most projects actually use — install, uninstall, freeze, list, show, compile. It reads the same flags, talks to the same PyPI, writes wheels into the same .venv/. Nothing downstream of pip install notices the swap.
From pip: yes, you still get a .venv/ directory. No, you don’t need to source it — uv pip install finds it via VIRTUAL_ENV or the nearest .venv/. If you’d rather keep activating, that still works.
From Node: this is the npm → pnpm install story. Same package.json, faster install, content-addressable cache. You’re not converting the project; you’re swapping the installer.
When to keep requirements.txt
There are honest reasons to leave requirements.txt in place, and pretending otherwise gets migration plans rejected on the third slide:
Docker layer caching.COPY requirements.txt . followed by pip install -r requirements.txt is a pattern every reviewer on your team already understands. The cache key is one file. Multi-stage uv-native builds work great (Post 6 covers them), but they’re a larger change.
Legacy CI scripts. Anything that greps requirements.txt for security scanning, license auditing, or vendor SBOM generation will keep working if the file is still there.
Ops handoffs. Teams that deploy your code without touching it shouldn’t have to learn a new tool to read your deps.
Public-facing reproducibility.pip install -r requirements.txt is the universal “I can run this” contract for tutorials, blog posts, and Kaggle notebooks.
None of these are bad reasons. None of them are reasons to keep pip as the installer, though.
The hybrid pattern
The middle ground that scales: pyproject.toml becomes the source of truth, and requirements.txt becomes an exported artifact.
Drop hashes if your downstream tools choke on them; keep them (--no-hashes off) for hash-verified installs in production. Commit the generated file or regenerate it in CI — both are valid.
Same input format. Same output format. Hashes, extras, constraints, custom indexes — all supported. This is the lowest-friction migration path of any of them: you keep your workflow, your file layout, your CI, and you get a faster compile.
When your team is ready, graduate from requirements.in to [project.dependencies] in pyproject.toml and from uv pip compile to uv lock. Until then, you’ve already won most of the speedup.
Gotchas
Editable installs.uv pip install -e . works inside a uv venv exactly like it does with pip. Inside a uv-managed project, prefer uv sync — it handles the editable install of the current package automatically.
Private indexes. Move --extra-index-url into pyproject.toml:
pyproject.toml
Set credentials via UV_INDEX_INTERNAL_USERNAME / UV_INDEX_INTERNAL_PASSWORD env vars in CI. UV_INDEX_URL is also honored if you’d rather configure entirely via env.
--system vs project mode.uv pip install --system installs into the active Python without a venv — the right choice in Docker base images where you don’t want .venv/ indirection. Outside Docker, you almost never want this; uv will create a .venv/ for you.
uv pip is not uv add.uv pip install fastapi installs into the current venv but does not touch pyproject.toml or uv.lock. That’s a feature during migration — and a footgun once you’ve adopted pyproject.toml. Switch to uv add fastapi the moment you commit a pyproject.toml.
From Node: if you’ve done the npm → pnpm move, you know this dance. Lockfile semantics tighten, the installer gets faster, and the legacy package-lock.json (or here, requirements.txt) sticks around as long as something downstream still reads it.
Next in series: uv tool and single-file scripts: pipx and shebang-Python, replaced.
About the author
Prakash Poudel Sharma
Engineering Manager · Product Owner · Varicon
Engineering Manager at Varicon, leading the Onboarding squad as Product Owner. Eleven years of building software — first as a programmer, then as a founder, now sharpening the product craft from the inside of a focused team.
A six-part series on uv — why it replaces pip, venv, pyenv, pipx, and pip-tools; how to start a new Python project in 2026; and a pragmatic migration path that keeps requirements.txt where it earns its keep.
What did you take away?
Thoughts, pushback, or a story of your own? Drop a reply below — I read every one.
Comments are powered by Disqus. By posting you agree to their terms.