Golden Master: o teste que você escreve sem saber o que o código deveria fazer

Golden Master: o teste que você escreve sem saber o que o código deveria fazer

Tem uma função no seu projeto que todo mundo evita mexer. Não porque o código seja complexo demais para entender, mas porque ninguém tem certeza do que ele deveria fazer, só do que ele faz. Algo assim: ...

25 de setembro de 2026 · 8 min · 1597 words · Riverfount
Seu datetime.utcnow() pode estar mentindo sobre o fuso horário

Seu datetime.utcnow() pode estar mentindo sobre o fuso horário

Cenário fictício, mas plausível em qualquer time que já mexeu em coluna de timestamp num banco em produção: pedidos criados às 21h aparecem no painel administrativo com horário de 18h do mesmo dia. Não é só uma questão de exibição no frontend, o valor gravado no banco já chega errado, e a diferença é sempre a mesma, três horas, exatamente o offset entre UTC e o horário de Brasília. A investigação leva até uma migração recente que trocou o tipo da coluna criado_em de TIMESTAMP para TIMESTAMP WITH TIME ZONE, parte de um trabalho maior para dar suporte a clientes em fusos diferentes. Ninguém precisou mexer no modelo em si para isso funcionar. ...

16 de setembro de 2026 · 6 min · 1177 words · Riverfount
O CI trava em lint antes de chegar no teste

O CI trava em lint antes de chegar no teste

Um pipeline de CI que ajudei a diagnosticar recentemente levava quatro minutos e meio para chegar no primeiro teste. Não quatro minutos e meio de suíte de testes: quatro minutos e meio de lint. flake8 rodando as regras de estilo, black --check validando formatação, isort --check conferindo ordem de imports, pylint fechando a lista com suas próprias verificações de design. Quatro processos Python inicializando, cada um lendo a árvore inteira do projeto, cada um com seu próprio cache (quando tinha cache configurado), cada um bloqueando o passo seguinte do workflow. O problema não é que essas ferramentas sejam ruins. black formata bem, isort organiza imports de forma previsível, pylint pega coisas que o resto ignora. O problema é a soma: quatro binários Python, quatro tempos de inicialização do interpretador, quatro passadas pela árvore de arquivos, para resolver um conjunto de checagens que, na prática, se sobrepõe bastante. ...

14 de setembro de 2026 · 5 min · 924 words · Riverfount
mongo_bakery: o fim da factory que você escrevia pra cada Document do MongoEngine

mongo_bakery: o fim da factory que você escrevia pra cada Document do MongoEngine

O teste queria garantir uma coisa só: cancelar um pedido pendente devolve 200. Pra chegar até essa asserção, ele teve que passar por isso primeiro: ...

16 de agosto de 2026 · 7 min · 1437 words · Riverfount
asyncio.TaskGroup: quando gather não é suficiente

asyncio.TaskGroup: quando gather não é suficiente

Um endpoint de checkout dispara três operações concorrentes com asyncio.gather: cobra o cartão, reserva o estoque, envia a notificação de confirmação do pedido. O serviço de pagamento cai no meio da chamada e lança uma exceção. gather propaga esse erro imediatamente para quem está aguardando. O handler captura a exceção, devolve um 500 para o cliente, registra a falha no log. Para quem está de plantão, o sistema reagiu do jeito certo. ...

12 de agosto de 2026 · 7 min · 1351 words · Riverfount
Protocol: duck typing com garantias em tempo de análise

Protocol: duck typing com garantias em tempo de análise

Você herdou um sistema de cálculo de descontos. Não tem classe base, não tem ABC, não tem interface formal nenhuma. Cada tipo de desconto (cupom, fidelidade, campanha sazonal) é só uma classe qualquer com um método aplicar(pedido) que devolve o valor final. O código que orquestra isso nem sabe que tipo de objeto está recebendo, só chama desconto.aplicar(pedido) e segue em frente. Duck typing raiz: se anda como pato e grasna como pato, aplica desconto como pato. ...

22 de julho de 2026 · 6 min · 1198 words · Riverfount
Uma API REST de mentira servida por json-server

O backend não ficou pronto e o frontend não pode esperar

Você está construindo a tela de uma lista de tarefas. Os componentes estão prontos, o layout se comporta em qualquer largura de tela, e falta uma única coisa: buscar as tarefas de algum lugar. Só que a API que deveria devolver essas tarefas ainda não existe. Está na fila de outra equipe, ou é você mesmo que vai escrevê-la depois. Talvez em FastAPI, talvez em Django, quando o frontend já estiver de pé. De qualquer forma, hoje ela não responde, e o fetch que você acabou de escrever aponta para o nada. ...

11 de julho de 2026 · 8 min · 1585 words · Riverfount
O bug que o mypy teria pego antes do deploy

O bug que o mypy teria pego antes do deploy

A função recebe um pedido vindo de uma integração externa, soma o valor de cada item e aplica um desconto. Em homologação, com os pedidos de teste, funciona perfeitamente. Em produção, na terceira semana, um cliente específico começa a receber o erro TypeError: unsupported operand type(s) for +: 'float' and 'str' em pleno checkout. 1 2 3 4 5 def calcular_total(itens: list) -> float: total = 0 for item in itens: total += item["preco"] * item["quantidade"] return total - item.get("desconto", 0) Note que a assinatura já tem uma anotação de tipo: itens: list. Ela não é falsa, mas é rasa o suficiente para não dizer nada de útil. list não revela que cada elemento é um dicionário com chaves específicas, nem que desconto deveria ser numérico. Essa anotação solta passa pelo code review sem levantar suspeita, porque parece tipagem, mas não carrega informação nenhuma sobre o formato real dos dados. É esse tipo de anotação incompleta, não a ausência total de anotação, que vai aparecer recorrentemente neste artigo como o verdadeiro adversário. ...

26 de junho de 2026 · 8 min · 1682 words · Riverfount
Decisões de arquitetura para uma integração com webhook, retry e fallback

Decisões de arquitetura para uma integração com webhook, retry e fallback

Você precisa integrar com dois serviços externos de avaliação de crédito, e eles não se parecem em nada na hora de responder. O principal recebe a proposta, processa por alguns minutos e te chama de volta por um webhook. O fallback — acionado só quando o principal falha — não chama ninguém: você é quem precisa ficar perguntando se ele já terminou. A tentação é tratar tudo isso como uma chamada com um try/except em volta. No caminho feliz, funciona. É no resto que mora este artigo. ...

18 de junho de 2026 · 8 min · 1640 words · Riverfount
suas queries do SQLAlchemy podem ser cacheadas sem Redis manual

suas queries do SQLAlchemy podem ser cacheadas sem Redis manual

O pool está configurado. As queries têm índice. O lru_cache eliminou as buscas repetidas nos endpoints mais simples. Mesmo assim, um endpoint de relatório continua lento, não porque está mal escrito, mas porque ele é genuinamente caro: agrega dados de várias tabelas, cruza informações de três meses, e faz isso a cada requisição, mesmo que os dados subjacentes não mudem por horas. O lru_cache não resolve. Ele cacheia por argumentos exatos, e os filtros de data variam o suficiente para inviabilizar o hit rate. O Redis resolve, mas exige serializar o resultado manualmente, gerenciar a chave, decidir em que camada a invalidação acontece, código de infraestrutura espalhado pela camada de negócio. O que falta é uma abstração que entenda o ORM. ...

2 de junho de 2026 · 7 min · 1298 words · Riverfount