<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Guilherme Yamakawa de Oliveira</title>
    <description>The latest articles on DEV Community by Guilherme Yamakawa de Oliveira (@guilherme44).</description>
    <link>https://dev.to/guilherme44</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%2F30816%2F90a4981b-417b-4d41-ac2c-4be6e1433b90.jpg</url>
      <title>DEV Community: Guilherme Yamakawa de Oliveira</title>
      <link>https://dev.to/guilherme44</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/guilherme44"/>
    <language>en</language>
    <item>
      <title>[PT-BR] Pela terceira vez, o Google enfia coisa no seu PC sem perguntar</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Fri, 08 May 2026 18:31:44 +0000</pubDate>
      <link>https://dev.to/guilherme44/pt-br-pela-terceira-vez-o-google-enfia-coisa-no-seu-pc-sem-perguntar-3pdf</link>
      <guid>https://dev.to/guilherme44/pt-br-pela-terceira-vez-o-google-enfia-coisa-no-seu-pc-sem-perguntar-3pdf</guid>
      <description>&lt;p&gt;Se você usa o Chrome, tem 4GB de modelo de IA na sua máquina que você nunca pediu. E não é a primeira vez que o Google faz isso, é a terceira. O pesquisador &lt;a href="https://www.thatprivacyguy.com/" rel="noopener noreferrer"&gt;Alexander Hanff&lt;/a&gt; &lt;a href="https://www.thatprivacyguy.com/blog/chrome-silent-nano-install/" rel="noopener noreferrer"&gt;publicou o relatório&lt;/a&gt; em 4 de maio de 2026. O arquivo se chama &lt;code&gt;weights.bin&lt;/code&gt;, fica em &lt;code&gt;OptGuideOnDeviceModel&lt;/code&gt; dentro do diretório do Chrome, e é o Gemini Nano. Sem aviso, sem checkbox. Se você descobrir e apagar, o Chrome baixa de novo no próximo restart.&lt;/p&gt;

&lt;p&gt;A primeira reação aqui foi de déjà vu, não de surpresa. Em dezembro de 2020 o &lt;a href="https://x.com/lorenb/status/1337832978253230081" rel="noopener noreferrer"&gt;Loren Brichter&lt;/a&gt; (@lorenb no X, o cara do "pull to refresh" e do &lt;a href="https://en.wikipedia.org/wiki/Tweetie" rel="noopener noreferrer"&gt;Tweetie&lt;/a&gt;, cliente que o Twitter comprou em 2010 pra virar o app oficial do iPhone) publicou o &lt;a href="https://chromeisbad.com/" rel="noopener noreferrer"&gt;chromeisbad.com&lt;/a&gt;, um site de uma página explicando como o Keystone, o updater do Google Chrome no Mac, ficava rodando em background deixando o sistema lento mesmo com o Chrome fechado. A reclamação dele veio onze anos depois da &lt;a href="https://www.wired.com/2009/02/why-googles-sof/" rel="noopener noreferrer"&gt;Wired publicar "Google Software Update Tool is evil"&lt;/a&gt; sobre o mesmo Keystone, quando ele apareceu enfiado no Google Earth coletando dados de hardware e baixando coisas em background sem avisar. O remédio que o Brichter indicou foi escrever instruções pra desinstalar Chrome e Keystone na mão, e recomendar Safari, Brave, Vivaldi, Opera no lugar.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que o Hanff descobriu
&lt;/h2&gt;

&lt;p&gt;A descoberta dele é mais detalhada que a do Brichter em 2020. O Hanff cruzou logs de filesystem do macOS (&lt;code&gt;.fseventsd&lt;/code&gt;), o &lt;code&gt;Local State&lt;/code&gt; do Chrome, feature flags e logs do GoogleUpdater pra montar uma cadeia de evidências de quatro pontas. No perfil de teste dele, o &lt;code&gt;weights.bin&lt;/code&gt; apareceu em 24 de abril de 2026 às 16:38:54 e levou 14 minutos e 28 segundos pra terminar de baixar. Silenciosamente. Cada perfil de Chrome roda esse processo de novo.&lt;/p&gt;

&lt;p&gt;A versão afetada é a Chrome 147 e seguintes. O modelo serve pra features como "Help me write" (composição de texto), detecção local de scam e APIs que sites podem chamar (&lt;code&gt;Summarizer API&lt;/code&gt;, &lt;code&gt;Translator API&lt;/code&gt;, &lt;code&gt;Prompt API&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  A parte que dá raiva
&lt;/h2&gt;

&lt;p&gt;Aquele botão chamativo de "AI Mode" que apareceu no omnibox do Chrome, que parece ser a feature visível de IA do browser, não usa o modelo local. As consultas digitadas ali vão pra cloud do Google. Ou seja: o usuário paga 4GB de download e 4GB de espaço em disco por um modelo que não roda quando ele clica no botão de IA do browser. O custo fica com o usuário, o uso fica com o Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  Em escala
&lt;/h2&gt;

&lt;p&gt;Hanff fez as contas: 0,24 kWh por dispositivo pra baixar e instalar, 0,06 kg de CO2e por dispositivo. Em 1 bilhão de instalações, são 240 GWh e 60 mil toneladas de CO2e, equivalente à emissão anual de cerca de 13 mil carros europeus. E com a União Europeia em cima do GDPR, da ePrivacy Directive (o Artigo 5(3) proíbe armazenar dados no dispositivo do usuário sem consentimento prévio) e da CSRD (a diretiva europeia de reporte de sustentabilidade), a chance de virar caso jurídico é alta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como tirar isso da sua máquina
&lt;/h2&gt;

&lt;p&gt;O Google adicionou em fevereiro de 2026 a opção pra desabilitar:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chrome Settings &amp;gt; System &amp;gt; "Turn On-device AI on or off"&lt;/strong&gt;, se a opção já apareceu pra você.&lt;/li&gt;
&lt;li&gt;Caminho alternativo: digite &lt;code&gt;chrome://flags&lt;/code&gt; na barra de endereço, busque por "optimization guide on device" e marque como "Disabled". Reinicie o Chrome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Lembrando que apagar o &lt;code&gt;weights.bin&lt;/code&gt; na unha não basta. Sem desabilitar a flag, o Chrome baixa de novo.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que faço no meu dia a dia
&lt;/h2&gt;

&lt;p&gt;Não uso o Chrome como browser principal há tempos. &lt;a href="https://guilherme44.com/blog/the-link-routing-gap/" rel="noopener noreferrer"&gt;Escrevi semana passada sobre roteamento de links&lt;/a&gt;, e no setup que descrevo lá os browsers que abrem cada link são Chromium-based ou Firefox-based (Brave, Vivaldi, Edge, Opera, Helium), não o Chrome do Google em si. Os mesmos nomes, que o Brichter recomendou em 2020, continuam servindo bem em 2026.&lt;/p&gt;

&lt;p&gt;A regra que adotei é simples. Não dá pra confiar num programa que decide instalar 4GB de IA, ou um daemon updater em background, sem te perguntar. O que tá em jogo não é só espaço em disco, é o controle sobre o que o seu computador faz quando você não tá olhando.&lt;/p&gt;

&lt;h2&gt;
  
  
  E lá vamos nós pela terceira vez
&lt;/h2&gt;

&lt;p&gt;Em 2009 a Wired publicou que o Keystone era evil quando o Google enfiou ele junto com o Google Earth. Em 2020 o Brichter publicou o chromeisbad porque o mesmo Keystone tava dentro do Chrome. Agora em 2026 o Hanff mostrou que são 4GB de IA, instalados do mesmo jeito. Entre essas datas o Google só ficou maior, e a forma de empurrar coisa no PC dos outros sem perguntar não mudou.&lt;/p&gt;

&lt;p&gt;Eu uso o Brave, e recomendo usar qualquer outro browser, menos o Google Chrome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Fontes
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.thatprivacyguy.com/blog/chrome-silent-nano-install/" rel="noopener noreferrer"&gt;Alexander Hanff, That Privacy Guy! (4 maio 2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chromeisbad.com/" rel="noopener noreferrer"&gt;Loren Brichter, chromeisbad.com (dez 2020)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tomshardware.com/tech-industry/cyber-security/google-chrome-silently-downloads-4gb-ai-model-to-your-device-without-permission-report-claims-researcher-says-practice-may-violate-eu-law-waste-thousands-of-kilowatts-of-energy" rel="noopener noreferrer"&gt;Tom's Hardware: Chrome silently downloads 4GB AI model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.androidauthority.com/google-chrome-weights-bin-ai-model-download-explained-3664043/" rel="noopener noreferrer"&gt;Android Authority: explicação técnica do weights.bin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.malwarebytes.com/blog/news/2026/05/google-chromes-silent-4gb-ai-download-problem" rel="noopener noreferrer"&gt;Malwarebytes: Chrome's silent 4GB AI download problem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente em &lt;a href="https://guilherme44.com/blog/chrome-silently-installed-ai/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>chrome</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>For the third time, Google sneaks something onto your PC without asking</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Fri, 08 May 2026 18:31:41 +0000</pubDate>
      <link>https://dev.to/guilherme44/for-the-third-time-google-sneaks-something-onto-your-pc-without-asking-gge</link>
      <guid>https://dev.to/guilherme44/for-the-third-time-google-sneaks-something-onto-your-pc-without-asking-gge</guid>
      <description>&lt;p&gt;If you use Chrome, there are 4GB of an AI model sitting on your machine that you never asked for. And it is not the first time Google does this, it is the third. Privacy researcher &lt;a href="https://www.thatprivacyguy.com/" rel="noopener noreferrer"&gt;Alexander Hanff&lt;/a&gt; &lt;a href="https://www.thatprivacyguy.com/blog/chrome-silent-nano-install/" rel="noopener noreferrer"&gt;published the report&lt;/a&gt; on May 4, 2026. The file is called &lt;code&gt;weights.bin&lt;/code&gt;, lives in &lt;code&gt;OptGuideOnDeviceModel&lt;/code&gt; inside the Chrome user data directory, and it is the Gemini Nano model. No prompt, no checkbox. Find it and delete it, and Chrome re-downloads it on the next restart.&lt;/p&gt;

&lt;p&gt;The first reaction here was déjà vu, not surprise. In December 2020, &lt;a href="https://x.com/lorenb/status/1337832978253230081" rel="noopener noreferrer"&gt;Loren Brichter&lt;/a&gt; (@lorenb on X, the guy behind "pull to refresh" and &lt;a href="https://en.wikipedia.org/wiki/Tweetie" rel="noopener noreferrer"&gt;Tweetie&lt;/a&gt;, the client Twitter bought in 2010 to become its official iPhone app) published &lt;a href="https://chromeisbad.com/" rel="noopener noreferrer"&gt;chromeisbad.com&lt;/a&gt;, a one-page site explaining how Keystone, Google Chrome's updater on Mac, kept running in the background, dragging the system down even when Chrome was closed. His complaint came eleven years after &lt;a href="https://www.wired.com/2009/02/why-googles-sof/" rel="noopener noreferrer"&gt;Wired published "Google Software Update Tool is evil"&lt;/a&gt; about the same Keystone, when it showed up bundled with Google Earth, gathering hardware data and downloading things in the background without warning. Brichter's prescription was a manual uninstall guide for Chrome and Keystone, plus a list of replacements: Safari, Brave, Vivaldi, Opera.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Hanff found
&lt;/h2&gt;

&lt;p&gt;His discovery is more detailed than Brichter's was in 2020. Hanff cross-referenced macOS filesystem logs (&lt;code&gt;.fseventsd&lt;/code&gt;), Chrome's &lt;code&gt;Local State&lt;/code&gt;, feature flags, and GoogleUpdater logs to build a four-way evidence chain. On his test profile, &lt;code&gt;weights.bin&lt;/code&gt; appeared on April 24, 2026 at 16:38:54 and took 14 minutes and 28 seconds to finish downloading. Silently. Each Chrome profile runs the process again.&lt;/p&gt;

&lt;p&gt;The affected version is Chrome 147 onward. The model powers features like "Help me write" (text composition), local scam detection, and APIs that sites can call (&lt;code&gt;Summarizer API&lt;/code&gt;, &lt;code&gt;Translator API&lt;/code&gt;, &lt;code&gt;Prompt API&lt;/code&gt;).&lt;/p&gt;

&lt;h2&gt;
  
  
  The frustrating part
&lt;/h2&gt;

&lt;p&gt;That bright "AI Mode" pill that showed up in Chrome's omnibox, the visible AI feature of the browser, does not use the local model. Queries typed there are sent to Google's cloud. So the user pays 4GB of download and 4GB of disk for a model that does not run when they click the most prominent AI button on the browser. The cost lives with the user, the use lives with Google.&lt;/p&gt;

&lt;h2&gt;
  
  
  At scale
&lt;/h2&gt;

&lt;p&gt;Hanff did the math: 0.24 kWh per device for the download and install, 0.06 kg of CO2e per device. At 1 billion installs, that is 240 GWh and 60,000 tonnes of CO2e, equivalent to the annual emissions of about 13,000 European cars. And with the European Union watching closely on GDPR, the ePrivacy Directive (Article 5(3) prohibits storing data on a user's device without prior consent), and the Corporate Sustainability Reporting Directive, the chance of this turning into a legal case is high.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to remove it
&lt;/h2&gt;

&lt;p&gt;Google added an opt-out in February 2026:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Chrome Settings &amp;gt; System &amp;gt; "Turn On-device AI on or off"&lt;/strong&gt;, if the option is showing up for you yet.&lt;/li&gt;
&lt;li&gt;Alternative path: type &lt;code&gt;chrome://flags&lt;/code&gt; in the address bar, search for "optimization guide on device", and set it to "Disabled". Restart Chrome.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that deleting &lt;code&gt;weights.bin&lt;/code&gt; by hand is not enough. Without disabling the flag, Chrome downloads it again.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I do in my daily setup
&lt;/h2&gt;

&lt;p&gt;I have not been using Chrome as my main browser for a long time. &lt;a href="https://guilherme44.com/blog/the-link-routing-gap/" rel="noopener noreferrer"&gt;I wrote last week about link routing&lt;/a&gt;, and in the setup I describe there the browsers that open each link are Chromium-based or Firefox-based (Brave, Vivaldi, Edge, Opera, Helium), not Google's own Chrome. The same names, recommended by Brichter back in 2020, still serve well in 2026.&lt;/p&gt;

&lt;p&gt;The rule I keep is simple. You cannot trust a program that decides to install 4GB of AI weights, or a background updater daemon, without asking you. What is at stake is not just disk space, it is control over what your computer does when you are not looking.&lt;/p&gt;

&lt;h2&gt;
  
  
  And here we go for the third time
&lt;/h2&gt;

&lt;p&gt;In 2009 Wired called Keystone evil when Google bundled it inside Google Earth. In 2020 Brichter published chromeisbad because the same Keystone was now sitting inside Chrome. Now in 2026 Hanff showed that 4GB of AI weights are being pushed the same way. Between those dates Google only got bigger, and the way it pushes things onto other people's machines without asking has not changed.&lt;/p&gt;

&lt;p&gt;I use Brave, and my recommendation is any browser, just not Google Chrome.&lt;/p&gt;

&lt;h2&gt;
  
  
  Sources
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.thatprivacyguy.com/blog/chrome-silent-nano-install/" rel="noopener noreferrer"&gt;Alexander Hanff, That Privacy Guy! (May 4, 2026)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://chromeisbad.com/" rel="noopener noreferrer"&gt;Loren Brichter, chromeisbad.com (Dec 2020)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.tomshardware.com/tech-industry/cyber-security/google-chrome-silently-downloads-4gb-ai-model-to-your-device-without-permission-report-claims-researcher-says-practice-may-violate-eu-law-waste-thousands-of-kilowatts-of-energy" rel="noopener noreferrer"&gt;Tom's Hardware: Chrome silently downloads 4GB AI model&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.androidauthority.com/google-chrome-weights-bin-ai-model-download-explained-3664043/" rel="noopener noreferrer"&gt;Android Authority: technical breakdown of weights.bin&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.malwarebytes.com/blog/news/2026/05/google-chromes-silent-4gb-ai-download-problem" rel="noopener noreferrer"&gt;Malwarebytes: Chrome's silent 4GB AI download problem&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally posted at &lt;a href="https://guilherme44.com/en/blog/chrome-silently-installed-ai/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>privacy</category>
      <category>chrome</category>
      <category>ai</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Awesome!!</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Tue, 05 May 2026 02:22:44 +0000</pubDate>
      <link>https://dev.to/guilherme44/awesome-4ehg</link>
      <guid>https://dev.to/guilherme44/awesome-4ehg</guid>
      <description>&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/aoligama/a-live-activity-isnt-a-notification-2k9o" class="crayons-story__hidden-navigation-link"&gt;A Live Activity isn't a notification&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="/aoligama" 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%2F1453761%2F78200e0f-c511-4bb3-bae3-712f13bfa74c.jpeg" alt="aoligama profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/aoligama" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Amanda Gama
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Amanda Gama
                
              
              &lt;div id="story-author-preview-content-3611931" 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="/aoligama" 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%2F1453761%2F78200e0f-c511-4bb3-bae3-712f13bfa74c.jpeg" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Amanda Gama&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/aoligama/a-live-activity-isnt-a-notification-2k9o" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;May 5&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/aoligama/a-live-activity-isnt-a-notification-2k9o" id="article-link-3611931"&gt;
          A Live Activity isn't a notification
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag crayons-tag--filled  " href="/t/showdev"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;showdev&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/ios"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;ios&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/mobile"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;mobile&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/tutorial"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;tutorial&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/aoligama/a-live-activity-isnt-a-notification-2k9o" 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="18" height="18"&gt;
                  &lt;/span&gt;
              &lt;/span&gt;
              &lt;span class="aggregate_reactions_counter"&gt;2&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/aoligama/a-live-activity-isnt-a-notification-2k9o#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;
            6 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>
    </item>
    <item>
      <title>[PT-BR] Roteamento de links: a feature que SO e browser fingem não existir</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Sun, 03 May 2026 21:57:23 +0000</pubDate>
      <link>https://dev.to/guilherme44/pt-br-roteamento-de-links-a-feature-que-so-e-browser-fingem-nao-existir-5hn7</link>
      <guid>https://dev.to/guilherme44/pt-br-roteamento-de-links-a-feature-que-so-e-browser-fingem-nao-existir-5hn7</guid>
      <description>&lt;p&gt;&lt;a href="https://guilherme44.com/blog/omarchy-from-day-one/" rel="noopener noreferrer"&gt;Migrei do Mac pro Linux com Omarchy&lt;/a&gt; faz quase um ano. Quase tudo voltou ao lugar. Tem uma coisa que continua incomodando, e percebi que o problema é maior do que parecia. Existe uma funcionalidade simples que Sistema Operacional nenhum entrega e browser nenhum entrega: regra pra decidir qual browser ou app abre cada link clicado.&lt;/p&gt;

&lt;p&gt;No Mac eu usava o &lt;a href="https://github.com/johnste/finicky" rel="noopener noreferrer"&gt;Finicky&lt;/a&gt; pra isso. Pra quem nunca viu: ele deixa você criar regras que decidem qual browser abre cada link. Você coloca o Finicky como browser padrão do sistema, e ele aplica suas regras a cada link clicado em qualquer app, escolhendo o browser certo. As regras são pequenos scripts em JavaScript, simples ou mais elaborados conforme a necessidade. Dá até pra reescrever a URL antes de abrir, tipo forçar HTTPS, remover parâmetros de rastreamento, ou converter num link interno que abre o app desktop diretamente. Se nenhuma regra bater, ele cai num browser de reserva que você define.&lt;/p&gt;

&lt;p&gt;Na prática, é assim. Link de &lt;code&gt;github.com/empresa&lt;/code&gt; abre no browser do trabalho, &lt;code&gt;github.com/pessoal&lt;/code&gt; no browser pessoal. &lt;code&gt;meet.google.com&lt;/code&gt; cai logado no perfil certo. Vídeo de YouTube volta sempre pro browser principal. Qualquer link que não bater em nenhuma regra cai no de reserva, no meu caso o browser pessoal. Trabalho e vida pessoal separados sem ter que copiar URL e colar na outra janela.&lt;/p&gt;

&lt;p&gt;E tem o caso dos apps que vêm com versão desktop e usam um endereço próprio que abre eles direto. Spotify, WhatsApp, Slack, Discord, Zoom, cada um tem um endereço interno tipo &lt;code&gt;spotify:&lt;/code&gt;, &lt;code&gt;whatsapp:&lt;/code&gt;, &lt;code&gt;slack:&lt;/code&gt;, que o sistema entende como "esse link é desse app, abre aqui". Quando alguém compartilha um link público (&lt;code&gt;open.spotify.com/track/...&lt;/code&gt;, &lt;code&gt;wa.me/&amp;lt;numero&amp;gt;&lt;/code&gt;, &lt;code&gt;chat.whatsapp.com/&amp;lt;convite&amp;gt;&lt;/code&gt;), o ideal é abrir direto no app desktop, não numa aba de browser que vai pedir login e perguntar "abrir no app?" toda vez. No Finicky você escreve uma regra que reconhece o link público e reescreve pro endereço interno do app (&lt;code&gt;open.spotify.com/track/abc&lt;/code&gt; vira &lt;code&gt;spotify:track:abc&lt;/code&gt;, &lt;code&gt;wa.me/5511...&lt;/code&gt; vira &lt;code&gt;whatsapp://send?phone=...&lt;/code&gt;), e o sistema entrega direto pro app. Aba intermediária zero.&lt;/p&gt;

&lt;p&gt;Solução simples, efeito grande. É aí que tá o estranho.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que existe no Linux
&lt;/h2&gt;

&lt;p&gt;Procurei equivalente e o que mais aparece é o &lt;a href="https://github.com/sonnyp/Junction" rel="noopener noreferrer"&gt;Junction&lt;/a&gt;: bonito, ativo, bem mantido. Mas ele só &lt;em&gt;pergunta&lt;/em&gt; qual browser usar a cada link. Não tem regras, não decide sozinho. Tem pedido aberto na comunidade pra ele lembrar de uma escolha padrão e só perguntar quando você segurar Alt, mas ninguém pegou pra implementar. Junction só pergunta, não roteia.&lt;/p&gt;

&lt;p&gt;O &lt;a href="https://github.com/BachoSeven/mimi" rel="noopener noreferrer"&gt;mimi&lt;/a&gt; é uma alternativa mais decente ao mecanismo padrão do Linux que abre links (&lt;code&gt;xdg-open&lt;/code&gt;), mas também não traz regras de roteamento prontas.&lt;/p&gt;

&lt;p&gt;Fora isso, sobra o caminho velho: criar um atalho próprio na pasta de apps do usuário (&lt;code&gt;~/.local/share/applications/&lt;/code&gt;), marcar esse atalho como o programa que vai abrir os links do sistema, e escrever um pequeno script que faz o roteamento na mão. Funciona, mas é peça caseira que cada um tem que manter por conta própria, sem comunidade no meio.&lt;/p&gt;

&lt;p&gt;O mais perto que tem hoje de Finicky no Linux é "monta o teu".&lt;/p&gt;

&lt;h2&gt;
  
  
  E no Mac?
&lt;/h2&gt;

&lt;p&gt;No Mac a paisagem é viva. Finicky tá em desenvolvimento ativo, lançou v4.2.2 em outubro de 2025 com editor visual de regras. Tem &lt;a href="https://sindresorhus.com/velja" rel="noopener noreferrer"&gt;Velja&lt;/a&gt;, &lt;a href="https://www.choosyosx.com/" rel="noopener noreferrer"&gt;Choosy&lt;/a&gt;, &lt;a href="https://www.defaulttamer.app/" rel="noopener noreferrer"&gt;Default Tamer&lt;/a&gt;, &lt;a href="https://github.com/fluffypony/yojam" rel="noopener noreferrer"&gt;Yojam&lt;/a&gt;. Mercado inteiro de ferramenta de terceiro tapando o mesmo buraco.&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%2F7add8stkab85432n1vft.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%2F7add8stkab85432n1vft.png" alt="Editor de regras do Finicky" width="800" height="588"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Editor visual de regras do &lt;a href="https://github.com/johnste/finicky" rel="noopener noreferrer"&gt;Finicky&lt;/a&gt;&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;E mesmo no Mac essa cena tem casualty. O &lt;a href="https://github.com/will-stone/browserosaurus" rel="noopener noreferrer"&gt;Browserosaurus&lt;/a&gt;, outro favorito da galera, foi arquivado em 2 de agosto de 2025. Mantenedor cansou, sem sucessor oficial. Comunidade tem fork, ninguém é o canônico.&lt;/p&gt;

&lt;p&gt;É o que costuma acontecer com ferramenta de terceiro que vive pra cobrir o que SO e browser deixam aberto.&lt;/p&gt;

&lt;h2&gt;
  
  
  E no Windows?
&lt;/h2&gt;

&lt;p&gt;No Windows a história é a mesma. Você define um único browser pra abrir todos os links do sistema, ponto. Não tem como dizer "esse site abre aqui, esse outro abre lá".&lt;/p&gt;

&lt;p&gt;A novidade recente é que o Edge ganhou em 2025 uma opção pra quem tem conta corporativa Microsoft (Entra ID, antiga Azure AD): link externo cai automaticamente no profile de trabalho do Edge. É só uma opção global. Você escolhe um profile e acabou. Sem regra por URL, sem distinção de qual app abriu o link.&lt;/p&gt;

&lt;p&gt;Fora do Edge, a cena de terceiro no Windows é até maior do que no Linux. O &lt;a href="https://github.com/U-C-S/Hurl" rel="noopener noreferrer"&gt;Hurl&lt;/a&gt; (ativo) e o &lt;a href="https://github.com/mortenn/BrowserPicker" rel="noopener noreferrer"&gt;BrowserPicker&lt;/a&gt; do mortenn (na Microsoft Store) lideram. O &lt;a href="https://github.com/nref/BrowseRouter" rel="noopener noreferrer"&gt;BrowseRouter&lt;/a&gt; é um fork ativo do &lt;a href="https://github.com/DanTup/BrowserSelector" rel="noopener noreferrer"&gt;BrowserSelector original&lt;/a&gt;, que foi arquivado em 2022. Tem também o &lt;a href="https://switchbar.com/" rel="noopener noreferrer"&gt;Switchbar&lt;/a&gt;, comercial e que roda no Mac e no Windows.&lt;/p&gt;

&lt;p&gt;Mesmo padrão de sempre. Terceiro tampa o que SO e browser não tampam, e quando o terceiro arquiva (oi, BrowserSelector) a comunidade aparece com fork, ninguém vira o canônico.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que nenhum Sistema Operacional entrega
&lt;/h2&gt;

&lt;p&gt;O modelo que existe nos SOs hoje é o mesmo em todo lugar. macOS, Windows, Linux. Pra cada tipo de link, você escolhe um único app que vai abrir e pronto. Link de site (&lt;code&gt;https&lt;/code&gt;)? Um browser. Email (&lt;code&gt;mailto&lt;/code&gt;)? Um cliente de email. Música (&lt;code&gt;spotify&lt;/code&gt;)? Um app. Não dá pra dizer "esse link específico vai pra outro app", nem "esse link veio do Slack, então abre em outro lugar". Cada SO chama esse mecanismo de um nome diferente (xdg-mime no Linux, LaunchServices no Mac, registry no Windows), mas a limitação é a mesma.&lt;/p&gt;

&lt;p&gt;Linux teria espaço pra ir além já que esse mecanismo é convenção, não imposição, mas ninguém moveu. macOS e Windows nunca abriram uma forma oficial pra você definir regras de roteamento de link. Trinta anos de SO desktop, e a ideia de "esse link tem que abrir aqui, esse outro tem que abrir lá" continua fora do escopo do sistema.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que browser nenhum entrega
&lt;/h2&gt;

&lt;p&gt;Chrome tem um seletor pra escolher qual perfil do &lt;em&gt;próprio Chrome&lt;/em&gt; abre o link. Firefox tem "containers" (uma forma de isolar abas em compartimentos separados), e com extensão (Containerise, Auto Containers) dá pra rotear por padrão de URL, mas tudo isso só funciona dentro do Firefox. Nenhum browser quer mandar tráfego pra outro browser. Cada um quer ser o browser padrão do sistema.&lt;/p&gt;

&lt;p&gt;Faz sentido pelo incentivo. Mandar link pra outro browser primeiro reduz a métrica que importa pra cada empresa, que é link aberto no produto deles. Pelo lado da segurança também tem desculpa: um único browser padrão de confiança é mais simples de auditar do que um esquema que decide pra onde mandar com base em regrinha de URL. Nada disso explica o tamanho do silêncio dos SOs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Enquanto isso
&lt;/h2&gt;

&lt;p&gt;Faz mais de dez anos que esse problema é o mesmo. SO e browser fingem que rotear link não é responsabilidade deles, e toda vez que aparece terceiro pra resolver, ele vira projeto solo até o mantenedor cansar. Aí volta todo mundo pra estaca zero (oi, Browserosaurus).&lt;/p&gt;

&lt;p&gt;Separar link clicado por regra é tarefa básica de quem trabalha com mais de um contexto no mesmo computador. Devia estar na base do sistema, no mesmo nível de "qual app abre PDF". Faz dez anos esperando.&lt;/p&gt;

&lt;p&gt;Seria muito bom essa era de IA finalmente trazer um multiplataforma decente para todos. Mac, Windows e Linux na mesma ferramenta. Eu, além de usar, seria o divulgador número um. Mercado pra ele a gente sabe que tem.&lt;/p&gt;




&lt;p&gt;Fontes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/johnste/finicky" rel="noopener noreferrer"&gt;Finicky&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sonnyp/Junction" rel="noopener noreferrer"&gt;Junction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BachoSeven/mimi" rel="noopener noreferrer"&gt;mimi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/will-stone/browserosaurus" rel="noopener noreferrer"&gt;Browserosaurus (arquivado)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sindresorhus.com/velja" rel="noopener noreferrer"&gt;Velja&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.choosyosx.com/" rel="noopener noreferrer"&gt;Choosy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/U-C-S/Hurl" rel="noopener noreferrer"&gt;Hurl (Windows)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mortenn/BrowserPicker" rel="noopener noreferrer"&gt;BrowserPicker (Windows)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://switchbar.com/" rel="noopener noreferrer"&gt;Switchbar (Mac + Windows)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.archlinux.org/title/XDG_MIME_Applications" rel="noopener noreferrer"&gt;XDG MIME Applications (ArchWiki)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://larsee.com/blog/2023/04/select-browser-dialog-in-linux/" rel="noopener noreferrer"&gt;Selecting the browser when opening links in Linux (larsee.com)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente em &lt;a href="https://guilherme44.com/blog/the-link-routing-gap/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>browsers</category>
      <category>productivity</category>
      <category>macos</category>
    </item>
    <item>
      <title>Link routing: the feature OSes and browsers pretend isn't there</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Sun, 03 May 2026 21:57:19 +0000</pubDate>
      <link>https://dev.to/guilherme44/link-routing-the-feature-oses-and-browsers-pretend-isnt-there-2n4d</link>
      <guid>https://dev.to/guilherme44/link-routing-the-feature-oses-and-browsers-pretend-isnt-there-2n4d</guid>
      <description>&lt;p&gt;&lt;a href="https://guilherme44.com/en/blog/omarchy-from-day-one/" rel="noopener noreferrer"&gt;I switched from Mac to Linux with Omarchy&lt;/a&gt; almost a year ago. Most things slid back into place. There's one thing still bothering me, and turns out it's not just a Linux problem. There's a simple feature no OS ships and no browser ships: rules for deciding which browser or app opens each link you click.&lt;/p&gt;

&lt;p&gt;On Mac I used &lt;a href="https://github.com/johnste/finicky" rel="noopener noreferrer"&gt;Finicky&lt;/a&gt; for this. For anyone who never saw it: it lets you write rules that decide which browser opens each link. You set Finicky as the system's default browser, and it applies your rules to every link clicked in any app, picking the right one. Rules are short scripts in JavaScript, simple or as elaborate as you need. It can even rewrite the URL before opening it: force HTTPS, strip tracking parameters, or convert it into an internal link that opens the desktop app directly. If no rule matches, it falls through to a backup browser you define.&lt;/p&gt;

&lt;p&gt;In practice, it looks like this. A &lt;code&gt;github.com/work-org&lt;/code&gt; link opens in the work browser, &lt;code&gt;github.com/personal&lt;/code&gt; in the personal one. &lt;code&gt;meet.google.com&lt;/code&gt; lands logged into the right profile. YouTube videos always come back to the main browser. Anything that matches no rule falls through to the backup, in my case the personal browser. Work and personal life kept apart without copy-pasting URLs into another window.&lt;/p&gt;

&lt;p&gt;There's also the case of apps that ship a desktop client and use their own private address to open it directly. Spotify, WhatsApp, Slack, Discord, Zoom, each has an internal address like &lt;code&gt;spotify:&lt;/code&gt;, &lt;code&gt;whatsapp:&lt;/code&gt;, &lt;code&gt;slack:&lt;/code&gt;, which the system reads as "this link belongs to that app, open it there". When someone shares a public link (&lt;code&gt;open.spotify.com/track/...&lt;/code&gt;, &lt;code&gt;wa.me/&amp;lt;phone&amp;gt;&lt;/code&gt;, &lt;code&gt;chat.whatsapp.com/&amp;lt;invite&amp;gt;&lt;/code&gt;), the right move is to open it straight in the desktop app, not in a browser tab that asks for login and prompts "open in app?" every single time. With Finicky you write a rule that matches the public link and rewrites it to the app's internal address (&lt;code&gt;open.spotify.com/track/abc&lt;/code&gt; becomes &lt;code&gt;spotify:track:abc&lt;/code&gt;, &lt;code&gt;wa.me/15551234567&lt;/code&gt; becomes &lt;code&gt;whatsapp://send?phone=...&lt;/code&gt;), and the system hands it straight to the app. No intermediate tab.&lt;/p&gt;

&lt;p&gt;Simple solution, big payoff. That's the strange part.&lt;/p&gt;

&lt;h2&gt;
  
  
  What exists on Linux
&lt;/h2&gt;

&lt;p&gt;Searched for an equivalent and the most common answer is &lt;a href="https://github.com/sonnyp/Junction" rel="noopener noreferrer"&gt;Junction&lt;/a&gt;: pretty, active, well maintained. But it only &lt;em&gt;asks&lt;/em&gt; which browser to use on every link. No rules, no auto-routing. There's a community request asking it to remember a default choice and only ask when you hold Alt, but nobody has picked it up. Junction only asks, it doesn't route.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/BachoSeven/mimi" rel="noopener noreferrer"&gt;mimi&lt;/a&gt; is a more decent replacement for the Linux mechanism that opens links (&lt;code&gt;xdg-open&lt;/code&gt;), still without ready-made routing rules.&lt;/p&gt;

&lt;p&gt;What's left is the old path: drop a custom shortcut file in the user's apps folder (&lt;code&gt;~/.local/share/applications/&lt;/code&gt;), mark that shortcut as the program that opens the system's links, and write a small script that does the routing by hand. It works, but it's a homebrew piece each user has to maintain on their own, with no community in the middle.&lt;/p&gt;

&lt;p&gt;The closest thing to Finicky on Linux today is "build your own".&lt;/p&gt;

&lt;h2&gt;
  
  
  And on Mac?
&lt;/h2&gt;

&lt;p&gt;The Mac landscape is alive. Finicky is actively developed, with v4.2.2 out in October 2025 bringing a visual rules editor. There's &lt;a href="https://sindresorhus.com/velja" rel="noopener noreferrer"&gt;Velja&lt;/a&gt;, &lt;a href="https://www.choosyosx.com/" rel="noopener noreferrer"&gt;Choosy&lt;/a&gt;, &lt;a href="https://www.defaulttamer.app/" rel="noopener noreferrer"&gt;Default Tamer&lt;/a&gt;, &lt;a href="https://github.com/fluffypony/yojam" rel="noopener noreferrer"&gt;Yojam&lt;/a&gt;. A whole third-party market plugging the same gap.&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%2F7add8stkab85432n1vft.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%2F7add8stkab85432n1vft.png" alt="Finicky rules editor" width="800" height="588"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;&lt;a href="https://github.com/johnste/finicky" rel="noopener noreferrer"&gt;Finicky&lt;/a&gt;'s visual rules editor&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;p&gt;Even there it has casualties. &lt;a href="https://github.com/will-stone/browserosaurus" rel="noopener noreferrer"&gt;Browserosaurus&lt;/a&gt;, another community favorite, was archived on August 2, 2025. Maintainer burned out, no official successor. Community has forks, none is canonical.&lt;/p&gt;

&lt;p&gt;That's what tends to happen to any third-party tool plugging a gap that neither the OS nor the browser wants to plug.&lt;/p&gt;

&lt;h2&gt;
  
  
  And on Windows?
&lt;/h2&gt;

&lt;p&gt;On Windows, same story. You set one browser to handle every link in the system, period. No way to say "this site opens here, that other one opens there".&lt;/p&gt;

&lt;p&gt;The recent twist is that in 2025 Edge added a toggle for users on a Microsoft corporate account (Entra ID, formerly Azure AD): external links automatically land in Edge's work profile. It's just a global toggle. You pick a profile and that's it. No URL rules, no awareness of which app opened the link.&lt;/p&gt;

&lt;p&gt;Outside Edge, the third-party scene on Windows is actually bigger than on Linux. &lt;a href="https://github.com/U-C-S/Hurl" rel="noopener noreferrer"&gt;Hurl&lt;/a&gt; (active) and mortenn's &lt;a href="https://github.com/mortenn/BrowserPicker" rel="noopener noreferrer"&gt;BrowserPicker&lt;/a&gt; (on the Microsoft Store) lead the pack. &lt;a href="https://github.com/nref/BrowseRouter" rel="noopener noreferrer"&gt;BrowseRouter&lt;/a&gt; is an active fork of the &lt;a href="https://github.com/DanTup/BrowserSelector" rel="noopener noreferrer"&gt;original BrowserSelector&lt;/a&gt;, which was archived in 2022. There's also &lt;a href="https://switchbar.com/" rel="noopener noreferrer"&gt;Switchbar&lt;/a&gt;, commercial and running on both Mac and Windows.&lt;/p&gt;

&lt;p&gt;Same old pattern. Third-party plugs what OS and browser won't, and when the third-party gets archived (hi, BrowserSelector) the community spins up forks, none becoming the canonical one.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why no OS ships this
&lt;/h2&gt;

&lt;p&gt;The model across operating systems is the same everywhere. macOS, Windows, Linux. For each kind of link, you pick one app that opens it, end of story. A web link (&lt;code&gt;https&lt;/code&gt;)? One browser. Email (&lt;code&gt;mailto&lt;/code&gt;)? One mail client. Music (&lt;code&gt;spotify&lt;/code&gt;)? One app. No way to say "this specific link goes to a different app", or "this link came from Slack so open it somewhere else". Each OS calls this mechanism by a different name (xdg-mime on Linux, LaunchServices on macOS, the registry on Windows), but the limit is the same.&lt;/p&gt;

&lt;p&gt;Linux has room to go further since this mechanism is convention, not enforcement, but nobody has moved. macOS and Windows never opened a public API for routing links by rule. Thirty years of desktop OS, and the idea of "this link belongs here, that other link belongs there" still sits outside the system's scope.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why no browser ships this
&lt;/h2&gt;

&lt;p&gt;Chrome has a selector for picking which profile of &lt;em&gt;Chrome itself&lt;/em&gt; opens the link. Firefox has "containers" (a way to isolate tabs into separate compartments), and with extensions (Containerise, Auto Containers) you can route by URL pattern, but all of that only works inside Firefox. No browser wants to hand traffic over to another browser. Each one wants to be the system's default browser.&lt;/p&gt;

&lt;p&gt;The incentive math is clear. Sending links to a different browser first reduces the metric that matters for each company, links opened in their own product. The security argument is also there: one trusted default browser is easier to audit than a system that decides where to send things based on URL-matching rules. None of that explains the size of the silence on the OS side.&lt;/p&gt;

&lt;h2&gt;
  
  
  Meanwhile
&lt;/h2&gt;

&lt;p&gt;This problem has been the same for over ten years. OSes and browsers act like routing a link isn't their responsibility, and every time a third-party tool steps up to fix it, it ends up a solo project until the maintainer burns out. Then everyone goes back to square one (hi, Browserosaurus).&lt;/p&gt;

&lt;p&gt;Splitting clicked links by rule is a basic task for anyone juggling more than one context on the same machine. It belongs in the base of the system, at the same level as "which app opens PDFs". Been ten years waiting.&lt;/p&gt;

&lt;p&gt;It would be great for this AI era to finally bring a decent cross-platform one for everyone. Mac, Windows and Linux in the same tool. Beyond just using it, I'd be its number-one evangelist. The market is there, we all know that.&lt;/p&gt;




&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://github.com/johnste/finicky" rel="noopener noreferrer"&gt;Finicky&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/sonnyp/Junction" rel="noopener noreferrer"&gt;Junction&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/BachoSeven/mimi" rel="noopener noreferrer"&gt;mimi&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/will-stone/browserosaurus" rel="noopener noreferrer"&gt;Browserosaurus (archived)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://sindresorhus.com/velja" rel="noopener noreferrer"&gt;Velja&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.choosyosx.com/" rel="noopener noreferrer"&gt;Choosy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/U-C-S/Hurl" rel="noopener noreferrer"&gt;Hurl (Windows)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/mortenn/BrowserPicker" rel="noopener noreferrer"&gt;BrowserPicker (Windows)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://switchbar.com/" rel="noopener noreferrer"&gt;Switchbar (Mac + Windows)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://wiki.archlinux.org/title/XDG_MIME_Applications" rel="noopener noreferrer"&gt;XDG MIME Applications (ArchWiki)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://larsee.com/blog/2023/04/select-browser-dialog-in-linux/" rel="noopener noreferrer"&gt;Selecting the browser when opening links in Linux (larsee.com)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally posted at &lt;a href="https://guilherme44.com/en/blog/the-link-routing-gap/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>linux</category>
      <category>browsers</category>
      <category>productivity</category>
      <category>macos</category>
    </item>
    <item>
      <title>[PT-BR] Por que troquei meu "Opinionated Linux" autoral (DIY) pelo Omarchy</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Sat, 02 May 2026 12:23:32 +0000</pubDate>
      <link>https://dev.to/guilherme44/pt-br-por-que-troquei-meu-opinionated-linux-autoral-diy-pelo-omarchy-5bab</link>
      <guid>https://dev.to/guilherme44/pt-br-por-que-troquei-meu-opinionated-linux-autoral-diy-pelo-omarchy-5bab</guid>
      <description>&lt;p&gt;Tô usando o &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; como setup principal desde 26 de junho de 2025, dia em que o DHH lançou a primeira versão. Antes tinha meu "Opinionated Linux" autoral (DIY), o &lt;a href="https://github.com/guilhermeyo/mclovin-ARCHived" rel="noopener noreferrer"&gt;mclovin-ARCHived&lt;/a&gt;: um installer Arch + &lt;a href="https://i3wm.org/" rel="noopener noreferrer"&gt;i3wm&lt;/a&gt; com tudo configurado do meu jeito. Era controle total sobre o SO: eu decidindo o que entra, mantendo cada peça (i3wm, &lt;a href="https://github.com/polybar/polybar" rel="noopener noreferrer"&gt;polybar&lt;/a&gt;, &lt;a href="https://github.com/yshui/picom" rel="noopener noreferrer"&gt;picom&lt;/a&gt;, &lt;a href="https://sw.kovidgoyal.net/kitty/" rel="noopener noreferrer"&gt;kitty&lt;/a&gt;, dotfiles) atualizada e fazendo elas conversarem entre si pra o SO inteiro funcionar. Servia bem, mas era custoso de manter atualizado: sempre desbravando algum TUI novo pra resolver um probleminha, e trocar de CPU ou notebook era ver compatibilidade de tudo e ajustar pra cada máquina.&lt;/p&gt;

&lt;p&gt;O &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; resolveu isso entregando o mesmo tipo de setup, só que com uma comunidade inteira por trás, trabalhando e discutindo melhorias toda semana. Aprendo algo novo o tempo todo.&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%2Fwommzbv5sh5f1dsbxqc2.jpg" 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%2Fwommzbv5sh5f1dsbxqc2.jpg" alt="Omarchy rodando com o tema Osaka Jade" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Tema: &lt;a href="https://omarchythemes.com/themes/osaka-jade" rel="noopener noreferrer"&gt;Osaka Jade&lt;/a&gt;&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que o Omarchy
&lt;/h2&gt;

&lt;p&gt;O &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; é uma distro Arch opinativa criada pelo &lt;a href="https://dhh.dk/" rel="noopener noreferrer"&gt;David Heinemeier Hansson (DHH)&lt;/a&gt;, criador do &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Rails&lt;/a&gt;, do &lt;a href="https://basecamp.com/" rel="noopener noreferrer"&gt;Basecamp&lt;/a&gt; e do &lt;a href="https://www.hey.com/" rel="noopener noreferrer"&gt;Hey&lt;/a&gt;. Por baixo é &lt;a href="https://archlinux.org/" rel="noopener noreferrer"&gt;Arch Linux&lt;/a&gt; + &lt;a href="https://hyprland.org/" rel="noopener noreferrer"&gt;Hyprland&lt;/a&gt;, mas vem com tudo pré-configurado: lock screen, menu bar, bluetooth, temas, atalhos. Nada de instalar pacote por pacote.&lt;/p&gt;

&lt;p&gt;O que me convenceu: Hyprland puro é bonito mas cru. Sozinho ele te deixa configurando coisas básicas por semanas. O &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; escolhe defaults bons e te entrega um sistema pronto pra trabalhar poucos minutos depois do boot.&lt;/p&gt;

&lt;p&gt;É irmão do &lt;a href="https://github.com/basecamp/omakub" rel="noopener noreferrer"&gt;Omakub&lt;/a&gt;, versão Ubuntu da mesma ideia. Quem quer Linux com curva mais suave vai pro Omakub, quem quer Arch sem perder dois dias configurando vai pro &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Temas
&lt;/h2&gt;

&lt;p&gt;Um dos pontos fortes do &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; é a comunidade de temas. O &lt;a href="https://omarchythemes.com/" rel="noopener noreferrer"&gt;omarchythemes.com&lt;/a&gt; reúne dezenas de temas prontos pra instalar com um comando, e a paleta inteira muda junta: terminal, editor, status bar, lock screen, wallpaper. E a troca é instantânea. Um atalho, dois segundos, todo o sistema já tá com o novo visual. Impressionante toda vez. A hero shot lá em cima tá com o Osaka Jade. Abaixo, um dia a dia com o Rose of Dune.&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%2Fndqdnzj5o5a3nth62y6i.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%2Fndqdnzj5o5a3nth62y6i.png" alt="Omarchy num workflow tiled com tema Rose of Dune" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Tema: &lt;a href="https://omarchythemes.com/themes/roseofdune" rel="noopener noreferrer"&gt;Rose of Dune&lt;/a&gt;&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  De onde vim
&lt;/h2&gt;

&lt;p&gt;Voltei pro Linux saindo de um MacBook M1 com 16GB de RAM. Vários fatores pesaram: o Docker estourava a memória e no Mac RAM é soldada, não dá pra subir, precisa comprar um novo (e configurar com mais memória vira coisa burocrática e cara); pra ter um tiling window manager precisava desabilitar parte da proteção do sistema (o SIP) pra forçar o &lt;a href="https://github.com/koekeishiya/yabai" rel="noopener noreferrer"&gt;Yabai&lt;/a&gt; (tiling WM pra Mac), e qualquer update do macOS quebrava algo; e queria saber o que tava rodando na máquina, já que Mac, Windows e até Ubuntu vêm cheios de coisa que você não pediu, mandando telemetria sem transparência. Até o Chrome no macOS instala um daemon silencioso chamado Keystone por trás (vale ler o &lt;a href="https://chromeisbad.com/" rel="noopener noreferrer"&gt;chromeisbad.com&lt;/a&gt;). Trocar pra Arch era o caminho natural.&lt;/p&gt;

&lt;p&gt;Construí o &lt;a href="https://github.com/guilhermeyo/mclovin-ARCHived" rel="noopener noreferrer"&gt;mclovin-ARCHived&lt;/a&gt; pra automatizar a instalação do meu setup Arch + i3wm + polybar + &lt;a href="https://neovim.io/" rel="noopener noreferrer"&gt;neovim&lt;/a&gt; + dotfiles. O nome era referência ao McLovin do filme Superbad, era basicamente meu "script superbad".&lt;/p&gt;

&lt;p&gt;A referência inicial foi o &lt;a href="https://github.com/basecamp/omakub" rel="noopener noreferrer"&gt;Omakub&lt;/a&gt; do DHH, que mostrou o padrão "rodar um script e ter tudo pronto". Não fui pro Omakub direto porque anos atrás já tinha usado muito Ubuntu e não curtia o estilo do &lt;a href="https://www.gnome.org/" rel="noopener noreferrer"&gt;GNOME&lt;/a&gt;, queria algo mais personalizável e tiling. Mas a ideia ficou.&lt;/p&gt;

&lt;p&gt;Outras inspirações:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.ml4w.com/" rel="noopener noreferrer"&gt;ML4W&lt;/a&gt;, outro projeto de automação de Arch que cheguei perto de adotar&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/@typecraft_dev" rel="noopener noreferrer"&gt;typecraft_dev&lt;/a&gt; no YouTube, com tutoriais de Hyprland, i3wm, Neovim e Arch&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://caffeine.wiki/x220.html" rel="noopener noreferrer"&gt;caffeine.wiki/x220&lt;/a&gt;, onde o Rodrigo Franco (caffo) personalizou o Arch + &lt;a href="https://dwm.suckless.org/" rel="noopener noreferrer"&gt;dwm&lt;/a&gt; num Thinkpad X220 pra ter uma máquina barata, durável e discreta pra trabalhar em qualquer canto (cafezinho de bairro suspeito incluso)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O installer fazia o trabalho. Mas era trabalho de manter sozinho.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que migrei
&lt;/h2&gt;

&lt;p&gt;Ter installer próprio é divertido até o dia que você precisa fazer o display manager funcionar com a webcam nova. Ou descobrir que aquele &lt;a href="https://aur.archlinux.org/" rel="noopener noreferrer"&gt;AUR&lt;/a&gt; helper que você escolheu três anos atrás virou abandonware. Ou ver que o tema do polybar quebrou porque a fonte mudou de nome num update.&lt;/p&gt;

&lt;p&gt;Tudo isso é resolvível. Mas resolvível por uma pessoa só não escala bem.&lt;/p&gt;

&lt;p&gt;Como uso Ruby on Rails, sempre acompanhei o trabalho do DHH. O Rails é o que ele chama de &lt;a href="https://dhh.dk/2012/rails-is-omakase.html" rel="noopener noreferrer"&gt;omakase&lt;/a&gt; (em japonês, "deixa comigo, eu escolho"): você usa os defaults bons que vieram com a caixa, sem ficar configurando cada peça. &lt;a href="https://github.com/basecamp/omakub" rel="noopener noreferrer"&gt;Omakub&lt;/a&gt; levou a mesma ideia pro Ubuntu, e em junho de 2025 saiu o &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;, omakase pra Arch + Hyprland. Continuação natural. Entrei no dia 1.&lt;/p&gt;

&lt;p&gt;Acompanhar cada release desde o início faz parte da graça. O sistema fica mais redondo a cada versão, e a vista de perto da evolução compensa uns perrengues no caminho.&lt;/p&gt;

&lt;h2&gt;
  
  
  Quando faz sentido pra você
&lt;/h2&gt;

&lt;p&gt;Antes da checklist prática: o &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; faz sentido se você é curioso pra entender como Linux funciona na prática. Vai acompanhar o projeto, atualizar, eventualmente quebrar o SO e ter que formatar pra arrumar. Não é caminho pra qualquer um. Mas é assim que se ganha conhecimento real de como o sistema funciona, em vez de só usar de longe.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; é ótimo se você:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Quer Arch + tiling WM mas não quer gastar dias configurando&lt;/li&gt;
&lt;li&gt;Curte a pegada omakase: defaults opinativos, convenção sobre configuração&lt;/li&gt;
&lt;li&gt;Tá disposto a aprender Hyprland (ele tem suas pegadinhas)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sobre hardware: roda bem até em máquinas antigas. Vários Macs com Intel que estavam parados em gaveta voltaram pra ativa rodando &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;. Alguns hardwares ficam mais difíceis de configurar, mas a comunidade no &lt;a href="https://discord.gg/tXFUdasqhY" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; e no &lt;a href="https://github.com/basecamp/omarchy" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; do projeto geralmente já passou pelo problema e ajuda a resolver.&lt;/p&gt;

&lt;p&gt;Não é pra você se quiser configurar cada bit do sistema na mão (nesse caso, Arch puro é o caminho), ou se prefere &lt;a href="https://kde.org/" rel="noopener noreferrer"&gt;KDE&lt;/a&gt;/GNOME (&lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; é Hyprland-only por design).&lt;/p&gt;

&lt;h2&gt;
  
  
  Finalizando
&lt;/h2&gt;

&lt;p&gt;Manter um installer próprio de Linux foi uma fase boa, aprendi muito. Mas chegar no &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; é poder usar a mesma filosofia sem ser o único responsável por mantê-la.&lt;/p&gt;

&lt;p&gt;Formato o computador e em poucos minutos tenho tudo configurado de novo. Mac não funciona bem assim, Windows muito menos.&lt;/p&gt;

&lt;p&gt;E nem tudo é escrito em pedra. Ainda quero montar um "Opinionated Script" pro macOS com keybindings e tiling window manager parecidos com o do &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;, já que preciso do macOS pra usar Xcode e publicar pra iOS. Faz sentido deixar a máquina configurada na mesma pegada, sem ter que viver dentro dela. Com IA hoje, montar um script desse é tranquilo.&lt;/p&gt;

&lt;p&gt;Pra quem quer Arch produtivo no dia a dia sem virar o mantenedor solo, &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; é a melhor pedida.&lt;/p&gt;




&lt;p&gt;Fontes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://world.hey.com/dhh/omarchy-is-out-4666dd31" rel="noopener noreferrer"&gt;DHH: Omarchy is out&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/basecamp/omakub" rel="noopener noreferrer"&gt;Omakub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ml4w.com/" rel="noopener noreferrer"&gt;ML4W&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/guilhermeyo/mclovin-ARCHived" rel="noopener noreferrer"&gt;mclovin-ARCHived&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/@typecraft_dev" rel="noopener noreferrer"&gt;typecraft_dev no YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caffeine.wiki/x220.html" rel="noopener noreferrer"&gt;Arch on Thinkpad X220 (caffeine.wiki)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente em &lt;a href="https://guilherme44.com/blog/omarchy-from-day-one/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>omarchy</category>
      <category>archlinux</category>
      <category>hyprland</category>
      <category>linux</category>
    </item>
    <item>
      <title>Why I traded my custom "Opinionated Linux" for Omarchy</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Sat, 02 May 2026 12:23:28 +0000</pubDate>
      <link>https://dev.to/guilherme44/why-i-traded-my-custom-opinionated-linux-for-omarchy-5i</link>
      <guid>https://dev.to/guilherme44/why-i-traded-my-custom-opinionated-linux-for-omarchy-5i</guid>
      <description>&lt;p&gt;I've been using &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; as my main setup since June 26, 2025, the day DHH released the first version. Before that I had my own custom Opinionated Linux, &lt;a href="https://github.com/guilhermeyo/mclovin-ARCHived" rel="noopener noreferrer"&gt;mclovin-ARCHived&lt;/a&gt;: an Arch + &lt;a href="https://i3wm.org/" rel="noopener noreferrer"&gt;i3wm&lt;/a&gt; installer set up exactly the way I liked. It was total control over the OS: me deciding what goes in, keeping every piece (i3wm, &lt;a href="https://github.com/polybar/polybar" rel="noopener noreferrer"&gt;polybar&lt;/a&gt;, &lt;a href="https://github.com/yshui/picom" rel="noopener noreferrer"&gt;picom&lt;/a&gt;, &lt;a href="https://sw.kovidgoyal.net/kitty/" rel="noopener noreferrer"&gt;kitty&lt;/a&gt;, dotfiles) up to date and making sure they all talked to each other for the whole OS to keep working. It did the job, but it was costly to keep up to date: always digging into some new TUI to solve a small issue, and changing CPU or laptop meant checking compatibility for everything and tweaking for each machine.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; solved that by delivering the same kind of setup, but with a whole community behind it, working and discussing improvements every week. I learn something new all the time.&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%2Fwommzbv5sh5f1dsbxqc2.jpg" 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%2Fwommzbv5sh5f1dsbxqc2.jpg" alt="Omarchy running the Osaka Jade theme" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Theme: &lt;a href="https://omarchythemes.com/themes/osaka-jade" rel="noopener noreferrer"&gt;Osaka Jade&lt;/a&gt;&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Omarchy
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; is an opinionated Arch distro created by &lt;a href="https://dhh.dk/" rel="noopener noreferrer"&gt;David Heinemeier Hansson (DHH)&lt;/a&gt;, creator of &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Rails&lt;/a&gt;, &lt;a href="https://basecamp.com/" rel="noopener noreferrer"&gt;Basecamp&lt;/a&gt; and &lt;a href="https://www.hey.com/" rel="noopener noreferrer"&gt;Hey&lt;/a&gt;. Under the hood it's &lt;a href="https://archlinux.org/" rel="noopener noreferrer"&gt;Arch Linux&lt;/a&gt; + &lt;a href="https://hyprland.org/" rel="noopener noreferrer"&gt;Hyprland&lt;/a&gt;, but it ships with everything pre-configured: lock screen, menu bar, bluetooth, themes, keybindings. No installing packages one by one.&lt;/p&gt;

&lt;p&gt;What sold me on it: bare Hyprland is beautiful but raw. On its own, it leaves you configuring basics for weeks. &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; picks good defaults and hands you a system that's ready to work a few minutes after boot.&lt;/p&gt;

&lt;p&gt;It's the sister project to &lt;a href="https://github.com/basecamp/omakub" rel="noopener noreferrer"&gt;Omakub&lt;/a&gt;, the Ubuntu version of the same idea. People who want Linux with a softer learning curve go for Omakub; people who want Arch without losing two days configuring it go for &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Themes
&lt;/h2&gt;

&lt;p&gt;One of &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;'s strong points is the theme community. &lt;a href="https://omarchythemes.com/" rel="noopener noreferrer"&gt;omarchythemes.com&lt;/a&gt; collects dozens of themes ready to install with a single command, and the whole palette switches together: terminal, editor, status bar, lock screen, wallpaper. And the switch is instant. A keybind, two seconds, the whole system already on the new look. Impressive every time. The hero shot up top is the Osaka Jade theme. Below, a day-to-day setup with the Rose of Dune.&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%2Fndqdnzj5o5a3nth62y6i.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%2Fndqdnzj5o5a3nth62y6i.png" alt="Omarchy in a tiled workflow with the Rose of Dune theme" width="800" height="450"&gt;&lt;/a&gt;&lt;br&gt;
&lt;small&gt;&lt;em&gt;Theme: &lt;a href="https://omarchythemes.com/themes/roseofdune" rel="noopener noreferrer"&gt;Rose of Dune&lt;/a&gt;&lt;/em&gt;&lt;/small&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Where I came from
&lt;/h2&gt;

&lt;p&gt;Came back to Linux from a MacBook M1 with 16GB of RAM. A few things piled up: Docker would eat through memory and RAM on a Mac is soldered, you can't upgrade, you have to buy a new one (and speccing one with more memory gets bureaucratic and expensive); for a tiling window manager I had to disable part of the system's security (SIP) to force &lt;a href="https://github.com/koekeishiya/yabai" rel="noopener noreferrer"&gt;Yabai&lt;/a&gt; (tiling WM for Mac), and any macOS update would break something; and I wanted to know what was actually running on the machine, since Mac, Windows and even Ubuntu come packed with stuff you never asked for, shipping telemetry without much transparency. Even Chrome on macOS installs a silent daemon called Keystone in the background (worth reading &lt;a href="https://chromeisbad.com/" rel="noopener noreferrer"&gt;chromeisbad.com&lt;/a&gt;). Switching to Arch was the natural move.&lt;/p&gt;

&lt;p&gt;I built &lt;a href="https://github.com/guilhermeyo/mclovin-ARCHived" rel="noopener noreferrer"&gt;mclovin-ARCHived&lt;/a&gt; to automate the install of my Arch + i3wm + polybar + &lt;a href="https://neovim.io/" rel="noopener noreferrer"&gt;neovim&lt;/a&gt; + dotfiles setup. The name was a reference to McLovin from the movie Superbad, it was basically my own "superbad script".&lt;/p&gt;

&lt;p&gt;The initial reference was &lt;a href="https://github.com/basecamp/omakub" rel="noopener noreferrer"&gt;Omakub&lt;/a&gt; by DHH, which showed me the "run a script and have everything ready" pattern. I didn't go for Omakub directly because I'd used Ubuntu a lot years ago and wasn't into the &lt;a href="https://www.gnome.org/" rel="noopener noreferrer"&gt;GNOME&lt;/a&gt; style, I wanted something more customizable and tiling. But the idea stuck.&lt;/p&gt;

&lt;p&gt;Other inspirations:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://www.ml4w.com/" rel="noopener noreferrer"&gt;ML4W&lt;/a&gt;, another Arch automation project I came close to adopting&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://www.youtube.com/@typecraft_dev" rel="noopener noreferrer"&gt;typecraft_dev&lt;/a&gt; on YouTube, with tutorials on Hyprland, i3wm, Neovim and Arch&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://caffeine.wiki/x220.html" rel="noopener noreferrer"&gt;caffeine.wiki/x220&lt;/a&gt;, where Rodrigo Franco (caffo) tuned Arch + &lt;a href="https://dwm.suckless.org/" rel="noopener noreferrer"&gt;dwm&lt;/a&gt; on a Thinkpad X220 for a cheap, durable and low-profile machine he could use anywhere (sketchy coffee shops included)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The installer did the job. But it was solo work to maintain.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why I switched
&lt;/h2&gt;

&lt;p&gt;Having your own installer is fun until the day you need to make the display manager work with a new webcam. Or you find out the &lt;a href="https://aur.archlinux.org/" rel="noopener noreferrer"&gt;AUR&lt;/a&gt; helper you picked three years ago is now abandonware. Or the polybar theme breaks because the font got renamed in some update.&lt;/p&gt;

&lt;p&gt;All solvable. But solvable by one person doesn't scale well.&lt;/p&gt;

&lt;p&gt;Since I work with Ruby on Rails, I've been following DHH's work for years. Rails is what he calls &lt;a href="https://dhh.dk/2012/rails-is-omakase.html" rel="noopener noreferrer"&gt;omakase&lt;/a&gt; (Japanese for "I'll leave it to you"): you take the good defaults that came in the box, instead of configuring every piece. &lt;a href="https://github.com/basecamp/omakub" rel="noopener noreferrer"&gt;Omakub&lt;/a&gt; took the same idea to Ubuntu, and in June 2025 came &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;, omakase for Arch + Hyprland. Natural continuation. I jumped in on day 1.&lt;/p&gt;

&lt;p&gt;Following every release from the start is part of the appeal. The system gets more polished with each version, and the up-close view of the evolution makes up for any rough patches along the way.&lt;/p&gt;

&lt;h2&gt;
  
  
  When it makes sense for you
&lt;/h2&gt;

&lt;p&gt;Before the practical checklist: &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; makes sense if you're curious about how Linux actually works under the hood. You'll follow the project, update, eventually break the OS and have to format to fix it. Not a path for everyone. But that's how you end up with real knowledge of how the system works, instead of just using it from a distance.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; is great if you:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Want Arch + tiling WM but don't want to spend days configuring&lt;/li&gt;
&lt;li&gt;Are into the omakase approach: opinionated defaults, convention over configuration&lt;/li&gt;
&lt;li&gt;Are willing to learn Hyprland (it has its quirks)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;About hardware: it runs well even on older machines. Several old Intel Macs that had been sitting in drawers are back in action running &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;. Some hardware takes more work to configure, but the community on the project's &lt;a href="https://discord.gg/tXFUdasqhY" rel="noopener noreferrer"&gt;Discord&lt;/a&gt; and &lt;a href="https://github.com/basecamp/omarchy" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt; has usually been there before and helps you sort it out.&lt;/p&gt;

&lt;p&gt;Not for you if you want to configure every bit of the system by hand (in which case bare Arch is the path), or if you prefer &lt;a href="https://kde.org/" rel="noopener noreferrer"&gt;KDE&lt;/a&gt;/GNOME (&lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; is Hyprland-only by design).&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping up
&lt;/h2&gt;

&lt;p&gt;Keeping my own Linux installer was a good phase, taught me a lot. But landing on &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; means I get to use the same philosophy without being the only one responsible for maintaining it.&lt;/p&gt;

&lt;p&gt;I can wipe the machine and have everything configured again in a few minutes. Macs don't work that way, Windows even less.&lt;/p&gt;

&lt;p&gt;And nothing's set in stone. I still want to build an "Opinionated Script" for macOS with keybindings and a tiling window manager similar to &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;, since I need macOS to use Xcode and ship apps to iOS. Makes sense to have the machine set up in the same vibe without having to live inside it. With AI these days, putting a script like that together is straightforward.&lt;/p&gt;

&lt;p&gt;If you want productive Arch in your day to day without being the solo maintainer, &lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt; is the way.&lt;/p&gt;




&lt;p&gt;Sources:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://omarchy.org/" rel="noopener noreferrer"&gt;Omarchy&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://world.hey.com/dhh/omarchy-is-out-4666dd31" rel="noopener noreferrer"&gt;DHH: Omarchy is out&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/basecamp/omakub" rel="noopener noreferrer"&gt;Omakub&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.ml4w.com/" rel="noopener noreferrer"&gt;ML4W&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/guilhermeyo/mclovin-ARCHived" rel="noopener noreferrer"&gt;mclovin-ARCHived&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/@typecraft_dev" rel="noopener noreferrer"&gt;typecraft_dev on YouTube&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://caffeine.wiki/x220.html" rel="noopener noreferrer"&gt;Arch on Thinkpad X220 (caffeine.wiki)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Originally posted at &lt;a href="https://guilherme44.com/en/blog/omarchy-from-day-one/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>omarchy</category>
      <category>archlinux</category>
      <category>hyprland</category>
      <category>linux</category>
    </item>
    <item>
      <title>Using MinIO with Rails</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Fri, 01 May 2026 03:27:57 +0000</pubDate>
      <link>https://dev.to/guilherme44/using-minio-with-rails-5044</link>
      <guid>https://dev.to/guilherme44/using-minio-with-rails-5044</guid>
      <description>&lt;p&gt;I'm on a project where the file upload setup uses Active Storage with Amazon S3. But there's no configuration for me to download files from S3 and use them in development.&lt;/p&gt;

&lt;p&gt;When I looked at &lt;code&gt;config/storage.yml&lt;/code&gt; I saw the following:&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;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Disk&lt;/span&gt;
  &lt;span class="na"&gt;root&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;&amp;lt;%= Rails.root.join("storage") %&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I figured it would be simple, just bring everything inside the S3 Bucket into the app's &lt;code&gt;app/storage&lt;/code&gt; folder.&lt;/p&gt;

&lt;p&gt;I downloaded the AWS CLI and set it up with my credentials.&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%2F8entetcwj3t4g75pkcea.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%2F8entetcwj3t4g75pkcea.png" alt="Configuration AWS"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Then I synced the S3 files into my &lt;code&gt;app/storage&lt;/code&gt; folder:&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="nv"&gt;$ &lt;/span&gt;aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://bucket-name ~/Projects/selected-project/storage
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Started the app and it didn't work 🙄.&lt;/p&gt;

&lt;p&gt;In ActiveStorage, when the configured service is Disk, after the blob generates the key, the file is saved inside two folders and then the blob key.&lt;/p&gt;

&lt;p&gt;I checked the file path in the console and it returned:&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="o"&gt;&amp;gt;&lt;/span&gt; user &lt;span class="o"&gt;=&lt;/span&gt; User.first
&lt;span class="o"&gt;&amp;gt;&lt;/span&gt; ActiveStorage::Blob.service.send&lt;span class="o"&gt;(&lt;/span&gt;:path_for, u.avatar.key&lt;span class="o"&gt;)&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s2"&gt;"storage/jt/Y7/jtY7656jGPvfPMUUA8kX6Vb4"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I noticed those folders were the first 4 characters of the key.&lt;/p&gt;

&lt;p&gt;I thought of a few solutions:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Write a script to generate those subfolders and place the files in them.&lt;br&gt;&lt;br&gt;&lt;br&gt;
I didn't go with this. It might not work, and since I don't have experience with scripts that change the filesystem, I figured it would take too long.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Check how Active Storage generates &lt;code&gt;path_for&lt;/code&gt; and tell it to generate directly without those subfolders.&lt;br&gt;&lt;br&gt;&lt;br&gt;
I searched and didn't find anywhere I could do that.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Duplicate the bucket inside S3 and use it for development.&lt;br&gt;&lt;br&gt;&lt;br&gt;
Didn't like the idea, I'd have to pay for storing 2 buckets.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Use &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;MinIO&lt;/a&gt;.&lt;br&gt;&lt;br&gt;&lt;br&gt;
This was the option I figured was fastest and had the best cost-benefit.&lt;br&gt;&lt;br&gt;&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For those who don't know, MinIO is an Open Source project written in &lt;a href="https://golang.org/" rel="noopener noreferrer"&gt;Go&lt;/a&gt;, designed from the start to be the standard for object storage in private cloud. It's a cloud-native object server with concurrent, scalable and lightweight performance (&lt;a href="https://min.io/product/overview" rel="noopener noreferrer"&gt;more info&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;I need to set up &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;MinIO&lt;/a&gt; like S3, since the app needs to think it's talking to S3. I downloaded &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;MinIO&lt;/a&gt; from their site and set up the environment variables:&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;export &lt;/span&gt;&lt;span class="nv"&gt;MINIO_ACCESS_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"minio_storage_development"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MINIO_SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"minio_storage_development"&lt;/span&gt;
&lt;span class="nb"&gt;export &lt;/span&gt;&lt;span class="nv"&gt;MINIO_REGION_NAME&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"us-east-1"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's configured through environment variables. You can check all the settings in the docs. (&lt;a href="https://docs.min.io/" rel="noopener noreferrer"&gt;MinIO docs&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Now just start it:&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="nv"&gt;$ &lt;/span&gt;minio server ~/minio_storage

Endpoint:  http://xxx.xxx.xxx.xxx:9000  http://xxx.xxx.xxx.xxx:9000  http://127.0.0.1:9000
AccessKey: minio_storage_development
SecretKey: minio_storage_development
Region:    us-east-1

Browser Access:
  http://xxx.xxx.xxx.xxx:9000  http://xxx.xxx.xxx.xxx:9000  http://127.0.0.1:9000

Command-line Access: https://docs.min.io/docs/minio-client-quickstart-guide
  &lt;span class="nv"&gt;$ &lt;/span&gt;mc config host add myminio http://100.100.101.162:9000 minio_storage_development minio_storage_development
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note: I hid my IP with &lt;code&gt;xxx.xxx.xxx.xxx&lt;/code&gt; because it's static and I don't want it exposed to the web.&lt;/p&gt;

&lt;p&gt;There you go, a &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;MinIO&lt;/a&gt; server running on your machine.&lt;/p&gt;

&lt;p&gt;I accessed it through my browser at &lt;code&gt;http://127.0.0.1:9000/&lt;/code&gt; using the Access Key and Secret Key, and created a Bucket called &lt;code&gt;rails_app_bucket&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In the Rails settings at &lt;code&gt;config/storage.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;local&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S3&lt;/span&gt;
  &lt;span class="na"&gt;endpoint&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;http://127.0.0.1:9000&lt;/span&gt;
  &lt;span class="na"&gt;access_key_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;minio_storage_development&lt;/span&gt;
  &lt;span class="na"&gt;secret_access_key&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;minio_storage_development&lt;/span&gt;
  &lt;span class="na"&gt;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;bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;rails_app_bucket&lt;/span&gt;
  &lt;span class="na"&gt;force_path_style&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I moved all the files I had downloaded from the S3 bucket into the &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;MinIO&lt;/a&gt; bucket folder:&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="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mv&lt;/span&gt; ~/Projects/selected-project/storage/&lt;span class="k"&gt;*&lt;/span&gt; ~/minio_storage/rails_app_bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To sync files from S3 I now use:&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="nv"&gt;$ &lt;/span&gt;aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;s3://bucket-name ~/minio_storage/rails_app_bucket
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also added &lt;a href="https://min.io/" rel="noopener noreferrer"&gt;MinIO&lt;/a&gt; to my &lt;code&gt;Procfile.development&lt;/code&gt; so the server starts whenever I run the app through foreman:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="c"&gt;# Procfile.development
&lt;/span&gt;
&lt;span class="n"&gt;server&lt;/span&gt;: &lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;rails&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt;
&lt;span class="n"&gt;webpacker&lt;/span&gt;: &lt;span class="n"&gt;bin&lt;/span&gt;/&lt;span class="n"&gt;webpack&lt;/span&gt;-&lt;span class="n"&gt;dev&lt;/span&gt;-&lt;span class="n"&gt;server&lt;/span&gt;
&lt;span class="n"&gt;redis&lt;/span&gt;: &lt;span class="n"&gt;redis&lt;/span&gt;-&lt;span class="n"&gt;server&lt;/span&gt;
&lt;span class="n"&gt;sidekiq&lt;/span&gt;: &lt;span class="n"&gt;bundle&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt; &lt;span class="n"&gt;sidekiq&lt;/span&gt;
&lt;span class="n"&gt;minio&lt;/span&gt;: &lt;span class="n"&gt;minio&lt;/span&gt; &lt;span class="n"&gt;server&lt;/span&gt; ~/&lt;span class="n"&gt;minio_storage&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;It became really simple to sync the files and not get any 404 errors when running the app, or that Rails error saying the file doesn't exist.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally posted at &lt;a href="https://guilherme44.com/en/blog/using-minio-with-rails/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>ruby</category>
      <category>minio</category>
      <category>s3</category>
    </item>
    <item>
      <title>[PT-BR] Slash commands: chega de commits sem sentido</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Fri, 01 May 2026 03:27:22 +0000</pubDate>
      <link>https://dev.to/guilherme44/pt-br-slash-commands-chega-de-commits-sem-sentido-1ooa</link>
      <guid>https://dev.to/guilherme44/pt-br-slash-commands-chega-de-commits-sem-sentido-1ooa</guid>
      <description>&lt;p&gt;Uma das coisas que mais uso desde que comecei com o Claude Code são os slash commands. E um que acho que todo dev hoje precisa ter é o &lt;code&gt;/commit&lt;/code&gt;. Ele acabou com um hábito que vários devs sofrem, até os mais experientes. O &lt;code&gt;/commit&lt;/code&gt; deixa a régua explícita, e o time todo passa a escrever commits do mesmo jeito.&lt;/p&gt;

&lt;p&gt;Sempre me importei com mensagens de commit. Mesmo dev que se importa acaba deixando um &lt;code&gt;"fixes"&lt;/code&gt; ou um &lt;code&gt;"update"&lt;/code&gt; genérico escapar numa sexta cansada ou no meio de um refactor gigante. É assim que um repo acaba com uma camada de ruído que ninguém decifra três meses depois.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é um slash command
&lt;/h2&gt;

&lt;p&gt;Slash commands não são exclusivos do Claude Code. OpenCode, Codex CLI, Aider e Continue deixam você criar seus próprios comandos de algum jeito. O formato e o gatilho mudam, mas a ideia é a mesma: um atalho curto roda um prompt mais longo que você escreveu uma vez.&lt;/p&gt;

&lt;p&gt;Vou mostrar com Claude Code, que é o que uso no dia a dia. A configuração se traduz bem pros outros.&lt;/p&gt;

&lt;p&gt;No Claude Code, um slash command é só um arquivo markdown. Você joga ele em &lt;code&gt;~/.claude/commands/&amp;lt;nome&amp;gt;.md&lt;/code&gt; (global) ou &lt;code&gt;.claude/commands/&amp;lt;nome&amp;gt;.md&lt;/code&gt; (por projeto), e pronto: &lt;code&gt;/&amp;lt;nome&amp;gt;&lt;/code&gt; vira atalho. O frontmatter tem um &lt;code&gt;description&lt;/code&gt;, e o corpo é o prompt que o agente lê quando você chama.&lt;/p&gt;

&lt;p&gt;É isso. Sem DSL, sem SDK, sem manifesto de plugin. Arquivo markdown entra, slash command sai.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema do commit
&lt;/h2&gt;

&lt;p&gt;Mesmo com um agente bom no loop, um simples &lt;code&gt;"commita minhas mudanças"&lt;/code&gt; te entrega algo passável, mas não excelente. O agente escolhe um tipo qualquer, escreve uma mensagem que serve e empilha mudanças sem relação no mesmo commit. Melhor do que sem agente nenhum. Mas dá pra melhorar.&lt;/p&gt;

&lt;p&gt;O que eu queria era algo que lesse minhas mudanças, agrupasse por domínio, escrevesse uma mensagem decente seguindo o &lt;a href="https://www.conventionalcommits.org/pt-br/v1.0.0/" rel="noopener noreferrer"&gt;Conventional Commits&lt;/a&gt; pra cada grupo, e pulasse os arquivos que não devem ser commitados.&lt;/p&gt;

&lt;h2&gt;
  
  
  /commit
&lt;/h2&gt;

&lt;p&gt;Aqui vai o slash command que montei pra manter os commits limpos e consistentes:&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="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Analyze&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;all&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;changes&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;and&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;commit&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;in&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;logical&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;groups."&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;

&lt;span class="gh"&gt;# Commit&lt;/span&gt;

&lt;span class="gu"&gt;## Step 1: Survey changes&lt;/span&gt;

Run &lt;span class="sb"&gt;`bin/commit-survey`&lt;/span&gt; to get the file lists and classification.

Read diffs of key files if you need more context on the changes.

&lt;span class="gu"&gt;## Step 2: Group the changes&lt;/span&gt;

Use the &lt;span class="sb"&gt;`--- classified ---`&lt;/span&gt; output as a starting point, then refine into logical commits.

Grouping strategy:
&lt;span class="p"&gt;-&lt;/span&gt; By domain/feature: e.g., all auth changes together
&lt;span class="p"&gt;-&lt;/span&gt; By layer: e.g., model tests, controller tests
&lt;span class="p"&gt;-&lt;/span&gt; By type: e.g., all config changes, all dependency updates

Files in &lt;span class="sb"&gt;`[skip]`&lt;/span&gt; should NOT be committed. If unsure about a file, skip it.

&lt;span class="gu"&gt;## Step 3: Commit each group&lt;/span&gt;

For each group, combine stage + commit in one call:

git add &lt;span class="nt"&gt;&amp;lt;specific&lt;/span&gt; &lt;span class="na"&gt;files&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; &amp;amp;&amp;amp; git commit -m "&lt;span class="nt"&gt;&amp;lt;message&amp;gt;&lt;/span&gt;"

Order commits from most independent to most dependent:
&lt;span class="p"&gt;-&lt;/span&gt; Config/tooling changes first
&lt;span class="p"&gt;-&lt;/span&gt; Then source code changes
&lt;span class="p"&gt;-&lt;/span&gt; Then test changes
&lt;span class="p"&gt;-&lt;/span&gt; Generated files last
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esses três passos são o motor inteiro.&lt;/p&gt;

&lt;h2&gt;
  
  
  bin/commit-survey
&lt;/h2&gt;

&lt;p&gt;O Step 1 chama um script. É o que faz o resto funcionar.&lt;/p&gt;

&lt;p&gt;Eu poderia pedir pro agente rodar &lt;code&gt;git status&lt;/code&gt;, &lt;code&gt;git diff&lt;/code&gt;, parsear a saída e classificar os arquivos na cabeça dele. Funcionaria na maioria das vezes. Mas aí cada execução gasta tokens na mesma lógica de parsing, e a saída fica do jeito que o Claude tiver inspirado naquele dia.&lt;/p&gt;

&lt;p&gt;Um script Ruby curto te dá:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Uma invocação previsível&lt;/li&gt;
&lt;li&gt;Menos tokens (sem saída de &lt;code&gt;git status&lt;/code&gt; pra digerir, só o resultado classificado)&lt;/li&gt;
&lt;li&gt;Buckets que batem com o jeito que você pensa no seu código&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;São ~22 linhas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;SKIP_PATTERNS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sx"&gt;%w[.env credentials master.key tasks.md notes.txt scratch .claude/]&lt;/span&gt;

&lt;span class="no"&gt;BUCKETS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"skip"&lt;/span&gt;   &lt;span class="o"&gt;=&amp;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="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="no"&gt;SKIP_PATTERNS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;any?&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;pat&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;include?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;pat&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="s2"&gt;"test"&lt;/span&gt;   &lt;span class="o"&gt;=&amp;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="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"test/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;"db"&lt;/span&gt;     &lt;span class="o"&gt;=&amp;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="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"db/"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;"config"&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"config/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Gemfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;".rubocop"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Procfile"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"Rakefile"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;"docs"&lt;/span&gt;   &lt;span class="o"&gt;=&amp;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="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;start_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"docs/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"README"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;end_with?&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;".md"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="s2"&gt;"app"&lt;/span&gt;    &lt;span class="o"&gt;=&amp;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="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kp"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="n"&gt;paths&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sb"&gt;`git status --porcelain`&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;lines&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;chomp: &lt;/span&gt;&lt;span class="kp"&gt;true&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="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;l&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="o"&gt;..&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="n"&gt;grouped&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;BUCKETS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;keys&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_h&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;b&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;b&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="n"&gt;paths&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;grouped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="no"&gt;BUCKETS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="n"&gt;matcher&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;call&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;path&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}.&lt;/span&gt;&lt;span class="nf"&gt;first&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;path&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="no"&gt;BUCKETS&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;each_key&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="o"&gt;|&lt;/span&gt;
  &lt;span class="n"&gt;files&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;grouped&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="s2"&gt;"[&lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;bucket&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;] &lt;/span&gt;&lt;span class="si"&gt;#{&lt;/span&gt;&lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;empty?&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="s2"&gt;"(none)"&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;files&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;join&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="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;skip&lt;/code&gt; vem primeiro pra que segredos e notas nunca acabem staged. O resto é um buffet que você ajusta por projeto. Uma app Phoenix teria &lt;code&gt;lib/&lt;/code&gt;, &lt;code&gt;priv/repo/migrations/&lt;/code&gt;, &lt;code&gt;assets/&lt;/code&gt;. Uma app Next.js teria &lt;code&gt;pages/&lt;/code&gt;, &lt;code&gt;app/&lt;/code&gt;, &lt;code&gt;public/&lt;/code&gt;, &lt;code&gt;prisma/&lt;/code&gt;. Mesma ideia, paths diferentes.&lt;/p&gt;

&lt;p&gt;Roda num repo sujo e sai isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;--- unstaged ---
&lt;/span&gt; M config.toml
 M templates/index.html
&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;--- untracked ---
&lt;/span&gt;&lt;span class="p"&gt;content/blog/_index.md
content/blog/post.md
&lt;/span&gt;&lt;span class="err"&gt;
&lt;/span&gt;&lt;span class="gd"&gt;--- classified ---
&lt;/span&gt;[skip]   (none)
[test]   (none)
[db]     (none)
[config] config.toml
[docs]   content/blog/_index.md, content/blog/post.md
[app]    templates/index.html
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O agente olha aquilo e escreve uns três commits: &lt;code&gt;chore(config): bump zola version&lt;/code&gt;, &lt;code&gt;feat(templates): add language toggle&lt;/code&gt;, &lt;code&gt;feat(blog): bootstrap section&lt;/code&gt;. Três mensagens focadas em vez de uma pra cobrir tudo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Na prática
&lt;/h2&gt;

&lt;p&gt;Eu digito &lt;code&gt;/commit&lt;/code&gt; e o Claude:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Roda &lt;code&gt;bin/commit-survey&lt;/code&gt; e lê a saída&lt;/li&gt;
&lt;li&gt;Lê diffs dos arquivos que precisar pra ter mais contexto&lt;/li&gt;
&lt;li&gt;Stage + commit de cada grupo com mensagem &lt;a href="https://www.conventionalcommits.org/pt-br/v1.0.0/" rel="noopener noreferrer"&gt;Conventional Commits&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Pula &lt;code&gt;.env&lt;/code&gt; e qualquer segredo silenciosamente&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A coisa toda leva 30 segundos, e cada commit sai limpo e bem escopado.&lt;/p&gt;

&lt;p&gt;Exemplo real desse próprio blog. Survey antes do run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[skip]   (none)
[test]   (none)
[db]     (none)
[config] config.toml
[docs]   content/blog/slash-commands-no-more-bad-commits.md, content/blog/slash-commands-no-more-bad-commits.pt-br.md
[app]    sass/_predefined.scss, sass/style.scss
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que o &lt;code&gt;/commit&lt;/code&gt; produziu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat(blog): add slash commands post in EN and pt-br
chore(config): drop syntax theme, switch to monochrome code blocks
style(sass): apply osaka jade palette and bump body font
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Três commits, cada um com escopo claro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Faça o seu
&lt;/h2&gt;

&lt;p&gt;O jeito mais simples de começar é copiar esse e ir ajustando. Joga o arquivo em &lt;code&gt;~/.claude/commands/commit.md&lt;/code&gt; se quiser global, ou em &lt;code&gt;.claude/commands/commit.md&lt;/code&gt; por projeto. Ajusta os patterns dos buckets pro teu stack. Pronto.&lt;/p&gt;

&lt;p&gt;Dá pra fazer a mesma coisa pra qualquer coisa que você faz toda hora: &lt;code&gt;/changelog&lt;/code&gt;, &lt;code&gt;/release&lt;/code&gt;, &lt;code&gt;/pr-draft&lt;/code&gt;, &lt;code&gt;/deploy&lt;/code&gt;. Cada um é um arquivo markdown com os passos que você teria que digitar no chat toda vez.&lt;/p&gt;

&lt;h2&gt;
  
  
  Finalizando
&lt;/h2&gt;

&lt;p&gt;A configuração é pequena, mas a diferença aparece em todo commit. Um slash command customizado mais um script pequeno já é suficiente pra fazer a IA pegar o trabalho rotineiro pra si.&lt;/p&gt;

&lt;p&gt;Chega de commits &lt;code&gt;"update"&lt;/code&gt;. Cada commit reflete a régua que você escreveu no arquivo markdown.&lt;/p&gt;




&lt;p&gt;Fontes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.conventionalcommits.org/pt-br/v1.0.0/" rel="noopener noreferrer"&gt;Conventional Commits 1.0.0&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://docs.claude.com/en/docs/claude-code/slash-commands" rel="noopener noreferrer"&gt;Documentação dos slash commands do Claude Code&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://opencode.ai/" rel="noopener noreferrer"&gt;OpenCode&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://github.com/openai/codex" rel="noopener noreferrer"&gt;OpenAI Codex CLI&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://aider.chat/" rel="noopener noreferrer"&gt;Aider&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://continue.dev/" rel="noopener noreferrer"&gt;Continue&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente em &lt;a href="https://guilherme44.com/blog/slash-commands-no-more-bad-commits/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>claudecode</category>
      <category>opencode</category>
      <category>productivity</category>
      <category>tooling</category>
    </item>
    <item>
      <title>Sending SMS with Ruby and Twilio</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Fri, 01 May 2026 03:27:11 +0000</pubDate>
      <link>https://dev.to/guilherme44/sending-sms-with-ruby-and-twilio-3n50</link>
      <guid>https://dev.to/guilherme44/sending-sms-with-ruby-and-twilio-3n50</guid>
      <description>&lt;p&gt;I had never built an app that needed to send SMS, but our last project at the firm required it. I talked to a friend about it and he told me about &lt;a href="http://twilio.com" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt;. I did some quick research and found the docs really complete and easy to follow.&lt;/p&gt;

&lt;p&gt;For those who don't know &lt;a href="http://twilio.com" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt;, it's a platform that lets you integrate voice, text messages, video, notifications and other things into your app through an API. Integrations are available in Ruby, Java, .NET, Node.js, PHP, and others.&lt;/p&gt;

&lt;p&gt;I'll show how easy it is to send SMS from the Ruby console. Soon I plan to do a screencast on how to implement this in a Ruby on Rails app.&lt;/p&gt;

&lt;h3&gt;
  
  
  Step 1: Create a Twilio account
&lt;/h3&gt;

&lt;p&gt;Create a Twilio account and access the &lt;a href="http://twilio.com/console" rel="noopener noreferrer"&gt;Twilio Console&lt;/a&gt;.&lt;br&gt;&lt;br&gt;
Sign up: &lt;a href="https://www.twilio.com/try-twilio" rel="noopener noreferrer"&gt;https://www.twilio.com/try-twilio&lt;/a&gt;&lt;br&gt;&lt;br&gt;
Console: &lt;a href="http://twilio.com/console" rel="noopener noreferrer"&gt;http://twilio.com/console&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When you create the account you get a Trial for running tests:&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%2Fijumb18ggvdmldl0g21v.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%2Fijumb18ggvdmldl0g21v.png" alt="Twilio credentials"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Don't forget to &lt;a href="https://www.twilio.com/console/phone-numbers/verified" rel="noopener noreferrer"&gt;register a verified number&lt;/a&gt;.&lt;br&gt;
&lt;a href="https://www.twilio.com/console/phone-numbers/verified" rel="noopener noreferrer"&gt;https://www.twilio.com/console/phone-numbers/verified&lt;/a&gt;&lt;br&gt;&lt;br&gt;
&lt;strong&gt;Note:&lt;/strong&gt; Verified numbers are required to send SMS in TRIAL mode.&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 2: Send the SMS
&lt;/h3&gt;

&lt;p&gt;Install the Twilio gem:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;gem &lt;span class="nb"&gt;install &lt;/span&gt;twilio-ruby &lt;span class="nt"&gt;-v&lt;/span&gt; 5.21.2
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open a Ruby console:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight irb"&gt;&lt;code&gt;&lt;span class="go"&gt;irb
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Inside the console we import the &lt;code&gt;twilio-ruby&lt;/code&gt; gem, set up the variables and send a message.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="nb"&gt;require&lt;/span&gt; &lt;span class="s1"&gt;'twilio-ruby'&lt;/span&gt;

&lt;span class="c1"&gt;# Get your SID and Auth Token from twilio.com/console&lt;/span&gt;
&lt;span class="c1"&gt;# DANGER! This is insecure.&lt;/span&gt;
&lt;span class="n"&gt;account_sid&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'ACXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'&lt;/span&gt;
&lt;span class="n"&gt;auth_token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your_auth_token'&lt;/span&gt;
&lt;span class="n"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'your_number'&lt;/span&gt; &lt;span class="c1"&gt;# Number you rented from twilio&lt;/span&gt;
&lt;span class="vi"&gt;@client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;Twilio&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;REST&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;new&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;account_sid&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;auth_token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="n"&gt;message&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="vi"&gt;@client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;messages&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="ss"&gt;from: &lt;/span&gt;&lt;span class="n"&gt;number&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;# +15017122661&lt;/span&gt;
  &lt;span class="ss"&gt;body: &lt;/span&gt;&lt;span class="s1"&gt;'My first message sent with ruby.'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="ss"&gt;to: &lt;/span&gt;&lt;span class="s1"&gt;'+5544999801281'&lt;/span&gt; &lt;span class="c1"&gt;# +5544998761234&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="nb"&gt;puts&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;sid&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Keep in mind this is insecure. It's important to keep credentials like the SID and Auth Token stored in a way that prevents unauthorized access. Since this is just a test example, I used them straight in the console.&lt;/p&gt;

&lt;h3&gt;
  
  
  Wrapping up
&lt;/h3&gt;

&lt;p&gt;As you can see, it's pretty easy to send SMS with Ruby.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://twilio.com" rel="noopener noreferrer"&gt;Twilio&lt;/a&gt; offers many other services and you can check the pricing for all of them at &lt;a href="https://www.twilio.com/pricing" rel="noopener noreferrer"&gt;https://www.twilio.com/pricing&lt;/a&gt;.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally posted at &lt;a href="https://guilherme44.com/en/blog/sending-sms-with-ruby-and-twilio/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ruby</category>
      <category>twilio</category>
      <category>sms</category>
    </item>
    <item>
      <title>[PT-BR] pluck vs. select</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Fri, 01 May 2026 03:24:39 +0000</pubDate>
      <link>https://dev.to/guilherme44/pt-br-pluck-vs-select-271f</link>
      <guid>https://dev.to/guilherme44/pt-br-pluck-vs-select-271f</guid>
      <description>&lt;h2&gt;
  
  
  pluck
&lt;/h2&gt;

&lt;p&gt;No Rails, temos o pluck, que retorna um array com os valores dos atributos que você selecionou.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Doctor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.9&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt; &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&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="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se você passa mais de um atributo, o pluck retorna um array com vários atributos.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Doctor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"updated_at"&lt;/span&gt; &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&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="no"&gt;Wed&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;23&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2019&lt;/span&gt; &lt;span class="mi"&gt;11&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;44&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;27.924159000&lt;/span&gt; &lt;span class="no"&gt;EST&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Tue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;29&lt;/span&gt; &lt;span class="no"&gt;Jan&lt;/span&gt; &lt;span class="mi"&gt;2019&lt;/span&gt; &lt;span class="mi"&gt;15&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;47&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;30.056920000&lt;/span&gt; &lt;span class="no"&gt;EST&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;05&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Thu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="no"&gt;May&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;29.238601000&lt;/span&gt; &lt;span class="no"&gt;EDT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Thu&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="no"&gt;May&lt;/span&gt; &lt;span class="mi"&gt;2020&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;29.251257000&lt;/span&gt; &lt;span class="no"&gt;EDT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;9&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Sat&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;26&lt;/span&gt; &lt;span class="no"&gt;Jun&lt;/span&gt; &lt;span class="mi"&gt;2021&lt;/span&gt; &lt;span class="mi"&gt;19&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;56&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;41.536687000&lt;/span&gt; &lt;span class="no"&gt;EDT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
 &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;Tue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;28&lt;/span&gt; &lt;span class="no"&gt;Jun&lt;/span&gt; &lt;span class="mi"&gt;2022&lt;/span&gt; &lt;span class="mi"&gt;16&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mi"&gt;49&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mf"&gt;45.091360000&lt;/span&gt; &lt;span class="no"&gt;EDT&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="mo"&gt;04&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="mo"&gt;00&lt;/span&gt;&lt;span class="p"&gt;]]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A query é precisa, busca só os atributos que você pediu.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="nv"&gt;"doctors"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;"doctors"&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;"updated_at"&lt;/span&gt; &lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"doctors"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  select
&lt;/h2&gt;

&lt;p&gt;O select faz a mesma query.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Doctor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Doctor&lt;/span&gt; &lt;span class="no"&gt;Load&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"updated_at"&lt;/span&gt; &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;

&lt;span class="no"&gt;Doctor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pluck&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
   &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.3&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"updated_at"&lt;/span&gt; &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Doctor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="no"&gt;Doctor&lt;/span&gt; &lt;span class="no"&gt;Load&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;0.6&lt;/span&gt;&lt;span class="n"&gt;ms&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;  &lt;span class="no"&gt;SELECT&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"id"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="s2"&gt;"updated_at"&lt;/span&gt; &lt;span class="no"&gt;FROM&lt;/span&gt; &lt;span class="s2"&gt;"doctors"&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="c1"&gt;#&amp;lt;Doctor:0x0000000111a2ec40 id: 1, updated_at: Wed, 23 Jan 2019 11:44:27.924159000 EST -05:00&amp;gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;#&amp;lt;Doctor:0x0000000111a2eab0 id: 3, updated_at: Tue, 29 Jan 2019 15:47:30.056920000 EST -05:00&amp;gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;#&amp;lt;Doctor:0x0000000111a2e998 id: 7, updated_at: Thu, 28 May 2020 19:30:29.238601000 EDT -04:00&amp;gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;#&amp;lt;Doctor:0x0000000111a2e858 id: 8, updated_at: Thu, 28 May 2020 19:30:29.251257000 EDT -04:00&amp;gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;#&amp;lt;Doctor:0x0000000111a2e4c0 id: 9, updated_at: Sat, 26 Jun 2021 19:56:41.536687000 EDT -04:00&amp;gt;,&lt;/span&gt;
 &lt;span class="c1"&gt;#&amp;lt;Doctor:0x0000000111a2e218 id: 5, updated_at: Tue, 28 Jun 2022 16:49:45.091360000 EDT -04:00&amp;gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mas o select retorna um &lt;code&gt;ActiveRecord::Relation&lt;/code&gt; com objetos do model em que ele foi chamado.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight ruby"&gt;&lt;code&gt;&lt;span class="no"&gt;Doctor&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="ss"&gt;:id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:updated_at&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;class&lt;/span&gt;
&lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="no"&gt;Doctor&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="no"&gt;ActiveRecord_Relation&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;é isso, pessoal :)&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Publicado originalmente em &lt;a href="https://guilherme44.com/blog/pluck-vs-select/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>rails</category>
      <category>activerecord</category>
      <category>ruby</category>
    </item>
    <item>
      <title>Organizing flash messages in Phoenix</title>
      <dc:creator>Guilherme Yamakawa de Oliveira</dc:creator>
      <pubDate>Fri, 01 May 2026 03:24:28 +0000</pubDate>
      <link>https://dev.to/guilherme44/organizing-flash-messages-in-phoenix-571p</link>
      <guid>https://dev.to/guilherme44/organizing-flash-messages-in-phoenix-571p</guid>
      <description>&lt;p&gt;I started building a system to better understand how &lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt; works and to learn about &lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.phoenixframework.org/" rel="noopener noreferrer"&gt;Phoenix&lt;/a&gt; is a framework for &lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt;, the same way &lt;a href="https://rubyonrails.org/" rel="noopener noreferrer"&gt;Rails&lt;/a&gt; is a framework for &lt;a href="https://www.ruby-lang.org/en/" rel="noopener noreferrer"&gt;Ruby&lt;/a&gt;. Its mission is to be a productive framework that doesn't compromise on speed or maintainability.&lt;/p&gt;

&lt;p&gt;Without further ado, I decided to build a simple CRUD in &lt;a href="https://elixir-lang.org/" rel="noopener noreferrer"&gt;Elixir&lt;/a&gt; to track the books I've read. I used the following commands:&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="c"&gt;# Create the app.&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;mix phx.new booklistx

&lt;span class="c"&gt;# Enter the project&lt;/span&gt;
&lt;span class="nb"&gt;cd &lt;/span&gt;booklistsx

&lt;span class="c"&gt;# CRUD generator (Rails scaffold style)&lt;/span&gt;
mix phx.gen.html Books Book books title:string

&lt;span class="c"&gt;# Create the database and the books table&lt;/span&gt;
mix ecto.create
mix ecto.migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I set the books listing as the app's root.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/booklistx_web/router.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;BooklistxWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;BooklistxWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:router&lt;/span&gt;

  &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="ss"&gt;:browser&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:accepts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"html"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:fetch_session&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:fetch_flash&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:protect_from_forgery&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:put_secure_browser_headers&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;pipeline&lt;/span&gt; &lt;span class="ss"&gt;:api&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;plug&lt;/span&gt; &lt;span class="ss"&gt;:accepts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;"json"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="n"&gt;scope&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;BooklistxWeb&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;pipe_through&lt;/span&gt; &lt;span class="ss"&gt;:browser&lt;/span&gt;

    &lt;span class="c1"&gt;# get "/", PageController, :index # &amp;lt;- I commented this line!&lt;/span&gt;
    &lt;span class="n"&gt;resources&lt;/span&gt; &lt;span class="s2"&gt;"/"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="no"&gt;BooksController&lt;/span&gt;    &lt;span class="c1"&gt;# &amp;lt;- I added this line!&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="c1"&gt;# Other scopes may use custom stacks.&lt;/span&gt;
  &lt;span class="c1"&gt;# scope "/api", BooklistxWeb do&lt;/span&gt;
  &lt;span class="c1"&gt;#   pipe_through :api&lt;/span&gt;
  &lt;span class="c1"&gt;# end&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I ran the command to start the app:&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="nv"&gt;$ &lt;/span&gt;mix phx.server
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fl0rcq4qyrh5qzkiv9kum.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%2Fl0rcq4qyrh5qzkiv9kum.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It was ready, I could already add and remove books. That's when, after creating a book, I saw the flash message appear.&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%2F45hm9a12uu73qsxtiuje.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%2F45hm9a12uu73qsxtiuje.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I used the browser inspector to see the html.&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%2Fh1sfhszj7o7fo9racqj4.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%2Fh1sfhszj7o7fo9racqj4.png" alt="Alt Text"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I noticed the html always came with the flash message tags:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-info"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Book updated successfully.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-danger"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's just a simple css trick to not show anything when the tag is empty:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* assets/css/phoenix.css */&lt;/span&gt;

&lt;span class="nc"&gt;.alert&lt;/span&gt;&lt;span class="nd"&gt;:empty&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;display&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;none&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;By default the file comes like this, loading the alert tags even when there's no flash message:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight eex"&gt;&lt;code&gt;# lib/booklistx_web/layout/app.html.exx

&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Booklistx · Phoenix Framework&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="no"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/css/app.css"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;csrf_meta_tag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"navigation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://hexdocs.pm/phoenix/overview.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Get Started&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://phoenixframework.org/"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"phx-logo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="no"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/images/phoenix.png"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Phoenix Framework Logo"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
#-&amp;gt;   &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-info"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;get_flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
#-&amp;gt;   &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-danger"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;get_flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;

      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="nv"&gt;@view_module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@view_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assigns&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="no"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/js/app.js"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That bothered me. I researched how it works and found an issue suggesting this approach:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight eex"&gt;&lt;code&gt;# lib/booklistx_web/layout/app.html.exx
...
&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:info&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-info"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;info&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;get_flash&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-danger"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;error&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;&amp;lt;%&lt;/span&gt; &lt;span class="k"&gt;end&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now it only shows when there's a flash message. But those variables in the middle of the code (&lt;code&gt;info&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt;) didn't look great.&lt;/p&gt;

&lt;p&gt;I decided to do something similar to what I've done in Rails.&lt;/p&gt;

&lt;p&gt;There must be many other ways to solve this, probably better, but this was the one I liked the most because it's simple and uses the concepts I've been studying.&lt;/p&gt;

&lt;p&gt;I created the following files:&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="c"&gt;# Create shared_view file&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;lib/booklistx_web/shared_view.ex
&lt;span class="c"&gt;# Create shared folder&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;mkdir &lt;/span&gt;lib/booklistx_web/templates/shared
&lt;span class="c"&gt;# Create _flash_message.html.exx file&lt;/span&gt;
&lt;span class="nv"&gt;$ &lt;/span&gt;&lt;span class="nb"&gt;touch &lt;/span&gt;lib/booklistx_web/templates/shared/_flash_message.html.eex
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight elixir"&gt;&lt;code&gt;&lt;span class="c1"&gt;# lib/booklistx_web/shared_view.ex&lt;/span&gt;

&lt;span class="k"&gt;defmodule&lt;/span&gt; &lt;span class="no"&gt;BooklistxWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;SharedView&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
  &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="no"&gt;BooklistxWeb&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;:view&lt;/span&gt;
  &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="no"&gt;BooklistxWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Router&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;Helpers&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;show_flash_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;conn&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;get_flash&lt;/span&gt;
    &lt;span class="o"&gt;|&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;flash_message&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;flash_message&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="s2"&gt;"info"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"_flash_message.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class:&lt;/span&gt; &lt;span class="s2"&gt;"primary"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;message:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;flash_message&lt;/span&gt;&lt;span class="p"&gt;(%{&lt;/span&gt;&lt;span class="s2"&gt;"error"&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;
    &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="s2"&gt;"_flash_message.html"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;class:&lt;/span&gt; &lt;span class="s2"&gt;"danger"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="ss"&gt;message:&lt;/span&gt; &lt;span class="n"&gt;message&lt;/span&gt;
  &lt;span class="k"&gt;end&lt;/span&gt;

  &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="n"&gt;flash_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="k"&gt;do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;nil&lt;/span&gt;
&lt;span class="k"&gt;end&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here I'm using things I've learned like pipe and pipeline in the &lt;code&gt;show_flash_message&lt;/code&gt; method, and pattern matching in &lt;code&gt;flash_message&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The partial ended up like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight eex"&gt;&lt;code&gt;# lib/booklistx_web/templates/shared/_flash_message.html.eex

&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nv"&gt;@class&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"alert"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="nv"&gt;@message&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And the layout ended up like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight eex"&gt;&lt;code&gt;# lib/booklistx_web/layout/app.html.exx

&lt;span class="cp"&gt;&amp;lt;!DOCTYPE html&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"en"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;charset=&lt;/span&gt;&lt;span class="s"&gt;"utf-8"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;http-equiv=&lt;/span&gt;&lt;span class="s"&gt;"X-UA-Compatible"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"IE=edge"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;meta&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"viewport"&lt;/span&gt; &lt;span class="na"&gt;content=&lt;/span&gt;&lt;span class="s"&gt;"width=device-width, initial-scale=1.0"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;title&amp;gt;&lt;/span&gt;Booklistx · Phoenix Framework&lt;span class="nt"&gt;&amp;lt;/title&amp;gt;&lt;/span&gt;

    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"stylesheet"&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="no"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/css/app.css"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;csrf_meta_tag&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;

  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;nav&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"navigation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;ul&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://hexdocs.pm/phoenix/overview.html"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Get Started&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/nav&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://phoenixframework.org/"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"phx-logo"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="no"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/images/phoenix.png"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt; &lt;span class="na"&gt;alt=&lt;/span&gt;&lt;span class="s"&gt;"Phoenix Framework Logo"&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"main"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"container"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="no"&gt;BooklistxWeb&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="no"&gt;SharedView&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;show_flash_message&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;

      &lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="n"&gt;render&lt;/span&gt; &lt;span class="nv"&gt;@view_module&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;@view_template&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;assigns&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="cp"&gt;&amp;lt;%=&lt;/span&gt; &lt;span class="no"&gt;Routes&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;static_path&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;@conn&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s2"&gt;"/js/app.js"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;%&amp;gt;&lt;/span&gt;&lt;span class="s"&gt;"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Conclusion
&lt;/h3&gt;

&lt;p&gt;In my view, this turned out much better than using the variables (&lt;code&gt;info&lt;/code&gt; and &lt;code&gt;error&lt;/code&gt;) and those IFs directly in the layout. There must be better solutions, but this was the one I came up with and liked the most. I got to put in practice some things I've been learning like pipe, pipeline and pattern matching.&lt;/p&gt;

&lt;p&gt;I'll leave the link to the code I made on github:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/guilhermeyo/booklistx" rel="noopener noreferrer"&gt;https://github.com/guilhermeyo/booklistx&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Feel free to leave feedback and improvements I could make.&lt;/p&gt;




&lt;h3&gt;
  
  
  References
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://elixircasts.io/partial-templates-with-phoenix" rel="noopener noreferrer"&gt;#23: Partial Templates with Phoenix&lt;/a&gt;&lt;br&gt;
&lt;a href="https://elixirforum.com/t/check-for-error-and-info-alert-in-phoenix/1984/" rel="noopener noreferrer"&gt;Elixir forum - Check for error and info alert in Phoenix&lt;/a&gt;&lt;br&gt;
&lt;a href="https://github.com/phoenixframework/phoenix/issues/1757" rel="noopener noreferrer"&gt;Issue phoenixframework - Add has_flash? functions. #1757&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Originally posted at &lt;a href="https://guilherme44.com/en/blog/organizing-flash-messages-in-phoenix/" rel="noopener noreferrer"&gt;guilherme44.com&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>phoenix</category>
      <category>elixir</category>
      <category>beginners</category>
    </item>
  </channel>
</rss>
