<?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: Tulio Calil</title>
    <description>The latest articles on DEV Community by Tulio Calil (@tuliocalil).</description>
    <link>https://dev.to/tuliocalil</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%2F520716%2F4f343a59-cabf-47e5-ac86-4cca5cd1e5b5.png</url>
      <title>DEV Community: Tulio Calil</title>
      <link>https://dev.to/tuliocalil</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/tuliocalil"/>
    <language>en</language>
    <item>
      <title>Desmistificando Concorrência e Consistência em Sistemas Distribuídos</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Tue, 07 Apr 2026 01:20:28 +0000</pubDate>
      <link>https://dev.to/tuliocalil/desmistificando-concorrencia-e-consistencia-em-sistemas-distribuidos-58oh</link>
      <guid>https://dev.to/tuliocalil/desmistificando-concorrencia-e-consistencia-em-sistemas-distribuidos-58oh</guid>
      <description>&lt;p&gt;Recentemente, como parte do meu mestrado em Computação Aplicada na UTFPR, mergulhei num artigo chamado &lt;a href="https://www.researchgate.net/publication/355745242_Comprehending_Concurrency_and_Consistency_in_Distributed_Systems" rel="noopener noreferrer"&gt;"Comprehending Concurrency and Consistency in Distributed Systems" (Nitin Naik, IEEE ISSE 2021)&lt;/a&gt;. A discussão foi bem legal e, como já tem um certo tempo que não posto nada, decidi trazer isso para cá. Vamos falar um pouco sobre concorrência e consistência e ver alguns exemplos aplicados ao mundo real.&lt;/p&gt;

&lt;h2&gt;
  
  
  Concorrência vs. Paralelismo (Não, não são a mesma coisa)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpgtkmz6ru48dhqulzn2h.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%2Fpgtkmz6ru48dhqulzn2h.png" alt="Concorrencia VS Paralelismo" width="543" height="721"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Concorrência&lt;/strong&gt;: É sobre lidar com várias coisas ao mesmo tempo. As tarefas se sobrepõem no tempo (estão em andamento), mas não rodam necessariamente no exato mesmo milissegundo. O objetivo aqui é esconder a latência.&lt;/li&gt;
&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Paralelismo&lt;/strong&gt;: É sobre fazer várias coisas no exato mesmo milissegundo. O objetivo é aumentar o throughput computacional, e isso exige hardware (múltiplos núcleos).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;De uma forma mais simples: concorrência é iniciar várias coisas, mas ir fazendo um pouco de cada. Quando a CPU fica ociosa esperando algo (como uma requisição de rede), eu troco o contexto e vou para outra tarefa, fazendo isso o tempo todo. Imagina você tentando fazer o almoço e faxinar a casa: você coloca uma panela com água para ferver; enquanto ela ferve, você pega a vassoura e começa a limpar; quando a água estiver fervendo, você para de varrer e coloca o macarrão, e assim por diante.&lt;/p&gt;

&lt;p&gt;Já o paralelismo depende necessariamente de hardware. Aqui, nós vamos pegar todo o sistema (ou partes dele) e executar em outro core da CPU. Com isso, conseguimos executar as coisas no mesmo milissegundo. Voltando à analogia das tarefas domésticas, aqui é como se você tivesse um clone seu (ou outra pessoa) para te ajudar: você começa a fazer o almoço, e, no exato mesmo instante que você começou a cozinhar, a outra pessoa começa a varrer a casa.&lt;/p&gt;

&lt;p&gt;Vamos para um exemplo prático disso com Node.js. Vou criar uma função com um loop que gira 2 bilhões de vezes. Em cada volta, ela pega a variável sum e soma com o valor de &lt;code&gt;i&lt;/code&gt;. A ideia é apenas criar uma função que gere uma alta carga de processamento na CPU (uma tarefa CPU-bound).&lt;/p&gt;

&lt;p&gt;Vamos ao primeiro código. Vamos rodar isso de forma sequencial:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ITERATIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="nx"&gt;_000_000_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;heavyCPUTask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;ITERATIONS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sequentially (One after the other)...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sequential Time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nf"&gt;heavyCPUTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 
&lt;span class="nf"&gt;heavyCPUTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; 

&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Sequential Time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rodando isso, temos: &lt;code&gt;Sequential Time: 3.250s&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Agora, vamos tentar fazer isso usando concorrência:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ITERATIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="nx"&gt;_000_000_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;heavyCPUTask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;ITERATIONS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startTest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Concurrently (Promises on the same Thread)...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Concurrent Time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;heavyCPUTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt;
        &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nf"&gt;heavyCPUTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="nf"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Concurrent Time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;startTest&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sim, as Promises são a forma padrão de rodar código em concorrência no JS. Para esse código, temos o seguinte resultado: &lt;code&gt;Concurrent Time: 3.191s&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Podemos perceber que o ganho de tempo aqui foi quase nulo em comparação ao sequencial, mas por quê? Lembra que eu falei que a gente "mascara a latência" quando a CPU fica ociosa? Então, nessa função a CPU está fritando fazendo cálculos. Não há pausas. O Event Loop (a thread principal do Node) fica totalmente bloqueado. O único ganho real aqui foi disparar as tarefas de uma vez.&lt;/p&gt;

&lt;p&gt;Caso essa função fosse algo como uma requisição HTTP ou um acesso a um banco de dados (onde o Node apenas "espera" a resposta), teríamos uma diferença absurda de tempo entre o sequencial e a concorrência.&lt;/p&gt;

&lt;p&gt;O Node.js, por padrão, trabalha em uma única thread, mas ele tem total suporte para mudarmos isso. Vamos usar o módulo nativo &lt;a href="https://nodejs.org/api/worker_threads.html" rel="noopener noreferrer"&gt;Worker Threads&lt;/a&gt;, para atingir o paralelismo real:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isMainThread&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;parentPort&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;worker_threads&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;ITERATIONS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="nx"&gt;_000_000_000&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;heavyCPUTask&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="nx"&gt;ITERATIONS&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="nx"&gt;i&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="nx"&gt;sum&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;i&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; 
    &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;sum&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;


&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="nx"&gt;isMainThread&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;result&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;heavyCPUTask&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nx"&gt;parentPort&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;result&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;runInWorker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;worker&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Worker&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;__filename&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
            &lt;span class="nx"&gt;worker&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;resolve&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;startTest&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Parallel (Multi-Core)...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;time&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Parallel Time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="nf"&gt;runInWorker&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="nf"&gt;runInWorker&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeEnd&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Parallel Time&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nf"&gt;startTest&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;O código ficou um pouco mais complexo, mas agora conseguimos ter um processo principal (main) que pede ao sistema operacional para criar um novo Worker para cada tarefa (nesse caso, duas), alocando-as em núcleos físicos diferentes do processador.&lt;/p&gt;

&lt;p&gt;Com isso, temos o seguinte resultado: &lt;code&gt;Parallel Time: 1.661s&lt;/code&gt;. &lt;br&gt;
Uma quebra de tempo absurda em comparação com as outras duas abordagens.&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%2Fn2zcncisdotit4d8uj9q.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%2Fn2zcncisdotit4d8uj9q.png" alt="Resultado concorrencia vs paralelismo" width="465" height="216"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  O Pesadelo da Consistência
&lt;/h2&gt;

&lt;p&gt;Se a concorrência é gerenciar o tempo da CPU, a consistência é gerenciar a "verdade" dos dados entre várias máquinas. E manter todo mundo concordando custa caro e é um trabalho árduo.&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%2Fr3bmeufvb5d5aa5cikzv.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%2Fr3bmeufvb5d5aa5cikzv.png" alt="consistencia" width="559" height="828"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine um banco de dados distribuído entre vários nós. Sempre que recebemos uma nova alteração (como o a=10 na imagem acima), precisamos replicar esse novo valor para os demais nós da rede.&lt;/p&gt;

&lt;p&gt;Isso cria um grande dilema arquitetural: enquanto essa replicação acontece pela rede, o que fazemos se uma nova consulta bater em um nó que ainda está desatualizado? Nós liberamos o acesso a esse dado velho ou travamos a leitura até que tudo esteja sincronizado?&lt;/p&gt;

&lt;p&gt;Pensando nisso, o autor do artigo discute duas visões principais de consistência:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistência Forte&lt;/strong&gt;: A atualização trava as consultas até que todos os nós do sistema estejam com o dado idêntico. O foco é a exatidão. Exemplo: O sistema bancário via PIX. Ninguém quer ver um saldo desatualizado. O sistema prefere ficar mais lento (latência maior) do que entregar um dado errado.&lt;/li&gt;
&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistência Fraca / Eventual&lt;/strong&gt;: A atualização é aceita, o sistema devolve o "OK" na hora e sincroniza o resto dos servidores no background. O foco é a alta performance, assumindo o risco de o sistema retornar um dado desatualizado (stale data) temporariamente. Exemplo: As visualizações do YouTube. Se você ver "10.000 views" no servidor do Nordeste e seu amigo em São Paulo ver "9.500" no mesmo segundo, não tem problema ler um dado desatualizado (stale data). Eventualmente, os dois servidores se alinham.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O Foco da Consistência
&lt;/h2&gt;

&lt;p&gt;Além de pensarmos no tempo (quando a sincronização acontece), o artigo traz uma outra pergunta para a arquitetura: o escopo. De quem é a perspectiva de consistência que realmente importa no nosso sistema?&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%2F9h02gefpsl16mhviuhwl.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%2F9h02gefpsl16mhviuhwl.png" alt="consistencia" width="581" height="253"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Para responder a isso, a arquitetura se divide em duas categorias:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistência Centrada em Dados&lt;/strong&gt; (System-Wide): Aqui, o foco é o banco de dados e o sistema como um todo. A regra é: qualquer cliente, de qualquer lugar do mundo, que acessar o sistema precisa enxergar as operações acontecendo exatamente na mesma ordem. O problema disso é que como a atualização precisa ser garantida em todas as réplicas do sistema, a sobrecarga (overhead) operacional e de rede é altíssima. É a escolha ideal para sistemas com alta concorrência de escritas. Pense no Google Docs. Se você e um colega digitam no mesmo parágrafo ao mesmo tempo, o sistema precisa ordenar essas operações e forçar que a tela de todos os 10 usuários conectados no documento mostre as letras aparecendo na exata mesma ordem no mesmo milissegundo. O mundo inteiro enxerga a mesma "fonte da verdade".&lt;/li&gt;
&lt;/ul&gt;




&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Consistência Centrada no Cliente&lt;/strong&gt; (Client-Specific): Aqui, nós tiramos o peso das costas do sistema global e focamos na sessão individual do usuário. O sistema garante a consistência apenas para a pessoa que acabou de realizar a ação. Diferentes clientes podem ver a ordem das atualizações de formas diferentes temporariamente. Claramente isso reduz drasticamente a sobrecarga dos servidores, pois o sistema não precisa coordenar uma atualização global imediata. É perfeito para sistemas de leitura massiva. Um exemplo é a foto de perfil do WhatsApp, se você atualiza a sua foto, o aplicativo se comunica com o servidor e garante que a sua tela exiba a foto nova imediatamente. Para você (o cliente), a consistência é perfeita. Mas o sistema não força essa replicação imediata no celular dos seus amigos. Eles podem continuar vendo sua foto antiga por mais algum tempo. A arquitetura prioriza a experiência do cliente ativo sem "fritar" a rede global.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O Preço da Perfeição
&lt;/h2&gt;

&lt;p&gt;O autor deixa uma coisa muito clara: NÃO EXISTE BALA DE PRATA. Nem todo sistema distribuído precisa/deve ter um nível perfeito de concorrência e consistência forte. O custo disso é inviável na maioria dos projetos.&lt;/p&gt;

&lt;p&gt;Considerando o custo absurdo da consistência forte em sistemas distribuídos sob alta carga, fica a provocação que encerrou meu seminário no mestrado:&lt;/p&gt;

&lt;p&gt;Até que ponto nós, como engenheiros, podemos sacrificar a exatidão e entregar dados temporariamente desatualizados (stale data) em nome da performance, sem quebrar a confiança do usuário final no produto?&lt;/p&gt;

&lt;p&gt;E você, no seu projeto atual, está pagando o preço da consistência forte ou abraçou o caos controlado da consistência eventual?&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>distributedsystems</category>
    </item>
    <item>
      <title>Elixir for IoT: Why It Feels Like the Future</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Tue, 02 Dec 2025 23:19:37 +0000</pubDate>
      <link>https://dev.to/tuliocalil/elixir-for-iot-why-it-feels-like-the-future-5cbd</link>
      <guid>https://dev.to/tuliocalil/elixir-for-iot-why-it-feels-like-the-future-5cbd</guid>
      <description>&lt;p&gt;Most IoT projects today lean heavily on Python, C, or Node.js, and that’s fine. But during my recent academic paper selection process, I came across &lt;a href="https://dl.acm.org/doi/10.1145/3677995.3678197" rel="noopener noreferrer"&gt;“The Benefits of Tierless Elixir/Potato for Engineering IoT Systems”&lt;/a&gt;, and it completely shifted how I think about building IoT architectures.&lt;/p&gt;

&lt;p&gt;The paper raised a question that stuck with me:&lt;br&gt;
why do we keep separating the logic, runtime and UI layers in IoT systems if functional, tierless architectures can unify everything?&lt;/p&gt;

&lt;p&gt;That curiosity, combined with the influence of a professor(&lt;a href="https://www.linkedin.com/in/adolfont/" rel="noopener noreferrer"&gt;Adolfo Neto&lt;/a&gt;) who strongly advocates for functional programming and the BEAM, pushed me to test this idea in practice.&lt;/p&gt;

&lt;p&gt;So I built a complete IoT prototype using Elixir, Circuits, Raspberry Pi, and Phoenix LiveView. And honestly? It felt like IoT the way it should be: supervised, fault-tolerant, reactive, and consistent from the edge to the dashboard.&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%2F02rcjwupk8ditkf5i2y7.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%2F02rcjwupk8ditkf5i2y7.jpg" alt="Dark-themed Phoenix LiveView dashboard showing real-time sensor readings for temperature (29.2°C), light level (62.9%), and button status."&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  The Prototype: What I Actually Built
&lt;/h2&gt;

&lt;p&gt;My goal was simple:&lt;/p&gt;

&lt;p&gt;Build an IoT system end-to-end using only Elixir, from the hardware to the backend.&lt;/p&gt;

&lt;p&gt;That resulted in a two-part monorepo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A Raspberry Pi running Elixir (no Nerves this time, just Elixir Circuits)&lt;/li&gt;
&lt;li&gt;A Phoenix LiveView dashboard receiving sensor data in real time&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The device communicates via Phoenix Channels/WebSockets, sending temperature, light level, and button events, while receiving commands to control a buzzer.&lt;/p&gt;
&lt;h2&gt;
  
  
  Architecture Overview
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fowsna5ai0byalkbgzdt4.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%2Fowsna5ai0byalkbgzdt4.png" alt="Architecture diagram illustrating data flow: hardware sensors on Raspberry Pi pass data to GenServers, then via WebSockets to the Phoenix Server, PubSub, and finally the LiveView dashboard process."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If a sensor process fails: only that process restarts.&lt;br&gt;
If the network blips: the BEAM keeps the system stable.&lt;br&gt;
If the dashboard disconnects: LiveView reconnects automatically.&lt;/p&gt;

&lt;p&gt;This is where the “future feeling” starts to show.&lt;/p&gt;
&lt;h2&gt;
  
  
  Circuit Setup (Everything on the Pi4)
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ftvfhukzi46y7414f52dh.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%2Ftvfhukzi46y7414f52dh.png" alt="Wiring diagram showing the Raspberry Pi 4 GPIO connected to a breadboard containing a DS18B20 temperature sensor, LDR photoresistor, push button, and active buzzer circuit."&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Hardware used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DS18B20 (1-Wire temperature sensor)&lt;/li&gt;
&lt;li&gt;LDR + Capacitor (for RC timing)&lt;/li&gt;
&lt;li&gt;Button (pull-up GPIO)&lt;/li&gt;
&lt;li&gt;Active buzzer + transistor&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The LDR timing loop runs in a separate process so it doesn’t block WebSocket communication, a natural fit for the actor model.&lt;/p&gt;
&lt;h2&gt;
  
  
  Why Elixir Feels Like the Future of IoT
&lt;/h2&gt;

&lt;p&gt;After building this, a few conclusions became obvious:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The BEAM VM solves problems IoT devs fight daily&lt;/li&gt;
&lt;li&gt;Concurrency is natural, not bolted-on&lt;/li&gt;
&lt;li&gt;Fault tolerance is built-in&lt;/li&gt;
&lt;li&gt;Real-time dashboards require zero JS&lt;/li&gt;
&lt;li&gt;A single language from hardware to UI reduces mental load&lt;/li&gt;
&lt;li&gt;Tierless architectures aren’t just academic, they’re practical&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This cohesiveness made the whole experience feel weirdly futuristic.&lt;/p&gt;

&lt;p&gt;Not flashy.&lt;br&gt;
Not hyped.&lt;br&gt;
Just… right.&lt;/p&gt;
&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Implementing a Nerves firmware version&lt;/li&gt;
&lt;li&gt;Adding more sensors&lt;/li&gt;
&lt;li&gt;Testing distributed BEAM cluster of Pis&lt;/li&gt;
&lt;li&gt;Applying Potato tierless concepts end-to-end&lt;/li&gt;
&lt;li&gt;Publishing a deeper academic report&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Elixir isn't mainstream in IoT, but maybe it should be.&lt;/p&gt;

&lt;p&gt;This small prototype convinced me that a unified, functional, message-driven approach has the potential to dramatically simplify embedded systems.&lt;/p&gt;

&lt;p&gt;If you're curious about IoT, the BEAM, or tierless architectures, give Elixir a try.&lt;br&gt;
It might surprise you the same way it surprised me.&lt;/p&gt;
&lt;h2&gt;
  
  
  Try It Yourself
&lt;/h2&gt;

&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/tuliocll" rel="noopener noreferrer"&gt;
        tuliocll
      &lt;/a&gt; / &lt;a href="https://github.com/tuliocll/elixir-iot-sample" rel="noopener noreferrer"&gt;
        elixir-iot-sample
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      A complete IoT example using Elixir, Phoenix LiveView, and Raspberry Pi.
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Elixir IoT Example 🚀&lt;/h1&gt;
&lt;/div&gt;
  &lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/tuliocll/elixir-iot-sample/doc/dash.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftuliocll%2Felixir-iot-sample%2Fdoc%2Fdash.png" width="600"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/9750317aa286f687191191e05113f8e86093bd79ac30c28b86a3454b44398217/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f456c697869722d3645344137453f7374796c653d666f722d7468652d6261646765266c6f676f3d656c69786972266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/9750317aa286f687191191e05113f8e86093bd79ac30c28b86a3454b44398217/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f456c697869722d3645344137453f7374796c653d666f722d7468652d6261646765266c6f676f3d656c69786972266c6f676f436f6c6f723d7768697465" alt="Elixir"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/e91818c9c82c91366faba8e75acc0f103c4ffce8bb2f38311f8bd7ebb5d58615/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52617370626572727925323050692d4335314134413f7374796c653d666f722d7468652d6261646765266c6f676f3d7261737062657272797069266c6f676f436f6c6f723d7768697465"&gt;&lt;img src="https://camo.githubusercontent.com/e91818c9c82c91366faba8e75acc0f103c4ffce8bb2f38311f8bd7ebb5d58615/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f52617370626572727925323050692d4335314134413f7374796c653d666f722d7468652d6261646765266c6f676f3d7261737062657272797069266c6f676f436f6c6f723d7768697465" alt="Raspberry Pi"&gt;&lt;/a&gt;
&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/5889867f34efe6d84c0b61dbf4cf30a91481433cae470ef1214ee8b4c4945d90/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f50686f656e69782532304c697665566965772d4632363732323f7374796c653d666f722d7468652d6261646765"&gt;&lt;img src="https://camo.githubusercontent.com/5889867f34efe6d84c0b61dbf4cf30a91481433cae470ef1214ee8b4c4945d90/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f50686f656e69782532304c697665566965772d4632363732323f7374796c653d666f722d7468652d6261646765" alt="LiveView"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Elixir IoT Example&lt;/strong&gt; is a monorepo demonstrating how to connect a Raspberry Pi to a real-time Phoenix LiveView dashboard using &lt;strong&gt;Phoenix Channels&lt;/strong&gt;, &lt;strong&gt;WebSockets&lt;/strong&gt;, and &lt;strong&gt;Elixir Circuits&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;It includes:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;🌡️ Real-time temperature readings (DS18B20)&lt;/li&gt;
&lt;li&gt;💡 Light level measurement using RC timing (LDR)&lt;/li&gt;
&lt;li&gt;🔘 Physical button input&lt;/li&gt;
&lt;li&gt;🔔 Remote buzzer control through WS commands&lt;/li&gt;
&lt;li&gt;📊 Web dashboard with auto-updating LiveView&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;This project serves as a practical, didactic example of building IoT systems entirely in &lt;strong&gt;Elixir&lt;/strong&gt;.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h4 class="heading-element"&gt;Requirements&lt;/h4&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;Elixir &lt;strong&gt;1.17+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Erlang/OTP &lt;strong&gt;26+&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;Node.js &lt;strong&gt;16+&lt;/strong&gt; (for assets)&lt;/li&gt;
&lt;li&gt;Raspberry Pi 4 / 3 (tested with Pi4)&lt;/li&gt;
&lt;li&gt;Erlang + Elixir installed in the Rasp&lt;/li&gt;
&lt;li&gt;Enable 1-Wire in Rasp (See &lt;a href="https://github.com/tuliocll/elixir-iot-sample/doc/1-wire.md" rel="noopener noreferrer"&gt;doc/1-wire.md&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;🏗️ Architecture and Circuit&lt;/h2&gt;

&lt;/div&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/tuliocll/elixir-iot-sample/doc/arch.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftuliocll%2Felixir-iot-sample%2Fdoc%2Farch.png" alt="Architecture"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/tuliocll/elixir-iot-sample/doc/circuit.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fgithub.com%2Ftuliocll%2Felixir-iot-sample%2Fdoc%2Fcircuit.png" alt="Circuit"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Running server&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c1"&gt;cd&lt;/span&gt; server
mix deps.get
mix phx.server&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;Open:&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;http://localhost:4000/dashboard
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📡 Raspberry Pi (Elixir Circuits)&lt;/h2&gt;

&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;Running&lt;/h3&gt;

&lt;/div&gt;
&lt;div class="highlight highlight-source-shell notranslate position-relative overflow-auto js-code-highlight"&gt;
&lt;pre&gt;&lt;span class="pl-c"&gt;&lt;span class="pl-c"&gt;#&lt;/span&gt; Clone the repo on the rasp and:&lt;/span&gt;
&lt;span class="pl-c1"&gt;cd&lt;/span&gt; rasp
mix deps.get
iex -S mix&lt;/pre&gt;

&lt;/div&gt;
&lt;p&gt;The device will:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;…&lt;/li&gt;&lt;/ul&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/tuliocll/elixir-iot-sample" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;




</description>
      <category>elixir</category>
      <category>iot</category>
      <category>raspberrypi</category>
    </item>
    <item>
      <title>Quick Tip: Fix Android Emulator DNS Issues in Arch/Linux</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Tue, 14 Oct 2025 14:02:40 +0000</pubDate>
      <link>https://dev.to/tuliocalil/quick-tip-fix-android-emulator-dns-issues-in-archlinux-gck</link>
      <guid>https://dev.to/tuliocalil/quick-tip-fix-android-emulator-dns-issues-in-archlinux-gck</guid>
      <description>&lt;p&gt;If your Android emulator can't resolve domains or loses internet connection, try this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;# Open this file:
code ~/.android/avd/&amp;lt;your_avd_name&amp;gt;.avd/user-settings.ini
# or
code ~/.config/.android/avd/&amp;lt;your_avd_name&amp;gt;.avd/user-settings.ini

# Add this line:
commandLineOptions=-dns-server 8.8.8.8
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then restart the emulator (preferably with a cold boot).&lt;/p&gt;

&lt;p&gt;This forces the AVD to use Google’s DNS, works like a charm when local DNS is messy.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Originally found this tip on &lt;a href="https://stackoverflow.com/a/79080407" rel="noopener noreferrer"&gt;Stack Overflow while troubleshooting DNS issues&lt;/a&gt; in the Android emulator.&lt;/p&gt;
&lt;/blockquote&gt;

</description>
      <category>linux</category>
      <category>archlinux</category>
      <category>android</category>
      <category>reactnative</category>
    </item>
    <item>
      <title>[Boost]</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Fri, 10 Oct 2025 12:22:39 +0000</pubDate>
      <link>https://dev.to/tuliocalil/-238d</link>
      <guid>https://dev.to/tuliocalil/-238d</guid>
      <description>&lt;p&gt;

&lt;/p&gt;
&lt;div class="ltag__link--embedded"&gt;
  &lt;div class="crayons-story "&gt;
  &lt;a href="https://dev.to/tuliocalil/react-conf-2025-resumo-10-anos-4m-downloads-semanais-e-muita-coisa-nova-362h" class="crayons-story__hidden-navigation-link"&gt;React Conf 2025: 10 anos de React Native, 4M downloads semanais e muita coisa nova no RN&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="/tuliocalil" 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%2F520716%2F4f343a59-cabf-47e5-ac86-4cca5cd1e5b5.png" alt="tuliocalil profile" class="crayons-avatar__image"&gt;
          &lt;/a&gt;
        &lt;/div&gt;
        &lt;div&gt;
          &lt;div&gt;
            &lt;a href="/tuliocalil" class="crayons-story__secondary fw-medium m:hidden"&gt;
              Tulio Calil
            &lt;/a&gt;
            &lt;div class="profile-preview-card relative mb-4 s:mb-0 fw-medium hidden m:inline-block"&gt;
              
                Tulio Calil
                
              
              &lt;div id="story-author-preview-content-2908546" 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="/tuliocalil" 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%2F520716%2F4f343a59-cabf-47e5-ac86-4cca5cd1e5b5.png" class="crayons-avatar__image" alt=""&gt;
                      &lt;/span&gt;
                      &lt;span class="crayons-link crayons-subtitle-2 mt-5"&gt;Tulio Calil&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/tuliocalil/react-conf-2025-resumo-10-anos-4m-downloads-semanais-e-muita-coisa-nova-362h" class="crayons-story__tertiary fs-xs"&gt;&lt;time&gt;Oct 9 '25&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/tuliocalil/react-conf-2025-resumo-10-anos-4m-downloads-semanais-e-muita-coisa-nova-362h" id="article-link-2908546"&gt;
          React Conf 2025: 10 anos de React Native, 4M downloads semanais e muita coisa nova no RN
        &lt;/a&gt;
      &lt;/h2&gt;
        &lt;div class="crayons-story__tags"&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/react"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;react&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/reactnative"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;reactnative&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/javascript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;javascript&lt;/a&gt;
            &lt;a class="crayons-tag  crayons-tag--monochrome " href="/t/typescript"&gt;&lt;span class="crayons-tag__prefix"&gt;#&lt;/span&gt;typescript&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/tuliocalil/react-conf-2025-resumo-10-anos-4m-downloads-semanais-e-muita-coisa-nova-362h#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;
            3 min read
          &lt;/small&gt;
            
              &lt;span class="bm-initial"&gt;
                

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

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

&lt;/div&gt;




</description>
      <category>react</category>
      <category>reactnative</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>React Conf 2025: 10 anos de React Native, 4M downloads semanais e muita coisa nova no RN</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Thu, 09 Oct 2025 13:34:51 +0000</pubDate>
      <link>https://dev.to/tuliocalil/react-conf-2025-resumo-10-anos-4m-downloads-semanais-e-muita-coisa-nova-362h</link>
      <guid>https://dev.to/tuliocalil/react-conf-2025-resumo-10-anos-4m-downloads-semanais-e-muita-coisa-nova-362h</guid>
      <description>&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%2Flzh2enuuikqlt2g8etxd.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%2Flzh2enuuikqlt2g8etxd.png" alt="React Conf" width="583" height="472"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O React Native chegou aos 10 anos de vida, e a React Conf 2025 mostrou que o framework está longe de desacelerar.&lt;br&gt;
De um projeto nascido em um hackathon interno do Facebook a uma das principais ferramentas para desenvolvimento mobile do mundo, o RN agora marca uma nova era, mais madura, aberta e integrada com o ecossistema web.&lt;/p&gt;

&lt;p&gt;Durante os dias 7 e 8 de outubro, a conferência apresentou uma série de novidades que consolidam essa transição:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;a Nova Arquitetura se torna padrão e obrigatória;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;o novo Hermes V1 traz ganhos significativos de performance;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;DOM APIs e Web Performance APIs aproximam o React Native ainda mais do React web;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;e o anúncio da React Foundation inaugura uma nova fase de governança e colaboração na comunidade.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Com mais de 4 milhões de downloads semanais no npm e 6 lançamentos anuais, o React Native celebra sua primeira década evoluindo no ritmo de um ecossistema que não para de inovar.&lt;/p&gt;




&lt;h2&gt;
  
  
  React Foundation
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpdhwwnpgbqhkjpu9eusf.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%2Fpdhwwnpgbqhkjpu9eusf.png" alt="React Foundation" width="800" height="230"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A React Foundation foi anunciada como uma nova estrutura de governança independente da Meta.&lt;br&gt;
O objetivo é abrir ainda mais o ecossistema React e React Native para contribuições externas, fortalecendo a comunidade e garantindo mais transparência e colaboração, um marco semelhante ao que já vimos com Node.js e OpenJS Foundation.&lt;/p&gt;




&lt;h2&gt;
  
  
  Nova Arquitetura como padrão
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F6i7y6nmgn0sorx6z7zn3.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%2F6i7y6nmgn0sorx6z7zn3.png" alt="Nova arquitetura" width="800" height="451"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A partir da versão 0.82, a Nova Arquitetura se torna a única suportada no React Native.&lt;br&gt;
Isso significa apps menores, builds mais rápidas e um modelo mental mais simples.&lt;br&gt;
A Meta recomenda migrar a partir da 0.81 para aproveitar os ganhos de performance, o Shopify já relatou melhorias significativas no tempo de inicialização e renderização.&lt;/p&gt;




&lt;h2&gt;
  
  
  Hermes V1
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1zf2erlvl98n0lgz8wxr.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%2F1zf2erlvl98n0lgz8wxr.png" alt="Hermes V1" width="800" height="258"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O novo Hermes V1 chegou com média de +60% de melhoria em performance nos benchmarks.&lt;br&gt;
Além de suporte a recursos modernos de JavaScript, ele está disponível de forma experimental na 0.82 e será o motor padrão nas próximas versões.&lt;/p&gt;




&lt;h2&gt;
  
  
  DOM APIs e Web Performance APIs
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjkd0xgfc5zqywb0f1awz.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%2Fjkd0xgfc5zqywb0f1awz.png" alt="DOM APIs" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O React Native está cada vez mais próximo do React Web.&lt;br&gt;
Com a 0.82, componentes nativos agora ganham DOM-like refs, permitindo usar métodos como getBoundingClientRect() e childNodes.&lt;br&gt;
Também chegam novas Web Performance APIs, que trazem métricas avançadas e integração nativa no DevTools, incluindo painéis de Performance e Network previstos para a 0.83.&lt;/p&gt;




&lt;h2&gt;
  
  
  Native CSS e Tailwind no Expo
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F0g4atrofaobms700kcfl.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%2F0g4atrofaobms700kcfl.png" alt="CSS e Expo" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Durante a conferência, Evan Bacon apresentou uma das demos mais comentadas: suporte nativo a CSS e Tailwind no Expo, sem precisar de pacotes adicionais.&lt;br&gt;
O recurso deve simplificar o estilo de componentes e aumentar a velocidade de prototipagem para apps React Native.&lt;/p&gt;




&lt;h2&gt;
  
  
  VirtualView e novas APIs experimentais
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgug8rmt9h465srs5o1at.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%2Fgug8rmt9h465srs5o1at.png" alt="VirtualView" width="800" height="684"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A nova API VirtualView promete otimizar o uso de memória dentro de ScrollView, reduzindo o footprint quando o componente não está visível.&lt;br&gt;
Outros recursos experimentais também foram introduzidos, incluindo melhorias em Reanimated v4 e suporte a novas estratégias de lazy loading e codegen.&lt;/p&gt;




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

&lt;p&gt;Com 10 anos completados, o React Native prova que continua evoluindo com força total.&lt;br&gt;
Entre fundações, arquiteturas e novas APIs, o ecossistema mostra que o foco agora é performance, interoperabilidade e colaboração aberta.&lt;br&gt;
Mais do que um framework, o RN se consolida como uma plataforma madura, e pronta pra mais uma década de inovação.&lt;/p&gt;

&lt;p&gt;E Ainda tem muito mais por vir:&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%2F7fd7zrzwhw4m0ej8q2ft.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%2F7fd7zrzwhw4m0ej8q2ft.png" alt="React Fir" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>react</category>
      <category>reactnative</category>
      <category>javascript</category>
      <category>typescript</category>
    </item>
    <item>
      <title>Usando múltiplas chaves SSH para diferentes contas Git (pessoal e trabalho) sem dor de cabeça</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Mon, 08 Sep 2025 15:05:39 +0000</pubDate>
      <link>https://dev.to/tuliocalil/usando-multiplas-chaves-ssh-para-diferentes-contas-git-pessoal-e-trabalho-sem-dor-de-cabeca-56fp</link>
      <guid>https://dev.to/tuliocalil/usando-multiplas-chaves-ssh-para-diferentes-contas-git-pessoal-e-trabalho-sem-dor-de-cabeca-56fp</guid>
      <description>&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%2Fimages.unsplash.com%2Fphoto-1512309739986-032cbacdb618%3Fixlib%3Drb-4.1.0%26q%3D85%26fm%3Djpg%26crop%3Dentropy%26cs%3Dsrgb%26dl%3Dchunli-ju-8fs1X0JFgFE-unsplash.jpg%26w%3D1920" 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%2Fimages.unsplash.com%2Fphoto-1512309739986-032cbacdb618%3Fixlib%3Drb-4.1.0%26q%3D85%26fm%3Djpg%26crop%3Dentropy%26cs%3Dsrgb%26dl%3Dchunli-ju-8fs1X0JFgFE-unsplash.jpg%26w%3D1920" alt="https://unsplash.com/pt-br/fotografias/lote-chave-8fs1X0JFgFE" width="800" height="502"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se você tem conta pessoal e de trabalho no Git (ou até freelas), já deve ter sofrido com o SSH misturando as chaves.&lt;br&gt;
A boa notícia é que dá pra resolver de dois jeitos: &lt;strong&gt;automático por diretório&lt;/strong&gt; ou &lt;strong&gt;via alias de host&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  Automático por diretório (requer OpenSSH 7.3+)
&lt;/h2&gt;

&lt;p&gt;Essa é a forma que eu mais gosto. Só de estar dentro da pasta do projeto, o SSH já sabe qual chave usar.&lt;br&gt;
Mas atenção: funciona apenas no &lt;strong&gt;OpenSSH 7.3&lt;/strong&gt; ou superior e você precisa separar os projetos por pastas (pessoais em uma e profissionais em outra, por exemplo). Verifique sua versão do SSH com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh &lt;span class="nt"&gt;-V&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora vamos a estrutura de pastas, essa parte é fundamental, precisamos de uma pasta para cada chave, atualmente eu uso algo como:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;projects/ &lt;span class="c"&gt;#vai usar a chave pessoal&lt;/span&gt;
 - blog
 - estudos
 - urubu_do_pix
 - work/ &lt;span class="c"&gt;# daqui pra frente vai usar a chave do trabalho &lt;/span&gt;
   - projectA
   - projectB
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou seja, tudo do trabalho fica na pasta ‘work’. Vamos fazer com que o SSH use uma chave pra todos os lugares e troque apenas quando estiver na ‘work’.&lt;/p&gt;

&lt;p&gt;Edite o arquivo de configuração no do SSH que fica em &lt;code&gt;~/.ssh/config&lt;/code&gt;:&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;# Configuração padrão, usa a chave pessoal
&lt;/span&gt;&lt;span class="n"&gt;Host&lt;/span&gt; &lt;span class="n"&gt;github&lt;/span&gt;.&lt;span class="n"&gt;com&lt;/span&gt;
  &lt;span class="n"&gt;AddKeysToAgent&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt;
  &lt;span class="n"&gt;User&lt;/span&gt; &lt;span class="n"&gt;git&lt;/span&gt;
  &lt;span class="c"&gt;# UseKeychain yes   # (se estiver no macOS)
&lt;/span&gt;  &lt;span class="n"&gt;IdentitiesOnly&lt;/span&gt; &lt;span class="n"&gt;yes&lt;/span&gt;

&lt;span class="c"&gt;# Chave para projetos do trabalho
&lt;/span&gt;&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;exec&lt;/span&gt; &lt;span class="s2"&gt;"[[ $PWD == /home/tulio/projects/work* ]]"&lt;/span&gt;
    &lt;span class="n"&gt;IdentityFile&lt;/span&gt; ~/.&lt;span class="n"&gt;ssh&lt;/span&gt;/&lt;span class="n"&gt;work_key&lt;/span&gt;

&lt;span class="n"&gt;Match&lt;/span&gt; &lt;span class="n"&gt;all&lt;/span&gt;
  &lt;span class="n"&gt;IdentityFile&lt;/span&gt; ~/.&lt;span class="n"&gt;ssh&lt;/span&gt;/&lt;span class="n"&gt;my_key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Lembre de trocar os caminhos, tanto das chaves quanto da pasta work.&lt;/p&gt;

&lt;p&gt;Com isso, se você fizer um clone, push ou pull em qualquer lugar diferente da pasta ‘work’, ele vai usar a chave pessoal. Na pasta ‘work’, usa a chave do trabalho. Simples assim.&lt;/p&gt;




&lt;h2&gt;
  
  
  Alternativa: usando aliases de host
&lt;/h2&gt;

&lt;p&gt;Se a sua versão do SSH não suporta Match exec, dá pra resolver usando aliases.&lt;br&gt;
Nesse caso você cria “atalhos” e aponta o git remote para eles.&lt;/p&gt;

&lt;p&gt;Edite o &lt;code&gt;~/.ssh/config&lt;/code&gt;:&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;# Conta pessoal&lt;/span&gt;
Host github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/my_key

&lt;span class="c"&gt;# Conta do trabalho&lt;/span&gt;
Host work.github.com
    HostName github.com
    User git
    IdentityFile ~/.ssh/work_key
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Aqui você pode ser criativo com o alias, pode usar até algo mais curto, como "w" apenas, sem o github.com, basta lembrar que na hora de clonar você terá que colonar com "w" no lugar do "github.com" (git@w:org/project.git)&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;E na hora do clone basta você adicionar &lt;code&gt;work&lt;/code&gt; antes do &lt;code&gt;github&lt;/code&gt;, exemplo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone git@work.github.com:...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caso ja tenha o projeto clonado basta trocar a URL da origin, você pode fazer assim:&lt;br&gt;
Veja a URL atual do repositorio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Copie a URL e adicione o &lt;code&gt;work&lt;/code&gt; na frente e troque com:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git remote set-url origin git@work.github.com:...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Fechando
&lt;/h2&gt;

&lt;p&gt;Hoje eu uso o esquema automático por diretório porque me poupa tempo e evita erro, mas a versão com alias funciona em qualquer lugar e é à prova de falhas.&lt;br&gt;
De qualquer jeito, o resultado é o mesmo: nunca mais push falhando por causa da chave errada.&lt;/p&gt;

&lt;p&gt;Você pode repetir a configuração para usar várias chaves, eu utilizei apenas duas para simplificar.&lt;/p&gt;

</description>
      <category>git</category>
      <category>linux</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Stop Overpaying: 3 Cheaper Alternatives to DigitalOcean VPS</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Mon, 09 Jun 2025 18:22:57 +0000</pubDate>
      <link>https://dev.to/tuliocalil/stop-overpaying-3-cheaper-alternatives-to-digitalocean-vps-449</link>
      <guid>https://dev.to/tuliocalil/stop-overpaying-3-cheaper-alternatives-to-digitalocean-vps-449</guid>
      <description>&lt;p&gt;If you're like me, you've probably started a few side projects or small apps and quickly realized that even "affordable" platforms like DigitalOcean can add up fast. In this post, I'll share some cheaper (and sometimes free) alternatives to host your projects without burning your wallet. Let's dive in!&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://my.cloudfanatic.net/aff.php?aff=588" rel="noopener noreferrer"&gt;Cloudfnatic (my personal choice)&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Price: Starts at $2.99/month&lt;br&gt;
One of the cheapest options out there. Simple UI, Docker support, and fast deployment make it great for small projects on a tight budget.&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%2Fmh5fpe9n58u7jjtn1yyc.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%2Fmh5fpe9n58u7jjtn1yyc.png" alt="Tulio Calil Cloudfnatic" width="800" height="447"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.hetzner.com/cloud/" rel="noopener noreferrer"&gt;Hetzner Cloud&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Price: Starts at €3.79/month&lt;br&gt;
High-performance VPS with SSD storage, IPv6, and generous bandwidth. EU-based and known for excellent price-to-performance ratio.&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%2Fk9z8cmws8gzmmto7k57h.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%2Fk9z8cmws8gzmmto7k57h.png" alt="Tulio Calil Hetzner" width="800" height="364"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;a href="https://www.oracle.com/cloud/free/" rel="noopener noreferrer"&gt;Oracle Cloud Free Tier&lt;/a&gt;
&lt;/h3&gt;

&lt;p&gt;Price: Free forever&lt;br&gt;
Offers 2 always-free VPS instances with 1 GB RAM. Surprisingly reliable for small apps, bots, or personal projects — and completely free.&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%2Fg3q745gxhqk3rz7mct8g.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%2Fg3q745gxhqk3rz7mct8g.png" alt="Tulio Calil Oracle Cloud" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are just a few of the most affordable VPS options I’ve found while trying to cut costs without sacrificing flexibility. If you know any other hidden gems or have experience with the ones listed here, drop a comment below — I'd love to hear your thoughts, tips, or even horror stories. Let's help each other host smarter! 🚀&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>backend</category>
      <category>nextjs</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>Node.Js on Windows in 2023 - Tools and tricks</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Sun, 05 Nov 2023 20:12:15 +0000</pubDate>
      <link>https://dev.to/tuliocalil/nodejs-on-windows-in-2023-tools-and-tricks-3dj1</link>
      <guid>https://dev.to/tuliocalil/nodejs-on-windows-in-2023-tools-and-tricks-3dj1</guid>
      <description>&lt;p&gt;In 2023, Node.js continues to be a game-changer in the world of web development, and for Windows users, harnessing its potential efficiently is more crucial than ever. Whether you're a seasoned Node.js developer or just embarking on your coding journey, this post is your essential guide to mastering Node.js on the Windows platform. We'll delve into the tools and tricks that can enhance your development experience, making it smoother and more productive. Get ready to turbocharge your Node.js journey and make the most of your Windows environment!&lt;/p&gt;

&lt;h2&gt;
  
  
  WSL 2
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffe0yniu9wwwarh3tzeuz.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%2Ffe0yniu9wwwarh3tzeuz.png" alt=" " width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Windows Subsystem for Linux 2 (WSL 2) is a technology that allows you to run a full Linux kernel on your Windows machine. It offers improved performance and compatibility compared to its predecessor, WSL 1, making it a valuable tool for developers who need to work with Linux-based software on their Windows systems. WSL 2 seamlessly combines the Windows and Linux environments, providing a versatile solution for development tasks.&lt;/p&gt;

&lt;p&gt;To install it open the Windows Powershell and run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;wsl &lt;span class="nt"&gt;--install&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;After installing reboot your PC.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Remember to enable virtualization on your BIOS.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now open the Microsoft Store and search for Ubuntu(or any other Linux distro supported by WSL), click on install.&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%2Fntsyr6ogzeye9gopc8ou.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%2Fntsyr6ogzeye9gopc8ou.png" alt=" " width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Windows Terminal
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffq85g9aihdomjvm15ym3.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%2Ffq85g9aihdomjvm15ym3.png" alt=" " width="800" height="642"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Windows Terminal is a modern and powerful command-line interface for Windows that offers a more versatile and user-friendly experience for developers and system administrators. It consolidates various command-line tools, shells, and scripts into a single application, enabling you to work with PowerShell, Command Prompt, and even Linux distributions through WSL.&lt;/p&gt;

&lt;p&gt;Search on Microsoft Store and install it (maybe it is already on your system).&lt;/p&gt;

&lt;h2&gt;
  
  
  Oh My Zsh
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F49r2umj7t4z4bqkw0itr.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%2F49r2umj7t4z4bqkw0itr.png" alt=" " width="800" height="535"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Zsh, short for Z Shell, is a highly customizable and powerful command-line shell that serves as an excellent alternative to the default shells like Bash or PowerShell. With a user-friendly and feature-rich environment, Zsh is popular among developers and power users for its extensive plugin support, auto-completion, and a plethora of productivity-enhancing features.&lt;/p&gt;

&lt;p&gt;To install it, open the Windows terminal click on the dropdown arrow, and select Ubuntu.&lt;br&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%2F52in71tte5p4s49rezj0.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%2F52in71tte5p4s49rezj0.png" alt=" " width="746" height="478"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now we are in a Linux! Maybe it take a while for the first time, then will ask you for a username and a password. When finished we will see or prompt ready, let's install the ZSH and OMZ:&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;sudo &lt;/span&gt;apt &lt;span class="nb"&gt;install &lt;/span&gt;zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When ask for your password type it and hit enter.&lt;/p&gt;

&lt;p&gt;Now install the OMZ:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The script will ask to change the default shell, type "y" and hit enter.&lt;/p&gt;

&lt;h2&gt;
  
  
  Shell font
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F260vn4worzdab849946o.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%2F260vn4worzdab849946o.png" alt=" " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We need some custom font to use with our shell theme, the new font will support some "icons" to improve the experience (really?).&lt;/p&gt;

&lt;p&gt;You can get one &lt;a href="https://www.nerdfonts.com/font-downloads" rel="noopener noreferrer"&gt;here&lt;/a&gt;, I recommend the Cascadia Code.&lt;/p&gt;

&lt;p&gt;After downloading, extract it and double-click to install.&lt;/p&gt;

&lt;h2&gt;
  
  
  Starship
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fgs51ytfpu3mt1zk9syq4.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%2Fgs51ytfpu3mt1zk9syq4.png" alt=" " width="800" height="371"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Starship is a sleek and highly customizable command-line prompt that enhances your shell experience by providing a visually appealing and informative prompt. It's designed to work with various shells, including Bash, Zsh, Fish, and PowerShell, and offers a wide range of features to make your command-line interactions more efficient and enjoyable.&lt;/p&gt;

&lt;p&gt;To install run this command:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-sS&lt;/span&gt; https://starship.rs/install.sh | sh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Type "y" and hit enter when asked, after:&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;eval&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="si"&gt;$(&lt;/span&gt;starship init zsh&lt;span class="si"&gt;)&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now you can get a preset or a theme or even create one, just &lt;a href="https://starship.rs/presets/#nerd-font-symbols" rel="noopener noreferrer"&gt;check the documentation here&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Node.js
&lt;/h2&gt;

&lt;p&gt;Let's install Node.js but we will use the &lt;code&gt;nvm&lt;/code&gt;, which is a Node version manager, so you can install any version of Node.js with no problems or conflicts.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-o-&lt;/span&gt; https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.5/install.sh | bash

&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, to install the LTS version:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;nvm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--lts&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Change "--lts" for a specific version number if you want&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Now you are ready to go! Pick your favorite code editor and go on!&lt;br&gt;
I recommend the Visual Studio Code cause it has an awesome integration with WSL system.&lt;/p&gt;

</description>
      <category>node</category>
      <category>beginners</category>
      <category>tutorial</category>
      <category>productivity</category>
    </item>
    <item>
      <title>Aumente as Conversões na Black Friday: Estratégia ASO &amp; Ícone Dinâmico no React Native</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Thu, 02 Nov 2023 22:09:00 +0000</pubDate>
      <link>https://dev.to/tuliocalil/aumente-as-conversoes-na-black-friday-estrategia-aso-icone-dinamico-no-react-native-3k81</link>
      <guid>https://dev.to/tuliocalil/aumente-as-conversoes-na-black-friday-estrategia-aso-icone-dinamico-no-react-native-3k81</guid>
      <description>&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%2Fsy9hmn98zp524nhfbj2e.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%2Fsy9hmn98zp524nhfbj2e.png" alt="Black friday" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Foto de &lt;a href="https://unsplash.com/pt-br/@cardmapr?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;CardMapr.nl&lt;/a&gt; na &lt;a href="https://unsplash.com/pt-br/fotografias/pessoa-segurando-o-smartphone-android-samsung-preto-pwxESDWRwDE?utm_content=creditCopyText&amp;amp;utm_medium=referral&amp;amp;utm_source=unsplash" rel="noopener noreferrer"&gt;Unsplash&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Bem-vindo ao mundo da Black Friday, a época do ano em que consumidores estão ávidos por ofertas incríveis, e as empresas estão ansiosas para maximizar suas vendas. Para ter sucesso nesse cenário altamente competitivo, cada detalhe do seu aplicativo móvel desempenha um papel crucial. E uma maneira eficaz de se destacar é personalizar a experiência do usuário, até mesmo os detalhes aparentemente pequenos, como o ícone do seu aplicativo.&lt;/p&gt;

&lt;p&gt;O sucesso de um aplicativo móvel vai além da sua funcionalidade e design. É preciso garantir que ele seja facilmente descoberto por seu público-alvo, e a Estratégia ASO (App Store Optimization) é a chave para isso. O ASO envolve um conjunto de técnicas voltadas para a otimização da presença de um aplicativo nas lojas de aplicativos, como a App Store e o Google Play. Desde a escolha de palavras-chave estratégicas até a criação de títulos cativantes e imagens atraentes, o ASO é uma parte essencial do marketing de aplicativos que pode impulsionar o número de downloads, melhorar a classificação nas listas de resultados e aumentar a visibilidade do seu aplicativo.&lt;/p&gt;

&lt;p&gt;Neste post, vamos explorar uma estratégia de diferenciação que pode impulsionar suas conversões na Black Friday: a capacidade de alterar dinamicamente os ícones do seu aplicativo React Native. Vamos mergulhar fundo nesse processo, fornecendo a você as ferramentas e conhecimentos necessários para atrair a atenção dos consumidores, destacar-se da concorrência e aumentar as conversões durante a Black Friday. Prepare-se para uma jornada de personalização e sucesso nesta temporada de compras intensas.&lt;/p&gt;

&lt;h2&gt;
  
  
  ASO
&lt;/h2&gt;

&lt;p&gt;Sabendo que ASO é um "SEO para mobile" vamos então entender alguns pontos que podemos e devemos olhar nesse período de Black Friday para atrair ainda mais usuários para o nosso funil de conversão. &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%2Fo5r2z9kbefwle8mp6owb.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%2Fo5r2z9kbefwle8mp6owb.png" alt="Shopee ASO example" width="800" height="452"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Palavras-chave: sempre inclua palavras-chave na descrição, títulos e metadados da sua aplicação. Isso faz com que os mecanismos de busca incorporem essas informações e ajude seu aplicativo aparecer entre os primeiros resultados.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Título e descrição: você já percebeu como aplicativos como Shopee e Aliexpress mudam constantemente o título deles nas lojas acompanhando os eventos do mercado? Isso também é uma técnica ASO.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Ícones e imagens: use imagens e ícones que acompanhe as ações comerciais e principalmente com qualidade alta e no formato certo, nada de reciclar imagens de outros formatos ou utilizar capturas de tela quaisquer.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Ícones no React Native, mude sem atualizar
&lt;/h2&gt;

&lt;p&gt;Você já deve ter percebido que um app simplesmente mudou de ícone no seu smartphone e você não o percebeu atualizando, aplicativos super famosos fazem isso o tempo todo, não apenas para BF, mas para eventos até exclusivos como Prime Day, 11.11, e outros.&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%2Fbbegd0fw53hw5n0sew0a.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%2Fbbegd0fw53hw5n0sew0a.jpg" alt="Apps com ícones de black friday" width="500" height="300"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Isso é uma técnica super simples, mas muito eficaz, onde você pode trocar o ícone do aplicativo sem precisar de um deploy para isso, você pode ter vários ícones lá dentro e utilizar a técnica de &lt;a href="https://tuliocalil.com/feature-toggle-controle-seu-app-em-producao/" rel="noopener noreferrer"&gt;Feature Toggle&lt;/a&gt; para controlar qual o ícone será exibido no período que você desejar.&lt;/p&gt;

&lt;p&gt;Configurar um ícone no React Native é algo muito simples, conta com passos nativos apenas, logo tudo que você tem que fazer é ter uma imagem para ser utilizada como ícone e adapta-la a diversos formatos para cada plataforma, felizmente existem várias ferramentas para isso hoje em dia, &lt;a href="https://www.appicon.co/" rel="noopener noreferrer"&gt;Appicon&lt;/a&gt; talvez seja a mais famosa.&lt;br&gt;
Eu utilizei o &lt;a href="https://icon.kitchen/" rel="noopener noreferrer"&gt;Icon Kitchen&lt;/a&gt; para gerar um ícone do zero e configurei, ficando assim:&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%2Fhyn8m3ha4kdk6o1t4d93.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%2Fhyn8m3ha4kdk6o1t4d93.png" alt="React Native app" width="698" height="686"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Então como eu quero um novo icone para Black Friday eu utilizei novamente o Icon Kitchen para criar um icone seguindo os padrões recomendados (sim, existe normatização para ícones em mobile) e cheguei nesse resultado:&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%2F9ra89jy69igu5zhw3ia4.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%2F9ra89jy69igu5zhw3ia4.png" alt="Ícone para Black Friday" width="800" height="666"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Eu quis trazer uma mensagem bem simples; estamos participando da BF (e por isso o fundo preto) e temos descontos de 70%.&lt;/p&gt;

&lt;p&gt;Agora vamos à implementação, podemos fazer isso de forma nativa e criando uma ponte entre o código nativo e o RN, não é algo tão difícil, mas para economizar nosso tempo e não reinventar a roda vamos utilizar uma biblioteca pronta e já madura no mercado: &lt;a href="https://github.com/skb1129/react-native-change-icon" rel="noopener noreferrer"&gt;react-change-icon&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Adicione a nova dependência ao seu projeto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yarn add react-native-change-icon
&lt;span class="c"&gt;# ou&lt;/span&gt;
npm i react-native-change-icon
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Para iOS rode o &lt;code&gt;pod install&lt;/code&gt; na pasta &lt;code&gt;ios&lt;/code&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;E agora é só seguir os passos da própria documentação para implementar, lembre-se de mudar o nome do ícone antes de copiar os arquivos para as pastas nativas de cada plataforma (Android e iOS).&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%2Fi62lf4sqcao8b949ifjd.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%2Fi62lf4sqcao8b949ifjd.png" alt="Configuração do iOS" width="666" height="440"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após toda configuração podemos controlar o ícone da aplicação usando a função &lt;code&gt;changeIcon()&lt;/code&gt;, ela aceita um parâmetro que é o nome do ícone da forma que configuramos nativamente, logo, para mudar para o ícone da BF eu chamaria:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nf"&gt;changeIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;BlackFriday&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Porém, isso ainda deixa nosso codigo um pouco estatico, e para resolver isso temos varias formas, uma delas seria criar &lt;code&gt;ifs&lt;/code&gt; para datas específicas, algo como 15 dias antes da BF eu chamar a função passando o novo ícone, mas isso também não é legal, vamos utilizar o conhecimento do post de &lt;a href="https://tuliocalil.com/feature-toggle-controle-seu-app-em-producao/" rel="noopener noreferrer"&gt;Feature Toggle&lt;/a&gt; para buscar essa regra remotamente, permitindo que a gente mude isso sem grandes problemas.&lt;/p&gt;

&lt;p&gt;Um exemplo bem simples de codigo para isso acontecer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkIcon&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Busca remotamente o icone que deve ser utilizado&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggleResponse&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://minhaapi.com.br/features/app-icon&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// Um object literal para armazenar os possiveis icones&lt;/span&gt;
    &lt;span class="c1"&gt;// já existentes no app&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;icons&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;blackFriday&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;BlackFriday&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;christmas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Christmas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;iconToggle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;toggleResponse&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="c1"&gt;// Busca qual o icone esta sendo usado&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actualIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;getIcon&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

    &lt;span class="c1"&gt;// Verifica retorna o nome do icone, caso não exista retorna default&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newIcon&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;icons&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;iconToggle&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;iconTheme&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Default&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// Se o icone do servidor for igual o atual, não fazemos nada&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actualIcon&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;newIcon&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Troca o icone&lt;/span&gt;
    &lt;span class="nf"&gt;changeIcon&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newIcon&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;checkIcon&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Lembre-se de que para mandar um icone, ele precisa existir previamente no aplicativo, então você ainda precisa se planejar pois precisará de um deploy, porém pode fazer com muita antecedência.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Dessa forma buscamos assim que o app iniciar a informação de qual ícone devemos exibir, caso seja diferente do exibido atualmente nós trocamos. É possível também ouvir o retorno da função de troca, para caso venha um ícone que não exista seja feito um reset para o ícone padrão.&lt;br&gt;
O resultado disso tudo:&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%2Ffmjmvrlq3db735ysurmw.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%2Ffmjmvrlq3db735ysurmw.png" alt="Imagem final" width="706" height="672"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Confira o GIF do fluxo simulando uma situação real:&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%2F1auluruwelybwup1vh5v.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F1auluruwelybwup1vh5v.gif" alt="GIF demo" width="600" height="257"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Na esquerda o backend, no meio o codigo e o terminal e na direita o simulador de iOS.&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Todo o conceito desse post pode ser aplicado em qualquer outro framework ou linguagem, não se prenda apenas ao exemplo em código do React Native aqui.&lt;/p&gt;

&lt;p&gt;Gostou desse conteúdo e quer que eu traga mais técnicas de ASO e assuntos de React Native? Comenta aqui embaixo! &lt;/p&gt;

&lt;p&gt;Compartilhe esse contéudo com amigos e nas suas redes, você ajuda muito!&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>react</category>
      <category>mobile</category>
      <category>braziliandevs</category>
    </item>
    <item>
      <title>Bridging the Gap Between Developers and QA Testers with Maestro</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Sun, 29 Oct 2023 14:04:45 +0000</pubDate>
      <link>https://dev.to/tuliocalil/bridging-the-gap-between-developers-and-qa-testers-with-maestro-4h6g</link>
      <guid>https://dev.to/tuliocalil/bridging-the-gap-between-developers-and-qa-testers-with-maestro-4h6g</guid>
      <description>&lt;p&gt;&lt;a href="https://maestro.mobile.dev/" rel="noopener noreferrer"&gt;Maestro&lt;/a&gt; is a mobile UI testing framework, very simple and effective. It's an alternative to other frameworks like Appium, Espresso, UIAutomator or XCTest and is built on top of the knowledge from its predecessors.&lt;/p&gt;

&lt;p&gt;Maestro doesn't depend on any mobile framework, so you can run it in React Native apps, Native Android or iOS, Flutter, Ionic, Native Script, etc.&lt;br&gt;
It doesn't need a special app package too, you can use it even in a production/release build.&lt;/p&gt;

&lt;p&gt;The best points of Maestro are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Simple setup.&lt;/li&gt;
&lt;li&gt;Declarative syntax.&lt;/li&gt;
&lt;li&gt;Fast iteration.&lt;/li&gt;
&lt;li&gt;Built-in tolerance delay and flakiness.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;
  
  
  Installing
&lt;/h2&gt;

&lt;p&gt;The Maestro documentation is complete and covers installing steps for the specific OS; &lt;a href="https://maestro.mobile.dev/getting-started/installing-maestro/macos" rel="noopener noreferrer"&gt;Macos&lt;/a&gt;, &lt;a href="https://maestro.mobile.dev/getting-started/installing-maestro/windows" rel="noopener noreferrer"&gt;Windows&lt;/a&gt; and &lt;a href="https://maestro.mobile.dev/getting-started/installing-maestro/linux" rel="noopener noreferrer"&gt;Linux&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;To install the Maestro CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-Ls&lt;/span&gt; &lt;span class="s2"&gt;"https://get.maestro.mobile.dev"&lt;/span&gt; | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Don't forget to add the Maestro to &lt;code&gt;PATH&lt;/code&gt; variable.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now we can check if the Maestro is successfully working:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;maestro &lt;span class="nt"&gt;-v&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Creating a sample Flow
&lt;/h2&gt;

&lt;p&gt;Start your Android or iOS emulator/simulator, create a new folder for store our files, and open it in your favorite code editor:&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%2Fvpzd3843c6uvmkmxhlur.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%2Fvpzd3843c6uvmkmxhlur.png" alt="Maestro Macos setup" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;For this sample, let's write a simple flow to open the "Maps" app and search for a city, click to change the view from 2D to 3D, and change the map type to Satellite.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Open the Maps before write/run this test, maybe you will be asked to give some permissions. I don't put this on the flow (but is possible) cause I want a clean example.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In your editor or terminal create a new file &lt;code&gt;maps-flow.yml&lt;/code&gt; and let's start to write the instructions.&lt;/p&gt;

&lt;p&gt;In the first line, we got:&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;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.apple.Maps&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;This &lt;code&gt;appId&lt;/code&gt; is for iOS, if you are running android change it to &lt;code&gt;com.google.android.apps.maps&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This is the first line for every flow that we write, it tells Maestro what application we want to manipulate.&lt;br&gt;
The first lines in the Maestro flow file are the configuration part, in fact, the configuration part is all the lines above the &lt;code&gt;---&lt;/code&gt; marker(we will use this in the next part).&lt;br&gt;
We can set the configuration in the flow file (like we see right now) or in a root &lt;code&gt;config.yml&lt;/code&gt; file in the folder.&lt;/p&gt;

&lt;p&gt;On the properties, we can for example run flow before the current in case we need any setup steps or somelike that.&lt;br&gt;
You can check all the &lt;a href="https://maestro.mobile.dev/api-reference/configuration/flow-configuration" rel="noopener noreferrer"&gt;properties here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;So using the name and appId properties our config part looks like this:&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;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.apple.Maps&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Maps Test&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Here we use the marker &lt;code&gt;---&lt;/code&gt; to tell the Maestro that above is the config and below is your workflow.&lt;/p&gt;

&lt;p&gt;Now let's write the commands flow, before any command in the app we need to open the app, and for that, we use the &lt;code&gt;launchApp&lt;/code&gt; command.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;code&gt;Command&lt;/code&gt; is the name of actions(the properties in the &lt;code&gt;YML&lt;/code&gt;) that we got in Maestro.&lt;br&gt;
&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.apple.Maps&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Maps Test&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;launchApp&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Note that have a space on the line of the command &lt;code&gt;launchApp&lt;/code&gt;, this is how &lt;code&gt;YML&lt;/code&gt; files work, we need indent to nesting. If it is not clear, you can &lt;a href="https://www.redhat.com/en/topics/automation/what-is-yaml" rel="noopener noreferrer"&gt;read about YML&lt;/a&gt; before starts.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;launchApp&lt;/code&gt; command &lt;a href="https://maestro.mobile.dev/api-reference/commands/launchapp" rel="noopener noreferrer"&gt;accepts other params too&lt;/a&gt;, you can launch another app, clear the state, give or not permissions, and more. &lt;br&gt;
All the commands got a really good doc.&lt;/p&gt;

&lt;p&gt;Now we can test the flow, in the terminal run:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;maestro &lt;span class="nb"&gt;test &lt;/span&gt;maps-flow.yml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;Sometimes it takes some time to install the maestro-drive in the device in the first run.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;If all is right we will see the following output:&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%2Fxno3hc9j217xtsbkuopp.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%2Fxno3hc9j217xtsbkuopp.png" alt="Maestro flow run" width="800" height="305"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's put one more command:&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;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.apple.Maps&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Maps Test&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;launchApp&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;tapOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Search&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Maps"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;🚩 For Android emulator change the &lt;code&gt;tapOn&lt;/code&gt; to "Search here".&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The &lt;code&gt;tapOn&lt;/code&gt; command tells the Maestro to tap/touch/click on a view/component/element that matches with the selector that we pass. By default we can pass a string as an argument, this means that Maestro will look for a view that has this EXACLTY text. It's very useful when you don't know or the application doesn't use things like &lt;code&gt;test-id&lt;/code&gt;.&lt;br&gt;
But like always we can pass any other option, in this case, other selectors. &lt;/p&gt;

&lt;p&gt;In Maestro we got a lot of selectors, &lt;code&gt;text&lt;/code&gt;, &lt;code&gt;id&lt;/code&gt;, &lt;code&gt;point&lt;/code&gt; (relative position in the screen), and more. &lt;a href="https://maestro.mobile.dev/api-reference/selectors" rel="noopener noreferrer"&gt;All selectors are documented here&lt;/a&gt;.&lt;br&gt;
Right now the &lt;code&gt;text&lt;/code&gt; selector is the best option for us.&lt;/p&gt;

&lt;p&gt;We tap on the search field, and putting some text is the next step, right?&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;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.apple.Maps&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Maps Test&lt;/span&gt;
&lt;span class="nn"&gt;---&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;launchApp&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;tapOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Search&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Maps"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;inputText&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Salvador,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bahia,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Brazil"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we type some text in the field, the &lt;code&gt;inputText&lt;/code&gt; command has some random generators too, you can see it on the docs.&lt;br&gt;
Now that we have typed the text let's confirm the search and seed the result:&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;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;com.apple.Maps&lt;/span&gt; &lt;span class="c1"&gt;# android = com.google.android.apps.maps &lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Maps Test&lt;/span&gt; 
&lt;span class="nn"&gt;---&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;launchApp&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;tapOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Search&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Maps"&lt;/span&gt; &lt;span class="c1"&gt;# android = Search here&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;inputText&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Salvador,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bahia,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Brazil"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pressKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enter&lt;/span&gt;
&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%2Fsx4g303dh4oyulddctw5.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%2Fsx4g303dh4oyulddctw5.png" alt="Maestro demo" width="800" height="465"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If we want we can swipe the info modal, just add and test:&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="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;swipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50%, 80%&lt;/span&gt;
      &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50%, 10%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using parameters
&lt;/h2&gt;

&lt;p&gt;We are changing some values between OS, for iOS we using the &lt;code&gt;appId&lt;/code&gt; "com.apple.Maps" and for Android "com.google.android.apps.maps". In this case, we can turn this flow generic for each OS using parameters.&lt;/p&gt;

&lt;p&gt;With parameters is possible to change the fixed values in the file for variables that will be read from the CLI arguments or from environment variables. Just a refactor in our file:&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;appId&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${APP}&lt;/span&gt;
&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Maps Test&lt;/span&gt; 
&lt;span class="nn"&gt;---&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;launchApp&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;tapOn&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${FIELD}&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;inputText&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Salvador,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Bahia,&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;Brazil"&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;pressKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Enter&lt;/span&gt;
  &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;swipe&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;start&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50%, 80%&lt;/span&gt;
      &lt;span class="na"&gt;end&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;50%, 10%&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;appId&lt;/code&gt; is the value of &lt;code&gt;APP&lt;/code&gt; and &lt;code&gt;tapOn&lt;/code&gt; is &lt;code&gt;FIELD&lt;/code&gt;, to set this value just run the Maestro CLI this way:&lt;/p&gt;

&lt;p&gt;For Android test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;maestro &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;APP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.google.android.apps.maps &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;FIELD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Search here"&lt;/span&gt;  maps-flow.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;For iOS test:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;maestro &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;APP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.apple.Maps &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;FIELD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Search Maps"&lt;/span&gt;  maps-flow.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We can use parameters for multiple things, if you have a secret (like a password) and don't want to store this in the &lt;code&gt;YML&lt;/code&gt; file or if this value is dynamic and you need to get a new one every running for example.&lt;/p&gt;

&lt;h2&gt;
  
  
  Recording and report
&lt;/h2&gt;

&lt;p&gt;You may need to get some evidence that all tests are passing, maybe a video or a report or both. Maestro has a built-in solution for the two cases.&lt;/p&gt;

&lt;h3&gt;
  
  
  Recording
&lt;/h3&gt;

&lt;p&gt;Maestro records the mobile screen and creates a virtual window to show the test state and formats it to &lt;code&gt;mp4&lt;/code&gt;, actually the rendering is done in the server and when the render is done you get a temporary URL to download the video.&lt;/p&gt;

&lt;p&gt;To record the video is ultra simple, just change &lt;code&gt;test&lt;/code&gt; to &lt;code&gt;record&lt;/code&gt; in the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;maestro record &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;APP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.google.android.apps.maps &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;FIELD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Search here"&lt;/span&gt;  maps-flow.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The output:&lt;br&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%2Faqfa0xt5jna6b0cc9h3w.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Faqfa0xt5jna6b0cc9h3w.gif" alt="Maestro record command" width="600" height="338"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Report
&lt;/h3&gt;

&lt;p&gt;Maestro can generate reports for test suites, in this case, we just got one test but we can generate the report too.&lt;br&gt;
Like the record, the report is very simple too, just add a new argument in the CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt; maestro &lt;span class="nb"&gt;test&lt;/span&gt; &lt;span class="nt"&gt;--format&lt;/span&gt; junit &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;APP&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;com.google.android.apps.maps &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nv"&gt;FIELD&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"Search Here"&lt;/span&gt;  maps-flow.yaml
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;--format junit&lt;/code&gt; will generate an &lt;code&gt;xml&lt;/code&gt; file in the root with the status of the tests, actually the only supported format is Junit. You can change the output path using the &lt;code&gt;--output&lt;/code&gt; argument.&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%2Fxy14r1jfnl7qct4ikpm8.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%2Fxy14r1jfnl7qct4ikpm8.png" alt="Tests report in xml" width="800" height="419"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Copy and past the &lt;code&gt;xml&lt;/code&gt; &lt;a href="https://lotterfriends.github.io/online-junit-parser/#suite.0" rel="noopener noreferrer"&gt;content here&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;Maestro is an awesome framework and we use it a lot at my current company, &lt;a href="https://www.gok.digital/?ref=tuliocalil" rel="noopener noreferrer"&gt;GOK&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This is just the first post of a series about Maestro, I will bring real-world examples and advanced concepts in the next posts, so &lt;a href="https://tuliocalil.com/" rel="noopener noreferrer"&gt;check my personal blog&lt;/a&gt; and stay updated with new posts!&lt;/p&gt;

</description>
      <category>mobile</category>
      <category>reactnative</category>
      <category>flutter</category>
      <category>testing</category>
    </item>
    <item>
      <title>React Native Debugging Native Code</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Tue, 24 Oct 2023 16:34:58 +0000</pubDate>
      <link>https://dev.to/tuliocalil/react-native-debugging-native-code-2bg1</link>
      <guid>https://dev.to/tuliocalil/react-native-debugging-native-code-2bg1</guid>
      <description>&lt;p&gt;In the world of React Native development, a single debugging tip can make all the difference. Join us as we uncover a valuable insight to streamline your debugging process. This tip will save you time and ensure your apps run smoothly. Let's dive right in!&lt;/p&gt;

&lt;p&gt;So run your application using the React Native CLI and attach the native debugger of the native IDE (Android Studio or Xcode) to the process.&lt;/p&gt;

&lt;h3&gt;
  
  
  Android Studio
&lt;/h3&gt;

&lt;p&gt;On Android Studio you can do this by going on the "Run" option on the menu bar, clicking on "Attach to Process..." and selecting the running React Native app.&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%2Fk0oe8j5qgdsdug43m3u3.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%2Fk0oe8j5qgdsdug43m3u3.png" alt="Android Debugging attach process" width="800" height="262"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Xcode
&lt;/h3&gt;

&lt;p&gt;On Xcode click on "Debug" on the top menu bar, select the "Attach to process" option, and select the application in the list of "Likely Targets".&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%2Fpqjxrj9y5kbu31joy2rd.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%2Fpqjxrj9y5kbu31joy2rd.png" alt="Xcode debugging attach process" width="800" height="377"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now you can put breaking points in your native code and tada 🥳!&lt;/p&gt;

&lt;p&gt;I added these instructions in the React Native official docs too &lt;a href="https://github.com/facebook/react-native-website/pull/3895" rel="noopener noreferrer"&gt;you can check the PR status here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Follow-me for more content about React Native.&lt;/p&gt;

</description>
      <category>reactnative</category>
      <category>react</category>
      <category>android</category>
      <category>ios</category>
    </item>
    <item>
      <title>Offline auth with Electron + SQLite + React</title>
      <dc:creator>Tulio Calil</dc:creator>
      <pubDate>Wed, 13 Sep 2023 17:08:23 +0000</pubDate>
      <link>https://dev.to/tuliocalil/offline-auth-with-electron-sqlite-react-986</link>
      <guid>https://dev.to/tuliocalil/offline-auth-with-electron-sqlite-react-986</guid>
      <description>&lt;p&gt;In an increasingly digital world, the need for secure and robust authentication systems is paramount. Whether you're building a desktop application, a mobile app, or a web platform, the ability to authenticate users reliably and securely is a fundamental requirement. While online authentication methods are well-established and widely adopted, there are situations where offline authentication becomes essential. This is particularly true for desktop applications, where internet connectivity is not always guaranteed.&lt;br&gt;
In this article, we dive into the world of "Offline Authentication" and explore a powerful combination of technologies to achieve this goal: Electron, SQLite, and React. By leveraging the capabilities of Electron, a framework for building cross-platform desktop applications, and harnessing the flexibility of SQLite, a lightweight and embedded database engine, we'll create a robust offline authentication system. To top it off, we'll enhance the user experience by incorporating the user-friendly React library for building the application's front end.&lt;br&gt;
Offline authentication isn't just about enabling access when the internet is down; it's about providing users with a seamless and secure experience, even when disconnected. Whether you're developing a stand-alone desktop application or a part of a broader software ecosystem, the techniques and principles we'll explore here will empower you to create reliable offline authentication solutions that protect user data and ensure a smooth user experience.&lt;br&gt;
Let's embark on this journey to learn how to implement offline authentication with Electron, SQLite, and React, and unlock new possibilities for your desktop applications.Login and Signup pages&lt;/p&gt;

&lt;p&gt;First, we will use the last Electron post to start this new one. You can clone the repository on Github:GitHub - tuliocll/electron-react-sqlite-todo: A TODO app with Auth built with Electron, ReactJS and SQLite&lt;br&gt;
A TODO app with Auth built with Electron, ReactJS and SQLite - GitHub - tuliocll/electron-react-sqlite-todo: A TODO app with Auth built with Electron, ReactJS and SQLite&lt;br&gt;
&lt;a href="https://github.com/tuliocll/electron-react-sqlite-todo" rel="noopener noreferrer"&gt;https://github.com/tuliocll/electron-react-sqlite-todo&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://tuliocalil.com/offline-auth-with-electron-sqlite-react/" rel="noopener noreferrer"&gt;Check the full post here&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
