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:
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)
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:
ESLint
Configuração (frontend/eslint.config.mjs):
Usa eslint-config-next + @typescript-eslint + eslint-config-prettier.
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.
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"
]
}
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/ --fixsem erros residuais - [ ]
uv run ruff format app/sem diffs - [ ]
uv run mypy appsem erros - [ ] Sem
floatpara valores financeiros - [ ] Type hints em todas as funções públicas
- [ ]
response_modelem todos os endpoints
Frontend
- [ ]
npm run type-checksem erros - [ ]
npm run lintsem warnings - [ ]
npm run format:checksem diffs - [ ] Sem
anynos tipos TypeScript - [ ] Schemas Zod para dados de API
- [ ] Sem
console.logde debug