Aplicativo criado por IA está lento: corrija consultas N+1 e índices ausentes
Aplicativo criado por IA está lento? Comece pelos principais problemas de banco: consultas N+1, índices ausentes, varreduras sem limites e ORMs verborrágicos, com correções rápidas.

O que “lento” costuma significar (e por que o banco de dados é uma causa comum)
“Lento” raramente é um único número. Para os usuários, parece uma página que fica presa num spinner, um login que demora, uma busca que retorna atrasada ou um checkout que expira. Mesmo quando algo finalmente carrega, o app parece pouco confiável porque você não sabe se o próximo clique vai travar.
Uma surpresa comum: o banco de dados muitas vezes domina o tempo total. Seu servidor pode renderizar uma página em milissegundos, mas se ele espera por dados, tudo espera. Uma consulta lenta pode bloquear toda a requisição. Um monte de consultas medianamente lentas pode fazer o mesmo.
Se seu app criado por IA está lento, vale checar o banco cedo porque código gerado por IA tende a produzir acesso a dados que parece razoável, mas é ineficiente na prática. Pode buscar os mesmos dados várias vezes, puxar muito mais linhas do que precisa ou rodar consultas que funcionam com 50 registros e desabam com 50.000.
Por que o banco vence (para o bem ou para o mal)
A maioria das páginas segue um padrão simples: ler dados, moldá-los, devolver. As leituras são onde o tempo desaparece.
Formas comuns de o banco de dados dominar:
- Muitas consultas por carregamento de página (cada uma adiciona tempo de espera).
- Consultas que varrem muitas linhas porque não usam um índice.
- Consultas que retornam resultados enormes e depois são filtradas no código.
- Locks ou transações longas que fazem outras requisições enfileirarem.
- Limites do pool de conexões que fazem requisições aguardarem por uma conexão livre.
O objetivo não é “corrigir tudo”. Comece encontrando o maior gargalo que você consegue medir, corrija e verifique novamente. Uma boa mudança pode cortar o tempo de carregamento pela metade; perseguir dez problemas pequenos muitas vezes consome um dia inteiro.
Na FixMyMess, esse padrão aparece sempre em protótipos gerados por IA: o app funciona bem na demo e fica dolorosamente lento quando chegam usuários reais e dados reais. O progresso mais rápido costuma vir de isolar a consulta única (ou pequeno conjunto de consultas) que está no caminho crítico da sua página mais lenta.
Sinais rápidos de que o banco é o gargalo
Quando um app criado por IA está lento, o banco costuma ser o primeiro lugar a olhar porque é o recurso compartilhado de que toda requisição depende. Uma consulta lenta pode bloquear muitos usuários ao mesmo tempo.
Sintomas comuns que apontam para problemas no banco:
- Endpoints específicos são lentos, enquanto outros parecem normais (frequentemente páginas de listagem, busca, dashboards).
- Você vê timeouts ou erros de “requisição demorou demais” sob tráfego real.
- A CPU do servidor de app está bem, mas a CPU do banco está alta ou o banco faz muitas leituras.
- O desempenho piora conforme os dados crescem (mais usuários, mais linhas, mais joins).
- A mesma página fica mais lenta semana a semana, sem novas funcionalidades.
Para separar lentidão do servidor de app da lentidão do banco, pense onde o tempo é gasto. Problemas no servidor geralmente aparecem assim: todas as rotas estão lentas, a CPU do web server está alta ou a memória sobe até travar. Problemas no banco geralmente aparecem assim: algumas rotas são dolorosamente lentas, a lentidão está ligada a certas tabelas e o problema piora muito com mais dados.
“Rápido localmente, lento em produção” é uma grande pista. Seu banco no laptop é pequeno, quente (cacheado) e não tem outros usuários competindo. Produção tem dados reais, concorrência real e, muitas vezes, rede e regras de segurança mais estritas. Se um endpoint é instantâneo localmente e arrasta em produção, muitas vezes o plano de consulta está fazendo trabalho extra em escala, como índices ausentes, consultas N+1 ou varredura completa.
Um check rápido: abra a página lenta e depois tente uma versão com menos dados (intervalo de datas menor, menos filtros, menos linhas). Se ficar rápido, você provavelmente está pagando uma “taxa de tamanho de dados”. Times que trazem esses casos para a FixMyMess costumam encontrar o mesmo padrão: o app “funciona” como protótipo e fica lento quando chegam usuários e tabelas reais.
Fluxo simples de troubleshooting passo a passo
Quando um app criado por IA está lento, é tentador mexer no código em todo lugar. Resista. Escolha uma ação do usuário e siga-a de ponta a ponta para medir o que realmente ajuda.
Comece escolhendo uma ação lenta que um usuário real faz, como “abrir Pedidos” ou “buscar clientes”. Então identifique o endpoint por trás dela (a rota API exata ou handler do servidor). Isso mantém seu teste repetível.
Em seguida, execute a ação enquanto captura a atividade do banco. Use o que já tiver: um log de queries em dev, um trace APM em produção ou logging temporário ao redor da requisição. Você busca as queries principais que ocorrem durante aquela ação, não um panorama geral do app.
Meça duas coisas em vez de adivinhar: quantas queries disparam e quanto tempo total o banco gasta com elas. Uma página que dispara 120 queries pequenas pode ser tão lenta quanto uma com 2 consultas enormes.
Fluxo prático e focado:
- Reproduza a ação lenta e anote o endpoint que a trata.
- Capture as queries durante essa ação.
- Registre contagem de queries e tempo total de BD para a requisição.
- Ordene por impacto: a query mais lenta e a mais repetida geralmente vêm primeiro.
- Corrija um problema e rode a mesma ação para confirmar a melhora.
Exemplo: se “abrir Pedidos” leva 6 segundos e você encontra 80 queries com 4,8s de tempo de BD, corrija o pior (frequentemente um filtro sem índice ou um loop N+1). Se o reteste cair para 2 segundos, você está no caminho certo.
Se você herdou uma base de código gerada por IA e os traces não fazem sentido, a FixMyMess pode fazer uma auditoria rápida e apontar os poucos problemas no banco que vão mover a agulha mais rápido.
5 checagens rápidas antes de mudar qualquer código
Se seu app criado por IA está lento, não comece reescrevendo recursos. Primeiro, tenha uma imagem clara do que o banco faz numa única página ou requisição lenta.
Checagens rápidas que normalmente revelam o problema
Comece com essas cinco checagens nos seus logs, APM ou histórico de queries:
- Conte queries por requisição. Se uma página simples dispara 50, 100 ou 500+ queries, o desempenho vai colapsar quando o tráfego crescer.
- Procure repetições. Se você vê o mesmo formato de query rodando várias vezes com só o ID mudando, provavelmente tem um padrão N+1.
- Rank por custo total, não só “a mais lenta”. Uma query de 30 ms rodando 1.000 vezes faz mais mal que uma única de 2 s. Olhe duração x contagem.
- Encontre consultas que trazem “dados demais”. Fique atento a SELECT * em tabelas grandes, WHEREs faltando, LIMITs faltando ou carregar colunas text/blob grandes que você não precisa.
- Cheque se índices estão sendo usados. Uma query pode parecer ok, mas o plano mostrar uma varredura em vez de um lookup por índice.
Um pequeno exemplo: um dashboard carrega uma lista de 50 clientes e, dentro de um loop, busca a “última fatura”. Você pode ver uma query para a lista e mais 50 queries quase idênticas para faturas. Cada query é rápida sozinha, mas juntas transformam uma requisição numa fila de tráfego.
O que capturar antes de mexer em algo
Anote três números para a requisição lenta: contagem total de queries, top 3 queries por tempo total e se essas queries usam índices ou fazem scan. Isso dá uma linha de base para confirmar que cada correção ajudou.
Em bases geradas por IA, esses problemas costumam ficar escondidos atrás do ORM e logs barulhentos. Na FixMyMess, puxamos esses dados durante uma auditoria gratuita para que a primeira mudança seja a certa.
Consultas N+1: como identificar e parar
Uma consulta N+1 acontece quando seu app roda 1 query para carregar uma lista e depois mais 1 query para cada linha dessa lista. Exemplo: carrega 50 usuários (1 query) e depois busca os pedidos de cada usuário um a um (50 queries adicionais).
Isso é comum em ORMs porque “lazy loading” é conveniente. Você itera sobre usuários, acessa user.orders e o ORM faz queries ao banco a cada acesso. Código gerado por IA frequentemente usa essas configurações padrão, então “aplicativo criado por IA está lento” pode significar “a página está disparando centenas de queries pequenas”.
Como identificar rápido
Procure um padrão repetido nos seus logs ou APM: o mesmo formato de SQL várias vezes, apenas o ID mudando. Outro indicador é uma página que fica mais lenta conforme seus dados crescem, mesmo sem mudanças no código.
Se puder, conte queries por uma requisição. Se o número cresce com a quantidade de itens na página (20 itens -> ~21 queries, 100 itens -> ~101 queries), provavelmente é N+1.
Correções rápidas que costumam funcionar
Escolha a menor mudança que elimina fetchs por linha:
- Eager load de relacionamentos (pré-carregar usuários com seus pedidos de uma vez só).
- Buscar em lote por IDs (uma query para todos os pedidos onde user_id IN (...)).
- Usar JOIN quando realmente precisa de campos das duas tabelas.
- Retornar apenas as colunas necessárias (evite carregar blobs grandes).
Valide a correção com dois cheques: a contagem de queries deve cair muito e o tempo da página deve melhorar em um teste repetível (mesmos dados, mesma requisição).
Um aviso: “corrigir” N+1 juntando tudo com JOINs pode piorar. Over-join pode gerar resultados grandes, linhas duplicadas e mais trabalho de memória. Carregue só o que a página mostra e mantenha o resultado pequeno e focado.
Em protótipos gerados por IA, auditorias de código geralmente identificam hotspots N+1 rapidamente, especialmente em listas, dashboards e tabelas administrativas.
Índices ausentes: o ganho mais rápido para muitos apps lentos
Um índice é como o índice no final de um livro. Sem ele, você folheia página por página até achar o que quer. Com ele, você pula direto para a seção. O banco funciona igual: sem índice, muitas vezes precisa ler muitas linhas para encontrar poucos matches.
Os misses mais comuns são chatos — e isso é bom, porque são fáceis de corrigir. Verifique colunas usadas frequentemente em filtros (WHERE), condições de JOIN e ordenações (ORDER BY). Chaves estrangeiras são causas frequentes, especialmente em schemas gerados por IA onde as relações existem no código, mas as constraints (e índices) não foram adicionadas no banco.
Exemplo simples: se seu app carrega pedidos de um usuário com WHERE user_id = ? ORDER BY created_at DESC, normalmente você quer um índice que combine com essa busca. Pode ser um índice em user_id, mas se você filtra e ordena junto, um índice composto como (user_id, created_at) costuma ser muito mais rápido.
Regras práticas ao escolher um índice:
- Indexe colunas que aparecem em
WHERE,JOINouORDER BYem queries quentes. - Prefira índices compostos quando você filtra por várias colunas com frequência.
- Garanta que colunas de foreign key estejam indexadas quando usadas para juntar tabelas.
- Não presuma que o ORM adicionou os índices certos para você.
Uma armadilha comum é indexar colunas de baixa cardinalidade (valores que se repetem bastante), tipo status com poucas opções. Esses índices muitas vezes não ajudam porque o banco ainda precisa tocar boa parte da tabela. Eles também podem deixar writes mais lentos porque cada insert/update atualiza o índice.
Para confirmar que corrigiu, cheque o plano de execução antes e depois. Você quer ver o plano mudando de um scan (lendo muitas linhas) para um lookup por índice (pulando direto para as linhas coincidentes). Se o plano ainda varrer, o índice pode estar na ordem errada de colunas, o filtro não é seletivo o suficiente ou a query está escrita de um jeito que impede o uso do índice.
Varreduras sem limites: quando o banco lê muito mais do que você imagina
Uma varredura sem limites ocorre quando uma query é tão ampla que o banco precisa ler um grande trecho da tabela (às vezes toda) só para devolver um resultado pequeno. Se seu app criado por IA está lento e piora conforme os dados crescem, isso costuma ser a causa.
O padrão comum é simples: a query não restringe a busca cedo, então o banco continua lendo linhas até achar o que precisa. Esse trabalho extra aparece como CPU alta no banco, queries longas e páginas que parecem ok em dev e lentas em produção.
Sinais de alerta
Alguns sinais aparecem em code reviews e logs de queries:
- Query sem WHERE em tabela grande.
- Falta de LIMIT em endpoints que listam registros.
- Paginação por offset (page=2000) em tabela que só cresce.
- Selecionar muitas colunas (ou
SELECT *) quando você precisa de poucas. - Queries de “itens mais recentes” sem filtro por tempo ou categoria.
Se vir isso em um caminho quente como um dashboard ou feed, trate como provável culpado.
Correções rápidas que costumam funcionar
Comece fazendo a query trabalhar menos por requisição. Pequenas mudanças podem reduzir muito a carga:
- Adicione um filtro real (status, user_id, tenant_id, created_at) e garanta que corresponda ao uso da página.
- Troque paginação por offset por paginação por chave (paginação por keyset usando o last_seen id ou timestamp).
- Retorne menos campos (só as colunas que a UI precisa).
- Adote uma janela temporal para tabelas tipo log (últimos 7 dias) e archive dados antigos.
Cuidado com recursos de “buscar em tudo”. Um LIKE '%termo%' em campos de texto grandes normalmente força scans. Se busca importa, use ferramentas de busca de texto que o banco oferece ou restrinja a busca para campos menores e indexados.
Exemplo realista: uma tabela de activity cresce infinitamente. A homepage pede “atividade recente” mas não filtra por conta e usa paginação por offset. Funciona com 5.000 linhas e vira scan com 5 milhões. Adicionar filtro por conta, paginação por keyset e janela de 30 dias costuma transformar uma query de 3s em 50 ms.
Em bases geradas por IA, esse problema é comum porque endpoints de lista são construídos rápido e deixados abertos. A FixMyMess frequentemente encontra algumas varreduras sem limites que respondem por grande parte da lentidão quando usuários reais chegam.
ORMs verborrágicos: muitas queries pequenas somam
Quando um app criado por IA está lento, o banco nem sempre está “fazendo trabalho pesado”. Às vezes está fazendo muito trabalho pequeno, repetidas vezes. Um ORM verborrágico é quando seu código faz muitas chamadas pequenas ao banco em vez de poucas e úteis.
Isso acontece sem perceber porque cada query parece inofensiva. Mas 200 queries “rápidas” podem ser mais lentas que 5 queries bem feitas, especialmente considerando tempo de rede e pooling de conexões.
Como o verbo soa
Você verá padrões como:
- Uma query para carregar uma lista e outra por linha para buscar um detalhe relacionado.
- Lookups por campo (por exemplo, carregar um usuário e depois consultar plano, time e configurações em queries separadas).
- Propriedades computadas ou getters que rodam uma query dentro de um loop.
- “Include everything” que puxa tabelas relacionadas grandes que você nunca mostra.
- Queries repetidas para a mesma linha em uma única requisição (sem cache a nível de requisição).
Uma armadilha comum em código gerado por IA é um método bonito como user.displayName() que faz uma query extra. Chamá-lo 100 vezes em uma página cria 100 round trips.
Correções rápidas que costumam funcionar
Comece fazendo a requisição “carregar os dados em uma passada”. Busque a lista e os dados relacionados juntos, ou faça uma segunda query que cubra todas as linhas (não uma por linha). Depois mantenha o payload pequeno: selecione só as colunas necessárias e evite carregar relações grandes que a página não usa.
Se o mesmo lookup acontece várias vezes na mesma requisição (como “time atual”, “plano atual”, “feature flags”), adicione um cache simples por requisição para bater no banco só uma vez.
Após qualquer mudança, meça novamente. Conte queries por requisição e observe a latência p95, não só a média. Um bom resultado é menos queries, menos round trips e uma queda perceptível no p95.
Em bases herdadas geradas por IA, a FixMyMess costuma identificar hotspots de ORM verborrágico rapidamente porque aparecem como padrões repetidos de queries ligados a um único endpoint.
Um exemplo realista: a página que ficou mais lenta toda semana
Uma história comum em marketplaces gerados por IA é uma página administrativa de Orders que funcionava no lançamento e foi ficando dolorida. Com 200 pedidos carrega em 1s. Um mês depois, com 10.000 pedidos, leva 12–20s e às vezes dá timeout. Nada “grande” mudou, mas o banco agora faz muito mais trabalho.
O que o app faz nessa página:
- Query 1: carrega os últimos 50 pedidos para a tabela (geralmente com filtros como
statuse intervalo de datas). - Depois, para cada linha de pedido, a UI mostra o nome do cliente e uma lista curta de itens.
O problema escondido é N+1. Você tem 1 query para a lista e N queries para clientes e N para itens. Com 50 linhas isso vira 101 queries (ou mais). Cada consulta é rápida isolada, mas o tempo total empilha e o pool de conexões do banco fica ocupado.
Ao mesmo tempo, o filtro fica mais lento por falta de índice. O código filtra por status e ordena/filtra por created_at, mas o banco não tem um índice que ajude nessa combinação. Conforme a tabela cresce, o banco passa a varrer muito mais linhas antes de devolver os 50 mais novos.
Ordem prática de correções mantendo mudanças pequenas:
- Meça primeiro: capture contagem total de queries e tempo total de BD para um carregamento de página.
- Corrija o índice: adicione um índice composto que combine filtro e ordenação (por exemplo,
status+created_at). Rerun o teste. - Corrija N+1: busque clientes de uma vez e items de uma vez (ou use eager-load/include). Rerun o teste.
- Adicione limites e guardrails de paginação (tamanho máximo de página). Rerun o teste.
O que você costuma ver a cada etapa: a mudança de índice reduz a query principal de segundos para dezenas de ms, mas a página pode continuar lenta. Remover N+1 costuma reduzir a contagem de queries de ~100 para menos de 10 e o carregamento vira previsível.
Mantenha restrições em mente: mude uma coisa por vez, rode a mesma requisição cada vez e verifique resultados com volume de dados “real”. Em código gerado por IA é fácil “corrigir” performance mudando comportamento acidentalmente, então passos pequenos e checagens rápidas importam. Se a base está bagunçada (calls de ORM espalhadas pelo view code), times geralmente usam uma auditoria curta para mapear os caminhos de queries antes de grandes refactors.
Erros comuns que fazem perder tempo (e às vezes pioram)
Quando um app criado por IA está lento, é tentador pegar a solução que parece mais rápida. O problema é que remendos rápidos frequentemente escondem a causa real, então a lentidão volta (ou migra).
Correções que parecem boas, mas costumam voltar
Um erro comum é adicionar cache antes de provar que a query é saudável. Se uma página precisa de 2s porque roda 120 queries, cachear a resposta pode mascarar o problema; quando o cache expira (ou os dados mudam), o pico volta.
Outro desperdício é adicionar índices às cegas. Um índice pode ser um grande ganho, mas o índice errado pode não ajudar, ou pode piorar writes e aumentar armazenamento. Sempre confirme o que o banco está realmente fazendo e escolha o menor índice que ajude o filtro/ordem real.
Over-fetching está por toda parte em código gerado por IA. Se a UI precisa de nome e plano do usuário, mas o ORM retorna o registro inteiro com relações aninhadas, você paga por leituras extras, memória e transferência de rede em cada requisição.
Por fim, a abordagem de “uma mega-query” pode ser ruim também. Juntar tudo em um JOIN para evitar N+1 pode resultar numa query difícil de alterar, depurar e ainda lenta por retornar muitas linhas.
Pular a verificação é o maior erro
Se você não monta uma baseline, não sabe se melhorou algo. Antes de mudar código, capture um snapshot antes/depois simples: tempo da requisição, número de queries e a query mais lenta.
Cinco sinais de que você está chutando em vez de consertar:
- Sem métricas de baseline (só “parece mais rápido”).
- Sem checagem do plano de execução antes de indexar.
- Sem limite ou paginação em endpoints que crescem com o tempo.
- Sem teste de regressão para o caminho lento (para não voltar na semana seguinte).
- Sem rechecagem em volume de produção.
Na FixMyMess, vemos times gastar dias em cache e refactors e descobrir que o problema real era um índice ausente mais uma chamada de ORM dentro de um loop. O caminho mais rápido é entediante: medir, verificar, mudar uma coisa, medir de novo.
Próximos passos: consolidar ganhos e pedir ajuda se precisar
Depois de achar os grandes problemas de query, não pare na primeira correção que fez o gráfico cair. Performance tende a voltar conforme features são lançadas e dados crescem. Trate suas primeiras vitórias como nova baseline a proteger.
Comece anotando uma pequena “lista de alvos” para revisar semanalmente. Mantenha concreta, não teórica.
- Top 3 ações lentas dos usuários (ex.: login, busca, checkout) e suas queries piores.
- Para cada ação: tempo médio, p95 e contagem de queries.
- Os padrões exatos de query que causam isso (N+1, índice ausente, varredura sem limites, muitas chamadas pequenas do ORM).
Depois, adicione alguns guardrails para pegar regressões cedo. São baratos e compensam rápido.
- Defina um orçamento de contagem de queries por requisição para páginas-chave.
- Ative alertas de queries lentas no banco e revise regularmente.
- Rode um teste básico de carga nas top 3 ações após cada release.
- Adicione uma checagem de performance no code review ("adicionamos queries?").
- Mantenha um lugar compartilhado com helpers de query aprovados para que todos usem os mesmos padrões.
Em seguida, planeje refactors que removam a causa raiz. Comuns: limpar padrões de acesso do ORM (buscar relacionados de uma vez, não em loops), padronizar paginação e criar helpers de query compartilhados para evitar repetir o mesmo erro em vários endpoints.
Se seu app gerado por IA está lento e foi feito com ferramentas como Lovable, Bolt, v0, Cursor ou Replit, considere um diagnóstico profissional. Essas bases costumam esconder lógica repetida de queries em muitos arquivos, então você conserta um endpoint e três continuam te prejudicando.
A FixMyMess é uma opção se quiser um próximo passo claro: podemos rodar uma auditoria gratuita para identificar problemas no banco e na aplicação e então aplicar correções direcionadas com verificação humana para que os ganhos permaneçam.