Toda função que você escreve tem uma pontuação de complexidade. Se a função não contém pontos de decisão, não há pontuação de complexidade. ifnão else, sem loop, não switch, sua complexidade ciclomática é 1: exatamente um caminho através do código. Adicione um if Adicione uma declaração e ela se torna 2. Adicione outra e ela se torna 3. Quando uma função acumula uma dúzia de ramificações condicionais em vários níveis aninhados, sua complexidade ciclomática pode ser 15 ou 20, e testá-la completamente requer um número correspondente de casos de teste, cada um cobrindo um caminho de execução distinto.
A complexidade ciclomática (CC) foi introduzida por Thomas J. McCabe em 1976 como uma medida quantitativa da complexidade lógica do fluxo de controle de um programa. Ela permanece uma das métricas de qualidade de código mais úteis na prática, pois suas implicações são concretas e acionáveis: uma pontuação de complexidade indica o número mínimo de casos de teste necessários para a cobertura completa do caminho, prevê a dificuldade de compreensão e modificação do código e identifica as funções com maior probabilidade de conter defeitos não descobertos. Este guia aborda a fórmula, os limites, exemplos específicos de cada linguagem, técnicas de refatoração que realmente reduzem a complexidade e como medi-la e monitorá-la automaticamente.
SMART TS XL
Ajuda você a dominar a complexidade ciclomática, otimizar o desempenho e evitar bugs ocultos
SAIBA MAIS…O que é complexidade ciclomática?
A complexidade ciclomática mede o número de caminhos linearmente independentes através do código-fonte de um programa. Thomas J. McCabe a derivou da teoria dos grafos: todo programa pode ser representado como um grafo de fluxo de controle, onde os nós são instruções e as arestas são os fluxos possíveis entre elas. A fórmula é:
CC = E - N + 2P
Onde:
- E = número de arestas no grafo de fluxo de controle
- N = número de nós
- P = número de componentes conectados (geralmente 1 para uma única função)
Para cálculos práticos, existe um equivalente mais simples: CC = número de pontos de decisão + 1. Cada if, else if, while, for, case, catch, && e || Adiciona um ponto de decisão. O CC inicial de qualquer função é 1.
Java
// CC = 1: no decision points
public String greet(String name) {
return "Hello, " + name;
}
// CC = 3: two decision points (two if statements)
public String classify(int score) {
if (score >= 90) return "Excellent";
if (score >= 70) return "Satisfactory";
return "Needs improvement";
}
// CC = 5: four decision points (three conditions + one loop)
public double calculateTotal(List<Item> items, boolean isMember, boolean isHoliday) {
double total = 0;
for (Item item : items) { // +1
total += item.getPrice();
}
if (isMember) total *= 0.9; // +1
if (isHoliday) total *= 0.95; // +1
if (total > 100) total -= 5; // +1
return total;
}
Limiares: Que pontuação é aceitável?
A orientação original de McCabe, que continua sendo a mais citada, define quatro níveis de risco:
| Pontuação CC | Nível de risco | Interpretação |
|---|---|---|
| 1 - 10 | Baixo | Simples, bem estruturado, fácil de testar |
| 11 - 20 | Moderado | Mais complexo; requer maior esforço de teste. |
| 21 - 50 | Alto | Complexo e difícil de testar; recomenda-se refatoração. |
| > 50 | Muito alto | Impossível de testar na prática; risco grave de qualidade. |
O limite de 10 é o mais comumente imposto em verificações de qualidade de CI/CD. O limite padrão de complexidade cognitiva do SonarQube é 15 (uma métrica relacionada, mas distinta). As diretrizes do NIST para sistemas críticos de segurança recomendam um máximo de 10 por módulo.
Uma nuance importante: o CC mede a complexidade estrutural, não a complexidade semântica. Uma função com CC = 8 que implementa um cálculo financeiro complexo pode ser mais difícil de entender do que uma função com CC = 15 que consiste em verificações defensivas simples. Use o CC como um sinal para investigação, não como um veredicto final.
Complexidade ciclomática em diferentes idiomas
Python
Construções de decisão do Python que contribuem para a CC: if, elif, else (não contabilizado, não possui condição), for, while, try/except (cada except contagens), with (não conta) e operadores booleanos and/or em condições.
python
# CC = 1
def format_name(first: str, last: str) -> str:
return f"{first} {last}"
# CC = 4: three decision points
def calculate_discount(price: float, is_member: bool, is_holiday: bool) -> float:
discount = 0.0
if is_member: # +1
discount += 0.10
if is_holiday: # +1
discount += 0.05
if price > 100: # +1
discount += 0.02
return price * (1 - discount)
# CC = 6: five decision points (list comprehension counts as a loop)
def process_orders(orders: list[dict]) -> list[dict]:
return [
{**order, "total": order["qty"] * order["price"]} # +1 (comprehension)
for order in orders
if order["qty"] > 0 # +1 (filter condition)
if order["price"] > 0 # +1 (second filter)
]
Ferramentas para medição de CC em Python: radon (radon cc src/ -s), flake8-cognitive-complexity, pylint Com o plugin de complexidade, análise SonarQube em Python.
Java
Java
// CC = 7: complex authentication with multiple conditions
public AuthResult authenticate(String userId, String password, boolean isMfa) {
if (userId == null || password == null) return AuthResult.INVALID; // +2 (||)
User user = userRepository.findById(userId);
if (user == null) return AuthResult.NOT_FOUND; // +1
if (!user.checkPassword(password)) return AuthResult.WRONG_PASSWORD;// +1
if (isMfa && !user.hasMfaEnabled()) return AuthResult.MFA_REQUIRED; // +2 (&&)
return AuthResult.SUCCESS;
}
// CC = 1 + 2 + 1 + 1 + 2 = 7
Reduzindo-o:
Java
// After refactoring: CC = 3 (main method) + small helpers with CC = 2 each
public AuthResult authenticate(String userId, String password, boolean isMfa) {
if (hasInvalidInputs(userId, password)) return AuthResult.INVALID;
User user = findVerifiedUser(userId, password);
if (user == null) return AuthResult.WRONG_PASSWORD;
if (requiresMfa(user, isMfa)) return AuthResult.MFA_REQUIRED;
return AuthResult.SUCCESS;
}
private boolean hasInvalidInputs(String userId, String password) {
return userId == null || password == null; // CC = 2
}
private boolean requiresMfa(User user, boolean isMfa) {
return isMfa && !user.hasMfaEnabled(); // CC = 2
}
Ferramentas para Java CC: Checkstyle, PMD, SonarQube, IntelliJ IDEA integrados, SMART TS XL.
C# e TypeScript
C# e TypeScript seguem as mesmas regras do Java. A principal adição: as cláusulas de expressão LINQ em C# e as cadeias ternárias em TypeScript adicionam pontos de decisão.
csharp
// CC = 5: switch with four cases
public decimal GetShippingCost(string zone) => zone switch {
"domestic" => 5.99m, // +1
"eu" => 15.99m, // +1
"international" => 29.99m, // +1
"express" => 49.99m, // +1
_ => throw new ArgumentException($"Unknown zone: {zone}")
};
COBOL
Construções de decisão do COBOL que contribuem para a computação em cadeia: IF/ELSE, EVALUATE WHEN (cada cláusula WHEN), PERFORM UNTIL, PERFORM VARYING ... WITH TEST BEFORE/AFTER, AT END, ON EXCEPTION, NOT ON EXCEPTION, ON SIZE ERROR.
cobol
CALCULATE-DISCOUNT.
IF WS-CUSTOMER-TYPE = 'GOLD' *> +1
IF WS-PURCHASE-AMT > 1000 *> +1
COMPUTE WS-DISCOUNT = 0.20
ELSE *> (no increment)
COMPUTE WS-DISCOUNT = 0.15
ELSE IF WS-CUSTOMER-TYPE = 'SILVER' *> +1
COMPUTE WS-DISCOUNT = 0.10
ELSE *> (no increment)
COMPUTE WS-DISCOUNT = 0.05
END-IF
EVALUATE TRUE
WHEN WS-REGION = 'NORTH' PERFORM APPLY-REGIONAL-RATE *> +1
WHEN WS-REGION = 'SOUTH' PERFORM APPLY-SOUTHERN-RATE *> +1
END-EVALUATE.
*> Total CC = 1 + 5 = 6
A sintaxe verbosa do COBOL significa que os parágrafos são tipicamente mais longos do que funções equivalentes em linguagens modernas. Programas COBOL com CC acima de 50 por parágrafo são comuns em bases de código legadas e representam os alvos de maior prioridade tanto para refatoração quanto para planejamento de modernização.
Como calcular a complexidade ciclomática: três métodos
Método 1: Contar pontos de decisão + 1 O método manual mais rápido. Conte cada if, else if, while, for, case, catch, &&, || na função. Adicione 1 para a própria função.
Método 2: Gráfico de fluxo de controle Desenhe a função como um gráfico: um nó por instrução ou bloco, arestas para cada fluxo de controle. Aplique. CC = E - N + 2.
Método 3: Ferramenta automatizada. O único método prático para qualquer coisa além de uma função trivial. A maioria das ferramentas de análise estática calcula o CC automaticamente e o integra aos pipelines de CI/CD.
Técnicas de refatoração que realmente reduzem a complexidade
Cláusulas de proteção (devoluções antecipadas)
As cláusulas de guarda encerram a função antecipadamente quando as pré-condições falham, eliminando ramificações `else` e reduzindo a profundidade de aninhamento.
python
# Before: deeply nested, CC = 5
def process_order(order):
if order is not None:
if order.is_valid():
if order.has_stock():
if order.payment_cleared():
return fulfill_order(order)
else:
return "Payment failed"
else:
return "Out of stock"
else:
return "Invalid order"
else:
return "No order"
# After: flat, CC = 5 (same complexity, dramatically better readability)
def process_order(order):
if order is None: return "No order"
if not order.is_valid(): return "Invalid order"
if not order.has_stock(): return "Out of stock"
if not order.payment_cleared(): return "Payment failed"
return fulfill_order(order)
O custo computacional (CC) não diminui, os mesmos pontos de decisão permanecem, mas o código se torna muito mais fácil de ler e testar. A verdadeira redução do CC exige a eliminação de pontos de decisão, não apenas o seu rearranjo.
Métodos de Extração
Mover grupos lógicos de decisões para métodos nomeados reduz o custo computacional da função que a chama, ao mesmo tempo que distribui a complexidade por unidades menores e testáveis.
Java
// Before: one method doing everything, CC = 9
public double calculateInvoiceTotal(Invoice invoice, Customer customer) {
double subtotal = 0;
for (LineItem item : invoice.getItems()) {
subtotal += item.getQuantity() * item.getUnitPrice();
if (item.isTaxable()) subtotal += item.getPrice() * 0.1;
}
if (customer.isMember()) subtotal *= 0.9;
if (customer.hasVoucher()) subtotal -= customer.getVoucherValue();
if (subtotal < 0) subtotal = 0;
return subtotal;
}
// After: main method CC = 4, helpers have CC = 2-3 each
public double calculateInvoiceTotal(Invoice invoice, Customer customer) {
double subtotal = computeLineItemTotal(invoice.getItems());
subtotal = applyCustomerDiscounts(subtotal, customer);
return Math.max(0, subtotal);
}
Substituindo condicionais por polimorfismo
Quando uma função se ramifica com base no tipo ou no estado, o polimorfismo elimina completamente a ramificação.
Java
// Before: switch on payment type, CC grows with each new type
public void processPayment(String type, double amount) {
switch (type) {
case "CREDIT": processCreditCard(amount); break;
case "PAYPAL": processPayPal(amount); break;
case "CRYPTO": processCrypto(amount); break;
default: throw new IllegalArgumentException("Unknown type: " + type);
}
}
// After: new payment types require no changes to this method, CC = 1
public interface PaymentProcessor {
void process(double amount);
}
public void processPayment(PaymentProcessor processor, double amount) {
processor.process(amount); // no branching
}
Decompondo Condicionais Complexas
Extrai expressões booleanas complexas e as transforma em métodos nomeados que revelam sua intenção.
python
# Before: dense boolean logic, hard to understand, easy to mis-test
if user.age >= 18 and user.country in ALLOWED_COUNTRIES and not user.is_banned and user.verified:
grant_access()
# After: named predicate, self-documenting, unit-testable independently
def is_eligible_for_access(user: User) -> bool:
return (
user.age >= 18
and user.country in ALLOWED_COUNTRIES
and not user.is_banned
and user.verified
)
if is_eligible_for_access(user):
grant_access()
Complexidade ciclomática em pipelines de CI/CD
A aplicação automática de critérios de correção de erros (CC) em pipelines de CI/CD impede que a complexidade se acumule de forma invisível entre as revisões de código.
yaml
# GitHub Actions: fail PR if any function exceeds CC threshold
name: Code Quality
on: [pull_request]
jobs:
complexity-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install radon (Python CC tool)
run: pip install radon
- name: Check cyclomatic complexity
run: |
radon cc src/ --min C --show-complexity
# Fails if any function has CC grade C (11-15) or worse
radon cc src/ --min C --total-average | grep -q "Average complexity" \
&& echo "Complexity check passed" \
|| (echo "Functions with high complexity found" && exit 1)
Para Java com SonarQube:
yaml
# SonarQube quality gate blocks merge if CC exceeds threshold
- name: SonarCloud Scan
uses: SonarSource/sonarcloud-github-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
with:
args: |
-Dsonar.qualitygate.wait=true
-Dsonar.java.complexity.Function.threshold=10
O controle de qualidade deve bloquear novos códigos de alta complexidade, e não toda a base de código legada, que pode já apresentar altos níveis de complexidade crítica que estão sendo tratados separadamente.
Complexidade ciclomática em COBOL e bases de código legadas
Para sistemas empresariais onde os programas COBOL podem ter parágrafos com complexidade ciclomática superior a 50 ou mesmo 100, a análise de complexidade ciclomática tem um propósito principal diferente do que em bases de código modernas. A questão não é "devemos refatorar esta função?", mas sim "qual é o risco de migração deste programa e em que ordem devemos modernizá-lo?".
Um programa COBOL com CC = 80 em seu parágrafo principal possui 80 caminhos de execução independentes, cada um dos quais requer um caso de teste para validação. Se o programa não possui casos de teste, o que ocorre na maioria dos programas COBOL legados, a pontuação CC é o principal indicador de quantos cenários de validação devem ser criados antes que qualquer trabalho de conversão possa ser considerado seguro.
SMART TS XL'S análise de código estático Calcula a complexidade ciclomática para COBOL, JCL, RPG, PL/I e todas as linguagens modernas simultaneamente, produzindo a distribuição de CC em nível de portfólio que torna as decisões de sequenciamento da modernização baseadas em evidências. Os programas com as maiores pontuações de CC e o maior número de chamadas (alto fan-in) são os alvos de migração de maior risco, conforme descrito no contexto de Estabelecer métricas de índice de manutenibilidade para aplicações COBOLO CC é apenas um componente de um panorama de qualidade mais amplo, que também inclui o Volume Halstead e as linhas de código.
A funcionalidade de análise de impacto utiliza a classificação de complexidade baseada em CC para definir o escopo do que precisa ser validado quando qualquer alteração em um programa de alta complexidade ocorre: um CC mais alto significa mais caminhos de execução, o que significa mais cenários de teste que devem ser verificados para confirmar a equivalência comportamental antes e depois de qualquer modificação.
Para equipes que planejam programas de modernização de sistemas legados , a distribuição de componentes críticos (CC) em todo o portfólio é a entrada para o sequenciamento das ondas de migração: programas com baixo CC e poucos usuários migram primeiro; programas com alto CC e muitos usuários migram por último, depois que a equipe adquire experiência nos componentes mais simples e a infraestrutura de testes está em vigor para validar os componentes complexos.
A complexidade não é a inimiga, a complexidade invisível é.
A complexidade ciclomática é uma das poucas métricas de qualidade de código que se conecta diretamente à testabilidade; o número de casos de teste necessários para a cobertura completa do caminho é, por definição, pelo menos igual à pontuação de complexidade ciclomática. Essa conexão a torna praticamente acionável de uma forma que muitas outras métricas de qualidade não são.
A disciplina de gerenciar a complexidade em computação exige a compreensão de que ela se acumula gradualmente. if A adição de uma declaração a uma função crescente é individualmente razoável. O resultado de três anos de decisões individualmente razoáveis pode ser uma função com CC = 40 que ninguém quer mexer porque é genuinamente complexa demais para ser analisada com segurança. As ferramentas e técnicas deste guia — cláusulas de guarda, extração de métodos, polimorfismo, decomposição condicional e controles de qualidade CI/CD — existem para impedir que esse acúmulo ocorra de forma invisível e para abordá-lo sistematicamente quando já tiver ocorrido.