Trabalho num ambiente onde, só de deploy, sobem mais de 5 mil por dia — em um dia calmo. Multiplica isso por milhares de desenvolvedores, dezenas de squads, e você tem uma escala onde nenhuma política interna sobrevive só no papel. Não tem revisor humano, comitê ou processo manual que dê conta de garantir que cada uma dessas entregas está indo pro ar com resiliência, segurança, confiabilidade e custo sob controle.
E o problema não é falta de política. A gente tinha (e tem) documentação boa, guardrails bem pensados, critérios claros do que é aceitável ou não em produção. O problema é que ninguém lê documentação no meio de uma sprint. O dev abre o Terraform, precisa subir o recurso até ontem, e a política — por melhor que seja — está em alguma página de documentação interna, algum PDF, algum canal do Teams que ele não tem tempo de vasculhar.
Foi essa distância entre "a política existe" e "a política é seguida" que me fez sentar e pensar diferente: e se, em vez de pedir pro desenvolvedor ir atrás da regra, a regra viesse até ele, exatamente no lugar onde ele já está trabalhando — o editor de código — no exato momento da decisão?
Foi daí que nasceu esse projeto, hoje em uso por milhares de devs no dia a dia. Não foi um plano de arquitetura perfeito desenhado num quadro branco de uma vez só — foi resolvendo um problema de cada vez até a coisa toda fazer sentido, e até virar algo que tira do desenvolvedor a preocupação com infra e devolve o foco dele pra regra de negócio da aplicação, que é o que realmente importa. Vou te contar como ficou, e vou explicar cada peça no caminho, porque sei que nem todo mundo que lê isso vive de AWS o dia inteiro.
Primeiro, o que raios é um MCP
Antes de entrar na arquitetura, um parênteses rápido pra quem não é do mundo de IA aplicada.
MCP significa Model Context Protocol. Na prática, é um jeito padronizado de dar "mãos e olhos" pra um modelo de IA — tipo o Claude que roda dentro do Copilot. Sozinho, o modelo só sabe o que aprendeu no treinamento. Com um MCP conectado, ele passa a poder fazer coisas de verdade: buscar um documento, consultar um banco de dados, chamar uma API.
Pensa assim: o modelo é o cérebro, e cada MCP é uma ferramenta que você coloca na mão dele. Uma chave de fenda pra abrir parafuso, um multímetro pra medir voltagem. O modelo decide qual ferramenta usar dependendo do que você pediu.
No meu caso, dei três ferramentas pro Claude (rodando dentro do Copilot, no VSCode de cada dev do time).
A arquitetura, sem economizar nos porquês
Vou desenhar o caminho que uma pergunta percorre, do VSCode até a resposta:
VSCode (Copilot usando Claude)
│
▼
Route 53 → o "sistema de nomes" que traduz um endereço tipo mcp.empresa.com pra um IP
│
▼
AWS Network Firewall → o segurança na porta, filtrando quem pode passar
│
▼
API Gateway → a recepção que recebe o pedido, confere credenciais e direciona
│
▼
Network Load Balancer → distribui a carga na camada de rede (rápido e "burro" de propósito)
│
▼
Application Load Balancer → distribui a carga entendendo o conteúdo da requisição
│
▼
Lambda → onde o código dos MCPs realmente roda
Parece exagero ter tanta coisa entre "o dev digitou uma pergunta" e "o código rodou", eu sei. Mas cada camada resolve um problema real de empresa grande:
- O Route 53 existe porque ninguém quer decorar IP, e porque dá pra trocar o que está por trás do domínio sem avisar ninguém.
- O Network Firewall é a garantia de que só tráfego que deveria estar ali, está ali — pense numa portaria de prédio que checa a lista de moradores antes de deixar alguém subir.
- O API Gateway é quem recebe a chamada de fato e já checa duas coisas antes de deixar passar: um Bearer Token (prova de que aquele usuário está autenticado, validado contra o Active Directory da empresa) e uma API Key (prova de que aquela aplicação — no caso, a extensão do Copilot — tem permissão de usar esse endpoint). É a diferença entre "eu sei quem é você" e "eu sei que o app que você está usando tem crachá".
- O NLB na frente do ALB é um detalhe mais de bastidor de rede: o NLB segura conexão de forma bem leve e estável (bom pra atravessar fronteiras entre redes internas), e passa a bola pro ALB, que aí sim entende o conteúdo da requisição e decide pra qual Lambda mandar.
- A Lambda, por fim, é onde o código roda de verdade — sem servidor fixo ligado 24h, só acorda quando alguém chama.
Sei que é bastante sigla. Mas se você tirar uma coisa desse bloco: essa cadeia inteira existe pra garantir duas coisas ao mesmo tempo — que só gente autorizada chegue até a resposta, e que a resposta chegue rápido mesmo vindo de dentro de uma estrutura de rede corporativa cheia de camadas de segurança.
As três ferramentas que dei pro modelo
Ferramenta 1 — a bibliotecária das políticas internas
Essa é a que eu mais gosto de explicar, porque resolve um problema bem humano: conhecimento que existe, mas está perdido em algum lugar que ninguém consulta.
Construí uma API própria que serve como porta de entrada pra uma Knowledge Base — no fundo, uma "biblioteca inteligente" onde ficam guardadas as especificações técnicas das nossas políticas de guardrails de débito técnico: o que conta como débito, como cada tipo é classificado, o que é aceitável e o que não é.
Pra essa biblioteca "entender" o que está escrito nos documentos (e não só guardar texto puro), cada documento passa por um processo chamado embedding — uma forma de transformar texto em números que representam o significado daquele trecho, não só as palavras exatas. Usei um modelo de embeddings da AWS via Bedrock pra fazer essa tradução, e o OpenSearch guarda esses números de um jeito que permite buscar por semelhança de significado, não só por palavra-chave.
Na prática: quando o Copilot precisa saber "o que diz nossa política sobre expor um bucket sem criptografia", ele não está perguntando pro conhecimento geral do modelo (que nunca viu nosso documento interno) — ele está indo direto na fonte, buscando o trecho certo, e respondendo com base nele. Isso tem um nome técnico, RAG (Retrieval-Augmented Generation), mas o conceito é simples: em vez do modelo "chutar" com o que sabe, ele primeiro busca a resposta certa e depois explica com as próprias palavras.
Ferramenta 2 — o raio-x da squad
Essa é mais direta. Toda squad acumula débito técnico, isso é normal — o problema é não saber quanto, nem quão grave.
Montei uma tabela no DynamoDB (um banco de dados que guarda informação em formato de "fichas", rápido de consultar) com a quantidade de débitos técnicos abertos por squad e a severidade de cada um. O segundo MCP simplesmente consulta essa tabela.
Parece pouco, mas muda o jogo: agora, quando o modelo está analisando algo pra uma squad específica, ele sabe se está falando com um time que já está afogado em débito crítico ou com um time relativamente tranquilo — e isso muda o tom e a prioridade da recomendação.
Ferramenta 3 — o roteiro da revisão
A terceira ferramenta não busca dado nenhum. Ela devolve um prompt — um conjunto de instruções bem pensadas — dizendo pro modelo exatamente como ele deve olhar pro código Terraform aberto no VSCode e o que checar.
É quase um "manual do bom revisor" que eu escrevi uma vez e que agora é reaproveitado toda vez que alguém pede uma revisão. O modelo lê esse roteiro e, seguindo ele, decide sozinho se precisa chamar as outras duas ferramentas pra completar a análise.
Como as três se encontram
Do ponto de vista de quem programa, nada disso aparece. A pessoa só abre o VSCode, com Copilot configurado pra usar o Claude Sonnet 4.6 na maior parte das tarefas, e pede algo tipo "revisa esse módulo aí pra mim".
Nos bastidores, mais ou menos isso acontece:
- O modelo pega a ferramenta 3 (o roteiro) pra saber como conduzir a análise.
- Seguindo o roteiro, ele chama a ferramenta 1 pra confirmar o que a política realmente diz sobre o que está vendo no código.
- Se fizer sentido, ele chama a ferramenta 2 pra saber se aquela squad já está no limite de débito — o que pode mudar a urgência da recomendação.
- Devolve pro dev uma sugestão de correção que não é "achismo de IA", é rastreável até um documento real da empresa.
É essa rastreabilidade que muda tudo. Não é o modelo dizendo "eu acho que isso não é uma boa prática" — é o modelo dizendo "a política X, seção Y, diz que isso não pode, e a squad de vocês já tem 12 débitos críticos abertos, então talvez seja hora de resolver".
O que doeu construir isso
Não foi tudo tranquilo, e acho importante contar a parte chata também:
- A cadeia de rede cobra seu preço em latência. Cinco saltos até a Lambda acordar, e às vezes o modelo ainda chama duas ou três ferramentas em sequência pra montar uma resposta. Tive que ficar de olho em timeout com bastante atenção.
- Cold start incomodou mais do que eu esperava. Lambda que fica muito tempo sem ser chamada demora um pouco a "acordar". A solução acabou sendo mais simples do que eu imaginava: o ALB já faz health check periódico nos targets pra saber se estão saudáveis, então usei esse próprio mecanismo a meu favor — configurei o health check pra bater na Lambda com uma frequência que a mantém sempre "quente", sem precisar pagar por concorrência provisionada.
- A biblioteca só é boa se alguém cuida dela. Um RAG com documento desatualizado é pior que não ter RAG nenhum, porque ele responde com uma confiança que não corresponde à realidade. Isso virou rotina: alguém do time precisa revisar os documentos-fonte periodicamente.
- Segurança dá trabalho de verdade. Ter token e API key não resolve sozinho — teve que entrar rotina de rotação de chave e revisão de quem ainda tem acesso, principalmente quando alguém troca de squad ou sai da empresa.
Quando a resposta simplesmente não cabia
Teve um problema que só apareceu quando o uso começou a crescer de verdade, e que vale a pena contar porque é o tipo de coisa que ninguém avisa em tutorial.
O MCP que consulta o status de débito técnico das squads começou a falhar. Não sempre — só quando a consulta trazia muita coisa de volta. Fui investigar e esbarrei em dois limites técnicos que eu conhecia de leve, mas nunca tinha sentido na pele: a Lambda tem um teto de 6MB pra resposta síncrona, e o Application Load Balancer, na frente dela, corta em 1MB. Ou seja: mesmo que minha Lambda conseguisse montar uma resposta gigante sem erro nenhum, o ALB simplesmente derrubava ela no meio do caminho antes de chegar no usuário.
E era exatamente isso que estava acontecendo: squads com muito débito acumulado geravam uma resposta grande demais, o pedido morria no meio da cadeia, e o dev só via um erro genérico sem entender por quê.
A solução envolveu duas mudanças, uma no banco e outra na API do MCP.
No banco, usei um GSI. Pra quem não é do mundo de banco de dados: no DynamoDB, cada tabela normalmente é otimizada pra ser consultada de um jeito só — geralmente por uma chave principal. Se você precisa buscar os mesmos dados por um critério diferente (por exemplo, "todos os débitos de uma squad, ordenados por severidade" em vez de "um débito específico pelo ID"), consultar direto na tabela principal fica lento ou simplesmente não é possível de forma eficiente. Um GSI (Global Secondary Index) é, na prática, uma segunda forma de indexar os mesmos dados, otimizada pra outro padrão de busca — como se você tivesse duas fichas catalográficas diferentes pro mesmo acervo de uma biblioteca, uma organizada por autor e outra por assunto. Criei um GSI organizado de um jeito que permite trazer os débitos de uma squad já ordenados por severidade, de forma rápida, sem precisar varrer a tabela inteira.
Na API do MCP, entrou paginação com limite. Em vez de trazer tudo de uma vez, o MCP agora devolve, por padrão, uma versão resumida — os primeiros resultados, dentro de um limite de tamanho seguro, bem abaixo do teto do ALB. Se o usuário (ou o próprio modelo, entendendo que precisa de mais contexto) quiser ver mais, ele pede a próxima página, e assim por diante. É o mesmo princípio de qualquer API paginada que você já usou sem perceber, tipo resultado de busca que vem "10 por página" em vez de devolver mil itens de uma vez.
O efeito colateral bom disso foi inesperado: além de resolver o erro, a resposta resumida ficou mais rápida e mais fácil do modelo processar. Na maioria dos casos, o resumo inicial já é suficiente pro dev entender a situação da squad — e só quando ele realmente precisa se aprofundar é que a paginação entra em ação. Às vezes o limite técnico te empurra pra uma solução que, no fim, é melhor do que a ideia original.
Pra fechar
Não vou fingir que foi simples. Foram horas estudando e pensando em como resolver cada um dos problemas que foram aparecendo pelo caminho, teve tentativa que não funcionou, teve momento de olhar pra arquitetura toda e achar que estava complexa demais pro problema que eu queria resolver. Construir isso deu trabalho de verdade — mas foi o tipo de trabalho que compensa quando você vê o resultado rodando na escala que roda hoje.
E tem um detalhe que eu acho bonito nesse projeto, e que talvez seja o motivo de eu ter topado escrever sobre ele: o custo de sustentar essa solução na AWS é baixo. Lambda, API Gateway, DynamoDB, OpenSearch — tudo isso é barato perto do que costuma vir à cabeça quando alguém ouve "infraestrutura de IA". E a razão é simples: quem faz o trabalho pesado, quem realmente "pensa", é a LLM que já está rodando do lado do usuário, dentro do Copilot. Eu não precisei subir nem manter nenhum modelo, não precisei de GPU, não precisei de nada disso. Minha parte foi só dar contexto certo, na hora certa, pro modelo que o próprio usuário já paga e já usa.
É essa a maior lição que fica pra mim: não precisa de um projeto gigante de IA generativa, com time dedicado e orçamento de milhões, pra criar algo que muda o dia a dia de quem programa. Às vezes basta pegar um problema real, entender exatamente onde ele dói, e conectar um pouco de engenharia com um pouco de inteligência artificial no lugar certo. Dá pra sair de "mais uma ideia de IA que não sai do papel" pra uma ferramenta que milhares de pessoas usam todo dia sem nem perceber a complexidade que tem por trás.
:wq!
Top comments (0)