The Day My Build Took Longer Than My Tests
Migrating from Poetry to uv guide isn’t something I planned to write.
I liked Poetry. I trusted it. For years, it felt like the responsible, grown-up way to manage Python projects.
The breaking point wasn’t a failure.
It was a pattern.
In CI, my test suite finished in ~55 seconds.
Dependency installation consistently took 4–5 minutes.
Nothing was broken.
No dependency conflicts.
No resolution errors.
Just a slow, predictable drag on every run.
That was the moment I realized I wasn’t debugging bugs anymore.
I was debugging tooling weight.
Why I Chose Poetry in the First Place
I didn’t adopt Poetry casually.
I chose it because I wanted discipline.
Poetry promised:
- A single source of truth via
pyproject.toml - Deterministic dependency resolution
- Fewer “works on my machine” issues
And for a long time, it delivered.
It helped standardize projects, simplified onboarding, and eliminated arguments over requirements.txt.
It was good enough — until the cost of “nice” tooling became visible.
When “Correct” Starts Feeling Heavy
The problem with Poetry was never correctness.
It was overhead.
Over time, I noticed consistent signals:
- CI installs always took 4–5 minutes
- Docker builds re-resolved dependencies repeatedly
- Poetry had to be installed just to install dependencies
- Debugging meant understanding Poetry internals instead of Python
That’s a dangerous place to be, because it’s easy to ignore… until you can’t.
The Question That Changed My Direction
The question that stuck with me was simple:
Why does dependency installation feel like a build step instead of a setup step?
Poetry was doing a lot:
- Project management
- Environment management
- Dependency resolution
- Script orchestration
- Packaging concerns
I didn’t need all of that in every context — especially not in CI or Docker.
That realization pushed me to look for something narrower.
How uv Entered the Picture
I didn’t go looking for a Poetry replacement.
I discovered uv while trying to make CI faster.
At first, I treated it as “pip, but faster.”
Then I noticed something important.uv doesn’t try to manage your entire project.
It focuses on installing dependencies quickly and deterministically.
That smaller scope turned out to be its biggest strength.
The Feature That Made Me Reconsider Everything
Speed alone wouldn’t have convinced me.
The real shift came from this:
uv installs dependencies directly from
pyproject.tomlwithout requiring a heavyweight project manager at runtime.
No environment orchestration.
No background services.
No lifecycle abstractions.
Just resolution and installation.
That’s when migrating from Poetry to uv stopped feeling risky.
My Poetry Workflow (Before the Switch)
This was my typical setup:
poetry install
poetry run pytestAnd in Docker:
RUN pip install poetry
RUN poetry install --only mainIt worked — but it came with baggage:
- Poetry had to exist in the image
- Dependency resolution ran repeatedly
- CI paid the full cost every time
Migrating from Poetry to uv: The Actual Steps
This is the part most guides skip.
So I won’t.
Step 1: Keep pyproject.toml
I didn’t rewrite anything.uv understands standard PEP-621 metadata directly from pyproject.toml.
No conversion.
No format changes.
That alone reduced risk significantly.
Step 2: Generate a Lockfile with uv
I started with uv’s pip-compatible mode:
uv pip compile pyproject.toml -o requirements.txtLater, once native locking stabilized, I switched to:
uv lockThis generates a native uv.lock.
Both approaches are valid — the key point is deterministic resolution.
Step 3: Install Dependencies
uv pip install --system --no-cache .This was the moment everything clicked.
No virtualenv gymnastics.
No Poetry runtime.
Just dependencies installed.
What Happened to poetry.lock?
I removed it — after verification.
I ran Poetry and uv installs side by side and confirmed that resolved versions matched.
Once uv.lock (or requirements.txt) existed, maintaining two lockfiles made no sense.
The important thing wasn’t which lockfile I used.
It was that versions were pinned and reproducible.
What About Poetry Scripts?
Poetry scripts like this:
[tool.poetry.scripts]
app = "myapp.cli:main"Still work.
These are standard Python entry points.
Without poetry run, you can execute them as:
python -m myapp.cliIf you rely on console scripts in production or Docker, install your package explicitly:
uv pip install --system .No rewrite required.
No Poetry runtime needed.
Version Pinning (The Real Stability Lever)
This needs to be said clearly:
Tool choice does not replace version pinning.
The real stability came from explicit versions:
ruff = "==0.1.9"
black = "==23.12.0"Whether you use Poetry or uv, this matters more than anything else.
uv simply made missing pins obvious faster.
Why This Setup Is Easier to Reason About
The biggest win wasn’t just speed.
It was clarity.
With this setup:
- Dependency resolution is explicit
- Installs are fast enough to repeat often
- CI environments are cheap to rebuild
If something breaks, I know exactly where to look.
The Migration Journey (Low-Risk, No Drama)
Phase 1: Parallel Installs
The environments matched exactly.
Phase 2: CI Cutover
Install time dropped from ~5 minutes to ~55 seconds.
Phase 3: Local Workflow
Onboarding became simpler.
Measured Results
| Metric | Poetry | uv |
|---|---|---|
| Dependency install (CI) | ~4m 30s | ~45s |
| Docker build time | ~6m | ~1m 30s |
| Tooling complexity | High | Low |
| Debug effort | Moderate | Low |
This wasn’t optimization.
It was workflow simplification.
Migration Gotchas (Be Aware)
Before switching, keep these in mind:
- Poetry-specific fields under
[tool.poetry]are ignored by uv - Monorepos may need separate lockfiles per service
- Team members need
uvinstalled locally
None of these are blockers — but they’re worth knowing.
When You Should Not Migrate from Poetry to uv
Poetry is still a strong tool.
If you rely heavily on:
- Poetry publishing workflows
- Integrated environment management
- A single-tool mental model
Then Poetry may still be the right choice.
Final Thoughts
I didn’t migrate from Poetry to uv because Poetry failed.
I migrated because my workflow outgrew it.
This migrating from Poetry to uv guide isn’t about declaring a winner.
It’s about choosing the right level of abstraction.
uv gave me:
- Speed
- Clarity
- Fewer surprises
And for infrastructure tooling, that’s everything.
