Skip to content

Developer guide

This guide gets a new developer productive on the TaskTree backend. It describes how the system is built — the layers, the data model, the moving parts — and links to the Architecture Decision Records that explain why each choice was made.

Audience and scope

These pages document design and architecture. They're public, so they deliberately contain no secrets, credentials, or environment-specific identifiers — those live in the operator's internal notes and in Azure Key Vault, never here. See ADR 0040 for the boundary.

What TaskTree is, in one paragraph

A self-hosted project-management tool for engineering R&D teams. A FastAPI + PostgreSQL backend serves a React + TypeScript single-page app that renders the same task data as a canvas tree and a filterable list, kept in sync across clients in real time. It runs as a Microsoft Teams tab with Entra ID single sign-on, hosted on Azure. Roughly ten users per workspace, hundreds of tasks, one project per deployment-grown-to-many.

The stack

Layer Technology
Backend Python 3.12, FastAPI, SQLAlchemy (sync), Alembic
Database PostgreSQL 16
Frontend React 18, TypeScript, Vite, React Flow (canvas), TanStack Table (list)
Auth Pluggable provider — local-dev for development, Entra ID for production
Real-time Server-Sent Events, in-process broadcaster
Hosting Azure App Service + Azure Database for PostgreSQL Flexible Server + Static Web Apps

Read these pages in order

If you're onboarding, this is a sensible path:

  1. Architecture — the layers, the request lifecycle, how a write flows end to end.
  2. Data model — tasks, the tree, dependencies, projects, memberships, the audit log.
  3. API surface — the /api/v1 shape, conventions, and the error/conflict contract.
  4. Authentication & authorization — the provider abstraction and per-project roles.
  5. Real-time sync — the SSE broadcaster, event payloads, catch-up, and the single-worker constraint.
  6. Concurrency & conflicts — last-write-wins with field-aware 409s.
  7. Migrations & schema — the Alembic workflow and the native_enum gotcha.
  8. Telemetry & observability — Application Insights wiring, with privacy filtering.
  9. Testing — the test layout, fixtures, and the coverage gate.
  10. Local development & contributing — get it running, and the Definition of Done.

How decisions are recorded

Every significant architectural choice is captured as an Architecture Decision Record (ADR) — a short Context / Decision / Consequences document, written before the code that implements it. ADRs are append-only history; they're not edited after they land. The pages here link to the relevant ADR for the reasoning behind each design. The ADRs themselves live in the repository under docs/adr/.

The golden rules

A handful of conventions run through the whole codebase. They're worth internalising early:

  • Sync, not async. Endpoints are plain def, not async def — the one exception is the SSE stream. (ADR 0009)
  • The database is the only durable store. No localStorage, no in-memory state that matters across a restart.
  • ADR-first. Structural decisions get an ADR before the code.
  • A change isn't done until lint, typecheck, and tests pass — and tests ship in the same change as the code they cover. See Contributing.