Vou direcionar minha resposta mais para o que vem depois de uma exceção: para que serve e como o software deve se comportar, o que os usuários devem fazer com a exceção? Uma ótima técnica que encontrei no início da minha carreira foi sempre reportar problemas e erros em 3 partes: contexto, problema & solução. O uso dessa diciplina altera enormemente o tratamento de erros e torna o software muito melhor para os operadores usarem.
Aqui estão alguns exemplos.
Context: Saving connection pooling configuration changes to disk.
Problem: Write permission denied on file '/xxx/yyy'.
Solution: Grant write permission to the file.
Neste caso, o operador sabe exatamente o que fazer e a qual arquivo deve ser afetado. Eles também sabem que as alterações no pool de conexão não ocorreram e devem ser repetidas.
Context: Sending email to '[email protected]' regarding 'Blah'.
Problem: SMTP connection refused by server 'mail.xyz.com'.
Solution: Contact the mail server administrator to report a service problem. The email will be sent later. You may want to tell '[email protected]' about this problem.
Eu escrevo sistemas no lado do servidor e meus operadores geralmente são especialistas em suporte de primeira linha. Eu escreveria as mensagens de forma diferente para softwares de desktop que têm um público diferente, mas incluem as mesmas informações.
Várias coisas maravilhosas acontecem se alguém usa essa técnica. O desenvolvedor de software geralmente está em melhor posição para saber como resolver os problemas em seu próprio código, de modo que codificar soluções dessa forma enquanto você escreve o código é de grande benefício para os usuários finais que estão em desvantagem ao encontrar soluções, pois o que exatamente o software estava fazendo. Qualquer pessoa que já tenha lido uma mensagem de erro da Oracle saberá o que quero dizer.
A segunda coisa maravilhosa que vem à mente é quando você está tentando descrever uma solução em sua exceção e está escrevendo "Check X e se A e B em C". Este é um sinal muito claro e óbvio de que sua exceção está sendo verificada no lugar errado. Você, o programador, tem a capacidade de comparar as coisas no código, então as instruções "if" devem ser executadas no código, por que envolver o usuário em algo que pode ser automatizado? As chances são de que é mais profundo no código e alguém fez a coisa preguiçosa e lançou IOException a partir de qualquer número de métodos e pegou erros potenciais de todos eles em um bloco de código de chamada que não pode descrever adequadamente o que deu errado, o que contexto específico é e como corrigi-lo. Isso o encoraja a escrever erros de granulação mais refinados, capturá-los e manipulá-los no lugar certo em seu código, para que você possa articular adequadamente as etapas que o operador deve seguir.
Em uma empresa, tínhamos operadores de primeira linha que conheciam o software muito bem e mantinham seu próprio "run book", que aumentava nosso relatório de erros e sugeria soluções. Para reconhecer isso, o software começou incluindo links wiki para o runbook em exceções, de modo que uma explicação básica estava disponível, assim como links para discussões e observações mais avançadas pelos operadores ao longo do tempo.
Se você já teve a disciplina para tentar esta técnica, torna-se muito mais óbvio o que você deve nomear suas exceções no código ao criar o seu próprio. O NonRecoverableConfigurationReadFailedException torna-se uma abreviação do que você está prestes a descrever mais detalhadamente para o operador. Eu gosto de ser detalhado e acho que será mais fácil para o próximo desenvolvedor que tocar meu código para interpretar.