<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: He4rt Developers</title>
    <description>The latest articles on DEV Community by He4rt Developers (he4rt).</description>
    <link>https://dev.to/he4rt</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F5490%2Fef471ad5-dbd9-40be-9951-743a6026d59c.png</url>
      <title>DEV Community: He4rt Developers</title>
      <link>https://dev.to/he4rt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/he4rt"/>
    <language>en</language>
    <item>
      <title>Do localhost para o mundo</title>
      <dc:creator>Bruno Freschi</dc:creator>
      <pubDate>Mon, 22 Jun 2026 15:07:19 +0000</pubDate>
      <link>https://dev.to/he4rt/do-localhost-para-o-mundo-2o44</link>
      <guid>https://dev.to/he4rt/do-localhost-para-o-mundo-2o44</guid>
      <description>&lt;p&gt;Por muito tempo eu acreditei que programação e desenvolvimento de software como sinônimos. Na prática, descobri que são habilidades diferentes.&lt;/p&gt;

&lt;p&gt;Existe uma diferença entre &lt;strong&gt;aprender a programar&lt;/strong&gt; e &lt;strong&gt;aprender a trabalhar como desenvolvedor&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Os Exercícios Isolados
&lt;/h2&gt;

&lt;p&gt;Quando você está aprendendo a programar, o cenário é controlado. Você trabalha em exercícios com começo, meio e fim:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O problema é perfeitamente definido.&lt;/li&gt;
&lt;li&gt;O código é criado do zero.&lt;/li&gt;
&lt;li&gt;Você conhece e domina todas as peças envolvidas.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Muitos concluem cursos, criam projetos pessoais e, ainda assim, não se sentem prontos para uma vaga.&lt;/p&gt;

&lt;p&gt;Então, o primeiro emprego chega e, com ele, o choque:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Eu consigo construir um CRUD, mas não consigo entender o projeto da empresa. Por que não consigo contribuir?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Isso não significa falta de capacidade. Significa apenas que você treinou programação, o que é totalmente natural, e agora vai começar a desenvolver software em um ambiente real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Do localhost para o mundo
&lt;/h2&gt;

&lt;p&gt;No primeiro emprego, o cenário muda drasticamente. Você não recebe uma tela em branco; recebe acesso a uma aplicação com milhares de arquivos, dezenas de bibliotecas, regras de negócio acumuladas por anos, débitos técnicos que nunca foram pagos e decisões tomadas por pessoas que nem trabalham mais lá.&lt;/p&gt;

&lt;p&gt;O desafio deixa de ser &lt;em&gt;"como criar uma solução"&lt;/em&gt; e passa a ser &lt;strong&gt;"como entender uma solução que já existe"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A maior dificuldade do mercado não é a sintaxe da linguagem ou escrever código, mas sim &lt;strong&gt;compreender o contexto&lt;/strong&gt;. Seus primeiros meses não serão consumidos por algoritmos complexos (pelo menos não nesse primeiro momento), mas por perguntas como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Como executar o projeto?&lt;/li&gt;
&lt;li&gt;Onde está a regra de negócio?&lt;/li&gt;
&lt;li&gt;Quem consome esta API?&lt;/li&gt;
&lt;li&gt;Posso alterar isso sem quebrar outra funcionalidade?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O gargalo não é a sintaxe. É o ecossistema.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que as trilhas tradicionais não ensinam
&lt;/h2&gt;

&lt;p&gt;Grande parte dos cursos foca no básico e salta diretamente para conceitos como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;POO&lt;/li&gt;
&lt;li&gt;SOLID&lt;/li&gt;
&lt;li&gt;Design Patterns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tudo isso é importante. Mas quase ninguém ensina habilidades como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Ler código.&lt;/li&gt;
&lt;li&gt;Navegar em bases.&lt;/li&gt;
&lt;li&gt;Investigar bugs, depurar aplicações.&lt;/li&gt;
&lt;li&gt;Trabalhar com Git.&lt;/li&gt;
&lt;li&gt;Participar de &lt;em&gt;code reviews&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Entender requisitos incompletos ou ambíguos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;No trabalho, o foco muda de &lt;em&gt;"Como eu resolvo este problema?"&lt;/em&gt; para &lt;strong&gt;"Como eu resolvo este problema sem quebrar o restante do sistema?"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Essa mudança transforma completamente a forma de pensar. O desenvolvedor deixa de otimizar apenas a solução e passa a considerar manutenção, impacto, riscos, dependências e custo de mudança.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sobrevivendo aos Primeiros Meses
&lt;/h2&gt;

&lt;p&gt;Para tentar mudar essa realidade, precisamos de uma base mais próxima do dia a dia profissional. Uma abordagem que foque menos em exercícios isolados e mais em entrar em projetos existentes, corrigir bugs, ler mais código do que escrever e lidar com a regra do negócio.&lt;/p&gt;

&lt;p&gt;Porque o que determina o seu sucesso no primeiro emprego raramente é saber um &lt;em&gt;Design Pattern&lt;/em&gt; de cor. É a capacidade de entrar em uma base de código desconhecida e, gradualmente, se tornar alguém capaz de fazer mudanças com segurança e gerar valor para o time.&lt;/p&gt;

&lt;p&gt;Para tentar aproximar esse cenário de uma forma mais prática, pensei em preparar uma série chamada &lt;strong&gt;"Do localhost para o mundo"&lt;/strong&gt;. Junto com ela, pretendo criar um repositório para simular alguns problemas de uma aplicação em produção e expor desafios semelhantes.&lt;/p&gt;




&lt;h3&gt;
  
  
  O que vem por aí:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Você recebeu acesso ao projeto. E agora?:&lt;/strong&gt; &lt;em&gt;(O primeiro dia, clonando e rodando o ambiente).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Git:&lt;/strong&gt; &lt;em&gt;Branches, merges, rebases e pull requests no fluxo real.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Como ler código que você não escreveu:&lt;/strong&gt; &lt;em&gt;(Identificando o entrypoint, seguindo o fluxo e usando o debugger).&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Encontrando sua primeira tarefa:&lt;/strong&gt; &lt;em&gt;(Entendendo o card, reproduzindo o bug e estimando o impacto).&lt;/em&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Prepare o seu café. &lt;br&gt;
Nos vemos no próximo artigo!&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>career</category>
      <category>programming</category>
      <category>softwaredevelopment</category>
    </item>
    <item>
      <title>De 6 horas para 40 segundos: como um índice de banco de dados salvou um job crítico de produção</title>
      <dc:creator>Fernando Andrade</dc:creator>
      <pubDate>Sat, 06 Jun 2026 20:19:13 +0000</pubDate>
      <link>https://dev.to/he4rt/de-6-horas-para-40-segundos-como-um-indice-de-banco-de-dados-salvou-um-job-critico-de-producao-c90</link>
      <guid>https://dev.to/he4rt/de-6-horas-para-40-segundos-como-um-indice-de-banco-de-dados-salvou-um-job-critico-de-producao-c90</guid>
      <description>&lt;h1&gt;
  
  
  De 6 horas para 40 segundos: como um índice de banco de dados salvou um job crítico de produção
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Às vezes, a solução mais elegante não está no código está em ensinar o banco de dados a encontrar o que ele já tem."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Índice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;O cenário: um job noturno que virou um problema diurno&lt;/li&gt;
&lt;li&gt;O problema cresceu junto com a tabela&lt;/li&gt;
&lt;li&gt;O diagnóstico: o Azure Application Insights apontou o caminho&lt;/li&gt;
&lt;li&gt;A solução: um índice bem posicionado&lt;/li&gt;
&lt;li&gt;O impacto real: economia de tempo total por dia&lt;/li&gt;
&lt;li&gt;Mas afinal: o que é um índice de banco de dados?&lt;/li&gt;
&lt;li&gt;Como identificar quando você precisa de um índice&lt;/li&gt;
&lt;li&gt;Anatomia do índice que resolveu o problema&lt;/li&gt;
&lt;li&gt;Conclusão&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O cenário: um job noturno que virou um problema diurno
&lt;/h2&gt;

&lt;p&gt;Todo sistema que lida com monitoramento contínuo eventualmente enfrenta o mesmo desafio: &lt;strong&gt;quanto mais dados acumulam, mais lenta fica a análise&lt;/strong&gt;. Foi exatamente isso que aconteceu em um projeto no qual trabalhei.&lt;/p&gt;

&lt;p&gt;A arquitetura era simples na teoria: um job agendado rodava durante a madrugada, disparando &lt;strong&gt;N processos paralelos&lt;/strong&gt; cada um cadastrado individualmente pelo cliente. A lógica de cada processo era fazer uma comparação entre o resultado do dia atual (&lt;strong&gt;D+0&lt;/strong&gt;) com o resultado do dia anterior (&lt;strong&gt;D-1&lt;/strong&gt;), algo como:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"O que mudou desde ontem?"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Para isso, cada processo precisava buscar &lt;strong&gt;seu último resultado registrado&lt;/strong&gt;, usando uma query com &lt;code&gt;ORDER BY last_execution DESC&lt;/code&gt; filtrada pelo identificador do processo. Parece trivial. E durante um bom tempo, foi.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema cresceu junto com a tabela
&lt;/h2&gt;

&lt;p&gt;Com o tempo, a tabela de resultados foi crescendo naturalmente, afinal, cada processo registra um novo resultado a cada execução. O que antes demorava milissegundos começou a demorar segundos. Depois, dezenas de segundos. Até que um dia percebemos:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A janela de execução do job, que deveria ser de até 4 horas, estava chegando a 12 horas.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Isso significava que um job que deveria terminar antes do horário comercial estava ainda em execução quando os usuários começavam a trabalhar de manhã, gerando inconsistências, bloqueios e reclamações.&lt;/p&gt;

&lt;p&gt;A pergunta era: &lt;strong&gt;onde estava o gargalo?&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  O diagnóstico: o Azure Application Insights apontou o caminho
&lt;/h2&gt;

&lt;p&gt;Ao analisar as métricas de performance no &lt;strong&gt;Azure Application Insights&lt;/strong&gt;, ficou evidente que o problema estava concentrado em uma única operação: a query que buscava o último resultado de cada processo.&lt;/p&gt;

&lt;p&gt;Internamente, a tabela de resultados tinha crescido o suficiente para que um &lt;code&gt;ORDER BY last_execution DESC&lt;/code&gt; &lt;strong&gt;sem suporte de índice&lt;/strong&gt; forçasse o banco a fazer um &lt;strong&gt;full scan&lt;/strong&gt;, ou seja, varrer linha por linha até encontrar o registro mais recente. Multiplique isso por dezenas (ou centenas) de processos rodando em paralelo e você tem uma receita para o caos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Antes da correção
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 linha(s) recuperada(s) — 1.754s, em 2025-08-11 às 09:19:01
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Quase &lt;strong&gt;2 segundos por consulta&lt;/strong&gt;. Para um único processo, tolerável. Para N processos simultâneos, catastrófico.&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução: um índice bem posicionado
&lt;/h2&gt;

&lt;p&gt;A correção foi aplicar um índice composto na tabela de resultados, cobrindo exatamente os campos usados na query crítica:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_job_result_process_date&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;app_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_results&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fk_process_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_created&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;INCLUDE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;final_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;report_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse índice foi criado diretamente no ambiente de produção (pode ser gerado localmente também, dependendo da política da equipe) e o resultado foi imediato.&lt;/p&gt;

&lt;h3&gt;
  
  
  Depois da correção
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;1 linha(s) recuperada(s) — 0.003s, em 2025-08-11 às 09:32:00
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;De &lt;strong&gt;1,754 segundos&lt;/strong&gt; para &lt;strong&gt;0,003 segundos&lt;/strong&gt; por consulta. Uma redução de &lt;strong&gt;99,8%&lt;/strong&gt; no tempo de resposta.&lt;/p&gt;

&lt;h2&gt;
  
  
  O impacto real: economia de tempo total por dia
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Cenário&lt;/th&gt;
&lt;th&gt;Tempo acumulado de processamento/dia&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;❌ Antes do índice&lt;/td&gt;
&lt;td&gt;~6 horas e 18 minutos&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;✅ Depois do índice&lt;/td&gt;
&lt;td&gt;~40 segundos&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;O job voltou a terminar bem antes do horário comercial. Os processos diurnos pararam de ser impactados. E tudo isso sem reescrever uma linha de código de negócio.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mas afinal: o que é um índice de banco de dados?
&lt;/h2&gt;

&lt;p&gt;Se você chegou até aqui e nunca parou para entender o que um índice faz de verdade, esse é o momento.&lt;/p&gt;

&lt;h3&gt;
  
  
  A analogia do livro
&lt;/h3&gt;

&lt;p&gt;Imagine que você tem um livro enciclopédico com 10.000 páginas e precisa encontrar tudo que fala sobre "fotossíntese". Você tem duas opções:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Sem índice:&lt;/strong&gt; Ler página por página do início ao fim. Funciona, mas demora.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Com índice:&lt;/strong&gt; Ir até o índice remissivo no final do livro, localizar "fotossíntese" em segundos e ir direto às páginas certas.
Um índice de banco de dados funciona exatamente assim. Ele é uma &lt;strong&gt;estrutura de dados separada&lt;/strong&gt; (geralmente uma B-Tree) que mantém uma cópia ordenada de uma ou mais colunas, com ponteiros para as linhas reais da tabela.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  O que um índice resolve?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Buscas por igualdade:&lt;/strong&gt; &lt;code&gt;WHERE id = 42&lt;/code&gt; o índice encontra o valor direto, sem varrer a tabela&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buscas por intervalo:&lt;/strong&gt; &lt;code&gt;WHERE date_created BETWEEN '2025-01-01' AND '2025-12-31'&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ordenação:&lt;/strong&gt; &lt;code&gt;ORDER BY last_execution DESC&lt;/code&gt; se o índice já estiver ordenado nessa direção, o banco nem precisa ordenar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Queries cobertas (covering index):&lt;/strong&gt; Com a cláusula &lt;code&gt;INCLUDE&lt;/code&gt;, o banco pode responder à query inteira só pelo índice, sem nem tocar na tabela original
### O que um índice &lt;em&gt;não&lt;/em&gt; é (e quando ele atrapalha)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Índice não é gratuito. Ele tem custos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Espaço em disco:&lt;/strong&gt; o índice ocupa armazenamento adicional&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Custo de escrita:&lt;/strong&gt; toda vez que um &lt;code&gt;INSERT&lt;/code&gt;, &lt;code&gt;UPDATE&lt;/code&gt; ou &lt;code&gt;DELETE&lt;/code&gt; acontece, os índices afetados também precisam ser atualizados&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Manutenção:&lt;/strong&gt; índices fragmentados precisam ser reorganizados periodicamente
Por isso, criar índices sem critério pode ser tão prejudicial quanto não tê-los. A regra de ouro é:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Crie índices nas colunas que aparecem frequentemente em cláusulas &lt;code&gt;WHERE&lt;/code&gt;, &lt;code&gt;JOIN&lt;/code&gt;, &lt;code&gt;ORDER BY&lt;/code&gt; e &lt;code&gt;GROUP BY&lt;/code&gt; de queries lentas, especialmente em tabelas grandes.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Como identificar quando você precisa de um índice
&lt;/h2&gt;

&lt;p&gt;Alguns sinais de alerta:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Queries que demoram mais conforme a tabela cresce&lt;/strong&gt; (como o nosso caso)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Full table scans&lt;/strong&gt; aparecendo nos planos de execução (&lt;code&gt;EXPLAIN&lt;/code&gt; / &lt;code&gt;Query Execution Plan&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Timeouts em operações que antes eram rápidas&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;CPU do banco de dados consistentemente alta&lt;/strong&gt; durante períodos de consulta
Ferramentas como o &lt;strong&gt;Azure Application Insights&lt;/strong&gt;, &lt;strong&gt;pg_stat_statements&lt;/strong&gt; (PostgreSQL), &lt;strong&gt;slow query log&lt;/strong&gt; (MySQL) e o &lt;strong&gt;Query Store&lt;/strong&gt; (SQL Server) são aliadas valiosas nesse diagnóstico.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Anatomia do índice que resolveu o problema
&lt;/h2&gt;

&lt;p&gt;Voltando ao índice criado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;CREATE&lt;/span&gt; &lt;span class="k"&gt;INDEX&lt;/span&gt; &lt;span class="n"&gt;idx_job_result_process_date&lt;/span&gt;
  &lt;span class="k"&gt;ON&lt;/span&gt; &lt;span class="n"&gt;app_schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;job_results&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;fk_process_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;date_created&lt;/span&gt; &lt;span class="k"&gt;DESC&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="n"&gt;INCLUDE&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;final_result&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;report_id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;result_payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Por que esse design?&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Componente&lt;/th&gt;
&lt;th&gt;Motivo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;fk_process_id&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Filtro principal da query (cada processo tem seu identificador)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;date_created DESC&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A query precisa do resultado mais recente primeiro&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;INCLUDE (...)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Colunas retornadas pela query, incluí-las evita um segundo acesso à tabela&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;O resultado é um &lt;strong&gt;covering index&lt;/strong&gt;: o banco responde à query inteira consultando apenas o índice, sem precisar buscar dados na tabela principal. É a forma mais eficiente de otimização de leitura possível.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Performance não é só sobre algoritmos ou arquitetura de microsserviços. Às vezes, o gargalo está numa operação aparentemente simples que o banco de dados precisa executar milhares de vezes por dia, e que ninguém percebe até que o custo acumulado se torne um problema real.&lt;/p&gt;

&lt;p&gt;Nesse caso, &lt;strong&gt;um único índice bem pensado&lt;/strong&gt; transformou 6 horas de processamento em 40 segundos. Sem refatoração. Sem mudança de arquitetura. Sem downtime.&lt;/p&gt;

&lt;p&gt;Se você ainda não tem o hábito de revisar os planos de execução das suas queries críticas, comece agora. O banco de dados tem muito a te contar, basta saber ouvir.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Sugestões de leitura para se aprofundar: B-Tree indexes, covering indexes, query execution plans, índices compostos e ferramentas de profiling como EXPLAIN ANALYZE.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>database</category>
      <category>braziliandevs</category>
      <category>performance</category>
      <category>career</category>
    </item>
    <item>
      <title>DataAccess: Entity Framework</title>
      <dc:creator>Bruno Freschi</dc:creator>
      <pubDate>Thu, 07 May 2026 03:13:39 +0000</pubDate>
      <link>https://dev.to/he4rt/dataaccess-entity-framework-2jp0</link>
      <guid>https://dev.to/he4rt/dataaccess-entity-framework-2jp0</guid>
      <description>&lt;p&gt;O Entity Framework (EF Core) fecha essa trindade do acesso a dados no .NET. Enquanto o ADO.NET te dá o controle mais baixo e o Dapper fica no meio termo, o EF Core é o peso-pesado da produtividade e da abstração.&lt;/p&gt;

&lt;p&gt;Aqui está o guia, estruturado.&lt;/p&gt;




&lt;h2&gt;
  
  
  Entity Framework Core — Guia Prático
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Visão Geral
&lt;/h3&gt;

&lt;p&gt;O Entity Framework Core (EF Core) é um mapeador objeto-relacional (ORM) moderno, de código aberto e multiplataforma para o .NET. Ele abstrai quase que totalmente o banco de dados, permitindo que você trabalhe com dados usando objetos C# fortemente tipados.&lt;/p&gt;

&lt;p&gt;Ele foi projetado para oferecer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Altíssima produtividade&lt;/strong&gt; (gera o SQL para você)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;LINQ (Language Integrated Query)&lt;/strong&gt; para escrever queries direto no C#&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Migrations&lt;/strong&gt; para controle de versão do esquema do banco de dados&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rastreamento de mudanças (&lt;em&gt;Change Tracking&lt;/em&gt;)&lt;/strong&gt; automático&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Diferente do Dapper e do ADO.NET, o EF Core foca em produtividade e em manter a lógica de dados próxima ao paradigma de orientação a objetos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quando usar EF Core
&lt;/h3&gt;

&lt;p&gt;Use EF Core quando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O foco principal do projeto é a velocidade de desenvolvimento (Time-to-Market)&lt;/li&gt;
&lt;li&gt;Você precisa de um CRUD padrão sem querer escrever SQL manualmente&lt;/li&gt;
&lt;li&gt;Deseja manter o código independente de banco de dados (fácil portabilidade de SQL Server para PostgreSQL, por exemplo)&lt;/li&gt;
&lt;li&gt;Precisa de recursos complexos como carregamento tardio (&lt;em&gt;Lazy Loading&lt;/em&gt;) ou carregamento explícito&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Evite quando:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Performance extrema de microssegundos for o requisito número um&lt;/li&gt;
&lt;li&gt;Você precisa executar queries altamente complexas ou muito específicas do banco&lt;/li&gt;
&lt;li&gt;Operações em lote (ETL) gigantescas sem o uso de recursos de Bulk específicos&lt;/li&gt;
&lt;li&gt;O overhead de memória do rastreamento de entidades for um problema&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  2. Instalação
&lt;/h3&gt;

&lt;p&gt;O EF Core é modular. Você instala o pacote principal e o provedor do banco de dados escolhido.&lt;/p&gt;

&lt;p&gt;Via CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Microsoft.EntityFrameworkCore.SqlServer
dotnet add package Microsoft.EntityFrameworkCore.Design
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;em&gt;(O pacote &lt;code&gt;Design&lt;/code&gt; é necessário para rodar as Migrations).&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  3. Conceitos Fundamentais
&lt;/h3&gt;

&lt;h3&gt;
  
  
  3.1 O DbContext
&lt;/h3&gt;

&lt;p&gt;O &lt;code&gt;DbContext&lt;/code&gt; é o coração do EF Core. Ele representa uma sessão com o banco de dados.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppDbContext&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;DbContext&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="n"&gt;DbSet&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;Clientes&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="k"&gt;override&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;OnConfiguring&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;DbContextOptionsBuilder&lt;/span&gt; &lt;span class="n"&gt;optionsBuilder&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;optionsBuilder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;UseSqlServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SuaConnectionStringAqui"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.2 Query (SELECT) com LINQ
&lt;/h3&gt;

&lt;p&gt;Você não escreve SQL. Você usa LINQ, e o EF Core traduz para SQL em tempo de execução:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Retorna todos os clientes com e-mail do Gmail&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;clientes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;EndsWith&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@gmail.com"&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.3 FirstOrDefault / SingleOrDefault
&lt;/h3&gt;

&lt;p&gt;Funcionam de forma idêntica ao Dapper, mas usando LINQ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Retorna o primeiro ou nulo&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Espera exatamente 1 resultado (lança exceção se houver mais de um)&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;clienteUnico&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SingleOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="s"&gt;"email@email.com"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3.4 Operações de Escrita (INSERT, UPDATE, DELETE)
&lt;/h3&gt;

&lt;p&gt;O EF Core rastreia as entidades em memória e só envia os comandos para o banco quando você chama &lt;code&gt;SaveChanges()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Insert:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;novoCliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Cliente&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Nome&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Gabrielly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"email@email.com"&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;novoCliente&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Aqui o SQL INSERT é executado&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Update:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nome&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Nome Alterado"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// O EF detecta a mudança e gera o UPDATE&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4. Parâmetros e Segurança
&lt;/h3&gt;

&lt;h3&gt;
  
  
  4.1 SQL Injection? O EF Core já te protege
&lt;/h3&gt;

&lt;p&gt;Como o LINQ gera consultas parametrizadas por padrão, você está automaticamente protegido contra SQL Injection na esmagadora maioria dos casos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="n"&gt;emailBusca&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"usuario@email.com"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// Seguro por padrão. O EF cria o parâmetro @p0 no SQL gerado.&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="n"&gt;emailBusca&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  4.2 Executando SQL Puro com Segurança
&lt;/h3&gt;

&lt;p&gt;Se precisar rodar SQL manual no EF, use &lt;code&gt;FromSql&lt;/code&gt; com interpolação segura:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// O EF Core converte a interpolação em parâmetros de forma segura!&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;clientes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;FromSql&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;$"SELECT * FROM Clientes WHERE Nome = &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;nomeVar&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5. Mapeamento Avançado
&lt;/h3&gt;

&lt;h3&gt;
  
  
  5.1 Carregamento de Relacionamentos (JOIN)
&lt;/h3&gt;

&lt;p&gt;Para trazer dados de tabelas relacionadas, você usa o &lt;code&gt;Include&lt;/code&gt; (&lt;em&gt;Eager Loading&lt;/em&gt;):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;clientesComPedidos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Include&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pedidos&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="c1"&gt;// Faz o JOIN com a tabela de Pedidos&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  6. Transações
&lt;/h3&gt;

&lt;p&gt;Por padrão, o &lt;code&gt;SaveChanges()&lt;/code&gt; já executa tudo dentro de uma transação implícita. Se você precisar envolver múltiplas operações ou múltiplos contextos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;AppDbContext&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Database&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;novoCliente&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Logs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="n"&gt;Log&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Mensagem&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Cliente inserido"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;SaveChanges&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Rollback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  7. Performance
&lt;/h3&gt;

&lt;h3&gt;
  
  
  7.1 Por que dizem que o EF Core é lento?
&lt;/h3&gt;

&lt;p&gt;O EF Core carrega uma árvore de expressões complexa para traduzir LINQ em SQL e, por padrão, mantém uma cópia de cada objeto lido na memória para rastrear alterações (&lt;em&gt;Change Tracking&lt;/em&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  7.2 Como torná-lo ultra rápido (Boas práticas):
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Use AsNoTracking():&lt;/strong&gt; Para consultas somente leitura (onde você não vai dar &lt;code&gt;Update&lt;/code&gt;), desabilite o rastreamento. O consumo de memória despenca e a velocidade dobra.C#
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;clientesLeitura&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AsNoTracking&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Evite trazer todas as colunas:&lt;/strong&gt; Use &lt;code&gt;Select&lt;/code&gt; para trazer apenas o que precisa (&lt;em&gt;Projection&lt;/em&gt;).
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;nomesClientes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Nome&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ToList&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Execute Delete/Update em massa:&lt;/strong&gt; Antigamente era preciso carregar para a memória para deletar. O EF Core permite fazer isso direto no banco:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Clientes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Where&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Ativo&lt;/span&gt; &lt;span class="p"&gt;==&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;ExecuteDelete&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  8. Uso em ETL
&lt;/h3&gt;

&lt;p&gt;Historicamente, o EF Core era péssimo para ETL devido ao &lt;em&gt;overhead&lt;/em&gt; do rastreamento de estado. Hoje, ele se tornou muito viável se usado corretamente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Use &lt;code&gt;AsNoTracking()&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Utilize &lt;code&gt;ExecuteUpdate&lt;/code&gt; e &lt;code&gt;ExecuteDelete&lt;/code&gt; para manipulação em massa sem carregar dados para a memória.&lt;/li&gt;
&lt;li&gt;Se precisar fazer inserções massivas, desabilite o rastreador e insira em lotes (&lt;em&gt;Batches&lt;/em&gt;) de 500 a 1000 registros por &lt;code&gt;SaveChanges()&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  9. Comparação Técnica
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Característica&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Entity Framework&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Dapper&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;ADO.NET&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Performance&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Média / Alta (se otimizado)&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;td&gt;Muito Alta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Geração de SQL&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Automática&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;td&gt;Manual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Migrations&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Sim (Nativo)&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Curva de Aprendizado&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;td&gt;Média&lt;/td&gt;
&lt;td&gt;Baixa&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  10. Boas Práticas
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Sempre use &lt;code&gt;AsNoTracking()&lt;/code&gt; em consultas de leitura.&lt;/li&gt;
&lt;li&gt;Não abuse do &lt;em&gt;Lazy Loading&lt;/em&gt; (pode gerar o problema de N+1 consultas no banco).&lt;/li&gt;
&lt;li&gt;Monitore o SQL que o EF está gerando no console durante o desenvolvimento.&lt;/li&gt;
&lt;li&gt;Mantenha suas entidades focadas no domínio e use DTOs para tráfego de dados.&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  11. Armadilhas Comuns
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Problema do N+1:&lt;/strong&gt; Executar uma query para listar clientes e depois disparar uma nova query de pedidos para cada cliente da lista por não usar o &lt;code&gt;Include&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Esquecer do &lt;code&gt;AsNoTracking&lt;/code&gt;&lt;/strong&gt; em telas de relatórios que carregam milhares de linhas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fazer filtros em memória:&lt;/strong&gt; Usar &lt;code&gt;.ToList()&lt;/code&gt; antes do &lt;code&gt;.Where()&lt;/code&gt;. Isso traz a tabela inteira para a memória do C# para só depois filtrar.&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>backend</category>
      <category>csharp</category>
      <category>database</category>
      <category>dotnet</category>
    </item>
    <item>
      <title>DataAccess: Dapper</title>
      <dc:creator>Bruno Freschi</dc:creator>
      <pubDate>Wed, 22 Apr 2026 17:59:33 +0000</pubDate>
      <link>https://dev.to/he4rt/dataaccess-dapper-1eba</link>
      <guid>https://dev.to/he4rt/dataaccess-dapper-1eba</guid>
      <description>&lt;p&gt;O &lt;strong&gt;Dapper&lt;/strong&gt; é um micro ORM (Object-Relational Mapper) para .NET que estende o ADO.NET. Ele foi projetado para oferecer:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Alta performance (próximo ao ADO.NET puro)&lt;/li&gt;
&lt;li&gt;Baixo overhead&lt;/li&gt;
&lt;li&gt;Controle total sobre SQL&lt;/li&gt;
&lt;li&gt;Mapeamento simples entre objetos e queries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Diferente de ORMs completos como Entity Framework, o Dapper &lt;strong&gt;não abstrai o SQL&lt;/strong&gt;, ele trabalha junto com ele.&lt;/p&gt;

&lt;h3&gt;
  
  
  Quando usar Dapper
&lt;/h3&gt;

&lt;p&gt;Use Dapper quando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance é crítica (ex: ETL, processamento em lote)&lt;/li&gt;
&lt;li&gt;Queries são complexas ou altamente otimizadas&lt;/li&gt;
&lt;li&gt;Você quer controle total do SQL&lt;/li&gt;
&lt;li&gt;Baixo overhead é necessário&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Evite quando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precisa de tracking automático de entidades&lt;/li&gt;
&lt;li&gt;Quer abstração completa do banco&lt;/li&gt;
&lt;li&gt;CRUD simples sem preocupação com performance&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Instalação
&lt;/h2&gt;

&lt;p&gt;Via CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;dotnet add package Dapper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou via NuGet Package Manager:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;Install-Package Dapper
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Conceitos Fundamentais
&lt;/h2&gt;

&lt;h3&gt;
  
  
  3.1 Extensão do IDbConnection
&lt;/h3&gt;

&lt;p&gt;O Dapper funciona como extensão de &lt;code&gt;IDbConnection&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SqlConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT * FROM Clientes"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  3.2 Query (SELECT)
&lt;/h3&gt;

&lt;p&gt;Retorna dados mapeados automaticamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;clientes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"SELECT Id, Nome, Email FROM Clientes"&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mapeia por convenção:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Nome da coluna = Nome da propriedade&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3.3 QueryFirst / QuerySingle
&lt;/h3&gt;

&lt;p&gt;Diferença importante:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Retorna o primeiro ou default&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QueryFirstOrDefault&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"SELECT * FROM Clientes WHERE Id = @Id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Espera exatamente 1 resultado&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;QuerySingle&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"SELECT * FROM Clientes WHERE Id = @Id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use &lt;code&gt;QuerySingle&lt;/code&gt; quando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Você garante que existe exatamente 1 registro&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  3.4 Execute (INSERT, UPDATE, DELETE)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;rows&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"INSERT INTO Clientes (Nome, Email) VALUES (@Nome, @Email)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Nome&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Gabrielly"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"email@email.com"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retorna:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Número de linhas afetadas&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Parâmetros e Segurança
&lt;/h2&gt;

&lt;h3&gt;
  
  
  4.1 Evitando SQL Injection
&lt;/h3&gt;

&lt;p&gt;Sempre use parâmetros:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"SELECT * FROM Clientes WHERE Email = @Email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;Email&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nunca faça isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ERRADO&lt;/span&gt;
&lt;span class="s"&gt;$"SELECT * FROM Clientes WHERE Email = '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  4.2 Parâmetros Dinâmicos
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;parameters&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;DynamicParameters&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="n"&gt;parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"Id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="s"&gt;"SELECT * FROM Clientes WHERE Id = @Id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;parameters&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Mapeamento Avançado
&lt;/h2&gt;

&lt;h3&gt;
  
  
  5.1 Multi-mapping (JOIN)
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sql&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;@"
SELECT c.*, p.*
FROM Clientes c
INNER JOIN Pedidos p ON c.Id = p.ClienteId
"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;result&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Pedido&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;=&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Pedidos&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h3&gt;
  
  
  5.2 Query Multiple
&lt;/h3&gt;

&lt;p&gt;Executa múltiplas queries em uma chamada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;multi&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;QueryMultiple&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;@"
    SELECT * FROM Clientes;
    SELECT * FROM Pedidos;
"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;clientes&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;pedidos&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;multi&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Pedido&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Transações
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SqlConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;BeginTransaction&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;try&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sql2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;param2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Commit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;catch&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;transaction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Rollback&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Essencial para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ETL&lt;/li&gt;
&lt;li&gt;Processamento em lote&lt;/li&gt;
&lt;li&gt;Consistência de dados&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Performance
&lt;/h2&gt;

&lt;h3&gt;
  
  
  7.1 Por que o Dapper é rápido?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Usa IL emit (geração dinâmica de código)&lt;/li&gt;
&lt;li&gt;Não faz tracking de entidades&lt;/li&gt;
&lt;li&gt;Não possui abstrações pesadas&lt;/li&gt;
&lt;li&gt;Trabalha direto sobre ADO.NET&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  7.2 Boas práticas de performance
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Reutilize conexões (connection pooling já ajuda)&lt;/li&gt;
&lt;li&gt;Use &lt;code&gt;QueryBuffered: false&lt;/code&gt; para grandes volumes:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;data&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;
    &lt;span class="n"&gt;sql&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;buffered&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;false&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;Evite SELECT *&lt;/li&gt;
&lt;li&gt;Use índices no banco&lt;/li&gt;
&lt;li&gt;Prefira queries específicas&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Uso em ETL
&lt;/h2&gt;

&lt;p&gt;Dapper é excelente para ETL por:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Baixo overhead&lt;/li&gt;
&lt;li&gt;Controle total do fluxo&lt;/li&gt;
&lt;li&gt;Alta velocidade em leitura/escrita&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Exemplo simplificado ETL:
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;dados&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;connectionOrigem&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Query&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="n"&gt;Cliente&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT * FROM Clientes"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;cliente&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;dados&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="n"&gt;connectionDestino&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s"&gt;"INSERT INTO Clientes (Id, Nome) VALUES (@Id, @Nome)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;cliente&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Otimização recomendada:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Processamento em lote&lt;/li&gt;
&lt;li&gt;Uso de transações&lt;/li&gt;
&lt;li&gt;Bulk insert quando possível&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Comparação Técnica
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Característica&lt;/th&gt;
&lt;th&gt;Dapper&lt;/th&gt;
&lt;th&gt;ADO.NET&lt;/th&gt;
&lt;th&gt;Entity Framework&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;td&gt;Muito Alta&lt;/td&gt;
&lt;td&gt;Média&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controle SQL&lt;/td&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;Parcial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facilidade&lt;/td&gt;
&lt;td&gt;Média&lt;/td&gt;
&lt;td&gt;Baixa&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tracking&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;td&gt;Não&lt;/td&gt;
&lt;td&gt;Sim&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Overhead&lt;/td&gt;
&lt;td&gt;Baixo&lt;/td&gt;
&lt;td&gt;Muito baixo&lt;/td&gt;
&lt;td&gt;Alto&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Boas Práticas
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Sempre use parâmetros&lt;/li&gt;
&lt;li&gt;Evite lógica no banco desnecessária&lt;/li&gt;
&lt;li&gt;Separe queries por responsabilidade&lt;/li&gt;
&lt;li&gt;Use DTOs específicos&lt;/li&gt;
&lt;li&gt;Não misture Dapper com lógica de domínio diretamente&lt;/li&gt;
&lt;li&gt;Centralize acesso a dados (Repository ou similar)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Armadilhas Comuns
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Esquecer de abrir conexão&lt;/li&gt;
&lt;li&gt;Não tratar múltiplos resultados corretamente&lt;/li&gt;
&lt;li&gt;Uso incorreto de &lt;code&gt;QuerySingle&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Falta de transação em operações críticas&lt;/li&gt;
&lt;li&gt;Carregar grandes volumes em memória sem controle&lt;/li&gt;
&lt;/ul&gt;




</description>
      <category>braziliandevs</category>
      <category>devjournal</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>DataAccess: ADO.net</title>
      <dc:creator>Bruno Freschi</dc:creator>
      <pubDate>Fri, 10 Apr 2026 11:31:20 +0000</pubDate>
      <link>https://dev.to/he4rt/dataaccess-adonet-3a79</link>
      <guid>https://dev.to/he4rt/dataaccess-adonet-3a79</guid>
      <description>&lt;h3&gt;
  
  
  Controle total e performance máxima
&lt;/h3&gt;

&lt;p&gt;Se você usa .NET, precisa entender o ADO.NET. Ele é a base de tudo — inclusive do Dapper e do Entity Framework.&lt;/p&gt;

&lt;h3&gt;
  
  
  O que é?
&lt;/h3&gt;

&lt;p&gt;ADO.NET é o conjunto de APIs nativas do .NET para acessar banco de dados diretamente. Sem abstrações pesadas. Sem mágica.&lt;/p&gt;

&lt;p&gt;👉 Resultado: &lt;strong&gt;máxima performance e controle absoluto&lt;/strong&gt;.&lt;/p&gt;




&lt;h3&gt;
  
  
  Quando faz sentido usar?
&lt;/h3&gt;

&lt;p&gt;Use ADO.NET quando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Performance é crítica (ETL, alto volume, baixa latência)&lt;/li&gt;
&lt;li&gt;Você precisa de controle fino sobre SQL e transações&lt;/li&gt;
&lt;li&gt;Quer evitar dependências externas&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Evite quando:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Precisa de produtividade rápida (CRUDs simples)&lt;/li&gt;
&lt;li&gt;Não quer escrever mapeamento manual&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Como funciona (essencial)
&lt;/h3&gt;

&lt;p&gt;Os 3 principais componentes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;SqlConnection&lt;/code&gt; → abre conexão&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SqlCommand&lt;/code&gt; → executa SQL&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;SqlDataReader&lt;/code&gt; → lê dados (streaming, rápido)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Exemplo direto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SqlConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connectionString&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SqlCommand&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"SELECT Id, Nome FROM Clientes"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Open&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;ExecuteReader&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;Read&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetInt32&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;nome&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;GetString&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="m"&gt;1&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Sem ORM. Sem mapeamento automático. Só você e o banco.&lt;/p&gt;


&lt;h3&gt;
  
  
  Segurança básica (obrigatório)
&lt;/h3&gt;

&lt;p&gt;Nunca faça isso:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="s"&gt;$"SELECT * FROM Clientes WHERE Email = '&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s"&gt;'"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Use parâmetros:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="n"&gt;command&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Parameters&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;AddWithValue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s"&gt;"@Email"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;👉 Evita SQL Injection e melhora cache de execução.&lt;/p&gt;


&lt;h3&gt;
  
  
  Performance: por que é tão rápido?
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Sem reflexão&lt;/li&gt;
&lt;li&gt;Sem geração de SQL&lt;/li&gt;
&lt;li&gt;Leitura em streaming (&lt;code&gt;DataReader&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Controle total de conexões&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  ETL? Aqui ele brilha
&lt;/h3&gt;

&lt;p&gt;Para cargas massivas no SQL Server:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="k"&gt;using&lt;/span&gt; &lt;span class="nn"&gt;var&lt;/span&gt; &lt;span class="n"&gt;bulkCopy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;SqlBulkCopy&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;connection&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="n"&gt;bulkCopy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;DestinationTableName&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s"&gt;"Clientes"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;bulkCopy&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;WriteToServer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;reader&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;👉 &lt;strong&gt;Ordens de magnitude mais rápido que INSERT em loop&lt;/strong&gt;&lt;/p&gt;


&lt;h3&gt;
  
  
  Comparação rápida
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;ADO.NET&lt;/th&gt;
&lt;th&gt;Dapper&lt;/th&gt;
&lt;th&gt;EF&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Performance&lt;/td&gt;
&lt;td&gt;🔥 Máxima&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;td&gt;Média&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Controle SQL&lt;/td&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;Total&lt;/td&gt;
&lt;td&gt;Parcial&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Facilidade&lt;/td&gt;
&lt;td&gt;Baixa&lt;/td&gt;
&lt;td&gt;Média&lt;/td&gt;
&lt;td&gt;Alta&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;


&lt;h3&gt;
  
  
  Regra prática
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Quer &lt;strong&gt;controle e performance&lt;/strong&gt; → ADO.NET&lt;/li&gt;
&lt;li&gt;Quer &lt;strong&gt;equilíbrio&lt;/strong&gt; → Dapper&lt;/li&gt;
&lt;li&gt;Quer &lt;strong&gt;produtividade&lt;/strong&gt; → EF&lt;/li&gt;
&lt;/ul&gt;


&lt;h3&gt;
  
  
  Insight final
&lt;/h3&gt;

&lt;p&gt;Dominar ADO.NET muda seu nível como backend .NET.&lt;/p&gt;

&lt;p&gt;Você passa a entender &lt;strong&gt;o que realmente acontece entre sua aplicação e o banco&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;E isso impacta diretamente em performance, custo e escalabilidade.&lt;/p&gt;
&lt;h3&gt;
  
  
  Link para o repo.
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/BrunoSFreschi" rel="noopener noreferrer"&gt;
        BrunoSFreschi
      &lt;/a&gt; / &lt;a href="https://github.com/BrunoSFreschi/DataAccess.Benchmark" rel="noopener noreferrer"&gt;
        DataAccess.Benchmark
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      DataAccess Benchmark é um laboratório de engenharia de software criado para analisar,  o custo das diferentes abordagens de acesso a dados no ecossistema .NET. 
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;a rel="noopener noreferrer" href="https://private-user-images.githubusercontent.com/89789210/574823833-e1d506d3-9daf-43e0-88a6-4ec48eb38abb.png?jwt=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Nzg1MDE4MzksIm5iZiI6MTc3ODUwMTUzOSwicGF0aCI6Ii84OTc4OTIxMC81NzQ4MjM4MzMtZTFkNTA2ZDMtOWRhZi00M2UwLTg4YTYtNGVjNDhlYjM4YWJiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA1MTElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNTExVDEyMTIxOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWUyMjFlODQwYzVkYTljMGM4ZjliMzZmNjhiMTFjMDc3MzUzNjJiNGQ4N2ZlZjg3ODQ4NWMyZDVmOWQ2NjQ2N2EmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRnBuZyJ9.i9Fz3kjy87Ule0V9ZCwmH_uK9aRnz8-VVUF2YnprCms"&gt;&lt;img width="1337" height="699" alt="Gemini_Generated_Image_g46htng46htng46h" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fprivate-user-images.githubusercontent.com%2F89789210%2F574823833-e1d506d3-9daf-43e0-88a6-4ec48eb38abb.png%3Fjwt%3DeyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3Nzg1MDE4MzksIm5iZiI6MTc3ODUwMTUzOSwicGF0aCI6Ii84OTc4OTIxMC81NzQ4MjM4MzMtZTFkNTA2ZDMtOWRhZi00M2UwLTg4YTYtNGVjNDhlYjM4YWJiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA1MTElMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNTExVDEyMTIxOVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWUyMjFlODQwYzVkYTljMGM4ZjliMzZmNjhiMTFjMDc3MzUzNjJiNGQ4N2ZlZjg3ODQ4NWMyZDVmOWQ2NjQ2N2EmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRnBuZyJ9.i9Fz3kjy87Ule0V9ZCwmH_uK9aRnz8-VVUF2YnprCms" class="js-gh-image-fallback"&gt;&lt;/a&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;DataAccess Benchmark&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;Ao estudar performance, não estamos apenas medindo tempo de execução
Estamos observando a &lt;strong&gt;história da engenharia de software se manifestando em código&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Este repositório foi criado para demonstrar, de forma prática, a evolução das abordagens de acesso a dados no ecossistema .NET, comparando:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;ADO.NET (baixo nível, controle total)&lt;/li&gt;
&lt;li&gt;Dapper (micro-ORM, performance com praticidade)&lt;/li&gt;
&lt;li&gt;Entity Framework Core (ORM completo, foco em produtividade)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Tudo medido com precisão usando &lt;strong&gt;BenchmarkDotNet&lt;/strong&gt;, a ferramenta padrão para benchmarks profissionais em .NET.&lt;/p&gt;
&lt;p&gt;Este projeto não é apenas um teste
Ele é um laboratório para entender &lt;strong&gt;como a engenharia de software evoluiu ao longo dos anos&lt;/strong&gt;.&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;A História do Acesso a Dados no .NET&lt;/h1&gt;

&lt;/div&gt;
&lt;p&gt;Assim como a POO surgiu da Crise do Software, as diferentes formas de acessar banco de dados surgiram de um problema recorrente:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;Como acessar dados com segurança, performance e manutenibilidade ao mesmo tempo?&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Durante a evolução do .NET, três…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/BrunoSFreschi/DataAccess.Benchmark" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


</description>
      <category>braziliandevs</category>
      <category>devjournal</category>
      <category>beginners</category>
      <category>learning</category>
    </item>
    <item>
      <title>Por que eu começo 10 projetos e não termino nenhum?</title>
      <dc:creator>Yuri Souza</dc:creator>
      <pubDate>Sun, 05 Apr 2026 23:48:52 +0000</pubDate>
      <link>https://dev.to/he4rt/por-que-eu-comeco-10-projetos-e-nao-termino-nenhum-1139</link>
      <guid>https://dev.to/he4rt/por-que-eu-comeco-10-projetos-e-nao-termino-nenhum-1139</guid>
      <description>&lt;p&gt;Você já passou uma tarde inteira configurando um projeto novo, escolhendo a stack, criando o repositório, estruturando as pastas e sentiu que estava &lt;em&gt;voando&lt;/em&gt;? Aquela sensação de que dessa vez vai ser diferente, que essa ideia é boa demais pra morrer na gaveta? Eu também. O problema é que dois dias depois eu estava fazendo exatamente a mesma coisa, só que com outra ideia.&lt;/p&gt;

&lt;p&gt;Se você se identificou, esse artigo é pra você. Não porque eu tenho a solução mágica, mas porque finalmente entendi o mecanismo por trás disso. E entender já muda muita coisa.&lt;/p&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;O prazer de começar&lt;/li&gt;
&lt;li&gt;O momento em que tudo desmorona&lt;/li&gt;
&lt;li&gt;O objeto brilhante&lt;/li&gt;
&lt;li&gt;A culpa que ninguém fala&lt;/li&gt;
&lt;li&gt;Não é preguiça. É neurologia.&lt;/li&gt;
&lt;li&gt;Conclusão&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  O prazer de começar
&lt;/h2&gt;

&lt;p&gt;Tem algo muito específico que acontece quando você começa um projeto novo.&lt;/p&gt;

&lt;p&gt;O problema ainda não existe de verdade. Tudo é possibilidade. Você ainda não encontrou o bug impossível de reproduzir, ainda não percebeu que a arquitetura que escolheu não escala, ainda não chegou na parte chata de fazer o CRUD de usuário pela décima vez na vida.&lt;/p&gt;

&lt;p&gt;Nessa fase, o cérebro libera dopamina. Bastante. A antecipação de uma recompensa futura ativa os mesmos circuitos que uma conquista real. Ou seja: &lt;strong&gt;só de imaginar o projeto funcionando, você já sente parte da recompensa.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Isso explica o prazer visceral do setup. Escolher o nome do repositório, montar a estrutura de pastas, escrever o README antes de ter uma linha de código que funciona... tudo isso alimenta aquela sensação.&lt;/p&gt;

&lt;p&gt;O problema? É que ela passa.&lt;/p&gt;




&lt;h2&gt;
  
  
  O momento em que tudo desmorona
&lt;/h2&gt;

&lt;p&gt;Existe um ponto específico em todo projeto onde a magia some. Eu chamo de &lt;strong&gt;o vale do tédio&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;É quando você já sabe o que precisa fazer, mas o trabalho deixou de ser estimulante. As decisões arquiteturais foram tomadas. O setup tá pronto. Agora é só... executar. Implementar a feature chata. Escrever o teste. Lidar com o caso que você não previu.&lt;/p&gt;

&lt;p&gt;Para a maioria das pessoas, esse momento é desconfortável mas passável. Você empurra, entrega, segue.&lt;/p&gt;

&lt;p&gt;Para alguns cérebros, incluindo o meu, esse momento é quase fisicamente doloroso. Não é falta de vontade. É que o sistema de recompensa do cérebro simplesmente não libera o combustível necessário pra continuar uma tarefa que deixou de ser nova.&lt;/p&gt;

&lt;p&gt;E aí começa o ciclo de autos sabotagem mais clássico do mundo do dev:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"Antes de continuar, vou refatorar essa parte aqui."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Preciso repensar a arquitetura antes de avançar."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Deixa eu criar um boilerplate melhor pra usar nos próximos projetos."&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Você não está sendo preguiçoso. Você está inconscientemente procurando a dopamina do recomeço dentro do próprio projeto.&lt;/p&gt;




&lt;h2&gt;
  
  
  O objeto brilhante
&lt;/h2&gt;

&lt;p&gt;E então aparece. Uma ideia nova.&lt;/p&gt;

&lt;p&gt;Pode ser um problema que você viu no trabalho, uma conversa no Twitter, um repositório no GitHub que te inspirou. De repente, aquela ideia nova parece &lt;strong&gt;muito mais interessante&lt;/strong&gt; do que o projeto que você abandonou no vale do tédio.&lt;/p&gt;

&lt;p&gt;E ela realmente é, pelo mesmo motivo que o projeto anterior era interessante no início. Ela ainda não tem o peso da implementação. Ainda não tem os bugs, as decisões difíceis, o trabalho repetitivo.&lt;/p&gt;

&lt;p&gt;Esse fenômeno tem um nome informal: &lt;strong&gt;Síndrome do Objeto Brilhante&lt;/strong&gt;. E ele é mais intenso em cérebros que têm dificuldade de regular dopamina naturalmente.&lt;/p&gt;

&lt;p&gt;O que acontece na prática? O projeto antigo não morre oficialmente. Ele só vai pra uma pasta chamada &lt;code&gt;projetos/&lt;/code&gt; e fica lá, acumulando poeira junto com outros oito projetos que passaram pelo mesmo ciclo.&lt;/p&gt;




&lt;h2&gt;
  
  
  A culpa que ninguém fala
&lt;/h2&gt;

&lt;p&gt;Aqui está a parte que eu não via ninguém discutir: &lt;strong&gt;a culpa.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Porque quando você abandona um projeto, você não apenas perde o projeto. Você coleciona evidência contra si mesmo.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Eu nunca termino nada."&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"Eu começo cheio de energia e nunca entrego."&lt;/em&gt;&lt;br&gt;
&lt;em&gt;"Por que eu deveria começar esse projeto se vou abandonar igual aos outros?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;E essa narrativa é perigosa por dois motivos.&lt;/p&gt;

&lt;p&gt;Primeiro, porque ela não é completamente verdadeira. Você provavelmente termina muita coisa, no trabalho, em tarefas com prazo real, em situações onde tem alguém esperando. O problema não é terminar em si. É terminar coisas que dependem 100% da sua motivação interna, sem deadline, sem stakeholder, sem pressão externa.&lt;/p&gt;

&lt;p&gt;Segundo, você começa a acreditar que não é capaz de terminar, então para de tentar de verdade, e aí realmente não termina. O ciclo se fecha.&lt;/p&gt;




&lt;h2&gt;
  
  
  Não é preguiça. É neurologia.
&lt;/h2&gt;

&lt;p&gt;Vou ser direto: se você se reconheceu em tudo que eu escrevi acima, há uma chance real de que seu cérebro simplesmente funciona de um jeito diferente da média.&lt;/p&gt;

&lt;p&gt;Tem um tipo de cérebro que odeia repetição mas ama coisa nova. Não é fraqueza, é como ele funciona. O problema é que todo projeto tem uma fase nova e uma fase chata — e esse cérebro simplesmente apaga na segunda.&lt;/p&gt;

&lt;p&gt;Não é falta de disciplina. É que o combustível que o seu cérebro usa pra manter o foco não funciona igual ao de todo mundo.&lt;/p&gt;

&lt;p&gt;A diferença entre entender isso e não entender é enorme. Quando você não entende, você passa anos se achando preguiçoso, incompetente, incapaz de terminar o que começa e quando entende você começa a fazer perguntas diferentes:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Como eu estruturo esse projeto pra ter recompensas menores e mais frequentes?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Como eu crio pressão externa pra compensar a falta de pressão interna?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;"Qual é o menor projeto possível que ainda entrega valor?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Não são perguntas fáceis de responder. Mas são as perguntas certas.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;Eu ainda começo projetos demais. Provavelmente sempre vou começar. Mas aprendi a parar de tratar isso como um defeito de caráter e passei a tratar como uma característica que precisa de estratégia.&lt;/p&gt;

&lt;p&gt;O problema nunca foi a quantidade de projetos que eu começo. Foi a narrativa que eu construí em volta dos que eu não terminei.&lt;/p&gt;

&lt;p&gt;Se você chegou até aqui e se reconheceu em alguma parte desse texto: você não está sozinho. E você provavelmente é melhor em começar coisas do que 90% das pessoas. Isso não é pouco, é uma habilidade real, que só precisa de direção.&lt;/p&gt;

&lt;p&gt;O próximo projeto vai começar. A questão é o que você vai fazer diferente dessa vez.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Escrito por alguém que tem pelo menos dez pastas &lt;code&gt;projetos/&lt;/code&gt; abertas no VS Code agora mesmo.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>braziliandevs</category>
      <category>productivity</category>
      <category>writing</category>
    </item>
    <item>
      <title>O que uma usina nuclear tem a ver com o seu processo de QA?</title>
      <dc:creator>Alicia Marianne Gonçalves</dc:creator>
      <pubDate>Sun, 05 Apr 2026 12:02:52 +0000</pubDate>
      <link>https://dev.to/he4rt/o-que-uma-usina-nuclear-tem-a-ver-com-o-seu-processo-de-qa-103j</link>
      <guid>https://dev.to/he4rt/o-que-uma-usina-nuclear-tem-a-ver-com-o-seu-processo-de-qa-103j</guid>
      <description>&lt;p&gt;A gente sabe que testar e validar um software antes de ir para produção é importante. Mas você já parou para pensar no peso real que isso carrega?&lt;/p&gt;

&lt;p&gt;Recentemente, estava revendo a série &lt;em&gt;Chernobyl&lt;/em&gt;, e ela me fez refletir sobre muita coisa — especialmente sobre a forma como encaro minha área, sendo QA, e sobre a responsabilidade que ela traz. Resolvi compartilhar isso com vocês.&lt;/p&gt;

&lt;p&gt;Para quem não conhece, &lt;em&gt;Chernobyl&lt;/em&gt; é uma minissérie dramática lançada em 2019 que retrata o desastre nuclear ocorrido na usina de mesmo nome, na então União Soviética, em 26 de abril de 1986. A história acompanha os eventos logo após a explosão do reator número 4 — o caos, as tentativas do governo soviético de esconder a gravidade do acidente e o enorme esforço de cientistas, bombeiros, militares e trabalhadores que arriscaram, e muitas vezes perderam, suas vidas para evitar uma catástrofe ainda maior. A série também segue o cientista Valery Legasov, que tenta descobrir a verdadeira causa do acidente e expor a verdade por trás da tragédia.&lt;/p&gt;

&lt;p&gt;Mas o ponto aqui vai além da série.&lt;/p&gt;

&lt;p&gt;O que mais me chamou atenção foi o quanto aquela tragédia conversa com algo que vivemos diariamente no desenvolvimento de software: &lt;strong&gt;a responsabilidade nas decisões&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  O que aconteceu em Chernobyl?
&lt;/h2&gt;

&lt;p&gt;Mesmo sabendo que existem elementos dramatizados, dá para aprender muita coisa com esse desastre. Não sou física nuclear, mas vou tentar resumir o que aconteceu — porque foi justamente essa parte que mais me fez refletir sobre responsabilidade e tomada de decisão.&lt;/p&gt;

&lt;p&gt;Na madrugada do dia &lt;strong&gt;26 de abril de 1986&lt;/strong&gt;, os operadores da usina realizavam um &lt;strong&gt;teste de segurança no reator 4&lt;/strong&gt;. O objetivo era validar se, em caso de queda de energia, as turbinas ainda conseguiriam gerar eletricidade por alguns segundos — tempo suficiente até que os geradores de emergência fossem acionados. No papel, o teste parecia simples.&lt;/p&gt;

&lt;p&gt;O problema é que, para executá-lo, diversos sistemas de segurança foram desativados e a potência do reator foi reduzida para um nível muito abaixo do ideal. Foi aí que tudo começou a sair do controle.&lt;/p&gt;

&lt;p&gt;O reator utilizado era do tipo &lt;strong&gt;RBMK&lt;/strong&gt;, um modelo com uma falha crítica de projeto: em determinadas condições, quanto mais vapor era gerado dentro do sistema, maior ficava a potência do reator. Em vez de estabilizar, ele se tornava cada vez mais instável. Para piorar, o teste foi conduzido sob forte pressão da liderança, mesmo diante de sinais claros de que não era seguro continuar.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1ar1wiltc1iy8426lz9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fk1ar1wiltc1iy8426lz9.png" alt="Chernobyl" width="800" height="640"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Ao longo da série, vemos os próprios operadores levantando preocupações — que foram ignoradas. Quando perceberam que a situação era crítica, acionaram o botão de desligamento de emergência. Em teoria, esse comando deveria encerrar a reação. Mas, por uma falha no design das barras de controle, o efeito inicial foi o oposto: a potência disparou. Em poucos segundos, temperatura e pressão subiram de forma descontrolada.&lt;/p&gt;

&lt;p&gt;O resultado foram duas explosões que destruíram o topo do reator, expuseram o núcleo à atmosfera e liberaram uma enorme quantidade de material radioativo. O incêndio que se seguiu espalhou radiação por boa parte da Europa, transformando Chernobyl no maior desastre nuclear da história.&lt;/p&gt;

&lt;p&gt;O que mais me impactou foi perceber que a tragédia não aconteceu por um único erro. Ela foi consequência de uma &lt;strong&gt;cadeia de decisões ruins&lt;/strong&gt;: falhas técnicas ignoradas, riscos mal avaliados e pessoas que não foram ouvidas.&lt;/p&gt;




&lt;h2&gt;
  
  
  E o que isso tem a ver com software?
&lt;/h2&gt;

&lt;p&gt;Depois de assistir à série, comecei a fazer um paralelo com a nossa área. Porque, no fim, quantas vezes um incidente em produção também não nasce da mesma forma?&lt;/p&gt;

&lt;p&gt;Nem sempre o problema vem de um único bug. Muitas vezes, ele é o resultado de uma sequência de decisões tomadas sem o devido cuidado: um requisito mal definido, um risco não mapeado, uma validação superficial, uma entrega apressada — ou um alerta levantado pelo time que acabou sendo ignorado.&lt;/p&gt;

&lt;p&gt;Foi aí que a série me fez enxergar algo que vai além do contexto dela: &lt;strong&gt;quando a pressa fala mais alto do que a análise, o custo quase sempre aparece depois&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  O maior problema: risco tratado de forma rasa
&lt;/h2&gt;

&lt;p&gt;Um dos maiores desafios que vejo hoje no desenvolvimento de software é justamente o planejamento e o levantamento de riscos feitos de maneira superficial.&lt;/p&gt;

&lt;p&gt;Tenho certeza de que você vai concordar: não tem coisa pior do que refazer algo ou ficar apagando incêndios que poderiam ter sido discutidos antes.&lt;/p&gt;

&lt;p&gt;Vivemos num mundo onde tempo é dinheiro. E é exatamente por isso que qualidade precisa estar presente desde o início — não como uma etapa final, mas como parte do processo.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomhezjqhepvjcvwbmn1d.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fomhezjqhepvjcvwbmn1d.png" alt="Cadeia de erros" width="800" height="729"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Como QA, vou compartilhar duas coisas que considero essenciais.&lt;/p&gt;

&lt;h3&gt;
  
  
  Ouça sua equipe
&lt;/h3&gt;

&lt;p&gt;Independentemente da sua posição no time, ouça as pessoas ao seu redor.&lt;/p&gt;

&lt;p&gt;Ninguém conhece melhor o produto do que quem o construiu, testou e convive com ele diariamente. Antes de uma nova funcionalidade entrar ou de um teste ser executado, converse com o time.&lt;/p&gt;

&lt;p&gt;Escute quem desenvolveu. Escute o produto. Escute suporte. Escute quem está mais próximo do usuário.&lt;/p&gt;

&lt;p&gt;Muitas vezes, o risco já foi identificado por alguém. Ele só não foi ouvido.&lt;/p&gt;

&lt;h3&gt;
  
  
  Analise antes de agir
&lt;/h3&gt;

&lt;p&gt;Entender o impacto de uma mudança não é só uma questão técnica — é também uma questão de negócio.&lt;/p&gt;

&lt;p&gt;Por exemplo: você vai melhorar a query de uma API. Em teoria, isso pode não alterar nenhuma regra de negócio. Mas como isso afeta o usuário final? A performance realmente melhorou? Existe algum impacto colateral? Como vamos medir se essa mudança foi positiva ou negativa?&lt;/p&gt;

&lt;p&gt;Nem toda melhoria técnica gera melhoria de produto. E esse olhar crítico é parte fundamental do papel de QA.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusão
&lt;/h2&gt;

&lt;p&gt;No fim, &lt;em&gt;Chernobyl&lt;/em&gt; me fez refletir sobre algo que vai muito além de uma série: &lt;strong&gt;a responsabilidade por trás de cada decisão que tomamos no dia a dia&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A gente não está lidando com um reator nuclear. Mas ainda assim lida com impacto real — no usuário, no negócio e no próprio time. Um risco ignorado, um teste mal planejado ou uma decisão tomada sem ouvir a equipe podem não gerar uma catástrofe, mas certamente geram problemas que poderiam ter sido evitados com mais atenção, diálogo e análise.&lt;/p&gt;

&lt;p&gt;Para mim, ser QA vai muito além de encontrar bugs.&lt;/p&gt;

&lt;p&gt;É questionar antes que o problema aconteça. É analisar cenários com olhar crítico. É antecipar riscos. É provocar conversas importantes dentro do time. É ajudar a construir decisões mais seguras e conscientes.&lt;/p&gt;

&lt;p&gt;No final, qualidade não é apenas sobre software funcionando.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;É sobre responsabilidade, colaboração e cuidado com tudo aquilo que criamos.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>testing</category>
      <category>qa</category>
      <category>software</category>
    </item>
    <item>
      <title>De front-end para UX, e de volta ao código: o que significa ser Design Engineer em 2026</title>
      <dc:creator>vitoriazzp</dc:creator>
      <pubDate>Fri, 03 Apr 2026 13:00:00 +0000</pubDate>
      <link>https://dev.to/he4rt/de-front-end-para-ux-e-de-volta-ao-codigo-o-que-significa-ser-design-engineer-em-2026-3j74</link>
      <guid>https://dev.to/he4rt/de-front-end-para-ux-e-de-volta-ao-codigo-o-que-significa-ser-design-engineer-em-2026-3j74</guid>
      <description>&lt;p&gt;&lt;strong&gt;Sou UX/UI designer, mas antes disso fui front-end.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Atuei cerca de 5 anos trabalhando com HTML e CSS, transformando layouts em páginas, entendendo hierarquia de informação e estrutura de interface. Depois, tomei o caminho oposto: migrei para UX/UI e agora completo 5 anos atuando em produtos, passando por fintech, utilities e atuando como Product Designer.&lt;/p&gt;

&lt;p&gt;Essa trajetória, de front para UX e agora voltando a se aproximar do código, é justamente o que me levou a me reconhecer em um termo que gosto bastante: &lt;strong&gt;Design Engineer&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  Minha trajetória: 5 anos de front-end, 5 anos de UX
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Quando era front-end&lt;/strong&gt;, eu via a tela como um resultado de código: HTML estruturando a informação, CSS dando forma e layout, um pouco de JavaScript dando comportamento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Quando migrei para UX&lt;/strong&gt;, passei a olhar mais para o todo do produto: pesquisa, fluxos, contexto do usuário, design systems, governança, revisão de interfaces, conversa com times de produto e de negócios.&lt;br&gt;
Hoje, percebo que essas duas visões não são opostas. &lt;em&gt;Elas se completam.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é design engineer em 2026?
&lt;/h2&gt;

&lt;p&gt;Se você pesquisar sobre "Design Engineer", vai encontrar muitas definições técnicas, mas na prática o que mais faz sentido para mim é:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A pessoa que entende UX, código e um pouco de backend ao mesmo tempo, e usa isso para desenhar interfaces que são pensadas desde o primeiro pixel até a última chamada de API.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Não é só "quem desenha + quem programa"&lt;/strong&gt;&lt;br&gt;
É quem pensa em &lt;strong&gt;&lt;em&gt;experiência e implementação juntas&lt;/em&gt;&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Um botão que não só parece bem-desenhado, mas também considera estados de loading, erro, disabled.&lt;/li&gt;
&lt;li&gt;Um fluxo de cadastro que não só é bonito, mas que já prevê o que o backend vai precisar para validar, salvar e devolver feedback.&lt;/li&gt;
&lt;li&gt;Um produto que pensa em performance, acessibilidade e usabilidade em uma única conversa.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Como front-end e UX mudam sua forma de ver produto
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Como eu via a tela como desenvolvedor front-end&lt;/strong&gt;&lt;br&gt;
HTML estruturando a informação. CSS dando forma. Um pouco de JavaScript dando comportamento. A tela era resultado direto do código.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O que mudou quando migrei para UX/UI&lt;/strong&gt;&lt;br&gt;
Passei a olhar para o produto como um todo: pesquisa, fluxos, contexto do usuário. O código virou uma consequência, não o ponto de partida.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O que muda agora é que percebo que não preciso mais ficar só de um lado&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Por que estou voltando ao código agora
&lt;/h2&gt;

&lt;p&gt;Com o avanço de ferramentas como IA integrada ao Figma, prototipagem cada vez mais próxima do código e experiências acumuladas como Product Designer, foi aí que o conceito de Design Engineer passou a fazer sentido de verdade pra mim.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JavaScript moderno, React e um pouco de backend&lt;/strong&gt;&lt;br&gt;
Comecei a estudar de forma mais focada para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Atualizar meu JavaScript (ES2024, async/await, fetch, arrays/objetos)&lt;/li&gt;
&lt;li&gt;Reativar React (componentes, hooks, estado compartilhado)&lt;/li&gt;
&lt;li&gt;Entender backend leve (Node/Express, rotas simples, persistência básica)&lt;/li&gt;
&lt;li&gt;Pensar em performance e otimização das interfaces que ajudo a construir&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Como estou usando IA no processo de aprendizado
&lt;/h3&gt;

&lt;p&gt;Hoje, uso o Claude AI não como substituto, mas como apoio para:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quebrar problemas de código em etapas menores&lt;/li&gt;
&lt;li&gt;Revisar fluxos de dados entre front e backend&lt;/li&gt;
&lt;li&gt;Organizar pensamentos e lógica de features&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Design Engineer não é um cargo. É uma forma de pensar
&lt;/h3&gt;

&lt;p&gt;O que mais gosto de dizer é que, em 2026, &lt;em&gt;&lt;strong&gt;Design Engineer não é só um título de empresa grande&lt;/strong&gt;&lt;/em&gt; ou de time específico. Pode existir em qualquer lugar onde UX e código se encontram:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Em um produto próprio&lt;/li&gt;
&lt;li&gt;Em um projeto freelancer&lt;/li&gt;
&lt;li&gt;Em um repositório público&lt;/li&gt;
&lt;li&gt;Em um fluxo de trabalho híbrido, mesmo sem ter um cargo oficial com esse nome&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O que importa é a &lt;strong&gt;maneira de pensar&lt;/strong&gt;: UX + código ao mesmo tempo. Protótipos e implementação como parte do mesmo processo. Interfaces que consideram o que o usuário sente e o que o backend precisa.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que vem por aí: UX data-driven e componentes React
&lt;/h2&gt;

&lt;p&gt;Esse movimento não é só "voltar" ao front-end. É re-aprender JavaScript, entender melhor React e backend, e levar essa visão de UX para dentro do código.&lt;br&gt;
Em breve, quero escrever mais sobre:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Como transformo um fluxo de UX do Figma em componentes React&lt;/li&gt;
&lt;li&gt;Como penso em performance e carregamento em UX para web&lt;/li&gt;
&lt;li&gt;Como organizar estudos de UX + código em ciclos curtos e práticos&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E numa parte que ainda não me sinto confortável, mas sei que é essencial: vou falar sobre como estou começando a me tornar uma &lt;strong&gt;UX mais data-driven&lt;/strong&gt;, porque ser Design Engineer em 2026 também é aprender a ouvir o que os números dizem sobre o UX que eu desenho.&lt;/p&gt;

&lt;h2&gt;
  
  
  Você também está nesse meio-termo entre UX e código?
&lt;/h2&gt;

&lt;p&gt;Se você chegou até aqui, é bem provável que também se sinta em algum meio-termo entre UX e código.&lt;/p&gt;

&lt;p&gt;Que tal comentar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Como você se vê hoje entre design e desenvolvimento?&lt;/li&gt;
&lt;li&gt;Você já tentou voltar pro código depois de virar UX, ou está no caminho invertido?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esse é o tipo de conversa que me ajuda a entender como o conceito de &lt;strong&gt;Design Engineer&lt;/strong&gt; está se moldando, bem além de títulos de empresa.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>design</category>
      <category>ux</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Construí um gerador de playlists no Spotify com Claude</title>
      <dc:creator>Leo Garcez</dc:creator>
      <pubDate>Tue, 24 Mar 2026 02:01:08 +0000</pubDate>
      <link>https://dev.to/he4rt/construi-um-gerador-de-playlists-no-spotify-com-claude-18ge</link>
      <guid>https://dev.to/he4rt/construi-um-gerador-de-playlists-no-spotify-com-claude-18ge</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;Eu queria digitar &lt;em&gt;“noite chuvosa, meio melancólica”&lt;/em&gt; e receber uma playlist perfeita. Então eu construí isso.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Eu construí um gerador de playlists com IA usando &lt;strong&gt;Claude + Spotify API&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Você descreve um humor → ele gera 50 músicas → salva direto no Spotify&lt;/li&gt;
&lt;li&gt;O maior problema foi OAuth local com NextAuth (sim, foi um inferno)&lt;/li&gt;
&lt;li&gt;Claude funciona bem, mas precisa de bastante controle pra não inventar músicas&lt;/li&gt;
&lt;li&gt;Streaming com SSE melhorou muito a UX&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Links:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://moodify.com.br" rel="noopener noreferrer"&gt;Demo&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/LeoGarcez/moodify" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://open.spotify.com/playlist/0L2bStHbYyfiGJFZfB1CDO?si=a93194d73eec484c" rel="noopener noreferrer"&gt;Playlist de exemplo&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Índice
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;A Ideia&lt;/li&gt;
&lt;li&gt;A Stack&lt;/li&gt;
&lt;li&gt;O Problema de Trabalhar com OAuth Localmente&lt;/li&gt;
&lt;li&gt;Endpoints Deprecated do Spotify&lt;/li&gt;
&lt;li&gt;Spotify em Produção&lt;/li&gt;
&lt;li&gt;Fazendo o Claude Obedecer&lt;/li&gt;
&lt;li&gt;Prompt Engineering Anti-Alucinação&lt;/li&gt;
&lt;li&gt;Modo Related Artists&lt;/li&gt;
&lt;li&gt;Construindo o Perfil Musical&lt;/li&gt;
&lt;li&gt;Streaming com SSE&lt;/li&gt;
&lt;li&gt;O Que Aprendi&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A Ideia
&lt;/h2&gt;

&lt;p&gt;Fazer um gerador de playlists que realmente &lt;em&gt;entendesse&lt;/em&gt; vibes, não só tags de gênero. Algo tipo: você digita &lt;strong&gt;"tarde fria num apartamento vazio"&lt;/strong&gt; e recebe uma playlist boa de verdade, já salva no seu Spotify.&lt;/p&gt;

&lt;p&gt;O conceito:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Usuário descreve um humor&lt;/li&gt;
&lt;li&gt;Claude retorna 50 músicas em JSON&lt;/li&gt;
&lt;li&gt;App busca cada faixa no Spotify&lt;/li&gt;
&lt;li&gt;Cria e salva a playlist na conta do usuário&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Três APIs, um app.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Stack
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Next.js 14 (App Router)
NextAuth v5 beta
Anthropic Claude API (claude-sonnet-4-6)
Spotify Web API
Supabase (PostgreSQL)
TypeScript + Tailwind CSS + Framer Motion
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Escolhi o Claude porque ele tá bem em alta agora e, na prática, é confiável, alucina menos do que eu esperava pra esse tipo de tarefa. Ele é um pouco menos criativo que o GPT nas recomendações, mas compensa sendo mais previsível no formato das respostas, o que importa bastante quando você tá parseando JSON.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Problema de Trabalhar com OAuth Localmente
&lt;/h2&gt;

&lt;p&gt;Isso me custou algumas horas e sessões de debug. Vou detalhar porque tem várias camadas de problema e você provavelmente vai bater na mesma parede se estiver usando NextAuth v5 com Spotify.&lt;/p&gt;

&lt;h3&gt;
  
  
  O Spotify não aceita &lt;code&gt;localhost&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;O Spotify &lt;a href="https://developer.spotify.com/documentation/web-api/concepts/redirect_uri" rel="noopener noreferrer"&gt;proíbe &lt;code&gt;localhost&lt;/code&gt; como redirect URI&lt;/a&gt; pra URIs de loopback. A solução é usar &lt;code&gt;127.0.0.1&lt;/code&gt;. Cadastrei &lt;code&gt;http://127.0.0.1:3000/api/auth/callback/spotify&lt;/code&gt; no dashboard e setei &lt;code&gt;AUTH_URL=http://127.0.0.1:3000&lt;/code&gt; no &lt;code&gt;.env.local&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Não foi suficiente.&lt;/p&gt;

&lt;h3&gt;
  
  
  O &lt;code&gt;NextRequest&lt;/code&gt; normaliza URLs pra &lt;code&gt;localhost&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;O Next.js, independente do host que você passa pra &lt;code&gt;next dev -H&lt;/code&gt;, normaliza &lt;code&gt;req.url&lt;/code&gt; e &lt;code&gt;req.nextUrl.href&lt;/code&gt; de volta pra &lt;code&gt;localhost&lt;/code&gt; em desenvolvimento. Isso não é bug documentado — é comportamento interno do framework.&lt;/p&gt;

&lt;p&gt;O NextAuth v5 tem um utilitário chamado &lt;code&gt;reqWithEnvURL&lt;/code&gt; que tenta corrigir exatamente isso, mas falha silenciosamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Dentro do next-auth — simplificado&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reqWithEnvURL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;url&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_URL&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ← o construtor normaliza de volta pra localhost&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mesmo passando &lt;code&gt;127.0.0.1&lt;/code&gt; explicitamente, o construtor do &lt;code&gt;NextRequest&lt;/code&gt; sobrescreve. A "correção" não funciona.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dois momentos onde o redirect URI importa
&lt;/h3&gt;

&lt;p&gt;O OAuth tem &lt;strong&gt;dois&lt;/strong&gt; momentos distintos onde o redirect URI aparece:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Requisição de autorização&lt;/strong&gt; — a URL do Spotify onde o usuário loga. O &lt;code&gt;redirect_uri&lt;/code&gt; aqui vem dos seus params de configuração, então você pode hardcodar &lt;code&gt;127.0.0.1&lt;/code&gt; na config do provider. Isso funcionou.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Troca de token&lt;/strong&gt; — quando o Spotify manda o código de volta, o &lt;code&gt;@auth/core&lt;/code&gt; envia um POST pra trocar o código por tokens. O &lt;code&gt;redirect_uri&lt;/code&gt; nessa requisição vem de &lt;code&gt;provider.callbackUrl&lt;/code&gt;, que é derivado de &lt;code&gt;params.url.origin&lt;/code&gt; — ou seja, da &lt;strong&gt;URL da requisição de callback&lt;/strong&gt;. Se essa URL ainda diz &lt;code&gt;localhost&lt;/code&gt;, a troca falha com &lt;code&gt;invalid_grant: Invalid redirect URI&lt;/code&gt;.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O sintoma era desconcertante: a URL de autorização mostrava &lt;code&gt;127.0.0.1&lt;/code&gt; corretamente, mas a troca de token continuava falhando. Fui atrás do código do &lt;code&gt;@auth/core&lt;/code&gt; pra entender o que tava acontecendo.&lt;/p&gt;

&lt;h3&gt;
  
  
  A solução: &lt;code&gt;Auth()&lt;/code&gt; direto com &lt;code&gt;Request&lt;/code&gt; nativo
&lt;/h3&gt;

&lt;p&gt;Objetos &lt;code&gt;Request&lt;/code&gt; nativos do browser/Node &lt;strong&gt;não normalizam URLs&lt;/strong&gt;. A correção é contornar os route handlers do NextAuth e chamar &lt;code&gt;Auth()&lt;/code&gt; do &lt;code&gt;@auth/core&lt;/code&gt; diretamente, passando um &lt;code&gt;Request&lt;/code&gt; nativo com a URL já corrigida:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/app/api/auth/[...nextauth]/route.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Auth&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@auth/core&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;authConfig&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../../../../../auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;next/server&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;buildRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authOrigin&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_URL&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="s2"&gt;`http://&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fixedUrl&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;url&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/^https&lt;/span&gt;&lt;span class="se"&gt;?&lt;/span&gt;&lt;span class="sr"&gt;:&lt;/span&gt;&lt;span class="se"&gt;\/\/[^/]&lt;/span&gt;&lt;span class="sr"&gt;+/&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;authOrigin&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;hasBody&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;GET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;HEAD&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fixedUrl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;method&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;body&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;hasBody&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;undefined&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="c1"&gt;// duplex necessário para streaming de body no Node.js&lt;/span&gt;
    &lt;span class="p"&gt;...(&lt;/span&gt;&lt;span class="nx"&gt;hasBody&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;duplex&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;half&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextRequest&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
  &lt;span class="nc"&gt;Auth&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;buildRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;authConfig&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nb"&gt;Parameters&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="k"&gt;typeof&lt;/span&gt; &lt;span class="nx"&gt;Auth&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;GET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;handler&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;POST&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Versão do &lt;code&gt;@auth/core&lt;/code&gt;:&lt;/strong&gt; ao usar &lt;code&gt;Auth()&lt;/code&gt; diretamente, instale a versão exata que o &lt;code&gt;next-auth&lt;/code&gt; usa internamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;ls&lt;/span&gt; @auth/core  &lt;span class="c"&gt;# veja qual versão o next-auth requer&lt;/span&gt;
npm &lt;span class="nb"&gt;install&lt;/span&gt; @auth/core@0.41.0 &lt;span class="nt"&gt;--save-exact&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Versões diferentes criam conflitos de tipo que explodem em runtime.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Configuração explícita no &lt;code&gt;auth.ts&lt;/code&gt;:&lt;/strong&gt; ao contornar os handlers do NextAuth, &lt;code&gt;setEnvDefaults&lt;/code&gt; não roda mais. Configure &lt;code&gt;basePath&lt;/code&gt;, &lt;code&gt;secret&lt;/code&gt; e &lt;code&gt;redirect_uri&lt;/code&gt; explicitamente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// auth.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;authConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;NextAuthConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;trustHost&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;basePath&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/auth&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="nc"&gt;Spotify&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;clientId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_SPOTIFY_ID&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;clientSecret&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_SPOTIFY_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;url&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;https://accounts.spotify.com/authorize&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;params&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="na"&gt;scope&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SPOTIFY_SCOPES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;show_dialog&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="na"&gt;redirect_uri&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;AUTH_URL&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;/api/auth/callback/spotify`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="c1"&gt;// ... callbacks e pages como antes&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Bônus: problema de domínio do cookie PKCE
&lt;/h3&gt;

&lt;p&gt;Mesmo com tudo acima, pode acontecer mais uma falha: se o usuário acessa &lt;code&gt;http://localhost:3000&lt;/code&gt;, o navegador seta o cookie PKCE pro domínio &lt;code&gt;localhost&lt;/code&gt;. Quando o Spotify redireciona de volta pra &lt;code&gt;http://127.0.0.1:3000/...&lt;/code&gt;, o navegador não envia o cookie — domínios diferentes — e o &lt;code&gt;code_verifier&lt;/code&gt; some. A troca falha de novo.&lt;/p&gt;

&lt;p&gt;A correção é garantir que &lt;code&gt;localhost:3000&lt;/code&gt; nunca apareça pro usuário, redirecionando via &lt;code&gt;next.config.mjs&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;redirects&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/:path*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;has&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:3000/:path*&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;has&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;host&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost:3000&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt;
      &lt;span class="na"&gt;destination&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;http://127.0.0.1:3000/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;permanent&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;},&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso, todo o fluxo de auth fica em &lt;code&gt;127.0.0.1&lt;/code&gt; e os cookies PKCE chegam onde precisam chegar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Endpoints Deprecated do Spotify
&lt;/h2&gt;

&lt;p&gt;Com o auth funcionando, bati num muro de 403s.&lt;/p&gt;

&lt;p&gt;O Spotify deprecated alguns endpoints sem muito alarde:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Antigo (deprecated)&lt;/th&gt;
&lt;th&gt;Novo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;POST /playlists/{id}/tracks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;POST /playlists/{id}/items&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;GET /playlists/{id}/tracks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;&lt;code&gt;GET /playlists/{id}/items&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;GET /audio-features&lt;/code&gt;, &lt;code&gt;GET /recommendations&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Deprecated, sem substituto&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A migração &lt;code&gt;/tracks&lt;/code&gt; → &lt;code&gt;/items&lt;/code&gt; está documentada, mas é fácil de perder se você seguiu um tutorial de 2022. Audio features e recomendações sumindo foi mais chato — tive que construir contexto de energia de outra forma, mais sobre isso abaixo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Spotify em Produção
&lt;/h2&gt;

&lt;p&gt;Tem uma limitação que eu não vi muito discutida: no modo de desenvolvimento, o Spotify permite apenas &lt;strong&gt;5 usuários autenticados&lt;/strong&gt; E eles precisam ser adicionados manualmente via allowlist no dashboard.&lt;/p&gt;

&lt;p&gt;Pra ir além disso, você precisa solicitar Extended Quota Mode. E o processo atual é bem pesado:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Entidade jurídica registrada (pessoa física não é aceita desde maio de 2025)&lt;/li&gt;
&lt;li&gt;Serviço já lançado e ativo&lt;/li&gt;
&lt;li&gt;Mínimo de &lt;strong&gt;250.000 usuários ativos mensais&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Viabilidade comercial&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A análise pode levar até seis semanas, e sem garantia de aprovação.&lt;/p&gt;

&lt;p&gt;Na prática, isso significa que se você tá construindo algo novo como indie dev, vai ficar travado em modo de desenvolvimento. Você consegue testar e mostrar pra até 4 pessoas além de você — e só. Vale saber disso antes de planejar algum lançamento público.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fazendo o Claude Obedecer
&lt;/h2&gt;

&lt;p&gt;O system prompt instrui o Claude a retornar &lt;strong&gt;apenas&lt;/strong&gt; um array JSON, sem markdown, sem explicações. Essa parte é direta. O problema mais difícil é conseguir 50 faixas &lt;em&gt;que realmente existam&lt;/em&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  One-shot prompting
&lt;/h3&gt;

&lt;p&gt;Incluir uma conversa de exemplo completa (usuário + assistente) antes do request real reduziu bastante os erros de formato, especialmente com artistas não-ingleses:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Create a playlist for this mood/vibe: late night drive, nostalgic&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;assistant&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Drive&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;The Cars&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Synth-pop clássico com energia perfeita de madrugada&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;title&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Running Up That Hill&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Kate Bush&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Art-pop etéreo, emocionalmente assombroso&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
      &lt;span class="c1"&gt;// ...&lt;/span&gt;
    &lt;span class="p"&gt;]),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;actualUserMessage&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Extração de JSON como fallback
&lt;/h3&gt;

&lt;p&gt;Mesmo com prompting cuidadoso, modelos eventualmente jogam texto de introdução. Sempre extraia o array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;match&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;raw&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;match&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="se"&gt;\[[\s\S]&lt;/span&gt;&lt;span class="sr"&gt;*&lt;/span&gt;&lt;span class="se"&gt;\]&lt;/span&gt;&lt;span class="sr"&gt;/&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Nenhum array JSON encontrado&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;suggestions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;match&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Prompt Engineering Anti-Alucinação
&lt;/h2&gt;

&lt;p&gt;Descobri que &lt;strong&gt;quanto mais músicas você pede, mais o modelo inventa títulos&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Pedindo 70 músicas, o Claude começa a criar faixas com nomes plausíveis que não existem. Com 50 e restrições explícitas, fica bem melhor.&lt;/p&gt;

&lt;p&gt;O que adicionei ao system prompt:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;EXISTÊNCIA NO SPOTIFY — CRÍTICO:
Cada música DEVE existir no Spotify. Antes de incluir uma faixa, pergunte:
"Tenho certeza que esta música existe no Spotify com este título e artista exatos?"
Se houver qualquer dúvida, escolha outra música que você tem certeza.

REGRAS DE FORMATO DO TÍTULO:
- Use apenas o título canônico limpo do lançamento
- SEM sufixos: sem "- Remastered", "- Live at...", "- Radio Edit"
- Use o nome do lançamento mais conhecido, não compilações

ARMADILHAS COMUNS DE ALUCINAÇÃO:
- Não invente títulos de músicas que parecem plausíveis mas podem não existir
- Não confunda dois artistas com nomes similares
- Não sugira deep cuts que você não tem certeza
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Também removi a abordagem de duas etapas "gerar 70, refinar pra 50". Era cara em tempo e custo, e uma única geração de 50 com boas instruções performa melhor.&lt;/p&gt;

&lt;h3&gt;
  
  
  O system prompt atual completo
&lt;/h3&gt;

&lt;p&gt;Esse é o prompt que tá rodando em produção hoje:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;You are a world-class Spotify playlist curator.

GOAL:
Generate EXACTLY 50 high-quality songs for a playlist.

OUTPUT:
Return ONLY a raw JSON array. No markdown, no explanations.

Each item:
- "title": string — the canonical Spotify title, nothing else
- "artist": string — the primary artist exactly as listed on Spotify
- "reason": string — max 10 words

SPOTIFY EXISTENCE — CRITICAL:
Every song MUST exist on Spotify. Before including a track, ask yourself:
"Am I certain this song exists on Spotify under this exact title and artist?"
If there is any doubt, pick a different song you are certain about.

TITLE FORMAT RULES:
- Use the clean, canonical release title only
- NO suffixes: no "- Remastered", "- Live at...", "- Radio Edit", "- feat. X"
- NO parentheticals unless part of the official title
- Use the most well-known release name, not compilations or bonus versions

COMMON HALLUCINATION TRAPS TO AVOID:
- Do not invent song titles that sound plausible but may not exist
- Do not confuse two artists with similar names
- Do not suggest deep cuts you are uncertain about
- Do not suggest songs only released in specific regions unavailable globally

DISTRIBUTION:
- 50% recognizable hits (high confidence they exist)
- 40% lesser-known but confirmed tracks
- 10% deep cuts you are fully certain about

DIVERSITY:
- At least 2 genres
- At least 3 decades
- Non-English tracks welcome if you are certain they are on Spotify

CURATION:
- Cohesive flow, playlist-worthy, non-random
- No duplicates

Return ONLY the JSON array. Exactly 50 items.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Curiosidade: o prompt tá em inglês mesmo que o usuário escreva em português. O Claude entende o humor no idioma que vier e retorna os dados no formato esperado sem problema.&lt;/p&gt;

&lt;h3&gt;
  
  
  Melhor resolução de busca no Spotify
&lt;/h3&gt;

&lt;p&gt;Mesmo com um bom prompt, algumas faixas voltam com títulos ou artistas levemente errados. Três ajustes no lado da busca:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Buscar 10 candidatos em vez de 1&lt;/strong&gt;&lt;br&gt;
Em vez de &lt;code&gt;limit=1&lt;/code&gt;, buscar &lt;code&gt;limit=10&lt;/code&gt; e escolher o melhor match.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Filtro de popularidade&lt;/strong&gt;&lt;br&gt;
Pular resultados com &lt;code&gt;popularity &amp;lt; 30&lt;/code&gt; — evita gravar versões ao vivo obscuras quando a faixa correta não é encontrada:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;POPULARITY_FLOOR&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;aboveFloor&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;filter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;t&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;t&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;popularity&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="nx"&gt;POPULARITY_FLOOR&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pool&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aboveFloor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="nx"&gt;aboveFloor&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;candidates&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;3. Fuzzy matching + seleção por popularidade&lt;/strong&gt;&lt;br&gt;
Normalizar strings e verificar correspondência bidirecional de substring, depois escolher o match com maior popularidade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fuzzyMatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SpotifyTrack&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;suggestion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ClaudeTrackSuggestion&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;trackName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeStr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;artistName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeStr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;candidate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;artists&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;]?.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="dl"&gt;""&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sugTitle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeStr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;suggestion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;sugArtist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalizeStr&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;suggestion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;artist&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;titleMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;trackName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sugTitle&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;sugTitle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;trackName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;artistMatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;artistName&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sugArtist&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;sugArtist&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;includes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;artistName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;titleMatch&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;artistMatch&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Modo Related Artists
&lt;/h2&gt;

&lt;p&gt;Playlists geradas por IA alucinam mais quando o humor é específico de artista — tipo "algo como Radiohead". Nesses casos, o grafo de artistas do próprio Spotify é mais confiável que o Claude.&lt;/p&gt;

&lt;p&gt;Adicionei detecção automática: uma chamada rápida ao Claude Haiku (~0,5s) classifica o prompt antes da geração principal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Retorna { mode: "ai" | "related", artists: ["Radiohead", "Nick Cave"] }&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;detected&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;detectPlaylistMode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;mood&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se artistas são detectados, o app:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Os resolve no Spotify&lt;/li&gt;
&lt;li&gt;Busca artistas relacionados (&lt;code&gt;GET /artists/{id}/related-artists&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Pega top tracks de cada artista relacionado&lt;/li&gt;
&lt;li&gt;Monta uma playlist com dados reais do Spotify, sem depender do Claude pra nada&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Se não detectar artistas, cai pro fluxo normal com Claude.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;detected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;mode&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;related&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;detected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;artists&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolvedSeeds&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;buildRelatedArtistsPlaylist&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;detected&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;artists&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;energy&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;NextResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;mode&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;related&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;seedArtists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;resolvedSeeds&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="c1"&gt;// fallthrough para modo AI se não encontrou nada&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Construindo o Perfil Musical
&lt;/h2&gt;

&lt;p&gt;O app deixa usuários escolher uma playlist de referência ou seus top artistas do Spotify (último mês / 6 meses / histórico completo). Esse contexto é passado pro Claude como uma impressão digital musical.&lt;/p&gt;

&lt;p&gt;Mandar nomes de faixas brutos confunde o modelo. Em vez disso, agregue num perfil:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Contar frequência de artistas em todas as faixas da playlist&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;artistFreq&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nb"&gt;Map&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nl"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;tracks&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;artists&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prev&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;artistFreq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;artistFreq&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;a&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prev&lt;/span&gt;&lt;span class="p"&gt;?.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pra playlists, também busco dados de gênero dos artistas principais via &lt;code&gt;GET /artists/{id}&lt;/code&gt; (5 chamadas paralelas), já que os itens de playlist não retornam gêneros nativamente.&lt;/p&gt;

&lt;p&gt;Mesmo sem seleção de referência explícita, o app passa os top artistas e gêneros baseline do usuário como contexto suave pra cada geração.&lt;/p&gt;

&lt;p&gt;A instrução no prompt mudou de "não recomende esses artistas" pra algo mais útil:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;→ Biase as recomendações em direção a este DNA musical: tempo, humor e estilo de produção similar.
→ Descubra artistas com som SIMILAR — não necessariamente os mesmos artistas.
→ NÃO repita faixas já listadas acima.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Streaming com SSE
&lt;/h2&gt;

&lt;p&gt;O fluxo original: buscar todas as 50 faixas em paralelo, retornar tudo de uma vez, mostrar um spinner.&lt;/p&gt;

&lt;p&gt;O problema é que o usuário ficava olhando "Salvando no Spotify..." por 5-10 segundos sem nenhum feedback. Server-Sent Events resolve isso — cada faixa é emitida conforme resolve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Na rota da API&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;stream&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ReadableStream&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;start&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;send&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
      &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;enqueue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;encoder&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;encode&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`data: &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt;&lt;span class="s2"&gt;\n\n`&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nx"&gt;suggestions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;suggestion&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;searchTrack&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;suggestion&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;accessToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;foundTracks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;suggestion&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
          &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;track&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;track&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;suggestion&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt; &lt;span class="c1"&gt;// emitido imediatamente&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Criar playlist depois que todas as buscas terminam&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;playlist&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;createPlaylist&lt;/span&gt;&lt;span class="p"&gt;(...);&lt;/span&gt;
    &lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;done&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;playlist&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;found&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;foundTracks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="nx"&gt;controller&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;close&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;stream&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Content-Type&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text/event-stream&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Cache-Control&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;no-cache&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A busca paralela continua na velocidade máxima. Mas agora o cliente vê cada faixa aparecer com album art conforme resolve, com uma barra de progresso ao vivo — em vez de um spinner em branco por 10 segundos.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Que Aprendi
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Sobre prompt engineering:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Menos é mais (50 &amp;gt; 70)&lt;/li&gt;
&lt;li&gt;Exemplos one-shot (par de mensagens usuário + assistente) são mais confiáveis que instruções de formato detalhadas.&lt;/li&gt;
&lt;li&gt;Dizer o que não fazer funciona muito bem&lt;/li&gt;
&lt;li&gt;Contexto de perfil funciona melhor como guia de DNA musical, não como lista de restrições.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sobre a API do Spotify:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Sempre verifique se o endpoint ainda é atual. &lt;code&gt;/tracks&lt;/code&gt; → &lt;code&gt;/items&lt;/code&gt;, audio features sumiu, recomendações sumiram.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;GET /artists/{id}/related-artists&lt;/code&gt; funciona bem pra descoberta e quase ninguém usa.&lt;/li&gt;
&lt;li&gt;O score de popularidade nas faixas é um bom proxy pra "essa faixa existe como esperado."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sobre streaming no Next.js:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;ReadableStream&lt;/code&gt; + &lt;code&gt;text/event-stream&lt;/code&gt; funciona limpo no App Router.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Promise.all&lt;/code&gt; + emit-on-resolve te dá paralelismo real com UI progressiva de graça.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Sobre Next.js + OAuth:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NextRequest&lt;/code&gt; normaliza URLs pra &lt;code&gt;localhost&lt;/code&gt; em desenvolvimento, mesmo que você passe &lt;code&gt;127.0.0.1&lt;/code&gt; explicitamente. Use &lt;code&gt;Request&lt;/code&gt; nativo quando a URL importa.&lt;/li&gt;
&lt;li&gt;O NextAuth v5 tem um utilitário &lt;code&gt;reqWithEnvURL&lt;/code&gt; que tenta corrigir isso mas usa &lt;code&gt;new NextRequest()&lt;/code&gt; internamente — que normaliza de novo. A correção do framework não funciona.&lt;/li&gt;
&lt;li&gt;O OAuth tem &lt;strong&gt;dois&lt;/strong&gt; momentos onde o &lt;code&gt;redirect_uri&lt;/code&gt; é verificado: na requisição de autorização e na troca de token. Você precisa garantir que os dois mostrem &lt;code&gt;127.0.0.1&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;provider.callbackUrl&lt;/code&gt; é derivado da URL da requisição de callback — não da sua config. Se a URL da requisição ainda diz &lt;code&gt;localhost&lt;/code&gt;, a troca falha com &lt;code&gt;invalid_grant&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ao usar &lt;code&gt;Auth()&lt;/code&gt; do &lt;code&gt;@auth/core&lt;/code&gt; diretamente, pin a versão exata que o &lt;code&gt;next-auth&lt;/code&gt; requer. Versões diferentes causam conflitos de tipo em runtime.&lt;/li&gt;
&lt;li&gt;Cookies PKCE são scopados por domínio. Se o usuário começa em &lt;code&gt;localhost&lt;/code&gt; e o callback chega em &lt;code&gt;127.0.0.1&lt;/code&gt;, o &lt;code&gt;code_verifier&lt;/code&gt; some. Redirecione todo o tráfego de &lt;code&gt;localhost&lt;/code&gt; pra &lt;code&gt;127.0.0.1&lt;/code&gt; no &lt;code&gt;next.config.mjs&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;NextAuth v5 é poderoso mas a documentação beta é bem escassa. Ler o código-fonte do &lt;code&gt;@auth/core&lt;/code&gt; foi necessário pra entender o que estava acontecendo.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Teste Você Mesmo
&lt;/h2&gt;

&lt;p&gt;O app se chama &lt;strong&gt;Moodify&lt;/strong&gt;. Está OpenSource no &lt;a href="https://github.com/LeoGarcez/moodify" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;. Se você tá fazendo algo parecido, espero que ajude um pouco.&lt;/p&gt;




&lt;p&gt;Como vocês melhorariam esse prompt? Se alguém já passou por algo parecido ou tiver ideias, comenta aí&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Construído com Next.js, Claude API, Spotify Web API e Supabase. Deploy no Vercel.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>api</category>
      <category>nextjs</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Engenharia de Prompt: Por Que a Forma Como Você Pergunta Muda Tudo(Um guia introdutório)</title>
      <dc:creator>Fran Borges</dc:creator>
      <pubDate>Mon, 23 Mar 2026 16:20:34 +0000</pubDate>
      <link>https://dev.to/he4rt/engenharia-de-prompt-por-que-a-forma-como-voce-pergunta-muda-tudoum-guia-introdutorio-3hb0</link>
      <guid>https://dev.to/he4rt/engenharia-de-prompt-por-que-a-forma-como-voce-pergunta-muda-tudoum-guia-introdutorio-3hb0</guid>
      <description>&lt;p&gt;Neste artigo irei explicar alguns pontos importantes sobre Engenharia de prompt, e como saber esses pontos pode te ajudar muito no dia a dia lidando com IAs, no seus estudos, pesquisas, trabalho, vibeconding, whatever.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Prefácio&lt;/li&gt;
&lt;li&gt;Antes de tudo, para lembrar, o que é uma LLM mesmo?&lt;/li&gt;
&lt;li&gt;
Fazendo Perguntas

&lt;ul&gt;
&lt;li&gt;1 - Evite a Ambiguidade&lt;/li&gt;
&lt;li&gt;2 - Delimite o Escopo&lt;/li&gt;
&lt;li&gt;3 - Forneça Contexto Relevante&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Considerações Finais&lt;/li&gt;

&lt;/ul&gt;

&lt;h1&gt;
  
  
  Prefácio
&lt;/h1&gt;

&lt;p&gt;A base do conhecimento é necessária em tudo que se quer aprender, com LLMs não é diferente. Analisando padrões, conversando com amigos, observei que a grande parte das pessoas que usam LLMs no dia a dia de trabalho, estudo, pessoas que trabalham com tecnologia e principalmente quem está começando na área tech, não tem ideia de como a LLM funciona e de como fazer as perguntas e tirar dúvidas de forma certa para um ChatGPT da vida.&lt;/p&gt;

&lt;p&gt;Tendo isso em vista, e como venho estudando bastante sobre esse assunto, como forma de compartilhar o conhecimento, já dizia a poetisa brasileira Cora Coralina: &lt;em&gt;"Feliz aquele que transfere o que sabe, e aprende o que ensina"&lt;/em&gt;, farei uma série de artigos explicando sobre o tema, e esse é só o primeiro deles...&lt;/p&gt;

&lt;h1&gt;
  
  
  Antes de tudo, para lembrar, o que é uma LLM mesmo?
&lt;/h1&gt;

&lt;p&gt;A LLM pode ser definida de algumas formas. Uma delas é: &lt;em&gt;"São modelos de linguagem de máquina, que usam algoritmos de aprendizado profundo (Deep Learning) para processar e aprender a linguagem natural"&lt;/em&gt;, essa é a sua definição estrutural. A definição que mais gosto é: &lt;em&gt;"A LLM, na sua essência, é composta por dois arquivos: um contendo os pesos (parâmetros) com os conhecimentos aprendidos, e o outro com o código necessário para rodar os dados aprendidos."&lt;/em&gt;, como uma pessoa visual, consigo imaginar melhor como funciona.&lt;/p&gt;

&lt;p&gt;Então, ChatGPT, Claude e muitos outros são exatamente isso, e atualmente têm a capacidade de executar várias atividades, como escrever código, traduzir texto, responder às mais variadas dúvidas e, dependendo da sua capacidade de escrever prompts mais robustos, pode até criar arquiteturas de produtos, te ajudar a resolver bugs complexos, te dar ideias, e até criar um SaaS revolucionário &lt;em&gt;(risos)&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;O ponto é que a capacidade das LLMs de "compreender" e "criar" chegou a um ponto bem avançado, e você só chega na camada -17 de boas respostas fazendo as perguntas de forma certa!&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; &lt;em&gt;Camada -17&lt;/em&gt; se refere à camada onde se acha o minério de ouro no jogo Minecraft ;). &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Fazendo Perguntas
&lt;/h1&gt;

&lt;p&gt;Como fazer as perguntas certas? E por que saber isso importa? Vamos lá.&lt;/p&gt;

&lt;p&gt;Saber fazer as perguntas certas para uma LLM é tão importante que existe uma área da tecnologia específica só para isso: a &lt;strong&gt;Engenharia de Prompts&lt;/strong&gt;, definida como &lt;em&gt;"a ciência empírica de planejar, criar e testar prompts para gerar melhores respostas em LLMs"&lt;/em&gt;. Saber fazer as perguntas certas te coloca em outro nível, você consegue obter as melhores respostas, e isso te traz muitos ganhos, sendo o principal deles a &lt;strong&gt;produtividade&lt;/strong&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mas afinal, como perguntar de forma certa?
&lt;/h2&gt;

&lt;p&gt;A LLM é poderosa, mas não adivinha sua intenção. Para obter os melhores resultados nas suas buscas, você precisa se concentrar na criação de prompts claros, na especificidade e ser rico em dar contexto. E tudo isso envolve:&lt;/p&gt;

&lt;h3&gt;
  
  
  1 - Evite a Ambiguidade
&lt;/h3&gt;

&lt;p&gt;"Espaço de possibilidades"&lt;/p&gt;

&lt;p&gt;Quando você escreve um prompt muito ambíguo, vago, o modelo enxerga vários caminhos estatisticamente válidos. É como se ele estivesse numa encruzilhada com 50 estradas e todas tivessem placas dizendo &lt;em&gt;"Talvez por aqui"&lt;/em&gt;, ele vai escolher uma, mas não necessariamente a que você precisa: a estrada com o percurso mais rápido e sem trânsito (a resposta correta de fato).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exemplos:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt ambíguo:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Crie uma API."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;O que a LLM "vê": Vários caminhos, API REST? GraphQL? Em qual linguagem? Para qual domínio? Com autenticação? Com banco? O modelo vai escolher o caminho mais estatisticamente comum nos dados de treino (provavelmente uma API REST genérica em Node.js com Express), que pode não ter nada a ver com o que você precisa.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Prompt sem ambiguidade:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Crie um microsserviço em Node.js com Express e TypeScript para processar pagamentos via
Stripe. Endpoints: criar pagamento, confirmar webhook e consultar status. O payload tem:
orderId(UUID), amount(number), currency(enum: BRL, USD) e customerId(string).
Use zod para validação, Prisma com PostgreSQL para persistir as transações e winston
para logs. Retorne status codes apropriados como(201, 200, 400, 422, 500). Trate falhas de
rede com retry automático (máx. 3 tentativas)."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;O que a LLM "vê": Um caminho quase único. Cada detalhe funciona como uma restrição que elimina ambiguidade: "Node.js com Express e TypeScript" define runtime, framework e linguagem de uma vez. "Pagamentos via Stripe" restringe o SDK e o domínio. "3 endpoints explícitos + payload com tipos" elimina adivinhações sobre rotas e schema. "Zod, Prisma, PostgreSQL, Winston" travam a stack, o modelo não vai sugerir alternativas. "Status codes específicos + retry com máximo de 3 tentativas" definem os status HTTP e a estratégia com limites claros. A distribuição de probabilidade fica concentrada e o modelo praticamente "só tem uma opção" a cada token gerado. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2 - Delimite o Escopo
&lt;/h3&gt;

&lt;p&gt;"Janela de atenção"&lt;/p&gt;

&lt;p&gt;LLMs têm um &lt;em&gt;context window&lt;/em&gt;(Janela), uma quantidade de tokens (pedaço de palavra) que conseguem ser processados de uma vez. Isso inclui o seu prompt e a resposta gerada. Dentro dessa janela, existe um fenômeno importante: nem todos os tokens recebem a mesma "atenção" no processamento.&lt;/p&gt;

&lt;p&gt;O mecanismo de &lt;em&gt;self-attention&lt;/em&gt; (o coração da arquitetura Transformer, não é um cubo rsrsrs, e a arquitetura de rede neural, que seria o framework da LLM se a mesma fosse uma linguagem de programação), ela define a estrutura de tudo, calcula relações entre todos os tokens do prompt. Quanto mais tokens irrelevantes existem, mais o modelo precisa "dividir atenção" entre informações úteis e inúteis. &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exemplos:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sem escopo:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Me ensine Docker."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;O que acontece internamente: O modelo precisa decidir entre centenas de subtópicos, instalação, conceitos básicos, Dockerfile, docker-compose, volumes,  orquestração, a atenção se fragmenta e o resultado é um overview superficial de tudo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Com escopo:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;"Explique o conceito de multi-stage build no Docker para um dev backend pleno que já usa
Docker no dia a dia mas nunca otimizou o tamanho das imagens. Mostre um exemplo prático
com uma aplicação JavaScript, comparando o Dockerfile sem e com multi-stage build,
incluindo o tamanho final de cada imagem."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;O que acontece internamente: O mecanismo de atenção se concentra em uma região muito específica, a interseção entre "Docker", "multi-stage build", "otimização de imagem" e "JavaScript". Os pesos de atenção ficam fortemente direcionados.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  3 - Forneça Contexto Relevante
&lt;/h3&gt;

&lt;p&gt;"Estado da aplicação"&lt;/p&gt;

&lt;p&gt;Uma LLM é &lt;em&gt;stateless&lt;/em&gt; por natureza, ela não tem memória entre requisições. Cada prompt é processado do zero, o único "estado" que ela tem é o que você coloca no prompt. Isso significa que todo contexto que você não fornece simplesmente não existe para o modelo.&lt;/p&gt;

&lt;p&gt;Internamente, o contexto funciona como um sistema de pesos no mecanismo de atenção. Quando você adiciona informações, elas criam "âncoras" que influenciam a distribuição de probabilidades de todos os tokens subsequentes, é como se cada pedaço de contexto fosse um ímã que puxa a resposta para uma direção específica.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Exemplos:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sem contexto:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Revise meu código."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;O que a LLM faz: Sem saber a linguagem, o framework, o nível do dev, o objetivo do código, o padrão do time ou o tipo de revisão esperada, ela vai fazer comentários genéricos: "adicione tratamento de erro", "use nomes mais descritivos", "considere adicionar testes".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;Com contexto:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;"Revise este endpoint Node.js com Express que lida com upload de arquivos para o S3.
O time usa ESLint + Prettier, então ignore estilo. O padrão do time é async/await com
try/catch e erros customizados. Endpoint em produção, recebe 200 uploads/min.
Foque em: memory leaks, tratamento de erros e uso correto do SDK do S3."
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;O que a LLM faz: Cada informação do prompt funciona como um filtro que elimina ruído e concentra a revisão: "Node.js com Express + upload para S3" ativa conhecimento específico sobre streams, buffers, multipart e AWS SDK. "ESLint + Prettier, ignore estilo" elimina os comentários possíveis que o linter já resolve. "async/await com erros customizados" faz o modelo pular sugestões que o time já aplica e focar em como estão sendo usadas. "Produção, 200 uploads/min" muda o peso de cada problema, um buffer não liberado que seria aceitável em dev vira um incidente crítico sob carga. "Foque em: memory leaks, erros, SDK S3" restringe a revisão a 3 eixos e ignora dezenas de outros tópicos. &lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Com base nisso, saber as limitações do modelo que você está usando, te ajuda também a entender até aonde você pode ir nas perguntas e inferências, então escolha a sua melhor IA, treine ela, faça as suas perguntas, teste, tente! ;)&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;É Lembre-se: a LLM não "pensa", ela calcula probabilidades!&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h1&gt;
  
  
  Considerações Finais
&lt;/h1&gt;

&lt;p&gt;É isto, tentei resumir cada ponto que achei interessante abordar e explicar nessa primeira etapa, mas há muito mais sobre engenharia de prompt de LLM para falar, como técnicas mais clássicas de engenharia de prompts(Zero-shot prompting, Role prompting), técnicas mais avançadas(Chain-of-Thought (CoT), Prompt Chaining), são muitas camadas que tentarei destrinchar nos próximos artigos, esse é apenas o primeiro que traz a minha volta para a escrita de artigos após alguns bons anos. Espero que você caro leitor tenha entendido e aprendido algo. Obrigado por ler até aqui. ;)&lt;/p&gt;

&lt;p&gt;Onde me encontrar:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.linkedin.com/in/franciele-borges/" rel="noopener noreferrer"&gt;Meu LinkedIn&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/franSborges/" rel="noopener noreferrer"&gt;Meu GitHub&lt;/a&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>beginners</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>POO Além do Código #1: Abstração (O Arquiteto de Soluções)</title>
      <dc:creator>Bruno Freschi</dc:creator>
      <pubDate>Thu, 12 Mar 2026 19:35:57 +0000</pubDate>
      <link>https://dev.to/he4rt/poo-alem-do-codigo-1-abstracao-o-arquiteto-de-solucoes-500m</link>
      <guid>https://dev.to/he4rt/poo-alem-do-codigo-1-abstracao-o-arquiteto-de-solucoes-500m</guid>
      <description>&lt;p&gt;Você já sentiu que seu código está virando um "emaranhado" de funções, mesmo usando classes? Muitas vezes, o problema não é a linguagem ou o framework, mas a falta de um conceito fundamental que separa os digitadores de código dos arquitetos de software: a &lt;strong&gt;Abstração&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Neste primeiro artigo da série sobre os pilares da POO, vamos descer um nível e entender por que a abstração é a alma de sistemas escaláveis.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧐 O que é Abstração, afinal?
&lt;/h2&gt;

&lt;p&gt;Abstrair não é "esconder código". &lt;strong&gt;Abstrair é a arte de ignorar o que não importa em um determinado contexto.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Imagine um objeto &lt;code&gt;Carro&lt;/code&gt;. Dependendo do sistema que você está construindo, a abstração dele muda completamente:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Contexto&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;O que importa (Abstração)&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;O que é irrelevante&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;GPS / Logística&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Latitude, Longitude, Velocidade&lt;/td&gt;
&lt;td&gt;Cor do estofado, Tipo de óleo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Oficina Mecânica&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Histórico de revisões, Peças, KM&lt;/td&gt;
&lt;td&gt;Destino da viagem atual&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;E-commerce&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Preço, Modelo, Ano, Estoque&lt;/td&gt;
&lt;td&gt;Pressão dos pneus&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Regra de Ouro:&lt;/strong&gt; Foque no &lt;strong&gt;O QUE&lt;/strong&gt; o objeto faz, e não no &lt;strong&gt;COMO&lt;/strong&gt; ele faz.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  🎮 O Exemplo do Controle Remoto
&lt;/h2&gt;

&lt;p&gt;Pense na interface de um controle remoto. Você tem botões para &lt;code&gt;Ligar()&lt;/code&gt;, &lt;code&gt;MudarCanal()&lt;/code&gt; e &lt;code&gt;AjustarVolume()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Para você, não importa se a TV recebe o sinal via &lt;strong&gt;Infravermelho, Bluetooth ou Wi-Fi&lt;/strong&gt;. A abstração (os botões) permanece a mesma, enquanto a implementação (a eletrônica interna) pode mudar completamente sem que você precise aprender a usar o controle de novo.&lt;/p&gt;




&lt;h2&gt;
  
  
  💻 Abstração na Prática (C#)
&lt;/h2&gt;

&lt;p&gt;Quando trazemos isso para o código, estamos definindo contratos de comportamento. Veja este exemplo focado no domínio de pagamentos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight csharp"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Definimos o "Contrato": Todo meio de pagamento DEVE ser processado.&lt;/span&gt;
&lt;span class="c1"&gt;// O COMO (Pix, Cartão, Boleto) não nos interessa neste nível.&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;MeioDePagamento&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;decimal&lt;/span&gt; &lt;span class="n"&gt;Valor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;get&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;set&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// O arquiteto define o comportamento, mas deixa a execução para o futuro.&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="k"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;Processar&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Estou aplicando neste repo. alguns conceitos
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/BrunoSFreschi/Blueprint-OOP/tree/master" rel="noopener noreferrer"&gt;BrunoSFreschi/Blueprint-OOP&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Por que isso é poderoso?
&lt;/h3&gt;

&lt;p&gt;Se amanhã surgir o "CryptoPay", você não precisa alterar a lógica principal do seu sistema. Você apenas cria uma nova classe que herda de &lt;code&gt;MeioDePagamento&lt;/code&gt; e implementa o método &lt;code&gt;Processar()&lt;/code&gt;. O restante do software continua conversando com a abstração.&lt;/p&gt;




&lt;h2&gt;
  
  
  🚨 O Erro Comum: O "Procedural Disfarçado"
&lt;/h2&gt;

&lt;p&gt;No ecossistema .NET, é muito comum cairmos na armadilha das classes "Anêmicas" acompanhadas de gerenciadores:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;UserService&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserHelper&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;UserManager&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se o seu objeto &lt;code&gt;User&lt;/code&gt; só tem propriedades (get/set) e toda a lógica está em um "Helper", você não está fazendo Orientação a Objetos. Você está fazendo &lt;strong&gt;Programação Procedural dentro de classes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A abstração real acontece quando o objeto possui comportamento e responsabilidades claras dentro do seu contexto.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Checkpoint Mental
&lt;/h2&gt;

&lt;p&gt;Antes de criar sua próxima classe, faça estas quatro perguntas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;🏦 &lt;strong&gt;Domínio:&lt;/strong&gt; Esta classe representa um objeto real do meu negócio?&lt;/li&gt;
&lt;li&gt;🔍 &lt;strong&gt;Contexto:&lt;/strong&gt; Estou colocando detalhes que não pertencem a este cenário?&lt;/li&gt;
&lt;li&gt;⚙️ &lt;strong&gt;Tecnologia:&lt;/strong&gt; Se eu trocar o banco de dados ou a UI, essa ideia ainda faz sentido?&lt;/li&gt;
&lt;li&gt;🎭 &lt;strong&gt;Comportamento:&lt;/strong&gt; Estou modelando ações ou apenas guardando dados (balde de variáveis)?&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  🛤️ Próximos Passos
&lt;/h2&gt;

&lt;p&gt;A abstração é o alicerce. Sem ela, os outros pilares perdem o sentido. No próximo post, vamos falar sobre &lt;strong&gt;Encapsulamento&lt;/strong&gt;: como proteger essa abstração e garantir que ninguém "mexa nos fios" por trás do controle remoto.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;E você? Já pegou algum sistema onde a falta de abstração tornou uma mudança simples em um pesadelo de refatoração? Deixe nos comentários!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;#dotnet #csharp #poo #architecture #programming&lt;/p&gt;




</description>
      <category>braziliandevs</category>
      <category>oop</category>
      <category>learning</category>
      <category>techtalks</category>
    </item>
    <item>
      <title>Do commit ao deploy: CI/CD de uma API na AWS usando GitHub Actions, ECS e Terraform</title>
      <dc:creator>Fernando Andrade</dc:creator>
      <pubDate>Thu, 12 Mar 2026 00:07:27 +0000</pubDate>
      <link>https://dev.to/he4rt/do-commit-ao-deploy-cicd-de-uma-api-na-aws-usando-github-actions-ecs-e-terraform-433g</link>
      <guid>https://dev.to/he4rt/do-commit-ao-deploy-cicd-de-uma-api-na-aws-usando-github-actions-ecs-e-terraform-433g</guid>
      <description>&lt;h2&gt;
  
  
  Sumário
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Introdução&lt;/li&gt;
&lt;li&gt;Pré-requisitos&lt;/li&gt;
&lt;li&gt;Visão Geral da Arquitetura&lt;/li&gt;
&lt;li&gt;Configurando o IAM para o Terraform&lt;/li&gt;
&lt;li&gt;
Infraestrutura como Código com Terraform

&lt;ul&gt;
&lt;li&gt;Recursos Provisionados&lt;/li&gt;
&lt;li&gt;IAM Role para Tarefas ECS&lt;/li&gt;
&lt;li&gt;Task Definition (Fargate)&lt;/li&gt;
&lt;li&gt;ECS Service&lt;/li&gt;
&lt;li&gt;OIDC: Autenticação Sem Credenciais Estáticas&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Dockerfile Multi-Stage&lt;/li&gt;

&lt;li&gt;Pipeline de CI&lt;/li&gt;

&lt;li&gt;Protegendo a Branch Main&lt;/li&gt;

&lt;li&gt;Configurando as Secrets no GitHub&lt;/li&gt;

&lt;li&gt;Pipeline de CD&lt;/li&gt;

&lt;li&gt;Acessando a Aplicação após o Deploy&lt;/li&gt;

&lt;li&gt;Segurança: OIDC em Detalhe&lt;/li&gt;

&lt;li&gt;Fluxo Completo: Do Commit ao Deploy&lt;/li&gt;

&lt;li&gt;Considerações Finais&lt;/li&gt;

&lt;/ul&gt;




&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Colocar uma aplicação em produção vai muito além de escrever código. Envolve compilar, testar, empacotar e entregar de forma confiável e repetível. Neste artigo, vou mostrar como construir uma pipeline completa — do commit ao deploy — usando &lt;strong&gt;GitHub Actions&lt;/strong&gt; para CI/CD, &lt;strong&gt;Terraform&lt;/strong&gt; para infraestrutura como código e &lt;strong&gt;AWS&lt;/strong&gt; (ECR, ECS Fargate) como plataforma de execução.&lt;/p&gt;

&lt;p&gt;O conceito apresentado aqui é &lt;strong&gt;agnóstico de linguagem&lt;/strong&gt; — funciona para qualquer stack que rode em um container Docker (Node.js, Go, Java, Python, etc.). Para os exemplos práticos, vamos utilizar &lt;strong&gt;.NET&lt;/strong&gt; como referência, mas os workflows, a infraestrutura Terraform e o fluxo de deploy são os mesmos independente da tecnologia escolhida.&lt;/p&gt;

&lt;p&gt;O objetivo é demonstrar como essas ferramentas se conectam para formar um fluxo automatizado onde um simples merge na branch &lt;code&gt;main&lt;/code&gt; resulta em uma nova versão rodando em produção, sem intervenção manual.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pré-requisitos
&lt;/h2&gt;

&lt;p&gt;Antes de começar, você precisa ter as seguintes ferramentas instaladas e configuradas:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ferramenta&lt;/th&gt;
&lt;th&gt;Descrição&lt;/th&gt;
&lt;th&gt;Link de instalação&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Docker&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Para construir e executar containers&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.docker.com/get-docker/" rel="noopener noreferrer"&gt;docs.docker.com/get-docker&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Terraform&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Para provisionar infraestrutura como código&lt;/td&gt;
&lt;td&gt;&lt;a href="https://developer.hashicorp.com/terraform/install" rel="noopener noreferrer"&gt;developer.hashicorp.com/terraform/install&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;AWS CLI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Para interagir com os serviços da AWS via terminal&lt;/td&gt;
&lt;td&gt;&lt;a href="https://docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html" rel="noopener noreferrer"&gt;docs.aws.amazon.com/cli/latest/userguide/install-cliv2.html&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Git&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Para versionamento de código&lt;/td&gt;
&lt;td&gt;&lt;a href="https://git-scm.com/downloads" rel="noopener noreferrer"&gt;git-scm.com/downloads&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conta AWS&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Com permissões para criar recursos (IAM, ECS, ECR)&lt;/td&gt;
&lt;td&gt;&lt;a href="https://aws.amazon.com/free/" rel="noopener noreferrer"&gt;aws.amazon.com/free&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Conta GitHub&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Para hospedar o repositório e rodar os workflows&lt;/td&gt;
&lt;td&gt;&lt;a href="https://github.com/" rel="noopener noreferrer"&gt;github.com&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Para o exemplo deste artigo, também é necessário o &lt;a href="https://dotnet.microsoft.com/download" rel="noopener noreferrer"&gt;.NET SDK&lt;/a&gt; instalado localmente para desenvolvimento. Se você estiver usando outra stack, substitua pelo SDK correspondente (Node.js, Go, JDK, etc.). Outro ponto é que a escolha em utilizar o ECS ao invés de um EKS ou EC2 é devido sua simplicidade na curva de aprendizado, baixo gerenciamento e que para fins de aprendizado os recursos mínimos definidos para esse laboratório não gerem altos gastos para o aprendizado.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Visão Geral da Arquitetura
&lt;/h2&gt;

&lt;p&gt;O fluxo completo funciona assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Developer → Feature Branch → Pull Request → Validação (CI)
                                                  ↓
                                            Merge na main
                                                  ↓
                                         Build &amp;amp; Push (CD)
                                                  ↓
                                        Deploy no ECS Fargate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Configurando o IAM para o Terraform
&lt;/h2&gt;

&lt;p&gt;Antes de rodar qualquer &lt;code&gt;terraform apply&lt;/code&gt;, é necessário que o Terraform tenha permissões para criar recursos na AWS. Para isso, precisamos de um &lt;strong&gt;usuário IAM&lt;/strong&gt; (ou role) com as permissões adequadas e configurar suas credenciais localmente.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando um usuário IAM para o Terraform
&lt;/h3&gt;

&lt;p&gt;No console da AWS (IAM &amp;gt; Users), crie um usuário dedicado para o Terraform:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Acesse &lt;strong&gt;IAM &amp;gt; Users &amp;gt; Create User&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Nomeie o usuário (ex: &lt;code&gt;terraform-deployer&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Anexe as policies necessárias para os recursos que serão criados:
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AmazonECS_FullAccess
AmazonEC2ContainerRegistryFullAccess
AmazonVPCReadOnlyAccess
IAMFullAccess
CloudWatchLogsFullAccess
AmazonS3FullAccess
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota de segurança:&lt;/strong&gt; Em um ambiente produtivo, o ideal é criar uma &lt;strong&gt;policy customizada&lt;/strong&gt; com o princípio do menor privilégio, concedendo apenas as permissões estritamente necessárias. Para fins de estudo, as managed policies acima simplificam o setup.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;ol&gt;
&lt;li&gt;Após criar o usuário, gere um &lt;strong&gt;Access Key&lt;/strong&gt; (IAM &amp;gt; Users &amp;gt; Security credentials &amp;gt; Create access key)&lt;/li&gt;
&lt;li&gt;Selecione o caso de uso &lt;strong&gt;Command Line Interface (CLI)&lt;/strong&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Configurando as credenciais localmente
&lt;/h3&gt;

&lt;p&gt;Com o AWS CLI instalado, configure as credenciais:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws configure
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Será solicitado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;AWS Access Key ID: AKIA...
AWS Secret Access Key: wJal...
Default region name: us-east-1
Default output format: json
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso cria o arquivo &lt;code&gt;~/.aws/credentials&lt;/code&gt; que o Terraform utilizará automaticamente via o provider AWS. Com isso feito, o Terraform tem autorização para provisionar os recursos que definiremos a seguir.&lt;/p&gt;




&lt;h2&gt;
  
  
  Infraestrutura como Código com Terraform
&lt;/h2&gt;

&lt;p&gt;Antes de qualquer pipeline rodar, a infraestrutura precisa existir. Com o Terraform, declaramos todos os recursos AWS em arquivos &lt;code&gt;.tf&lt;/code&gt; e provisionamos com um único comando.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recursos Provisionados
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Provider AWS&lt;/span&gt;
&lt;span class="nx"&gt;provider&lt;/span&gt; &lt;span class="s2"&gt;"aws"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;region&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Repositório ECR para armazenar imagens Docker&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ecr_repository"&lt;/span&gt; &lt;span class="s2"&gt;"app_repository"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"minha-app-repository"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Cluster ECS&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ecs_cluster"&lt;/span&gt; &lt;span class="s2"&gt;"app_cluster"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"minha-app-cluster"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  IAM Role para Tarefas ECS
&lt;/h3&gt;

&lt;p&gt;O ECS precisa de uma role para puxar imagens e enviar logs:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"ecs_task_execution_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ecs-task-execution-role"&lt;/span&gt;

  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
    &lt;span class="nx"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="nx"&gt;Action&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRole"&lt;/span&gt;
      &lt;span class="nx"&gt;Effect&lt;/span&gt;    &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
      &lt;span class="nx"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Service&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ecs-tasks.amazonaws.com"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role_policy_attachment"&lt;/span&gt; &lt;span class="s2"&gt;"ecs_policy"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;role&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ecs_task_execution_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;
  &lt;span class="nx"&gt;policy_arn&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"arn:aws:iam::aws:policy/service-role/AmazonECSTaskExecutionRolePolicy"&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Task Definition (Fargate)
&lt;/h3&gt;

&lt;p&gt;Aqui definimos como o container será executado:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ecs_task_definition"&lt;/span&gt; &lt;span class="s2"&gt;"app_task"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;family&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"minha-app-task"&lt;/span&gt;
  &lt;span class="nx"&gt;network_mode&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awsvpc"&lt;/span&gt;
  &lt;span class="nx"&gt;requires_compatibilities&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;cpu&lt;/span&gt;                      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"256"&lt;/span&gt;
  &lt;span class="nx"&gt;memory&lt;/span&gt;                   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"512"&lt;/span&gt;
  &lt;span class="nx"&gt;execution_role_arn&lt;/span&gt;       &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ecs_task_execution_role&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;

  &lt;span class="nx"&gt;container_definitions&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt;
    &lt;span class="nx"&gt;name&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"app"&lt;/span&gt;
    &lt;span class="nx"&gt;image&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"${aws_ecr_repository.app_repository.repository_url}:latest"&lt;/span&gt;
    &lt;span class="nx"&gt;portMappings&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="nx"&gt;containerPort&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;
      &lt;span class="nx"&gt;hostPort&lt;/span&gt;      &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8080&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
    &lt;span class="nx"&gt;logConfiguration&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;logDriver&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"awslogs"&lt;/span&gt;
      &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="s2"&gt;"awslogs-group"&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"/ecs/minha-app"&lt;/span&gt;
        &lt;span class="s2"&gt;"awslogs-region"&lt;/span&gt;        &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
        &lt;span class="s2"&gt;"awslogs-stream-prefix"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"ecs"&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}])&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  ECS Service
&lt;/h3&gt;

&lt;p&gt;O service mantém o container rodando e gerencia o deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_ecs_service"&lt;/span&gt; &lt;span class="s2"&gt;"app_service"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;            &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"minha-app-service"&lt;/span&gt;
  &lt;span class="nx"&gt;cluster&lt;/span&gt;         &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_cluster&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;task_definition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_ecs_task_definition&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_task&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
  &lt;span class="nx"&gt;desired_count&lt;/span&gt;   &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;
  &lt;span class="nx"&gt;launch_type&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"FARGATE"&lt;/span&gt;

  &lt;span class="nx"&gt;network_configuration&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;subnets&lt;/span&gt;          &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;aws_subnets&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;default&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ids&lt;/span&gt;
    &lt;span class="nx"&gt;security_groups&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;aws_security_group&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;app_sg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="nx"&gt;assign_public_ip&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  OIDC: Autenticação Sem Credenciais Estáticas
&lt;/h3&gt;

&lt;p&gt;Este é um dos pontos mais importantes da arquitetura. Em vez de armazenar &lt;code&gt;AWS_ACCESS_KEY&lt;/code&gt; e &lt;code&gt;AWS_SECRET_KEY&lt;/code&gt; como secrets no GitHub, usamos &lt;strong&gt;OIDC (OpenID Connect)&lt;/strong&gt; para que o GitHub Actions troque um token temporário por credenciais AWS.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="c1"&gt;# Registra o GitHub como provedor OIDC na AWS&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_openid_connect_provider"&lt;/span&gt; &lt;span class="s2"&gt;"github"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;             &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"https://token.actions.githubusercontent.com"&lt;/span&gt;
  &lt;span class="nx"&gt;client_id_list&lt;/span&gt;  &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"sts.amazonaws.com"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nx"&gt;thumbprint_list&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"ffffffffffffffffffffffffffffffffffffffff"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;# Role que o GitHub Actions vai assumir&lt;/span&gt;
&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"aws_iam_role"&lt;/span&gt; &lt;span class="s2"&gt;"github_actions_role"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"github-actions-role"&lt;/span&gt;

  &lt;span class="nx"&gt;assume_role_policy&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;jsonencode&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="nx"&gt;Version&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2012-10-17"&lt;/span&gt;
    &lt;span class="nx"&gt;Statement&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;
      &lt;span class="nx"&gt;Effect&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"Allow"&lt;/span&gt;
      &lt;span class="nx"&gt;Principal&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;Federated&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;aws_iam_openid_connect_provider&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;github&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;arn&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;Action&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts:AssumeRoleWithWebIdentity"&lt;/span&gt;
      &lt;span class="nx"&gt;Condition&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;StringEquals&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"token.actions.githubusercontent.com:aud"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"sts.amazonaws.com"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="nx"&gt;StringLike&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="s2"&gt;"token.actions.githubusercontent.com:sub"&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"repo:meu-usuario/meu-repo:ref:refs/heads/main"&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}]&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Por que isso importa?&lt;/strong&gt; Credenciais estáticas são um risco de segurança. Com OIDC, as credenciais são temporárias e o acesso é restrito a uma branch específica de um repositório específico. Mesmo que alguém tenha acesso ao repositório, não consegue assumir a role a partir de outra branch.&lt;/p&gt;




&lt;h2&gt;
  
  
  Dockerfile Multi-Stage
&lt;/h2&gt;

&lt;p&gt;Usamos um build multi-stage para separar o ambiente de compilação do ambiente de execução, resultando em uma imagem final menor e mais segura:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight docker"&gt;&lt;code&gt;&lt;span class="c"&gt;# Stage 1: Build&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/sdk:10.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; ["MeuProjeto/MeuProjeto.csproj", "MeuProjeto/"]&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet restore &lt;span class="s2"&gt;"MeuProjeto/MeuProjeto.csproj"&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; . .&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; "/src/MeuProjeto"&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet build &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; /app/build

&lt;span class="c"&gt;# Stage 2: Publish&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;publish&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;dotnet publish &lt;span class="nt"&gt;-c&lt;/span&gt; Release &lt;span class="nt"&gt;-o&lt;/span&gt; /app/publish /p:UseAppHost&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;false&lt;/span&gt;

&lt;span class="c"&gt;# Stage 3: Runtime&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;mcr.microsoft.com/dotnet/aspnet:10.0&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="k"&gt;AS&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;final&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /app&lt;/span&gt;
&lt;span class="k"&gt;EXPOSE&lt;/span&gt;&lt;span class="s"&gt; 8080&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=publish /app/publish .&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["dotnet", "MeuProjeto.dll"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Benefícios do multi-stage:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A imagem final contém apenas o runtime, não o SDK completo&lt;/li&gt;
&lt;li&gt;Reduz significativamente o tamanho da imagem&lt;/li&gt;
&lt;li&gt;O código-fonte não fica presente na imagem de produção&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Pipeline de CI
&lt;/h2&gt;

&lt;p&gt;A primeira pipeline roda automaticamente quando um Pull Request é aberto contra a branch &lt;code&gt;main&lt;/code&gt;. Seu objetivo é validar que o código compila e que os testes passam.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;PR Validation&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;pull_request&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;build&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup .NET&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;10.0.x'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Restore&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet restore MinhaSolution.sln&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dotnet build MinhaSolution.sln --no-restore -c Release&lt;/span&gt;

  &lt;span class="na"&gt;test&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;needs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;build&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Setup .NET&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/setup-dotnet@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;dotnet-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;10.0.x'&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Run Tests with Coverage&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;dotnet test MinhaSolution.sln \&lt;/span&gt;
            &lt;span class="s"&gt;--collect:"XPlat Code Coverage" \&lt;/span&gt;
            &lt;span class="s"&gt;--results-directory ./coverage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  O que acontece nesta pipeline:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Job &lt;code&gt;build&lt;/code&gt;&lt;/strong&gt; — Compila a solução para garantir que não há erros de compilação&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Job &lt;code&gt;test&lt;/code&gt;&lt;/strong&gt; — Roda os testes unitários com cobertura de código usando Coverlet&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Separar em dois jobs traz clareza: se o build falha, você sabe que é erro de compilação. Se o test falha, o código compila mas tem um bug.&lt;/p&gt;




&lt;h2&gt;
  
  
  Protegendo a Branch Main
&lt;/h2&gt;

&lt;p&gt;Com a pipeline de CI configurada, é fundamental garantir que &lt;strong&gt;nenhum código chegue à &lt;code&gt;main&lt;/code&gt; sem passar pela validação&lt;/strong&gt;. Para isso, configuramos uma &lt;strong&gt;branch protection rule&lt;/strong&gt; no GitHub.&lt;/p&gt;

&lt;p&gt;Acesse &lt;strong&gt;Settings &amp;gt; Branches &amp;gt; Add branch protection rule&lt;/strong&gt; no seu repositório e configure:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Branch name pattern:&lt;/strong&gt; &lt;code&gt;main&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Marque &lt;strong&gt;Require a pull request before merging&lt;/strong&gt; — impede push direto na main&lt;/li&gt;
&lt;li&gt;Marque &lt;strong&gt;Require status checks to pass before merging&lt;/strong&gt; — bloqueia o merge até que os checks passem&lt;/li&gt;
&lt;li&gt;Em &lt;strong&gt;Status checks that are required&lt;/strong&gt;, busque e adicione os jobs da pipeline de CI:

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;build&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;test&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Nota:&lt;/strong&gt; Na primeira vez que configurar, os status checks podem não aparecer na busca. Eles só ficam disponíveis após a pipeline rodar pelo menos uma vez no repositório. Abra um PR de teste para que os checks sejam registrados.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Configurando as Secrets no GitHub
&lt;/h2&gt;

&lt;p&gt;Antes de configurar a pipeline de deploy, é necessário cadastrar as &lt;strong&gt;secrets&lt;/strong&gt; no repositório do GitHub. A pipeline de CD depende delas para autenticar na AWS e saber para onde enviar a imagem Docker.&lt;/p&gt;

&lt;p&gt;Acesse &lt;strong&gt;Settings &amp;gt; Secrets and variables &amp;gt; Actions &amp;gt; New repository secret&lt;/strong&gt; no seu repositório e crie as seguintes secrets:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Secret&lt;/th&gt;
&lt;th&gt;Valor&lt;/th&gt;
&lt;th&gt;Exemplo&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AWS_ROLE&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O &lt;strong&gt;ARN completo&lt;/strong&gt; da IAM Role criada para o GitHub Actions&lt;/td&gt;
&lt;td&gt;&lt;code&gt;arn:aws:iam::123456789012:role/github-actions-role&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;ECR_REPOSITORY&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;A &lt;strong&gt;URI completa&lt;/strong&gt; do repositório ECR (não apenas o nome)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;123456789012.dkr.ecr.us-east-1.amazonaws.com/minha-app-repository&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;AWS_ACCOUNT_ID&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O ID numérico da sua conta AWS&lt;/td&gt;
&lt;td&gt;&lt;code&gt;123456789012&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Atenção:&lt;/strong&gt; Um erro comum é colocar apenas o nome do repositório ECR (ex: &lt;code&gt;minha-app-repository&lt;/code&gt;) na secret &lt;code&gt;ECR_REPOSITORY&lt;/code&gt;. O valor correto é a &lt;strong&gt;URI completa&lt;/strong&gt; que inclui o domínio do ECR. Você pode obter essa URI no console da AWS em &lt;strong&gt;ECR &amp;gt; Repositories&lt;/strong&gt; ou via CLI:&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws ecr describe-repositories &lt;span class="nt"&gt;--repository-names&lt;/span&gt; minha-app-repository &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"repositories[0].repositoryUri"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Da mesma forma, a secret &lt;code&gt;AWS_ROLE&lt;/code&gt; deve conter o &lt;strong&gt;ARN&lt;/strong&gt; (Amazon Resource Name) completo da role, não apenas o nome. Para consultar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;aws iam get-role &lt;span class="nt"&gt;--role-name&lt;/span&gt; github-actions-role &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"Role.Arn"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com as secrets configuradas, a pipeline de deploy consegue autenticar via OIDC, fazer push da imagem para o ECR e disparar o deploy no ECS.&lt;/p&gt;




&lt;h2&gt;
  
  
  Pipeline de CD
&lt;/h2&gt;

&lt;p&gt;Quando o PR é aprovado e mergeado na &lt;code&gt;main&lt;/code&gt;, a pipeline de deploy entra em ação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build and Deploy&lt;/span&gt;

&lt;span class="na"&gt;on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;push&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;branches&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;main&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;permissions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;#permissões necessárias para autenticação OIDC&lt;/span&gt;
  &lt;span class="na"&gt;id-token&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;write&lt;/span&gt;
  &lt;span class="na"&gt;contents&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;read&lt;/span&gt; 

&lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt;

&lt;span class="na"&gt;jobs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;deploy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Configure AWS Credentials (OIDC)&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/configure-aws-credentials@v4&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;role-to-assume&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ secrets.AWS_ROLE_ARN }}&lt;/span&gt;
          &lt;span class="na"&gt;aws-region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${{ env.AWS_REGION }}&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Login to Amazon ECR&lt;/span&gt;
        &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws-actions/amazon-ecr-login@v2&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Build Docker Image&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker build -t minha-app .&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Tag Image&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker tag minha-app:latest ${{ secrets.ECR_REPOSITORY }}:latest&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Push to ECR&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;docker push ${{ secrets.ECR_REPOSITORY }}:latest&lt;/span&gt;

      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Deploy to ECS&lt;/span&gt;
        &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;|&lt;/span&gt;
          &lt;span class="s"&gt;aws ecs update-service \&lt;/span&gt;
            &lt;span class="s"&gt;--cluster minha-app-cluster \&lt;/span&gt;
            &lt;span class="s"&gt;--service minha-app-service \&lt;/span&gt;
            &lt;span class="s"&gt;--force-new-deployment&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  O que acontece nesta pipeline:
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Autenticação OIDC&lt;/strong&gt; — O GitHub troca seu token JWT por credenciais AWS temporárias&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Login no ECR&lt;/strong&gt; — Autentica o Docker no registro da AWS&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Build e Push&lt;/strong&gt; — Constrói a imagem Docker e envia para o ECR&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deploy&lt;/strong&gt; — Dispara um novo deployment no ECS, que puxa a imagem atualizada e substitui o container antigo&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;O &lt;code&gt;--force-new-deployment&lt;/code&gt; garante que o ECS vai iniciar uma nova task mesmo que a tag da imagem (&lt;code&gt;latest&lt;/code&gt;) não tenha mudado.&lt;/p&gt;




&lt;h2&gt;
  
  
  Acessando a Aplicação após o Deploy
&lt;/h2&gt;

&lt;p&gt;Após a pipeline de CD concluir com sucesso, a aplicação estará rodando no ECS Fargate. Como configuramos &lt;code&gt;assign_public_ip = true&lt;/code&gt; no Terraform, a task recebe um &lt;strong&gt;IP público&lt;/strong&gt; que pode ser usado para acessar a API.&lt;/p&gt;

&lt;h3&gt;
  
  
  Obtendo o IP público pelo Console AWS
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Acesse &lt;strong&gt;ECS &amp;gt; Clusters &amp;gt; minha-app-cluster&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Clique na aba &lt;strong&gt;Tasks&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Clique na task em execução (status &lt;code&gt;RUNNING&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Na seção &lt;strong&gt;Network&lt;/strong&gt;, copie o &lt;strong&gt;Public IP&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Acesse no navegador: &lt;code&gt;http://&amp;lt;PUBLIC_IP&amp;gt;:8080&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Obtendo o IP público via CLI
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# 1. Obtenha o ARN da task em execução&lt;/span&gt;
&lt;span class="nv"&gt;TASK_ARN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecs list-tasks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; minha-app-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--service-name&lt;/span&gt; minha-app-service &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"taskArns[0]"&lt;/span&gt; &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 2. Obtenha o ID da interface de rede (ENI)&lt;/span&gt;
&lt;span class="nv"&gt;ENI_ID&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;aws ecs describe-tasks &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--cluster&lt;/span&gt; minha-app-cluster &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--tasks&lt;/span&gt; &lt;span class="nv"&gt;$TASK_ARN&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"tasks[0].attachments[0].details[?name=='networkInterfaceId'].value"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text&lt;span class="si"&gt;)&lt;/span&gt;

&lt;span class="c"&gt;# 3. Obtenha o IP público&lt;/span&gt;
aws ec2 describe-network-interfaces &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--network-interface-ids&lt;/span&gt; &lt;span class="nv"&gt;$ENI_ID&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--query&lt;/span&gt; &lt;span class="s2"&gt;"NetworkInterfaces[0].Association.PublicIp"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;--output&lt;/span&gt; text
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Importante:&lt;/strong&gt; O IP público muda toda vez que uma nova task é criada (ou seja, a cada deploy). Para um ambiente produtivo, o ideal é utilizar um &lt;strong&gt;Application Load Balancer (ALB)&lt;/strong&gt; ou um &lt;strong&gt;domínio com Route 53&lt;/strong&gt; apontando para o serviço ECS, garantindo um endereço fixo. Para fins de estudo, o IP público direto é suficiente.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Segurança: OIDC em Detalhe
&lt;/h2&gt;

&lt;p&gt;Vale reforçar a importância do OIDC neste fluxo. O modelo tradicional funciona assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;❌ Modelo Tradicional:
   GitHub Secrets → AWS_ACCESS_KEY_ID + AWS_SECRET_ACCESS_KEY
   - Credenciais estáticas que nunca expiram
   - Se vazadas, acesso total até serem rotacionadas manualmente
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com OIDC:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;✅ Modelo OIDC:
   GitHub Actions → JWT Token → AWS STS → Credenciais Temporárias
   - Credenciais expiram automaticamente
   - Escopo restrito: apenas uma branch de um repositório específico
   - Sem segredos de longa duração armazenados
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A configuração requer:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Registrar o GitHub como OIDC Provider na AWS (via Terraform)&lt;/li&gt;
&lt;li&gt;Criar uma IAM Role com trust policy apontando para o repositório&lt;/li&gt;
&lt;li&gt;No workflow, usar &lt;code&gt;permissions: id-token: write&lt;/code&gt; e a action &lt;code&gt;configure-aws-credentials&lt;/code&gt; com &lt;code&gt;role-to-assume&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Fluxo Completo: Do Commit ao Deploy
&lt;/h2&gt;

&lt;p&gt;Resumindo o ciclo de vida de uma mudança:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;1. git checkout &lt;span class="nt"&gt;-b&lt;/span&gt; feature/minha-feature
2. &lt;span class="c"&gt;# Desenvolve e commita&lt;/span&gt;
3. git push origin feature/minha-feature
4. &lt;span class="c"&gt;# Abre Pull Request → Dispara pr-validation.yml&lt;/span&gt;
   │
   ├── ✅ Build compila com sucesso
   └── ✅ Testes passam com cobertura
   │
5. &lt;span class="c"&gt;# Code review + Aprovação&lt;/span&gt;
6. &lt;span class="c"&gt;# Merge na main → Dispara build-and-deploy.yml&lt;/span&gt;
   │
   ├── 🔐 Autenticação via OIDC
   ├── 🐳 Build da imagem Docker &lt;span class="o"&gt;(&lt;/span&gt;multi-stage&lt;span class="o"&gt;)&lt;/span&gt;
   ├── 📦 Push para o ECR
   └── 🚀 Deploy no ECS Fargate
   │
7. &lt;span class="c"&gt;# Nova versão rodando em produção&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Considerações Finais
&lt;/h2&gt;

&lt;p&gt;Este setup demonstra como é possível construir uma pipeline profissional de CI/CD com ferramentas modernas e boas práticas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Infraestrutura como Código&lt;/strong&gt; — Toda a infraestrutura é versionada e reproduzível&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autenticação Keyless&lt;/strong&gt; — OIDC elimina o risco de credenciais estáticas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Serverless Containers&lt;/strong&gt; — Fargate remove a necessidade de gerenciar servidores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Separação CI/CD&lt;/strong&gt; — Validação em PRs e deploy apenas na main&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Imagens otimizadas&lt;/strong&gt; — Multi-stage build reduz a superfície de ataque&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O custo de infraestrutura para um setup como esse é mínimo — com Fargate usando 0.25 vCPU e 512MB de memória, o custo fica na faixa de poucos dólares por mês para ambientes de estudo e projetos pequenos.&lt;/p&gt;

&lt;p&gt;A barreira de entrada para CI/CD profissional diminuiu muito. Ferramentas como GitHub Actions e Terraform tornam acessível o que antes exigia equipes dedicadas de DevOps. O importante é começar simples, entender cada peça, e evoluir conforme a necessidade.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Este artigo foi escrito com base em um projeto prático de estudo. Todo o código-fonte está disponível publicamente neste repositório do &lt;a href="https://github.com/fernanduandrade/api-quality-lab" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>tutorial</category>
      <category>devops</category>
      <category>aws</category>
      <category>braziliandevs</category>
    </item>
  </channel>
</rss>
