Opinionated list of best practices and conventions we use at our startups.
After several years of building production systems, we've made both good and bad decisions that significantly impacted our developer experience. Here are some lessons worth sharing.
Working with an AI agent? See AGENTS.md for the same rules in a terse, machine-readable format with a version matrix, Do/Don't blocks, and an anti-patterns checklist.
- Project Structure
- Async Routes
- Pydantic
- Dependencies
- Miscellaneous
- Follow the REST
- FastAPI response serialization
- If you must use sync SDK, then run it in a thread pool.
- BackgroundTasks vs a real task queue
- ValueErrors might become Pydantic ValidationError
- Docs
- Set DB keys naming conventions
- Migrations. Alembic
- Set DB naming conventions
- SQL-first. Pydantic-second
- Set tests client async from day 0
- Use ruff
- Bonus Section
There are many ways to structure a project, but the best structure is one that is consistent, straightforward, and free of surprises.
Many example projects and tutorials organize projects by file type (e.g., crud, routers, models), which works well for microservices or smaller projects. However, this approach didn't scale well for our monolith with many domains and modules.
The structure I found more scalable and evolvable is inspired by Netflix's Dispatch, with some minor modifications.
fastapi-project
├── alembic/
├── src
│ ├── auth
│ │ ├── router.py
│ │ ├── schemas.py # pydantic models
│ │ ├── models.py # db models
│ │ ├── dependencies.py
│ │ ├── config.py # local configs
│ │ ├── constants.py
│ │ ├── exceptions.py
│ │ ├── service.py
│ │ └── utils.py
│ ├── aws
│ │ ├── client.py # client model for external service communication
│ │ ├── schemas.py
│ │ ├── config.py
│ │ ├── constants.py
│ │ ├── exceptions.py
│ │ └── utils.py
│ ├── posts
│ │ ├── router.py
│ │ ├── schemas.py
│ │ ├── models.py
│ │ ├── dependencies.py
│ │ ├── constants.py
│ │ ├── exceptions.py
│ │ ├── service.py
│ │ └── utils.py
│ ├── config.py # global configs
│ ├── models.py # global models
│ ├── exceptions.py # global exceptions
│ ├── pagination.py # global module e.g. pagination
│ ├── database.py # db connection related stuff
│ └── main.py
├── tests/
│ ├── auth
│ ├── aws
│ └── posts
├── templates/
│ └── index.html
├── requirements
│ ├── base.txt
│ ├── dev.txt
│ └── prod.txt
├── .env
├── .gitignore
├── logging.ini
└── alembic.ini