Complexidade Ciclomática: o que refatorar a seguir
Resumo
A complexidade ciclomática mede o número de caminhos independentes numa função. Acima de 10 por função, o risco de bugs aumenta de forma consistente e documentada. Ferramentas como SonarQube, CodeScene e Code Climate calculam e monitorizam esta métrica automaticamente. A regra prática: não refatores tudo ao mesmo tempo. Combina a complexidade com a frequência de modificação e a cobertura de testes para decidir onde agir primeiro. O impacto em produção é o critério decisivo.
Complexidade Ciclomática: como saber o que refatorar (ou construir) a seguir
A complexidade ciclomática mede o número de caminhos independentes numa função ou módulo. Um valor superior a 10 é um sinal claro: mais bugs, mais testes necessários, mais dificuldade de manutenção. Se não sabes por onde começar a refatorar o teu projeto, começa pelos módulos com maior complexidade ciclomática. Não é a métrica perfeita, mas é uma das mais diretamente acionáveis que existem.
TL;DR: A complexidade ciclomática conta os ramos de decisão no código. Acima de 10 por função, o risco de bugs aumenta de forma consistente. Ferramentas como SonarQube, CodeScene e Code Climate identificam automaticamente os pontos críticos. Prioriza pelo impacto em produção, não pelo valor absoluto da métrica.
O que é exatamente a complexidade ciclomática
Thomas McCabe definiu esta métrica em 1976, num artigo publicado nas IEEE Transactions on Software Engineering. A base teórica vem da teoria dos grafos: cada função pode ser representada como um grafo de fluxo de controlo, onde os nós são blocos de código sequencial e as arestas são transições possíveis entre eles.
A fórmula completa é M = E - N + 2P, onde E é o número de arestas, N o número de nós, e P o número de componentes conexos (geralmente 1 para uma função isolada). Na prática, existe um atalho mais direto: conta o número de pontos de decisão na função e adiciona 1.
def processar_pedido(pedido): # M começa em 1
if pedido.urgente: # +1 -> M = 2
notificar_equipa()
for item in pedido.itens: # +1 -> M = 3
if item.stock == 0: # +1 -> M = 4
pedir_reposicao(item)
return pedidoEsta função tem complexidade 4. Gerível. Uma função real de 300 linhas com vários if encadeados, múltiplos try/except, e lógica de negócio acumulada ao longo de anos pode chegar facilmente a 35 ou 50.
Cada if, elif, else if, for, while, case, catch, and, or e operador ternário conta como um ponto de decisão adicional. Em Python, o radon calcula isto automaticamente. Em JavaScript, o ESLint tem uma regra dedicada. Em Java, o Checkstyle e o PMD têm detetores nativos.
Os limiares que realmente importam no dia a dia
Os valores de referência mais usados pela indústria:
1-10: código simples, fácil de testar sem grande esforço.
11-20: moderado; vale a pena monitorar a evolução ao longo dos sprints.
21-50: complexo; cobertura de testes obrigatória antes de qualquer modificação.
Acima de 50: praticamente impossível de testar de forma exaustiva; refatoração prioritária.
O limite de 10 não é uma lei da natureza. É um ponto de equilíbrio empiricamente identificado em múltiplos estudos sobre bases de código industriais. Em projetos legacy com décadas de histórico, uma função de complexidade 20 pode ser o melhor resultado possível após uma refatoração parcial -- e já é um ganho real.
O que importa mais do que o valor absoluto é a tendência: complexidade a aumentar sprint após sprint é um sinal de que a base de código está a degradar-se mais depressa do que está a ser mantida. Um dashboard de tendência temporal vale mais do que um snapshot único.

Por que a complexidade ciclomática está associada a bugs
A relação não é teórica. Estudos sobre bases de código C++, Java e Python em contextos industriais mostram uma correlação consistente: módulos com complexidade acima de 20 apresentam entre 3x e 5x mais defeitos por linha de código do que módulos com complexidade abaixo de 10.
O mecanismo é direto. Mais caminhos de execução significa mais combinações de entradas a cobrir em testes. Cada caminho não coberto é um bug potencial que só se manifesta quando um utilizador ou processo específico aciona esse caminho em produção. Não é uma questão de qualidade do programador -- é física do código.
Em equipas, a complexidade elevada tem um custo adicional: o tempo de onboarding. Um dev que entra num projeto e encontra uma função de complexidade 40 vai demorar duas a três vezes mais a compreender o que acontece do que se a função tivesse complexidade 8. Durante esse período de compreensão incompleta, a probabilidade de introduzir um bug ao modificar a função é significativamente mais alta.
Num estudo sobre o código-fonte do OpenBSD publicado em 2009, módulos com complexidade ciclomática média acima de 15 tinham uma densidade de bugs 4.6x superior à dos módulos com complexidade abaixo de 6. Estes números variam por linguagem e domínio, mas a direção é consistente em projetos de dimensão real.
Como calcular a complexidade ciclomática no teu projeto hoje
Antes de refatorar seja o que for, tens de saber onde estás. Sem medição, a refatoração é baseada em intuição -- que costuma apontar para o código que o dev mais detesta, não para o código que mais beneficiaria de atenção.
Não precisas de fazer nada à mão. As ferramentas calculam automaticamente:
Python: radon cc src/ -a -s retorna a complexidade média por módulo com classificação de A (baixo risco) a F (alto risco). Instala com pip install radon.
JavaScript e TypeScript: configura a regra "complexity": ["error", 10] no teu .eslintrc. Qualquer função que ultrapasse 10 falha o lint automaticamente.
Java e Kotlin: o Checkstyle tem a regra CyclomaticComplexity. O PMD tem a mesma regra na categoria design. Ambas configuram-se via XML e integram com Maven e Gradle.
Go: o gocyclo é a ferramenta padrão. gocyclo -over 10 . lista todas as funções acima do limiar definido.
Para projetos multi-linguagem ou equipas que querem visibilidade agregada num dashboard unificado, o SonarQube é a escolha mais completa. Integra com GitHub, GitLab, Bitbucket e Azure DevOps. O tier Community é gratuito para projetos open-source.
Quando refatorar, quando monitorar e quando ignorar
A complexidade ciclomática elevada não obriga a uma refatoração imediata. A decisão racional passa por três critérios combinados:
Frequência de modificação. Um módulo com complexidade 30 que ninguém toca há dois anos tem risco operacional baixo. Um módulo com complexidade 15 que é modificado toda a semana é mais urgente. O historial de commits é um input crítico nesta decisão.
Cobertura de testes. Alta complexidade com 90% de cobertura é tolerável -- os caminhos estão verificados. Alta complexidade com 20% de cobertura é uma bomba de tempo: qualquer modificação pode quebrar um caminho não testado e o bug só aparece em produção.
Impacto em produção. Módulos no caminho crítico (autenticação, pagamentos, processamento de encomendas) têm prioridade máxima. Scripts de migração que correm uma vez por trimestre podem esperar.
O CodeScene automatiza esta análise combinada. Em vez de mostrar apenas "este módulo é complexo", mostra "este módulo é complexo, muda 3x por semana, e tem 25% de cobertura de testes". Isso é acionável.
O que construir à volta desta métrica
Se trabalhas numa equipa pequena e queres um projeto interno com impacto imediato:
Dashboard de tendência de complexidade. Um script que corre radon ou eslint em CI, persiste os valores numa base de dados simples (SQLite ou Postgres), e renderiza um gráfico de tendência semanal por módulo. O valor prático: ver se a complexidade está a aumentar ou a diminuir ao longo dos sprints. Custo de construção: menos de um fim de semana.
Bot de pull request. Um GitHub Action que comenta automaticamente em PRs que introduzem funções com complexidade acima de 15. Não bloqueia o merge -- informa o autor e os revisores antes da revisão de código. Implementação: cerca de 80 linhas de Python mais um ficheiro de workflow YAML.
Refactoring advisor. Um script que passa o AST de uma função de alta complexidade para a API de um modelo de linguagem e pede sugestões de decomposição. Util para contextos com devs junior a aprender a escrever funções mais simples. Não substitui a revisão humana -- complementa-a nos primeiros anos de carreira.
Nenhum destes projetos requer um produto SaaS. São ferramentas internas com impacto mensurável e escopo limitado -- o tipo de projeto que se constrói, usa durante seis meses, e já valeu a pena independentemente de qualquer outra coisa.

Code Climate como alternativa ao SonarQube
Para equipas que não querem gerir a infraestrutura do SonarQube -- que tem uma curva de setup não trivial, especialmente com autenticação e configuração de regras por linguagem -- o Code Climate Quality é uma alternativa gerida na cloud.
A integração com GitHub faz-se em menos de 10 minutos. Cada função recebe um rating de A a F com base na complexidade ciclomática e noutras métricas de duplicação e estabilidade. O dashboard agrega por repositório e mostra tendências temporais. A integração com PRs pode bloquear merges que pioram o rating global, configurável por threshold.
O limite do tier gratuito: repositórios públicos apenas. Para repos privados, o custo começa em torno de $25-50 por mês para uma equipa pequena. Razoável se substitui horas de análise manual e discussões recorrentes em code review sobre o que priorizar.
O próximo passo prático: escolhe uma ferramenta, corre-a no teu projeto hoje, e identifica as três funções com maior complexidade ciclomática. Depois decide, para cada uma, se a frequência de modificação e a cobertura de testes justificam refatoração imediata ou apenas monitorização. Provavelmente já tens a resposta antes de terminar a análise.