Isolate each project in its own venv, pin the dependencies and adopt a reproducible project structure.
Open this lesson in KodokonInstalling the dependencies of all your projects in the same interpreter leads straight to a version conflict: project A needs requests 2.31, project B an incompatible version. The virtual environment (venv) isolates each project: its interpreter, its pip, its packages. It is the non-negotiable professional practice - one project, one environment, recreatable identically on any machine.
python -m venv .venv
source .venv/bin/activate
# On Windows:
# .venv\Scripts\activate
python -m pip install requests
python -m pip listTo make the installation reproducible, pin the versions. pip freeze exports the entire environment, transitive dependencies included, with exact versions: maximum reproducibility, laborious updates. The modern approach separates the two needs: the direct dependencies declared in pyproject.toml with loose constraints, and a lock file generated for the exact installation. At the scale of a small project, requirements.txt remains perfectly respectable.
python -m pip freeze > requirements.txt
# On another machine:
python -m venv .venv
source .venv/bin/activate
python -m pip install -r requirements.txtOn the structure side, the src layout is the reference: the code lives in src/<package>/, the tests in tests/, the configuration in pyproject.toml. The point of the src folder is not cosmetic: it prevents accidentally importing the local package from the current directory, which would hide packaging errors - your tests run against the actually installed package. During development, pip install -e . installs the project in editable mode.
mkdir -p src/billing tests
touch src/billing/__init__.py
touch src/billing/invoices.py
touch tests/test_invoices.py
touch pyproject.toml README.md .gitignore
echo ".venv/" >> .gitignore