<?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: Denis Arruda</title>
    <description>The latest articles on Forem by Denis Arruda (@denisarruda).</description>
    <link>https://forem.com/denisarruda</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%2Fuser%2Fprofile_image%2F3740095%2Fb112192e-5387-434c-832a-8cc735174404.jpeg</url>
      <title>Forem: Denis Arruda</title>
      <link>https://forem.com/denisarruda</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/denisarruda"/>
    <language>en</language>
    <item>
      <title>Gemma 4 Plays Snake: A Real-Time AI Game Loop</title>
      <dc:creator>Denis Arruda</dc:creator>
      <pubDate>Mon, 18 May 2026 12:54:21 +0000</pubDate>
      <link>https://forem.com/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-ad3</link>
      <guid>https://forem.com/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-ad3</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-47n5" class="crayons-story__hidden-navigation-link"&gt;Gemma 4 Plays Snake: A Real-Time AI Game Loop&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
      &lt;a href="https://dev.to/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-47n5" class="crayons-article__context-note crayons-article__context-note__feed"&gt;&lt;p&gt;Gemma 4 Challenge: Build With Gemma 4 Submission&lt;/p&gt;

&lt;/a&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/denisarruda" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3740095%2Fb112192e-5387-434c-832a-8cc735174404.jpeg" alt="denisarruda profile" class="crayons-avatar__image" width="460" height="460"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/denisarruda" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Denis Arruda
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Denis Arruda
                
              
              &lt;div id="story-author-preview-content-3659453" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/denisarruda" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3740095%2Fb112192e-5387-434c-832a-8cc735174404.jpeg" class="crayons-avatar__image" alt="" width="460" height="460"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Denis Arruda&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-47n5" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 13&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-47n5" id="article-link-3659453"&gt;
          Gemma 4 Plays Snake: A Real-Time AI Game Loop
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/devchallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;devchallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/gemmachallenge"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;gemmachallenge&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/gemma"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;gemma&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-47n5" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/raised-hands-74b2099fd66a39f2d7eed9305ee0f4553df0eb7b4f11b01b6b1b499973048fe5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/exploding-head-daceb38d627e6ae9b730f36a1e390fca556a4289d5a41abb2c35068ad3e2c4b5.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;4&lt;span class="hidden s:inline"&gt; reactions&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-47n5#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              &lt;span class="hidden s:inline"&gt;Add Comment&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            2 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>ai</category>
      <category>gamedev</category>
      <category>llm</category>
      <category>showdev</category>
    </item>
    <item>
      <title>Arquiteturas multiagentes não devem ser tratadas como um problema clássico de engenharia de sistemas distribuídos.</title>
      <dc:creator>Denis Arruda</dc:creator>
      <pubDate>Wed, 13 May 2026 00:44:42 +0000</pubDate>
      <link>https://forem.com/denisarruda/arquiteturas-multiagentes-nao-devem-ser-tratadas-como-um-problema-classico-de-engenharia-de-41bj</link>
      <guid>https://forem.com/denisarruda/arquiteturas-multiagentes-nao-devem-ser-tratadas-como-um-problema-classico-de-engenharia-de-41bj</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/denisarruda/construindo-sistemas-multiagentes-resilientes-26h9" class="crayons-story__hidden-navigation-link"&gt;Construindo Sistemas Multiagentes Resilientes&lt;/a&gt;


  &lt;div class="crayons-story__body crayons-story__body-full_post"&gt;
    &lt;div class="crayons-story__top"&gt;
      &lt;div class="crayons-story__meta"&gt;
        &lt;div class="crayons-story__author-pic"&gt;

          &lt;a href="/denisarruda" class="crayons-avatar  crayons-avatar--l  "&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%2Fuser%2Fprofile_image%2F3740095%2Fb112192e-5387-434c-832a-8cc735174404.jpeg" alt="denisarruda profile" class="crayons-avatar__image" width="460" height="460"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/denisarruda" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Denis Arruda
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Denis Arruda
                
              
              &lt;div id="story-author-preview-content-3629772" class="profile-preview-card__content crayons-dropdown branded-7 p-4 pt-0"&gt;
                &lt;div class="gap-4 grid"&gt;
                  &lt;div class="-mt-4"&gt;
                    &lt;a href="/denisarruda" class="flex"&gt;
                      &lt;span class="crayons-avatar crayons-avatar--xl mr-2 shrink-0"&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%2Fuser%2Fprofile_image%2F3740095%2Fb112192e-5387-434c-832a-8cc735174404.jpeg" class="crayons-avatar__image" alt="" width="460" height="460"&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Denis Arruda&lt;/span&gt;
                    &lt;/a&gt;
                  &lt;/div&gt;
                  &lt;div class="print-hidden"&gt;
                    
                      Follow
                    
                  &lt;/div&gt;
                  &lt;div class="author-preview-metadata-container"&gt;&lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
            &lt;/div&gt;

          &lt;/div&gt;
          &lt;a href="https://dev.to/denisarruda/construindo-sistemas-multiagentes-resilientes-26h9" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 8&lt;/time&gt;&lt;span class="time-ago-indicator-initial-placeholder"&gt;&lt;/span&gt;&lt;/a&gt;
        &lt;/div&gt;
      &lt;/div&gt;

    &lt;/div&gt;

    &lt;div class="crayons-story__indention"&gt;
      &lt;h2 class="crayons-story__title crayons-story__title-full_post"&gt;
        &lt;a href="https://dev.to/denisarruda/construindo-sistemas-multiagentes-resilientes-26h9" id="article-link-3629772"&gt;
          Construindo Sistemas Multiagentes Resilientes
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/braziliandevs"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;braziliandevs&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/java"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;java&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/agents"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;agents&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/eventdriven"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;eventdriven&lt;/a&gt;
        &lt;/div&gt;
      &lt;div class="crayons-story__bottom"&gt;
        &lt;div class="crayons-story__details"&gt;
          &lt;a href="https://dev.to/denisarruda/construindo-sistemas-multiagentes-resilientes-26h9" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left"&gt;
            &lt;div class="multiple_reactions_aggregate"&gt;
              &lt;span class="multiple_reactions_icons_container"&gt;
                  &lt;span class="crayons_icon_container"&gt;
                    &lt;img src="https://assets.dev.to/assets/sparkle-heart-5f9bee3767e18deb1bb725290cb151c25234768a0e9a2bd39370c382d02920cf.svg" width="24" height="24"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;1&lt;span class="hidden s:inline"&gt; reaction&lt;/span&gt;&lt;/span&gt;
            &lt;/div&gt;
          &lt;/a&gt;
            &lt;a href="https://dev.to/denisarruda/construindo-sistemas-multiagentes-resilientes-26h9#comments" class="crayons-btn crayons-btn--s crayons-btn--ghost crayons-btn--icon-left flex items-center"&gt;
              Comments


              3&lt;span class="hidden s:inline"&gt; comments&lt;/span&gt;
            &lt;/a&gt;
        &lt;/div&gt;
        &lt;div class="crayons-story__save"&gt;
          &lt;small class="crayons-story__tertiary fs-xs mr-2"&gt;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

              &lt;/span&gt;
              &lt;span class="bm-success"&gt;
                

              &lt;/span&gt;
            
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
  &lt;/div&gt;
&lt;/div&gt;

&lt;/div&gt;


</description>
      <category>agents</category>
      <category>ai</category>
      <category>architecture</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Gemma 4 Plays Snake: A Real-Time AI Game Loop</title>
      <dc:creator>Denis Arruda</dc:creator>
      <pubDate>Wed, 13 May 2026 00:16:30 +0000</pubDate>
      <link>https://forem.com/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-47n5</link>
      <guid>https://forem.com/denisarruda/gemma-4-plays-snake-a-real-time-ai-game-loop-47n5</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-gemma-2026-05-06"&gt;Gemma 4 Challenge: Build with Gemma 4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built a Snake game where the snake is driven entirely by Gemma 4. The model reads the board, reasons about the move, and the snake follows.&lt;/p&gt;

&lt;p&gt;The backend is a Quarkus application written in Java 26 with virtual threads. A scheduler fires every 200 ms, advances the snake one cell, checks for collisions and food, and broadcasts the updated board as JSON to every connected browser over WebSocket. The UI is a plain HTML5 Canvas   that renders the live game in real time.&lt;/p&gt;

&lt;p&gt;What makes the architecture interesting is how the AI fits in. Every tick, if Gemma 4 is not already busy, the engine snapshots the board state, builds a structured prompt and fires it off asynchronously via langChain4j. The game never waits for a response. If the model is still thinking when the next tick fires, the snake keeps going straight. When the answer arrives, the direction is stored atomically and picked up on the next tick.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Gemma 4 playing Snake in real time — every move decided by the model, every frame rendered live over WebSocket.&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%2Fg6scr2z57oz3tf5o7m1u.gif" 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%2Fg6scr2z57oz3tf5o7m1u.gif" alt="Snake game board" width="426" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;Project on Github: &lt;a href="https://github.com/denis-arruda/gemma4-snake-ai" rel="noopener noreferrer"&gt;https://github.com/denis-arruda/gemma4-snake-ai&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Gemma 4
&lt;/h2&gt;

&lt;p&gt;Gemma 4 (gemini-4-flash) is the right model for this loop: low latency, strong instruction-following, and capable enough to respect the safety constraints in the prompt and navigate toward food. A slower model stalls the game; a weaker one ignores the labels and dies.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>gemmachallenge</category>
      <category>gemma</category>
    </item>
    <item>
      <title>Construindo Sistemas Multiagentes Resilientes</title>
      <dc:creator>Denis Arruda</dc:creator>
      <pubDate>Fri, 08 May 2026 00:50:42 +0000</pubDate>
      <link>https://forem.com/denisarruda/construindo-sistemas-multiagentes-resilientes-26h9</link>
      <guid>https://forem.com/denisarruda/construindo-sistemas-multiagentes-resilientes-26h9</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Os sistemas com agentes de IA estão evoluindo rapidamente. Hoje já vemos arquiteturas multiagentes capazes de resolver tarefas complexas através da divisão de subtarefas executadas por agentes especializados, cada um operando com contexto e responsabilidades específicas. As demos de sistemas multiagentes se tornaram extremamente populares, mas durante o desenho da arquitetura existe um ponto fundamental que não pode ser ignorado: qualquer componente pode falhar. Em ambientes distribuídos, agentes podem ficar lentos, indisponíveis ou responder com atraso. Por isso, arquiteturas resilientes precisam ser desenhadas para continuar funcionando mesmo em cenários de falha, operando de forma degradada quando necessário, mas ainda mantendo a entrega de valor para o cliente.&lt;/p&gt;

&lt;p&gt;Para explorar esses conceitos na prática, foi criado um projeto utilizando Java 26, Quarkus, Apache Kafka e LangChain4j controlando cobras em uma versão distribuída do jogo Snake. O objetivo é demonstrar padrões reais de resiliência, comunicação assíncrona, tolerância a falhas e observabilidade em arquiteturas multiagentes.&lt;/p&gt;

&lt;p&gt;Projeto no GitHub: &lt;a href="https://github.com/denis-arruda/snake-ai-simulation" rel="noopener noreferrer"&gt;https://github.com/denis-arruda/snake-ai-simulation&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%2Ftf9qvn4locr6vz0mobop.gif" 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%2Ftf9qvn4locr6vz0mobop.gif" alt="board do jogo snake" width="426" height="240"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Neste projeto, cada cobra é controlada por um agente independente executando em Quarkus e se comunicando de forma assíncrona através de eventos. Apesar da simplicidade visual, a arquitetura reproduz vários desafios encontrados em sistemas corporativos reais: latência, falhas, informação parcial e consistência eventual.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sistemas Multiagentes São Sistemas Distribuídos
&lt;/h2&gt;

&lt;p&gt;Um dos maiores erros ao projetar agentes de IA é tratá-los como componentes síncronos de request/response.&lt;/p&gt;

&lt;p&gt;Na prática, aplicações multiagentes se comportam muito mais como sistemas distribuídos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Decisões chegam atrasadas&lt;/li&gt;
&lt;li&gt;Agentes podem falhar&lt;/li&gt;
&lt;li&gt;Informações ficam desatualizadas&lt;/li&gt;
&lt;li&gt;Respostas são inconsistentes&lt;/li&gt;
&lt;li&gt;Coordenação é probabilística&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Na simulação do Snake, a engine do jogo continua executando a cada poucos milissegundos enquanto agentes de IA podem levar segundos para decidir o próximo movimento.&lt;/p&gt;

&lt;p&gt;Em vez de bloquear o sistema esperando todas as respostas, a engine utiliza a última decisão conhecida e mantém a simulação funcionando. Esse comportamento reflete um princípio importante de sistemas resilientes: disponibilidade frequentemente é mais importante do que consistência perfeita.&lt;/p&gt;

&lt;p&gt;Resiliência com Quarkus e Kafka&lt;/p&gt;

&lt;p&gt;No lado da aplicação, o Quarkus oferece recursos importantes para tolerância a falhas em arquiteturas multiagentes.&lt;/p&gt;

&lt;p&gt;Com SmallRye Fault Tolerance é possível implementar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Timeout&lt;/li&gt;
&lt;li&gt;Retry&lt;/li&gt;
&lt;li&gt;Circuit breaker&lt;/li&gt;
&lt;li&gt;Fallback&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Timeout evita que a engine espere indefinidamente por um agente.&lt;br&gt;
Circuit breaker isola serviços de IA instáveis.&lt;br&gt;
Fallback permite que a cobra continue se movendo mesmo sem nova decisão.&lt;/p&gt;

&lt;p&gt;Esses mecanismos se tornam fundamentais porque aplicações baseadas em LLM introduzem latência imprevisível e comportamento não determinístico em ambientes distribuídos.&lt;/p&gt;

&lt;p&gt;Outro ponto essencial em arquiteturas resilientes é a mensageria assíncrona. Neste projeto, utilizamos o Apache Kafka como camada de comunicação entre a engine do jogo e os agentes de IA. O uso de eventos desacopla os componentes e aumenta significativamente a tolerância a falhas do sistema. Mesmo que um agente fique indisponível temporariamente, as mensagens continuam armazenadas e podem ser processadas posteriormente sem interromper o funcionamento geral da aplicação. Esse modelo permite que o sistema continue operando de forma degradada, mas ainda disponível para o usuário final.&lt;/p&gt;

&lt;p&gt;Além disso, o Quarkus oferece recursos importantes de observabilidade, incluindo métricas de performance, tracing distribuído e integração com ferramentas de monitoramento. Essas métricas podem ser utilizadas para gerar alertas quando algo estiver fora do comportamento esperado, permitindo identificar degradação de performance, falhas em agentes ou aumento de latência antes que o problema impacte o usuário final.&lt;/p&gt;

&lt;p&gt;Com métricas utilizando OpenTelemetry e Micrometer é possível acompanhar indicadores importantes do sistema, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tempo médio de resposta dos agentes&lt;/li&gt;
&lt;li&gt;Quantidade de decisões processadas por segundo&lt;/li&gt;
&lt;li&gt;Latência de consumo das mensagens no Kafka&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso cria uma demonstração de como observabilidade ajuda a entender comportamento emergente e diagnosticar falhas em sistemas multiagentes.&lt;/p&gt;

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

&lt;p&gt;Muitas organizações estão experimentando agentes de IA, mas poucas estão aplicando o mesmo rigor de engenharia utilizado em sistemas distribuídos tradicionais.&lt;/p&gt;

&lt;p&gt;Agentes de IA introduzem incerteza, latência e comportamento não determinístico em arquiteturas corporativas. Sem resiliência, mensageria assíncrona e observabilidade, esses sistemas rapidamente se tornam frágeis em produção.&lt;/p&gt;

&lt;p&gt;Às vezes, um simples jogo da Snake é suficiente para demonstrar como sistemas multiagentes carregam toda a complexidade inerente aos sistemas distribuídos: comunicação assíncrona, falhas parciais, latência, consistência eventual e necessidade de resiliência.&lt;/p&gt;

&lt;p&gt;No fim, arquiteturas multiagentes não devem ser tratadas apenas como um desafio de IA, mas como um problema clássico de engenharia de sistemas distribuídos.&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>java</category>
      <category>agents</category>
      <category>eventdriven</category>
    </item>
    <item>
      <title>Criando um CLI de Código Morse com Java Moderno</title>
      <dc:creator>Denis Arruda</dc:creator>
      <pubDate>Wed, 29 Apr 2026 15:16:15 +0000</pubDate>
      <link>https://forem.com/denisarruda/criando-um-cli-de-codigo-morse-com-java-moderno-3h5</link>
      <guid>https://forem.com/denisarruda/criando-um-cli-de-codigo-morse-com-java-moderno-3h5</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Nem todo código precisa começar com um projeto estruturado, build tool (maven/gradle) e dezenas de dependências.&lt;/p&gt;

&lt;p&gt;Com as evoluções recentes do Java, especialmente nas versões mais novas como o Java 25, ficou muito mais simples escrever pequenos utilitários diretamente como scripts.&lt;/p&gt;

&lt;p&gt;Neste artigo, vamos explorar isso na prática construindo um CLI que converte texto para código Morse.&lt;/p&gt;

&lt;p&gt;E tem um detalhe interessante: o dia 27 de abril marca o nascimento de Samuel Morse (1791–1872), criador do código Morse, por isso essa data é lembrada como o Dia do Código Morse.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Objetivo
&lt;/h2&gt;

&lt;p&gt;Queremos algo direto ao ponto:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;./morse "hello world"&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;E obter:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.... . .-.. .-.. --- / .-- --- .-. .-.. -..&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;O objetivo deste CLI é receber uma string via argumentos de linha de comando, consolidar essa entrada preservando os espaços e convertê-la para Código Morse Internacional, suportando letras, números, espaços e pontuação comum, enquanto caracteres não reconhecidos são representados por ? na saída.&lt;/p&gt;

&lt;p&gt;Sem setup. Sem projeto. Apenas código.&lt;/p&gt;

&lt;h2&gt;
  
  
  O Código
&lt;/h2&gt;

&lt;p&gt;Aqui está o programa completo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="err"&gt;#&lt;/span&gt;&lt;span class="o"&gt;!/&lt;/span&gt;&lt;span class="n"&gt;usr&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;bin&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;java&lt;/span&gt; &lt;span class="o"&gt;--&lt;/span&gt;&lt;span class="n"&gt;source&lt;/span&gt; &lt;span class="mi"&gt;25&lt;/span&gt;

&lt;span class="kt"&gt;void&lt;/span&gt; &lt;span class="nf"&gt;main&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;...&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;input&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;join&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;" "&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt; &lt;span class="n"&gt;args&lt;/span&gt;&lt;span class="o"&gt;).&lt;/span&gt;&lt;span class="na"&gt;trim&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;isEmpty&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="no"&gt;IO&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;println&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;toMorse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;input&lt;/span&gt;&lt;span class="o"&gt;));&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;static&lt;/span&gt; &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="nf"&gt;toMorse&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;sb&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;StringBuilder&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
    &lt;span class="kt"&gt;var&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;char&lt;/span&gt; &lt;span class="n"&gt;c&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toUpperCase&lt;/span&gt;&lt;span class="o"&gt;().&lt;/span&gt;&lt;span class="na"&gt;toCharArray&lt;/span&gt;&lt;span class="o"&gt;())&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;String&lt;/span&gt; &lt;span class="n"&gt;code&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;switch&lt;/span&gt; &lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;c&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'A'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'B'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-..."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'C'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-.-."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'D'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-.."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'E'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'F'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"..-."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'G'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"--."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'H'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"...."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'I'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'J'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".---"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'K'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-.-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'L'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".-.."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'M'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"--"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'N'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'O'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"---"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'P'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".--."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'Q'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"--.-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'R'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".-."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'S'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"..."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'T'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'U'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"..-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'V'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"...-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'W'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".--"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'X'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-..-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'Y'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-.--"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'Z'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"--.."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'0'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-----"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'1'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".----"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'2'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"..---"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'3'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"...--"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'4'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"....-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'5'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"....."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'6'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-...."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'7'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"--..."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'8'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"---.."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'9'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"----."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;' '&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"/"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'.'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".-.-.-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;','&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"--..--"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'?'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"..--.."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'!'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-.-.--"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;':'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"---..."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;';'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-.-.-."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'('&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-.--."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;')'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"-.--.-"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'"'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".-..-."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'\''&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".----."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'@'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".--.-."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt; &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="sc"&gt;'&amp;amp;'&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;".-..."&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
            &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt; &lt;span class="s"&gt;"?"&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
        &lt;span class="o"&gt;};&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;(!&lt;/span&gt;&lt;span class="n"&gt;first&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="sc"&gt;' '&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;append&lt;/span&gt;&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="n"&gt;code&lt;/span&gt;&lt;span class="o"&gt;);&lt;/span&gt;
        &lt;span class="n"&gt;first&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
    &lt;span class="o"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="na"&gt;toString&lt;/span&gt;&lt;span class="o"&gt;();&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse código utiliza o modo script com &lt;code&gt;--source 25&lt;/code&gt; no topo, o que permite escrever o programa como um único arquivo executável, eliminando a necessidade de compilação, estrutura de projeto e até mesmo o boilerplate tradicional de classes.&lt;/p&gt;

&lt;p&gt;Além disso, o código tira proveito de recursos modernos da linguagem:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;var&lt;/code&gt; para reduzir ruído na declaração de variáveis.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;switch&lt;/code&gt; expression com &lt;code&gt;-&amp;gt;&lt;/code&gt;, deixando o mapeamento direto e legível.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;String.join&lt;/code&gt; para lidar com argumentos de forma simples.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O resultado é um código enxuto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como executar
&lt;/h2&gt;

&lt;p&gt;Para rodar o script, é necessário ter o Java 25 (ou uma versão mais recente) instalado.&lt;/p&gt;

&lt;p&gt;Você pode executá-lo de duas formas:&lt;/p&gt;

&lt;p&gt;Como script executável:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;chmod&lt;/span&gt; +x morse
./morse Hello World
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou via Java:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;java --source 25 morse Hello World&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;O resultado será:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;.... . .-.. .-.. --- / .-- --- .-. .-.. -..&lt;/code&gt;&lt;/p&gt;

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

&lt;p&gt;Esse exemplo simples mostra como o Java evoluiu para reduzir significativamente o boilerplate, permitindo escrever código mais direto e focado no problema. Hoje, já é possível criar scripts em Java sem precisar de um processo explícito de compilação ou até mesmo conhecer shell scripting, tornando a linguagem uma opção viável para automações e pequenas ferramentas.&lt;/p&gt;

&lt;p&gt;Que tipo de ferramenta você criaria com essa abordagem? Compartilhe nos comentários suas ideias, sugestões.&lt;/p&gt;

&lt;p&gt;Projeto: &lt;a href="https://github.com/denis-arruda/morse-cli-java" rel="noopener noreferrer"&gt;https://github.com/denis-arruda/morse-cli-java&lt;/a&gt;&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>java</category>
    </item>
    <item>
      <title>Observabilidade de agentes de IA com LangChain4j</title>
      <dc:creator>Denis Arruda</dc:creator>
      <pubDate>Thu, 02 Apr 2026 01:26:02 +0000</pubDate>
      <link>https://forem.com/denisarruda/observabilidade-de-agentes-de-ia-com-langchain4j-ncd</link>
      <guid>https://forem.com/denisarruda/observabilidade-de-agentes-de-ia-com-langchain4j-ncd</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Estamos vivendo uma onda no desenvolvimento de software impulsionada pelo uso de &lt;strong&gt;IA generativa&lt;/strong&gt; e, mais recentemente, por &lt;strong&gt;agentes de IA&lt;/strong&gt; capazes de tomar decisões, orquestrar chamadas a modelos e interagir com ferramentas externas.&lt;/p&gt;

&lt;p&gt;Esses agentes vão além de simples integrações com LLMs. Eles executam fluxos dinâmicos, fazem múltiplas chamadas ao modelo, utilizam ferramentas e tomam decisões com base no contexto. Esse comportamento os aproxima muito mais de sistemas distribuídos.&lt;/p&gt;

&lt;p&gt;Com isso, à medida que começamos a levar esses agentes para &lt;strong&gt;ambientes corporativos&lt;/strong&gt;, surge um requisito essencial que não pode ser ignorado: &lt;strong&gt;observabilidade e monitoramento&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;De forma simplificada:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Monitoramento&lt;/strong&gt; está relacionado a acompanhar métricas do sistema, como latência, taxa de erro, uso de recursos e disponibilidade. Ele responde perguntas como &lt;em&gt;"o sistema está saudável?"&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Observabilidade&lt;/strong&gt; está relacionado a  capacidade de entender o comportamento interno do sistema a partir de sinais externos (métricas, logs e traces). Ela permite responder perguntas como &lt;em&gt;"por que o sistema está se comportando dessa forma?"&lt;/em&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Precisamos dessa visibilidade para poder responder perguntas como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quantas chamadas ao modelo foram feitas?&lt;/li&gt;
&lt;li&gt;Qual foi o custo em tokens dessa operação?&lt;/li&gt;
&lt;li&gt;Onde está o gargalo de performance?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esse desafio se torna ainda mais difícil em cenários com &lt;strong&gt;multiagentes&lt;/strong&gt;, onde diferentes agentes interagem entre si, ampliando a complexidade do fluxo e tornando a análise ainda mais difícil sem ferramentas adequadas.&lt;/p&gt;

&lt;p&gt;Neste artigo, vamos focar na &lt;strong&gt;observabilidade de agentes de IA&lt;/strong&gt;, explorando como instrumentar e monitorar suas execuções utilizando o &lt;strong&gt;LangChain4j&lt;/strong&gt; em conjunto com sua extensão para o &lt;strong&gt;Quarkus&lt;/strong&gt;, e como visualizar essas informações de forma prática através de dashboards no Grafana.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que observar em agentes de IA
&lt;/h2&gt;

&lt;p&gt;Diferente de aplicações tradicionais, onde observamos requisições HTTP, bancos de dados e filas, agentes de IA introduzem uma nova complexidade.&lt;/p&gt;

&lt;p&gt;Abaixo estão os principais pontos que devem ser observados:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Latência de cada chamada ao modelo.&lt;/li&gt;
&lt;li&gt;Consumo de tokens de entrada.&lt;/li&gt;
&lt;li&gt;Consumo de tokens de saída.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;As métricas são fundamentais em ambientes corporativos, onde o custo precisa ser previsível e controlado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Usando o LangChain4j com Quarkus
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;LangChain4j&lt;/strong&gt; já fornece mecanismos nativos para observar esses aspectos através de eventos do ciclo de vida da execução do agente, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Início da execução&lt;/li&gt;
&lt;li&gt;Requisição ao modelo&lt;/li&gt;
&lt;li&gt;Resposta recebida&lt;/li&gt;
&lt;li&gt;Execução de ferramentas&lt;/li&gt;
&lt;li&gt;Erros&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Na próxima seção, vamos explorar como esses eventos funcionam na prática e como utilizá-los para instrumentar nossos agentes.&lt;/p&gt;

&lt;h2&gt;
  
  
  Expondo métricas com LangChain4j no Quarkus
&lt;/h2&gt;

&lt;p&gt;Uma das grandes vantagens de utilizar o &lt;strong&gt;Quarkus&lt;/strong&gt; em conjunto com o &lt;strong&gt;LangChain4j&lt;/strong&gt; é a facilidade de integrar observabilidade utilizando padrões já consolidados no ecossistema Java.&lt;/p&gt;

&lt;p&gt;Para expor métricas da aplicação (incluindo aquelas relacionadas à execução de agentes de IA), basta incluir a extensão de Micrometer no projeto para que métricas passem a ser expostas automaticamente.&lt;/p&gt;

&lt;p&gt;Exemplo de trecho do &lt;code&gt;pom.xml&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;dependencies&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.quarkus&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;quarkus-micrometer&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;dependency&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;groupId&amp;gt;&lt;/span&gt;io.quarkus&lt;span class="nt"&gt;&amp;lt;/groupId&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;artifactId&amp;gt;&lt;/span&gt;quarkus-micrometer-registry-prometheus&lt;span class="nt"&gt;&amp;lt;/artifactId&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/dependency&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/dependencies&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Após incluir essas dependências, o Quarkus automaticamente expõe as métricas da aplicação no endpoint:&lt;/p&gt;

&lt;p&gt;Por padrão, elas ficam disponíveis em:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;/q/metrics&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Como vamos utilizar o Prometheus, esse comportamento é exatamente o que precisamos. O endpoint já expõe as métricas no formato nativo do Prometheus, permitindo que ele realize o scraping diretamente, sem necessidade de adaptações ou conversões adicionais.&lt;/p&gt;

&lt;p&gt;Com essa configuração, qualquer métrica registrada via Micrometer, incluindo as geradas pelo LangChain4j, estará disponível para coleta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configurando Prometheus e Grafana
&lt;/h2&gt;

&lt;p&gt;Para visualizar as métricas coletadas, vamos utilizar duas ferramentas bastante conhecidas de observabilidade:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Prometheus&lt;/strong&gt;: responsável por coletar (scraping) e armazenar as métricas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Grafana&lt;/strong&gt;: utilizado para visualização e criação de dashboards.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Neste artigo, não vamos nos aprofundar na configuração detalhada dessas ferramentas, pois o foco será a &lt;strong&gt;construção dos dashboards para agentes de IA&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configurando o Grafana e Prometheus
&lt;/h3&gt;

&lt;p&gt;Para que o Prometheus consiga coletar as métricas expostas pelo Quarkus, precisamos apenas configurar um &lt;em&gt;job&lt;/em&gt; apontando para o endpoint &lt;code&gt;/q/metrics&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Exemplo de configuração (&lt;code&gt;prometheus.yml&lt;/code&gt;):&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;scrape_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;job_name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;quarkus-app'&lt;/span&gt;
    &lt;span class="na"&gt;metrics_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/q/metrics'&lt;/span&gt;
    &lt;span class="na"&gt;static_configs&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;targets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;localhost:8080'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com isso, o Prometheus já será capaz de coletar todas as métricas expostas pela aplicação.&lt;/p&gt;

&lt;p&gt;Para facilitar, você pode utilizar uma imagem pronta que já inclui Prometheus, Grafana e outras ferramentas integradas. Uma ótima opção é a imagem &lt;br&gt;
&lt;code&gt;grafana/otel-lgtm&lt;/code&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Dashboards no Grafana para agentes de IA
&lt;/h2&gt;

&lt;p&gt;Em aplicações corporativas, é comum termos diversos dashboards cobrindo diferentes aspectos do sistema, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Métricas de APIs REST (latência, throughput, erros)&lt;/li&gt;
&lt;li&gt;Banco de dados (conexões, tempo de query)&lt;/li&gt;
&lt;li&gt;Infraestrutura (CPU, memória)&lt;/li&gt;
&lt;li&gt;Métricas da JVM (GC, threads, heap)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Esses dashboards continuam sendo fundamentais e fazem parte da base de qualquer estratégia de observabilidade.&lt;/p&gt;

&lt;p&gt;No entanto, ao introduzirmos &lt;strong&gt;agentes de IA&lt;/strong&gt;, surge uma nova complexidade que precisa ser monitorada. Aqui, vamos focar exclusivamente em &lt;strong&gt;como observar agentes de IA através de dashboards no Grafana&lt;/strong&gt;.&lt;/p&gt;
&lt;h2&gt;
  
  
  Métricas disponíveis com LangChain4j no Quarkus
&lt;/h2&gt;

&lt;p&gt;Utilizando a extensão do &lt;strong&gt;LangChain4j&lt;/strong&gt; com o &lt;strong&gt;Quarkus&lt;/strong&gt;, as seguintes métricas estão disponíveis:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;langchain4j_aiservices_counted_total&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Total de execuções de serviços de IA (quantidade de chamadas realizadas)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;langchain4j_aiservices_timed_seconds_count&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Número de execuções monitoradas para cálculo de tempo&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;langchain4j_aiservices_timed_seconds_sum&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Soma total do tempo gasto nas execuções&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;code&gt;langchain4j_aiservices_timed_seconds_max&lt;/code&gt;&lt;br&gt;&lt;br&gt;
Tempo máximo registrado em uma execução&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Construindo dashboards no Grafana
&lt;/h2&gt;

&lt;p&gt;Com base nas métricas expostas pelo &lt;strong&gt;LangChain4j&lt;/strong&gt;, podemos construir os dashboards no Grafana.&lt;/p&gt;

&lt;p&gt;A seguir estão alguns exemplos de painéis e as métricas utilizadas em cada um deles:&lt;/p&gt;
&lt;h3&gt;
  
  
  AI Services (visão geral)
&lt;/h3&gt;

&lt;p&gt;Um painel do tipo tabela pode ser utilizado para exibir uma visão geral dos serviços de IA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Métrica utilizada:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;langchain4j_aiservices_counted_total&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Com essa métrica, conseguimos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Identificação dos agentes de IA&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Input Token Usage Total
&lt;/h3&gt;

&lt;p&gt;Esse painel exibe o total de &lt;strong&gt;tokens de entrada (input)&lt;/strong&gt; consumidos pelos agentes de IA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Métrica utilizada:&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;gen_ai_client_token_usage_total{gen_ai_token_type="input"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Output Token Usage Total
&lt;/h3&gt;

&lt;p&gt;Esse painel exibe o total de &lt;strong&gt;tokens de saída (output)&lt;/strong&gt; gerados pelos agentes de IA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Métrica utilizada:&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;gen_ai_client_token_usage_total{gen_ai_token_type="output"}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AI Estimated Cost Total
&lt;/h3&gt;

&lt;p&gt;Esse painel exibe o &lt;strong&gt;custo estimado total&lt;/strong&gt; das interações com os modelos de IA ao longo do tempo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Métrica utilizada:&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;gen_ai_client_estimated_cost_total
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AI Client Operation Duration (latência média)
&lt;/h3&gt;

&lt;p&gt;Esse painel exibe a &lt;strong&gt;latência média das chamadas ao modelo (LLM)&lt;/strong&gt; realizadas pelos agentes de IA.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Métrica utilizada:&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;sum by (service_name) (
  rate(gen_ai_client_operation_duration_seconds_sum[5m])
)
/
sum by (service_name) (
  rate(gen_ai_client_operation_duration_seconds_count[5m])
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  AI Services Duration (latência média do agente)
&lt;/h3&gt;

&lt;p&gt;Esse painel exibe a &lt;strong&gt;latência média de execução dos serviços de IA&lt;/strong&gt;, ou seja, o tempo total que um agente leva para processar uma requisição completa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Métrica utilizada:&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;sum by (aiservice) (
  rate(langchain4j_aiservices_timed_seconds_sum[5m])
)
/
sum by (aiservice) (
  rate(langchain4j_aiservices_timed_seconds_count[5m])
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você pode importar diretamente o dashboard e adaptar conforme sua necessidade:&lt;/p&gt;

&lt;p&gt;👉 &lt;a href="https://github.com/denis-arruda/langchain4j-grafana-dashboard" rel="noopener noreferrer"&gt;https://github.com/denis-arruda/langchain4j-grafana-dashboard&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Aplicações que utilizam &lt;strong&gt;agentes de IA&lt;/strong&gt;, e principalmente cenários com &lt;strong&gt;multiagentes&lt;/strong&gt;, trazem uma nova complexidade para o desenvolvimento de software.&lt;/p&gt;

&lt;p&gt;Esses sistemas se comportam, na prática, como &lt;strong&gt;aplicações distribuídas&lt;/strong&gt;, com fluxos dinâmicos e  múltiplas interações com modelos de linguagem. Isso torna ainda mais desafiador entender seu comportamento em produção.&lt;/p&gt;

&lt;p&gt;Por esse motivo, &lt;strong&gt;observabilidade deixa de ser um diferencial e passa a ser um requisito essencial&lt;/strong&gt;, especialmente em ambientes corporativos.&lt;/p&gt;

&lt;p&gt;Ao longo deste artigo, vimos é simples dar os primeiros passos.&lt;/p&gt;

&lt;p&gt;A integração entre Prometheus e Grafana é bastante natural e já consolidada no ecossistema Java, o que reduz significativamente o esforço de adoção.&lt;/p&gt;

&lt;p&gt;Além de construir agentes inteligentes, é preciso  entender como eles se comportam em produção.&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>java</category>
      <category>quarkus</category>
    </item>
    <item>
      <title>Java em Containers: Estratégias Modernas para Build</title>
      <dc:creator>Denis Arruda</dc:creator>
      <pubDate>Thu, 05 Feb 2026 12:12:27 +0000</pubDate>
      <link>https://forem.com/denisarruda/java-em-containers-estrategias-modernas-para-build-1bpa</link>
      <guid>https://forem.com/denisarruda/java-em-containers-estrategias-modernas-para-build-1bpa</guid>
      <description>&lt;h2&gt;
  
  
  Introdução
&lt;/h2&gt;

&lt;p&gt;Quando pensamos em entregar software com qualidade e em automatizar ao máximo esse processo, a infraestrutura como código e os containers desempenham um papel fundamental. &lt;/p&gt;

&lt;p&gt;Criar containers nem sempre é a parte mais divertida do nosso dia a dia, mas faz parte do nosso trabalho como desenvolvedores: não apenas escrever código, mas também garantir que ele seja executado da melhor forma possível em produção.&lt;/p&gt;

&lt;p&gt;O objetivo deste artigo é avaliar as principais opções disponíveis hoje para a criação de containers para aplicações Java, apresentando uma visão geral de cada abordagem. Nenhuma delas é universalmente melhor que as outras, todas possuem vantagens e desvantagens. A ideia é ajudar você a escolher a alternativa mais adequada ao seu contexto e, com isso, aprimorar o processo de build do seu projeto.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é um Container
&lt;/h2&gt;

&lt;p&gt;Antes de escolher a alternativa mais adequada, é fundamental entender o que realmente é um container. Compreender esse conceito nos ajuda a enxergar o que pode ser melhorado.&lt;/p&gt;

&lt;p&gt;Um container pode ser entendido como um processo autocontido, que reúne todas as dependências necessárias para executar uma aplicação, como bibliotecas, configurações e runtime. Diferente de uma máquina virtual, ele não precisa carregar um sistema operacional completo, o que o torna muito mais leve e rápido.&lt;/p&gt;

&lt;p&gt;Os containers são executados sobre um Docker Daemon, que é o responsável por gerenciar imagens, redes, volumes e a execução dos processos. Além disso, eles compartilham o kernel do sistema operacional do host onde estão rodando.&lt;/p&gt;

&lt;p&gt;Essa arquitetura permite que múltiplos containers coexistam no mesmo ambiente de forma eficiente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dockerfile simples
&lt;/h2&gt;

&lt;p&gt;O Dockerfile é como se fosse uma receita para a criação de uma imagem de container. É nele que descrevemos, passo a passo, como o ambiente da aplicação deve ser montado, quais dependências devem ser instaladas e como o sistema deve ser inicializado.&lt;/p&gt;

&lt;p&gt;Cada instrução presente no Dockerfile gera uma camada na imagem final. Essas camadas são armazenadas em cache pelo Docker e reaproveitadas sempre que possível, tornando o processo de build mais rápido e eficiente.&lt;/p&gt;

&lt;p&gt;Por esse motivo, a ordem das instruções é extremamente importante. As primeiras camadas devem conter aquilo que muda com menos frequência, como a imagem base e as dependências do projeto. Já as últimas camadas devem conter os elementos que mudam mais frequentemente, como o código-fonte e os arquivos compilados.&lt;/p&gt;

&lt;p&gt;A seguir, temos um exemplo de Dockerfile simples:&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="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; bellsoft/liberica-openjre-debian:25&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; app.jar .&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;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "app.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse modelo é frequentemente utilizado por ser fácil de entender, mas ele também apresenta algumas limitações importantes.&lt;/p&gt;

&lt;p&gt;A primeira linha define a imagem base, que, nesse caso, contém um sistema Linux Debian com a JRE do Java na versão 25. Essa escolha facilita a configuração, pois já inclui o runtime necessário.&lt;/p&gt;

&lt;p&gt;Em seguida, o comando COPY assume que já existe um arquivo app.jar pronto para uso. No entanto, o Dockerfile não deixa claro como esse artefato foi gerado. Isso pode gerar inconsistências entre ambientes e dificultar a reprodução do build.&lt;/p&gt;

&lt;p&gt;Outro ponto importante é que esse Dockerfile não define um usuário para executar a aplicação. Como consequência, o container será iniciado com o usuário root, o que representa um risco de segurança.&lt;/p&gt;

&lt;p&gt;Apesar de funcional, esse tipo de Dockerfile deve ser encarado apenas como um ponto de partida.&lt;/p&gt;

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

&lt;p&gt;O Dockerfile pode ser composto por múltiplos estágios, em um modelo conhecido como &lt;strong&gt;multi-stage&lt;/strong&gt;. Nesse modelo, cada estágio é responsável por uma etapa específica e gera uma imagem intermediária, que pode servir de entrada no estágio seguinte.&lt;/p&gt;

&lt;p&gt;Essa separação traz diversos benefícios. Um dos principais é a padronização do processo de build, já que toda a construção do projeto passa a ser definida dentro do próprio Dockerfile. Com isso, não é mais necessário depender de configurações externas para gerar o artefato do projeto.&lt;/p&gt;

&lt;p&gt;A seguir, temos um exemplo de Dockerfile multi-stage:&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="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;maven:3-eclipse-temurin-25&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; /opt/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src ./src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pom.xml .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;~/.m2 mvn &lt;span class="nt"&gt;-f&lt;/span&gt; /opt/app/pom.xml package &lt;span class="nt"&gt;-DskipTests&lt;/span&gt;

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; bellsoft/liberica-openjre-debian:25&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; /opt/app
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /opt/app/target/app.jar /opt/app.jar&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;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "/opt/app.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse Dockerfile é dividido em dois estágios principais: o estágio de build e o estágio de execução.&lt;/p&gt;

&lt;p&gt;No primeiro estágio, identificado pelo alias build, é utilizada a imagem maven:3-eclipse-temurin-25, que já contém o Maven e o JDK necessários para compilar a aplicação.&lt;/p&gt;

&lt;p&gt;Em seguida, os diretórios src e o arquivo pom.xml são copiados para dentro da imagem. Esses arquivos são necessários para que o Maven possa resolver as dependências e compilar o projeto. Ela utiliza um recurso para criar um cache persistente do repositório local do Maven.&lt;/p&gt;

&lt;p&gt;No segundo estágio, é utilizada a imagem bellsoft/liberica-openjre-debian:25. Essa imagem é leve e contém apenas a JRE necessária para executar a aplicação.&lt;/p&gt;

&lt;p&gt;Em seguida, o artefato &lt;code&gt;app.jar&lt;/code&gt; gerado no estágio de build é copiado para essa imagem.&lt;/p&gt;

&lt;p&gt;Por fim, a instrução ENTRYPOINT define o comando responsável por iniciar a aplicação Java quando o container é executado.&lt;/p&gt;

&lt;p&gt;Esse modelo permite um processo de build mais padronizado.&lt;/p&gt;

&lt;h2&gt;
  
  
  Criando JREs Customizadas com jdeps e jlink
&lt;/h2&gt;

&lt;p&gt;Desde o Java versão 9, a JVM passou a ser organizada em módulos, permitindo um controle sobre quais módulos da JVM são necessárias para uma aplicação. A partir dessa modularização, surgiram ferramentas como o &lt;strong&gt;jdeps&lt;/strong&gt; e o &lt;strong&gt;jlink&lt;/strong&gt;, que possibilitam a criação de uma JRE customizada contendo apenas os módulos necessários para uma aplicação.&lt;/p&gt;

&lt;p&gt;Uma JRE reduzida também contribui para a segurança da aplicação. Quanto menor o número de módulos e bibliotecas presentes na imagem, menor é a superfície de ataque e, consequentemente, o risco de exploração de vulnerabilidades.&lt;/p&gt;

&lt;p&gt;A seguir, temos um exemplo de Dockerfile multi-stage utilizando jdeps e jlink:&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="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;maven:3-eclipse-temurin-25&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;maven-build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /opt/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src ./src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pom.xml .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;~/.m2 mvn &lt;span class="nt"&gt;-f&lt;/span&gt; /opt/app/pom.xml package &lt;span class="nt"&gt;-DskipTests&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;eclipse-temurin:25-alpine&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;jre-build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /opt/app&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=maven-build /opt/app/target/app.jar .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;jar xf app.jar
&lt;span class="k"&gt;RUN &lt;/span&gt;jdeps &lt;span class="nt"&gt;--ignore-missing-deps&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt;  &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--recursive&lt;/span&gt;  &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--multi-release&lt;/span&gt; 25  &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--print-module-deps&lt;/span&gt;  &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--class-path&lt;/span&gt; &lt;span class="s1"&gt;'BOOT-INF/lib/*'&lt;/span&gt;  &lt;span class="se"&gt;\
&lt;/span&gt;    app.jar &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deps.info
&lt;span class="k"&gt;RUN &lt;/span&gt;jlink &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--verbose&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--add-modules&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;deps.info&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--strip-debug&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--compress&lt;/span&gt; 2 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--no-header-files&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--no-man-pages&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--output&lt;/span&gt; /customjre

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:3.18&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=jre-build /customjre /opt/jre&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JAVA_HOME=/opt/jre&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="$PATH:$JAVA_HOME/bin"&lt;/span&gt;

&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=maven-build /opt/app/target/app.jar /opt/app.jar&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;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "/opt/app.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Nesse exemplo, combinamos o multi-stage com o uso do jdeps e do jlink para gerar uma JRE customizada, resultando em uma imagem final ainda mais enxuta.&lt;/p&gt;

&lt;p&gt;No segundo estágio, utilizamos uma imagem baseada em Alpine com o JDK, necessária para executar o jdeps e o jlink.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;jdeps&lt;/code&gt; é utilizado para identificar quais módulos da JVM  são utilizados pela aplicação. O resultado é gravado no arquivo &lt;code&gt;deps.info&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Em seguida, o &lt;code&gt;jlink&lt;/code&gt; utiliza a lista gerada pelo jdeps para montar uma runtime personalizada.&lt;/p&gt;

&lt;p&gt;No último estágio, utilizamos uma imagem Alpine mínima. Copiamos apenas a JRE customizada criada anteriormente e configuramos as variáveis de ambiente para que o sistema utilize essa versão do Java.&lt;/p&gt;

&lt;p&gt;Em seguida, copiamos novamente o JAR da aplicação, para a imagem final.&lt;/p&gt;

&lt;h2&gt;
  
  
  Extração de Camadas em Aplicações Spring Boot
&lt;/h2&gt;

&lt;p&gt;Quando utilizamos o Spring Boot para empacotar uma aplicação no formato de &lt;strong&gt;uber JAR&lt;/strong&gt; ou &lt;strong&gt;fat JAR&lt;/strong&gt;, todas as dependências, bibliotecas e o código da aplicação ficam  em um único arquivo. Esse modelo facilita a distribuição, mas não é o mais eficiente para construção de imagens Docker.&lt;/p&gt;

&lt;p&gt;O Spring Boot também oferece um mecanismo de extração de camadas. O objetivo principal é otimizar o processo de build da imagem, aproveitando melhor o cache do Docker.&lt;/p&gt;

&lt;p&gt;Com esse mecanimos, o JAR é dividido em quatro camadas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Dependências do Spring Framework&lt;/li&gt;
&lt;li&gt;Spring Boot Loader&lt;/li&gt;
&lt;li&gt;Dependências da aplicação&lt;/li&gt;
&lt;li&gt;Código da aplicação&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Cada uma dessas partes passa a ser tratada como uma camada independente dentro da imagem Docker.&lt;/p&gt;

&lt;p&gt;Isso permite organizar o Dockerfile de forma que os componentes que mudam com menos frequência, como as dependências e o loader do Spring, sejam copiados primeiro. Já o código da aplicação, que sofre alterações com mais frequência, fica nas últimas camadas.&lt;/p&gt;

&lt;p&gt;Além disso, ao separar as camadas, o tempo de inicialização da aplicação Spring Boot também pode ser reduzido em alguns milissegundos.&lt;/p&gt;

&lt;p&gt;A seguir, um exemplo de Dockerfile com extração das camadas:&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="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;maven:3-eclipse-temurin-25&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; /build&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src ./src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pom.xml .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;~/.m2 mvn &lt;span class="nt"&gt;-f&lt;/span&gt; /build/pom.xml package &lt;span class="nt"&gt;-DskipTests&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;bellsoft/liberica-openjre-debian:25&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;extractor&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /extract&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /build/target/app.jar application.jar&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;java &lt;span class="nt"&gt;-Djarmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tools &lt;span class="nt"&gt;-jar&lt;/span&gt; application.jar extract &lt;span class="nt"&gt;--layers&lt;/span&gt; &lt;span class="nt"&gt;--destination&lt;/span&gt; extracted

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; bellsoft/liberica-openjre-debian:25&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /application&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=extractor /extract/extracted/dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=extractor /extract/extracted/spring-boot-loader/ ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=extractor /extract/extracted/snapshot-dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=extractor /extract/extracted/application/ ./&lt;/span&gt;
&lt;span class="k"&gt;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "application.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Assim como nos exemplos anteriores, o primeiro estágio é responsável por compilar o projeto utilizando Maven.&lt;/p&gt;

&lt;p&gt;No segundo estágio, utilizamos uma imagem que contém apenas a JRE, suficiente para realizar a extração das camadas.&lt;/p&gt;

&lt;p&gt;Em seguida, executamos o comando &lt;code&gt;java -Djarmode=tools -jar application.jar extract --layers&lt;/code&gt; para extrair as camadas do arquivo.&lt;/p&gt;

&lt;p&gt;No último estágio, copiamos cada camada separadamente.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abordagem Ultimate
&lt;/h2&gt;

&lt;p&gt;A versão que vamos chamar aqui de Ultimate combina as principais técnicas apresentadas ao longo deste artigo para criar imagens Docker para aplicações Java. Nessa abordagem, unimos o uso de multi-stage, a criação de uma JRE customizada com jdeps e jlink, e a extração de camadas do Spring Boot.&lt;/p&gt;

&lt;p&gt;Além disso, essa estratégia também inclui a criação de um usuário específico, com permissões apenas para executar a aplicação. Dessa forma, evitamos que o container seja iniciado com o usuário root, reduzindo o risco em caso de falhas ou vulnerabilidades.&lt;/p&gt;

&lt;p&gt;Ao combinar essas técnicas, conseguimos separar as responsabilidade dentro do processo de build. Um estágio é responsável pela compilação, outro pela análise e geração da JRE runtime, outro pela extração das camadas, e o estágio final reúne apenas o que é necessário para execução.&lt;/p&gt;

&lt;p&gt;Em seguida, o Dockerfile na versão Ultimate:&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="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;maven:3-eclipse-temurin-25&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; /build&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; src ./src&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; pom.xml .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nt"&gt;--mount&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;cache,target&lt;span class="o"&gt;=&lt;/span&gt;~/.m2 mvn &lt;span class="nt"&gt;-f&lt;/span&gt; /build/pom.xml package &lt;span class="nt"&gt;-DskipTests&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;eclipse-temurin:25-alpine&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;jre-build&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /jrebuild&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /build/target/app.jar .&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;jar xf app.jar
&lt;span class="k"&gt;RUN &lt;/span&gt;jdeps &lt;span class="nt"&gt;--ignore-missing-deps&lt;/span&gt; &lt;span class="nt"&gt;-q&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--recursive&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--multi-release&lt;/span&gt; 25 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--print-module-deps&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--class-path&lt;/span&gt; &lt;span class="s1"&gt;'BOOT-INF/lib/*'&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    app.jar &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; deps.info
&lt;span class="k"&gt;RUN &lt;/span&gt;jlink &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--verbose&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--add-modules&lt;/span&gt; &lt;span class="si"&gt;$(&lt;/span&gt;&lt;span class="nb"&gt;cat &lt;/span&gt;deps.info&lt;span class="si"&gt;)&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--strip-debug&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--compress&lt;/span&gt; 2 &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--no-header-files&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--no-man-pages&lt;/span&gt; &lt;span class="se"&gt;\
&lt;/span&gt;    &lt;span class="nt"&gt;--output&lt;/span&gt; /customjre

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s"&gt;bellsoft/liberica-openjre-debian:25&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;extractor&lt;/span&gt;
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /extract&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=build /build/target/app.jar application.jar&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;java &lt;span class="nt"&gt;-Djarmode&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;tools &lt;span class="nt"&gt;-jar&lt;/span&gt; application.jar extract &lt;span class="nt"&gt;--layers&lt;/span&gt; &lt;span class="nt"&gt;--destination&lt;/span&gt; extracted

&lt;span class="k"&gt;FROM&lt;/span&gt;&lt;span class="s"&gt; alpine:3.18&lt;/span&gt;
&lt;span class="c"&gt;# Create appuser&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;addgroup &lt;span class="nt"&gt;-S&lt;/span&gt; appgroup &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; adduser &lt;span class="nt"&gt;-S&lt;/span&gt; appuser &lt;span class="nt"&gt;-G&lt;/span&gt; appgroup
&lt;span class="k"&gt;WORKDIR&lt;/span&gt;&lt;span class="s"&gt; /application&lt;/span&gt;
&lt;span class="c"&gt;# Copy custom JRE&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=jre-build /customjre /opt/jre&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; JAVA_HOME=/opt/jre&lt;/span&gt;
&lt;span class="k"&gt;ENV&lt;/span&gt;&lt;span class="s"&gt; PATH="$PATH:$JAVA_HOME/bin"&lt;/span&gt;
&lt;span class="c"&gt;# Copy layered jar contents&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=extractor /extract/extracted/dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=extractor /extract/extracted/spring-boot-loader/ ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=extractor /extract/extracted/snapshot-dependencies/ ./&lt;/span&gt;
&lt;span class="k"&gt;COPY&lt;/span&gt;&lt;span class="s"&gt; --from=extractor /extract/extracted/application/ ./&lt;/span&gt;
&lt;span class="c"&gt;# Set permissions&lt;/span&gt;
&lt;span class="k"&gt;RUN &lt;/span&gt;&lt;span class="nb"&gt;chown&lt;/span&gt; &lt;span class="nt"&gt;-R&lt;/span&gt; appuser:appgroup /application
&lt;span class="k"&gt;USER&lt;/span&gt;&lt;span class="s"&gt; appuser&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;ENTRYPOINT&lt;/span&gt;&lt;span class="s"&gt; ["java", "-jar", "application.jar"]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Jib: Construindo Imagens Java Sem Dockerfile
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;Jib&lt;/strong&gt; é um plugin desenvolvido pelo Google que tem como objetivo simplificar a criação de imagens de container para aplicações Java. Ele está disponível tanto para &lt;strong&gt;Maven&lt;/strong&gt; quanto para &lt;strong&gt;Gradle&lt;/strong&gt; e foi projetado especificamente para projetos baseados na JVM.&lt;/p&gt;

&lt;p&gt;Uma das principais características do Jib é permitir a construção da imagem sem a necessidade de escrever um Dockerfile.&lt;/p&gt;

&lt;p&gt;Além disso, o Jib não depende de um Docker Daemon para funcionar. Ele consegue construir e publicar imagens diretamente em registries externos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Buildpacks: Containers Inteligentes sem Dockerfile
&lt;/h2&gt;

&lt;p&gt;Os &lt;strong&gt;Buildpacks&lt;/strong&gt; surgiram inicialmente no Heroku e, depois, evoluíram para um projeto mantido pela CNCF (Cloud Native Computing Foundation). O principal objetivo dessa tecnologia é automatizar a criação de imagens de container para diferentes stacks.&lt;/p&gt;

&lt;p&gt;Ao utilizar Buildpacks, não é necessário escrever um Dockerfile. A ferramenta identifica a stack utilizada e seleciona os componentes mais adequados para gerar uma imagem otimizada, seguindo boas práticas de segurança e performance.&lt;/p&gt;

&lt;p&gt;Os Buildpacks dependem de um Docker Daemon para funcionar. &lt;/p&gt;

&lt;p&gt;No ecossistema Spring, essa abordagem é ainda mais simples. Tanto o plugin do Maven quanto o do Gradle oferecem um target específico para construir a imagem do container, que internamente utiliza Buildpacks.&lt;/p&gt;

&lt;p&gt;Por exemplo, no Maven, é possível gerar a imagem com um comando como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;mvn spring-boot:build-image
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse comando utiliza Buildpacks por padrão.&lt;/p&gt;

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

&lt;p&gt;Neste artigo, exploramos diferentes abordagens para a construção de containers para aplicações Java, desde Dockerfiles simples até estratégias mais avançadas e ferramentas automatizadas como Jib e Buildpacks.&lt;/p&gt;

&lt;p&gt;Cada uma dessas opções possui vantagens e não existe uma solução única que seja ideal para todos os cenários.&lt;/p&gt;

&lt;p&gt;Uma das boas práticas mais importante é configurar a execução da aplicação com um usuário que possua apenas as permissões necessárias, evitando o uso do usuário root.&lt;/p&gt;

&lt;p&gt;Para ver um exemplo prático dessas abordagens aplicadas em um projeto real, você pode consultar o repositório no GitHub: &lt;a href="https://github.com/denis-arruda/taskmanager/tree/spring" rel="noopener noreferrer"&gt;TaskManager Spring Boot&lt;/a&gt;. Nele estão disponíveis diferentes configurações de Dockerfiles seguindo os modos explicados neste artigo.&lt;/p&gt;

&lt;p&gt;Agora, experimente aplicar essas técnicas no seu projeto. Teste a criação de uma JRE customizada com jdeps e jlink e observe o quanto o tamanho da imagem é reduzido. Experimente também a extração de camadas do Spring Boot e verifique se o tempo de inicialização da aplicação se torna mais rápido. Depois, compartilhe seus resultados, comparações e aprendizados nos comentários. Essa troca de experiências é fundamental para evoluirmos juntos como comunidade.&lt;/p&gt;

&lt;p&gt;À medida que o projeto cresce e os requisitos mudam, é importante revisitar essas decisões e buscar constantemente melhorias no processo de build e entrega. Investir tempo nessas práticas é investir diretamente na qualidade, confiabilidade e sustentabilidade do software.&lt;/p&gt;

</description>
      <category>braziliandevs</category>
      <category>java</category>
      <category>docker</category>
      <category>containers</category>
    </item>
  </channel>
</rss>
