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.
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.
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.