Testes e qualidade de código

[Code smell #3] Código ravioli: código cujo fluxo ninguém conhece

Código ravioli é aquele em que todas as funções têm cinco linhas e uma responsabilidade, mas ninguém consegue explicar o que acontece ao clicar em um botão. Reunimos os motivos da fragmentação excessiva, o custo revelado pela quantidade de saltos e como recuperar o fluxo.

6 min de leitura
Imagem de capa de [Code smell #3] Código ravioli: código cujo fluxo ninguém conhece

Em uma revisão de código, há situações assim: você abre o arquivo e todas as funções têm no máximo cinco linhas.

Este texto dá continuidade ao artigo anterior Code smell #2.

Os nomes são bons e cada classe tem apenas uma responsabilidade. Não há o que apontar.

Mas ninguém consegue responder na hora à pergunta: “O que acontece quando este botão é pressionado?”

Esse tipo de código é chamado de Ravioli Code (Texto original do C2).

Não era apenas uma crítica

Curiosamente, o termo nem sempre é usado de forma negativa. Ravioli é uma massa formada por pequenos pedaços que envolvem bem o recheio.

São pequenos objetos bem encapsulados, exatamente o que a orientação a objetos buscava. De fato, há usos de ravioli como oposto ao código espaguete.

O problema é a quantidade.

Cada pedaço é perfeito, mas há 200 no prato e não está escrito em lugar nenhum em que ordem ou como eles se conectam.

Na prática, é assim.

final class CheckoutCoordinator {
    func start() { validator.validate(cart) }
}

final class CartValidator {
    func validate(_ cart: Cart) { stockChecker.check(cart.items) }
}

final class StockChecker {
    func check(_ items: [Item]) { priceCalculator.calculate(items) }
}

final class PriceCalculator {
    func calculate(_ items: [Item]) { paymentPreparer.prepare(items) }
}
// ... Há mais dezesseis classes como esta

Cada classe é impecável. O nome é preciso e ela faz uma única coisa.

Mas não existe um arquivo que conheça todo o fluxo de pagamento.

O fluxo existe apenas nas relações de chamada entre as classes e só pode ser descoberto conectando o depurador, não lendo o código.

Por que a divisão fica excessiva

Quando “quanto menor a função, melhor” é aceito como regra.『Clean Code』chega a dizer que uma função deve ter duas ou três linhas, no máximo quatro.

O objetivo real desse conselho é fazer uma função lidar com apenas um nível de abstração. Quando isso vira um número de linhas, o propósito desaparece.

O resultado são trinta funções de cinco linhas, das quais vinte e oito são chamadas de um único lugar.

Quando o nome não resume o conteúdo., handleUserAction, processData e updateState não dizem o que fazem.

Ao dividir o código em funções com esses nomes, o leitor precisa abrir o corpo, e o número de lugares a consultar aumenta.

Uma boa extração elimina a necessidade de olhar o corpo; aqui acontece o contrário.

Quando níveis de abstração se misturam. Na mesma função convivem uma frase de política, como “validar o pedido”, e uma frase de detalhe, como “incrementar o índice em 1”. O olhar sobe e desce sem parar.

Dividir sem critério nesse estado cria fragmentos com níveis desordenados.

Quando um código usado uma vez é extraído pensando em reutilização. É a mesma mentalidade que cria código lasanha; só muda a direção, de vertical para horizontal.

Diagrama comparando a estrutura de classes ravioli encadeadas com a estrutura de uma função orquestradora
Um único lugar para registrar o fluxo resolve grande parte do problema

O custo aparece na quantidade de saltos

O custo do código ravioli pode ser resumido em uma frase: quantos arquivos você abre para responder a uma pergunta?

Ao ler código, mantemos o fluxo na memória de curto prazo. Depois de três ou quatro elementos, essa memória começa a falhar.

Depois de saltar por seis arquivos, você esquece o que procurava no início e volta ao começo.

Quando isso se repete, executar para conferir fica mais rápido do que ler o código.

Também surgem efeitos colaterais.

  • Ao adicionar uma funcionalidade, você não encontra o fragmento existente e cria outro parecido. A duplicação cresce silenciosamente.
  • Sem encontrar onde corrigir o bug, você adiciona um tratamento temporário na ponta do fluxo, ou seja, na tela.
  • Como o fluxo completo não está registrado no código, ele fica apenas na documentação ou na cabeça de alguém. Quando essa pessoa sai da equipe, ele desaparece junto.

Como recuperar o fluxo

Crie um lugar para registrar o fluxo. É a medida mais eficaz.

Tenha uma função que mostre toda a ordem de uma vez e faça com que ela descreva apenas a sequência.

func checkout(_ cart: Cart) async throws -> Receipt {
    try validate(cart)
    try await reserveStock(cart.items)
    let amount = calculateTotal(cart)
    let payment = try await charge(amount)
    return try await confirm(cart, payment)
}

Os detalhes continuam em seus próprios lugares. A mudança é que o fluxo passa a estar escrito em uma única tela.

Funções assim também são chamadas de orquestradoras. É exatamente esse lugar que falta ao código ravioli.

**Mantenha um único nível por camada.**As cinco linhas da função acima são frases do mesmo nível. Se uma condição detalhada como items.count > 0 entrar ali, o nível se rompe.

Criar o hábito de verificar se as frases estão no mesmo nível ao ler uma função é mais útil do que seguir um critério de divisão.

**Decida a extração pelo nome.**O critério não é o número de linhas, mas esta pergunta: “O nome deste fragmento diz mais do que o corpo?”

Extrair if user.age >= 19 como isAdult(user) revela a intenção e traz benefício. Extrair array.append(item) como addItem não traz nada.

**Mantenha perto o que é próximo.**Código que muda junto deve ficar no mesmo arquivo e na mesma pasta.

Se, para seguir a convenção de um tipo por arquivo, você espalha tipos que sempre são abertos juntos, a proximidade é melhor que a convenção.

**O mesmo critério vale para unidades de serviço.**O estado de dividir microserviços demais é chamado de nanosserviços.

Uma arquitetura que atravessa doze serviços para processar uma requisição é código ravioli estendido para além da rede. Nesse caso, ao custo dos saltos somam-se latência e pontos de falha.

Imagem comparando lado a lado as estruturas de três code smells: espaguete, lasanha e ravioli
Os três são perfeitos no nível dos fragmentos

Lasanha, ravioli e espaguete

Colocando os três lado a lado, temos o seguinte resumo.

Forma Estado dos fragmentos Problema
Espaguete Grande e emaranhado Não é possível prever o fluxo de execução
Lasanha Camadas verticais Uma alteração atravessa várias camadas
Ravioli Pequeno e organizado O fluxo entre os fragmentos não existe em lugar nenhum

Nos três, o custo de compreender o todo é alto; só muda a forma.

Se você mede a qualidade do código apenas pela beleza de cada fragmento, não percebe a lasanha nem o ravioli. Ambos são perfeitos no nível dos fragmentos.

Resumo

  • Código ravioli é aquele com tantos fragmentos pequenos e organizados que ninguém consegue explicar o fluxo completo.
  • O termo também já foi usado positivamente para indicar código bem encapsulado. O problema é a quantidade.
  • As principais causas são regras de quantidade de linhas, nomes ambíguos e níveis de abstração misturados.
  • O ponto central da solução é ter um lugar para registrar o fluxo.
  • O critério de extração não é o tamanho, mas o nome. Extraia apenas quando o nome comunicar mais do que o corpo.

O próximo artigo trata do extremo oposto: não há fragmentos demais, mas apenas um.

Vamos conhecer o God Object que sabe tudo sobre o aplicativo a partir de uma única classe.

Fontes e critérios de verificação

  • Ravioli Code — C2 Wiki · texto original do autor · verificado em 2026-08-17 · base: metáfora do código ravioli como objetos pequenos e encapsulados fragmentados em excesso

Continue lendo