🚀 Oferta especial: 60% OFF no CrazyStack - Últimas vagas!Garantir vaga →
Backend

Como vencer a Rinha de Backend 2025 com TypeScript, Node.js e Redis

Descubra os segredos para processar milhares de pagamentos com máxima confiabilidade usando Node.js, TypeScript e Redis sob condições extremas.

CrazyStack
15 min de leitura
Rinha de BackendNode.jsTypeScriptRedisArquiteturaPerformance

Por que isso é importante

Saber criar sistemas resilientes e de alta performance é uma habilidade essencial para profissionais de backend. Em competições como a Rinha de Backend, soluções eficientes são testadas ao extremo, revelando aprendizados sólidos que podem ser aplicados em aplicações reais, especialmente quando há limitação de recursos e alto volume de transações críticas.

Desafio: O que é a Rinha de Backend?

A Rinha de Backend propõe um desafio extremo de sistemas distribuídos: processar rapidamente um grande volume de pagamentos com máxima confiabilidade e o mínimo de recursos de CPU e memória. O equilíbrio entre tempo de resposta e total de transações sem falhas é vital para vencer. Os participantes enfrentam limitações rígidas e precisam optar por arquiteturas enxutas e estratégia cirúrgica para garantir robustez e velocidade sob pressão.

⚠️Atenção

É impossível alcançar 100% de confiabilidade com o menor tempo de resposta em todas as situações: sempre há um equilíbrio crucial entre performance e segurança dos dados.

Proposta: Estratégias e escolhas iniciais

O primeiro passo foi optar por linguagens menos convencionais no contexto da Rinha, priorizando stacks interpretadas como Python e JavaScript/TypeScript, conhecidas pelo rápido bootstrap e facilidade de adaptação, mas o desempenho inicial não foi suficiente. A estratégia girava em torno de filas, paralelismo e uso de recursos compartilhados como Redis, apostando em design mais enxuto e migrável.

1
Passo 1: Testar soluções rápidas em Python e medir performance real.
2
Passo 2: Migrar para TypeScript e Node.js, validando melhorias no consumo de recursos.
3
Passo 3: Usar Redis como fila e store central para processar mensagens sem depender de bancos SQL convencionais.

Arquitetura inicial e primeiros aprendizados

O início seguiu uma abordagem com múltiplas instâncias de API e workers, estruturadas em torno de um load balancer (Nginx), Redis para enfileiramento e um banco SQL como persistência original. A ideia era escalar horizontalmente, mas logo os limites de 1.5 vCPU e 350 MB de RAM mostraram que dividir recursos em excesso era prejudicial: o sistema ficou instável sob carga.

ℹ️Dica de performance

Pulverizar CPU/RAM em muitas instâncias pode ser armadilha quando há limite estrito de recursos: consolidar componentes é mais eficiente em cargas intensas.

Otimizando: A dor do banco de dados

O ponto crítico foi a estabilidade do banco de dados SQL, que caía frequentemente e causava explosões de uso de memória durante stress. Tentar reduzir o pool de conexões até ajudou, mas aumentou a latência (P99), prejudicando a experiência real dos usuários. O uso de ORM ou SQL puro foi insuficiente para mitigar as limitações impostas pelo hardware e pela arquitetura escolhida.

⚠️Atenção ao P99

Sempre monitore o P99 (percentil 99) nas métricas de latência, especialmente sob carga: ele reflete a experiência do usuário no cenário mais crítico!

Virada de chave: tirando o SQL do caminho

Observando que muitos competidores abandonavam bancos relacionais, a decisão foi consolidar fila e storage no Redis. A alteração eliminou boa parte da instabilidade, permitiu processar altos volumes sem gargalos de I/O e elevou a robustez do sistema sob limites estritos de CPU/RAM. O segredo foi priorizar funcionamento simples e sem “over engineering”, focando no objetivo do desafio.

Aprendizado-chave

Implemente primeiro o mais simples que funciona. Otimize e refatore só após garantir a estabilidade mínima, iterando com pequenas mudanças e testes constantes.

Balanceando APIs, workers e recursos

Após vários experimentos, ficou claro que dividir demais recursos entre APIs e workers gerava escassez. Fundindo API e worker numa mesma instância, cada processo pôde acessar mais RAM/CPU, reduzindo drasticamente a latência (P99), enquanto ainda mantinha o sistema simples e altamente performático. O ajuste fino foi essencial para o equilíbrio entre rendimento e confiabilidade.

ℹ️Otimize recursos

Fundir funções pode liberar recursos preciosos em ambientes restritos, ao invés de fragmentar em muitos processos pesados.

Gateways, health-checks e fallback resiliente

O backend precisava decidir entre dois gateways de pagamento, “default” e “fallback”, cada um com custo e estabilidade próprios. Um health check a cada 5 segundos orientava essa escolha, mas exceder frequência levava a penalizações. Uma estratégia sábia de caching dessas informações e escolha dinâmica do gateway aumentou a robustez e reduziu custos sem sacrificar throughput.

Health-check Dedicado

Processo externo mantém estado atualizado e compartilhável, minimizando I/O desnecessário dos workers.

Prós
  • Baixo overhead nos workers
  • Menor latência
  • Atualização consistente
Contras
  • Complexidade ligeiramente maior
  • Gerência paralela

Health-check Embutido no Worker

Cada worker realiza seu próprio health-check, armazenando localmente.

Prós
  • Implementação mais simples
  • Menos dependências externas
Contras
  • Mais requisições desnecessárias
  • Inconsistência de estado atual sob concorrência

Filas, Workers e o papel do Redis

O Redis cumpriu dupla função: fila FIFO para pagamentos e armazenamento de dados chave-valor dos estados intermediários. Workers processavam mensagens e atualizavam o status imediatamente, garantindo que mesmo sob crash parcial, nada se perdia. A leveza do Redis fez diferença crucial, sem penalizar a performance com bloqueios ou I/O pesados.

Node.js

Runtime assíncrono de alta performance para JavaScript/TypeScript

TypeScript

Tipagem forte para código robusto com menos erros em produção

Redis

Fila e armazenamento ultrarrápidos, perfeito para cenários de baixa latência

Nginx

Balanceamento de carga simples e eficiente para múltiplas APIs

Monitoração, métricas e ajuste de gargalos

Analisar métricas como P99 permitiu identificar os verdadeiros gargalos em cada etapa. Ajustando número de instâncias, quantidade de workers, e tamanho da fila/fila de conexões, foi possível equilibrar throughput total com o baixo tempo de resposta, até atingir a zona de máxima performance dentro do limite da plataforma.

⚠️Atenção ao ajuste fino

Pequenas alterações em quantidade de threads/processos podem causar impactos exponenciais no consumo de recursos sob restrições.

Iteração, aprendizado e mentalidade de competição

O diferencial está na mentalidade: primeiro faça funcionar, depois otimize detalhes e só então busque estratégias exóticas. Iterar rapidamente, medir cada mudança e parar para comparar com soluções já validadas economiza horas e impede sobrecarga desnecessária no código e na infraestrutura.

ℹ️Aprenda com os outros

Observar soluções de concorrentes, analisar o que realmente está funcionando para cenários similares, acelera seu aprendizado e reduz retrabalho.

Resumo visual: De arquitetura robusta para o minimalismo eficiente

A arquitetura mais enxuta, com API e worker consolidados, uso inteligente de Redis e balanceamento preciso, superou a robustez da arquitetura inicial, mostrando que na Rinha (e em muitos projetos reais), menos é mais.

Arquitetura Robustas e Distribuídas

Várias instâncias, bancos SQL e filas separadas.

Prós
  • Escalabilidade teórica
  • Componentes desacoplados
Contras
  • Overhead alto
  • Latência acumulada
  • Gestão complexa

Arquitetura Minimalista Consolidada

API e workers juntos, Redis no papel central.

Prós
  • Latência baixíssima
  • Consumo controlado de recursos
  • Operação simplificada
Contras
  • Escalabilidade menor fora do limite de hardware
  • Menos isolamento em falhas críticas

Principais lições e recomendações para projetos reais

Priorizando simplicidade arquitetural, uso inteligente de recursos e aprendizado contínuo com métricas e benchmarking, torna-se possível conquistar alta performance mesmo em ambientes restritos. Pratique iterar rápido, medir tudo e opte por soluções que funcionem sob pressão real.

Checklist de Implementação

Testou soluções em diferentes linguagens para balancear performance e facilidade?
Eliminou dependências pesadas e optou por persistência leve?
Aplicou filas eficientes (ex: Redis) para desacoplar processamento?
Consolidou funções para maximizar recursos de CPU e memória?
Configurou health-checks otimizados para garantir robustez?
Iterou, mediu e ajustou cada parte baseada em métricas reais?

Domine React e Node com o CrazyStack

Aprenda técnicas avançadas de React com nosso curso completo