El día que me di cuenta
Era un día por la tarde. Tenía una demo al día siguiente y necesitaba añadir una librería más al proyecto. Nada del otro mundo, solo pandas para procesar unos CSVs. Tecleé con confianza:
poetry add pandasY ahí empezó. La ruedita girando. "Resolving dependencies..." decía la consola. Un minuto. Dos minutos. Me levanté a beber. Tres minutos. Empecé a revisar emails. Cuatro minutos. Ya estaba navegando por LinkedIn cuando, finalmente, después de casi cinco minutos, terminó.
Cinco. Minutos. Para añadir pandas.
Me quedé mirando la pantalla y pensé: "Tiene que haber una manera mejor".
No era la primera vez que me pasaba. Llevaba meses justificando esas esperas. "Es que resuelve las dependencias correctamente", me decía. "Es el precio de tener un poetry.lock consistente". Pero ese día, se me acabó la paciencia.
Esa noche empecé a investigar alternativas. Y me topé con uv.
Mi historia con Poetry
Empecemos por el principio. Para ser justo con Poetry, tengo que reconocer que me cambió la vida cuando lo descubrí. Venía del caos absoluto que era manejar dependencias en Python.
¿Te acuerdas? Tenías que crear un requirements.txt a mano (o con pip freeze, que incluía todo). Si querías separar dependencias de desarrollo, hacías otro archivo requirements-dev.txt. Cada proyecto una aventura.
Poetry llegó para resolver esa aventura.
El pyproject.toml era limpio y legible. El poetry.lock garantizaba que todos en el equipo tuvieran exactamente las mismas versiones. Los comandos eran intuitivos: poetry add, poetry remove, poetry install. Todo tenía sentido.
Durante años, Poetry fue mi herramienta de cabecera. Lo recomendaba en todos mis proyectos.
Pero el amor se enfría
Con el tiempo, empezaron a aparecer las grietas.
Los proyectos crecieron. De 5-6 dependencias pasamos a 20, luego a 40. Proyectos de data science con numpy, pandas, scipy, scikit-learn, matplotlib... cada una con sus propias dependencias transitivas. De repente, cada operación con Poetry se volvía dolorosamente lenta.
poetry add tardaba eternidades. poetry update era una excusa perfecta para una pausa de café de 10 minutos. Incluso poetry install en un repo recién clonado podía tardar 5-7 minutos en un proyecto mediano.
Y no era solo la velocidad. A veces Poetry se quedaba atascado resolviendo dependencias, dando vueltas en círculos.
Otras veces, las resoluciones de dependencias eran...cuestionables. Te obligaba a usar versiones específicas que entraban en conflicto con otras herramientas.
El caché de Poetry también se corrompía de vez en cuando, obligándome a buscar dónde narices guardaba sus archivos en mi sistema (~/Library/Caches/pypoetry en Mac, por cierto) y borrar todo manualmente.
Pero seguía ahí. Por inercia. Porque ya lo conocía. Porque cambiar daba pereza.
El primer encuentro con uv
Así que un día busqué en Google "Poetry alternative faster". Entre los resultados apareció uv, con un claim que me pareció casi cómico: "An extremely fast Python package installer and resolver, written in Rust".
"Sí, claro", pensé. "Otro reemplazo de pip que promete ser rápido".
Pero seguí leyendo. Era de Astral, los que hicieron Ruff, el linter que efectivamente es ridículamente rápido. Y había benchmarks. Muchos benchmarks. Y todos mostraban a uv siendo 10-100 veces más rápido que Poetry.
No puede ser verdad, pensé. Pero la curiosidad pudo más. Lo instalé y creé un proyecto de prueba:
mkdir test-uv
cd test-uv
uv initBoom. En menos de un segundo tenía un pyproject.toml limpio y una estructura básica. Nada revolucionario hasta ahí.
Luego vino la prueba de fuego:
uv add pandasPresioné Enter y... ¿ya? ¿Ya terminó? Miré el reloj. Habían pasado 7 segundos. SIETE. SEGUNDOS.
Lo mismo que Poetry había tardado casi 5 minutos esa tarde.
"Ok, pero pandas es solo un paquete", pensé. "Vamos a probar algo más pesado".
uv add numpy pandas matplotlib seaborn plotly nltk spacy scipy scikit-learnUn proyecto típico de data Science, con unas cuantas de dependencias. En Poetry esto era un café de 10 minutos fácil.
25 segundos. Lo hizo en 25 segundos.
Me quedé mirando la pantalla sin poder creerlo. Volví a borrar el .venv y el uv.lock. Lo repetí. Mismo resultado.
En ese momento supe que no había vuelta atrás.
Entendiendo qué es realmente uv
Lo primero, necesitaba entender qué era exactamente uv. Porque no es solo un Poetry más rápido. Es mucho más que eso.
uv aspira a ser la herramienta unificada para todo lo relacionado con Python. No solo gestiona paquetes como pip. No solo maneja proyectos como Poetry. Va más allá:
Reemplaza pip: instala paquetes del PyPI
Reemplaza pip-tools: gestiona dependencias con archivos de lock
Reemplaza Poetry/Pipenv: maneja proyectos completos
Reemplaza pyenv: descarga e instala versiones de Python
Reemplaza pipx: ejecuta herramientas en entornos aislados
Reemplaza virtualenv: crea y gestiona entornos virtuales
Un todo en uno.
Y está escrito en Rust, lo que explica la velocidad. Mientras Python tiene que interpretarse, Rust compila a código nativo que vuela. Es la misma razón por la que Ruff es tan rápido comparado con Pylint o Flake8.
La migración: más fácil de lo que esperaba
Decidí empezar con un proyecto mediano: una API de FastAPI con unas 25 dependencias. Nada crítico, pero lo suficientemente complejo como para ser una buena prueba.
Paso 1: Backup (por si acaso)
git checkout -b test-uv
cp poetry.lock poetry.lock.backupSiempre con una red de seguridad.
Paso 2: Instalar uv globalmente
Ya lo tenía instalado de mis experimentos, pero el comando es:
curl -LsSf https://astral.sh/uv/install.sh | shO si tienes Homebrew en Mac:
brew install uvPaso 3: La migración en sí
Aquí viene lo bonito: uv entiende el formato de pyproject.toml de Poetry. No perfectamente, pero bastante bien.
En el directorio del proyecto:
# Eliminar el entorno virtual viejo de Poetry
rm -rf .venv
# Eliminar el lock de Poetry
rm poetry.lock
# Dejar que uv haga su magia
uv syncY... funcionó. Así de simple. uv leyó mi pyproject.toml, resolvió todas las dependencias, creó un nuevo .venv, instaló todo, y generó un uv.lock.
Todo el proceso: 23 segundos.
En Poetry, solo el poetry install inicial solía tardar 5 minutos en este proyecto.
Paso 4: Ajustes menores
No todo fue color de rosa. Hubo algunos detalles que ajustar:
Scripts de Poetry:
En Poetry tenía esto en pyproject.toml:
[tool.poetry.scripts]
dev = "uvicorn app.main:app --reload"
test = "pytest"En uv, esto se convierte en:
[project.scripts]
dev = "app.main:main"Básicamente cambiar tool.poetry.scripts por project.scripts.
Nota: Los scripts deben apuntar a funciones Python, no a comandos de shell. Para ejecutar comandos como pytest o uvicorn, simplemente usa uv run pytest o uv run uvicorn app.main:app --reload.
Grupos de dependencias:
Poetry usa [tool.poetry.group.dev.dependencies] para dependencias de desarrollo. uv soporta dos formas modernas:
Para grupos de dependencias (recomendado, siguiendo PEP 735):
[dependency-groups]
dev = [
"pytest>=7.0.0",
"black>=23.0.0",
"ruff>=0.1.0",
]O usando dependencias opcionales:
[project.optional-dependencies]
dev = [
"pytest>=7.0.0",
"black>=23.0.0",
"ruff>=0.1.0",
]La diferencia principal: dependency-groups es para dependencias que NO se distribuyen con el paquete (desarrollo, testing), mientras que optional-dependencies define "extras" que los usuarios pueden instalar opcionalmente.
Source repositories personalizados:
Si usabas repos privados o PyPI alternativo en Poetry:
[[tool.poetry.source]]
name = "internal"
url = "https://pypi.internal.company.com/simple"En uv es:
[[tool.uv.index]]
name = "internal"
url = "https://pypi.internal.company.com/simple"Paso 5: Actualizar el CI/CD
Si tienes GitHub Actions con Poetry. Lo puedes actualizar a:
- name: Install uv
uses: astral-sh/setup-uv@v4
- name: Install dependencies
run: uv sync
- name: Run tests
run: uv run pytestResultado: el pipeline que tardaba 8 minutos ahora tarda menos de 3. Solo por cambiar a uv.
Los comandos que uso cada día
Después de probar uv, estos son los comandos que forman parte de mi rutina diaria:
Iniciando proyectos nuevos
uv init mi-proyecto
cd mi-proyectoEsto crea un proyecto básico con estructura recomendada. Si quieres más control:
uv init --lib mi-libreria # Para una librería publicable
uv init --app mi-app # Para una aplicaciónTrabajando con dependencias
Añadir paquetes (mi comando más usado):
# Dependencias normales
uv add requests pandas
# Dependencias de desarrollo
uv add --dev pytest black ruff mypy
# Con versiones específicas
uv add "fastapi>=0.100.0,<0.200.0"
# Desde Git
uv add git+https://github.com/usuario/repo.git
# Extra opcional
uv add "pandas[excel]"Eliminar paquetes:
uv remove requestsActualizar dependencias:
# Actualizar todo
uv lock --upgrade
# Actualizar un paquete específico
uv lock --upgrade-package fastapi
# Ver qué se puede actualizar
uv tree --outdatedVer el árbol de dependencias:
uv treeEsto es oro puro cuando tienes conflictos. Puedes ver exactamente qué depende de qué.
Ejecutando código
Esta es una de mis partes favoritas. No necesitas activar el entorno virtual manualmente:
# Ejecutar un script
uv run python mi_script.py
# Ejecutar pytest
uv run pytest
# Ejecutar cualquier comando en el entorno
uv run black .
uv run mypy src/uv automáticamente activa el entorno virtual correcto. Adiós a los source .venv/bin/activate.
Gestionando versiones de Python
# Ver versiones de Python disponibles
uv python list
# Instalar una versión específica
uv python install 3.12
# Instalar la última versión
uv python install
# Ver versiones instaladas
uv python list --only-installed
# Fijar versión para el proyecto
uv python pin 3.12Esto añade una línea requires-python = ">=3.12" a tu pyproject.toml y crea un archivo .python-version.
Ya no necesito pyenv. uv lo hace todo.
Scripts y herramientas únicas
¿Necesitas ejecutar un script rápido con dependencias específicas sin contaminar tu proyecto?
# Ejecutar código inline con dependencias temporales
uv run --with requests --with beautifulsoup4 python script.py
# O directamente desde la línea de comandos
uv run --with httpx python -c "import httpx; print(httpx.get('https://api.github.com/users/github').json())"Las dependencias se instalan en un cache temporal y no tocan tu proyecto.
También puedes ejecutar herramientas globales:
# Ejecutar ruff sin instalarlo globalmente
uvx ruff check .
# Ejecutar cualquier herramienta del PyPI
uvx black .
uvx mypy src/uvx es el reemplazo de pipx. Descarga la herramienta, la ejecuta, y limpia todo automáticamente.
Comandos de debugging
# Ver información del cache
uv cache dir
uv cache prune # Limpiar cache viejo
# Reinstalar todo desde cero
rm -rf .venv uv.lock
uv sync
# Verbose mode para ver qué está pasando
uv sync -v
uv sync -vv # Aún más verboseLas ventajas que no esperaba
Más allá de la velocidad (que por sí sola ya justifica el cambio), he descubierto ventajas que no esperaba:
1. El caché inteligente
uv tiene un sistema de caché global en tu máquina. Si instalas requests en un proyecto, y luego lo instalas en otro, la segunda vez es instantánea. Reutiliza los paquetes ya descargados.
Esto significa que crear nuevos proyectos o cambiar de rama es prácticamente instantáneo.
2. Compatibilidad con pip
uv es compatible con pip. Puedes hacer:
uv pip install requests
uv pip list
uv pip freezeTodos los comandos que conoces de pip funcionan. Esto hace que migrar sea menos aterrador.
3. Resolución de dependencias más inteligente
uv es más flexible que Poetry al resolver dependencias. Poetry a veces se pone muy estricto y se niega a resolver configuraciones perfectamente válidas. uv es más pragmático.
4. Herramientas integradas
No necesito pipx, pyenv, virtualenv, pip-tools... todo está en uv. Menos herramientas que mantener actualizadas, menos configuraciones que recordar.
5. Actualizaciones constantes
Astral libera nuevas versiones de uv cada pocas semanas con mejoras y nuevas características. El proyecto está activamente desarrollado.
Para actualizar uv:
uv self updateCasos de uso reales
Algunos escenarios reales donde uv puede ser muy útil:
Caso 1: Onboarding de nuevos dev
Antes: "Instala Python, instala Poetry, clona el repo, ejecuta poetry install, espera 10 minutos, reza para que no haya errores".
Ahora: "Instala uv (curl -LsSf https://astral.sh/uv/install.sh | sh), clona el repo, ejecuta uv sync, empieza a trabajar en 30 segundos".
El onboarding se reduce de 30-40 minutos a menos de 5.
Caso 2: CI/CD
Las pipelines de CI/CD son significativamente más rápidas. En un proyecto mediano:
Poetry: 8-10 minutos totales (4-5 solo en instalar dependencias)
uv: 3-4 minutos totales (menos de 1 en dependencias)
Multiplicado por decenas de builds al día, es un ahorro enorme de tiempo y recursos de CI.
Caso 3: Experimentación rápida
Cuando quieres probar una librería nueva o hacer un experimento rápido:
mkdir experimento
cd experimento
uv init
uv add libreria-interesante
uv run pythonDe idea a código ejecutándose: menos de 10 segundos.
Caso 4: Scripts de automatización
Para scripts que necesitan dependencias específicas, ahora puedes usar:
#!/usr/bin/env -S uv run --with requests --with beautifulsoup4
# /// script
# requires-python = ">=3.11"
# dependencies = [
# "requests",
# "beautifulsoup4",
# ]
# ///
import requests
from bs4 import BeautifulSoup
# Tu código aquíLo guardas como script.py, le das permisos de ejecución, y:
./script.pyuv lee los comentarios especiales, instala las dependencias automáticamente, y ejecuta el script. Es mágico.
Consejos para la transición
Si estás pensando en hacer el cambio, estos son mis consejos:
1. Empieza con un proyecto pequeño
No migres tu proyecto más crítico el primer día. Prueba con algo pequeño, personal, de bajo riesgo. Familiarízate con los comandos.
2. Lee la documentación de migración
uv tiene una guía específica para migrar desde Poetry: https://docs.astral.sh/uv/guides/projects/#migrating-from-poetry
Léela. Si, es importante leer documentación. Te ahorrará dolores de cabeza.
3. Compara los lockfiles
Después de migrar, compara qué versiones instalaba Poetry vs las que instala uv. Asegúrate de que sean compatibles. Ejecuta tus tests.
4. Actualiza tu .gitignore
Añade .python-version y uv.lock (y elimina poetry.lock):
# Python
.venv/
__pycache__/
*.pyc
# uv
.python-version
uv.lock # O commitéalo, según tu preferencia5. Documenta el cambio
Si trabajas en equipo (o no), haz un pequeño documento o README explicando:
Por qué el cambio
Cómo instalar uv
Equivalencias de comandos (poetry add → uv add)
Qué hacer si algo falla
6. Mantén Poetry instalado (temporalmente)
Durante las primeras semanas, mantén Poetry instalado por si necesitas volver atrás. Una vez que estés cómodo con uv, puedes desinstalarlo:
pipx uninstall poetry
# o
pip uninstall poetryEl futuro: ¿qué viene?
El equipo de Astral está trabajando constantemente en nuevas características. Algunas que están en roadmap o en beta:
Mejor integración con IDEs
Soporte para más formatos de lockfile
Características avanzadas de workspace
Optimizaciones adicionales de velocidad (sí, puede ser aún más rápido)
El proyecto está en un momento de mucho momentum, y la comunidad está creciendo rápidamente.
Mi veredicto final
Ha pasado ya un tiempo desde que migré mi primer proyecto a uv. Desde entonces, se ha convertido en una herramienta fundamental para mi.
¿Me arrepiento de algo? Solo de no haberlo hecho antes.
La ganancia en productividad es real y medible. Ya no pierdo tiempo esperando.
Poetry fue genial en su momento. Me sirvió bien durante un tiempo. Pero uv representa el siguiente paso en la evolución de las herramientas de Python. Es más rápido, más versátil, y está mejor diseñado desde cero.
Si trabajas con Python, te debes a ti mismo probar uv. Solo en un proyecto. Solo una vez. Y después me cuentas.
Porque cuando veas que una operación que tardaba minutos ahora tarda segundos, cuando veas que tu CI corre el doble de rápido, cuando veas que ya no necesitas cinco herramientas diferentes para gestionar Python...vas a entender por qué no voy a volver.
Recursos útiles
Para que puedas empezar tu propia transición:
Documentación oficial: https://docs.astral.sh/uv/
Guía de migración desde Poetry: https://docs.astral.sh/uv/guides/projects/#migrating-from-poetry
Repositorio de GitHub: https://github.com/astral-sh/uv
Comparativa de velocidad: https://github.com/astral-sh/uv/blob/main/BENCHMARKS.md
Y si tienes dudas o encuentras problemas, el equipo de Astral es muy receptivo en GitHub Issues.
¿Y tú, ya usas uv? ¿Todavía en Poetry? ¿Usando pip y requirements.txt como antaño?
Me encantaría escuchar tu experiencia. Responde a este email o déjame un comentario.
Y si este artículo te convenció de probar uv, avísame. Siempre me gusta saber que no soy el único que se emociona con herramientas de línea de comandos.
