Setting Up a Python Project: Local vs. Containerized Development
When starting a new Python project, one of the first decisions you'll face is how to set up your development environment. Should you go with a traditional local setup using virtual environments, or should you containerize your development with Docker? Let's explore both approaches through a practical example.
The Project Setup
We'll use a simple FastAPI project as our example. Here's what we need to get started:
mkdir hello-world
pyenv local 3.9.18
python -m venv .venv
source ./.venv/bin/activate
python -m pip install --upgrade pip
pip install "fastapi[standard]"
pip freeze > requirements.txt
This gives us a basic Python project with FastAPI installed. But how should other developers run this project? Let's look at two approaches.
The Traditional Way: Local Development
The traditional approach involves each developer setting up Python and managing dependencies on their local machine. This is what most Python developers start with, and it looks familiar:
- Install the correct Python version
- Create a virtual environment
- Install dependencies
Simple, right? Well, not always. Consider these scenarios:
- A new team member can't get the exact Python version working on their machine
- Someone forgets to activate their virtual environment and installs packages globally
- The project needs system-level dependencies that aren't documented
- Different operating systems cause subtle compatibility issues
Enter Containerized Development
Here's where Docker comes in. Instead of managing the environment locally, we can define it in code:
ARG PYTHON_VERSION=3.9.18
FROM python:${PYTHON_VERSION} as base
WORKDIR /usr/src/app
RUN --mount=type=cache,target=/root/.cache/pip \
--mount=type=bind,source=requirements.txt,target=requirements.txt \
python -m pip install -r requirements.txt
CMD uvicorn src.main:app --reload --port 8000 --host 0.0.0.0
And orchestrate it with Docker Compose:
services:
fastapi:
image: fastapi
build:
context: .
dockerfile: Dockerfile.dev
ports:
- 8000:8000
volumes:
- ./src:/usr/src/app/src:z
restart: 'no'
Now, any developer can start the project with just:
docker compose up --build
Why Choose Containerization?
After setting up both approaches, I recommend containerized development for most team projects. Here's why:
- Environment Consistency: The Docker configuration ensures everyone runs the exact same environment. No more "works on my machine" problems.
- Simple Onboarding: New team members don't need to worry about Python versions or virtual environments. They just need Docker installed.
- Version Control: The development environment itself becomes code that can be version controlled. Changes to the environment are explicit and tracked.
- Production Similarity: Docker-based development environments are closer to how the application will run in production, catching environment-related issues earlier.
Conclusion
The choice between local and containerized development isn't just about technical preferences—it's about team workflow and project needs. While containerization adds a layer of abstraction, the benefits of consistency and reproducibility make it worth considering for team projects. Whether you choose local or containerized development, the key is to document your choice and its rationale clearly. This helps team members understand not just how to run the project, but why it's set up that way. Remember: the goal is to spend less time fighting with environment issues and more time writing code. Choose the approach that best helps your team achieve that goal.