3

Introdução ao SonarQube [Estudo]

1. O que é o SonarQube?

O SonarQube é uma plataforma de revisão automatizada e análise estática de código. Ele examina o código sem precisar executar a aplicação e identifica problemas relacionados a:

  • confiabilidade;
  • segurança;
  • manutenibilidade;
  • duplicação;
  • cobertura de testes;
  • complexidade;
  • possíveis falhas de implementação.

O SonarQube Server é instalado e administrado pela própria organização. Existe também o SonarQube Cloud, hospedado pela Sonar.

Uma forma simples de entendê-lo:

O compilador verifica se o código é válido.
Os testes verificam se os comportamentos esperados funcionam.
O SonarQube verifica se o código apresenta riscos técnicos, problemas de segurança e dificuldades de manutenção.

Ele não substitui revisão de código, testes ou análise arquitetural. Ele automatiza parte dessas verificações.


2. Como o SonarQube funciona?

O fluxo básico é:

Código-fonte
    ↓
SonarScanner
    ↓
Análise estática
    ↓
SonarQube Server
    ↓
Dashboard, métricas e Quality Gate

O SonarScanner lê o projeto e envia o resultado da análise ao servidor. Existem scanners específicos para ferramentas como Maven, Gradle e .NET, além do scanner genérico por linha de comando. Para projetos Maven, o scanner pode ser executado diretamente como um objetivo do Maven.

Exemplo com Maven:

mvn clean verify sonar:sonar

Exemplo genérico:

sonar-scanner

O processo normalmente acontece no pipeline:

Checkout
   ↓
Compilação
   ↓
Testes
   ↓
Geração da cobertura
   ↓
Análise SonarQube
   ↓
Validação do Quality Gate
   ↓
Deploy

3. Principais conceitos

3.1 Issue

Uma issue é um problema encontrado por alguma regra do SonarQube.

Exemplo:

public boolean isActive(User user) {
    if (user.isActive()) {
        return true;
    } else {
        return false;
    }
}

O SonarQube pode sugerir simplificar:

public boolean isActive(User user) {
    return user.isActive();
}

Outro exemplo:

String password = "admin123";

Uma credencial escrita diretamente no código pode ser apontada como problema de segurança.

3.2 Rule

Uma rule, ou regra, define algo que o SonarQube procurará no código.

Exemplos de regras:

  • não deixar recursos abertos;
  • não usar credenciais fixas;
  • não ignorar exceções;
  • não repetir condições desnecessárias;
  • não manter código morto;
  • evitar métodos excessivamente complexos;
  • não realizar comparações incorretas;
  • remover variáveis não utilizadas.

Cada linguagem possui seu próprio conjunto de regras.

3.3 Quality Profile

O Quality Profile determina quais regras serão aplicadas na análise de uma linguagem. Um projeto Java pode possuir um perfil Java, enquanto o mesmo projeto pode usar outro perfil para JavaScript ou Kotlin.

Pense no Quality Profile como:

Quality Profile = conjunto de regras

Exemplo:

Perfil Java da empresa
├── Credenciais fixas são proibidas
├── Métodos não devem ter complexidade excessiva
├── Recursos devem ser fechados
├── Exceções não podem ser ignoradas
└── Código duplicado deve ser reduzido

O perfil padrão normalmente é chamado de Sonar way.

Uma empresa pode criar um perfil próprio, mas não é recomendável ativar centenas de regras sem avaliar seu impacto. Isso costuma gerar excesso de alertas e faz com que os desenvolvedores passem a ignorar a ferramenta.

3.4 Quality Gate

O Quality Gate é um conjunto de condições que determina se o projeto foi aprovado ou reprovado.

Quality Gate = critérios de aprovação

Um Quality Gate pode verificar, por exemplo:

Cobertura no código novo ≥ 80%
Duplicação no código novo ≤ 3%
Nenhuma nova vulnerabilidade
Nenhum novo bug crítico
Todos os Security Hotspots revisados

Quando alguma condição não é satisfeita:

Quality Gate: FAILED

Quando todas são satisfeitas:

Quality Gate: PASSED

A documentação define o Quality Gate como um conjunto de condições comparadas com os resultados da análise, indicando se o código deve ser corrigido antes de ser integrado ou liberado.

Diferença essencial

Quality Profile → o que será analisado
Quality Gate    → o que precisa ser aprovado

Exemplo:

  • o Quality Profile possui uma regra que detecta métodos muito complexos;
  • o Quality Gate determina que nenhum problema crítico novo pode existir.

4. Categorias de problemas

A classificação pode variar conforme a versão e o modo de configuração, mas os resultados normalmente são apresentados em dimensões relacionadas a confiabilidade, segurança e manutenibilidade.

4.1 Problemas de confiabilidade

São situações que podem provocar comportamento incorreto ou falha na aplicação.

Exemplo:

public int getSize(List<String> names) {
    return names.size();
}

Se names puder ser null, existe risco de NullPointerException.

Possível correção:

public int getSize(List<String> names) {
    return names == null ? 0 : names.size();
}

Mas a melhor solução depende do contrato do método. Às vezes, aceitar null é o problema real.

4.2 Problemas de segurança

São vulnerabilidades com impacto potencialmente explorável.

Exemplo:

String sql = "SELECT * FROM users WHERE name = '" + name + "'";

Esse código pode permitir SQL Injection.

Correção:

String sql = "SELECT * FROM users WHERE name = ?";

E usar parâmetros preparados.

Outro exemplo:

String token = "meu-token-secreto";

Segredos não devem ficar no repositório.

4.3 Security Hotspot

Um Security Hotspot não significa necessariamente que existe uma vulnerabilidade confirmada. Significa que um trecho sensível precisa ser revisado por uma pessoa.

Exemplo:

MessageDigest md = MessageDigest.getInstance("MD5");

O SonarQube pode pedir que o desenvolvedor verifique se o algoritmo é adequado ao contexto.

Após a revisão, o hotspot pode ser marcado como:

  • seguro;
  • corrigido;
  • reconhecido;
  • pendente de análise.

A ideia é diferente de uma vulnerabilidade automática:

Vulnerabilidade → problema de segurança detectado
Hotspot         → trecho sensível que exige revisão

4.4 Problemas de manutenibilidade

São problemas que dificultam entender, alterar ou testar o código.

Exemplo:

if (user != null) {
    if (user.getCompany() != null) {
        if (user.getCompany().isActive()) {
            if (user.hasPermission()) {
                // ...
            }
        }
    }
}

Esse código pode funcionar, mas apresenta alta complexidade.

Uma refatoração possível:

if (!isAuthorized(user)) {
    return;
}

// ...
private boolean isAuthorized(User user) {
    return user != null
        && user.getCompany() != null
        && user.getCompany().isActive()
        && user.hasPermission();
}

5. Severidade dos problemas

As issues recebem prioridades ou severidades para orientar a correção.

Uma interpretação prática:

NívelInterpretação
BaixoMelhoria pequena
MédioDeve ser avaliado
AltoRisco relevante
CríticoPode gerar falha grave
BloqueadorNão deveria chegar à produção

A severidade ajuda na priorização, mas não substitui a análise do contexto.

Um alerta classificado como médio pode ser extremamente importante em um sistema financeiro. Um alerta alto pode não representar risco real em um teste isolado.


6. Métricas importantes

6.1 Cobertura de testes

A cobertura indica quanto do código foi executado pelos testes.

Exemplo:

Linhas executáveis: 100
Linhas cobertas: 80
Cobertura: 80%

Entretanto:

Cobertura alta ≠ testes bons

Este teste aumenta cobertura, mas é fraco:

@Test
void test() {
    service.process();
}

Um teste melhor valida o comportamento:

@Test
void shouldRejectOrderWhenCustomerHasNoCredit() {
    var order = createOrderWithoutAvailableCredit();

    assertThrows(
        InsufficientCreditException.class,
        () -> service.process(order)
    );
}

O SonarQube geralmente não executa os testes nem calcula sozinho a cobertura. A ferramenta de testes gera um relatório, e o scanner o importa.

Em Java, uma combinação comum é:

JUnit + JaCoCo + SonarQube

Em Flutter:

flutter test --coverage

Isso gera:

coverage/lcov.info

Depois o SonarQube importa o arquivo.

6.2 Duplicação

O SonarQube identifica blocos semelhantes ou repetidos.

Exemplo:

void validateCustomer(Customer customer) {
    if (customer == null) {
        throw new IllegalArgumentException();
    }

    if (!customer.isActive()) {
        throw new IllegalStateException();
    }
}
void validateSupplier(Supplier supplier) {
    if (supplier == null) {
        throw new IllegalArgumentException();
    }

    if (!supplier.isActive()) {
        throw new IllegalStateException();
    }
}

Pode existir oportunidade de abstração. Porém, nem toda duplicação deve ser removida imediatamente.

Duas regras de negócio apenas coincidentemente parecidas podem evoluir em direções diferentes. Criar uma abstração prematura também gera acoplamento.

6.3 Complexidade cognitiva

A complexidade cognitiva tenta representar o esforço necessário para entender o fluxo de um método.

Este método é difícil de acompanhar:

void process(Order order) {
    if (order != null) {
        if (order.isValid()) {
            for (Item item : order.getItems()) {
                if (item.isAvailable()) {
                    if (item.getPrice() > 0) {
                        // ...
                    }
                }
            }
        }
    }
}

Uma alternativa:

void process(Order order) {
    validate(order);

    order.getItems().stream()
        .filter(Item::isAvailable)
        .filter(item -> item.getPrice() > 0)
        .forEach(this::processItem);
}

O objetivo não é apenas diminuir um número. É tornar o código mais fácil de compreender.

6.4 Débito técnico

O SonarQube estima o esforço necessário para corrigir determinados problemas.

Exemplo:

12 issues
Dívida técnica estimada: 3h20

Essa estimativa não é um orçamento preciso. Ela serve principalmente para comparação, tendência e priorização.

Não significa necessariamente:

Um desenvolvedor levará exatamente 3 horas e 20 minutos.

6.5 Ratings

O SonarQube pode apresentar classificações para dimensões como:

  • confiabilidade;
  • segurança;
  • manutenibilidade;
  • revisão de segurança.

Essas avaliações podem participar das condições do Quality Gate.


7. Código geral versus código novo

Um dos conceitos mais importantes é separar:

Overall Code → código completo do projeto
New Code     → código novo ou alterado

Imagine um sistema legado com:

10.000 problemas existentes
Cobertura geral: 18%
Duplicação geral: 12%

Exigir imediatamente:

Cobertura geral ≥ 80%
Nenhuma issue existente

pode tornar o Quality Gate impossível de cumprir.

A estratégia recomendada é impedir que novas alterações aumentem a dívida:

Cobertura do código novo ≥ 80%
Nenhum bug novo
Nenhuma vulnerabilidade nova
Duplicação nova ≤ 3%
Hotspots novos revisados

Essa abordagem é chamada de Clean as You Code: manter limpo o código criado ou alterado agora, melhorando o projeto de forma incremental. A documentação recomenda priorizar métricas de código novo no Quality Gate.

A lógica é:

Não consigo corrigir todo o legado hoje.
Mas posso impedir que o código novo piore o sistema.
Carregando publicação patrocinada...