Project Architecture Decision Checklist
Use this checklist when starting or restructuring a Python service so layout, boundaries, and tooling decisions are made deliberately before code hardens around accidents.
Search across all documentation pages
Use this checklist when starting or restructuring a Python service so layout, boundaries, and tooling decisions are made deliberately before code hardens around accidents.
pyproject.toml, CI, and Docker images.
uv 0.6+ for lockfiles and fast installs vs plain pip + requirements.txt.
src/ layout vs flat package at repo root.
main entry point.pydantic-settings vs config files checked into git.
.env excluded from git.
.env: local developer machines only.ruff 0.9+ as the default all-in-one tool.pytest with fixtures colocated under tests/.print in library code.python -m package or uvicorn/gunicorn command documented.Choose FastAPI when the primary surface is a JSON/HTTP API with OpenAPI docs and async I/O. Choose Django when you need the admin, auth ecosystem, and ORM as one cohesive stack. Either can grow - but switching later is costly.
Yes for anything installed in CI or Docker. src layout forces tests to import the package the way production does, catching "works locally because PYTHONPATH hack" bugs early.
Skip it for scripts under ~300 lines with no expected lifespan beyond a quarter. Once multiple endpoints share rules, extract domain logic before duplication wins.
Usually no. Pick one primary tabular engine per service. Use the other only at integration boundaries if a partner pipeline requires it.
At boundaries: HTTP request/response DTOs, settings, and external API shapes. Keep core domain types as dataclasses or plain classes unless validation is inherently boundary-focused.
Enable checking in CI immediately but allow overrides in legacy modules. Ratchet: new files must pass strict rules; tighten global settings each sprint.
Settings classes should read secrets from the environment or a secret backend. Never commit secret values; document required variable names in .env.example.
Monorepo when they share models and release together. Polyrepo when teams, SLAs, and deploy permissions are independent.
Run tests on 3.14.0 plus 3.13 if you ship libraries. Application-only repos may pin a single version to match production images.
Write a short ADR when the decision affects multiple teams, is hard to reverse, or surprised someone during review. A one-line README note is enough for obvious defaults like "we use ruff."
Add a tracked issue per gap: owner, risk level, and whether it blocks production. Review in sprint planning instead of leaving silent unchecked boxes.
Use the Foundation and Quality tiers. Boundary and Ops tiers apply when an experiment promotes to a scheduled job or API.
Stack versions: This page was written for Python 3.14.0 (stable 3.14, maintenance 3.13), FastAPI 0.115+, Django 5.2, Flask 3.1, Pydantic 2, PyTorch 2.6+, pandas 2.2+, Polars 1.x, ruff 0.9+, and uv 0.6+.
Reviewed by Chris St. John·Last updated Jul 19, 2026