Se você codificar em C, Objective-C ou C ++, poderá usar o CLang Static Analyzer para criticar sua fonte sem realmente executá-la.
Existem algumas ferramentas de depuração de memória disponíveis: ValGrind, Guard Malloc no Mac OS X, Cerca Elétrica no * NIX.
Alguns ambientes de desenvolvimento oferecem a opção de usar um alocador de memória de depuração, que faz coisas como preencher páginas recém-alocadas e páginas recém-liberadas com lixo, detectar a liberação de ponteiros não alocados e gravar alguns dados antes e depois de cada bloco de heap, com o depurador sendo chamado se o padrão conhecido desses dados mudar.
Um cara do Slashdot disse que ganhou muito valor com a nova linha de fonte de etapa única em um depurador. "É isso aí", ele disse. Eu nem sempre sigo o seu conselho, mas quando o recebi, foi muito útil para mim. Mesmo se você não tiver um caso de teste que estimule um caminho de código incomum, poderá modificar uma variável no depurador para seguir esses caminhos, digamos, alocando um pouco de memória e, em seguida, usando o depurador para definir seu novo ponteiro como NULL em vez de endereço de memória e, em seguida, percorrer o manipulador de falhas de alocação.
Use assertions - a macro assert () em C, C ++ e Objective-C. Se o seu idioma não fornecer uma função de afirmação, escreva um você mesmo.
Use afirmações liberalmente e deixe-as em seu código. Eu chamo assert () "O teste que continua testando". Eu os uso com mais frequência para verificar as pré-condições no ponto de entrada da maioria das minhas funções. Essa é uma parte da "Programação por contrato", incorporada à linguagem de programação Eiffel. A outra parte são pós-condições, ou seja, usando assert () nos pontos de retorno da função, mas acho que não obtenho tanta milhagem quanto as pré-condições.
Você também pode usar assert para verificar invariantes de classe. Embora nenhuma classe seja estritamente obrigada a ter nenhuma invariável, as classes mais sensatas projetadas as possuem. Uma classe invariável é uma condição que sempre é verdadeira, exceto dentro das funções de membro que podem temporariamente colocar seu objeto em um estado inconsistente. Tais funções sempre devem restaurar a consistência antes de retornar.
Assim, cada função membro poderia verificar a invariante ao entrar e sair, e a classe poderia definir uma função chamada CheckInvariant que qualquer outro código poderia chamar a qualquer momento.
Use uma ferramenta de cobertura de código para verificar quais linhas de sua fonte estão realmente sendo testadas e, em seguida, crie testes que estimulem as linhas não testadas. Por exemplo, você pode verificar os manipuladores com pouca memória executando seu aplicativo em uma VM configurada com pouca memória física e nenhum arquivo de troca ou um arquivo muito pequeno.
(Por alguma razão que eu nunca tive conhecimento, embora o BeOS pudesse ser executado sem um arquivo de troca, era altamente instável dessa forma. Dominic Giampaolo, que escreveu o sistema de arquivos BFS, me pediu para nunca executar o BeOS sem troca. Eu não veja por que isso importa, mas deve ter sido algum tipo de artefato de implementação.)
Você também deve testar a resposta do seu código a erros de E / S. Tente armazenar todos os seus arquivos em um compartilhamento de rede e desconecte o cabo de rede enquanto o aplicativo tem uma carga de trabalho alta. Da mesma forma, desconecte o cabo - ou desligue a sua conexão sem fio - se estiver se comunicando pela rede.
Uma coisa que acho particularmente irritante são sites que não possuem código Javascript robusto. As páginas do Facebook carregam dezenas de pequenos arquivos Javascript, mas se algum deles falhar no download, a página inteira é quebrada. Só precisa haver alguma maneira de fornecer alguma tolerância a falhas, digamos, repetindo um download ou fornecer algum tipo de fallback razoável quando alguns de seus scripts não foram baixados.
Tente matar seu aplicativo com o depurador ou com "kill -9" no * NIX enquanto ele estiver no meio da gravação de um arquivo grande e importante. Se o seu aplicativo estiver bem arquitetado, o arquivo inteiro será gravado ou não será gravado, ou talvez seja apenas parcialmente gravado, o que for gravado não será corrompido, com os dados salvos sendo completamente utilizáveis por o aplicativo após a leitura do arquivo.
os bancos de dados sempre têm E / S de disco tolerantes a falhas, mas quase nenhum outro tipo de aplicativo possui. Embora os sistemas de arquivos registrados no diário evitem a corrupção do sistema de arquivos em caso de falha de energia ou falha, eles não fazem nada para impedir a corrupção ou a perda de dados do usuário final. Essa é a responsabilidade dos aplicativos do usuário, mas quase nenhum outro banco de dados implementa tolerância a falhas.