<?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, 05 Aug 2026 12:00:00 GMT</lastBuildDate>
    <atom:link href="https://alessandromelo.com.br/feed.xml" rel="self" type="application/rss+xml" />
    <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 12: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 12: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>
