Atenção à Documentação

por Fabio A. Mazzarino

Já trabalhei em equipes que eram extremamente detalhistas, mas também trabalhei em equipes que praticamente não tinham documentação, nem mesmo funcional, ou mesmo manuais. Mas a falta de atenção a documentação pode causar problemas de qualidade e estabilidade em um sistema.

Documentação de Bibliotecas de Terceiros

Certa vez encontrei um sistema que precisava ser reiniciado a cada 24 horas, como envolvia bibliotecas de terceiros o time explicava a situação por vazamento de dados da biblioteca. Mas avaliando o código descobri uma situação devido a falta de atenção à documentação existente.

Error error = getClientList();
if (!error.code) {
    std::cerr << "Cannot get client list. ";
    std::cerr << error.shortmsg << " - " << error.longmsg << std::endl;
    return false;
}

Para quem não consultou a documentação parece estar tudo bem. Porém ao avaliar a documentação da estrutura Error encontramos aproximadamente o seguinte texto:

struct {
    int code;
    char* shortmsg;
    char* longmsg;
} Error;
. code contains the error code, 0 if no error
. shortmsg contains short message for error, NULL if no error
. longmsg contains long message for error, NULL if no error
. ATTENTION: calling function must deallocate shortmsg and longmsg using delete[]

Não estou usando exatamente o mesmo código, nem a mesma documentação por conta de questões de confidencialidade.

Na documentação fica explícito que é necessário desalocar a memória das mensagens de erro. Como a biblioteca não utiliza STL, a opção para as mensagens de erro foi alocar um array de caracteres e solicitar que o trecho de código que receber o erro desaloque os arrays.

Como não houve preocupação com a documentação da biblioteca, a cada erro gerado havia um vazamento de memória, o que poderia não ser a principal causa de instabilidade, mas era, com certeza, um dos fatores.

Documentação Interna

Em outra equipe havia detalhados manuais de usuários, que eram utilizados pelos desenvolvedores, e para criar os casos de teste. Apesar da documentação de funcionalidades do sistema ser muito completa, faltava documentação do próprio código.

Consequentemente, cada novo programador que entrava na equipe tinha uma curva de aprendizado muito suave e demorava um tempo até dominar a arquitetura do software. E por conta disso era muito comum um desenvolvimento de um novo programador criar uma instabilidade no sistema.

Adicionalmente a duplicidade das estruturas de dados adicionava uma complexidade ao sistema que piorava ainda mais a dificuldade em entender a arquitetura de software, dificultava a manutenção, e aumentava os custos de manutenção.

Uma documentação apropriada da arquitetura do desenvolvimento traria diversas facilidades, tanto para novos desenvolvedores, como para os mais experientes. Reduzindo o tempo e custo de manutenção, e consequentemente trazendo redução nos custos, possivelmente até reduzindo o tamanho da equipe.

Funções que Alocam Memória

Já discutimos no Lab C++ a importância do cuidado com a alocação de memória, e que a melhor prática indica que é necessário evitar a alocação de memória para outras funções, salvo em condições específicas, ou quando estritamente necessário.

Certa vez encontrei um código com uma funcionalidade aproximadamente assim:.

char* buildNodeName(int nodetype, struct node childnode, struct node parentnode) {
    char *nodename = NULL;
    switch (nodetype) {
    case NODE_TYPE1:
        namesize = strlen(node.code) + 
            strlen(node.location) + 
            strlen(node.city) + 1;
        nodename = malloc(namesize);
        strcpy(nodename, node.code);
        strcat(nodename, node.location);
        strcat(nodename, node.city);
        return nodename;
 
    case NODE_TYPE2:
        // ...
        return nodename;
    // ...
    }
    nodename = (char*)malloc(1);
    *nodename = '\x0';
    return nodename;
}

Note que a função já não segue as melhores práticas, pois poderia receber um buffer com tamanho suficiente para ser preenchido. Ainda assim, supondo-se ser necessário, já que a regra para construção está na função, a documentação deveria deixar bem claro que é necessário desalocar a memória retornada.

A função em questão era uma função muito importante e recorrente, chamada cada vez em que um Node fosse criado ou alterado. E no código todo nenhuma função que a chamava se preocupava em desalocar a memória, e fazia ainda pior, eram comuns funções que repassavam a memória alocada adiante.

Consequentemente esse trecho de código causava um grande vazamento de memória que contribuía para a instabilidade do sistema.

Conclusão

Eu conheço pouquíssimos programadores que se sentem a vontade fazendo documentação de código e arquitetura, mas é um “mal” necessário. Por mais que seja chato, consuma tempo, atrase sua entrega, não traga benefícios imediatos, etc, é necessário, irá poupar tempo no futuro.

E se sua equipe falha na documentação procure demonstrar o quanto a falta de documentação está causando instabilidades, dificuldades na manutenção, aumento de custos, etc. Com certeza a liderança irá considerar a necessidade de dar mais atenção para essa questão.

Neste caso, por mais que cada equipe tenha sua cultura, as melhores práticas indicam que a documentação é estritamente necessária.