Skip to content

Coding Standards

These standards are enforced by linters (ruff, eslint) and reviewed in every PR.

Python (Backend)

Style

  • Format with ruff format (configured in pyproject.toml)
  • Lint with ruff check — all rules enforced
  • Type check with mypy in strict mode

Type hints

Type hints are required on every function signature. No Any without a code comment explaining why.

# Good
async def get_project(project_id: UUID, db: AsyncSession) -> Project | None:
    ...

# Bad
async def get_project(project_id, db):
    ...

Async

All I/O-bound code is async. Use async def for routes, services, and database access.

Naming

  • snake_case for functions, variables, modules
  • PascalCase for classes
  • UPPER_SNAKE_CASE for module-level constants
  • Database tables: snake_case, plural (e.g. projects, site_diary_entries)

Comments

Comments explain why, not what. The code shows what.

# Bad: Cache the user for 5 minutes
user = cache.get_or_set(user_id, lambda: db.get_user(user_id), ttl=300)

# Good: Cache user lookups for 5 minutes — see ADR-0012 for the trade-off
# between freshness and DB load. Invalidated on user.updated events.
user = cache.get_or_set(user_id, lambda: db.get_user(user_id), ttl=300)

TypeScript (Frontend)

Style

  • Format with Prettier (config in .prettierrc)
  • Lint with ESLint — all our rules enforced

Type safety

  • No any — use unknown if the type is genuinely unknown, then narrow it
  • Prefer interfaces over type aliases for object shapes
  • Use the auto-generated api.ts types — never hand-type API responses
// Good
import type { components } from "@/types/api"
type Project = components["schemas"]["Project"]

// Bad
type Project = {
  id: string
  name: string
  // ... drift inevitable
}

React

  • Functional components only (no class components)
  • Hooks for state and side effects
  • One component per file
  • Component file name matches the component name (ProjectList.tsx exports ProjectList)

Imports

Order:

  1. External libraries
  2. Internal aliases (@/...)
  3. Relative imports

Both languages

File organisation

modules/
└── [feature]/
    ├── models.py          # SQLAlchemy
    ├── schemas.py         # Pydantic
    ├── service.py         # Business logic
    ├── router.py          # FastAPI routes
    └── tests/             # Pytest tests

Don't repeat yourself, but don't abstract prematurely

If you see the same pattern in 2 places, fine. By the 3rd, extract.

Make impossible states impossible

Use union types and discriminated unions to make invalid states uncompilable.