<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Marcello Oliveira · Em Produção</title><description>Histórias sobre colocar IA e software em produção, por Marcello Oliveira.</description><link>https://mrcll.me/pt/</link><language>pt-BR</language><atom:link href="https://mrcll.me/pt/rss.xml" rel="self" type="application/rss+xml"/><item><title>Prompt injection é o XSS da nossa geração</title><link>https://mrcll.me/pt/stories/prompt-injection-is-our-xss/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/prompt-injection-is-our-xss/</guid><description>Não existe textContent para um modelo: o que XSS e XSRF ensinam sobre prompt injection, e por que a defesa sai da entrada e vai para o privilégio.</description><pubDate>Wed, 11 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;XSS e XSRF são a mesma falha: um sistema que não separa dado de instrução, ou que age com uma autoridade que ninguém pediu. Os LLMs herdaram as duas.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Imagine uma aplicação web que guarda um JWT no localStorage, válido por duas horas. Uma das páginas tem um input, e o que você digita aparece para todo mundo que visitar depois. Um atacante digita um script ali. Daí em diante o navegador de cada visitante roda esse script e entrega o token, e o atacante pode usar a conta dessas pessoas por até duas horas.&lt;/p&gt;
&lt;p&gt;A correção é quase sem graça: escrever o texto com textContent em vez de innerHTML, ou sanitizar antes de chegar ao DOM. O navegador tem um jeito confiável de dizer “isto é texto, não código”, e o buraco fecha.&lt;/p&gt;
&lt;p&gt;O primo dele, o XSRF, é sobre outro erro. Cookies vão junto com qualquer requisição para um site, mesmo quando outro site a dispara, e até por um iframe invisível que faz você clicar em algo que nunca viu. O navegador age com uma autoridade que o usuário não deu naquele clique.&lt;/p&gt;
&lt;p&gt;Agora olhe para um assistente com LLM. Ele lê texto de fora, tickets, e-mails, documentos, páginas web, e normalmente tem ferramentas: enviar e-mail, consultar dados, criar registros. Dado e instrução viajam no mesmo canal, e a autoridade vai junto sem ninguém pedir. É a mistura antiga, as duas falhas de uma vez.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (InjectionSandbox): use a versão web em https://mrcll.me/pt/stories/prompt-injection-is-our-xss/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Sem defesa nenhuma, o ticket vira ordem. Cercar o texto não confiável ajuda até o atacante fechar a cerca ele mesmo; teste o terceiro exemplo. O que segura de verdade é tirar a ferramenta da tarefa. Resumir não precisa de send_email, e qualquer efeito colateral espera uma pessoa confirmar.&lt;/p&gt;
&lt;h2&gt;Nada com que escapar&lt;/h2&gt;
&lt;p&gt;O XSS foi resolvido porque o navegador consegue traçar uma linha firme em volta do texto. Um modelo não consegue. Você pode pedir, cercar, repetir e avisar, e ainda assim não existe escape garantido. Então a defesa precisa sair da entrada e ir para o privilégio. O que o modelo pode fazer importa mais do que o que ele leu.&lt;/p&gt;
&lt;p&gt;Na minha demonstração de XSS, um token roubado durava duas horas. Para um agente com ferramentas, a janela é uma chamada. Trato tudo o que um modelo lê como não confiável, inclusive os nossos próprios documentos. Dou a cada tarefa o menor número de ferramentas de que ela precisa. E a saída do modelo é renderizada como texto, nunca jogada em innerHTML, SQL ou shell.&lt;/p&gt;</content:encoded><category>AI</category><category>Backend</category><category>Frontend</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Somos maus estatísticos intuitivos. Os LLMs também.</title><link>https://mrcll.me/pt/stories/bad-intuitive-statisticians/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/bad-intuitive-statisticians/</guid><description>Monty Hall, um hostel em Foz do Iguaçu e o que temperatura e top-p fazem de verdade com o próximo token de um LLM.</description><pubDate>Tue, 10 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Um hostel em Foz do Iguaçu, três portas e uma cabra me ensinaram mais sobre temperatura e top-p do que qualquer model card.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Eu estava mochilando por Foz do Iguaçu com o &lt;em&gt;Rápido e Devagar&lt;/em&gt;, do Kahneman, na mochila, quando dois outros mochileiros perguntaram do que o livro tratava. Contei a armadilha mais famosa dele: um rapaz é descrito como tímido e prestativo. Ele é bibliotecário ou fazendeiro?&lt;/p&gt;
&lt;p&gt;Os dois disseram bibliotecário. Todo mundo diz. Só que existem muito mais fazendeiros do que bibliotecários, então a resposta sem graça é a mais provável. A descrição ganha e a taxa de base perde. Não somos estatísticos intuitivos, e quem constrói com modelos de linguagem não é exceção.&lt;/p&gt;
&lt;p&gt;Um modelo não consulta uma resposta. A cada passo ele tem uma distribuição de probabilidade sobre o próximo token e sorteia dela. Temperatura e top-p não deixam o modelo mais criativo nem mais cuidadoso; eles mudam o formato dessa distribuição antes do sorteio. A maioria dos times ajusta esses valores do jeito que meus amigos do hostel chutaram: no feeling, a partir de uma demo.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (Sampler): use a versão web em https://mrcll.me/pt/stories/bad-intuitive-statisticians/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Desça a temperatura para 0,1 e “tests” ganha todas as vezes: determinístico e confiantemente sem graça. Suba para 2 e “moon” começa a aparecer no seu resumo de incidente. O top-p é o guarda-corpo. Ele corta a cauda antes do sorteio, então uma temperatura alta não alcança tokens que nunca foram plausíveis. Nada disso aparece numa resposta só. Aparece quando você sorteia mil vezes, que é exatamente o que seus usuários vão fazer por você.&lt;/p&gt;
&lt;h2&gt;Três portas&lt;/h2&gt;
&lt;p&gt;No hostel eu puxei o problema de Monty Hall. Três portas, um carro, duas cabras. Você escolhe uma porta. O apresentador, que sabe onde está o carro, abre outra e mostra uma cabra, e oferece a troca. Os dois disseram que tanto fazia: sobraram duas portas, cinquenta por cento. Jogue uma rodada e depois deixe a máquina jogar mil.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (MontyHall): use a versão web em https://mrcll.me/pt/stories/bad-intuitive-statisticians/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Trocar ganha duas vezes em três. Sua primeira escolha acerta um terço das vezes, então erra dois terços, e o apresentador acabou de mostrar onde não olhar. A escolha dele é evidência, e evidência muda a distribuição. Isso é Bayes, e é também o trabalho inteiro com modelos de linguagem: o prompt, os documentos recuperados e os exemplos são evidências que mexem naquilo de onde o modelo sorteia. A temperatura só decide até onde dessa distribuição você deixa ele ir.&lt;/p&gt;
&lt;p&gt;Teve mais uma armadilha naquela noite, e é a que mais vejo hoje em avaliações. Um atacante do Fluminense marca 1 de 3 chutes no Brasileirão e 95 de 97 na Libertadores. Um do Flamengo é melhor nos dois: 101 de 300 e 96 de 97. Some os totais e o Fluminense vence, 96% a 50%. Paradoxo de Simpson. Um modelo pode ganhar do baseline em todas as fatias da sua avaliação e perder no total, ou o contrário.&lt;/p&gt;
&lt;p&gt;Então eu escolho a temperatura por feature, a partir de amostras. Julgo mudanças de prompt em centenas de execuções, nunca na resposta única da demo. E quebro toda avaliação por segmento antes de acreditar no total.&lt;/p&gt;</content:encoded><category>AI</category><category>Fundamentos</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>0.1 + 0.2 e o salário de alguém</title><link>https://mrcll.me/pt/stories/floats-and-salaries/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/floats-and-salaries/</guid><description>Por que dinheiro nunca é float: bits IEEE 754, desvio na folha e arredondamento de taxa, um contracheque por vez.</description><pubDate>Mon, 09 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Na maratona de programação, um épsilon faz o bug sumir. Na folha de pagamento, um épsilon é uma pessoa que recebeu um centavo a menos.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Aqui está um programa que deveria imprimir true:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;double phi = (1 + sqrt(5)) / 2;
printf(1 / phi == phi - 1 ? &quot;true&quot; : &quot;false&quot;);   /* false */&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A razão áurea satisfaz 1/φ = φ − 1 exatamente. O computador discorda na décima sexta casa decimal, porque os dois lados chegam ao mesmo número por arredondamentos diferentes. Na maratona de programação a correção é reflexo: comparar com tolerância, &lt;code&gt;fabs(x - y) &amp;lt; 1e-9&lt;/code&gt;, e ir para o próximo problema. Fiz isso por anos.&lt;/p&gt;
&lt;p&gt;Para entender o porquê, veja o que um double realmente guarda. 0,1 não tem forma binária exata, então a máquina guarda o valor mais próximo que consegue.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (FloatBits): use a versão web em https://mrcll.me/pt/stories/floats-and-salaries/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Aí os números viraram salários. Folha de pagamento entre países é soma, taxa e arredondamento, milhões de vezes por mês, em moedas que nem concordam sobre quantas casas decimais têm: o iene não tem nenhuma, o dinar kuwaitiano tem três. Cada arredondamento cai na conta de alguém, e os totais precisam fechar até a última unidade. O cenário abaixo é genérico, não é o sistema de nenhuma empresa. Some o mesmo valor a um total, contracheque após contracheque.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (PayrollDrift): use a versão web em https://mrcll.me/pt/stories/floats-and-salaries/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;O desvio é uma fração minúscula de centavo. Arredonde o total e ele até parece certo, e essa é a armadilha. A checagem de igualdade falha, o job de conciliação dispara um alarme e alguém coloca um épsilon para silenciar. Daí em diante o épsilon vira política.&lt;/p&gt;
&lt;h2&gt;Para onde os centavos vão de verdade&lt;/h2&gt;
&lt;p&gt;O dinheiro que some de verdade vem de outro lugar: aplicar uma taxa, um câmbio ou um reajuste e arredondar linha a linha.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (RateRounding): use a versão web em https://mrcll.me/pt/stories/floats-and-salaries/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Com taxa 1,15, 2.376 de 100.000 linhas arredondam para o centavo errado com ponto flutuante. Com centavos inteiros e taxa decimal, nenhuma. Um épsilon não resolve isso, porque não existe “perto o bastante” para um centavo pago à pessoa errada.&lt;/p&gt;
&lt;p&gt;A correção é uma representação, não uma comparação:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Guarde dinheiro como uma contagem inteira de unidades menores mais uma moeda, e tire o número de casas decimais da moeda (ISO 4217), nunca de um “2” global.&lt;/li&gt;&lt;li&gt;Faça a conta de taxa num tipo decimal, escolha uma regra de arredondamento (half-up ou half-even), escreva, aplique uma vez num passo definido e teste por propriedade contra aritmética exata.&lt;/li&gt;&lt;li&gt;Trate conciliação como igualdade. Se os totais só batem com épsilon, o bug está antes.&lt;/li&gt;&lt;/ol&gt;</content:encoded><category>Backend</category><category>Fundamentos</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Tokens são compressão: o que Shannon explica sobre a sua conta de IA</title><link>https://mrcll.me/pt/stories/tokens-are-compression/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/tokens-are-compression/</guid><description>Entropia de Shannon, tokenizadores e a sua conta de IA: digite qualquer texto e veja informação, tokens e custo se mexerem juntos.</description><pubDate>Sun, 08 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Por que alguns prompts custam o dobro de outros, explicado por um gigabyte de 1s e uma frase sobre um incêndio.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Toda fatura de LLM é uma contagem de tokens, de entrada e de saída. Os times olham para ela como olham para a conta de celular: um número que sobe. É mais interessante do que isso. Uma contagem de tokens mede o quanto o seu texto é surpreendente.&lt;/p&gt;
&lt;p&gt;Comece com um arquivo em que todo byte é 1. Faça ele ter um gigabyte. Quanta informação tem ali? Quase nenhuma. Se você sabe que é tudo 1, só precisa do tamanho. Dado e informação são coisas diferentes. Ou pegue a frase “Vi um incêndio e tinha fogo.” Todo incêndio tem fogo; a segunda metade pode sair e nada se perde. Informação é surpresa.&lt;/p&gt;
&lt;p&gt;Um tokenizador trabalha em cima dessa ideia. Antes de o modelo ler qualquer coisa, o texto é comprimido em tokens, pedaços de um dicionário aprendido com os dados de treino. Pedaços frequentes ganham um token só. Os raros são soletrados em vários. Por isso a conta acompanha a surpresa, não o tamanho. Experimente:&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (TokenMeter): use a versão web em https://mrcll.me/pt/stories/tokens-are-compression/&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2&gt;A aritmética de “euameiela”&lt;/h2&gt;
&lt;p&gt;Rode a figura com “euameiela”. Nove caracteres, 72 bits em disco e só uns 21,8 bits de informação, 2,419 bits por caractere. Cerca de 70% do espaço desperdiçado. Um código de prefixo feito à mão, um bit para o “e” frequente e códigos maiores para as letras raras, espreme tudo em 22 bits. Não dá para ir abaixo disso: bits são inteiros e a entropia é o piso.&lt;/p&gt;
&lt;p&gt;O byte-pair encoding é o mesmo truque em escala. Em vez de dar ao “e” um código curto, ele dá a pedaços comuns de texto um token só. Agora cole um UUID, um base64 ou um JSON minificado na caixa acima. A entropia por caractere sobe, as palavras deixam de parecer palavras e a contagem de tokens passa na frente da de caracteres.&lt;/p&gt;
&lt;p&gt;O idioma também pesa. Com dados de treino majoritariamente em inglês, uma frase em inglês costuma custar menos tokens do que a mesma frase em português. Os dois exemplos mostram isso.&lt;/p&gt;
&lt;p&gt;O que eu faço com isso: trato IDs, hashes e JSON ruidoso nos prompts como a parte cara que eles são. Meço com o tokenizador do próprio provedor em cada idioma que atendemos. E tudo o que vai em toda requisição é cacheado, encurtado ou removido, porque é uma linha no orçamento, leia alguém ou não.&lt;/p&gt;</content:encoded><category>AI</category><category>Fundamentos</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>De Q-learning a agentes com LLM</title><link>https://mrcll.me/pt/stories/q-learning-to-llm-agents/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/q-learning-to-llm-agents/</guid><description>De um labirinto com Q-learning a agentes com LLM em produção: num agente, a recompensa é o design.</description><pubDate>Sat, 07 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;O primeiro agente que treinei precisou de 799 episódios para sair de um labirinto pequeno. Agentes em produção não ganham nenhum ensaio.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;O primeiro agente que treinei vivia numa grade. Uma casa valia +1, uma custava −1, uma era parede e o resto era chão vazio. Ele não sabia nada. Aprendia só com recompensas, com a equação de Bellman fazendo a contabilidade:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;no episódio 0, ele vagava;&lt;/li&gt;&lt;li&gt;no 199, sabia mais e ainda falhava;&lt;/li&gt;&lt;li&gt;no 599, estava chegando lá;&lt;/li&gt;&lt;li&gt;no 799, resolvia o labirinto todas as vezes.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Hoje “agente” quase sempre quer dizer um modelo de linguagem num loop: lê o estado, escolhe uma ferramenta, olha o resultado, repete. O vocabulário sobreviveu inteiro. Agente, ambiente, recompensa, política. O que não sobreviveu foi o treino. Ninguém te dá 800 episódios com usuários reais antes de o agente precisar se comportar.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (QMaze): use a versão web em https://mrcll.me/pt/stories/q-learning-to-llm-agents/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Treine um episódio de cada vez e veja as setas se espalharem para trás a partir do +1, como uma mancha. Zere a penalidade por passo e o agente fica preguiçoso: continua chegando, sem pressa nenhuma. Zere a exploração e ele pode se contentar com o primeiro caminho que funcionou. O labirinto deixa óbvio algo fácil de esquecer com LLMs: a função de recompensa é o design. São as penalidades que fazem o agente terminar rápido.&lt;/p&gt;
&lt;p&gt;Num agente com LLM a política já chega treinada: o modelo mais o seu prompt. A recompensa continua sendo sua. Ela vira a avaliação que pontua a trajetória inteira, e não só a resposta final. A penalidade por passo vira o limite de passos, de tokens e de tempo. E uma propriedade do Q-learning vale igualzinho: os valores só continuam certos enquanto o ambiente continua o mesmo. Mudou uma ferramenta, uma API ou um schema, a política antiga ficou velha até você rodar as avaliações de novo.&lt;/p&gt;
&lt;p&gt;799 episódios para uma grade de três por quatro. Seu agente em produção ganha zero. Escreva a recompensa primeiro.&lt;/p&gt;</content:encoded><category>AI</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Rodar código no site dos outros</title><link>https://mrcll.me/pt/stories/running-code-on-other-peoples-sites/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/running-code-on-other-peoples-sites/</guid><description>Um pixel de rastreamento, três sites e o que é preciso para rodar seu código como visita no site dos outros.</description><pubDate>Fri, 06 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Já construí um script que vivia dentro do site dos nossos clientes. O mecanismo que te rastreia pela internet é o mesmo; o que muda é a responsabilidade.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Na Social Miner, construí e mantive o URSA: um script que nossos clientes embutiam por um pixel, do jeito que se embute o Google Tag Manager. Ele rodava num ambiente hostil, dentro do sandbox do navegador em sites que não eram nossos e que não dava para testar antes. As partes onsite e offsite conversavam por postMessage, com um pequeno pub/sub por cima. Eventos de comportamento iam em lote para o IndexedDB e eram enviados por um service worker, para nada se perder quando alguém fechava a aba. O URSA também capturava a permissão de push, e foi isso que viabilizou nosso produto de Push Notification. Liderei um time de quatro pessoas nele e no Channel Builder, feito com .NET e React.&lt;/p&gt;
&lt;p&gt;Código na página dos outros enxerga muita coisa. Para entender quanto, imagine uma empresa grande, a MOZART, que quer reconhecer a mesma pessoa em qualquer lugar da internet sem nunca saber quem ela é. Ela define a pessoa como um conjunto de ações: visitou a página do Palmeiras no globoesporte às 18h30, depois o site do governo de São Paulo às 18h35. Daí deduz: torce pro Palmeiras, mora em São Paulo, tem tempo livre perto das 18h30. Para costurar as visitas bastam dois headers HTTP. O referer diz de onde veio a requisição. Um cookie, mozart.id, identifica o navegador. Qualquer site que carregue mozart.com/external.png entrega os dois.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (TrackingPixel): use a versão web em https://mrcll.me/pt/stories/running-code-on-other-peoples-sites/&lt;/p&gt;&lt;/blockquote&gt;
&lt;h2&gt;Um pixel basta&lt;/h2&gt;
&lt;p&gt;Visite os três sites com tudo liberado e veja o perfil se montar. Depois bloqueie cookies de terceiros: cada requisição chega anônima e as visitas deixam de se ligar. Corte o referer para a origem e o “Palmeiras” some, porque o caminho da página não viaja mais. Os navegadores foram para esse lado, limitando cookies de terceiros e cortando o referer entre sites por padrão.&lt;/p&gt;
&lt;p&gt;Para código embutido de boa-fé isso não é obstáculo. É o contrato. Todo widget, SDK e chat embutido é visita na casa de outra pessoa, e uma boa visita:&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;fica isolada, num iframe ou em Shadow DOM, sem variáveis globais e sem CSS vazando;&lt;/li&gt;&lt;li&gt;depende só do que foi oferecido, presume que não há cookies de terceiros e que o referer vem cortado, e pede qualquer outra coisa de forma explícita;&lt;/li&gt;&lt;li&gt;tem orçamento de peso e de falha, para que, quando quebrar, a página do anfitrião continue de pé.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Uma imagem de um por um já bastou para descobrir um time, uma cidade e um horário. O que você faz com o que chega é toda a diferença.&lt;/p&gt;</content:encoded><category>Frontend</category><category>Backend</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Progressive enhancement para interfaces geradas por IA</title><link>https://mrcll.me/pt/stories/progressive-enhancement-ai-ui/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/progressive-enhancement-ai-ui/</guid><description>Progressive enhancement para interfaces geradas por IA: entregue o essencial sem o modelo e detecte a camada de IA.</description><pubDate>Thu, 05 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Parte do público não vai receber a experiência completa. Era verdade com navegadores antigos e voltou a ser agora que existe um modelo dentro da interface.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Vale mesmo usar um recurso que nem todo navegador suporta? A gente já aceita experiências diferentes em dispositivos diferentes. Por que não em navegadores diferentes?&lt;/p&gt;
&lt;p&gt;A resposta depende de um detalhe. Quem está num navegador antigo pode não ter escolha, ou não saber atualizar. A culpa não é dessa pessoa, então o essencial tem que funcionar em todo lugar e o resto vem por cima: &lt;code&gt;@supports&lt;/code&gt; para detectar recursos de CSS, Modernizr quando precisar de mais. Progressive enhancement é uma ideia antiga. E está prestes a ser útil de novo.&lt;/p&gt;
&lt;p&gt;Interface gerada por IA traz uma dependência que se comporta pior do que qualquer navegador já se comportou. Às vezes é lenta, às vezes erra, às vezes cai, às vezes o usuário desliga. Se a página só faz sentido quando o modelo responde, o pior dia do seu provedor vira o pior dia do seu produto. O modelo é a camada menos confiável que você já colocou em produção.&lt;/p&gt;
&lt;p&gt;Aqui está um card construído em camadas. Tire uma de cada vez.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (LayeredCard): use a versão web em https://mrcll.me/pt/stories/progressive-enhancement-ai-ui/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Desligue o JavaScript e os fatos continuam lá. Finja que o navegador não tem container queries e os filtros ficam, só que empilhados. Faça o modelo falhar e só o resumo vai embora; o espaço reservado avisa em vez de quebrar o layout. Deixe o modelo lento e o texto chega aos pedaços, num lugar que já era dele.&lt;/p&gt;
&lt;p&gt;Nenhuma chamada ao modelo é necessária para a página ser útil. Esse é o design inteiro.&lt;/p&gt;
&lt;p&gt;As ferramentas mudaram; a ideia não. Onde antes ficava o &lt;code&gt;@supports&lt;/code&gt;, agora ficam um timeout, um kill switch e uma configuração do usuário. O essencial não depende da camada nova, e a camada nova se anuncia quando chega: em streaming num espaço reservado para ela, para nada pular, e identificada, para ninguém confundir texto gerado com os fatos em volta.&lt;/p&gt;</content:encoded><category>AI</category><category>Frontend</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Por que eu escrevi meu próprio renderizador</title><link>https://mrcll.me/pt/stories/why-i-wrote-my-own-renderer/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/why-i-wrote-my-own-renderer/</guid><description>bradom: seis estratégias de renderização escritas do zero, de innerHTML a Virtual DOM e tagged templates, para tomar uma decisão.</description><pubDate>Wed, 04 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Antes de escolher o ferramental do design system da XP, eu quis entender o que ele fazia por dentro. O resultado foi o bradom: seis jeitos de renderizar a mesma interface, escritos do zero.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Quando precisei escolher as ferramentas do design system da XP Inc., nenhuma servia perfeitamente. Toda opção tinha benefícios e custos, e ler comparações não resolvia nada. Então levantei uma possibilidade meio atrevida com o time: e se a gente construísse a nossa própria biblioteca? Se outros construíram, por que não nós?&lt;/p&gt;
&lt;p&gt;Essa pergunta virou o bradom, um monorepo de estudo com seis pacotes, cada um renderizando a mesma interface de um jeito:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;innerHTML ingênuo;&lt;/li&gt;&lt;li&gt;Document Fragment;&lt;/li&gt;&lt;li&gt;Virtual DOM sem diff;&lt;/li&gt;&lt;li&gt;Virtual DOM completo;&lt;/li&gt;&lt;li&gt;tagged template literals (incompleto);&lt;/li&gt;&lt;li&gt;um benchmark com React para me manter honesto.&lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;O código está no &lt;a href=&quot;https://github.com/marcello0liveira/bradom&quot;&gt;GitHub&lt;/a&gt;.&lt;/p&gt;
&lt;h2&gt;Onde o tempo vai&lt;/h2&gt;
&lt;p&gt;O navegador transforma bytes em caracteres, tokens, nós, DOM e CSSOM, junta tudo na render tree, calcula o layout e pinta. Mexer no DOM pode reiniciar parte desse caminho, e evitar reflow desnecessário é quase tudo o que otimização de renderização quer dizer.&lt;/p&gt;
&lt;p&gt;O jeito mais fácil, reescrever o innerHTML, também é o pior. Ele joga fora o estado dos elementos, como o foco de um input e o texto dentro dele, e abre uma porta para XSS. Não é à toa que o React chama a versão dele de dangerouslySetInnerHTML. Digite no filtro abaixo e veja o que cada estratégia faz com a lista.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (RenderCost): use a versão web em https://mrcll.me/pt/stories/why-i-wrote-my-own-renderer/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Indo de “ab” para “a”, o innerHTML joga tudo fora e reconstrói. O Virtual DOM deixa você escrever o código como se recriasse tudo, depois compara a árvore nova com a antiga e só mexe no que mudou, e o estado sobrevive. O preço é avaliar a árvore inteira a cada mudança, inclusive as partes que nunca mudam.&lt;/p&gt;
&lt;h2&gt;O cache que ninguém te conta&lt;/h2&gt;
&lt;p&gt;Tagged templates têm uma propriedade discreta. O primeiro argumento que a função recebe, o array de strings estáticas, é o mesmo objeto toda vez que o mesmo template roda. Dá para usar como chave de cache. O HTML já vem separado em partes estáticas e buracos dinâmicos, então só os buracos precisam ser reavaliados. UI = f(estado), recalculada só onde o estado mudou.&lt;/p&gt;
&lt;p&gt;E a gente colocou o bradom em produção? Não. Para o Soma, o design system, escolhi uma tecnologia que não era minha. Escrever o renderizador nunca foi para entregar. Foi para escolher com as mãos e não com posts de blog, sabendo exatamente onde estaria o custo: em nós avaliados e operações no DOM, não em linhas de código.&lt;/p&gt;</content:encoded><category>Frontend</category><category>Fundamentos</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>O Nordeste e o Brasileirão</title><link>https://mrcll.me/pt/stories/northeast-brasileirao/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/northeast-brasileirao/</guid><description>Clubes nordestinos no Brasileirão: 44.006 km de viagem para o Fortaleza contra 11.992 para os clubes paulistas em 2023.</description><pubDate>Tue, 03 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Dois times em vinte. Uma fatia que já foi metade da tabela, e já foi zero.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Cinco jogos em quinze dias. Em 2022 o Fortaleza recebeu o São Paulo, viajou para enfrentar Vitória, Botafogo e Alianza Lima e voltou para casa para pegar o Fluminense. São 10.362 km em linha reta, e o voo nem sempre é fretado.&lt;/p&gt;
&lt;p&gt;A Série A de 2023 começou com 2 clubes nordestinos em 20, ou 10%. Já foi bem diferente. Em 1962, 9 dos clubes da Taça Brasil, metade dos participantes, eram do Nordeste. Em 1967, no Torneio Roberto Gomes Pedrosa, não havia nenhum. A fatia de hoje fica mais perto da participação da região no PIB, 13,6%, do que na população, 30%.&lt;/p&gt;
&lt;p&gt;Os motivos são muitos, e a distância pesa mais do que a maioria, porque o calendário nunca para para ninguém se recuperar. Assim é uma temporada de viagens.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (TravelChart): use a versão web em https://mrcll.me/pt/stories/northeast-brasileirao/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Para jogar a Série A de 2023, o Fortaleza viaja 44.006 km em linha reta. Os clubes paulistas viajam 11.992.&lt;/p&gt;
&lt;p&gt;E mesmo assim: o Fortaleza virou o primeiro clube nordestino a jogar a Libertadores em anos seguidos, 2022 e 2023, e em 2022 levou os dois maiores públicos da fase de grupos, 50.429 pagantes contra o Colo-Colo e 48.867 contra o River Plate. Toda tabela esconde um custo que não mostra. Nesta, é a distância.&lt;/p&gt;</content:encoded><category>Off the code</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Fibonacci de 10¹⁸ em 84 multiplicações</title><link>https://mrcll.me/pt/stories/linear-recurrences-log-time/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/linear-recurrences-log-time/</guid><description>Fibonacci de 10^18 em 84 multiplicações de matrizes: recorrências lineares em tempo logarítmico.</description><pubDate>Mon, 02 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Toda recorrência linear pode ser escrita como multiplicação de matrizes. E matrizes podem ser elevadas a uma potência rápido.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Qual é o 10¹⁸-ésimo número de Fibonacci, módulo algo razoável? Um loop ainda estaria rodando muito depois do fim da maratona. Dá para chegar lá com 84 multiplicações.&lt;/p&gt;
&lt;p&gt;Uma recorrência linear é uma sequência em que cada termo é uma combinação linear dos anteriores, e Fibonacci é o exemplo famoso. Qualquer passo assim pode ser escrito como uma matriz T vezes um vetor com os termos recentes. Aplicar T uma vez anda a sequência um passo. Aplicar duas, T², anda dois. E T^(n−1), aplicada a f(0) e f(1), que você já conhece, chega em f(n).&lt;/p&gt;
&lt;p&gt;Então a pergunta vira: quão rápido dá para elevar uma matriz a uma potência? Você não precisa de n multiplicações para chegar a xⁿ. De x sai x², de x² sai x⁴, de x⁴ sai x⁸. Qualquer expoente é uma soma dessas potências de dois, lida direto dos seus dígitos binários. Elevar ao quadrado transforma n passos em uns log n.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (Recurrence): use a versão web em https://mrcll.me/pt/stories/linear-recurrences-log-time/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;O loop precisa de 10¹⁸ passos; a matriz precisa de 84 multiplicações. Quando algo cresce um passo de cada vez, procure a forma que dobra. Quase sempre existe uma.&lt;/p&gt;</content:encoded><category>ICPC</category><category>Fundamentos</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Quantas trocas até ficar ordenado?</title><link>https://mrcll.me/pt/stories/counting-inversions/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/counting-inversions/</guid><description>Contando inversões com Bubblesort e Mergesort, e por que a mesma contagem compara dois rankings.</description><pubDate>Sun, 01 Nov 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Contar inversões é um problema famoso com muitas soluções conhecidas. O Bubblesort conta trocando; o Mergesort conta sem olhar cada par.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Pegue [1,4,3,2] e rode uma passada de Bubblesort. Compare 1 e 4: deixa. Compare 4 e 3: troca. Compare 4 e 2: troca. Sobra [1,3,2,4], e mais uma troca termina o serviço. Três trocas no total.&lt;/p&gt;
&lt;p&gt;Não é coincidência. Cada troca de vizinhos desfaz exatamente uma inversão, um par fora de ordem, então o número de trocas do Bubblesort é o número de inversões. Um contador ao lado da troca é o algoritmo inteiro.&lt;/p&gt;
&lt;p&gt;Também é O(n²), o que serve para quatro números e não serve para um milhão. O Mergesort ordena em O(n log n) e consegue contar enquanto ordena. Quando o merge pega um elemento da metade direita antes do que sobrou da metade esquerda, esse elemento pula todos eles, e cada pulo é uma inversão. Some tudo e você nunca olha um par diretamente.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (Inversions): use a versão web em https://mrcll.me/pt/stories/counting-inversions/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;A contagem é mais do que truque de maratona. É uma distância entre duas ordenações, o número de pares em que duas listas discordam. Quando um reranker novo vai para produção e você quer saber o quanto ele se afastou do ranking antigo, o número é esse, e agora dá para calcular em O(n log n).&lt;/p&gt;</content:encoded><category>ICPC</category><category>Fundamentos</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Contar sem enumerar</title><link>https://mrcll.me/pt/stories/digit-dp/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/digit-dp/</guid><description>Programação dinâmica de dígitos e a flag tight: contando números com uma propriedade nos dígitos sem enumerá-los.</description><pubDate>Sat, 31 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Quantos números entre A e B têm alguma propriedade nos dígitos? A DP de dígitos responde sem olhar um por um.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Quantos números até 1.000 não têm o dígito 4 e nunca contêm “13”? Dá para conferir os mil. A resposta é 712, e você chega nela visitando 9 estados.&lt;/p&gt;
&lt;p&gt;Primeiro, um intervalo são dois prefixos: a contagem de [A, B] é a de [0, B] menos a de [0, A−1]. Então você só precisa contar de zero até algum N.&lt;/p&gt;
&lt;p&gt;Dentro de [0, N], monte o número a partir do dígito mais significativo e carregue uma flag, tight, que diz se você ainda está colado no limite. Se N é 723 e você coloca 7 primeiro, o próximo dígito só vai até 2. Coloque 6 primeiro e você está livre: daí em diante vale qualquer coisa de 0 a 9.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (DigitDP): use a versão web em https://mrcll.me/pt/stories/digit-dp/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Por que a memorização compensa? Pense num número de 18 dígitos que termina em 0123456789. Todo prefixo possível na frente desse final leva à mesma pergunta sobre os mesmos dígitos restantes, 10⁸ cálculos idênticos se você refizer toda vez. Guarde uma vez e consulte.&lt;/p&gt;
&lt;p&gt;O estado continua pequeno: a posição, o que a pergunta precisa lembrar (aqui, se o último dígito foi 1) e o tight. A quantidade de estados depende de quantos dígitos N tem, não do tamanho de N. Antes de enumerar qualquer coisa, pergunte qual é o menor estado que resume tudo o que veio antes.&lt;/p&gt;</content:encoded><category>ICPC</category><category>Fundamentos</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Corte em √n pedaços</title><link>https://mrcll.me/pt/stories/root-decomposition/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/root-decomposition/</guid><description>Decomposição em raiz quadrada para consultas em intervalos, da minha primeira palestra, no primeiro Code Camp IFPB/ACM.</description><pubDate>Fri, 30 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;A primeira palestra que dei, no primeiro Code Camp IFPB/ACM, foi sobre isso.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;A primeira palestra que dei foi no primeiro Code Camp IFPB/ACM, sobre uma técnica que cabe numa frase: divida o vetor em blocos de tamanho √n e guarde a soma de cada bloco.&lt;/p&gt;
&lt;p&gt;É isso. Uma consulta de soma num intervalo, no loop simples, custa até n passos. Com blocos, custa as pontas soltas, no máximo 2√n elementos nas bordas, mais os blocos inteiros do meio, no máximo √n somas que você já tem. Atualizar um valor é corrigir um elemento e a soma de um bloco.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (SqrtDecomp): use a versão web em https://mrcll.me/pt/stories/root-decomposition/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Para 800.000 elementos, é a diferença entre 800.000 passos e 2.798. Por que √n e não outro tamanho? Blocos maiores dão menos blocos e pontas mais longas; blocos menores, o contrário. √n é onde os dois custos se encontram.&lt;/p&gt;
&lt;p&gt;Não é a estrutura mais rápida que existe; uma segment tree faz melhor. Mas a decomposição em raiz é a que você escreve certo em dez minutos, numa maratona, sob pressão, e isso conta muito. A estrutura boa o bastante que você entende por inteiro ganha da ótima que você copia.&lt;/p&gt;</content:encoded><category>ICPC</category><category>Fundamentos</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item><item><title>Brasileirão 2023, o maior número de títulos em campo</title><link>https://mrcll.me/pt/stories/brasileirao-2023-titles/</link><guid isPermaLink="true">https://mrcll.me/pt/stories/brasileirao-2023-titles/</guid><description>O Brasileirão 2023 começou com 65 títulos nacionais em campo, o maior número da era dos pontos corridos, desde 2003.</description><pubDate>Thu, 29 Oct 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;em&gt;Desde o início dos pontos corridos, em 2003, a Série A nunca tinha começado com tanta tradição.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Some os títulos nacionais de todos os clubes de uma Série A e você tem uma medida grosseira do peso das camisas. O Brasileirão 2023 começou com 65, o maior número desde o início dos pontos corridos, em 2003. No ano anterior eram 52. O recorde anterior era 60.&lt;/p&gt;
&lt;blockquote&gt;&lt;p&gt;Figura interativa (TitlesChart): use a versão web em https://mrcll.me/pt/stories/brasileirao-2023-titles/&lt;/p&gt;&lt;/blockquote&gt;
&lt;p&gt;Comparar temporadas pede uma nota de rodapé. Em 2003 e 2004 o campeonato tinha 24 times, em 2005 tinha 22, e só a partir de 2006 ficou em 20. Mais times em campo, mais títulos possíveis. Dois campeões também ficaram de fora da conta: Sport (1987) e Guarani (1978) estavam na Série B.&lt;/p&gt;
&lt;p&gt;Na outra ponta, cinco clubes podiam levantar a taça pela primeira vez: América-MG, Bragantino, Cuiabá, Fortaleza e Goiás.&lt;/p&gt;
&lt;p&gt;Camisas mais pesadas fazem uma disputa mais equilibrada? Não necessariamente. Uma das temporadas perto do topo deste gráfico terminou com desvio padrão de 16,26 pontos na tabela final, bem longe do equilíbrio de costume. Tradição enche o campo. Não decide a tabela.&lt;/p&gt;</content:encoded><category>Off the code</category><author>oliv.marq.marcello@gmail.com (Marcello Oliveira)</author></item></channel></rss>