O problema
Trabalhei recentemente em uma solução para um cenário que muita gente que
mexe com sistemas em escala já enfrentou: um sistema gera diariamente
arquivos JSON que vão para um storage ou qualquer outro banco como histórico, seguindo um padrão de nomenclatura previsível. Com o tempo, esses arquivos se acumulam aos milhões.
A dor apareceu quando surgiu a necessidade de consultar esse histórico.
Três problemas surgiram ao mesmo tempo
Parte da informação que identifica cada arquivo nem sempre estava preenchida no nome, porque é um dado mutável e nem sempre estava disponível no momento da geração.
As pessoas precisavam buscar por critérios variados, alguns estruturados, outros por palavra-chave dentro do conteúdo, e nada disso estava exposto no nome do arquivo
Eram milhões de arquivos espalhados em containers diferentes, varrer linearmente era inviável.
Em resumo: tínhamos os dados, mas eram efetivamente invisíveis.
A solução
Construí um pipeline em .NET 9 que varre o storage e indexa todo o conteúdo em um cluster Elasticsearch (rodando em Kubernetes via ECK
Operator), com mapping otimizado para texto em português (analyzer com
stemmer, asciifolding e stopwords em PT-BR). O resultado é busca textual
em milhões de documentos em tempo real, mesmo quando o termo aparece
apenas no meio de uma seção específica do conteúdo.
Os pontos de design que fizeram diferença
Pipeline producer/consumer com System.Threading.Channels
Producer lista arquivos paginado, workers em paralelo baixam e parseiam em streaming, e bulk indexer envia batches pro Elastic. Channels bounded controlam pressão de memória.
Parser tolerante a dados sujos
Os arquivos históricos vinham com toda sorte de inconsistência: HTML entities, campos ausentes, datas inválidas, números como string, ordem de seções variável. Implementei discriminated union ParseResult (Success/Skipped/Failed) com helpers TryGet defensivos.
Política: indexa o que conseguiu, descarta apenas o irrecuperável, nunca derruba o pipeline por um arquivo ruim.
Busca híbrida em duas camadas
Quando o termo digitado tem um padrão estruturado, a API consulta o próprio storage (prefix lookup nativo, extremamente rápido) e o Elastic em paralelo. Resultados unidos: hits do storage no topo, hits textuais abaixo. O melhor de dois mundos sem latência adicional.
Boosts diferenciados por campo no Elastic
Match em campos estruturados tem boost maior. Match em texto solto tem boost menor. Resultado: busca por uma palavra específica sobe primeiro quem tem essa palavra em campo identificador, e só depois quem mencionou em texto livre.
Estratégia de alias para reindexação sem downtime
Cada carga gera um índice físico novo (com timestamp no nome) e o alias é trocado atomicamente no final. Permite reprocessar o histórico inteiro sem afetar produção.
Checkpointing stateless
Cada partição tem seu progresso salvo em storage externo. Se o pod morrer no meio da carga, retoma exatamente de onde parou. Sem volumes persistentes, sem estado local.
Highlights com fragmentos contextuais
Quando a busca casa em texto livre, o Elastic retorna trechos do conteúdo com a palavra encontrada destacada. A interface mostra o contexto direto na lista de resultados, sem precisar abrir cada item para descobrir por que ele apareceu.
🤖 O papel da IA nesse processo
Não vou fingir que cheguei nessa arquitetura sozinho em uma tarde. Usei
LLM como par técnico ao longo de todo o caminho, e a forma como usei
fez toda a diferença no resultado.
Onde a IA ajudou de verdade
Brainstorming de trade-offs arquiteturais
"Mensageria entre processos faz sentido aqui ou é overhead?", "Cursor pagination ou from/size pra infinite scroll?", "Alias com swap ou reindex in-place?".
Em vez de só pedir resposta, debati cada decisão e pesei prós e contras. A IA é excelente como sparring de design.
Análise crítica do código existente
O projeto inicial tinha mais peças do que precisava. Pedi uma análise técnica completa, code smells, dead code, bugs latentes, aderência a boas práticas. Saiu um documento de diagnóstico priorizado por severidade. Decidi simplificar a arquitetura com base nesse diagnóstico.
Modelagem do parser tolerante
Os cenários de dados sujos foram enumerados de forma exaustiva e cada um virou um caso de teste antes da implementação. Test-driven, mas com IA gerando o checklist.
Mapping do Elasticsearch
Definir analyzer correto pra português, decidir entre tipos de campo, projetar nested vs flat, é o tipo de decisão onde experiência prévia conta muito. Conversar com a IA acelerou em horas o que levaria dias de leitura de docs.
Prompts estruturados para agente de codificação
Quebrei a implementação em fases, cada uma com prompt detalhado contendo arquitetura alvo, decisões já tomadas, restrições, e ordem de execução. Resultado: o agente executou sem perder contexto e sem inventar.
Onde a IA NÃO substituiu o engenheiro
- Decisões de negócio (volumes reais, padrões de acesso, regras de tratamento de exceções específicas do domínio)
- Validar suposições contra a realidade do projeto (rodar build, ler logs, executar queries pra verificar comportamento)
- Conhecer a infra real (capacidade dos volumes, classes de storage disponíveis, limites do cluster)
- Priorizar segurança (a IA aponta riscos, mas a decisão de parar tudo pra resolver é humana)
A lição mais importante
IA é multiplicador, não substituto. Quem souber fazer perguntas certas, validar respostas e contextualizar com a realidade do projeto extrai muito valor. Quem só pede "me dá a solução" recebe código genérico que não resolve o problema real.
Stack utilizada
- .NET 9 (Worker Service + ASP.NET Core)
- Azure Blob Storage (origem dos dados)
- Elasticsearch 8.x em Kubernetes via ECK Operator
- Azure Table Storage (checkpoint stateless)
- SDK oficial do Elastic (tipado)
- System.Threading.Channels (pipeline assíncrono in-process)
- Claude (sparring técnico e análise de código)
- Claude Code (execução guiada por prompts estruturados)
Lições que valeram o post
Dados reais são sujos. Investi mais tempo projetando tolerância a inconsistências do que no pipeline em si. E foi o tempo mais bem gasto, em produção, um único arquivo malformado não pode derrubar uma carga em larga escala.
Mensageria nem sempre é a resposta. Para uma carga histórica única + ingestão futura via eventos do próprio storage, removi a camada de filas intermediária e simplifiquei para um indexer mais direto. Menos infra, menos custo, mais simples de testar.
Storage tem capacidades subutilizadas. O prefix lookup nativo do storage é absurdamente rápido e barato. Combinar isso com Elastic para o resto deu uma estratégia híbrida que nenhum dos dois sozinho daria.
Reindexar sem downtime é decisão arquitetural, não otimização. Resolver isso com alias desde o dia 1 me poupou de um problema futuro que apareceria do nada.
IA não pensa por você, mas amplifica quem pensa. O diferencial está em fazer as perguntas certas, contextualizar com seu projeto e validar tudo na prática.
United States
NORTH AMERICA
Related News
What Does "Building in Public" Actually Mean in 2026?
20h ago
The Agentic Headless Backend: What Vibe Coders Still Need After the UI Is Done
20h ago
Why I’m Still Learning to Code Even With AI
22h ago
Students Boo Commencement Speaker After She Calls AI the 'Next Industrial Revolution'
5h ago

Testing for ‘Bad Cholesterol’ Doesn’t Tell the Whole Story
5h ago