Como reduzir latência em API na prática

Latência não vira problema quando o dashboard fica vermelho. Ela já estava custando conversão, sobrecarregando worker, aumentando timeout em cascata e corroendo a margem da operação muito antes disso. Quando alguém pergunta como reduzir latência em API, a resposta útil não começa com framework nem com troca de linguagem. Começa com medição decente, entendimento do caminho crítico e cortes cirúrgicos nos pontos que mais pesam no p95 e no p99.

A maioria dos times olha média. Esse é o primeiro erro. Média esconde fila, lock, query ruim e dependência externa instável. O usuário sente o pior trecho da distribuição, não o número bonito no dashboard mensal. Se a sua API responde em 80 ms na média, mas explode para 2 s no p99 sob carga moderada, você tem um problema operacional, não estatístico.

Como reduzir latência em API sem atacar sintoma

Antes de otimizar qualquer coisa, separe latência em camadas. Tempo de rede, tempo de aplicação, tempo de banco, tempo de cache, tempo em chamadas externas e tempo esperando recurso. Sem essa decomposição, o time tende a mexer no que é visível, não no que é relevante.

O caminho certo é instrumentar tracing distribuído, métricas por endpoint e logs estruturados com correlation id. Não para “ter observabilidade”, mas para responder perguntas simples: qual endpoint degrada primeiro, em qual dependência o tempo se concentra, qual payload piora a curva e em que faixa de concorrência o sistema começa a formar fila.

Aqui entra um ponto que muita operação madura aprende do jeito difícil: latência raramente tem causa única. Em produção, ela costuma ser a soma de pequenos erros tolerados por tempo demais. Serialização pesada, N+1 silencioso, conexão mal gerida, retry agressivo, DNS lento, pool subdimensionado e query sem índice convivem até o tráfego aumentar. A partir daí, tudo aparece ao mesmo tempo.

Comece pelo banco, porque quase sempre ele participa

Em APIs de SaaS, banco relacional segue sendo um dos principais vetores de latência. Nem sempre o problema está no banco em si. Muitas vezes está no padrão de acesso da aplicação. Query excessiva, ORM gerando SQL ineficiente, paginação ruim, joins caros em rota quente e leitura de colunas demais são clássicos.

O primeiro trabalho é identificar as queries mais lentas e as mais frequentes. Às vezes a query mais cara nem é a que mais pesa no total. Uma consulta de 40 ms executada 5 mil vezes por minuto costuma fazer mais estrago do que uma de 700 ms executada poucas vezes. Depois disso, olhe plano de execução, índice ausente, índice mal escolhido, cardinalidade e concorrência.

Também vale revisar o modelo de consistência da rota. Nem toda leitura precisa ir ao banco principal, nem toda operação precisa ser síncrona. Se o endpoint monta uma visão agregada para tela administrativa, talvez faça sentido materializar parte dos dados ou servir via cache com invalidação controlada. Já se a rota é transacional e sensível a consistência, a otimização precisa vir mais de desenho de query, particionamento e pool tuning do que de atalhos.

Outro erro comum é ignorar contenção. Query rápida em ambiente ocioso pode ficar lenta sob lock, disputa de CPU ou saturação de IOPS. Por isso load test sem perfil de concorrência real ajuda pouco. O problema aparece quando o banco precisa atender muitas transações competindo pelo mesmo recurso.

Cache resolve bastante coisa. Cache ruim piora tudo

Cache é uma das respostas mais práticas para como reduzir latência em API, mas só quando existe estratégia. Colocar Redis no diagrama não melhora endpoint por mágica. É preciso saber o que cachear, por quanto tempo, como invalidar e qual o impacto de servir dado levemente defasado.

Rotas de leitura com alta repetição e baixa volatilidade são boas candidatas. Configurações, catálogos, permissões parcialmente pré-computadas e resultados de agregações pesadas costumam responder bem. Em contrapartida, cache em entidade muito mutável, sem política clara de invalidação, só troca latência por inconsistência.

Também existe o custo operacional do cache stampede. Quando várias requisições tentam recomputar a mesma chave ao mesmo tempo, a camada que deveria aliviar tráfego derruba o banco. Técnicas como single flight, TTL com jitter e warming controlado ajudam bastante. E vale lembrar: cache em excesso aumenta complexidade de depuração. Se o time não tem visibilidade sobre hit rate, miss rate, latência do próprio cache e fallback para origem, a solução vira outra fonte de incidentes.

Rede, payload e protocolo importam mais do que parece

Tem time que passa semanas espremendo query de 12 ms e ignora payload de 2 MB em JSON, compressão mal configurada ou tráfego atravessando regiões diferentes. Latência de API também é topologia. Se a aplicação roda em uma região, o banco em outra e um serviço de autenticação em uma terceira, você está comprando atraso em cada salto.

Revise proximidade entre componentes críticos, uso de keep-alive, reaproveitamento de conexão, TLS handshake excessivo e resolução de DNS. Em workloads internos, isso pesa. Em rotas públicas, payload e serialização também entram forte. JSON verboso, campos desnecessários e processamento caro na borda aumentam tempo sem gerar valor para o usuário.

Nem sempre trocar REST por gRPC faz sentido. Em alguns cenários internos, sim, o ganho de serialização e multiplexação compensa. Em outros, o gargalo segue no banco ou na chamada externa, então a mudança só desloca esforço de engenharia. O ponto é simples: protocolo não corrige arquitetura ruim.

Dependências externas são campeãs de p99 alto

API de pagamento, antifraude, CRM, LLM, provedor de identidade, fila gerenciada. Tudo isso adiciona variabilidade. O maior erro aqui é tratar dependência externa como se fosse extensão confiável da sua aplicação. Não é. Ela tem jitter, limite de taxa, degradação parcial e comportamento diferente por região e horário.

Se uma rota depende de terceiro, isole o impacto. Use timeout curto e explícito, circuit breaker, retry com backoff e, principalmente, idempotência. Retry sem critério multiplica latência e carga ao mesmo tempo. Em alguns casos, o melhor caminho é responder ao usuário com aceitação assíncrona e processar depois. Isso reduz latência percebida e protege a operação.

Também vale distinguir o que é crítico do que é enriquecimento. Se a API consegue responder a operação principal sem uma chamada acessória, degrade com elegância. Derrubar endpoint inteiro porque um serviço secundário falhou é desenho fraco.

Aplicação lenta também forma fila

Nem toda latência vem de I/O. CPU alta, garbage collection, pool de threads mal ajustado, lock em memória, serialização pesada e middleware excessivo geram fila dentro do processo. E fila interna costuma aparecer primeiro no p99.

Aqui, profiling faz diferença. Não chute. Meça tempo por handler, alocação de memória, saturação de worker, uso de event loop ou thread pool e impacto de bibliotecas transversais, como autenticação, validação, tracing e logging. Já vimos muito endpoint perder mais tempo serializando resposta e escrevendo log do que executando a regra de negócio.

Outro ponto prático é remover sincronismo desnecessário. Se uma operação pode ser quebrada em etapa síncrona mínima e pós-processamento assíncrono, a latência de resposta cai sem sacrificar consistência do fluxo principal. Mas isso exige desenho operacional correto: fila confiável, DLQ, reprocessamento e telemetria. Sem isso, o ganho vira dívida.

Como priorizar o que mexer primeiro

Se você quer reduzir latência de verdade, organize o trabalho em impacto e risco. Comece pelas rotas que concentram tráfego, receita ou incidentes. Depois, ataque os maiores contribuintes de p95 e p99. Nem sempre a maior lentidão absoluta é a primeira prioridade.

Uma abordagem pragmática funciona bem: estabeleça um SLO por endpoint crítico, meça baseline real, identifique os três maiores ofensores e execute mudanças pequenas com validação. Exemplo: corrigir uma query, ajustar pool de conexão e cachear uma leitura cara já pode derrubar centenas de milissegundos sem tocar em arquitetura macro.

Reescrita completa quase nunca é a primeira resposta. Em ambiente SaaS, isso costuma atrasar entrega, aumentar risco e desviar atenção do problema concreto. A maior parte dos ganhos relevantes vem de observabilidade melhor, tuning de gargalo, redesenho de fluxo quente e disciplina operacional. É exatamente nesse tipo de frente que a MGM Tech costuma entrar: menos teatro, mais intervenção onde a latência realmente nasce.

O que medir para não regredir depois

Depois de melhorar, proteja o resultado. Monitore p50, p95 e p99 por endpoint, taxa de erro, timeout, saturação de CPU, uso de pool de conexão, latência de queries, hit rate de cache e tempo por dependência externa. Coloque deploy e mudança de configuração no mesmo timeline das métricas. Sem isso, regressão vira caça ao culpado.

Também crie orçamento de performance para rotas críticas. Nova feature que adiciona duas chamadas externas e dobra payload precisa ser tratada como risco de produção, não como detalhe de implementação. Performance não é polimento. É parte do contrato do sistema com o negócio.

No fim, reduzir latência em API é menos sobre truque técnico e mais sobre maturidade para enxergar onde o tempo está sendo desperdiçado. Quando o time mede bem, entende o caminho crítico e aceita fazer otimização com contexto, o sistema fica mais rápido, mais previsível e muito mais fácil de operar sob pressão.

← Todos os posts