<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Blog — Alessandro Melo</title>
    <link>https://alessandromelo.com.br/blog</link>
    <description>Artigos sobre Laravel, PHP moderno, React, Next.js e IA aplicada a produto.</description>
    <language>pt-BR</language>
    <lastBuildDate>Wed, 23 Sep 2026 16:46:02 GMT</lastBuildDate>
    <atom:link href="https://alessandromelo.com.br/feed.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Opus 5.5, GPT-6 Sol e Luna, Grok 4.7: o que muda no código em 48 horas</title>
      <link>https://alessandromelo.com.br/blog/opus-55-gpt6-grok-47-o-que-muda-no-codigo</link>
      <guid isPermaLink="true">https://alessandromelo.com.br/blog/opus-55-gpt6-grok-47-o-que-muda-no-codigo</guid>
      <pubDate>Wed, 23 Sep 2026 16:46:02 GMT</pubDate>
      <description>Três modelos de fronteira saíram em dois dias e o preço caiu junto. Como eu escolho modelo por tarefa e o que isso faz com a conta no fim do mês.</description>
      <content:encoded><![CDATA[<h2>O que aconteceu</h2>
<p>Em 21 e 22 de setembro de 2026 saíram três modelos de fronteira. A xAI lançou o Grok 4.7 na segunda, com 2,1 trilhões de parâmetros — 40% a mais que o 4.6 — a US$ 2 por milhão de tokens de entrada e US$ 6 de saída. No dia seguinte a Anthropic publicou o Claude Opus 5.5 e a OpenAI respondeu com dois modelos da família GPT-6, o Sol e o Luna.</p>
<p>Os números que interessam estão na tabela de preço. Opus 5.5 custa US$ 4 na entrada e US$ 20 na saída, por milhão de tokens, com leitura de cache a US$ 0,20 — uma queda de 60% nesse item específico. GPT-6 Sol fica em US$ 2 e US$ 10; GPT-6 Luna, em US$ 0,10 e US$ 0,50, com 90% de desconto na leitura de token em cache. Em benchmark, a Anthropic reporta 66,4% no Terminal-Bench 4.0 contra 55,8% do Fable 5.1, e 40% de tarefas concluídas no AutomationBench contra 26,9% do Opus 5. A OpenAI reporta 33,2% no mesmo AutomationBench para o Sol, a US$ 0,27 por tarefa.</p>
<h2>Por que isso importa</h2>
<p>A notícia não é &quot;modelo novo ficou mais inteligente&quot;. É que <strong>a distância de preço entre o topo e a base da mesma família virou duas ordens de grandeza</strong>. Entre o Luna e o Opus 5.5 há 40x na entrada e 40x na saída. Isso muda o desenho do sistema, não a escolha do fornecedor.</p>
<p>Até o ano passado eu escolhia um modelo por produto e vivia com ele. Com essa escada de preço, escolher um só é desperdício nas duas pontas: ou eu pago modelo de fronteira para classificar e-mail, ou uso modelo barato para escrever código que vai para produção e gasto revisão humana no lugar.</p>
<p>O segundo ponto é o cache. Quando a leitura de token em cache cai 60% ou 90%, o custo deixa de ser dominado pelo tamanho do prompt e passa a ser dominado por quantas vezes você <strong>muda</strong> o prompt. Prompt de sistema estável e longo ficou barato; prompt montado do zero a cada requisição, não. Isso premia uma arquitetura específica: contexto fixo no começo, variável no fim.</p>
<h2>Na prática</h2>
<p>O que eu faço em projeto Laravel é declarar as camadas por tarefa, não por modelo, e deixar o nome do modelo na configuração. Assim a troca de setembro não vira caça a string espalhada pelo código:</p>
<pre><code class="language-php">// config/ia.php
return [
    &#39;camadas&#39; =&gt; [
        &#39;triagem&#39;  =&gt; env(&#39;IA_MODELO_TRIAGEM&#39;, &#39;gpt-6-luna&#39;),
        &#39;redacao&#39;  =&gt; env(&#39;IA_MODELO_REDACAO&#39;, &#39;gpt-6-sol&#39;),
        &#39;critico&#39;  =&gt; env(&#39;IA_MODELO_CRITICO&#39;, &#39;claude-opus-5-5&#39;),
    ],
    &#39;teto_mensal_usd&#39; =&gt; env(&#39;IA_TETO_MENSAL_USD&#39;, 50),
];
</code></pre>
<pre><code class="language-php">class RoteadorDeModelo
{
    public function __construct(private GastoMensal $gasto) {}

    public function para(string $camada): string
    {
        // Estourou o teto: cai para a camada barata em vez de derrubar a fila.
        if ($this-&gt;gasto-&gt;acumuladoUsd() &gt;= config(&#39;ia.teto_mensal_usd&#39;)) {
            return config(&#39;ia.camadas.triagem&#39;);
        }

        return config(&quot;ia.camadas.{$camada}&quot;)
            ?? throw new InvalidArgumentException(&quot;camada desconhecida: {$camada}&quot;);
    }
}
</code></pre>
<p>Três detalhes que eu não pulo:</p>
<ol>
<li><strong>Registrar tokens de entrada, de saída e de cache separados</strong> em cada chamada. Sem isso não dá para saber se a conta subiu por volume ou por prompt reescrito.</li>
<li><strong>Prompt de sistema imutável</strong>, com o que varia no fim da mensagem. É o que faz o desconto de cache existir.</li>
<li><strong>Teto de gasto com degradação</strong>, e não com erro. Fila que morre por limite de orçamento vira incidente de madrugada.</li>
</ol>
<h2>O que eu faria</h2>
<p>Se eu tivesse um sistema em produção usando modelo de fronteira para tudo, eu mexeria esta semana — mas só na camada de baixo. Trocar a classificação e a extração de campo para o modelo mais barato da família é uma mudança de configuração com efeito imediato na fatura, e o erro, quando acontece, é visível e barato de corrigir.</p>
<p>A camada que escreve código ou responde cliente eu não trocaria pelo preço. Trocaria depois de rodar o meu próprio conjunto de casos — de 30 a 50 exemplos reais do projeto, com resposta esperada — porque benchmark público mede a média do mundo, e o que importa é a média do seu domínio.</p>
<p>Grok 4.7 eu deixo de fora por enquanto: o próprio anúncio o posiciona abaixo do topo em capacidade, e o preço dele não é baixo o bastante para competir com o Luna na camada de triagem. É o pior lugar para ficar — nem o mais barato, nem o melhor.</p>
<p>E a parte chata, que eu repito em toda consultoria: antes de trocar qualquer coisa, tenha como medir. Se você não consegue dizer hoje quanto custou a última semana de IA no seu sistema, a resposta para &quot;vale a pena trocar de modelo?&quot; não é técnica, é chute.</p>
]]></content:encoded>
      <dc:creator>Alessandro Melo</dc:creator>
      <category>ia</category>
      <category>llm</category>
      <category>arquitetura</category>
      <category>laravel</category>
    </item>
    <item>
      <title>Next.js 16.3.6: patch crítico no next/og e o que revisar na sua rota de imagem</title>
      <link>https://alessandromelo.com.br/blog/nextjs-16-3-6-patch-critico-next-og</link>
      <guid isPermaLink="true">https://alessandromelo.com.br/blog/nextjs-16-3-6-patch-critico-next-og</guid>
      <pubDate>Tue, 22 Sep 2026 23:59:45 GMT</pubDate>
      <description>Falha crítica no ImageResponse do next/og (CVE-2026-94545): quem é afetado, como atualizar e o padrão de código que você deve abandonar hoje.</description>
      <content:encoded><![CDATA[<h2>O que aconteceu</h2>
<p>Hoje, 22 de setembro de 2026, o time do Next.js publicou uma atualização de segurança fora do calendário: as versões 16.3.6 e 15.5.26. O <a href="https://nextjs.org/blog/nextjs-security-update-september-22-2026">comunicado oficial</a> explica que as duas atualizam dependências upstream, entre elas o Satori, a biblioteca que transforma JSX em SVG por baixo do <code>ImageResponse</code> de <code>next/og</code>. O advisory <a href="https://github.com/vercel/next.js/security/advisories/GHSA-vcvr-r3jv-pc5j">GHSA-vcvr-r3jv-pc5j</a> (CVE-2026-94545) tem severidade crítica, com nota 9.5, e descreve execução remota de código.</p>
<p>O recorte é estreito, e isso importa. Só as versões <code>&gt;=16.2.0 &lt;16.3.6</code> são afetadas, e só a implementação Node.js do <code>ImageResponse</code>. Quem gera imagem no runtime Edge está fora. O 15.x não tem a falha de execução remota: o 15.5.26 traz só endurecimento relacionado. A condição para explorar também é específica. A aplicação precisa colocar um valor controlado pelo atacante dentro do conteúdo, dos atributos ou dos estilos do SVG gerado. Um escape mal feito nesse SVG abre caminho para falhas em outras dependências.</p>
<h2>Por que isso importa mais do que parece</h2>
<p>Imagem de Open Graph é o tipo de código que ninguém revisa. Ela nasce num tutorial, gera aquela prévia bonita do link no LinkedIn e no WhatsApp, e fica esquecida em <code>opengraph-image.tsx</code> ou numa rota <code>/api/og</code>. O padrão mais copiado da internet é exatamente o que o advisory usa como exemplo de código vulnerável: ler <code>?title=</code> da URL e desenhar esse texto na imagem.</p>
<p>Esse padrão já era ruim antes da falha. Qualquer pessoa consegue gerar uma imagem com o seu domínio e o texto que quiser, e ainda gasta CPU do seu servidor a cada variação. A falha só transforma um problema de reputação num problema de servidor comprometido.</p>
<p>Tem um segundo ponto. Muita gente trata o bump de patch do framework como tarefa de segunda-feira. Aqui o advisory e o patch saíram juntos, com o detalhe técnico público. A janela entre &quot;saiu a correção&quot; e &quot;tem gente varrendo rota <code>/api/og</code> na internet&quot; costuma ser curta.</p>
<h2>Na prática</h2>
<p>Primeiro, descubra se você está no intervalo afetado:</p>
<pre><code class="language-bash">npm ls next
</code></pre>
<p>Se aparecer 16.2.x ou 16.3.0 a 16.3.5, atualize:</p>
<pre><code class="language-bash">npm install next@16.3.6
</code></pre>
<p>No 15.5, atualizar para <code>next@15.5.26</code> é recomendado, mas não é emergência de execução remota.</p>
<p>Depois, procure onde o projeto usa <code>ImageResponse</code> e olhe de onde vem cada valor que entra no JSX:</p>
<pre><code class="language-bash">grep -rn &quot;ImageResponse&quot; app src --include=*.tsx --include=*.ts
</code></pre>
<p>A correção de verdade, que vale mesmo depois do patch, é não desenhar entrada do usuário. Troque o texto vindo da query string por dado que você controla, buscado pelo parâmetro da rota. No Next 16, <code>params</code> é uma Promise:</p>
<pre><code class="language-tsx">// app/blog/[slug]/opengraph-image.tsx
import { ImageResponse } from &#39;next/og&#39;
import { getPostBySlug } from &#39;@/lib/posts&#39;

export const size = { width: 1200, height: 630 }
export const contentType = &#39;image/png&#39;

export default async function Image({
  params,
}: {
  params: Promise&lt;{ slug: string }&gt;
}) {
  const { slug } = await params
  const post = await getPostBySlug(slug)

  // O slug só serve para buscar. Nunca vai para o SVG.
  if (!post) return new Response(&#39;Not found&#39;, { status: 404 })

  return new ImageResponse(
    (
      &lt;div
        style={{
          width: &#39;100%&#39;,
          height: &#39;100%&#39;,
          display: &#39;flex&#39;,
          alignItems: &#39;center&#39;,
          padding: 80,
          fontSize: 64,
          background: &#39;#0b0b0b&#39;,
          color: &#39;#fafafa&#39;,
        }}
      &gt;
        {post.title}
      &lt;/div&gt;
    ),
    { ...size },
  )
}
</code></pre>
<p>O título sai do banco ou do markdown do próprio site, não da URL. Se o slug não existe, a rota devolve 404 em vez de desenhar qualquer coisa. <code>getPostBySlug</code> é a sua função de leitura, seja do banco ou do markdown. De bônus, sem API de request a imagem fica em cache, conforme a documentação, e não é redesenhada a cada acesso.</p>
<p>Se você não pode atualizar hoje, o próprio advisory dá o contorno: parar de passar valor controlado pelo atacante para conteúdo, atributo ou estilo do SVG. Na prática, é aplicar o exemplo acima antes do bump.</p>
<h2>O que eu faria</h2>
<p>Atualizaria hoje quem estiver no 16.2 ou no 16.3. O custo é um patch dentro da mesma minor, e o risco de não fazer é execução remota no servidor.</p>
<p>Quem está no 15.5 pode colocar o 15.5.26 no próximo deploy normal, sem correria. Mas eu aproveitaria para fazer a busca por <code>ImageResponse</code> do mesmo jeito. Se a rota de OG lê texto da query string, isso vira o próximo problema na hora em que o projeto subir para o 16.</p>
<p>E eu tiraria uma regra desse caso: gerador de imagem é renderizador, e renderizador que recebe texto de fora precisa da mesma desconfiança que um template de e-mail ou uma query SQL. A prévia do link parece enfeite, mas o código roda no seu servidor.</p>
]]></content:encoded>
      <dc:creator>Alessandro Melo</dc:creator>
      <category>nextjs</category>
      <category>react</category>
      <category>seguranca</category>
      <category>typescript</category>
    </item>
    <item>
      <title>IA em produto não é o modelo — é tudo o que fica em volta dele</title>
      <link>https://alessandromelo.com.br/blog/ia-em-produto-nao-e-o-modelo</link>
      <guid isPermaLink="true">https://alessandromelo.com.br/blog/ia-em-produto-nao-e-o-modelo</guid>
      <pubDate>Wed, 05 Aug 2026 00:00:00 GMT</pubDate>
      <description>A parte fácil de colocar LLM num produto é a chamada da API. O que decide se a funcionalidade sobrevive ao segundo mês é saída estruturada, avaliação, custo por operação e um plano honesto para quando o modelo errar — porque ele vai errar.</description>
      <content:encoded><![CDATA[<p>Quase todo projeto de IA aplicada que me chega começa com a mesma frase: &quot;a gente já testou no chat e funcionou muito bem&quot;. Funcionou mesmo. No chat, com uma pessoa atenta lendo a resposta, corrigindo o prompt na hora e descartando em silêncio as saídas ruins.</p>
<p>Produto não tem essa pessoa. Produto tem mil execuções por dia, sem ninguém olhando, e uma resposta ruim vira registro no banco, e-mail enviado ou cobrança errada. A distância entre esses dois mundos é onde mora todo o trabalho de engenharia — e é a parte que quase nunca aparece na demo.</p>
<h2>Texto livre é dívida técnica</h2>
<p>O primeiro erro estrutural é deixar o modelo devolver prosa e depois tentar entender essa prosa com regex. Já vi parser de trinta linhas para extrair um valor monetário de uma frase que o modelo escrevia de quatro jeitos diferentes dependendo do dia.</p>
<p>A saída de um LLM dentro de um sistema precisa ser um contrato, exatamente como qualquer outra integração. Hoje isso se resolve com saída estruturada: você declara o schema, o modelo responde dentro dele, e o que chega no seu código é um objeto validável.</p>
<pre><code class="language-json">{
  &quot;categoria&quot;: &quot;reembolso&quot;,
  &quot;urgencia&quot;: &quot;alta&quot;,
  &quot;valor_centavos&quot;: 12990,
  &quot;resumo&quot;: &quot;Cliente pede estorno de cobrança duplicada em 12/07.&quot;,
  &quot;confianca&quot;: 0.82
}
</code></pre>
<p>Repare no último campo. <code>confianca</code> não é enfeite: é o que permite rotear. Acima de um limiar, o fluxo segue automático; abaixo, cai numa fila de revisão humana. Sem esse número, você só tem duas opções ruins — confiar em tudo ou revisar tudo.</p>
<p>E, mesmo com schema, valide de novo do seu lado. O schema garante a forma, não o sentido. <code>valor_centavos: 999999999</code> passa em qualquer validação de tipo e ainda assim é absurdo para o seu domínio.</p>
<h2>A chamada é lenta, então trate como sistema externo</h2>
<p>Um LLM é uma dependência de rede com latência variável, limite de taxa e indisponibilidade eventual. Ou seja: é exatamente igual a um gateway de pagamento, e merece o mesmo tratamento arquitetural.</p>
<p>Na prática, isso significa que a chamada raramente deve acontecer dentro do request HTTP. No pipeline de vídeo que mantenho, tudo que envolve modelo — transcrição do áudio, seleção dos melhores trechos, geração de título e descrição — está em jobs separados, com retentativa e backoff. O usuário do painel vê um status mudando, não uma tela travada por quarenta segundos.</p>
<p>Fila também é o que torna a falha barata. Quando a API responde 529 num pico, o job volta pra fila e roda dez minutos depois. Sem fila, isso vira um erro 500 na cara do cliente e um chamado no suporte.</p>
<h2>Cache é a diferença entre viável e inviável</h2>
<p>A conta que ninguém faz na fase de protótipo é o custo por operação em escala real. Um pipeline que processa duzentos itens por dia e chama o modelo três vezes por item faz dezoito mil chamadas por mês. Se você não mediu isso antes de aprovar a feature, vai descobrir na fatura.</p>
<p>Duas coisas cortam esse custo de forma quase gratuita:</p>
<ul>
<li><strong>Cache de resultado por hash da entrada.</strong> Se o input é idêntico, a resposta pode ser reaproveitada. Em classificação de tickets, chamado repetido é regra, não exceção — vi taxa de acerto de cache passar de 30% só com isso.</li>
<li><strong>Escolher o modelo por tarefa.</strong> Classificar em cinco categorias não precisa do modelo mais caro do catálogo. Redação final para o cliente talvez precise. Tratar &quot;o modelo&quot; como uma escolha única para o produto inteiro é desperdício.</li>
</ul>
<p>E tem a economia que é só disciplina: não mande o documento inteiro quando três parágrafos resolvem. Contexto é o item de linha mais caro da conta.</p>
<h2>Sem avaliação, você está chutando</h2>
<p>Essa é a parte que mais separa protótipo de produto. Como você sabe que mexer no prompt melhorou alguma coisa?</p>
<p>A resposta útil é chata: um conjunto de casos com o resultado esperado, rodado a cada mudança. Não precisa de ferramenta sofisticada — comecei com um arquivo de casos e um comando Artisan cuspindo uma taxa de acerto no terminal. Cinquenta casos reais, tirados do que já aconteceu em produção, incluindo os feios: texto truncado, mensagem em duas línguas, cliente irritado escrevendo tudo em caixa alta.</p>
<p>Sem isso, cada ajuste de prompt é superstição. Alguém troca uma frase, testa em dois exemplos, acha que melhorou, e ninguém percebe que quebrou um caso que funcionava desde março.</p>
<h2>Projete para o erro, não para o acerto</h2>
<p>A pergunta que eu faço em toda reunião de escopo de IA é: <strong>o que acontece quando o modelo erra?</strong></p>
<p>Se a resposta for &quot;o cliente recebe um e-mail errado&quot; ou &quot;a cobrança sai com o valor errado&quot;, a funcionalidade não pode ser automática. Ela pode sugerir, pré-preencher, ordenar por prioridade, rascunhar — tudo com uma pessoa apertando o botão final. Se a resposta for &quot;o item fica na categoria errada e alguém arrasta de volta&quot;, aí sim automatize.</p>
<p>Essa distinção define o produto muito mais do que a escolha do modelo. E ela é uma decisão de negócio, não técnica.</p>
<h2>O resumo</h2>
<p>A parte de IA de uma feature de IA costuma ser dez linhas de código. O resto — schema, validação, fila, cache, avaliação, fallback e desenho do momento em que o humano entra — é engenharia comum, do tipo que todo dev sênior já sabe fazer. A boa notícia é que a experiência anterior conta. A má é que ninguém escapa dessa parte.</p>
]]></content:encoded>
      <dc:creator>Alessandro Melo</dc:creator>
      <category>IA</category>
      <category>Produto</category>
      <category>Arquitetura</category>
      <category>Laravel</category>
    </item>
    <item>
      <title>PHP moderno não precisa de defesa — precisa de gente usando direito</title>
      <link>https://alessandromelo.com.br/blog/php-moderno-nao-precisa-de-defesa</link>
      <guid isPermaLink="true">https://alessandromelo.com.br/blog/php-moderno-nao-precisa-de-defesa</guid>
      <pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate>
      <description>Depois de dez anos entregando sistemas em PHP, a discussão que ainda me cansa não é &apos;PHP é ruim?&apos;. É ver time novo escrevendo Laravel 12 com a cabeça do PHP 5.6. Tipos, enums, readonly, filas e testes mudam mais o dia a dia do que qualquer troca de linguagem.</description>
      <content:encoded><![CDATA[<p>Toda vez que aparece uma thread sobre &quot;PHP morreu&quot;, eu abro o terminal de algum projeto em produção e olho o que está rodando ali: Laravel 12, PHP 8.4, filas em Redis, painel em Filament, testes em Pest, deploy em Docker atrás de Nginx. Nada disso lembra o PHP que virou meme. E, sinceramente, a defesa da linguagem já foi ganha faz tempo — o problema hoje é outro.</p>
<p>O problema é que muito time escreve código <strong>de 2013 dentro de um framework de 2026</strong>. A stack evoluiu; o hábito, não.</p>
<h2>O sintoma: array como contrato</h2>
<p>O primeiro cheiro é sempre o mesmo. Um método de service que recebe <code>array $data</code>, mexe em dez chaves e devolve outro <code>array</code>. Ninguém sabe o que entra, ninguém sabe o que sai, e o autocomplete da IDE vira decoração. Aí o time compensa isso com documentação em Notion, que envelhece em duas sprints.</p>
<p>PHP tem tipagem forte o suficiente para acabar com isso desde a 7.4, e desde a 8.x ficou confortável de verdade. Um objeto de valor com <code>readonly</code> resolve mais do que um bloco de PHPDoc:</p>
<pre><code class="language-php">final readonly class NovaAssinatura
{
    public function __construct(
        public int $clienteId,
        public PlanoAssinatura $plano,
        public Carbon $inicioEm,
        public ?string $cupom = null,
    ) {}
}
</code></pre>
<p>Agora o contrato existe no código, não na cabeça de quem escreveu. Se alguém tentar passar um cupom como <code>int</code>, o erro aparece no CI, não no suporte.</p>
<h2>Enum é a ferramenta mais subaproveitada da linguagem</h2>
<p>Máquina de estados em string solta é a causa de bug mais chata que já depurei. <code>&#39;pending&#39;</code>, <code>&#39;PENDING&#39;</code>, <code>&#39;pendente&#39;</code>, <code>&#39;aguardando&#39;</code> — todos no mesmo banco, todos escritos por pessoas diferentes ao longo de dois anos. Enums nativos, que chegaram no PHP 8.1, matam essa categoria inteira de problema.</p>
<pre><code class="language-php">enum StatusDemanda: string
{
    case Aberta = &#39;aberta&#39;;
    case EmVisita = &#39;em_visita&#39;;
    case Orcada = &#39;orcada&#39;;
    case Concluida = &#39;concluida&#39;;

    public function podeAvancarPara(self $proximo): bool
    {
        return in_array($proximo, $this-&gt;proximosPassos(), strict: true);
    }
}
</code></pre>
<p>O Eloquent faz o cast direto no model, o Filament monta o select sozinho a partir do enum, e a regra de transição fica no mesmo arquivo que define os estados. Num marketplace de serviços que eu mantenho, esse único movimento cortou a maior fonte de chamado do suporte: pedido que &quot;pulava&quot; etapa porque alguém escreveu a string errada numa migration de correção.</p>
<h2>Fila não é otimização, é design</h2>
<p>A outra herança do PHP antigo é achar que tudo tem que acontecer dentro do request. Envio de e-mail, geração de PDF, chamada de gateway, processamento de imagem — tudo empurrado no ciclo do HTTP porque &quot;é rápido&quot;. Até o dia em que o gateway demora oito segundos e o cliente clica duas vezes no botão de pagar.</p>
<p>Filas mudam a forma de modelar a operação, não só o tempo de resposta. Quando o job é a unidade de trabalho, você ganha de graça três coisas que dificilmente ia implementar na mão: retentativa com backoff, isolamento de falha e visibilidade. Eu tenho pipeline de vídeo em produção onde cada etapa — baixar, transcrever, selecionar trecho, cortar, publicar — é um job separado. Quando a transcrição falha por limite de API, só aquela etapa volta para a fila. O resto do pipeline não sabe e não precisa saber.</p>
<p>A regra prática que uso: <strong>se a operação depende de um sistema que eu não controlo, ela não roda no request.</strong></p>
<h2>Teste que você escreve porque quer</h2>
<p>A objeção clássica contra teste em PHP era a ergonomia. PHPUnit funciona, mas escrever suíte nele nunca foi prazeroso. Pest resolveu isso de um jeito quase injusto — a barreira caiu tanto que hoje é mais rápido escrever o teste do que abrir o Postman e reproduzir o cenário na mão.</p>
<pre><code class="language-php">it(&#39;bloqueia checkout quando a loja está fechada&#39;, function () {
    $loja = Loja::factory()-&gt;fechada()-&gt;create();

    postJson(&#39;/api/checkout&#39;, payloadValido($loja))
        -&gt;assertStatus(422)
        -&gt;assertJsonPath(&#39;erro&#39;, &#39;loja_fechada&#39;);
});
</code></pre>
<p>Não é sobre cobertura de 100%. É sobre ter uma rede embaixo das regras que dão dinheiro: pagamento, cupom, zona de entrega, transição de status. Essas eu testo sempre. Layout de e-mail, não.</p>
<h2>O que eu não faço mais</h2>
<p>Duas coisas que já defendi e abandonei.</p>
<p>A primeira é encher o projeto de camada. Repository que só faz proxy pro Eloquent, DTO para toda ação, interface com uma implementação só. Isso não é arquitetura limpa, é imposto de indireção. O Eloquent já é a camada de dados; brigar com ele custa caro e não devolve nada.</p>
<p>A segunda é perseguir a última versão de tudo no dia do lançamento. Laravel tem ciclo previsível e política de suporte clara justamente para você <strong>planejar</strong> a atualização. Subir versão major numa sexta porque saiu no Twitter é escolha, não obrigação.</p>
<h2>O ponto</h2>
<p>PHP moderno é uma linguagem tipada, com enums, com atributos, com um ecossistema que resolve fila, painel, teste e deploy sem você escrever nada disso do zero. A linguagem cumpriu a parte dela. Falta o hábito acompanhar — e isso não se resolve trocando de stack, se resolve lendo a documentação da versão que você está usando e parando de escrever <code>array $data</code>.</p>
]]></content:encoded>
      <dc:creator>Alessandro Melo</dc:creator>
      <category>Laravel</category>
      <category>PHP</category>
      <category>Backend</category>
      <category>Arquitetura</category>
    </item>
  </channel>
</rss>
