python-best-practices
Python Best Practices
Guidelines for writing high-quality Python code in data and consulting projects.
When to Apply
Reference these guidelines when:
- Writing new Python scripts or modules
- Reviewing or refactoring existing Python code
- Building data pipelines or ETL workflows
- Developing APIs or CLI tools
- Conducting code reviews
Code Style and Readability
- Follow PEP 8 for code formatting
- Use Ruff for formatting and linting
- Limit line length to 88 characters
- Use descriptive variable and function names
- Every method must have type hints following PEP 484.
- Write docstrings for all public functions, classes, and modules (Google or NumPy style)
Type Hints
- Use type hints for all function signatures
- Use
T | Nonefor nullable values (do not useOptional). - Use
dict[str, str]overDict, same forset,list, etc. - Use
dataclassto structure data. - Use
TypedDictto provide type hints for dictionaries. - Run Astral's
tyfor static type checking
# Good
def process_data(records: list[dict[str, str]], max_rows: int = 1000) -> pd.DataFrame:
...
# Avoid
def process_data(records, max_rows=1000):
...
Error Handling
- Use specific exception types rather than bare
except - Log errors with context before re-raising or handling
- Use custom exception classes for domain-specific errors
- Never silently swallow exceptions
# Good
try:
result = fetch_data(url)
except requests.Timeout:
logger.error("Request timed out for URL: %s", url)
raise
except requests.HTTPError as e:
logger.error("HTTP error %s for URL: %s", e.response.status_code, url)
raise
# Avoid
try:
result = fetch_data(url)
except Exception:
pass
Project Structure
Organize projects consistently:
my-project/
├── src/
│ └── my_package/
│ ├── __init__.py
│ ├── core.py
│ └── utils.py
├── tests/
│ ├── __init__.py
│ └── test_core.py
├── pyproject.toml
└── README.md
- Use
pyproject.tomlfor project configuration - Use a
src/layout to prevent import errors during development - Keep tests alongside or adjacent to source code
Dependencies
- Pin dependencies with exact versions in
requirements.txtor lock files - Separate dev dependencies from production dependencies in
pyproject.toml - Prefer uv for fast dependency management
- Always use
uv add ...to add packages, which ensures that pyproject.toml and uv.lock remain the source of truth. - Use virtual environments through
uv, i.e.uv venv,uv sync,uv run .... - Use
uv add --dev ...to add dev dependencies.
Data Handling
- Use
polarsfor tabular data together withpatitoif schema definition and validation is needed. - Load only necessary columns when reading large files
- Use chunking or streaming for files that don't fit in memory
- Validate data schemas at ingestion boundaries (e.g., with
patito/pydantic)
# Good — validate early
from pydantic import BaseModel
class Record(BaseModel):
id: int
name: str
value: float
records = [Record(**row) for row in raw_data]
Testing
- Write unit tests with
pytest - Aim for meaningful test coverage, not just high percentages
- Use
pytest-mockorunittest.mockfor mocking external dependencies - Use fixtures for reusable test setup
- Test edge cases and error paths, not just the happy path
- If appropriate, consider using
dirty-equals,hypothesis,inline-snapshotto write cleaner tests.
Logging
- Use Python's built-in
loggingmodule, notprint - Configure logging at the application entry point only
- Use structured logging (e.g.,
structlog) for production services - Include relevant context in log messages
import logging
logger = logging.getLogger(__name__)
# Good
logger.info("Processing %d records from %s", len(records), source)
# Avoid
print(f"Processing {len(records)} records from {source}")
Security
- Never hardcode secrets or credentials in source code
- Use environment variables or a secrets manager for sensitive configuration. Use
uv run --env-file=.env ...to activate the environment variables. - Validate and sanitize all external inputs
- Use
banditfor static security analysis
Scripts
Ready-to-use helper scripts are in the scripts/ directory:
| Script | Purpose |
|---|---|
scripts/lint.sh [path] |
Run Ruff format, Ruff lint, ty, and Bandit checks on the project |
scripts/setup-project.sh <name> |
Scaffold a new Python project using uv init --lib |
Lint a project:
bash scripts/lint.sh ./src
Create a new project:
bash scripts/setup-project.sh my-new-service
cd my-new-service
uv sync
References
Supporting files in the references/ directory:
| File | Purpose |
|---|---|
references/pyproject-template.toml |
Starter pyproject.toml with Ruff, Bandit, and pytest configured |
references/logging-config.py |
Reference logging setup — stdlib JSON formatter and structlog patterns |
Add tool configuration from the template — copy the relevant [tool.*] sections into your project's pyproject.toml.
Use the logging reference — copy the relevant setup_* function into your application entry point.
More from b12consulting/skills
yuma-design
Yuma brand design reference covering color palette, logos and typography guidance. Use this skill when creating or reviewing Yuma-branded visual assets to stay aligned with the design system.
15pptx
Use this skill any time a .pptx file is involved in any way — as input, output, or both. This includes: creating slide decks, pitch decks, or presentations; reading, parsing, or extracting text from any .pptx file (even if the extracted content will be used elsewhere, like in an email or summary); editing, modifying, or updating existing presentations; combining or splitting slide files; working with templates, layouts, speaker notes, or comments. Trigger whenever the user mentions "deck," "slides," "presentation," or references a .pptx filename, regardless of what they plan to do with the content afterward. If a .pptx file needs to be opened, created, or touched, use this skill.
12specs-setup
Initialize the spec-driven project methodology. Use when: no specs/ folder exists; critical files like PRD.md, Vision.md, or Architecture/ are missing; setting up a new project for structured requirements and ticket management. Triggers on: 'setup specs', 'initialize specs', 'create PRD', 'start project methodology', 'missing specs', 'init specs', 'no specs folder'.
10specs-review
Review the health of a spec-driven project. Use when: checking if specs are up to date; detecting drift between code and specs; finding stale or incomplete tickets; auditing consistency across Vision, PRD, Architecture, and tickets. Triggers on: 'specs review', 'spec health', 'check specs', 'audit specs', 'are specs up to date', 'drift check', 'stale tickets', 'spec consistency'.
10spec-driven
Spec-driven project methodology reference. Use when a project has a specs/ folder for managing product requirements, vision, architecture, success measures, decision records (ADR), glossary, changelog, and ticket-based work tracking. Loaded when: working on a project with specs/; reading or updating PRD, vision, architecture, glossary, or changelog; detecting drift between code and specs; reviewing project methodology or document formats. Triggers on: 'specs', 'PRD', 'vision', 'success metrics', 'milestones', 'architecture', 'ADR', 'decision record', 'glossary', 'changelog', 'ticket', 'project methodology', 'ground truth'.
10specs-tickets
Create new spec-driven tickets and resume existing ones through the full lifecycle: research, specification, planning, and implementation. Use when: the user describes new work to be done; continuing a previously started ticket; a feature, bug fix, or task needs planning and implementation; starting or resuming planned work on a project with specs/. Triggers on: 'new ticket', 'new feature', 'fix bug', 'implement', 'create ticket', 'I want to build', 'let us work on', 'add feature', 'continue ticket', 'resume work', 'pick up where we left off', 'work on ticket', 'existing ticket', 'check ticket status', 'what is the state of ticket'.
10