<?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>Forem: He4rt Developers</title>
    <description>The latest articles on Forem by He4rt Developers (@he4rt).</description>
    <link>https://forem.com/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.amazonaws.com%2Fuploads%2Forganization%2Fprofile_image%2F5490%2Fef471ad5-dbd9-40be-9951-743a6026d59c.png</url>
      <title>Forem: He4rt Developers</title>
      <link>https://forem.com/he4rt</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/he4rt"/>
    <language>en</language>
    <item>
      <title>DataAccess: ADO.net</title>
      <dc:creator>Bruno Freschi</dc:creator>
      <pubDate>Fri, 10 Apr 2026 11:31:20 +0000</pubDate>
      <link>https://forem.com/he4rt/dataaccess-adonet-3a79</link>
      <guid>https://forem.com/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.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NzYzNjMyMjEsIm5iZiI6MTc3NjM2MjkyMSwicGF0aCI6Ii84OTc4OTIxMC81NzQ4MjM4MzMtZTFkNTA2ZDMtOWRhZi00M2UwLTg4YTYtNGVjNDhlYjM4YWJiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA0MTYlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNDE2VDE4MDg0MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWY5NTViODBmMTFlOTkzMjIyNTMyMzYyM2YyYWUwNWJiMTBiZGI5YjRmNTk5YTkwOTg0Zjc0ZjA3NjQ2MmJiOTEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRnBuZyJ9.dLj7lEuaYYV9xOX4DQpvLo3wDNRCtmQpGR2CpjF3YD0"&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.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3NzYzNjMyMjEsIm5iZiI6MTc3NjM2MjkyMSwicGF0aCI6Ii84OTc4OTIxMC81NzQ4MjM4MzMtZTFkNTA2ZDMtOWRhZi00M2UwLTg4YTYtNGVjNDhlYjM4YWJiLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNjA0MTYlMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjYwNDE2VDE4MDg0MVomWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWY5NTViODBmMTFlOTkzMjIyNTMyMzYyM2YyYWUwNWJiMTBiZGI5YjRmNTk5YTkwOTg0Zjc0ZjA3NjQ2MmJiOTEmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JnJlc3BvbnNlLWNvbnRlbnQtdHlwZT1pbWFnZSUyRnBuZyJ9.dLj7lEuaYYV9xOX4DQpvLo3wDNRCtmQpGR2CpjF3YD0"&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://forem.com/he4rt/por-que-eu-comeco-10-projetos-e-nao-termino-nenhum-1139</link>
      <guid>https://forem.com/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 🇧🇷 </dc:creator>
      <pubDate>Sun, 05 Apr 2026 12:02:52 +0000</pubDate>
      <link>https://forem.com/he4rt/o-que-uma-usina-nuclear-tem-a-ver-com-o-seu-processo-de-qa-103j</link>
      <guid>https://forem.com/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://forem.com/he4rt/de-front-end-para-ux-e-de-volta-ao-codigo-o-que-significa-ser-design-engineer-em-2026-3j74</link>
      <guid>https://forem.com/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://forem.com/he4rt/construi-um-gerador-de-playlists-no-spotify-com-claude-18ge</link>
      <guid>https://forem.com/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://forem.com/he4rt/engenharia-de-prompt-por-que-a-forma-como-voce-pergunta-muda-tudoum-guia-introdutorio-3hb0</link>
      <guid>https://forem.com/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>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://forem.com/he4rt/do-commit-ao-deploy-cicd-de-uma-api-na-aws-usando-github-actions-ecs-e-terraform-433g</link>
      <guid>https://forem.com/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>
    <item>
      <title>Criei uma pipeline de planejamento no OpenCode e olha no que deu</title>
      <dc:creator>Clinton Rocha</dc:creator>
      <pubDate>Mon, 09 Mar 2026 04:37:00 +0000</pubDate>
      <link>https://forem.com/he4rt/sistema-de-planejamento-estruturado-no-opencode-16pb</link>
      <guid>https://forem.com/he4rt/sistema-de-planejamento-estruturado-no-opencode-16pb</guid>
      <description>&lt;p&gt;Você é como eu e vem buscando formas de otimizar o uso de ferramentas como o &lt;a href="https://opencode.ai/docs/pt-br" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt;? Talvez este conteúdo te ajude a ter novas ideias e até mesmo entender alguns fluxos dessa e de outras ferramentas.&lt;/p&gt;

&lt;p&gt;Enquanto eu estudava a documentação do OpenCode, encontrei a aba de &lt;a href="https://pt.wikipedia.org/wiki/Plug-in" rel="noopener noreferrer"&gt;plugins&lt;/a&gt; feitos pela comunidade. Ali, encontrei três plugins que, na minha visão, se complementam bem. Na verdade, um deles eu já utilizava: o &lt;a href="https://github.com/backnotprop/plannotator/tree/main/apps/opencode-plugin" rel="noopener noreferrer"&gt;&lt;strong&gt;Plannotator&lt;/strong&gt;&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;A partir disso, comecei a estudar como funcionava os comandos, agentes e como eu poderia fazer um fluxo que fizesse sentido e principalmente que funcionasse, calma que já te explico oq cada um dos plugins fazem e qual a ideia final da integração.&lt;/p&gt;

&lt;h1&gt;
  
  
  Índice
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;Como eu estava usando o OpenCode&lt;/li&gt;
&lt;li&gt;Objetivo da integração&lt;/li&gt;
&lt;li&gt;Plannotator: Revisão Visual&lt;/li&gt;
&lt;li&gt;Octto: Coleta estruturada de contexto&lt;/li&gt;
&lt;li&gt;Subtask2: Orquestração de Pipeline&lt;/li&gt;
&lt;li&gt;Comandos&lt;/li&gt;
&lt;li&gt;Considerações&lt;/li&gt;
&lt;li&gt;Referências&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Como eu estava usando o OpenCode
&lt;/h1&gt;

&lt;p&gt;Após começar a utilizar o OpenCode, notei o quão importante é o uso do módulo de planejamento e, principalmente, não apenas o planejamento em si, mas também as perguntas que são feitas durante esse processo. O resultado dessas duas novas abordagens (planejamento e perguntas) melhora bastante não só o código produzido, mas também a minha compreensão sobre a regra de negócio, além de comportamentos e fluxos do sistema.&lt;/p&gt;

&lt;p&gt;Mesmo com todas essas descobertas que trouxeram otimizações para o processo, eu ainda sentia que dava para melhorar mais.&lt;/p&gt;

&lt;p&gt;O fluxo de uso do OpenCode era basicamente o seguinte: eu enviava o prompt, a ferramenta vasculhava o código para obter contexto, depois vinham algumas perguntas e, em seguida, ela ia para a parte de &lt;em&gt;build&lt;/em&gt;, ou seja, para colocar em prática o que tinha sido planejado.&lt;/p&gt;

&lt;h1&gt;
  
  
  Objetivo da integração
&lt;/h1&gt;

&lt;p&gt;A ideia vai ser conseguir extrair o máximo de contexto com o &lt;code&gt;Octto&lt;/code&gt;, usar o &lt;code&gt;subtask2&lt;/code&gt; para orquestrar os comandos e visualizar o plano final com &lt;code&gt;Plannotator&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+---------------------------------------------------------------+
|                                                               |
|  IDEIA -&amp;gt; CONTEXTO -&amp;gt; PLANO -&amp;gt; REFINAMENTO -&amp;gt; REVISAO -&amp;gt; OK   |
|                                                               |
+---------------------------------------------------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O resultado é um &lt;strong&gt;único comando&lt;/strong&gt; que orquestra todo o processo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/generate-plan &lt;span class="s2"&gt;"descrição do objetivo"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Plannotator: Revisão visual
&lt;/h1&gt;

&lt;p&gt;O primeiro plugin chegou até mim por recomendação de uma amiga da comunidade (&lt;a href="https://github.com/he4rt/" rel="noopener noreferrer"&gt;He4rtDevs&lt;/a&gt;), a &lt;a href="https://dev.to/cherryramatis"&gt;Cherry&lt;/a&gt;, e o plugin era o &lt;a href="https://github.com/backnotprop/plannotator/tree/main/apps/opencode-plugin" rel="noopener noreferrer"&gt;Plannotator&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Esse plugin basicamente faz com que, ao final de um planejamento, seja aberta uma página no navegador contendo todo o plano gerado. Isso facilita muito a visualização em comparação com o terminal. Na interface do navegador, você consegue selecionar trechos do planejamento e dar feedback, que retorna para o processo de planejamento. Também é possível aprovar o plano e seguir diretamente para a etapa de &lt;em&gt;build&lt;/em&gt;, ou seja, aplicar o que foi planejado.&lt;/p&gt;

&lt;h1&gt;
  
  
  Octto: Coleta estruturada de contexto
&lt;/h1&gt;

&lt;p&gt;O &lt;a href="https://github.com/vtemian/octto" rel="noopener noreferrer"&gt;Octto&lt;/a&gt; quando você envia o primeiro prompt, ele abre uma página no navegador com perguntas relacionadas ao que você pediu inicialmente. Essa página funciona como uma interface interativa de brainstorming. Conforme você responde às perguntas, o sistema utiliza essas respostas para gerar novas perguntas dentro da mesma sessão, aprofundando o entendimento do problema e ajudando a estruturar melhor o contexto antes de seguir para as próximas etapas, ao final ele gera um arquivo &lt;code&gt;.md&lt;/code&gt; no diretório &lt;code&gt;/docs/plans&lt;/code&gt; dentro do projeto.&lt;/p&gt;

&lt;p&gt;A unica configuração que adicionei a ele, foi uma que faz com que a primeira pergunta seja um campo de texto livre onde o usuário consiga descrever o contexto com maior liberdade (colocar uma task completa, por exemplo).&lt;/p&gt;

&lt;h2&gt;
  
  
  Fluxo de Funcionamento
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;+----------------------+
|       ask_text       |  &amp;lt;-- Primeira ação obrigatória
+----------+-----------+
           |
           | Campo de texto no browser:
           | "Descreva com detalhes o que
           | você quer planejar..."
           v
+----------------------+
|   create_brainstorm  |  &amp;lt;-- Agente inicia sessão
+----------+-----------+      com contexto coletado
           |
           | Define branches de exploração
           v
+--------------------------------------------------------------+
|                    BRANCHES DE EXPLORAÇÃO                    |
|                                                              |
|  +----------------+  +-------------------+  +---------------+|
|  | Motivação &amp;amp;    |  | Requisitos &amp;amp;      |  | Riscos &amp;amp;      ||
|  | Objetivos      |  | Restrições        |  | Dependências  ||
|  +--------+-------+  +---------+---------+  +-------+-------+|
|           |                    |                    |        |
+-----------+--------------------+--------------------+--------+
            |
            v
+----------------------+
|      BROWSER UI      |
|                      |
|  [ Pergunta 1 ]      |
|  [ Pergunta 2 ]      |
|  [ Pergunta N ]      |
|                      |
|      [Responder]     |
+----------+-----------+
           |
           | Usuário responde
           v
+----------------------+
| await_brainstorm_    |
| complete             |  &amp;lt;-- Agente aguarda respostas
+----------+-----------+
           |
           | Sintetiza respostas
           v
+----------------------+
|      docs/plans/     |
| 2026-03-08-objetivo  |  &amp;lt;-- Arquivo gerado
|        .md           |
+----------------------+
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Configuração via Fragments
&lt;/h2&gt;

&lt;p&gt;O comportamento do Octto é customizado através de &lt;strong&gt;fragments&lt;/strong&gt;, instruções adicionadas ao prompt dos agentes internos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"fragments"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"octto"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"Sempre pergunte a motivação e o 'porquê' das mudanças"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"Gere nomes de arquivo no formato: YYYY-MM-DD-slug.md"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"Salve os planos em docs/plans/"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"probe"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"Priorize perguntas sobre motivações e restrições"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"Inclua perguntas sobre requisitos não-funcionais"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"bootstrapper"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
            &lt;/span&gt;&lt;span class="s2"&gt;"Crie branches focados em: requisitos, arquitetura, riscos"&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Função de cada agente interno:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;bootstrapper&lt;/strong&gt;: cria a estrutura inicial de branches&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;probe&lt;/strong&gt; : gera perguntas de aprofundamento&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;octto&lt;/strong&gt;: define o comportamento geral da coleta de contexto&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Subtask2: Orquestração de pipeline
&lt;/h1&gt;

&lt;p&gt;O &lt;a href="https://github.com/spoons-and-mirrors/subtask2" rel="noopener noreferrer"&gt;Subtask2&lt;/a&gt; permite &lt;strong&gt;encadear comandos&lt;/strong&gt; de forma sequencial. Quando um comando termina, o próximo da cadeia é executado automaticamente. &lt;/p&gt;

&lt;p&gt;Nesse meu contexto só vou usar a funcionalidade de &lt;code&gt;return&lt;/code&gt;, mas esse plugin tem muitas outras funcionalidades interessantes como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;loop&lt;/code&gt; de subtarefas até que a condição do usuário seja atendida&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;paralela&lt;/code&gt; de subtarefas simultaneamente - PR pendente.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;$TURN[n]&lt;/code&gt; passa turnos da sessão (mensagens do usuário/assistente).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;{as:name}&lt;/code&gt; + &lt;code&gt;$RESULT[name]&lt;/code&gt; captura e referência as saídas das subtarefas.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Sintaxe de Return Chain
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;subtask&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;return&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/comando-1&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/comando-2&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/comando-3&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h1&gt;
  
  
  Comandos
&lt;/h1&gt;

&lt;p&gt;Com o &lt;code&gt;subtask2&lt;/code&gt;, consigo adicionar comandos ao fluxo, e isso abre muitas possibilidades. Mas, por enquanto, estou com os pés no chão e vou fazer um fluxo simples com alguns novos comandos.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Eu criei esses comandos com o único intuito de usar na pipeline.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Comando Principal
&lt;/h2&gt;

&lt;p&gt;O comando que vai dar inicio a todo o fluxo, vai ser o &lt;code&gt;/generate-plan&lt;/code&gt; ao executar, ele da inicio a pipeline de planejamento.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Você é livre para escolher qualquer nome para o comando, para escolher o nome desse aqui eu só segui as vozes da minha cabeça.&lt;/p&gt;

&lt;p&gt;Caso queira saber mais sobre como criar e configurar &lt;a href="https://opencode.ai/docs/pt-br/commands/" rel="noopener noreferrer"&gt;comandos no opencode&lt;/a&gt;, a doc é legalzinha.&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;/generate-plan refatorar sistema de autenticação
/generate-plan &lt;span class="s2"&gt;"adicionar suporte a múltiplos gateways de pagamento"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As aspas são opcionais: tudo após o comando é passado como &lt;code&gt;$ARGUMENTS&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Pipeline completo de planejamento&lt;/span&gt; 
&lt;span class="na"&gt;agent&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;octto&lt;/span&gt;
&lt;span class="na"&gt;subtask&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="na"&gt;return&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/refine-plan&lt;/span&gt;
&lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;/review-plan&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

prompt aqui, e lembre de usar o $ARGUMENTS
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Caso você queira ver como deixei o meu &lt;a href="https://gist.github.com/Clintonrocha98/f9f5cb146a799a3a2a340ef12a0bf9bf#file-prompt-generate-plan-md" rel="noopener noreferrer"&gt;prompt usado no comando generate-plan&lt;/a&gt; é só ir no gist.&lt;/p&gt;

&lt;p&gt;Todos os comandos podem ser usados individualmente/isolado no OpenCode, a unica diferença ao usar um comando com o &lt;code&gt;subtask&lt;/code&gt; é que ele vai ser executado em um contexto diferente.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Refinamento Técnico
&lt;/h2&gt;

&lt;p&gt;O &lt;code&gt;/refine-plan&lt;/code&gt; tem como objetivo expandir um plano existente com detalhes arquiteturais.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/refine-plan &lt;span class="c"&gt;# usa plano mais recente&lt;/span&gt;
/refine-plan docs/plans/meu-plano.md &lt;span class="c"&gt;# plano específico&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;refine-plan&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Expande e refina tecnicamente um plano existente com arquitetura e diagramas&lt;/span&gt;
&lt;span class="na"&gt;subtask&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

Mesma coisa que o anterior, aqui você consegue adicionar a instrução que você quiser.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;No meu caso, o prompt que estou sando tá no &lt;a href="https://gist.github.com/Clintonrocha98/f9f5cb146a799a3a2a340ef12a0bf9bf#file-refine-plan-md" rel="noopener noreferrer"&gt;gist 'refine-plan.md'&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Revisão Visual
&lt;/h2&gt;

&lt;p&gt;Por último vem o comando &lt;code&gt;/review-plan&lt;/code&gt; com o objetivo de mandar o plano para revisão interativa via Plannotator.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;/review-plan &lt;span class="c"&gt;# usa plano mais recente&lt;/span&gt;
/review-plan docs/plans/meu-plano.md &lt;span class="c"&gt;# plano específico&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Importante:&lt;/strong&gt; Este comando &lt;strong&gt;não usa &lt;code&gt;subtask: true&lt;/code&gt;&lt;/strong&gt;. Ele roda na sessão primária para que a ferramenta &lt;code&gt;submit_plan&lt;/code&gt; possa abrir a interface do Plannotator no browser corretamente. Comandos rodando como subtarefa não conseguem inicializar a UI do browser.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fluxo Completo de Execução:&lt;/strong&gt;&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%2Fc22oiuapqumis0k3m5ns.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%2Fc22oiuapqumis0k3m5ns.png" alt="diagrama contendo todo o fluxo"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;O mais importante de tudo: funcionou? Sim. É uma bazuca? Sim, hahahaha. Esse projeto acabou saindo porque entrei em hiperfoco ao me fazer a pergunta “será que funciona?”. No fim, tive minha resposta.&lt;/p&gt;

&lt;p&gt;Olhando para a usabilidade no dia a dia, acredito que os melhores momentos para utilizar algo assim são em novas &lt;em&gt;features&lt;/em&gt;, refatoração de projeto e até mesmo para estruturar estudos, quem sabe. Só não use para algo como “quero trocar a cor de um botão”, aí você acaba complicando.&lt;/p&gt;

&lt;p&gt;O ponto que mais curti foi o &lt;code&gt;Octto&lt;/code&gt;, com as perguntas geradas de forma dinâmica. Aquilo ali, ao meu ver, auxilia na contextualização de uma baita maneira. Fora que é possível dar instruções para as &lt;em&gt;branches&lt;/em&gt; dele, especificar qual tipo de input você quer ou não quer, entre outras coisas (vale ler a documentação).&lt;/p&gt;

&lt;p&gt;Já o &lt;code&gt;subtask2&lt;/code&gt; abre portas para muita coisa. Como eu tinha dito antes, acabei indo pelo simples, mas alguém com a mente mais aberta provavelmente vai conseguir extrair o melhor da ferramenta.&lt;/p&gt;

&lt;p&gt;Eu pretendo criar novos comandos, refinar mais os &lt;em&gt;prompts&lt;/em&gt; e ver o que acontece. É isso, obrigado por ler até aqui e tente brincar com isso ai.&lt;/p&gt;

&lt;p&gt;Como meu &lt;em&gt;Tech Lead&lt;/em&gt; fala: &lt;/p&gt;

&lt;p&gt;&lt;code&gt;Faça codigo inútil e quebre alguma coisa.&lt;/code&gt;&lt;/p&gt;

&lt;h1&gt;
  
  
  Referências
&lt;/h1&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://opencode.ai/docs/commands/" rel="noopener noreferrer"&gt;OpenCode - Comandos&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opencode.ai/docs/agents/" rel="noopener noreferrer"&gt;OpenCode - Agentes&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/spoons-and-mirrors/subtask2" rel="noopener noreferrer"&gt;Subtask2 - GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/backnotprop/plannotator/tree/main/apps/opencode-plugin" rel="noopener noreferrer"&gt;Plannotator - Plugin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/vtemian/octto" rel="noopener noreferrer"&gt;Octto - GitHub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/%40summer12126/what-does-adhoc-mean-in-sw-development-2196f09826d6" rel="noopener noreferrer"&gt;what does ad-hoc&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>ai</category>
      <category>braziliandevs</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Seu código é Orientado a Objetos</title>
      <dc:creator>Bruno Freschi</dc:creator>
      <pubDate>Thu, 05 Mar 2026 17:19:48 +0000</pubDate>
      <link>https://forem.com/he4rt/seu-codigo-e-orientado-a-objetos-e5e</link>
      <guid>https://forem.com/he4rt/seu-codigo-e-orientado-a-objetos-e5e</guid>
      <description>&lt;h2&gt;
  
  
  🚀 O seu código é Orientado a Objetos ou apenas um "Procedural Disfarçado"?
&lt;/h2&gt;

&lt;p&gt;Vamos tocar na ferida: o que mais existe por aí é o famoso &lt;strong&gt;"Código de Estimação"&lt;/strong&gt;. Aquele código empacotado em classes, cheio de &lt;em&gt;getters&lt;/em&gt; e &lt;em&gt;setters&lt;/em&gt; para todo lado, mas sem um pingo de inteligência ou comportamento real.&lt;/p&gt;

&lt;p&gt;Se o código procedural resolve o problema e entrega o software, por que se dar ao trabalho de modelar objetos? A resposta não está na execução, mas na &lt;strong&gt;sustentabilidade&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por que a OO (bem feita) salva o seu projeto?
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Controle da Complexidade (O fim do Espaguete):&lt;/strong&gt; No procedural, os dados são passivos e as funções globais. Conforme o sistema cresce, mexer em um ponto quebra cinco outros lugares imprevisíveis. A OO propõe o &lt;strong&gt;Encapsulamento&lt;/strong&gt;: cada objeto é uma "caixa preta" que cuida da própria vida.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Flexibilidade sem Cirurgia (Polimorfismo):&lt;/strong&gt; Precisa adicionar um novo meio de pagamento (ex: Cripto)? Em vez de espalhar &lt;code&gt;if/else&lt;/code&gt; por todo o sistema, você apenas cria um novo objeto que sabe "Processar". Você adiciona funções sem tocar no que já está funcionando.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  🏛️ Os 4 Pilares na Prática
&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;Pilar&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;O que resolve na vida real?&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Abstração&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Esconde a complexidade. Você dirige o carro sem precisar entender a explosão interna do motor.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Encapsulamento&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Protege os dados. Ninguém altera o "Saldo" sem passar pelas regras de negócio.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Herança&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Compartilha características comuns, evitando redundância (mas cuidado para não virar um pesadelo!).&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Polimorfismo&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Permite que diferentes objetos respondam à mesma mensagem de formas específicas.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h3&gt;
  
  
  💡 O Veredito
&lt;/h3&gt;

&lt;p&gt;Usamos Orientação a Objetos para que o código seja legível para &lt;strong&gt;humanos&lt;/strong&gt;, e não apenas compreensível para máquinas. O custo de manutenção de um software é, em média, &lt;strong&gt;80% do seu ciclo de vida&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A OO bem aplicada é um investimento para que o desenvolvedor do futuro (que pode ser você daqui a seis meses) não queira deletar tudo e começar do zero.&lt;/p&gt;




&lt;p&gt;&lt;strong&gt;E aí, o seu código atual passaria em um teste de pureza de OO?&lt;/strong&gt; 🧐&lt;/p&gt;

&lt;p&gt;#programação #softwareDevelopment #cleanCode #OOP #devLife #brazilianDevs&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>developer</category>
      <category>beginners</category>
      <category>discuss</category>
    </item>
    <item>
      <title>API vs Event Streaming: O Que Muda Para Quem Testa?</title>
      <dc:creator>Alicia Marianne 🇧🇷 </dc:creator>
      <pubDate>Thu, 12 Feb 2026 20:50:04 +0000</pubDate>
      <link>https://forem.com/he4rt/api-vs-event-streaming-o-que-muda-para-quem-testa-1f1h</link>
      <guid>https://forem.com/he4rt/api-vs-event-streaming-o-que-muda-para-quem-testa-1f1h</guid>
      <description>&lt;p&gt;✨ &lt;strong&gt;Oi, gente!&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Recentemente, tenho pensado bastante em como contribuir para a comunidade de QA e ajudar aqueles que estão começando ou que já estão há algum tempo na área. Também quero relatar mais sobre os desafios de ser uma brasileira morando em terras lusitanas 🇵🇹.&lt;/p&gt;

&lt;p&gt;O que me levou a querer voltar — na verdade, começar — a escrever na minha língua materna?&lt;/p&gt;

&lt;p&gt;Bom, se eu fosse dizer de bate-pronto: saudade. 💛&lt;/p&gt;

&lt;p&gt;Sim, aqui em Portugal fala-se português também, porém, como boa mineira que sou, sinto falta do nosso jeitinho de falar e escrever as coisas. Acredito que falar sobre o que sei na minha língua materna me deixa mais próxima de casa.&lt;/p&gt;

&lt;p&gt;Outra razão que me fez querer começar a escrever em português foi voltar a ser mais ativa em comunidades de desenvolvimento (&lt;a href="https://discord.gg/he4rt" rel="noopener noreferrer"&gt;He4rt&lt;/a&gt; 💜). Tenho visto muitas mulheres entrando para a área, e algumas não são fluentes em inglês ou não confiam no que sabem para se arriscar em conteúdos em outros idiomas.&lt;/p&gt;

&lt;p&gt;Com isso, eu pensei: por que não focar por um tempo em escrever em português? E cá estamos! ✍️&lt;/p&gt;

&lt;p&gt;E, para que isso não seja apenas um comunicado, mas algo útil, já vou começar falando sobre serviços de mensageria (Event Streaming) e por que é importante que um QA (ou aspirante a QA) entenda pelo menos o básico sobre isso.&lt;/p&gt;




&lt;h2&gt;
  
  
  📡 Em termos técnicos
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Event Streaming (Transmissão de Eventos)&lt;/strong&gt; é a prática de capturar dados em tempo real a partir de fontes como bancos de dados, sensores, dispositivos móveis, serviços na nuvem e aplicações de software, na forma de fluxos de eventos; armazenar esses fluxos de forma duradoura para recuperação posterior; manipular, processar e reagir a esses eventos tanto em tempo real quanto retrospectivamente; e encaminhá-los para diferentes destinos, conforme necessário.&lt;/p&gt;

&lt;p&gt;Lindo, né? 😅&lt;/p&gt;

&lt;p&gt;Mas como podemos entender isso na prática?&lt;/p&gt;




&lt;h2&gt;
  
  
  🧠 Vamos imaginar o seu corpo
&lt;/h2&gt;

&lt;p&gt;Sim, ele mesmo!&lt;/p&gt;

&lt;p&gt;Você sente aquela dor no estômago 🍽️, que é um sinal de que está com fome. Esse sinal “fala” para o seu cérebro:&lt;/p&gt;

&lt;p&gt;— Querido, estou precisando de nutrientes. Quando eles chegam?&lt;/p&gt;

&lt;p&gt;Normalmente, você para o que está fazendo e vai comer algo. Mas perceba: seu estômago precisa esperar até o momento certo. Ainda assim, seu corpo continua funcionando normalmente. Ele não para.&lt;/p&gt;

&lt;p&gt;No mundo do software, isso significa que, sempre que existir um sinal ou uma modificação, essa informação será interpretada &lt;strong&gt;sem que outras funcionalidades do sistema sejam interrompidas&lt;/strong&gt;.&lt;/p&gt;




&lt;h2&gt;
  
  
  🛒 Exemplo prático: compras online
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Você faz o seu pedido&lt;/li&gt;
&lt;li&gt;Realiza o pagamento&lt;/li&gt;
&lt;li&gt;O sistema de pagamento processa esse pagamento&lt;/li&gt;
&lt;li&gt;Ele publica um evento informando que o pagamento foi aprovado&lt;/li&gt;
&lt;li&gt;O sistema de notificação consome esse evento&lt;/li&gt;
&lt;li&gt;E o cliente recebe a confirmação de que a compra será entregue 📦&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Tudo isso pode acontecer sem que um sistema precise “esperar parado” pelo outro.&lt;/p&gt;




&lt;h2&gt;
  
  
  🔧 Algumas ferramentas de Event Streaming
&lt;/h2&gt;

&lt;p&gt;Hoje, existem várias ferramentas no mercado que trabalham com esse modelo de comunicação baseada em eventos. Algumas das mais conhecidas são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Apache Kafka&lt;/strong&gt; 🐘 (uma das mais populares no mercado)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;RabbitMQ&lt;/strong&gt; 🐰&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;AWS Kinesis&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cada uma tem suas particularidades, mas todas trabalham com o conceito de produtores, consumidores e eventos trafegando por tópicos ou filas.&lt;/p&gt;

&lt;p&gt;E aqui começa a parte interessante para nós, QAs. 👀&lt;/p&gt;




&lt;h2&gt;
  
  
  🔄 Event Streaming vs API
&lt;/h2&gt;

&lt;p&gt;Você pode se perguntar:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;“Alicia, mas qual a diferença disso para uma API? Eu consigo fazer o mesmo apenas chamando uma API.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Conseguir, você consegue. Mas será que é a melhor opção? 🤔&lt;/p&gt;

&lt;p&gt;Depende.&lt;/p&gt;

&lt;p&gt;Normalmente, sistemas que utilizam apenas APIs (geralmente síncronas) funcionam assim: fazem uma requisição, &lt;strong&gt;esperam a resposta&lt;/strong&gt;, e só depois continuam o processamento. Isso pode deixar o sistema mais acoplado e dependente.&lt;/p&gt;

&lt;p&gt;Já em um sistema de &lt;strong&gt;Event Streaming (assíncrono)&lt;/strong&gt;, não é necessário esperar uma resposta para continuar o fluxo. O sistema segue funcionando enquanto os eventos são processados em paralelo.&lt;/p&gt;

&lt;p&gt;E, principalmente, não queremos que o usuário fique esperando uma resposta para continuar usando o software ⏳.&lt;/p&gt;




&lt;h2&gt;
  
  
  🧪 E onde o QA e automação entram nisso?
&lt;/h2&gt;

&lt;p&gt;Quando falamos de sistemas orientados a eventos, surgem novas perguntas de teste:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O evento foi realmente publicado?&lt;/li&gt;
&lt;li&gt;O formato (schema) está correto?&lt;/li&gt;
&lt;li&gt;O consumidor está processando corretamente?&lt;/li&gt;
&lt;li&gt;O que acontece se o consumidor falhar?&lt;/li&gt;
&lt;li&gt;Existe retry?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Percebe como o nível de complexidade aumenta?&lt;/p&gt;

&lt;p&gt;Testar Event Streaming não é só validar payload. Envolve entender fluxo, consistência eventual, ordenação de mensagens, tolerância a falhas e comportamento assíncrono e partir dessa compreensão, é possível pensar em automações, do tipo: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Produzir eventos automatizados para testes&lt;/li&gt;
&lt;li&gt;Validar consumo de mensagens&lt;/li&gt;
&lt;li&gt;Testes de contrato (como com Schema Registry no Kafka)&lt;/li&gt;
&lt;li&gt;Testes de integração ponta a ponta&lt;/li&gt;
&lt;li&gt;Validação de mensagens em filas durante pipelines de CI/CD&lt;/li&gt;
&lt;/ul&gt;




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

&lt;p&gt;Entender mensageria e Event Streaming não é apenas “coisa de backend” — é conhecimento essencial para qualquer QA que queira testar sistemas modernos com mais profundidade. 💡&lt;/p&gt;

&lt;p&gt;Quando compreendemos como os eventos trafegam, como os sistemas se comunicam de forma assíncrona e onde podem ocorrer falhas, ampliamos nossa capacidade de análise, criamos melhores cenários de teste e nos tornamos profissionais mais estratégicos.&lt;/p&gt;

&lt;p&gt;Escrever sobre isso em português é, para mim, uma forma de compartilhar conhecimento, fortalecer a comunidade e também me sentir mais próxima de casa. 💛&lt;/p&gt;

&lt;p&gt;E esse é só o começo. 🚀&lt;/p&gt;

</description>
      <category>testing</category>
      <category>braziliandevs</category>
      <category>beginners</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>Figma MCP: sou designer, o que eu tenho a ver?</title>
      <dc:creator>vitoriazzp</dc:creator>
      <pubDate>Thu, 04 Dec 2025 16:41:05 +0000</pubDate>
      <link>https://forem.com/he4rt/figma-mcp-sou-designer-o-que-eu-tenho-a-ver-1dke</link>
      <guid>https://forem.com/he4rt/figma-mcp-sou-designer-o-que-eu-tenho-a-ver-1dke</guid>
      <description>&lt;p&gt;Se você trabalha com produto, já percebeu que entre o “nosso design está pronto!” e o “isso está no ar!” existe um universo paralelo de desalinhamentos, ajustes de última hora e aquele eterno vai e volta entre Figma e código. Nada mais normal.&lt;/p&gt;

&lt;p&gt;Mas agora temos um novo personagem nessa história: o &lt;strong&gt;Figma MCP (Model Context Protocol)&lt;/strong&gt; — uma ponte direta entre seus arquivos Figma e ferramentas de IA que geram código.&lt;br&gt;
E aí você pensa: “Beleza… mas eu sou designer. O que eu tenho a ver com isso?”&lt;/p&gt;

&lt;p&gt;A resposta curta: tudo.&lt;br&gt;
A resposta longa: vem comigo.&lt;/p&gt;




&lt;h2&gt;
  
  
  O MCP traduz seu design de forma fiel para o código
&lt;/h2&gt;

&lt;p&gt;O Figma MCP cria um servidor que permite que agentes de IA — como Cursor, VS Code ou outros editores — acessem o &lt;strong&gt;contexto real&lt;/strong&gt; do seu arquivo: componentes, variáveis, tokens, layouts, constraints.&lt;/p&gt;

&lt;p&gt;Ou seja, quando você manda a IA implementar um card, ela não está só “vendo um retângulo bonito”, ela está entendendo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;spacing real do design system&lt;/li&gt;
&lt;li&gt;layout grid&lt;/li&gt;
&lt;li&gt;variantes&lt;/li&gt;
&lt;li&gt;tokens aplicados&lt;/li&gt;
&lt;li&gt;comportamento definido no Dev Mode&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso transforma o handoff. Literalmente.&lt;/p&gt;




&lt;h2&gt;
  
  
  Benefícios que designers vão sentir na prática
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;1) Converter frames direto em código confiável&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Você seleciona um frame, copia o link e a IA escreve o código já alinhado ao design system — sem “interpretações criativas”.&lt;br&gt;
Pra quem já trabalhou estruturando DS (como eu), isso é ouro puro.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;2) As variáveis entram automaticamente no prompt&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Nada de prompts longos explicando spacing, grids ou tokens.&lt;br&gt;
O MCP envia tudo isso sozinho, deixando a IA mais esperta que nunca.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;3) Colaboração com devs melhora MUITO&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O MCP até audita diferenças entre design e implementação, sugerindo ajustes ou avisando onde algo não bate.&lt;br&gt;
É quase um mini Design QA automatizado.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Um exemplo real do meu dia a dia em que MCP teria salvado tempo
&lt;/h2&gt;

&lt;p&gt;Quando eu trabalhava ajustando componentes do design system, muitas vezes criava layouts complexos — tabelas responsivas, modais com várias variantes, dropdowns cheios de estados.&lt;/p&gt;

&lt;p&gt;Mas para validar tudo isso, eu dependia de um dev parar o que estava fazendo para montar uma versão funcional.&lt;/p&gt;

&lt;p&gt;Com o MCP, teria sido assim:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;copiar link → pedir no Cursor → “gera esse componente seguindo meu design system”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Eu teria uma implementação inicial em minutos, pronta para iterar e testar. Sem interrupções, sem esperar sprint, sem perder contexto.&lt;/p&gt;




&lt;p&gt;Mas… e os desenvolvedores frontend?&lt;/p&gt;

&lt;p&gt;Eles continuam absolutamente essenciais.&lt;/p&gt;

&lt;p&gt;O MCP não substitui devs — ele substitui o retrabalho, a adivinhação e as idas e vindas desnecessárias.&lt;/p&gt;

&lt;p&gt;O frontend ainda será responsável por:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;lógica de negócio&lt;/li&gt;
&lt;li&gt;performance&lt;/li&gt;
&lt;li&gt;arquitetura de componentes&lt;/li&gt;
&lt;li&gt;integrações reais&lt;/li&gt;
&lt;li&gt;otimizações&lt;/li&gt;
&lt;li&gt;acessibilidade avançada&lt;/li&gt;
&lt;li&gt;padrões de engenharia&lt;/li&gt;
&lt;li&gt;boas práticas que IA nenhuma domina sozinha&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;O que muda é o foco:&lt;/strong&gt;&lt;br&gt;
menos tempo reescrevendo interface e mais tempo criando soluções melhores.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;O MCP não tira o espaço do dev&lt;/strong&gt; — ele tira o peso do handoff manual e dá espaço para que o dev faça o que só ele sabe fazer.&lt;/p&gt;




&lt;h2&gt;
  
  
  Então… Figma MCP: sou designer, o que eu tenho a ver?
&lt;/h2&gt;

&lt;p&gt;Tudo.&lt;br&gt;
Você tem a ver porque o MCP coloca designers no centro do fluxo: seus componentes, suas variáveis, sua lógica visual passam a ser entendidas pelo código — sem intermediários distorcendo suas intenções.&lt;/p&gt;

&lt;p&gt;Ele acelera protótipos, reduz ruído, aumenta fidelidade e melhora a colaboração.&lt;/p&gt;

&lt;p&gt;É um daqueles raros momentos em que design, dev e IA entram em sintonia real.&lt;/p&gt;

&lt;p&gt;Se você trabalha com UX, prototipagem, design system ou quer entregar mais rápido sem depender de dez etapas intermediárias…&lt;br&gt;
o MCP é exatamente onde você deveria prestar atenção.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Quer aprender mais sobre?&lt;/strong&gt; Te recomendo ver o vídeo do próprio Figma aplicando a funcionalidade a Design Systems aqui: &lt;a href="https://www.youtube.com/watch?v=A4mqzgFbmjI" rel="noopener noreferrer"&gt;https://www.youtube.com/watch?v=A4mqzgFbmjI&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>figmamcp</category>
      <category>ai</category>
      <category>ux</category>
      <category>designsystem</category>
    </item>
    <item>
      <title>Compartilhando seu conhecimento com o mundo! Como escrever artigos</title>
      <dc:creator>Cherry Ramatis</dc:creator>
      <pubDate>Mon, 13 Nov 2023 21:31:19 +0000</pubDate>
      <link>https://forem.com/he4rt/compartilhando-seu-conhecimento-com-o-mundo-como-escrever-artigos-5ghc</link>
      <guid>https://forem.com/he4rt/compartilhando-seu-conhecimento-com-o-mundo-como-escrever-artigos-5ghc</guid>
      <description>&lt;p&gt;Compartilhar conhecimento escrito é uma ótima forma de dominar um assunto específico, além de ser uma excelente maneira de melhorar a organização das ideias, comunicação e obviamente se autopromover na comunidade. Essa produção de artigos tanto técnicos quanto sociais são muito importantes tanto para quem escreve quanto para quem está lendo, lembre-se sempre: &lt;code&gt;Você sabe mais hoje do que quem começou ontem&lt;/code&gt;. Pensando nisso vamos discutir um pouco mais sobre como organizar as próprias ideias na produção de um artigo técnico para a plataforma dev.to!&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;Método de Feynman e por que produzir conteúdo&lt;/li&gt;
&lt;li&gt;Coletando ideias e se motivando para escrever&lt;/li&gt;
&lt;li&gt;Entendendo a plataforma e achando a propria linguagem&lt;/li&gt;
&lt;li&gt;Aprendendo markdown e dicas gerais para uma boa formatação&lt;/li&gt;
&lt;li&gt;A estrutura básica de um artigo técnico&lt;/li&gt;
&lt;li&gt;Como revisar e melhorar a escrita&lt;/li&gt;
&lt;li&gt;Conclusão e agradecimentos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Método de Feynman e por que produzir conteúdo
&lt;/h2&gt;

&lt;p&gt;Primeiramente precisamos discutir o porquê devemos compartilhar conteúdo, seja em formato textual (o foco desse artigo) ou em qualquer outro formato. Para iniciar essa discussão é importante entender o que é o método de Feynman e como ele pode nos ajudar a aprender 10 vezes mais com confiança e domínio do assunto.&lt;/p&gt;

&lt;p&gt;O método de Feynman foi criado por um físico muito importante chamado Richard Feynman visando desenvolver uma abordagem nova ao aprendizado, essa nova proposta assume uma verdade central: "Se você não consegue explicar algo de maneira clara e simples, então você ainda não entendeu completamente".&lt;/p&gt;

&lt;p&gt;Essa frase nos ajuda muito a pensar em como nosso aprendizado deve ser estruturado, pois a partir do momento que começamos a pensar em ensinar o que estamos estudando focamos muito mais em dominar as bases essenciais do assunto e se preparar para dúvidas que te forçam a olhar o problema, por outro lado, completamente diferente.&lt;/p&gt;

&lt;p&gt;Ao se preparar para situações como essas, o resultado claro é uma excelente confiança e domínio sobre o assunto em questão que estava sendo estudado.&lt;/p&gt;

&lt;p&gt;Eu particularmente amo esse método, o único problema dele é que quando saímos do ambiente acadêmico fica muito difícil de achar pessoas que se interessam pelo mesmo assunto que você esta estudando no momento, seja por não ter pessoas de TI no ciclo de amizade ou simplesmente por ter interesse em assuntos específicos. &lt;/p&gt;

&lt;p&gt;Para esse problema temos uma solução muito incrível chamada "Learning in public"! Essa prática consiste em compartilhar seu aprendizado online em comunidades de tecnologia, seja produzindo vídeos, fazendo live streams ou no objetivo desse artigo: escrevendo!&lt;/p&gt;

&lt;p&gt;Plataformas como o dev.to (que você usando para ler agora :D) visam tornar a ideia de "Learning in public" cada vez mais simples e próxima de quem está consumindo, pois agora é possível produzir artigos que vão chegar em pessoas com os mesmos interesses que os nossos que podem: aprender, tirar dúvidas ou mesmo sugerir mudanças e corrigir pensamentos. Incrível né?&lt;/p&gt;

&lt;h2&gt;
  
  
  Coletando ideias e se motivando para escrever
&lt;/h2&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%2Fec5y48jxgchcgz4e5vnf.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%2Fec5y48jxgchcgz4e5vnf.png" alt="writing meme" width="462" height="438"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O processo de inspiração é provavelmente uma das fases mais chatinhas antes de produzir um artigo online, muitas vezes ficamos presos em um loop infinito de técnicas mirabolantes para ter ideias incríveis quando, na verdade, a solução acaba sendo muito simples: aceite suas ideias e consuma o máximo de conteúdos possível. &lt;/p&gt;

&lt;p&gt;A forma mais prática de encontrar ideias e de construir sua própria linguagem é ler o que as outras pessoas já produzem sobre os temas que lhe interessam, quer se trate de uma linguagem de programação, de um tema específico em TI, etc; esse consumo de conteúdo vem de diversas fontes diferentes como artigos técnicos, vídeos no YouTube, tweets da bolha Tech, discussões no Github e muitos outros locais possíveis.&lt;/p&gt;

&lt;p&gt;Bom, sei que falando assim parece simplificar algo que não é tão simples assim e eu concordo com você! Não é só ler ou assistir tudo o que existe na internet que vai nos tornar capazes de produzir os mesmos conteúdos, a habilidade mais importante que vai destacar essas pessoas é &lt;strong&gt;organizar as ideias que chegam no cérebro&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Mantendo um segundo cérebro
&lt;/h3&gt;

&lt;p&gt;Nosso cérebro é uma excelente maquina de absorção de informação, praticamente uma esponja que guarda todas as informações ao nosso redor. O grande problema dessa maquina é que ela se tornou péssima em organização com o tempo, isso aconteceu principalmente para poupar energia já que não precisamos lembrar de tudo o tempo todo, mas sabendo disso o que podemos fazer para guardar as informações que queremos de forma organizada? Well well well jovem gafanhoto, precisamos parar de confiar no nosso cérebro é claro!&lt;/p&gt;

&lt;p&gt;Manter um &lt;em&gt;segundo cérebro&lt;/em&gt; é uma prática muito famosa entre escritores e pesquisadores, consiste em um local físico ou virtual onde você copia pequenos pedaços de conteúdo que consumiu junto de uma observação utilizando suas próprias palavras sobre o assunto. Esse amontoado de anotações vai compor o seu &lt;em&gt;segundo cérebro&lt;/em&gt; e vai te empoderar com a habilidade de achar qualquer conteúdo rapidamente e referenciar seus autores sem nunca esquecer nada!&lt;/p&gt;

&lt;p&gt;Para resumir a história, consuma o máximo de conteúdo possível, armazene-o em um segundo cérebro que pode ser armazenado e pesquisado e finalmente se desafie a escrever! Seja sobre um assunto que quer aprender, sobre algo específico que aprendeu recentemente ou até mesmo algo que domina há muitos anos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Entendendo a plataforma e achando a propria linguagem
&lt;/h2&gt;

&lt;p&gt;Entender a plataforma e o público que vamos atingir escrevendo nossos conteúdos é muito importante para podermos filtrar como vamos estruturar os artigos de forma geral certo? Na &lt;em&gt;minha opinião&lt;/em&gt;, o &lt;a href="https://dev.to"&gt;dev.to&lt;/a&gt; é uma plataforma bem informal que valoriza muito conteúdos no formato de tutoriais com um estilo conversacional e direto ao ponto, com essa informação podemos deduzir algumas formas de estruturar nossos artigos para que possamos ilustrar nossa ideia em um modelo conhecido pelos leitores.&lt;/p&gt;

&lt;p&gt;Isso significa que tudo o que você vai produzir são tutoriais diretos e informais?  De forma alguma! Só significa que você pode moldar o seu conteúdo para conter essa linguagem mais informal, conversacional e direta mesmo que o tema tratado seja super complexo, isso inclusive vira um desafio muito interessante de simplificar o complexo. &lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A habilidade de simplificar o complexo vai te acompanhar para o resto da sua vida profissional, é importantíssimo a criação de analogias e exemplos para que facilite o entendimento e a identificação com os problemas apresentados e as soluções propostas.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Aprendendo markdown e dicas gerais para uma boa formatação
&lt;/h2&gt;

&lt;p&gt;A forma para produzirmos nossos artigos no dev.to é utilizando uma linguagem de marcação (exatamente, igual o HTML) chamada &lt;a href="https://www.markdownguide.org" rel="noopener noreferrer"&gt;Markdown&lt;/a&gt;, apesar dela ser super simples é importante termos um domínio bem solido do que é possível fazer quando falamos sobre organizar e deixar nosso texto bonitinho, semelhante como vamos produzir uma estrutura complexa no Microsoft Word devemos ser capazes de produzir o mesmo com código Markdown.&lt;/p&gt;

&lt;p&gt;É sempre importante ressaltar a importância de um material educacional bem estruturado (afinal você está lendo esse artigo justamente para isso certo?) e quando o assunto é excelência e qualidade não consigo recomendar o suficiente o projeto &lt;a href="https://github.com/he4rt/4noobs" rel="noopener noreferrer"&gt;4noobs&lt;/a&gt; que junta em um único repositório diversos cursos grátis e no formato de texto sobre diversos assuntos em TI, para o tema desse artigo recomendo o uso do &lt;a href="https://github.com/jpaulohe4rt/markdown4noobs" rel="noopener noreferrer"&gt;markdown4noobs&lt;/a&gt; no aprendizado da linguagem de marcação Markdown.&lt;/p&gt;

&lt;h3&gt;
  
  
  O básico sobre manipulação de texto e blocos de código
&lt;/h3&gt;

&lt;p&gt;Markdown nos permite marcar partes do nosso texto com estruturas super básicas e necessárias como negrito, itálico, highlight, níveis de título, etc. Abaixo vamos ver rapidamente como realizar cada uma das ações com a sintaxe correta.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Primeiro titulo equivalente a um h1
## Segundo titulo equivalente a um h2
### Terceiro titulo equivalente a um h3
#### Quarto titulo equivalente a um h4

`Texto em highlight`
**Texto em negrito**
*Texto em itálico*
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esses artifícios da linguagem markdown nos permitem controlar a narrativa a nosso favor e deixar a leitura mais simples de seguir, utilizando &lt;strong&gt;bold&lt;/strong&gt; no meio do texto para chamar a atenção, deixar explicito um &lt;code&gt;termo técnico&lt;/code&gt; usando highlight ou até mesmo utilizando imagens ilustrativas que introduzem o ponto do parágrafo enquanto mantém o clima geral do texto muito mais gostoso de ler.&lt;/p&gt;

&lt;p&gt;Outra coisa importante de se mencionar é o bloco especifico que usei acima, ele é extremamente útil quando vamos escrever artigos técnicos, tanto, pois ele permite um destaque maior a um bloco de texto quanto ele permite habilitar syntax highlighting caso esteja escrevendo um bloco de código, ele é utilizado da seguinte maneira:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Disclaimer: Como markdown não permite um bloco dentro de um bloco optei por mostrar com um screenshot:&lt;/p&gt;
&lt;/blockquote&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%2Fnpbqkad5a1cpuab3faau.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%2Fnpbqkad5a1cpuab3faau.png" alt="Bloco de codigo" width="650" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após os símbolos de &lt;code&gt;backtick&lt;/code&gt;, podemos incluir o nome da linguagem (no meu caso ruby) para que o dev.to possa habilitar o syntax highlighting especifico para essa linguagem de programação.&lt;/p&gt;

&lt;h3&gt;
  
  
  Tabela de conteúdo
&lt;/h3&gt;

&lt;p&gt;Caso o seu artigo esteja ultrapassando a margem das duas mil palavras ou esteja com pelo menos 4 títulos principais, eu recomendo fortemente que deixe definido um &lt;code&gt;Table of contents&lt;/code&gt;, ou &lt;code&gt;Tabela de conteúdo&lt;/code&gt;. Essa tabela de conteúdo serve para guiar a leitura durante os pontos principais que serão lidados durante o artigo, para a criação de um existem uns macetes que vou demonstrar abaixo:&lt;/p&gt;

&lt;h4&gt;
  
  
  Na plataforma dev.to, use listas não ordenadas ao invés de listas numeradas
&lt;/h4&gt;

&lt;p&gt;Listas em Markdown são bem simples de serem usadas e elas possuem dois tipos &lt;strong&gt;principais&lt;/strong&gt;: não ordenadas e numeradas.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- Uma lista
- Não
- Ordenada

1. Uma lista
2. Numerada
3. Aqui
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O problema de usar as listas numeradas no dev.to é que elas ficam desalinhadas como podemos ver no exemplo abaixo, portanto não recomendo o uso delas de maneira geral, sempre tento utilizar as listas não ordenadas e se for necessário aplicar alguma ordem utilizar um número após o símbolo da lista não ordenada manualmente.&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%2Fz2ss025hp6dzwy1rpw4j.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%2Fz2ss025hp6dzwy1rpw4j.png" alt="Listas no dev.to" width="749" height="485"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  Como organizar os links para um título
&lt;/h4&gt;

&lt;p&gt;Assumindo que você já descobriu como estruturar um link no Markdown (já que você leu o markdown4noobs né?) vamos aprender os macetes simples para indicar um link em um título e como estruturar nossa tabela de conteúdo.&lt;/p&gt;

&lt;p&gt;Uma tabela de conteúdo de exemplo pode ser vista abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;## Table of contents

- [What is metaprogramming anyway?](#what-is-metaprogramming-anyway)
- [In ruby everything is an object, what does that mean?](#in-ruby-everything-is-an-object-what-does-that-mean)
- [But what about rails? How this framework applies that concept for maximum developer experience](#but-what-about-rails-how-this-framework-applies-that-concept-for-maximum-developer-experience)
- [How to define methods dynamically](#how-to-define-methods-dynamically)
- [Using hooks to detect moments on the instantiation of the class](#using-hooks-to-detect-moments-on-the-instantiation-of-the-class)
- [Conclusion](#conclusion)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como você pode ver, a ideia geral para definir a segunda parte do link é incluir uma hashtag &lt;code&gt;#&lt;/code&gt; junto ao título em um formato especifico seguindo as regras:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Trocar todos os espaços em branco por hifens &lt;code&gt;-&lt;/code&gt; &lt;/li&gt;
&lt;li&gt;Deixar todo o título em minúsculo&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E é isso! Títulos com acentos podem continuar igual sem nenhum problema, o Markdown compreende como texto padrão igual mostrado abaixo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;- [Um título com muitos acentos e çedilha](#um-título-com-muitos-acentos-e-çedilha)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  A estrutura básica de um artigo técnico
&lt;/h2&gt;

&lt;p&gt;Agora que temos uma noção interessante de como podemos marcar nosso texto para ficar legível e agradável de ler, vamos entender um pouco a estrutura de um artigo. É importante ressaltar que um modelo não vai servir para todo tipo de texto possível, a ideia é prover uma ideia geral que deve ser adaptada e mudada conforme o contexto.&lt;/p&gt;

&lt;p&gt;Primeiro é muito importante definir o parágrafo inicial para chamar o leitor para o problema ou a situação que você vai dissecar ao longo do artigo, é importante fazer isso, pois esse primeiro paragrafo vai ser usado pelo dev.to para comunicações de marketing por e-mail ou por redes sociais. Um exemplo de parágrafo inicial pode ser achado nesse mesmo artigo que você esta lendo ou em alguns outros que deixo abaixo:&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%2F95ps97f2imqyu7ootikc.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%2F95ps97f2imqyu7ootikc.png" alt="Paragrafo inicial exemplo 1" width="748" height="373"&gt;&lt;/a&gt;&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%2Fjpebk61g2cv73pbm974f.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%2Fjpebk61g2cv73pbm974f.png" alt="Paragrafo inicial exemplo 2" width="710" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A ideia é sempre utilizar perguntas e pausas no texto para podermos alcançar uma comunicação direta e bem conversacional, sempre tentando apresentar a situação da maneira mais geral possível para que quem esteja lendo fique com bastante curiosidade e vontade de ler.&lt;/p&gt;

&lt;p&gt;Após o primeiro parágrafo de apresentação, é muito importante definir a Tabela de conteúdo para guiar o usuário ao longo dos títulos principais do seu artigo, sobre isso inclusive eu particularmente não recomendo listar os subtítulos junto dos títulos por conta de deixar a tabela de conteúdo muito grande e pouco útil para quem estiver lendo, obviamente que se você considerar seus subtítulos muito importantes de serem listados é completamente valido incluir.&lt;/p&gt;

&lt;p&gt;Passando para o corpo geral do artigo, entramos em uma área muito subjetiva, pois depende muito do assunto que está sendo abordado para entender a estrutura dos seus títulos e parágrafos. Vou assumir um artigo no modelo de tutorial simples para ser possível partir de algum lugar.&lt;/p&gt;

&lt;p&gt;Recomendo sempre ter três &lt;em&gt;títulos satélites&lt;/em&gt; que vão nortear o seu artigo e dar inspiração para aumentar o conteúdo com mais detalhes. Esses títulos satélites são os seguintes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;Introdução a tecnologia ou problema&lt;/code&gt;: Esse parágrafo vai servir para podermos detalhar o que foi falado no começo do artigo, respondendo às perguntas que criamos para atiçar a curiosidade e aprofundando mais no tema que vai ser discutido com os tópicos específicos.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Prós e contras&lt;/code&gt;: Nesse momento vamos deixar claro os prós e contras da solução que vai ser apresentada no artigo, seja ela uma arquitetura, um padrão de código, uma linguagem, framework, etc. A existência desse paragrafo pode ser bem situacional dependendo do seu tema, mas costuma ser bem útil caso esteja apresentando uma solução em forma de tutorial!&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Conclusão&lt;/code&gt;: Esse ponto é mais uma opinião do que uma regra geral, mas eu acho muito importante ter um parágrafo onde vai ser indicado o final do processo de leitura, assim podemos deixar argumentos finais, agradecimentos, contato e qualquer outra mensagem interessante.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ao redor desses três principais títulos, podemos desenvolver nosso artigo com uma escrita ilustrativa e que apresente exemplos práticos ou analogias, facilitando a visualização tanto do problema quanto da solução pelo leitor. É importante ressaltar também o cuidado ao aprofundar muito nas analogias, elas são extremamente uteis, mas podem ser um tiro no pé quando você abusa e nunca volta para o mundo real com uma solução e explicação clara.&lt;/p&gt;

&lt;p&gt;Uma dica geral ao redor da estrutura do artigo é manter o sentimento geral da leitura leve, portanto é super aconselhável utilizar imagens (seja um meme para descontrair ou um gráfico para ilustrar melhor o ponto apresentado), como a plataforma do dev.to comporta artigos técnicos mais informais, abusar dessa linguagem mais próxima é uma estratégia muito certeira.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como revisar e melhorar a escrita
&lt;/h2&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%2Ffca2sx1frbz29241ne8j.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%2Ffca2sx1frbz29241ne8j.png" alt="code review meme" width="572" height="459"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Bom, agora que temos uma boa ideia de como estruturar nosso artigo, como mantê-lo bonito utilizando Markdown e como ponderar nossa linguagem para a plataforma especifica, o que falta? Bom, agora o que falta é entender que não somos perfeitos e erramos, portanto, precisamos de uma boa estratégia para revisar o artigo que acabamos de produzir com nossas técnicas aprendidas.&lt;/p&gt;

&lt;p&gt;Para auxiliar na escrita, eu recomendo fortemente utilizar um editor que oferece visualização do Markdown em tempo real como &lt;a href="https://code.visualstudio.com/Docs/languages/markdown" rel="noopener noreferrer"&gt;VSCode&lt;/a&gt; ou o queridinho da comunidade &lt;a href="https://obsidian.md" rel="noopener noreferrer"&gt;Obsidian&lt;/a&gt;. Inclusive esse artigo foi escrito utilizando o Obsidian!&lt;/p&gt;

&lt;p&gt;No que se entende por revisão temos algumas ferramentas bem interessantes que nos auxiliam em diversos aspectos da escrita:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://languagetool.org" rel="noopener noreferrer"&gt;LanguageTool&lt;/a&gt;: Essa ferramenta é meu xodózinho e ela cuida de toda correção de ortografia, a parte legal é que nessa ferramenta você pode ter dicas de contexto que vão melhorar a frase e também correção de termos específicos de programação como nomes de linguagens visto que o banco de dados deles é super atualizado.&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.deepl.com/translator" rel="noopener noreferrer"&gt;Deepl&lt;/a&gt;: Entrando no mundo das inteligências artificiais, o Deepl utiliza deep learning para oferecer uma interface incrível de tradução, mas não só isso! Com ele podemos ter uma segunda opinião para refrasear nosso parágrafo de forma muito simples, você só precisa traduzir o texto para inglês e traduzir o texto em inglês para português de novo; normalmente no Google Translate isso destruiria o contexto, mas essa ferramenta mantém o contexto e melhora as expressões para que você possua uma segundo percepção de um mesmo paragrafo.&lt;/li&gt;
&lt;li&gt;
&lt;a href="http://chat.openai.com" rel="noopener noreferrer"&gt;ChatGPT&lt;/a&gt; ou &lt;a href="https://bard.google.com" rel="noopener noreferrer"&gt;Bard&lt;/a&gt;: Bom, aqui eu confesso que não tenho muito domínio e não uso tanto, mas essas interfaces de chat com IA ajudam muito a ter visões diferentes para refrasear um parágrafo já existente ou até mesmo começar a escrever algum. &lt;strong&gt;Importante: Preciso ressaltar para usar essas ferramentas apenas para ter ideias ou ajudar a refrasear textos que você já escreveu, por favor não produza artigos inteiros utilizando inteligência artificial&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://heartdevs.com" rel="noopener noreferrer"&gt;Comunidade&lt;/a&gt;: Na comunidade He4rt Developers tentamos providenciar o máximo de auxílio para a escrita de artigos técnicos na plataforma dev.to. Fazemos isso providenciando um fórum onde você pode postar seu artigo ainda em progresso e ganhar feedback da comunidade até a publicação, após a publicação a gente ainda faz um trabalho de divulgação para pessoas ativas! &lt;strong&gt;Disclaimer: Obviamente estou citando a He4rt, pois temos um projeto voltado para isso, mas a lição geral é compartilhar seus progressos com a comunidade de maneira geral.&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Esse é o último artigo que estou postando seguindo o desafio &lt;a href="https://www.100diasdecodigo.dev" rel="noopener noreferrer"&gt;100 dias de código&lt;/a&gt;, foi um desafio muito intenso e com muito aprendizado onde eu descobri uma nova paixão: escrever e compartilhar conhecimento! Não tenho nem palavras para agradecer a comunidade da He4rt por ter me apoiado 100% nessa jornada imensa. Espero que esse artigo seja útil para quem esteja lendo e que inspire qualquer um a compartilhar conhecimento online para podermos criar uma internet mais segura e rica em informação. &lt;/p&gt;

&lt;p&gt;Gostaria também de deixar um agradecimento especial aos revisores desse artigo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/anibalsolon" rel="noopener noreferrer"&gt;Anibal Sólon&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/m4rri4nne" rel="noopener noreferrer"&gt;Alicia Marianne&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/m1guelsb" rel="noopener noreferrer"&gt;Miguel S. Barbosa&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/Clintonrocha98" rel="noopener noreferrer"&gt;Clinton Rocha&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/SamucaDev" rel="noopener noreferrer"&gt;Samuel Rodrigues&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;May the force be with you! 🍒&lt;/p&gt;

</description>
      <category>writing</category>
      <category>beginners</category>
      <category>braziliandevs</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
