# Why use native uv commands instead of uv pip

`uv pip install` is the fastest way to start using [uv](https://pydevtools.com/handbook/reference/uv.md). Replace `pip` with `uv pip` in your existing commands, and everything runs 10-100x faster with zero other changes. But `uv pip` deliberately mimics pip's behavior, which means it also inherits pip's limitations. uv's native project commands offer a different set of guarantees.

## Two interfaces, different models

The choice between these interfaces determines whether your project's dependencies are reproducible or just fast. uv ships two distinct interfaces:

| | pip-compatible | Native project |
|---|---|---|
| **Install a package** | `uv pip install requests` | `uv add requests` |
| **Lock dependencies** | `uv pip compile requirements.in` | `uv lock` |
| **Sync environment** | `uv pip sync requirements.txt` | `uv sync` |
| **Run code** | `source .venv/bin/activate && python app.py` | `uv run python app.py` |

These are not interchangeable commands that produce the same result. The pip-compatible interface operates on environments the same way pip does: it installs packages into whatever virtual environment is active and tracks nothing beyond that environment's `site-packages`. The native interface manages dependencies through [pyproject.toml](https://pydevtools.com/handbook/reference/pyproject.toml.md) and a [lockfile](https://pydevtools.com/handbook/explanation/what-is-a-lock-file.md), treating the virtual environment as a derived artifact that can be recreated at any time.

## What native uv provides that `uv pip` cannot

### Cross-platform lockfile

`uv pip install` resolves dependencies for the current platform and Python version, then installs them. Run the same command on a different OS or Python version and you may get different package versions.

`uv lock` resolves dependencies across platforms and records the result in `uv.lock`. The lockfile captures platform-specific forks (a package might resolve to one version on Linux and another on Windows), so when a collaborator runs `uv sync`, they get the correct versions for their platform without re-resolving. The lockfile is the single source of truth for what should be installed.

```bash
# Resolve once
uv lock

# Everyone gets the same result
uv sync
```

See [What is a lockfile?](https://pydevtools.com/handbook/explanation/what-is-a-lock-file.md) for more on why this matters.

### Declarative dependency tracking

`uv pip install requests` puts `requests` (and its transitive dependencies) into the virtual environment. There is no record of which packages were explicitly requested and which arrived as dependencies of dependencies. If the virtual environment is deleted, the knowledge of what was installed is gone.

`uv add requests` writes `requests` to `pyproject.toml`, updates `uv.lock`, and installs the package. The intent is captured in version control. Deleting `.venv` costs nothing because `uv sync` rebuilds it from the lockfile.

```bash
# Intent is recorded in pyproject.toml, resolved in uv.lock
uv add requests
uv add flask

# Environment is disposable — rebuild it anytime
rm -rf .venv
uv sync
```

### Automatic virtual environment management

With `uv pip`, you create and activate virtual environments yourself:

```bash
uv venv
source .venv/bin/activate
uv pip install -r requirements.txt
python app.py
```

With native uv, the virtual environment is managed for you:

```bash
uv run python app.py
```

`uv run` ensures the virtual environment exists, matches the lockfile, and has the correct Python version before executing the command. No activation step. No stale environment from yesterday's dependency changes.

### Integrated dependency groups

`uv add --dev pytest` adds pytest as a [development dependency](https://pydevtools.com/handbook/explanation/what-are-optional-dependencies-and-dependency-groups.md), separate from production dependencies. Production deploys can exclude dev dependencies with `uv sync --no-dev`. Adding, removing, and syncing dependency groups is a single workflow: edit `pyproject.toml` (or use `uv add --group`), lock, sync.

`uv pip compile` and `uv pip sync` also support a `--group` flag, but you must manage group definitions in `pyproject.toml` yourself and run separate compile/sync steps.

### Reliable CI/CD

In continuous integration, `uv sync --locked` installs from the lockfile and fails if it doesn't match `pyproject.toml`. This catches cases where a developer added a dependency locally but forgot to commit the updated lockfile. (`--frozen` is a stricter option that skips the freshness check entirely and uses whatever lockfile exists, which is useful when you've already validated the lockfile in an earlier CI step.)

`uv pip install -r requirements.txt` in CI has the same drift problems as regular pip: unpinned dependencies resolve to whatever version is newest at build time, and there's no mechanism to detect when the requirements file is stale.

## When `uv pip` still makes sense

The pip-compatible interface exists for a reason. Some situations where it's the right choice:

- Legacy projects that rely on `requirements.txt` files and can't adopt `pyproject.toml` yet. `uv pip install -r requirements.txt` gives an immediate speed boost with no structural changes.
- Interop with tools that generate or consume `requirements.txt`, such as deployment platforms that expect a `requirements.txt` at the repository root.
- Throwaway environments where reproducibility doesn't matter, like a quick experiment in a temporary virtualenv.
- Gradual migration as a first step. Start with `uv pip` for speed, then move to native uv when the project is ready for `pyproject.toml`.

## Moving from `uv pip` to native uv

If the project already has a `requirements.txt`, the migration is a few commands:

```bash
uv init --bare
uv add -r requirements.txt
```

The full process, including handling dev dependencies and cleanup, is covered in [How to migrate from requirements.txt to pyproject.toml with uv](https://pydevtools.com/handbook/how-to/migrate-requirements.txt.md).

For new projects, `uv init` sets up the native workflow from the start. See [Create your first Python project](https://pydevtools.com/handbook/tutorial/create-your-first-python-project.md) for a hands-on walkthrough.

## Related

- [uv: A Complete Guide](https://pydevtools.com/handbook/explanation/uv-complete-guide.md) covers both interfaces in detail
- [What's the difference between pip and uv?](https://pydevtools.com/handbook/explanation/whats-the-difference-between-pip-and-uv.md) compares pip (the tool) with uv at an architectural level
- [What is a lockfile?](https://pydevtools.com/handbook/explanation/what-is-a-lock-file.md) explains cross-platform dependency locking
- [Why choose pyproject.toml over requirements.txt?](https://pydevtools.com/handbook/explanation/pyproject-vs-requirements.md) makes the broader case for declarative project metadata
