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.

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.0

As 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.0

Exceçã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.