Existe um tipo de código que passa em qualquer linter, mas quebra em produção: a função que cria o próprio cliente HTTP, espera na rede para sempre e registra “falha no processamento” sem dizer qual job falhou. Nenhum desses problemas é de estilo — são de engenharia. É aí que a prática sênior em Python começa.
1. Passe dependências em vez de escondê-las
Código é mais fácil de testar quando quem chama a função consegue ver quais colaboradores ela precisa. Em vez de a função construir o próprio httpx.Client() e chamar a rede de verdade, aceite o colaborador como parâmetro, tipado como Protocol:
from typing import Protocol
class OrderClient(Protocol):
def submit(self, payload: dict) -> dict: ...
def process_order(order: dict, client: OrderClient) -> str:
response = client.submit(order)
return response["status"]Com isso, um fake de cinco linhas substitui a rede nos testes. Uma ressalva honesta: Protocol é um contrato para o type checker, não valida nada em tempo de execução.
2. Deixe os context managers cuidarem da limpeza
Adquirir e liberar recursos no mesmo bloco visível é a função do with — e vale para muito além de arquivos: locks, transações de banco, diretórios temporários. O ponto crítico é o caso de falha: se o corpo levantar uma exceção, a limpeza ainda acontece.
3. Dê um prazo para cada espera externa
Uma espera sem limite é um modo de falha não declarado. No Python 3.11+, asyncio.timeout() resolve para código assíncrono; para clientes síncronos, cada biblioteca (HTTP, banco, fila) tem seu próprio mecanismo. O hábito é o prazo mais uma decisão sobre o que fazer quando ele estourar.
4. Registre eventos com contexto para investigar
“Processing failed” não é um registro operacional. A biblioteca padrão já resolve sem dependência de logging estruturado:
log.info("import finished", extra={"job_id": "j-193", "records": 4211})A regra de ouro: nomes de eventos estáveis e campos úteis — nunca tokens, senhas ou payloads sensíveis.
5. Teste o contrato de falha, não só o caminho feliz
Um teste que passa com um único input amigável não diz quase nada sobre como o código se comporta sob pressão. Use pytest.mark.parametrize para cobrir os inputs feios e monkeypatch para forçar o caminho de timeout ou resposta malformada.
6. Trate os metadados do pacote como parte do contrato
Declare no pyproject.toml como o projeto compila, do que depende e quais versões de Python suporta. Declarar httpx>=0.27 é uma suposição; bloquear versões exatas é outra decisão — confundir as duas é como dois ambientes “idênticos” divergem.
7. Depreque antes de deletar
Comportamento público não se remove sem aviso. Use warnings.warn com stacklevel=2 para apontar o aviso para a linha de quem chama, e sempre nomeie o substituto na mensagem. Surpresa para quase todo mundo: o Python esconde DeprecationWarning fora do __main__ — torne-o explícito nos testes com filterwarnings = ["error::DeprecationWarning"].
O hábito que resume tudo
As sete práticas colapsam em uma única pergunta de code review: onde este código espera, do que ele depende, e qual contrato acabou de mudar? Nenhum desses hábitos adiciona cerimônia por si só — cada um move uma suposição da cabeça de alguém para um lugar onde outro desenvolvedor, um teste ou um operador consegue enxergá-la.
Descubra mais sobre noticiAI
Assine para receber nossas notícias mais recentes por e-mail.



