Skip to content

Code Style

Carbon Connect enforces consistent code style across Python and TypeScript codebases through automated tooling.


Python Style Guide

Formatters and Linters

Tool Purpose Configuration
Black Code formatting 100-char line limit
Ruff Linting (replaces flake8, isort, pyflakes) pyproject.toml
isort Import sorting Integrated with Ruff
mypy Static type checking Strict mode
Bandit Security linting Excludes test files

Rules

  • Python 3.11+ with type hints mandatory on all function signatures
  • 4-space indentation (no tabs)
  • 100-character line limit (enforced by Black)
  • Double quotes for strings (Black default)
  • Trailing commas in multi-line collections

Naming Conventions

Element Convention Example
Modules snake_case matching_engine.py
Classes PascalCase MatchingEngine
Functions snake_case calculate_rule_score()
Constants UPPER_SNAKE_CASE SEMANTIC_WEIGHT = 0.25
Variables snake_case grant_score
Type aliases PascalCase GrantList = list[Grant]
Pydantic schemas PascalCase GrantResponse
SQLAlchemy models PascalCase Company
Test functions test_<feature>_should_<behavior> test_matching_should_disqualify_country_mismatch

Import Order

Enforced by Ruff (isort-compatible):

# 1. Standard library
from datetime import UTC, datetime
from uuid import UUID

# 2. Third-party
import httpx
import structlog
from fastapi import APIRouter, Depends
from sqlalchemy.ext.asyncio import AsyncSession

# 3. Local application
from backend.app.core.config import get_settings
from backend.app.models.database.grant import Grant
from backend.app.services.matching_engine import MatchingEngine

Type Hints

Type hints are mandatory:

# CORRECT: Full type annotations
async def calculate_matches(
    company_id: UUID,
    db: AsyncSession,
    min_score: float = 0.5,
    limit: int = 50,
) -> list[MatchResult]:
    ...

# WRONG: Missing type annotations
async def calculate_matches(company_id, db, min_score=0.5, limit=50):
    ...

Use modern Python 3.11+ syntax:

# CORRECT: Python 3.11+ syntax
def get_name(value: str | None) -> str:
    ...

items: list[str] = []
mapping: dict[str, int] = {}

# WRONG: Legacy syntax
from typing import Optional, List, Dict
def get_name(value: Optional[str]) -> str:
    ...

Docstrings

Use Google-style docstrings for public APIs:

def calculate_rule_score(company: Company, grant: Grant) -> float:
    """
    Calculate rule-based matching score.

    Components:
    - Country match (40% of rule score) - disqualifying if no match
    - NACE code match (35% of rule score)
    - Company size match (25% of rule score)

    Args:
        company: The company profile to evaluate.
        grant: The grant opportunity to match against.

    Returns:
        Float between 0.0 and 1.0 representing the rule-based score.

    Raises:
        ValueError: If company or grant data is invalid.
    """

TypeScript Style Guide

Formatters and Linters

Tool Purpose Configuration
ESLint Linting and code quality .eslintrc.json
Prettier Code formatting .prettierrc
TypeScript Type checking tsconfig.json (strict mode)

Rules

  • ES2022+ target with strict mode enabled
  • 2-space indentation (Prettier default)
  • Double quotes for strings
  • Semicolons required
  • Trailing commas in multi-line

Naming Conventions

Element Convention Example
Files (components) PascalCase.tsx GrantCard.tsx
Files (utilities) camelCase.ts apiClient.ts
Components PascalCase DashboardStats
Functions/hooks camelCase useGrants()
Constants UPPER_SNAKE_CASE API_BASE_URL
Types/interfaces PascalCase GrantSearchParams
CSS classes kebab-case via Tailwind text-sm font-medium

Component Style

Prefer functional components with hooks:

// CORRECT: Functional component with TypeScript
interface GrantCardProps {
  grant: Grant;
  onSave: (id: string) => void;
}

export function GrantCard({ grant, onSave }: GrantCardProps) {
  const [isSaved, setIsSaved] = useState(false);

  return (
    <div className="rounded-lg border p-4">
      <h3 className="text-lg font-semibold">{grant.title}</h3>
      {/* ... */}
    </div>
  );
}

Running Style Checks

Python

# Format code
poetry run black backend/ tests/
poetry run ruff check --fix backend/ tests/

# Check without modifying
poetry run black --check backend/ tests/
poetry run ruff check backend/ tests/

# Type checking
poetry run mypy backend/

# Security linting
poetry run bandit -r backend/ -ll

TypeScript

cd frontend

# Lint
npx eslint src/ --ext .ts,.tsx

# Format
npx prettier --check "src/**/*.{ts,tsx}"

# Type check
npx tsc --noEmit

Editor Configuration

VS Code

Recommended extensions:

  • Python (ms-python)
  • Ruff (charliermarsh.ruff)
  • Prettier (esbenp.prettier-vscode)
  • ESLint (dbaeumer.vscode-eslint)
  • Tailwind CSS IntelliSense (bradlc.vscode-tailwindcss)

Settings

{
  "editor.formatOnSave": true,
  "python.formatting.provider": "black",
  "python.linting.ruffEnabled": true,
  "[python]": {
    "editor.rulers": [100]
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  }
}