<?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: Denis Augusto</title>
    <description>The latest articles on DEV Community by Denis Augusto (@denisgusto1).</description>
    <link>https://dev.to/denisgusto1</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4003020%2F1774852b-de68-4d3c-bfca-b13d3d86c6ac.jpeg</url>
      <title>DEV Community: Denis Augusto</title>
      <link>https://dev.to/denisgusto1</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/denisgusto1"/>
    <language>en</language>
    <item>
      <title>Você testou a API funcionando. E quando ela cair?</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Tue, 04 Aug 2026 22:58:21 +0000</pubDate>
      <link>https://dev.to/denisgusto1/voce-testou-a-api-funcionando-e-quando-ela-cair-4n3i</link>
      <guid>https://dev.to/denisgusto1/voce-testou-a-api-funcionando-e-quando-ela-cair-4n3i</guid>
      <description>&lt;h2&gt;
  
  
  O caminho feliz sempre passa. O problema é o outro.
&lt;/h2&gt;

&lt;p&gt;Sua integração com o gateway funciona. Você testou. O teste tá verde. Deploy feito, tá em produção há meses.&lt;/p&gt;

&lt;p&gt;Aí, numa terça qualquer, o gateway devolve um erro 500. Instabilidade do lado deles.&lt;/p&gt;

&lt;p&gt;E o seu app? Cospe uma tela branca com &lt;code&gt;Undefined array key "transaction_id"&lt;/code&gt; na cara do cliente. Porque seu código pegou a resposta de erro e tentou ler o &lt;code&gt;transaction_id&lt;/code&gt; que não existe ali.&lt;/p&gt;

&lt;p&gt;Você testou a API &lt;strong&gt;funcionando&lt;/strong&gt;. Nunca testou ela &lt;strong&gt;falhando&lt;/strong&gt;. E é exatamente aí que mora o bug.&lt;/p&gt;

&lt;h2&gt;
  
  
  O teste que só conhece o dia bonito
&lt;/h2&gt;

&lt;p&gt;Olha o teste típico de integração. Aposto que o seu se parece com esse:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_processa_o_pagamento&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'gateway.com/*'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'status'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'aprovado'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'transaction_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'tx_123'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$resultado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cobrar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertTrue&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resultado&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;aprovado&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;Bonito. Verde. E completamente ingênuo.&lt;/p&gt;

&lt;p&gt;Ele responde uma única pergunta: "quando dá tudo certo, meu código funciona?". Ótimo. Mas a produção não é só o dia bonito. A API vai devolver 500, vai dar timeout, vai responder um JSON estranho, vai ficar fora do ar. &lt;strong&gt;E você nunca escreveu uma linha testando o que acontece nesses casos.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;O caminho feliz é o que menos quebra. O caminho de falha é onde o cliente vê tela branca.&lt;/p&gt;

&lt;h2&gt;
  
  
  O resgate: &lt;code&gt;Http::fake()&lt;/code&gt; também simula desastre
&lt;/h2&gt;

&lt;p&gt;Aqui tá a beleza de mockar a API — você não fica refém de esperar ela cair pra ver o que acontece. &lt;strong&gt;Você manda ela cair.&lt;/strong&gt; Na hora que quiser, no teste que quiser.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_gateway_fora_do_ar_nao_quebra_o_app&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// simula o gateway devolvendo erro 500&lt;/span&gt;
    &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'gateway.com/*'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Service Unavailable'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$resultado&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cobrar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// seu código degrada com elegância, em vez de explodir?&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertFalse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$resultado&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;aprovado&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'gateway_indisponivel'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$resultado&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;motivo&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pendente'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;fresh&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;status&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;Esse teste vale ouro. Ele te obriga a responder: "quando o gateway cai, meu pedido fica num estado consistente ou vira lixo?". Se o teste passa, você &lt;strong&gt;sabe&lt;/strong&gt; que o cliente vê uma mensagem decente e o pedido fica pendente pra retentar — não uma tela branca.&lt;/p&gt;

&lt;h2&gt;
  
  
  Os três desastres que você precisa testar
&lt;/h2&gt;

&lt;p&gt;Não é só o erro 500. O mundo real tem sabores variados de falha:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// 1. Erro do servidor (a API respondeu, mas com pau)&lt;/span&gt;
&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'gateway.com/*'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;

&lt;span class="c1"&gt;// 2. Resposta inesperada (mudaram o contrato, ou veio corrompido)&lt;/span&gt;
&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'gateway.com/*'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'algo'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'diferente'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)]);&lt;/span&gt;

&lt;span class="c1"&gt;// 3. A API nem respondeu (timeout / fora do ar)&lt;/span&gt;
&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;fn&lt;/span&gt; &lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;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;ConnectionException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'timeout'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cada um exercita um pedaço diferente do seu tratamento de erro. O 500 testa seu &lt;code&gt;throw()&lt;/code&gt;. A resposta estranha testa se você valida o que recebeu antes de usar. A &lt;code&gt;ConnectionException&lt;/code&gt; testa o que acontece quando a requisição nem chega — o cenário do post sobre &lt;code&gt;timeout&lt;/code&gt; e &lt;code&gt;retry&lt;/code&gt;, agora coberto por teste.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pegadinha: caminho de falha sem tratamento não "passa", ele engana
&lt;/h2&gt;

&lt;p&gt;Cuidado com uma armadilha sutil. Se seu código &lt;strong&gt;não trata&lt;/strong&gt; o erro, esse teste de falha pode passar por engano — porque o Laravel lança a exceção e o teste morre com ela, ou o &lt;code&gt;assert&lt;/code&gt; bate em cima de um estado que você nem pensou.&lt;/p&gt;

&lt;p&gt;O teste de falha só tem valor se você escreveu o teste &lt;strong&gt;esperando um comportamento específico e digno&lt;/strong&gt;: uma flag &lt;code&gt;aprovado = false&lt;/code&gt;, um status &lt;code&gt;pendente&lt;/code&gt;, uma exceção sua tratada. Se você não sabe o que seu código deveria fazer quando a API cai... esse é o verdadeiro bug, e o teste acabou de te mostrar.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;O caminho feliz é o mínimo. Ele prova que seu código funciona no melhor dos mundos — que é justamente o mundo que menos acontece.&lt;/p&gt;

&lt;p&gt;O teste que te salva de plantão às 2 da manhã é o do caminho de falha. Pega a sua integração externa mais crítica e se pergunta: "eu tenho um teste que simula essa API caindo?". Se a resposta é não, você já sabe qual teste escrever amanhã.&lt;/p&gt;

&lt;p&gt;Me conta: você testa o caminho de falha das suas integrações, ou só o dia bonito? Sem julgamento — a maioria só testa o feliz. 👀&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>testing</category>
      <category>api</category>
    </item>
    <item>
      <title>Seu teste bate na API de verdade? Um dia ela vai te trair</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Tue, 04 Aug 2026 22:57:40 +0000</pubDate>
      <link>https://dev.to/denisgusto1/seu-teste-bate-na-api-de-verdade-um-dia-ela-vai-te-trair-4l97</link>
      <guid>https://dev.to/denisgusto1/seu-teste-bate-na-api-de-verdade-um-dia-ela-vai-te-trair-4l97</guid>
      <description>&lt;h2&gt;
  
  
  O teste quebrou. E não foi você.
&lt;/h2&gt;

&lt;p&gt;Segunda de manhã, você abre o projeto e o CI tá vermelho.&lt;/p&gt;

&lt;p&gt;Você não mexeu em nada desde sexta. Abre o log: o teste de integração com a API de pagamento falhou. Timeout.&lt;/p&gt;

&lt;p&gt;O gateway deles teve uma instabilidade às 3 da manhã. Seu código tá perfeito. Mesmo assim, o build tá vermelho, o deploy travado, e agora você perde meia hora provando que o problema não é seu.&lt;/p&gt;

&lt;p&gt;Já passou por isso? Teste que depende de API externa é assim: &lt;strong&gt;lento, instável e quebra por motivos que não são culpa sua&lt;/strong&gt;. E tem o cenário pior — o teste que roda de verdade contra a API de pagamento, e a cada &lt;code&gt;php artisan test&lt;/code&gt; você dispara 30 cobranças reais no sandbox até o gateway achar suspeito e bloquear.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema: seu teste não deveria conhecer a internet
&lt;/h2&gt;

&lt;p&gt;Olha esse teste. Parece razoável, né?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_busca_o_cep&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// isso bate no ViaCEP de VERDADE, toda vez que o teste roda&lt;/span&gt;
    &lt;span class="nv"&gt;$endereco&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;buscarCep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'01001000'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'São Paulo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$endereco&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'cidade'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que tem de errado? Tudo que importa num teste:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Lento&lt;/strong&gt; — cada teste espera a ida e volta pela rede.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Instável&lt;/strong&gt; — a API cair, ficar lenta ou mudar o retorno quebra seu teste sem seu código ter mudado.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perigoso&lt;/strong&gt; — se for uma API que cobra, envia e-mail ou cria pedido, você tá fazendo isso pra valer a cada execução.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sem controle&lt;/strong&gt; — como você testa "o que acontece quando a API devolve erro 500"? Não dá pra pedir pra ela cair na hora certa.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;O teste devia verificar o &lt;strong&gt;seu&lt;/strong&gt; código. Não a saúde do servidor dos outros.&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução: &lt;code&gt;Http::fake()&lt;/code&gt; intercepta tudo
&lt;/h2&gt;

&lt;p&gt;Aqui vem o &lt;code&gt;$pedido-&amp;gt;restore()&lt;/code&gt; dessa história. Se você usa o HTTP Client do Laravel (&lt;code&gt;Http::get()&lt;/code&gt;, &lt;code&gt;Http::post()&lt;/code&gt;...), uma linha resolve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Support\Facades\Http&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;test_busca_o_cep&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// A partir daqui, NENHUMA requisição sai de verdade.&lt;/span&gt;
    &lt;span class="c1"&gt;// Você diz o que cada URL deve responder.&lt;/span&gt;
    &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'viacep.com.br/*'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'localidade'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'São Paulo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'uf'&lt;/span&gt;         &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'SP'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="nv"&gt;$endereco&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;buscarCep&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'01001000'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;assertEquals&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'São Paulo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$endereco&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'cidade'&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;Pronto. O &lt;code&gt;Http::fake()&lt;/code&gt; &lt;strong&gt;intercepta&lt;/strong&gt; a requisição e devolve a resposta que você mandou, sem tocar na internet. O teste roda offline, em milissegundos, e dá o mesmo resultado toda vez. Você virou dono do comportamento da API.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como usar na prática
&lt;/h2&gt;

&lt;p&gt;O &lt;code&gt;fake()&lt;/code&gt; é flexível. Alguns cenários que você vai usar toda hora:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Fingir QUALQUER requisição como 200 vazio (o mais simples)&lt;/span&gt;
&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Respostas diferentes por URL&lt;/span&gt;
&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'github.com/*'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'plano'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'pro'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="s1"&gt;'viacep.com.br/*'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;''&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;404&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="c1"&gt;// simula CEP não achado&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="c1"&gt;// Simular a API fora do ar (erro 500) — o caminho que ninguém testa&lt;/span&gt;
&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'gateway.com/*'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Erro interno'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;500&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;Repara no último: agora você consegue testar &lt;strong&gt;o caminho de falha&lt;/strong&gt;. "Se o gateway devolver 500, meu app mostra uma mensagem amigável ou explode na cara do usuário?" Sem &lt;code&gt;fake()&lt;/code&gt;, você nunca teve como responder isso.&lt;/p&gt;

&lt;h2&gt;
  
  
  Não basta simular: confirme que você mandou certo
&lt;/h2&gt;

&lt;p&gt;Aqui mora a pegadinha que separa o teste que testa do teste que só finge.&lt;/p&gt;

&lt;p&gt;Simular a resposta não garante que &lt;strong&gt;você montou a requisição direito&lt;/strong&gt;. Passou o header de autenticação? Mandou o valor no campo certo? O &lt;code&gt;Http::assertSent()&lt;/code&gt; confere isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;cobrar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;valor&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;150&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;cartao&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'tok_abc'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;assertSent&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&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="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;url&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'https://gateway.com/charges'&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;hasHeader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Authorization'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'amount'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;150&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;Agora o teste garante os dois lados: você mandou o que devia &lt;strong&gt;e&lt;/strong&gt; reagiu certo à resposta.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: proíba requisições reais escaparem
&lt;/h2&gt;

&lt;p&gt;Tem um detalhe traiçoeiro. Se você esquecer de fakear uma URL, a requisição sai de verdade — e você nem percebe que aquele teste ainda depende da internet.&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;preventStrayRequests()&lt;/code&gt; fecha essa porta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;preventStrayRequests&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fake&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
    &lt;span class="s1"&gt;'gateway.com/*'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'ok'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://gateway.com/charges'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// ok, tá fakeado&lt;/span&gt;
&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://outra-api.com'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;        &lt;span class="c1"&gt;// 💥 lança exceção na hora&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Qualquer requisição sem fake correspondente estoura na hora, em vez de vazar silenciosamente pra rede. Coloca isso no &lt;code&gt;setUp()&lt;/code&gt; da sua suíte de testes e durma tranquilo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;A regra de ouro: &lt;strong&gt;teste não conversa com o mundo lá fora&lt;/strong&gt;. Ele verifica o seu código, de forma rápida e previsível. A conversa real com a API você deixa pra um teste de integração separado, que roda de vez em quando — não a cada commit.&lt;/p&gt;

&lt;p&gt;Um detalhe importante: isso só funciona se você usa o HTTP Client do Laravel (&lt;code&gt;Http::&lt;/code&gt;). Se ainda chama Guzzle ou cURL na mão, o &lt;code&gt;fake()&lt;/code&gt; não te alcança — e migrar pro &lt;code&gt;Http::&lt;/code&gt; é justamente o próximo passo (spoiler: ele também te dá &lt;code&gt;timeout&lt;/code&gt; e &lt;code&gt;retry&lt;/code&gt; de graça, mas isso é papo pra outro post).&lt;/p&gt;

&lt;p&gt;Me conta: você mocka suas APIs nos testes ou ainda reza pro sandbox ficar de pé? 👀&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>testing</category>
      <category>api</category>
    </item>
    <item>
      <title>A API de terceiro caiu e derrubou seu app junto</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Tue, 04 Aug 2026 22:56:07 +0000</pubDate>
      <link>https://dev.to/denisgusto1/a-api-de-terceiro-caiu-e-derrubou-seu-app-junto-1366</link>
      <guid>https://dev.to/denisgusto1/a-api-de-terceiro-caiu-e-derrubou-seu-app-junto-1366</guid>
      <description>&lt;h2&gt;
  
  
  A API deles caiu. Por que o SEU app caiu junto?
&lt;/h2&gt;

&lt;p&gt;O fornecedor de frete teve um problema. A API deles ficou lenta — respondendo em 40 segundos em vez de 200 milissegundos.&lt;/p&gt;

&lt;p&gt;Do lado deles, um incidente. Do seu lado? Caos.&lt;/p&gt;

&lt;p&gt;Cada requisição que seu app fez pra calcular o frete ficou pendurada esperando. Os workers da fila entupiram. As conexões do PHP-FPM esgotaram. Em poucos minutos, seu app inteiro parou de responder — não só o checkout, &lt;strong&gt;tudo&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Um problema que era deles virou seu problema. E o pior: era evitável com três linhas.&lt;/p&gt;

&lt;h2&gt;
  
  
  O código que confia demais no mundo lá fora
&lt;/h2&gt;

&lt;p&gt;Chamada de API é um ato de fé. Você pede algo pra um servidor que não é seu e espera que ele responda rápido, sempre, sem erro. Spoiler: ele não vai.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Confiança cega: espera pra sempre, e reza pra dar certo&lt;/span&gt;
&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api-frete.com/cotacao'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
    &lt;span class="s1"&gt;'cep'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$cep&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="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que tá faltando aqui?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Sem limite de espera.&lt;/strong&gt; Se a API travar, sua requisição trava junto. Por padrão o HTTP Client espera até 30 segundos — uma eternidade quando isso acontece em cascata.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sem tentar de novo.&lt;/strong&gt; Uma falha de rede momentânea (acontece o tempo todo) já derruba a operação inteira.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sem tratar erro.&lt;/strong&gt; Se a API devolver 500, o &lt;code&gt;$response-&amp;gt;json()&lt;/code&gt; segue como se nada fosse, e você processa lixo achando que é cotação.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  A blindagem: &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;retry&lt;/code&gt; e &lt;code&gt;throw&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;O HTTP Client do Laravel já traz o resgate embutido. Você encadeia e pronto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;   &lt;span class="c1"&gt;// desiste depois de 5s, não fica pendurado&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;            &lt;span class="c1"&gt;// tenta 3x, esperando 200ms entre as tentativas&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://api-frete.com/cotacao'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'cep'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$cep&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;

&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="k"&gt;throw&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;            &lt;span class="c1"&gt;// erro 4xx/5xx? lança exceção, não segue com lixo&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;json&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Três métodos, três problemas resolvidos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;timeout(5)&lt;/code&gt;&lt;/strong&gt; — se em 5 segundos não respondeu, o Laravel desiste e lança uma &lt;code&gt;ConnectionException&lt;/code&gt;. Sua requisição não fica travada arrastando o app junto.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;retry(3, 200)&lt;/code&gt;&lt;/strong&gt; — falha de rede? Ele tenta de novo, até 3 vezes, com 200ms de intervalo. A instabilidade passageira some sozinha.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;throw()&lt;/code&gt;&lt;/strong&gt; — se depois de tudo a resposta for erro, ele lança exceção. Você trata de propósito, em vez de processar dado inválido silenciosamente.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Como usar na prática
&lt;/h2&gt;

&lt;p&gt;Alguns ajustes que valem a pena conhecer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Separar timeout de conexão do timeout de resposta&lt;/span&gt;
&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;connectTimeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;10&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;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="cm"&gt;/* ... */&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Só relançar em erro de servidor, não em 404 esperado&lt;/span&gt;
&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;throwIf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;serverError&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

&lt;span class="c1"&gt;// Ou garantir que veio exatamente 200&lt;/span&gt;
&lt;span class="nv"&gt;$response&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;throwUnlessStatus&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;connectTimeout&lt;/code&gt; é quanto você espera pra &lt;strong&gt;conectar&lt;/strong&gt; (3s costuma bastar). &lt;code&gt;timeout&lt;/code&gt; é o total até a resposta chegar. Separar os dois te dá controle fino: "conecta rápido, mas se conectou, dou mais um tempinho pra processar".&lt;/p&gt;

&lt;h2&gt;
  
  
  A pegadinha: retry cego pode cobrar o cartão duas vezes
&lt;/h2&gt;

&lt;p&gt;Aqui mora o perigo que quase ninguém pensa. O &lt;code&gt;retry&lt;/code&gt; é maravilhoso pra operações &lt;strong&gt;idempotentes&lt;/strong&gt; — buscar uma cotação, consultar um CEP. Repetir não faz mal.&lt;/p&gt;

&lt;p&gt;Mas e uma cobrança? Se a API processou o pagamento e a resposta se perdeu no caminho, o &lt;code&gt;retry&lt;/code&gt; vai tentar de novo — e cobrar o cliente &lt;strong&gt;duas vezes&lt;/strong&gt;. 😬&lt;/p&gt;

&lt;p&gt;Pra esses casos, o &lt;code&gt;retry&lt;/code&gt; aceita uma condição: só repete se for erro de conexão (a requisição nem chegou), nunca se o servidor já respondeu:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Client\ConnectionException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Http\Client\PendingRequest&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nc"&gt;Http&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;retry&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;200&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$exception&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;PendingRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// só tenta de novo se NEM CHEGOU no servidor&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$exception&lt;/span&gt; &lt;span class="k"&gt;instanceof&lt;/span&gt; &lt;span class="nc"&gt;ConnectionException&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="nf"&gt;post&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'https://gateway.com/charges'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$dados&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A régua: &lt;strong&gt;repetir consulta, sempre. Repetir cobrança, só se tiver certeza de que nem chegou.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: e agora?
&lt;/h2&gt;

&lt;p&gt;Trocar Guzzle na mão pelo &lt;code&gt;Http::&lt;/code&gt; do Laravel não é só sintaxe mais bonita. É &lt;code&gt;timeout&lt;/code&gt;, &lt;code&gt;retry&lt;/code&gt;, &lt;code&gt;throw&lt;/code&gt; e — de brinde — a possibilidade de mockar tudo nos testes com &lt;code&gt;Http::fake()&lt;/code&gt;, sem tocar na API real (isso rende um post inteiro à parte).&lt;/p&gt;

&lt;p&gt;Se você tem uma integração externa crítica sem &lt;code&gt;timeout&lt;/code&gt;, essa é sua lição de casa pra hoje. Uma API lenta lá fora não pode ser capaz de derrubar seu app aqui dentro.&lt;/p&gt;

&lt;p&gt;Me conta: seu app já foi derrubado por uma API de terceiro? O que travou primeiro — a fila ou o FPM? 👀&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>api</category>
      <category>backend</category>
    </item>
    <item>
      <title>N+1: o bug silencioso que trava seu Laravel em produção</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Tue, 04 Aug 2026 22:56:06 +0000</pubDate>
      <link>https://dev.to/denisgusto1/n1-o-bug-silencioso-que-trava-seu-laravel-em-producao-4olp</link>
      <guid>https://dev.to/denisgusto1/n1-o-bug-silencioso-que-trava-seu-laravel-em-producao-4olp</guid>
      <description>&lt;h2&gt;
  
  
  Sua tela demora 8 segundos e o servidor não tem culpa
&lt;/h2&gt;

&lt;p&gt;O cliente manda a mensagem: "o painel tá travando".&lt;/p&gt;

&lt;p&gt;Você abre em produção. Conta os segundos. Um... dois... oito. A tela de listagem de pedidos, que tem 50 linhas, demora uma eternidade pra carregar.&lt;/p&gt;

&lt;p&gt;Primeiro palpite: servidor fraco. Você já tá quase abrindo um chamado pra subir o plano da VPS.&lt;/p&gt;

&lt;p&gt;Segura o dedo. Na esmagadora maioria das vezes, não é o servidor. É o &lt;strong&gt;N+1 query&lt;/strong&gt; — o bug mais silencioso e mais comum do Eloquent. Ele não quebra nada, não estoura erro, não aparece no log. Só deixa tudo lento e finge que a culpa é de outro.&lt;/p&gt;

&lt;h2&gt;
  
  
  O código parece inofensivo (e é aí que mora o problema)
&lt;/h2&gt;

&lt;p&gt;Olha esse trecho. Aposto que tem um igualzinho no seu projeto agora:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$pedidos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pedido&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// 1 query pra buscar os pedidos&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedidos&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// e aqui... +1 query POR pedido, toda vez que você toca no cliente&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nome&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;Bonito, limpo, legível. E secretamente fazendo o banco chorar.&lt;/p&gt;

&lt;p&gt;Cada vez que o loop acessa &lt;code&gt;$pedido-&amp;gt;cliente&lt;/code&gt;, o Eloquent dispara uma nova consulta pra buscar aquele cliente. São 50 pedidos? &lt;strong&gt;1 query dos pedidos + 50 dos clientes = 51 queries&lt;/strong&gt; numa tela só.&lt;/p&gt;

&lt;p&gt;Agora imagina uma listagem com 500 registros e três relações (cliente, produtos, endereço). O número explode. Já vi tela abrir o Debugbar e mostrar &lt;strong&gt;mais de 3.000 queries&lt;/strong&gt;. Cada uma custa alguns milissegundos de ida e volta ao banco. Some tudo: sua tela de 8 segundos.&lt;/p&gt;

&lt;h2&gt;
  
  
  Por que "N+1"? O nome entrega o problema
&lt;/h2&gt;

&lt;p&gt;É matemática simples e meio cruel.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;1&lt;/strong&gt; query pra buscar a lista principal (os N pedidos).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;N&lt;/strong&gt; queries extras, uma pra cada item, quando você acessa a relação dentro do loop.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Total: &lt;strong&gt;N + 1&lt;/strong&gt;. Quanto mais dados, pior fica — e o ambiente de dev, com 5 registros de teste, nunca te avisa. O bug só acorda em produção, com dado de verdade. Traiçoeiro.&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução cabe em uma palavra: &lt;code&gt;with()&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;Aqui vem o &lt;code&gt;$pedido-&amp;gt;restore()&lt;/code&gt; dessa história. Uma linha e você vira o herói do dia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Eager loading: já traz os clientes junto, de uma vez&lt;/span&gt;
&lt;span class="nv"&gt;$pedidos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pedido&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cliente'&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;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedidos&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;echo&lt;/span&gt; &lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nome&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// já tá na memória, ZERO query nova&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que mudou? Em vez de buscar cliente por cliente dentro do loop (&lt;em&gt;lazy loading&lt;/em&gt;), o &lt;code&gt;with()&lt;/code&gt; faz o Eloquent buscar &lt;strong&gt;todos os clientes de uma vez só&lt;/strong&gt;, com um &lt;code&gt;WHERE IN&lt;/code&gt;, antes do loop começar. Isso é &lt;em&gt;eager loading&lt;/em&gt; — "carregamento adiantado".&lt;/p&gt;

&lt;p&gt;De &lt;strong&gt;51 queries pra 2&lt;/strong&gt;. Sempre 2, tanto faz se são 50 ou 5.000 pedidos. A tela de 8 segundos vira 200 milissegundos.&lt;/p&gt;

&lt;p&gt;Não mexeu no servidor. Não instalou nada. Uma palavra.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como usar na prática
&lt;/h2&gt;

&lt;p&gt;O &lt;code&gt;with()&lt;/code&gt; é mais flexível do que parece:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Várias relações de uma vez&lt;/span&gt;
&lt;span class="nc"&gt;Pedido&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'cliente'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'itens'&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;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Relação aninhada: o item e o produto de cada item&lt;/span&gt;
&lt;span class="nc"&gt;Pedido&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;with&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'itens.produto'&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;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

&lt;span class="c1"&gt;// Já filtrou a query? Carrega depois, na collection que você já tem&lt;/span&gt;
&lt;span class="nv"&gt;$pedidos&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Pedido&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="nv"&gt;$pedidos&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'cliente'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// mesmo efeito, pra collection pronta&lt;/span&gt;

&lt;span class="c1"&gt;// Só quer a CONTAGEM, não os dados? withCount é seu amigo&lt;/span&gt;
&lt;span class="nc"&gt;Pedido&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;withCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'itens'&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;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt; &lt;span class="c1"&gt;// vira a coluna itens_count&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;with()&lt;/code&gt; você usa na query. &lt;code&gt;load()&lt;/code&gt; quando a collection já foi buscada. &lt;code&gt;withCount()&lt;/code&gt; quando só precisa do número, sem carregar os registros.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pegadinha: eager loading não é "coloca em tudo"
&lt;/h2&gt;

&lt;p&gt;Antes de sair jogando &lt;code&gt;with()&lt;/code&gt; em toda query, cuidado com o outro extremo.&lt;/p&gt;

&lt;p&gt;Se você carrega uma relação que &lt;strong&gt;não vai usar&lt;/strong&gt; naquela tela, tá trazendo dados à toa — gastando memória e banda pra nada. O &lt;code&gt;with('itens.produto.fornecedor.endereco')&lt;/code&gt; que ninguém lê é tão ruim quanto o N+1.&lt;/p&gt;

&lt;p&gt;A régua é simples: &lt;strong&gt;carregue o que você vai acessar no loop. Nada além disso.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;E tem a confusão clássica: &lt;code&gt;with()&lt;/code&gt; &lt;strong&gt;não&lt;/strong&gt; é a mesma coisa que &lt;code&gt;has()&lt;/code&gt; ou &lt;code&gt;whereHas()&lt;/code&gt;. O &lt;code&gt;with()&lt;/code&gt; &lt;em&gt;traz&lt;/em&gt; a relação junto. O &lt;code&gt;whereHas()&lt;/code&gt; &lt;em&gt;filtra&lt;/em&gt; pela existência dela. Um carrega, o outro filtra. Já vi muita gente usar um esperando o comportamento do outro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: faça o Laravel gritar quando você esquecer
&lt;/h2&gt;

&lt;p&gt;O melhor jeito de nunca mais deixar um N+1 passar é pedir pro próprio Laravel te avisar. No &lt;code&gt;AppServiceProvider&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Database\Eloquent\Model&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;boot&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Em dev, estoura exceção sempre que uma relação&lt;/span&gt;
    &lt;span class="c1"&gt;// for carregada de forma lazy (o gatilho do N+1)&lt;/span&gt;
    &lt;span class="nc"&gt;Model&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;preventLazyLoading&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nf"&gt;app&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;isProduction&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;Agora, se você esquecer um &lt;code&gt;with()&lt;/code&gt;, o Laravel lança uma exceção na sua cara &lt;strong&gt;em desenvolvimento&lt;/strong&gt; — muito antes de virar tela travada em produção. Deixa ligado só fora de produção pra não derrubar o app do cliente por um esquecimento.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;O N+1 é aquele bug que a gente escreve sem perceber e só descobre quando alguém reclama. A boa notícia: achar e matar é fácil quando você sabe o que procurar.&lt;/p&gt;

&lt;p&gt;Abre o Laravel Debugbar (ou o Telescope) numa tela lenta do seu projeto e olha a contagem de queries. Achou uma listagem com dezenas ou centenas? É N+1 quase na certa. Um &lt;code&gt;with()&lt;/code&gt; e você resolve.&lt;/p&gt;

&lt;p&gt;Me conta nos comentários: qual foi a tela mais absurda de queries que você já pegou? Aposto que tem gente com número maior que 3.000. 👀&lt;/p&gt;

</description>
      <category>laravel</category>
      <category>php</category>
      <category>eloquent</category>
      <category>performance</category>
    </item>
    <item>
      <title>Seu código dispara um Event e você não faz ideia do que acontece depois</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Thu, 30 Jul 2026 03:10:57 +0000</pubDate>
      <link>https://dev.to/denisgusto1/seu-codigo-dispara-um-event-e-voce-nao-faz-ideia-do-que-acontece-depois-37nd</link>
      <guid>https://dev.to/denisgusto1/seu-codigo-dispara-um-event-e-voce-nao-faz-ideia-do-que-acontece-depois-37nd</guid>
      <description>&lt;h2&gt;
  
  
  "De onde saiu esse e-mail?"
&lt;/h2&gt;

&lt;p&gt;O cliente abre um chamado: está recebendo dois e-mails de confirmação toda vez que faz um pedido.&lt;/p&gt;

&lt;p&gt;Você abre o &lt;code&gt;PedidoController&lt;/code&gt;. Não tem &lt;code&gt;Mail::&lt;/code&gt; ali. Abre a Action. Nada. Segue o rastro e chega numa linha inocente:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;PedidoConfirmado&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E agora? Quem escuta isso? Você dá um Ctrl+Shift+F em &lt;code&gt;PedidoConfirmado&lt;/code&gt; e aparecem seis arquivos. Dois deles mandam e-mail.&lt;/p&gt;

&lt;p&gt;Meia hora de investigação pra uma linha de código que, na teoria, deixava tudo mais organizado.&lt;/p&gt;

&lt;p&gt;Já passou por isso?&lt;/p&gt;

&lt;h2&gt;
  
  
  O que Event resolve de verdade
&lt;/h2&gt;

&lt;p&gt;Antes de bater no coitado: Event é ótimo. O problema é o uso, não a ferramenta.&lt;/p&gt;

&lt;p&gt;Olha esse controller, que é o cenário que motiva todo mundo a usar Event:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CriarPedidoRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$pedido&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;criarPedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cliente&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PedidoConfirmado&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;estoque&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;baixar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;itens&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;slack&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;avisarVendas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;analytics&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;registrarConversao&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;ActivityLog&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;registrar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pedido.criado'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$pedido&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;redirect&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;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pedidos.show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$pedido&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;Toda vez que o negócio quer mais uma coisinha quando um pedido é criado, você volta aqui e adiciona uma linha. Um ano depois o controller tem quatorze responsabilidades e cinco dependências que não têm nada a ver com "criar pedido".&lt;/p&gt;

&lt;p&gt;Com Event, vira isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CriarPedidoRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$pedido&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;criarPedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;execute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="nc"&gt;PedidoConfirmado&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$pedido&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;redirect&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;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'pedidos.show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O código que cria pedido não precisa saber que existe Slack. Você adiciona um listener novo sem tocar em nada que já funciona. Isso é desacoplamento de verdade.&lt;/p&gt;

&lt;h2&gt;
  
  
  E o que ele cobra em troca
&lt;/h2&gt;

&lt;p&gt;Só que tem uma conta chegando junto: &lt;strong&gt;você trocou legibilidade por flexibilidade.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Na versão de cima, você lê o controller e sabe exatamente tudo que acontece. Na versão de baixo, você lê o controller e sabe... que algo acontece. Em algum lugar. Talvez.&lt;/p&gt;

&lt;p&gt;O fluxo virou invisível. E código invisível é código que dá medo de mexer.&lt;/p&gt;

&lt;p&gt;Tem um segundo custo: &lt;strong&gt;erro em listener vira erro no request&lt;/strong&gt;. Se o listener não estiver na fila, ele roda no mesmo processo, e uma exceção lá dentro estoura na cara do usuário — depois do pedido já ter sido criado. O desacoplamento é só de código, não de execução.&lt;/p&gt;

&lt;h2&gt;
  
  
  A regra que eu uso pra decidir
&lt;/h2&gt;

&lt;p&gt;É uma pergunta só:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Isso é parte da operação, ou é uma consequência dela?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Se a operação está errada sem aquele passo, é parte dela. Chamada direta.&lt;/p&gt;

&lt;p&gt;Se a operação está completa e correta mesmo que aquele passo falhe, é consequência. Event.&lt;/p&gt;

&lt;p&gt;Aplicando no exemplo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Baixar estoque&lt;/strong&gt; → parte da operação. Pedido com estoque não baixado é bug de negócio. Vai direto no código, dentro da mesma transaction.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;E-mail, Slack, analytics, log de atividade&lt;/strong&gt; → consequência. O pedido continua válido se o Slack estiver fora. Event.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Repara que "baixar estoque" saiu do Event. Muita gente joga tudo pra lá porque parece mais limpo, e aí descobre em produção que um listener falhou silenciosamente e o estoque ficou errado por três dias.&lt;/p&gt;

&lt;p&gt;Regra curta: &lt;strong&gt;Event é pra efeito colateral, não pra regra de negócio.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Fazendo direito
&lt;/h2&gt;

&lt;p&gt;O listener fica em &lt;code&gt;app/Listeners&lt;/code&gt;, e desde o Laravel 11 você nem precisa registrar nada — o framework descobre sozinho pelo type-hint do &lt;code&gt;handle()&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Listeners&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Events\PedidoConfirmado&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Contracts\Queue\ShouldQueue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnviarEmailDeConfirmacao&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ShouldQueue&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;PedidoConfirmado&lt;/span&gt; &lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;pedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cliente&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;ConfirmacaoDePedido&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$event&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;pedido&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;Aquele &lt;code&gt;implements ShouldQueue&lt;/code&gt; não é detalhe. Ele resolve o segundo custo que falei: o listener vai pra fila, o request devolve na hora, e se o e-mail falhar isso não derruba o pedido. &lt;strong&gt;Efeito colateral quase sempre quer ser um listener enfileirado.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;E o nome do evento importa muito. Evento é fato consumado, no passado: &lt;code&gt;PedidoConfirmado&lt;/code&gt;, &lt;code&gt;UsuarioRegistrou&lt;/code&gt;, &lt;code&gt;PagamentoRecusado&lt;/code&gt;. Se você escreveu &lt;code&gt;EnviarEmailDePedido&lt;/code&gt;, isso não é um evento — é um comando com fantasia. Comando você chama direto.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pegadinha do Ctrl+Shift+F
&lt;/h2&gt;

&lt;p&gt;Lembra do problema lá do começo, de não saber quem escuta o quê? Tem comando pra isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan event:list
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ele lista todos os eventos e seus listeners. Deixa esse comando no bolso — é a primeira coisa a rodar quando alguém pergunta "de onde saiu esse e-mail?".&lt;/p&gt;

&lt;p&gt;Duas outras armadilhas que valem o aviso:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Dispatch dentro de transaction.&lt;/strong&gt; Se o listener está na fila e você despacha o evento antes do &lt;code&gt;commit&lt;/code&gt;, o worker pode buscar um registro que ainda não existe. Usa &lt;code&gt;ShouldHandleEventsAfterCommit&lt;/code&gt; no listener e dorme tranquilo.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model events como atalho.&lt;/strong&gt; Aquele &lt;code&gt;static::created()&lt;/code&gt; no &lt;code&gt;boot()&lt;/code&gt; da model dispara em seed, em factory de teste, em import em massa. Você vai mandar e-mail de boas-vindas pros 5 mil usuários de um import. Já vi acontecer. 💀&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: um Event, muitos ouvidos
&lt;/h2&gt;

&lt;p&gt;O momento em que Event realmente brilha é quando o mesmo fato interessa a áreas diferentes do sistema que não deveriam se conhecer.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;PagamentoAprovado&lt;/code&gt; pode liberar o acesso, disparar a nota fiscal, notificar o suporte e alimentar o dashboard — quatro listeners, quatro contextos, zero acoplamento entre eles. Adicionar o quinto não mexe em nenhum dos outros.&lt;/p&gt;

&lt;p&gt;Se o seu evento tem um listener só e nunca vai ter outro, você provavelmente só escreveu uma chamada de função com três arquivos a mais.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;Da próxima vez que for criar um Event, faz a pergunta: parte da operação ou consequência dela?&lt;/p&gt;

&lt;p&gt;E se a resposta for "sei lá", começa com chamada direta. É bem mais fácil transformar código explícito em Event depois do que caçar um fluxo invisível às onze da noite.&lt;/p&gt;

&lt;p&gt;Você é time "dispara Event pra tudo" ou time "só quando precisa mesmo"? Conta aí nos comentários — e se você já perdeu meia hora caçando quem mandou um e-mail, sabe exatamente do que eu tô falando.&lt;/p&gt;




</description>
      <category>laravel</category>
      <category>php</category>
      <category>backend</category>
      <category>cleancode</category>
    </item>
    <item>
      <title>Seu code review não precisa discutir espaçamento</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Wed, 29 Jul 2026 21:38:32 +0000</pubDate>
      <link>https://dev.to/denisgusto1/seu-code-review-nao-precisa-discutir-espacamento-1ild</link>
      <guid>https://dev.to/denisgusto1/seu-code-review-nao-precisa-discutir-espacamento-1ild</guid>
      <description>&lt;h2&gt;
  
  
  O pull request com 14 comentários (13 são sobre vírgula)
&lt;/h2&gt;

&lt;p&gt;Você abre o PR do colega. Quarenta arquivos alterados, uma feature importante.&lt;/p&gt;

&lt;p&gt;E os comentários são esses:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"faltou linha em branco antes do return"&lt;/p&gt;

&lt;p&gt;"acho que aqui cabia trailing comma"&lt;/p&gt;

&lt;p&gt;"aspas simples, né? o resto do projeto usa simples"&lt;/p&gt;

&lt;p&gt;"esse import não tá sendo usado"&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Enquanto isso, no meio do diff, tem um método recebendo &lt;code&gt;?User&lt;/code&gt; e chamando &lt;code&gt;$user-&amp;gt;email&lt;/code&gt; sem checar null. Ninguém viu. Todo mundo estava ocupado contando espaço.&lt;/p&gt;

&lt;p&gt;Esse é o custo real da falta de automação: &lt;strong&gt;não é o tempo gasto com detalhe, é a atenção que sobra pra revisar o que importa.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;E tem o lado humano. Ninguém gosta de receber cinco comentários sobre estilo. Cria atrito onde não precisa ter.&lt;/p&gt;

&lt;p&gt;Três ferramentas resolvem isso. Trinta minutos de setup.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Pint — o fim da discussão sobre estilo
&lt;/h2&gt;

&lt;p&gt;O Pint já vem instalado em projetos Laravel novos. Se o seu é antigo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require laravel/pint &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E roda:&lt;br&gt;
&lt;/p&gt;

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

&lt;/div&gt;



&lt;p&gt;Pronto. Ele formata o projeto inteiro seguindo o padrão Laravel. Aspas, espaçamento, ordem de import, trailing comma — tudo resolvido sem ninguém opinar.&lt;/p&gt;

&lt;p&gt;Se quiser ajustar alguma coisa, cria um &lt;code&gt;pint.json&lt;/code&gt; na raiz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"preset"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"laravel"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rules"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"declare_strict_types"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="nl"&gt;"ordered_imports"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"sort_algorithm"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"alpha"&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Dois flags que valem ouro no dia a dia:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./vendor/bin/pint &lt;span class="nt"&gt;--dirty&lt;/span&gt;   &lt;span class="c"&gt;# só o que você mexeu (rápido)&lt;/span&gt;
./vendor/bin/pint &lt;span class="nt"&gt;--test&lt;/span&gt;    &lt;span class="c"&gt;# não muda nada, só falha se estiver fora do padrão&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;code&gt;--test&lt;/code&gt; é o que vai pro CI.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Aviso importante:&lt;/strong&gt; rode o Pint no projeto inteiro em um commit isolado, sozinho, antes de tudo. Se você misturar formatação com mudança de lógica, o diff fica ilegível e o &lt;code&gt;git blame&lt;/code&gt; vira sopa.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Larastan — o bug que aparece antes do deploy
&lt;/h2&gt;

&lt;p&gt;Aqui o jogo muda de nível. O Larastan (PHPStan com esteroides de Laravel) lê seu código sem executar e aponta erro de tipo, método que não existe, variável possivelmente nula.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require &lt;span class="nt"&gt;--dev&lt;/span&gt; &lt;span class="s2"&gt;"larastan/larastan:^3.0"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Cria o &lt;code&gt;phpstan.neon&lt;/code&gt; na raiz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;includes:
    - vendor/larastan/larastan/extension.neon

parameters:
    paths:
        - app/
        - routes/

    level: 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E roda:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./vendor/bin/phpstan analyse
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Na primeira execução ele vai encontrar coisa. Muita coisa. Não entra em pânico e nem tenta corrigir tudo hoje.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Comece no nível 0 ou 1.&lt;/strong&gt; Sério. Zera os erros nesse nível, sobe pro próximo, zera de novo. Um nível por sprint é um ritmo saudável. Level 5 já pega a maioria dos problemas de verdade; do 6 pra cima começa a exigir docblock em tudo.&lt;/p&gt;

&lt;p&gt;E se o projeto for grande demais pra começar do zero, tem a saída elegante:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./vendor/bin/phpstan analyse &lt;span class="nt"&gt;--generate-baseline&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ele guarda todos os erros atuais num arquivo e passa a ignorá-los. A partir de agora, &lt;strong&gt;código novo não pode introduzir erro novo&lt;/strong&gt;. A dívida antiga fica lá, congelada, e você paga aos poucos.&lt;/p&gt;

&lt;p&gt;O que ele pega e o code review não pega:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Larastan: "Cannot call method email() on User|null"&lt;/span&gt;
&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;find&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$id&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mf"&gt;...&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   &lt;span class="c1"&gt;// 💥 em produção quando não achar&lt;/span&gt;

&lt;span class="c1"&gt;// Larastan: "Call to an undefined method App\Models\Pedido::pagos()"&lt;/span&gt;
&lt;span class="nc"&gt;Pedido&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;pagos&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;get&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;   &lt;span class="c1"&gt;// você renomeou o scope e esqueceu daqui&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse segundo é ouro puro em refatoração.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Rector — refatoração automática
&lt;/h2&gt;

&lt;p&gt;O Rector reescreve código pra você. É o que você usa quando sobe de versão do PHP ou do Laravel e não quer varrer 800 arquivos na mão.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;composer require rector/rector &lt;span class="nt"&gt;--dev&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;rector.php&lt;/code&gt; na raiz:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Rector\Config\RectorConfig&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nc"&gt;RectorConfig&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;configure&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;withPaths&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/app'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;__DIR__&lt;/span&gt; &lt;span class="mf"&gt;.&lt;/span&gt; &lt;span class="s1"&gt;'/tests'&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="nf"&gt;withPhpSets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;                        &lt;span class="c1"&gt;// moderniza pra sua versão do PHP&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withPreparedSets&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;deadCode&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;     &lt;span class="c1"&gt;// remove código morto&lt;/span&gt;
    &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;withComposerBased&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;laravel&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    &lt;span class="c1"&gt;// regras específicas de Laravel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Sempre rode em dry-run primeiro:&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./vendor/bin/rector &lt;span class="nt"&gt;--dry-run&lt;/span&gt;   &lt;span class="c"&gt;# mostra o que faria&lt;/span&gt;
./vendor/bin/rector             &lt;span class="c"&gt;# aplica&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que ele faz sozinho: converte construtor antigo pra property promotion, troca &lt;code&gt;array()&lt;/code&gt; por &lt;code&gt;[]&lt;/code&gt;, transforma if/else em operador ternário quando cabe, adiciona tipo de retorno, remove &lt;code&gt;use&lt;/code&gt; não usado, atualiza chamadas descontinuadas do Laravel.&lt;/p&gt;

&lt;p&gt;Uma tarde de Rector num projeto legado economiza semanas. Só faz duas coisas: &lt;strong&gt;tenha testes&lt;/strong&gt; e &lt;strong&gt;commit separado&lt;/strong&gt; — igual ao Pint.&lt;/p&gt;

&lt;h2&gt;
  
  
  Colocando pra rodar sozinho
&lt;/h2&gt;

&lt;p&gt;De nada adianta se depender de alguém lembrar. Dois lugares:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No CI&lt;/strong&gt;, um job que não deixa passar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight yaml"&gt;&lt;code&gt;  &lt;span class="na"&gt;qualidade&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;runs-on&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;ubuntu-latest&lt;/span&gt;
    &lt;span class="na"&gt;steps&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;actions/checkout@v4&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;uses&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;shivammathur/setup-php@v2&lt;/span&gt;
        &lt;span class="na"&gt;with&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;php-version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;8.4'&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;composer install --prefer-dist --no-interaction&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./vendor/bin/pint --test&lt;/span&gt;
      &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;run&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;./vendor/bin/phpstan analyse --error-format=github&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Repara que o Rector não está aí. Ele é ferramenta de refatoração pontual, não de validação contínua — deixa ele fora do CI, você roda quando precisa.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No pre-commit&lt;/strong&gt;, pra você nem ver o erro chegar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;# .git/hooks/pre-commit&lt;/span&gt;
&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;
./vendor/bin/pint &lt;span class="nt"&gt;--dirty&lt;/span&gt;
git add &lt;span class="si"&gt;$(&lt;/span&gt;git diff &lt;span class="nt"&gt;--name-only&lt;/span&gt; &lt;span class="nt"&gt;--cached&lt;/span&gt; | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="s1"&gt;'\.php$'&lt;/span&gt;&lt;span class="si"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Formata só o que mudou e já adiciona ao commit. Você nunca mais commita código fora do padrão — e nem percebe que isso aconteceu.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pegadinha: ferramenta não substitui revisão
&lt;/h2&gt;

&lt;p&gt;Vale deixar claro, porque tem gente que liga tudo isso e acha que resolveu code review.&lt;/p&gt;

&lt;p&gt;Não resolveu. Pint, Larastan e Rector não sabem se o nome da sua classe faz sentido, se a regra de negócio está certa, se aquela query vai derrubar o banco com 100 mil registros ou se você acabou de criar uma falha de autorização.&lt;/p&gt;

&lt;p&gt;Eles só limpam o ruído pra que a revisão humana sobre pro que só humano faz. Que é justamente o que você quer.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: um comando pra tudo
&lt;/h2&gt;

&lt;p&gt;Coloca no &lt;code&gt;composer.json&lt;/code&gt; e a vida fica mais fácil:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"qa"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"./vendor/bin/pint"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"./vendor/bin/phpstan analyse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
        &lt;/span&gt;&lt;span class="s2"&gt;"php artisan test"&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora é &lt;code&gt;composer qa&lt;/code&gt; antes de abrir qualquer PR. ✅&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;O objetivo não é ter código perfeito. É parar de gastar energia humana com coisa que máquina resolve.&lt;/p&gt;

&lt;p&gt;Se você tiver tempo pra só uma hoje, instala o Pint. São dois comandos e o retorno é imediato — na próxima revisão, ninguém mais vai comentar sobre vírgula.&lt;/p&gt;

&lt;p&gt;Seu projeto já usa alguma dessas? E qual foi o erro mais bizarro que o PHPStan achou pra você? Conta nos comentários — os melhores são sempre naquele arquivo que "funcionava bem há três anos".&lt;/p&gt;




</description>
      <category>laravel</category>
      <category>php</category>
      <category>productivity</category>
      <category>devops</category>
    </item>
    <item>
      <title>Seu usuário não deveria esperar o e-mail ser enviado</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Mon, 27 Jul 2026 18:05:53 +0000</pubDate>
      <link>https://dev.to/denisgusto1/seu-usuario-nao-deveria-esperar-o-e-mail-ser-enviado-3l81</link>
      <guid>https://dev.to/denisgusto1/seu-usuario-nao-deveria-esperar-o-e-mail-ser-enviado-3l81</guid>
      <description>&lt;h2&gt;
  
  
  Quatro segundos olhando um spinner
&lt;/h2&gt;

&lt;p&gt;O usuário preenche o cadastro, clica em "Criar conta" e... espera. Um. Dois. Três. Quatro segundos com o botão em loading.&lt;/p&gt;

&lt;p&gt;Do lado de cá, o INSERT no banco levou 12 milissegundos. O resto foi o servidor de e-mail respondendo com a pressa de quem não tem pressa. Depois teve a chamada pro CRM. E o PDF de boas-vindas sendo gerado.&lt;/p&gt;

&lt;p&gt;O usuário não sabe de nada disso. Ele só sabe que o site é lento. E se a conexão cair no meio, ele clica de novo — e agora você tem dois cadastros.&lt;/p&gt;

&lt;p&gt;A pergunta que resolve isso é bem simples: &lt;strong&gt;o usuário realmente precisa esperar por isso?&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Na esmagadora maioria das vezes, não.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema na prática
&lt;/h2&gt;

&lt;p&gt;Esse controller aqui provavelmente existe no seu projeto com outro nome:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CriarContaRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="c1"&gt;// 2s (se o SMTP estiver de bom humor)&lt;/span&gt;
    &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BoasVindas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;

    &lt;span class="c1"&gt;// 1,5s e depende de uma API que não é sua&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;crm&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sincronizar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// mais 1s, e ninguém vai abrir esse PDF hoje&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;gerarPdfDeBoasVindas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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;redirect&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;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dashboard'&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;Quatro operações. Só a primeira é urgente.&lt;/p&gt;

&lt;p&gt;E tem um detalhe pior que a lentidão: se o CRM estiver fora do ar, o cadastro &lt;strong&gt;inteiro&lt;/strong&gt; explode. O usuário toma um erro 500 numa conta que já foi criada no banco. Você acabou de acoplar o sucesso do seu cadastro à disponibilidade de um sistema de terceiro.&lt;/p&gt;

&lt;h2&gt;
  
  
  O conceito: agora ou depois?
&lt;/h2&gt;

&lt;p&gt;Fila é uma lista de tarefas que ficam guardadas em algum lugar (banco, Redis, SQS) esperando um processo separado — o &lt;em&gt;worker&lt;/em&gt; — pegar e executar.&lt;/p&gt;

&lt;p&gt;A regra pra decidir o que vai pra fila é quase sempre essa: &lt;strong&gt;se o usuário não precisa ver o resultado na tela seguinte, pode ir pra fila.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;E-mail, notificação, sincronização com sistema externo, geração de relatório, redimensionamento de imagem, webhook — tudo isso é "depois". Criar o registro que a próxima tela vai mostrar é "agora".&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução: um job por tarefa
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:job EnviarEmailDeBoasVindas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Jobs&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Contracts\Queue\ShouldQueue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Foundation\Queue\Queueable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;EnviarEmailDeBoasVindas&lt;/span&gt; &lt;span class="kd"&gt;implements&lt;/span&gt; &lt;span class="nc"&gt;ShouldQueue&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Queueable&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// tenta 3 vezes antes de desistir&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;int&lt;/span&gt; &lt;span class="nv"&gt;$tries&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="c1"&gt;// espera 10s, depois 30s, depois 60s entre as tentativas&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$backoff&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;30&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BoasVindas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user&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;O &lt;code&gt;implements ShouldQueue&lt;/code&gt; é a chave. Sem ele, o job roda na hora, igual antes.&lt;/p&gt;

&lt;p&gt;E o controller volta a ser rápido:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CriarContaRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;

    &lt;span class="nc"&gt;EnviarEmailDeBoasVindas&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;SincronizarComCrm&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nc"&gt;GerarPdfDeBoasVindas&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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;redirect&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;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'dashboard'&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;Três &lt;code&gt;dispatch&lt;/code&gt; custam alguns milissegundos — é só um INSERT na tabela de jobs. O usuário cai no dashboard quase instantaneamente, e o trabalho pesado acontece por trás.&lt;/p&gt;

&lt;p&gt;Melhor ainda: se o CRM estiver fora, só aquele job falha. O cadastro seguiu firme.&lt;/p&gt;

&lt;h2&gt;
  
  
  Colocando pra rodar
&lt;/h2&gt;

&lt;p&gt;Pra começar, o driver &lt;code&gt;database&lt;/code&gt; resolve bem (você troca por Redis quando o volume pedir). No &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight conf"&gt;&lt;code&gt;&lt;span class="n"&gt;QUEUE_CONNECTION&lt;/span&gt;=&lt;span class="n"&gt;database&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A tabela de jobs já vem na migration padrão do Laravel. Se seu projeto for antigo e não tiver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:queue-table
php artisan migrate
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E aí você sobe o worker:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan queue:work
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Em produção esse comando precisa de alguém tomando conta dele — Supervisor, systemd, ou o Horizon se você usa Redis. Worker que morre e ninguém reinicia é fila que enche em silêncio.&lt;/p&gt;

&lt;h2&gt;
  
  
  As pegadinhas (todas custam caro pelo menos uma vez)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. Job não guarda o objeto, guarda o ID.&lt;/strong&gt; O trait &lt;code&gt;Queueable&lt;/code&gt; serializa só a chave primária do model e busca no banco de novo na hora de executar. Isso é ótimo — o dado nunca fica velho — mas tem um efeito colateral: se o registro for deletado entre o dispatch e a execução, o job estoura &lt;code&gt;ModelNotFoundException&lt;/code&gt;. Por isso o padrão é passar o &lt;strong&gt;model&lt;/strong&gt;, não um array com os dados: você ganha o dado fresco de graça.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Deploy sem &lt;code&gt;queue:restart&lt;/code&gt; não muda nada.&lt;/strong&gt; O worker carrega seu código na memória quando sobe e fica lá rodando. Corrigiu um bug no job, subiu pra produção, e continua com o comportamento antigo? É isso. Coloca no final do seu script de deploy:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan queue:restart
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Todo mundo passa por essa. Uma vez.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Job falhou e ninguém viu.&lt;/strong&gt; Depois de esgotar as tentativas, o job vai pra tabela &lt;code&gt;failed_jobs&lt;/code&gt; e some da sua vida. Se você nunca olha essa tabela, e-mail nenhum foi enviado e você não sabe. Trate a falha:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;failed&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;\Throwable&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="n"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Log&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"Falha ao enviar boas-vindas pro user &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="si"&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="s1"&gt;'erro'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$e&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;getMessage&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;Se a tabela não existir no seu projeto: &lt;code&gt;php artisan make:queue-failed-table&lt;/code&gt;. Pra reprocessar tudo depois de corrigir: &lt;code&gt;php artisan queue:retry all&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Dispatch dentro de transaction.&lt;/strong&gt; Se você despacha o job antes do &lt;code&gt;commit&lt;/code&gt;, o worker pode ser rápido demais e ir buscar um registro que ainda não existe. O &lt;code&gt;afterCommit()&lt;/code&gt; resolve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;EnviarEmailDeBoasVindas&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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;afterCommit&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Ou você liga &lt;code&gt;'after_commit' =&amp;gt; true&lt;/code&gt; na conexão dentro de &lt;code&gt;config/queue.php&lt;/code&gt; e esquece o assunto.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: e se eu não quiser manter um worker?
&lt;/h2&gt;

&lt;p&gt;Nem todo projeto tem infra pra Supervisor rodando. Pra esses casos, o Laravel 12 trouxe duas conexões que ajudam bastante:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;RegistrarAcesso&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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;onConnection&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'deferred'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A &lt;code&gt;deferred&lt;/code&gt; executa o job no mesmo processo, mas &lt;strong&gt;depois&lt;/strong&gt; que a resposta HTTP já foi enviada pro navegador. A &lt;code&gt;background&lt;/code&gt; dispara um processo PHP separado. Nos dois casos o usuário não espera — e você não precisa de worker nenhum.&lt;/p&gt;

&lt;p&gt;Não substitui uma fila de verdade (não tem retry robusto, não tem monitoramento, não aguenta volume), mas pra um projeto pequeno que só quer tirar o e-mail do caminho do request, é uma mão na roda.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;Abre o controller mais lento do seu projeto e lê linha por linha se perguntando: "o usuário precisa ver isso na próxima tela?".&lt;/p&gt;

&lt;p&gt;Cada "não" é um &lt;code&gt;dispatch&lt;/code&gt; esperando pra acontecer. E provavelmente uns 2 segundos a menos no botão. ⚡&lt;/p&gt;

&lt;p&gt;E você, já tomou a do deploy sem &lt;code&gt;queue:restart&lt;/code&gt; ou ainda tem esse prazer pela frente? Conta nos comentários — e se seu projeto tem &lt;code&gt;failed_jobs&lt;/code&gt; acumulando poeira, essa é a hora de dar uma olhada lá.&lt;/p&gt;




</description>
      <category>laravel</category>
      <category>php</category>
      <category>backend</category>
      <category>webdev</category>
    </item>
    <item>
      <title>O if de permissão que você copiou pra 12 controllers</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Mon, 27 Jul 2026 17:39:58 +0000</pubDate>
      <link>https://dev.to/denisgusto1/o-if-de-permissao-que-voce-copiou-pra-12-controllers-3m90</link>
      <guid>https://dev.to/denisgusto1/o-if-de-permissao-que-voce-copiou-pra-12-controllers-3m90</guid>
      <description>&lt;h2&gt;
  
  
  Ctrl+C, Ctrl+V, e mais uma vez
&lt;/h2&gt;

&lt;p&gt;Toda aplicação Laravel tem essa linha. Geralmente várias cópias dela:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nf"&gt;auth&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;id&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&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;Ela nasce inocente, no &lt;code&gt;update()&lt;/code&gt; do controller. Aí você precisa da mesma checagem no &lt;code&gt;destroy()&lt;/code&gt;. Copia. Depois no &lt;code&gt;edit()&lt;/code&gt;. Copia. Aí o Blade precisa esconder o botão "Editar" de quem não pode. Copia de novo, agora em outro formato.&lt;/p&gt;

&lt;p&gt;Quando você percebe, a regra "quem pode mexer nesse post" está espalhada em oito, dez, doze lugares. Nenhum deles sabe da existência dos outros.&lt;/p&gt;

&lt;p&gt;E aí chega a segunda-feira em que o cliente pede: "admin também precisa poder editar qualquer post". Você já sentiu o frio na barriga? Porque agora você precisa achar todas as cópias. E vai esquecer uma. Sempre esquece uma.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema não é o if, é o endereço dele
&lt;/h2&gt;

&lt;p&gt;Olha o controller depois de umas três rodadas de "só mais uma regrinha":&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nf"&gt;auth&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;id&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nf"&gt;auth&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;user&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;isAdmin&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;publicado_em&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;publicado_em&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;diffInHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;abort&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;403&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'Posts publicados há mais de 24h não podem ser editados.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&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;back&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;Metade do método é autorização. E essa mesma metade precisa existir, idêntica, no &lt;code&gt;destroy()&lt;/code&gt;, no &lt;code&gt;edit()&lt;/code&gt; e na view.&lt;/p&gt;

&lt;p&gt;Repara no que aconteceu: uma regra de negócio de verdade — "quem, e em que condições, pode alterar um post" — virou detalhe de implementação de um método HTTP. Ela não tem casa própria. Mora de favor em todo canto.&lt;/p&gt;

&lt;h2&gt;
  
  
  O conceito: autorização merece um endereço
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Policy&lt;/strong&gt; é uma classe que concentra as regras de autorização de um model. Um arquivo, um assunto: "o que cada usuário pode fazer com um Post".&lt;/p&gt;

&lt;p&gt;O controller volta a fazer o que deveria: receber requisição, chamar o serviço, devolver resposta. E quando o cliente mudar de ideia sobre quem pode editar o quê, você abre &lt;strong&gt;um&lt;/strong&gt; arquivo.&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução: cria a policy e esquece o if
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;php artisan make:policy PostPolicy &lt;span class="nt"&gt;--model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;Post
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;code&gt;--model&lt;/code&gt; já gera o esqueleto com os métodos padrão (&lt;code&gt;viewAny&lt;/code&gt;, &lt;code&gt;view&lt;/code&gt;, &lt;code&gt;create&lt;/code&gt;, &lt;code&gt;update&lt;/code&gt;, &lt;code&gt;delete&lt;/code&gt;...). Preenche o que interessa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Policies&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\Post&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;App\Models\User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;PostPolicy&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&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="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isAdmin&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="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="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&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="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="c1"&gt;// autor só edita nas primeiras 24h após publicar&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;publicado_em&lt;/span&gt;
            &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;publicado_em&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;diffInHours&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;24&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&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;Não precisa registrar nada: o Laravel descobre a policy sozinho pela convenção de nomes (&lt;code&gt;Post&lt;/code&gt; → &lt;code&gt;PostPolicy&lt;/code&gt;). Se sua estrutura de pastas fugir do padrão, aí sim você registra manualmente com &lt;code&gt;Gate::policy(Post::class, PostPolicy::class)&lt;/code&gt; no &lt;code&gt;AppServiceProvider&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;E o controller emagrece:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nc"&gt;Gate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'update'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&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;back&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;Uma linha. Se não puder, o Laravel lança &lt;code&gt;AuthorizationException&lt;/code&gt; e devolve 403 sem você escrever nada.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como usar na prática
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;No Blade&lt;/strong&gt;, pra esconder o botão de quem não pode clicar:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@can('update', $post)
    &amp;lt;a href="{{ route('posts.edit', $post) }}"&amp;gt;Editar&amp;lt;/a&amp;gt;
@endcan
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Mesma regra, mesmo arquivo. Zero chance de a view discordar do controller.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Na rota&lt;/strong&gt;, com o middleware &lt;code&gt;can&lt;/code&gt;, quando o controller inteiro depende da permissão:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Route&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;put&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'/posts/{post}'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nc"&gt;PostController&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'update'&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;middleware&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'can:update,post'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Em qualquer lugar do código&lt;/strong&gt;, com o próprio usuário:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;can&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'delete'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Funciona em Job, em Livewire, em comando de console. A regra é uma só, e todo mundo pergunta pra ela.&lt;/p&gt;

&lt;h2&gt;
  
  
  As pegadinhas (essas pegam todo mundo)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. O &lt;code&gt;$this-&amp;gt;authorize()&lt;/code&gt; não existe mais por padrão.&lt;/strong&gt; Do Laravel 11 pra frente, o controller base do skeleton não traz mais o trait &lt;code&gt;AuthorizesRequests&lt;/code&gt;. Se você seguiu um tutorial antigo e tomou "Call to undefined method", é isso. Ou usa &lt;code&gt;Gate::authorize('update', $post)&lt;/code&gt;, ou adiciona o trait no seu controller base:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Foundation\Auth\Access\AuthorizesRequests&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;abstract&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Controller&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;AuthorizesRequests&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;2. Métodos sem model precisam do nome da classe.&lt;/strong&gt; &lt;code&gt;create&lt;/code&gt; e &lt;code&gt;viewAny&lt;/code&gt; não recebem instância — afinal, o post ainda não existe. Então você passa a classe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;Gate&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'create'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nc"&gt;Post&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="n"&gt;class&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esquecer disso e passar &lt;code&gt;$post&lt;/code&gt; num &lt;code&gt;create&lt;/code&gt; é campeão de confusão.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. O &lt;code&gt;before()&lt;/code&gt; é poderoso demais.&lt;/strong&gt; Dá pra liberar geral pro admin de uma vez:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;before&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$ability&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;?bool&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isAdmin&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&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;Só que atenção no retorno: &lt;code&gt;true&lt;/code&gt; autoriza, &lt;code&gt;false&lt;/code&gt; &lt;strong&gt;nega tudo&lt;/strong&gt; e nem chama o resto da policy, e &lt;code&gt;null&lt;/code&gt; deixa o fluxo continuar. Retornar &lt;code&gt;false&lt;/code&gt; sem querer no &lt;code&gt;before()&lt;/code&gt; derruba a policy inteira — e é um bug bem chato de achar, porque tudo simplesmente vira 403.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Visitante deslogado nem chega na policy.&lt;/strong&gt; Por padrão o Laravel nega antes de executar o método. Se um post público pode ser visto por qualquer um, tipe o parâmetro como opcional:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;view&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;?User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;publicado_em&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;?-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user_id&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;
  
  
  Bônus: mensagem de erro que ajuda
&lt;/h2&gt;

&lt;p&gt;Retornar &lt;code&gt;false&lt;/code&gt; dá um 403 genérico. Às vezes você quer explicar o motivo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Auth\Access\Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;update&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;Post&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$post&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;
        &lt;span class="o"&gt;?&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;allow&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
        &lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nc"&gt;Response&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;deny&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Você só pode editar seus próprios posts.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A mensagem chega no front, e o usuário para de abrir chamado perguntando por que o botão não funciona.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;Dá um &lt;code&gt;grep&lt;/code&gt; por &lt;code&gt;abort(403)&lt;/code&gt; no seu projeto agora. Cada resultado é uma regra de negócio morando no lugar errado, esperando o dia em que vai divergir das outras cópias.&lt;/p&gt;

&lt;p&gt;A boa notícia: migrar é rápido. Cria a policy, move o &lt;code&gt;if&lt;/code&gt; pra lá, e vai trocando os controllers um por um. Dá pra fazer em partes, sem parar o projeto.&lt;/p&gt;

&lt;p&gt;E aí, seu projeto tem policy ou tá no esquema &lt;code&gt;if&lt;/code&gt; copiado? Conta nos comentários — e se você já caiu na do &lt;code&gt;before()&lt;/code&gt; retornando &lt;code&gt;false&lt;/code&gt;, quero muito saber quanto tempo levou pra descobrir. 😄&lt;/p&gt;




</description>
      <category>laravel</category>
      <category>php</category>
      <category>cleancode</category>
      <category>backend</category>
    </item>
    <item>
      <title>Seu array associativo vai te trair (e vai ser em produção)</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Mon, 27 Jul 2026 17:35:00 +0000</pubDate>
      <link>https://dev.to/denisgusto1/seu-array-associativo-vai-te-trair-e-vai-ser-em-producao-22o0</link>
      <guid>https://dev.to/denisgusto1/seu-array-associativo-vai-te-trair-e-vai-ser-em-producao-22o0</guid>
      <description>&lt;h2&gt;
  
  
  O array que ninguém sabe o que tem dentro
&lt;/h2&gt;

&lt;p&gt;Você abre um Service depois de três meses sem mexer. A assinatura do método é essa aqui:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E aí? O que vai dentro desse &lt;code&gt;$data&lt;/code&gt;? Tem &lt;code&gt;nome&lt;/code&gt; ou &lt;code&gt;name&lt;/code&gt;? O &lt;code&gt;enviar_boas_vindas&lt;/code&gt; é booleano ou a string &lt;code&gt;"1"&lt;/code&gt; que veio do formulário? Tem &lt;code&gt;telefone&lt;/code&gt; ou aquilo era opcional?&lt;/p&gt;

&lt;p&gt;A resposta honesta é: ninguém sabe. Pra descobrir, você lê o corpo do método inteiro, abre o Form Request, e ainda dá um &lt;code&gt;dd($data)&lt;/code&gt; só pra ter certeza. Toda vez.&lt;/p&gt;

&lt;p&gt;E o pior nem é isso. O pior é que no dia em que alguém escrever &lt;code&gt;$data['name']&lt;/code&gt; em vez de &lt;code&gt;$data['nome']&lt;/code&gt;, o PHP não vai gritar em lugar nenhum útil. Vai gritar em produção, num log que você só vê depois que o cliente reclama.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema na prática
&lt;/h2&gt;

&lt;p&gt;Olha esse fluxo bem comum — Form Request valida, controller repassa, service usa:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Controller&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CriarUsuarioRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&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;redirect&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;route&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'usuarios.show'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// Service — boa sorte adivinhando o que tem aqui dentro&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'nome'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
        &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="c1"&gt;// esse aqui vem como string "0" do form... ou não vem nunca. Depende.&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'enviar_boas_vindas'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BoasVindas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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="nv"&gt;$user&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;Repara nos problemas empilhados:&lt;/p&gt;

&lt;p&gt;O &lt;code&gt;$data['enviar_boas_vindas']&lt;/code&gt; estoura um &lt;code&gt;Undefined array key&lt;/code&gt; se o campo não veio marcado no formulário — porque checkbox desmarcado simplesmente não é enviado. E se vier a string &lt;code&gt;"0"&lt;/code&gt;, o &lt;code&gt;if&lt;/code&gt; considera falso, mas &lt;code&gt;"false"&lt;/code&gt; seria verdadeiro. Divirta-se.&lt;/p&gt;

&lt;p&gt;Sua IDE não te ajuda em nada. Zero autocomplete, zero aviso. O array é uma caixa preta que aceita qualquer coisa e só reclama quando o código já rodou.&lt;/p&gt;

&lt;h2&gt;
  
  
  O conceito: dar um nome e uma forma pros dados
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;DTO&lt;/strong&gt; é &lt;em&gt;Data Transfer Object&lt;/em&gt;. Nome pomposo pra uma ideia bem simples: em vez de carregar dados soltos num array, você cria uma classe que descreve exatamente quais campos existem e de que tipo cada um é.&lt;/p&gt;

&lt;p&gt;Só isso. Não tem lógica de negócio, não fala com banco, não é model. É um envelope tipado que atravessa as camadas do seu sistema.&lt;/p&gt;

&lt;p&gt;A vantagem é que o contrato para de morar na sua cabeça e passa a morar no código. E o PHP vira seu revisor de código de graça.&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução: uma classe boba que resolve tudo
&lt;/h2&gt;

&lt;p&gt;Com PHP 8 e &lt;code&gt;readonly&lt;/code&gt;, um DTO cabe em poucas linhas:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;final&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;CriarUsuarioData&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$nome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;bool&lt;/span&gt; &lt;span class="nv"&gt;$enviarBoasVindas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;readonly&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt; &lt;span class="nv"&gt;$telefone&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&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;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;fromRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CriarUsuarioRequest&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;self&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="n"&gt;nome&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'nome'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="c1"&gt;// boolean() resolve "1", "on", "true", ausente... tudo&lt;/span&gt;
            &lt;span class="n"&gt;enviarBoasVindas&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'enviar_boas_vindas'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
            &lt;span class="n"&gt;telefone&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validated&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'telefone'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
        &lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora o service fica assim:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;CriarUsuarioData&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;create&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
        &lt;span class="s1"&gt;'name'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;nome&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;email&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;]);&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$data&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;enviarBoasVindas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nc"&gt;Mail&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;to&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;BoasVindas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&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="nv"&gt;$user&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;E o controller vira uma linha honesta:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;service&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;handle&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nc"&gt;CriarUsuarioData&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;fromRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O que mudou de verdade: se você digitar &lt;code&gt;$data-&amp;gt;nomee&lt;/code&gt;, a IDE marca em vermelho antes de você salvar. Se esquecer de passar o e-mail, o PHP reclama na hora de construir o objeto, não três camadas depois. E o &lt;code&gt;readonly&lt;/code&gt; garante que ninguém vai alterar o dado no meio do caminho e te deixar sem entender de onde veio aquele valor.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como usar na prática
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Na fronteira do sistema.&lt;/strong&gt; O DTO nasce onde o dado externo entra — Form Request, comando de console, payload de webhook — e daí pra dentro tudo trafega tipado. Um &lt;code&gt;fromRequest()&lt;/code&gt;, um &lt;code&gt;fromArray()&lt;/code&gt;, um &lt;code&gt;fromWebhook()&lt;/code&gt;, cada um sabendo traduzir seu formato de origem.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Em Jobs, que agradecem muito.&lt;/strong&gt; Job recebe array e você descobre o problema quando o worker já engoliu a fila? Com DTO, o construtor tipado é uma barreira antes do &lt;code&gt;dispatch&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="nc"&gt;ProcessarPedido&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;PedidoData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;pedidoId&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;valorTotal&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nv"&gt;$pedido&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Na saída também.&lt;/strong&gt; Integração com API externa devolvendo um JSON gigante e imprevisível? Converte pra DTO logo na resposta. Se o fornecedor mudar um campo, você quebra num lugar só — no &lt;code&gt;fromResponse()&lt;/code&gt; — em vez de em oito arquivos espalhados.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pegadinha: DTO não é model, e nem sempre vale
&lt;/h2&gt;

&lt;p&gt;Aqui vem o cuidado, porque tem quem descubra DTO e saia criando um pra cada método do projeto.&lt;/p&gt;

&lt;p&gt;DTO &lt;strong&gt;não&lt;/strong&gt; substitui o Eloquent Model. Model representa uma linha do banco com comportamento; DTO representa dados em trânsito. Se você criou um &lt;code&gt;UserData&lt;/code&gt; que é uma cópia exata da tabela &lt;code&gt;users&lt;/code&gt;, provavelmente você só reescreveu o model com mais trabalho.&lt;/p&gt;

&lt;p&gt;DTO &lt;strong&gt;não&lt;/strong&gt; guarda regra de negócio. Sem método &lt;code&gt;salvar()&lt;/code&gt;, sem &lt;code&gt;calcularDesconto()&lt;/code&gt; dentro dele. No momento em que ele começa a decidir coisas, virou service disfarçado.&lt;/p&gt;

&lt;p&gt;E DTO &lt;strong&gt;não&lt;/strong&gt; faz validação. Quem valida é o Form Request. O DTO assume que o dado já chegou limpo e só garante o formato daí pra frente.&lt;/p&gt;

&lt;p&gt;A régua que uso: se o dado atravessa mais de uma camada, ou se você já se pegou perguntando "o que vem nesse array?", vale o DTO. Pra um &lt;code&gt;$request-&amp;gt;only('email')&lt;/code&gt; que morre no controller, não vale.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: e quando o DTO tem 15 campos?
&lt;/h2&gt;

&lt;p&gt;Escrever &lt;code&gt;fromRequest()&lt;/code&gt; na mão fica chato rápido quando o formulário é grande. É exatamente aí que entra o &lt;a href="https://github.com/spatie/laravel-data" rel="noopener noreferrer"&gt;spatie/laravel-data&lt;/a&gt;, que faz o mapeamento automático, integra com validação, serializa pra JSON e ainda funciona com collections tipadas.&lt;/p&gt;

&lt;p&gt;Mas comece na mão. Escreve dois ou três DTOs com &lt;code&gt;readonly&lt;/code&gt; puro, sente o que muda no seu dia a dia, e só depois traga o pacote. Assim você usa a ferramenta entendendo o problema que ela resolve — em vez de instalar mais uma dependência por hype.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;Faz um teste rápido: abre seu projeto e procura por &lt;code&gt;array $data&lt;/code&gt; nas assinaturas de método. Quantos você achou? Agora escolhe o mais bagunçado e tenta lembrar, sem abrir o Form Request, o que exatamente vai dentro dele.&lt;/p&gt;

&lt;p&gt;Se você travou, achou seu primeiro DTO. 🎯&lt;/p&gt;

&lt;p&gt;E aí, você já usa DTO ou ainda tá no time do array associativo com fé? Conta nos comentários — e se conhece alguém que vive dando &lt;code&gt;dd($data)&lt;/code&gt; pra descobrir o que chegou, marca a pessoa lá.&lt;/p&gt;




</description>
      <category>php</category>
      <category>laravel</category>
      <category>cleancode</category>
      <category>backend</category>
    </item>
    <item>
      <title>Validação condicional no Laravel sem virar espaguete de if</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Wed, 22 Jul 2026 14:48:34 +0000</pubDate>
      <link>https://dev.to/denisgusto1/validacao-condicional-no-laravel-sem-virar-espaguete-de-if-4nok</link>
      <guid>https://dev.to/denisgusto1/validacao-condicional-no-laravel-sem-virar-espaguete-de-if-4nok</guid>
      <description>&lt;h2&gt;
  
  
  "Esse campo aqui só é obrigatório às vezes"
&lt;/h2&gt;

&lt;p&gt;Toda regra de validação começa simples. &lt;code&gt;required&lt;/code&gt;, &lt;code&gt;email&lt;/code&gt;, &lt;code&gt;min:8&lt;/code&gt;, beleza. Aí o PO chega e solta a frase mágica:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Ah, mas o CNPJ só é obrigatório se a pessoa for pessoa jurídica."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;E lá se foi a simplicidade. De repente sua validação depende de outro campo, e você faz o que todo mundo faz na primeira vez: joga um &lt;code&gt;if&lt;/code&gt; no controller, valida um pedaço, depois valida outro pedaço, e o método &lt;code&gt;store()&lt;/code&gt; vira um monstro de ramificação.&lt;/p&gt;

&lt;p&gt;Já passou por isso? A boa notícia é que o Laravel tem ferramenta pra praticamente todo tipo de "depende". Você só precisa parar de resolver tudo na mão.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema: validação picada no controller
&lt;/h2&gt;

&lt;p&gt;O jeito ingênuo é validar em pedaços, dependendo do que veio:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$regras&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
        &lt;span class="s1"&gt;'tipo'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|in:pf,pj'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'nome'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;];&lt;/span&gt;

    &lt;span class="c1"&gt;// e aqui começa a bagunça&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;tipo&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="s1"&gt;'pj'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$regras&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'cnpj'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'required|string|size:14'&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="nv"&gt;$regras&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'cpf'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'required|string|size:11'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;quer_newsletter&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$regras&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'required|email'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;validate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$regras&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="c1"&gt;// ...&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Funciona? Funciona. Mas repara no que aconteceu: sua regra de negócio agora tá espalhada em construção manual de array, o controller sabe demais sobre validação, e testar isso é um inferno. Cada &lt;code&gt;if&lt;/code&gt; novo é mais uma ramificação pra dar errado.&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução 1: &lt;code&gt;required_if&lt;/code&gt; e amigos
&lt;/h2&gt;

&lt;p&gt;Pra dependências simples entre campos, o Laravel já tem regras prontas. Você quase nunca precisa de &lt;code&gt;if&lt;/code&gt; pra isso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&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="s1"&gt;'tipo'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|in:pf,pj'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'cnpj'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required_if:tipo,pj|nullable|size:14'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'cpf'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required_if:tipo,pf|nullable|size:11'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required_if:quer_newsletter,true|email'&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;Tem uma família inteira dessas: &lt;code&gt;required_unless&lt;/code&gt;, &lt;code&gt;required_with&lt;/code&gt;, &lt;code&gt;required_without&lt;/code&gt;, &lt;code&gt;required_with_all&lt;/code&gt;, &lt;code&gt;exclude_if&lt;/code&gt;, &lt;code&gt;prohibited_if&lt;/code&gt;... Antes de escrever um &lt;code&gt;if&lt;/code&gt;, vale conferir se já não existe uma regra que faz exatamente o que você quer. Na maioria dos casos, existe.&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução 2: &lt;code&gt;Rule::when&lt;/code&gt; pra condição no meio da regra
&lt;/h2&gt;

&lt;p&gt;Quando a condição não é "um campo depende de outro", mas sim algo do contexto (o usuário logado, um plano, uma feature flag), o &lt;code&gt;Rule::when&lt;/code&gt; deixa a coisa declarativa dentro do próprio array:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Validation\Rule&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&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="s1"&gt;'titulo'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|string'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="c1"&gt;// só valida como obrigatório+único se for admin&lt;/span&gt;
        &lt;span class="s1"&gt;'slug'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nc"&gt;Rule&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="nf"&gt;when&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
            &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;user&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;isAdmin&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'required'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'unique:posts,slug'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
            &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'nullable'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="c1"&gt;// senão, opcional&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;Leu de cima pra baixo e entendeu a regra inteira, sem sair caçando um &lt;code&gt;if&lt;/code&gt; três métodos acima.&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução 3: &lt;code&gt;sometimes()&lt;/code&gt; pra lógica que não cabe no array
&lt;/h2&gt;

&lt;p&gt;E quando a condição é complexa de verdade, tipo "valida o desconto só se ele veio E o pedido passa de 500 reais"? Aí entra o método &lt;code&gt;sometimes()&lt;/code&gt; do Validator, ideal no &lt;code&gt;withValidator()&lt;/code&gt; de um Form Request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nc"&gt;Illuminate\Validation\Validator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;withValidator&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Validator&lt;/span&gt; &lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$validator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;sometimes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'desconto'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'numeric|max:50'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$input&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="nv"&gt;$input&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cupom&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$input&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;total&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;500&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;A regra do &lt;code&gt;desconto&lt;/code&gt; só é aplicada se aquele closure retornar &lt;code&gt;true&lt;/code&gt;. Toda a lógica maluca fica isolada num lugar só, com nome, testável, e longe do caminho principal.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como usar na prática
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Validação de array/coleção&lt;/strong&gt; — aquele formulário com N itens repetidos (um carrinho, uma lista de convidados). Usa o &lt;code&gt;.*&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;rules&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt; &lt;span class="kt"&gt;array&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="s1"&gt;'itens'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|array|min:1'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'itens.*.produto_id'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|exists:produtos,id'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="s1"&gt;'itens.*.quantidade'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required|integer|min:1'&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;Uma regra só valida os N itens do array. Nada de &lt;code&gt;foreach&lt;/code&gt; validando na mão.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mensagem customizada por condição&lt;/strong&gt; — combina com &lt;code&gt;messages()&lt;/code&gt; no Form Request pra falar a língua do usuário: "Informe o CNPJ para pessoa jurídica" em vez do genérico.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pegadinha: &lt;code&gt;nullable&lt;/code&gt; vs a ausência da regra
&lt;/h2&gt;

&lt;p&gt;Aqui cai muita gente. Quando você usa &lt;code&gt;required_if&lt;/code&gt;, quase sempre precisa combinar com &lt;code&gt;nullable&lt;/code&gt; no mesmo campo. Sem isso, se o campo vier vazio (mas presente no request), outras regras como &lt;code&gt;size:14&lt;/code&gt; ou &lt;code&gt;email&lt;/code&gt; ainda vão tentar validar a string vazia e reclamar.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ❌ se vier cnpj='' num PF, o size:14 reclama&lt;/span&gt;
&lt;span class="s1"&gt;'cnpj'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required_if:tipo,pj|size:14'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;

&lt;span class="c1"&gt;// ✅ vazio é permitido quando não obrigatório&lt;/span&gt;
&lt;span class="s1"&gt;'cnpj'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'required_if:tipo,pj|nullable|size:14'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;nullable&lt;/code&gt; diz "pode ser nulo/vazio, e nesse caso ignora as outras regras". É o par natural do &lt;code&gt;required_if&lt;/code&gt;. Guarda isso que economiza uma hora de debug.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: e agora?
&lt;/h2&gt;

&lt;p&gt;Abre o Form Request (ou controller) mais cheio de &lt;code&gt;if&lt;/code&gt; do seu projeto e faz o exercício: cada &lt;code&gt;if&lt;/code&gt; que existe só pra montar array de regra provavelmente tem um &lt;code&gt;required_if&lt;/code&gt;, &lt;code&gt;Rule::when&lt;/code&gt; ou &lt;code&gt;sometimes&lt;/code&gt; equivalente esperando pra substituir ele.&lt;/p&gt;

&lt;p&gt;E se você ainda valida direto no controller com &lt;code&gt;$request-&amp;gt;validate()&lt;/code&gt;, esse é o empurrãozinho pra migrar tudo pra um Form Request. Todo esse arsenal fica muito mais organizado lá dentro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;A regra de ouro: se você tá construindo array de validação com &lt;code&gt;if&lt;/code&gt;, o Laravel provavelmente já tem uma forma declarativa de fazer isso. Vale abrir a doc de validação e passar o olho na lista de regras — tem umas 20 que quase ninguém usa e resolvem casos que a gente insiste em fazer na mão.&lt;/p&gt;

&lt;p&gt;Qual regra de validação condicional você mais usa? Ou tem algum caso cabeludo que não soube resolver? Joga nos comentários que a gente destrincha junto. 👇&lt;/p&gt;




</description>
      <category>laravel</category>
      <category>php</category>
      <category>validation</category>
      <category>backend</category>
    </item>
    <item>
      <title>Early return: o jeito de matar aquele if aninhado que ninguém lê</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Wed, 22 Jul 2026 14:40:07 +0000</pubDate>
      <link>https://dev.to/denisgusto1/early-return-o-jeito-de-matar-aquele-if-aninhado-que-ninguem-le-5oa</link>
      <guid>https://dev.to/denisgusto1/early-return-o-jeito-de-matar-aquele-if-aninhado-que-ninguem-le-5oa</guid>
      <description>&lt;h2&gt;
  
  
  Seu código tem forma de seta e ninguém quer admitir
&lt;/h2&gt;

&lt;p&gt;Você já abriu um método, olhou pro lado direito da tela e viu aquilo? A indentação vai crescendo, crescendo, o código forma uma seta apontando pro canto, e lá no fundo, uns cinco níveis pra dentro, mora a linha que realmente importa.&lt;/p&gt;

&lt;p&gt;A gente até tem nome carinhoso pra isso: &lt;strong&gt;arrow code&lt;/strong&gt; (código seta). Ou, pros mais dramáticos, "a pirâmide da perdição".&lt;/p&gt;

&lt;p&gt;O pior é que quase sempre esse método começou pequeno. Um &lt;code&gt;if&lt;/code&gt; inocente. Aí veio uma validação, mais um caso, uma exceção pra tratar... e quando você percebe, precisa rolar o olho por seis linhas de fechamento de chave &lt;code&gt;}&lt;/code&gt; só pra descobrir a qual &lt;code&gt;if&lt;/code&gt; cada uma pertence.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema na prática
&lt;/h2&gt;

&lt;p&gt;Olha esse método de processar um pedido. Parece inofensivo, mas repara na indentação:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;processar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Order&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isPago&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;itens&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isNotEmpty&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;estaAtivo&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;foiEnviado&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="c1"&gt;// finalmente, o código de verdade&lt;/span&gt;
                    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;enviar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
                    &lt;span class="k"&gt;return&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="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
                &lt;span class="p"&gt;}&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
                &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
            &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&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;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Pra chegar na única linha que interessa (&lt;code&gt;$this-&amp;gt;enviar($order)&lt;/code&gt;), seu olho precisou atravessar quatro &lt;code&gt;if&lt;/code&gt; e ainda torcer pra não confundir qual &lt;code&gt;else&lt;/code&gt; fecha qual bloco. E se amanhã aparecer mais uma regra? Mais um nível pra dentro. A seta cresce.&lt;/p&gt;

&lt;p&gt;Esse código não é difícil porque o problema é difícil. Ele é difícil porque tá escrito de trás pra frente.&lt;/p&gt;

&lt;h2&gt;
  
  
  O conceito: verifica o que dá errado primeiro
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Early return&lt;/strong&gt; (ou "cláusula de guarda", &lt;em&gt;guard clause&lt;/em&gt;) é a ideia de tratar os casos de saída logo no começo do método e sair fora na hora. Em vez de aninhar o "caminho feliz" lá no fundo de um monte de &lt;code&gt;if&lt;/code&gt;, você inverte a lógica:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Não tá pago? Cai fora. Sem itens? Cai fora. Cliente inativo? Cai fora. Passou por tudo? Então executa."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;O caminho feliz fica no nível principal do método, sem indentação nenhuma. As exceções ficam no topo, cada uma resolvida e esquecida. Você lê de cima pra baixo, uma condição de cada vez, sem precisar segurar nada na cabeça.&lt;/p&gt;

&lt;h2&gt;
  
  
  A solução: mesmo método, agora legível
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;processar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Order&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;bool&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="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isPago&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="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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;itens&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isEmpty&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="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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;cliente&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;estaAtivo&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="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;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;foiEnviado&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="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="c1"&gt;// o caminho feliz, no nível principal, sem seta nenhuma&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;enviar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Zero &lt;code&gt;else&lt;/code&gt;. Zero aninhamento. Cada &lt;code&gt;if&lt;/code&gt; é uma pergunta simples: "tem algum motivo pra parar aqui?". Se tiver, para. Se chegou no fim, é porque passou por todos os filtros e agora pode fazer o trabalho de verdade com a consciência tranquila.&lt;/p&gt;

&lt;p&gt;E o de bônus: adicionar uma regra nova amanhã é colar mais um bloco de guarda no meio. Não mexe na estrutura, não empurra nada pra dentro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Como usar na prática
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Lançando exceção em vez de retornar&lt;/strong&gt; — funciona igualzinho, e é ótimo em Services e Actions:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="n"&gt;reembolsar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;Order&lt;/span&gt; &lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="kt"&gt;void&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="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;isPago&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;OrderException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Não dá pra reembolsar um pedido não pago.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;foiReembolsado&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;OrderException&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Esse pedido já foi reembolsado.'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;gateway&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;refund&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$order&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Saindo cedo dentro de um loop&lt;/strong&gt; com &lt;code&gt;continue&lt;/code&gt; — mesma filosofia, evita o &lt;code&gt;if&lt;/code&gt; gigante envolvendo o corpo inteiro:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;foreach&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$usuarios&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nv"&gt;$usuario&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$usuario&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="n"&gt;optOut&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;continue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="c1"&gt;// pula esse e segue&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="nf"&gt;notificar&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$usuario&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;No Blade também&lt;/strong&gt;, com &lt;code&gt;@continue&lt;/code&gt; e &lt;code&gt;@break&lt;/code&gt;, pra não aninhar a view inteira num &lt;code&gt;@if&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  A pegadinha: não é "return em todo canto"
&lt;/h2&gt;

&lt;p&gt;Aqui vem o aviso, porque tem sempre alguém que lê isso e sai espalhando &lt;code&gt;return&lt;/code&gt; no meio de qualquer função só pra parecer moderno.&lt;/p&gt;

&lt;p&gt;Early return brilha quando as saídas são &lt;strong&gt;cláusulas de guarda de verdade&lt;/strong&gt;: validações, pré-condições, casos de borda que impedem o fluxo principal. Aí ele reduz complexidade.&lt;/p&gt;

&lt;p&gt;O que ele &lt;strong&gt;não&lt;/strong&gt; é: desculpa pra ter dez &lt;code&gt;return&lt;/code&gt; espalhados no meio da lógica principal, cada um saindo de um lugar diferente, sem ninguém entender por onde o método realmente termina. Isso não é early return, é fuga.&lt;/p&gt;

&lt;p&gt;A régua é simples: os &lt;code&gt;return&lt;/code&gt; de guarda ficam &lt;strong&gt;agrupados no topo&lt;/strong&gt;, tratando o que dá errado. O &lt;code&gt;return&lt;/code&gt; do caminho feliz fica &lt;strong&gt;sozinho no fim&lt;/strong&gt;. Se seus returns estão espalhados feito confete no meio do código, você trocou um problema por outro.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: e agora?
&lt;/h2&gt;

&lt;p&gt;Abre o arquivo com o método mais bagunçado do seu projeto — você já sabe qual é. Conta quantos níveis de indentação ele tem. Passou de dois ou três? Provavelmente dá pra inverter os &lt;code&gt;if&lt;/code&gt; do topo em cláusulas de guarda e achatar aquilo em cinco minutos.&lt;/p&gt;

&lt;p&gt;Uma dica: comece pelos &lt;code&gt;else&lt;/code&gt;. Todo &lt;code&gt;else&lt;/code&gt; que existe só pra retornar algo é um forte candidato a virar um early return e sumir.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;Legibilidade não é frescura de quem gosta de código bonito. É você economizando o tempo do seu eu do futuro (ou do coitado que vai dar manutenção depois) na hora de entender o que aquele método faz.&lt;/p&gt;

&lt;p&gt;E você, é time "trata o erro primeiro e cai fora" ou ainda tem carinho pela pirâmide de &lt;code&gt;if&lt;/code&gt;? Conta nos comentários — e se conhece alguém que ama um &lt;code&gt;else&lt;/code&gt; aninhado, marca lá. 😅&lt;/p&gt;




</description>
      <category>php</category>
      <category>laravel</category>
      <category>cleancode</category>
      <category>beginners</category>
    </item>
    <item>
      <title>"fix: ajustes": o commit que não ajuda ninguém (nem você)</title>
      <dc:creator>Denis Augusto</dc:creator>
      <pubDate>Tue, 21 Jul 2026 10:24:20 +0000</pubDate>
      <link>https://dev.to/denisgusto1/fix-ajustes-o-commit-que-nao-ajuda-ninguem-nem-voce-24gd</link>
      <guid>https://dev.to/denisgusto1/fix-ajustes-o-commit-que-nao-ajuda-ninguem-nem-voce-24gd</guid>
      <description>&lt;h2&gt;
  
  
  Roda &lt;code&gt;git log --oneline&lt;/code&gt; no seu projeto agora.
&lt;/h2&gt;

&lt;p&gt;Vai. Roda mesmo.&lt;/p&gt;

&lt;p&gt;Se apareceu uma sequência tipo &lt;code&gt;ajustes&lt;/code&gt;, &lt;code&gt;correção&lt;/code&gt;, &lt;code&gt;wip&lt;/code&gt;, &lt;code&gt;agora vai&lt;/code&gt;, &lt;code&gt;ajustes 2&lt;/code&gt;, &lt;code&gt;revert dos ajustes&lt;/code&gt; — bem-vindo ao clube. Todo mundo já escreveu essa novela.&lt;/p&gt;

&lt;p&gt;O problema aparece três meses depois, quando você precisa entender por que aquela linha do &lt;code&gt;OrderService&lt;/code&gt; está daquele jeito. Você roda um &lt;code&gt;git blame&lt;/code&gt;, chega no commit e lê: &lt;strong&gt;"ajustes"&lt;/strong&gt;. Obrigado, eu do passado. Muito esclarecedor.&lt;/p&gt;

&lt;p&gt;Histórico do Git não é burocracia. É a única documentação que envelhece junto com o código, porque está grudada nele. E ela custa 10 segundos a mais por commit pra ficar boa.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema: seu histórico não responde nenhuma pergunta
&lt;/h2&gt;

&lt;p&gt;Olha esse log e tenta responder três coisas: o que entrou de novo? o que quebrou? dá pra fazer deploy?&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a3f9c21 ajustes
7b2e881 correção do bug
0c41d7a wip
e91f3b2 mudanças solicitadas
5d8a04c agora vai
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Não dá. E isso tem custo real:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Code review&lt;/strong&gt; — o revisor não sabe se &lt;code&gt;ajustes&lt;/code&gt; é refactor inofensivo ou mudança de regra de negócio&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Release&lt;/strong&gt; — alguém tem que reler o diff inteiro pra escrever o "o que mudou nessa versão"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Investigação de bug&lt;/strong&gt; — &lt;code&gt;git log --grep&lt;/code&gt; não serve pra nada quando tudo se chama a mesma coisa&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Você mesmo, na segunda-feira&lt;/strong&gt; — depois de um fim de semana, aquele &lt;code&gt;wip&lt;/code&gt; não significa nada&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O conceito: um prefixo, e só
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Conventional Commits&lt;/strong&gt; é uma convenção de mensagem tão simples que dá pra aprender no tempo de ler este parágrafo:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;tipo&amp;gt;(&amp;lt;escopo opcional&amp;gt;): &amp;lt;descrição&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Os tipos que você vai usar em 95% dos casos:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;feat&lt;/code&gt;&lt;/strong&gt; — funcionalidade nova&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;fix&lt;/code&gt;&lt;/strong&gt; — correção de bug&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;refactor&lt;/code&gt;&lt;/strong&gt; — mudou o código sem mudar comportamento&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;test&lt;/code&gt;&lt;/strong&gt; — mexeu em teste&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;docs&lt;/code&gt;&lt;/strong&gt; — mexeu em documentação&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;chore&lt;/code&gt;&lt;/strong&gt; — tarefa de manutenção (dependência, config, build)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;perf&lt;/code&gt;&lt;/strong&gt; — melhoria de performance&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Na prática, o mesmo histórico de antes vira isto:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;a3f9c21 feat(checkout): aplica cupom de desconto no total
7b2e881 fix(auth): corrige redirect após login expirado
0c41d7a refactor(orders): extrai cálculo de frete para Action
e91f3b2 test(checkout): cobre cupom expirado e cupom inválido
5d8a04c chore(deps): atualiza laravel/framework para 11.9
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora as três perguntas do começo se respondem só de bater o olho. E &lt;code&gt;git log --oneline --grep "^feat"&lt;/code&gt; te dá a lista de tudo que entrou de novo desde a última tag. Sem esforço.&lt;/p&gt;

&lt;h2&gt;
  
  
  O escopo, o corpo e o &lt;code&gt;!&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;escopo&lt;/strong&gt; entre parênteses é opcional, mas vale muito num projeto Laravel: &lt;code&gt;checkout&lt;/code&gt;, &lt;code&gt;auth&lt;/code&gt;, &lt;code&gt;api&lt;/code&gt;, &lt;code&gt;admin&lt;/code&gt;. É o "onde" da mudança, e permite filtrar por área.&lt;/p&gt;

&lt;p&gt;Quando a mensagem de uma linha não conta a história toda, o &lt;strong&gt;corpo&lt;/strong&gt; entra. Regra de ouro: o título diz &lt;em&gt;o que&lt;/em&gt;, o corpo diz &lt;em&gt;por quê&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fix(pedidos): usa transaction ao confirmar pedido

Sem a transaction, uma falha no gateway deixava o pedido como
confirmado e o estoque já debitado. Agora ou tudo entra ou nada entra.

Refs #412
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Esse commit vale ouro no &lt;code&gt;git blame&lt;/code&gt; daqui a um ano. O diff mostra o que mudou; ele explica por quê.&lt;/p&gt;

&lt;p&gt;E tem o caso da mudança que &lt;strong&gt;quebra compatibilidade&lt;/strong&gt; — sinalizada com &lt;code&gt;!&lt;/code&gt; antes dos dois-pontos:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;feat(api)!: remove campo legacy_id do UserResource
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Isso é um aviso gritante pra quem consome a sua API, e é o que separa uma release menor de uma release maior.&lt;/p&gt;

&lt;h2&gt;
  
  
  O CHANGELOG de graça
&lt;/h2&gt;

&lt;p&gt;Aqui é onde o padrão deixa de ser organização e vira ferramenta.&lt;/p&gt;

&lt;p&gt;Como a mensagem passa a ser &lt;strong&gt;legível por máquina&lt;/strong&gt;, dá pra derivar a versão automaticamente, seguindo Semantic Versioning:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Só &lt;code&gt;fix&lt;/code&gt;, &lt;code&gt;chore&lt;/code&gt;, &lt;code&gt;refactor&lt;/code&gt; → sobe o &lt;strong&gt;patch&lt;/strong&gt; (1.2.3 → 1.2.4)&lt;/li&gt;
&lt;li&gt;Tem &lt;code&gt;feat&lt;/code&gt; no meio → sobe o &lt;strong&gt;minor&lt;/strong&gt; (1.2.3 → 1.3.0)&lt;/li&gt;
&lt;li&gt;Tem &lt;code&gt;!&lt;/code&gt; ou &lt;code&gt;BREAKING CHANGE&lt;/code&gt; → sobe o &lt;strong&gt;major&lt;/strong&gt; (1.2.3 → 2.0.0)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Ferramentas como o &lt;strong&gt;release-please&lt;/strong&gt; ou o &lt;strong&gt;git-cliff&lt;/strong&gt; leem o histórico, calculam a nova versão, geram o &lt;code&gt;CHANGELOG.md&lt;/code&gt; agrupado por tipo e criam a tag. Num workflow do GitHub Actions, isso roda sozinho a cada merge na &lt;code&gt;main&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Você não escreve release notes nunca mais. Elas são um subproduto de você ter escrito commits decentes.&lt;/p&gt;

&lt;p&gt;Se quiser garantir o padrão no time, o &lt;strong&gt;commitlint&lt;/strong&gt; rejeita mensagem fora do formato no hook &lt;code&gt;commit-msg&lt;/code&gt;. Só uma dica: comece pela convenção combinada com o time e só depois automatize a validação. Ferramenta antes de acordo vira briga no PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pegadinha: o prefixo não é mágico
&lt;/h2&gt;

&lt;p&gt;Esse é o mal-entendido mais comum, e eu já vi acontecer:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;fix: ajustes
feat: mudanças
chore: coisas
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Parabéns, agora seu histórico é inútil &lt;strong&gt;e&lt;/strong&gt; padronizado. 😅&lt;/p&gt;

&lt;p&gt;O prefixo é a menor parte do valor. O que faz diferença é a descrição ser específica: &lt;code&gt;fix(auth): corrige redirect após login expirado&lt;/code&gt; conta uma história. &lt;code&gt;fix: ajustes&lt;/code&gt; não.&lt;/p&gt;

&lt;p&gt;Duas outras convenções que ajudam:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Imperativo, sem "eu"&lt;/strong&gt; — &lt;code&gt;adiciona&lt;/code&gt;, &lt;code&gt;corrige&lt;/code&gt;, &lt;code&gt;remove&lt;/code&gt;. Completa a frase "esse commit ___ o quê?"&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Commit atômico&lt;/strong&gt; — uma mudança lógica por commit. Se você precisa de um "e" no meio da mensagem, provavelmente eram dois commits&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;E fica tranquilo: bagunçou na hora de escrever? &lt;code&gt;git commit --amend&lt;/code&gt; conserta o último, e &lt;code&gt;git rebase -i&lt;/code&gt; conserta o resto antes de virar PR.&lt;/p&gt;

&lt;h2&gt;
  
  
  Bônus: e agora?
&lt;/h2&gt;

&lt;p&gt;Adota no próximo commit. Não precisa reescrever o passado nem convencer o time inteiro numa reunião — o histórico melhora a partir de hoje, e a diferença aparece sozinha no primeiro &lt;code&gt;git log&lt;/code&gt; que alguém rodar.&lt;/p&gt;

&lt;p&gt;Depois de um mês, roda isso e vê o que o seu projeto anda fazendo de verdade:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git log &lt;span class="nt"&gt;--oneline&lt;/span&gt; &lt;span class="nt"&gt;--since&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"1 month ago"&lt;/span&gt; | &lt;span class="nb"&gt;cut&lt;/span&gt; &lt;span class="nt"&gt;-d&lt;/span&gt;&lt;span class="s1"&gt;' '&lt;/span&gt; &lt;span class="nt"&gt;-f2&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; | &lt;span class="nb"&gt;uniq&lt;/span&gt; &lt;span class="nt"&gt;-c&lt;/span&gt; | &lt;span class="nb"&gt;sort&lt;/span&gt; &lt;span class="nt"&gt;-rn&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Se &lt;code&gt;chore&lt;/code&gt; e &lt;code&gt;fix&lt;/code&gt; dominam e mal aparece &lt;code&gt;feat&lt;/code&gt;, você acabou de descobrir algo importante sobre onde o seu tempo está indo.&lt;/p&gt;

&lt;h2&gt;
  
  
  Antes de você fechar a aba
&lt;/h2&gt;

&lt;p&gt;Vai lá, roda o &lt;code&gt;git log --oneline&lt;/code&gt; de novo e me conta nos comentários: qual é a pior mensagem de commit que você encontrou (bônus se for sua)? 😄&lt;/p&gt;




</description>
      <category>git</category>
      <category>github</category>
      <category>productivity</category>
      <category>webdev</category>
    </item>
  </channel>
</rss>
