Home Projetos & Releases BigBangcraft Blog Sobre & Carreira
Arquitetura & CTO 6 min de leitura 16 visualizações
Raw .md

O custo invisível do reduce na manutenção de sistemas reais

PD

Pedro Dalben

CTO no Integrar Plus • Publicado em 14 de September de 2026

O custo invisível do reduce na manutenção de sistemas reais

Toda vez que vejo um pull request com um reduce de seis ou sete linhas tentando montar uma estrutura complexa, meu primeiro impulso é pedir refatoração. Não se trata de implicância com o paradigma funcional. Trata-se de sobrevivência. Na rotina de quem gerencia sistemas em produção e lidera equipes, o código que você escreve é lido dezenas de vezes a mais do que é alterado. E o método reduce, embora matematicamente brilhante, cobra um pedágio desproporcional da memória de trabalho de quem está tentando entender o que o software faz.

Essa discussão reapareceu com força recentemente na comunidade técnica, girando em torno de uma constatação que muitos desenvolvedores sentem na pele: programadores detestam o reduce.

O debate revelou duas linhas de raciocínio muito claras sobre a raiz dessa rejeição.

De um lado, existe a tese teórica de que o reduce é rejeitado justamente por ser poderoso demais. Enquanto operações como map e filter possuem um contrato restrito e previsível (o primeiro transforma cada elemento mantendo o tamanho da coleção, o segundo apenas seleciona itens sob uma condição), o reduce não impõe limite algum. Na teoria dos tipos e no cálculo lambda, um fold estrutural pode implementar map, filter e virtualmente qualquer operação sobre listas. Por ser a operação universal de acumulação, ela não comunica intenção. Quando você bate o olho em um map, seu cérebro já sabe o que esperar. Quando você encontra um reduce, o espaço de possibilidades continua infinito até você ler e dissecar cada detalhe da função acumuladora.

De outro lado, há o argumento puramente ergonômico, focado na sobrecarga cognitiva imediata. Cada linguagem resolveu implementar essa operação com uma ordem de parâmetros diferente. Em JavaScript, o primeiro argumento do callback é o acumulador e o segundo é o elemento. Em outras linguagens ou frameworks, a ordem se inverte. Pior: a posição do valor inicial varia, e as regras para o que acontece quando a lista está vazia e nenhum valor inicial foi passado costumam ser confusas. Até mesmo Rich Hickey já declarou publicamente que copiar a semântica do reduce do Common Lisp para o Clojure foi uma das piores decisões que tomou, justamente pelas inconsistências entre coleções com zero, um ou múltiplos itens.

Eu tendo a concordar muito mais com o argumento ergonômico, mas vejo que os teóricos erram o alvo ao sugerir que a solução é trocar o reduce por mais abstrações funcionais com nomes rebuscados.

No dia a dia com Ruby on Rails, lidando com regras de negócio financeiras, emissão de cobranças e relatórios no Integrar Plus, o problema raramente é a falta de poder expressivo. O problema é a perda de contexto do desenvolvedor. Quando alguém precisa abrir o terminal ou pegar papel e caneta para simular mentalmente qual é o estado do acumulador na terceira iteração de uma coleção, a abstração falhou.

Guido van Rossum teve uma clareza ímpar ao rebaixar o reduce de função nativa para o módulo functools na transição para o Python 3. O raciocínio dele era direto: fora somas ou multiplicações triviais, quase todo uso de reduce exige esforço desnecessário para decifrar o fluxo de dados. Um loop imperativo claro ou um método dedicado com intenção explícita resolve o problema em menos tempo e sem margem para dúvidas.

Veja um caso clássico em Ruby. Suponha que você precise agrupar totais de faturamento a partir de uma lista de faturas, separando por status:

## Abordagem com reduce
totais = faturas.reduce({}) do |acc, fatura|
  acc[fatura.status] ||= 0
  acc[fatura.status] += fatura.valor_centavos
  acc
end

O código acima parece inofensivo, mas contém armadilhas típicas. Ele exige retornar explicitamente o objeto acumulador na última linha do bloco, sob pena de passar nil para a próxima iteração. Se a coleção for muito grande e alguém decidir usar métodos não mutáveis como acc.merge, o consumo de memória explode alocando hashes intermediários a cada passo.

Agora compare com uma abordagem que expressa intenção direta, usando ferramentas nativas da biblioteca padrão:

## Abordagem com each_with_object ou acumulador explícito
totais = Hash.new(0)
faturas.each do |fatura|
  totais[fatura.status] += fatura.valor_centavos
end

Não há dúvida sobre retorno de bloco. Não há dúvida sobre ordem de parâmetros. Qualquer pessoa do time, do estagiário ao engenheiro principal, lê esse bloco em dois segundos durante uma revisão de código e entende o comportamento exato. A semântica de mutação está explícita, o escopo do acumulador é evidente e a intenção de negócio não fica escondida atrás de uma ginástica sintática.

Alguns defensores do paradigma funcional puro argumentam que loops imperativos são antiquados porque abrem espaço para efeitos colaterais descontrolados. Essa preocupação faz sentido em linguagens concorrentes com compartilhamento de memória bruta, mas se torna preciosismo teórico na imensa maioria das aplicações web corporativas. Quando você precisa debugar uma regressão em produção às duas da manhã, a última coisa que você quer é desvendar se o acumulador sofreu uma mutação indevida ou se o fold era pela esquerda ou pela direita.

O Ruby, em particular, fornece um meio-termo muito saudável. Para somas simples, temos sum. Para contagens, temos tally. Para agrupamentos, temos group_by. Para transformações chave-valor, temos index_by. Todas essas operações são, no fundo, especializações convenientes de uma redução. A diferença é que elas carregam um nome que reflete o domínio do problema, enquanto reduce apenas descreve o mecanismo matemático da computação.

Existe espaço legítimo para reduce? Sim. Operações puramente monoidais, cálculos matemáticos curtos ou transformações em coleções imutáveis onde a assinatura cabe em uma linha única, como valores.reduce(:+), são perfeitamente aceitáveis e limpas.

Fora desse nicho estrito, o uso indiscriminado de reduce costuma ser sintoma de código que prioriza o prazer estético de quem escreve sobre a produtividade de quem lê. Em arquitetura de software, a clareza sempre vence a esperteza. Se uma construção imperativa simples com três linhas de código economiza dez minutos de dúvida em cada code review pelos próximos dois anos, ela é a escolha técnica correta para o projeto.

Fontes

PD

Sobre Pedro Dalben

CTO e arquiteto full stack especializado em Ruby on Rails, Vue.js, sistemas multi-tenant e infraestrutura distribuída.