Categorias
Software Development

Os dois casos em que você deve tratar exceções

Exceções geralmente são tratadas sem critério, mas há apenas dois casos bem específicos em que você deve capturá-las. Veja como fazer o melhor uso das exceções.

É muito comum encontrar aplicações que usam exceções para retornar erros de validação de dados diretamente para o usuário, ou para controlar o fluxo de execução da aplicação.

Mas exceções são um artifício técnico, feito para programadores e pessoas da área, e não deveriam ser usadas para retornar erros para o usuário sobre coisas que ele pode corrigir (ex. alterando os dados informados).

Exceções representam um bug no sistema, um erro cometido pelos programadores, ou uma situação inesperada como queda na internet.

Sendo assim, há duas situações bem específicas sobre quando (ou onde) você deve usar blocos try catch para tratar exceções.

1. No nível mais baixo possível

Este é o nível em que você interage com código terceiro, como uma ferramenta de ORM ou qualquer biblioteca que faça operações de IO (que acesse recursos por HTTP, que leia um arquivo, que salve no banco de dados, etc.) Isto é, o nível em que você deixa o código nativo da sua aplicação para interagir com outros componentes.

Neste nível, problemas inesperados fora do seu controle, como falhas na conexão e arquivos bloqueados por outros processos, podem ocorrer.

Você pode querer tratar um erro de conexão com o banco de dados capturando um TimeoutException para que você possa tentar a conexão novamente dentro de alguns segundos.

As orientações neste cenário são:

  • Trate somente exceções específicas, como SqlTimeoutException or IOException. Nunca capture uma exceção genérica (do tipo Exception)
  • Trate somente se tiver algo útil para fazer a respeito, como tentar novamente uma conexão, ativar ações compensatórias, ou adicionar mais informações à exceção (ex. variáveis de contexto), e então lance a exceção novamente para que ela continue seu caminho
  • Não gere log neste nível
  • Deixe todas as demais exceções subirem, pois elas serão tratadas pelo segundo caso

2. No nível mais alto possível

Por “nível mais alto” eu me refiro ao último local onde é possível capturar a exceção antes que ela seja lançada diretamente para o usuário.

Seu objetivo aqui é gerar log do erro e enviar os detalhes aos programadores para que eles possam identificar e corrigir o problema. Adicione o máximo de informações possível, gere o log, e então mostre um pedido de desculpas para o usuário, já que provavelmente há pouco ou nada que ele possa fazer, principalmente se o erro foi causado por um bug no sistema.

As orientações neste segundo cenário são:

  • Capture a classe genérica Exception
  • Adicione mais informação sobre o contexto de execução atual
  • Gere o log do erro e notifique os programadores
  • Mostre um pedido de desculpas para o usuário
  • Resolva o problema o mais rápido que você puder

Motivos

1. Exceções representam erros irreversíveis

Mais uma vez, a razão por trás destas orientações é que uma exceção deve ser usada para representar um bug no sistema, um erro cometido pelos programadores, ou uma situação fora do controle da aplicação.

Nestes casos, geralmente não há nada que o usuário possa fazer. Assim, a única coisa que você pode fazer é gerar um log do erro, tomar as ações compensatórias necessárias, e se desculpar para o usuário. Se é um erro cometido pelos programadores, é melhor informá-los e deixar que eles consertem, trabalhando para gerar uma versão mais estável do software.

Você provavelmente terá que lidar com muitos erros no início do ciclo de vida da aplicação, mas isso é bom porque você também poderá corrigí-los no início, gerando uma aplicação mais estável, praticamente sem erros num curto espaço de tempo.

Um erro de validação não entra neste caso, porque geralmente tem a ver com dados incorretos informados pelos próprios usuários. Validação de dados deve acontecer na camada de apresentação, onde você pode retornar uma mensagem amigável na mesma hora, antes de enviar os dados para as partes internas do sistema. Se dados incorretos conseguiram chegar ao coração da aplicação, significa que o programador cometeu um erro, já que ele deixou de validar os dados na camada apropriada. Neste caso, é melhor deixar a exceção estourar, para que ela seja gravada em log no nível mais alto. Depois disso, resta apenas se desculpar com usuário e trabalhar na correção.

2. Blocos try catch mascaram o fluxo de execução da aplicação

Um bloco try catch tem uma função similar à de um label e seu companheiro goto, que faz com que o fluxo de execução da aplicação pule de um ponto para outro.

O uso indiscriminado de blocos try catch vão te fazer brincar de esconde-esconde com seus novos bugs 🙂

Veja posts como este para entender porque usar o goto é ruim.

Se você usar blocos try catch sem critérios claros na sua aplicação, você poderá estar criando situações de erro que serão complicadas de encontrar e debugar depois.

Então, reserve os blocos try catch somente para o tratamento de exceções de acordo com as dicas acima.

Bonus: quando lançar exceções

É mais fácil explicar no contexto do desenvolvimento de uma biblioteca ou plugin. A orientação é: lance uma exceção quando você se deparar com uma situação irreversível, em que não houver nada mais a fazer além de informar o consumidor das suas APIs, e deixá-lo decidir.

Imagine que você é um dos desenvolvedores da biblioteca ADO.Net (uma biblioteca de acesso a dados). Quando você tiver um erro de conexão neste contexto, não há nada que você possa fazer além de lançar uma exceção. Este é uma situação irreversível do ponto de vista de uma biblioteca de acesso a dados.

A conversa é outra se você estiver desenvolvendo um site. Você provavelmente capturaria esta exceção para tentar novamente por X vezes, mas lançaria uma exceção caso recebesse parâmetros inválidos das camadas mais externas, já que os dados deveriam ter sido validados lá.

O que também é outra história dentro da camada de Apresentação, onde você já espera que o usuário forneça dados incorretos e, neste caso, você só precisa mostrar uma mensagem amigável, sem a necessidade de lançar uma exceção.

Recapitulando…

  • Capture exceções específicas no nível mais baixo possível da aplicação, mas somente quando você tiver algo relevante para fazer a respeito. Do contrário, deixe a exceção subir
  • Capture exceções genéricas no nível mais alto possível, para que você possa gerar o log e se desculpar com o usuário de maneira elegante
  • Lance exceções quando você chegar num erro irreversível, o que depende do contexto em que você se encontra

E aí, ajudou? Deixe um comentário ali em baixo, falando como você lida com exceções no seu código e quais os benefícios ou problemas você teve com essa abordagem.

Por Phillippe Santana

Apaixonado por escrever código que as pessoas possam entender. Sou um desenvolvedor de software, gerente de projetos, empreendedor e entusiasta de pessoas/cultura. Me adicione no [Linkedin](https://www.linkedin.com/in/phillippesantana/) e no [Medium](https://medium.com/@phillippesantana).