Coding Standards
Padrões de código para Python (Backend) e JavaScript/React (Frontend).
Geral
Princípios
- KISS: Keep It Simple, Stupid
- DRY: Don't Repeat Yourself
- YAGNI: You Aren't Gonna Need It
- Legibilidade > Cleverness
- Consistência > Preferência pessoal
Idioma
- Código: Inglês (variáveis, funções, classes, comentários)
- Documentação: Português para docs internas, Inglês para docs de API
- Commits: Inglês
Python (Backend)
Style Guide
Seguimos PEP 8 com algumas customizações.
# Formatar código
black .
# Verificar estilo
ruff check .
# Auto-fix quando possível
ruff check --fix .
Naming Conventions
# Variáveis e funções: snake_case
user_name = "John"
def calculate_total(): ...
# Classes: PascalCase
class UserService: ...
# Constantes: UPPER_SNAKE_CASE
MAX_RETRIES = 3
API_TIMEOUT = 30
# Private: prefixo com _
def _internal_helper(): ...
_cache = {}
# Arquivos: snake_case
# user_service.py, api_client.py
Docstrings
Usamos Google Style:
def calculate_discount(price: float, discount_rate: float = 0.1) -> float:
"""Calcula preço com desconto aplicado.
Args:
price: Preço original do produto
discount_rate: Taxa de desconto (0.0 a 1.0). Default 0.1 (10%)
Returns:
Preço final com desconto aplicado
Raises:
ValueError: Se price for negativo ou discount_rate fora do range
Examples:
>>> calculate_discount(100.0, 0.2)
80.0
>>> calculate_discount(50.0)
45.0
"""
if price < 0:
raise ValueError("Price cannot be negative")
if not 0 <= discount_rate <= 1:
raise ValueError("Discount rate must be between 0 and 1")
return price * (1 - discount_rate)
Type Hints
Sempre use type hints:
from typing import Optional, List, Dict, Union
# Funções
def get_user(user_id: int) -> Optional[User]:
...
# Variáveis (quando não óbvio)
users: List[User] = []
config: Dict[str, Any] = {}
# Generics
from typing import Generic, TypeVar
T = TypeVar('T')
class Repository(Generic[T]):
def get(self, id: int) -> Optional[T]:
...
Estrutura de Arquivos
"""Module docstring explaining purpose.
This module handles user authentication and authorization.
"""
# 1. Standard library imports
import os
import sys
from datetime import datetime
# 2. Third-party imports
from fastapi import APIRouter, Depends
from sqlalchemy import select
# 3. Local application imports
from app.models import User
from app.services import AuthService
# 4. Constants
MAX_LOGIN_ATTEMPTS = 5
SESSION_TIMEOUT = 3600
# 5. Functions/Classes
class UserController:
...
FastAPI Específico
from fastapi import APIRouter, HTTPException, status
from pydantic import BaseModel, Field
# Pydantic models com exemplos
class CreateUserRequest(BaseModel):
"""Request model for creating user."""
email: str = Field(..., example="user@example.com")
name: str = Field(..., min_length=2, max_length=100)
age: Optional[int] = Field(None, ge=0, le=150)
class Config:
json_schema_extra = {
"example": {
"email": "john@example.com",
"name": "John Doe",
"age": 30
}
}
# Endpoints documentados
@router.post("/users", response_model=UserResponse, status_code=status.HTTP_201_CREATED)
async def create_user(
request: CreateUserRequest,
service: UserService = Depends(get_user_service)
) -> UserResponse:
"""Create a new user.
Args:
request: User creation data
service: Injected user service
Returns:
Created user data
Raises:
HTTPException 400: If email already exists
HTTPException 422: If validation fails
"""
try:
user = await service.create_user(request)
return UserResponse.from_orm(user)
except EmailExistsError:
raise HTTPException(
status_code=status.HTTP_400_BAD_REQUEST,
detail="Email already registered"
)
Async/Await
# Prefer async quando possível
async def fetch_user(user_id: int) -> User:
async with get_session() as session:
result = await session.execute(
select(User).where(User.id == user_id)
)
return result.scalar_one_or_none()
# Use asyncio.gather para paralelizar
users, products = await asyncio.gather(
fetch_users(),
fetch_products()
)
Error Handling
# Use exceções específicas
class UserNotFoundError(Exception):
"""Raised when user is not found."""
pass
# Catch específico
try:
user = await service.get_user(user_id)
except UserNotFoundError:
raise HTTPException(status_code=404, detail="User not found")
except DatabaseError as e:
logger.error(f"Database error: {e}")
raise HTTPException(status_code=500, detail="Internal server error")
Logging
import logging
logger = logging.getLogger(__name__)
# Levels: DEBUG, INFO, WARNING, ERROR, CRITICAL
logger.info("User logged in", extra={"user_id": user.id})
logger.warning("Invalid login attempt", extra={"email": email})
logger.error("Database connection failed", exc_info=True)
# Structured logging (JSON)
logger.info(
"Payment processed",
extra={
"user_id": user.id,
"amount": amount,
"transaction_id": tx_id
}
)
JavaScript/TypeScript (Frontend)
Style Guide
Seguimos Airbnb Style Guide com TypeScript.
Formatter: Prettier
Linter: ESLint
Naming Conventions
// Variáveis e funções: camelCase
const userName = "John";
function calculateTotal() {}
// Classes e Componentes: PascalCase
class UserService {}
function UserProfile() {}
// Constantes: UPPER_SNAKE_CASE
const MAX_RETRIES = 3;
const API_BASE_URL = "https://api.example.com";
// Private: prefixo com #
class Example {
#privateField = 0;
}
// Arquivos
// - Componentes: PascalCase (UserProfile.tsx)
// - Utils/Services: camelCase (apiClient.ts)
// - Hooks: camelCase com use prefix (useAuth.ts)
React Components
import React, { useState, useEffect } from 'react';
interface UserProfileProps {
userId: number;
onUpdate?: (user: User) => void;
}
/**
* Displays user profile information.
*
* @param userId - The ID of the user to display
* @param onUpdate - Optional callback when user is updated
*/
export function UserProfile({ userId, onUpdate }: UserProfileProps) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser(userId)
.then(setUser)
.finally(() => setLoading(false));
}, [userId]);
if (loading) return <Loader />;
if (!user) return <NotFound />;
return (
<div className="user-profile">
<h1>{user.name}</h1>
<p>{user.email}</p>
</div>
);
}
Hooks
import { useState, useEffect, useCallback } from 'react';
/**
* Custom hook for fetching and managing user data.
*
* @param userId - The ID of the user to fetch
* @returns User data, loading state, and refresh function
*/
export function useUser(userId: number) {
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
const [error, setError] = useState<Error | null>(null);
const fetchUser = useCallback(async () => {
try {
setLoading(true);
const data = await apiClient.getUser(userId);
setUser(data);
} catch (err) {
setError(err as Error);
} finally {
setLoading(false);
}
}, [userId]);
useEffect(() => {
fetchUser();
}, [fetchUser]);
return { user, loading, error, refresh: fetchUser };
}
TypeScript
Sempre use tipos explícitos:
// Interfaces para objetos
interface User {
id: number;
name: string;
email: string;
createdAt: Date;
}
// Types para unions/intersections
type Status = 'pending' | 'approved' | 'rejected';
type UserWithRole = User & { role: string };
// Generics
function getValue<T>(key: string): T | null {
const value = localStorage.getItem(key);
return value ? JSON.parse(value) as T : null;
}
Error Handling
// Try-catch para async
async function loadData() {
try {
const data = await apiClient.getData();
return data;
} catch (error) {
if (error instanceof ApiError) {
toast.error(error.message);
} else {
logger.error('Unexpected error', error);
toast.error('Something went wrong');
}
return null;
}
}
// Error boundaries para React
class ErrorBoundary extends React.Component<Props, State> {
state = { hasError: false };
static getDerivedStateFromError(error: Error) {
return { hasError: true };
}
componentDidCatch(error: Error, info: React.ErrorInfo) {
logger.error('React error boundary', { error, info });
}
render() {
if (this.state.hasError) {
return <ErrorFallback />;
}
return this.props.children;
}
}
Testes
Naming
# Python (pytest)
def test_user_creation_with_valid_data():
"""Test that user is created successfully with valid input."""
...
def test_user_creation_fails_with_invalid_email():
"""Test that user creation raises error with invalid email."""
...
# JavaScript (Jest)
describe('UserService', () => {
describe('createUser', () => {
it('creates user with valid data', () => {
...
});
it('throws error with invalid email', () => {
...
});
});
});
Estrutura
# Arrange-Act-Assert (AAA)
def test_calculate_discount():
# Arrange
price = 100.0
discount_rate = 0.2
# Act
result = calculate_discount(price, discount_rate)
# Assert
assert result == 80.0
Comentários
Quando Comentar
✅ Comentar: - Por quê, não o quê - Decisões não óbvias - Workarounds temporários (com TODO/FIXME) - Lógica complexa que não pode ser simplificada - Avisos importantes
❌ Não Comentar: - Código autoexplicativo - Repetir o que o código já diz - Código comentado (delete!)
# ❌ Ruim
# Incrementa i
i += 1
# ✅ Bom
# Skip processing for admin users as they have different workflow
if user.is_admin:
continue
# TODO(user): Refatorar para usar queue quando disponível (#123)
process_sync(data) # Temporário até migration completa
Security
Nunca Commitar
- ❌ Senhas ou API keys
- ❌ Tokens de acesso
- ❌ Certificados privados
- ❌ Dados de produção
- ❌
.envfiles (use.env.example)
Sempre
- ✅ Validar input do usuário
- ✅ Sanitizar output
- ✅ Usar prepared statements (SQL injection)
- ✅ Escapar HTML (XSS)
- ✅ Verificar autenticação/autorização
- ✅ Rate limiting em APIs públicas
Pre-commit Hooks
Instalamos hooks que rodam antes de commit:
# Instalar pre-commit
pip install pre-commit
pre-commit install
# Hooks configurados:
# - black (formatter Python)
# - ruff (linter Python)
# - prettier (formatter JS/TS)
# - eslint (linter JS/TS)
# - check-yaml
# - check-json
# - detect-secrets
Referências
Python: - PEP 8 - Google Python Style Guide - FastAPI Best Practices
JavaScript/TypeScript: - Airbnb Style Guide - React TypeScript Cheatsheet - Clean Code JavaScript
Geral: - Clean Code by Robert C. Martin