Python Monorepo Management — Core Concepts
What Is a Python Monorepo?
A monorepo stores multiple Python packages — libraries, services, CLI tools — in a single version-controlled repository. Each package has its own pyproject.toml and can be versioned, tested, and released independently, but they share CI infrastructure, linting rules, and developer tooling.
Why Teams Choose Monorepos
Atomic Cross-Package Changes
When a shared library changes its API, every dependent service updates in the same commit. There is no “publish version 2.0, then chase down six repos to upgrade” cycle. Code review covers the full blast radius.
Shared Tooling Configuration
Ruff, mypy, and pre-commit configs live at the root. Individual packages inherit these settings, reducing configuration drift across dozens of projects.
Simplified Dependency Graphs
Internal packages reference each other by path instead of by published version. A service can depend on the latest commit of an internal library without waiting for a PyPI release.
Typical Structure
company-mono/
├── packages/
│ ├── auth-service/
│ │ ├── src/auth_service/
│ │ ├── tests/
│ │ └── pyproject.toml
│ ├── billing-service/
│ │ ├── src/billing_service/
│ │ ├── tests/
│ │ └── pyproject.toml
│ └── shared-models/
│ ├── src/shared_models/
│ ├── tests/
│ └── pyproject.toml
├── pyproject.toml # workspace config
├── ruff.toml # shared lint rules
└── .github/workflows/
└── ci.yml
Workspace Tooling
Hatch Workspaces
Hatch treats each sub-directory with a pyproject.toml as a workspace member. Running hatch test from the root can execute tests across all packages, while hatch build -t wheel packages/auth-service targets a single package.
uv Workspaces
uv (from Astral, the makers of Ruff) supports workspace resolution natively. A root pyproject.toml declares members, and uv sync resolves dependencies across all packages in one lockfile — fast enough to run on every commit.
Pants and Bazel
For larger monorepos (50+ packages), build systems like Pants or Bazel provide dependency graph analysis. They determine which packages are affected by a code change and run only those tests, cutting CI time from hours to minutes.
Selective CI
The biggest monorepo pitfall is CI that rebuilds everything on every push. Solutions:
- Path filters — GitHub Actions
paths:or GitLabrules: changes:triggers CI only when relevant directories change. - Affected-package detection — tools like Pants compute the transitive dependency graph and test only affected targets.
- Layered caching — cache dependency installs per package hash so unchanged packages skip installation entirely.
Common Misconception
“Monorepos mean everyone works on everything.” Access boundaries still exist. GitHub CODEOWNERS files assign review responsibility per directory, and CI can enforce that changes to packages/billing-service/ require approval from the billing team. A monorepo centralizes code, not ownership.
When Not to Use a Monorepo
- Completely unrelated projects with different teams and release cadences gain little from proximity.
- Organizations without CI infrastructure to handle selective builds will suffer slow pipelines.
- Open-source libraries that external contributors clone independently are easier to manage as separate repos.
The one thing to remember: A Python monorepo pays off when packages share dependencies and change together — invest in workspace tooling and selective CI to keep it manageable.
See Also
- Python Black Formatter Understand Black Formatter through a practical analogy so your Python decisions become faster and clearer.
- Python Bumpversion Release Change your software's version number in every file at once with a single command — no more find-and-replace mistakes.
- Python Changelog Automation Let your git commits write the changelog so you never forget what changed in a release.
- Python Ci Cd Python Understand CI CD Python through a practical analogy so your Python decisions become faster and clearer.
- Python Cicd Pipelines Use Python CI/CD pipelines to remove setup chaos so Python projects stay predictable for every teammate.