<?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: Diego Chueri</title>
    <description>The latest articles on DEV Community by Diego Chueri (@dchueri).</description>
    <link>https://dev.to/dchueri</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%2F931232%2F295578bd-fdd8-44ae-b2be-f72dab1b9266.jpeg</url>
      <title>DEV Community: Diego Chueri</title>
      <link>https://dev.to/dchueri</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dchueri"/>
    <language>en</language>
    <item>
      <title>A IA está sabotando sua evolução? Velocidade de Entrega vs Profundidade de Aprendizado</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Fri, 21 Nov 2025 09:43:18 +0000</pubDate>
      <link>https://dev.to/dchueri/a-ia-esta-sabotando-sua-evolucao-velocidade-de-entrega-vs-profundidade-de-aprendizado-73h</link>
      <guid>https://dev.to/dchueri/a-ia-esta-sabotando-sua-evolucao-velocidade-de-entrega-vs-profundidade-de-aprendizado-73h</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/dchueri/is-ai-sabotaging-your-career-growth-delivery-speed-vs-learning-depth-297o"&gt;English Version&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Você entregou a feature em tempo recorde. O código está organizado, tipado e passou nos testes. O PR foi aprovado. Você se sente invencível por ter finalizado sua sprint sem grandes esforços.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Mas seja sincero com você mesmo por um segundo: se o Cursor, Copilot ou outra IA qualquer que você tenha utilizado durante o desenvolvimento tivesse ficado offline hoje, você teria conseguido escrever aquela solução de manipulação de streams complexa? Ou aquela regex de validação? Ou a configuração do Dockerfile?&lt;/p&gt;

&lt;p&gt;Estamos vivendo a "Era de Ouro" da produtividade no desenvolvimento. Ferramentas de IA não são apenas assistentes, elas são catalisadores de força. Elas nos permitem pular a parte chata, o boilerplate, a sintaxe que esquecemos. Mas existe um efeito colateral silencioso acontecendo, afetando desde estagiários até desenvolvedores experientes: &lt;strong&gt;a terceirização do "Productive Struggle" (Esforço Produtivo).&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;A neurociência e a pedagogia concordam em um ponto: aprendizado profundo requer atrito. Requer aquele momento de frustração, de olhar para a documentação, de tentar e errar, de construir o modelo mental do problema na sua cabeça.&lt;/p&gt;

&lt;p&gt;Quando a IA nos dá a resposta "ideal" instantaneamente, ganhamos velocidade, mas perdemos o aprendizado, a prática. Estamos construindo softwares cada vez mais complexos e robustos, mas será que estamos nos tornando desenvolvedores melhores? Ou estamos caminhando para um futuro de "Seniores de Vitrine", que sabem orquestrar prompts, mas travam quando precisam debugar o que a máquina escreveu?&lt;/p&gt;

&lt;p&gt;Neste artigo, não vou pedir para você largar a IA, isso seria loucura. Mas vamos conversar sobre como usá-la sem atrofiar seu pensamento crítico e sua evolução técnica.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Ciência do Aprendizado: Por que o "difícil" é necessário
&lt;/h2&gt;

&lt;p&gt;Quando você usa o GPS para ir a um lugar novo, você chega lá rápido, mas reparou que não faz ideia de como voltar sem ele? Por outro lado, quando você se perde, pede referências pelo caminho, usa um mapa, erra uma rua e finalmente chega... você nunca mais esquece o caminho.&lt;/p&gt;

&lt;p&gt;Na psicologia cognitiva, isso tem nome: &lt;strong&gt;"Dificuldades Desejáveis" (Desirable Difficulties)&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;O pesquisador Robert Bjork demonstrou que introduzir certas dificuldades no processo de aprendizado como o esforço de tentar lembrar algo ou resolver um problema sem ajuda imediata é fundamental para a retenção a longo prazo. O aprendizado real não acontece quando lemos a resposta certa, ele acontece no esforço cognitivo de &lt;em&gt;chegar&lt;/em&gt; até ela.&lt;/p&gt;

&lt;p&gt;Esse é o conceito de &lt;strong&gt;"Productive Struggle"&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Quando delegamos todo o "trabalho sujo" de pensar na sintaxe, na estrutura e na lógica para a IA, estamos removendo o atrito. Estamos transformando o desenvolvimento de software em uma atividade passiva (como seguir o GPS) em vez de ativa (como navegar pelo terreno).&lt;/p&gt;

&lt;p&gt;O resultado? O código vai para produção, mas o conhecimento não é absorvido.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Armadilha da Velocidade vs. Profundidade
&lt;/h2&gt;

&lt;p&gt;Isso nos leva a um problema moderno: a &lt;strong&gt;Ilusão de Competência&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Antigamente, um desenvolvedor travado em um problema passava horas lendo documentação, entendendo o ciclo de vida do React ou a estrutura de memória do Rust. Hoje, o Copilot resolve isso em segundos.&lt;/p&gt;

&lt;p&gt;Isso cria dois tipos de riscos, não só para iniciantes, mas também para desenvolvedores experientes:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;O Platô da "Caixa Preta":&lt;/strong&gt; Você sabe &lt;em&gt;que&lt;/em&gt; funciona, mas não sabe &lt;em&gt;como&lt;/em&gt; funciona. Caso se depare com uma leaky abstraction ou com um bug obscuro, você não tem o mínimo modelo mental necessário para corrigir, porque não construiu a base.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Perda do Julgamento Crítico:&lt;/strong&gt; Quando a IA sugere um código elegante, nosso viés é aceitar. Esquecemos de fazer as perguntas de arquitetura: &lt;em&gt;Isso escala? Isso adiciona uma dependência desnecessária? Isso é seguro?&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Estamos trocando &lt;strong&gt;profundidade de entendimento&lt;/strong&gt; por &lt;strong&gt;velocidade de implementação&lt;/strong&gt;. E em um mercado que cada vez mais valoriza a capacidade de resolver problemas complexos (já que os simples a IA resolve), essa é uma troca perigosa para a sua carreira.&lt;/p&gt;

&lt;h2&gt;
  
  
  Estratégias para manter o controle
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1 - Para o Desenvolvedor (Autodefesa Contra a Atrofia)
&lt;/h3&gt;

&lt;p&gt;Se não podemos parar de usar a IA, temos que mudar a forma &lt;strong&gt;como&lt;/strong&gt; usamos. A meta é garantir que o &lt;strong&gt;processo de pensamento&lt;/strong&gt; permaneça humano.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A Regra do "AI Sandwich":&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Fatia 1 (Você):&lt;/strong&gt; Tente resolver o problema por 15 a 30 minutos. Sinta o &lt;em&gt;struggle&lt;/em&gt;. Entenda a dor, o erro, a documentação.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O Recheio (IA):&lt;/strong&gt; Só então, use a IA.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Fatia 2 (Você):&lt;/strong&gt; Não aceite o código. Use-o como &lt;em&gt;referência&lt;/em&gt;. Sua tarefa agora é &lt;strong&gt;criticar, refatorar e explicar&lt;/strong&gt; o código da IA, linha por linha, como se fosse um PR de um estagiário.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Lição:&lt;/strong&gt; Você usou a IA para &lt;em&gt;validar&lt;/em&gt; e &lt;em&gt;acelerar&lt;/em&gt; sua ideia, não para &lt;em&gt;gerar&lt;/em&gt; ou evitar seu pensamento.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Evite a Abstração Prematura:&lt;/strong&gt; Use a IA para gerar &lt;em&gt;boilerplates&lt;/em&gt;, configurações, testes básicos. &lt;strong&gt;Nunca&lt;/strong&gt; para criar a lógica do core da sua aplicação ou as decisões de arquitetura. É aqui que o seu julgamento de trade-offs é &lt;strong&gt;insubstituível&lt;/strong&gt;.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;A Testemunha do Erro:&lt;/strong&gt; Quando a IA gera um código que falha, &lt;strong&gt;não peça para ela corrigir&lt;/strong&gt;. Tente debugar e entender o &lt;em&gt;porquê&lt;/em&gt; ela errou primeiro. Seu cérebro aprende muito mais ao consertar o erro de um modelo do que ao receber a correção final.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  2 - Para o Líder Técnico (Cultura de Questionamento)
&lt;/h3&gt;

&lt;p&gt;A responsabilidade de manter o padrão de engenharia agora recai sobre quem revisa o código.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;O Code Review Socrático (O "Porquê" em vez do "O Quê"):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Pare de comentar: "Use &lt;code&gt;map&lt;/code&gt; em vez de &lt;code&gt;forEach&lt;/code&gt;." (Isso é sintaxe, a IA resolve).&lt;/li&gt;
&lt;li&gt;Comece a perguntar: &lt;strong&gt;"Quais são os trade-offs de performance e manutenção ao usar essa biblioteca, em comparação com a solução nativa? Por que você descartou o padrão Y?"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;O foco deve ser na &lt;strong&gt;decisão arquitetural&lt;/strong&gt;, forçando o desenvolvedor a articular o processo de pensamento, mesmo que a IA tenha escrito o código.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;O Desafio Intencional (Voltar ao Básico):&lt;/strong&gt; Ocasionalmente, atribua tarefas que &lt;em&gt;exijam&lt;/em&gt; o conhecimento fundamental e proíba (ou desincentive fortemente) o uso de LLMs. Exemplo: "Implemente um cache LRU do zero sem usar bibliotecas de terceiros." Isso garante que o músculo mental não atrofie.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Valorização da Dúvida:&lt;/strong&gt; Crie uma cultura onde &lt;strong&gt;perguntar sobre trade-offs e incertezas&lt;/strong&gt; seja valorizado. Apenas uma LLM não pode te dar um aumento de salário, a sua capacidade de avaliar riscos e comunicar decisões difíceis sim.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  O Futuro da Maestria: Decisão Humana, Execução Assistida
&lt;/h2&gt;

&lt;p&gt;Chegamos ao ponto crucial: A IA não está roubando nosso trabalho, ela está nos forçando a elevar o nível dele.&lt;/p&gt;

&lt;p&gt;Nossas ferramentas nos tornaram mais rápidos, mas não podemos deixar que elas nos tornem rasos. O valor do desenvolvedor Sênior nunca esteve em digitar código mais rápido, mas sim na &lt;strong&gt;capacidade de tomar decisões difíceis, de balancear trade-offs&lt;/strong&gt; (como abordamos em meus &lt;a href="https://dev.to/dchueri/a-sua-boa-pratica-e-o-tradeoff-de-alguem-4ae1"&gt;artigos anteriores&lt;/a&gt;) e de entender o custo oculto das abstrações.&lt;/p&gt;

&lt;p&gt;Se a IA assume o &lt;strong&gt;"O Quê"&lt;/strong&gt; (o código a ser escrito), nós devemos dominar o &lt;strong&gt;"O Porquê"&lt;/strong&gt; (a decisão de arquitetura). Lembre-se: o esforço de hoje é o conhecimento fundamental de amanhã. O &lt;strong&gt;"Productive Struggle"&lt;/strong&gt; não é uma punição, é o processo de &lt;em&gt;criação de valor&lt;/em&gt; no seu cérebro.&lt;/p&gt;

&lt;p&gt;O desenvolvedor de sucesso na era da IA será aquele que trata a ferramenta com respeito, mas com ceticismo, exigindo que o atrito cognitivo aconteça antes de apertar o &lt;code&gt;Tab&lt;/code&gt; ou aceitar o &lt;code&gt;PR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;A Maestria é construída no &lt;em&gt;processo&lt;/em&gt;, não no &lt;em&gt;resultado final&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>career</category>
      <category>ai</category>
      <category>productivity</category>
      <category>programming</category>
    </item>
    <item>
      <title>Is AI Sabotaging Your Career Growth? Delivery Speed vs. Learning Depth</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Fri, 21 Nov 2025 09:42:58 +0000</pubDate>
      <link>https://dev.to/dchueri/is-ai-sabotaging-your-career-growth-delivery-speed-vs-learning-depth-297o</link>
      <guid>https://dev.to/dchueri/is-ai-sabotaging-your-career-growth-delivery-speed-vs-learning-depth-297o</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/dchueri/a-ia-esta-sabotando-sua-evolucao-velocidade-de-entrega-vs-profundidade-de-aprendizado-73h"&gt;Portuguese Version&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;You shipped the feature in record time. The code is organized, well-typed, and passed the tests. The PR was approved. You feel invincible for having finished your sprint without much effort.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;But be honest with yourself for a second: if Cursor, Copilot, or any other AI tool you used during development had gone offline today, would you have been able to write that complex stream manipulation solution? Or that validation regex? Or the Dockerfile configuration?&lt;/p&gt;

&lt;p&gt;We are living in the "Golden Age" of development productivity. AI tools are not just assistants; they are force multipliers. They allow us to skip the boring part, the boilerplate, the syntax we've forgotten. But a silent side effect is happening, affecting everyone from interns to seasoned developers: &lt;strong&gt;the outsourcing of "Productive Struggle."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Neuroscience and pedagogy agree on one point: deep learning requires friction. It requires that moment of frustration, of looking at the documentation, of trial and error, of building the mental model of the problem in your head.&lt;/p&gt;

&lt;p&gt;When AI gives us the "ideal" answer instantly, we gain speed, but we lose the learning, the practice. We are building increasingly complex and robust software, but are we becoming better developers? Or are we heading for a future of "Showroom Seniors"—developers who know how to orchestrate prompts but freeze when they need to debug what the machine wrote?&lt;/p&gt;

&lt;p&gt;In this article, I'm not going to ask you to quit AI; that would be crazy. But let's talk about how to use it without atrophying your critical thinking and technical evolution.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Science of Learning: Why the "Hard" is Necessary
&lt;/h2&gt;

&lt;p&gt;Remember when you use GPS to go somewhere new? You get there fast, but you realize you have no idea how to get back without it. Conversely, when you get lost, ask for directions along the way, use a map, take a wrong turn, and finally arrive... you never forget the route.&lt;/p&gt;

&lt;p&gt;In cognitive psychology, this has a name: &lt;strong&gt;"Desirable Difficulties."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Researcher Robert Bjork demonstrated that introducing certain difficulties into the learning process—such as the effort of trying to recall something or solve a problem without immediate help—is fundamental for long-term retention. Real learning doesn't happen when we read the right answer; it happens in the cognitive effort of &lt;em&gt;getting&lt;/em&gt; to it.&lt;/p&gt;

&lt;p&gt;This is the concept of &lt;strong&gt;"Productive Struggle."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;When we delegate all the "dirty work" of thinking about syntax, structure, and logic to AI, we are removing the friction. We are turning software development into a passive activity (like following GPS) instead of an active one (like navigating the terrain).&lt;/p&gt;

&lt;p&gt;The result? The code goes to production, but the knowledge is not absorbed.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Trap of Speed vs. Depth
&lt;/h2&gt;

&lt;p&gt;This leads us to a modern problem: the &lt;strong&gt;"Illusion of Competence."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Back in the day, a developer stuck on a problem would spend hours reading documentation, understanding the React lifecycle, or the memory structure of Rust. Today, Copilot solves that in seconds.&lt;/p&gt;

&lt;p&gt;This creates two types of risks, not just for beginners, but also for experienced developers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The "Black Box" Plateau:&lt;/strong&gt; You know &lt;em&gt;that&lt;/em&gt; it works, but you don't know &lt;em&gt;how&lt;/em&gt; it works. Should you encounter a leaky abstraction or an obscure bug, you lack the minimum mental model required to fix it because you never built the foundation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Loss of Critical Judgment:&lt;/strong&gt; When the AI suggests elegant code, our bias is to accept it. We forget to ask the architectural questions: &lt;em&gt;Does this scale? Does this add an unnecessary dependency? Is this secure?&lt;/em&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We are trading &lt;strong&gt;depth of understanding&lt;/strong&gt; for &lt;strong&gt;speed of implementation&lt;/strong&gt;. And in a market that increasingly values the ability to solve complex problems (since AI solves the simple ones), this is a dangerous trade-off for your career.&lt;/p&gt;

&lt;h2&gt;
  
  
  Strategies for Maintaining Control
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1 - For the Developer (Self-Defense Against Atrophy)
&lt;/h3&gt;

&lt;p&gt;If we can't stop using AI, we have to change &lt;strong&gt;how&lt;/strong&gt; we use it. The goal is to ensure the &lt;strong&gt;thought process&lt;/strong&gt; remains human.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The "AI Sandwich" Rule:&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Slice 1 (You):&lt;/strong&gt; Try to solve the problem for 15 to 30 minutes. Feel the &lt;em&gt;struggle&lt;/em&gt;. Understand the pain, the error, the documentation.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Filling (AI):&lt;/strong&gt; Only then, use the AI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Slice 2 (You):&lt;/strong&gt; Do not accept the code. Use it as a &lt;em&gt;reference&lt;/em&gt;. Your task now is to &lt;strong&gt;critique, refactor, and explain&lt;/strong&gt; the AI's code, line by line, as if it were an intern's PR.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Lesson:&lt;/strong&gt; You used the AI to &lt;em&gt;validate&lt;/em&gt; and &lt;em&gt;accelerate&lt;/em&gt; your idea, not to &lt;em&gt;generate&lt;/em&gt; or bypass your thought process.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Avoid Premature Abstraction:&lt;/strong&gt; Use AI to generate &lt;em&gt;boilerplate&lt;/em&gt;, configurations, or basic tests. &lt;strong&gt;Never&lt;/strong&gt; for creating the core application logic or architectural decisions. This is where your trade-off judgment is &lt;strong&gt;irreplaceable&lt;/strong&gt;.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;The Error Witness:&lt;/strong&gt; When the AI generates code that fails, &lt;strong&gt;don't ask it to fix it.&lt;/strong&gt; Try to debug and understand &lt;em&gt;why&lt;/em&gt; it failed first. Your brain learns much more by fixing a model's error than by receiving the final correction.&lt;/li&gt;

&lt;/ul&gt;

&lt;h3&gt;
  
  
  2 - For the Technical Leader (A Culture of Questioning)
&lt;/h3&gt;

&lt;p&gt;The responsibility for maintaining the engineering standard now falls on those who review the code.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The Socratic Code Review (The "Why" instead of the "What"):&lt;/strong&gt;

&lt;ul&gt;
&lt;li&gt;Stop commenting: "Use &lt;code&gt;map&lt;/code&gt; instead of &lt;code&gt;forEach&lt;/code&gt;." (That's syntax, the AI solves it).&lt;/li&gt;
&lt;li&gt;Start asking: &lt;strong&gt;"What are the performance and maintenance trade-offs of using this library, compared to the native solution? Why did you discard pattern Y?"&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;The focus must be on the &lt;strong&gt;architectural decision&lt;/strong&gt;, forcing the developer to articulate the thought process, even if the AI wrote the code.&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;The Intentional Challenge (Back to Basics):&lt;/strong&gt; Occasionally, assign tasks that &lt;em&gt;require&lt;/em&gt; foundational knowledge and prohibit (or strongly discourage) the use of LLMs. Example: "Implement an LRU cache from scratch without using third-party libraries." This ensures the mental muscle doesn't atrophy.&lt;/li&gt;

&lt;li&gt;

&lt;strong&gt;Valuing Doubt:&lt;/strong&gt; Create a culture where &lt;strong&gt;asking about trade-offs and uncertainties&lt;/strong&gt; is valued. An LLM alone cannot give you a raise; your ability to evaluate risks and communicate difficult decisions can.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Future of Mastery: Human Decision, Assisted Execution
&lt;/h2&gt;

&lt;p&gt;We have reached the crucial point: AI is not stealing our jobs; it is forcing us to elevate the level of our work.&lt;/p&gt;

&lt;p&gt;Our tools have made us faster, but we cannot let them make us shallow. The value of the Senior developer has never been in typing code faster, but rather in the &lt;strong&gt;ability to make difficult decisions, balance trade-offs&lt;/strong&gt; (as discussed in my &lt;a href="https://dev.to/dchueri/your-best-practice-is-someone-elses-tradeoff-1k87"&gt;previous articles&lt;/a&gt;), and understand the hidden cost of abstractions.&lt;/p&gt;

&lt;p&gt;If AI assumes the &lt;strong&gt;"What"&lt;/strong&gt; (the code to be written), we must master the &lt;strong&gt;"Why"&lt;/strong&gt; (the architectural decision). Remember: today's effort is tomorrow's fundamental knowledge. &lt;strong&gt;"Productive Struggle"&lt;/strong&gt; is not a punishment; it is the process of &lt;em&gt;creating value&lt;/em&gt; in your brain.&lt;/p&gt;

&lt;p&gt;The successful developer in the AI era will be the one who treats the tool with respect, but with skepticism, demanding that cognitive friction happens before hitting &lt;code&gt;Tab&lt;/code&gt; or accepting the &lt;code&gt;PR&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Mastery is built in the &lt;em&gt;process&lt;/em&gt;, not the &lt;em&gt;final result&lt;/em&gt;.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>productivity</category>
      <category>career</category>
      <category>programming</category>
    </item>
    <item>
      <title>Your "Best Practice" is Someone Else's Tradeoff</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Mon, 10 Nov 2025 15:21:07 +0000</pubDate>
      <link>https://dev.to/dchueri/your-best-practice-is-someone-elses-tradeoff-1k87</link>
      <guid>https://dev.to/dchueri/your-best-practice-is-someone-elses-tradeoff-1k87</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/dchueri/a-sua-boa-pratica-e-o-tradeoff-de-alguem-4ae1"&gt;Versão em Português&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let's be honest. How many hours have you spent Googling the "best way" to do something?&lt;/p&gt;

&lt;p&gt;"What's the best database for an e-commerce site?"&lt;br&gt;
"Should I use Microservices or a Monolith?"&lt;br&gt;
"What's the 'best practice' for managing state in React?"&lt;/p&gt;

&lt;p&gt;The software development ecosystem seems somewhat obsessed with the idea of "Best Practices." They are sold as golden rules, infallible paths to clean, scalable, and easy-to-maintain software. In &lt;em&gt;code reviews&lt;/em&gt;, in blog articles, in conferences... we are surrounded by "the best way" to build software.&lt;/p&gt;

&lt;p&gt;But what if this search is, in fact, a trap?&lt;/p&gt;

&lt;p&gt;Here's the problem: when we apply these "rules" blindly, the result is often the opposite of what we expected. You implement microservices for a simple application and suddenly, you're spending 80% of your time managing a complex deployment infrastructure instead of delivering value to the user.&lt;/p&gt;

&lt;p&gt;Recently, I wrote about &lt;a href="https://dev.to/dchueri/the-cost-of-perfection-49lk"&gt;&lt;strong&gt;"The Cost of Perfection"&lt;/strong&gt;&lt;/a&gt; (overengineering). In it, I explored how our tendency to build "fortresses" trying to cover every possible future scenario often leads to complex, expensive, and hard-to-maintain systems.&lt;/p&gt;

&lt;p&gt;What I realized is that overengineering is often a &lt;em&gt;symptom&lt;/em&gt;. But what is the root cause? While reading the book &lt;strong&gt;"Software Architecture: The Hard Parts"&lt;/strong&gt;, I was able to understand at least part of why this happens.&lt;/p&gt;

&lt;p&gt;The book's central insight is both brutal and liberating: true, efficient software architecture isn't JUST about following rules or best practices. It's about understanding, analyzing, and managing &lt;strong&gt;tradeoffs&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In this article, I want to share the main insights I took from this book, focusing on why we should stop looking for the "silver bullet" and start developing the most important skill of an architect: analyzing tradeoffs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Breaking the Myth: Why "Best Practices" Can Be Dangerous
&lt;/h2&gt;

&lt;p&gt;The biggest problem with the term "best practice" is that most of the time I see it being used, I also see it ignoring one of the most important words in software engineering (if not the most important): &lt;strong&gt;context&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;A solution is never good or bad in a vacuum. It's good or bad &lt;em&gt;for a specific problem&lt;/em&gt;, &lt;em&gt;for a specific team&lt;/em&gt;, &lt;em&gt;at a specific time&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;What is a "best practice" for Netflix, with its tens of thousands of microservices and an elite engineering culture, is almost certainly a &lt;em&gt;terrible&lt;/em&gt; practice for a 5-person startup trying to validate a product idea (MVP) in the next 3 months.&lt;/p&gt;

&lt;p&gt;Let's take the most classic example from the last decade: &lt;strong&gt;Microservices&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;If you listen to the conferences and read the most popular blogs, the "best practice" seems to be decomposing everything. The promise is tempting:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Independent scalability!&lt;/li&gt;
&lt;li&gt;Autonomous teams!&lt;/li&gt;
&lt;li&gt;Deploy without fear!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But that's only one side of the coin. What "Software Architecture: The Hard Parts" emphasizes is that this choice &lt;strong&gt;is not an improvement, it's a tradeoff&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;By choosing microservices, you are &lt;em&gt;trading&lt;/em&gt; the development simplicity of a Monolith for massive operational and infrastructural complexity.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Microservices Tradeoff:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You GAIN:&lt;/strong&gt; Granular scalability, independent deployment, fault isolation (maybe, in the best-case scenario with this architecture being well-implemented).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You PAY WITH:&lt;/strong&gt; Network complexity (latency!), data consistency (sagas, anyone?), difficulty in observability (what called what?), and the need for a much more mature DevOps culture.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;The "best practice" of microservices is only "good" if the problem you have (e.g., difficulty scaling a specific part of your system) is greater than the new set of problems you will gain (e.g., managing a complex distributed architecture).&lt;/p&gt;

&lt;p&gt;For 90% of applications at the beginning, the "cost" of microservices far outweighs the "gain."&lt;/p&gt;

&lt;p&gt;This is the danger: a "best practice" is just the &lt;em&gt;solution&lt;/em&gt; someone found for a &lt;em&gt;specific tradeoff&lt;/em&gt;. Copying the solution without understanding the original tradeoff is a recipe for disaster (or, at the very least, for a lot of overengineering, as we mentioned before).&lt;/p&gt;




&lt;h2&gt;
  
  
  Architecture as Tradeoff Analysis
&lt;/h2&gt;

&lt;p&gt;If our job is no longer to blindly apply "best practices," what is our function as developers and architects?&lt;/p&gt;

&lt;p&gt;The book makes this very clear: our job is to &lt;strong&gt;analyze tradeoffs&lt;/strong&gt;. It redefines software architecture in a powerful way. It ceases to be the search for a "perfect" or "correct" final state and becomes the continuous process of making hard decisions in a sea of ambiguities.&lt;/p&gt;

&lt;p&gt;The most important part to understand is this: a tradeoff is &lt;strong&gt;not&lt;/strong&gt; a choice between a "good" and a "bad" option. That would be easy. A tradeoff is almost always a choice between two (or more) options that are &lt;strong&gt;bad&lt;/strong&gt; in different ways, or &lt;strong&gt;good&lt;/strong&gt; in different ways.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A tradeoff is choosing between:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Option A: Fast to develop, but hard to scale.&lt;/li&gt;
&lt;li&gt;Option B: Slow to develop, but easy to scale.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Which one is "right"? &lt;strong&gt;It depends.&lt;/strong&gt; Do you need to launch fast (startup MVP) or do you need to support millions of users (established company)?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The book argues that the "hard parts" of architecture (like decomposing systems or managing distributed data) are hard precisely because there is no easy answer. All that exists are concessions.&lt;/p&gt;

&lt;p&gt;The role of the architect or senior developer, therefore, changes drastically:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;From:&lt;/strong&gt; "Knowing what the best solution is (e.g., Kubernetes)."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;To:&lt;/strong&gt; "Knowing what the right questions to ask are (e.g., 'What are the operational costs of adopting Kubernetes &lt;em&gt;now&lt;/em&gt; vs. the cost of migrating to it &lt;em&gt;later&lt;/em&gt;?')."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The book teaches us that the architect is not the one with the magic answer. They are the one who deeply understands the &lt;strong&gt;business requirements&lt;/strong&gt; and can map which architectural attributes (performance, maintainability, scalability, cost, etc.) are most important &lt;em&gt;for that context&lt;/em&gt; and which can be sacrificed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Three Practical Examples of Tradeoffs
&lt;/h2&gt;

&lt;p&gt;Alright, the theory of "analyzing tradeoffs" is great. But what does it look like in day-to-day life? Let's analyze some classic software engineering dilemmas through the lens of the book.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. The Data Dilemma: Consistency vs. Availability
&lt;/h3&gt;

&lt;p&gt;The question "SQL or NoSQL?" is perhaps the most misunderstood "best practice" of the last decade. At this moment, you are not facing a simple database choice; you are primarily facing a choice of consistency model.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The "Best Practice" (The Myth):&lt;/strong&gt; "NoSQL scales better!" or "SQL is ACID and reliable!"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Tradeoff Analysis:&lt;/strong&gt; The real question is: What is more expensive for &lt;em&gt;your&lt;/em&gt; business? Being temporarily &lt;em&gt;inconsistent&lt;/em&gt; or being temporarily &lt;em&gt;unavailable&lt;/em&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Tradeoff (CAP Theorem):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choosing Strong Consistency (e.g., a traditional SQL database):&lt;/strong&gt; You &lt;strong&gt;GAIN&lt;/strong&gt; the guarantee that data is 100% correct and up-to-date across the entire system. A bank transaction &lt;em&gt;always&lt;/em&gt; reflects the real balance.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cost (Tradeoff):&lt;/strong&gt; You &lt;strong&gt;PAY&lt;/strong&gt; with availability. In a distributed system, if the network fails between two nodes, the database must &lt;em&gt;block&lt;/em&gt; the operation (become unavailable) to ensure consistency is not violated.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choosing Availability (e.g., NoSQL with Eventual Consistency):&lt;/strong&gt; You &lt;strong&gt;GAIN&lt;/strong&gt; a system that &lt;em&gt;always&lt;/em&gt; responds. A "like" on Instagram or an item added to a shopping cart works even if part of the system is slow.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cost (Tradeoff):&lt;/strong&gt; You &lt;strong&gt;PAY&lt;/strong&gt; with consistency. The system might take a few milliseconds (or seconds) for all nodes to agree. Your "like" might not appear to someone else immediately. You might sell a shopping cart item that just went out of stock (and you'll have to deal with that later).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Architect's Job:&lt;/strong&gt; It's not to know if "SQL is better." It's to ask: "For &lt;em&gt;this&lt;/em&gt; feature (e.g., shopping cart vs. payment), what is more critical: availability (the customer being able to use it) or consistency (the data being 100% correct right now)?"&lt;/p&gt;

&lt;h3&gt;
  
  
  2. The Communication Dilemma: Synchronous vs. Asynchronous
&lt;/h3&gt;

&lt;p&gt;How should your services (or microservices) talk to each other?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The "Best Practice" (The Myth):&lt;/strong&gt; "Decouple everything! Use a Message Broker (Kafka/RabbitMQ) for everything!"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Tradeoff Analysis:&lt;/strong&gt; Asynchronous communication is powerful, but it is incredibly complex.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Tradeoff (Coupling):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choosing Synchronous (e.g., a direct REST API call):&lt;/strong&gt; You &lt;strong&gt;GAIN&lt;/strong&gt; simplicity and immediacy. You call the &lt;code&gt;ServiceB.DoThing()&lt;/code&gt; API and you know &lt;em&gt;immediately&lt;/em&gt; if it worked or failed. The code is linear and easy to debug.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cost (Tradeoff):&lt;/strong&gt; You &lt;strong&gt;PAY&lt;/strong&gt; with temporal coupling. If &lt;code&gt;ServiceB&lt;/code&gt; is slow or down, your &lt;code&gt;ServiceA&lt;/code&gt; blocks along with it, waiting for a response. A failure cascades.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choosing Asynchronous (e.g., publishing a &lt;code&gt;ThingDone&lt;/code&gt; event):&lt;/strong&gt; You &lt;strong&gt;GAIN&lt;/strong&gt; resilience. &lt;code&gt;ServiceA&lt;/code&gt; just "shouts" the event and moves on. If &lt;code&gt;ServiceB&lt;/code&gt; is down, it's not affected. &lt;code&gt;ServiceB&lt;/code&gt; will process the event when it comes back up.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cost (Tradeoff):&lt;/strong&gt; You &lt;strong&gt;PAY&lt;/strong&gt; with massive complexity. How do you monitor if the event was processed? How do you handle retries? What about the order of events? You've traded development simplicity for operational resilience.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Architect's Job:&lt;/strong&gt; "For &lt;em&gt;this&lt;/em&gt; workflow, what is more important: resilience (asynchronous) or simplicity of development and debugging (synchronous)?"&lt;/p&gt;

&lt;h3&gt;
  
  
  3. The Code Dilemma: Performance vs. Maintainability
&lt;/h3&gt;

&lt;p&gt;How should you write that new module?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The "Best Practice" (The Myth):&lt;/strong&gt; "Clean Code! SOLID, DRY, KISS! Create all the right abstractions!"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The Tradeoff Analysis:&lt;/strong&gt; Every layer of abstraction that aids maintainability ("Clean Code") has a performance cost.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;The Tradeoff (Abstraction):&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Choosing Maintainability (Clean Code):&lt;/strong&gt; You &lt;strong&gt;GAIN&lt;/strong&gt; code that is easy to test, easy to change, and easy to understand (new devs thank you). You use Repositories, Use Cases, Dependency Injection, etc.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cost (Tradeoff):&lt;/strong&gt; You &lt;strong&gt;PAY&lt;/strong&gt; with performance (even if it's small) and "boilerplate." Each layer of abstraction adds &lt;em&gt;overhead&lt;/em&gt; (more function calls, more memory allocation, more indirection).&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Choosing Performance ("quick and dirty"):&lt;/strong&gt; You &lt;strong&gt;GAIN&lt;/strong&gt; raw speed. You write a "raw," optimized SQL query that does 5 joins directly in your &lt;em&gt;handler&lt;/em&gt;, skips the ORM, and returns the data in 10ms.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;The Cost (Tradeoff):&lt;/strong&gt; You &lt;strong&gt;PAY&lt;/strong&gt; with maintainability. This code is fragile, hard to test, and a nightmare to change. If the database schema changes, good luck.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;The Architect's Job:&lt;/strong&gt; "Is this a critical part of the system (hot path), like a game's rendering loop, where every nanosecond counts? Or is it a 'settings' CRUD screen that's used once a month, where the speed of &lt;em&gt;modification&lt;/em&gt; is more important than the speed of execution?"&lt;/p&gt;




&lt;h2&gt;
  
  
  How to Start Thinking in Tradeoffs
&lt;/h2&gt;

&lt;p&gt;From now on, "it depends" is your new favorite answer. But how can we turn this philosophy into a practical tool? How do you build this "tradeoff analysis" muscle?&lt;/p&gt;

&lt;p&gt;Here are a few practical actions you can start applying &lt;em&gt;today&lt;/em&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Change Your Vocabulary (and Your Questions)
&lt;/h3&gt;

&lt;p&gt;The first step is the simplest and the most powerful. Stop asking "What's the &lt;em&gt;best&lt;/em&gt;?" and start asking "What are the &lt;em&gt;costs&lt;/em&gt;?".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Stop asking:&lt;/strong&gt; "What's the &lt;em&gt;best&lt;/em&gt; database for this project?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start asking:&lt;/strong&gt; "What are the tradeoffs of using PostgreSQL vs. MongoDB &lt;em&gt;for this specific use case&lt;/em&gt;? What are we optimizing for? Query speed, infrastructure cost, or schema flexibility?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Stop saying:&lt;/strong&gt; "We should use microservices because it's 'best practice'."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Start saying:&lt;/strong&gt; "If we use microservices, we will gain X, but we will pay with complexity Y. Does this &lt;em&gt;gain&lt;/em&gt; justify this &lt;em&gt;cost&lt;/em&gt;... &lt;em&gt;right now&lt;/em&gt;?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Identify the Business "Drivers"
&lt;/h3&gt;

&lt;p&gt;You can't weigh a tradeoff if you don't know what's most important on the scale. Architecture must &lt;em&gt;always&lt;/em&gt; serve the business goals.&lt;/p&gt;

&lt;p&gt;Before any major technical decision, ask your team, PM, or leader: "What is the most important 'driver' for this feature?"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Is it &lt;strong&gt;Speed of Delivery (Time-to-Market)&lt;/strong&gt;? (Then maybe the "simpler" and faster solution to implement, even if it's "ugly," is the best one &lt;em&gt;now&lt;/em&gt;.)&lt;/li&gt;
&lt;li&gt;Is it &lt;strong&gt;Extreme Performance&lt;/strong&gt;? (Maybe that "Clean Code" with 5 layers of abstraction is a luxury we can't afford &lt;em&gt;here&lt;/em&gt;.)&lt;/li&gt;
&lt;li&gt;Is it &lt;strong&gt;Operational Cost&lt;/strong&gt;? (Will using that managed AWS service be more expensive or cheaper than maintaining a Kubernetes cluster ourselves?)&lt;/li&gt;
&lt;li&gt;Is it &lt;strong&gt;Maintainability&lt;/strong&gt;? (Is our team junior and in need of code that's easy to understand, even if it's slower?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Only after you know what you're optimizing for can you fairly analyze the tradeoff.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use (and Learn to Like) ADRs: Architecture Decision Records
&lt;/h3&gt;

&lt;p&gt;This is the most practical takeaway you can have. "Software Architecture: The Hard Parts" strongly advocates for &lt;strong&gt;documenting your decisions&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;An &lt;strong&gt;ADR (Architecture Decision Record)&lt;/strong&gt; is a simple, short Markdown document where your team records an important architectural decision. The basic format is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Title:&lt;/strong&gt; "Choice of Communication between Service A and B"&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Context:&lt;/strong&gt; "We need Service A to notify Service B when an order is paid."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Decision:&lt;/strong&gt; "We have chosen to use Synchronous Communication (REST API) for now."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Consequences (The Tradeoff):&lt;/strong&gt; "We gain simplicity in implementation and debugging. We &lt;em&gt;accept&lt;/em&gt; the risk that if B fails, A will fail with it (temporal coupling). This is acceptable &lt;em&gt;today&lt;/em&gt; because..."&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Options Considered (and why they were discarded):&lt;/strong&gt; "We considered RabbitMQ, but the infrastructure cost and monitoring complexity were deemed too high for the current project phase."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;An ADR is the perfect antidote to the question every dev asks 6 months after something was built: &lt;em&gt;"Why the hell did we do it this way?"&lt;/em&gt; It forces you to articulate and remember the &lt;em&gt;whys&lt;/em&gt; behind the tradeoffs you analyzed at the time of the decision.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusion: The Most Important Skill
&lt;/h2&gt;

&lt;p&gt;For a long time, I measured my seniority by the number of "best practices" I knew. I thought the best architect was the guy who had the &lt;em&gt;blueprint&lt;/em&gt; for the "right way" to build everything.&lt;/p&gt;

&lt;p&gt;After gaining more experience, collecting some project scars (like the one I mentioned in my &lt;a href="https://dev.to/dchueri/the-cost-of-perfection-49lk"&gt;article on overengineering&lt;/a&gt;), my perspective changed completely. And the book "Software Architecture: The Hard Parts" was definitely one of the lenses that made me see the &lt;em&gt;whys&lt;/em&gt; behind some of the challenges I faced (at least a part of them).&lt;/p&gt;

&lt;p&gt;True seniority isn't about having all the answers. It's about understanding that &lt;strong&gt;there are no easy answers&lt;/strong&gt;, only hard tradeoffs.&lt;/p&gt;

&lt;p&gt;Software engineering is not an exact science like mathematics; it is a discipline of managing complexity and concessions. The "Myth of the Best Practice" gives us a false sense of security, while "Tradeoff Analysis" gives us the real tool to navigate real-world complexity.&lt;/p&gt;

&lt;p&gt;So, the next time you find yourself in a debate about which technology is "better," stop and change the question: &lt;strong&gt;"What are the tradeoffs we are making here?"&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Thank you for reading and for your attention thus far. I would love to hear from you. Our community is built on real-world experiences.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What was the most difficult (or painful) tradeoff you've had to make on a project recently?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Leave your story in the comments!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>career</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>A sua "Boa Prática" é o Tradeoff de Alguém</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Mon, 10 Nov 2025 15:00:00 +0000</pubDate>
      <link>https://dev.to/dchueri/a-sua-boa-pratica-e-o-tradeoff-de-alguem-4ae1</link>
      <guid>https://dev.to/dchueri/a-sua-boa-pratica-e-o-tradeoff-de-alguem-4ae1</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/dchueri/your-best-practice-is-someone-elses-tradeoff-1k87"&gt;English Version&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos ser honestos. Quantas horas você já passou pesquisando no Google a "melhor forma" de fazer alguma coisa?&lt;/p&gt;

&lt;p&gt;"Qual o melhor banco de dados para um e-commerce?"&lt;br&gt;
"Devo utilizar Microsserviços ou Monolito?"&lt;br&gt;
"Qual seria a 'melhor prática' para gerenciar estados no React?"&lt;/p&gt;

&lt;p&gt;O ecossistema do desenvolvimento de software se mostra de certa forma obcecado pela ideia de "Boas Práticas". Elas são vendidas como regras de ouro, caminhos infalíveis para um software limpo, escalável e fácil de manter. Em &lt;em&gt;code reviews&lt;/em&gt;, em artigos de blog, em conferências... estamos cercados pela "melhor forma" de construir software.&lt;/p&gt;

&lt;p&gt;Mas e se essa busca for, na verdade, uma armadilha?&lt;/p&gt;

&lt;p&gt;Aqui está o problema: quando aplicamos essas "regras" cegamente, muitas vezes o resultado é o oposto do esperado. Você implementa microsserviços para uma aplicação simples e, de repente, gasta 80% do tempo gerenciando uma infraestrutura de deploy complexa em vez de entregar valor para o usuário.&lt;/p&gt;

&lt;p&gt;Recentemente, escrevi sobre &lt;a href="https://dev.to/dchueri/o-custo-da-perfeicao-172"&gt;&lt;strong&gt;"O Custo da Perfeição"&lt;/strong&gt;&lt;/a&gt; (overengineering). Nele, explorei como nossa tendência de construir "fortalezas" tentando cobrir todos os cenários futuros possíveis muitas vezes leva a sistemas complexos, caros e difíceis de manter.&lt;/p&gt;

&lt;p&gt;O que eu percebi é que o overengineering é, frequentemente, um &lt;em&gt;sintoma&lt;/em&gt;. Mas qual é a causa raiz? Durante a leitura do livro &lt;strong&gt;"Arquitetura de Software: As partes difíceis"&lt;/strong&gt; pude entender pelo menos parte do porquê disso.&lt;/p&gt;

&lt;p&gt;O insight central do livro é ao mesmo tempo brutal e libertador: a verdadeira arquitetura de software eficiente não é sobre APENAS seguir regras ou boas práticas. É sobre entender, analisar e gerenciar &lt;strong&gt;tradeoffs&lt;/strong&gt; (trocas e concessões).&lt;/p&gt;

&lt;p&gt;Neste artigo, quero compartilhar os principais insights que tirei desse livro, focando em por que devemos parar de procurar a "bala de prata" e começar a desenvolver a habilidade mais importante de um arquiteto: analisar tradeoffs.&lt;/p&gt;




&lt;h2&gt;
  
  
  Quebrando o Mito: Por que "Boas Práticas" Podem ser Perigosas
&lt;/h2&gt;

&lt;p&gt;O maior problema com o termo "boa prática" é que na maioria das vezes que vejo ele sendo utilizado vejo também ignorarem uma das palavras mais importantes da engenharia de software (se não a mais importante): &lt;strong&gt;contexto&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Uma solução nunca é boa ou ruim no vácuo. Ela é boa ou ruim &lt;em&gt;para um problema específico&lt;/em&gt;, &lt;em&gt;para uma equipe específica&lt;/em&gt;, &lt;em&gt;em um momento específico&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;O que é uma "boa prática" para a Netflix, com suas dezenas de milhares de microsserviços e uma cultura de engenharia de elite, é quase certamente uma &lt;em&gt;péssima&lt;/em&gt; prática para uma startup de 5 pessoas tentando validar uma ideia de produto (MVP) nos próximos 3 meses.&lt;/p&gt;

&lt;p&gt;Vamos pegar o exemplo mais clássico da última década: &lt;strong&gt;Microsserviços&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Se você ouvir as conferências e ler os blogs mais populares, a "boa prática" parece ser decompor tudo. A promessa é tentadora:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Escalabilidade independente!&lt;/li&gt;
&lt;li&gt;Times autônomos!&lt;/li&gt;
&lt;li&gt;Deploy sem medo!&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Mas isso é apenas um lado da moeda. O que o livro "Arquitetura de Software: As partes difíceis" enfatiza é que essa escolha &lt;strong&gt;não é uma melhoria, é um tradeoff&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Ao escolher microsserviços, você está &lt;em&gt;trocando&lt;/em&gt; a simplicidade de desenvolvimento de um Monolito por uma complexidade operacional e de infraestrutura massiva.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;O Tradeoff dos Microsserviços:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Você GANHA:&lt;/strong&gt; Escalabilidade granular, deploy independente, isolamento de falhas (talvez, na melhor das hipóteses com essa arquitetura sendo bem implementada).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Você PAGA COM:&lt;/strong&gt; Complexidade de rede (latência!), consistência de dados (sagas, anyone?), dificuldade de observabilidade (o que chamou o quê?), e a necessidade de um DevOps muito mais maduro.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;A "boa prática" dos microsserviços só é "boa" se o problema que você tem (ex: dificuldade de escalar uma parte específica do seu sistema) for maior do que o novo conjunto de problemas que você vai ganhar (ex: gerenciar uma arquitetura distribuída complexa).&lt;/p&gt;

&lt;p&gt;Para 90% das aplicações no início, o "custo" dos microsserviços supera em muito o "ganho".&lt;/p&gt;

&lt;p&gt;Esse é o perigo: uma "boa prática" é apenas a &lt;em&gt;solução&lt;/em&gt; que alguém encontrou para um &lt;em&gt;tradeoff&lt;/em&gt; específico. Copiar a solução sem entender o tradeoff original é a receita para o desastre (ou, no mínimo, para muito overengineering, como falamos antes).&lt;/p&gt;




&lt;h2&gt;
  
  
  A Arquitetura como Análise de Tradeoffs
&lt;/h2&gt;

&lt;p&gt;Se o nosso trabalho não é mais aplicar "boas práticas" cegamente, qual é a nossa função como desenvolvedores e arquitetos?&lt;/p&gt;

&lt;p&gt;O livro deixa isso muito claro: nosso trabalho é &lt;strong&gt;analisar tradeoffs&lt;/strong&gt;. Ele redefine a arquitetura de software de uma forma poderosa. Ela deixa de ser a busca por um estado final "perfeito" ou "correto" e passa a ser o processo contínuo de tomar decisões difíceis em um mar de ambiguidades.&lt;/p&gt;

&lt;p&gt;A parte mais importante de entender é esta: um tradeoff &lt;strong&gt;não é&lt;/strong&gt; uma escolha entre uma opção "boa" e uma "ruim". Isso seria fácil. Um tradeoff é quase sempre uma escolha entre duas (ou mais) opções &lt;strong&gt;ruins&lt;/strong&gt; de formas diferentes, ou &lt;strong&gt;boas&lt;/strong&gt; de formas diferentes.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Um tradeoff é escolher entre:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Opção A: Rápida de desenvolver, mas difícil de escalar.&lt;/li&gt;
&lt;li&gt;Opção B: Lenta de desenvolver, mas fácil de escalar.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Qual é a "certa"? &lt;strong&gt;Depende.&lt;/strong&gt; Você precisa lançar rápido (startup em MVP) ou você precisa aguentar milhões de usuários (empresa estabelecida)?&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;O livro argumenta que as "partes difíceis" da arquitetura (como decompor sistemas ou gerenciar dados distribuídos) são difíceis precisamente porque não existe uma resposta fácil. Tudo o que  existe são concessões.&lt;/p&gt;

&lt;p&gt;O papel do arquiteto ou do desenvolvedor sênior, portanto, muda drasticamente:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;De:&lt;/strong&gt; "Saber qual é a melhor solução (ex: Kubernetes)."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Para:&lt;/strong&gt; "Saber quais são as perguntas certas a se fazer (ex: 'Quais são os custos operacionais de adotar Kubernetes &lt;em&gt;agora&lt;/em&gt; vs. o custo de migrar para ele &lt;em&gt;depois&lt;/em&gt;?')."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O livro nos ensina que o arquiteto não é aquele que tem a resposta mágica. É aquele que entende profundamente os &lt;strong&gt;requisitos de negócio&lt;/strong&gt; e consegue mapear quais atributos de arquitetura (performance, manutenibilidade, escalabilidade, custo, etc.) são mais importantes &lt;em&gt;para aquele contexto&lt;/em&gt; e quais podem ser sacrificados.&lt;/p&gt;




&lt;h2&gt;
  
  
  Três Exemplos Práticos de Tradeoffs
&lt;/h2&gt;

&lt;p&gt;Tudo bem, a teoria de "analisar tradeoffs" é ótima. Mas como ela se parece no dia a dia? Vamos analisar alguns dilemas clássicos da engenharia de software sob a ótica do livro.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. O Dilema dos Dados: Consistência vs. Disponibilidade
&lt;/h3&gt;

&lt;p&gt;A pergunta "SQL ou NoSQL?" talvez seja a "boa prática" mais mal interpretada da última década. Nesse momento você não está diante de uma simples escolha de banco de dados; você está principalmente diante da escolha de um modelo de consistência.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A "Boa Prática" (O Mito):&lt;/strong&gt; "NoSQL escala melhor!" ou "SQL é ACID e confiável!"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Análise de Tradeoff:&lt;/strong&gt; A verdadeira questão é: O que é mais caro para o &lt;em&gt;seu&lt;/em&gt; negócio? Estar temporariamente &lt;em&gt;inconsistente&lt;/em&gt; ou estar temporariamente &lt;em&gt;indisponível&lt;/em&gt;?&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;O Tradeoff (Teorema CAP):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Escolhendo Consistência Forte (ex: um banco SQL tradicional):&lt;/strong&gt; Você &lt;strong&gt;GANHA&lt;/strong&gt; a garantia de que os dados estão 100% corretos e atualizados em todo o sistema. Uma transação bancária &lt;em&gt;sempre&lt;/em&gt; reflete o saldo real.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O Custo (Tradeoff):&lt;/strong&gt; Você &lt;strong&gt;PAGA&lt;/strong&gt; com disponibilidade. Em um sistema distribuído, se a rede falhar entre dois nós, o banco precisa &lt;em&gt;travar&lt;/em&gt; a operação (ficar indisponível) para garantir que a consistência não seja violada.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Escolhendo Disponibilidade (ex: NoSQL com Consistência Eventual):&lt;/strong&gt; Você &lt;strong&gt;GANHA&lt;/strong&gt; um sistema que &lt;em&gt;sempre&lt;/em&gt; responde. Um "like" no Instagram ou um item adicionado ao carrinho de compras funciona mesmo que parte do sistema esteja lenta.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O Custo (Tradeoff):&lt;/strong&gt; Você &lt;strong&gt;PAGA&lt;/strong&gt; com consistência. O sistema pode levar alguns milissegundos (ou segundos) para que todos os nós concordem. O seu "like" pode não&lt;br&gt;
aparecer para outra pessoa imediatamente. Você pode vender um item do carrinho que acabou de ficar sem estoque (e terá que lidar com isso depois).&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;O Trabalho do Arquiteto:&lt;/strong&gt; Não é saber se "SQL é melhor". É perguntar: "Para &lt;em&gt;esta&lt;/em&gt; funcionalidade (ex: carrinho de compras vs. pagamento), o que é mais crítico: a disponibilidade (o cliente conseguir usar) ou a consistência (o dado estar 100% correto agora)?"&lt;/p&gt;

&lt;h3&gt;
  
  
  2. O Dilema da Comunicação: Síncrona vs. Assíncrona
&lt;/h3&gt;

&lt;p&gt;Como os seus serviços (ou microsserviços) devem conversar?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A "Boa Prática" (O Mito):&lt;/strong&gt; "Desacople tudo! Use um Message Broker (Kafka/RabbitMQ) para tudo!"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Análise de Tradeoff:&lt;/strong&gt; A comunicação assíncrona é poderosa, mas é incrivelmente complexa.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;O Tradeoff (Acoplamento):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Escolhendo Síncrona (ex: uma chamada de API REST direta):&lt;/strong&gt; Você &lt;strong&gt;GANHA&lt;/strong&gt; simplicidade e imediatismo. Você chama a API &lt;code&gt;ServicoB.FazerCoisa()&lt;/code&gt; e sabe &lt;em&gt;imediatamente&lt;/em&gt; se funcionou ou falhou. O código é linear e fácil de debugar.&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O Custo (Tradeoff):&lt;/strong&gt; Você &lt;strong&gt;PAGA&lt;/strong&gt; com acoplamento temporal. Se o &lt;code&gt;ServicoB&lt;/code&gt; estiver lento ou cair, o seu &lt;code&gt;ServicoA&lt;/code&gt; trava junto, esperando uma resposta. Uma falha se espalha em cascata.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Escolhendo Assíncrona (ex: publicando um evento &lt;code&gt;CoisaFeita&lt;/code&gt;):&lt;/strong&gt; Você &lt;strong&gt;GANHA&lt;/strong&gt; resiliência. O &lt;code&gt;ServicoA&lt;/code&gt; apenas "grita" o evento e segue a vida. Se o &lt;code&gt;ServicoB&lt;/code&gt; estiver caído, ele não é afetado. O &lt;code&gt;ServicoB&lt;/code&gt; processa o evento quando voltar.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;O Custo (Tradeoff):&lt;/strong&gt; Você &lt;strong&gt;PAGA&lt;/strong&gt; com complexidade massiva. Como você monitora se o evento foi processado? Como lida com re-tentativas? E a ordem dos eventos? Você trocou a simplicidade de desenvolvimento por resiliência operacional.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;O Trabalho do Arquiteto:&lt;/strong&gt; "Para &lt;em&gt;este&lt;/em&gt; fluxo de trabalho, o que é mais importante: a resiliência (assíncrono) ou a simplicidade de desenvolvimento e debug (síncrono)?"&lt;/p&gt;

&lt;h3&gt;
  
  
  3. O Dilema do Código: Desempenho vs. Manutenibilidade
&lt;/h3&gt;

&lt;p&gt;Como você deve escrever aquele novo módulo?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;A "Boa Prática" (O Mito):&lt;/strong&gt; "Clean Code! SOLID, DRY, KISS! Crie todas as abstrações corretas!"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Análise de Tradeoff:&lt;/strong&gt; Cada camada de abstração que facilita a manutenção (o "Clean Code") tem um custo de desempenho.&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;O Tradeoff (Abstração):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Escolhendo Manutenibilidade (Clean Code):&lt;/strong&gt; Você &lt;strong&gt;GANHA&lt;/strong&gt; um código fácil de testar, fácil de mudar e fácil de entender (novos devs agradecem). Você usa Repositórios, Casos de Uso, Injeção de Dependência, etc.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O Custo (Tradeoff):&lt;/strong&gt; Você &lt;strong&gt;PAGA&lt;/strong&gt; com desempenho (mesmo que pequeno) e "boilerplate". Cada camada de abstração adiciona &lt;em&gt;overhead&lt;/em&gt; (mais chamadas de função, mais alocação de memória, mais indireção).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Escolhendo Desempenho (”quick and dirty”):&lt;/strong&gt; Você &lt;strong&gt;GANHA&lt;/strong&gt; velocidade bruta. Você escreve uma query SQL "crua" e otimizada que faz 5 joins direto no seu &lt;em&gt;handler&lt;/em&gt;, pula o ORM e retorna os dados em 10ms.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;O Custo (Tradeoff):&lt;/strong&gt; Você &lt;strong&gt;PAGA&lt;/strong&gt; com manutenibilidade. Esse código é frágil, difícil de testar e um pesadelo para alterar. Se o schema do banco mudar, boa sorte.&lt;/li&gt;
&lt;/ul&gt;
&lt;/blockquote&gt;

&lt;p&gt;&lt;strong&gt;O Trabalho do Arquiteto:&lt;/strong&gt;&lt;br&gt;
 "Esta é uma parte crítica do sistema (hot path), como um loop de renderização de um jogo, onde cada nanossegundo conta? Ou é uma tela de CRUD de ‘configurações’ que é usada uma vez por mês e onde a velocidade de modificação é mais importante que a de execução?"&lt;/p&gt;




&lt;h2&gt;
  
  
  Como Começar a Pensar em Tradeoffs
&lt;/h2&gt;

&lt;p&gt;A partir de agora, "depende" é sua nova resposta favorita. Mas como podemos transformar essa filosofia em uma ferramenta prática? Como se constrói esse "músculo" de análise de tradeoffs?&lt;/p&gt;

&lt;p&gt;Aqui estão algumas ações práticas que você pode começar a aplicar &lt;em&gt;hoje&lt;/em&gt;:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Mude o seu Vocabulário (e suas Perguntas)
&lt;/h3&gt;

&lt;p&gt;O primeiro passo é o mais simples e o mais poderoso. Pare de perguntar "Qual é o &lt;em&gt;melhor&lt;/em&gt;?" e comece a perguntar "Quais são os &lt;em&gt;custos&lt;/em&gt;?".&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Pare de perguntar: "Qual é o melhor banco de dados para esse projeto?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comece a perguntar:&lt;/strong&gt; "Quais são os tradeoffs de usar PostgreSQL vs. MongoDB &lt;em&gt;para este caso de uso específico&lt;/em&gt;? O que estamos otimizando? Velocidade de consulta, custo de infra, ou flexibilidade de esquema?"&lt;/p&gt;

&lt;p&gt;Pare de dizer: "Deveríamos usar microsserviços porque é a 'boa prática'."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comece a dizer:&lt;/strong&gt; "Se usarmos microsserviços, ganharemos X, mas pagaremos com a complexidade Y. Esse &lt;em&gt;ganho&lt;/em&gt; justifica esse &lt;em&gt;custo&lt;/em&gt;... &lt;em&gt;agora&lt;/em&gt;?"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  2. Identifique os "Drivers" do Negócio
&lt;/h3&gt;

&lt;p&gt;Você não pode pesar um tradeoff se não souber o que é mais importante na balança. A arquitetura &lt;em&gt;sempre&lt;/em&gt; deve servir aos objetivos do negócio.&lt;/p&gt;

&lt;p&gt;Antes de qualquer decisão técnica grande, pergunte ao seu time, PM ou líder: &lt;br&gt;
"Qual é o requisito mais importante para esta funcionalidade?"&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;É &lt;strong&gt;Velocidade de Entrega (Time-to-Market)&lt;/strong&gt;? (Então talvez a solução mais "simples" e rápida de implementar, mesmo que "feia", seja a melhor &lt;em&gt;agora&lt;/em&gt;.)&lt;/li&gt;
&lt;li&gt;É &lt;strong&gt;Desempenho Extremo&lt;/strong&gt;? (Talvez aquele "Clean Code" com 5 camadas de abstração seja um luxo que não podemos pagar &lt;em&gt;aqui&lt;/em&gt;.)&lt;/li&gt;
&lt;li&gt;É &lt;strong&gt;Custo Operacional&lt;/strong&gt;? (Usar aquele serviço gerenciado da AWS vai ser mais caro ou mais barato do que manter um cluster Kubernetes nós mesmos?)&lt;/li&gt;
&lt;li&gt;É &lt;strong&gt;Manutenibilidade&lt;/strong&gt;? (Nosso time é júnior e precisa de um código fácil de entender, mesmo que seja mais lento?)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Só depois de saber o que você está otimizando, você pode analisar o tradeoff de forma justa.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Use (e aprenda a gostar dos) ADRs: Architecture Decision Records
&lt;/h3&gt;

&lt;p&gt;Este é o &lt;em&gt;takeaway&lt;/em&gt; mais prático que você pode ter. "Arquitetura de Software: As partes difíceis" defende veementemente a &lt;strong&gt;documentação das suas decisões&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Um &lt;strong&gt;ADR (Architecture Decision Record)&lt;/strong&gt; é um documento de Markdown simples e curto onde seu time registra uma decisão de arquitetura importante. O formato básico é:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Título:&lt;/strong&gt; "Escolha de Comunicação entre Serviço A e B"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Contexto:&lt;/strong&gt; "Precisamos que A notifique B quando um pedido for pago."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decisão:&lt;/strong&gt; "Escolhemos usar Comunicação Síncrona (API REST) por enquanto."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consequências (O Tradeoff):&lt;/strong&gt; "Ganhamos simplicidade de implementação e debug. &lt;em&gt;Aceitamos&lt;/em&gt; o risco de que, se B falhar, A falhará junto (acoplamento temporal). Isso é aceitável &lt;em&gt;hoje&lt;/em&gt; porque..."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Opções Consideradas (e por que foram descartadas):&lt;/strong&gt; "Consideramos RabbitMQ, mas o custo de infra e complexidade de
monitoramento foi considerado muito alto para a fase atual do projeto."&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Um ADR é o antídoto perfeito para a pergunta que todo dev faz 6 meses depois de que algo foi desenvolvido: &lt;em&gt;"Por que diabos nós fizemos isso desse jeito?"&lt;/em&gt; Ele força você a articular e lembrar dos porquês dos tradeoffs que você analisou no momento da decisão.&lt;/p&gt;




&lt;h2&gt;
  
  
  Conclusão: A Habilidade Mais Importante
&lt;/h2&gt;

&lt;p&gt;Por muito tempo, eu interpretava a minha senioridade pela quantidade de "boas práticas" que eu conhecia. Eu achava que o melhor arquiteto era o cara que mais tinha o &lt;em&gt;blueprint&lt;/em&gt; da "forma certa" de construir tudo.&lt;/p&gt;

&lt;p&gt;Depois de adquirir mais experiência, colecionar algumas cicatrizes de projetos, como a que mencionei no meu artigo sobre &lt;a href="https://dev.to/dchueri/o-custo-da-perfeicao-172"&gt;overengineering&lt;/a&gt;, minha perspectiva mudou completamente. E com certeza o livro “Arquitetura de Software: As partes difíceis” foi uma das lentes que me fez enxergar os porquês de alguns desafios que encontrei (pelo menos uma parcela deles). &lt;/p&gt;

&lt;p&gt;A verdadeira senioridade não é sobre ter todas as respostas. É sobre entender que &lt;strong&gt;não existem respostas fáceis&lt;/strong&gt;, apenas tradeoffs difíceis.&lt;/p&gt;

&lt;p&gt;A engenharia de software não é uma ciência exata como a matemática; ela é uma disciplina de gerenciamento de complexidade e concessões. O "Mito da Boa Prática" nos dá uma falsa sensação de segurança, enquanto a "Análise de Tradeoffs" nos dá a verdadeira ferramenta para navegar na complexidade do mundo real.&lt;/p&gt;

&lt;p&gt;Portanto, da próxima vez que você se encontrar em um debate sobre qual tecnologia é "melhor", pare e mude a pergunta: &lt;strong&gt;"Quais são os tradeoffs que estamos fazendo aqui?"&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;Obrigado pela sua leitura e atenção até aqui. Eu adoraria saber de você, a nossa comunidade é feita de experiências do mundo real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Qual foi o tradeoff mais difícil (ou doloroso) que você teve que fazer recentemente em um projeto?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Deixe sua história nos comentários!&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>career</category>
      <category>webdev</category>
      <category>backend</category>
    </item>
    <item>
      <title>Shopify POS Extensions: Connecting the Backend Securely</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Fri, 24 Oct 2025 15:09:03 +0000</pubDate>
      <link>https://dev.to/dchueri/shopify-pos-extensions-connecting-the-backend-securely-5fld</link>
      <guid>https://dev.to/dchueri/shopify-pos-extensions-connecting-the-backend-securely-5fld</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/dchueri/shopify-pos-extensions-conectando-o-backend-com-seguranca-2ljm"&gt;Versão em Português&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building a &lt;a href="https://shopify.dev/docs/apps/pos" rel="noopener noreferrer"&gt;Shopify POS UI Extension&lt;/a&gt; is a great way to add custom features to your Shopify POS. You use the Shopify CLI, generate your extension, customize the React component, and everything seems to work fine... until you need to make &lt;em&gt;that one&lt;/em&gt; &lt;code&gt;fetch&lt;/code&gt; call to your backend.&lt;/p&gt;

&lt;p&gt;At this point, the following questions might come up:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How can my backend know this request is legitimate?&lt;/li&gt;
&lt;li&gt;How does it know which shop this call is coming from without exposing that data in the request?&lt;/li&gt;
&lt;li&gt;How can we prevent someone malicious from pretending to be our extension and sending fake data?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you've ever faced this, you know the answer is &lt;strong&gt;&lt;a href="https://jwt.io/introduction" rel="noopener noreferrer"&gt;JWT&lt;/a&gt; authentication&lt;/strong&gt;. Shopify solves this elegantly using &lt;em&gt;Session Tokens&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;The official documentation &lt;a href="https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens/set-up-session-tokens" rel="noopener noreferrer"&gt;shows the way&lt;/a&gt;, but it can be a bit abstract.&lt;/p&gt;

&lt;p&gt;In this article, I'll show you the practical, straightforward guide on how &lt;em&gt;we&lt;/em&gt; implemented this end-to-end authentication: from the &lt;code&gt;fetch&lt;/code&gt; in the POS Extension to the verification middleware in our Node.js backend, using the official library.&lt;/p&gt;

&lt;p&gt;Let's dive in!&lt;/p&gt;

&lt;h2&gt;
  
  
  The General Flow
&lt;/h2&gt;

&lt;p&gt;Before diving into the code, here’s a summary of the authentication flow in 3 steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Frontend (POS Extension):&lt;/strong&gt; Our extension uses the &lt;a href="https://shopify.dev/docs/api/pos-ui-extensions/2025-07/apis/session-api" rel="noopener noreferrer"&gt;Session API&lt;/a&gt; (&lt;code&gt;useApi&lt;/code&gt;) to request a unique JWT from Shopify at the time of the request.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;The Request:&lt;/strong&gt; The frontend sends this JWT in the &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; header to our backend.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Backend (Your Server):&lt;/strong&gt; Our middleware intercepts the call, uses the &lt;code&gt;@shopify/shopify-api&lt;/code&gt; library to verify if the token is valid, and if so, identifies the shop and allows the request to continue.&lt;/li&gt;
&lt;/ol&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%2F624xyrzwh6romp7doqr0.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%2F624xyrzwh6romp7doqr0.png" alt="A simple diagram showing: POS Extension -&amp;gt; (JWT) -&amp;gt; Backend -&amp;gt; (Verifies JWT) -&amp;gt; OK" width="800" height="343"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend - Getting the Token
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: The token retrieval method may vary depending on the Shopify API version used. In this article, we are using version 2025-07.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Shopify makes this step very simple. Inside your extension (which likely uses React), you can use the &lt;code&gt;useApi&lt;/code&gt; hook from the &lt;code&gt;@shopify/ui-extensions-react/point-of-sale&lt;/code&gt; package.&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="c1"&gt;// extensions/pos-ui-your-extension/src/YourComponent.jsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;reactExtension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;useApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@shopify/ui-extensions-react/point-of-sale&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;SmartGridModal&lt;/span&gt; &lt;span class="o"&gt;=&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="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;currentSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getSessionToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;useApi&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pos.home.modal.render&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;shopId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;staffMemberId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentSession&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sessionToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSessionToken&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="err"&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nf"&gt;getSessionToken&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;newToken&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nf"&gt;setSessionToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ScreenOne&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Screen One Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;shopId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;shopId&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;locationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;locationId&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;staffId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;staffMemberId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
 &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;sessionToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sessionToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
 &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Screen&lt;/span&gt;&lt;span class="err"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;reactExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pos.home.modal.render&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SmartGridModal&lt;/span&gt; &lt;span class="o"&gt;/&amp;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;&lt;strong&gt;Note&lt;/strong&gt;: In cases where the call is triggered by an action (e.g., clicking a "Submit" button triggers a fetch to the backend with a body to be processed), I personally prefer to trigger &lt;code&gt;getSessionToken&lt;/code&gt; inside the handler that will make the call. This ensures that the sessionToken is valid at the time of sending.&lt;/p&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onClickHandle&lt;/span&gt; &lt;span class="o"&gt;=&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="nf"&gt;getSessionToken&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;newToken&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &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="s2"&gt;[https://myapi.com](https://myapi.com)&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="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;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="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;/blockquote&gt;

&lt;h2&gt;
  
  
  Backend - Verifying the Token
&lt;/h2&gt;

&lt;p&gt;This is the crucial part. The backend will receive the token from the POS Extension and needs to do two things:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Authenticate: Is it a valid token and was it really issued by Shopify?&lt;/li&gt;
&lt;li&gt;Authorize: Does this shop (which the token claims to represent) exist in our database?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;We'll use the official Shopify library to do the heavy lifting.&lt;/p&gt;

&lt;h3&gt;
  
  
  API Configuration (Your shopifyApi.js)
&lt;/h3&gt;

&lt;p&gt;First, we need to ensure our backend knows our API keys. You probably have a configuration file similar to this one (let's call it config/shopifyApi.js) that initializes the Shopify library:&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;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@shopify/shopify-api/adapters/node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shopifyApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApiVersion&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@shopify/shopify-api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// We initialize the 'shopify' object, which will be used as the main Shopify instance throughout the API&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shopify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shopifyApi&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SHOPIFY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;apiSecretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SHOPIFY_API_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;hostName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SCOPES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2025-07&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Use your version&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you're not familiar with &lt;code&gt;@shopify/shopify-api&lt;/code&gt;, &lt;a href="https://github.com/Shopify/shopify-app-js/tree/main/packages/apps/shopify-api#getting-started" rel="noopener noreferrer"&gt;access the complete documentation here&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Creating the Middleware
&lt;/h3&gt;

&lt;p&gt;Now we will finally create the authentication and authorization middleware for the routes that will be used by the POS Extension. It will run before the protected routes.&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="c1"&gt;// src/middlewares/auth.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StoreRepository&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../repositories/StoreRepository.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shopify&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../config/shopifyApi.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// The config from Step 2a&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&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="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// 1. Get the token from the request header&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// 2. THE MAGIC: Decode and Verify the Token&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// The `decodeSessionToken` does everything:&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// - Fetches Shopify's keys&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// - Verifies the token's signature&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// - Checks if it has expired&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// - If everything is OK, returns the payload (the data)&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&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;shopify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeSessionToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// 3. Get the shop's URL (e.g., "[https://my-store.myshopify.com](https://my-store.myshopify.com)")&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shopId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// 4. Authorization: Check if this shop exists in OUR database&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&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;Store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;shopId&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&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;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// The token is valid, but the shop is not installed in our app&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Store not found&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// 5. Done! We attach the store to the request&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// Now, all subsequent routes will have access to `req.store`&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Continues the flow to the route&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// If `decodeSessionToken` fails or the store is not found,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// we return 401 Unauthorized.&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To summarize what happens in the code above:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;shopify.session.decodeSessionToken(token)&lt;/code&gt; performs &lt;strong&gt;authentication&lt;/strong&gt; (proves the user is who they claim to be, i.e., Shopify).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Store.findOne()&lt;/code&gt; performs &lt;strong&gt;authorization&lt;/strong&gt; (proves the user has permission to access the resource, in the example's case, checks if the shop is registered in the system).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Applying the Middleware to Routes
&lt;/h3&gt;

&lt;p&gt;With the middleware ready, just add it to the routes that need protection.&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="c1"&gt;// src/routes.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;shopifyAuthMiddleware&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./middleware/shopifyAuth.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ... other application routes&lt;/span&gt;

&lt;span class="c1"&gt;// This route is public, no token needed&lt;/span&gt;
&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/public/health-check&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;res&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="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&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;// This route is PROTECTED by our middleware&lt;/span&gt;
&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/private/my-data&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;shopifyAuthMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- The middleware goes here!&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="c1"&gt;// If the code reached here, the token is valid and req.store exists!&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="nx"&gt;res&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="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Hello, shop &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="err"&gt; &lt;/span&gt; &lt;span class="err"&gt; &lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="err"&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;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;It's done! With this architecture, we have closed the end-to-end authentication loop.&lt;/p&gt;

&lt;p&gt;Let's recap what we did:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;On the Frontend (POS Extension): We used the &lt;code&gt;getSessionToken&lt;/code&gt; hook to get a valid session token (JWT) for each API call.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;On the Backend (Middleware): We used the &lt;code&gt;shopify.session.decodeSessionToken&lt;/code&gt; method to validate the token's signature against Shopify's public keys.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;In the Route: We checked if the token's shop exists in our database (&lt;code&gt;Store.findOne&lt;/code&gt;) before allowing access.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now, your backend has a robust and secure way to ensure that every request coming from your POS Extension is legitimate and authenticated. No more "invalid token" or mysterious calls!&lt;/p&gt;

&lt;h3&gt;
  
  
  So, how do you do it?
&lt;/h3&gt;

&lt;p&gt;This was the way we found to implement authentication following Shopify's best practices.&lt;/p&gt;

&lt;p&gt;How does your team handle extension authentication? Do you use a different approach? Did you encounter any problems along the way?&lt;/p&gt;

&lt;p&gt;Leave your comment below! Let's share experiences.&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>tutorial</category>
      <category>jwt</category>
      <category>apps</category>
    </item>
    <item>
      <title>Shopify POS Extensions: Conectando o Backend com Segurança</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Fri, 24 Oct 2025 15:04:26 +0000</pubDate>
      <link>https://dev.to/dchueri/shopify-pos-extensions-conectando-o-backend-com-seguranca-2ljm</link>
      <guid>https://dev.to/dchueri/shopify-pos-extensions-conectando-o-backend-com-seguranca-2ljm</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/dchueri/shopify-pos-extensions-connecting-the-backend-securely-5fld"&gt;English Version&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Construir uma &lt;a href="https://shopify.dev/docs/apps/pos" rel="noopener noreferrer"&gt;Shopify POS UI Extension&lt;/a&gt; é uma ótima maneira de adicionar funcionalidades personalizadas ao seu Shopify POS. Você usa o Shopify CLI, gera sua extensão, personaliza o componente React e tudo parece funcionar bem... até você precisar fazer &lt;em&gt;aquela&lt;/em&gt; chamada &lt;code&gt;fetch&lt;/code&gt; para o seu backend.&lt;/p&gt;

&lt;p&gt;Nesse momento podem surgir as seguintes perguntas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Como meu backend pode saber que essa requisição é legítima?&lt;/li&gt;
&lt;li&gt;Como ele sabe de qual loja (shop) essa chamada está vindo sem expor esse dado na requisição?&lt;/li&gt;
&lt;li&gt;Como impedir que alguém mal-intencionado finja ser a nossa extensão e envie dados falsos?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você já se deparou com isso, você sabe que a resposta é &lt;strong&gt;autenticação &lt;a href="https://jwt.io/introduction" rel="noopener noreferrer"&gt;JWT&lt;/a&gt;&lt;/strong&gt;. A Shopify resolve isso de forma elegante usando &lt;em&gt;Session Tokens&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;A documentação oficial &lt;a href="https://shopify.dev/docs/apps/build/authentication-authorization/session-tokens/set-up-session-tokens" rel="noopener noreferrer"&gt;mostra o caminho&lt;/a&gt;, mas pode ser um pouco abstrata.&lt;/p&gt;

&lt;p&gt;Neste artigo, vou mostrar o guia prático e direto de como &lt;em&gt;nós&lt;/em&gt; implementamos essa autenticação de ponta a ponta: do &lt;code&gt;fetch&lt;/code&gt; na POS Extension até o middleware de verificação no nosso backend Node.js, utilizando a biblioteca oficial.&lt;/p&gt;

&lt;p&gt;Vamos lá!&lt;/p&gt;

&lt;h2&gt;
  
  
  O Fluxo Geral
&lt;/h2&gt;

&lt;p&gt;Antes de mergulhar no código, aqui está o resumo do fluxo de autenticação em 5 etapas:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt; &lt;strong&gt;Frontend (POS Extension):&lt;/strong&gt; Nossa extensão usa o &lt;a href="https://shopify.dev/docs/api/pos-ui-extensions/2025-07/apis/session-api" rel="noopener noreferrer"&gt;Session API&lt;/a&gt; (&lt;code&gt;useApi&lt;/code&gt;) para pedir um JWT exclusivo para a Shopify no momento da requisição.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;A Requisição:&lt;/strong&gt; O frontend envia esse JWT no cabeçalho &lt;code&gt;Authorization: Bearer &amp;lt;token&amp;gt;&lt;/code&gt; para o nosso backend.&lt;/li&gt;
&lt;li&gt; &lt;strong&gt;Backend (Seu Servidor):&lt;/strong&gt; Nosso middleware intercepta a chamada, usa a biblioteca &lt;code&gt;@shopify/shopify-api&lt;/code&gt; para verificar se o token é válido e, se for, identifica a loja e permite que a requisição continue.&lt;/li&gt;
&lt;/ol&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%2F6ibc3mhnk7k35jhcu4kl.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%2F6ibc3mhnk7k35jhcu4kl.png" alt="Um diagrama simples mostrando: POS Extension -&amp;gt; (JWT) -&amp;gt; Backend -&amp;gt; (Verifica JWT) -&amp;gt; OK" width="800" height="348"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Frontend - Pegando o token
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Importante&lt;/strong&gt;: A forma de resgate do token pode variar conforme a versão da API Shopify utlizada. Neste artigo estamos utilizando a versão 2025-07.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A Shopify torna essa etapa bem simples. Dentro da sua extensão (que provavelmente utiliza React), você pode usar o hook &lt;code&gt;useApi&lt;/code&gt; do pacote &lt;code&gt;@shopify/ui-extensions-react/point-of-sale&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// extensions/pos-ui-sua-extensao/src/SeuComponente.jsx&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;reactExtension&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;useApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Screen&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@shopify/ui-extensions-react/point-of-sale&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;SmartGridModal&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;currentSession&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getSessionToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt;
    &lt;span class="nx"&gt;useApi&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pos.home.modal.render&lt;/span&gt;&lt;span class="dl"&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="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;shopId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;locationId&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;staffMemberId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;currentSession&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;sessionToken&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setSessionToken&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&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;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;getSessionToken&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;newToken&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;setSessionToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;newToken&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[])&lt;/span&gt;

    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Screen&lt;/span&gt; &lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ScreenOne&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Screen One Title&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="na"&gt;shopId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;shopId&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;userId&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;locationId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;locationId&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="na"&gt;staffId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;staffMemberId&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Text&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;sessionToken&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;sessionToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Text&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/Screen&lt;/span&gt;&lt;span class="err"&gt;&amp;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;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;reactExtension&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;pos.home.modal.render&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;SmartGridModal&lt;/span&gt; &lt;span class="o"&gt;/&amp;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;&lt;strong&gt;Obs&lt;/strong&gt;: Nos casos onde a chamada é provocada por uma ação (ex.: ao clicar no botão Enviar, é realizada uma fetch para o backend com um body que será processado) eu particularmente dou preferência para acionar o &lt;code&gt;getSessionToken&lt;/code&gt; dentro do handle que realizará a chamada. Isso garante que o sessionToken estará valido no momento do envio.&lt;/p&gt;


&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;onClickHandle&lt;/span&gt; &lt;span class="o"&gt;=&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;getSessionToken&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;newToken&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;headers&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;Authorization&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Bearer &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;newToken&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
    &lt;span class="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="s2"&gt;https://myapi.com&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="nx"&gt;headers&lt;/span&gt; &lt;span class="p"&gt;}).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;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="nx"&gt;res&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;/blockquote&gt;

&lt;h2&gt;
  
  
  Backend - Verificando o Token
&lt;/h2&gt;

&lt;p&gt;Essa é a parte crucial. O backend vai receber o token da POS Extension e precisa fazer duas coisas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Autenticar: É um token válido e foi realmente emitido pela Shopify?&lt;/li&gt;
&lt;li&gt;Autorizar: Essa loja (que o token diz representar) existe no nosso banco de dados?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Vamos usar a biblioteca oficial da Shopify para fazer o trabalho pesado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Configuração da API (Seu shopifyApi.js)
&lt;/h3&gt;

&lt;p&gt;Primeiro, precisamos garantir que o nosso backend conheça nossas chaves de API. Você provavelmente tem um arquivo de configuração parecido com esse (vamos chamar de config/shopifyApi.js) que inicializa a biblioteca da Shopify:&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;import&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@shopify/shopify-api/adapters/node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shopifyApi&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ApiVersion&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@shopify/shopify-api&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Inicializamos o objeto 'shopify' que será utilizado como instância principal da Shopify em toda a api&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shopify&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;shopifyApi&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SHOPIFY_API_KEY&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiSecretKey&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SHOPIFY_API_SECRET&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;hostName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;HOST&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;scopes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SCOPES&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;apiVersion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;2025-07&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// Use sua versão&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Caso você não esteja familiarizado com a &lt;code&gt;@shopify/shopify-api&lt;/code&gt;, &lt;a href="https://github.com/Shopify/shopify-app-js/tree/main/packages/apps/shopify-api#getting-started" rel="noopener noreferrer"&gt;acesse aqui&lt;/a&gt; a documentação completa.&lt;/p&gt;

&lt;h3&gt;
  
  
  Criando o Middleware
&lt;/h3&gt;

&lt;p&gt;Agora finalmente iremos criar o middleware de autenticação e autorização para as rotas que serão utilizadas pelo POS Extension. Ele será executado antes das rotas protegidas.&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="c1"&gt;// src/middlewares/auth.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;StoreRepository&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../repositories/StoreRepository.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shopify&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../config/shopifyApi.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// A config da Etapa 2a&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;next&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// 1. Pegue o token do header da requisição&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;token&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;headers&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;authorization&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;split&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)[&lt;/span&gt;&lt;span class="mi"&gt;1&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;token&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&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;// 2. A MÁGICA: Decodifica e Verifica o Token&lt;/span&gt;
    &lt;span class="c1"&gt;// O `decodeSessionToken` faz tudo:&lt;/span&gt;
    &lt;span class="c1"&gt;// - Pega as chaves da Shopify&lt;/span&gt;
    &lt;span class="c1"&gt;// - Verifica a assinatura do token&lt;/span&gt;
    &lt;span class="c1"&gt;// - Verifica se não expirou&lt;/span&gt;
    &lt;span class="c1"&gt;// - Se tudo estiver OK, retorna o payload (os dados)&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;payload&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;shopify&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;decodeSessionToken&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;token&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// 3. Pega a URL da loja (ex: "https://minha-loja.myshopify.com")&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shopId&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;dest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// 4. Autorização: Verifica se essa loja existe no NOSSO banco&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&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;Store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;findOne&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;shopId&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;store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="c1"&gt;// O token é válido, mas a loja não está instalada no nosso app&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Store not found&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;// 5. Feito! Anexamos a loja na requisição&lt;/span&gt;
    &lt;span class="c1"&gt;// Agora, todas as rotas seguintes terão acesso a `req.store`&lt;/span&gt;
    &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// Continua o fluxo para a rota&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Se `decodeSessionToken` falhar ou a loja não for encontrada,&lt;/span&gt;
    &lt;span class="c1"&gt;// retornamos 401 Unauthorized.&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;status&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;401&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Unauthorized&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resumindo o que ocorre no código acima:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;shopify.session.decodeSessionToken(token)&lt;/code&gt; realiza a &lt;strong&gt;autenticação&lt;/strong&gt; (prova que o usuário é quem diz ser, ou seja, a Shopify).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;Store.findOne()&lt;/code&gt; realiza a &lt;strong&gt;autorização&lt;/strong&gt; (prova que o usuário tem permissão para acessar o recurso, no caso do exemplo, verifica se a loja está cadastrada no sistema).&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Aplicando o Middleware nas rotas
&lt;/h3&gt;

&lt;p&gt;Com o middleware pronto, basta adicionar nas rotas que precisam de proteção.&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="c1"&gt;// src/routes.js&lt;/span&gt;

&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;shopifyAuthMiddleware&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./middleware/shopifyAuth.js&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// ... demais rotas da aplicação&lt;/span&gt;

&lt;span class="c1"&gt;// Esta rota é pública, não precisa de token&lt;/span&gt;
&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/public/health-check&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="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="nx"&gt;res&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="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;ok&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;// Esta rota é PROTEGIDA pelo nosso middleware&lt;/span&gt;
&lt;span class="nx"&gt;routes&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;/api/private/meus-dados&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;shopifyAuthMiddleware&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// &amp;lt;-- O middleware entra aqui!&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;res&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="c1"&gt;// Se o código chegou aqui, o token é válido e req.store existe!&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;req&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="nx"&gt;res&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="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`Olá, loja &lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;name&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;!`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Está feito! Com essa arquitetura fechamos o ciclo de autenticação de ponta a ponta.&lt;/p&gt;

&lt;p&gt;Vamos recapitular o que fizemos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;No Frontend (POS Extension): Usamos o hook getSessionToken para pegar um session token (JWT) válido a cada chamada de API.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;No Backend (Middleware): Usamos o método shopify.session.decodeSessionToken para validar a assinatura do token contra as chaves públicas da Shopify.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Na Rota: Verificamos se a loja do token existe em nosso banco de dados (Store.findOne) antes de permitir o acesso.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Agora, seu backend tem uma forma robusta e segura de garantir que cada requisição vinda da sua POS Extension é legítima e autenticada. Chega de "token inválido" ou chamadas misteriosas!&lt;/p&gt;

&lt;h3&gt;
  
  
  E aí, como você faz?
&lt;/h3&gt;

&lt;p&gt;Essa foi a maneira que encontramos para implementar a autenticação seguindo as melhores práticas da Shopify.&lt;/p&gt;

&lt;p&gt;Como sua equipe lida com a autenticação de extensões? Você usa uma abordagem diferente? Encontrou algum problema no caminho?&lt;/p&gt;

&lt;p&gt;Deixe seu comentário abaixo! Vamos trocar experiências.&lt;/p&gt;

</description>
      <category>shopify</category>
      <category>tutorial</category>
      <category>jwt</category>
      <category>apps</category>
    </item>
    <item>
      <title>O Custo da “Perfeição”</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Thu, 10 Apr 2025 00:25:40 +0000</pubDate>
      <link>https://dev.to/dchueri/o-custo-da-perfeicao-172</link>
      <guid>https://dev.to/dchueri/o-custo-da-perfeicao-172</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/dchueri/the-cost-of-perfection-49lk"&gt;English Version&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cada atividade desenvolvida em uma empresa de tecnologia possui suas peculiaridades e desafios. Sem dúvidas, o overengineering é um dos medos comuns a praticamente todas as áreas do desenvolvimento de software.&lt;/p&gt;

&lt;p&gt;Na constante corrida do mercado da tecnologia, as empresas sofrem uma “pressão natural” para adotarem novas soluções. Pensar demais antes de implementar uma arquitetura mais robusta ou uma nova ferramenta inovadora que ofereça performance superior e custos significativamente menores pode ser decisivo para alcançar destaque ou para gerar um atraso em relação aos concorrentes. Contudo, muitas vezes esquecemos que o excesso de recursos pode ser tão prejudicial quanto a sua ausência.&lt;/p&gt;

&lt;p&gt;Gostaria de trazer uma reflexão sobre o quão custoso ou mortífero para um produto pode ser a busca pela perfeição. Acho que a maioria dos desenvolvedores vai se identificar com o ciclo abaixo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Ideia genial surge:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Nossa, isso resolveria um problema real! Ninguém fez isso ainda??"&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Pesquisa de mercado/concorrência:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Hm, já tem algo parecido... mas acho que dá pra fazer melhor."&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Escolha da stack e arquitetura:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Ok, vou usar a stack mais moderna que conheço, com clean architecture, DDD, CQRS, event sourcing, monorepo com turborepo, microserviços com gRPC, e claro, full TypeScript."&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Estruturação exagerada:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Antes de escrever qualquer feature, vou montar os folders certinhos, configs, pipelines, testes, linter, husky, commitizen, docker-compose, etc."&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Cansaço/ansiedade/overload:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;"Putz... só de pensar em seguir isso tudo já dá preguiça. E ainda tenho aquele outro trabalho/freela/prazo..."&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Abandono silencioso:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“Um dia eu volto...”&lt;/p&gt;

&lt;p&gt;O repositório fica lá, com um README inacabado e três commits.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;E se pensarmos no impacto desse ciclo para uma empresa? No exemplo acima vemos o cenário onde o desenvolvedor decide ser dono do seu próprio negócio e por em prática uma ideia autoral, mas a única diferença entre essa realidade para uma em que esse mesmo desenvolvedor trabalha para uma empresa é o último passo, onde na maioria das vezes o projeto não é abandonado, muito pelo contrário, ele cresce.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que o excesso pode ser pior que a falta?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“A diferença entre o remédio e o veneno é a dose.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Essa frase, atribuída ao médico Paracelso, transcende a medicina e se aplica perfeitamente ao desenvolvimento de software. O sistema que você desenvolveu pode apresentar deficiências ou patologias; porém, iniciar a aplicação de todos os “remédios” disponíveis, sem compreender a real necessidade ou o porquê de cada solução, pode agravar ainda mais os problemas.&lt;/p&gt;

&lt;p&gt;Para ilustrar, veja os pontos negativos de cada extremo:&lt;/p&gt;

&lt;h3&gt;
  
  
  Falta:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dificuldade de manutenção:&lt;/strong&gt; A falta de organização e padronização na base de código pode transformar a busca pela causa de um bug em um verdadeiro martírio, mesmo para o próprio desenvolvedor que criou a funcionalidade.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Falta de escalabilidade;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Baixo nível de confiabilidade;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Desempenho ineficiente;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Queda no ritmo das entregas.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Excesso:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Dificuldade de manutenção:&lt;/strong&gt; Complexidades desnecessárias podem tornar o código confuso e difícil de atualizar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Necessidade de um nível técnico elevado por parte do time;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A soma dos problemas citados na falta, potencializados pela complexidade adicional.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Causas e “Miragens Técnicas”
&lt;/h2&gt;

&lt;p&gt;As causas para a falta e o excesso de engenharia podem seguir caminhos opostos. Enquanto a carência de engenharia costuma estar relacionada ao débito técnico ou ao raciocínio imediatista, o excesso muitas vezes surge da busca incessante pela perfeição, combinada com um conhecimento superficial de diversas ferramentas e um raciocínio hiperprudente.&lt;/p&gt;

&lt;p&gt;Um programador com uma “maleta recheada de ferramentas” não necessariamente sabe o momento ideal para usar cada uma delas. No mundo atual, onde a informação circula rapidamente, é comum conhecer diversas soluções por meio de vídeos curtos ou cursos rápidos. Contudo, compreender superficialmente um conceito (explicado em um vídeo de poucos minutos) não equivale a dominar sua aplicação em projetos reais.&lt;/p&gt;

&lt;p&gt;Devemos ter cuidado para não confundir “conhecer” com “dominar”. Especialmente quando estamos aprendendo um novo framework, é importante evitar as “miragens técnicas” que podem nos levar a acreditar que entendemos o suficiente para aplicar soluções complexas sem uma análise profunda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Especulações e Suas Ramificações
&lt;/h2&gt;

&lt;p&gt;Durante pesquisas sobre o tema, notei que muitos artigos e vídeos (irei disponibilizar alguns links no final) destacam a “especulação” como um fator comum. Essa especulação gera diversas ramificações, como:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Escopo mal definido:&lt;/strong&gt; Quanto mais vaga a definição dos requisitos, mais amplo tende a ser o código desenvolvido para “proteger” contra incertezas.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cobertura excessiva:&lt;/strong&gt; Desenvolvedores, na tentativa de cobrir todos os cenários, podem acabar expandindo tarefas simples para problemas maiores, especialmente quando não há desafios empolgantes ou prazos curtos.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Além disso, o tédio pode levar à superengenharia. Existem dois perfis profissionais comuns no desenvolvimento de software:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;O profissional que executa suas tarefas de forma pontual, recorrendo a hábitos que, em momentos de ociosidade, se refletem em comportamentos pouco produtivos;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Aquele que está sempre em busca de aprender e aplicar novas tecnologias.&lt;/p&gt;

&lt;p&gt;Embora o segundo perfil seja geralmente mais produtivo, se não for desafiado com tarefas estimulantes, pode transformar uma solução simples em um problema complexo na tentativa de experimentar novidades.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&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%2Fc0r9xhud0dd2n28ypxai.jpeg" 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%2Fc0r9xhud0dd2n28ypxai.jpeg" alt="Gráfico de complexidade do código com base na experiência do desenvolvedor" width="800" height="623"&gt;&lt;/a&gt;&lt;br&gt;
(&lt;a href="https://www.mindtheproduct.com/overengineering-can-kill-your-product/" rel="noopener noreferrer"&gt;Overengineering can kill your product&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Outro exemplo clássico é a &lt;strong&gt;otimização prematura&lt;/strong&gt;. Preparar um sistema para absorver um alto tráfego com uma infraestrutura excessivamente complicada, antes mesmo de ter usuários, é uma armadilha. Arquiteturas mirabolantes e complexas podem não ser necessárias se seus pontos fortes não se encaixarem no contexto atual. Uma solução boa e escalável é aquela que traça uma trilha clara para o crescimento e facilita as próximas decisões, sem tentar abarcar todos os cenários possíveis.&lt;/p&gt;
&lt;h2&gt;
  
  
  Explorando as Consequências
&lt;/h2&gt;

&lt;p&gt;Toda decisão envolve trade-offs. A complexidade crescente aumenta a curva de adaptação dos novos membros da equipe, elevando os custos. Em alguns casos, esse custo pode ser compensado no longo prazo, mas, frequentemente, a complexidade excessiva gera dependência dos desenvolvedores mais experientes e dificulta o trabalho dos menos experientes, acumulando débito técnico. Essa situação se reflete na dificuldade para debugar, na manutenção onerosa e no aumento dos custos com infraestrutura.&lt;/p&gt;

&lt;p&gt;Fugindo um pouco da esfera do desenvolvimento de código e entrando na parte de &lt;strong&gt;gerenciamento de processos&lt;/strong&gt;, no &lt;strong&gt;Scrum&lt;/strong&gt;, por exemplo, apenas o ato de granularizar melhor as atividades já tem um impacto direto na regra &lt;strong&gt;INVEST&lt;/strong&gt; (Independente, Negociável, Valioso, Estimável, Pequeno, Testável). Isso facilita significativamente o refinamento e permite estimativas mais precisas e realistas. Vamos a um exemplo simples:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Card 1 --

Nome: Desenvolver uma tela de login

Descrição:
[ ] O usuário deverá conseguir realizar o login e recuperar a senha.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;








&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Card 2 --

Nome: Desenvolver uma tela de login

Descrição:
[ ] Deve possuir um campo de E-mail, com validação de formato e feedback visual;
[ ] Deve possuir um campo de Senha, com validação de tamanho (mínimo de 6 caracteres) e feedback visual;
[ ] Deve haver um link “Recuperar senha”, que redireciona o usuário para uma outra página (essa página será desenvolvida posteriormente).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ao se deparar com o &lt;strong&gt;Card 1&lt;/strong&gt;, surgem imediatamente uma série de dúvidas e possibilidades na mente do desenvolvedor: o login será feito com e-mail ou username? As validações serão feitas apenas no backend ou também no frontend? Quais as regras exatas de validação? Quanto tempo levaria para desenvolver a página de recuperação de senha? Enviaremos o link por e-mail ou SMS? Talvez seja melhor criar algo mais flexível desde já para facilitar futuras alterações...&lt;/p&gt;

&lt;p&gt;Esse cenário tende a resultar em estimativas infladas, maior esforço para entrega, risco elevado de &lt;strong&gt;fuga do escopo&lt;/strong&gt;, maior probabilidade de adicionar funcionalidades desnecessárias e, claro, &lt;strong&gt;overengineering&lt;/strong&gt;. A sensação é semelhante àquelas cenas clássicas de desenhos animados, onde a sombra na parede parece ser de um monstro assustador, mas ao acender a luz, percebemos que era apenas um bichinho de pelúcia.&lt;/p&gt;

&lt;p&gt;Esse efeito psicológico impacta diretamente a &lt;strong&gt;pró-atividade&lt;/strong&gt; da equipe e aumenta os níveis de &lt;strong&gt;estresse&lt;/strong&gt;, comprometendo (e muito) a adesão aos &lt;strong&gt;valores do Scrum&lt;/strong&gt;: foco, coragem, comprometimento, respeito e abertura.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como Evitar o Overengineering?
&lt;/h2&gt;

&lt;p&gt;O principal caminho para evitar o overengineering é questionar o porquê de cada decisão, mantendo o foco na resolução de problemas reais. Algumas diretrizes que ajudam nesse processo são:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep It Simple, Stupid (KISS):&lt;/strong&gt; Busque sempre a solução mais simples que atenda aos requisitos do problema. Isso garante um código mais legível, com menor necessidade de um alto nível técnico para manutenção e entendimento.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You Ain’t Gonna Need It (YAGNI):&lt;/strong&gt; Se não há uma demanda concreta para determinada funcionalidade, não a implemente. Soluções para problemas imaginários geram código desnecessário e desperdício de tempo. Estudos, como o realizado pela Pennsylvania State University, mostram que aproximadamente 91% dos problemas especulados não se concretizam.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Existem algumas formas mais específicas de se evitar, dependendo do seu papel no ciclo de desenvolvimento. No caso de uma liderança, converse com sua equipe e esteja aberto a novas perspectivas. Se, ao apresentar uma solução, você se vê explicando demasiadamente seus fundamentos, talvez seja um sinal de que a solução está sendo mais complexa do que o necessário. Foque na otimização das partes com maior impacto no produto, aquele 1% do código que pode potencialmente otimizar 90% dos resultados, em vez de investir esforços em áreas de menor relevância. &lt;/p&gt;

&lt;p&gt;Nas funções de gerência, buscar ser o mais específico possível ao repassar as demandas tende a criar “guard rails” para o time de desenvolvimento, evitando que o desenvolvedor tenha que pensar demasiadamente sobre os possíveis problemas/cenários que a essa nova solução envolve. &lt;/p&gt;

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

&lt;p&gt;Este texto não foi criado para justificar divergências de opinião sobre arquiteturas ou ferramentas em projetos colaborativos, mas para enfatizar a importância de compreender os impactos de cada decisão. O equilíbrio é crucial para um desenvolvimento saudável. Busque conhecimento, experiências, teste e treine; porém, sempre lembrando de que um bom marinheiro não usa sua experiência para enfrentar mais tempestades, mas sim para evitá-las.&lt;/p&gt;

&lt;p&gt;Compartilhe suas experiências, revise seus projetos com essa perspectiva e mantenha o foco na criação de valor real através de soluções simples e eficazes.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Links e Referências&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.mindtheproduct.com/overengineering-can-kill-your-product/" rel="noopener noreferrer"&gt;Overengineering can kill your product&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@matthaus/over-engineering-e-times-ref%C3%A9ns-da-pr%C3%B3pria-arquitetura-5bd952b40d18" rel="noopener noreferrer"&gt;Over Engineering e times reféns da própria arquitetura&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@talkanel/drawing-the-line-where-engineering-become-over-engineering-a8af6674d266" rel="noopener noreferrer"&gt;Drawing the Line Where Engineering Become Over-Engineering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://c0de517e.blogspot.com/2016/10/over-engineering-root-of-all-evil.html" rel="noopener noreferrer"&gt;Over-engineering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=FLe5dvqV6xs" rel="noopener noreferrer"&gt;I Bet You’re Overengineering Your Software&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>discuss</category>
      <category>architecture</category>
    </item>
    <item>
      <title>The Cost of Perfection</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Thu, 10 Apr 2025 00:24:14 +0000</pubDate>
      <link>https://dev.to/dchueri/the-cost-of-perfection-49lk</link>
      <guid>https://dev.to/dchueri/the-cost-of-perfection-49lk</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/dchueri/o-custo-da-perfeicao-172"&gt;Versão em Português&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Each activity developed in a tech company has its own peculiarities and challenges. Without a doubt, overengineering is one of the common fears across practically every area of software development.&lt;/p&gt;

&lt;p&gt;In the constant race of the tech market, companies face a “natural pressure” to adopt new solutions. Overthinking before implementing a more robust architecture or an innovative new tool — one that offers superior performance and significantly lower costs — can be decisive in achieving a competitive edge or falling behind the competition. However, we often forget that an excess of features can be just as harmful as their absence.&lt;/p&gt;

&lt;p&gt;I'd like to reflect on how costly — or even deadly — the pursuit of perfection can be for a product. I think most developers will relate to the cycle below:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Brilliant idea pops up:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“Wow, this would solve a real problem! No one’s done this yet??”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Market/competitor research:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“Hmm, there's something similar… but I think I can do it better.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Stack and architecture selection:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“Okay, I’ll use the most modern stack I know, with clean architecture, DDD, CQRS, event sourcing, monorepo with turborepo, microservices with gRPC, and of course, full TypeScript.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Overstructuring:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“Before writing any feature, I’ll set up all the folders just right, configs, pipelines, tests, linter, husky, commitizen, docker-compose, etc.”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Fatigue/anxiety/overload:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“Ugh… just thinking about doing all of this is exhausting. And I still have that other job/freelance/deadline…”&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Silent abandonment:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;“One day I’ll come back…”&lt;/p&gt;

&lt;p&gt;The repo stays there, with an unfinished README and three commits.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Now imagine the impact of this cycle on a company. In the example above, the developer decides to become their own boss and put an original idea into practice — but the only difference between this and a scenario where the same developer works for a company is the last step. In that case, the project usually isn’t abandoned — on the contrary, it grows.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Can Too Much Be Worse Than Too Little?
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;“The difference between a remedy and a poison is the dose.”&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;This quote, attributed to the physician Paracelsus, goes far beyond medicine and perfectly applies to software development. The system you’re building might have deficiencies or pathologies; however, applying every possible “remedy” without understanding the real needs or rationale behind each solution might worsen the situation.&lt;/p&gt;

&lt;p&gt;To illustrate, here are the downsides of each extreme:&lt;/p&gt;

&lt;h3&gt;
  
  
  Too Little:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance difficulties:&lt;/strong&gt; Lack of organization and standardization in the codebase can turn debugging into a nightmare — even for the developer who wrote it.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lack of scalability;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Low reliability;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Inefficient performance;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Slower delivery pace.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Too Much:
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Maintenance difficulties:&lt;/strong&gt; Unnecessary complexities can make the code confusing and hard to update.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Need for a highly skilled team;&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;A combination of the problems mentioned in the “Too Little” section, amplified by additional complexity.&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Causes and “Technical Mirages”
&lt;/h2&gt;

&lt;p&gt;The causes of under- and overengineering may go in opposite directions. While a lack of engineering is often linked to technical debt or short-term thinking, excess usually stems from a relentless pursuit of perfection, mixed with a shallow understanding of various tools and an overly cautious mindset.&lt;/p&gt;

&lt;p&gt;A developer with a “toolbox full of tools” doesn’t necessarily know the right time to use each one. In today’s fast-paced information world, it’s common to learn about many solutions through short videos or quick courses. However, having a superficial understanding of a concept — as explained in a five-minute video — is not the same as mastering its real-world application.&lt;/p&gt;

&lt;p&gt;We must be careful not to confuse “knowing” with “mastering.” Especially when learning a new framework, it’s important to avoid “technical mirages” that trick us into thinking we understand enough to apply complex solutions without deep analysis.&lt;/p&gt;

&lt;h2&gt;
  
  
  Speculations and Their Branches
&lt;/h2&gt;

&lt;p&gt;While researching this topic, I noticed that many articles and videos (I'll link some at the end) highlight “speculation” as a common factor. Speculation leads to several consequences, such as:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Poorly defined scope:&lt;/strong&gt; The vaguer the requirements, the broader the code tends to be, in an attempt to “protect” against uncertainties.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Excessive coverage:&lt;/strong&gt; Developers, trying to cover all scenarios, may end up turning simple tasks into bigger problems — especially when there are no exciting challenges or tight deadlines.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Moreover, boredom can lead to overengineering. There are two common developer profiles:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The one who completes tasks on time, but whose idle habits lead to unproductive behavior;&lt;/li&gt;
&lt;li&gt;The one who is always looking to learn and apply new technologies.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;While the second profile is generally more productive, if not challenged with stimulating tasks, they may turn a simple solution into a complex one just to try out something new.&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%2Fc0r9xhud0dd2n28ypxai.jpeg" 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%2Fc0r9xhud0dd2n28ypxai.jpeg" alt="Code complexity graph based on developer experience" width="800" height="623"&gt;&lt;/a&gt;&lt;br&gt;&lt;br&gt;
(&lt;a href="https://www.mindtheproduct.com/overengineering-can-kill-your-product/" rel="noopener noreferrer"&gt;Overengineering can kill your product&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;Another classic example is &lt;strong&gt;premature optimization&lt;/strong&gt;. Preparing a system for high traffic with an overly complex infrastructure — before having any users — is a trap. Fancy and complex architectures may be unnecessary if their strengths don’t match the current context. A good, scalable solution is one that paves a clear path for growth and simplifies future decisions without trying to cover every possible scenario.&lt;/p&gt;
&lt;h2&gt;
  
  
  Exploring the Consequences
&lt;/h2&gt;

&lt;p&gt;Every decision involves trade-offs. Growing complexity increases the onboarding curve for new team members, raising costs. In some cases, this may pay off in the long run. But often, excessive complexity creates dependency on senior developers and makes work harder for junior ones, increasing technical debt. This shows up in harder debugging, expensive maintenance, and rising infrastructure costs.&lt;/p&gt;

&lt;p&gt;Stepping a bit outside the coding side and into &lt;strong&gt;process management&lt;/strong&gt;, in &lt;strong&gt;Scrum&lt;/strong&gt;, simply breaking down tasks more effectively has a direct impact on the &lt;strong&gt;INVEST&lt;/strong&gt; rule (Independent, Negotiable, Valuable, Estimable, Small, Testable). This significantly improves refinement and allows for more precise, realistic estimations. Here’s a simple example:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Card 1 --

Name: Develop a login screen

Description:
[ ] The user should be able to log in and recover their password.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;








&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;-- Card 2 --

Name: Develop a login screen

Description:
[ ] Must include an Email field with format validation and visual feedback;
[ ] Must include a Password field with length validation (minimum 6 characters) and visual feedback;
[ ] Must include a “Recover password” link that redirects the user to another page (this page will be developed later).
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When faced with &lt;strong&gt;Card 1&lt;/strong&gt;, developers immediately think of many questions and scenarios: will the login use email or username? Will validation happen only on the backend or also on the frontend? What are the exact validation rules? How long would it take to develop the password recovery page? Will the link be sent via email or SMS? Maybe it’s better to make something flexible now to allow future changes...&lt;/p&gt;

&lt;p&gt;This scenario often leads to inflated estimates, more effort required for delivery, a high risk of &lt;strong&gt;scope creep&lt;/strong&gt;, a greater chance of adding unnecessary features, and, of course, &lt;strong&gt;overengineering&lt;/strong&gt;. The feeling is similar to those classic cartoon scenes, where a shadow on the wall looks like a scary monster — but when the light comes on, it turns out to be just a stuffed toy.&lt;/p&gt;

&lt;p&gt;This psychological effect directly impacts the team’s &lt;strong&gt;proactiveness&lt;/strong&gt; and raises &lt;strong&gt;stress&lt;/strong&gt; levels, seriously undermining adherence to the &lt;strong&gt;Scrum values&lt;/strong&gt;: focus, courage, commitment, respect, and openness.&lt;/p&gt;

&lt;h2&gt;
  
  
  How to Avoid Overengineering?
&lt;/h2&gt;

&lt;p&gt;The key to avoiding overengineering is questioning the “why” behind each decision and staying focused on solving real problems. Some guidelines that help in this process are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Keep It Simple, Stupid (KISS):&lt;/strong&gt; Always aim for the simplest solution that meets the requirements. This ensures more readable code and reduces the need for high technical knowledge for maintenance and understanding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You Ain’t Gonna Need It (YAGNI):&lt;/strong&gt; If there’s no concrete demand for a feature, don’t implement it. Solutions for imaginary problems generate unnecessary code and wasted time. Studies — such as the one from Pennsylvania State University — show that about 91% of speculated problems never materialize.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are more specific ways to avoid overengineering depending on your role in the development cycle. For those in leadership, talk to your team and stay open to new perspectives. If you find yourself explaining your solution at length to justify it, that might be a sign that it’s more complex than necessary. Focus on optimizing the areas that have the greatest impact on the product — that 1% of code that could potentially improve 90% of results — instead of investing effort in less relevant areas.&lt;/p&gt;

&lt;p&gt;In management roles, being as specific as possible when passing along requirements tends to create “guard rails” for the dev team, preventing developers from having to overthink all the possible problems/scenarios a new solution might involve.&lt;/p&gt;

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

&lt;p&gt;This text wasn’t written to justify differences of opinion regarding architectures or tools in collaborative projects, but to emphasize the importance of understanding the impact of each decision. Balance is essential for healthy development. Seek knowledge, gain experience, test and train — but always remember that a good sailor doesn’t use their experience to sail through more storms, but to avoid them.&lt;/p&gt;

&lt;p&gt;Share your experiences, review your projects with this perspective in mind, and stay focused on delivering real value through simple, effective solutions.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Links and References&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.mindtheproduct.com/overengineering-can-kill-your-product/" rel="noopener noreferrer"&gt;Overengineering can kill your product&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@matthaus/over-engineering-e-times-ref%C3%A9ns-da-pr%C3%B3pria-arquitetura-5bd952b40d18" rel="noopener noreferrer"&gt;Over Engineering e times reféns da própria arquitetura&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://medium.com/@talkanel/drawing-the-line-where-engineering-become-over-engineering-a8af6674d266" rel="noopener noreferrer"&gt;Drawing the Line Where Engineering Become Over-Engineering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://c0de517e.blogspot.com/2016/10/over-engineering-root-of-all-evil.html" rel="noopener noreferrer"&gt;Over-engineering&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?v=FLe5dvqV6xs" rel="noopener noreferrer"&gt;I Bet You’re Overengineering Your Software&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>programming</category>
      <category>discuss</category>
      <category>architecture</category>
    </item>
    <item>
      <title>[React + Jest] Introducing to Components Tests</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Sat, 10 Dec 2022 13:40:25 +0000</pubDate>
      <link>https://dev.to/dchueri/react-jest-introducing-to-components-tests-9d9</link>
      <guid>https://dev.to/dchueri/react-jest-introducing-to-components-tests-9d9</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://dev.to/dchueri/react-jest-introduzindo-testes-para-components-3ao3"&gt;Versão em português&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Different from the Backend where the unit tests aim to evaluate the behavior of a certain method, the unit tests for the Frontend must focus on "simulating" the possible actions of the users and how a certain component reacts to a certain action.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why write tests?
&lt;/h2&gt;

&lt;p&gt;Although it may seem complicated at first, automated tests can save you headaches in the future. They are the most effective way to verify that your code does what you want it to do, can prevent a bug that has already been fixed from occurring again, and more others benefit your application.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technologies
&lt;/h2&gt;

&lt;p&gt;To write our tests, we will use the following stack:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; 18.2.0;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; 28.1.2; e&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;Testing Library&lt;/a&gt; 13.4.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Component to be tested
&lt;/h2&gt;

&lt;p&gt;To write a good test you must know the code and the behavior you expect, so let's analyze our &lt;strong&gt;LoginForm&lt;/strong&gt; before starting the tests.&lt;/p&gt;

&lt;h3&gt;
  
  
  Introducing the Code
&lt;/h3&gt;

&lt;p&gt;As an example, a simple &lt;em&gt;component&lt;/em&gt; of a login form will be used:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&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;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diego@test.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123456&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoginForm&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;logged&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLogged&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setLogged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setLogged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SyntheticEvent&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Email
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
          &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Password
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
          &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Log in&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;logged&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Wellcome!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;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;error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Email or password invalid&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resulting in the following interface:&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%2Frrd6cirar3vvkakn5pym.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%2Frrd6cirar3vvkakn5pym.png" alt="Print screen" width="522" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Understand how LoginForm works
&lt;/h3&gt;

&lt;p&gt;Was used the &lt;code&gt;db&lt;/code&gt; constant as a mock of a user on the database:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diego@test.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123456&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next is our &lt;strong&gt;LoginForm&lt;/strong&gt;. Basically, it stores the input value of each form field in &lt;em&gt;state&lt;/em&gt; through &lt;code&gt;onChange&lt;/code&gt;. When clicking on the &lt;strong&gt;Log In&lt;/strong&gt; button, the &lt;code&gt;handleSubmit&lt;/code&gt; is activated, which, through the &lt;code&gt;login&lt;/code&gt; method, verifies whether the e-mail and password passed by the user coincide with the user's password in the bank (in our case, in the mock). &lt;/p&gt;

&lt;p&gt;If the email and password are valid, a welcome message (&lt;code&gt;Welcome!&lt;/code&gt;) is displayed:&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%2Fkl9lay2w3i0kx5grl49e.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%2Fkl9lay2w3i0kx5grl49e.png" alt=" " width="522" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And if any of the two fields have any invalid data, it is displayed to the user (&lt;code&gt;Invalid email or password&lt;/code&gt;):&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Starting the tests
&lt;/h2&gt;

&lt;p&gt;Let's create the test file for our &lt;em&gt;component&lt;/em&gt; in the same folder where it is inserted. For good practices, I usually name the test file with the same name as the &lt;em&gt;component&lt;/em&gt;, followed by the &lt;code&gt;.spec.tsx&lt;/code&gt; extension (if you are not using typescript like me, the extension would be &lt;code&gt;.spec.jsx&lt;/code&gt;).&lt;/p&gt;

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

&lt;p&gt;Starting, let's use the &lt;a href="https://jestjs.io/docs/api#describename-fn" rel="noopener noreferrer"&gt;describe&lt;/a&gt; method, where we'll pass a string as the first parameter, which is usually the name of the tested component. The second parameter is a callback function that will receive our tests:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Tests are written inside the &lt;code&gt;it&lt;/code&gt; or &lt;code&gt;test&lt;/code&gt; functions, the two are functionally identical, and I prefer to use &lt;code&gt;it&lt;/code&gt; just for semantics, improving readability. The function also receives two parameters, where the first is a description of what should happen during the test and the second is another callback, but this time with the test code.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                &lt;span class="c1"&gt;// LoginForm...&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// ...deve renderizar o formulário de login&lt;/span&gt;
        &lt;span class="c1"&gt;//code for test here&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;At this point, we can start testing the rendering of the elements.&lt;/p&gt;

&lt;h2&gt;
  
  
  Rendering UI
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;render()&lt;/code&gt; method will render the component. It takes the component as a parameter and returns a &lt;em&gt;RenderResult&lt;/em&gt; that has several utility methods and properties.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../LoginForm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;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;h2&gt;
  
  
  Capturing an element on DOM
&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;screen&lt;/code&gt; is used to query the DOM. It has a series of &lt;a href="https://testing-library.com/docs/queries/about/" rel="noopener noreferrer"&gt;queries&lt;/a&gt; that will query the DOM as needed and return the chosen element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&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;passwordField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&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;loginButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Log in&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three examples of queries were presented among the many that can be used:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getByLabelText&lt;/code&gt;: Recommended by the official documentation to search for form fields (in the example I did not use this query in all options to expand the presentation of the possibilities of use);&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getByText&lt;/code&gt;: Outside of forms, text content is the main way to find elements. This method is recommended for locating non-interactive elements (such as divs, spans, and paragraphs); and&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getByRole&lt;/code&gt;: Used to query elements by their accessible name (&lt;em&gt;button&lt;/em&gt;, &lt;em&gt;div&lt;/em&gt;, &lt;em&gt;p&lt;/em&gt;, among others). It is normally used with the &lt;em&gt;option&lt;/em&gt; {&lt;em&gt;name: …}&lt;/em&gt; object, avoiding unwanted elements.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I suggest reading the documentation with all queries &lt;a href="https://testing-library.com/docs/queries/about/" rel="noopener noreferrer"&gt;in this link&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Time to test
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Render test
&lt;/h3&gt;

&lt;p&gt;Now let's finally test if the elements are being rendered. To test the values, the &lt;code&gt;expect&lt;/code&gt; function is used, it uses functions &lt;a href="https://jestjs.io/docs/expect" rel="noopener noreferrer"&gt;&lt;em&gt;matchers&lt;/em&gt;&lt;/a&gt; that perform checks between the expected value and the received value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testing-library/jest-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&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;passwordField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&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;loginButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Log in&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passwordField&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginButton&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  src/components/LoginForm/LoginForm.spec.tsx
  LoginForm
    ✓ should render the login form (82 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.01 s, estimated 4 s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 Don't forget to import the &lt;code&gt;@testing-library/jest-dom&lt;/code&gt; module to use &lt;em&gt;mathcer&lt;/em&gt; &lt;code&gt;toBeInDocument&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;See more about Testing Library &lt;a href="https://github.com/testing-library/jest-dom#custom-matchers" rel="noopener noreferrer"&gt;Custom Matchers&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  More tests
&lt;/h3&gt;

&lt;p&gt;Now that we know that the expected elements are being rendered normally, we will check their behavior when interacting with the user, and for that we will use the &lt;code&gt;userEvent&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Successful login test
&lt;/h4&gt;

&lt;p&gt;At this point, remember that it is necessary to imagine the user's actions. Therefore, to perform the login, the user first enters the data in the &lt;code&gt;email&lt;/code&gt; and &lt;code&gt;password&lt;/code&gt; fields, finally he clicks on the &lt;code&gt;Log In&lt;/code&gt; button, receiving the welcome message if the data are correct or, if otherwise, you will receive an error message.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;type&lt;/code&gt; method emulates typing by the user, receiving the element into which the text will be inserted as the first parameter and the text itself as the second parameter. It returns a &lt;code&gt;Promise&lt;/code&gt; so it is necessary to add the &lt;code&gt;await&lt;/code&gt; before the &lt;em&gt;userEvent&lt;/em&gt; and the &lt;code&gt;async&lt;/code&gt; at the beginning of the test's callback function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passwordField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With the form completed, we will use the &lt;code&gt;click&lt;/code&gt; method to click on the login button.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now we can perform the comparison between the expected values and the received values:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// expects the element with the text "Welcome!" to be in the document&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// expects that the element with the text "Invalid email or password" is not found, returning null&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid email or password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&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;Note that in the first &lt;em&gt;expect&lt;/em&gt; it is expected that an element with the text &lt;em&gt;“Welcome!”&lt;/em&gt; is in the document, using the already known &lt;code&gt;getByText&lt;/code&gt; method. In the second, &lt;code&gt;queryByText&lt;/code&gt; was used instead of &lt;code&gt;getByText&lt;/code&gt;, because it is expected that the element is not in the document, and when we use any method started with &lt;code&gt;get&lt;/code&gt; and no element is found, an error interrupting the test, since the methods started with &lt;code&gt;query&lt;/code&gt; return &lt;code&gt;null&lt;/code&gt; , allowing the continuation of the test.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Staying our second test in this way:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/user-event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should show an welcome message if login success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&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;passwordField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&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;loginButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Log in&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;userInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diego@test.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123456&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="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passwordField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid email or password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  src/components/LoginForm/LoginForm.spec.tsx
  LoginForm
    ✓ should render the login form (109 ms)
    ✓ should show a welcome message if login success (141 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.6 s, estimated 3 s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Failed login test
&lt;/h4&gt;

&lt;p&gt;The last test we are going to write in this article is in case the email or password entered by the user is invalid. The logic will be the same as in the previous test, only changing the order of the expected messages:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...imports&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should show an welcome message if login success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should show an error message if login failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&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;passwordField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&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;loginButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Log in&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;userInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diego@test.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worngPassword&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="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passwordField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid email or password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  src/components/LoginForm/LoginForm.spec.tsx
  LoginForm
    ✓ should render the login form (89 ms)
    ✓ should show a welcome message if login success (166 ms)
    ✓ should show an error message if login failed (126 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.388 s, estimated 3 s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;I sought in this article mainly to open the door to the universe of tests. An idea to exercise what you've learned here is to clone this project's repository and create other tests, such as testing what will happen if the user doesn't fill in any of the fields when there is a &lt;code&gt;required&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;With this article, as with any other article you read, &lt;strong&gt;try not to restrict yourself only to what has been demonstrated&lt;/strong&gt;, use the auxiliary links made available during the explanation, explore, exercise, and get to know each functionality better to improve your knowledge.&lt;/p&gt;

&lt;h3&gt;
  
  
  Project Repository
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/dchueri/react-tests" rel="noopener noreferrer"&gt;React Tests - GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Read too
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/dchueri/archlinux-config-on-wsl2-to-developers-48lm"&gt;ArchLinux config on WSL2 to developers&lt;/a&gt;&lt;/p&gt;

</description>
      <category>frontend</category>
      <category>test</category>
      <category>react</category>
      <category>beginners</category>
    </item>
    <item>
      <title>[React + Jest] Introduzindo Testes para "Components"</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Fri, 09 Dec 2022 22:03:49 +0000</pubDate>
      <link>https://dev.to/dchueri/react-jest-introduzindo-testes-para-components-3ao3</link>
      <guid>https://dev.to/dchueri/react-jest-introduzindo-testes-para-components-3ao3</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://dev.to/dchueri/react-jest-introducing-to-components-tests-9d9"&gt;English version&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

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

&lt;p&gt;Diferente do Backend onde os testes unitários visam avaliar o funcionamento de um determinado método, os testes unitários para o Frontend devem focar em "simular" as possíveis ações dos usuários e como determinado componente reage a determinada ação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Porque escrever testes?
&lt;/h2&gt;

&lt;p&gt;Embora possa parecer complicado no início, testes automatizados podem evitar dor de cabeça no futuro. São a forma mais efetiva de verificar se o seu código realmente faz o que você deseja que ele faça, podem evitar que algum bug que já tenha sido corrigido previamente volte a ocorrer, e diversos outros benefícios para a sua aplicação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Tecnologias
&lt;/h2&gt;

&lt;p&gt;Para escrever nossos testes iremos utilizar a stack a seguir:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;a href="https://pt-br.reactjs.org/" rel="noopener noreferrer"&gt;React&lt;/a&gt; 18.2.0;&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://jestjs.io/pt-BR/" rel="noopener noreferrer"&gt;Jest&lt;/a&gt; 28.1.2; e&lt;/li&gt;
&lt;li&gt;
&lt;a href="https://testing-library.com/" rel="noopener noreferrer"&gt;Testing Library&lt;/a&gt; 13.4.0.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  "Component" a ser testado
&lt;/h2&gt;

&lt;p&gt;Para escrever um bom teste você deve conhecer bem o código e o comportamento que espera, então vamos analisar o nosso &lt;strong&gt;LoginForm&lt;/strong&gt; antes de iniciar os testes:&lt;/p&gt;

&lt;h3&gt;
  
  
  Apresentando o Código
&lt;/h3&gt;

&lt;p&gt;Como exemplo, será utilizado um &lt;em&gt;component&lt;/em&gt; simples de um formulário de login:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;react&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;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diego@test.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123456&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="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;LoginForm&lt;/span&gt; &lt;span class="o"&gt;=&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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="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="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;logged&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setLogged&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Boolean&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;login&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;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="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;password&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setLogged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nf"&gt;setError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setLogged&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;

  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;handleSubmit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="na"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;React&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;SyntheticEvent&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="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;login&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;password&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="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;handleSubmit&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Email
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt;
          &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        Password
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt;
          &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
          &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
          &lt;span class="na"&gt;onChange&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Log in&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;logged&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Wellcome!&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;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;error&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Email or password invalid&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Resultando na seguinte interface:&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%2Frrd6cirar3vvkakn5pym.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%2Frrd6cirar3vvkakn5pym.png" alt="Print screen" width="522" height="45"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Entendendo o funcionamento
&lt;/h3&gt;

&lt;p&gt;Foi utilizado como &lt;a href="https://pt.wikipedia.org/wiki/Objeto_mock" rel="noopener noreferrer"&gt;mock&lt;/a&gt; de um usuário no banco de dados a constante &lt;code&gt;db&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diego@test.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123456&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em seguida está o nosso &lt;strong&gt;LoginForm&lt;/strong&gt;. Basicamente ele armazena o valor do input de cada campo do formulário no &lt;em&gt;state&lt;/em&gt; por meio do &lt;code&gt;onChange&lt;/code&gt;. Ao clicar no botão de &lt;strong&gt;Log In&lt;/strong&gt; é acionado o &lt;code&gt;handleSubmit&lt;/code&gt; que por meio do método de &lt;code&gt;login&lt;/code&gt; faz a verificação se o e-mail e senha passados pelo usuário coincidem com a senha do usuário no banco (no nosso caso, no mock). &lt;/p&gt;

&lt;p&gt;Caso o e-mail e senha sejam válidos, é exibido uma mensagem de boas vindas (&lt;code&gt;Welcome!&lt;/code&gt;):&lt;/p&gt;

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

&lt;p&gt;E caso algum dos dois campos possuam algum dado inválido é exibido para o usuário (&lt;code&gt;Invalid email or password&lt;/code&gt;):&lt;/p&gt;

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

&lt;h2&gt;
  
  
  Iniciando os testes
&lt;/h2&gt;

&lt;p&gt;Vamos criar o arquivo de testes para o nosso &lt;em&gt;component&lt;/em&gt; na mesma pasta em que ele está inserido. Por boas práticas, costumo nomear o arquivo de testes com o mesmo nome do &lt;em&gt;component&lt;/em&gt;, seguido da extensão &lt;code&gt;.spec.tsx&lt;/code&gt; (caso não esteja utilizando typescript como eu a extensão seria &lt;code&gt;.spec.jsx&lt;/code&gt;).&lt;/p&gt;

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

&lt;p&gt;Iniciando, vamos utilizar o método &lt;a href="https://jestjs.io/pt-BR/docs/api#describename-fn" rel="noopener noreferrer"&gt;describe&lt;/a&gt;, onde passaremos como primeiro parâmetro uma string que normalmente é o nome do component testado. O segundo parâmetro é uma função callback que receberá nossos testes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Os testes são escritos dentro das funções &lt;code&gt;it&lt;/code&gt; ou &lt;code&gt;test&lt;/code&gt;, os dois são funcionalmente idênticos, sendo que prefiro utilizar o &lt;code&gt;it&lt;/code&gt; apenas por semântica, melhorando a legibilidade. A função também recebe dois parâmetros, onde o primeiro é a descrição do que deverá acontecer durante o teste e o segundo é mais um callback, mas dessa vez com o código do teste.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;                &lt;span class="c1"&gt;// LoginForm...&lt;/span&gt;
    &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="c1"&gt;// ...deve renderizar o formulário de login&lt;/span&gt;
        &lt;span class="c1"&gt;//code for test here&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;Neste momento podemos começar testando a renderização dos elementos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Renderizando a UI
&lt;/h2&gt;

&lt;p&gt;O método &lt;code&gt;render()&lt;/code&gt; realizará a renderização do componente. Ele recebe como parâmetro o componente e retorna um &lt;em&gt;RenderResult&lt;/em&gt; que tem vários métodos e propriedades utilitários.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;../LoginForm&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;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;h2&gt;
  
  
  Capturando elementos no DOM
&lt;/h2&gt;

&lt;p&gt;O objeto &lt;code&gt;screen&lt;/code&gt; é utilizado para fazer consultas no DOM. Ele possui uma série de &lt;a href="https://testing-library.com/docs/queries/about/" rel="noopener noreferrer"&gt;queries&lt;/a&gt; que farão a consulta no DOM conforme a necessidade e retornando o elemento escolhido.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;render&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/react&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&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;passwordField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&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;loginButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Log in&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Foram apresentados três exemplos de queries dentre as diversas que podem ser utilizadas:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;getByLabelText&lt;/code&gt;: Recomendado pela documentação oficial para buscar campos de formulários (no exemplo não utilizei esta query em todas as opções para ampliar a apresentação das possibilidades de uso);&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getByText&lt;/code&gt;: Fora dos formulários, o conteúdo de texto é a principal forma de encontrar elementos. Este método é recomendado para localizar elementos não interativos (como divs, spans e parágrafos).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getByRole&lt;/code&gt;: Utilizado para consultar elementos pelo nome acessível dele (&lt;em&gt;button&lt;/em&gt;, &lt;em&gt;div&lt;/em&gt;, &lt;em&gt;p&lt;/em&gt;, entre outros). Normalmente é utilizando com o objeto &lt;em&gt;option&lt;/em&gt; {&lt;em&gt;name: …}&lt;/em&gt;, evitando elementos indesejados.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sugiro a leitura documentação com todas as queries &lt;a href="https://testing-library.com/docs/queries/about/" rel="noopener noreferrer"&gt;neste link&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Hora de testar
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Testando a renderização
&lt;/h3&gt;

&lt;p&gt;Vamos agora finalmente testar se os elementos estão sendo renderizados. Para testar os valores é utilizada a função &lt;code&gt;expect&lt;/code&gt;, ela utiliza funções &lt;a href="https://jestjs.io/docs/expect" rel="noopener noreferrer"&gt;&lt;em&gt;matchers&lt;/em&gt;&lt;/a&gt; que realizam verificações entre o valor esperado e o valor recebido.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@testing-library/jest-dom&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

        &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&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;passwordField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&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;loginButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Log in&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;

        &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passwordField&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginButton&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Rodando o teste com os comandos abaixo, poderemos observar o resultado no terminal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;yarn test
ou
npm run test
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  src/components/LoginForm/LoginForm.spec.tsx
  LoginForm
    ✓ should render the login form (82 ms)

Test Suites: 1 passed, 1 total
Tests:       1 passed, 1 total
Snapshots:   0 total
Time:        3.01 s, estimated 4 s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;blockquote&gt;
&lt;p&gt;💡 Não se esqueça de importar o módulo &lt;code&gt;@testing-library/jest-dom&lt;/code&gt; para utilizar o &lt;em&gt;mathcer&lt;/em&gt; &lt;code&gt;toBeInDocument&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Veja mais sobre os Testing Library &lt;a href="https://github.com/testing-library/jest-dom#custom-matchers" rel="noopener noreferrer"&gt;Custom Matchers&lt;/a&gt;.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Mais testes
&lt;/h3&gt;

&lt;p&gt;Agora que sabemos que os elementos esperados estão sendo renderizados normalmente, iremos verificar o comportamento deles diante uma interação do usuário, e para isso utilizaremos o &lt;code&gt;userEvent&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Testando o login bem sucedido
&lt;/h4&gt;

&lt;p&gt;Nesse momento relembro que é necessário imaginar as ações que o usuário irá executar. Por tanto, para realizar o login o usuário primeiro insere os dados nos campos de &lt;code&gt;email&lt;/code&gt; e &lt;code&gt;password&lt;/code&gt; , por último ele clica no botão de &lt;code&gt;Log In&lt;/code&gt; recebendo então a mensagem de boas vindas caso os dados estejam corretos ou, caso contrário, receberá uma mensagem de erro.&lt;/p&gt;

&lt;p&gt;O método &lt;code&gt;type&lt;/code&gt; emula a digitação pelo usuário, recebendo como primeiro parâmetro o elemento no qual será inserido o texto e como segundo parâmetro o texto propriamente dito. Ele retorna uma &lt;code&gt;Promise&lt;/code&gt; portanto é necessário adicionar o &lt;code&gt;await&lt;/code&gt; antes do &lt;em&gt;userEvent&lt;/em&gt; e o &lt;code&gt;async&lt;/code&gt; no início da função callback do teste.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passwordField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Com o formulário preenchido, utilizaremos o método &lt;code&gt;click&lt;/code&gt; para realizar o clique no botão de login.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora podemos realizar a comparação entre os valores esperados e os valores recebidos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// espera que o elemento com o texto "Welcome!" esteja no documento&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="c1"&gt;// espera que o elemento com o texto "Invalid email or password" não seja encontrado, retornando null&lt;/span&gt;
&lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid email or password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&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;Observe que no primeiro &lt;em&gt;expect&lt;/em&gt; é esperado que um elemento com o texto &lt;em&gt;“Welcome!”&lt;/em&gt; esteja no documento, utilizando o já conhecido método &lt;code&gt;getByText&lt;/code&gt;. Já no segundo foi utilizado o &lt;code&gt;queryByText&lt;/code&gt; no lugar de &lt;code&gt;getByText&lt;/code&gt;, isso por conta de que é esperado que o elemento não esteja no documento, e ao utilizarmos qualquer método iniciado com &lt;code&gt;get&lt;/code&gt; e não for encontrado nenhum elemento será lançado um erro interrompendo o teste, já os métodos iniciados com &lt;code&gt;query&lt;/code&gt; retornam &lt;code&gt;null&lt;/code&gt; , possibilitando a continuação do teste.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Fincando dessa forma o nosso segundo teste:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...imports&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@testing-library/user-event&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

    &lt;span class="c1"&gt;// deve exibir uma mensagem de boas vindas caso tenha sucesso no login&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should show an welcome message if login success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&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;passwordField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&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;loginButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Log in&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;userInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diego@test.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;123456&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="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passwordField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid email or password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  src/components/LoginForm/LoginForm.spec.tsx
  LoginForm
    ✓ should render the login form (109 ms)
    ✓ should show an welcome message if login success (141 ms)

Test Suites: 1 passed, 1 total
Tests:       2 passed, 2 total
Snapshots:   0 total
Time:        2.6 s, estimated 3 s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  Testando o login mal sucedido
&lt;/h4&gt;

&lt;p&gt;O último teste que vamos escrever neste artigo é para caso o e-mail ou a senha inseridos pelo usuário sejam inválidos. A lógica será basicamente a mesma do teste anterior alternado somente a ordem das mensagens esperadas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight tsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;//...imports&lt;/span&gt;

&lt;span class="nf"&gt;describe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;LoginForm&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should render the login form&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="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should show an welcome message if login success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;
  &lt;span class="p"&gt;});&lt;/span&gt;

   &lt;span class="c1"&gt;// deve exibir uma mensagem de erro caso o login tenha falhado&lt;/span&gt;
  &lt;span class="nf"&gt;it&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;should show an error message if login failed&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;LoginForm&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;);&lt;/span&gt;

    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;emailField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByLabelText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Email&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;passwordField&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Password&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;loginButton&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByRole&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;button&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Log in&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;userInput&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;diego@test.com&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;worngPassword&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="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;emailField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;email&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;type&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;passwordField&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;password&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;userEvent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;click&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;loginButton&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Invalid email or password&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeInTheDocument&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;expect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;screen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;queryByText&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Welcome!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)).&lt;/span&gt;&lt;span class="nf"&gt;toBeNull&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;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;PASS  src/components/LoginForm/LoginForm.spec.tsx
  LoginForm
    ✓ should render the login form (89 ms)
    ✓ should show an welcome message if login success (166 ms)
    ✓ should show an error message if login failed (126 ms)

Test Suites: 1 passed, 1 total
Tests:       3 passed, 3 total
Snapshots:   0 total
Time:        2.388 s, estimated 3 s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;Busquei neste artigo principalmente abrir a porta para o universo dos testes. Uma ideia para exercitar o que aprendeu aqui é clonando o repositório deste projeto e criar outros testes, como por exemplo testar o que ocorrerá caso o usuário não preencha algum dos campos quando houver um &lt;code&gt;required&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Com este artigo, assim como com qualquer outro artigo que você ler, &lt;strong&gt;busque não se restringir somente ao que foi demonstrado&lt;/strong&gt;, utilize os links auxiliares disponibilizados ao decorrer da explicação, explore, exercite e conheça melhor cada funcionalidade para aprimorar seu conhecimento.&lt;/p&gt;

&lt;h3&gt;
  
  
  Repositório do Projeto
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://github.com/dchueri/react-tests" rel="noopener noreferrer"&gt;React Tests - GitHub&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Leia também
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://dev.to/dchueri/configurando-o-archlinux-com-wsl-2-para-devs-393n"&gt;Configurando o ArchLinux com WSL 2 para Devs&lt;/a&gt;&lt;/p&gt;

</description>
      <category>cloud</category>
    </item>
    <item>
      <title>ArchLinux config on WSL2 to developers</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Mon, 21 Nov 2022 16:27:22 +0000</pubDate>
      <link>https://dev.to/dchueri/archlinux-config-on-wsl2-to-developers-48lm</link>
      <guid>https://dev.to/dchueri/archlinux-config-on-wsl2-to-developers-48lm</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://dev.to/dchueri/configurando-o-archlinux-com-wsl-2-para-devs-393n"&gt;Versão em português&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;In the last few days, I needed to format my equipment again and I felt the need to have documentation to speed up this part of configuring my development environment. I use some apps in my day-to-day life that prevent me from using only Linux, for a while I used dual-boot, but the need to restart the PC for some quick code changes made me look for another alternative, in this case, WSL.&lt;/p&gt;

&lt;p&gt;The initial idea was just to document to facilitate my configuration process, but as lemonade comes from lemon, I decided to share it with you here. I'm not going to sound deep about the WSL or the reasons for each choice, but I'm open to suggestions and dialogues with anyone interested.&lt;/p&gt;

&lt;p&gt;If you don't know what WSL is or are looking for more information about it, follow the link to the official Microsoft documentation (numerous articles can be found by a quick search):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/en-us/windows/wsl/about" rel="noopener noreferrer"&gt;What is the Windows Subsystem for Linux?&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Installing WSL
&lt;/h2&gt;

&lt;p&gt;If you have not yet installed WSL on your Windows, enter the following command in Windows PowerShell:&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;&lt;a href="https://learn.microsoft.com/en-us/windows/wsl/install#install-wsl-command" rel="noopener noreferrer"&gt;Install WSL | Microsoft Learn&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;I prefer to use Windows Terminal for two main reasons: customization and taking advantage of some Linux commands. Installation is simple and can be done using the link below:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.microsoft.com/store/detail/windows-terminal/9N0DX20HK701?hl=en-us" rel="noopener noreferrer"&gt;Windows Terminal - Microsoft Store Apps&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Install Arch
&lt;/h2&gt;

&lt;p&gt;This version of Arch that I use is very lightweight compared to other options (like Manjaro):&lt;/p&gt;

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

&lt;p&gt;Just extract the files to a preferred folder and run the Arch file, which will start the installation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Creating a user
&lt;/h2&gt;

&lt;p&gt;Once the installation is complete, it's time to configure a user and make it the default at startup, avoiding using the root user. Just follow the commands below, remembering to replace &lt;code&gt;{username}&lt;/code&gt; with the desired &lt;em&gt;name&lt;/em&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="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"%wheel ALL=(ALL) ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/wheel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; wheel &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash &lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;passwd &lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now to set the created user as default, close Arch and open your terminal in the folder where the Arch.exe file is and enter the following command:&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="se"&gt;\A&lt;/span&gt;rch.Exe config &lt;span class="nt"&gt;--default-user&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://wsldl-pg.github.io/ArchW-docs/How-to-Setup/#set-up-the-default-user" rel="noopener noreferrer"&gt;How to Setup | ArchWSL official documentation&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Updating distro packages
&lt;/h2&gt;

&lt;p&gt;Before updating we need to initialize pacman's keyring:&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;pacman-key &lt;span class="nt"&gt;--init&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And also perform the initial configuration of the keys with the:&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;pacman-key &lt;span class="nt"&gt;--populate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;pacman &lt;span class="nt"&gt;-Sy&lt;/span&gt; archlinux-keyring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;pacman &lt;span class="nt"&gt;-Syu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://wiki.archlinux.org/title/Pacman/Package_signing" rel="noopener noreferrer"&gt;pacman/Package signing - ArchWiki&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wsldl-pg.github.io/ArchW-docs/How-to-Setup/#initialize-keyring" rel="noopener noreferrer"&gt;Initialize keyring | ArchWSL official documentation (wsldl-pg.github.io)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Package managers
&lt;/h2&gt;

&lt;p&gt;I will initially install the YARN and NPM managers:&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;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; yarn npm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As &lt;a href="https://wiki.archlinux.org/title/Arch_User_Repository" rel="noopener noreferrer"&gt;AUR&lt;/a&gt; manager I use &lt;a href="https://github.com/Jguer/yay" rel="noopener noreferrer"&gt;Yay&lt;/a&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="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;--needed&lt;/span&gt; git base-devel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, with &lt;em&gt;“Rust”&lt;/em&gt; package to use &lt;em&gt;“cargo”&lt;/em&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="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; rust
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To install Yay it will be necessary to clone the repository, as after installation the files will no longer be used I choose to create a temporary folder to facilitate deletion later:&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;mkdir &lt;/span&gt;tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now just clone the repository and build it with &lt;code&gt;makepkg&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;git clone https://aur.archlinux.org/yay.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;yay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/Jguer/yay#installation" rel="noopener noreferrer"&gt;Jguer/yay: Yet another Yogurt - An AUR Helper written in Go (github.com)&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Z Shell
&lt;/h3&gt;

&lt;p&gt;Install ZSH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yay &lt;span class="nt"&gt;-S&lt;/span&gt; zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Customizing the Terminal
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Changing the terminal theme
&lt;/h3&gt;

&lt;p&gt;&lt;a href="https://windowsterminalthemes.dev/" rel="noopener noreferrer"&gt;Windows Terminal Themes&lt;/a&gt; has several themes ready for your terminal, just choose the theme of your choice and click on the “&lt;em&gt;Get theme&lt;/em&gt;” button, and with that selected theme will be copied to your clipboard. My favorite theme is &lt;a href="https://windowsterminalthemes.dev/?theme=Dracula" rel="noopener noreferrer"&gt;Dracula&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Just go back to the terminal, use the shortcut &lt;code&gt;Ctrl + ,&lt;/code&gt; to open the &lt;em&gt;Configurations&lt;/em&gt;, and click on &lt;em&gt;Open JSON file&lt;/em&gt; in the lower left corner.&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%2F1s0r2jmfx7ks4pdrwdhj.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%2F1s0r2jmfx7ks4pdrwdhj.jpg" alt="Print screen" width="200" height="94"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After the last theme (themes are inside the "&lt;em&gt;schemes&lt;/em&gt;" array), put a &lt;code&gt;,&lt;/code&gt; and paste the chosen theme. Save and close the file.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; If you "break" the JSON file, while the terminal is open it may work normally, but when restarting it you will find an error similar to &lt;a href="https://i.imgur.com/Dp8w5WD.png" rel="noopener noreferrer"&gt;this&lt;/a&gt;. If this is tolerated, just click “OK”, open the JSON file again, check the syntax and save the file.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Return to the &lt;em&gt;Settings screen,&lt;/em&gt; open the Arch profile, under “&lt;em&gt;Additional settings”&lt;/em&gt; and click on “&lt;em&gt;Appearance”&lt;/em&gt;. Now just change the “&lt;em&gt;Color Scheme&lt;/em&gt;” to the desired option. Click “&lt;em&gt;Save&lt;/em&gt;” and close the settings.&lt;/p&gt;

&lt;h3&gt;
  
  
  Customizing ZSH
&lt;/h3&gt;

&lt;p&gt;To change the ZSH theme I use &lt;a href="https://github.com/romkatv/powerlevel10k" rel="noopener noreferrer"&gt;Powerlevel10k&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yay &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;--noconfirm&lt;/span&gt; zsh-theme-powerlevel10k-git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;chsh &lt;span class="nt"&gt;-s&lt;/span&gt; /usr/bin/zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Restart the terminal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Font
&lt;/h3&gt;

&lt;p&gt;After installing Powerlevel10k, when you start the terminal again, a settings screen will appear where some symbols should be displayed:&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%2Flzc52918hwliw624th5j.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%2Flzc52918hwliw624th5j.png" alt="Print of terminal" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you are unable to view the displayed symbols, just install one of the calls &lt;a href="https://www.nerdfonts.com/font-downloads" rel="noopener noreferrer"&gt;Nerd Fonts&lt;/a&gt; and change the Arch profile font in “&lt;em&gt;Additional Settings&lt;/em&gt;” &amp;gt; “&lt;em&gt;Appearance&lt;/em&gt;” &amp;gt; “&lt;em&gt;Font type&lt;/em&gt;”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When you want to access the Powerlevel10k configuration screen again, just type:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Plugins
&lt;/h3&gt;

&lt;p&gt;In my case, I usually use the following plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/zsh-users/zsh-autosuggestions" rel="noopener noreferrer"&gt;ZSH Autosuggestions&lt;/a&gt;: which does an autocomplete based on my command history;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/ogham/exa" rel="noopener noreferrer"&gt;exa&lt;/a&gt;: serves as an alternative to “&lt;em&gt;ls&lt;/em&gt;”; and&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;bat&lt;/a&gt;: alternative “&lt;em&gt;cat&lt;/em&gt;”.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; .zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;bat exa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Open the .zshrc file in your preferred editor and add to the end of the file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code .zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh
&lt;span class="nb"&gt;alias ls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"exa --icons"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;bat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bat --style-auto"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  NVM
&lt;/h2&gt;

&lt;p&gt;For managing node versions I use &lt;a href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer"&gt;NVM&lt;/a&gt;:&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.2/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;It is necessary to have Docker Desktop installed on Windows. With that, just type in the terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yay &lt;span class="nt"&gt;-S&lt;/span&gt; docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;And finally, enable the Arch distro in the &lt;em&gt;Resources&lt;/em&gt; of Docker Desktop.&lt;/p&gt;

&lt;h2&gt;
  
  
  And is this
&lt;/h2&gt;

&lt;p&gt;Throughout the article, I tried to provide some useful links so that each one can perform the best configuration as appropriate. As said at the beginning, feel free to make suggestions, as well as indicate any errors that occurred during the process and also share your preferences with others.&lt;/p&gt;

</description>
      <category>java</category>
    </item>
    <item>
      <title>Configurando o ArchLinux com WSL 2 para Devs</title>
      <dc:creator>Diego Chueri</dc:creator>
      <pubDate>Mon, 21 Nov 2022 16:24:48 +0000</pubDate>
      <link>https://dev.to/dchueri/configurando-o-archlinux-com-wsl-2-para-devs-393n</link>
      <guid>https://dev.to/dchueri/configurando-o-archlinux-com-wsl-2-para-devs-393n</guid>
      <description>&lt;blockquote&gt;
&lt;p&gt;&lt;a href="https://dev.to/dchueri/archlinux-config-on-wsl2-to-developers-48lm"&gt;English version&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Nos últimos dias eu precisei formatar novamente meu equipamento e senti a necessidade de ter uma documentação para agilizar essa parte de configuração do meu ambiente de desenvolvimento. Utilizo alguns apps no meu dia a dia que me impedem de utilizar somente o Linux, por um tempo utilizei o dual-boot, porém a necessidade de reiniciar o PC para algumas alterações rápidas em código me fizeram buscar outra alternativa, no caso o WSL.&lt;/p&gt;

&lt;p&gt;A ideia inicial era somente documentar para facilitar o meu processo de configuração, mas como do limão surge a limonada, decidi compartilhar com vocês aqui. Não vou me apegar a explicações sobre o que é o WSL ou sobre os motivos de cada escolha, mas fico completamente aberto a sugestões e diálogos com quem possuir interesse.&lt;/p&gt;

&lt;p&gt;Se você não sabe o que é o WSL ou busca mais informações a respeito segue o link com a documentação oficial da Microsoft (há também inúmeros artigos que podem ser encontrados por uma pesquisa rápida):&lt;/p&gt;

&lt;p&gt;&lt;a href="https://learn.microsoft.com/pt-br/windows/wsl/about" rel="noopener noreferrer"&gt;O que é o Subsistema do Windows para Linux&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalando o WSL
&lt;/h2&gt;

&lt;p&gt;Caso ainda não tenha instalado o WSL no seu Windows insira o seguinte comando no Windows PowerShell:&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;&lt;a href="https://learn.microsoft.com/pt-br/windows/wsl/install#install-wsl-command" rel="noopener noreferrer"&gt;Instalar o WSL | Microsoft Learn&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Eu prefiro utilizar o Windows Terminal por dois principais motivos: personalização e aproveitamento de alguns comandos Linux. A instalação é simples e pode ser feita pelo link abaixo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://apps.microsoft.com/store/detail/windows-terminal/9N0DX20HK701?hl=pt-br&amp;amp;gl=br" rel="noopener noreferrer"&gt;Windows Terminal - Aplicativos Microsoft Store&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Instalando o Arch
&lt;/h2&gt;

&lt;p&gt;Essa versão do Arch que eu utilizo é bem leve se comparado a outras opções (como o Manjaro por exemplo):&lt;/p&gt;

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

&lt;p&gt;Basta extrair os arquivos para uma pasta de preferência e executar o arquivo Arch que será iniciada a instalação.&lt;/p&gt;

&lt;h2&gt;
  
  
  Configurando um usuário
&lt;/h2&gt;

&lt;p&gt;Feita a instalação é hora de configurar um usuário e torná-lo padrão na inicialização, evitando utilizar o usuário root. Basta seguir os comandos abaixo, lembrando de substituir &lt;code&gt;{username}&lt;/code&gt; pelo &lt;em&gt;nome&lt;/em&gt; de usuário desejado:&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;echo&lt;/span&gt; &lt;span class="s2"&gt;"%wheel ALL=(ALL) ALL"&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; /etc/sudoers.d/wheel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;useradd &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="nt"&gt;-G&lt;/span&gt; wheel &lt;span class="nt"&gt;-s&lt;/span&gt; /bin/bash &lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;passwd &lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora para definir o usuário criado como padrão, feche o Arch e abra o seu terminal na pasta onde está o arquivo Arch.exe e insira o comando a seguir:&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="se"&gt;\A&lt;/span&gt;rch.exe config &lt;span class="nt"&gt;--default-user&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt;username&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://wsldl-pg.github.io/ArchW-docs/How-to-Setup/#set-up-the-default-user" rel="noopener noreferrer"&gt;How to Setup | ArchWSL official documentation (wsldl-pg.github.io)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Atualizando os pacotes da distro
&lt;/h2&gt;

&lt;p&gt;Antes de atualizar precisamos inicializar o keyring do pacman:&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;pacman-key &lt;span class="nt"&gt;--init&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E também realizar a configuração inicial das chaves com:&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;pacman-key &lt;span class="nt"&gt;--populate&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;pacman &lt;span class="nt"&gt;-Sy&lt;/span&gt; archlinux-keyring
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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;pacman &lt;span class="nt"&gt;-Syu&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://wiki.archlinux.org/title/Pacman_(Portugu%C3%AAs)/Package_signing_(Portugu%C3%AAs)" rel="noopener noreferrer"&gt;pacman (Português)/Package signing (Português) - ArchWiki (archlinux.org)&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://wsldl-pg.github.io/ArchW-docs/How-to-Setup/#initialize-keyring" rel="noopener noreferrer"&gt;Initialize keyring | ArchWSL official documentation (wsldl-pg.github.io)&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Gerenciadores de pacotes
&lt;/h2&gt;

&lt;p&gt;Vou instalar incialmente os gerenciadores YARN e NPM:&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;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; yarn npm
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Como gerenciador de &lt;a href="https://wiki.archlinux.org/title/Arch_User_Repository_(Portugu%C3%AAs)" rel="noopener noreferrer"&gt;AUR&lt;/a&gt; eu utilizo o &lt;a href="https://github.com/Jguer/yay" rel="noopener noreferrer"&gt;Yay&lt;/a&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="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;--needed&lt;/span&gt; git base-devel
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Por fim, o pacote &lt;em&gt;“Rust”&lt;/em&gt; para utilizar o &lt;em&gt;“cargo”&lt;/em&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="nb"&gt;sudo &lt;/span&gt;pacman &lt;span class="nt"&gt;-S&lt;/span&gt; rust
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Para instalar o Yay será necessário clonar o repositório, como após a instalação os arquivos não serão mais utilizados eu opto por criar uma pasta temporária para facilitar a exclusão posteriormente:&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;mkdir &lt;/span&gt;tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;tmp
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora basta clonar o repositório e fazer o build com o &lt;code&gt;makepkg&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;git clone https://aur.archlinux.org/yay.git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;cd &lt;/span&gt;yay
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





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

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://github.com/Jguer/yay#installation" rel="noopener noreferrer"&gt;Jguer/yay: Yet another Yogurt - An AUR Helper written in Go (github.com)&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Z Shell
&lt;/h3&gt;

&lt;p&gt;Instalando o ZSH:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yay &lt;span class="nt"&gt;-S&lt;/span&gt; zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Personalizando o Terminal
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Alterando o tema do terminal
&lt;/h3&gt;

&lt;p&gt;O &lt;a href="https://windowsterminalthemes.dev/" rel="noopener noreferrer"&gt;Windows Terminal Themes&lt;/a&gt; possui diversos temas prontos para o seu terminal, basta escolher o tema de sua preferência e clicar no botão “&lt;em&gt;Get theme&lt;/em&gt;”, com isso o tema selecionado será copiado para a sua área de transferência. O meu tema preferido é o &lt;a href="https://windowsterminalthemes.dev/?theme=Dracula" rel="noopener noreferrer"&gt;Dracula&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Basta agora voltar ao terminal, utilizar o atalho &lt;code&gt;Ctrl + ,&lt;/code&gt; para abrir as &lt;em&gt;Configurações&lt;/em&gt; e clicar em &lt;em&gt;Abrir arquivo JSON&lt;/em&gt; no canto inferior esquerdo.&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%2Fsxwa3nf86ranoliefgj2.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%2Fsxwa3nf86ranoliefgj2.png" alt="Print do terminal" width="407" height="173"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Após o último tema (os temas estão dentro do array "&lt;em&gt;schemes&lt;/em&gt;”), coloque uma &lt;code&gt;,&lt;/code&gt; e cole o tema escolhido. Salve e feche o arquivo.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Obs.:&lt;/strong&gt; Caso você “quebre” o arquivo JSON, enquanto o terminal estiver aberto ele pode funcionar normalmente, porém ao reiniciá-lo você encontrará algum erro parecido com &lt;a href="https://i.imgur.com/Dp8w5WD.png" rel="noopener noreferrer"&gt;este&lt;/a&gt;. Caso isso ocorra, apenas clique em “OK”, abra o arquivo JSON novamente, verifique a sintaxe e salve o arquivo.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Volte à tela de &lt;em&gt;Configurações,&lt;/em&gt; abra o perfil do Arch, em “&lt;em&gt;Configurações adicionais”&lt;/em&gt; clique em “&lt;em&gt;Aparência”&lt;/em&gt;. Agora é só alterar o “&lt;em&gt;Esquema de cores&lt;/em&gt;” para a opção desejada. Clique em “&lt;em&gt;Salvar&lt;/em&gt;” e feche as configurações.&lt;/p&gt;

&lt;h3&gt;
  
  
  Alterando tema do ZSH
&lt;/h3&gt;

&lt;p&gt;Para alterar o tema do ZSH eu utilizo o &lt;a href="https://github.com/romkatv/powerlevel10k" rel="noopener noreferrer"&gt;Powerlevel10k&lt;/a&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yay &lt;span class="nt"&gt;-S&lt;/span&gt; &lt;span class="nt"&gt;--noconfirm&lt;/span&gt; zsh-theme-powerlevel10k-git
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s1"&gt;'source /usr/share/zsh-theme-powerlevel10k/powerlevel10k.zsh-theme'&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt;~/.zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;chsh &lt;span class="nt"&gt;-s&lt;/span&gt; /usr/bin/zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Reinicie o terminal.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fonte
&lt;/h3&gt;

&lt;p&gt;Após instalado o Powerlevel10k, quando iniciar o terminal novamente irá aparecer uma tela de configurações onde deverá ser visualizado alguns símbolos:&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%2Flzc52918hwliw624th5j.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%2Flzc52918hwliw624th5j.png" alt="Print do terminal" width="800" height="448"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Caso você não esteja conseguindo visualizar os símbolos apresentados, basta instalar alguma das chamadas &lt;a href="https://www.nerdfonts.com/font-downloads" rel="noopener noreferrer"&gt;Nerd Fonts&lt;/a&gt; e alterar a fonte do perfil Arch em “&lt;em&gt;Configurações adicionais&lt;/em&gt;” &amp;gt; “&lt;em&gt;Aparência&lt;/em&gt;” &amp;gt; “&lt;em&gt;Tipo de fonte&lt;/em&gt;”.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quando quiser acessar novamente a tela de configuração do Powerlevel10k basta digitar:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;h3&gt;
  
  
  Plugins
&lt;/h3&gt;

&lt;p&gt;No meu caso, costumo utilizar os seguintes plugins:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/zsh-users/zsh-autosuggestions" rel="noopener noreferrer"&gt;ZSH Autosuggestions&lt;/a&gt;: que faz um autocomplete com base no meu histórico de comandos;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/ogham/exa" rel="noopener noreferrer"&gt;exa&lt;/a&gt;: serve como alternativa ao “&lt;em&gt;ls&lt;/em&gt;”; e&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;a href="https://github.com/sharkdp/bat" rel="noopener noreferrer"&gt;bat&lt;/a&gt;: alternativa ao “&lt;em&gt;cat&lt;/em&gt;”.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;mkdir&lt;/span&gt; .zsh
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/zsh-users/zsh-autosuggestions ~/.zsh/zsh-autosuggestions
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;cargo &lt;span class="nb"&gt;install &lt;/span&gt;bat exa
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Abra o arquivo .zshrc no seu editor de preferência e adicione ao final do arquivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;code .zshrc
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;source&lt;/span&gt; ~/.zsh/zsh-autosuggestions/zsh-autosuggestions.zsh
&lt;span class="nb"&gt;alias ls&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"exa --icons"&lt;/span&gt;
&lt;span class="nb"&gt;alias &lt;/span&gt;&lt;span class="nv"&gt;bat&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"bat --style-auto"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  NVM
&lt;/h2&gt;

&lt;p&gt;Para o gerenciamento de versões do node eu utilizo o &lt;a href="https://github.com/nvm-sh/nvm" rel="noopener noreferrer"&gt;NVM&lt;/a&gt;:&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.2/install.sh | bash
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Docker
&lt;/h2&gt;

&lt;p&gt;É necessário possuir o Docker Desktop instalado no windows. Com isso, apenas digite no terminal:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;yay &lt;span class="nt"&gt;-S&lt;/span&gt; docker
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E por fim habilite a distro do Arch nas &lt;em&gt;Resources&lt;/em&gt; do Docker Desktop.&lt;/p&gt;

&lt;h2&gt;
  
  
  E é isso
&lt;/h2&gt;

&lt;p&gt;Ao decorrer do artigo, busquei disponibilizar alguns links úteis para que cada um consiga realizar a melhor configuração conforme for o caso. Como dito no início sinta-se livre para dar sugestões, assim como indicar algum erro que ocorreu durante o processo e bem como compartilhar suas preferências com os demais.&lt;/p&gt;

</description>
      <category>archlinux</category>
      <category>programming</category>
      <category>wsl</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
