A Dor Real: Quando o Reactive vira Over‑engineering no Node.js
O problema não é o async/await. O problema é tentar enfiar um paradigma reativo complexo em cima de um runtime que já lida muito bem com concorrência cooperativa. O resultado é sempre o mesmo: mais operadores, mais stream pipelines, menos clareza e muito mais tempo de onboarding.
**O sintoma clássico:** o time passa mais tempo brigando com operadores RxJS do que entregando valor. Debugging vira caça ao stack trace. Monitoring perde contexto de causa‑raiz. E tudo isso para resolver problemas que um async bem escrito resolve em dez linhas.
A promessa é bonita — *backpressure*, *composição declarativa*, *alto throughput*. A realidade: arquiteturas mais difíceis de manter e devs queimando horas para entender um fluxo simples que virou grafo mental.
A Solução Pragmática: Volte ao modelo que o Node já sabe dominar
Node nasceu para I/O assíncrono simples. Quando a demanda é throughput, escalabilidade horizontal e código legível, o combo certo é: async/await, filas, circuit breakers e observabilidade decente.
**O segredo é parar de lutar contra o modelo natural da plataforma.** Não precisa “WebFlux‑izar” seu backend para ganhar performance.
Se você realmente precisa de processamento por streaming? Use os Streams nativos do Node. Se precisa de resiliência? Use librarias como p-limit ou BullMQ. Sem inventar DSLs reativas.
Implementação de Sênior: Resolvendo backpressure de forma honesta
Aqui vai um exemplo real, direto e sem firulas: controlar backpressure em processamento de alta carga usando streams nativos e um limitador simples.
import { Readable } from 'node:stream';
import pLimit from 'p-limit';
// Fonte de dados simulada
const source = Readable.from(Array.from({ length: 10000 }, (_, i) => i));
// Limitador para evitar saturar o event loop
const limit = pLimit(20);
async function processItem(n) {
// Simula I/O pesado
await new Promise(r => setTimeout(r, 10));
return n * 2;
}
(async () => {
for await (const item of source) {
limit(() => processItem(item))
.then(result => console.log('OK:', result))
.catch(err => console.error('ERR:', err));
}
})();
Sem operadores obscuros. Sem perda de stack trace. Sem curva de aprendizado artificial. Backpressure resolvido com ferramentas nativas e previsíveis.
O Custo da Escolha: Reactive não é de graça
Escolher reactive em Node tem preço — e não é baixo:
- Mais dívida técnica: operadores e fluxos difíceis de explicar.
- Debugging mais caro: stack trace fragmentado e difícil de correlacionar.
- Onboarding lento: devs gastam tempo aprendendo o framework, não o negócio.
- Monitoramento fraco: correlação de transações vira loteria.
E o pior: **quase sempre era desnecessário**.
Direto das Trincheiras
- Stream nativo do Node resolve 90% dos casos onde tentam empurrar RxJS.
- Se você precisa ler documentação de operadores para entender o fluxo, o design já morreu.
- Reatividade não substitui boas decisões de arquitetura: filas resolvem gargalos melhores que pipes mágicos.
Fontes
Complexidade do WebFlux: Estamos exagerando em operações …
Página 2 – Coisas sobre desenvolvimento de software – CØdeZØne!
As pessoas ainda constroem sites apenas com HTML/CSS … – Reddit
Fechando
Obrigado por acompanhar essa reflexão até o fim!
Espero que esses pontos ajudem você a tomar decisões mais lúcidas no seu próximo projeto. Não deixe de conferir outros artigos aqui no blog, onde descascamos outros hypes da nossa área.
Valeu e até a próxima! 😉


