Documentation
Table of Contents
Why Documentation Matters in a Production Backend
Good documentation turns your backend from a mysterious black box into a system that others can safely use, maintain, and extend. For a production project, documentation is not optional. It is part of the product.
You do not need to write a book. You need clear, focused documents that answer concrete questions for specific audiences: developers, operators, and API consumers.
In this chapter you will learn what to document for your final project, how to structure it, and see concrete examples you can adapt directly.
Rule: If someone cannot deploy, run, use, or debug your backend without talking to you, your documentation is incomplete.
Types of Documentation You Need
For a production backend, aim for at least these main categories:
| Type | Primary audience | Purpose |
|---|---|---|
| High-level overview | New developers, stakeholders | What the system does and how it is structured |
| Setup & development guide | Developers | How to run and work on the project locally |
| Deployment & operations | DevOps, SRE, you-in-6-months | How to deploy, configure, and maintain it |
| API documentation | Frontend devs, integrators | How to call the API and what to expect |
| Security & secrets | Developers, ops | How sensitive data is handled |
| Testing & QA | Developers, QA | How to run tests and check system health |
| Troubleshooting | On-call engineers | How to debug common problems |
You can put these in different files, or group some together. For a final project, a compact but complete set might be:
README.mddocs/architecture.mddocs/deployment.mddocs/api.mddocs/security.mddocs/troubleshooting.md
You do not have to match these names exactly, but keep the ideas.
Writing a Good README
The README.md is usually the first file someone sees. It must answer:
- What is this project?
- How do I run it locally?
- How do I run tests?
- Where can I find more detailed docs?
At minimum, include:
- Project description
- Quick start (development)
- Requirements
- Basic usage
- Links to other documentation
Example README Structure
Below is a template you can adapt. Replace names and commands with your own.
# TaskHub Backend
TaskHub is a production-ready REST API for managing tasks, users, and teams.
Tech stack: FastAPI, PostgreSQL, Redis, Celery, Docker.
## Features
- User registration, login, JWT-based authentication
- Role-based authorization (user, admin)
- Task CRUD endpoints with filtering and pagination
- Background jobs for sending emails and processing reports
- Caching for frequently accessed endpoints
- Dockerized for easy local development and deployment
## Quick Start (Development)
### Prerequisites
- Docker and Docker Compose installed
- `make` (optional, for convenience)
### 1. Clone the repository
```bash
git clone https://github.com/yourname/taskhub-backend.git
cd taskhub-backend2. Create environment file
Copy the example env file and adjust values if needed:
cp .env.example .env3. Start services
docker compose up --build
The API will be available at: http://localhost:8000
API docs: http://localhost:8000/docs
4. Run tests
docker compose run --rm api pytestProject Structure
.
ββ app/
β ββ api/ # API routes
β ββ core/ # Configuration, security, dependencies
β ββ db/ # Models, migrations, database session
β ββ services/ # Business logic, external integrations
β ββ workers/ # Celery tasks
β ββ main.py # FastAPI application entrypoint
ββ migrations/ # Alembic migrations
ββ tests/ # Test suite
ββ docker/ # Docker-related config
ββ docs/ # Additional documentationDocumentation
Focus on making the README actionable. Someone should be able to run the app locally in a few minutes following it.
:::danger
**Rule:** Your README must contain a working, copy-pasteable sequence of commands to start the app locally.
:::
# Architectural Documentation
Your architecture documentation explains how the system is put together and why it is designed that way. It is not a full backend textbook, it is a map for your specific project.
## What to Include
At minimum:
- Short system overview
- Main components and responsibilities
- Data flow through the system
- Key models / entities and main relationships
- External dependencies (databases, queues, storage, etc)
- Important design decisions and tradeoffs
## Example: Architecture Summary
```markdown
# Architecture
## Overview
TaskHub Backend is a monolithic FastAPI application using a layered architecture:
- **API layer**: HTTP endpoints and request/response models.
- **Service layer**: Business logic (tasks, users, notifications).
- **Persistence layer**: SQLAlchemy models and repository classes.
- **Infrastructure**: PostgreSQL, Redis, Celery, S3-compatible storage.
## Components
- **FastAPI application (`app/main.py`)**
- Creates FastAPI instance
- Includes API routers
- Configures middleware, exception handlers, and startup/shutdown hooks
- **API Routers (`app/api/`)**
- Group endpoints by domain: `users`, `auth`, `tasks`, `admin`
- Handle input validation using Pydantic models
- Delegate to services for business logic
- **Services (`app/services/`)**
- `user_service.py`: registration, login, profile management
- `task_service.py`: create/update/complete tasks, permissions
- `email_service.py`: send verification and reset-password emails
- Services depend on repositories and other services
- **Repositories (`app/db/repositories/`)**
- Encapsulate all direct database access
- Expose methods like `get_user_by_email`, `list_tasks_for_user`
- Implemented using SQLAlchemy ORM and sessions
- **Database models (`app/db/models.py`)**
- SQLAlchemy models describing PostgreSQL tables
- Main entities: `User`, `Role`, `Task`, `Project`, `RefreshToken`
- **Background Workers (`app/workers/`)**
- Celery tasks: `send_email`, `cleanup_expired_tokens`
- Use Redis as message broker and result backend
- **Caching**
- Redis used to cache:
- Auth token blacklist
- Frequently accessed task lists for dashboards
## Data Flow Example: Creating a Task
1. Client sends `POST /tasks` with JSON payload.
2. `api.tasks.create_task` endpoint validates input using `TaskCreate` model.
3. Endpoint calls `task_service.create_task(user_id, task_in)`.
4. Service performs permission checks, then calls `task_repository.create(...)`.
5. Repository creates SQLAlchemy `Task` instance and commits to PostgreSQL.
6. Service may trigger background `send_task_created_email` Celery task.
7. Endpoint returns `TaskRead` response model to the client.
## External Dependencies
- **PostgreSQL**
- Primary relational database
- Stores users, tasks, projects, and audit logs
- **Redis**
- Cache, Celery message broker, and rate limiting store
- **Object Storage**
- S3-compatible storage for file attachments
- Accessed via `storage_service` abstraction
## Key Design Decisions
- **Monolith instead of microservices**
- Single codebase and deployment is simpler for this project size.
- Easier local development and testing.
- **Service + Repository pattern**
- Keeps business logic separate from API and database details.
- Makes it easier to test services without hitting the database.
- **JWT for stateless auth**
- Scales horizontally without sticky sessions.
- Refresh tokens stored in database for security.Use diagrams if you like, but simple text descriptions are fine for a small to medium project.
API Documentation and OpenAPI
Your API is how others interact with your backend. For production, you must clearly describe:
- Endpoints and methods
- Path and query parameters
- Request and response schemas
- Authentication requirements
- Error formats and status codes
For a FastAPI project, you already get OpenAPI and interactive docs at /docs. You should still add:
- Clear summaries and descriptions on routes and models
- High-level API docs file that explains authentication and conventions
Documenting Authentication and Conventions
Create docs/api.md to explain how to use the API overall, without listing every field twice.
Example:
# API Usage Guide
Base URL (production):
- `https://api.taskhub.example.com`
Base URL (development):
- `http://localhost:8000`
## Authentication
Most endpoints require a Bearer token.
1. Register: `POST /auth/register`
2. Login: `POST /auth/login`
3. Use the `access_token` in the `Authorization` header:
```http
Authorization: Bearer <access_token>
Access tokens expire after 15 minutes. Use POST /auth/refresh with your refresh token to get a new access token.
Error Format
All errors follow this JSON structure:
{
"detail": "Human-readable error message",
"code": "ERROR_CODE",
"fields": {
"field_name": ["description of problem"]
}
}Examples:
code:"AUTH_INVALID_CREDENTIALS"code:"TASK_NOT_FOUND"
Pagination
List endpoints are paginated using limit and offset query parameters:
limitinteger, maximum 100, default 20offsetinteger, default 0
Response format:
{
"items": [ ... ],
"total": 123,
"limit": 20,
"offset": 0
}Common Endpoints
For full interactive documentation see /docs.
Tasks
GET /tasks
List tasks for the authenticated user.
Query params:
status:open,in_progress, ordone(optional)search: search query (optional)
POST /tasks
Create a new task.
Request body:
{
"title": "Write documentation",
"description": "Document the TaskHub API",
"due_date": "2026-09-01T12:00:00Z",
"priority": "high"
}
Response 201 Created:
{
"id": 42,
"title": "Write documentation",
"description": "Document the TaskHub API",
"status": "open",
"priority": "high",
"due_date": "2026-09-01T12:00:00Z",
"created_at": "2026-08-28T10:00:00Z",
"updated_at": "2026-08-28T10:00:00Z"
}You do not need to write every single endpoint by hand if your OpenAPI spec is clear, but a guide like this helps users understand the general rules quickly.
:::danger
**Rule:** Document how to authenticate, how pagination works, and what error format you use, even if the OpenAPI schema exists.
:::
# Configuration and Environment Documentation
Your app uses configuration through environment variables or config files. If these are not documented, deployment and local setup become guesswork.
Create a section or a file such as `docs/configuration.md` (or include it in deployment docs) that lists:
- All required environment variables
- Optional ones and their defaults
- Accepted values and purpose
## Example: Environment Variables Table
```markdown
# Configuration
The application is configured through environment variables. For development you can use the `.env` file.
| Variable | Required | Default | Description |
|-----------------------------|----------|----------------------------|--------------------------------------------------|
| `APP_ENV` | no | `development` | Environment name (`development`, `production`) |
| `APP_PORT` | no | `8000` | Port for the API server |
| `DATABASE_URL` | yes | | PostgreSQL URL, e.g. `postgresql+psycopg://...` |
| `REDIS_URL` | yes | | Redis URL for cache and Celery |
| `SECRET_KEY` | yes | | Random secret key for JWT signing |
| `ACCESS_TOKEN_EXPIRE_MIN` | no | `15` | Access token lifetime in minutes |
| `REFRESH_TOKEN_EXPIRE_DAYS` | no | `7` | Refresh token lifetime in days |
| `SMTP_HOST` | yes* | | SMTP server for sending email |
| `SMTP_PORT` | no | `587` | SMTP server port |
| `SMTP_USER` | yes* | | SMTP username |
| `SMTP_PASSWORD` | yes* | | SMTP password |
| `EMAIL_FROM` | yes* | | Default from address for emails |
| `S3_ENDPOINT_URL` | no | | S3-compatible storage endpoint |
| `S3_ACCESS_KEY_ID` | no | | S3 access key |
| `S3_SECRET_ACCESS_KEY` | no | | S3 secret key |
| `S3_BUCKET_NAME` | no | | S3 bucket for file uploads |
\*Required only if email features are enabled.Document any configuration that affects behavior in production, such as feature flags or rate limiting thresholds.
Deployment and Operations Documentation
Deployment documentation is for running your app in production, not just locally. It should describe:
- Required infrastructure
- Deployment steps
- Database migrations
- How to run workers and supporting services
- Health checks and monitoring endpoints
You can keep this in docs/deployment.md.
Example: Basic Deployment Guide
# Deployment Guide
This document describes how to deploy TaskHub Backend to a Linux server using Docker Compose.
## Requirements
- Linux server (Ubuntu 22.04 or similar)
- Docker and Docker Compose installed
- Domain name pointing to server IP (optional, for HTTPS)
## 1. Clone the repository on the server
```bash
git clone https://github.com/yourname/taskhub-backend.git
cd taskhub-backend2. Create production environment file
cp .env.example .env
nano .envSet at least:
APP_ENV=productionDATABASE_URL=postgresql+psycopg://...REDIS_URL=redis://redis:6379/0SECRET_KEY=<secure-random-value>- SMTP and S3 values as needed.
3. Start services
docker compose -f docker-compose.prod.yml up -dThis will start:
api: FastAPI app with Uvicorn/Gunicorndb: PostgreSQLredis: Redisworker: Celery workerscheduler: Celery beat for scheduled tasksnginx: Reverse proxy and HTTPS termination (if configured)
4. Run database migrations
docker compose -f docker-compose.prod.yml exec api alembic upgrade head5. Health Check
- API health check:
GET /healthshould return:
{ "status": "ok" }- Worker status: check logs
docker compose -f docker-compose.prod.yml logs worker6. Updating the Application
- Pull latest code:
git pull origin main- Rebuild and restart:
docker compose -f docker-compose.prod.yml up -d --build- Run migrations:
docker compose -f docker-compose.prod.yml exec api alembic upgrade headIf something goes wrong, you can roll back to the previous image tag in Docker or use Git to revert.
Also document:
- Where logs go, and how to view them.
- Any cron jobs or external schedulers.
- Backup procedures for the database and storage, if relevant.
# Security and Secrets Documentation
Security documentation should not contain secrets. It should describe:
- What secrets exist and where they must be stored
- How authentication and authorization work at a high level
- How to rotate secrets and tokens
- Any security features you have implemented (rate limiting, CORS, etc)
## Example: Security Overview
```markdown
# Security
## Secrets Management
The following sensitive values must never be committed to Git:
- `SECRET_KEY`
- Database passwords
- Redis passwords
- SMTP credentials
- S3 credentials
In production we recommend:
- Using environment variables managed by the deployment platform
- Limiting access to `.env` files to the application user only
- Rotating credentials periodically
## Authentication
- Access tokens are JWTs signed with `SECRET_KEY` using HS256.
- Access token lifetime: 15 minutes.
- Refresh tokens are stored in the database and can be revoked individually.
- A token blacklist is stored in Redis to immediately invalidate compromised tokens.
## Authorization
- Role-based access control:
- `user`: regular user, access only own tasks and profile.
- `admin`: manage all users and tasks, access to `/admin/*` endpoints.
- Some endpoints check resource ownership using the `user_id` associated with the task.
## Rate Limiting
- Per-IP rate limits for login and other auth endpoints.
- Limits are enforced using Redis counters:
- Example: max 5 login attempts per minute per IP.
## CORS
- Allowed origins are configured via `CORS_ALLOWED_ORIGINS` environment variable.
- In production, only the official frontend origins are allowed.
## HTTPS
- HTTPS is terminated at Nginx using Let's Encrypt certificates.
- HTTP traffic is redirected to HTTPS.This document tells others how security is supposed to work and where they must be careful.
Rule: Never store real secrets in your repository. Document how to provide them instead.
Testing and Quality Documentation
Explain how to run tests and what kind of tests you have. This helps others validate changes and understand quality expectations.
What to Document
- How to run all tests
- How to run a subset of tests
- How to generate coverage reports
- Any required external services or fixtures
Example: Testing Section
# Testing
We use `pytest` for testing.
## Running Tests Locally
```bash
# With Docker
docker compose run --rm api pytest
# On host (without Docker), with virtualenv activated
pytestRunning Specific Tests
Run tests in a specific file:
pytest tests/api/test_tasks.pyRun a single test:
pytest tests/api/test_tasks.py::test_create_taskCoverage
To generate a coverage report:
pytest --cov=app --cov-report=term-missingAim for at least 80% coverage on core modules:
app/services/app/api/app/db/repositories/
Integration Tests
Some tests require running PostgreSQL and Redis.
With Docker this is automatic. Without Docker:
- Ensure PostgreSQL and Redis are running.
- Configure
DATABASE_URLandREDIS_URLaccordingly.
# Troubleshooting and Common Issues
A short troubleshooting guide can save a lot of time. Capture common failure modes and how to debug them.
## What to Include
- Common startup errors and their causes
- Database and migration issues
- Authentication problems
- Worker and queue issues
## Example: Troubleshooting
```markdown
# Troubleshooting
## API container exits immediately
Check logs:
```bash
docker compose logs apiCommon causes:
- Missing or invalid
DATABASE_URL - Alembic migration error
- Missing
SECRET_KEY
"Database is locked" or connection errors
- Ensure PostgreSQL container is healthy:
docker compose ps
docker compose logs db- Check
DATABASE_URLin.env. - Ensure you are not running multiple migration processes in parallel.
"Invalid token" errors in API responses
Possible causes:
- Access token expired, use refresh token to obtain a new access token.
- Secret key changed; all existing tokens are invalid.
- Token is in the blacklist (user logged out or token revoked).
Celery tasks not executing
- Check worker logs:
docker compose logs worker- Ensure Redis is running and
REDIS_URLis correct. - Verify that tasks are being queued from the API (look for log messages).
Emails not sent
- Verify SMTP configuration:
SMTP_HOST,SMTP_PORT,SMTP_USER,SMTP_PASSWORD. - Check
email_servicelogs in API and worker containers. - Test manually using a simple script (documented in
docs/email-debug.mdif needed).
Views: 8
KAHIBARO