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 seus usuários devem fazer com a exceção? Uma ótima técnica que me deparei no início de minha carreira foi sempre relatar problemas e erros em três partes: contexto, problema e 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.
Nesse caso, o operador sabe exatamente o que fazer e para qual arquivo deve ser afetado. Eles também sabem que as alterações no pool de conexões não foram necessárias e devem ser repetidas.
Context: Sending email to 'abc@xyz.com' 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 'abc@xyz.com' about this problem.
Eu escrevo sistemas do lado do servidor e meus operadores geralmente oferecem suporte técnico de primeira linha. Eu escreveria as mensagens de maneira diferente para o software de desktop que tem um público diferente, mas inclui 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, portanto, codificar soluções dessa maneira ao escrever o código é de grande benefício para os usuários finais que estão em desvantagem em encontrar soluções, pois geralmente faltam informações sobre o que exatamente o software estava fazendo. Quem já leu uma mensagem de erro do Oracle saberá o que quero dizer.
A segunda coisa maravilhosa que vem à mente é quando você se encontra tentando descrever uma solução em sua exceção e está escrevendo "Marque X e se A então B mais C". Este é um sinal muito claro e óbvio de que sua exceção está sendo verificada no lugar errado. Você programador tem a capacidade de comparar as coisas no código, de modo que as instruções "if" devem ser executadas no código, por que envolver o usuário em algo que pode ser automatizado? Provavelmente, isso é mais profundo no código e alguém fez a coisa preguiçosa e lançou o IOException de vários métodos e detectou possíveis erros de todos eles em um bloco de código de chamada que não pode descrever adequadamente o que deu errado, o que o específicocontexto é e como corrigi-lo. Isso o encoraja a escrever erros de granulação mais refinados, capturá-los e manipulá-los no lugar certo no seu código, para que você possa articular corretamente as etapas que o operador deve executar.
Em uma empresa, tínhamos operadores de primeira linha que conheciam muito bem o software e mantinham seu próprio "caderno de atividades" que aumentava nosso relatório de erros e soluções sugeridas. Para reconhecer isso, o software começou a incluir, em exceções, links wiki para o livro de execução, para que uma explicação básica estivesse disponível, bem como links para discussões e observações mais avançadas pelos operadores ao longo do tempo.
Se você teve a diciplina para tentar essa técnica, fica muito mais óbvio o que você deve nomear suas exceções no código ao criar a sua própria. NonRecoverableConfigurationReadFailedException se torna um pequeno atalho para o 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 no meu código interpretar.