Já mostramos aqui no Tableless ferramentas para testes e ferramentas para garantir o padrão do seu código JavaScript, mas, enquanto esses utilitários asseguram uma consistência maior, eles nem sempre acabam com complexidades desnecessárias.
Neste artigo vamos falar sobre complexidade ciclomática e mostrar uma ferramenta para análise de códigos JavaScript, a biblioteca Plato.
Complexidade Ciclomática
A primeira regra de funções é que elas devem ser pequenas. A segunda regra de funções é que elas devem ser ainda menores.
— Uncle BobFunções devem fazer uma coisa apenas. Fazê-la bem. Fazer somente ela.
— Uncle Bob
Explicando de forma bem direta, complexidade ciclomática é uma métrica do número de caminhos possíveis no seu código. Por exemplo, vejamos o código abaixo:
function authenticate() { if (user.isValid() === true) { user.login(); } else { showMessage('Invalid credentials', 'error'); } }
A função authenticate possui valor 2 de complexidade ciclomática. Na prática, isso quer dizer que precisaríamos escrever dois testes unitários para cobrir todos os possíveis caminhos. Ou seja, quanto mais caminhos, maior a complexidade ciclomática e, quanto maior a complexidade ciclomática, mais difícil será de manter/testar seu código.
Estudos recomendam 10 como o valor máximo que você deve permitir de complexidade ciclomática no seu método ou sua função. Este é um bom valor, mas tenha em mente que 10 já é uma complexidade alta e não deve, de forma alguma, ser a média de complexidade do seu projeto.
Bad Fix
Outra métrica tirada a partir da complexidade ciclomática é a probabilidade de uma correção injetar novos bugs no seu código. O pessoal da Aivosto, uma empresa especializada em ferramentas para desenvolvedores, chegou a seguinte tabela:
Complexidade Ciclomática | Probabilidade de “bad fix” |
---|---|
1-10 | 5% |
20-30 | 20% |
>50 | 40% |
próximo de 100 | 60% |
Segundo a pesquisa da Aivosto, uma correção aplicada em um método com complexidade ciclomática 25 tem 20% de chances de introduzir um novo bug na sua aplicação. Tente lembrar quantas vezes isso já aconteceu com você? E tente lembrar também do tamanho do método ou função que você estava “corrigindo”. Por isso é muito importante tentar medir tudo a respeito do seu código.
Plato
Desenvolvida por Jarrod Overson, a ferramenta Plato aplica na prática todas as teorias de medição de complexidade ciclomática, exibindo na forma de gráficos dados como taxa de mantenabilidade, bugs estimados e erros de lint.
A instalação é feita através do npm, gerenciador de pacotes do nodejs:
npm install -g plato
A forma mais básica de uso é a seguinte:
plato -d report src
Onde -d report é a flag para indicar o diretório report como saída do seu relatório e src é o diretório dos arquivos JavaScript a serem analisados.
Outras opções importantes são as flag -r para ler o diretório recursivamente e -x
Os relatórios do Plato armazenam históricos e é bem interessante ver os números subindo e descendo durante o desenvolvimento do seu projeto. Uma prática legal é guardar e exibir o relatório em algum lugar disponível para todo o seu time.
Exemplos de relatórios
Abaixo temos alguns exemplos de relatórios disponibilizados no repositório do projeto, gerados a partir de bibliotecas e utilitários populares:
Bugs estimados
Um gráfico que chama a atenção nos relatórios do Plato é o de bugs estimados. Afinal de contas, entregar um produto sem bugs é (ou deveria ser) o objetivo final de qualquer desenvolvedor.
Maurice Howard Halstead criou um conjunto de fórmulas para medir coisas como volume, esforço, dificuldade e bugs estimados em um código. As fórmulas são baseadas nos números únicos e totais de operadores e operandos.
Não vou entrar muito em detalhes sobre os valores e as fórmulas, mas é bem interessante ler sobre esse assunto (não precisa ser o livro, a Wikipedia mesmo fornece uma página bem completa sobre as fórmulas).
Integração com Grunt
Overson também desenvolveu um módulo que disponibiliza uma task Grunt para relatórios Plato.
A instalação segue o padrão de pacotes Grunt:
npm install grunt-plato --save-dev
Uma vez instalado o pacote, basta carregar a task no seu Gruntfile.js e rodar a task com o comando grunt plato:
grunt.initConfig({ plato: { your_task: { files: { 'report/output/directory': ['src/**/*.js', 'test/**/*.js'], } }, }, }); grunt.loadNpmTasks('grunt-plato');
Métricas, métricas e mais métricas
Medir o código do seu projeto ajuda você e seu time a entender e prevenir problemas. Com a ajuda de métricas você vai conseguir manter um código fácil de ler e entender. Além de métricas dos níveis de complexidade também é importante possuir um relatório visível de cobertura de testes e uma documentação simples e direta do seu projeto.
Apesar do nome pomposo e de muita teoria, não é pra ninguém ficar assustado. Pode parecer um conceito avançado, mas na verdade é uma coisa muito básica: o que você estará fazendo é medir se é fácil (ou difícil) manter o seu código.
E lembrem-se: nunca refatore um código sem que ele possua uma cobertura de testes satisfatória!