Medindo a complexidade do seu código

Sabemos que um dos grandes vilões na hora de mantermos um sistema legado são os trechos de código difíceis ou complicados de entender. Todo programador já se deparou com (ou mesmo escreveu!) códigos com péssimos nomes de variáveis ou muitas linhas de código em um único método, sem contar o excesso de responsabilidade. Mas, sem dúvida, um dos maiores responsáveis pela dificuldade em entender o que um código faz é o simples if.

Com essa simples instrução, o desenvolvedor faz com que o mesmo método possa responder de duas, três, quatro, N, …, maneiras diferentes, de acordo com certas condições! E pior, o número de caminhos diferentes pode crescer muito rápido!. Considere o trecho de código:

public int contaMaluca(int numero) {
  int resultado = 10;
  if (numero > 10) resultado += numero;
  return resultado;
}

Quantos caminhos diferentes esse método possui? Dois! O primeiro acontece se o if falhar; o segundo acontece se o if for verdadeiro. Agora, olhe o código abaixo, que contém apenas um if a mais:

public int contaMaluca(int numero) {
  int resultado = 10;
  if (numero > 10) resultado += numero;
  if (numero < 30) resultado *= 2;
  return resultado;
}

Repare que temos agora quatro caminhos diferentes! Veja que adicionamos apenas um if a mais, mas a quantidade de caminhos em relação ao código anterior, simplesmente dobrou! Se adicionarmos um if a mais no código acima, parecido com os dois já existentes, veja que o número de caminhos pula pra oito! A figura abaixo exemplifica os possíveis caminhos:

Devemos, quase sempre, manter esse número de caminhos o menor possível. Afinal, quanto menor o número de caminhos, mais fácil é entender o código. Mas o if não é o único vilão! Outras instruções podem aumentar esse “número de complexidade”. Sempre que vemos um for, while, case (do switch), catch, &&, || ou ?, podemos incrementar esse número.

Essa conta é bastante conhecida, é a Complexidade Ciclomática. A ideia é basicamente contar o número de caminhos diferentes que um método pode ter. Entretanto, como calcular o número de caminhos não é tão fácil, um algoritmo muito conhecido é a Complexidade de McCabe, que basicamente conta o número de vezes que um for, while, e todas as instruções mencionadas acima aparecem. Além disso, todo método tem seu contador já inicializado com 1 (afinal, todo método tem pelo menos um caminho). No nosso segundo código, por exemplo, a complexidade de McCabe seria 3.

Não há um número ideal para a complexidade de um método. Mas fato é que uma complexidade ciclomática de 20 ou 30 é demais e esse método deve ser reescrito. Mesmo quebrando-o em dois, é interessante observar a complexidade dos métodos de uma classe em conjunto. Existem diversas ferramentas que calculam esse número para você. Uma das mais populares é o Eclipse Metrics, que é um plugin do Eclipse. Você pode baixá-lo aqui: http://metrics.sourceforge.net/. Ele calcula a complexidade ciclomática de todos os métodos de um projeto!

Faça o teste: instale o plugin e procure pelos métodos cuja complexidade ciclomática seja alta! Compartilhe conosco o maior número que encontrou! E, obviamente, refatore seu código para diminuir esse número.

54 Comentários

  1. Geraldo Ferraz 01/03/2012 at 10:13 #

    Um ponto importante pra ressaltar na frase “Afinal, quanto menor o número de caminhos, mais fácil é entender o código” é que fica bem mais fácil de testar.

  2. Mauricio Aniche 01/03/2012 at 10:48 #

    Oi Geraldo,

    Com certeza! Imagine um método que tenha complexidade ciclomática 20. Para garantir que esse método realmente funcione, você precisa de, *no mínimo*, 20 testes!!

    É trabalho demais!

    Nesses casos, devemos refatorar, criar métodos menores, classes menores, e testar mais fácil!

    Um abraço!

  3. Daniel Wildt 01/03/2012 at 10:57 #

    Legal o post. Eu curto muito complexidade ciclomática. Ela é simples de se medir, e pode ser automatizada como uma tarefa no build usando PMD por exemplo.

    O ponto de tudo isto é poder apoiar o desenvolvimento e garantir melhor cobertura de teste e principalmente apoiar refactorings.

    Ainda no PMD, se pode configurar ele de modo bem malvado, fazendo que o código dentro do Eclipse por exemplo mostre erro de compilação caso a complexidade esteja fora dos padrões que a equipe configurou.

  4. Carlos 01/03/2012 at 10:58 #

    Pra mim isso é igual a falar que o esquema tático x é melhor que o y.

    O melhor é aquele que te da a vitória!

    Mais concordo em parte com a opinião de vocês!

    Mais acho que que a complexidade do código deve variar de acordo com a necessidade de cada um, isso não quer dizer que o código é de difícil entendimento.

    Num mundo real é necessário atender as necessidades do cliente e essas necessidades muitas vezes são complexas.

  5. depp 01/03/2012 at 11:02 #

    cara, as vezes é bem dificil vc criar um método menor… seria interessante um post tipo estudo de caso mostrando um exemplo da vida real.

  6. Mauricio Aniche 01/03/2012 at 11:04 #

    Oi Carlos,

    Concordo. Devemos sempre ir atrás do nosso objetivo concreto, que é entregar software. Mas não podemos esquecer que o produto/serviço que vendemos possui uma característica bem interessante e difícil de lidar: as coisas mudam o tempo todo, e nosso produto vive pra sempre!

    Quantas vezes já participamos de projetos cujo trabalho era reescrever um sistema que já existia antes? Veja que isso não faz sentido nenhum do ponto de vista econômico. É dinheiro jogado fora!

    É muito fácil escrever código ruim. E por isso que gosto de métricas. Elas podem não refletir a realidade 100% do tempo, mas são um bom guia. Sempre que encontro um número muito fora da curva, eu estranho!

    Concordo que o mundo real é complexo. Mas é por isso que estudamos e trabalhamos tanto para achar boas práticas, certo? Não estou argumentando que métodos não podem ter ifs. Mas um método que tem 30 ifs pode, com certeza, ser reescrito! A grande mágica da Orientação a Objetos é justamente nos permitir criar classes coesas e pequenas e depois juntá-las de maneira fácil e super flexível !

    Um abraço!

  7. Mauricio Aniche 01/03/2012 at 11:06 #

    Oi Daniel,

    Ótimo! Dá pra avisar o desenvolvedor na hora que ele bagunçar a métrica! Quanto mais feedback (e mais rápido) melhor!

    Em breve, minha ferramenta de cálculo de métricas sai também! E essa vai ser legal! Aguarde e verá! 😛

    Um abraço!

  8. Mauricio Aniche 01/03/2012 at 11:10 #

    Oi Depp,

    É aí que a mágica da orientação a objetos entra! Falando bem tecnicamente, o uso de polimorfismo nos possibilita a criação de classes menores que trabalham juntas!

    Veja os padrões de projeto, por exemplo! Todos eles, no fim, são isso! Eles separam os compartamentos em classes pequenas e coesas, e no fim, os juntam de alguma forma! Já viu nosso curso de Design Patterns online? Lá damos vários exemplos desses!

    Mas aceito sim o desafio, Depp! Se quiser, me mande um código, nós refatoramos, e blogamos sobre isso! O que acha?

    Um abraço!

  9. Igor Steinmacher 01/03/2012 at 11:10 #

    Muito bom Aniche! Parabéns!
    Li o artigo sem ver que era seu e estou utilizando com meus alunos de qualidade de software.

    Aguardo ansiosamente sua ferramenta

  10. Douglas Rossignolli (@xdougx) 01/03/2012 at 11:11 #

    Realmente você ter todos esses caminhos a percorrer como mostra na figura é uma grande dor de cabeça para fazer testes, mas eu acredito que é como ensinado quando você esta la na faculdade ou como escutamos é “Dividir para conquistar” 😀

  11. Carlos 01/03/2012 at 11:21 #

    Oi Mauricio não quis dizer que o objetivo é entregar o software..

    Pelo contrário acho que um programador novato nem consegue desenvolver um código complexo, ele irá desenvolver um código com falhas e erros de lógica, não é culpa dele é falte de experiência.

    Quis dizer é que num mundo real esse tipo de exemplo não é visto e os métodos são muito mais complexos e tem várias condições e dependências.

    Oque quis fizer é que só porque o código é complexo não quer dizer que ele foi mal feito, ou terá baixo desempenho em teste de software.

    Outra coisa o programador que só pensa em entregar o projeto, não é programador, vai fazer outra coisa..

  12. Diego 01/03/2012 at 11:28 #

    Eu era uma das piores pessoas para se escrever códigos, sempre pensava na maior quantidade de erros possíveis.

    E nem sempre essa é a solução, um código com uma lógica mais inteligente te elimina boa parte dos problemas de código.

    O resto é da cabeça do programador.

  13. depp 01/03/2012 at 11:35 #

    Qual vc considera a metrica ideal? fiz um teste aqui em um metodo que eu achava que tava com bastante if e ele mediu 7 de complexidade ciclomatica.

  14. Helder da Rocha 01/03/2012 at 12:19 #

    Uma ferramenta que sempre procuro usar para rapidamente coletar métricas de código (principalmente quando me deparo com aplicações pela primeira vez) é o Sonar (www.sonarsource.org). Ele coleta bastante informação sobre o código, inclusive medidas de complexidade, e organiza os dados coletados de PMD, CheckStyle, FindBugs. Usando com o Hudson, ele permite o acompanhamento da qualidade do projeto ao longo de sua vida. Se for um projeto Maven, é muito fácil usar porque há um plug-in nativo. Se não for, é possível ainda obter métricas simplesmente criando um POM para o projeto indicando onde estão as fontes.

    Métricas são muito importantes pois fornecem feedback detalhado sobre o código que podem guiar refatoramentos e chamar a atenção sobre pontos que podem se tornar críticos. Nem sempre as otimizações devem ser feitas. Tudo depende de quão crítico é o código em questão, do custo/complexidade da otimização e de outros fatores. Mas qualquer solução que ajude a deixar o código mais fácil de entender é sempre boa.

  15. Mauricio Aniche 01/03/2012 at 12:20 #

    Oi Igor,

    Obrigado!!

  16. Pablo 01/03/2012 at 12:20 #

    Informação excepcional, utilidade inestimável para quem quer melhorar seu código.

  17. Mauricio Aniche 01/03/2012 at 12:21 #

    Oi Carlos,

    Concordo! Mas acho que esse é exatamente nosso desafio. Como escrever código de maneira simples?

    Com certeza você terá trechos de código no seu sistema mais complexos que outros. Mas prefiro acreditar que eles serão a exceção, e não a regra.

    Um abraço!

  18. Mauricio Aniche 01/03/2012 at 12:22 #

    Oi Diego e Douglas,

    Obrigado!

  19. Mauricio Aniche 01/03/2012 at 12:25 #

    Oi Depp,

    7 quer dizer que esse seu método tem no mínimo 7 caminhos diferentes, certo? Em minha opinião, CC=7 não é um problema, mas já é algo para monitorar, prestar atenção. Eu quebraria esse método grande em pequenos métodos privados, ou até mesmo em classes separadas.

    O prof. Harald Gall, de uma universidade suiça, fez um trabalho uma vez sobre métricas que ele considera de referência. Na opinião dele, quando sua complexidade ciclomática, por linha de código, for maior que 0.24, ele considerado alto. Ou seja, pega a complexidade ciclomática, divida pelo número de linhas, e veja o valor.

    Aqui a tabela dele:
    Baixo = 0.16
    Na Média: 0.20
    Alta: 0.24

    Um abraço!

  20. Mauricio Aniche 01/03/2012 at 12:27 #

    Oi Helder,

    Concordo. Ferramentas como Hudson, Sonar, e etc, tiram nosso trabalho de calcular a métrica! Recomendadíssimo!

    Um abraço!

  21. Mauricio Aniche 01/03/2012 at 12:27 #

    Oi Pablo!

    Obrigado!

  22. Elizeu Santos 01/03/2012 at 13:59 #

    perfeito o artigo, recomendo!

  23. Guilherme 01/03/2012 at 14:16 #

    Olá Maurício,

    Qual foi a ferramenta gráfica que você utilizou para fazer os caminhos do código ?

    abs.

  24. Mauricio Aniche 01/03/2012 at 14:20 #

    Oi Guilherme,

    Xiii, você vai se decepcionar! Eu usei o Google Docs para desenhar. Fiz na mão mesmo! 😉

    Não sei se existe alguma ferramenta que desenha o grafo de caminhos possíveis. O que pode ajudar é usar ferramentas como Jude ou ArgoUML e, a partir do código-fonte Java, criar diagramas de sequência. Isso deve dar uma noção da complexidade do método!

    Um abraço!

  25. Guilherme 01/03/2012 at 14:30 #

    OK, tudo bem 🙂 hehe obrigado!

    Relacionado ao post:
    Uma recomendação para os rubistas de plantão: http://ruby.sadi.st/Flog.html

    Esta ferramenta analisa a complexidade usando a métrica abc. bom, não deixa de ser útil e relacionado ao tema 🙂

    abs.

  26. Márcio Silva 01/03/2012 at 14:31 #

    Bom artigo e tb bons comentários.
    1. Meça mesmo – ninguém é obrigado a levar em conta os achismos de ninguém.
    2. Comece simples e se esforce para manter a simplicidade pois a complexidade tende a aumentar sempre.
    3. Código complexo = mágica. Lugar de fazer mágica não é em componente de negócio. Em componentes de frameworks é mais aceitável. Bom design, refactoring são algumas armas.

  27. Luis Eugênio 01/03/2012 at 17:04 #

    Muito bom Aniche! Bem interessante o post. Um link com mais informações em português (baseado, muito provavelmente no link que você passou do linux journal) e também um exemplo em ruby http://goo.gl/RFRzf.

  28. Mauricio Aniche 01/03/2012 at 18:40 #

    Oi Guilherme,

    O Flog é bem legal também! Eu só não sei bem como ele incrementa o contador, afinal, em Ruby você tem muito mais coisas na linguagem! 🙂

    Oi Márcio,

    Concordo em absoluto!!

    Oi Luis Eugênio,

    Ótimo link! Galera, não deixem de ler!

    Abraços!

  29. Gilmar P.S.L. 06/03/2012 at 14:07 #

    Maurício, parabéns pelo artigo, muito bom.

    Guilherme, já usei essa ferramenta para estudo de caso para métricas.

    http://www.codeswat.com/cswat/index.php?option=com_content&task=view&id=15&Itemid=30

    Ela é gera flowcharts do smétodos:

    Exemplo
    http://www.codeswat.com/cswat/s4j/target2.html

    Porém, a não ser que seja um método de outro mundo, tipo: 5 mil linhas de código.., vc não vai usar muito.

    Prefiro usar sempre as métricas pelo sonar como parte da integração contínua. E avaliá-las periodicamente durante a semana.

  30. Raphael 13/03/2012 at 17:05 #

    Então, foi daí que vc tirou sua tese à respeito de número de asserts nos testes?

  31. Mauricio Aniche 13/03/2012 at 18:04 #

    Oi Rapha,

    Não! Mas usei complexidade ciclomática como uma das métricas para validar a minha hipótese!

    Te mando o artigo se quiser! Devo blogar sobre isso em breve também!

    Abraços!

  32. Alexandre Aquiles 14/03/2012 at 23:16 #

    Excelente artigo, Aniche!

    E vale a dica do Sonar, dada pelo Helder. A ferramenta é excelente.

    ____

    No meu TCC de uma Pós que fiz em 2011, eu tentei estudar a relação entre métricas e refatorações.

    Aprofundei bem menos do que queria mas foi uma pesquisa interessante.

    A idéia inicial era ajudar iniciantes a identificar oportunidades de refatoração no código e depender um pouco menos de um olhar de especialista em design.

    Mas o que acabei percebendo é que as métricas não tem uma correlação direta com as refatorações. O olhar tático de um especialista em design é muito importante.

    Uma referência interessante do assunto é o trabalho de Carneiro:

    CARNEIRO, G. F. Usando Medição de Código Fonte Para Refactoring. 2003. 129 f.
    Dissertação (Mestrado em Redes de Computadores) – Universidade Salvador,
    Salvador, 2003.

    Quem quiser olhar meu TCC, coloquei em:
    http://alexandreaquiles.wordpress.com/2012/03/14/identificando-oportunidades-de-refatoracao-atraves-de-metricas/

  33. Mauricio Aniche 14/03/2012 at 23:51 #

    Vou ler sim, Alexandre!!

  34. Phenders 23/03/2012 at 12:24 #

    Bom Podemos dar a seguinte solução

    public int contaMaluca(int numero) {

    int resultado = 10;

    if (numero > 10 && > 30)
    resultado += numero;
    else
    resultado *= 2;

    return resultado;
    }

  35. Mauricio Aniche 23/03/2012 at 12:27 #

    Oi Phenders,

    A complexidade ciclomática seria a mesma! Afinal o “&&” é contabilizado pelo algoritmo!

    Faz sentido?

    Um abraço!

  36. Marcio 30/05/2012 at 14:56 #

    Plugin meio antigo heim… Não tem nenhum equivalente q funcione nas novas versões do eclipse?

  37. Mauricio Aniche 05/06/2012 at 18:11 #

    Oi Márcio,

    Realmente os plugins existentes não são lá as melhores coisas do mundo. Mas ele roda sim na última versão do Eclipse. Você teve problemas?

    Outros que recomendo: JDepend, Java NCSS, Byecycle.

  38. Tatiana 12/07/2012 at 00:28 #

    Olá Mauricio,

    Estava buscando na net artigos sobre complexidade ciclomática e me deparei com o seu. Achei bem interessante, porém perdi algum tempo tentando fazer o fluxo desse seu código e nunca achava oito caminhos independentes. Utilizando o cálculo por número de regiões ou pela famosa fórmula V(G)= Aresta – Nós +2, sempre achava 3 como resultado. Relendo os conceitos, percebi uma frase na literatura do presmam que dizia o seguinte: “Um caminho independente é qualquer caminho ao longo do programa que introduz pelo menos um novo conjunto de comandos de processamento ou uma nova condição. Quando enunciado em termos de grafo de fluxo, um caminho independente deve incluir pelo menos uma nova aresta que não tenha sido atravessada antes do caminho ser definido.” No seu exemplo temos todos os caminhos possíveis, porém nem todos são independentes, pois são combinação de caminhos já percorridos, por isso tantos caminhos diferentes. Resolvi escrever, pois preciso dar uma aula sobre isso e realmente fiquei encucada…rs. Vc concorda com o meu raciocínio ou eu deixei passar alguma coisa??
    Parabens por compartilhar o conhecimento.

  39. Mauricio Aniche 12/07/2012 at 14:14 #

    Oi Tatiana,

    No texto, eu comento que serão 8 caminhos se você adicionar mais um if naquele meu método de exemplo. Se tenho 3 ifs em seguida, então tenho 8 possibilidades diferentes, concorda?

    Existem muitas maneiras diferentes de se calcular CC. Uma delas (e bem complicada) é traçar o grafo e realmente entender quais os possíveis caminhos que seu código pode ter (if, else ifs, ifs aninhados e etc, podem fazer esse número mudar bastante).

    Mas geralmente calcular esse número exato é muito difícil e caro. McCabe, por exemplo, sugere que você apenas conte a quantidade de ifs, fors, switchs e etc. Não é perfeito, mas já é uma boa métrica! 🙂

    No fim, acho que a ideia é ter uma maneira (mesmo que não totalmente precisa) de medir complexidade e descobrir trechos de código fora da curva.

    O que acha?

  40. Tatiana 12/07/2012 at 23:04 #

    Olá Maurício,

    Realmente para quem tá metendo a mão na massa, ou seja, tendo que entender complexidades, preparar testes para programas, toda informação de tendência é bem vinda! Realmente quis me certificar, pois não sou especialista nessa área, e precisarei lecionar sobre o assunto . Como isso costuma cair em concursos fiquei preocupada de não passar nada de errado para os alunos. Vlw pela atenção e a partir de agora vou seguindo seu blog!

  41. Frederico Engels Alencar Ferreira Lima Filho 16/07/2012 at 16:40 #

    Maurício, parabéns pelo artigo.

    Estou com uma dúvida quanto ao cálculo da complexidade a partir de um grafo de fluxo de controle.

    Existe uma certa confusão na literatura a respeito da fórmula utilizada, alguns utilizam V(G) = Arestas – Nós + p , outros utilizam V(G) = arestas – nós + 2p… Qual a forma mais aceita de ser medir a complexidade ciclomática?

    Segue um trecho retirado de um comentário a respeito de uma questão do ENADE de 2008, do material: “ENADE Comentado da PUCRS”

    “O método dos caminhos básicos de McCabe coloca ainda que, como os grafos de programa não são fortemente conexos, é necessário acrescentar uma aresta conectando o último nodo ao primeiro de maneira a obter esta característica. Por esta razão a questão coloca que o número de caminhos básicos a serem testados é igual à complexidade ciclomática mais um”

    Por esse motivo alguns utilizam a fórmula: V(G) = Arestas – Nós + 2p , para acrescentar esse arco extra e tornar o grafo fortemente conexo.

  42. João Talles 27/12/2012 at 09:24 #

    Curso, simples, objetivo e muito esclarecedor.
    Parabéns pelo post Maurício.

  43. Diego 01/06/2013 at 10:02 #

    Excelente post e ótimos comentários!!! Concordo com o que colega que disse “meça mesmo, ninguém é obrigado a entender os nossos achismos”. Temos que pensar sempre na manutenção do produto que pode ou não ser realizada pelo próprio desenvolvedor. Se não me engano, foi Pressman quem disse que se gasta 60% de $$ desenvolvendo e 40% testando. Imagina realizar testes em um código absurdamente complexo. Esse % irá ultrapassar o custo!

  44. Arthur Ronald 18/02/2014 at 13:13 #

    Olá Maurício,

    Parabéns pela iniciativa de expor o conceito de complexidade ciclomática. Agora, um pouco fora do escopo porém de certa forma relacionado, como seria uma forma de reduzir if’s aninhados. Já vi o padrão Guard Clause. No entanto, ao ver o Google Guava Predicates.and(Predicate… p), lembra muito o padrão de projeto Chain of responsibility. Embora possa gerar controversa, principalmente devido aos conceitos definidos pelo GoF, acredito que seja adequado ao caso, vc não acha ? Predicates.and(GreaterThan(10), LessThan(30)). Isolados além de facilitar a implementação de testes.

    Se vc puder, envie sua opinião por email pois pode ser que eu me esqueça de revisitar seu artigo. Gostaria de parabenizar pelo livro de sua auroria: TDD com Java. Simples e objetivo além prover insight’s que colaboram para a vida do desenvolvedor.

  45. Mauricio Aniche 26/02/2014 at 13:27 #

    Oi Arthur,

    É uma discussão interessante. Veja que se você fizer alguma expressão lambda meio doida, talvez McCabe não mude. Mas você sabe que o código ficou sim mais complexo.

    Não há uma resposta mágica. Você precisa fazer caso a caso. Um Chain of Responsibility talvez seja mais fácil de ser testado e possibilite uma maior flexibilidade. Mas a lambda também tem menos código e é rápido de ser escrito.

    Voltando ao assunto das métricas, é por isso que temos várias delas.

    Um abraço!

  46. Leonardo 12/03/2014 at 15:00 #

    Onde eu configuro o número do grau de complexidade no Sonar?

  47. pedro cunha 01/04/2014 at 09:14 #

    Quanto menor o codigo mais facil sera a capacidade de entender e de testar

Deixe uma resposta