Estou pensando nesse problema há algum tempo e gostaria de ter opiniões de outros desenvolvedores.
Eu costumo ter um estilo de programação muito defensivo. Meu bloco ou método típico é assim:
T foo(par1, par2, par3, ...)
{
// Check that all parameters are correct, return undefined (null)
// or throw exception if this is not the case.
// Compute and (possibly) return result.
}
Além disso, durante o cálculo, verifico todos os ponteiros antes de retirá-los de referência. Minha idéia é que, se houver algum bug e algum ponteiro NULL aparecer em algum lugar, meu programa deve lidar com isso muito bem e simplesmente se recusar a continuar o cálculo. Obviamente, ele pode notificar o problema com uma mensagem de erro no log ou algum outro mecanismo.
Para colocar de uma maneira mais abstrata, minha abordagem é
if all input is OK --> compute result
else --> do not compute result, notify problem
Outros desenvolvedores, entre os quais alguns colegas meus, usam outra estratégia. Por exemplo, eles não verificam ponteiros. Eles assumem que um pedaço de código deve receber a entrada correta e não deve ser responsável pelo que acontece se a entrada estiver incorreta. Além disso, se uma exceção de ponteiro NULL travar o programa, um erro será encontrado mais facilmente durante o teste e terá mais chances de ser corrigido.
Minha resposta é normalmente: mas e se o bug não for encontrado durante o teste e aparecer quando o produto já estiver sendo usado pelo cliente? Qual é a maneira preferida para o bug se manifestar? Deve ser um programa que não executa uma determinada ação, mas ainda pode continuar funcionando, ou um programa que trava e precisa ser reiniciado?
Resumindo
Qual das duas abordagens para lidar com informações erradas você recomendaria?
Inconsistent input --> no action + notification
ou
Inconsistent input --> undefined behaviour or crash
Editar
Obrigado pelas respostas e sugestões. Também sou fã de design por contrato. Mas mesmo que eu confie na pessoa que escreveu o código chamando meus métodos (talvez seja eu mesmo), ainda pode haver erros, levando a informações incorretas. Portanto, minha abordagem é nunca assumir que um método recebe a entrada correta.
Além disso, eu usaria um mecanismo para capturar o problema e notificá-lo. Em um sistema de desenvolvimento, por exemplo, abriria uma caixa de diálogo para notificar o usuário. Em um sistema de produção, basta escrever algumas informações no log. Não acho que verificações extras possam levar a problemas de desempenho. Não tenho certeza se afirmações são suficientes, se elas estão desativadas em um sistema de produção: talvez alguma situação ocorra na produção que não ocorreu durante o teste.
De qualquer forma, fiquei realmente surpreso que muitas pessoas sigam a abordagem oposta: elas deixam o aplicativo travar "de propósito", porque sustentam que isso facilitará a localização de erros durante o teste.