Skip to main content
This guide outlines the coding standards and best practices for the Definable backend codebase. Following these standards ensures code consistency, maintainability, and quality across the project.

Code Formatting and Linting

Ruff

We use Ruff as our primary linter and formatter. Ruff combines many Python linting tools into one fast package. Our Ruff configuration is defined in ruff.toml at the project root:
Key standards enforced by our Ruff configuration:
  1. Indentation: Use 2 spaces for indentation (not tabs)
  2. Line Length: Maximum 150 characters per line
  3. Python Version: Code should be compatible with Python 3.10+
  4. Whitespace: No trailing whitespace, consistent spacing around operators

Type Checking with Mypy

We use Mypy for static type checking. Our Mypy configuration is defined in mypy.ini:
Key type checking requirements:
  1. Type Annotations: All function parameters and return values should have type annotations
  2. Imported Types: Use proper imports for types (e.g., from typing import List, Optional, Dict)
  3. Pydantic Models: Use properly typed Pydantic models for data validation

Pre-commit Hooks

We use pre-commit to run linting and type-checking automatically before commits. Our pre-commit configuration is in .pre-commit-config.yaml:
To set up pre-commit:
  1. Install pre-commit: pip install pre-commit
  2. Install hooks: pre-commit install

Code Structure and Organization

Directory Structure

Follow the established directory structure:

Import Order

Organize imports in the following order, with a blank line between each group:
  1. Standard library imports
  2. Third-party library imports
  3. Application-specific imports
Example:

Naming Conventions

  1. Files and Directories: Use snake_case (e.g., user_service.py)
  2. Classes: Use PascalCase (e.g., UserService)
  3. Functions and Variables: Use snake_case (e.g., get_user_by_id)
  4. Constants: Use UPPER_SNAKE_CASE (e.g., MAX_CONNECTIONS)
  5. Database Models: Use PascalCase with β€œModel” suffix (e.g., UserModel)
  6. Pydantic Schemas: Use PascalCase with descriptive suffixes:
    • Base schemas: UserBase
    • Create operations: UserCreate
    • Update operations: UserUpdate
    • Responses: UserResponse

Documentation Standards

Docstrings

Use triple-quoted docstrings for modules, classes, and functions:

Comments

  • Use comments to explain β€œwhy” not β€œwhat” when code is not self-explanatory
  • Keep comments up-to-date with code changes
  • Use complete sentences with proper punctuation

Error Handling

  1. HTTP Exceptions: Use FastAPI’s HTTPException with appropriate status codes
  2. Validation Errors: Use Pydantic for input validation
  3. Database Errors: Catch and handle specific database exceptions
  4. Logging: Include appropriate logging for errors with context
Example:

Service Design Patterns

Dependency Injection

Use FastAPI’s dependency injection system for:
  • Database sessions
  • Authentication
  • Authorization
  • Configuration
Example:

Separation of Concerns

  1. Models: Define database structure only
  2. Schemas: Handle data validation and serialization
  3. Services: Implement business logic
  4. API Routes: Handle HTTP concerns only

Testing Standards

All code should include appropriate tests:
  1. Unit Tests: Test individual functions and methods
  2. Integration Tests: Test interactions between components
  3. API Tests: Test HTTP endpoints
Use pytest fixtures for common test setup and async testing support. Example test:

Contributing Guidelines

Before submitting your code:
  1. Ensure all linting and type checking passes: ruff check and mypy src/
  2. Write or update tests for your changes
  3. Document new features or behavior changes
  4. Keep commits focused and with clear messages
  5. Rebase your branch on the latest main before requesting review

Common Mistakes to Avoid

  1. Circular Imports: Organize imports to prevent circular dependencies
  2. Missing Type Annotations: Ensure all functions have proper type hints
  3. Undocumented Magic: Avoid complex code without clear documentation
  4. Hardcoded Values: Use configuration instead of hardcoding sensitive data
  5. Overly Complex Functions: Keep functions focused on a single task

Best Practices

  1. Pagination: Always implement pagination for list endpoints
  2. Error Details: Provide specific, actionable error messages
  3. Defensive Programming: Validate inputs and handle unexpected cases
  4. Security: Follow security best practices for authentication and data handling
  5. Performance: Be mindful of database query efficiency