
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>Vaz · Engenharia de informação</title>
    <link>https://vazdeng.pages.dev/</link>
    <description>Engenharia de dados de produção. Agente de IA para cripto. Mestrado traduzido para a prática.</description>
    <generator>Hugo -- gohugo.io</generator>
    <language>pt-BR</language>
    <lastBuildDate>Thu, 11 Jun 2026 00:00:00 +0000</lastBuildDate>
    
	  <atom:link href="https://vazdeng.pages.dev/index.xml" rel="self" type="application/rss+xml" />
    
    
      
      
    
    
    <item>
      <title>Prompt caching: o ajuste de 1 linha que corta 90% do custo de LLM em produção</title>
      <link>https://vazdeng.pages.dev/2026/06/11/prompt-caching-corte-90-custo-llm/</link>
      <pubDate>Thu, 11 Jun 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/06/11/prompt-caching-corte-90-custo-llm/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/06/11/prompt-caching-corte-90-custo-llm/cover_hu_236ef434b3576c83.png" width="640" height="336"/>]]>
        
        &lt;p&gt;18 mil tokens. Era o custo de cada execução do meu pipeline de notícias com 6 sub-agents paralelos. Depois de uma linha de código, virou 4 mil e quinhentos. Sem mudar o modelo. Sem mudar o prompt. Sem mudar o output. Só liguei o cache.&lt;/p&gt;
&lt;p&gt;A feature existe na API Anthropic há mais de um ano. A maioria dos times que usa LLM em produção ainda não ligou. Eu mesma rodei meses pagando preço cheio antes de olhar a fatura com atenção. É o ajuste com melhor retorno por minuto de trabalho que conheço hoje.&lt;/p&gt;
&lt;h2&gt;Por que o custo de LLM em produção é prefixo&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;por-que-o-custo-de-llm-em-produção-é-prefixo&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#por-que-o-custo-de-llm-em-produ%c3%a7%c3%a3o-%c3%a9-prefixo&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Toda chamada pra API manda 4 coisas: system prompt, few-shots, contexto e pergunta. Em pipeline de verdade, os 3 primeiros somam 80 a 95 por cento dos tokens, e repetem a cada chamada. A pergunta muda. O resto é prefixo.&lt;/p&gt;
&lt;p&gt;Sem cache, você paga pelo prefixo inteiro toda vez. Em pipeline que roda dezenas ou centenas de vezes por hora, isso vira a conta. Em pipeline com fan-out paralelo (vários sub-agents, mesmo system prompt), vira a conta vezes o número de sub-agents.&lt;/p&gt;
&lt;p&gt;Com cache, você paga o prefixo uma vez (cache write), e depois só o delta da nova chamada (cache read). Cache read custa cerca de 10% do preço de input normal.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/11/prompt-caching-corte-90-custo-llm/images/01-custo-prefixo.png&#34; alt=&#34;Anatomia de uma chamada: system prompt, few-shots e contexto são 80-95% dos tokens e repetem; só a pergunta muda&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;Como funciona o cache na Anthropic&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;como-funciona-o-cache-na-anthropic&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#como-funciona-o-cache-na-anthropic&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Você marca um bloco do prompt com &lt;code&gt;cache_control: ephemeral&lt;/code&gt;. Exemplo simplificado:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;system&amp;#34;&lt;/span&gt;&lt;span class=&#34;err&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;&amp;lt;system prompt longo e estável aqui&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;nt&#34;&gt;&amp;#34;cache_control&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;ephemeral&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;  &lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;TTL padrão é 5 minutos. Próxima chamada dentro desse intervalo: o prefixo cacheado é lido a 10% do preço normal. A Anthropic também oferece TTL de 1 hora como opção paga, útil pra workflows mais espaçados.&lt;/p&gt;
&lt;p&gt;A API retorna 2 métricas que você precisa monitorar:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;cache_creation_input_tokens&lt;/code&gt;: você pagou o write.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;cache_read_input_tokens&lt;/code&gt;: você pagou só o read (90% de desconto).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Sem mexer no modelo, sem reescrever prompt. Só sinalizar o que é cacheável.&lt;/p&gt;
&lt;h2&gt;Bench real do pipeline noticias-diarias&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;bench-real-do-pipeline-noticias-diarias&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#bench-real-do-pipeline-noticias-diarias&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O número da abertura vem de um pipeline que eu construí e mantenho: minha skill de notícias diárias, que roda todo dia às 8h BRT. Dispara 6 sub-agents paralelos via tool Agent: data-eng, IA, invest, cripto, política BR, política internacional. Cada um carrega um system prompt fixo de aproximadamente 3 mil tokens com regras de tom, formato Telegram, fontes priorizadas e estilo de síntese.&lt;/p&gt;
&lt;p&gt;Sem cache, a conta que eu pagava era direta:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;6 sub-agents × 3 mil tokens de prefixo = 18 mil tokens pagos por execução.&lt;/li&gt;
&lt;li&gt;Multiplicado por 1 execução por dia = 540 mil tokens por mês só de prefixo.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Com cache:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;1 cache write inicial (3 mil tokens) + 5 cache reads (com delta de ~300 tokens cada) = ~4 mil e 500 tokens efetivos.&lt;/li&gt;
&lt;li&gt;Aproximadamente 75% de corte no custo de prefixo, sem perder qualidade nem mudar uma vírgula do output.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Em pipeline de produção mais agressivo (que roda dezenas de vezes por hora, com prefixos maiores), o corte chega a 90%.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/11/prompt-caching-corte-90-custo-llm/images/02-bench-real.png&#34; alt=&#34;Bench real: 18 mil tokens por execução sem cache vs 4,5 mil com cache, corte de 75%&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;Onde brilha, onde não brilha&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;onde-brilha-onde-não-brilha&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#onde-brilha-onde-n%c3%a3o-brilha&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Brilha:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;System prompt fixo e grande (regras, format spec, exemplos).&lt;/li&gt;
&lt;li&gt;Fan-out: vários sub-agents com mesmo prefixo na mesma sessão.&lt;/li&gt;
&lt;li&gt;Agentes em loop iterando sobre mesmo contexto.&lt;/li&gt;
&lt;li&gt;Chat com documento grande anexado, com várias perguntas seguidas.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Não brilha:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Chamada one-shot sem padrão repetido.&lt;/li&gt;
&lt;li&gt;Prompt que muda significativamente a cada chamada.&lt;/li&gt;
&lt;li&gt;Workflow com cadência maior que 5 minutos entre calls (cache expirou).&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/11/prompt-caching-corte-90-custo-llm/images/03-brilha-nao-brilha.png&#34; alt=&#34;Onde o cache brilha: prefixo fixo, fan-out, loop, documento grande. Onde não: one-shot, prompt instável, cadência acima do TTL&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cuidados que matam o ganho se você não conhece:&lt;/strong&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Cache write é mais lento que call normal. Você paga uma vez em latência, ganha em todas as seguintes. Em pipeline noturno isso não importa. Em chat interativo, importa.&lt;/li&gt;
&lt;li&gt;Não cachear PII ou dado sensível sem auditar. Cache é per-account na Anthropic, mas o princípio vale.&lt;/li&gt;
&lt;li&gt;TTL 5 min é janela curta. Se sua skill roda o pipeline a cada 10 minutos, o cache nunca pega. Pra esses casos, use o TTL de 1 hora.&lt;/li&gt;
&lt;li&gt;Você só vê o ganho se monitora as 2 métricas. Um timestamp no começo do system prompt basta pra o prefixo nunca cachear, e sem olhar &lt;code&gt;cache_read&lt;/code&gt; você acha que ligou e não ligou.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Não é micro-otimização. É arquitetura.&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;não-é-micro-otimização-é-arquitetura&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#n%c3%a3o-%c3%a9-micro-otimiza%c3%a7%c3%a3o-%c3%a9-arquitetura&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Quem está pagando 100% do preço de cada chamada porque &amp;ldquo;não teve tempo de configurar&amp;rdquo; está acumulando dívida com a Anthropic todo mês. Em pipeline de produção com volume sério, isso vira milhares de reais por ano. Por uma linha de código.&lt;/p&gt;
&lt;p&gt;A regra que eu sigo em tudo que construo agora: estruture o prompt em camadas. Estável primeiro (cacheável), volátil depois. Marque o estável com &lt;code&gt;cache_control: ephemeral&lt;/code&gt;. Monitore &lt;code&gt;cache_creation&lt;/code&gt; e &lt;code&gt;cache_read&lt;/code&gt;. Pague uma vez, leia muitas.&lt;/p&gt;
&lt;p&gt;É o ABC. E ainda tem time chamando isso de &amp;ldquo;otimização avançada&amp;rdquo;.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Próximo post sábado 10h:&lt;/strong&gt; Zero to Expert Ep 02 sobre dependências em DAG, a lei que todo orquestrador segue por baixo do nome. Sem Airflow no centro.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Assina o VazDEng&lt;/strong&gt; se ainda não assina: &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;vazdeng.substack.com&lt;/a&gt;.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>SQL ainda é a linguagem mais importante de DE em 2026</title>
      <link>https://vazdeng.pages.dev/2026/06/10/sql-ainda-importa-2026/</link>
      <pubDate>Wed, 10 Jun 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/06/10/sql-ainda-importa-2026/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/06/10/sql-ainda-importa-2026/cover_hu_9181cf658c307756.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Tem dev hoje fazendo onboarding em time sênior que nunca escreveu um &lt;code&gt;GROUP BY&lt;/code&gt; na vida. Aprendeu ORM antes de SQL. Acha que &lt;code&gt;df.groupby()&lt;/code&gt; resolve. Quando a query trava porque o plan de execução virou full scan em tabela de 80 milhões de linhas, copia o erro pro ChatGPT, cola a resposta, e quando trava de novo, copia de novo. Loop infinito.&lt;/p&gt;
&lt;p&gt;Esse dev é o que o Akita chama de codificador. E a IA está acelerando a extinção dele.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/10/sql-ainda-importa-2026/coder-loop.png&#34; alt=&#34;O loop do codificador: query trava, copia o erro, cola a resposta, trava de novo&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;O codificador terceirizou o entendimento&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-codificador-terceirizou-o-entendimento&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-codificador-terceirizou-o-entendimento&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Eu aprendi SQL antes de qualquer framework, porque era o único jeito de falar com o banco. Hoje é o contrário. Framework antes de SQL. ORM antes de SQL. pandas antes de SQL. Camadas e mais camadas de abstração que escondem a query que de fato vai rodar.&lt;/p&gt;
&lt;p&gt;O problema da abstração não é a abstração. É que ela esconde o custo. Você acha que &lt;code&gt;User.objects.filter().select_related().prefetch_related()&lt;/code&gt; é cheap. Não é. É um JOIN que pode estourar memória se você não souber por que está rodando JOIN, em quantas tabelas, com qual cardinalidade. O ORM escreve a query certa em 70% dos casos. Os 30% restantes destroem teu cluster.&lt;/p&gt;
&lt;h2&gt;Em pipeline real, abstração não cabe&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;em-pipeline-real-abstração-não-cabe&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#em-pipeline-real-abstra%c3%a7%c3%a3o-n%c3%a3o-cabe&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Pipeline de DE moderno processa bilhões de linhas por dia. Toda decisão de query custa minutos vezes cluster vezes DBU vezes dia vezes mês. A diferença entre uma query bem escrita e uma gerada por ORM despreparado é fator 10 a 100x no custo final.&lt;/p&gt;
&lt;p&gt;Caso concreto que apareceu numa consultoria: pipeline de fechamento contábil em fintech brasileira. ORM gerando 47 subqueries pra coisa que SQL nativo resolve em 1 CTE com WINDOW. Custo Databricks/Snowflake: R$ 8 mil/mês. Depois que alguém finalmente escreveu a query em SQL puro, R$ 800/mês. Mesmo resultado de negócio, fator 10x de diferença.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/10/sql-ainda-importa-2026/abstraction-cost.png&#34; alt=&#34;Caso real de fechamento contábil: ORM despreparado a R$ 8 mil/mês vs SQL puro a R$ 800/mês, 10x mais barato&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Não foi um caso isolado. É o padrão. Onde tem pipeline grande gerado por abstração, tem fator 10x de gordura esperando alguém ler o plan de execução.&lt;/p&gt;
&lt;h2&gt;A IA gera SQL ruim em escala&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-ia-gera-sql-ruim-em-escala&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-ia-gera-sql-ruim-em-escala&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Toda IA generativa hoje gera SQL fluente. Compila, roda, retorna o número certo na primeira tentativa. O problema não é correção, é eficiência.&lt;/p&gt;
&lt;p&gt;Padrões observados em SQL gerado por LLM sem revisão:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;SELECT *&lt;/code&gt; em CTE empilhada, arrastando colunas que ninguém vai usar pelo pipeline inteiro.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WHERE coluna IN (SELECT ... )&lt;/code&gt; em vez de JOIN, em casos onde o JOIN seria 100x mais rápido.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;WHERE UPPER(coluna) = &#39;X&#39;&lt;/code&gt; em coluna indexada, derrubando o índice.&lt;/li&gt;
&lt;li&gt;Sem hint de partition em Spark/Snowflake, lendo tabela inteira quando só precisa de 1 dia.&lt;/li&gt;
&lt;li&gt;Window function sem &lt;code&gt;PARTITION BY&lt;/code&gt; correto, computando coisa errada sem dar erro.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/10/sql-ainda-importa-2026/llm-antipatterns.png&#34; alt=&#34;Antipadrões de SQL gerado por LLM sem revisão: SELECT * em CTE, IN no lugar de JOIN, função em coluna indexada, sem hint de partition, WINDOW sem PARTITION BY&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Desses cinco padrões, não tem um que eu não tenha visto em query gerada. Quem não lê plan de execução não vê. Vai pra produção, paga os juros no fim do mês. Dívida técnica com IA não é a mesma dívida de 5 anos atrás. Você contrai 10x mais rápido, achando que está levando vantagem.&lt;/p&gt;
&lt;h2&gt;O plan de execução é onde mora a diferença&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-plan-de-execução-é-onde-mora-a-diferença&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-plan-de-execu%c3%a7%c3%a3o-%c3%a9-onde-mora-a-diferen%c3%a7a&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;code&gt;EXPLAIN ANALYZE&lt;/code&gt; no Postgres. &lt;code&gt;EXPLAIN COST&lt;/code&gt; no Snowflake. Plano físico no Spark UI. É a primeira coisa que eu olho antes de deixar query nova rodar em escala. Todos te dizem a mesma coisa: quantos rows o engine vai escanear, quais joins escolheu, onde tem shuffle, onde tem broadcast, onde tem fila de espera.&lt;/p&gt;
&lt;p&gt;Codificador olha pro plan e não entende. Engenheiro lê e sabe se vale rodar em produção ou se precisa reescrever. Não é decoreba. É leitura de causa pra custo.&lt;/p&gt;
&lt;p&gt;Quando você pede pra LLM gerar SQL, peça também o plan estimado, peça pra comparar com versão alternativa, peça pra discutir trade-off de partition vs broadcast. Se você não sabe avaliar a resposta, você não está fazendo engenharia ainda. Está terceirizando decisão.&lt;/p&gt;
&lt;h2&gt;A decisão é antes da próxima feature&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-decisão-é-antes-da-próxima-feature&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-decis%c3%a3o-%c3%a9-antes-da-pr%c3%b3xima-feature&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;SQL não morreu. Quem morreu foi quem fingiu que sabia.&lt;/p&gt;
&lt;p&gt;A IA é darwinismo profissional. Quem aprende SQL de verdade fica 10x mais produtivo com ela, porque sabe avaliar o que ela gera. Quem terceiriza ORM mais IA acumula dívida que vai quebrar produção em 18 meses, e nesse dia não vai ter ninguém pra debugar porque ninguém mais lê plan de execução.&lt;/p&gt;
&lt;p&gt;A escolha é antes da próxima feature. Vai aprender o que está rodando ou vai apostar que a IA cobre teu vão? A aposta é ruim.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;strong&gt;Próximo post na quinta:&lt;/strong&gt; prompt caching cortando 90% do custo de LLM em produção. Bench real, configuração em uma linha, onde brilha e onde não brilha.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Assina o VazDEng&lt;/strong&gt; se ainda não assina. Engenharia de dados em português, padrão sênior, sem hype: &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;vazdeng.substack.com&lt;/a&gt;.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>YouTube bloqueia o endpoint de legendas. O de áudio continua aberto.</title>
      <link>https://vazdeng.pages.dev/2026/06/04/youtube-timedtext-bloqueia-googlevideo-nao/</link>
      <pubDate>Thu, 04 Jun 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/06/04/youtube-timedtext-bloqueia-googlevideo-nao/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/06/04/youtube-timedtext-bloqueia-googlevideo-nao/cover_hu_7177a10538791195.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Bati em HTTP 429 do YouTube em 14 vídeos seguidos. Eu tentei &lt;code&gt;--sleep-subtitles 60&lt;/code&gt;, backoff exponencial até 45s, cookies do Chrome, yt-dlp pré-release. Nada destravou. Todos os pedidos pro &lt;code&gt;timedtext&lt;/code&gt; voltavam 429.&lt;/p&gt;
&lt;p&gt;Mudei pro endpoint de áudio. Zero 429.&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;Em uma frase: o &lt;code&gt;timedtext&lt;/code&gt; (legendas) e o &lt;code&gt;googlevideo&lt;/code&gt; (áudio/vídeo) do YouTube são endpoints diferentes. Só o primeiro está agressivamente rate-limited em 2026. Baixar áudio e transcrever localmente sai mais barato do que insistir nas legendas.&lt;/p&gt;

&lt;/blockquote&gt;
&lt;h2&gt;O problema que pipelines de transcrição ignoram&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-problema-que-pipelines-de-transcrição-ignoram&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-problema-que-pipelines-de-transcri%c3%a7%c3%a3o-ignoram&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O rate limit do &lt;code&gt;timedtext&lt;/code&gt; virou comum o suficiente em 2026 pra ter 3 issues abertas no yt-dlp (#7123, #13770, #13831), sem fix definitivo. O conselho oficial é caching e usar a YouTube Data API com OAuth. Os dois funcionam mas mudam o problema, não resolvem. Quem rodou 50 URLs num cron e viu metade vazia conhece o sintoma.&lt;/p&gt;
&lt;h2&gt;Por que &lt;code&gt;googlevideo&lt;/code&gt; não cai junto&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;por-que-googlevideo-não-cai-junto&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#por-que-googlevideo-n%c3%a3o-cai-junto&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A descoberta que demorei pra fazer está nas duas camadas distintas que o YouTube expõe. O &lt;code&gt;timedtext&lt;/code&gt; é uma camada de API: serve XML/VTT pequenos sob quota global por IP e por dia, com cache pesado e bot detection endurecida em 2025. Cada request conta. Já o &lt;code&gt;googlevideo&lt;/code&gt; é a CDN de vídeo e áudio, que responde via segments DASH a partir de edges do Google Global Cache, com peering direto pro seu ISP. A camada de billing é por banda agregada no servidor que serve seu ISP, não por request individual. O rate limit lá só dispara em padrão claramente robótico.&lt;/p&gt;
&lt;p&gt;Na prática que eu vi: 60 requests em 5 minutos no &lt;code&gt;timedtext&lt;/code&gt; resulta em 429 garantido. Os mesmos 60 downloads no &lt;code&gt;googlevideo&lt;/code&gt; com intervalo natural passam sem aviso. Esse detalhe não está documentado em lugar óbvio. Eu descobri quando o cron quebrou e abri o Wireshark.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/04/youtube-timedtext-bloqueia-googlevideo-nao/images/01-dois-endpoints.png&#34; alt=&#34;Dois endpoints, só um bloqueia: timedtext é API com quota por IP e 429 garantido; googlevideo é CDN cobrada por banda, zero 429&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;A pipeline que aguenta batch real&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-pipeline-que-aguenta-batch-real&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-pipeline-que-aguenta-batch-real&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Empacotei a lógica num CLI Python open source chamado &lt;a href=&#34;https://github.com/thaiscvaz/yt-nota&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;yt-nota&lt;/a&gt;. Junta 3 ferramentas.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Etapa&lt;/th&gt;
          &lt;th&gt;Ferramenta&lt;/th&gt;
          &lt;th&gt;Custo&lt;/th&gt;
          &lt;th&gt;Quando falha&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Metadata + URL da legenda&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;yt-dlp&lt;/code&gt; (Python API)&lt;/td&gt;
          &lt;td&gt;$0&lt;/td&gt;
          &lt;td&gt;Vídeo privado, region lock&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Áudio fallback&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;yt-dlp&lt;/code&gt; formato 139 (m4a 49kbps)&lt;/td&gt;
          &lt;td&gt;$0&lt;/td&gt;
          &lt;td&gt;Members-only sem cookie&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Transcrição local&lt;/td&gt;
          &lt;td&gt;&lt;code&gt;faster-whisper&lt;/code&gt; int8 CPU&lt;/td&gt;
          &lt;td&gt;$0&lt;/td&gt;
          &lt;td&gt;Vídeo &amp;gt; 1h em hardware fraco&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;code&gt;faster-whisper&lt;/code&gt; é 4x mais rápido que &lt;code&gt;openai-whisper&lt;/code&gt; no mesmo modelo, com a mesma acurácia (mesmos pesos). A API do meu CLI fica assim:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;result&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;extract_transcript&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;url&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;whisper_fallback&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# default ligado&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;whisper_model&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;small&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;# ou tiny/base/medium&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;No 429, ele desce pro &lt;code&gt;googlevideo&lt;/code&gt;, baixa só o áudio, transcreve e devolve o mesmo formato. Quem chama nem sabe se veio do &lt;code&gt;timedtext&lt;/code&gt; ou do Whisper.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/04/youtube-timedtext-bloqueia-googlevideo-nao/images/02-fallback-audio.png&#34; alt=&#34;Fallback em 3 etapas: legenda via timedtext, áudio via googlevideo no 429, transcrição local com faster-whisper, mesmo formato de saída&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;Benchmark em CPU (Intel i7 12ª gen, 16 GB, int8)&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;benchmark-em-cpu-intel-i7-12ª-gen-16-gb-int8&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#benchmark-em-cpu-intel-i7-12%c2%aa-gen-16-gb-int8&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Eu rodei o pipeline em vídeos reais de duração variada pra medir tempo de processo. Sem GPU.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Duração do vídeo&lt;/th&gt;
          &lt;th&gt;&lt;code&gt;base&lt;/code&gt; (74 MB)&lt;/th&gt;
          &lt;th&gt;&lt;code&gt;small&lt;/code&gt; (244 MB)&lt;/th&gt;
          &lt;th&gt;&lt;code&gt;medium&lt;/code&gt; (769 MB)&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;5 min&lt;/td&gt;
          &lt;td&gt;35 s&lt;/td&gt;
          &lt;td&gt;1 min 30 s&lt;/td&gt;
          &lt;td&gt;5 min&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;13 min&lt;/td&gt;
          &lt;td&gt;1 min 50 s&lt;/td&gt;
          &lt;td&gt;4 min&lt;/td&gt;
          &lt;td&gt;13 min&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;45 min&lt;/td&gt;
          &lt;td&gt;6 min&lt;/td&gt;
          &lt;td&gt;14 min&lt;/td&gt;
          &lt;td&gt;45 min&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Sobre acurácia em português técnico, fiz leitura comparativa em ~14 horas de áudio de aulas. O modelo &lt;code&gt;base&lt;/code&gt; confunde 1 em cada 6 termos técnicos (95% legível mas pede revisão humana). O &lt;code&gt;small&lt;/code&gt; confunde 1 em cada 20 (default por uma razão: o LLM downstream corrige os erros raros pelo contexto). O &lt;code&gt;medium&lt;/code&gt; chega quase em erro zero, mas dobra o tempo. Pro meu fluxo (transcript → síntese via Claude Code), &lt;code&gt;small&lt;/code&gt; é o sweet spot.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/04/youtube-timedtext-bloqueia-googlevideo-nao/images/03-sweet-spot-small.png&#34; alt=&#34;Qual Whisper em CPU: base erra 1 em 6 termos, small erra 1 em 20 e é o sweet spot, medium quase zero mas processa em tempo real&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;E os SaaS já existem com Whisper fallback?&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;e-os-saas-já-existem-com-whisper-fallback&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#e-os-saas-j%c3%a1-existem-com-whisper-fallback&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Existem. Dois principais em 2026.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Solução&lt;/th&gt;
          &lt;th&gt;Preço&lt;/th&gt;
          &lt;th&gt;Quando faz sentido&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Supadata&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;A partir de $0,001/min, free tier 1000 req/mês&lt;/td&gt;
          &lt;td&gt;Empresa com SLA, não quer manter infra&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Apify YouTube Transcript Scraper&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;$0,40 por 1000 actor runs + compute&lt;/td&gt;
          &lt;td&gt;Pipeline já no Apify&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;yt-nota self-host&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;250 MB deps + 244 MB modelo&lt;/td&gt;
          &lt;td&gt;Privacidade, batch acadêmico, controle&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;A decisão pra mim é trivial: nota de aprendizado e vault Obsidian não atravessam API de terceiro. Se fosse pipeline corporativo com SLA e auditoria, Supadata ganha por operacional. Self-host só faz sentido quando você é o cliente do dado.&lt;/p&gt;
&lt;h2&gt;Verdict honesto&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;verdict-honesto&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#verdict-honesto&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O que funciona: batch de 50+ vídeos sem cair no meio, zero custo recorrente depois dos 500 MB iniciais, qualidade em português técnico boa o suficiente pra LLM digerir depois.&lt;/p&gt;
&lt;p&gt;O que cobra: primeira instalação é pesada (&lt;code&gt;pip install yt-nota[whisper]&lt;/code&gt;), modelo &lt;code&gt;small&lt;/code&gt; pode confundir termos exóticos (pra áudio crítico, sobe pra &lt;code&gt;medium&lt;/code&gt;), e CPU vira gargalo em vídeo maior que 1h.&lt;/p&gt;
&lt;p&gt;Quando NÃO vale: volume de 10.000 horas por mês com SLA apertado (a Whisper API da OpenAI a $0,006/min sai mais barato por hora-engenheiro do que manter infra), ou áudio com música e várias vozes simultâneas (faster-whisper não faz diarização, pyannote sim).&lt;/p&gt;
&lt;h2&gt;Anti-padrões que vi pelo caminho&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;anti-padrões-que-vi-pelo-caminho&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#anti-padr%c3%b5es-que-vi-pelo-caminho&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Confiar no &lt;code&gt;--sleep-subtitles 60&lt;/code&gt; como bala de prata. Eu testei: ele não dispara antes do request, ele dispara depois do primeiro 429. Já era. Pular pra API paga sem ter tentado o pipeline local também é armadilha. $36k/ano em transcrição (cálculo público do faster-whisper) é dinheiro que devia comprar uma GPU intermediária. E apagar o áudio bruto depois de transcrever é erro de quem nunca quis rerodar com modelo melhor 6 meses depois. Eu guardo.&lt;/p&gt;
&lt;h2&gt;O que isso muda pra você&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-isso-muda-pra-você&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-isso-muda-pra-voc%c3%aa&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Se você usa YouTube como fonte de aprendizado, entrada de RAG ou pipeline de notas:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; Sua pipeline atual aguenta 50 URLs em sequência sem cair?&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; Você sabe distinguir 429 de &lt;code&gt;timedtext&lt;/code&gt; versus 429 de &lt;code&gt;googlevideo&lt;/code&gt;?&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; Você tem fallback automático ou trata cada falha manual?&lt;/li&gt;
&lt;li&gt;&lt;input disabled=&#34;&#34; type=&#34;checkbox&#34;&gt; Custo mensal real da sua transcrição cabe ou já passou de 1 GPU amortizada?&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Se respondeu &amp;ldquo;não&amp;rdquo; pra mais de uma, vale uma tarde refatorando.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Code review do meu próprio repo. Cinco coisas que eu mudaria hoje.</title>
      <link>https://vazdeng.pages.dev/2026/06/02/code-review-meu-codigo-antigo/</link>
      <pubDate>Tue, 02 Jun 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/06/02/code-review-meu-codigo-antigo/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/06/02/code-review-meu-codigo-antigo/cover_hu_997d5e46c0010a94.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Abri um repositório meu de dois anos atrás. Continuava público no GitHub, eu citava ele no portfólio em entrevista, e eu nunca tinha relido o código depois de submeter. Esse fim de semana resolvi reler.&lt;/p&gt;
&lt;p&gt;Achei cinco anti-padrões. No meu próprio código, escrito por mim. Mas o tipo de problema que eu vejo aparecer em pipelines reais de empresa grande, não só em case de entrevista.&lt;/p&gt;
&lt;p&gt;Resolvi escrever sobre porque é mais honesto criticar o próprio código do que apontar dedo pra repo dos outros. E porque se você tem um repo público de dois anos atrás citado no seu portfólio, você provavelmente também tem pelo menos três desses cinco.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/02/code-review-meu-codigo-antigo/images/01-cinco-antipadroes.png&#34; alt=&#34;Os 5 anti-padrões do review: credencial na função, tasks em série sem motivo, dado dentro da imagem Docker, DAG diário sobre dado estático, fillna(0) apagando sinal&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;A credencial do banco estava dentro da função&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-credencial-do-banco-estava-dentro-da-função&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-credencial-do-banco-estava-dentro-da-fun%c3%a7%c3%a3o&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;load_data_to_snowflake&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;df_merged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;conn&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;snowflake&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connector&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;connect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;user&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;thaiscxxx&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;password&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;xxx*&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;n&#34;&gt;account&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;xxx&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Mascarei com &lt;code&gt;xxx&lt;/code&gt; antes de subir, mas o padrão de design é o problema, não a string. Credencial dentro da função significa que cada task que precisa do Snowflake duplica essa conexão, rotacionar a senha exige tocar em código, e auditoria precisa varrer o repo inteiro pra saber quem conecta no banco.&lt;/p&gt;
&lt;p&gt;A versão honesta usaria um Hook do Airflow (&lt;code&gt;SnowflakeHook&lt;/code&gt;) ou variável de ambiente, com a conexão gerenciada fora do código:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;from&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;airflow.providers.snowflake.hooks.snowflake&lt;/span&gt; &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SnowflakeHook&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;hook&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;SnowflakeHook&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;snowflake_conn_id&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;snowflake_default&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Conexão criptografada, rastreável, e nunca aparece em pull request.&lt;/p&gt;
&lt;h2&gt;O pipeline perdia paralelismo de graça&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-pipeline-perdia-paralelismo-de-graça&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-pipeline-perdia-paralelismo-de-gra%c3%a7a&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t2&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;code&gt;t1&lt;/code&gt; validava &lt;code&gt;students.json&lt;/code&gt;. &lt;code&gt;t2&lt;/code&gt; validava &lt;code&gt;missed_days.json&lt;/code&gt;. Eu encadeei os dois em sequência, mas eles são independentes. Não existe motivo pra &lt;code&gt;t2&lt;/code&gt; esperar &lt;code&gt;t1&lt;/code&gt; terminar. Em arquivo pequeno, dá quase no mesmo. Quando o JSON pesa gigabytes e validação leva minutos, paralelizar cai a duração pela metade.&lt;/p&gt;
&lt;p&gt;A versão correta seria:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;t1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t2&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t3&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;t4&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Quem lê o pipeline hoje entende que validação roda em paralelo e depois faz o join. Quem lia o original ia assumir que existia alguma dependência escondida que não existia.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/02/code-review-meu-codigo-antigo/images/02-serie-vs-paralelo.png&#34; alt=&#34;Como eu escrevi vs como deveria ser: t1 » t2 » t3 » t4 em série contra [t1, t2] » t3 » t4 com validações em paralelo&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;Os dados estavam dentro da imagem Docker&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;os-dados-estavam-dentro-da-imagem-docker&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#os-dados-estavam-dentro-da-imagem-docker&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;No Dockerfile:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;COPY files/students.json /students.json
COPY files/missed_days.json /missed_days.json&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Embuti o dado de entrada na imagem. Cada rebuild da imagem assume o mesmo dado. Pra rodar o pipeline com um JSON diferente, eu teria que rebuildar a imagem ou modificar o código. Acoplamento entre artefato de execução e dado de entrada, no mesmo lugar.&lt;/p&gt;
&lt;p&gt;A regra que eu cobrava de outros e ignorei no meu próprio repo: imagem é imutável, dado é mutável. Dado entra via volume montado, S3, GCS, ou parâmetro de execução. Nunca dentro da imagem.&lt;/p&gt;
&lt;h2&gt;O DAG rodava todo dia sobre dado estático&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-dag-rodava-todo-dia-sobre-dado-estático&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-dag-rodava-todo-dia-sobre-dado-est%c3%a1tico&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;with&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;DAG&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;migrate_student_data_to_snowflake&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;schedule_interval&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;timedelta&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;days&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;         &lt;span class=&#34;n&#34;&gt;catchup&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;dag&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Agendei o pipeline pra rodar todo dia. O dado de entrada é os dois JSONs estáticos copiados pra dentro da imagem (o anti-padrão acima). Rodar todo dia significa processar exatamente os mesmos arquivos, gerar exatamente os mesmos registros, e tentar inserir tudo de novo na mesma tabela. Na segunda execução, o &lt;code&gt;write_pandas&lt;/code&gt; duplicaria as linhas. Na terceira, duplicaria de novo.&lt;/p&gt;
&lt;p&gt;O dado é estático. A escolha correta seria &lt;code&gt;schedule_interval=None&lt;/code&gt; (dispara só manual ou por trigger) ou um sensor que detecta arquivo novo no bucket. Agendar pipeline sem fonte mutável é cerimônia: gasta worker slot todo dia, dispara alerta se quebrar, polui o histórico de execução. E quando você precisa rodar de verdade com dado novo, a operação fica indistinguível do ruído de fundo.&lt;/p&gt;
&lt;p&gt;Era pra rodar uma vez. Eu agendei pra rodar todo dia. Sutil, mas o tipo de coisa que cria DAG cerimonial em produção: pipeline que existe sem motivo de existir naquele intervalo.&lt;/p&gt;
&lt;h2&gt;O &lt;code&gt;fillna(0)&lt;/code&gt; apagou um sinal importante&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-fillna0-apagou-um-sinal-importante&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-fillna0-apagou-um-sinal-importante&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;df_merged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;missed_days&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;fillna&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;inplace&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Quando um aluno aparece em &lt;code&gt;students.json&lt;/code&gt; mas não em &lt;code&gt;missed_days.json&lt;/code&gt;, o join deixa &lt;code&gt;missed_days&lt;/code&gt; nulo. Substituí por zero. Parecia certo na hora.&lt;/p&gt;
&lt;p&gt;Zero falta tem significado de negócio: aluno foi todos os dias. Ausência de registro tem outro significado: a escola não passou o dado desse aluno. Misturar os dois mascara um problema de qualidade de dado upstream. O dashboard que filtra alunos com &amp;ldquo;zero faltas&amp;rdquo; vai contar como exemplares justamente os alunos cujo dado nunca chegou.&lt;/p&gt;
&lt;p&gt;A versão honesta deixaria nulo e abriria coluna nova marcando se houve registro:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;n&#34;&gt;df_merged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;missed_data_source&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;df_merged&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;missed_days&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;notna&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;()&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;map&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;kc&#34;&gt;True&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;reported&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;not_reported&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Pequena mudança, completamente diferente o que o dashboard mostra.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/06/02/code-review-meu-codigo-antigo/images/03-zero-nao-e-nulo.png&#34; alt=&#34;Zero não é nulo: 0 significa aluno presente todos os dias, NULL significa dado que não chegou, fillna(0) mistura os dois&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;O incômodo de revisar código próprio&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-incômodo-de-revisar-código-próprio&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-inc%c3%b4modo-de-revisar-c%c3%b3digo-pr%c3%b3prio&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Reescrever esses cinco trechos hoje levaria uma hora. O incômodo de admitir publicamente que estavam errados é maior do que a hora. Mas o repo continuou público com os defeitos, e eu cito esse repo no meu portfólio. Manter o repo intacto e fazer review honesto em cima é mais útil pra quem está aprendendo do que apagar a história e fingir que sempre escrevi código bom.&lt;/p&gt;
&lt;p&gt;Se você tem um repo público antigo que continua no seu portfólio, abre ele essa semana. Vai achar pelo menos três desses cinco.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Data Flows Ep01: o conceito que vem antes de qualquer ferramenta</title>
      <link>https://vazdeng.pages.dev/2026/05/30/data-flows-ep01/</link>
      <pubDate>Sat, 30 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/05/30/data-flows-ep01/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/05/30/data-flows-ep01/cover_hu_5dccf7c4c79d4e55.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Em 1 de agosto de 2012, a Knight Capital perdeu 440 milhões de dólares em 45 minutos.&lt;/p&gt;
&lt;p&gt;Não foi bug de algoritmo. Não foi crise de mercado. Foi um único servidor entre oito que recebeu o deploy do novo código, enquanto outro manteve uma flag antiga reativada (Power Peg, código de 2003). Os dois rodaram em paralelo. O resultado foi uma cascata de ordens automáticas que ninguém conseguiu parar.&lt;/p&gt;
&lt;p&gt;O SEC documentou o caso (Release No. 70694, outubro 2013): a causa raiz não era um erro de lógica de trading. Era inconsistência de estado entre servidores que deveriam estar sincronizados. Em linguagem de engenharia de dados, era um data flow quebrado.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/30/data-flows-ep01/images/01-knight-timeline.png&#34; alt=&#34;Timeline 1 de agosto de 2012: Knight Capital perde 440 milhões em 45 minutos por estado divergente entre servidores&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;A Knight Capital tinha algoritmos sofisticados. Tinha mais de uma década de operação. O que não tinha era um modelo mental claro sobre onde o dado nascia, por onde passava, e onde precisava chegar de forma consistente.&lt;/p&gt;
&lt;p&gt;Esse modelo mental é o que define o resto. Eu trabalho com dados há tempo suficiente pra ter visto, em escalas menores, variações dessa mesma falha. Antes de Apache Spark, antes de dbt, antes de Snowflake, antes de qualquer ferramenta, existe um conceito que separa pipeline robusto de pipeline frágil.&lt;/p&gt;
&lt;h2&gt;Em uma frase&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;em-uma-frase&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#em-uma-frase&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;blockquote&gt;
  &lt;p&gt;Data flow é o caminho que o dado percorre da fonte até o destino, com toda transformação no meio. Acertar esse caminho é decisão arquitetural. Errar custa caro.&lt;/p&gt;

&lt;/blockquote&gt;
&lt;h2&gt;De onde veio essa ideia&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;de-onde-veio-essa-ideia&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#de-onde-veio-essa-ideia&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Não é nova. Bill Inmon publicou &lt;em&gt;Building the Data Warehouse&lt;/em&gt; em 1992 defendendo arquitetura top-down, normalizada, enterprise-wide. Ralph Kimball respondeu em 1996 com &lt;em&gt;The Data Warehouse Toolkit&lt;/em&gt;: bottom-up, modelagem dimensional, data marts compondo o todo. O debate Inmon vs Kimball dominou os anos 90 e ainda aparece em qualquer revisão de arquitetura.&lt;/p&gt;
&lt;p&gt;O que mudou entre 1996 e 2026 não foi o conceito, foi a escala. Em 2017, Martin Kleppmann publicou &lt;em&gt;Designing Data-Intensive Applications&lt;/em&gt; e formalizou no capítulo 11 a distinção que organiza a engenharia de dados moderna:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&amp;ldquo;A stream refers to data that is incrementally made available over time&amp;hellip; in contrast to batch processing, where the input is a known, finite size.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;Bounded vs unbounded. Um conjunto de dados com tamanho conhecido (batch) versus um que nunca termina (stream). Toda decisão de arquitetura de dados começa nessa distinção.&lt;/p&gt;
&lt;p&gt;Em 2021, o paper do Lakehouse (Armbrust, Ghodsi, Xin, Zaharia, CIDR) propôs unificar warehouse e lake via metadata layer (Delta, Iceberg, Hudi). Em 2020, o pessoal da dbt Labs popularizou ELT no lugar de ETL: transformação dentro do warehouse, não antes. Cada onda mudou ferramenta, não princípio.&lt;/p&gt;
&lt;h2&gt;Bounded vs unbounded: a decisão que define tudo&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;bounded-vs-unbounded-a-decisão-que-define-tudo&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#bounded-vs-unbounded-a-decis%c3%a3o-que-define-tudo&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Toda decisão de pipeline começa aqui. Resumo prático em tabela:&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/30/data-flows-ep01/images/02-data-flow-overview.png&#34; alt=&#34;Diagrama do fluxo de dados: fonte, transformação, destino, com batch, micro-batch e streaming por SLA&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Tipo&lt;/th&gt;
          &lt;th&gt;Característica&lt;/th&gt;
          &lt;th&gt;Quando usar&lt;/th&gt;
          &lt;th&gt;Custo&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Batch&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;Dataset finito, processado em janela definida&lt;/td&gt;
          &lt;td&gt;SLA de horas, relatórios contábeis, snapshots históricos&lt;/td&gt;
          &lt;td&gt;Simples de construir, debugar, recuperar&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Streaming&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;Dataset infinito, evento processado quando chega&lt;/td&gt;
          &lt;td&gt;SLA de segundos a poucos minutos, fraude em tempo real, dashboards operacionais&lt;/td&gt;
          &lt;td&gt;Complexo, exige watermarks, exactly-once, observabilidade pesada&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Micro-batch&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;Streaming em janelas curtas (segundos a minutos)&lt;/td&gt;
          &lt;td&gt;Meio termo: dashboard de minutos, ML feature store próximo do real-time&lt;/td&gt;
          &lt;td&gt;Spark Structured Streaming, Flink mini-batches&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Tyler Akidau e equipe (Google) publicaram em VLDB 2015 o paper &lt;em&gt;The Dataflow Model&lt;/em&gt; que formalizou o vocabulário moderno: event time, processing time, watermarks, triggers, windowing. A frase central:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&amp;ldquo;A practical approach to balancing the inherent tension between correctness, latency, and cost in massive-scale, unbounded, out-of-order data.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;Tradução: streaming é correto em três variáveis ao mesmo tempo. Você não maximiza as três, escolhe duas e paga a terceira.&lt;/p&gt;
&lt;h2&gt;Quando batch, quando streaming&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-batch-quando-streaming&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-batch-quando-streaming&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A regra prática que eu uso é simples: SLA de latência aceitável define a resposta.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;SLA acima de 1h&lt;/strong&gt; tende a batch. Reprocessamento simples, debugging direto, infraestrutura barata.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SLA abaixo de 1 minuto&lt;/strong&gt; exige streaming. Quem tenta forçar batch nesse cenário cria janelas tão curtas que reinventa streaming com o pior dos dois mundos.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;SLA entre 1 minuto e 1h&lt;/strong&gt; é zona de micro-batch. Spark Structured Streaming ou Flink mini-batches resolvem.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Jay Kreps, fundador do Confluent, escreveu em 2014 o ensaio &lt;em&gt;Questioning the Lambda Architecture&lt;/em&gt; atacando o modelo proposto por Nathan Marz, que mantinha duas camadas paralelas (batch + speed). A frase que ficou:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&amp;ldquo;The problem with the Lambda Architecture is that maintaining code that needs to produce the same result in two complex distributed systems is exactly as painful as it seems.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;Kreps propôs Kappa: log unificado (Kafka) como fonte de verdade, reprocessamento via replay. Kappa virou padrão em quem opera streaming sério.&lt;/p&gt;
&lt;p&gt;O erro mais comum que eu vejo é forçar streaming porque &amp;ldquo;soa moderno&amp;rdquo;. Streaming não é versão melhor de batch. É contrato diferente, custo diferente, modelo mental diferente. Quando a decisão é tomada por moda em vez de por SLA, a equipe gasta meses construindo complexidade que o problema não pediu, e eu já passei por essa armadilha mais de uma vez.&lt;/p&gt;
&lt;h2&gt;O que dá errado quando ignoram o flow&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-dá-errado-quando-ignoram-o-flow&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-d%c3%a1-errado-quando-ignoram-o-flow&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Knight Capital não foi um acidente isolado. O padrão se repete em outras escalas.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;GitHub, outubro de 2018&lt;/strong&gt;: outage de 24 horas. Causa raiz documentada pelo Jason Warner (post-mortem oficial): 43 segundos de partição de rede entre data centers no US East causaram divergência no failover do MySQL Orchestrator, replication storm e inconsistência cross-DC. Foi falha pura de data flow na camada de replicação.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Airbnb, antes da Minerva&lt;/strong&gt;: equipes diferentes calculavam &amp;ldquo;active user&amp;rdquo; com queries divergentes no mesmo Spark cluster. Métricas batiam de cabeça em reuniões executivas. A solução não foi outro dashboard, foi uma camada única de definição de métricas com lineage explícito da fonte ao destino. O Minerva indexa hoje mais de 200 mil data assets.&lt;/p&gt;
&lt;p&gt;Esses casos cabem em padrões nomeados na literatura. Vale conhecer cada um:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Pipeline jungle&lt;/strong&gt; (Sculley et al, NeurIPS 2015, &lt;em&gt;Hidden Technical Debt in Machine Learning Systems&lt;/em&gt;): &lt;em&gt;&amp;ldquo;pipeline jungles often appear as data preparation evolves organically&amp;hellip; testing such pipelines requires expensive end-to-end integration tests.&amp;rdquo;&lt;/em&gt; É o que acontece quando ninguém desenhou o flow no começo e ele cresce por adição.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Data swamp&lt;/strong&gt; (Nick Heudecker, Gartner 2014): &lt;em&gt;&amp;ldquo;lakes turn into swamps when there is no metadata, governance, or quality control.&amp;rdquo;&lt;/em&gt; Lake virou pasta de arquivos jogados em qualquer lugar.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Schema drift&lt;/strong&gt;: campos mudam sem aviso entre runs, contratos downstream quebram silenciosamente.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Lineage gaps&lt;/strong&gt;: ninguém sabe de onde veio o dado que está no dashboard.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Reverse-ETL chaos&lt;/strong&gt;: dado volta do warehouse pra SaaS sem governança, vira fonte secreta de verdade que ninguém audita.&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Como os grandes documentam o próprio flow&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;como-os-grandes-documentam-o-próprio-flow&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#como-os-grandes-documentam-o-pr%c3%b3prio-flow&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Empresas que operam dado em produção real publicam a arquitetura. Vale ler.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Empresa&lt;/th&gt;
          &lt;th&gt;Documento&lt;/th&gt;
          &lt;th&gt;Anchor&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Netflix&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;em&gt;Maestro: Netflix&amp;rsquo;s Workflow Orchestrator&lt;/em&gt; (TechBlog, jul 2024)&lt;/td&gt;
          &lt;td&gt;Orquestra centenas de milhares de workflows por dia, padrão WAP (Write-Audit-Publish) sobre Iceberg&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Uber&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;em&gt;Uber&amp;rsquo;s Big Data Platform&lt;/em&gt; (Eng Blog, out 2018)&lt;/td&gt;
          &lt;td&gt;Hudi reduziu latência de ingestão de 24h para menos de 1h em 100+ PB&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Airbnb&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;em&gt;Democratizing Data at Airbnb&lt;/em&gt; (mai 2017)&lt;/td&gt;
          &lt;td&gt;Dataportal indexa 200K+ data assets com lineage explícito&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Stripe&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;em&gt;Online migrations at scale&lt;/em&gt; (Eng Blog, fev 2017)&lt;/td&gt;
          &lt;td&gt;Dual-write + backfill + reconciliation para migrar dados financeiros sem perda&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;&lt;strong&gt;Slack&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;&lt;em&gt;How We Built Slack&amp;rsquo;s Data Warehouse&lt;/em&gt; (set 2023)&lt;/td&gt;
          &lt;td&gt;Migração de Presto+Hive para Trino+Iceberg, 60K queries por dia&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Padrão comum: cada uma documentou o flow antes de construir a próxima ferramenta. Ferramenta nasceu a partir do diagrama, não o contrário.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/30/data-flows-ep01/images/03-quote-ferramenta.png&#34; alt=&#34;Citação: ferramenta nasceu a partir do diagrama, não o contrário&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;Anti-padrões pra evitar&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;anti-padrões-pra-evitar&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#anti-padr%c3%b5es-pra-evitar&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Forçar streaming porque soa moderno&lt;/strong&gt;. Se SLA é diário, batch resolve com 10% da complexidade.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Construir pipeline sem desenhar o flow primeiro&lt;/strong&gt;. Pipeline jungle é literalmente isso: crescer sem mapa.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Aceitar lake como &amp;ldquo;joga tudo aqui que organizo depois&amp;rdquo;&lt;/strong&gt;. Vira swamp em 6 meses.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Ignorar schema contracts&lt;/strong&gt;. Schema drift quebra downstream silenciosamente. Use Schema Registry ou contrato versionado em SQL.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Manter duas implementações paralelas (Lambda)&lt;/strong&gt;. Custo de manutenção dobra, comportamentos divergem, ninguém confia em nenhuma.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Pular lineage&lt;/strong&gt;. Lineage não é luxo. É a única forma de responder &amp;ldquo;de onde veio esse número&amp;rdquo; sem abrir 12 jobs.&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;Onde começar&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;onde-começar&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#onde-come%c3%a7ar&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Você consegue desenhar, em um guardanapo, o data flow do seu pipeline mais crítico? Fonte exata, transformações principais, destinos, SLA por etapa.&lt;/p&gt;
&lt;p&gt;Se sim, está à frente da maioria. Se não, comece por aí. Antes de Spark, antes de dbt, antes de qualquer ferramenta nova.&lt;/p&gt;
&lt;p&gt;Os próximos episódios da série Zero to Expert vão entrar em cada camada com profundidade: ingestão (formatos, idempotência, CDC), transformação (SQL vs Python vs Spark), destino (warehouse vs lake vs lakehouse), orquestração. Cada episódio com caso concreto e decisão no centro, não teoria.&lt;/p&gt;
&lt;p&gt;Se tem algum conceito específico que você quer ver coberto, me manda no &lt;a href=&#34;https://linkedin.com/in/thaisvaz&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;LinkedIn&lt;/a&gt; ou assina a &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;newsletter&lt;/a&gt; pra receber os próximos episódios.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>SLA, não moda: quando batch, quando streaming, quando ambos</title>
      <link>https://vazdeng.pages.dev/2026/05/30/sla-nao-moda-batch-streaming/</link>
      <pubDate>Sat, 30 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/05/30/sla-nao-moda-batch-streaming/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/05/30/sla-nao-moda-batch-streaming/cover_hu_ecfe8a72c8fe9d0c.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Vi um time de marketing fazer o que toda equipe faz uma vez: adotar streaming porque soava moderno. Kafka gerenciado, workers 24x7, exactly-once de garantia. Pra processar eventos que chegavam a cada 10 minutos. Batch noturno resolveria igual. Custava um décimo. Levou seis meses até alguém medir.&lt;/p&gt;
&lt;p&gt;O padrão se repete. Eu já passei pela mesma decisão em quatro domínios diferentes: pipelines de finanças, processos industriais, marketing, analytics. A discussão sempre começa errada. &amp;ldquo;Vamos pra streaming porque é mais moderno.&amp;rdquo; Ou &amp;ldquo;vamos manter batch porque é o que a gente sempre fez.&amp;rdquo; As duas perdem a pergunta certa.&lt;/p&gt;
&lt;p&gt;A pergunta certa é uma só: qual o SLA real do consumidor que vai usar esse dado?&lt;/p&gt;
&lt;h2&gt;A pergunta certa não é &amp;ldquo;qual é mais moderno&amp;rdquo;&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-pergunta-certa-não-é-qual-é-mais-moderno&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-pergunta-certa-n%c3%a3o-%c3%a9-qual-%c3%a9-mais-moderno&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Martin Kleppmann formaliza no capítulo 11 de &lt;em&gt;Designing Data-Intensive Applications&lt;/em&gt; a distinção que organiza qualquer arquitetura de dados em 2026. Dado &lt;strong&gt;bounded&lt;/strong&gt; (conjunto finito, tamanho conhecido) versus &lt;strong&gt;unbounded&lt;/strong&gt; (fluxo que nunca termina). Toda decisão começa aí.&lt;/p&gt;
&lt;p&gt;Mas a distinção bounded/unbounded é técnica, não comportamental. O dado real raramente é só uma coisa. Logs de aplicação são unbounded por natureza. Se eu agrego eles em batches de 1 hora pra alimentar um dashboard que ninguém olha mais de hora em hora, o consumidor está tratando como bounded. O dado é o que o consumo decide.&lt;/p&gt;
&lt;p&gt;Tyler Akidau e equipe do Google publicaram em 2015 o paper que virou padrão da indústria, &lt;em&gt;The Dataflow Model&lt;/em&gt;. A frase central:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;A practical approach to balancing the inherent tension between correctness, latency, and cost in massive-scale, unbounded, out-of-order data.&lt;/em&gt;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;Tradução: streaming é certo em três variáveis ao mesmo tempo. Correção, latência e custo. Você escolhe duas, paga a terceira. Batch é mais simples justamente porque não tenta otimizar latência.&lt;/p&gt;
&lt;h2&gt;Tabela de decisão: SLA × tecnologia&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;tabela-de-decisão-sla--tecnologia&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#tabela-de-decis%c3%a3o-sla--tecnologia&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/30/sla-nao-moda-batch-streaming/images/01-tabela-sla-tecnologia.png&#34; alt=&#34;Tabela visual SLA versus tecnologia recomendada&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Pra a maioria dos pipelines que vejo, a tabela acima resolve a decisão em 30 segundos. SLA acima de 1 hora é território de batch. SLA abaixo de 1 minuto exige streaming. O meio é micro-batch, e a maioria dos casos cai aí, não nos extremos.&lt;/p&gt;
&lt;h2&gt;Quando batch ganha (mesmo em 2026)&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-batch-ganha-mesmo-em-2026&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-batch-ganha-mesmo-em-2026&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Spotify roda recomendação em batch noturno no BigQuery. Netflix tem o Maestro orquestrando centenas de milhares de workflows por dia com padrão Write-Audit-Publish sobre Iceberg. Nenhuma das duas é &amp;ldquo;atrasada&amp;rdquo;. Elas escolheram batch onde batch resolve melhor.&lt;/p&gt;
&lt;p&gt;Batch ganha quando:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;O SLA do consumidor é horário ou diário (relatório contábil, fechamento, snapshot histórico, ML training)&lt;/li&gt;
&lt;li&gt;O dado de entrada é estável o suficiente pra você reprocessar quando quiser&lt;/li&gt;
&lt;li&gt;Seu time tem mais facilidade em debugar Python rodando 1 vez por noite do que stream processor 24x7&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;O custo importa muito. Cluster Spark batch noturno fica desligado durante o dia. Infraestrutura quando não tem job rodando: zero. Kafka gerenciado fica ligado 24x7. Confluent Cloud Standard começa em 1 a 3 mil dólares por mês, e o egress pode chegar a 47 mil dólares por mês em 300 MiB/s de saída. A diferença sobre o ano é o salário de um engenheiro pleno em Curitiba.&lt;/p&gt;
&lt;h2&gt;Quando streaming é a única resposta&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-streaming-é-a-única-resposta&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-streaming-%c3%a9-a-%c3%banica-resposta&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Pix tem SLA de menos de 10 segundos, 24x7. O BACEN publica isso. Batch diário não funciona. Não é opcional. Sistema de detecção de fraude em ponto de venda também é assim: ou identifica antes da transação fechar ou não serve pra nada. Dashboard de operações em call center, mesma lógica: o agente precisa ver o cliente atualizado no instante em que atende.&lt;/p&gt;
&lt;p&gt;Esses casos não admitem batch. Streaming é a única resposta.&lt;/p&gt;
&lt;p&gt;Pra eles, Flink entrega latência abaixo de 100 milissegundos. Spark Structured Streaming fica em 100 milissegundos a 1 segundo (micro-batch). Kafka Streams roda embutido na aplicação, sem cluster próprio, e processa cerca de 1 milhão de eventos por segundo. A escolha entre os três é outro post.&lt;/p&gt;
&lt;p&gt;Uber é o caso mais interessante. Adotou streaming sem virar 100% streaming. Adicionou o Hudi pra incremental processing e baixou latência de ingestão de 24 horas pra menos de 1 hora em mais de 100 PB. O Flink IngestionNext deles consome 25% menos compute que o batch antigo. Streaming bem feito também economiza, desde que resolva o problema certo.&lt;/p&gt;
&lt;h2&gt;Quando &amp;ldquo;ambos&amp;rdquo; é a resposta certa&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-ambos-é-a-resposta-certa&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-ambos-%c3%a9-a-resposta-certa&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Jay Kreps publicou em 2014 o ensaio que matou a Lambda Architecture. Lambda mantém duas pipelines paralelas pra produzir o mesmo resultado: uma batch confiável, uma streaming rápida. A frase que ficou:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;The problem with the Lambda Architecture is that maintaining code that needs to produce the same result in two complex distributed systems is exactly as painful as it seems.&lt;/em&gt;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/30/sla-nao-moda-batch-streaming/images/02-quote-kreps-lambda.png&#34; alt=&#34;Quote Kreps sobre Lambda Architecture&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Kreps propôs Kappa: log único (Kafka) como fonte de verdade, com reprocessamento via replay. Batch vira caso especial de streaming sobre o histórico.&lt;/p&gt;
&lt;p&gt;O Lakehouse foi um passo a mais. O paper do Databricks de 2021 propõe uma camada de metadata (Delta, Iceberg, Hudi) que serve as duas naturezas. O mesmo dado pode ser consumido em batch pela equipe de BI e em streaming pela aplicação de fraude. Não tem 2 stacks. Tem contrato único.&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Ambos&amp;rdquo; não é covardia técnica. É design consciente quando você tem consumidores com SLAs diferentes sobre o mesmo dado.&lt;/p&gt;
&lt;h2&gt;Perguntas que decidem o caso&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;perguntas-que-decidem-o-caso&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#perguntas-que-decidem-o-caso&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Antes de abrir Terraform ou docker-compose, responde isso honestamente:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Qual o SLA real do consumidor que vai ler esse dado?&lt;/strong&gt; Não o SLA que você imagina. O que ele de fato precisa.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Esse SLA é diferente por consumidor?&lt;/strong&gt; Se sim, considera Lakehouse com contrato único, não 2 pipelines paralelas.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Quanto custa rodar 1 mês de streaming vs batch nesse volume?&lt;/strong&gt; Faz a conta antes, não depois do invoice.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Seu time tem maturidade pra debugar exactly-once, watermarks e estado distribuído?&lt;/strong&gt; Se não, o custo de aprender vem embutido no projeto.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Você já tem infraestrutura de batch ou streaming rodando?&lt;/strong&gt; Reaproveitar reduz risco. Greenfield permite escolher melhor.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/30/sla-nao-moda-batch-streaming/images/03-decision-tree-5-perguntas.png&#34; alt=&#34;Decision tree visual das 5 perguntas&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Se você respondeu honestamente as 5 e ainda assim chegou em streaming, ótimo. Streaming faz sentido. Se chegou em batch, ótimo também. Batch resolve a maioria dos casos.&lt;/p&gt;
&lt;p&gt;O erro não é escolher streaming. O erro é escolher streaming sem responder as 5.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Qual foi o pipeline que você escolheu errado e teve que refazer depois? Me conta no &lt;a href=&#34;https://linkedin.com/in/thaisvaz&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;LinkedIn&lt;/a&gt; ou responde esse email. Quero ver quantos casos batem.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Airflow por 2 anos: o que eu faria diferente</title>
      <link>https://vazdeng.pages.dev/2026/05/24/airflow-2-anos-o-que-faria-diferente/</link>
      <pubDate>Sun, 24 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/05/24/airflow-2-anos-o-que-faria-diferente/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/05/24/airflow-2-anos-o-que-faria-diferente/cover_hu_f193dea45ae9e3c3.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Era 2h da manhã quando o alerta chegou. O DAG de relatório mensal tinha falhado no step 8 de 12. Dados financeiros, prazo 6h da manhã, e eu passei as 4 horas seguintes tentando entender se a task realmente falhou, se foi timeout silencioso, ou se o worker tinha morrido sem avisar ninguém. Quando descobri que era a terceira coisa, faltavam 40 minutos.&lt;/p&gt;
&lt;p&gt;Esse cenário é rotineiro em times que usam Airflow em produção. O Airflow funciona. E também cria trabalho que ninguém avisa no primeiro tutorial.&lt;/p&gt;
&lt;p&gt;Esse post não é para convencer ninguém a abandonar o Airflow. É sobre o que vale mudar antes que o problema apareça.&lt;/p&gt;
&lt;h2&gt;Contexto: o que é e quem usa&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;contexto-o-que-é-e-quem-usa&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#contexto-o-que-%c3%a9-e-quem-usa&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O Airflow foi criado por Maxime Beauchemin no Airbnb em outubro de 2014 para orquestrar pipelines de dados com dependências complexas. Virou open source em junho de 2015 e projeto top-level da Apache Foundation em janeiro de 2019.&lt;/p&gt;
&lt;p&gt;Hoje é o orquestrador de dados mais usado no mundo: 320 milhões de downloads só em 2024, dez vezes mais que o segundo colocado. Uber roda 200.000 pipelines com 750.000 task runs por dia. Shopify tem 10.000 DAGs ativos. Stripe processa 150.000 tasks diárias.&lt;/p&gt;
&lt;p&gt;É adoção real, não hype.&lt;/p&gt;
&lt;p&gt;Mas o mesmo relatório que aponta esses números também revela que 46% dos usuários dizem que quando o Airflow tem problema, a operação inteira para. Essa é a tensão que ninguém conta no primeiro tutorial.&lt;/p&gt;
&lt;h2&gt;O que o Airflow resolve bem&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-o-airflow-resolve-bem&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-o-airflow-resolve-bem&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Dependências entre tasks são garantidas.&lt;/strong&gt; Você define o grafo em Python. O Airflow garante que task B só roda quando task A termina com sucesso. Com 50 tasks interdependentes num pipeline financeiro, ter isso garantido por um orquestrador evita reescrever lógica de retry e dependência em cada DAG, e elimina a categoria inteira de bug &amp;ldquo;task rodou antes da hora porque o cron disparou&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Retry com backoff é nativo.&lt;/strong&gt; Duas linhas e sua task tenta de novo automaticamente. Em pipelines que dependem de APIs externas instáveis, isso elimina alertas às 2h da manhã para erros transitórios.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O histórico de execução é auditável.&lt;/strong&gt; Toda execução, cada task, cada log fica registrado. Quando compliance pergunta &amp;ldquo;o relatório de março foi gerado com dados de 31/03 ou de 01/04?&amp;rdquo;, você abre o Airflow e responde em segundos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Backfill funciona.&lt;/strong&gt; Pipeline parado por três dias? Você reprocessa as execuções históricas com um comando. Para pipelines que precisam de histórico completo e consistente, isso importa muito.&lt;/p&gt;
&lt;h2&gt;Onde o Airflow complica&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;onde-o-airflow-complica&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#onde-o-airflow-complica&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/24/airflow-2-anos-o-que-faria-diferente/images/01-diagrama-arquitetura.png&#34; alt=&#34;Diagrama dos 3 componentes do Airflow e onde cada problema acontece&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h3&gt;O scheduler parseia todo o seu código a cada 30 segundos&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-scheduler-parseia-todo-o-seu-código-a-cada-30-segundos&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-scheduler-parseia-todo-o-seu-c%c3%b3digo-a-cada-30-segundos&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;O scheduler precisa executar o código Python de cada arquivo DAG repetidamente para entender o que existe e quais são as dependências. Com 200 DAGs, esse ciclo de parse pode levar minutos.&lt;/p&gt;
&lt;p&gt;O que torna isso crítico: 98% dos casos de lentidão no scheduler são causados por imports pesados no nível do módulo. Um arquivo que faz &lt;code&gt;import pandas as pd&lt;/code&gt; no topo, fora de qualquer função, faz o scheduler executar esse import a cada ciclo. Em 200 DAGs com imports pesados, isso vira minutos de parse antes de qualquer task executar.&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Errado: pandas é importado a cada ciclo do scheduler&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;pandas&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;pd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@dag&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;pipeline&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;# Certo: import apenas quando a task executa&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@task&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;processar&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;():&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;kn&#34;&gt;import&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;pandas&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;as&lt;/span&gt; &lt;span class=&#34;nn&#34;&gt;pd&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;o&#34;&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/24/airflow-2-anos-o-que-faria-diferente/images/02-antes-depois-imports.png&#34; alt=&#34;Comparação visual: import pandas no topo do DAG vs dentro da task&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h3&gt;XCom tem limite severo que ninguém avisa no começo&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;xcom-tem-limite-severo-que-ninguém-avisa-no-começo&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#xcom-tem-limite-severo-que-ningu%c3%a9m-avisa-no-come%c3%a7o&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;XCom é o mecanismo do Airflow para tasks se comunicarem. O problema: foi projetado para mensagens pequenas, não para dados.&lt;/p&gt;
&lt;p&gt;No PostgreSQL, o limite default de linha é 8KB. Um DataFrame de 1.000 linhas vai explodir o XCom. Em produção, o erro aparece como timeout ou crash silencioso do metadata database, não como uma mensagem clara de &amp;ldquo;dado grande demais&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;A solução usada em produção: passar apenas o path no S3 via XCom, nunca o dado em si.&lt;/p&gt;
&lt;h3&gt;catchup=True já disparou backfills indesejados em muitos times&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;catchuptrue-já-disparou-backfills-indesejados-em-muitos-times&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#catchuptrue-j%c3%a1-disparou-backfills-indesejados-em-muitos-times&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Por padrão em versões antigas, se você reimplantar um DAG com &lt;code&gt;start_date&lt;/code&gt; no passado e &lt;code&gt;catchup=True&lt;/code&gt;, o Airflow vai criar e tentar executar todas as runs históricas desde &lt;code&gt;start_date&lt;/code&gt;. Com um DAG mensal e &lt;code&gt;start_date&lt;/code&gt; dois anos atrás, isso são 24 runs disparadas de uma vez.&lt;/p&gt;
&lt;p&gt;A DoubleVerify documentou que depois de migrar para um setup com &lt;code&gt;catchup=False&lt;/code&gt; como padrão do cluster e outras mudanças, os incidentes caíram 80%.&lt;/p&gt;
&lt;h3&gt;Renomear um DAG perde todo o histórico&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;renomear-um-dag-perde-todo-o-histórico&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#renomear-um-dag-perde-todo-o-hist%c3%b3rico&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Não existe operação de rename no Airflow. Renomear um DAG cria uma entrada nova no metadata database e perde todo o histórico de execuções. Em produção, isso significa que você não consegue comparar o comportamento atual com o passado, e qualquer alert que dependa do histórico quebra.&lt;/p&gt;
&lt;h3&gt;Lógica de negócio dentro do operador vira problema depois&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;lógica-de-negócio-dentro-do-operador-vira-problema-depois&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#l%c3%b3gica-de-neg%c3%b3cio-dentro-do-operador-vira-problema-depois&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;A tentação é colocar transformações e regras de negócio direto dentro do &lt;code&gt;PythonOperator&lt;/code&gt;. Funciona no começo. Depois de seis meses, você tem lógica não testável presa dentro de infraestrutura, a mesma regra duplicada em três operadores diferentes, e um DAG que só dá para debugar subindo o Airflow inteiro.&lt;/p&gt;
&lt;p&gt;O padrão correto: o operador é infraestrutura e chama funções testáveis que vivem fora do DAG.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/24/airflow-2-anos-o-que-faria-diferente/images/03-quote-card.png&#34; alt=&#34;Lógica de negócio dentro do operador vira problema depois&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;O que eu faria diferente&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-eu-faria-diferente&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-eu-faria-diferente&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;TaskFlow API desde o primeiro dia.&lt;/strong&gt; Lançada no Airflow 2.0, permite escrever DAGs com decoradores Python em vez de instanciar operadores manualmente. O código fica mais limpo, as dependências ficam implícitas no fluxo, e é mais fácil de testar. Passei tempo demais escrevendo no estilo antigo antes de migrar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;catchup=False&lt;/code&gt; como padrão do cluster na configuração inicial.&lt;/strong&gt; Uma linha em &lt;code&gt;airflow.cfg&lt;/code&gt; que evita dezenas de incidentes.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Resource pools desde o primeiro DAG.&lt;/strong&gt; Por padrão o Airflow não limita quantas tasks de um DAG rodam em paralelo. Um DAG pesado pode consumir todos os slots e bloquear os outros. Configurar pools antes do primeiro problema, não depois.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Nada de multi-tenant numa mesma instância.&lt;/strong&gt; Compartilhar uma instância Airflow entre times diferentes cria conflitos de dependências Python, falta de isolamento de recursos, e upgrade paralysis: um time não consegue atualizar sem coordenar com todos os outros. Uma instância por time é o padrão recomendado.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Monitorar o scheduler, não só as tasks.&lt;/strong&gt; O scheduler é o coração do Airflow e pode degradar silenciosamente. Grafana no heartbeat do scheduler identifica problemas antes que as tasks comecem a falhar.&lt;/p&gt;
&lt;h2&gt;Sobre o Airflow 3.0&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;sobre-o-airflow-30&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#sobre-o-airflow-30&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Em abril de 2025 o Airflow lançou a versão 3.0, a maior release da história do projeto. Ela resolve problemas que a comunidade documentou durante anos: Task Execution API que elimina a necessidade dos workers acessarem diretamente o banco de metadados, DAG Versioning nativo, interface React reconstruída, e suporte a tasks em múltiplas linguagens além de Python.&lt;/p&gt;
&lt;p&gt;Se você está começando um projeto novo, avalie o Airflow 3.0 antes de escolher a versão a instalar. As mudanças são breaking, então migrar um cluster existente exige planejamento.&lt;/p&gt;
&lt;h2&gt;Quando avaliar alternativas&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-avaliar-alternativas&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-avaliar-alternativas&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O Airflow tem 320 milhões de downloads por uma razão: ele funciona, tem o maior ecossistema de integrações do mercado, e a comunidade é vasta.&lt;/p&gt;
&lt;p&gt;Mas existem casos onde outras ferramentas resolvem melhor:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Prefect ou Dagster&lt;/strong&gt; para times menores que valorizam desenvolvimento local simples, workflows event-driven, e observabilidade mais rica sem overhead operacional do Airflow.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;dbt Cloud&lt;/strong&gt; quando a maioria dos pipelines são transformações SQL num warehouse. A orquestração nativa é mais simples para esse caso específico.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Airflow gerenciado&lt;/strong&gt; (Astronomer, Amazon MWAA, Google Cloud Composer) se o custo cabe e você não quer manter a infraestrutura. Remove parte significativa da dor operacional.&lt;/p&gt;
&lt;p&gt;O que não vale é escolher pela popularidade sem avaliar se o problema que o Airflow resolve é o seu problema.&lt;/p&gt;
&lt;h2&gt;O que fica&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-fica&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-fica&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O Airflow funciona bem para o que foi feito: orquestrar pipelines batch com dependências complexas, histórico auditável e retry confiável.&lt;/p&gt;
&lt;p&gt;Os problemas que encontrei foram quase todos evitáveis com configuração correta desde o início: imports fora de funções, XCom para dados grandes, catchup sem controle, lógica de negócio dentro de operadores.&lt;/p&gt;
&lt;p&gt;Se você está começando: imports dentro das funções, &lt;code&gt;catchup=False&lt;/code&gt; no cluster, XCom só para coordenação, lógica de negócio em módulos testáveis separados. São quatro decisões que evitam a maioria dos problemas que eu encontrei.&lt;/p&gt;
&lt;p&gt;Qual foi o problema mais irritante que você já viu com Airflow? Me conta no &lt;a href=&#34;https://linkedin.com/in/thaisvaz&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;LinkedIn&lt;/a&gt; ou assina a &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;newsletter&lt;/a&gt;.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Vinte conceitos de IA que você precisa entender em 2026</title>
      <link>https://vazdeng.pages.dev/2026/05/23/20-conceitos-ia-2026/</link>
      <pubDate>Sat, 23 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/05/23/20-conceitos-ia-2026/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/05/23/20-conceitos-ia-2026/cover_hu_e9316444b961433b.png" width="640" height="336"/>]]>
        
        &lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/23/20-conceitos-ia-2026/images/infografo.png&#34; alt=&#34;Vinte conceitos de IA que você precisa entender em 2026&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Toda semana aparece um termo novo de IA. Agente, RAG, fine-tuning, embedding, top-p, RLHF. Você abre o LinkedIn e três pessoas já estão &amp;ldquo;construindo agentes autônomos&amp;rdquo; antes do café da manhã. No Twitter alguém reclama que o RAG dele alucina, enquanto o post do lado debate se vale a pena fine-tunar Llama 3. Aí você vai na documentação da API que ia testar pra resolver uma coisa simples e cai num glossário de cem palavras antes da primeira chamada útil.&lt;/p&gt;
&lt;p&gt;O problema não é a quantidade de termos. É que ninguém para pra desenhar como eles se conectam.&lt;/p&gt;
&lt;p&gt;Esse infográfico é a minha tentativa de mapa. Vinte conceitos, seis seções, uma sequência que faz sentido se você for da base pra fronteira. Está longe de cobrir tudo que existe em IA. Mas dá pra abrir ele numa reunião técnica em 2026 e entender do que estão falando, ou ler o código de um sistema agêntico e identificar o que cada peça faz no fluxo.&lt;/p&gt;
&lt;h2&gt;Como a IA funciona (1 a 4)&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;como-a-ia-funciona-1-a-4&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#como-a-ia-funciona-1-a-4&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Tudo começa em &lt;strong&gt;redes neurais&lt;/strong&gt;. Camadas de neurônios conectados por pesos, ajustados durante o treinamento pra fazer previsões. É a única primitiva de tudo isso. Modelo que vê imagem, modelo que escreve texto, modelo que entende áudio: todos são variações dela, com escolhas diferentes de arquitetura por cima.&lt;/p&gt;
&lt;p&gt;Pra linguagem entrar nessa rede, precisa virar número. Esse é o trabalho da &lt;strong&gt;tokenização&lt;/strong&gt;: quebrar texto em pedaços que o modelo consegue mastigar. A IA não lê palavras. Lê tokens. Depois cada token vira um vetor num espaço de centenas de dimensões, e isso é &lt;strong&gt;embedding&lt;/strong&gt;. Significados parecidos ficam perto. É o que faz busca semântica, recomendação e RAG funcionarem.&lt;/p&gt;
&lt;p&gt;E no topo desses três vem &lt;strong&gt;atenção&lt;/strong&gt;. O mecanismo que deixa cada palavra olhar pra todas as outras da entrada e decidir o que importa pra ela. Antes de atenção, modelos liam texto em sequência e esqueciam o começo no meio da frase. Atenção quebrou esse gargalo. Sem ela, o resto da IA contemporânea simplesmente não existiria no formato que a gente conhece hoje.&lt;/p&gt;
&lt;h2&gt;A mágica por trás (5 a 8)&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-mágica-por-trás-5-a-8&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-m%c3%a1gica-por-tr%c3%a1s-5-a-8&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;&lt;strong&gt;Transformers&lt;/strong&gt; são a arquitetura que empacotou atenção em algo treinável em paralelo. Antes deles, modelos de linguagem eram lentos e curtos. Depois deles, viraram GPT, Claude, Gemini.&lt;/p&gt;
&lt;p&gt;Mas arquitetura sem dado é nada. &lt;strong&gt;Pré-treinamento&lt;/strong&gt; é a fase em que o modelo lê o equivalente a uma biblioteca de Alexandria. Trilhões de tokens. É aqui que ele absorve sintaxe, gramática, fatos sobre o mundo e os padrões de raciocínio que os humanos deixaram escritos. &lt;strong&gt;Ajuste fino&lt;/strong&gt; é o que vem depois: pegar esse modelo geral e especializar em tarefa específica com dado específico. E &lt;strong&gt;RLHF&lt;/strong&gt; é a etapa que pegou modelos que sabiam responder qualquer coisa e ensinou eles a responder de um jeito que serve pra alguém. Pessoas reais comparam saídas, dizem qual é melhor, o modelo aprende a preferência. É o que separa &amp;ldquo;modelo que sabe muito&amp;rdquo; de &amp;ldquo;modelo que conversa bem&amp;rdquo;.&lt;/p&gt;
&lt;h2&gt;Além dos modelos (9 a 12)&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;além-dos-modelos-9-a-12&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#al%c3%a9m-dos-modelos-9-a-12&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Modelo nenhum vai pra produção sozinho. Em volta dele vai uma camada de &lt;strong&gt;salvaguardas&lt;/strong&gt;: filtros e classificadores construídos sobre regras explícitas, pra evitar que o sistema diga coisa que machuca alguém ou que reproduz viés óbvio. É a parte chata que ninguém quer construir e que todo produto sério precisa ter.&lt;/p&gt;
&lt;p&gt;E quando o modelo precisa saber algo que não estava no pré-treinamento, entra &lt;strong&gt;RAG&lt;/strong&gt;. Geração aumentada por recuperação. O sistema busca documentos relevantes, injeta no contexto, e o modelo responde ancorado neles. RAG depende de dois primos: &lt;strong&gt;bancos vetoriais&lt;/strong&gt; (que armazenam embeddings de forma que dá pra achar o mais parecido em milissegundos) e &lt;strong&gt;fragmentação&lt;/strong&gt; (que quebra documentos grandes em pedaços indexáveis). RAG sem boa fragmentação é RAG que alucina elegantemente. Eu vi mais sistema RAG quebrar por chunking ruim do que por qualquer outro motivo isolado.&lt;/p&gt;
&lt;h2&gt;Como a IA gera saídas (13 a 14)&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;como-a-ia-gera-saídas-13-a-14&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#como-a-ia-gera-sa%c3%addas-13-a-14&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Quando o modelo responde, ele não escreve a frase inteira de uma vez. Ele prevê um token, depois o próximo, depois o próximo. Isso é &lt;strong&gt;decodificação&lt;/strong&gt;. E o jeito que ele escolhe cada próximo token muda completamente o caráter da saída. &lt;strong&gt;Temperatura&lt;/strong&gt; alta dá criatividade e variação. &lt;strong&gt;Top-p&lt;/strong&gt; baixo dá foco nas opções mais prováveis. Mexer nesses dois parâmetros é a diferença entre um modelo que escreve poesia e um modelo que escreve documentação técnica.&lt;/p&gt;
&lt;h2&gt;Como a IA age (15 a 16)&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;como-a-ia-age-15-a-16&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#como-a-ia-age-15-a-16&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Até aqui o modelo só responde. &lt;strong&gt;Agentes&lt;/strong&gt; são o passo seguinte: ele decide e age. Recebe um objetivo, decompõe em passos, escolhe qual ferramenta usar, executa, observa o resultado, ajusta o próximo passo. &lt;strong&gt;Ferramentas e funções&lt;/strong&gt; são as mãos que damos pra esse agente: API, calculadora, busca, execução de código, acesso a banco. Sem isso o agente fica preso na própria cabeça falando sozinho. Toda a parte que importa de sistema agêntico começa quando o modelo finalmente pode chamar alguma coisa que muda estado no mundo real.&lt;/p&gt;
&lt;h2&gt;Melhoria e avaliação (17 a 20)&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;melhoria-e-avaliação-17-a-20&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#melhoria-e-avalia%c3%a7%c3%a3o-17-a-20&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Sistema agêntico sem &lt;strong&gt;planejamento&lt;/strong&gt; explícito vira caos rápido. Sem &lt;strong&gt;avaliação&lt;/strong&gt; rigorosa, qualquer afirmação sobre o modelo virou torcida. &lt;strong&gt;Melhoria iterativa&lt;/strong&gt; é o que separa protótipo bonito de sistema que sobrevive em produção: testar, medir, ajustar, repetir. E &lt;strong&gt;viés e justiça&lt;/strong&gt; tem uma característica chata: se você ignora no design, vai te encontrar no incidente.&lt;/p&gt;
&lt;h2&gt;Fechamento&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;fechamento&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#fechamento&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;IA não é mágica. É matemática com dados em cima, lógica em volta e iteração no centro. Quem entende esses vinte conceitos lê arquitetura de sistema agêntico sem se perder no glossário. Consegue debugar comportamento esquisito do modelo a partir de hipóteses reais, não chute. E numa conversa técnica fala como quem participou da construção, não como quem leu o release.&lt;/p&gt;
&lt;p&gt;Pega o infográfico. Salva no celular, imprime e cola na parede, joga no Notion. Eu volto nele toda vez que aparece um termo que parece novo, e quase sempre o termo é um caso particular de um desses vinte. E mais importante que tudo isso: constrói alguma coisa com ele. É quando você tenta fazer um RAG funcionar de verdade que descobre o que cada uma dessas palavras realmente quer dizer.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;&lt;em&gt;Esse é o primeiro post da trilha IA Foundations no &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;VazDEng&lt;/a&gt;. Por lá saem três posts por semana sobre engenharia de dados em português, no nível sênior que faltava na BR.&lt;/em&gt;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Quando o modelo deveria dizer &#39;não sei&#39;</title>
      <link>https://vazdeng.pages.dev/2026/05/17/hmm-aprende-a-abster/</link>
      <pubDate>Sun, 17 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/05/17/hmm-aprende-a-abster/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/05/17/hmm-aprende-a-abster/cover_hu_3e18e146efd31b6a.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Em setembro de 1998, a Long-Term Capital Management perdeu 4.6 bilhões de dólares em algumas semanas. Os modelos de spread foram treinados em correlações de tempo normal. O default russo e a fuga subsequente para qualidade fizeram correlações que historicamente eram 0.3 convergirem para 1 em dias. Em &lt;em&gt;When Genius Failed&lt;/em&gt;, o Lowenstein cita o cálculo interno do fundo sobre a probabilidade do que aconteceu:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&amp;ldquo;An event so freakish as to be unlikely to occur even once over the entire life of the universe.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;Os modelos estavam tecnicamente corretos. Estavam só extrapolando confiança numa região do espaço que nunca tinham visto. Não tinham botão de &amp;ldquo;não sei&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Meu agente quant tinha o mesmo problema, em escala incomparavelmente menor mas com a mesma natureza. Resolvi essa semana.&lt;/p&gt;
&lt;h2&gt;Em uma frase&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;em-uma-frase&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#em-uma-frase&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;blockquote&gt;
  &lt;p&gt;Conservative degradation é o princípio que diz que um modelo precisa ter o direito de abster. Quando os dados estão fora do que ele viu, devolver &amp;ldquo;não sei&amp;rdquo; é mais útil que devolver uma classificação espúria com confiança matemática alta.&lt;/p&gt;

&lt;/blockquote&gt;
&lt;h2&gt;O ponto cego que sobrava depois do data leakage&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-ponto-cego-que-sobrava-depois-do-data-leakage&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-ponto-cego-que-sobrava-depois-do-data-leakage&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O post anterior fechou o capítulo do Sharpe -1.14. Posterior virou causal, data leakage saiu, número ficou honesto. Mas tinha um ponto cego que não aparecia no Sharpe.&lt;/p&gt;
&lt;p&gt;O HMM gaussiano de 3 estados sempre classifica. Recebe um candle, calcula posterior sobre BULL/SIDEWAYS/BEAR, e devolve aquela com maior probabilidade. Por construção. Se as features estão na zona normal de treino, ótimo. Se estão completamente fora, ele continua classificando, e a posterior continua somando 1.&lt;/p&gt;
&lt;p&gt;Cenário concreto: ATR diário 4x acima da média de 90 dias, funding rate em extremo negativo histórico, volume 10x acima do normal. Um pico que o modelo simplesmente não tem ponto de referência. O HMM devolve algo como &amp;ldquo;BULL com 73% de confiança&amp;rdquo;, porque uma das três classes vai ganhar.&lt;/p&gt;
&lt;p&gt;Matematicamente legítimo. Operacionalmente perigoso.&lt;/p&gt;
&lt;h2&gt;O que a literatura chama disso&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-a-literatura-chama-disso&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-a-literatura-chama-disso&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Olhei a literatura antes de implementar nada. Três fios convergem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Out-of-Distribution detection (visão computacional, ML clássico).&lt;/strong&gt; A linhagem começa em Hendrycks &amp;amp; Gimpel 2017 (&amp;ldquo;A Baseline for Detecting Misclassified and Out-of-Distribution Examples in Neural Networks&amp;rdquo;), que mostra que a probabilidade máxima do softmax já é um sinal razoável de confiança. Liang et al 2018 (ODIN) adiciona temperature scaling e perturbação adversarial, reduzindo false positive rate de 34.7% para 4.3%. Lee et al 2018 propõe Mahalanobis distance no espaço de features pra capturar covariância entre dimensões. Os três são o canon de OOD.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Selective classification (estatística, reconhecimento de padrões).&lt;/strong&gt; Chow 1957 já formalizou o reject option em IRE Trans. Electronic Computers. Em 1970 ele deriva a curva error-reject ótima. Em 2017, Geifman e El-Yaniv levam o conceito pra deep learning com garantia formal de risco:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&amp;ldquo;We can achieve a target coverage with a guaranteed level of risk.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;A métrica canônica de avaliar abstenção é AURC (Area Under Risk-Coverage curve): mostra como o erro cai conforme o modelo se permite rejeitar mais casos.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Sistemas críticos com conservative degradation.&lt;/strong&gt; Aviação tem regulamentação explícita (FAA AC 25.1329-1B): autopilot deve alertar quando envelope protection é invocada e desengajar em condições off-nominal. SAE J3016 (autonomous driving) define Operational Design Domain (ODD) e exige que o sistema saia de operação ou peça takeover quando opera fora dele. O princípio é o mesmo: modelo treinado pra condições X não opera em Y, ele alerta e devolve controle.&lt;/p&gt;
&lt;p&gt;Trading se beneficia desse vocabulário. Foi o que faltava.&lt;/p&gt;
&lt;h2&gt;Já fizeram isso em finanças&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;já-fizeram-isso-em-finanças&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#j%c3%a1-fizeram-isso-em-finan%c3%a7as&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Dois precedentes pra ancorar.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Kritzman e Li 2010&lt;/strong&gt; (&amp;ldquo;Skulls, Financial Turbulence, and Risk Management&amp;rdquo;, Financial Analysts Journal). Definem o Turbulence Index como distância de Mahalanobis multivariada dos retornos contra média e covariância históricas. Frase central:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&amp;ldquo;The more asset returns, volatilities and correlations differ from their historical norms, the more likely it is that these differences result from a significant market event rather than from random noise.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;Empiricamente o índice alinha com 1987, default russo de 98, 9/11, e crise de 2008. Turbulência é persistente, o que justifica abster por janelas, não por tick isolado.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Chalkidis et al 2021&lt;/strong&gt; (&amp;ldquo;Trading via Selective Classification&amp;rdquo;, ACM ICAIF, arXiv 2110.14914). Esse paper é o caso direto do que eu fiz. Classificador binário up/down vira estratégia que toma posição apenas quando confia, e abstém quando não confia. Resultado empírico: coverage menor com mesmo risco melhora Sharpe. A frase do abstract:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&lt;em&gt;&amp;ldquo;Selective classifiers give rise to trading strategies that do not take a trading position when the classifier abstains.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;Selective classification em trading não é meu insight. É tema documentado em ACM. O que faltava era trazer pro meu HMM.&lt;/p&gt;
&lt;h2&gt;Como implementei&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;como-implementei&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#como-implementei&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;As features do HMM passam por &lt;code&gt;StandardScaler&lt;/code&gt; antes do treino. No espaço escalado, a média de cada feature é zero e o desvio é um. Qualquer candle novo com uma feature em z-score absoluto muito alto está, por definição, fora da distribuição que o modelo viu.&lt;/p&gt;
&lt;p&gt;Limite em 5 sigmas (conservador, cripto tem fat tails). Método estático no &lt;code&gt;MarketRegimeHMM&lt;/code&gt;:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nd&#34;&gt;@staticmethod&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;def&lt;/span&gt; &lt;span class=&#34;nf&#34;&gt;is_ood&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x_scaled_row&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;threshold&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;OOD_SIGMA_THRESHOLD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;x_scaled_row&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;size&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;==&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;        &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;kc&#34;&gt;False&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;nb&#34;&gt;bool&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;nanmax&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;np&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;abs&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;x_scaled_row&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;))&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;threshold&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;E o &lt;code&gt;predict_state&lt;/code&gt; checa antes de chamar a posterior:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;if&lt;/span&gt; &lt;span class=&#34;bp&#34;&gt;self&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;is_ood&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;last_features&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;):&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;n&#34;&gt;logger&lt;/span&gt;&lt;span class=&#34;o&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;warning&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;&amp;#34;OOD detectado: max |z| = &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%.2f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt; &amp;gt; &lt;/span&gt;&lt;span class=&#34;si&#34;&gt;%.1f&lt;/span&gt;&lt;span class=&#34;s2&#34;&gt;. Abstem-se.&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;                   &lt;span class=&#34;n&#34;&gt;max_dev&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;OOD_SIGMA_THRESHOLD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;    &lt;span class=&#34;k&#34;&gt;return&lt;/span&gt; &lt;span class=&#34;n&#34;&gt;REGIME_OOD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;REGIME_OOD&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;1.0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Downstream da decisão (&lt;code&gt;decide_position&lt;/code&gt; na camada 4) já tinha lookup em &lt;code&gt;REGIME_MULTIPLIER&lt;/code&gt;. Adicionei &lt;code&gt;&amp;quot;OOD&amp;quot;: 0.0&lt;/code&gt; por defesa em camadas, e log explícito de &amp;ldquo;ABSTAIN&amp;rdquo; pra deixar visível quando o sistema preferiu não operar.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/17/hmm-aprende-a-abster/images/01-gate-ood.png&#34; alt=&#34;O gate de abstenção: candle novo passa pelo check de max |z| acima de 5 sigmas, dentro classifica e opera, fora retorna OOD e zera o sizing&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;70 testes passaram, mais 2 novos cobrindo o caminho OOD. Suite completa em 6 segundos.&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Cenário&lt;/th&gt;
          &lt;th&gt;Antes&lt;/th&gt;
          &lt;th&gt;Depois&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Features dentro da distribuição&lt;/td&gt;
          &lt;td&gt;Classifica BULL/SIDEWAYS/BEAR com posterior real&lt;/td&gt;
          &lt;td&gt;Igual&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Features 5+ sigmas fora (raras)&lt;/td&gt;
          &lt;td&gt;Classifica mesmo assim, com posterior espúria&lt;/td&gt;
          &lt;td&gt;Retorna OOD, sizing zera&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Log do tick OOD&lt;/td&gt;
          &lt;td&gt;Não havia distinção&lt;/td&gt;
          &lt;td&gt;&amp;ldquo;ABSTAIN: regime sem playbook (OOD, conf=0.000)&amp;rdquo;&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Trade aberto em condição anômala&lt;/td&gt;
          &lt;td&gt;Possível, com cap de 2%&lt;/td&gt;
          &lt;td&gt;Impossível&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;h2&gt;Por que 5 sigmas, não 3&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;por-que-5-sigmas-não-3&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#por-que-5-sigmas-n%c3%a3o-3&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A escolha do threshold é o ponto onde teoria encontra dados reais de cripto. Em features gaussianas perfeitas, 3 sigmas cobririam 99.73% e seria razoável. Cripto não é gaussiana. Realized volatility, funding rate, e DI spread têm caudas pesadas. Bulla 2011 (Quantitative Finance) já mostrou que HMM gaussiano subestima caudas em retornos financeiros, propondo Student-t em vez.&lt;/p&gt;
&lt;p&gt;Em 5 sigmas, o detector dispara só quando o tick está em região genuinamente sem precedente recente. Em 3, dispararia em movimentos grandes mas históricos, gerando abstenção excessiva. A próxima iteração é trocar z-score univariado por Mahalanobis multivariada (capta correlação entre features), que é exatamente o que Kritzman-Li fizeram em 2010 para retornos.&lt;/p&gt;
&lt;h2&gt;O que mudei no sono&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-mudei-no-sono&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-mudei-no-sono&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O número mais útil pra mim não é o aumento ou queda de Sharpe (vou medir em backtest na próxima semana). É o seguinte:&lt;/p&gt;
&lt;p&gt;Antes, quando o agente tomava uma posição na madrugada e eu acordava com Telegram piscando, eu precisava abrir o auditor e ler decisão por decisão pra entender se o modelo tinha alguma lógica naquele momento ou se estava chutando em mercado caótico.&lt;/p&gt;
&lt;p&gt;Agora, se o sistema abstém, o log diz &lt;code&gt;ABSTAIN&lt;/code&gt;. Se opera, é porque estava em território que ele viu. A pergunta &amp;ldquo;essa decisão tem base?&amp;rdquo; virou binária: existe um log de ABSTAIN antes, ou não.&lt;/p&gt;
&lt;p&gt;Nick Leeson, Jérôme Kerviel, LTCM, Knight Capital. A história de perdas operacionais em finanças quase sempre tem o mesmo padrão: um sistema continuando a tomar decisão quando não devia. O custo do &amp;ldquo;não sei&amp;rdquo; sempre foi mais barato que o custo do &amp;ldquo;achei que era&amp;rdquo;.&lt;/p&gt;
&lt;h2&gt;Anti-padrões pra evitar&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;anti-padrões-pra-evitar&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#anti-padr%c3%b5es-pra-evitar&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Aceitar posterior alta como evidência de boa decisão.&lt;/strong&gt; A posterior de um HMM sempre soma 1. Confiança é métrica intra-modelo, não evidência de que o modelo entende o que está vendo.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Usar threshold de OOD baseado em intuição, não em distribuição.&lt;/strong&gt; 3 sigmas funciona em gaussiana pura. Cripto não é gaussiana. Mede a cauda real do teu dado primeiro.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Abster por tick isolado e voltar a operar no próximo.&lt;/strong&gt; Turbulência é persistente. Bom design abstém por janela, não por candle.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adicionar OOD sem mexer no decisor.&lt;/strong&gt; Detector que não muda comportamento downstream é decoração. O &lt;code&gt;REGIME_MULTIPLIER&lt;/code&gt; é onde o efeito acontece.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Esconder a abstenção do log.&lt;/strong&gt; Se o sistema preferiu não operar, isso é decisão. Tem que aparecer no audit trail com o motivo, não silenciosamente.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/17/hmm-aprende-a-abster/images/02-anti-padroes.png&#34; alt=&#34;Os 5 anti-padrões de abstenção: posterior alta como evidência, threshold por intuição, abster por tick isolado, OOD sem mexer no decisor, abstenção fora do log&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;O próximo capítulo&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-próximo-capítulo&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-pr%c3%b3ximo-cap%c3%adtulo&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A versão atual usa um único critério (z-score absoluto por feature). Duas extensões já estão em backlog: Mahalanobis distance no espaço completo (captura covariância, é o que Kritzman-Li implementaram pra retornos em 2010) e likelihood do tick sob o modelo HMM treinado (mais sensível, mais caro).&lt;/p&gt;
&lt;p&gt;Por enquanto, o que está em produção é a versão simples. E ela já mudou o que eu olho quando acordo.&lt;/p&gt;
&lt;p&gt;Você já teve um modelo que devolveu confiança alta numa decisão que não deveria ter sido tomada? Me conta no &lt;a href=&#34;https://linkedin.com/in/thaisvaz&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;LinkedIn&lt;/a&gt; ou assina a &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;newsletter&lt;/a&gt; para receber os próximos posts.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Instrumentando lineage do zero com Unity Catalog</title>
      <link>https://vazdeng.pages.dev/2026/05/13/databricks-unity-catalog-lineage/</link>
      <pubDate>Wed, 13 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/05/13/databricks-unity-catalog-lineage/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/05/13/databricks-unity-catalog-lineage/cover_hu_54642d929bdf4bb.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Quando me perguntam &amp;ldquo;de onde vem esse número?&amp;rdquo;, tenho duas respostas possíveis.&lt;/p&gt;
&lt;p&gt;A primeira é abrir o código, rastrear manualmente qual job leu de qual tabela, entender quais transformações foram aplicadas, e voltar até a fonte. Em pipelines com 20 steps isso pode levar horas.&lt;/p&gt;
&lt;p&gt;A segunda é abrir o Unity Catalog, clicar na coluna em questão, e ver o grafo completo: fonte, transformações, tabelas intermediárias, destino. Em segundos.&lt;/p&gt;
&lt;p&gt;Essa diferença é o que lineage resolve na prática. Mas o Unity Catalog não captura tudo automaticamente. Entender o que ele cobre e o que exige trabalho adicional é o que separa uma implementação que funciona de uma que dá falsa sensação de segurança.&lt;/p&gt;
&lt;h2&gt;O que o Unity Catalog captura automaticamente&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-o-unity-catalog-captura-automaticamente&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-o-unity-catalog-captura-automaticamente&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O Unity Catalog intercepta os planos de execução do Spark em runtime e registra cada leitura e escrita em tabelas do metastore. Não precisa de configuração extra no código.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lineage de tabelas&lt;/strong&gt; funciona para qualquer operação SELECT, CREATE TABLE AS SELECT, INSERT INTO SELECT em qualquer linguagem: Python, SQL, Scala, R. Para cada operação, o sistema registra qual tabela foi lida, qual foi escrita, em qual job, em qual notebook, com qual usuário, em qual horário.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Lineage de colunas&lt;/strong&gt; vai além: mapeia quais colunas de origem alimentam quais colunas de destino. Requer Databricks Runtime 11.3 LTS ou superior para jobs comuns. Para Delta Live Tables, requer 13.3 LTS ou superior.&lt;/p&gt;
&lt;p&gt;Essas informações ficam acessíveis de duas formas: pelo Catalog Explorer com interface visual, e pelos system tables &lt;code&gt;system.access.table_lineage&lt;/code&gt; e &lt;code&gt;system.access.column_lineage&lt;/code&gt; para quem precisa programaticamente.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/13/databricks-unity-catalog-lineage/images/unity-catalog-cobertura.png&#34; alt=&#34;Cobertura do Unity Catalog: o que ele captura automaticamente vs onde a maioria erra&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;O que não é capturado e onde a maioria erra&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-não-é-capturado-e-onde-a-maioria-erra&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-n%c3%a3o-%c3%a9-capturado-e-onde-a-maioria-erra&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;A documentação oficial é clara mas discreta sobre as limitações. Vi essas limitações morderem em produção mais de uma vez.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;UPDATE, DELETE e INSERT VALUES não geram edges de lineage.&lt;/strong&gt; Essa é a limitação mais crítica para quem trabalha com CDC, SCD Type 2, ou qualquer pipeline com atualizações in-place. O dado foi modificado, mas o Unity Catalog não registra essa relação.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;MERGE INTO não captura lineage por padrão.&lt;/strong&gt; É possível ativar com &lt;code&gt;spark.databricks.dataLineage.mergeIntoV2Enabled&lt;/code&gt;, mas exige configuração explícita em cada cluster ou job.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;RDDs não são suportados.&lt;/strong&gt; A Unity Catalog API não funciona com RDD e portanto qualquer pipeline que use a API de baixo nível do Spark fica completamente fora do rastreamento.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Objetos renomeados perdem o histórico permanentemente.&lt;/strong&gt; Se você renomear uma tabela, um schema ou um catálogo, o lineage histórico quebra. Não existe migração automática do grafo quando o objeto muda de nome.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;JDBC connections fazem bypass completo.&lt;/strong&gt; Dados lidos ou escritos via JDBC não passam pelo mecanismo de captura do Unity Catalog.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tabelas referenciadas por path (s3://&amp;hellip;) não capturam column lineage.&lt;/strong&gt; Table lineage via path funciona, mas mapeamento de colunas não.&lt;/p&gt;
&lt;p&gt;E um detalhe prático importante: os system tables só têm dados a partir de setembro de 2024. Se você precisa de lineage histórico antes dessa data, não existe nos system tables.&lt;/p&gt;
&lt;h2&gt;Multi-hop lineage: o que o Catalog Explorer não mostra&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;multi-hop-lineage-o-que-o-catalog-explorer-não-mostra&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#multi-hop-lineage-o-que-o-catalog-explorer-n%c3%a3o-mostra&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O visualizador do Catalog Explorer exibe apenas um hop em cada direção: uma tabela upstream e uma tabela downstream imediata. Se o dado passou por cinco transformações, você vê só a adjacente.&lt;/p&gt;
&lt;p&gt;Para rastrear a cadeia completa, a abordagem é fazer queries iterativas nos system tables:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-sql&#34; data-lang=&#34;sql&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;-- Encontrar todos os ancestrais de uma tabela (multi-hop)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;WITH&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;RECURSIVE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineage&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;AS&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source_table_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target_table_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;as&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hop&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;system&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;access&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;table_lineage&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;WHERE&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target_table_name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;s1&#34;&gt;&amp;#39;minha_tabela_gold&amp;#39;&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;UNION&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ALL&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source_table_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target_table_name&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineage&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hop&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;+&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;system&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;k&#34;&gt;access&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;table_lineage&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tl&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;w&#34;&gt;  &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;JOIN&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineage&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ON&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;tl&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;target_table_name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;=&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;l&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;n&#34;&gt;source_table_name&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt;&lt;span class=&#34;w&#34;&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;SELECT&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;o&#34;&gt;*&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;FROM&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;lineage&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;ORDER&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;k&#34;&gt;BY&lt;/span&gt;&lt;span class=&#34;w&#34;&gt; &lt;/span&gt;&lt;span class=&#34;n&#34;&gt;hop&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Databricks não suporta CTE recursiva nativa nos system tables. Na prática, isso precisa de lógica iterativa em Python que vai fazendo a query por nível.&lt;/p&gt;
&lt;h2&gt;OpenLineage como complemento&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;openlineage-como-complemento&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#openlineage-como-complemento&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Para pipelines que saem do ecossistema Databricks (Airflow orquestrando jobs externos, dbt rodando num warehouse diferente, scripts Python com pandas), o OpenLineage é a alternativa mais usada para unificar lineage cross-platform.&lt;/p&gt;
&lt;p&gt;O OpenLineage integra via &lt;code&gt;OpenLineageSparkListener&lt;/code&gt; e captura lineage de S3, GCS, JDBC, Redshift e BigQuery. A integração existe, mas tem bugs documentados com Databricks Spark 3.4+: payloads gerados às vezes contêm apenas inputs sem outputs, e há incompatibilidades entre a versão do agente Spark 3.3 do OpenLineage e a implementação 3.4.1 do Databricks.&lt;/p&gt;
&lt;p&gt;Se OpenLineage é crítico no seu setup, verifique a compatibilidade de versão antes de ir para produção.&lt;/p&gt;
&lt;h2&gt;O que instrumentar manualmente&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-instrumentar-manualmente&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-instrumentar-manualmente&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Para ter lineage completo em pipelines reais, essas são as lacunas que precisam de trabalho adicional:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Ferramentas de BI&lt;/strong&gt; (Tableau, Power BI, Looker) precisam de connector explícito ou cadastro manual via External Lineage API, que está em Public Preview. O limite é de 10.000 objetos externos e 100.000 relações por metastore.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Orchestradores externos&lt;/strong&gt; (Airflow, Prefect) precisam de integração via API para que os jobs apareçam no grafo de lineage.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pipelines com UPDATE/DELETE extensivos&lt;/strong&gt; precisam de logging complementar via &lt;code&gt;system.query.history&lt;/code&gt; para auditoria, já que o lineage automático não cobre essas operações.&lt;/p&gt;
&lt;h2&gt;Por onde começar do zero&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;por-onde-começar-do-zero&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#por-onde-come%c3%a7ar-do-zero&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Se você está instrumentando lineage pela primeira vez num ambiente Databricks:&lt;/p&gt;
&lt;p&gt;Primeiro, confirme que os clusters e jobs estão em workspaces com Unity Catalog habilitado. Sem isso, nenhuma captura automática funciona.&lt;/p&gt;
&lt;p&gt;Segundo, valide o Databricks Runtime: 11.3 LTS ou superior para column lineage em jobs comuns. Projetos mais antigos rodando em runtimes abaixo disso não vão ter column lineage mesmo com Unity Catalog ativo.&lt;/p&gt;
&lt;p&gt;Terceiro, mapeie quais pipelines usam UPDATE/DELETE/MERGE extensivamente. Para esses, defina desde o início qual será a estratégia de auditoria complementar, seja via &lt;code&gt;system.query.history&lt;/code&gt; ou via logging explícito no código.&lt;/p&gt;
&lt;p&gt;Quarto, crie uma query de validação que roda semanalmente contra os system tables e verifica se tabelas críticas têm lineage registrado. Ausência de lineage em tabela importante é sinal de que algo saiu do scope de captura.&lt;/p&gt;
&lt;p&gt;Lineage não é uma feature que se ativa e esquece. Eu uso como prática contínua: a cada pipeline novo, valido o que o Unity Catalog capturou e o que ficou de fora.&lt;/p&gt;
&lt;p&gt;Com qual parte do lineage você tem mais dificuldade hoje? Me conta no &lt;a href=&#34;https://linkedin.com/in/thaisvaz&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;LinkedIn&lt;/a&gt; ou assina a &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;newsletter&lt;/a&gt;.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Quando a Medallion Architecture atrapalha mais do que ajuda</title>
      <link>https://vazdeng.pages.dev/2026/05/12/medallion-architecture-quando-nao/</link>
      <pubDate>Tue, 12 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/05/12/medallion-architecture-quando-nao/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/05/12/medallion-architecture-quando-nao/cover_hu_a0e26d2a2c83edec.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Existe um padrão de arquitetura que vi crescer desde 2020, criado pelo Databricks, adotado pela Microsoft como padrão oficial da plataforma Fabric em 2023, e que hoje está em quase toda conversa sobre engenharia de dados: o Medallion Architecture.&lt;/p&gt;
&lt;p&gt;Bronze, Silver, Gold. Dado bruto, dado limpo, dado agregado.&lt;/p&gt;
&lt;p&gt;O problema não é o padrão. O problema é que ele virou resposta automática. E quando qualquer arquitetura vira resposta automática, ela começa a criar mais problema do que resolve.&lt;/p&gt;
&lt;p&gt;O próprio Databricks deixa isso claro na documentação oficial: &lt;em&gt;&amp;ldquo;Following the medallion architecture is a recommended best practice but not a requirement.&amp;rdquo;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;Isso raramente aparece nas apresentações.&lt;/p&gt;
&lt;h2&gt;O que é Medallion Architecture, de verdade&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-é-medallion-architecture-de-verdade&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-%c3%a9-medallion-architecture-de-verdade&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O Databricks define assim: um padrão de design que organiza dados em um lakehouse em camadas que &lt;em&gt;progressivamente melhoram&lt;/em&gt; a estrutura e qualidade do dado, da Bronze para a Silver para a Gold.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Bronze&lt;/strong&gt; guarda o dado exatamente como veio da fonte, sem nenhuma transformação. É o arquivo histórico imutável. Se algo der errado nas camadas seguintes, você volta aqui.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Silver&lt;/strong&gt; aplica o mínimo de transformação necessário para criar uma visão consistente da empresa: limpeza, padronização, deduplicação, joins entre fontes. É onde o dado vira informação confiável.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Gold&lt;/strong&gt; organiza os dados para consumo específico: dashboards de analytics, modelos de ML, relatórios financeiros. É desnormalizada, otimizada para leitura, pensada para o usuário final.&lt;/p&gt;
&lt;p&gt;Vale um dado histórico: o conceito de pipeline em camadas não é novo. Data Warehousing dos anos 1990 já usava staging, cleansed e presentation layers. O que o Databricks criou em 2020 foi a terminologia Bronze/Silver/Gold e o branding &amp;ldquo;Medallion&amp;rdquo;, não o princípio em si. Isso não torna o padrão inválido, só ajuda a entender o que é inovação e o que é nomenclatura.&lt;/p&gt;
&lt;h2&gt;Quando Medallion funciona bem&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-medallion-funciona-bem&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-medallion-funciona-bem&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O padrão resolve três problemas reais, e resolve bem.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Primeiro: reprocessamento sem perda.&lt;/strong&gt; Quando um bug aparece na transformação Silver, você volta ao Bronze e reprocessa sem precisar buscar os dados novamente na fonte. Em sistemas onde a fonte só mantém os últimos 90 dias de histórico, essa proteção pode ser a diferença entre corrigir um problema e perder dois anos de dados.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Segundo: múltiplas equipes com necessidades diferentes.&lt;/strong&gt; O time de analytics precisa de totais por mês. O time de ciência de dados precisa do dado no menor grão para treinar o modelo. Os dois compartilham o Silver, cada um constrói sua camada Gold de forma independente. Sem duplicação do trabalho de limpeza, sem inconsistência entre as visões.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Terceiro: separação de responsabilidade em times grandes.&lt;/strong&gt; A equipe de ingestão cuida do Bronze sem precisar conhecer as regras de negócio. A equipe de transformação cuida do Silver sem depender da equipe de ingestão. Em organizações com mais de 20 profissionais de dados trabalhando em paralelo, isso reduz acoplamento e bloqueios.&lt;/p&gt;
&lt;p&gt;Quando esses três problemas existem, Medallion é uma escolha sólida. Quando não existem, você está adicionando complexidade sem contrapartida.&lt;/p&gt;
&lt;h2&gt;Onde Medallion começa a atrapalhar&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;onde-medallion-começa-a-atrapalhar&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#onde-medallion-come%c3%a7a-a-atrapalhar&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;h3&gt;Quando há um único consumidor&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-há-um-único-consumidor&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-h%c3%a1-um-%c3%banico-consumidor&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Você tem um pipeline que ingere dados de folha de pagamento para alimentar um único dashboard de RH. Uma equipe consome, uma finalidade, uma transformação.&lt;/p&gt;
&lt;p&gt;Aplicar Medallion aqui significa criar Bronze, Silver e Gold para servir exatamente a mesma coisa. O dado passa por três camadas de leitura e escrita, três conjuntos de jobs para monitorar, e três vezes a latência. Por zero ganho.&lt;/p&gt;
&lt;p&gt;O sinal prático: se a camada Gold é idêntica à Silver com um agrupamento a mais, você não precisa de três layers. Uma única transformação direta da fonte para a tabela consumida faz o mesmo trabalho com metade da infraestrutura.&lt;/p&gt;
&lt;p&gt;Um caso documentado por um arquiteto de dados: um cliente tinha 4,2 bilhões de linhas no Bronze acumuladas em seis anos de dados, mas o Silver só consumia os últimos 90 dias. 97% dos dados armazenados nunca eram usados. O custo de storage era real, o benefício não era.&lt;/p&gt;
&lt;h3&gt;Quando a latência importa mais do que a qualidade&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-a-latência-importa-mais-do-que-a-qualidade&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-a-lat%c3%aancia-importa-mais-do-que-a-qualidade&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Cada transição Bronze para Silver, Silver para Gold, é um job separado. Em pipelines com Spark, isso costuma ser 20 a 40 minutos por camada. Três camadas em sequência e a latência total passa de uma hora antes de o dado chegar em qualquer lugar.&lt;/p&gt;
&lt;p&gt;Análises com dados reais de praticantes mostram overhead de 53% ou mais em casos simples: 23 minutos com Medallion contra 15 minutos com transformação direta, para o mesmo resultado.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/12/medallion-architecture-quando-nao/images/medallion_latency_infographic.png&#34; alt=&#34;Comparativo de latência: transformação direta 15min vs Medallion 23min&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Quando o negócio precisa do dado em 30 minutos para tomar decisão, uma arquitetura com 80 minutos de latência não é um problema de código. É um problema de arquitetura.&lt;/p&gt;
&lt;p&gt;Para dados que precisam chegar em tempo real ou próximo disso, o Databricks é explícito: recomenda micro-batch (latência de segundos a poucos minutos) para Medallion, e orienta explicitamente que quando a ingestão vem de um message broker como Kafka, a leitura direta sem etapa intermediária reduz complexidade e latência. Para sub-segundo, a própria documentação aponta limitações no modo real-time que afetam negativamente o throughput.&lt;/p&gt;
&lt;h3&gt;Quando é protótipo ou análise de vida curta&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-é-protótipo-ou-análise-de-vida-curta&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-%c3%a9-prot%c3%b3tipo-ou-an%c3%a1lise-de-vida-curta&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Uma exploração rápida de dados. Um modelo que vai existir por três meses. Uma análise pontual que vai virar um número num slide e não será consumida de novo.&lt;/p&gt;
&lt;p&gt;Forçar Medallion num protótipo cria tabelas que nunca serão mantidas, jobs que ninguém vai monitorar, e estrutura que será abandonada em duas semanas. A equipe gasta tempo e energia organizando o que deveria ser descartável.&lt;/p&gt;
&lt;p&gt;Protótipo precisa ser rápido de construir e fácil de jogar fora. Três camadas dificultam as duas coisas.&lt;/p&gt;
&lt;h3&gt;Quando o time é pequeno e os dados são simples&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-o-time-é-pequeno-e-os-dados-são-simples&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-o-time-%c3%a9-pequeno-e-os-dados-s%c3%a3o-simples&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Uma startup com 3 engenheiros de dados processando 500 GB não tem os mesmos problemas que um banco com 50 engenheiros e 50 TB. O overhead operacional de manter Bronze, Silver e Gold, com todas as tabelas, jobs, documentação e monitoramento que isso exige, pode ser injustificável quando o benefício real é pequeno.&lt;/p&gt;
&lt;p&gt;Para times pequenos com um ou dois casos de uso, duas camadas (dado bruto e dado consumível) ou uma solução com dbt direto na fonte resolvem o problema sem a complexidade adicional.&lt;/p&gt;
&lt;h2&gt;O anti-padrão que ninguém comenta&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-anti-padrão-que-ninguém-comenta&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-anti-padr%c3%a3o-que-ningu%c3%a9m-comenta&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Vi um problema específico aparecer mais do que qualquer outro quando Medallion não funciona bem: a Bronze fica exposta como produto de dados.&lt;/p&gt;
&lt;p&gt;Elliott Cordo, engenheiro de dados com trabalho publicado sobre arquitetura de dados, documenta isso como anti-padrão direto: expor a camada Bronze para quem consome cria acoplamento forte entre quem usa os dados e os detalhes internos de como eles são armazenados. Quando a fonte muda, todos os consumidores quebram junto.&lt;/p&gt;
&lt;p&gt;O segundo problema documentado: quando Silver é Bronze com um campo renomeado, e Gold é Silver com um GROUP BY, as camadas intermediárias não agregam valor real. Analistas acabam escrevendo SQL complexo no Gold ou criando planilhas paralelas para compensar. Múltiplas equipes implementam a mesma métrica de formas diferentes, e os números começam a divergir.&lt;/p&gt;
&lt;p&gt;Nesses casos, o padrão não está sendo aplicado, está sendo imitado.&lt;/p&gt;
&lt;h2&gt;A pergunta certa antes de decidir&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-pergunta-certa-antes-de-decidir&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-pergunta-certa-antes-de-decidir&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Três perguntas definem se Medallion é a arquitetura certa:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Há múltiplos consumidores com necessidades diferentes?&lt;/strong&gt; Se sim, uma camada compartilhada entre eles faz sentido. Se não, você está criando separação sem benefício.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Reprocessar os dados na fonte é caro ou impossível?&lt;/strong&gt; Se sim, Bronze imutável é proteção real. Se você consegue reprocessar sem custo ou perda de histórico, o benefício diminui.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;A latência de cada camada cabe no prazo que o negócio exige?&lt;/strong&gt; Se sim, Medallion funciona. Se não, você precisa de uma arquitetura diferente para esse caso de uso.&lt;/p&gt;
&lt;p&gt;Três &amp;ldquo;sim&amp;rdquo;: Medallion é uma escolha sólida. Dois ou menos: vale questionar quantas camadas você realmente precisa.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/12/medallion-architecture-quando-nao/images/decision-tree.png&#34; alt=&#34;Diagrama de decisão: quando usar Medallion Architecture&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;O que empresas grandes usam na prática&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-empresas-grandes-usam-na-prática&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-empresas-grandes-usam-na-pr%c3%a1tica&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Um detalhe importante que raramente aparece nas discussões: Netflix e Uber, duas das empresas mais referenciadas em engenharia de dados, não usam a terminologia Bronze/Silver/Gold.&lt;/p&gt;
&lt;p&gt;A Netflix usa o padrão WAP (Write-Audit-Publish) com Apache Iceberg: o dado é escrito em snapshot oculto, auditado automaticamente, publicado se aprovado. O problema que resolve é o mesmo (qualidade antes da exposição), mas a implementação é diferente e não usa as três camadas do Medallion.&lt;/p&gt;
&lt;p&gt;O Uber usa um data lake transacional com Apache Hudi, com tabelas raw, derivadas e agregadas. A migração de batch completo para incremental ETL reduziu o tempo de pipeline em 82% e o custo em 78%, segundo o Uber Engineering Blog de março de 2023. Mas esses números são do incremental ETL, não do padrão de camadas em si.&lt;/p&gt;
&lt;p&gt;A Microsoft adotou Medallion como arquitetura oficial do Fabric em 2023 e é hoje o maior case público de adoção institucional. Ainda assim, a documentação da própria Microsoft orienta: antes de construir pipelines complexos entre camadas, avalie Materialized Lake Views, que gerenciam as transformações automaticamente sem overhead operacional.&lt;/p&gt;
&lt;h2&gt;O que fica&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-fica&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-fica&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Medallion Architecture é um padrão bom para os problemas certos: times grandes, múltiplos consumidores, dados críticos que precisam de histórico protegido e qualidade progressiva.&lt;/p&gt;
&lt;p&gt;Não é obrigatório. Não é universal. E quando aplicado onde não cabe, o custo é real: latência desnecessária, storage desperdiçado, complexidade operacional sem benefício.&lt;/p&gt;
&lt;p&gt;A escolha da arquitetura deveria começar pelo problema, não pelo padrão. O que esse pipeline precisa resolver? Quem vai consumir? Qual é o prazo aceitável? Reprocessar da fonte é caro?&lt;/p&gt;
&lt;p&gt;Se as respostas apontam para Medallion, ótimo. Se não apontam, uma arquitetura mais simples vai resolver melhor.&lt;/p&gt;
&lt;p&gt;Você já implementou Medallion num lugar que não precisava? O que aconteceu depois? Me conta no &lt;a href=&#34;https://linkedin.com/in/thaisvaz&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;LinkedIn&lt;/a&gt; ou assina a &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;newsletter&lt;/a&gt; para receber os próximos posts.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>LGPD e modelos de ML: o que fazer com dados que já viraram pesos de modelo</title>
      <link>https://vazdeng.pages.dev/2026/05/02/lgpd-ml-modelos-treinados/</link>
      <pubDate>Sat, 02 May 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/05/02/lgpd-ml-modelos-treinados/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/05/02/lgpd-ml-modelos-treinados/cover_hu_739f7067a1194401.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Um titular pediu exclusão dos dados. Você deletou a linha do banco. E o modelo?&lt;/p&gt;
&lt;p&gt;Os pesos de um modelo de ML treinado com dados pessoais guardam, de forma não explícita, a contribuição de cada registro de treino. Deletar o dado original não apaga essa influência. Pesquisas de membership inference conseguem, com alguma probabilidade, determinar se um CPF específico fez parte do dataset de treino de um modelo. Isso é dado pessoal sob a LGPD.&lt;/p&gt;
&lt;p&gt;Vi a maioria dos times sem processo para esse cenário. Não por falta de intenção: ninguém estabeleceu o fluxo antes de treinar o primeiro modelo.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/02/lgpd-ml-modelos-treinados/images/01-deletar-nao-apaga.png&#34; alt=&#34;Deletar a linha não apaga o modelo: o CPF entra no treino, a influência continua nos pesos em produção, e o Art. 18 alcança o modelo&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;O que o Art. 18 efetivamente exige&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-o-art-18-efetivamente-exige&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-o-art-18-efetivamente-exige&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O Art. 18, IV da LGPD garante ao titular o direito de solicitar anonimização, bloqueio ou eliminação de dados &amp;ldquo;desnecessários, excessivos ou tratados em desconformidade&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;A interpretação que a ANPD vem sinalizando em suas consultas públicas sobre IA é que modelos de ML são processadores de dados pessoais quando os dados de treino eram pessoais no momento do tratamento. O modelo em produção herda essa classificação.&lt;/p&gt;
&lt;p&gt;Se um titular pediu exclusão e você consegue demonstrar que os dados dele foram usados no treino, o direito ao apagamento se aplica ao modelo também. Não só ao dataset.&lt;/p&gt;
&lt;p&gt;A lei não especifica como executar esse apagamento. Ela especifica o resultado esperado: o titular não deve mais ter influência sobre as decisões do modelo. Como você chega lá é problema técnico seu.&lt;/p&gt;
&lt;h2&gt;O problema técnico real&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-problema-técnico-real&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-problema-t%c3%a9cnico-real&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Três cenários que vi na prática, com dificuldades diferentes.&lt;/p&gt;
&lt;p&gt;Dados genuinamente anonimizados antes do treino: se você aplicou anonimização real, não pseudonimização, antes de qualquer processamento de ML, está fora do escopo da LGPD para esse dado. O Art. 12 é claro: dado anonimizado não é dado pessoal. Mas anonimização precisa ser irreversível. K-anonymity com k=3 em transações financeiras não é anonimização real.&lt;/p&gt;
&lt;p&gt;Dados pseudonimizados no treino: você substituiu o CPF por um token, mas manteve o mapeamento. O dado continua sendo pessoal. O modelo foi treinado com esse dado e agora está em produção. Um pedido de exclusão ativa o problema completo.&lt;/p&gt;
&lt;p&gt;Dados brutos no treino, sem tratamento: o cenário mais comum em modelos mais antigos, treinados antes de qualquer preocupação regulatória. Também o mais difícil de resolver.&lt;/p&gt;
&lt;h2&gt;O que os times fazem na prática&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-os-times-fazem-na-prática&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-os-times-fazem-na-pr%c3%a1tica&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Três abordagens que uso de referência, com trade-offs reais, nenhuma gratuita.&lt;/p&gt;
&lt;p&gt;Retraining completo sem o dado: você remove o registro do dataset, retreina do zero ou a partir de um checkpoint anterior. É a abordagem mais limpa juridicamente, a mais defensável numa auditoria, e a mais cara computacionalmente. Para modelos que levam semanas para treinar, é impraticável como resposta rotineira.&lt;/p&gt;
&lt;p&gt;Machine unlearning seletivo: técnicas que tentam remover a influência de registros específicos sem retreinamento completo. SISA training (Sharded, Isolated, Sliced, Aggregated) e gradient-based unlearning reduzem o custo. O problema: a maioria das implementações em produção ainda não tem certificação formal de que o apagamento foi efetivo. Numa disputa com a ANPD, &amp;ldquo;usamos machine unlearning&amp;rdquo; sem evidência mensurável não resolve.&lt;/p&gt;
&lt;p&gt;Documentar a impraticabilidade e mitigar o risco: a LGPD permite, em alguns casos, a continuidade do tratamento quando o apagamento é impossível e existe base legal residual. Documentar que o modelo foi treinado com dados que à época tinham base legal, que o retraining é tecnicamente inviável, e que medidas de mitigação foram implementadas pode ser a resposta juridicamente defensável. Isso precisa de opinião jurídica, não só técnica.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/02/lgpd-ml-modelos-treinados/images/02-tres-respostas.png&#34; alt=&#34;Três respostas, nenhuma gratuita: retraining completo, machine unlearning seletivo, documentar a impraticabilidade, prós e contras de cada uma&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;Como arquitetar antes de treinar&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;como-arquitetar-antes-de-treinar&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#como-arquitetar-antes-de-treinar&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O momento certo para resolver isso é antes do primeiro modelo ir para produção, não depois da primeira requisição de exclusão.&lt;/p&gt;
&lt;p&gt;Versionamento de datasets por titulares: manter um índice de quais registros foram usados em qual versão de treino. Sem esse índice, você nem sabe quais modelos precisam de ação quando um titular pede exclusão.&lt;/p&gt;
&lt;p&gt;Separação de dados de treino por consentimento: se parte do dataset veio de consentimento explícito e parte de legítimo interesse, trate como datasets separados desde o início. Quando o consentimento for revogado, você sabe exatamente qual subconjunto está comprometido.&lt;/p&gt;
&lt;p&gt;Checkpoints rotulados por composição de dataset: se você usa treinamento modular, mantenha os checkpoints com metadados sobre quais shards foram usados. Isso reduz o custo de retraining seletivo de semanas para horas.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/05/02/lgpd-ml-modelos-treinados/images/03-antes-de-treinar.png&#34; alt=&#34;Arquitete antes do primeiro treino: versionamento de dataset por titular, datasets separados por base legal, checkpoints rotulados por composição, retraining cai de semanas pra horas&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;A decisão que todo time vai precisar tomar&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-decisão-que-todo-time-vai-precisar-tomar&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-decis%c3%a3o-que-todo-time-vai-precisar-tomar&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O cenário vai aparecer: um titular envia uma requisição de exclusão, você deleta o dado, e alguém pergunta o que fazer com o modelo de score de crédito que usou esse CPF no treino.&lt;/p&gt;
&lt;p&gt;A resposta honesta hoje é: depende de qual modelo, quando foi treinado, como o dataset foi gerenciado, e qual é a base legal original do tratamento.&lt;/p&gt;
&lt;p&gt;O que não é mais aceitável é não ter a resposta. A ANPD está construindo sua posição sobre IA e LGPD. Os times que já documentaram suas decisões arquiteturais vão estar em posição muito melhor do que os que estão improvisando quando a orientação chegar.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Delta Lake ou Parquet? Você está fazendo a pergunta errada</title>
      <link>https://vazdeng.pages.dev/2026/04/30/delta-lake-vs-parquet/</link>
      <pubDate>Thu, 30 Apr 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/04/30/delta-lake-vs-parquet/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/04/30/delta-lake-vs-parquet/cover_hu_481920f272d5ed99.png" width="640" height="336"/>]]>
        
        &lt;p&gt;No Slack do meu time aparece toda semana: &amp;ldquo;deve usar Delta Lake ou Parquet?&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Delta Lake não é um formato de arquivo concorrente ao Parquet. É uma camada de gerenciamento transacional que armazena os dados em arquivos Parquet. Você não está escolhendo entre dois formatos. Está decidindo se precisa de uma camada transacional por cima dos seus arquivos.&lt;/p&gt;
&lt;p&gt;Essa distinção muda o critério de decisão completamente. E confundir os dois em produção custa caro.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/04/30/delta-lake-vs-parquet/images/01-camada-nao-formato.png&#34; alt=&#34;Delta Lake não é um formato: é o _delta_log transacional por cima dos seus arquivos Parquet, as duas camadas juntas&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;O que o Parquet não faz&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-o-parquet-não-faz&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-o-parquet-n%c3%a3o-faz&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Parquet resolve um problema específico muito bem: armazenar dados de forma colunar, comprimida, eficiente para leitura analítica. É o formato certo para isso.&lt;/p&gt;
&lt;p&gt;O que Parquet não faz: controle de concorrência. Se dois jobs escrevem na mesma partição ao mesmo tempo, o resultado é não-determinístico. Sem transação, sem rollback, sem detecção de conflito. O arquivo que chegou por último vence. O outro desaparece.&lt;/p&gt;
&lt;p&gt;Numa fintech onde trabalhei, com pipelines de ingestão distribuídos, isso não era teórico. Era o cenário padrão toda vez que um job de streaming e um job de backfill rodavam juntos na mesma tabela.&lt;/p&gt;
&lt;p&gt;Em pipelines com job de streaming e backfill simultâneos esse cenário aparece sem aviso. O sintoma é sutil: contagem de linhas correta, valores que divergem do dia anterior sem nenhum erro no log. O último writer sobrescreveu o anterior. Silencioso e sem rollback.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/04/30/delta-lake-vs-parquet/images/02-corrupcao-silenciosa.png&#34; alt=&#34;Corrupção silenciosa em Parquet puro: streaming e backfill na mesma partição, resultado não-determinístico, zero erros no log&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;O que o Delta Lake adiciona&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-o-delta-lake-adiciona&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-o-delta-lake-adiciona&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Delta Lake resolve o problema de concorrência com o &lt;code&gt;_delta_log&lt;/code&gt;: um diretório de commits JSON e checkpoints Parquet que registra cada transação. Todo writer registra o que adicionou, o que removeu e a versão resultante. Leitores veem estados consistentes, nunca parciais.&lt;/p&gt;
&lt;p&gt;Isso habilita quatro capacidades que Parquet puro não tem:&lt;/p&gt;
&lt;p&gt;Operações UPDATE, DELETE e MERGE sem reescrever a tabela inteira. O Delta marca os arquivos afetados como removidos e adiciona novos. O dado antigo fica acessível via time travel (&lt;code&gt;SELECT * FROM tabela VERSION AS OF 10&lt;/code&gt;), mas não aparece nas consultas correntes.&lt;/p&gt;
&lt;p&gt;Schema enforcement. Se um pipeline tenta escrever uma coluna com tipo incompatível, a escrita falha antes de contaminar a tabela. Com Parquet puro, você descobre o problema no consumidor, não na fonte.&lt;/p&gt;
&lt;p&gt;Compactação controlada via &lt;code&gt;OPTIMIZE&lt;/code&gt;. Ingestões de streaming geram dezenas de pequenos arquivos por hora. O Delta consolida esses fragmentos sem downtime, mantendo o log de transações intacto.&lt;/p&gt;
&lt;p&gt;Data skipping usando estatísticas min/max por arquivo. Numa tabela de 2 TB com 10 mil arquivos Parquet, uma query filtrada por data precisa abrir potencialmente todos os arquivos para checar os metadados. O Delta mantém min/max de cada coluna no log e pula arquivos inteiros sem leitura.&lt;/p&gt;
&lt;h2&gt;Quando Delta Lake é excessivo&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quando-delta-lake-é-excessivo&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quando-delta-lake-%c3%a9-excessivo&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Delta Lake tem custo. O &lt;code&gt;_delta_log&lt;/code&gt; adiciona overhead em escritas pequenas. O checkpoint é gerado a cada 10 commits por padrão. Para datasets imutáveis, o custo não tem contrapartida.&lt;/p&gt;
&lt;p&gt;Três cenários onde Parquet é a escolha certa:&lt;/p&gt;
&lt;p&gt;Datasets de referência que nunca mudam. Tabelas de código BACEN, tabelas de calendário, dados históricos selados após processamento. Nenhum escritor concorrente, nenhum update. Parquet direto, sem overhead de log.&lt;/p&gt;
&lt;p&gt;Pipelines de exportação para sistemas externos. Você está gerando arquivos para enviar a um parceiro, um sistema legado, ou um bucket S3 consumido por ferramenta que não lê Delta. Parquet é o padrão de interoperabilidade.&lt;/p&gt;
&lt;p&gt;Experimentos e dados efêmeros. Um notebook de análise que lê um arquivo CSV e salva o resultado. Não precisa de controle de versão nem de transação. O overhead do Delta não agrega nada aqui.&lt;/p&gt;
&lt;h2&gt;A decisão em três perguntas&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-decisão-em-três-perguntas&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-decis%c3%a3o-em-tr%c3%aas-perguntas&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Antes de escolher o formato, responda:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;Mais de um processo escreve nessa tabela ao mesmo tempo, ou vai escrever no futuro? Se sim, Delta Lake.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Os dados são atualizados, deletados ou têm requisito de auditoria? Se sim, Delta Lake.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;A tabela é consumida apenas por leitura e nunca muda depois de escrita? Parquet é suficiente.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;A maioria das tabelas operacionais em um lakehouse produtivo responde &amp;ldquo;sim&amp;rdquo; para a primeira ou segunda pergunta. A maioria das tabelas de lookup responde &amp;ldquo;sim&amp;rdquo; para a terceira.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/04/30/delta-lake-vs-parquet/images/03-decisao-3-perguntas.png&#34; alt=&#34;A decisão em 3 perguntas: escrita concorrente ou update/auditoria levam a Delta Lake; tabela só de leitura fica em Parquet&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;No contexto de compliance com o BACEN 521, que entra em vigor em outubro de 2026, tabelas de auditoria de transações financeiras precisam de time travel e schema enforcement. Usar Parquet puro nessas tabelas não é só ineficiente. É um risco regulatório.&lt;/p&gt;
&lt;h2&gt;A decisão arquitetural real&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-decisão-arquitetural-real&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-decis%c3%a3o-arquitetural-real&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Delta Lake não é uma versão melhorada do Parquet. É uma camada diferente que resolve um problema diferente.&lt;/p&gt;
&lt;p&gt;O Parquet resolve: como armazenar dados de forma eficiente para leitura analítica.&lt;/p&gt;
&lt;p&gt;O Delta Lake resolve: como garantir consistência quando múltiplos processos acessam o mesmo dado ao mesmo tempo.&lt;/p&gt;
&lt;p&gt;A pergunta certa não é &amp;ldquo;qual formato usar&amp;rdquo;. É &amp;ldquo;esse dado precisa de controle transacional?&amp;rdquo; Se precisar, Delta Lake. Se não precisar, Parquet. Passei pelas duas direções em projetos diferentes. O errado custou caro nos dois lados.&lt;/p&gt;
&lt;p&gt;Se você já encontrou corrupção silenciosa por concorrência em Parquet, ou se optou por Delta em algo que depois pareceu excessivo, conta nos comentários qual foi o contexto.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Sharpe -1.14 é sucesso de engenharia, não fracasso</title>
      <link>https://vazdeng.pages.dev/2026/04/23/sharpe-negativo-sucesso-engenharia/</link>
      <pubDate>Thu, 23 Apr 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/04/23/sharpe-negativo-sucesso-engenharia/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/04/23/sharpe-negativo-sucesso-engenharia/cover_hu_ccaca7f9ebe447b3.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Por 6 meses, construí um agente quant para trading de BTC/USDT.&lt;/p&gt;
&lt;p&gt;Objetivo: maximizar retorno.&lt;/p&gt;
&lt;p&gt;Resultado: Sharpe ratio de &lt;strong&gt;-1.14&lt;/strong&gt;. Não é bom.&lt;/p&gt;
&lt;p&gt;O sistema não fracassou. Fracassou em um objetivo (alpha) e se saiu bem em outro (capital preservation).&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;A arquitetura por camadas&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;a-arquitetura-por-camadas&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#a-arquitetura-por-camadas&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Quant trading é complexo. Não é &amp;ldquo;compre aqui, venda ali&amp;rdquo;. É isso:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;L1: Ingestion        (dados de verdade)
L2: Processing       (sinais)
L3: Intelligence     (previsões)
L4: Decision         (sizing)
L5: Execution        (minimizar impacto)
L6: Evaluation       (backtests)
L7: Compliance       (auditoria)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Cada camada é independente. Cada uma tem fallbacks.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/04/23/sharpe-negativo-sucesso-engenharia/images/01-sete-camadas.png&#34; alt=&#34;As 7 camadas do agente quant: ingestion, processing, intelligence, decision, execution, evaluation e compliance, cada uma independente e com fallback&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h3&gt;L1: Ingestion&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;l1-ingestion&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#l1-ingestion&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;- BinanceFetcher: OHLCV, funding rates, open interest, order book
- MacroFetcher: DXY, S&amp;amp;P 500 via yfinance
- GlassnodeFetcher: on-chain metrics&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Por que 3 fontes?&lt;/strong&gt; Triangulação. Se Binance cai, você continua com macro + on-chain.&lt;/p&gt;
&lt;h3&gt;L2: Processing&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;l2-processing&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#l2-processing&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;32&amp;#43; indicadores técnicos:
- RSI, MACD, Bollinger Bands (clássicos)
- ATR, Stochastic, Williams %R (volatilidade)
- Volume profile, Time-weighted moving average
- On-chain: MVRV, SOPR, Cumulative delta
- Macro: VIX-like crypto index

Tudo normalizado (z-score, min-max).
Tudo alinhado temporalmente (sem forward-looking bias).&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;L3: Intelligence&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;l3-intelligence&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#l3-intelligence&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Gaussian HMM (Hidden Markov Model) com 3 estados:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;BULL (uptrend)    → RSI &amp;gt; 60 &amp;#43; momentum &amp;#43; macro positive
SIDEWAYS (range)  → RSI 40-60 &amp;#43; low volatility
BEAR (downtrend)  → RSI &amp;lt; 40 &amp;#43; momentum negative&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;LightGBM regressor prediz retornos nos próximos 4 candles (walk-forward).&lt;/p&gt;
&lt;p&gt;Você não precisa de accuracy 60% pra ter alpha. Precisa de &lt;em&gt;consistency&lt;/em&gt;. Um modelo que acerta 45% das vezes mas com low drawdown supera um modelo que acerta 70% com 30% max DD.&lt;/p&gt;
&lt;h3&gt;L4: Decision&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;l4-decision&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#l4-decision&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Quarter Kelly sizing. Não full Kelly (agressivo demais).&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;Position size = (edge * odds) / odds_ratio
Capped at 2% of portfolio (max risk per trade)

Guardrails (inegociáveis):
- Max drawdown: 15%
- Circuit breaker: 3 consecutive losses = pausa
- Kill switch: manual override sempre disponível&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;L5: Execution&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;l5-execution&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#l5-execution&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Almgren-Chriss (minimizar market impact):&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;Não execute 100% em 1 candle.
Quebre em 5-10 pequenas ordens.
Use TWAP/VWAP pra timing melhor.
Cheque liquidez antes de cada ordem.&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;L6: Evaluation&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;l6-evaluation&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#l6-evaluation&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Walk-forward backtesting (não data leakage):&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;Train window: 60 days
Test window: 5 days
Roll forward: shift 5 days, repeat

Métricas:
- Sharpe, Sortino, Calmar ratios
- Max drawdown
- Win rate
- Recovery factor&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;L7: Compliance&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;l7-compliance&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#l7-compliance&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;- KillSwitch thread-safe (emergência)
- Auditor append-only em JSONL (irrevogável)
- Telegram notifications (alertas em tempo real)
- 202 testes (Python, pytest)
- CI/CD (GitHub Actions)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;O insight:&lt;/strong&gt; Engenharia de quant não é &amp;ldquo;acertar previsões&amp;rdquo;. É construir um &lt;strong&gt;sistema&lt;/strong&gt; testado, auditável, que falha com graça (drawdown mínimo).&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;O Bug Que Revelou Tudo&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-bug-que-revelou-tudo&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-bug-que-revelou-tudo&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Inicialmente, o Sharpe era &lt;strong&gt;+0.66&lt;/strong&gt;. Parecia bom.&lt;/p&gt;
&lt;p&gt;Então encontrei &lt;strong&gt;data leakage no HMM&lt;/strong&gt;: o modelo via o futuro durante treinamento.&lt;/p&gt;
&lt;p&gt;Um simples descuido:&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;# WRONG: treina com dados inteiros (future data vaza)
hmm.fit(all_indicators)

# RIGHT: treina apenas com passado até data T
hmm.fit(indicators_until_date_T)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Ao corrigir: Sharpe caiu para &lt;strong&gt;-1.14&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;Esse momento foi crucial: &lt;strong&gt;real &amp;raquo; espúrio&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/04/23/sharpe-negativo-sucesso-engenharia/images/02-leakage-sharpe.png&#34; alt=&#34;Data leakage: Sharpe espúrio de &amp;#43;0.66 com o modelo vendo o futuro vira -1.14 real depois de corrigir uma linha do fit&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Eu poderia ter:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Ignorado o bug e lançado o sistema (risco: fraude)&lt;/li&gt;
&lt;li&gt;Abandonado o projeto (risco: oportunidade de aprendizado perdida)&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Em vez disso, documentei a correção, refiz os testes, e fiz a pergunta certa: &amp;ldquo;O que este sistema &lt;em&gt;realmente&lt;/em&gt; resolve?&amp;rdquo;&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;O Tradeoff: Alpha vs Preservação de Capital&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-tradeoff-alpha-vs-preservação-de-capital&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-tradeoff-alpha-vs-preserva%c3%a7%c3%a3o-de-capital&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Vamos aos números (out-of-sample, walk-forward):&lt;/p&gt;
&lt;table&gt;
  &lt;thead&gt;
      &lt;tr&gt;
          &lt;th&gt;Métrica&lt;/th&gt;
          &lt;th&gt;Agente Quant&lt;/th&gt;
          &lt;th&gt;Buy &amp;amp; Hold&lt;/th&gt;
      &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
      &lt;tr&gt;
          &lt;td&gt;Sharpe ratio&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;-1.14&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;-0.04&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Max drawdown&lt;/td&gt;
          &lt;td&gt;&lt;strong&gt;0.29%&lt;/strong&gt;&lt;/td&gt;
          &lt;td&gt;26.24%&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
          &lt;td&gt;Win rate&lt;/td&gt;
          &lt;td&gt;1/7 windows&lt;/td&gt;
          &lt;td&gt;4/7 windows&lt;/td&gt;
      &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;Leia isso novamente.&lt;/p&gt;
&lt;p&gt;Agente não tem alpha. Mas reduz drawdown em &lt;strong&gt;~90x&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/04/23/sharpe-negativo-sucesso-engenharia/images/03-drawdown-90x.png&#34; alt=&#34;Max drawdown out-of-sample: buy &amp; hold 26,24% contra 0,29% do agente quant, 90x menos drawdown, preservação de capital acima de alpha&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;Pergunte-se: em qual cenário você preferiria estar?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cenário 1:&lt;/strong&gt; Você compra e segura (Buy &amp;amp; Hold). Em um ano, há 1 dia onde você perde 26% de tudo. Dia seguinte, você recupera 15%. Você dorme?&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Cenário 2:&lt;/strong&gt; Você tá no agente. Max loss é 0.29% em qualquer dia. Você dorme melhor.&lt;/p&gt;
&lt;p&gt;Preservação de capital &amp;gt; busca por alpha.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Framework vs Resultado&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;framework-vs-resultado&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#framework-vs-resultado&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O código não &amp;ldquo;falhou&amp;rdquo;. O código &lt;em&gt;resolveu um problema diferente&lt;/em&gt; do planejado.&lt;/p&gt;
&lt;p&gt;Systems thinking:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Objetivo inicial:&lt;/strong&gt; Gerar retorno positivo (alpha)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Problema descoberto:&lt;/strong&gt; Alpha é raro (até pros profissionais)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Solução emergente:&lt;/strong&gt; Risk management é consistente&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Resultado real:&lt;/strong&gt; Um sistema de preservação de capital&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Às vezes, falhar no objetivo original é a forma que o universo tem de te mostrar o verdadeiro objetivo.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;O Stack Técnico&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-stack-técnico&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-stack-t%c3%a9cnico&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Para devs, aqui está o que funcionou:&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;O que funcionou:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Python + SQLAlchemy (ORM robusto)&lt;/li&gt;
&lt;li&gt;asyncio (concorrência real, non-blocking I/O)&lt;/li&gt;
&lt;li&gt;pytest (202 testes passando)&lt;/li&gt;
&lt;li&gt;Postgres (auditoria append-only, compliance)&lt;/li&gt;
&lt;li&gt;Task Scheduler do Windows (low-cost orchestration)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;O que foi desafiador:&lt;/strong&gt;&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;HMM em dados não-estacionários (quant é &lt;em&gt;hard&lt;/em&gt;)&lt;/li&gt;
&lt;li&gt;Market microstructure (Almgren-Chriss é complexo)&lt;/li&gt;
&lt;li&gt;Real-time data latency (lag = slippage real)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;Stack final:&lt;/strong&gt;&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;Data ingestion:  Binance API &amp;#43; Glassnode &amp;#43; yfinance
ML stack:        scikit-learn (HMM), LightGBM (regressão)
Backend:         FastAPI (opcional, current: local scheduler)
Database:        Postgres 16 &amp;#43; JSONL audit trail
Notifications:   Telegram bot &amp;#43; Discord webhook
Infrastructure:  VPS barato (1 vCPU, 4GB RAM, 50GB NVMe)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Tudo roda em &lt;strong&gt;uma máquina barata&lt;/strong&gt;. Sem Kubernetes, sem AWS bill assustador.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;Lições duradouras&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;lições-duradouras&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#li%c3%a7%c3%b5es-duradouras&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;h3&gt;1. Testes Primeiro (TDD)&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;1-testes-primeiro-tdd&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#1-testes-primeiro-tdd&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;202 testes = confiança. Você refatora sem medo.&lt;/p&gt;
&lt;p&gt;Sem testes? Falhas silenciosas. Você descobre em produção.&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;pre&gt;&lt;code&gt;Cada feature tem teste associado:
- test_hmmpredict.py (validação do modelo)
- test_kelly_sizing.py (risk management)
- test_market_impact.py (execution)
- test_audit_trail.py (compliance)&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;h3&gt;2. Auditoria é Design&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;2-auditoria-é-design&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#2-auditoria-%c3%a9-design&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;JSONL append-only logs me salvaram quando questionei resultados.&lt;/p&gt;
&lt;div class=&#34;hextra-code-block hx:relative hx:mt-6 hx:first:mt-0 hx:group/code&#34;&gt;

&lt;div&gt;&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-json&#34; data-lang=&#34;json&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2026-04-22T10:30:00&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;BUY&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;size&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.05&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;price&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;65000&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;reason&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;BULL_regime_high_momentum&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;p&#34;&gt;{&lt;/span&gt;&lt;span class=&#34;nt&#34;&gt;&amp;#34;timestamp&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;2026-04-22T11:45:00&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;action&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;s2&#34;&gt;&amp;#34;CLOSE&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;pnl&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;50&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nt&#34;&gt;&amp;#34;drawdown&amp;#34;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;:&lt;/span&gt; &lt;span class=&#34;mf&#34;&gt;0.0015&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;div class=&#34;hextra-code-copy-btn-container hx:opacity-0 hx:transition hx:group-hover/code:opacity-100 hx:flex hx:gap-1 hx:absolute hx:m-[11px] hx:right-0 hx:top-0&#34;&gt;
  &lt;button
    class=&#34;hextra-code-copy-btn hx:group/copybtn hx:cursor-pointer hx:transition-all hx:active:opacity-50 hx:bg-primary-700/5 hx:border hx:border-black/5 hx:text-gray-600 hx:hover:text-gray-900 hx:rounded-md hx:p-1.5 hx:dark:bg-primary-300/10 hx:dark:border-white/10 hx:dark:text-gray-400 hx:dark:hover:text-gray-50&#34;
    title=&#34;Copiar código&#34;
    aria-label=&#34;Copiar código&#34;
    data-copied-label=&#34;Copiado!&#34;
  &gt;
    &lt;div class=&#34;hextra-copy-icon hx:group-[.copied]/copybtn:hidden hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
&lt;div class=&#34;hextra-success-icon hx:hidden hx:group-[.copied]/copybtn:block hx:pointer-events-none hx:h-4 hx:w-4&#34;&gt;&lt;/div&gt;
  &lt;/button&gt;
&lt;/div&gt;
&lt;/div&gt;
&lt;p&gt;Você pode rastrear &lt;em&gt;por que&lt;/em&gt; cada decisão foi tomada.&lt;/p&gt;
&lt;h3&gt;3. Constraints Geram Inovação&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;3-constraints-geram-inovação&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#3-constraints-geram-inova%c3%a7%c3%a3o&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Quarter Kelly sizing é mais conservador que full Kelly. Mas foi mais efetivo.&lt;/p&gt;
&lt;p&gt;Constraints (2% max risk, 15% max DD) obrigaram criatividade na decisão.&lt;/p&gt;
&lt;p&gt;Livre demais = overfitting.&lt;/p&gt;
&lt;h3&gt;4. Real-time é Diferente de Backtesting&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;4-real-time-é-diferente-de-backtesting&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#4-real-time-%c3%a9-diferente-de-backtesting&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Walk-forward validation previne surpresas.&lt;/p&gt;
&lt;p&gt;Seu modelo pode ter 70% de accuracy no backtest, mas em produção? 45%. Por quê?&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Slippage (você não pega o preço exato)&lt;/li&gt;
&lt;li&gt;Latência (0.5s de delay = preço diferente)&lt;/li&gt;
&lt;li&gt;Spread (bid/ask widening em volatilidade)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Real-time não perdoa.&lt;/p&gt;
&lt;h3&gt;5. Falhar é Learning&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;5-falhar-é-learning&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#5-falhar-%c3%a9-learning&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Data leakage (-1.14 vs +0.66) foi a descoberta mais valiosa.&lt;/p&gt;
&lt;p&gt;Correção daquele bug = aprendi mais do que 10 livros sobre quant.&lt;/p&gt;
&lt;p&gt;Não tenha medo de &amp;ldquo;falhas&amp;rdquo; que educam.&lt;/p&gt;
&lt;h3&gt;6. Simplicidade &amp;gt; Complexidade&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;6-simplicidade--complexidade&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#6-simplicidade--complexidade&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;3 estados no HMM funcionou melhor que 10+ features.&lt;/p&gt;
&lt;p&gt;6 meses construindo. Resultado: simples.&lt;/p&gt;
&lt;p&gt;Inversão de tempo: 95% construindo, 5% simplificando. Mas aqueles 5% = o código que realmente roda em produção.&lt;/p&gt;
&lt;h3&gt;7. Preservação de Capital &amp;gt; Busca por Alpha&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;7-preservação-de-capital--busca-por-alpha&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#7-preserva%c3%a7%c3%a3o-de-capital--busca-por-alpha&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h3&gt;&lt;p&gt;Seu objetivo deve ser: &amp;ldquo;Não perder dinheiro.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Alpha (extra retorno) é bônus.&lt;/p&gt;
&lt;p&gt;A maioria dos quants inverte: &amp;ldquo;Busco alpha, tolero perda.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;Errado.&lt;/p&gt;
&lt;hr&gt;
&lt;h2&gt;O Que Vem Depois&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-vem-depois&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-vem-depois&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Este agente não vai gerar riqueza da noite para o dia.&lt;/p&gt;
&lt;p&gt;(Se alguém prometer isso, corre.)&lt;/p&gt;
&lt;p&gt;Mas ele resolve um problema real:&lt;/p&gt;
&lt;blockquote&gt;
  &lt;p&gt;&amp;ldquo;Como construir um sistema robusto de decisão em Python?&amp;rdquo;&lt;/p&gt;

&lt;/blockquote&gt;
&lt;p&gt;Próximos passos para você:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;O código:&lt;/strong&gt; projeto fechado por enquanto. A arquitetura descrita acima (HMM + LightGBM + Kelly + HRP, separação treino/produção, evento-baseado vs polling) é o que importa pra replicar a abordagem.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Adapte:&lt;/strong&gt; Para stocks, commodities, cripto (framework é agnóstico)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Realize:&lt;/strong&gt; Quão difícil é quant. Respeite quem faz bem.&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2&gt;Qual É a Sua Métrica?&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;qual-é-a-sua-métrica&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#qual-%c3%a9-a-sua-m%c3%a9trica&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Sharpe é útil. Mas talvez você otimize para outra coisa:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Máxima riqueza em tempo mínimo?&lt;/strong&gt; (tempo alocado)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mínimo drawdown?&lt;/strong&gt; (paz de espírito)&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mínimo capital needed?&lt;/strong&gt; (acessibilidade)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Escolha sua métrica. Construa para ela. Valide com dados reais.&lt;/p&gt;
&lt;p&gt;Não a escolha dele. Não a moda. A sua.&lt;/p&gt;
&lt;hr&gt;
&lt;p&gt;Sharpe de -1.14 é um fracasso de marketing. Mas é um sucesso de engenharia.&lt;/p&gt;
&lt;p&gt;Se o objetivo era aprender a construir um sistema robusto, testado, auditável, escalável, missão cumprida.&lt;/p&gt;
&lt;p&gt;O próximo objetivo é seu.&lt;/p&gt;
&lt;p&gt;Responde no &lt;a href=&#34;https://linkedin.com/in/thacvaz&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;LinkedIn&lt;/a&gt; ou assina a &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;newsletter no Substack&lt;/a&gt; pra receber os próximos posts.&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>Engenharia de dados em português de verdade é rara. Vou ajudar a mudar isso.</title>
      <link>https://vazdeng.pages.dev/2026/04/18/engenharia-dados-em-portugues/</link>
      <pubDate>Sat, 18 Apr 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/04/18/engenharia-dados-em-portugues/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/04/18/engenharia-dados-em-portugues/cover_hu_70018923dc641386.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Conteúdo de engenharia de dados em português, daquele tipo que você abre e sente que a pessoa viveu o que está escrevendo, é raro de achar.&lt;/p&gt;
&lt;p&gt;Procura agora. Você vai encontrar muito material de qualidade pra começar: traduções de artigos gringos, tutoriais que partem da documentação oficial, cursos de Pandas com datasets simples. Tudo isso tem espaço, é por onde muita gente começa, e quem produz esse material está fazendo um trabalho importante.&lt;/p&gt;
&lt;p&gt;O que ainda é difícil de achar é alguém te contando como decidiu usar Delta Lake em vez de Parquet num ambiente que processa centenas de milhões de transações por dia. Ou em quais momentos a Medallion Architecture ajuda e em quais ela só atrapalha. Ou como a LGPD muda, na prática, a forma como você desenha uma camada de ingestão.&lt;/p&gt;
&lt;p&gt;É esse pedaço que eu quero ajudar a preencher.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/04/18/engenharia-dados-em-portugues/bookshelf.png&#34; alt=&#34;Estante de livros vazia rotulada “Data engineering · Português” com uma silhueta colocando o primeiro livro&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;h2&gt;Quem sou eu pra dizer isso&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;quem-sou-eu-pra-dizer-isso&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#quem-sou-eu-pra-dizer-isso&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Não vou listar certificados. Vou te contar o que construí.&lt;/p&gt;
&lt;p&gt;Sou engenheira de dados sênior há mais de 8 anos. Comecei em qualidade de dados num grande banco brasileiro, passei por uma fintech brasileira em escala global construindo pipelines ETL, atuei em consultoria internacional num projeto de big tech em Silicon Valley, e hoje atuo em outro grande banco brasileiro. (Currículo completo na &lt;a href=&#34;https://vazdeng.pages.dev/sobre/&#34;&gt;página /sobre/&lt;/a&gt;.)&lt;/p&gt;
&lt;p&gt;Minha stack principal é Databricks. Não porque eu li um tutorial. Porque é o que roda em produção nos lugares onde trabalhei nos últimos anos.&lt;/p&gt;
&lt;p&gt;Em 2026 entrei num mestrado em métodos computacionais aplicados. Pesquiso uso de IA pra monitoramento preditivo em sistemas operacionais críticos. Tudo que aprendo lá eu pretendo trazer pra cá traduzido pra realidade de quem trabalha com dados todo dia.&lt;/p&gt;
&lt;h2&gt;Por que cripto entrou nessa história&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;por-que-cripto-entrou-nessa-história&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#por-que-cripto-entrou-nessa-hist%c3%b3ria&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Alguns anos atrás eu comecei a estudar análise on-chain. E percebi uma coisa que pouca gente parece estar dizendo de forma clara: cripto, em boa parte, é um problema de engenharia de dados ainda mal resolvido.&lt;/p&gt;
&lt;p&gt;Os dados estão todos ali. Na blockchain, abertos, públicos. Mas boa parte de quem investe não sabe tratá-los, e grande parte das engenheiras de dados ainda não está olhando pra eles.&lt;/p&gt;
&lt;p&gt;Então decidi construir um agente de IA especialista em cripto. Do zero, em público, documentando cada decisão de arquitetura. Com as mesmas ferramentas que uso no trabalho: pipelines reais, backtesting rigoroso, modelos estatísticos de verdade. Sem hype, sem promessa de enriquecimento rápido.&lt;/p&gt;
&lt;h2&gt;O que você vai encontrar aqui&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-você-vai-encontrar-aqui&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-voc%c3%aa-vai-encontrar-aqui&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Três frentes, uma newsletter.&lt;/p&gt;
&lt;p&gt;A primeira é &lt;strong&gt;engenharia de dados de produção&lt;/strong&gt;: Databricks, Delta Lake, Spark, dbt, Airflow. Decisões reais de arquitetura, erros que cometi e o que aprendi com eles, contexto brasileiro onde for relevante (LGPD na prática, custo de cloud, a realidade de dados em instituições financeiras).&lt;/p&gt;
&lt;p&gt;A segunda é o &lt;strong&gt;agente de IA pra cripto&lt;/strong&gt;, construído em público. Arquitetura, código, backtesting, análise on-chain. Cada etapa documentada. Se der errado, você vai saber por quê.&lt;/p&gt;
&lt;p&gt;A terceira é o &lt;strong&gt;mestrado traduzido pra prática&lt;/strong&gt;. O que a pesquisa acadêmica tem a dizer sobre os problemas que você enfrenta todo dia. Sem filtro, sem academiquês.&lt;/p&gt;
&lt;p&gt;Publicações em português e inglês, toda semana.&lt;/p&gt;
&lt;p&gt;Responde esse post com uma pergunta: qual é o maior desafio de dados que você está enfrentando agora? Eu leio tudo.&lt;/p&gt;
&lt;p&gt;Thais Vaz&lt;/p&gt;
&lt;p&gt;&lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Newsletter no Substack →&lt;/a&gt;&lt;/p&gt;

      </description>
    </item>
    
    <item>
      <title>LGPD na ingestão de dados: 4 princípios que mudam sua arquitetura</title>
      <link>https://vazdeng.pages.dev/2026/04/16/lgpd-ingestao-de-dados/</link>
      <pubDate>Thu, 16 Apr 2026 00:00:00 +0000</pubDate>
      
      <guid>https://vazdeng.pages.dev/2026/04/16/lgpd-ingestao-de-dados/</guid>
      <description>
        
        
        
        <![CDATA[<img src="https://vazdeng.pages.dev/2026/04/16/lgpd-ingestao-de-dados/cover_hu_8477b73067869427.png" width="640" height="336"/>]]>
        
        &lt;p&gt;Vi a maioria dos times tratar LGPD como algo pra resolver &amp;ldquo;depois&amp;rdquo;.&lt;/p&gt;
&lt;p&gt;Primeiro o pipeline é montado, os dados entram no lake, os dashboards começam a sair. Aí, um dia, chega uma requisição de titular pedindo exclusão de dados pessoais. E o time descobre que não sabe onde aquele CPF está, quantas cópias existem no Bronze, quantos modelos de ML já foram treinados com ele.&lt;/p&gt;
&lt;p&gt;É tarde.&lt;/p&gt;
&lt;p&gt;LGPD não é compliance no fim do pipeline. É uma restrição de design que começa no primeiro byte que você ingere. Existem quatro princípios que, se você incorporar logo na camada de ingestão, evitam praticamente todas as dores que vêm depois.&lt;/p&gt;
&lt;h2&gt;Princípio 1: minimize na fonte, não no destino&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;princípio-1-minimize-na-fonte-não-no-destino&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#princ%c3%adpio-1-minimize-na-fonte-n%c3%a3o-no-destino&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O Art. 6º, III da LGPD fala em &lt;strong&gt;necessidade&lt;/strong&gt;: só trate dados adequados e limitados ao que é necessário para a finalidade.&lt;/p&gt;
&lt;p&gt;A tradução prática que aprendi é simples. Não ingira o que você não vai usar.&lt;/p&gt;
&lt;p&gt;Parece óbvio, mas não é. A maioria dos pipelines ingere tabelas inteiras (incluindo colunas de CPF, RG, telefone, endereço, e-mail) &amp;ldquo;porque está na fonte&amp;rdquo;. Aí o compliance chega, pede o mapeamento desses campos, e descobre que 80% deles nunca foram consumidos por ninguém.&lt;/p&gt;
&lt;p&gt;O padrão correto é aplicar schema filtering antes da persistência. No pipeline de ingestão, você define explicitamente quais campos entram no lake. O que não entrar, não vira problema seu de retenção, de anonimização, de auditoria.&lt;/p&gt;
&lt;p&gt;A pergunta que vale a pena fazer antes de cada campo é: &lt;em&gt;qual caso de uso concreto precisa desse dado?&lt;/em&gt;. Se a resposta for &amp;ldquo;sei lá, pode ser útil&amp;rdquo;, é porque não precisa.&lt;/p&gt;
&lt;h2&gt;Princípio 2: pseudonimize desde o primeiro byte&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;princípio-2-pseudonimize-desde-o-primeiro-byte&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#princ%c3%adpio-2-pseudonimize-desde-o-primeiro-byte&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Três termos que parecem iguais e não são.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Anonimização&lt;/strong&gt; é o dado tornado irreversível. Não dá mais pra identificar ninguém. É o único estado que a LGPD trata como fora do escopo (Art. 12).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Pseudonimização&lt;/strong&gt; é a identidade substituída por um código, mas com possibilidade de reverter via uma tabela separada. Continua sendo dado pessoal (Art. 13, §4º). Reduz risco, mas não remove a obrigação.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Tokenização&lt;/strong&gt; é uma variante específica de pseudonimização com token determinístico, útil pra preservar joins sem expor o dado original.&lt;/p&gt;
&lt;p&gt;&lt;img src=&#34;https://vazdeng.pages.dev/2026/04/16/lgpd-ingestao-de-dados/lifecycle.png&#34; alt=&#34;Lifecycle de um campo de dado pessoal passando por anonimização, pseudonimização e tokenização&#34;  loading=&#34;lazy&#34; /&gt;&lt;/p&gt;
&lt;p&gt;A prática que funciona é tokenizar na ingestão. O Bronze nunca vê o dado cru. Vê o token determinístico. O mapeamento &lt;code&gt;token ↔ dado original&lt;/code&gt; vive numa tabela isolada, com criptografia em repouso, acesso auditado e política de retenção própria.&lt;/p&gt;
&lt;p&gt;Isso resolve três problemas de uma vez. Você consegue fazer join entre tabelas no lake sem expor o dado original. Direito ao apagamento vira um &lt;code&gt;DELETE&lt;/code&gt; no mapeamento, sem precisar mexer no Bronze. E analistas e modelos de ML trabalham com dados pseudonimizados por padrão, reduzindo a superfície de risco.&lt;/p&gt;
&lt;h2&gt;Princípio 3: lineage é requisito, não feature&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;princípio-3-lineage-é-requisito-não-feature&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#princ%c3%adpio-3-lineage-%c3%a9-requisito-n%c3%a3o-feature&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Quando chega uma requisição de titular (Art. 18, direito de acesso, correção, exclusão), você tem 15 dias pra responder. Sem lineage completo, esse prazo vira pesadelo.&lt;/p&gt;
&lt;p&gt;Lineage de verdade responde três perguntas pra qualquer dado pessoal. De onde veio? Sistema fonte, campo original, timestamp de ingestão. Que transformações sofreu? Passos do pipeline, regras aplicadas, derivações. Onde está agora? Tabelas, modelos treinados, dashboards que o consomem.&lt;/p&gt;
&lt;p&gt;Ferramentas como OpenLineage, DataHub e o Unity Catalog do Databricks entregam isso, mas só se você instrumentar desde a ingestão. Colocar lineage depois que o pipeline já está rodando é dez vezes mais caro do que colocar antes.&lt;/p&gt;
&lt;p&gt;O teste prático é direto: você consegue, em menos de uma hora, listar todas as tabelas e modelos que contêm o CPF &lt;code&gt;123.456.789-00&lt;/code&gt;? Se não consegue, seu lineage não está pronto pra LGPD.&lt;/p&gt;
&lt;h2&gt;Princípio 4: retenção por finalidade, não por tabela&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;princípio-4-retenção-por-finalidade-não-por-tabela&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#princ%c3%adpio-4-reten%c3%a7%c3%a3o-por-finalidade-n%c3%a3o-por-tabela&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;O Art. 15 diz que o tratamento termina quando a finalidade for alcançada. O Art. 16 completa: depois disso, os dados devem ser eliminados.&lt;/p&gt;
&lt;p&gt;Na prática da engenharia de dados, cada dado passa a ter um relógio próprio. Não dá pra criar uma política única de &amp;ldquo;retenção igual a 5 anos&amp;rdquo; pra todas as tabelas. Algumas finalidades exigem meses, outras anos, outras são indefinidas (por base legal diferente).&lt;/p&gt;
&lt;p&gt;Padrões que funcionam: tabelas particionadas por data de tratamento, com &lt;code&gt;VACUUM&lt;/code&gt; ou &lt;code&gt;TRUNCATE PARTITION&lt;/code&gt; no fim do ciclo. Mapa de finalidades documentado em código, num YAML que define, por tabela e por campo, qual finalidade justifica, qual base legal e qual prazo. E jobs de expiração automáticos, sem confiar em processo manual: configura retention policies que rodam sozinhas.&lt;/p&gt;
&lt;p&gt;Delta Lake, BigQuery e Snowflake têm mecanismos pra isso. O trabalho é traduzir finalidade jurídica em configuração técnica, e esse é o trabalho que ninguém quer fazer, mas que determina se você vai ou não bater de frente com a ANPD.&lt;/p&gt;
&lt;h2&gt;O que o time de dados precisa combinar com o jurídico&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-o-time-de-dados-precisa-combinar-com-o-jurídico&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-o-time-de-dados-precisa-combinar-com-o-jur%c3%addico&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;Três conversas que a engenharia não pode terceirizar.&lt;/p&gt;
&lt;p&gt;A primeira é a base legal de cada dado. Consentimento? Legítimo interesse? Execução contratual? Cada base tem implicações técnicas diferentes. Direito de revogação, por exemplo, só existe em consentimento.&lt;/p&gt;
&lt;p&gt;A segunda é a finalidade concreta de cada pipeline. &amp;ldquo;Analytics&amp;rdquo; não vale. Qual decisão de negócio esse dado suporta?&lt;/p&gt;
&lt;p&gt;A terceira é o processo de resposta a requisições. Quem recebe? Qual o fluxo? Qual o SLA interno? Isso precisa estar documentado, testado e ter dono.&lt;/p&gt;
&lt;p&gt;Se essas três conversas ainda não aconteceram, seu pipeline de dados pessoais está operando em dívida técnica de compliance.&lt;/p&gt;
&lt;h2&gt;O que fica&lt;span class=&#34;hx:absolute hx:-mt-20&#34; id=&#34;o-que-fica&#34;&gt;&lt;/span&gt;
    &lt;a href=&#34;#o-que-fica&#34; class=&#34;subheading-anchor&#34; aria-label=&#34;Link permanente para esta secção&#34;&gt;&lt;/a&gt;&lt;/h2&gt;&lt;p&gt;LGPD não é checklist no fim. É uma restrição de design que muda quatro coisas. O que você ingere (minimização). Como você ingere (pseudonimização). O que você rastreia (lineage). Por quanto tempo mantém (retenção por finalidade).&lt;/p&gt;
&lt;p&gt;Times que tratam LGPD como &amp;ldquo;resolvemos depois&amp;rdquo; pagam o retrabalho inteiro na primeira requisição de titular que chega. Times que tratam como design constraint desde o primeiro byte nem percebem que ela está ali, porque é só como as coisas funcionam.&lt;/p&gt;
&lt;p&gt;A diferença entre um e outro não é jurídica. É de engenharia.&lt;/p&gt;
&lt;p&gt;Qual foi a requisição de titular mais cabulosa que você já viu chegar no seu time? Me responde no &lt;a href=&#34;https://linkedin.com/in/thacvaz&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;LinkedIn&lt;/a&gt; ou assina o &lt;a href=&#34;https://vazdeng.substack.com&#34;target=&#34;_blank&#34; rel=&#34;noopener&#34;&gt;Substack&lt;/a&gt; pra receber os próximos posts.&lt;/p&gt;

      </description>
    </item>
    
  </channel>
</rss>
