Python Development Overview¶
Modern Python development in 2025 is faster, simpler, and more reliable than ever. This section covers the essential tools and practices for building production Python applications.
Core Philosophy¶
Guiding Principles
- Modern is better: Use Python 3.13+ for new projects
- Fast tooling: Tools like Ruff and uv are 10-100x faster than predecessors
- Simplicity wins: Fewer tools, less configuration, more productivity
- Type hints help: Gradual typing improves code quality
- Test everything: But be pragmatic about coverage
The Modern Python Stack (2025)¶
Core Tools¶
| Tool | Purpose | Why | Replaces |
|---|---|---|---|
| Python 3.13+ | Runtime | Better performance, error messages, security | Python 3.6-3.12 |
| uv | Dependency management | 10-100x faster than pip | pip-tools, pipenv, poetry* |
| Ruff | Linting + Formatting | Single tool, extremely fast | Black, isort, flake8, pylint* |
| pytest | Testing | Industry standard, excellent plugins | unittest |
| Bandit | Security scanning | Find security issues automatically | Manual review |
| Vulture | Dead code detection | Keep codebase clean | Manual cleanup |
* Can complement, not always replace
Development Workflow¶
graph LR
A[Write Code] --> B[Ruff Format]
B --> C[Ruff Lint]
C --> D[pytest]
D --> E[Bandit Security]
E --> F{All Pass?}
F -->|Yes| G[Commit]
F -->|No| A
G --> H[Pre-commit Hooks]
H --> I[CI/CD]
This entire workflow runs in seconds thanks to modern tools.
Quick Start¶
1. Install uv (Dependency Manager)¶
# Install uv
curl -LsSf https://astral.sh/uv/install.sh | sh
# Create project
mkdir myproject && cd myproject
uv venv
source .venv/bin/activate # On Windows: .venv\Scripts\activate
2. Configure Ruff (Linting & Formatting)¶
Create pyproject.toml:
[tool.ruff]
line-length = 180
target-version = "py313"
[tool.ruff.lint]
select = ["E", "F", "W", "I", "N", "UP", "S", "B", "A", "C4", "T20", "RET", "SIM", "ARG"]
ignore = []
3. Set Up pytest¶
[tool.pytest.ini_options]
testpaths = ["tests"]
addopts = "--cov=myproject --cov-report=html --cov-report=term"
4. Create justfile¶
# Development commands
test:
pytest
lint:
ruff check .
format:
ruff format .
check: format lint test
What Changed from 2019?¶
Python Version¶
Old (2019): - Python 3.6-3.8 - End of life for 3.6 was December 2021 - Missing modern features
New (2025): - Python 3.13+ (released October 2024) - 15-20% faster than 3.10 - Better error messages - Pattern matching (3.10+) - Improved type hints - Active security support
Dependency Management¶
Old (2019):
New (2025):
# uv workflow (much faster)
uv pip compile requirements.in -o requirements.txt
uv pip sync requirements.txt
Why uv? - 10-100x faster than pip - Better dependency resolution - Compatible with existing workflows - Single binary, no Python dependency
Code Quality¶
Old (2019):
New (2025):
Why Ruff? - 10-100x faster than Black/flake8 - Replaces multiple tools - Compatible with Black/flake8 configs - Written in Rust
Task Running¶
Old (2019):
New (2025):
Recommended Project Structure¶
myproject/
├── .devcontainer/ # Devcontainer configuration
│ └── devcontainer.json
├── docs/ # Documentation
├── myproject/ # Source code
│ ├── __init__.py
│ ├── models/
│ ├── views/
│ └── utils/
├── tests/ # Tests
│ ├── unit/
│ ├── integration/
│ └── conftest.py
├── .env.example # Environment variable template
├── .gitignore
├── .pre-commit-config.yaml # Pre-commit hooks
├── .python-version # Pin Python version
├── justfile # Task automation
├── pyproject.toml # Project configuration
├── README.md
└── requirements.in # Direct dependencies
Best Practices¶
Version Management¶
- ✅ Use
.python-versionfile to pin exact Python version - ✅ Use
pyenvorasdffor managing multiple Python versions - ✅ Target Python 3.13+ for new projects
- ✅ Plan upgrades for projects on older versions
Dependency Management¶
- ✅ Use
uvfor speed and reliability - ✅ Split requirements:
base.in,dev.in,test.in - ✅ Pin direct dependencies, let uv resolve transitive ones
- ✅ Run
pip-auditregularly for security vulnerabilities - ❌ Don't check in
__pycache__or.venv
Code Quality¶
- ✅ Use Ruff for linting and formatting
- ✅ Configure in
pyproject.tomlfor centralization - ✅ Run via pre-commit hooks
- ✅ Address lint errors, don't just disable them
- ✅ Use type hints gradually (don't need 100% coverage)
Testing¶
- ✅ Use pytest exclusively
- ✅ Organize tests by type (unit, integration, ui)
- ✅ Aim for 80%+ coverage (be pragmatic)
- ✅ Use fixtures for setup/teardown
- ✅ Mock external services
- ❌ Don't test third-party libraries
Common Patterns¶
Environment Variables¶
import os
from pathlib import Path
# Development
DEBUG = os.getenv("DEBUG", "False") == "True"
# Database
DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///db.sqlite3")
# Secrets (never hardcode!)
SECRET_KEY = os.getenv("SECRET_KEY")
if not SECRET_KEY:
raise ValueError("SECRET_KEY environment variable is required")
Logging¶
import logging
import structlog
# Structured logging for production
structlog.configure(
processors=[
structlog.stdlib.filter_by_level,
structlog.processors.TimeStamper(fmt="iso"),
structlog.stdlib.add_logger_name,
structlog.stdlib.add_log_level,
structlog.processors.StackInfoRenderer(),
structlog.processors.format_exc_info,
structlog.processors.JSONRenderer()
],
logger_factory=structlog.stdlib.LoggerFactory(),
)
logger = structlog.get_logger()
logger.info("user_login", user_id=123, ip_address="1.2.3.4")
Error Handling¶
from typing import Optional
def get_user(user_id: int) -> Optional[User]:
"""Get user by ID.
Args:
user_id: The user's ID
Returns:
User object if found, None otherwise
Raises:
DatabaseError: If database connection fails
"""
try:
return User.objects.get(id=user_id)
except User.DoesNotExist:
logger.warning("user_not_found", user_id=user_id)
return None
except DatabaseError as e:
logger.error("database_error", user_id=user_id, error=str(e))
raise
Performance Considerations¶
Python 3.13 Improvements¶
- 15-20% faster than Python 3.10
- Better memory management
- Faster startup time
- Improved GC performance
Tool Performance¶
| Operation | Old Tools | New Tools | Improvement |
|---|---|---|---|
| Formatting | Black (1.2s) | Ruff (0.01s) | 100x faster |
| Linting | flake8 (3s) | Ruff (0.02s) | 150x faster |
| Dependency resolution | pip (30s) | uv (0.5s) | 60x faster |
These speed improvements dramatically improve developer experience.
Next Steps¶
- Python Version Guidelines
- Dependency Management with uv
- Code Formatting with Ruff
- Linting with Ruff
- Testing with pytest
- Security with Bandit
Migration Strategy
If you have existing projects on older Python or tools: 1. Start with Ruff (easiest, fastest wins) 2. Migrate to uv (improves CI/CD times) 3. Upgrade Python version (plan carefully, test thoroughly)
Further Reading