Read practical articles about Python, JavaScript, React, and modern software tools.
Python Packaging Wars
A beginner-friendly guide to the modern Python packaging debate.
python
Read article →pip vs uv: Is Python Finally Getting a Package Manager People Can Agree On?
For small scripts, pip may be enough. For modern projects, uv is hard to ignore.
python
Read article →Python Packaging Wars
A beginner-friendly guide to the modern Python packaging debate.
python
Read article →pip vs uv: Is Python Finally Getting a Package Manager People Can Agree On?
For small scripts, pip may be enough. For modern projects, uv is hard to ignore.
python
Read article →Python Packaging Wars
A beginner-friendly guide to the modern Python packaging debate.
python
Read article →pip vs uv: Is Python Finally Getting a Package Manager People Can Agree On?
For small scripts, pip may be enough. For modern projects, uv is hard to ignore.
python
Read article →Python Packaging Wars
A beginner-friendly guide to the modern Python packaging debate.
python
Read article →pip vs uv: Is Python Finally Getting a Package Manager People Can Agree On?
For small scripts, pip may be enough. For modern projects, uv is hard to ignore.
python
Read article →Python Packaging Wars
A beginner-friendly guide to the modern Python packaging debate.
python
Read article →pip vs uv: Is Python Finally Getting a Package Manager People Can Agree On?
For small scripts, pip may be enough. For modern projects, uv is hard to ignore.
python
Read article →Python Packaging Wars
A beginner-friendly guide to the modern Python packaging debate.
python
Read article →pip vs uv: Is Python Finally Getting a Package Manager People Can Agree On?
For small scripts, pip may be enough. For modern projects, uv is hard to ignore.
python
Read article →Python Packaging Wars
A beginner-friendly guide to the modern Python packaging debate.
python
Read article →pip vs uv: Is Python Finally Getting a Package Manager People Can Agree On?
For small scripts, pip may be enough. For modern projects, uv is hard to ignore.
python
Read article →Python Packaging Wars
A beginner-friendly guide to the modern Python packaging debate.
python
Read article →pip vs uv: Is Python Finally Getting a Package Manager People Can Agree On?
For small scripts, pip may be enough. For modern projects, uv is hard to ignore.
python
Read article →For small scripts, pip may be enough. For modern projects, uv is hard to ignore.
Python has always been loved for its simplicity, but Python packaging has a reputation for being anything but simple.
For years, beginners were told to use pip, then create a virtual environment, then maybe use requirements.txt, then maybe use pip-tools, then maybe use pipx, then maybe learn Poetry, Conda, pyenv, Hatch, or PDM depending on who they asked.

That is where the controversy starts.
Some developers look at uv and say, “Finally, Python has a fast modern tool that brings everything together.”
Others say, “Do we really need another Python package tool?”
And then there is the deeper question:
Should Python’s future packaging workflow be centered around a newer external tool, or should the traditional pip workflow remain the main path?
That is the heart of the pip vs uv debate.
At the simplest level:
pip = the traditional Python package installer
uv = a newer, faster Python package and project manager
pip installs Python packages.
uv can install packages too, but it also handles virtual environments, lockfiles, Python versions, project dependencies, development tools, and more.
So a very short comparison would be:
pip installs packages.
uv manages Python projects.
That does not mean pip is bad. It means pip has a narrower job.
pip is the familiar standard. Almost every Python developer has seen this:
python -m pip install requestsIt is simple, widely documented, and supported across countless tutorials, courses, hosting platforms, and deployment guides.
For beginners, pip is still useful because it teaches the basic idea:
I need a package → I install the package
For example:
python -m pip install flaskThat command is easy to understand. It installs Flask.
The problem is that real projects usually need more than one package. They need isolated environments, repeatable installs, development dependencies, and version control over the full dependency tree.
That is where plain pip starts to feel limited.
Plain pip does not have a first-class lockfile workflow like uv, Poetry, npm, Cargo, or pnpm.
With traditional pip, people often use:
python -m pip freeze > requirements.txtThat creates a file like this:
requests==2.32.5
urllib3==2.5.0
certifi==2025.10.5
charset-normalizer==3.4.4
idna==3.11
This is useful, but it is not quite the same thing as a modern lockfile.
A requirements.txt file can be a pinned snapshot of what is currently installed, but it is not usually a resolver-managed record of the project’s full dependency decision process.
A simple way to think about it:
requirements.txt = a package install list or pinned snapshot
lockfile = the exact resolved dependency graph chosen for the project
That difference matters because projects often depend on packages that depend on other packages.
For example, your project might directly depend on:
requests
But requests may bring in other packages such as:
urllib3
certifi
charset-normalizer
idna
A lockfile records the exact resolved versions of both direct and indirect dependencies.
With uv, the lockfile is:
uv.lock
In a modern uv project, your pyproject.toml says what your project needs, and uv.lock records the exact versions that were resolved.
In plain terms:
pyproject.toml says what you want.
uv.lock says exactly what you got.

pyproject.toml is often compared to JavaScript’s package.json. That comparison is not perfect, but it is useful.

A basic pyproject.toml might look like this:
[project]
name = "my-python-app"
version = "0.1.0"
description = "A simple Python project"
requires-python = ">=3.11"
dependencies = [
"requests",
"fastapi",
]
This file describes the project. It can include the project name, version, Python version, dependencies, build settings, and tool settings.
For example, it can also configure tools like Ruff, Black, Pytest, and Mypy:
[project]
name = "my-python-app"
version = "0.1.0"
requires-python = ">=3.11"
dependencies = [
"requests",
"fastapi",
]
[dependency-groups] dev = [
"ruff",
"black",
"pytest",
"mypy",
]
[tool.ruff]
line-length = 100
[tool.black]
line-length = 100
[tool.pytest.ini_options]
testpaths = ["tests"]
[tool.mypy]
python_version = "3.11"
strict = true
Those tools do different jobs:
Ruff = checks code and can format code Black = formats code Pytest = runs tests Mypy = checks type hints
So yes, pyproject.toml is a lot like Python’s modern version of package.json.
But the lockfile is separate.
pyproject.toml = project configuration
uv.lock = exact resolved dependency versions
A common traditional pip workflow looks like this:
python -m venv .venv
source .venv/bin/activate
python -m pip install requests
python -m pip freeze > requirements.txt
On Windows PowerShell, activation usually looks more like:
.venv\Scripts\Activate.ps1This workflow is familiar and still works.
The weakness is that you are manually combining several ideas:
venv creates the environment pip installs packages pip freeze records installed versions requirements.txt stores the package list
That is a lot of separate mental steps, especially for beginners.
With uv, you can still use a pip-like workflow:
uv venv
source .venv/bin/activate
uv pip install requests
But uv also supports a more project-centered workflow:
uv init my-python-app
cd my-python-app
uv add requests
uv run python main.py
In that style, uv updates your pyproject.toml, resolves dependencies, manages the environment, and creates or updates the lockfile.
That is the major shift.
With pip, you usually think:
Create environment → activate environment → install packages → freeze dependencies
With uv, you can think:
Declare dependencies → lock dependencies → sync environment → run project
By default, this command creates a virtual environment named .venv in the current project directory:
uv venv
That is similar to:
python -m venv .venv
You can also choose a custom environment name in the current directory:
uv venv my_custom_env
That creates:
./my_custom_env
You can also create the virtual environment in a custom location:
uv venv ~/python-envs/my-project-env
Or with an absolute path:
uv venv /home/andy/python-envs/my-project-env
So yes, uv can create virtual environments outside the current directory.
That is useful if you do not want every project folder to contain a .venv directory.
For a project-managed uv environment, .venv is the default, but uv also supports a custom project environment path through UV_PROJECT_ENVIRONMENT.
For example, a custom relative path:
UV_PROJECT_ENVIRONMENT=envs/my-app uv syncThat creates or uses a project environment relative to the workspace root.
A custom absolute path can also be used:
UV_PROJECT_ENVIRONMENT=/home/andy/python-envs/my-app uv syncThat tells uv to use that exact location as the project environment.[1]
One reason uv gets so much attention is that it does not only compete with pip.
It aims to replace or reduce the need for several tools in the Python workflow.
Here is the rough picture:
pip → package installation
pip-tools → dependency compiling / pinned requirements
pipx → installing and running Python command-line tools
poetry → project and dependency management
pyenv → Python version management
twine → package publishing virtualenv → virtual environment creation
uv tries to bring many of those jobs into one tool.
For example:
uv add requestsadds a dependency.
uv syncsyncs the environment from the lockfile.
uv run pytestruns a command inside the project environment.
uv tool install ruffinstalls a Python command-line tool.
uv python install 3.12installs a Python version.
This is why uv feels different from pip. pip is an installer.
uv is trying to be a full Python workflow manager.
There are still good reasons to use pip.
Use pip when:
You are following older tutorials. You want the most universal Python command. You are teaching absolute beginners. You need the simplest possible install command. You are working in an environment where uv is not available.
For example, this is still clear and useful:
python -m pip install djangoAlmost everyone understands it.
pip is also deeply established. It is not going away just because uv exists.
Developers like uv because it is fast, modern, and more complete.
Use uv when:
You are starting a new Python project. You want faster package installation. You want a lockfile. You want cleaner virtual environment management. You want one tool instead of several tools. You want a workflow closer to npm, Cargo, or pnpm.
A typical uv project can look like this:[1]
uv init my-app
cd my-app
uv add fastapi
uv add --dev ruff pytest mypy
uv run pytest
That is clean.
It also feels familiar to developers coming from JavaScript, Rust, or other ecosystems with stronger project management tools.
The real debate is not just speed.
Yes, uv is fast. That matters.
But the deeper debate is about Python’s identity.
Python has always been flexible. You can use pip, venv, Conda, Poetry, Hatch, PDM, pyenv, pipx, pip-tools, or some combination of them.
That flexibility is powerful.
It is also confusing.
uv is popular because it says:
What if one tool handled most of this?
That is exciting, but it also makes some developers cautious.
They may wonder:
Is uv mature enough? Should beginners learn uv before pip? Will uv become the new default workflow? Is Python packaging becoming simpler, or just changing tools again? So Which One Should You Use?
For beginners, it still helps to understand pip. pip teaches the basic idea of installing packages, and it appears everywhere in Python documentation and tutorials.
But for new real-world projects, uv is very attractive.
A balanced recommendation would be:
Learn what pip is. Use uv for modern project workflows.
Or even simpler:
pip is the classic installer. uv is the modern project manager.
If you only need to install one package quickly, pip is fine.

If you are building a project with dependencies, development tools, virtual environments, and reproducible installs, uv may give you a cleaner path.
The pip vs uv debate is not really about whether pip is useless.
It is not.
pip is still important, familiar, and widely supported.
The debate is about whether Python developers should keep stitching together several separate tools, or whether a tool like uv should become the new center of the Python workflow.
pip installs packages.
uv manages projects, environments, tools, Python versions, and lockfiles.
For small scripts, pip may be enough.
For modern projects, uv is hard to ignore.