English

Hi! If you're working with Python backend frameworks like FastAPI, Django, Flask, or Sanic, you’re in the right place.

In this article, I’ll share some of the best practices and coding patterns I’ve learned over the years.

These aren’t just conventional "best practices"—they’re also my personal preferences that help make projects more maintainable, testable, and scalable.


Project Structure

Always use a well-structured layout for your backend service. Whether it’s FastAPI, Flask, or Django, having a modular structure goes a long way.

  project-name/
  ├── app/
  │   ├── api/
  │   ├── services/
  │   ├── models/
  │   ├── db/
  │   └── utils/
  ├── tests/
  ├── Dockerfile
  ├── requirements.txt
  └── README.md

Organize your logic by domains instead of types (i.e., group by feature not function).


Dependency Injection (FastAPI)

Use FastAPI’s Depends mechanism to keep your code modular and testable.

from fastapi import Depends

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.get("/users")
def list_users(db: Session = Depends(get_db)):
    return db.query(User).all()


Environment Configuration

Never hardcode secrets. Use environment variables and .env files with something like python-dotenv.

from dotenv import load_dotenv
import os

load_dotenv()
DATABASE_URL = os.getenv("DATABASE_URL")

Use pydantic.BaseSettings for better structure in FastAPI:

from pydantic import BaseSettings

class Settings(BaseSettings):
    database_url: str

    class Config:
        env_file = ".env"

settings = Settings()

Logging

Always use Python’s built-in logging module, and configure log levels properly.

import logging

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
logger.info("App started")

Avoid using print() in production code.


Error Handling

Use custom exception handlers to manage errors cleanly, especially in FastAPI:

from fastapi.responses import JSONResponse
from fastapi import Request

@app.exception_handler(Exception)
async def generic_exception_handler(request: Request, exc: Exception):
    return JSONResponse(status_code=500, content={"detail": str(exc)})

SQLAlchemy ORM

Always use scoped_session and sessionmaker for DB access. Abstract queries inside repositories or services folders.


Background Tasks

Use Celery for background jobs:

from celery import Celery

celery_app = Celery(__name__, broker="redis://localhost:6379/0")

@celery_app.task
def send_email_task(email_data):
    send_email(email_data)

Testing

Use pytest and follow test-driven development (TDD) where possible. Separate unit and integration tests.

# test_main.py
from fastapi.testclient import TestClient
from app.main import app

client = TestClient(app)

def test_read_main():
    response = client.get("/")
    assert response.status_code == 200

Use factories or fixtures to mock data and avoid flaky tests.


API Versioning

Structure your API endpoints with versioning to support future updates:

/api/v1/users
/api/v2/users

Use Type Hints

Always use type hints for function arguments and return types. Improves readability and helps IDEs catch bugs early.

def get_user(user_id: int) -> Optional[User]:
    ...

Bonus: Auto Docs in FastAPI

Use docstrings and response models to auto-generate OpenAPI docs.

@app.get("/users", response_model=List[UserSchema])
def get_users():
    """Retrieve all users"""
    ...

You get beautiful Swagger UI out of the box!


0
0
0
0