Python Dev Dependencies vs Production Dependencies

In Python packaging, managing external libraries effectively requires separating production dependencies from development dependencies. Production dependencies are the runtime packages essential for your application or library to execute its primary functions in a live environment, whereas development dependencies are auxiliary tools used exclusively for testing, formatting, linting, and documentation during the software lifecycle. Separating these dependencies minimizes security vulnerabilities, reduces container image sizes, prevents version conflicts, and streamlines deployment pipelines.

What Are Production Dependencies?

Production dependencies—often called runtime dependencies—are the packages your code imports directly to perform its core tasks. Without these libraries, the application will encounter import errors or crash at runtime.

In standard pyproject.toml configurations (PEP 621), production dependencies are defined under the main [project] table:

[project]
name = "my-package"
version = "0.1.0"
dependencies = [
    "requests>=2.28.0",
    "pydantic>=2.0.0",
]

What Are Development Dependencies?

Development dependencies are libraries required only by developers working on the codebase or by automated Continuous Integration (CI) pipelines. They are never required to execute the application in a production environment.

In traditional setups, these were isolated in a requirements-dev.txt file. In modern pyproject.toml configurations, they are categorized under optional dependencies or specific dependency groups:

# Using PEP 621 optional dependencies
[project.optional-dependencies]
dev = [
    "pytest>=7.0.0",
    "ruff>=0.1.0",
    "mypy>=1.0.0",
]

# Or using Poetry dependency groups
[tool.poetry.group.dev.dependencies]
pytest = "^7.0.0"
ruff = "^0.1.0"

Key Differences

Feature Production Dependencies Development Dependencies
Execution Context Live server, cloud instance, client runtime Local machine, CI/CD runners, build agents
PyPI Distribution Automatically installed when running pip install your-package Omitted by default; must be explicitly requested
Impact on Security High (directly exposed to attack vectors in production) Low (isolated from production environments)
Image Size Impact Necessary footprint Unnecessary bloat if bundled into release artifacts

Why the Distinction Matters

1. Security and Attack Surface

Every package introduced into a production environment adds potential security vulnerabilities. Developer tools like test runners or documentation generators frequently require extensive dependency trees of their own. Excluding them from production significantly lowers the risk of supply chain attacks and Common Vulnerabilities and Exposures (CVEs).

2. Deployment Speed and Artifact Size

Container images (such as Docker) and deployment bundles should be as lightweight as possible. Installing unnecessary development tools inflates image sizes, increases memory consumption, and prolongs continuous deployment build steps.

3. Avoiding Dependency Conflicts

Development tools often have strict dependency requirements that can clash with production libraries. By decoupling them, developers can upgrade application-critical libraries without being blocked by pinned versions in testing frameworks or linters.

Best Practices for Management