Skip to content

Qualidade de Código

Ferramentas, regras e processos de qualidade do fast_deliv.


Backend (Python)

Ruff — Linter e Formatter

Ruff é o linter e formatter principal do backend. É ultrarapido e substitui flake8, isort, black e pyupgrade.

Configuração (backend/pyproject.toml):

[tool.ruff]
line-length = 100
target-version = "py312"

[tool.ruff.lint]
select = ["E", "W", "F", "I", "B", "UP", "N", "S", "ANN"]
ignore = ["ANN401"]  # permite Any em casos específicos

[tool.ruff.lint.per-file-ignores]
"tests/**" = ["S", "ANN"]  # sem type hints e security em testes

Regras habilitadas: | Código | Categoria | Exemplos | |--------|-----------|---------| | E, W | pycodestyle | indentação, espaçamento | | F | pyflakes | imports não usados, variáveis indefinidas | | I | isort | ordenação de imports | | B | flake8-bugbear | bugs comuns | | UP | pyupgrade | modernizar sintaxe Python | | N | pep8-naming | convenções de nomes | | S | bandit | problemas de segurança | | ANN | flake8-annotations | type hints obrigatórios |

Comandos:

# Verificar sem modificar
uv run ruff check app/

# Corrigir automaticamente
uv run ruff check app/ --fix

# Formatar código
uv run ruff format app/

# Verificar formatação (sem modificar — para CI)
uv run ruff format app/ --check


Mypy — Verificação de Tipos

Mypy verifica tipos em modo strict — sem atalhos.

Configuração:

[tool.mypy]
python_version = "3.12"
strict = true
plugins = ["pydantic.mypy"]
ignore_missing_imports = true

[[tool.mypy.overrides]]
module = ["supabase.*", "pywebpush.*"]
ignore_missing_imports = true

[tool.pydantic-mypy]
init_forbid_extra = true
init_typed = true
warn_required_dynamic_aliases = true

O que strict = true ativa: - --disallow-any-generics — sem List sem tipo - --disallow-untyped-defs — todas as funções precisam de type hints - --no-implicit-optional — sem None implícito - --warn-return-any — aviso ao retornar Any - --warn-unused-ignores# type: ignore desnecessários

Comandos:

# Verificar tipagem
uv run mypy app

# Com relatório HTML
uv run mypy app --html-report mypy-report/

Regras de # type: ignore:

# CORRETO — sempre com comentário explicando o motivo
result = supabase.table("users").select("*").execute()  # type: ignore[no-untyped-call]

# ERRADO — nunca sem explicação
result = supabase.table("users").select("*").execute()  # type: ignore


Pydantic v2

Todos os schemas usam Pydantic v2 exclusivamente.

Regras:

# CORRETO — validators v2
from pydantic import BaseModel, field_validator

class PricingConfig(BaseModel):
    base_fare: Decimal

    @field_validator("base_fare", mode="before")
    @classmethod
    def coerce_decimal(cls, v: object) -> Decimal:
        return Decimal(str(v))

# ERRADO — validators v1 (não usar)
from pydantic import validator  # v1 - proibido

class OldConfig(BaseModel):
    @validator("base_fare")  # v1 - proibido
    def check_positive(cls, v: Decimal) -> Decimal:
        ...

Configurações de model:

class MyModel(BaseModel):
    model_config = ConfigDict(
        from_attributes=True,    # permite from ORM objects
        str_strip_whitespace=True,
    )


Valores Financeiros

Regra absoluta: Nunca usar float para dinheiro.

from decimal import Decimal

# CORRETO
earning = Decimal("15.00") + extra_km * Decimal("2.00")
total: Decimal = Decimal("0")

# ERRADO — float pode ter imprecisões
earning = 15.0 + extra_km * 2.0  # NUNCA

# Banco de dados: sempre converter via str
row = {"amount": str(Decimal("29.50"))}  # CORRETO
row = {"amount": float(earning)}         # NUNCA

Pre-commit Hooks (Backend)

# Instalar hooks
uv run pre-commit install

# Executar manualmente
uv run pre-commit run --all-files

Hooks configurados: 1. ruff check --fix — lint automático 2. ruff format — formatação automática 3. mypy app — verificação de tipos


Frontend (TypeScript)

TypeScript Strict

O tsconfig.json usa modo strict:

{
  "compilerOptions": {
    "strict": true,
    "noImplicitAny": true,
    "noImplicitReturns": true,
    "exactOptionalPropertyTypes": true
  }
}

Regras:

// CORRETO
function getRole(user: Profile): UserRole {
  return user.role
}

// ERRADO — sem any
function processData(data: any) { ... }           // proibido
function processData(data: unknown) {             // correto
  if (typeof data === 'string') { ... }
}

// ERRADO — sem as unknown
const delivery = response as unknown as Delivery  // proibido

Verificar:

npm run type-check  # tsc --noEmit


ESLint

Configuração (frontend/eslint.config.mjs):

Usa eslint-config-next + @typescript-eslint + eslint-config-prettier.

# Verificar
npm run lint

# Corrigir automaticamente
npm run lint:fix

Regras importantes: - Sem console.log em produção (no-console) - Sem imports não utilizados - Hooks React com deps corretas (react-hooks/exhaustive-deps)


Prettier

Formatação automática consistente.

# Formatar todos os arquivos
npm run format

# Verificar sem modificar (CI)
npm run format:check

Zod — Validação de Dados de API

Todas as respostas da API devem ser validadas com Zod:

import { z } from 'zod'

const DeliverySchema = z.object({
  id: z.string().uuid(),
  title: z.string().min(1),
  status: z.enum(['pending', 'assigned', 'in_progress', 'completed', 'cancelled']),
  driver_earning: z.number().positive(),
  // ...
})

type Delivery = z.infer<typeof DeliverySchema>

// Uso
const response = await fetch('/api/v1/deliveries')
const data = await response.json()
const deliveries = z.array(DeliverySchema).parse(data)  // lança se inválido

Husky + lint-staged

O frontend usa Husky para executar verificações antes de cada commit:

// frontend/package.json
"lint-staged": {
  "*.{ts,tsx}": [
    "eslint --fix",
    "prettier --write"
  ],
  "*.{json,css,mjs,mts}": [
    "prettier --write"
  ]
}
# Instalar hooks (uma vez após clonar)
cd frontend
npm run prepare  # instala husky

CI/CD

GitHub Actions — Backend

# .github/workflows/backend-ci.yml
name: Backend CI

on:
  pull_request:
    paths: ['backend/**']

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: astral-sh/setup-uv@v3
      - name: Install deps
        run: cd backend && uv sync --group dev
      - name: Lint
        run: cd backend && uv run ruff check app/
      - name: Format check
        run: cd backend && uv run ruff format app/ --check
      - name: Type check
        run: cd backend && uv run mypy app
      - name: Tests
        run: cd backend && uv run pytest

GitHub Actions — Frontend

# .github/workflows/frontend-ci.yml
name: Frontend CI

on:
  pull_request:
    paths: ['frontend/**']

jobs:
  quality:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: '20'
      - run: cd frontend && npm ci
      - name: Type check
        run: cd frontend && npm run type-check
      - name: Lint
        run: cd frontend && npm run lint
      - name: Format check
        run: cd frontend && npm run format:check

Checklist de Qualidade por Arquivo

Antes de abrir uma PR, verifique:

Backend

  • [ ] uv run ruff check app/ --fix sem erros residuais
  • [ ] uv run ruff format app/ sem diffs
  • [ ] uv run mypy app sem erros
  • [ ] Sem float para valores financeiros
  • [ ] Type hints em todas as funções públicas
  • [ ] response_model em todos os endpoints

Frontend

  • [ ] npm run type-check sem erros
  • [ ] npm run lint sem warnings
  • [ ] npm run format:check sem diffs
  • [ ] Sem any nos tipos TypeScript
  • [ ] Schemas Zod para dados de API
  • [ ] Sem console.log de debug