quinta-feira, 27 de junho de 2013

A Regra de Ouro da Programação

Desde que as linguagens de programação são linguagens de programação, existe um problema em particular que é a principal causa de bugs na maioria dos softwares desenvolvidos.

E esse problema sempre aparece, linguagem após linguagem, programador após programador. E na maioria das vezes não adianta nem mesmo se dizer um programador experiente; esse problema sempre aparecerá. Como com a maioria dos programadores, esse problema já me atormentou por vários dias em programas em Pascal, Delphi, Java e principalmente em C.

Null Pointer Exception

Desde que eu comecei a contribuir com o canal #DelphiX na antiga Brasnet (e hoje na FreeNode, mas que não participo mais) uma das grandes causas de problemas com códigos era esse erro.

O Null Pointer Exception é um erro complexo de se tratar e descobrir, pois ele não é fruto de nenhum erro de sintaxe ou de atribuição indevida e ele só vai realmente aparecer muito mais para frente na execução do seu código. Desde então, eu criei uma filosofia, ensinei para quem foi possível ensinar e penso até que todo novo programador deveria tatuar isso em si mesmo, como lembrança...

Mas, qual é essa regra de ouro?


A regra é simples: "Se a variável pode ser Null, ELA SERÁ NULL" (com todos os maiúsculos dessa exata forma).

Por que?

Pelo simples fato de que erros de referência de tipo são os grandes causadores de bugs (e exploits exploráveis) em todas as aplicações com algum tipo de erro. E mesmo sendo tão danosos, são problemas relativamente simples de serem tratados. A causa mais comum desses erros é a não atenção de colocar uma lógica adicional que assegure que as variáveis envolvidas no problema tenham seus valores confirmados antes do seu uso no algoritmo.

É fácil pensar em vários casos em que esse tipo de erro pode aparecer. Imagine o seguinte código:



Esse código não verifica se há um ponteiro válido em origem e já inicia a execução do código da função e chama outra função (strlen(3)) em cima do ponteiro. Esse código pode, entre outros erros, causar um Null Pointer Exception ou até erros mais complexos, como corrompimento de memória.

E não é só isso. Algumas funções do sistema podem retornar Null em condições específicas (como o malloc(3) que retorna Null quando não pode alocar mais memória) ou funções de conexão à bancos de dados ou endpoints de APIs usadas pelo sistema. Algumas chamadas de API podem retornar campos não preenchidos em estruturas (ou pior, atualizações de API podem mudar a forma como interagem com estruturas). BOOOOOOMMMMMMM!!!

A boa notícia é que é muito fácil lidar com esse tipo de erro de referência. Null Pointer Exception pode ser evitado com um pouco de lógica adicional para garantir que os objetos não serão Null quando forem usados. Programadores devem estar sempre prontos para assumir que tudo está FUBAR e com isso ser bastante defensivo enquanto estiver escrevendo código. Assuma que toda e qualquer alocação de memória, alocação de arquivo, criação de socket, conexão de banco de dados (e o que mais sua criatividade mandar) irá falhar ou que irá vir com dados que você não está esperando.

Algumas dicas para prevenir Null Pointer Exceptions:
  1. Inicializar toda e qualquer variável que você for usar, dos tipos primitivos aos objetos;
  2. Checar todo e qualquer retorno de valor de chamadas de função, por mais prolixo que isso torne seu código;
  3. Se a variável pode ser Null, faça a checagem e trate o erro da maneira apropriada;
  4. Em linguagens de programação que não possuem tratamento de exceções, faça seus tratamentos com cuidado ainda maior para preservar o fluxo de execução do código (ou dispare um erro e finalize a aplicação)
A contra-dica da primeira dica do começo o post é: "Cheque toda variável antes de usar. Inicie toda variável depois de criar".

Conclusão

Se vocês seguirem essa regra de ouro, eu prometo a vocês que o nível de bugs em seus códigos irá diminuir consideravelmente. Ou não, se estiverem programando em Python, Ruby ou Erlang, mas aí a história é outra completamente diferente.


Extras

Ahh sim, sobre a função do começo ... Eu deixo para vocês escolherem a melhor forma de tornar a função segura contra Null Pointer Exceptions. Postem a resposta no gist.github.com (ou site à sua escolha) e compartilhe aqui no post. Boa brincadeira!