Developer overview¶
LifeFile is a Django 6 app + a small static docs site. It's deliberately monolithic — one repo, one runtime, SQLite on a single Fly.io VM. The complexity is in the services (classifier, email scan, schedule eval, DVLA lookup), not in the deployment.
Tech stack¶
| Layer | Choice | Notes |
|---|---|---|
| Backend | Django 6 + Python 3.14 | One repo, ~12 apps under apps/ |
| Templates | Django templates + HTMX + Alpine.js | Server-rendered; HTMX for partial swaps |
| Styling | Tailwind via CDN (Play) | Build pipeline deferred |
| Database | SQLite on Fly volume | Postgres migration planned |
| Storage | Local disk on the volume | R2 + envelope encryption planned |
| Classifier | Claude Sonnet via Anthropic SDK | Stub mode for tests/CI; live mode in prod |
| Email scan | Gmail API (read-only) | OAuth via auth_google (sign-in) + emailscan (Gmail) |
| Vehicle lookup | DVLA VES + DVSA MOT History | Mock mode by default; live keys via Fly secrets |
| Tests | pytest + pytest-django + model-bakery | 376+ tests, runs in ~75s |
| CI | GitHub Actions | Pytest gate on PRs; deploy to Fly on main |
| Docs | Zensical (this site) | Cloudflare Pages |
| Pre-commit | prek | docs-drift check |
Where to start¶
- Architecture — the apps, the data flow, the boundaries.
- Local setup — Python env, env vars, running the dev server.
- Contributing — branch flow, commit style, PR review expectations.
- Adding features — where new code goes, how to wire up a new "slice".
- Testing — pytest conventions, the red/green discipline, mocking external APIs.
- Deploying the app — Fly.io deploy + day-two ops.
- Deploying the docs — Cloudflare Pages for this docs site.
- Vehicle lookup setup — DVLA + DVSA registration walkthrough.
- Docs drift check — the prek hook + GitHub Actions guard.
Conventions¶
A few decisions made repeatedly:
- Services-first. Views are thin shells that parse the request, call a service function, render a template. Most logic lives in
apps/<app>/services.py. - Household-scoped managers. Every domain model has a
HouseholdScopedManagerasobjectsand anAllTenantsManagerasall_tenants. Default reads scope automatically. - Stub modes for external APIs. Classifier, vehicle lookup, and (planned) email-in all support a
mock/stubmode toggled by an env var so dev and CI run without keys. - Static template checks. We have CI tests for multi-line
{# #}Django comments (which silently leak as text) and cross-fileidclass drift (which OOB swaps used to silently overwrite). - Atomic commits, conventional messages. PR titles are short; bodies explain the why. CHANGELOG.md gets a dated entry per merged PR.
- Tests before code. Most slices are TDD: write a failing test, watch it go red, write just enough code to go green, refactor.