Módulo 12 - Exceções, logging e robustez
Programação defensiva: validar cedo, falhar claro
9 min de leitura · por Cesar Gargiulo, revisado pela equipe ValorFinal e GuardiaSec · Atualizado em 01/07/2026
O que você vai aprender
- Adotar a postura de validar a entrada logo no início da função.
- Falhar rápido, com uma exceção clara, em vez de propagar dados inválidos.
- Diferenciar assert (invariante interna) de exceção (erro de entrada).
- Juntar exceções, logging e validação em código robusto.
Ouvir o resumo desta aula
Um recap de cerca de 2 minutos na voz do Valim, para ouvir no trânsito ou na academia.
Ler a transcrição do resumo
Resumo da aula: Programação defensiva: validar cedo, falhar claro.
Os objetivos desta aula. Adotar a postura de validar a entrada logo no início da função. Falhar rápido, com uma exceção clara, em vez de propagar dados inválidos. Diferenciar assert (invariante interna) de exceção (erro de entrada). Juntar exceções, logging e validação em código robusto.
Veja o essencial, parte por parte.
Validar logo na porta. Valide a entrada no começo da função, antes de qualquer cálculo.
assert ou exceção. Os assert podem ser desligados: rodar o Python com a opção -O remove todos eles.
Juntando robustez. As quatro aulas anteriores convergem aqui.
Esse foi o resumo do essencial. Para se aprofundar, leia a aula completa e responda os exercícios.
Validar logo na porta
Programação defensiva parte de uma ideia simples: não confie nos dados que chegam. Toda função que recebe entrada de fora, do usuário, de um arquivo, de outra parte do sistema, deve verificar se essa entrada faz sentido antes de agir. E o lugar dessa checagem é o começo da função, logo na porta. Validar cedo tem uma vantagem enorme: o erro estoura perto de quem o causou, com contexto claro, em vez de se propagar e explodir dez funções adiante, onde ninguém entende de onde veio o dado ruim.
class EntradaInvalida(ValueError):
pass
def media(notas):
# Valida na porta: recusa o que nao faz sentido, cedo e claro.
if not notas:
raise EntradaInvalida("a lista de notas esta vazia")
for n in notas:
if not isinstance(n, (int, float)):
raise EntradaInvalida(f"nota nao numerica: {n!r}")
if not 0 <= n <= 10:
raise EntradaInvalida(f"nota fora da faixa 0-10: {n}")
# A partir daqui, os dados sao confiaveis.
return sum(notas) / len(notas)
print(media([7, 8, 9])) # 8.0As checagens ficam no topo; depois delas, o corpo trabalha com dados confiáveis.
Note o efeito na clareza do corpo. Depois do bloco de validação, a última linha calcula a média sem nenhum medo: a lista não é vazia, todos os itens são números e estão na faixa. Toda a incerteza foi resolvida na porta. Se algo estava errado, o chamador recebe uma EntradaInvalida com uma mensagem que aponta exatamente o problema, o item específico e o motivo. Comparado a um erro genérico surgindo no meio de um cálculo, isso economiza horas de investigação.
assert ou exceção
Há dois instrumentos para dizer isto não deveria acontecer, e confundi-los é um erro clássico. A exceção, que você levanta com raise, é para condições que podem ocorrer em uso normal, sobretudo entrada inválida vinda de fora. O assert é para invariantes: condições que, se a sua lógica estiver correta, deveriam ser sempre verdadeiras. O assert existe para pegar bugs do próprio programa durante o desenvolvimento, não para validar o que o usuário digitou. E há um motivo técnico forte para essa separação.
def desconto(preco, percentual):
# Entrada externa: valide com excecao (sempre ativa).
if not 0 <= percentual <= 100:
raise ValueError(f"percentual invalido: {percentual}")
final = preco * (1 - percentual / 100)
# Invariante interna: se a logica esta certa, isto e sempre verdade.
# assert e para pegar bug de programacao, nao entrada do usuario.
assert final <= preco, "erro de logica: desconto aumentou o preco"
return final
print(desconto(100, 10)) # 90.0Exceção valida a entrada externa; assert confere uma invariante da própria lógica.
Juntando robustez
As quatro aulas anteriores convergem aqui. Uma função robusta valida a entrada na porta e recusa o que não faz sentido com uma exceção do seu domínio, aquela hierarquia da primeira aula. Se ela captura um erro técnico para traduzi-lo, encadeia com from para não perder a causa. Usa else e finally para organizar o sucesso e a limpeza. E, quando algo falha, registra com logging.exception, deixando um rastro que alguém vai ler depois. Nenhuma dessas peças é complicada; juntas, elas formam a diferença entre um script e software confiável.
Vale fechar com o espírito da postura, não só a técnica. Programar defensivamente não é encher o código de checagens paranoicas em cada linha. É desconfiar nas fronteiras, onde dados externos entram, e confiar no interior, onde os dados já foram validados. É preferir um erro barulhento e claro a um resultado errado e silencioso, porque o resultado errado silencioso é o que corrói a confiança do usuário sem aviso. Robustez, no fim, é uma forma de respeito por quem vai usar o seu programa e por quem vai mantê-lo, inclusive você mesmo daqui a seis meses.
Teste rápido
Por que não se deve validar a entrada do usuário com assert?
Perguntas frequentes
- Validar cedo não deixa as funções cheias de if no começo?
- Um bloco de validação no topo é um preço pequeno pela clareza que traz. Ele concentra a desconfiança num lugar só e deixa o resto da função limpo, trabalhando com dados já confiáveis. Se a validação repete muito, dá para extraí-la para uma função auxiliar ou usar um esquema com uma biblioteca de validação.
- Qual a regra simples para escolher entre assert e raise?
- Se a condição pode falhar por causa de dados de fora, use raise com uma exceção clara. Se ela só falharia por um bug na sua própria lógica, e nunca em uso normal, use assert. Resumindo: raise para o mundo externo, assert para invariantes internas do seu código.
- Falhar rápido não deixa o programa menos tolerante?
- Falhar rápido não significa derrubar tudo ao primeiro problema; significa detectar o erro perto da causa em vez de mascará-lo. Você ainda pode capturar essa falha numa camada acima e reagir com elegância. O que se evita é deixar dados inválidos seguirem e causarem um erro distante e obscuro.
- Onde entra o logging na programação defensiva?
- Quando uma função captura ou levanta um erro, registrar com logging deixa um rastro para investigação. A validação recusa o dado ruim; o log conta que isso aconteceu, quando e com qual valor. Juntos, eles fazem o programa falhar de forma visível e diagnosticável, não silenciosa.
- Devo validar dentro de uma função que só eu chamo?
- Nas fronteiras internas, uma validação leve com assert de invariantes já ajuda a pegar bugs cedo. A validação rigorosa com exceções é mais importante onde entram dados externos, de usuários ou de sistemas terceiros. Confie mais no interior já validado e desconfie das bordas por onde o mundo entra.
Fontes
Seu progresso fica salvo neste aparelho. Assinantes sincronizam entre os aparelhos.