<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:googleplay="http://www.google.com/schemas/play-podcasts/1.0"><channel><title><![CDATA[Chronicles of a Pragmatic Programmer]]></title><description><![CDATA[Engenharia de sistemas financeiros no Brasil: Pix, crédito, cobrança e core bancário, contados por quem opera isso em produção.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com</link><image><url>https://substackcdn.com/image/fetch/$s_!eeaL!,w_256,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F033b4aa8-5924-4a31-af48-6efdc2faf85c_608x608.png</url><title>Chronicles of a Pragmatic Programmer</title><link>https://chroniclesofapragmaticprogrammer.substack.com</link></image><generator>Substack</generator><lastBuildDate>Fri, 09 Oct 2026 20:33:29 GMT</lastBuildDate><atom:link href="https://chroniclesofapragmaticprogrammer.substack.com/feed" rel="self" type="application/rss+xml"/><copyright><![CDATA[Rafael dos Santos Miguel Filho]]></copyright><language><![CDATA[pt-br]]></language><webMaster><![CDATA[showmethecode@substack.com]]></webMaster><itunes:owner><itunes:email><![CDATA[showmethecode@substack.com]]></itunes:email><itunes:name><![CDATA[Rafael Miguel]]></itunes:name></itunes:owner><itunes:author><![CDATA[Rafael Miguel]]></itunes:author><googleplay:owner><![CDATA[showmethecode@substack.com]]></googleplay:owner><googleplay:email><![CDATA[showmethecode@substack.com]]></googleplay:email><googleplay:author><![CDATA[Rafael Miguel]]></googleplay:author><itunes:block><![CDATA[Yes]]></itunes:block><item><title><![CDATA[System Design: as partes difíceis — edição podcast]]></title><description><![CDATA[A vers&#227;o em &#225;udio do artigo: seis frentes em que arquitetura deixa de ser diagrama e vira negocia&#231;&#227;o.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/system-design-as-partes-dificeis-91b</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/system-design-as-partes-dificeis-91b</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Mon, 05 Oct 2026 13:05:02 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/218871342/d34da01e89231fef1fd539cc7f78aec6.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>Arquitetura de software n&#227;o &#233; um diagrama bonito no Miro. &#201; a disciplina de fazer legado e moderno coexistirem, com squads que t&#234;m prazos e apetite a risco diferentes, sem esperar que a empresa inteira se alinhe de uma vez &#8212; porque isso nunca vai acontecer.</p><p>Este epis&#243;dio &#233; a vers&#227;o em &#225;udio de "System Design: As partes dif&#237;ceis!". Mesmo argumento, outro formato: d&#225; para ouvir no deslocamento, na academia ou enquanto o build roda.</p><p>Aqui eu preciso ser honesto sobre o m&#233;todo: o &#225;udio foi gerado com apoio de IA, a partir do texto original do artigo. O argumento, os exemplos e as analogias s&#227;o os mesmos que escrevi &#8212; a conversa &#233; que &#233; sint&#233;tica. Se isso te incomodar, o texto continua inteiro no link l&#225; embaixo.</p><h3>O que voc&#234; vai ouvir</h3><ul><li><p>Resili&#234;ncia de sistema: blindar o usu&#225;rio do atrito entre o s&#237;ncrono e o ass&#237;ncrono.</p></li><li><p>Rompendo silos: o contrato de integra&#231;&#227;o (API First) como acordo &#250;nico entre times que n&#227;o se falam.</p></li><li><p>Falando a l&#237;ngua do neg&#243;cio: ROI de componente reutiliz&#225;vel contra prazo r&#237;gido de projeto.</p></li><li><p>Gest&#227;o de mudan&#231;a: evoluir a arquitetura sem quebrar o que j&#225; est&#225; em produ&#231;&#227;o.</p></li><li><p>Assimetria t&#233;cnica entre squads: desenhar para o time que existe, n&#227;o para o time ideal.</p></li><li><p>Engenharia de conflitos: a PoC como atalho leg&#237;timo para a burocracia dos comit&#234;s.</p></li></ul><p>O fio que costura os seis &#233; o mesmo. Num ecossistema corporativo fechado, voc&#234; n&#227;o uniformiza os navios. Voc&#234; padroniza o ponto de encaixe entre eles.</p><blockquote><p>Pense comigo:</p><p>1. Quantos dos seus fluxos hoje s&#227;o s&#237;ncronos porque a regra de neg&#243;cio exige, e quantos porque ningu&#233;m nunca reescreveu?</p><p>2. Se o squad vizinho precisasse integrar com o seu servi&#231;o amanh&#227;, ele conseguiria fazer isso lendo s&#243; o contrato?</p><p>3. Qual foi a &#250;ltima decis&#227;o arquitetural do seu time que virou decis&#227;o porque algu&#233;m levou uma PoC, e n&#227;o um slide?</p></blockquote><p>O texto completo, com os trade-offs de cada frente e a parte sobre como comunicar cada uma delas, est&#225; em <a href="https://chroniclesofapragmaticprogrammer.substack.com/p/system-design-as-partes-dificeis">System Design: As partes dif&#237;ceis!</a>.</p><p>E a&#237;: no seu contexto hoje, qual dos seis &#233; o mais dif&#237;cil &#8212; o legado, a burocracia dos comit&#234;s ou a assimetria entre os times? Responde nos coment&#225;rios.</p>]]></content:encoded></item><item><title><![CDATA[Todo crédito barato é uma reserva num banco de dados que não é seu]]></title><description><![CDATA[Consignado, gravame de ve&#237;culo, receb&#237;vel de cart&#227;o. Produtos diferentes, o mesmo problema de engenharia, e um estado que quase ningu&#233;m modela.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/todo-credito-e-uma-reserva-de-saldo</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/todo-credito-e-uma-reserva-de-saldo</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Fri, 02 Oct 2026 20:35:50 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/6ff20057-4c21-4900-963a-69e04ccc7c14_3200x2134.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Sexta-feira, 17h40. Dona Lurdes, aposentada, aceita pelo aplicativo do banco um empr&#233;stimo consignado de R$ 8.000 em 84 parcelas.</p><p>Se voc&#234; nunca contratou um, consignado &#233; o empr&#233;stimo em que a parcela &#233; descontada direto do sal&#225;rio ou da aposentadoria, antes de o dinheiro chegar &#224; conta. Quem entrega a parcela ao banco n&#227;o &#233; a dona Lurdes: &#233; o INSS, que paga o benef&#237;cio dela. Para o banco, isso quase elimina o risco de calote. Para ela, significa uma das taxas de juros mais baixas do mercado.</p><p>S&#243; que, para o desconto acontecer, o banco precisa avisar ao INSS: "deste benef&#237;cio, R$ 190 por m&#234;s agora s&#227;o meus". Esse aviso tem nome, averba&#231;&#227;o<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>, e passa pela Dataprev<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>, a empresa que processa os dados da Previd&#234;ncia.</p><p>A proposta sai do sistema do banco em dire&#231;&#227;o &#224; Dataprev. A resposta demora. Depois de 30 segundos sem retorno, o sistema desiste de esperar, o famoso timeout, e faz exatamente o que foi programado para fazer: tenta de novo. A segunda tentativa tamb&#233;m n&#227;o volta. A&#237; entra a &#250;ltima regra do fluxo, escrita meses antes por algu&#233;m que queria evitar proposta travada no fim de semana: se a averba&#231;&#227;o n&#227;o responder em duas tentativas, segue para o desembolso e concilia na segunda-feira.</p><p>Segunda-feira de manh&#227;, o atendimento abre dois chamados. No primeiro, a margem da dona Lurdes aparece com duas parcelas reservadas e um &#250;nico dep&#243;sito de R$ 8.000 na conta: ela vai pagar R$ 380 por m&#234;s por um empr&#233;stimo cuja parcela &#233; R$ 190. A Dataprev tinha recebido os dois pedidos e estava apenas lenta para responder.</p><p>No segundo chamado, outro cliente recebeu o dinheiro via Pix e a margem dele nunca foi reservada: ali os dois pedidos morreram no caminho, e a Dataprev nunca soube que existiram. Na folha seguinte, n&#227;o vai haver parcela para descontar.</p><p>Ningu&#233;m escreveu um bug no sentido cl&#225;ssico. Os testes passavam. O c&#243;digo fazia o que dizia. O problema estava no modelo: ele n&#227;o tinha onde guardar a pergunta <em>"a Dataprev recebeu o meu pedido ou n&#227;o?"</em>.</p><p><strong>O contrato estava perfeito no banco de dados do banco e n&#227;o existia no &#250;nico lugar que importava.</strong></p><p>Uma &#250;nica regra de timeout, dois incidentes opostos. A diferen&#231;a entre eles n&#227;o estava no c&#243;digo: estava num fato que o c&#243;digo n&#227;o tinha como saber, se o pedido chegou do outro lado ou n&#227;o. Sem lugar para guardar essa d&#250;vida, o sistema foi obrigado a chutar. Chutou duas vezes, e errou nas duas dire&#231;&#245;es poss&#237;veis.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!-eTL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!-eTL!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png 424w, https://substackcdn.com/image/fetch/$s_!-eTL!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png 848w, https://substackcdn.com/image/fetch/$s_!-eTL!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png 1272w, https://substackcdn.com/image/fetch/$s_!-eTL!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!-eTL!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png" width="1200" height="670.8791208791209" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:814,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1200,&quot;bytes&quot;:444406,&quot;alt&quot;:&quot;Figura 1. A mesma regra de timeout, dois desfechos opostos. O que decide qual deles acontece n&#227;o est&#225; no seu c&#243;digo: est&#225; do outro lado da linha.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/218175093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="Figura 1. A mesma regra de timeout, dois desfechos opostos. O que decide qual deles acontece n&#227;o est&#225; no seu c&#243;digo: est&#225; do outro lado da linha." title="Figura 1. A mesma regra de timeout, dois desfechos opostos. O que decide qual deles acontece n&#227;o est&#225; no seu c&#243;digo: est&#225; do outro lado da linha." srcset="https://substackcdn.com/image/fetch/$s_!-eTL!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png 424w, https://substackcdn.com/image/fetch/$s_!-eTL!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png 848w, https://substackcdn.com/image/fetch/$s_!-eTL!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png 1272w, https://substackcdn.com/image/fetch/$s_!-eTL!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb1ecd00e-10bb-4796-8685-9ed55295d80b_3400x1900.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figura 1. A mesma regra de timeout, dois desfechos opostos. O que decide qual deles acontece n&#227;o est&#225; no seu c&#243;digo: est&#225; do outro lado da linha.</figcaption></figure></div><p>A cena &#233; fict&#237;cia; os ingredientes, n&#227;o. Este artigo &#233; sobre por que isso acontece com consignado, com financiamento de carro, com antecipa&#231;&#227;o de receb&#237;vel e com praticamente qualquer produto de cr&#233;dito no Brasil, e sobre por que todos eles quebram do mesmo jeito.</p><p>Ele foi escrito para dois tipos de leitor. Se voc&#234; trabalha com tecnologia, vai encontrar modelagem, estados e c&#243;digo. Se n&#227;o trabalha, vai entender por que o seu empr&#233;stimo &#224;s vezes demora a cair, e por que a taxa de um cr&#233;dito &#233; t&#227;o diferente da de outro. As partes t&#233;cnicas v&#234;m sempre com uma tradu&#231;&#227;o ao lado.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Antes de tudo: o que &#233; cr&#233;dito?</h2><p>Cr&#233;dito &#233; dinheiro de hoje trocado por uma promessa de amanh&#227;.</p><p>A palavra vem do latim <em>credere</em>, acreditar. Quem empresta acredita que o dinheiro vai voltar. E essa cren&#231;a tem pre&#231;o: os juros<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a>, que pagam duas coisas ao mesmo tempo. <strong>Uma &#233; o tempo, porque o dinheiro emprestado deixa de render para quem emprestou. A outra &#233; o risco de a promessa n&#227;o ser cumprida.</strong></p><p>Muita gente usa cr&#233;dito sem chamar pelo nome. <strong>A compra parcelada no cart&#227;o &#233; cr&#233;dito. O carn&#234; da loja &#233; cr&#233;dito. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">O cheque especial que entra sozinho quando a conta fica negativa &#233; cr&#233;dito, e dos mais caros.</mark></strong> At&#233; o "parcelado sem juros" &#233; cr&#233;dito: algu&#233;m est&#225; financiando aquela compra, e o custo foi embutido no pre&#231;o ou pago pela loja.</p><h3>As duas perguntas de quem empresta</h3><p>Imagine que um conhecido te pede R$ 500 emprestados. Antes de responder, voc&#234; faz duas perguntas, mesmo sem perceber:</p><ol><li><p><strong>Essa pessoa vai me pagar?</strong> Voc&#234; pensa no que sabe dela: se trabalha, se costuma honrar o que promete, se j&#225; te deu calote.</p></li><li><p><strong>E se n&#227;o pagar, o que eu fa&#231;o?</strong> Se ela deixar o celular com voc&#234; at&#233; devolver o dinheiro, voc&#234; empresta mais tranquilo. Se n&#227;o deixar nada, voc&#234; pensa duas vezes, ou empresta menos.</p></li></ol><p>Essas duas perguntas s&#227;o o mercado de cr&#233;dito inteiro. A primeira os bancos respondem com informa&#231;&#227;o: hist&#243;rico de pagamentos, renda, d&#237;vidas em aberto. A segunda eles respondem com garantia<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a>: alguma coisa que o banco pode segurar se a promessa falhar.</p><h2>Por que existem tantos tipos de cr&#233;dito?</h2><p>Porque existem muitas respostas diferentes para a segunda pergunta. Cada tipo de cr&#233;dito &#233;, no fundo, uma resposta diferente para "o que o banco pode segurar?".</p><ul><li><p>No <strong>consignado</strong>, o banco segura a parcela antes de ela chegar ao cliente, porque ela sai direto do sal&#225;rio ou do benef&#237;cio.</p></li><li><p>No <strong>financiamento de ve&#237;culo</strong>, o banco segura o pr&#243;prio carro. Ele fica no nome do banco at&#233; a &#250;ltima parcela.</p></li><li><p>No <strong>cr&#233;dito imobili&#225;rio</strong> e no <strong>home equity</strong><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a>, o banco segura o im&#243;vel.</p></li><li><p>Na <strong>antecipa&#231;&#227;o de receb&#237;veis</strong>, que &#233; cr&#233;dito para empresas, o banco segura o dinheiro que a loja ainda vai receber das vendas no cart&#227;o.</p></li><li><p>No <strong>cr&#233;dito pessoal</strong>, no <strong>cheque especial</strong> e no <strong>rotativo do cart&#227;o</strong><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-6" href="#footnote-6" target="_self">6</a>, o banco n&#227;o segura nada. S&#243; tem a informa&#231;&#227;o da primeira pergunta.</p></li></ul><p>E a regra geral &#233; intuitiva: quanto mais o banco consegue segurar, menos risco ele corre, e menor tende a ser a taxa. Com uma corre&#231;&#227;o que importa mais do que parece: o que pesa n&#227;o &#233; ter garantia, &#233; poder execut&#225;-la r&#225;pido e barato. Garantia que leva anos no Judici&#225;rio para virar dinheiro entra no pre&#231;o quase como se n&#227;o existisse, e &#233; por isso que o carro, que vale muito menos que o apartamento, sustenta uma taxa melhor que o home equity.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!keIE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!keIE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png 424w, https://substackcdn.com/image/fetch/$s_!keIE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png 848w, https://substackcdn.com/image/fetch/$s_!keIE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png 1272w, https://substackcdn.com/image/fetch/$s_!keIE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!keIE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png" width="1440" height="780" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/dc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:780,&quot;width&quot;:1440,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:84656,&quot;alt&quot;:&quot;Figura 2. Ordem aproximada, para pessoa f&#237;sica: quanto mais o banco consegue segurar, menor tende a ser a taxa. A antecipa&#231;&#227;o de receb&#237;veis fica de fora porque &#233; cr&#233;dito para empresas. O que ordena a fila n&#227;o &#233; o valor do bem, e sim a velocidade e o custo de transform&#225;-lo em dinheiro.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/218175093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figura 2. Ordem aproximada, para pessoa f&#237;sica: quanto mais o banco consegue segurar, menor tende a ser a taxa. A antecipa&#231;&#227;o de receb&#237;veis fica de fora porque &#233; cr&#233;dito para empresas. O que ordena a fila n&#227;o &#233; o valor do bem, e sim a velocidade e o custo de transform&#225;-lo em dinheiro." title="Figura 2. Ordem aproximada, para pessoa f&#237;sica: quanto mais o banco consegue segurar, menor tende a ser a taxa. A antecipa&#231;&#227;o de receb&#237;veis fica de fora porque &#233; cr&#233;dito para empresas. O que ordena a fila n&#227;o &#233; o valor do bem, e sim a velocidade e o custo de transform&#225;-lo em dinheiro." srcset="https://substackcdn.com/image/fetch/$s_!keIE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png 424w, https://substackcdn.com/image/fetch/$s_!keIE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png 848w, https://substackcdn.com/image/fetch/$s_!keIE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png 1272w, https://substackcdn.com/image/fetch/$s_!keIE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fdc2d2046-0e03-4c76-a524-ed5a0e9ba002_1440x780.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figura 2. Ordem aproximada, para pessoa f&#237;sica: quanto mais o banco consegue segurar, menor tende a ser a taxa. A antecipa&#231;&#227;o de receb&#237;veis fica de fora porque &#233; cr&#233;dito para empresas. O que ordena a fila n&#227;o &#233; o valor do bem, e sim a velocidade e o custo de transform&#225;-lo em dinheiro.</figcaption></figure></div><p>Agora vem a parte que quase ningu&#233;m percebe, e que d&#225; t&#237;tulo a este artigo. <strong>O que torna um cr&#233;dito barato &#233; a garantia &#8212; e, em quase todos os casos, ela n&#227;o est&#225; com o banco.</strong> O sal&#225;rio est&#225; na folha do INSS ou da empresa. O carro est&#225; registrado no Detran. O im&#243;vel, no cart&#243;rio. Para o cr&#233;dito sair mais barato, o banco precisa reservar alguma coisa dentro de um sistema que n&#227;o &#233; dele.</p><p>Vale a exce&#231;&#227;o, porque ela prova o ponto: existe cr&#233;dito barato cuja garantia est&#225; dentro de casa. No empr&#233;stimo com garantia de investimento, o banco bloqueia um CDB do pr&#243;prio cliente, e a reserva vira uma linha numa tabela sua, com transa&#231;&#227;o e <code>UNIQUE</code>. Todo este artigo deixa de existir. O problema nunca foi a garantia: &#233; a fronteira. O custo de engenharia nasce no instante em que o recurso escasso passa a morar do lado de fora.</p><p>&#201; a&#237; que a conversa vira engenharia.</p><h2>Um restaurante que n&#227;o &#233; seu</h2><p>Quando um banco oferece cr&#233;dito com garantia, ele est&#225; tentando reservar algo que n&#227;o mora no seu pr&#243;prio banco de dados.</p><p>Pense numa reserva de restaurante feita por telefone. Voc&#234; liga, pergunta se tem mesa para quatro &#224;s 20h, pede para reservar e desliga. A mesa n&#227;o &#233; sua. O caderno de reservas fica no restaurante. A &#250;nica coisa que voc&#234; tem &#233; a lembran&#231;a de que ligou.</p><p>Agora imagine que a liga&#231;&#227;o caiu no meio da frase "ent&#227;o fica reservado no nome de...". Voc&#234; reservou ou n&#227;o? Se ligar de novo e disser "quero uma mesa para quatro &#224;s 20h", pode sair com duas mesas no seu nome. A pergunta certa &#233; outra: <em>"voc&#234;s t&#234;m alguma reserva no nome Rafael?"</em></p><p>S&#243; que essa pergunta tem uma condi&#231;&#227;o escondida, e ela &#233; a parte que mais se esquece: voc&#234; s&#243; pode perguntar "tem reserva no nome Rafael?" porque disse o seu nome antes de a liga&#231;&#227;o cair. <strong>Se n&#227;o disse nada que identifique o pedido, n&#227;o existe pergunta a fazer. Existe apenas pedir de novo, e torcer.</strong></p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Guarde essa diferen&#231;a entre repetir o pedido e perguntar pelo pedido. Ela &#233; o artigo inteiro.</mark></p><p>Todo cr&#233;dito com garantia segue os mesmos quatro movimentos:</p><ol><li><p><strong>Consultar</strong> se o recurso escasso est&#225; livre: a margem do aposentado, o carro sem restri&#231;&#227;o, o receb&#237;vel ainda n&#227;o comprometido.</p></li><li><p><strong>Reservar</strong> esse recurso junto a quem guarda o registro oficial dele.</p></li><li><p><strong>Liberar</strong> o dinheiro. S&#243; a partir daqui o contrato existe de verdade.</p></li><li><p>Devolver, se o contrato n&#227;o nascer. Este &#233; o movimento que quase ningu&#233;m desenha, e &#233; dele que sai o incidente que ningu&#233;m abre.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!YS4O!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!YS4O!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png 424w, https://substackcdn.com/image/fetch/$s_!YS4O!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png 848w, https://substackcdn.com/image/fetch/$s_!YS4O!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png 1272w, https://substackcdn.com/image/fetch/$s_!YS4O!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!YS4O!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png" width="1016" height="393.56043956043953" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:564,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1016,&quot;bytes&quot;:244059,&quot;alt&quot;:&quot;Figura 3. Os quatro movimentos de qualquer cr&#233;dito com garantia. O recurso escasso muda de nome a cada produto; a forma da conversa n&#227;o muda. O quarto movimento &#233; o que mais falta nos sistemas reais.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/218175093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="Figura 3. Os quatro movimentos de qualquer cr&#233;dito com garantia. O recurso escasso muda de nome a cada produto; a forma da conversa n&#227;o muda. O quarto movimento &#233; o que mais falta nos sistemas reais." title="Figura 3. Os quatro movimentos de qualquer cr&#233;dito com garantia. O recurso escasso muda de nome a cada produto; a forma da conversa n&#227;o muda. O quarto movimento &#233; o que mais falta nos sistemas reais." srcset="https://substackcdn.com/image/fetch/$s_!YS4O!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png 424w, https://substackcdn.com/image/fetch/$s_!YS4O!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png 848w, https://substackcdn.com/image/fetch/$s_!YS4O!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png 1272w, https://substackcdn.com/image/fetch/$s_!YS4O!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5678573b-5fed-476c-a6e9-d700430b4e6b_3200x1240.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figura 3. Os quatro movimentos de qualquer cr&#233;dito com garantia. O recurso escasso muda de nome a cada produto; a forma da conversa n&#227;o muda. O quarto movimento &#233; o que mais falta nos sistemas reais.</figcaption></figure></div><p>O que muda de um produto para outro s&#227;o tr&#234;s caracter&#237;sticas da contraparte<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-7" href="#footnote-7" target="_self">7</a>:</p><ul><li><p><strong>Quem guarda a verdade.</strong> Um sistema central, dezenas de sistemas diferentes, uma entidade de registro, ou ningu&#233;m.</p></li><li><p><strong>Quanto demora e quando fecha.</strong> Milissegundos, horas ou dias, e com quais janelas de indisponibilidade.</p></li><li><p><strong>Se d&#225; para desfazer.</strong> Existe desaverba&#231;&#227;o, baixa de gravame, cancelamento de registro? Em quanto tempo?</p></li></ul><p>Com essas tr&#234;s perguntas, os produtos de cr&#233;dito brasileiros se agrupam em quatro fam&#237;lias. Antes de visitar cada uma, vale resolver o problema da dona Lurdes, porque ele aparece em todas.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/todo-credito-e-uma-reserva-de-saldo?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/todo-credito-e-uma-reserva-de-saldo?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p><h2>O estado que quase ningu&#233;m modela</h2><p>Entre pedir a reserva e saber o resultado existe um intervalo, e o seu modelo precisa ter um nome para ele.</p><p>Antes de nomear o intervalo, a condi&#231;&#227;o t&#233;cnica que o torna trat&#225;vel. A pergunta "voc&#234; recebeu o meu pedido?" s&#243; existe se o pedido levar um identificador est&#225;vel, gerado e gravado pelo banco antes da primeira chamada e repetido em toda tentativa e em toda consulta. &#201; o seu nome na reserva do restaurante. Essa chave de correla&#231;&#227;o &#233; o que separa um estado de desconhecimento, que voc&#234; resolve depois, de um beco sem sa&#237;da, em que voc&#234; sabe que n&#227;o sabe e n&#227;o tem como descobrir.</p><p>No artigo sobre modelagem de dados, vimos que estados intermedi&#225;rios s&#227;o cidad&#227;os de primeira classe: dinheiro em tr&#226;nsito precisa de endere&#231;o. Com a garantia &#233; igual. Veja uma m&#225;quina de estados<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-8" href="#footnote-8" target="_self">8</a> para a origina&#231;&#227;o, que &#233; o nome t&#233;cnico para o nascimento de um contrato de cr&#233;dito.</p><p>Se voc&#234; n&#227;o programa, leia o bloco abaixo como uma lista de situa&#231;&#245;es poss&#237;veis para o contrato, na ordem em que acontecem. As frases depois de <code>//</code> s&#227;o coment&#225;rios explicando cada uma.</p><pre><code><code>public enum OriginationStatus {
    PROPOSAL_CREATED,
    COLLATERAL_CHECKED,       // a snapshot, not a reservation: it can go stale
    RESERVATION_REQUESTED,    // sent with our correlation key; no answer yet
    RESERVATION_CONFIRMED,    // has an expiry date: the counterparty may drop it
    RESERVATION_REJECTED,
    RESERVATION_UNKNOWN,      // timeout: we do not know. Bounded in time, not forever
    RESERVATION_EXPIRED,      // confirmed, then aged out before the money left
    MANUAL_REVIEW,            // UNKNOWN hit its ceiling: a human owns it now
    DISBURSEMENT_SENT,        // reachable only from RESERVATION_CONFIRMED
    DISBURSEMENT_FAILED,      // the money never left: the reservation must go back
    RESERVATION_RELEASED,     // margin returned. The only honest end for a dead proposal
    ACTIVE,
    SETTLED,
    CANCELLED
}</code></code></pre><p>Os estados <code>CONFIRMED</code> e <code>REJECTED</code> todo sistema tem. O <code>RESERVATION_UNKNOWN</code> quase nenhum tem, e &#233; ele que representa a liga&#231;&#227;o que caiu. Sem esse estado, o sistema s&#243; tem duas mentiras &#224; disposi&#231;&#227;o, e nada o impede de contar as duas em sequ&#234;ncia:</p><ul><li><p>tratar o timeout como falha e pedir de novo, o que pode gerar <strong>reserva duplicada</strong>;</p></li><li><p>tratar o timeout como sucesso e liberar o dinheiro, o que pode gerar <strong>dinheiro sem garantia</strong>.</p></li></ul><p>Foi exatamente o que o fluxo da sexta-feira fez, nessa ordem: pediu de novo e, sem resposta, liberou o dinheiro. Os dois chamados da segunda-feira n&#227;o foram dois bugs diferentes. Foram a mesma omiss&#227;o, vista por dois clientes com sortes diferentes.</p><p>Repare tamb&#233;m no COLLATERAL_CHECKED, que existe por um motivo espec&#237;fico: consultar disponibilidade &#233; tirar um retrato, n&#227;o reservar nada. Entre o retrato e a reserva, a margem pode ter sido tomada, e o concorrente mais prov&#225;vel n&#227;o &#233; outro banco. &#201; a sua pr&#243;pria casa. Duas propostas do mesmo CPF entrando por canais diferentes na mesma tarde consultam a mesma margem livre, as duas passam na valida&#231;&#227;o e as duas pedem reserva. Se a soma n&#227;o couber, a contraparte recusa uma delas num dia bom; se couber, ela aceita as duas e o cliente descobre no extrato. Por isso a reserva come&#231;a dentro de casa, com um bloqueio local da margem, com prazo de validade, antes de qualquer chamada externa. A fila de quem liga para o restaurante tamb&#233;m &#233; sua.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!uWlO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uWlO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png 424w, https://substackcdn.com/image/fetch/$s_!uWlO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png 848w, https://substackcdn.com/image/fetch/$s_!uWlO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png 1272w, https://substackcdn.com/image/fetch/$s_!uWlO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uWlO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png" width="1456" height="728" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:728,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:324315,&quot;alt&quot;:&quot;Figura 4. O ciclo de vida da reserva, inteiro. Do RESERVATION_UNKNOWN nunca sai dinheiro: sai uma pergunta, e ela tem prazo. E toda proposta que morre devolve o que tinha reservado.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/218175093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figura 4. O ciclo de vida da reserva, inteiro. Do RESERVATION_UNKNOWN nunca sai dinheiro: sai uma pergunta, e ela tem prazo. E toda proposta que morre devolve o que tinha reservado." title="Figura 4. O ciclo de vida da reserva, inteiro. Do RESERVATION_UNKNOWN nunca sai dinheiro: sai uma pergunta, e ela tem prazo. E toda proposta que morre devolve o que tinha reservado." srcset="https://substackcdn.com/image/fetch/$s_!uWlO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png 424w, https://substackcdn.com/image/fetch/$s_!uWlO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png 848w, https://substackcdn.com/image/fetch/$s_!uWlO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png 1272w, https://substackcdn.com/image/fetch/$s_!uWlO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff4a6dadd-0160-48c2-8c54-40fd1b8d1366_3280x1640.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figura 4. O ciclo de vida da reserva, inteiro. Do RESERVATION_UNKNOWN nunca sai dinheiro: sai uma pergunta, e ela tem prazo. E toda proposta que morre devolve o que tinha reservado.</figcaption></figure></div><p>A regra que sai disso cabe numa linha: <strong>timeout n&#227;o &#233; falha nem sucesso. &#201; desconhecimento, e desconhecimento se resolve consultando, n&#227;o repetindo.</strong></p><h3>O incidente que ningu&#233;m abre: a reserva que ningu&#233;m devolve</h3><p>Os dois chamados da segunda-feira t&#234;m um irm&#227;o silencioso, e ele &#233; mais comum que os dois juntos. Entre RESERVATION_CONFIRMED e DISBURSEMENT_SENT existe uma janela em que a margem j&#225; est&#225; comprometida e o dinheiro ainda n&#227;o saiu. Se o desembolso falha, se a an&#225;lise reprova numa segunda passada, se o cliente desiste, ou se a proposta simplesmente morre no backlog, aquela fatia da renda fica presa a um contrato que nunca existiu.</p><p>Do lado do banco ningu&#233;m abre chamado: nenhum real saiu, nenhum painel ficou vermelho. Do lado do cliente, &#233; um dos atendimentos mais frequentes do consignado, "minha margem est&#225; presa e eu n&#227;o consigo contratar em lugar nenhum". &#201; o caso da dona Lurdes de novo, com o preju&#237;zo do outro lado do balc&#227;o.</p><p>Da&#237; saem duas consequ&#234;ncias de modelagem. A primeira: desfazer &#233; um estado, n&#227;o um script de emerg&#234;ncia. Todo caminho que n&#227;o termina em contrato ativo precisa passar por uma libera&#231;&#227;o expl&#237;cita, com protocolo de volta da contraparte, exatamente como a reserva teve. A segunda: reserva confirmada tem prazo. Boa parte das contrapartes derruba a reserva sozinha se o contrato n&#227;o for conclu&#237;do em alguns dias, o que significa que RESERVATION_CONFIRMED n&#227;o &#233; um estado permanente. Ele envelhece. Quem trata essa confirma&#231;&#227;o como definitiva vai, em algum dia movimentado, desembolsar contra uma garantia que j&#225; caiu.</p><p>A mesma r&#233;gua vale para o desconhecimento. RESERVATION_UNKNOWN precisa de teto. Consultar com intervalo crescente &#233; correto por algumas horas; depois disso, continuar consultando &#233; s&#243; uma forma elegante de nunca decidir. Vencido o teto, o contrato vai para revis&#227;o humana, com dono e prazo, e algu&#233;m avisa o cliente, porque do lado dele a proposta est&#225; parada sem explica&#231;&#227;o desde sexta-feira.</p><blockquote><p><strong>Pense comigo</strong></p><p>Se a contraparte cair entre o seu pedido e a resposta dela, em qual estado do seu modelo o contrato fica? Se a resposta for "depende do log que a gente olhar", est&#225; faltando um estado.</p><p>O seu retry pergunta antes de repetir? Se n&#227;o pergunta, ele n&#227;o &#233; retry. &#201; um segundo contrato esperando para acontecer. E o pedido original leva uma chave que torne essa pergunta poss&#237;vel, ou voc&#234; s&#243; tem o seu pr&#243;prio id interno, que a contraparte nunca viu?</p><p>Existe algum caminho no c&#243;digo em que o dinheiro sai sem a reserva estar confirmada? Vale procurar hoje, porque em produ&#231;&#227;o ele aparece de qualquer jeito. E o caminho inverso, que ningu&#233;m procura: existe algum em que a proposta morre e a margem fica reservada?</p></blockquote><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/todo-credito-e-uma-reserva-de-saldo/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/todo-credito-e-uma-reserva-de-saldo/comments"><span>Deixe um coment&#225;rio</span></a></p><h2>Quatro jeitos de guardar a verdade</h2><p>Os produtos de cr&#233;dito brasileiros cabem em <strong>quatro arqu&#233;tipos</strong>, definidos por quem guarda o registro da garantia. Cada um exige uma coisa diferente do seu sistema.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!9qVF!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!9qVF!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png 424w, https://substackcdn.com/image/fetch/$s_!9qVF!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png 848w, https://substackcdn.com/image/fetch/$s_!9qVF!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png 1272w, https://substackcdn.com/image/fetch/$s_!9qVF!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!9qVF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png" width="1440" height="860" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:860,&quot;width&quot;:1440,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:111579,&quot;alt&quot;:&quot;Figura 5. Os quatro arqu&#233;tipos de integra&#231;&#227;o de cr&#233;dito, organizados por quem guarda a verdade da garantia.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/218175093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figura 5. Os quatro arqu&#233;tipos de integra&#231;&#227;o de cr&#233;dito, organizados por quem guarda a verdade da garantia." title="Figura 5. Os quatro arqu&#233;tipos de integra&#231;&#227;o de cr&#233;dito, organizados por quem guarda a verdade da garantia." srcset="https://substackcdn.com/image/fetch/$s_!9qVF!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png 424w, https://substackcdn.com/image/fetch/$s_!9qVF!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png 848w, https://substackcdn.com/image/fetch/$s_!9qVF!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png 1272w, https://substackcdn.com/image/fetch/$s_!9qVF!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4fad5884-5072-4adf-bad7-0db6b1d3e9ee_1440x860.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figura 5. Os quatro arqu&#233;tipos de integra&#231;&#227;o de cr&#233;dito, organizados por quem guarda a verdade da garantia.</figcaption></figure></div><h2>Arqu&#233;tipo 1: uma contraparte central, e o calend&#225;rio dela</h2><p>Quando toda a verdade mora num lugar s&#243;, a integra&#231;&#227;o fica mais simples e voc&#234; passa a viver no ritmo de outra organiza&#231;&#227;o.</p><p>O consignado de aposentados e pensionistas sempre passou pela Dataprev. Em 2025, o consignado dos trabalhadores com carteira assinada foi pelo mesmo caminho. A Lei 15.179/2025 criou o programa Cr&#233;dito do Trabalhador e acabou com os conv&#234;nios individuais entre empresas e bancos. Hoje a consulta de margem, a averba&#231;&#227;o e o controle dos contratos passam por uma plataforma central, integrada ao eSocial<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-9" href="#footnote-9" target="_self">9</a>, &#224; CTPS Digital e ao FGTS Digital. Para o trabalhador CLT, a margem &#233; de at&#233; 35% da remunera&#231;&#227;o dispon&#237;vel.</p><p>E tem um detalhe do Cr&#233;dito do Trabalhador que quase n&#227;o aparece nas an&#225;lises de arquitetura: a garantia n&#227;o &#233; s&#243; a margem. O programa usa tamb&#233;m o FGTS do trabalhador, at&#233; 10% do saldo da conta mais a multa rescis&#243;ria, como lastro para o caso de demiss&#227;o. S&#227;o duas reservas, em dois registros que n&#227;o s&#227;o seus, com prazos e regras de baixa diferentes: a folha, pelo eSocial, e o fundo, pelo FGTS Digital. Pelo mesmo motivo, a antecipa&#231;&#227;o do saque-anivers&#225;rio &#233; arqu&#233;tipo 1 em estado puro, s&#243; com outro recurso escasso: a parcela anual do FGTS, e quem guarda a verdade dela &#233; a Caixa.</p><p>Do ponto de vista de arquitetura, isso &#233; trocar dezenas de integra&#231;&#245;es ponto a ponto por um hub<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-10" href="#footnote-10" target="_self">10</a>. Antes, cada banco precisava de conv&#234;nio com cada empresa. Agora, todos falam com um lugar s&#243;. &#201; uma padroniza&#231;&#227;o excelente, com dois custos que costumam ser subestimados.</p><h3>Primeiro custo: a padaria fecha para contar o caixa</h3><p>Pense na padaria do bairro que, no fim do m&#234;s, baixa a porta por algumas horas para fechar o caixa. Ningu&#233;m compra p&#227;o nesse intervalo, e isso n&#227;o &#233; defeito: &#233; o jeito de a padaria saber quanto vendeu.</p><p>A Dataprev faz algo parecido em escala nacional. Todo m&#234;s acontece a virada de compet&#234;ncia<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-11" href="#footnote-11" target="_self">11</a>, um processamento em que ela consolida os contratos para que os descontos saiam certos na folha. No consignado CLT, durante esses 3 a 4 dias, averba&#231;&#227;o, simula&#231;&#227;o e consulta de margem ficam suspensas. No INSS existe o equivalente, a chamada maci&#231;a: o processamento mensal que fecha a folha de todos os benefici&#225;rios e recalcula a margem do m&#234;s seguinte.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Se9-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Se9-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png 424w, https://substackcdn.com/image/fetch/$s_!Se9-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png 848w, https://substackcdn.com/image/fetch/$s_!Se9-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png 1272w, https://substackcdn.com/image/fetch/$s_!Se9-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Se9-!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png" width="848" height="292.0888888888889" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:496,&quot;width&quot;:1440,&quot;resizeWidth&quot;:848,&quot;bytes&quot;:42946,&quot;alt&quot;:&quot;Figura 6. A virada de compet&#234;ncia vista da origina&#231;&#227;o. Quando o balc&#227;o fecha, a fila n&#227;o some: ela acumula.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/218175093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="Figura 6. A virada de compet&#234;ncia vista da origina&#231;&#227;o. Quando o balc&#227;o fecha, a fila n&#227;o some: ela acumula." title="Figura 6. A virada de compet&#234;ncia vista da origina&#231;&#227;o. Quando o balc&#227;o fecha, a fila n&#227;o some: ela acumula." srcset="https://substackcdn.com/image/fetch/$s_!Se9-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png 424w, https://substackcdn.com/image/fetch/$s_!Se9-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png 848w, https://substackcdn.com/image/fetch/$s_!Se9-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png 1272w, https://substackcdn.com/image/fetch/$s_!Se9-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fab67f663-49f5-44a6-818b-434cd1d2ef95_1440x496.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figura 6. A virada de compet&#234;ncia vista da origina&#231;&#227;o. Quando o balc&#227;o fecha, a fila n&#227;o some: ela acumula.</figcaption></figure></div><p>Para o seu sistema, isso significa uma coisa: <strong>indisponibilidade planejada &#233; requisito, n&#227;o incidente.</strong> Se o fluxo trata "proposta aceita" e "contrato averbado" como o mesmo estado, a virada de compet&#234;ncia aparece no monitoramento como um apag&#227;o mensal. Se trata como estados separados, ela vira uma fila que volta a andar sozinha quando o balc&#227;o reabre.</p><h3>Segundo custo: a regra muda por medida provis&#243;ria</h3><p>2026 entregou o melhor exemplo poss&#237;vel. At&#233; maio, aposentados e pensionistas do INSS podiam comprometer 45% do benef&#237;cio: 35% com empr&#233;stimos, 5% com cart&#227;o de cr&#233;dito consignado e 5% com cart&#227;o consignado de benef&#237;cio, as rubricas RMC e RCC<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-12" href="#footnote-12" target="_self">12</a>. Em 19 de maio, a Medida Provis&#243;ria<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-13" href="#footnote-13" target="_self">13</a> 1.355/2026 reduziu o total para 40% e acabou com a divis&#227;o. Virou uma margem &#250;nica, usada livremente entre empr&#233;stimo e cart&#245;es.</p><p>A MP n&#227;o foi convertida em lei pelo Congresso. Perdeu a validade em 31 de agosto, a regra anterior voltou, e a Dataprev come&#231;ou o rec&#225;lculo na noite de 1&#186; de setembro.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sNM3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sNM3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png 424w, https://substackcdn.com/image/fetch/$s_!sNM3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png 848w, https://substackcdn.com/image/fetch/$s_!sNM3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png 1272w, https://substackcdn.com/image/fetch/$s_!sNM3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sNM3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png" width="728" height="303.3333333333333" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:600,&quot;width&quot;:1440,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:57875,&quot;alt&quot;:&quot;Figura 7. Margem consign&#225;vel total de aposentados e pensionistas do INSS em 2026. Um par&#226;metro de neg&#243;cio que mudou duas vezes em quatro meses, sem nenhuma decis&#227;o do seu time de produto.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/218175093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="Figura 7. Margem consign&#225;vel total de aposentados e pensionistas do INSS em 2026. Um par&#226;metro de neg&#243;cio que mudou duas vezes em quatro meses, sem nenhuma decis&#227;o do seu time de produto." title="Figura 7. Margem consign&#225;vel total de aposentados e pensionistas do INSS em 2026. Um par&#226;metro de neg&#243;cio que mudou duas vezes em quatro meses, sem nenhuma decis&#227;o do seu time de produto." srcset="https://substackcdn.com/image/fetch/$s_!sNM3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png 424w, https://substackcdn.com/image/fetch/$s_!sNM3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png 848w, https://substackcdn.com/image/fetch/$s_!sNM3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png 1272w, https://substackcdn.com/image/fetch/$s_!sNM3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F998d0420-3f9b-4aef-9ccc-1784af52df24_1440x600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figura 7. Margem consign&#225;vel total de aposentados e pensionistas do INSS em 2026. Um par&#226;metro de neg&#243;cio que mudou duas vezes em quatro meses, sem nenhuma decis&#227;o do seu time de produto.</figcaption></figure></div><p>Agora a parte que interessa a quem modela dados. Quem contratou durante a MP, usando a margem &#250;nica, voltou para uma regra em que empr&#233;stimo s&#243; cabe em 35%. Parte desses benefici&#225;rios passou a aparecer com <strong>margem negativa</strong>. O contrato era v&#225;lido quando nasceu e, pela regra de hoje, n&#227;o caberia. Os dois fatos s&#227;o verdadeiros ao mesmo tempo.</p><p>E a hist&#243;ria n&#227;o parou a&#237;: o TCU, no Ac&#243;rd&#227;o 1.094/2026, suspendeu novas averba&#231;&#245;es de cart&#227;o consignado at&#233; nova delibera&#231;&#227;o. Mais uma mudan&#231;a de regra vinda de fora, sem aviso ao seu backlog.</p><p>Isso s&#243; &#233; administr&#225;vel se o sistema souber responder <em>"qual regra valia na data em que este contrato foi assinado?"</em>. A resposta de modelagem &#233; simples: o percentual da margem n&#227;o &#233; uma constante, &#233; um dado com vig&#234;ncia.</p><p>Para quem n&#227;o programa: o bloco abaixo cria uma tabela em que cada regra fica guardada junto com a data em que come&#231;ou e a data em que deixou de valer. A &#250;ltima consulta pergunta "qual regra valia no dia em que este contrato foi assinado?".</p><pre><code><code>CREATE TABLE consignable_margin_rule (
    id           BIGSERIAL     PRIMARY KEY,
    segment      VARCHAR(20)   NOT NULL,  -- INSS, CLT, SIAPE, BPC
    bucket       VARCHAR(20)   NOT NULL,  -- LOAN, RMC, RCC, UNIFIED
    max_ratio    DECIMAL(5,4)  NOT NULL,  -- 0.3500 = 35%
    valid_from   DATE          NOT NULL,
    valid_to     DATE,                    -- NULL = still in force
    legal_basis  VARCHAR(120)  NOT NULL
);
-- INSS in 2026, as it actually happened
-- (earlier history and the RMC/RCC rows omitted for brevity)
INSERT INTO consignable_margin_rule
  (segment, bucket, max_ratio, valid_from, valid_to, legal_basis)
VALUES
  ('INSS', 'LOAN',    0.3500, '2026-01-01', '2026-05-18', 'Lei 10.820/2003'),
  ('INSS', 'UNIFIED', 0.4000, '2026-05-19', '2026-08-31', 'MP 1.355/2026'),
  ('INSS', 'LOAN',    0.3500, '2026-09-01', NULL,         'Lei 10.820/2003 (MP expired)');
-- which rule applied to this contract on the day it was signed?
SELECT bucket, max_ratio, legal_basis
FROM consignable_margin_rule
WHERE segment = 'INSS'
  AND :signed_on BETWEEN valid_from AND COALESCE(valid_to, DATE '9999-12-31');</code></code></pre><p>Se o percentual estava como constante no c&#243;digo, 2026 custou dois deploys de emerg&#234;ncia em quatro meses, e ainda deixou a pergunta sem resposta para os contratos antigos. Se estava numa tabela com vig&#234;ncia, custou dois <code>INSERT</code>s.</p><h2>Arqu&#233;tipo 2: muitas contrapartes, muitos dialetos</h2><p>Quando o mesmo conceito de neg&#243;cio mora em dezenas de sistemas diferentes, o problema deixa de ser integrar e passa a ser traduzir.</p><p>O consignado de servidores p&#250;blicos &#233; o caso-limite. Os servidores federais t&#234;m a folha processada pelo SIAPE<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-14" href="#footnote-14" target="_self">14</a>. Estados e munic&#237;pios t&#234;m os pr&#243;prios sistemas, muitas vezes operados por empresas contratadas, cada um com portal, protocolo, layout de arquivo e regra de reserva diferentes. Para o banco, "reservar margem" &#233; uma coisa s&#243;. Do outro lado, s&#227;o dezenas de jeitos de dizer a mesma coisa.</p><p>O financiamento de ve&#237;culos tem o mesmo desenho. A garantia precisa ser registrada no Detran do estado onde o carro est&#225; emplacado e no SNG<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-15" href="#footnote-15" target="_self">15</a>, o Sistema Nacional de Gravames operado pela B3, em que a financeira inclui, consulta e d&#225; baixa na restri&#231;&#227;o. S&#227;o 27 Detrans, e cada um tem suas particularidades.</p><h3>O adaptador de tomada</h3><p>Quem j&#225; viajou para outro pa&#237;s com um carregador de celular conhece a solu&#231;&#227;o. Voc&#234; n&#227;o troca de carregador a cada fronteira. Voc&#234; leva um adaptador que encaixa na tomada local e, quando a tens&#227;o tamb&#233;m muda, um conversor junto, e o seu aparelho continua recebendo sempre a mesma coisa.</p><p>Em arquitetura, esse adaptador tem nome: Anti-Corruption Layer<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-16" href="#footnote-16" target="_self">16</a>, que j&#225; discutimos aqui aplicado &#224; integra&#231;&#227;o entre o Pix e o core banc&#225;rio. O seu dom&#237;nio fala uma l&#237;ngua s&#243;, e cada contraparte ganha um adaptador que traduz o dialeto dela. E traduzir aqui n&#227;o &#233; converter formato de arquivo: &#233; converter significado. Quando um estado chega como "em processamento" e outro como "pendente de confirma&#231;&#227;o", algu&#233;m precisa decidir se os dois querem dizer a mesma coisa no seu modelo. Essa decis&#227;o &#233; a camada; deix&#225;-la impl&#237;cita &#233; como importar o vocabul&#225;rio de 27 &#243;rg&#227;os para dentro do seu dom&#237;nio.</p><p>Em linguagem comum: o c&#243;digo abaixo define as quatro perguntas que o banco sabe fazer a qualquer contraparte (reserve, qual o status, desfa&#231;a, o que voc&#234; suporta), sem se importar com o jeito que cada uma responde por dentro. A segunda parte lista as quatro respostas poss&#237;veis para a pergunta de status: reservei, recusei, n&#227;o tenho nada com essa chave, e n&#227;o consigo responder agora.</p><pre><code><code>public interface CollateralRegistry {

    // Ask the counterparty to reserve. The request carries OUR correlation key,
    // persisted before this call. A timeout here is not a failure.
    ReservationReceipt reserve(ReservationRequest request) throws TimeoutException;

    // "Do you have anything under this correlation key?"
    // Four answers, never two. "I cannot tell you now" is not the same as "nothing here".
    StatusAnswer status(CorrelationKey key);

    // Undo: cancel the payroll deduction, lift the lien, release the encumbrance.
    // Idempotent, and retried until acknowledged.
    void release(ReservationId reservationId);

    // Declared, not assumed: is status() authoritative or eventually consistent?
    // How long before a NOT_FOUND can be trusted? Maintenance windows?
    // Does it support file reconciliation, and how long may a reservation sit unused?
    CounterpartyCapabilities capabilities();
}

sealed interface StatusAnswer {
    record Reserved(ReservationReceipt receipt, Instant expiresAt) implements StatusAnswer {}
    record Rejected(String reason)                                 implements StatusAnswer {}
    record NotFound(Instant observedAt)                            implements StatusAnswer {}
    record Unavailable(Instant retryAfter)                         implements StatusAnswer {}
}</code></code></pre><p>O detalhe que costuma faltar &#233; o m&#233;todo <code>status</code>. Toda contraparte responde a um pedido de reserva. Nem toda oferece um jeito de perguntar "voc&#234; recebeu isto?". Quando n&#227;o oferece, o adaptador precisa de um plano B, geralmente concilia&#231;&#227;o<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-17" href="#footnote-17" target="_self">17</a> por arquivo no dia seguinte. E esse plano precisa estar escrito no adaptador, n&#227;o na cabe&#231;a de quem o construiu.</p><p>E tem um detalhe mais fino, que &#233; onde eu j&#225; vi time competente escorregar depois de acertar tudo o resto: NOT_FOUND n&#227;o &#233; prova de que nada chegou. Se a contraparte grava de forma ass&#237;ncrona, se a sua consulta cai numa r&#233;plica atrasada, ou se o pedido ainda est&#225; numa fila do outro lado, a resposta "n&#227;o tenho nada com essa chave" &#233; um falso negativo. Reenviar na hora reproduz exatamente a reserva duplicada que voc&#234; estava tentando evitar, agora com a consci&#234;ncia tranquila de quem consultou antes.</p><p>A corre&#231;&#227;o &#233; barata: NOT_FOUND s&#243; autoriza reenvio depois de um intervalo m&#237;nimo declarado pela contraparte, ou depois de duas consultas consistentes separadas no tempo. &#201; para isso que existe o capabilities(), que na maioria dos sistemas &#233; um m&#233;todo decorativo: ele precisa responder se o status &#233; autoritativo ou eventual, e quanto tempo &#233; preciso esperar antes de acreditar numa aus&#234;ncia.</p><p>Vale o mesmo cuidado na forma da resposta. Se "n&#227;o consigo responder agora" chega ao seu c&#243;digo do mesmo jeito que "n&#227;o tenho nada", voc&#234; voltou a ter duas op&#231;&#245;es onde existem quatro, e o desconhecimento desapareceu do modelo no &#250;ltimo metro, justamente na fronteira em que ele nasce. Estado de primeira classe no banco de dados e aus&#234;ncia indistinta no contrato da integra&#231;&#227;o &#233; meio caminho.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jX03!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jX03!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png 424w, https://substackcdn.com/image/fetch/$s_!jX03!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png 848w, https://substackcdn.com/image/fetch/$s_!jX03!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png 1272w, https://substackcdn.com/image/fetch/$s_!jX03!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jX03!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png" width="996" height="437.11813186813185" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:639,&quot;width&quot;:1456,&quot;resizeWidth&quot;:996,&quot;bytes&quot;:335449,&quot;alt&quot;:&quot;Figura 8. A mesma resposta NOT_FOUND, dois significados. Enquanto o pedido est&#225; em tr&#226;nsito do outro lado, a aus&#234;ncia n&#227;o &#233; informa&#231;&#227;o: &#233; atraso.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/218175093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="Figura 8. A mesma resposta NOT_FOUND, dois significados. Enquanto o pedido est&#225; em tr&#226;nsito do outro lado, a aus&#234;ncia n&#227;o &#233; informa&#231;&#227;o: &#233; atraso." title="Figura 8. A mesma resposta NOT_FOUND, dois significados. Enquanto o pedido est&#225; em tr&#226;nsito do outro lado, a aus&#234;ncia n&#227;o &#233; informa&#231;&#227;o: &#233; atraso." srcset="https://substackcdn.com/image/fetch/$s_!jX03!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png 424w, https://substackcdn.com/image/fetch/$s_!jX03!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png 848w, https://substackcdn.com/image/fetch/$s_!jX03!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png 1272w, https://substackcdn.com/image/fetch/$s_!jX03!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe401604d-f640-4a5e-9f09-31d7a9586843_3600x1580.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figura 8. A mesma resposta NOT_FOUND, dois significados. Enquanto o pedido est&#225; em tr&#226;nsito do outro lado, a aus&#234;ncia n&#227;o &#233; informa&#231;&#227;o: &#233; atraso.</figcaption></figure></div><p>Tem outra consequ&#234;ncia: monitoramento agregado mente. "99% de sucesso na averba&#231;&#227;o" pode esconder um estado inteiro fora do ar. Cada adaptador precisa do pr&#243;prio painel, do pr&#243;prio SLA<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-18" href="#footnote-18" target="_self">18</a> e do pr&#243;prio alarme.</p><blockquote><p><strong>Pense comigo</strong></p><p>Quantos lugares do seu c&#243;digo conhecem o nome de uma contraparte espec&#237;fica? Se for mais de um, a pr&#243;xima mudan&#231;a de layout vira projeto.</p><p>Se amanh&#227; um Detran mudar o formato do retorno, quantos servi&#231;os voc&#234; precisa publicar de novo? A resposta certa &#233; um.O seu painel mostra a taxa de erro por contraparte ou s&#243; a m&#233;dia de todas? E alguma contraparte do seu cat&#225;logo declara, em c&#243;digo, se o status dela &#233; confi&#225;vel no mesmo instante, ou isso &#233; conhecimento que vive na cabe&#231;a de duas pessoas?</p></blockquote><h2>Arqu&#233;tipo 3: a garantia que s&#243; existe quando o mundo sabe</h2><p>Em algumas garantias, o registro n&#227;o &#233; uma burocracia que vem depois do contrato. &#201; o que faz a garantia valer.</p><p>Voltemos ao carro. Na aliena&#231;&#227;o fiduci&#225;ria<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-19" href="#footnote-19" target="_self">19</a>, o banco &#233; o dono do ve&#237;culo at&#233; a &#250;ltima parcela e o cliente &#233; quem usa. S&#243; que isso s&#243; vale perante terceiros depois que o gravame<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-20" href="#footnote-20" target="_self">20</a> est&#225; registrado no &#243;rg&#227;o de tr&#226;nsito. Sem o registro, algu&#233;m pode comprar o carro de boa-f&#233;, ou outro banco pode aceit&#225;-lo como garantia de um segundo empr&#233;stimo, e o seu contrato n&#227;o impede nenhuma das duas coisas.</p><p>A analogia &#233; o ingresso de show com QR code. O papel na sua m&#227;o n&#227;o prova nada. O que prova &#233; a catraca validar o c&#243;digo contra a base do organizador e barrar a segunda pessoa que aparecer com a mesma imagem.</p><h3>Tr&#234;s catracas, tr&#234;s velocidades</h3><ul><li><p><strong>Ve&#237;culo.</strong> Registro no Detran e no SNG, com resposta eletr&#244;nica em segundos ou minutos.</p></li><li><p><strong>Im&#243;vel.</strong> Registro na matr&#237;cula, no Cart&#243;rio de Registro de Im&#243;veis. Desde a Lei 14.382/2022, os cart&#243;rios est&#227;o interligados pelo SERP<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-21" href="#footnote-21" target="_self">21</a>, mas o prazo continua em dias: 10 dias &#250;teis para t&#237;tulos de aliena&#231;&#227;o fiduci&#225;ria de im&#243;veis, e 5 dias &#250;teis quando o t&#237;tulo eletr&#244;nico estruturado chega pelo sistema.</p></li><li><p><strong>Receb&#237;veis</strong><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-22" href="#footnote-22" target="_self">22</a><strong>.</strong> Registradoras<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-23" href="#footnote-23" target="_self">23</a> autorizadas pelo Banco Central, como CERC, TAG e N&#250;clea, registram as agendas de cart&#227;o e as duplicatas<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-24" href="#footnote-24" target="_self">24</a>. O arcabou&#231;o come&#231;ou com a Resolu&#231;&#227;o CMN 4.734 e existe para dar publicidade e impedir que o mesmo receb&#237;vel seja onerado<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-25" href="#footnote-25" target="_self">25</a> duas vezes.</p></li></ul><p>A mesma opera&#231;&#227;o de reserva vai de segundos a semanas conforme o bem. Um sistema que trata as tr&#234;s com o mesmo timeout de 30 segundos n&#227;o est&#225; simplificando; est&#225; errando em duas delas.</p><p>Mas a conclus&#227;o arquitetural importa mais que a escolha do n&#250;mero. Acima de alguns minutos, reserva deixa de ser chamada e passa a ser processo. Algu&#233;m precisa guardar o pedido, acordar sozinho depois, consultar ou receber o callback, e sobreviver a deploy, a rein&#237;cio de servi&#231;o e a fim de semana. Timeout &#233; a ferramenta errada para um prazo de 10 dias &#250;teis: ali n&#227;o existe "esperar a resposta", existe um protocolo em aberto, com dono, prazo e alarme no vencimento. A carteira monitorada do cart&#243;rio n&#227;o &#233; zelo operacional de backoffice; &#233; a consequ&#234;ncia de ter modelado a reserva como processo de longa dura&#231;&#227;o em vez de chamada demorada. Quem n&#227;o faz essa troca acaba com um thread pool inteiro esperando um cart&#243;rio, ou com um job que reenvia o mesmo t&#237;tulo todo dia porque nunca soube que o primeiro envio continua v&#225;lido.</p><h3>A registradora &#233; idempot&#234;ncia entre concorrentes</h3><p>Este &#233; o ponto que mais me interessa como engenheiro. No artigo de modelagem, defendi que a idempot&#234;ncia<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-26" href="#footnote-26" target="_self">26</a> mora no schema: uma restri&#231;&#227;o <code>UNIQUE</code> impede que o mesmo lan&#231;amento entre duas vezes. Funciona muito bem dentro da sua casa.</p><p>Agora pense no lojista que vende R$ 100 mil no cart&#227;o por m&#234;s e tenta antecipar a mesma agenda com tr&#234;s bancos na mesma tarde. Nenhum deles enxerga o banco de dados do outro, e n&#227;o existe <code>UNIQUE</code> poss&#237;vel entre institui&#231;&#245;es concorrentes.</p><p>A registradora &#233; esse <code>UNIQUE</code>. &#201; uma chave &#250;nica compartilhada pelo mercado, mantida por um terceiro em quem todos precisam confiar. Quando o segundo banco tenta registrar a onera&#231;&#227;o de um receb&#237;vel que o primeiro j&#225; registrou, a registradora recusa, do mesmo jeito que o seu banco recusa a segunda linha com a mesma chave.</p><p>Traduzindo o bloco abaixo: a primeira parte &#233; a regra que o banco consegue impor dentro de casa ("esta chave s&#243; pode existir uma vez"). A segunda mostra a mesma regra oferecida como servi&#231;o pela registradora, que responde "j&#225; est&#225; comprometido" para quem chegar depois.</p><pre><code><code>-- inside your walls: the database enforces one row per fact
ALTER TABLE ledger_entry
  ADD CONSTRAINT uk_idempotency UNIQUE (idempotency_key);
-- across the market nobody can write that constraint,
-- so a registry enforces it as a service:
--   POST /encumbrances { receivable_id, creditor_id }
--   -&gt; 409 ALREADY_ENCUMBERED</code></code></pre><p>Agora preciso estragar um pouco a minha pr&#243;pria analogia, sen&#227;o quem trabalha com receb&#237;vel para de confiar no resto do artigo. Esse UNIQUE n&#227;o &#233; um s&#243;. Existem v&#225;rias registradoras autorizadas, e a unicidade &#233; forte dentro de cada uma e historicamente fr&#225;gil entre elas. O mesmo receb&#237;vel aparecendo em duas registradoras diferentes &#233; um problema conhecido do mercado, e a interoperabilidade entre elas vem sendo constru&#237;da por regula&#231;&#227;o e conv&#234;nio, n&#227;o por um &#237;ndice &#250;nico que qualquer um possa consultar.</p><p>Ent&#227;o a imagem certa n&#227;o &#233; uma tabela com uma restri&#231;&#227;o UNIQUE. &#201; um &#237;ndice particionado em que as parti&#231;&#245;es ainda est&#227;o aprendendo a conversar. E isso muda o que o seu sistema precisa fazer: consultar uma registradora n&#227;o &#233; consultar o mercado, e "n&#227;o encontrei onera&#231;&#227;o" quer dizer "n&#227;o encontrei onde eu olhei". Quem antecipa receb&#237;vel tratando aus&#234;ncia de registro como aus&#234;ncia de &#244;nus est&#225; confiando numa garantia de unicidade que n&#227;o cobre o universo, o que &#233; uma forma car&#237;ssima de aprender a diferen&#231;a entre chave &#250;nica e chave &#250;nica por parti&#231;&#227;o.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ffXb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ffXb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png 424w, https://substackcdn.com/image/fetch/$s_!ffXb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png 848w, https://substackcdn.com/image/fetch/$s_!ffXb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png 1272w, https://substackcdn.com/image/fetch/$s_!ffXb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ffXb!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png" width="980" height="472.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:702,&quot;width&quot;:1456,&quot;resizeWidth&quot;:980,&quot;bytes&quot;:372526,&quot;alt&quot;:&quot;Figura 9. A registradora como restri&#231;&#227;o de unicidade do mercado, e o limite dela: a garantia &#233; forte dentro de cada parti&#231;&#227;o, e depende de conv&#234;nio entre elas.&quot;,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/218175093?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="Figura 9. A registradora como restri&#231;&#227;o de unicidade do mercado, e o limite dela: a garantia &#233; forte dentro de cada parti&#231;&#227;o, e depende de conv&#234;nio entre elas." title="Figura 9. A registradora como restri&#231;&#227;o de unicidade do mercado, e o limite dela: a garantia &#233; forte dentro de cada parti&#231;&#227;o, e depende de conv&#234;nio entre elas." srcset="https://substackcdn.com/image/fetch/$s_!ffXb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png 424w, https://substackcdn.com/image/fetch/$s_!ffXb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png 848w, https://substackcdn.com/image/fetch/$s_!ffXb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png 1272w, https://substackcdn.com/image/fetch/$s_!ffXb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec7c1214-8f7c-4030-8736-4fb2f2855000_3320x1600.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Figura 9. A registradora como restri&#231;&#227;o de unicidade do mercado, e o limite dela: a garantia &#233; forte dentro de cada parti&#231;&#227;o, e depende de conv&#234;nio entre elas.</figcaption></figure></div><p>Com isso, o registro deixa de ser detalhe de backoffice e vira fato de primeira classe no seu modelo. Ele tem protocolo externo, a data em que passou a valer e a data em que voc&#234; ficou sabendo: os tr&#234;s tempos que vimos no artigo de modelagem. Quando algu&#233;m perguntar daqui a tr&#234;s anos "essa garantia estava registrada no dia do inadimplemento?", a resposta precisa ser um <code>SELECT</code>.</p><h2>Arqu&#233;tipo 4: quando n&#227;o h&#225; nada para reservar</h2><p>No cr&#233;dito pessoal, no cheque especial e no rotativo do cart&#227;o, n&#227;o h&#225; garantia para reservar em lugar nenhum. O problema inteiro migra para a decis&#227;o.</p><p>Aqui a integra&#231;&#227;o muda de natureza. Voc&#234; n&#227;o fala com quem guarda uma garantia; fala com quem guarda informa&#231;&#227;o: o SCR<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-27" href="#footnote-27" target="_self">27</a> do Banco Central, os bureaus de cr&#233;dito e, cada vez mais, o Open Finance<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-28" href="#footnote-28" target="_self">28</a>. Nenhum deles reserva nada. Eles s&#243; reduzem a incerteza, e o que sobra de incerteza vira pre&#231;o.</p><p>Os n&#250;meros mostram o tamanho disso: em abril de 2026, a taxa m&#233;dia do consignado INSS foi de 23,9% ao ano, contra 63% ao ano na m&#233;dia do cr&#233;dito pessoal n&#227;o consignado para pessoa f&#237;sica, segundo as s&#233;ries de taxas de juros do Banco Central. Vale dizer de qual recorte se trata, porque ele muda muito: cheque especial e rotativo do cart&#227;o correm em outra ordem de grandeza e puxariam esse n&#250;mero para muito acima de 63%.</p><p>E aqui eu preciso ser honesto com o n&#250;mero, porque o jeito mais f&#225;cil de us&#225;-lo &#233; circular. A taxa do consignado INSS n&#227;o &#233; s&#243; pre&#231;o de risco: ela tem teto, fixado pelo Conselho Nacional de Previd&#234;ncia Social e revisado de tempo em tempo. N&#227;o foi o mercado que chegou a 23,9% sozinho, olhando para a perda esperada.</p><p>O argumento n&#227;o cai com isso; ele fica mais preciso. O teto n&#227;o explica a taxa, ele &#233; o que a integra&#231;&#227;o torna sustent&#225;vel. Em qualquer modalidade sem reserva na fonte, um teto nessa altura secaria a oferta, porque ningu&#233;m empresta a 24% ao ano contra risco de cr&#233;dito pessoal puro. No consignado ele funciona porque a reserva na folha derruba a perda esperada a ponto de 24% ainda cobrir funding, opera&#231;&#227;o e a inadimpl&#234;ncia que sobra, que &#233; basicamente morte do benefici&#225;rio e bloqueio de benef&#237;cio. A integra&#231;&#227;o n&#227;o define o pre&#231;o. Ela define qual pre&#231;o &#233; poss&#237;vel sem matar o produto.</p><p>No extremo em que n&#227;o h&#225; nada para reservar em lugar nenhum, a taxa de juros &#233;, em boa parte, o pre&#231;o da integra&#231;&#227;o que n&#227;o existe.</p><p>Lembra das duas perguntas do come&#231;o, "vai me pagar?" e "se n&#227;o pagar, o que eu fa&#231;o?"? Neste arqu&#233;tipo s&#243; existe a primeira. &#201; o conhecido dos R$ 500 do come&#231;o do artigo, agora na escala de um sistema financeiro: sem o celular em cima da mesa, voc&#234; empresta menos, cobra mais, ou n&#227;o empresta. Consignado &#233; o mesmo empr&#233;stimo feito por quem j&#225; tem a m&#227;o na folha de pagamento.</p><p>O cart&#227;o de cr&#233;dito &#233; um caso curioso, porque mistura os dois mundos. A autoriza&#231;&#227;o &#233; uma microrreserva: a bandeira reserva limite em milissegundos. Depois v&#234;m a captura, a liquida&#231;&#227;o em D+N<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-29" href="#footnote-29" target="_self">29</a> e o faturamento, cada um com contraparte e prazo pr&#243;prios. &#201; a mesma forma, em miniatura, repetida milh&#245;es de vezes por dia.</p><p><mark data-color="#fff2cc" style="background-color: rgb(255, 242, 204); color: rgb(0, 0, 0);">Uma li&#231;&#227;o de modelagem fica deste arqu&#233;tipo: se a decis&#227;o substitui a garantia, a decis&#227;o &#233; um fato. Ela precisa ser guardada junto com o que a produziu, ou seja, quais dados foram consultados, quando, e qual vers&#227;o do modelo decidiu. Esse tema merece um artigo s&#243; para ele.</mark></p><h2>E quando a contraparte n&#227;o responde?</h2><p>Cada arqu&#233;tipo pede uma resposta diferente para o timeout, e escolher a resposta errada produz os dois incidentes da dona Lurdes.</p><ol><li><p><strong>Contraparte central.</strong> Consulte o status pela sua chave de correla&#231;&#227;o. Se ela estiver em janela planejada, estacione o contrato e retome quando reabrir.</p></li><li><p><strong>Contrapartes fragmentadas.</strong> A mesma regra, mas cada adaptador precisa declarar se oferece consulta de status e se a resposta dela &#233; autoritativa na hora. As que n&#227;o oferecem entram em concilia&#231;&#227;o por arquivo.</p></li><li><p><strong>Registro.</strong> Nunca considere a garantia constitu&#237;da sem o protocolo de volta. Opera&#231;&#245;es pendentes de registro entram numa carteira monitorada, com prazo e respons&#225;vel.</p></li><li><p><strong>Sem reserva.</strong> O risco do timeout est&#225; na libera&#231;&#227;o do dinheiro. O desembolso precisa ser idempotente, e o E2E ID<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-30" href="#footnote-30" target="_self">30</a> do Pix &#233; uma chave natural pronta para isso.</p></li></ol><p>E, nos quatro, a mesma regra de sa&#237;da, que &#233; a metade esquecida de todas as listas desse tipo: proposta que n&#227;o vira contrato devolve o que reservou, com protocolo de volta.</p><p>Em c&#243;digo, a regra geral cabe em poucas linhas.</p><p>Para quem n&#227;o programa: quando a resposta n&#227;o chega, o contrato vai para o estado "n&#227;o sei" e o sistema agenda uma consulta. Na consulta, se a contraparte diz que reservou, segue; se diz que recusou, encerra; se diz que n&#227;o consegue responder agora, espera e pergunta de novo. E se diz que nunca viu o pedido, ele s&#243; reenvia depois do tempo m&#237;nimo em que essa resposta pode ser levada a s&#233;rio. Vencido o teto de tentativas, um humano assume. O &#250;ltimo trecho &#233; o caminho inverso, o que quase ningu&#233;m escreve: proposta que morre devolve a margem.</p><pre><code><code>void requestReservation(Contract contract) {
    contract.moveTo(RESERVATION_REQUESTED);   // correlation key already persisted
    try {
        ReservationReceipt receipt = registry.reserve(contract.toReservationRequest());
        contract.confirmReservation(receipt);  // carries the expiry date. It is not forever
    } catch (TimeoutException e) {
        contract.moveTo(RESERVATION_UNKNOWN);  // never retry blindly
        resolver.scheduleCheck(contract.id(), Duration.ofMinutes(2));
    }
}

void resolveUnknown(Contract contract) {
    if (contract.unknownFor().compareTo(caps.unknownCeiling()) &gt; 0) {
        contract.moveTo(MANUAL_REVIEW);        // stop asking. A human owns it now
        return;
    }
    switch (registry.status(contract.correlationKey())) {
        case Reserved r    -&gt; contract.confirmReservation(r.receipt());
        case Rejected x    -&gt; contract.moveTo(RESERVATION_REJECTED);
        case Unavailable u -&gt; resolver.scheduleCheck(contract.id(), u.retryAfter());

        // NOT_FOUND is not proof that nothing arrived. Trust it only after the
        // settling window the counterparty itself declares, or we recreate
        // the very duplicate we were trying to avoid.
        case NotFound n    -&gt; {
            if (caps.notFoundIsConclusive(n.observedAt())) requestReservation(contract);
            else resolver.scheduleCheck(contract.id(), backoffFor(contract));
        }
    }
}

// The mirror image, and the one almost nobody writes:
// a proposal that dies gives the margin back.
void abandon(Contract contract, Reason reason) {
    if (contract.holdsReservation()) {
        registry.release(contract.reservationId());  // idempotent, retried until acked
        contract.moveTo(RESERVATION_RELEASED);
    }
    contract.close(reason);
}</code></code></pre><p>Por baixo de tudo, uma rede de seguran&#231;a: concilia&#231;&#227;o di&#225;ria entre o que voc&#234; acha que reservou e o que a contraparte diz que foi reservado. E a concilia&#231;&#227;o tem duas dire&#231;&#245;es, n&#227;o uma: reserva que voc&#234; acha que tem e a contraparte n&#227;o reconhece, e reserva que a contraparte ainda segura para uma proposta que na sua base j&#225; morreu. Diverg&#234;ncia encontrada ali &#233; incidente, com dono e prazo, e nunca um ajuste silencioso.</p><blockquote><p><strong>Pense comigo</strong></p><p>Quando foi a &#250;ltima vez que algu&#233;m do time leu o relat&#243;rio de concilia&#231;&#227;o? Relat&#243;rio que ningu&#233;m l&#234; &#233; s&#243; um arquivo ocupando espa&#231;o.</p><p>Se a contraparte ficar fora do ar por quatro dias, o que acontece com as propostas aceitas nesse per&#237;odo? Quem avisa o cliente?</p><p>Qual &#233; o maior valor que j&#225; saiu do seu sistema com a reserva em estado desconhecido? Se ningu&#233;m sabe responder, esse &#233; o primeiro painel a construir. O segundo &#233; mais barato e d&#243;i mais: quantas margens o seu sistema est&#225; segurando agora para propostas que nunca viraram contrato?</p></blockquote><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/todo-credito-e-uma-reserva-de-saldo/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/todo-credito-e-uma-reserva-de-saldo/comments"><span>Deixe um coment&#225;rio</span></a></p><h2>O contrato de sexta-feira</h2><p>O contrato da dona Lurdes n&#227;o quebrou por causa da Dataprev. A Dataprev fez o que qualquer sistema faz &#224;s vezes: demorou numa sexta-feira &#224; tarde. Ele quebrou porque o modelo n&#227;o tinha lugar para <em>"eu pedi e ainda n&#227;o sei"</em>.</p><p>Consignado, carro, im&#243;vel, receb&#237;vel, cr&#233;dito pessoal. O produto muda de nome, de taxa e de prazo. A pergunta de engenharia &#233; sempre a mesma: <strong>onde mora a verdade da garantia, e o que o seu sistema faz enquanto ela n&#227;o responde. E o arqu&#233;tipo 4 &#233; a exce&#231;&#227;o que prova a regra: quando n&#227;o existe nada para reservar em lugar nenhum, algu&#233;m paga a diferen&#231;a, em taxa, em limite ou em porta fechada. Reservar num banco de dados que n&#227;o &#233; seu n&#227;o &#233; um detalhe de integra&#231;&#227;o. &#201; o que separa 24% ao ano de 63%.</strong></p><p>Fica a pergunta para os coment&#225;rios: na sua institui&#231;&#227;o, qual contraparte mais te acorda de madrugada? Dataprev, Detran, cart&#243;rio ou registradora? E voc&#234;s t&#234;m um estado para "n&#227;o sei"? E um para "devolvi"?</p><h2>Fontes</h2><ul><li><p><a href="https://www.correiodamanha.com.br/economia/previdencia/2026/09/316550-fim-de-mp-faz-margem-do-consignado-do-inss-voltar-ao-limite-de-45.html">Correio da Manh&#227;: fim de MP faz margem do consignado do INSS voltar a 45%</a></p></li><li><p><a href="https://www.contabeis.com.br/noticias/79300/consignado-do-inss-margem-volta-a-45-apos-fim-de-mp">Cont&#225;beis: consignado do INSS, margem volta a 45% ap&#243;s fim de MP</a></p></li><li><p><a href="https://blog.bevioficial.com.br/mp-consignado-nova-margem-inss-siape/">Bevi: o que muda na margem do INSS e do SIAPE com o fim da MP 1.355</a></p></li><li><p><a href="https://seucreditodigital.com.br/consignado-inss-margem-cartoes-bloqueados/">Seu Cr&#233;dito Digital: consignado INSS e cart&#245;es bloqueados (Ac&#243;rd&#227;o TCU 1.094/2026)</a></p></li><li><p><a href="https://www.serasa.com.br/blog/dataprev-vai-atualizar-margem-consignado/">Serasa: quando a margem do consignado atualiza na Dataprev</a></p></li><li><p><a href="https://celcoin.com.br/articles/regras-apis-credito-consignado-privado/">Celcoin: regras de APIs para cr&#233;dito consignado privado em 2026</a></p></li><li><p><a href="https://blog.bancomercantil.com.br/produtos/consignado/privado/virada-competencia-consignado-clt/">Banco Mercantil: virada de compet&#234;ncia no consignado CLT</a></p></li><li><p><a href="https://www.b3.com.br/pt_br/produtos-e-servicos/gestao-de-garantias/sistema-nacional-de-gravames-sng/sistema-nacional-de-gravames-sng/">B3: Sistema Nacional de Gravames (SNG)</a></p></li><li><p><a href="https://forumimob.org.br/wp-content/uploads/2024/06/Nota-Te%CC%81cnica_Prazos-na-Lei-Federal-14.382_Fo%CC%81rum-de-Desenvolvimento-Imobilia%CC%81rio_Setembro-de-2023.pdf">F&#243;rum de Desenvolvimento Imobili&#225;rio: prazos na Lei 14.382/2022 (nota t&#233;cnica)</a></p></li><li><p><a href="https://antecipafacil.com.br/financiadores/artigos/registro-de-recebiveis-cerc-b3">Antecipa F&#225;cil: registro de receb&#237;veis, CERC e B3</a></p></li><li><p>Planalto: Medida Provis&#243;ria 1.355/2026, texto original (fonte prim&#225;ria)</p></li><li><p>TCU: Ac&#243;rd&#227;o 1.094/2026, &#237;ntegra (fonte prim&#225;ria)</p></li><li><p>Planalto: Lei 15.179/2025, Cr&#233;dito do Trabalhador (fonte prim&#225;ria)</p></li><li><p>Banco Central: s&#233;ries de taxas m&#233;dias de juros por modalidade de cr&#233;dito</p></li><li><p>Conselho Nacional de Previd&#234;ncia Social: resolu&#231;&#245;es de teto de juros do consignado do INSS</p></li></ul><p>Dados regulat&#243;rios verificados em 30 de setembro de 2026.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><strong>Averba&#231;&#227;o</strong>: registro oficial, feito por quem administra a folha ou o benef&#237;cio, de que um peda&#231;o daquela renda j&#225; tem dono. &#201; o carimbo que transforma uma promessa de desconto em desconto garantido.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p><strong>Dataprev</strong>: empresa p&#250;blica de tecnologia que processa os dados da Previd&#234;ncia. &#201; por ela que passam a consulta de margem e a averba&#231;&#227;o do consignado. Desde a Lei 15.179/2025, tamb&#233;m opera a plataforma do consignado dos trabalhadores com carteira assinada.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><strong>Juros</strong>: o pre&#231;o de usar o dinheiro de outra pessoa por um tempo. Como um aluguel, s&#243; que do dinheiro em vez de um im&#243;vel.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p><strong>Garantia</strong>: algo de valor que fica comprometido com o credor e pode ser usado para cobrir a d&#237;vida se ela n&#227;o for paga.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p><strong>Home equity</strong>: empr&#233;stimo em que a pessoa d&#225; o im&#243;vel que j&#225; tem como garantia, para conseguir uma taxa menor. O dinheiro pode ser usado para qualquer fim.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-6" href="#footnote-anchor-6" class="footnote-number" contenteditable="false" target="_self">6</a><div class="footnote-content"><p><strong>Rotativo do cart&#227;o</strong>: o cr&#233;dito que entra automaticamente quando a fatura n&#227;o &#233; paga inteira. O valor que ficou para tr&#225;s passa a ser financiado, com uma das taxas mais altas do mercado.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-7" href="#footnote-anchor-7" class="footnote-number" contenteditable="false" target="_self">7</a><div class="footnote-content"><p><strong>Contraparte</strong>: o outro lado de uma opera&#231;&#227;o. Neste artigo, quem guarda o registro oficial da garantia, e n&#227;o o cliente.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-8" href="#footnote-anchor-8" class="footnote-number" contenteditable="false" target="_self">8</a><div class="footnote-content"><p><strong>M&#225;quina de estados</strong>: um jeito de modelar algo que s&#243; pode estar em um estado por vez, com regras expl&#237;citas sobre de qual estado se pode ir para qual. Como um sem&#225;foro, que nunca pula do verde direto para o vermelho.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-9" href="#footnote-anchor-9" class="footnote-number" contenteditable="false" target="_self">9</a><div class="footnote-content"><p><strong>eSocial</strong>: sistema do governo em que as empresas informam folha de pagamento, v&#237;nculos e obriga&#231;&#245;es trabalhistas. &#201; de l&#225; que sai a informa&#231;&#227;o de quanto cada trabalhador ganha.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-10" href="#footnote-anchor-10" class="footnote-number" contenteditable="false" target="_self">10</a><div class="footnote-content"><p><strong>Hub</strong>: ponto central por onde todas as conex&#245;es passam, como um aeroporto de conex&#227;o. Simplifica as rotas e concentra os atrasos.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-11" href="#footnote-anchor-11" class="footnote-number" contenteditable="false" target="_self">11</a><div class="footnote-content"><p><strong>Compet&#234;ncia</strong>: o m&#234;s de refer&#234;ncia de uma folha ou de um fato cont&#225;bil. A virada de compet&#234;ncia &#233; a passagem de um m&#234;s de refer&#234;ncia para o seguinte.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-12" href="#footnote-anchor-12" class="footnote-number" contenteditable="false" target="_self">12</a><div class="footnote-content"><p><strong>RMC e RCC</strong>: Reserva de Margem Consign&#225;vel (cart&#227;o de cr&#233;dito consignado) e Reserva de Cart&#227;o Consignado de benef&#237;cio. S&#227;o fatias da margem separadas por lei para os dois tipos de cart&#227;o.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-13" href="#footnote-anchor-13" class="footnote-number" contenteditable="false" target="_self">13</a><div class="footnote-content"><p><strong>Medida provis&#243;ria (MP)</strong>: norma com for&#231;a de lei editada pela Presid&#234;ncia. Vale na hora, mas precisa ser aprovada pelo Congresso em at&#233; 120 dias. Se n&#227;o for, <em>caduca</em>: perde a validade e a regra anterior volta a valer.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-14" href="#footnote-anchor-14" class="footnote-number" contenteditable="false" target="_self">14</a><div class="footnote-content"><p><strong>SIAPE</strong>: sistema que processa a folha de pagamento dos servidores p&#250;blicos federais.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-15" href="#footnote-anchor-15" class="footnote-number" contenteditable="false" target="_self">15</a><div class="footnote-content"><p><strong>SNG (Sistema Nacional de Gravames)</strong>: base operada pela B3 em que financeiras registram, consultam e d&#227;o baixa nas restri&#231;&#245;es sobre ve&#237;culos dados em garantia.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-16" href="#footnote-anchor-16" class="footnote-number" contenteditable="false" target="_self">16</a><div class="footnote-content"><p><strong>Anti-Corruption Layer</strong> (camada anticorrup&#231;&#227;o): padr&#227;o do Domain-Driven Design em que uma camada traduz o modelo de um sistema externo para o seu, para que os conceitos do outro lado n&#227;o contaminem o seu dom&#237;nio.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-17" href="#footnote-anchor-17" class="footnote-number" contenteditable="false" target="_self">17</a><div class="footnote-content"><p><strong>Concilia&#231;&#227;o</strong>: comparar dois registros do mesmo fato, feitos por lados diferentes, para encontrar diverg&#234;ncias. &#201; o bater do caixa dos sistemas.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-18" href="#footnote-anchor-18" class="footnote-number" contenteditable="false" target="_self">18</a><div class="footnote-content"><p><strong>SLA</strong> (<em>Service Level Agreement</em>): o n&#237;vel de servi&#231;o combinado, como disponibilidade m&#237;nima ou tempo m&#225;ximo de resposta.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-19" href="#footnote-anchor-19" class="footnote-number" contenteditable="false" target="_self">19</a><div class="footnote-content"><p><strong>Aliena&#231;&#227;o fiduci&#225;ria</strong>: o devedor transfere a propriedade do bem ao credor como garantia e a recebe de volta quando quita a d&#237;vida. Voc&#234; dirige o carro, mas ele &#233; do banco at&#233; a &#250;ltima parcela.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-20" href="#footnote-anchor-20" class="footnote-number" contenteditable="false" target="_self">20</a><div class="footnote-content"><p><strong>Gravame</strong>: restri&#231;&#227;o registrada sobre um bem, avisando que ele garante uma d&#237;vida. Enquanto existir, o bem n&#227;o pode ser vendido livremente.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-21" href="#footnote-anchor-21" class="footnote-number" contenteditable="false" target="_self">21</a><div class="footnote-content"><p><strong>SERP</strong> (Sistema Eletr&#244;nico dos Registros P&#250;blicos): criado pela Lei 14.382/2022 para interligar os cart&#243;rios pela internet. Na parte de im&#243;veis, o operador &#233; o ONR.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-22" href="#footnote-anchor-22" class="footnote-number" contenteditable="false" target="_self">22</a><div class="footnote-content"><p><strong>Receb&#237;vel</strong>: dinheiro que algu&#233;m tem a receber no futuro. O exemplo cl&#225;ssico s&#227;o as vendas no cart&#227;o, que o lojista s&#243; recebe dias ou semanas depois.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-23" href="#footnote-anchor-23" class="footnote-number" contenteditable="false" target="_self">23</a><div class="footnote-content"><p><strong>Registradora</strong>: entidade autorizada pelo Banco Central a registrar ativos financeiros, dando publicidade a quem &#233; o dono de cada um e se ele j&#225; est&#225; comprometido.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-24" href="#footnote-anchor-24" class="footnote-number" contenteditable="false" target="_self">24</a><div class="footnote-content"><p><strong>Duplicata</strong>: t&#237;tulo que representa uma venda a prazo entre empresas. Hoje ela &#233; escritural, ou seja, existe apenas como registro eletr&#244;nico.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-25" href="#footnote-anchor-25" class="footnote-number" contenteditable="false" target="_self">25</a><div class="footnote-content"><p><strong>Onerar</strong>: comprometer um ativo como garantia. Um receb&#237;vel onerado j&#225; est&#225; prometido a um credor e n&#227;o pode ser prometido de novo.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-26" href="#footnote-anchor-26" class="footnote-number" contenteditable="false" target="_self">26</a><div class="footnote-content"><p><strong>Idempot&#234;ncia</strong>: propriedade de uma opera&#231;&#227;o que, repetida v&#225;rias vezes, produz o mesmo efeito de uma vez s&#243;. Apertar o bot&#227;o do elevador cinco vezes n&#227;o chama cinco elevadores.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-27" href="#footnote-anchor-27" class="footnote-number" contenteditable="false" target="_self">27</a><div class="footnote-content"><p><strong>SCR</strong> (Sistema de Informa&#231;&#245;es de Cr&#233;ditos): base do Banco Central com as opera&#231;&#245;es de cr&#233;dito de cada pessoa e empresa no sistema financeiro.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-28" href="#footnote-anchor-28" class="footnote-number" contenteditable="false" target="_self">28</a><div class="footnote-content"><p><strong>Open Finance</strong>: sistema regulado pelo Banco Central em que o cliente autoriza uma institui&#231;&#227;o a acessar os dados dele em outra.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-29" href="#footnote-anchor-29" class="footnote-number" contenteditable="false" target="_self">29</a><div class="footnote-content"><p><strong>D+N</strong>: prazo contado em dias &#250;teis a partir da data da opera&#231;&#227;o. D+2 significa dois dias &#250;teis depois.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-30" href="#footnote-anchor-30" class="footnote-number" contenteditable="false" target="_self">30</a><div class="footnote-content"><p><strong>E2E ID</strong> (<em>End-to-End ID</em>): identificador &#250;nico que acompanha cada Pix de ponta a ponta, do pagador ao recebedor.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Engenharia de Cobrança: Modelando Dados no DynamoDB]]></title><description><![CDATA[Este epis&#243;dio &#233; a vers&#227;o em &#225;udio de Engenharia de Cobran&#231;a: Modelando Dados no DynamoDB, Gerado por IA.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-cobranca-modelando</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-cobranca-modelando</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Sun, 27 Sep 2026 15:28:24 GMT</pubDate><enclosure url="https://api.substack.com/feed/podcast/217683389/4d713713b1eded0bf8139340734a3b09.mp3" length="0" type="audio/mpeg"/><content:encoded><![CDATA[<p>Este epis&#243;dio &#233; a vers&#227;o em &#225;udio de <strong>Engenharia de Cobran&#231;a: Modelando Dados no DynamoDB</strong>, <em>Gerado por IA.</em></p><p>Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p><p>Segunda-feira, 8h40. O canal de atendimento ao cliente est&#225; pegando fogo&#8230; o cl&#225;ssico pesadelo matinal de qualquer opera&#231;&#227;o! Ser&#225; que tudo est&#225; perdido? N&#227;o&#8230; por enquanto n&#227;o&#8230; &#128064;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption"></p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p>A conversa percorre as escolhas que separam um modelo que responde o neg&#243;cio de um que s&#243; grava entidade: por que a lista de perguntas vem antes da tabela, por que dias de atraso nunca deveriam estar gravados, por que CPF n&#227;o pode ir na chave de parti&#231;&#227;o, e por que um &#237;ndice de agenda sobrecarregado &#233; capaz de impedir o registro de um pagamento &#224;s 00h07.</p><p><strong>No epis&#243;dio:</strong></p><ul><li><p>A reuni&#227;o que ningu&#233;m quer marcar &#8212; e por que o ORM n&#227;o pode representar o neg&#243;cio nela</p></li><li><p>Grave o fato, derive o estado: a diferen&#231;a entre a data de nascimento e a idade</p></li><li><p>Num BaaS, a chave de parti&#231;&#227;o &#233; uma decis&#227;o de isolamento</p></li><li><p>&#205;ndice esparso: sair da r&#233;gua &#233; arrancar o post-it da geladeira</p></li><li><p>O engarrafamento do estacionamento que tranca a rua da ambul&#226;ncia</p></li><li><p>Onde o dinheiro realmente vaza &#8212; e por que n&#227;o &#233; armazenamento</p></li></ul><p>O artigo completo traz os exemplos o cat&#225;logo dos doze padr&#245;es de acesso, a anatomia da chave passo a passo e a conta de custo dos dois modelos lado a lado: <strong><a href="https://chroniclesofapragmaticprogrammer.substack.com/p/dynamodb-motor-de-cobranca?r=2615ny">Engenharia de Cobran&#231;a: Modelando Dados no DynamoDB</a></strong></p><p>Se voc&#234; opera cobran&#231;a, cr&#233;dito ou pagamentos em DynamoDB, me conta nos coment&#225;rios: qual foi a pergunta que a sua tabela n&#227;o soube responder?</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-cobranca-modelando/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-cobranca-modelando/comments"><span>Deixe um coment&#225;rio</span></a></p><p></p>]]></content:encoded></item><item><title><![CDATA[Engenharia de Cobrança: Modelando Dados no DynamoDB]]></title><description><![CDATA[Como modelar um motor de cobran&#231;a no DynamoDB para que cada chave responda a uma pergunta do neg&#243;cio, e n&#227;o crie um incidente.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/dynamodb-motor-de-cobranca</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/dynamodb-motor-de-cobranca</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Fri, 25 Sep 2026 14:48:18 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b8582dd7-294c-47d0-84b5-e488f5d0a85a_1200x630.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Segunda-feira, 8h40. No canal compartilhado com um dos clientes da plataforma, um varejista que vende no credi&#225;rio<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> e terceiriza a cobran&#231;a, a head de cobran&#231;a escreve tr&#234;s perguntas seguidas:</p><blockquote><p><em>Quantos contratos viraram 31+</em><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a><em> no fim de semana? Quanto disso tem promessa de pagamento</em><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a><em><strong><sup> </sup></strong>para esta semana? E por que o cliente do protocolo 88213 recebeu tr&#234;s SMS no domingo?</em></p></blockquote><p>S&#227;o perguntas absolutamente normais para quem opera uma carteira<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a>. E o time descobre, ao vivo, que nenhuma delas tem resposta barata.</p><p>A primeira exige um Scan na tabela inteira com filtro por dias de atraso, e o campo <code>daysPastDue</code> foi atualizado pela &#250;ltima vez na madrugada de sexta, porque o job de rec&#225;lculo falhou no s&#225;bado. A segunda depende de um array <code>promises</code> dentro do item do contrato, que ningu&#233;m consegue consultar sem ler todos os contratos. A terceira nem est&#225; no banco: tentativas de contato viraram linhas de log no CloudWatch, com reten&#231;&#227;o de trinta dias e sem nada que identifique o devedor.</p><p>Nenhuma das tr&#234;s falhas &#233; bug de c&#243;digo. S&#227;o decis&#245;es de modelagem que nunca foram tomadas. Algu&#233;m escolheu uma chave de parti&#231;&#227;o num refinamento, algu&#233;m adicionou um array &#8220;s&#243; por enquanto&#8221;, algu&#233;m decidiu que contato &#8220;&#233; log&#8221;. Cada escolha respondeu &#224; pergunta de engenharia do momento, <em>como eu gravo isso?</em>, e ignorou a pergunta do neg&#243;cio: <strong>quem vai perguntar o qu&#234; sobre esse dado, com que frequ&#234;ncia, e o que acontece se a resposta vier errada?</strong></p><p>Em dezembro do ano passado escrevi <em><a href="https://chroniclesofapragmaticprogrammer.substack.com/p/um-breve-mergulho-dynamodb">Um breve mergulho no DynamoDB</a></em>, sobre a mec&#226;nica: parti&#231;&#227;o e ordena&#231;&#227;o, particionamento vertical, a conta de RCU e WCU. Este texto sobe um andar. Ele n&#227;o &#233; sobre como a chave funciona; &#233; sobre como um tech lead ou arquiteto decide qual chave existe, qual &#237;ndice merece nascer, qual atributo deve ser gravado e qual deve ser calculado, e quanto cada uma dessas decis&#245;es custa em d&#243;lar, em lat&#234;ncia e em risco jur&#237;dico.</p><p>O fio condutor &#233; um motor de cobran&#231;a oferecido como BaaS<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a>: uma plataforma que atende dezenas de institui&#231;&#245;es (SCDs<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-6" href="#footnote-6" target="_self">6</a>, varejistas com credi&#225;rio pr&#243;prio, fintechs de consignado<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-7" href="#footnote-7" target="_self">7</a>), cada uma com sua carteira, sua pol&#237;tica de encargos e sua r&#233;gua<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-8" href="#footnote-8" target="_self">8</a>. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Vou cham&#225;-la de </mark><em><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Lastro</mark></strong></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">. Ela n&#227;o existe, mas cada problema descrito aqui existe.</mark></p><div class="callout-block" data-callout="true"><p><strong><span>Antes de come&#231;ar</span></strong></p><p>Assumo que voc&#234; conhece PK, SK, GSI e o modelo de cobran&#231;a por capacidade; este texto parte de onde o de dezembro parou. Os pre&#231;os citados s&#227;o da lista p&#250;blica da AWS em us-east-1 (setembro de 2026). A regi&#227;o de S&#227;o Paulo &#233; mais cara. Use os n&#250;meros como ordem de grandeza, n&#227;o como or&#231;amento. Os exemplos de c&#243;digo est&#227;o em TypeScript com o AWS SDK v3. Termos de neg&#243;cio marcados com um n&#250;mero pequeno t&#234;m explica&#231;&#227;o, com analogia, nas notas ao final.</p></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>1. Comece pela reuni&#227;o que ningu&#233;m quer marcar</strong></h2><p>O guia de design NoSQL da AWS &#233; direto num ponto que engenheiros formados no relacional subestimam: voc&#234; n&#227;o deveria come&#231;ar o schema no DynamoDB antes de saber quais perguntas ele precisa responder. No relacional, voc&#234; normaliza e deixa as perguntas para depois; o otimizador se vira. No DynamoDB, as estruturas s&#227;o desenhadas sob medida para os casos de uso do neg&#243;cio, e consultar fora deles &#233; caro e lento.</p><p>A consequ&#234;ncia organizacional &#233; mais importante que a t&#233;cnica. Se o schema &#233; derivado das perguntas, ent&#227;o <strong>a lista de perguntas &#233; o artefato de arquitetura mais importante do projeto</strong>. E ela n&#227;o pode ser escrita s&#243; por engenheiros.</p><blockquote><p><strong><span>Imagine assim</span></strong></p><p>Um banco relacional &#233; um supermercado: tudo est&#225; em algum corredor, e voc&#234; encontra qualquer coisa, desde que aceite andar. O DynamoDB &#233; a cozinha de um restaurante com a <em>mise en place</em> pronta: cada ingrediente j&#225; est&#225; cortado e posicionado para os pratos do card&#225;pio, e o prato sai em segundos.</p><p>Mas, se o cliente pedir algo que n&#227;o est&#225; no card&#225;pio, o cozinheiro precisa ir ao estoque e revirar prateleira por prateleira. Isso &#233; o Scan. A lista de padr&#245;es de acesso &#233; o card&#225;pio, e card&#225;pio n&#227;o se define s&#243; na cozinha: define-se com quem conhece os clientes.</p></blockquote><p>Em cobran&#231;a, as perguntas v&#234;m de cinco atores com necessidades radicalmente diferentes: <strong>a mesa de cobran&#231;a</strong><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-9" href="#footnote-9" target="_self">9</a> (o operador buscando um devedor, a supervisora olhando a carteira por faixa de atraso), <strong>a r&#233;gua autom&#225;tica</strong> (o scheduler que acorda e precisa saber o que fazer hoje), <strong>o pr&#243;prio devedor</strong> (no app ou no portal de autoatendimento, querendo ver o que deve e fechar um acordo), <strong>a concilia&#231;&#227;o</strong> (o webhook de liquida&#231;&#227;o<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-10" href="#footnote-10" target="_self">10</a> de Pix e boleto que precisa achar a cobran&#231;a em milissegundos) e <strong>o compliance</strong> (auditoria, ouvidoria, relat&#243;rio regulat&#243;rio).</p><p>A documenta&#231;&#227;o pede que voc&#234; entenda tr&#234;s propriedades de cada padr&#227;o de acesso: o tamanho do dado lido e gravado, a forma como ele precisa chegar e a velocidade, especialmente nos picos. Eu acrescento duas colunas que mudam tudo em cobran&#231;a: <strong>quem pergunta</strong> e <strong>qual &#233; o custo de neg&#243;cio se a resposta atrasar ou vier errada</strong>. O cat&#225;logo da Lastro ficou assim:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;markdown&quot;,&quot;nodeId&quot;:&quot;f9bc75ec-a4c0-46ed-994a-d8accf48947c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-markdown">ID    QUESTION                                          ASKED BY            VELOCITY / SHAPE
AP01  Everything about one debtor (360 view)            operator, app       ~40 req/s, one debtor, &lt; 50 ms
AP02  Open installments of a contract, oldest first     app, agreements     ~120 req/s, &lt; 30 ms
AP03  Contracts with a dunning action due today         dunning scheduler   daily burst, ~2M items in &lt; 20 min
AP04  Promises due today (and broken yesterday)         dunning scheduler   daily, ~80k items
AP05  Portfolio by delinquency bucket, largest first    supervisor desk     ~5 req/s, paginated, &lt; 200 ms
AP06  Find the charge behind a Pix txid / boleto number settlement webhook  peaks ~800 req/s on paydays
AP07  Was this settlement already applied?              settlement webhook  every webhook, MUST BE EXACT
AP08  How many contacts did this debtor get today?      contact gateway     every attempt, MUST BE EXACT
AP09  Full audit trail of one installment               compliance, ombuds  rare, &lt; 2 s
AP10  Contracts eligible for credit bureau reporting    dunning scheduler   daily
AP11  Monthly aging and roll rate of the portfolio      finance, BI         monthly  -&gt; NOT IN DYNAMODB
AP12  Regulatory and accounting reports                 compliance          monthly  -&gt; NOT IN DYNAMODB</code></pre></div><p>Tr&#234;s leituras dessa lista valem mais que qualquer diagrama.</p><ul><li><p><strong>A primeira est&#225; nas duas &#250;ltimas linhas.</strong> A pergunta mais valiosa do workshop &#233; &#8220;qual destas perguntas <em>n&#227;o</em> vamos responder aqui?&#8221;. Aging, roll rate<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-11" href="#footnote-11" target="_self">11</a> e relat&#243;rios regulat&#243;rios s&#227;o perguntas anal&#237;ticas, agrega&#231;&#245;es sobre a carteira inteira. DynamoDB responde perguntas com chave; agrega&#231;&#227;o sobre tudo &#233; Scan, e Scan &#233; o jeito mais caro de descobrir que voc&#234; escolheu o banco errado para aquela pergunta. Essas perguntas v&#227;o para o S3 via export e s&#227;o respondidas com Athena ou no lakehouse. Declarar isso no workshop evita que, seis meses depois, algu&#233;m crie um GSI sobre <code>status</code> (quatro valores poss&#237;veis, cardinalidade de cat&#225;strofe) para alimentar um dashboard.</p></li><li><p><strong>A segunda: velocidade n&#227;o &#233; m&#233;dia, &#233; forma.</strong> AP03 n&#227;o &#233; &#8220;25 requisi&#231;&#245;es por segundo&#8221;; &#233; dois milh&#245;es de itens numa janela curta e quase nada no resto do dia. AP06 n&#227;o &#233; uniforme: ele explode no quinto dia &#250;til, no dia do pagamento do INSS<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-12" href="#footnote-12" target="_self">12</a>, na v&#233;spera de feriado. S&#227;o esses picos, n&#227;o a m&#233;dia, que v&#227;o definir sharding e capacidade.</p></li><li><p><strong>A terceira: as linhas marcadas como exatas.</strong> AP07 e AP08 n&#227;o toleram leitura eventualmente consistente. Uma liquida&#231;&#227;o aplicada duas vezes &#233; dinheiro errado no saldo do cliente; um contato a mais pode ser uma liga&#231;&#227;o que a lei pro&#237;be. Isso j&#225; diz, antes de qualquer desenho, que essas perguntas n&#227;o podem ser respondidas por GSI, que s&#243; oferece leitura eventualmente consistente. Voltaremos a isso na se&#231;&#227;o 4.</p></li></ul><blockquote><p><strong><span>Ferramenta para o workshop</span></strong></p><p>A AWS mant&#233;m o NoSQL Workbench, que permite desenhar o modelo com dados de exemplo e executar as queries antes de existir uma linha de c&#243;digo. Use na reuni&#227;o, com a supervisora de cobran&#231;a presente: ver o pr&#243;prio &#8220;devedor de teste&#8221; aparecer inteiro numa &#250;nica query convence mais do que qualquer slide.</p></blockquote><div class="callout-block" data-callout="true"><p><strong><span>Pense comigo</span></strong></p><p>Se a gerente de cobran&#231;a n&#227;o estava na sala quando voc&#234;s listaram os padr&#245;es de acesso, quem representou o neg&#243;cio?</p><p>Muito provavelmente, o ORM. E o ORM s&#243; conhece uma pergunta: &#8220;me d&#225; a entidade por ID&#8221;.</p></div><div><hr></div><h4>Pontos altos</h4><ol><li><p>Em DynamoDB, a lista de perguntas vem antes da tabela. Ela &#233; o card&#225;pio que organiza a cozinha, e se escreve junto com o neg&#243;cio.</p></li><li><p>Para cada pergunta, anote quatro coisas: quem pergunta, em que formato a resposta precisa chegar, qual &#233; o pico de uso (n&#227;o a m&#233;dia) e o que acontece se a resposta vier errada.</p></li><li><p>Decida em voz alta o que a tabela n&#227;o vai responder. Relat&#243;rios que olham a carteira inteira v&#227;o para o S3, n&#227;o para um Scan.</p></li><li><p>Perguntas que n&#227;o podem errar nunca, como pagamento aplicado em dobro ou contato acima do limite legal, n&#227;o podem depender de &#237;ndice secund&#225;rio.</p></li></ol><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/dynamodb-motor-de-cobranca?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/dynamodb-motor-de-cobranca?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p><h2><strong>2. Cada atributo &#233; uma afirma&#231;&#227;o sobre o mundo</strong></h2><p>Um item no DynamoDB n&#227;o tem schema imposto al&#233;m da chave. Isso &#233; liberdade para o c&#243;digo e perigo para o neg&#243;cio: nada impede que o mesmo conceito exista com tr&#234;s nomes, em tr&#234;s formatos, em itens diferentes. A disciplina que o banco n&#227;o imp&#245;e precisa vir do modelo.</p><p>A regra que uso: <strong>cada atributo deve ser uma afirma&#231;&#227;o verific&#225;vel sobre a realidade do neg&#243;cio, com significado &#250;nico, unidade expl&#237;cita e dono claro</strong>. Vamos aos dois itens centrais da Lastro, o cabe&#231;alho do contrato e a parcela.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;json&quot;,&quot;nodeId&quot;:&quot;ad4f21ff-d5ba-4094-83c7-45cc5c8cb0a1&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-json">// CONTRACT header: one per contract, inside the debtor's item collection
{
  "PK": "T#t042#D#9f2c1a7e4b0d3e61",
  "SK": "CT#C-2026-004512",
  "type": "CONTRACT",
  "tenantId": "t042",
  "contractId": "C-2026-004512",
  "productCode": "CREDIARIO_12X",
  "currency": "BRL",
  "chargesPolicy": "CP#v3",
  "status": "IN_COLLECTION",
  "statusReason": "OLDEST_INSTALLMENT_OVERDUE",
  "oldestOverdueDueDate": "2026-08-10",
  "bucket": "D031_060",
  "outstandingCents": 292740,
  "nextActionDate": "2026-09-24",
  "nextAction": "SMS_D45",
  "shard": 7,
  "version": 22,
  "updatedAt": "2026-09-23T03:12:44Z"
}

// INSTALLMENT: one per installment, sorted under its contract
{
  "PK": "T#t042#D#9f2c1a7e4b0d3e61",
  "SK": "CT#C-2026-004512#IN#007",
  "type": "INSTALLMENT",
  "seq": 7,
  "originalDueDate": "2026-08-10",
  "dueDate": "2026-08-10",
  "principalCents": 41230,
  "interestCents": 7560,
  "fineCents": 0,
  "lateInterestCents": 0,
  "paidCents": 0,
  "status": "OVERDUE",
  "version": 9
}</code></pre></div><p>A primeira linha dos dois itens, <code>"PK": "T#t042#D#9f2c1a7e4b0d3e61"</code>, parece cr&#237;ptica, e &#233; de prop&#243;sito. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Por ora, leia assim: </mark><em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#8220;institui&#231;&#227;o t042, devedor cujo CPF, embaralhado, virou o c&#243;digo 9f2c1a7e4b0d3e61&#8221;</mark></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">.</mark> <strong>Na se&#231;&#227;o 3 desmonto essa chave pe&#231;a por pe&#231;a e mostro como se chega a ela.</strong></p><p>Nada nesses dois itens est&#225; ali por acaso. Vamos por partes.</p><h3><strong>Atraso &#233; do contrato; vencimento &#233; da parcela</strong></h3><p>A primeira decis&#227;o &#233; puramente de neg&#243;cio: dias de atraso n&#227;o pertencem &#224; parcela. No mercado, o atraso de um contrato &#233; medido pela parcela mais antiga em aberto, e &#233; esse n&#250;mero que define faixa, r&#233;gua, provis&#227;o<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-13" href="#footnote-13" target="_self">13</a> e negativa&#231;&#227;o<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-14" href="#footnote-14" target="_self">14</a>. Por isso <code>oldestOverdueDueDate</code> e <code>bucket</code> moram no cabe&#231;alho do contrato, e a parcela guarda apenas o pr&#243;prio vencimento e o pr&#243;prio status.</p><p>Se voc&#234; colocar faixa de atraso em cada parcela, o contrato do Jo&#227;o aparece tr&#234;s vezes na carteira da supervisora (uma por parcela vencida) e, cedo ou tarde, algu&#233;m soma o saldo em triplo num relat&#243;rio para a diretoria.</p><h3><strong>Grave o fato, derive o estado</strong></h3><p>Repare no que <em>n&#227;o</em> est&#225; no item: <code>daysPastDue</code>. Dias de atraso mudam todo dia, para todo contrato inadimplente, sem que nada tenha acontecido no mundo. Gravar esse n&#250;mero exige uma escrita por contrato por dia. Chamo isso de <strong>escrita sem evento</strong>, e ela &#233; o vazamento de custo e de risco mais comum que j&#225; vi em motores de cobran&#231;a.</p><p>O fato &#233; <code>oldestOverdueDueDate</code>. Ele s&#243; muda quando algo acontece: um pagamento quita a parcela mais antiga, um acordo reestrutura o contrato. Dias de atraso s&#227;o derivados na leitura, hoje menos essa data. No exemplo, em 24 de setembro o contrato est&#225; com 45 dias, exatamente o gatilho do <code>SMS_D45</code>. &#201; um c&#225;lculo de uma linha que nunca fica desatualizado porque o job da madrugada falhou.</p><p>J&#225; <code>bucket</code> &#233; estado derivado <em>gravado</em>, e a exce&#231;&#227;o &#233; deliberada. Ele precisa existir como atributo porque &#233; chave de um &#237;ndice (a carteira por faixa, que veremos adiante) e muda raramente: no m&#225;ximo uma vez a cada trinta dias por contrato. A regra geral fica assim: <strong>estado derivado s&#243; merece virar atributo quando &#233; necess&#225;rio numa chave e muda muito menos do que &#233; lido</strong>. Na se&#231;&#227;o 5 essa decis&#227;o vira dinheiro.</p><div class="callout-block" data-callout="true"><p><strong><span>Imagine assim</span></strong></p><p>O seu RG n&#227;o traz a sua idade. Traz a sua data de nascimento. A idade muda todo ano sem que nada aconte&#231;a com voc&#234;; a data de nascimento nunca muda. Se o documento trouxesse a idade, o pa&#237;s inteiro precisaria reemitir o RG a cada anivers&#225;rio, e sempre haveria documentos errados circulando.</p><p><code>oldestOverdueDueDate</code> &#233; a data de nascimento do atraso; <code>daysPastDue</code> &#233; a idade. E o <code>bucket</code>? &#201; a s&#233;rie escolar. D&#225; para calcular pela idade, mas a escola grava a s&#233;rie porque &#233; por ela que organiza as salas (o &#237;ndice), e ela s&#243; muda uma vez por ano.</p></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!RTHu!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!RTHu!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png 424w, https://substackcdn.com/image/fetch/$s_!RTHu!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png 848w, https://substackcdn.com/image/fetch/$s_!RTHu!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png 1272w, https://substackcdn.com/image/fetch/$s_!RTHu!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!RTHu!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png" width="1012" height="476.9881956155143" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:559,&quot;width&quot;:1186,&quot;resizeWidth&quot;:1012,&quot;bytes&quot;:81971,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/217078847?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!RTHu!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png 424w, https://substackcdn.com/image/fetch/$s_!RTHu!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png 848w, https://substackcdn.com/image/fetch/$s_!RTHu!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png 1272w, https://substackcdn.com/image/fetch/$s_!RTHu!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe3e92e19-30ba-4556-97fa-2c8475b2ec8d_1186x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Cinco dias sem nenhum evento no neg&#243;cio: ningu&#233;m pagou, ningu&#233;m negociou. O modelo A reemite o documento todo dia s&#243; para atualizar a idade impressa; o modelo B imprime a data de nascimento uma vez.</figcaption></figure></div><h3><strong>O vencimento original &#233; um fato; o vencimento vigente &#233; uma decis&#227;o</strong></h3><p><code>originalDueDate</code> nunca muda. <code>dueDate</code> muda quando h&#225; prorroga&#231;&#227;o<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-15" href="#footnote-15" target="_self">15</a>. Parece redundante at&#233; algu&#233;m do risco perguntar quanto da carteira de agosto foi prorrogada e quantas dessas prorroga&#231;&#245;es acabaram pagas. Se voc&#234; sobrescreveu, perdeu a resposta. Pense numa passagem a&#233;rea remarcada: o bilhete precisa mostrar o voo original e o novo, sen&#227;o ningu&#233;m sabe depois se o passageiro se atrasou ou se a companhia mudou o hor&#225;rio. &#201; o mesmo princ&#237;pio do artigo sobre modelagem de dados: voc&#234; guardou o resultado e jogou fora a causa.</p><p>E vencimento &#233; data civil, n&#227;o instante: <code>"2026-08-10"</code>, nunca <code>"2026-08-10T00:00:00Z"</code>. Meia-noite UTC &#233; 21h do dia anterior em Bras&#237;lia; um vencimento gravado como timestamp vai, em algum relat&#243;rio, aparecer um dia antes. Pior: a regra de mercado que permite pagar sem encargos no pr&#243;ximo dia &#250;til um boleto que vence em feriado &#233; uma regra de calend&#225;rio, n&#227;o de rel&#243;gio. Modele o conceito que o neg&#243;cio usa.</p><h3><strong>Dinheiro tem unidade, e encargo tem origem</strong></h3><p>Valores em centavos inteiros, com a moeda expl&#237;cita no contrato. O tipo Number do DynamoDB &#233; decimal de alta precis&#227;o, ent&#227;o o problema n&#227;o est&#225; no banco; est&#225; no caminho: JSON parseado como double num cliente JavaScript, uma biblioteca que converte para float no meio de um mapper. Inteiro em centavos n&#227;o tem ambiguidade em nenhuma camada.</p><p>A decomposi&#231;&#227;o em <code>principalCents</code>, <code>interestCents</code>, <code>fineCents</code> e <code>lateInterestCents</code> n&#227;o &#233; preciosismo. Ela existe porque o neg&#243;cio precisa provar duas coisas: a ordem de imputa&#231;&#227;o do pagamento<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-16" href="#footnote-16" target="_self">16</a> (o art. 354 do C&#243;digo Civil manda imputar primeiro nos juros vencidos e depois no capital, salvo estipula&#231;&#227;o em contr&#225;rio) e o respeito ao teto da multa morat&#243;ria<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-17" href="#footnote-17" target="_self">17</a> em rela&#231;&#245;es de consumo, de 2% pelo art. 52, &#167;1&#186;, do CDC. Um &#250;nico <code>amountCents</code> n&#227;o prova nenhuma das duas.</p><p>Note que multa e juros de mora aparecem zerados na parcela vencida. N&#227;o &#233; esquecimento. Encargo de atraso &#233; calculado quando vira fato (na liquida&#231;&#227;o ou no fechamento de um acordo<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-18" href="#footnote-18" target="_self">18</a>) a partir da pol&#237;tica vigente, referenciada no contrato por <code>chargesPolicy: "CP#v3"</code>. A pol&#237;tica &#233; um item versionado na pr&#243;pria tabela. Quando o tenant mudar a regra em mar&#231;o, os contratos antigos continuam apontando para a vers&#227;o sob a qual foram assinados. Recalcular encargo todo dia seria, de novo, escrita sem evento.</p><blockquote><p><em>Regra de neg&#243;cio &#233; dado versionado, n&#227;o um </em><code>if</code><em> que algu&#233;m alterou num PR de sexta-feira.</em></p></blockquote><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!f6nh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!f6nh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png 424w, https://substackcdn.com/image/fetch/$s_!f6nh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png 848w, https://substackcdn.com/image/fetch/$s_!f6nh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png 1272w, https://substackcdn.com/image/fetch/$s_!f6nh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!f6nh!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png" width="1010" height="451.29664179104475" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:479,&quot;width&quot;:1072,&quot;resizeWidth&quot;:1010,&quot;bytes&quot;:50156,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/217078847?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!f6nh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png 424w, https://substackcdn.com/image/fetch/$s_!f6nh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png 848w, https://substackcdn.com/image/fetch/$s_!f6nh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png 1272w, https://substackcdn.com/image/fetch/$s_!f6nh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe8c8c2ce-69ea-465e-8504-930ed6076186_1072x479.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Cinco dias sem nenhum evento no neg&#243;cio: ningu&#233;m pagou, ningu&#233;m negociou. O modelo A reemite o documento todo dia s&#243; para atualizar a idade impressa; o modelo B imprime a data de nascimento uma vez.</figcaption></figure></div><h3><strong>O estado n&#227;o carrega contexto</strong></h3><p><code>status</code> e <code>statusReason</code> s&#227;o atributos separados, pelo mesmo princ&#237;pio que defendi no artigo sobre m&#225;quinas de estado em cr&#233;dito: <code>IN_COLLECTION</code> &#233; um estado; o motivo &#233; atributo. Criar <code>IN_COLLECTION_BY_OVERDUE</code> e <code>IN_COLLECTION_BY_BROKEN_AGREEMENT</code> gera explos&#227;o combinat&#243;ria na m&#225;quina, nos &#237;ndices e nos relat&#243;rios.</p><h3><strong>A r&#233;gua e o pagamento v&#227;o correr um contra o outro</strong></h3><p>&#192;s 00h07 o scheduler est&#225; reagendando a pr&#243;xima a&#231;&#227;o do contrato. &#192;s 00h07 o webhook de Pix est&#225; aplicando um pagamento no mesmo contrato. Sem controle de concorr&#234;ncia, o &#250;ltimo a escrever vence, e o contrato quitado ganha um <code>nextAction</code> de SMS. O atributo <code>version</code>, com toda escrita condicionada a <code>version = :expected</code>, transforma essa corrida num erro expl&#237;cito e retent&#225;vel em vez de um dado silenciosamente errado. Imagine dois atendentes editando a mesma ficha em papel: quem entrega por &#250;ltimo apaga, sem saber, o que o outro escreveu. O <code>version</code> &#233; o n&#250;mero da p&#225;gina no rodap&#233; da ficha: se ele mudou desde que voc&#234; a pegou, devolva e pegue a vers&#227;o nova.</p><h3><strong>Nome de atributo custa bytes, mas legibilidade custa mais</strong></h3><p>O nome do atributo entra no tamanho do item; a AWS documenta isso nas restri&#231;&#245;es do servi&#231;o. Esse fato levou muita gente a nomes como <code>a1</code>, <code>dd</code>, <code>pc</code>. N&#227;o fa&#231;a isso. O item precisa ser leg&#237;vel por quem vai investigar um incidente &#224;s 3h da manh&#227; direto no console. O equil&#237;brio &#233; nome curto e sem&#226;ntico: <code>dueDate</code>, e n&#227;o <code>installmentCurrentDueDateLocal</code> nem <code>dd</code>. Na se&#231;&#227;o 5 fica claro que a economia de bytes que importa vem de outro lugar.</p><h3><strong>Chaves sint&#233;ticas est&#227;o saindo dos &#237;ndices</strong></h3><p>At&#233; novembro de 2025, um GSI tinha exatamente um atributo de parti&#231;&#227;o e um de ordena&#231;&#227;o. Isso obrigava a concatenar valores em strings como <code>GSI1PK = "T#t042#ACT#2026-09-24#07"</code> e, pior, a fazer backfill da tabela inteira toda vez que um &#237;ndice novo exigia uma chave nova. <em><strong>Desde ent&#227;o, GSIs aceitam at&#233; quatro atributos na parti&#231;&#227;o e quatro na ordena&#231;&#227;o, usando os atributos naturais do item, sem custo adicional.</strong></em></p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Isso &#233; mudan&#231;a de modelagem, n&#227;o s&#243; de sintaxe: &#237;ndices novos podem nascer a partir de atributos que j&#225; existem, e o item deixa de carregar strings que s&#243; servem ao banco.</mark> &#201; por isso que <code>tenantId</code> aparece duas vezes no contrato, dentro da PK e como atributo pr&#243;prio: &#233; o pre&#231;o, barato, de &#237;ndices com chaves naturais. A chave prim&#225;ria da tabela base continua com um atributo de cada, ent&#227;o PK e SK sint&#233;ticos continuam existindo ali. Antes de adotar, confira se o seu SDK e o seu mapper j&#225; suportam o recurso.</p><blockquote><p><strong><span>Pense comigo</span></strong></p><p>Em 2029, uma a&#231;&#227;o judicial pergunta por que a parcela 7 do contrato <code>C-2026-004512</code> foi liquidada com R$ 9,75 de multa. Qual atributo responde?</p><p>Se a resposta for &#8220;a gente recalcula com a regra atual&#8221;, voc&#234; n&#227;o tem uma resposta. Tem uma estimativa, e estimativa n&#227;o se defende.</p></blockquote><div><hr></div><h4>Pontos altos</h4><ol><li><p>O atraso pertence ao contrato e &#233; medido pela parcela mais antiga em aberto. O vencimento pertence a cada parcela.</p></li><li><p>Grave a data de nascimento, n&#227;o a idade: guarde o que muda quando algo acontece e calcule o que muda s&#243; porque o dia virou.</p></li><li><p>Um valor calculado s&#243; vira campo gravado quando organiza um &#237;ndice e muda raramente, como a s&#233;rie escolar.</p></li><li><p>Vencimento &#233; data do calend&#225;rio, n&#227;o hora do rel&#243;gio. Dinheiro &#233; n&#250;mero inteiro em centavos, separado pela origem: principal, juros, multa.</p></li><li><p>Regras de encargo s&#227;o versionadas, e cada contrato aponta para a vers&#227;o sob a qual foi assinado.</p></li><li><p>Toda escrita que pode concorrer com outra confere o n&#250;mero da vers&#227;o. Nomes de campo curtos, mas que qualquer pessoa entenda no console.</p></li></ol><h2><strong>3. Num BaaS, a chave de parti&#231;&#227;o &#233; uma decis&#227;o de isolamento</strong></h2><p>A documenta&#231;&#227;o da AWS descreve um bloco de multi-tenancy em que todos os dados de um tenant ficam na mesma cole&#231;&#227;o de itens, com o tenant como chave de parti&#231;&#227;o. Funciona muito bem para muitos tenants pequenos e parecidos, e oferece de brinde uma exclus&#227;o barata quando o tenant vai embora.</p><p>Num BaaS de cobran&#231;a, &#233; uma armadilha. O maior tenant da Lastro tem 60% dos contratos. Toda a escrita da r&#233;gua dele cairia numa &#250;nica chave, sujeita ao limite f&#237;sico de throughput por parti&#231;&#227;o (mil unidades de escrita por segundo). As alternativas &#243;bvias tamb&#233;m falham. CPF puro como PK mistura tenants: a mesma pessoa pode dever para o varejista e para a SCD, e cada institui&#231;&#227;o &#233; controladora<strong><a href="#n19"><sup>19</sup></a></strong> do pr&#243;prio dado perante a LGPD, ent&#227;o a cole&#231;&#227;o do devedor n&#227;o pode ser compartilhada. Contrato como PK fragmenta o devedor: a vis&#227;o 360 vira N consultas.</p><p>A escolha: <code>PK = "T#" + tenantId + "#D#" + debtorKey</code>. O tenant garante isolamento; o devedor garante cardinalidade.</p><div class="callout-block" data-callout="true"><p><strong><span>Imagine assim</span></strong></p><p>Pense na plataforma como um pr&#233;dio comercial. Usar o tenant como chave &#233; mandar todos os funcion&#225;rios da maior empresa do pr&#233;dio para um &#250;nico elevador: no hor&#225;rio de pico, ningu&#233;m sobe. Usar s&#243; o CPF &#233; guardar na mesma gaveta os pap&#233;is que duas empresas diferentes t&#234;m sobre a mesma pessoa, quando cada uma s&#243; pode ver os seus.</p><p>Tenant mais devedor &#233; cada empresa no seu andar, com um arm&#225;rio por cliente. Ningu&#233;m disputa elevador, e nenhum papel vai parar no andar errado.</p></div><h3><strong>N&#227;o coloque CPF na chave</strong></h3><p>Minha proposta de dezembro usava o CPF na chave. Hoje eu n&#227;o faria isso, e a raz&#227;o &#233; operacional, n&#227;o te&#243;rica: <strong>chaves vazam</strong>. Elas aparecem nas mensagens de erro que v&#227;o para o log, nos registros do stream que alimentam outros sistemas, nos payloads da DLQ e no CloudWatch Contributor Insights, cuja fun&#231;&#227;o &#233; justamente listar as chaves mais acessadas. CPF em chave &#233; CPF em todo lugar.</p><p><code>debtorKey</code> &#233; um HMAC-SHA256 do documento, com um segredo por tenant, truncado:</p><pre><code><code>import { createHmac } from "node:crypto";

// Same CPF, different tenants -&gt; different keys. No index needed to find a debtor.
export function debtorKey(tenantSecret: Buffer, document: string): string {
  const normalized = document.replace(/\D/g, "");
  return createHmac("sha256", tenantSecret)
    .update(normalized)
    .digest("hex")
    .slice(0, 16);
}</code></code></pre><p>Tr&#234;s propriedades importam. &#201; determin&#237;stico: o operador digita o CPF, o servi&#231;o calcula a chave e faz uma Query direta, sem &#237;ndice por CPF. N&#227;o &#233; revers&#237;vel sem o segredo. E o mesmo CPF gera chaves diferentes em tenants diferentes, o que impede correlacionar carteiras. Funciona como a ficha da chapelaria de um restaurante: o n&#250;mero encontra o seu casaco na hora, mas n&#227;o revela o seu nome a quem olha a ficha, e outro restaurante daria outro n&#250;mero para a mesma pessoa. O documento em si fica como atributo, criptografado no cliente; o AWS Database Encryption SDK faz criptografia por atributo exatamente para esse cen&#225;rio.</p><div class="callout-block" data-callout="true"><p><strong><span>O segredo &#233; parte da chave</span></strong></p><p>Escolha e guarde esse segredo com o mesmo cuidado com que se escolhe uma chave de parti&#231;&#227;o. Rotacion&#225;-lo significa recalcular a PK de todos os devedores do tenant, ou seja, migrar a tabela. Planeje versionamento do segredo desde o primeiro dia, ou aceite conscientemente que ele n&#227;o roda.</p></div><h3><strong>Anatomia da chave, pe&#231;a por pe&#231;a</strong></h3><p>Com as duas decis&#245;es tomadas (tenant mais devedor, e CPF nunca em claro), a chave se monta em quatro passos. Vamos seguir um CPF fict&#237;cio, <code>123.456.789-09</code>, de um devedor do tenant <code>t042</code>:</p><pre><code><code>step 1  normalize the document         "123.456.789-09"  -&gt;  "12345678909"
step 2  HMAC-SHA256 with t042's secret  -&gt;  "9f2c1a7e4b0d3e61a84c&#8230;e07b"   (64 hex chars)
step 3  keep the first 16 characters    -&gt;  "9f2c1a7e4b0d3e61"             (debtorKey)
step 4  add tenant and entity prefixes  -&gt;  "T#" + "t042" + "#D#" + "9f2c1a7e4b0d3e61"

PK = "T#t042#D#9f2c1a7e4b0d3e61"</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Jkkf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Jkkf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png 424w, https://substackcdn.com/image/fetch/$s_!Jkkf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png 848w, https://substackcdn.com/image/fetch/$s_!Jkkf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png 1272w, https://substackcdn.com/image/fetch/$s_!Jkkf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Jkkf!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png" width="990" height="295.0888030888031" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:386,&quot;width&quot;:1295,&quot;resizeWidth&quot;:990,&quot;bytes&quot;:62188,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/217078847?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Jkkf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png 424w, https://substackcdn.com/image/fetch/$s_!Jkkf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png 848w, https://substackcdn.com/image/fetch/$s_!Jkkf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png 1272w, https://substackcdn.com/image/fetch/$s_!Jkkf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fee8c1b9e-946b-45cb-a910-086f0f06c20b_1295x386.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Toda chave da tabela segue a mesma gram&#225;tica: prefixo que diz o que vem depois, valor, separador.</figcaption></figure></div><div class="pullquote"><p>Cada peda&#231;o da chave responde a uma pergunta: de qual institui&#231;&#227;o, de que tipo de coisa, de quem.</p></div><p><strong>Do geral para o espec&#237;fico, como um endere&#231;o.</strong> Voc&#234; escreve um endere&#231;o come&#231;ando pelo estado, depois a cidade, a rua e o n&#250;mero, e o carteiro vai estreitando a busca a cada peda&#231;o. A chave faz o mesmo: primeiro a institui&#231;&#227;o, depois o tipo de coisa, depois quem. Essa ordem n&#227;o &#233; est&#233;tica. &#201; ela que permite dizer ao IAM &#8220;este papel s&#243; pode tocar chaves que come&#231;am com <code>T#t042#</code>&#8220;, porque prefixo &#233; a &#250;nica coisa que uma regra de acesso consegue comparar.</p><p><strong>Prefixos dizem o que vem depois.</strong> A mesma tabela guarda fam&#237;lias de chave diferentes, e o prefixo &#233; o que impede que elas se confundam:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;4081392d-0773-4f3d-aef8-9baca62c07b7&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">T#t042#D#9f2c1a7e4b0d3e61       debtor item collection (profile, contracts, installments...)
T#t042#CHG#&lt;txid&gt;               charge lookup           (section 4)
T#t042#STL#&lt;settlementId&gt;       settlement idempotency  (section 4)</code></pre></div><p>Sem o <code>D#</code>, <code>CHG#</code> e <code>STL#</code>, nada impediria que o identificador de uma liquida&#231;&#227;o coincidisse, por acaso, com o c&#243;digo de um devedor. Com o prefixo, a colis&#227;o entre fam&#237;lias &#233; imposs&#237;vel por constru&#231;&#227;o, e quem abre o console &#224;s 3h da manh&#227; sabe o que est&#225; olhando sem ler o c&#243;digo.</p><p><strong>Por que </strong><code>#</code><strong> como separador?</strong> Porque esse caractere nunca aparece nos identificadores do neg&#243;cio, <em><strong>e porque &#233; a conven&#231;&#227;o que a pr&#243;pria documenta&#231;&#227;o da AWS usa nas chaves compostas.</strong></em> Qualquer caractere que n&#227;o apare&#231;a nos valores serviria; o importante &#233; escolher um e nunca mais mudar.</p><p><strong>Por que letras t&#227;o curtas?</strong> A chave se repete em todos os itens da cole&#231;&#227;o e em todos os &#237;ndices que a projetam, e o tamanho dela entra na conta de cada item. <code>T</code> e <code>D</code> bastam, desde que o time tenha o dicion&#225;rio por escrito, <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">no README ou no ADR:</mark></p><pre><code><code>T        tenant                 CT       contract
D        debtor                 IN       installment
CHG      charge (Pix / boleto)  AG       agreement
STL      settlement             EV       event (audit trail)
PROFILE  debtor profile         LIMIT    daily contact counter
CONTACT  contact attempt        PROMISE  payment promise</code></code></pre><p>Uma chave curta s&#243; &#233; leg&#237;vel se existir o dicion&#225;rio. Sem ele, <code>T#t042#D#</code> vira hier&#243;glifo no primeiro rod&#237;zio do time.</p><p><strong>Por que o tenant aparece em claro e o devedor n&#227;o?</strong> O identificador da institui&#231;&#227;o n&#227;o &#233; dado pessoal e precisa estar leg&#237;vel para a regra de acesso e para a opera&#231;&#227;o. Quando o Contributor Insights mostrar uma chave quente, <code>T#t042</code> diz na hora qual cliente est&#225; gerando a carga, enquanto o c&#243;digo do devedor esconde quem &#233; a pessoa. Voc&#234; sabe qual cliente est&#225; pegando fogo sem saber o nome de ningu&#233;m.</p><p><strong>Por que s&#243; 16 caracteres do hash?</strong> Dezesseis caracteres hexadecimais s&#227;o 64 bits, um espa&#231;o de 18 quintilh&#245;es de valores. Para um tenant com 10 milh&#245;es de devedores, a chance de dois CPFs diferentes gerarem o mesmo c&#243;digo fica na ordem de uma em algumas centenas de milhares: baixa, mas n&#227;o zero. Por isso o cadastro do devedor grava o item <code>PROFILE</code> com o documento criptografado e, ao criar um devedor novo, confere se o documento bate com o que j&#225; est&#225; l&#225;. Uma colis&#227;o improv&#225;vel vira um erro detect&#225;vel, e nunca dois devedores misturados na mesma pasta.<mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> Se voc&#234; preferir n&#227;o conviver com esse cen&#225;rio, use 20 caracteres (80 bits): a chance cai para algo desprez&#237;vel, ao custo de quatro bytes a mais por item.</mark></p><h3><strong>Isolamento no IAM, n&#227;o s&#243; no c&#243;digo</strong></h3><p>Com o prefixo do tenant na chave, o isolamento pode ser imposto pela pol&#237;tica de acesso. A condi&#231;&#227;o <code>dynamodb:LeadingKeys</code> restringe quais valores de parti&#231;&#227;o um papel pode tocar. A camada de API assume um papel com o tenant como tag de sess&#227;o, e a pol&#237;tica s&#243; aceita chaves que comecem com o prefixo daquele tenant:</p><pre><code><code>{
  "Effect": "Allow",
  "Action": ["dynamodb:GetItem", "dynamodb:Query", "dynamodb:PutItem", "dynamodb:UpdateItem"],
  "Resource": "arn:aws:dynamodb:*:*:table/collections",
  "Condition": {
    "ForAllValues:StringLike": {
      "dynamodb:LeadingKeys": ["T#${aws:PrincipalTag/tenantId}#*"]
    }
  }
}</code></code></pre><p>Um bug que monte a chave do tenant errado vira <code>AccessDeniedException</code>, n&#227;o vazamento de dados entre institui&#231;&#245;es. Num BaaS regulado, essa diferen&#231;a cabe inteira num parecer de seguran&#231;a.</p><h3><strong>O pre&#231;o dessa escolha: offboarding</strong></h3><p>Nenhuma chave &#233; gr&#225;tis. Com o tenant como parti&#231;&#227;o, apagar um tenant seria uma Query seguida de deletes. Com o devedor como parti&#231;&#227;o, os dados de um tenant est&#227;o espalhados por milh&#245;es de cole&#231;&#245;es. Quando um tenant encerra o contrato e pede exclus&#227;o, voc&#234; precisa enumer&#225;-los: um export filtrado, ou um GSI esparso com proje&#231;&#227;o <code>KEYS_ONLY</code> sobre os itens <code>PROFILE</code>, particionado por <code>tenantId</code>. Registre esse custo no ADR. Trade-off n&#227;o documentado &#233; d&#237;vida com data de vencimento desconhecida.</p><h3><strong>A cole&#231;&#227;o do devedor</strong></h3><p>Tudo o que a plataforma sabe sobre um devedor, num tenant, mora na mesma cole&#231;&#227;o de itens, ordenado pela sort key. Pense numa pasta suspensa de arquivo com abas separadoras em ordem alfab&#233;tica: puxar a pasta inteira &#233; a vis&#227;o 360; abrir direto na aba das parcelas &#233; um <code>begins_with</code>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!nq3y!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!nq3y!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png 424w, https://substackcdn.com/image/fetch/$s_!nq3y!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png 848w, https://substackcdn.com/image/fetch/$s_!nq3y!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png 1272w, https://substackcdn.com/image/fetch/$s_!nq3y!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!nq3y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png" width="1126" height="657" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:657,&quot;width&quot;:1126,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:126585,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/217078847?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!nq3y!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png 424w, https://substackcdn.com/image/fetch/$s_!nq3y!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png 848w, https://substackcdn.com/image/fetch/$s_!nq3y!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png 1272w, https://substackcdn.com/image/fetch/$s_!nq3y!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0f2704f2-ff0a-4e77-821d-01bbe9475bf3_1126x657.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Ordem lexicogr&#225;fica: CONTACT &lt; CT &lt; LIMIT &lt; PROFILE &lt; PROMISE. Dentro do contrato: AG &lt; EV &lt; IN.</figcaption></figure></div><p>Os prefixos da sort key n&#227;o s&#227;o est&#233;tica: s&#227;o o plano de consultas. Cada faixa cont&#237;gua &#233; uma pergunta de neg&#243;cio respondida com uma &#250;nica Query.</p><p>A vis&#227;o 360 (AP01) &#233; uma Query na parti&#231;&#227;o, sem filtro. As parcelas do contrato (AP02) s&#227;o uma Query com <code>begins_with</code> no prefixo <code>#IN#</code>. A trilha de auditoria da parcela 7 (AP09) &#233; outra, no prefixo <code>#EV#007#</code>. Os prefixos foram escolhidos para que a ordem alfab&#233;tica coincida com a necessidade de leitura: eventos ficam antes das parcelas porque <code>E</code> vem antes de <code>I</code>, e por isso o prefixo das parcelas nunca devolve eventos por acidente.</p><p>Um detalhe que j&#225; gerou chamado em produ&#231;&#227;o: a sort key &#233; string, ent&#227;o a sequ&#234;ncia precisa de zeros &#224; esquerda. Sem eles, <code>IN#10</code> vem antes de <code>IN#2</code>, e o app mostra a parcela dez antes da segunda. Ordem lexicogr&#225;fica &#233; regra de neg&#243;cio disfar&#231;ada de detalhe t&#233;cnico.</p><h3><strong>Hist&#243;rico n&#227;o mora dentro do item</strong></h3><p>A tenta&#231;&#227;o &#233; &#243;bvia: um array <code>statusHistory</code> dentro da parcela. O problema t&#233;cnico &#233; que, numa atualiza&#231;&#227;o, o DynamoDB cobra a escrita pelo maior tamanho entre o item antes e depois da mudan&#231;a. Cada update na parcela passa a pagar pelo hist&#243;rico inteiro. &#201; como levar o &#225;lbum de fam&#237;lia completo ao cart&#243;rio toda vez que voc&#234; s&#243; precisa trocar a foto do documento. Uma parcela com dezoito meses de eventos passa de 1 KB e dobra o custo de toda escrita futura, inclusive a do pagamento. O problema de neg&#243;cio &#233; que o array n&#227;o &#233; consult&#225;vel: a auditoria vira &#8220;baixar tudo e procurar&#8221;.</p><p>A sa&#237;da &#233; o particionamento vertical que a pr&#243;pria AWS recomenda: eventos como itens irm&#227;os (<code>#EV#</code>), append-only. Cada evento custa uma escrita pequena, uma &#250;nica vez, e s&#243; &#233; lido quando algu&#233;m pergunta.</p><h3><strong>Cuidado com LSI</strong></h3><p>Um &#237;ndice secund&#225;rio local s&#243; pode ser criado junto com a tabela e, quando existe, imp&#245;e um limite de 10 GB a cada cole&#231;&#227;o de itens. A cole&#231;&#227;o de um devedor antigo, com anos de contatos e eventos, &#233; exatamente o tipo de coisa que cresce sem ningu&#233;m olhar. Prefira GSI, que pode nascer depois e n&#227;o imp&#245;e esse teto.</p><blockquote><p><strong><span>Pense comigo</span></strong></p><p>Se o seu maior tenant dobrar de tamanho amanh&#227;, qual chave esquenta primeiro? Se a resposta for &#8220;a do tenant&#8221;, o seu modelo tem um teto de crescimento que ningu&#233;m escreveu em lugar nenhum, e ele vai ser descoberto no pior dia poss&#237;vel.</p></blockquote><div><hr></div><h4>Pontos altos</h4><ol><li><p>Num BaaS, a chave junta institui&#231;&#227;o e devedor: cada inquilino no seu andar, cada devedor no seu arm&#225;rio.</p></li><li><p>Documento pessoal nunca vai na chave. Use um c&#243;digo derivado (HMAC), como a ficha da chapelaria: encontra o casaco sem expor o nome.</p></li><li><p>O isolamento entre institui&#231;&#245;es &#233; garantido pela pol&#237;tica de acesso da AWS, e n&#227;o s&#243; pelo cuidado do c&#243;digo.</p></li><li><p>Toda chave tem um pre&#231;o. O desta &#233; o trabalho de remover um tenant inteiro; escreva isso no ADR.</p></li><li><p>A ordem das abas da pasta &#233; planejada: prefixos pensados para as perguntas e n&#250;meros com zeros &#224; esquerda.</p></li><li><p>Hist&#243;rico vira folhas novas na pasta, nunca um anexo que cresce dentro do mesmo item.</p></li></ol><h2><strong>4. &#205;ndices secund&#225;rios s&#227;o respostas, n&#227;o atalhos</strong></h2><p>Cada GSI &#233; uma c&#243;pia parcial da tabela, com capacidade, custo de escrita e custo de armazenamento pr&#243;prios. Por isso cada &#237;ndice deve nascer com o nome da pergunta que responde. Na Lastro, s&#227;o dois &#237;ndices de neg&#243;cio e dois tipos de item de lookup, e a escolha entre um e outro diz muito sobre maturidade de modelagem.</p><h3><strong>GSI1: a r&#233;gua do dia &#233; um &#237;ndice esparso</strong></h3><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;38e9ec1e-9312-469c-9c7a-e1d6ce3af773&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">GSI1  dunning-agenda          sparse: only items that have nextActionDate
  partition key   tenantId (S) + nextActionDate (S) + shard (N)
  sort key        nextAction (S) + contractId (S)
  projection      INCLUDE debtorKey, bucket, outstandingCents, channelPrefs</code></pre></div><p>A AWS descreve o &#237;ndice esparso como um bloco de constru&#231;&#227;o: s&#243; entram no GSI os itens que possuem os atributos de chave. Com chaves de m&#250;ltiplos atributos vale a mesma regra; se faltar qualquer componente, o item fica de fora. Na Lastro, apenas contratos em cobran&#231;a ativa e promessas pendentes t&#234;m <code>nextActionDate</code>.</p><p>O efeito &#233; elegante. Quando um pagamento cura o contrato, a mesma transa&#231;&#227;o que baixa a parcela remove <code>nextActionDate</code> e <code>nextAction</code>, e o contrato sai da r&#233;gua. N&#227;o existe &#8220;job para limpar a r&#233;gua&#8221;. <strong>Sair da r&#233;gua &#233; remover um atributo.</strong> A modelagem est&#225; dizendo, em estrutura de dados, uma regra de neg&#243;cio: r&#233;gua s&#243; existe para quem deve.</p><div class="callout-block" data-callout="true"><p><strong><span>Imagine assim</span></strong></p><p>A r&#233;gua &#233; o quadro de post-its na porta da geladeira. S&#243; vai para o quadro o que precisa ser feito, cada post-it com a sua data. Quando a tarefa acaba, voc&#234; arranca o post-it; ningu&#233;m precisa &#8220;fazer faxina no quadro&#8221; no fim do m&#234;s.</p><p>A tabela &#233; a casa inteira. O &#237;ndice esparso &#233; s&#243; a porta da geladeira, e &#233; por isso que ele &#233; pequeno, barato e r&#225;pido de ler.</p></div><p>Promessas de pagamento usam o mesmo &#237;ndice, com <code>nextAction = PROMISE_CHECK</code> na data prometida. A elegibilidade para negativa&#231;&#227;o (AP10) tamb&#233;m: <code>BUREAU_REPORT</code> &#233; s&#243; mais uma a&#231;&#227;o agendada, na data em que o contrato passa a ser eleg&#237;vel pela pol&#237;tica do tenant. Um &#237;ndice, tr&#234;s perguntas, porque as tr&#234;s t&#234;m a mesma forma: <em>o que precisa acontecer para este tenant nesta data</em>. E, como o sort key come&#231;a por <code>nextAction</code>, o worker que s&#243; envia SMS consulta s&#243; os SMS.</p><p>H&#225; ainda um benef&#237;cio de custo que a documenta&#231;&#227;o destaca: um &#237;ndice esparso s&#243; cobra escrita e armazenamento dos itens que entram nele. A r&#233;gua indexa os 25% da carteira em cobran&#231;a, n&#227;o os 100%.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!CM1c!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!CM1c!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png 424w, https://substackcdn.com/image/fetch/$s_!CM1c!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png 848w, https://substackcdn.com/image/fetch/$s_!CM1c!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png 1272w, https://substackcdn.com/image/fetch/$s_!CM1c!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!CM1c!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png" width="958" height="441.3821989528796" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:528,&quot;width&quot;:1146,&quot;resizeWidth&quot;:958,&quot;bytes&quot;:110606,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/217078847?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!CM1c!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png 424w, https://substackcdn.com/image/fetch/$s_!CM1c!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png 848w, https://substackcdn.com/image/fetch/$s_!CM1c!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png 1272w, https://substackcdn.com/image/fetch/$s_!CM1c!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb56cc272-d26b-49c9-a060-0e7a9dfd90aa_1146x528.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">O quadro de post-its da geladeira: s&#243; est&#225; no &#237;ndice o que precisa ser feito, e concluir a tarefa &#233; arrancar o post-it....</figcaption></figure></div><h3><strong>O shard &#233; a unidade de paralelismo do seu batch</strong></h3><p>Primeiro, a palavra. <em>Shard</em>, em ingl&#234;s, &#233; caco, fragmento, o peda&#231;o de algo que se partiu. Em banco de dados, fazer <em>sharding</em> &#233; dividir de prop&#243;sito um volume grande em peda&#231;os menores, guardados em lugares diferentes, para que nenhum peda&#231;o receba trabalho demais sozinho. Na Lastro, o shard &#233; apenas um n&#250;mero de 0 a 15 gravado no contrato, que diz em qual dos dezesseis peda&#231;os do &#237;ndice aquele contrato mora.</p><p>Sem o atributo <code>shard</code>, a parti&#231;&#227;o do &#237;ndice seria apenas tenant e data. Toda madrugada, depois de executar as a&#231;&#245;es do dia, o scheduler reagenda a pr&#243;xima a&#231;&#227;o de cada contrato. Mudar <code>nextActionDate</code> &#233; mudar a chave do &#237;ndice, e para o GSI isso significa remover o item de uma posi&#231;&#227;o e inseri-lo em outra. No maior tenant, s&#227;o cerca de 1,2 milh&#227;o de contratos convergindo na mesma chave de &#8220;amanh&#227;&#8221;.</p><p>E aqui mora o detalhe que transforma um problema de performance num incidente de neg&#243;cio. A AWS avisa que throttling de escrita num GSI devolve back-pressure para a tabela base: quando o &#237;ndice n&#227;o aguenta, as escritas na tabela falham. Quem mais escreve na tabela base &#224;s 00h07? O webhook de liquida&#231;&#227;o.</p><p>O cliente que pagou o Pix &#224; meia-noite para &#8220;limpar o nome&#8221; gera um webhook que toma throttling, os retries se esgotam, a mensagem cai na DLQ e a baixa s&#243; entra &#224;s 9h30, no reprocessamento manual. &#192;s 8h, a r&#233;gua mandou SMS de cobran&#231;a para quem j&#225; tinha pago. Reclama&#231;&#227;o no Procon, post no Reclame Aqui, risco de dano moral. Tudo porque um &#237;ndice de <em>agenda</em> estava quente.</p><div class="callout-block" data-callout="true"><p><strong><span>Imagine assim</span></strong></p><p>&#201; o engarrafamento na sa&#237;da do estacionamento de um shopping que tranca a rua por onde passa a ambul&#226;ncia. O estacionamento (a r&#233;gua, que pode esperar) n&#227;o tem nada a ver com o hospital (o pagamento, que n&#227;o pode esperar), mas os dois usam a mesma rua: a tabela base.</p></div><p>A sa&#237;da &#233; o write sharding com sufixo calculado: <code>shard = hash(contractId) mod N</code>. O N sai de uma conta simples, o pico de escritas por segundo esperado no &#237;ndice dividido por mil, com folga para crescimento. O sufixo &#233; calculado, n&#227;o aleat&#243;rio, para que qualquer contrato saiba sempre em que shard est&#225;.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!i2JI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!i2JI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png 424w, https://substackcdn.com/image/fetch/$s_!i2JI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png 848w, https://substackcdn.com/image/fetch/$s_!i2JI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png 1272w, https://substackcdn.com/image/fetch/$s_!i2JI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!i2JI!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png" width="962" height="474.23655913978496" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/07e1aa20-750b-4966-a7df-784d053be887_1209x596.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:596,&quot;width&quot;:1209,&quot;resizeWidth&quot;:962,&quot;bytes&quot;:91700,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/217078847?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!i2JI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png 424w, https://substackcdn.com/image/fetch/$s_!i2JI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png 848w, https://substackcdn.com/image/fetch/$s_!i2JI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png 1272w, https://substackcdn.com/image/fetch/$s_!i2JI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F07e1aa20-750b-4966-a7df-784d053be887_1209x596.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">O sharding que protege o banco &#233; o mesmo que distribui o trabalho: cada shard &#233; uma fila natural para um worker, sem locks nem coordena&#231;&#227;o.</figcaption></figure></div><p>O insight que costuma passar despercebido: <strong>o shard n&#227;o &#233; gambiarra de banco. &#201; exatamente a unidade de paralelismo que o seu batch j&#225; queria.</strong> &#192;s 8h, dezesseis workers fazem, cada um, uma Query paginada no pr&#243;prio shard. Sem coordena&#231;&#227;o, sem locks, sem a eterna pergunta &#8220;quem pegou este contrato?&#8221;. O desenho que protege a parti&#231;&#227;o &#233; o mesmo que distribui o trabalho. Pense nos caixas de um supermercado: com uma fila &#250;nica, tudo trava; com dezesseis caixas e uma regra fixa para escolher o caixa (o final do n&#250;mero do contrato, digamos), a fila anda, e cada caixa j&#225; tem o seu operador.</p><p>Sobre a proje&#231;&#227;o: <code>INCLUDE</code> apenas com o que o worker precisa para decidir e agir. Cada atributo projetado &#233; armazenamento replicado e custo extra em toda escrita que o altere. Projetar <code>ALL</code> &#8220;por garantia&#8221; duplica a tabela.</p><h3><strong>GSI2: a carteira da supervisora</strong></h3><pre><code><code>GSI2  portfolio-by-bucket     sparse: only CONTRACT items that have bucket
  partition key   tenantId (S) + bucket (S)
  sort key        outstandingCents (N) + contractId (S)
  projection      INCLUDE debtorKey, oldestOverdueDueDate, productCode, lastContactAt</code></code></pre><p>Uma Query com <code>ScanIndexForward = false</code> devolve os maiores saldos primeiro; os dias de atraso s&#227;o calculados na tela a partir de <code>oldestOverdueDueDate</code>. &#201; aqui que a decis&#227;o da se&#231;&#227;o 2 aparece com clareza. Se <code>daysPastDue</code> fosse chave de ordena&#231;&#227;o, cada contrato inadimplente mudaria de posi&#231;&#227;o no &#237;ndice todos os dias. Com a faixa na parti&#231;&#227;o, o contrato se move no m&#225;ximo uma vez por m&#234;s. <strong>Nunca coloque numa chave de &#237;ndice um atributo que muda com o calend&#225;rio.</strong></p><p><code>outstandingCents</code> na ordena&#231;&#227;o muda com pagamento parcial, o que &#233; raro e at&#233; desej&#225;vel: o contrato desce na lista. A parti&#231;&#227;o tem baixa cardinalidade (poucas faixas por tenant), e a documenta&#231;&#227;o recomenda considerar sharding tamb&#233;m em GSIs assim. Aqui o volume de escrita &#233; baixo, ent&#227;o a decis&#227;o foi come&#231;ar sem shard e alarmar as m&#233;tricas de throttling do &#237;ndice. Nem todo &#237;ndice precisa de shard; todo &#237;ndice precisa de alarme.</p><div class="callout-block" data-callout="true"><p><strong><span>Quando a mesa quer filtrar por tudo</span></strong></p><p>Se a supervisora pedir filtros livres (produto, cidade, faixa, canal, &#250;ltimo contato, score), a resposta n&#227;o &#233; GSI3, GSI4 e GSI5. &#201; um motor de busca, como o OpenSearch, alimentado pelo stream da tabela. DynamoDB &#233; excelente em perguntas conhecidas e ruim em perguntas abertas. O erro de arquitetura &#233; tentar transform&#225;-lo, &#237;ndice a &#237;ndice, no que ele n&#227;o &#233;.</p></div><h3><strong>Onde GSI n&#227;o serve: exatid&#227;o</strong></h3><p>GSI s&#243; oferece leitura eventualmente consistente. Na maior parte do tempo a propaga&#231;&#227;o &#233; r&#225;pida, mas &#8220;na maior parte do tempo&#8221; n&#227;o &#233; contrato. O GSI &#233; o quadro de avisos que uma secret&#225;ria atualiza copiando do livro oficial: quase sempre em dia, mas, quando o assunto &#233; dinheiro, voc&#234; consulta o livro. No Pix, o QR Code &#233; gerado e pago em segundos. Se o webhook procurar a cobran&#231;a pelo <code>txid</code><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-19" href="#footnote-19" target="_self">19</a>num GSI, existe uma janela em que a cobran&#231;a rec&#233;m-criada ainda n&#227;o chegou ao &#237;ndice. &#8220;Cobran&#231;a n&#227;o encontrada&#8221; num webhook de liquida&#231;&#227;o &#233; dinheiro sem dono.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Zkxm!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Zkxm!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png 424w, https://substackcdn.com/image/fetch/$s_!Zkxm!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png 848w, https://substackcdn.com/image/fetch/$s_!Zkxm!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png 1272w, https://substackcdn.com/image/fetch/$s_!Zkxm!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Zkxm!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png" width="968" height="398.3826714801444" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:456,&quot;width&quot;:1108,&quot;resizeWidth&quot;:968,&quot;bytes&quot;:63849,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/217078847?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Zkxm!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png 424w, https://substackcdn.com/image/fetch/$s_!Zkxm!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png 848w, https://substackcdn.com/image/fetch/$s_!Zkxm!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png 1272w, https://substackcdn.com/image/fetch/$s_!Zkxm!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F68104cf3-50ac-4374-ac5a-3f0f91304afe_1108x456.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">O &#237;ndice &#233; o quadro de avisos copiado do livro oficial. Quando o assunto &#233; dinheiro, consulte o livro.</figcaption></figure></div><p>Por isso AP06 e AP07 s&#227;o respondidos por <strong>itens de lookup na tabela base</strong>, lidos com consist&#234;ncia forte:</p><pre><code><code>// Written in the same transaction that creates the charge
PK = "T#t042#CHG#&lt;txid or boleto number&gt;"     SK = "CHG"
     -&gt; debtorKey, contractId, seq, amountCents, expiresAt

// Written in the same transaction that applies the payment
PK = "T#t042#STL#&lt;settlementId&gt;"              SK = "STL"
     -&gt; amountCents, receivedAt, appliedTo</code></code></pre><p><span>O item de liquida&#231;&#227;o </span><em>&#233;</em><span> a idempot&#234;ncia. Pense no carimbo de PAGO num boleto de papel: se a mesma via voltar ao caixa, o carimbo j&#225; est&#225; l&#225;, e ningu&#233;m recebe duas vezes. O webhook vai chegar duas vezes: o provedor reenviou, o consumer reprocessou, algu&#233;m rodou o replay da DLQ. A transa&#231;&#227;o que aplica o pagamento inclui um Put condicionado &#224; inexist&#234;ncia desse item. Na segunda tentativa, a transa&#231;&#227;o inteira &#233; cancelada e o saldo n&#227;o &#233; baixado duas vezes.</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;csharp&quot;,&quot;nodeId&quot;:&quot;91b56051-ac34-4916-888b-4e59d58aa3e6&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-csharp">import { TransactWriteCommand } from "@aws-sdk/lib-dynamodb";

// Settlement webhook: apply a payment exactly once
await doc.send(new TransactWriteCommand({
  TransactItems: [
    {
      // 1. Idempotency: the settlement id IS the key
      Put: {
        TableName: TABLE,
        Item: {
          PK: `T#${tenantId}#STL#${settlementId}`,
          SK: "STL",
          type: "SETTLEMENT",
          amountCents,
          receivedAt,
          appliedTo: `${contractId}#${pad3(seq)}`,
        },
        ConditionExpression: "attribute_not_exists(PK)",
      },
    },
    {
      // 2. Installment: optimistic lock on version
      Update: {
        TableName: TABLE,
        Key: { PK: debtorPk, SK: `CT#${contractId}#IN#${pad3(seq)}` },
        UpdateExpression:
          "SET paidCents = paidCents + :amt, #status = :newStatus, version = version + :one",
        ConditionExpression: "version = :expected",
        ExpressionAttributeNames: { "#status": "status" },
        ExpressionAttributeValues: {
          ":amt": amountCents,
          ":newStatus": installmentTransition.to, // decided by the state machine, not here
          ":one": 1,
          ":expected": installmentVersion,
        },
      },
    },
    // 3. Contract header: new balance and delinquency fact.
    //    When cured: REMOVE oldestOverdueDueDate, #bucket, nextActionDate, nextAction
    //    -&gt; the contract leaves the dunning agenda and the bucket portfolio in the same commit.
    { Update: contractUpdate(contract, installmentTransition, expectedContractVersion) },
  ],
}));</code></pre></div><div class="callout-block" data-callout="true"><p><strong><span>Por que n&#227;o usar o ClientRequestToken?</span></strong></p><p><code>TransactWriteItems</code> aceita um token de idempot&#234;ncia, mas ele vale por uma janela de dez minutos. Provedores de pagamento reenviam webhooks por horas; replays de DLQ acontecem dias depois. O token protege o retry do SDK. A idempot&#234;ncia de neg&#243;cio precisa morar na chave, com a vida &#250;til do pr&#243;prio fato.</p></div><h3><strong>Limite de contato: a data na chave garante a regra; o TTL s&#243; faz a limpeza</strong></h3><p>No artigo sobre resili&#234;ncia em sistemas de cobran&#231;a, falei do &#8220;guarda de tr&#226;nsito&#8221; da r&#233;gua: um limite di&#225;rio de contatos por devedor, independentemente do canal. Na Lastro, ele &#233; um item na cole&#231;&#227;o do devedor, incrementado de forma at&#244;mica e condicional:</p><pre><code><code>import { UpdateCommand } from "@aws-sdk/lib-dynamodb";

// Contact gateway: atomically claim one of today's allowed contacts
await doc.send(new UpdateCommand({
  TableName: TABLE,
  Key: { PK: debtorPk, SK: `LIMIT#${localDate}` }, // civil date in America/Sao_Paulo
  UpdateExpression: "SET expiresAt = if_not_exists(expiresAt, :ttl) ADD attempts :one",
  ConditionExpression: "attribute_not_exists(attempts) OR attempts &lt; :max",
  ExpressionAttributeValues: {
    ":one": 1,
    ":max": policy.maxDailyContacts,
    ":ttl": toEpochSeconds(addDays(localDate, 8)), // housekeeping only
  },
}));
// ConditionalCheckFailedException =&gt; do NOT contact. It is not "retry later".</code></code></pre><p>Dois detalhes carregam o neg&#243;cio. Primeiro, a data na chave &#233; local, no fuso de S&#227;o Paulo: o limite legal &#233; por dia civil do devedor, n&#227;o por dia UTC. Segundo, o TTL. <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Deletes por TTL s&#227;o gratuitos, mas ass&#237;ncronos, e a documenta&#231;&#227;o avisa que podem levar mais de 48 horas</mark></strong>. Se a corre&#231;&#227;o dependesse do TTL (um &#250;nico item <code>LIMIT</code> que &#8220;expira&#8221; &#224; meia-noite), <strong>o devedor poderia ficar bloqueado por dois dias, ou o contador poderia zerar tarde demais</strong>. Com a data na chave, o contador de amanh&#227; &#233; outro item, e o TTL s&#243; varre o lixo. <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">TTL &#233; ferramenta de custo, nunca de corre&#231;&#227;o.</mark></strong> O TTL &#233; o caminh&#227;o de lixo, que passa sem hor&#225;rio fixo. Ningu&#233;m espera o caminh&#227;o para saber se o leite venceu: a data est&#225; impressa na embalagem. Aqui, a data est&#225; na chave.</p><p>Cada tentativa tamb&#233;m grava um item <code>CONTACT#{timestamp}</code> com canal, template e resultado. &#201; prova, n&#227;o log. A pergunta &#8220;por que ele recebeu tr&#234;s SMS no domingo?&#8221; deixa de ser uma investiga&#231;&#227;o e vira uma Query.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QBn1!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QBn1!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png 424w, https://substackcdn.com/image/fetch/$s_!QBn1!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png 848w, https://substackcdn.com/image/fetch/$s_!QBn1!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png 1272w, https://substackcdn.com/image/fetch/$s_!QBn1!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QBn1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png" width="1320" height="668" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:668,&quot;width&quot;:1320,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:124681,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/217078847?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QBn1!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png 424w, https://substackcdn.com/image/fetch/$s_!QBn1!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png 848w, https://substackcdn.com/image/fetch/$s_!QBn1!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png 1272w, https://substackcdn.com/image/fetch/$s_!QBn1!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7d52dc31-f64b-4c46-b867-b0609460c6a6_1320x668.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Doze perguntas, cinco estruturas na tabela, uma fora dela. Nenhuma resposta depende de Scan.</figcaption></figure></div><p>A imagem acima &#233; o mapa que deveria estar anexado a todo ADR de modelagem em DynamoDB: pergunta de um lado, estrutura do outro, e nenhuma linha solta.</p><blockquote><p><strong><span>Pense comigo</span></strong></p><p>Qual dos seus &#237;ndices, se ficasse lento amanh&#227; &#224;s 00h07, impediria um cliente de pagar?</p><p>Se voc&#234; n&#227;o sabe responder, abra agora o painel de m&#233;tricas dos seus GSIs. N&#227;o depois do incidente.</p></blockquote><div><hr></div><h4>Pontos altos</h4><ol><li><p>Cada &#237;ndice existe para responder uma pergunta com nome e dono.</p></li><li><p>&#205;ndice esparso &#233; o post-it na geladeira: sair da r&#233;gua &#233; arrancar o post-it, n&#227;o rodar um job de limpeza.</p></li><li><p>Um &#237;ndice sobrecarregado trava as escritas da tabela principal. A r&#233;gua, que pode esperar, pode impedir o registro de um pagamento, que n&#227;o pode.</p></li><li><p>O shard espalha a carga como os caixas de um supermercado e, de brinde, define quantos workers o batch usa.</p></li><li><p>Nunca use como chave de &#237;ndice um valor que muda s&#243; porque o calend&#225;rio andou.</p></li><li><p>O que precisa ser exato (pagamento aplicado uma &#250;nica vez, limite legal de contatos, cobran&#231;a rec&#233;m-criada) fica na tabela principal, lido com consist&#234;ncia forte.</p></li><li><p>O TTL &#233; o caminh&#227;o de lixo: limpa, mas n&#227;o decide o que venceu. A regra mora na data da chave.</p></li><li><p>Quando a opera&#231;&#227;o quer filtrar por qualquer coisa, a resposta &#233; um motor de busca, n&#227;o mais &#237;ndices.</p></li></ol><h2><strong>5. Custo: onde o dinheiro realmente vaza</strong></h2><p>Vamos dimensionar a Lastro com n&#250;meros redondos e premissas expl&#237;citas: 8 milh&#245;es de contratos ativos, em m&#233;dia 10 parcelas por contrato, 25% da carteira em cobran&#231;a (2 milh&#245;es de contratos), quarenta tenants, o maior deles com 60% do volume.</p><p>Primeiro, a verdade que desmonta metade das discuss&#245;es de revis&#227;o: <strong>armazenamento quase nunca &#233; a conta</strong>. Contratos, parcelas, eventos, contatos e itens de lookup somam algo em torno de 95 GB. A US$ 0,25 por GB-m&#234;s na classe Standard, s&#227;o cerca de US$ 24 por m&#234;s. Discutir abrevia&#231;&#227;o de nome de atributo por causa de armazenamento &#233; otimizar o centavo errado.</p><p>O dinheiro, e principalmente o risco, vaza em tr&#234;s lugares.</p><h3><strong>Vazamento 1: escrita sem evento</strong></h3><p>Compare dois modelos para o mesmo requisito, &#8220;saber o atraso de cada contrato&#8221;, a US$ 0,625 por milh&#227;o de unidades de escrita (on-demand):</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;fd7f4c13-dc98-4a54-8b6d-d7583b9a0503&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">MODEL A: store daysPastDue, recompute nightly, daysPastDue in GSI2 sort key
         contract item at 1.1 KB (embedded promises and history)
  2M delinquent contracts x 30 nights            = 60M updates / month
  base table: 1.1 KB rounds to 2 KB -&gt; 2 WRU     = 120M WRU
  GSI2 key change: delete + insert (~0.4 KB)     = 120M WRU
  total                                          = 240M WRU  ~ US$ 150 / month

MODEL B: store oldestOverdueDueDate, derive days past due at read time
         contract item under 1 KB, bucket changes at most once a month
  ~2M bucket moves / month x (1 base + 2 index)  = 6M WRU    ~ US$ 4 / month</code></pre></div><div class="callout-block" data-callout="true"><p><strong><span>Imagine assim</span></strong></p><p>O modelo A reemite, toda madrugada, o RG de dois milh&#245;es de pessoas s&#243; para atualizar a idade impressa. O modelo B imprime a data de nascimento uma &#250;nica vez e deixa cada um fazer a conta quando precisar.</p><p>E repare no detalhe cruel do modelo A: quanto mais gente entra na inadimpl&#234;ncia, maior fica a fila da gr&#225;fica.</p></div><p>Quarenta vezes mais caro. E, sendo honesto, ningu&#233;m &#233; demitido por US$ 150 por m&#234;s. O custo real do modelo A n&#227;o est&#225; na fatura; est&#225; no formato da carga. Ele cria, por design, um pico di&#225;rio de escrita proporcional &#224; inadimpl&#234;ncia, concentrado em poucas chaves de baixa cardinalidade, na mesma janela em que a r&#233;gua reagenda as a&#231;&#245;es. No modo on-demand, a tabela acomoda instantaneamente at&#233; o dobro do pico anterior; um salto al&#233;m disso pode gerar throttling enquanto a capacidade se ajusta.</p><p>Agora pense no que acontece numa crise de cr&#233;dito. A inadimpl&#234;ncia sobe, o pico noturno do modelo A cresce junto, e o risco de throttling aumenta exatamente quando a cobran&#231;a mais importa. <strong>Um bom modelo n&#227;o aumenta a pr&#243;pria carga com not&#237;cia ruim.</strong> No modelo B, a carga cresce com eventos (pagamentos, contatos, acordos), n&#227;o com o calend&#225;rio.</p><h3><strong>Vazamento 2: ler o que voc&#234; n&#227;o precisa</strong></h3><p>A primeira vers&#227;o da tela de carteira quase sempre &#233; um Scan com <code>FilterExpression</code>. O filtro d&#225; a sensa&#231;&#227;o de que voc&#234; s&#243; paga pelo que volta. N&#227;o &#233; assim: a capacidade consumida &#233; calculada sobre os itens lidos, n&#227;o sobre os devolvidos. &#201; uma biblioteca que cobra por cada livro que o bibliotec&#225;rio tirou da estante procurando o seu, e n&#227;o pelo &#250;nico livro que voc&#234; levou para casa.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;d41e62ca-6007-485e-ae1d-66c334245e64&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">Eventually consistent Scan over 95 GB
  95 GB / 4 KB x 0.5                             ~ 12.5M RRU  ~ US$ 1.56 per scan
Supervisor screen opened hourly by 40 tenants
  40 x 24 h x 30 days = 28,800 scans / month     ~ US$ 45,000 / month

Same screen on GSI2, 100 contracts per page (~0.3 KB each)
  30 KB / 4 KB x 0.5                             ~ 4 RRU per page  -&gt; cents per month</code></pre></div><p>Esse &#233; o &#250;nico n&#250;mero do artigo que realmente assusta, e ele n&#227;o vem de um recurso caro do DynamoDB. Vem de uma pergunta sem estrutura para respond&#234;-la.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!dJl8!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!dJl8!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png 424w, https://substackcdn.com/image/fetch/$s_!dJl8!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png 848w, https://substackcdn.com/image/fetch/$s_!dJl8!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png 1272w, https://substackcdn.com/image/fetch/$s_!dJl8!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!dJl8!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png" width="994" height="383.51181102362204" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c786871-2747-4862-a45c-0b415819428f_1143x441.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:441,&quot;width&quot;:1143,&quot;resizeWidth&quot;:994,&quot;bytes&quot;:49457,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/217078847?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!dJl8!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png 424w, https://substackcdn.com/image/fetch/$s_!dJl8!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png 848w, https://substackcdn.com/image/fetch/$s_!dJl8!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png 1272w, https://substackcdn.com/image/fetch/$s_!dJl8!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c786871-2747-4862-a45c-0b415819428f_1143x441.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A biblioteca cobra por cada livro que o bibliotec&#225;rio tirou da estante, n&#227;o pelo que voc&#234; levou para casa....</figcaption></figure></div><h3><strong>Vazamento 3: fronteiras de tamanho e transa&#231;&#245;es por reflexo</strong></h3><p>Escritas s&#227;o cobradas em blocos de 1 KB; leituras, em blocos de 4 KB. Um cabe&#231;alho de contrato com 1,02 KB paga duas unidades em toda atualiza&#231;&#227;o, para sempre, como na tabela dos Correios, em que um pacote de 1,02 kg paga a tarifa de 2 kg. Por isso o hist&#243;rico sai do item (se&#231;&#227;o 3), e por isso a proje&#231;&#227;o dos &#237;ndices &#233; enxuta.</p><p>Transa&#231;&#245;es custam o dobro: cada item &#233; preparado e confirmado. &#201; o reconhecimento de firma em cart&#243;rio: vale quando o documento exige, e &#233; desperd&#237;cio quando uma assinatura simples resolve. Use-as apenas quando a invariante atravessa itens, como na liquida&#231;&#227;o (item de idempot&#234;ncia, parcela e contrato precisam mudar juntos, ou nenhum muda). O contador de contatos n&#227;o precisa de transa&#231;&#227;o; um <code>ADD</code> condicional num &#250;nico item j&#225; &#233; at&#244;mico. E lembre que escrita condicional que falha tamb&#233;m consome capacidade: cada webhook duplicado custa uma escrita. &#201; um seguro barato, mas entra na conta.</p><div class="callout-block" data-callout="true"><p><strong><span>Custo como teste automatizado</span></strong></p><p>Use <code>ReturnConsumedCapacity: "INDEXES"</code> nos testes de integra&#231;&#227;o e declare um or&#231;amento por opera&#231;&#227;o de neg&#243;cio: &#8220;aplicar pagamento consome no m&#225;ximo 8 WRU&#8221;, &#8220;abrir a vis&#227;o 360 consome no m&#225;ximo 4 RRU&#8221;. Quando algu&#233;m embutir um array no item ou projetar <code>ALL</code> num &#237;ndice, o CI quebra antes da fatura.</p></div><h3><strong>Modo de capacidade e classe de tabela</strong></h3><p>O perfil da Lastro (picos em dia de pagamento, lote noturno, vales no resto do dia) favorece on-demand, cujo pre&#231;o caiu pela metade em novembro de 2024. Provisionado com auto scaling fica em torno de tr&#234;s vezes mais barato por requisi&#231;&#227;o quando a utiliza&#231;&#227;o &#233; alta e est&#225;vel, ent&#227;o revisite a decis&#227;o quando o tr&#225;fego amadurecer. Antes de picos previs&#237;veis, como o dia do INSS, a AWS permite pr&#233;-aquecer a capacidade de tabelas e &#237;ndices (warm throughput).</p><p>Para o hist&#243;rico, a documenta&#231;&#227;o de fundamentos &#233; clara: uma tabela que mistura dado operacional e hist&#243;rico n&#227;o aproveita bem a classe Standard-Infrequent Access. Contratos encerrados, eventos antigos e evid&#234;ncias de contato com mais de alguns meses v&#227;o para uma tabela separada em Standard-IA, com armazenamento a US$ 0,10 por GB-m&#234;s contra US$ 0,25, e requisi&#231;&#245;es cerca de 25% mais caras. O caminho natural &#233; TTL na tabela quente, stream configurado com imagem antiga, e uma Lambda que reconhece o delete feito pelo pr&#243;prio servi&#231;o e arquiva no destino frio. Tabelas separadas tamb&#233;m permitem pol&#237;ticas de backup e de recupera&#231;&#227;o diferentes para dados de criticidade diferente.</p><h3><strong>A pergunta de unit economics</strong></h3><div class="callout-block" data-callout="true"><p><strong>Unit economics.</strong><span> Quanto custa, e quanto rende, uma unidade do neg&#243;cio; aqui, um contrato ativo por m&#234;s. &#201; o que diz se o pre&#231;o cobrado de cada tenant cobre o custo de atend&#234;-lo, como saber o custo de cada prato antes de fechar o pre&#231;o do card&#225;pio.</span></p></div><p>Num BaaS, o n&#250;mero que importa n&#227;o &#233; a fatura do DynamoDB; &#233; o custo por contrato ativo por m&#234;s, porque &#233; ele que entra na precifica&#231;&#227;o para o tenant. Somando escrita (r&#233;gua, contatos, liquida&#231;&#245;es, eventos), leitura, armazenamento, PITR e o export mensal, a Lastro fica na casa de US$ 200 por m&#234;s em us-east-1. Algo como dois centavos de d&#243;lar por mil contratos.</p><p>Agora compare com o custo operacional da pr&#243;pria cobran&#231;a: 2 milh&#245;es de contratos em cobran&#231;a, dez tentativas de contato por m&#234;s, metade por SMS a R$ 0,05, s&#227;o R$ 500 mil por m&#234;s s&#243; em SMS. O banco custa menos do que algumas horas de disparo.</p><blockquote><p><em>A conclus&#227;o n&#227;o &#233; &#8220;DynamoDB &#233; barato, relaxa&#8221;. &#201; que o valor do modelo est&#225; nas decis&#245;es de neg&#243;cio que ele torna corretas: o contador que impede o quarto contato economiza mais, em SMS e em processo judicial, do que qualquer otimiza&#231;&#227;o de byte.</em></p></blockquote><blockquote><p><strong><span>Pense comigo</span></strong></p><p>Voc&#234; sabe quanto custa, em unidades de escrita, aplicar um pagamento na sua plataforma?</p><p>Se n&#227;o sabe, tamb&#233;m n&#227;o sabe quanto vai custar o pr&#243;ximo quinto dia &#250;til, nem se ele vai caber na capacidade que voc&#234; tem.</p></blockquote><div><hr></div><h4>Pontos altos</h4><ol><li><p>Guardar dado custa pouco. O que custa &#233; escrever sem motivo e ler o que n&#227;o precisava.</p></li><li><p>A carga do banco deve crescer com o que acontece (pagamentos, contatos, acordos), n&#227;o com a passagem dos dias, nem com o aumento da inadimpl&#234;ncia.</p></li><li><p>Scan com filtro cobra cada livro tirado da estante, n&#227;o os que voc&#234; levou.</p></li><li><p>Transa&#231;&#227;o custa o dobro, como reconhecer firma: use s&#243; quando v&#225;rios itens precisam mudar juntos ou nenhum muda.</p></li><li><p>As faixas de 1 KB e 4 KB funcionam como a tabela de peso dos Correios. Ponha no CI um teste que mede o consumo de cada opera&#231;&#227;o de neg&#243;cio.</p></li><li><p>O hist&#243;rico vai para uma tabela pr&#243;pria, na classe mais barata para dado pouco acessado.</p></li><li><p>Me&#231;a o custo por contrato ativo e compare com o custo de uma decis&#227;o de cobran&#231;a errada.</p></li></ol><h2><strong>6. A tabela &#233; o estado; o stream &#233; a hist&#243;ria</strong></h2><p>A tabela responde &#8220;como est&#225; agora&#8221;. O resto da plataforma quer saber &#8220;o que aconteceu&#8221;: negativa&#231;&#227;o, contabilidade, notifica&#231;&#245;es, o motor de busca da mesa, o lakehouse. O DynamoDB Streams, consumido por Lambda com filtros de evento, publica as transi&#231;&#245;es como eventos de dom&#237;nio (<code>InstallmentOverdue</code>, <code>PromiseBroken</code>, <code>AgreementSigned</code>, <code>SettlementApplied</code>) em EventBridge ou Kafka.</p><p>No single-table design, toda mudan&#231;a de todo tipo de item passa pelo mesmo stream. A documenta&#231;&#227;o lembra que, com os filtros de evento da Lambda, isso n&#227;o pesa na fatura; com outros consumidores, pesa. Filtre por <code>type</code> na borda.</p><p>O cuidado de modelagem aqui &#233; sutil: <strong>o stream mostra o antes e o depois, mas n&#227;o o porqu&#234;</strong>. Ele &#233; a c&#226;mera de seguran&#231;a da portaria: grava que a porta abriu &#224;s 14h02, mas n&#227;o se foi o morador, o entregador ou o s&#237;ndico. O motivo precisa estar no livro de registro. Se o contrato mudou de <code>IN_COLLECTION</code> para <code>ACTIVE</code>, foi pagamento, acordo ou estorno? O consumer n&#227;o tem como saber, a menos que o item carregue essa informa&#231;&#227;o. &#201; mais um motivo para <code>statusReason</code> existir: ele n&#227;o serve s&#243; para a tela, serve para que o evento derivado da mudan&#231;a tenha significado.</p><p>E as perguntas anal&#237;ticas da se&#231;&#227;o 1 nunca tocam a tabela. O export incremental para o S3 &#233; cobrado por GB de dado da tabela e n&#227;o consome capacidade de leitura; aging, roll rate e relat&#243;rios regulat&#243;rios rodam em Athena ou no lakehouse, sobre dados que o time de dados pode consultar livremente sem acordar ningu&#233;m de plant&#227;o.</p><div><hr></div><h4>Pontos altos</h4><ol><li><p>A tabela guarda o agora, o stream conta o que aconteceu, e o S3 atende as an&#225;lises.</p></li><li><p>O stream &#233; a c&#226;mera da portaria: mostra que a porta abriu, n&#227;o por qu&#234;. O motivo precisa estar gravado no item.</p></li><li><p>BI nunca faz Scan na tabela que atende a opera&#231;&#227;o; usa o export.</p></li></ol><h2><strong>7. A revis&#227;o de design que eu faria no seu PR</strong></h2><p>Se voc&#234; &#233; tech lead ou arquiteto e vai revisar um modelo em DynamoDB, estas s&#227;o as perguntas que eu levaria para a sala. Nenhuma delas &#233; sobre sintaxe.</p><ol><li><p>Onde est&#225; a lista de padr&#245;es de acesso, e quem do neg&#243;cio a validou?</p></li><li><p>Qual pergunta voc&#234;s decidiram n&#227;o responder na tabela, e para onde ela vai?</p></li><li><p>Qual atributo muda sem que nada aconte&#231;a no neg&#243;cio? Por que ele est&#225; gravado em vez de derivado?</p></li><li><p>Alguma chave, da tabela ou de &#237;ndice, cont&#233;m dado pessoal?</p></li><li><p>Qual ser&#225; o maior tenant daqui a dois anos, e qual chave ele esquenta primeiro?</p></li><li><p>Qual GSI, se sofrer throttling, bloqueia uma escrita cr&#237;tica na tabela base?</p></li><li><p>Quais perguntas exigem leitura forte, e por que nenhuma delas depende de GSI?</p></li><li><p>Onde mora a idempot&#234;ncia de cada comando externo, e qual &#233; a janela real de retry do provedor?</p></li><li><p>Quanto custa, em unidades de capacidade, cada opera&#231;&#227;o de neg&#243;cio, e existe teste para isso?</p></li><li><p>Qual &#233; o plano para hist&#243;rico, reten&#231;&#227;o, rota&#231;&#227;o do segredo de chave e offboarding de tenant?</p></li></ol><h2><strong>Voltando &#224; segunda-feira, 8h40</strong></h2><p>Com o modelo da Lastro, as tr&#234;s perguntas da head de cobran&#231;a t&#234;m dono.</p><ul><li><p>&#8220;Quantos contratos viraram 31+ no fim de semana?&#8221; Operacionalmente, uma Query na faixa <code>D031_060</code> do tenant no GSI2, restrita por <code>oldestOverdueDueDate</code>, que l&#234; a faixa e n&#227;o a tabela. Analiticamente, a contagem dos eventos de mudan&#231;a de faixa que o stream j&#225; publicou.</p></li><li><p>&#8220;Quanto disso tem promessa para esta semana?&#8221; Consultas no GSI1 com <code>nextAction = PROMISE_CHECK</code>, uma por dia da semana, em cada shard, pelos mesmos workers que j&#225; leem a r&#233;gua.</p></li><li><p>&#8220;Por que ele recebeu tr&#234;s SMS no domingo?&#8221; Uma Query na cole&#231;&#227;o do devedor com o prefixo <code>CONTACT#2026-09-20</code>, ao lado do contador <code>LIMIT#2026-09-20</code>. Se foram tr&#234;s, a prova est&#225; ali, e o limite respeitado (ou violado) tamb&#233;m.</p></li></ul><p>No relacional, o schema descreve o dado, e as perguntas se resolvem depois. No DynamoDB, o schema descreve as perguntas. Em cobran&#231;a, as perguntas s&#227;o o neg&#243;cio: quem deve, desde quando, quanto, o que foi prometido, o que foi feito e o que a lei permite fazer a seguir. Um modelo que responde a isso com uma Query por pergunta n&#227;o &#233; s&#243; r&#225;pido e barato. &#201; audit&#225;vel, explic&#225;vel e defens&#225;vel.</p><p><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Toda chave &#233; uma pergunta. Garanta que algu&#233;m do neg&#243;cio a tenha feito.</mark></strong></p><p>Se voc&#234; opera cobran&#231;a, cr&#233;dito ou pagamentos em DynamoDB, me conta nos coment&#225;rios: qual foi a pergunta que a sua tabela n&#227;o soube responder?</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><strong>Credi&#225;rio.</strong><span> Venda parcelada em que a pr&#243;pria loja, ou a financeira ligada a ela, empresta o dinheiro ao cliente. &#201; o velho caderninho do fiado da mercearia, s&#243; que com contrato, juros, CPF e, claro, uma opera&#231;&#227;o de cobran&#231;a por tr&#225;s.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p><strong>31+ e faixas de atraso.</strong><span> Jarg&#227;o para "contratos com 31 dias ou mais de atraso". O mercado agrupa a inadimpl&#234;ncia em faixas (1 a 30, 31 a 60, 61 a 90 e acima de 90 dias), e cada faixa pede um tratamento diferente. Pense num term&#244;metro: 37,5 &#176;C pede aten&#231;&#227;o, 39 &#176;C pede m&#233;dico. Com cinco dias de atraso, um lembrete gentil resolve; com 45, a conversa j&#225; &#233; sobre acordo; depois de 90, o contrato costuma seguir para negativa&#231;&#227;o e para perda. "Virar 31+" &#233; atravessar a linha da primeira para a segunda faixa: o sinal de que o atraso deixou de ser esquecimento.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><strong>Promessa de pagamento.</strong><span> Quando o devedor diz "pago no dia 26", a cobran&#231;a registra a data, pausa as a&#231;&#245;es at&#233; l&#225; e confere, no dia, se o dinheiro entrou. Se n&#227;o entrou, &#233; uma promessa quebrada, um dos sinais de risco mais fortes que existem. &#201; o fiado com data marcada.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p><strong>Carteira.</strong><span> O conjunto de todos os contratos de cr&#233;dito que uma institui&#231;&#227;o tem a receber. Imagine um pomar: cada &#225;rvore &#233; um contrato, e a gestora olha a sa&#250;de do pomar inteiro (quantas &#225;rvores est&#227;o doentes, em que est&#225;gio, onde a praga avan&#231;a), n&#227;o s&#243; a de uma fruta.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p><strong>BaaS e tenant.</strong><span> Banking as a Service: uma empresa que oferece a infraestrutura financeira para que outras marcas operem cr&#233;dito, conta ou cobran&#231;a sem construir tudo do zero. Pense numa cozinha industrial compartilhada, as chamadas </span><em>dark kitchens</em><span>: v&#225;rias marcas de restaurante usam a mesma cozinha, cada uma com card&#225;pio, clientes e receitas pr&#243;prios, e o pedido de uma nunca pode sair com a comida da outra. Cada marca cliente da plataforma &#233; um </span><em>tenant</em><span>, "inquilino" em ingl&#234;s.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-6" href="#footnote-anchor-6" class="footnote-number" contenteditable="false" target="_self">6</a><div class="footnote-content"><p><strong>SCD.</strong><span> Sociedade de Cr&#233;dito Direto: um tipo de fintech autorizada pelo Banco Central a emprestar dinheiro usando capital pr&#243;prio.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-7" href="#footnote-anchor-7" class="footnote-number" contenteditable="false" target="_self">7</a><div class="footnote-content"><p><strong>Consignado.</strong><span> Empr&#233;stimo cuja parcela &#233; descontada direto do sal&#225;rio ou do benef&#237;cio do INSS, antes de o dinheiro chegar &#224; conta. Como o pagamento &#233; quase autom&#225;tico, o risco &#233; menor, e os juros tamb&#233;m.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-8" href="#footnote-anchor-8" class="footnote-number" contenteditable="false" target="_self">8</a><div class="footnote-content"><p><strong>R&#233;gua de cobran&#231;a.</strong><span> A sequ&#234;ncia programada de a&#231;&#245;es conforme os dias de atraso: lembrete no primeiro dia, SMS no quinto, liga&#231;&#227;o no d&#233;cimo quinto, proposta de acordo no quadrag&#233;simo quinto, e assim por diante. Funciona como a caderneta de vacina&#231;&#227;o: cada idade tem a sua vacina, cada dia de atraso tem a sua a&#231;&#227;o. O </span><code>SMS_D45</code><span> do exemplo &#233; "o SMS do 45&#186; dia de atraso".</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-9" href="#footnote-anchor-9" class="footnote-number" contenteditable="false" target="_self">9</a><div class="footnote-content"><p><strong>Mesa de cobran&#231;a.</strong><span> A equipe humana da cobran&#231;a: operadores que ligam, negociam e registram acordos, e supervisores que acompanham a carteira e as metas. &#201; o call center da cobran&#231;a, com roteiros e indicadores pr&#243;prios.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-10" href="#footnote-anchor-10" class="footnote-number" contenteditable="false" target="_self">10</a><div class="footnote-content"><p><strong>Liquida&#231;&#227;o, baixa e concilia&#231;&#227;o.</strong><span> Liquida&#231;&#227;o &#233; o dinheiro efetivamente entrando. O webhook de liquida&#231;&#227;o &#233; o aviso autom&#225;tico que o banco ou o provedor de pagamento envia quando isso acontece: o "plim" do caixa. Dar baixa &#233; marcar a parcela como paga. Conciliar &#233; casar cada valor recebido com a cobran&#231;a que o originou, como conferir o extrato do cart&#227;o com as notas fiscais do m&#234;s: nenhum real pode ficar sem dono.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-11" href="#footnote-anchor-11" class="footnote-number" contenteditable="false" target="_self">11</a><div class="footnote-content"><p><strong>Aging e roll rate.</strong><span> Aging &#233; a foto da carteira distribu&#237;da pelas faixas de atraso num dia espec&#237;fico. Roll rate mede quanto de uma faixa "escorrega" para a seguinte de um m&#234;s para o outro, por exemplo, quantos contratos que estavam com 1 a 30 dias chegaram a 31 a 60. Se o aging &#233; a foto de uma escada com pessoas em cada degrau, o roll rate mostra quantas desceram um degrau desde a &#250;ltima foto.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-12" href="#footnote-anchor-12" class="footnote-number" contenteditable="false" target="_self">12</a><div class="footnote-content"><p><strong>Quinto dia &#250;til e pagamento do INSS.</strong><span> Datas em que sal&#225;rios (pagos at&#233; o quinto dia &#250;til do m&#234;s, pela CLT) e benef&#237;cios do INSS caem na conta. Com dinheiro na m&#227;o, muita gente paga as contas no mesmo dia, e o volume de pagamentos dispara. Para a cobran&#231;a, s&#227;o os dias de Black Friday.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-13" href="#footnote-anchor-13" class="footnote-number" contenteditable="false" target="_self">13</a><div class="footnote-content"><p><strong>Provis&#227;o.</strong><span> Uma reserva cont&#225;bil que a institui&#231;&#227;o separa pensando nas perdas que espera ter; quanto maior o atraso, maior a reserva. &#201; o envelope de "imprevistos" do or&#231;amento dom&#233;stico, s&#243; que calculado com regras do Banco Central.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-14" href="#footnote-anchor-14" class="footnote-number" contenteditable="false" target="_self">14</a><div class="footnote-content"><p><strong>Negativa&#231;&#227;o.</strong><span> A inclus&#227;o do CPF do devedor nos bir&#244;s de cr&#233;dito (Serasa, SPC, Boa Vista), que outras empresas consultam antes de conceder cr&#233;dito. O CDC exige comunica&#231;&#227;o pr&#233;via por escrito ao consumidor, e um erro aqui costuma virar processo. &#201; o "nome no mural" que todo o com&#233;rcio consulta, e por isso n&#227;o admite engano: negativar quem j&#225; pagou &#233; dano quase certo.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-15" href="#footnote-anchor-15" class="footnote-number" contenteditable="false" target="_self">15</a><div class="footnote-content"><p><strong>Prorroga&#231;&#227;o.</strong><span> Mudar a data de vencimento de uma parcela, por pedido do cliente, por pol&#237;tica comercial ou dentro de uma renegocia&#231;&#227;o. Pense numa consulta m&#233;dica remarcada: o prontu&#225;rio precisa registrar a data original e a nova, sen&#227;o ningu&#233;m sabe depois se o paciente faltou ou se a cl&#237;nica mudou a agenda. Quando algu&#233;m pergunta "quanto da carteira foi prorrogada?", quer medir quanta d&#237;vida foi empurrada para frente, porque prorroga&#231;&#227;o em excesso pode esconder inadimpl&#234;ncia real: o contrato parece em dia s&#243; porque o vencimento mudou de lugar.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-16" href="#footnote-anchor-16" class="footnote-number" contenteditable="false" target="_self">16</a><div class="footnote-content"><p><strong>Imputa&#231;&#227;o do pagamento e principal.</strong><span> Principal &#233; o valor emprestado; juros s&#227;o o pre&#231;o desse empr&#233;stimo. Imputa&#231;&#227;o &#233; a regra que decide qual parte da d&#237;vida o dinheiro quita primeiro. Imagine baldes enchidos em sequ&#234;ncia: o pagamento enche primeiro o balde dos juros vencidos, e s&#243; o que sobra cai no balde do principal. Sem os valores separados no dado, n&#227;o h&#225; como provar em qual balde cada real caiu.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-17" href="#footnote-anchor-17" class="footnote-number" contenteditable="false" target="_self">17</a><div class="footnote-content"><p><strong>Multa morat&#243;ria e juros de mora.</strong><span> Dois encargos diferentes pelo mesmo atraso. A multa &#233; cobrada uma &#250;nica vez, como uma taxa de religa&#231;&#227;o; em rela&#231;&#245;es de consumo, n&#227;o pode passar de 2% do valor da presta&#231;&#227;o. Os juros de mora correm enquanto o atraso durar, como um tax&#237;metro ligado. Guardar os dois separados &#233; o que permite provar que o teto foi respeitado.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-18" href="#footnote-anchor-18" class="footnote-number" contenteditable="false" target="_self">18</a><div class="footnote-content"><p><strong>Acordo.</strong><span> Uma renegocia&#231;&#227;o: as parcelas em atraso s&#227;o substitu&#237;das por uma nova condi&#231;&#227;o, geralmente com desconto de encargos, uma entrada e um novo parcelamento. Se o cliente deixa de pagar o acordo, ele "quebra", e o contrato volta para a r&#233;gua.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-19" href="#footnote-anchor-19" class="footnote-number" contenteditable="false" target="_self">19</a><div class="footnote-content"><p><strong>txid, nosso n&#250;mero e E2E.</strong><span> Os c&#243;digos de rastreio do dinheiro. O txid identifica uma cobran&#231;a Pix, o QR Code que voc&#234; escaneia. O nosso n&#250;mero identifica um boleto dentro do banco que o emitiu. O E2E (end-to-end ID) identifica uma transa&#231;&#227;o Pix espec&#237;fica no momento em que ela &#233; liquidada. &#201; como o n&#250;mero de rastreio de uma encomenda: &#233; por ele que se descobre de onde o pacote veio e para quem ele vai.</span></p><h2><strong>Fontes e leituras</strong></h2><ul><li><p>AWS, <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/data-modeling.html">Data modeling for DynamoDB tables</a></p></li><li><p>AWS, <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-general-nosql-design.html">NoSQL design for DynamoDB</a></p></li><li><p>AWS, <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/data-modeling-foundations.html">Data modeling foundations: single table and multiple table design</a></p></li><li><p>AWS, <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/data-modeling-blocks.html">Data modeling building blocks: composite sort key, multi-tenancy, sparse index, TTL, vertical partitioning, write sharding</a></p></li><li><p>AWS, <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/Constraints.html">Constraints in Amazon DynamoDB</a></p></li><li><p>AWS, <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GSI.html">Using Global Secondary Indexes in DynamoDB</a></p></li><li><p>AWS, <a href="https://aws.amazon.com/about-aws/whats-new/2025/11/amazon-dynamodb-multi-attribute-composite-keys-global-secondary-indexes">Multi-attribute composite keys in global secondary indexes (nov/2025)</a></p></li><li><p>AWS, <a href="https://aws.amazon.com/dynamodb/pricing/">Amazon DynamoDB pricing</a></p></li><li><p>Rafael Miguel, <a href="https://chroniclesofapragmaticprogrammer.substack.com/p/um-breve-mergulho-dynamodb">Um breve mergulho no DynamoDB</a></p></li><li><p>Rafael Miguel, <a href="https://chroniclesofapragmaticprogrammer.substack.com/p/quando-orientacao-a-objetos-estraga">Quando Orienta&#231;&#227;o a Objetos Estraga Seu Modelo de Dados</a></p></li><li><p>Rafael Miguel, <a href="https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-credito-implementando">Engenharia de Cr&#233;dito: Implementando M&#225;quinas de Estado</a></p></li><li><p>Rafael Miguel, <a href="https://chroniclesofapragmaticprogrammer.substack.com/p/desafios-de-arquitetura-e-resiliencia">Desafios de Arquitetura e Resili&#234;ncia em Sistemas de Cobran&#231;a</a></p></li></ul></div></div>]]></content:encoded></item><item><title><![CDATA[Pix: 318 milhões de transações em um dia. Quantos pods isso é?]]></title><description><![CDATA[Parte 1: como sair de &#8220;318 milh&#245;es de transa&#231;&#245;es em um dia&#8221; e chegar em replicas: 28 &#8212; sem chutar, sem copiar YAML da internet e sem derrubar o cluster no quinto dia &#250;til.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/pix-318-milhoes-de-transacoes-em</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/pix-318-milhoes-de-transacoes-em</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Tue, 22 Sep 2026 20:46:33 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/2cef7c07-3c50-4329-b868-5e07ac4e8006_1600x900.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>No dia 4 de setembro de 2026, uma sexta-feira, o Pix liquidou 318.073.816 transa&#231;&#245;es em 24 horas, movimentando R$ 186,89 bilh&#245;es. Foi o segundo recorde di&#225;rio em nove meses &#8212; o anterior, de 313,3 milh&#245;es, tinha sido cravado em 5 de dezembro de 2025. As duas datas t&#234;m a mesma assinatura, e n&#227;o &#233; acaso: as duas s&#227;o o &#250;ltimo dia banc&#225;rio antes do prazo legal de pagamento de sal&#225;rios. Em dezembro de 2025 esse prazo caiu exatamente na sexta, dia 5. Em setembro de 2026 caiu num s&#225;bado, e as empresas anteciparam para a sexta anterior. O pico n&#227;o segue a data &#8212; segue o calend&#225;rio banc&#225;rio. Em 2026, a m&#233;dia nacional j&#225; ronda tr&#234;s mil transa&#231;&#245;es por segundo, todos os dias, o dia inteiro.</p><p>Guarde duas coisas desse par&#225;grafo. A primeira &#233; a palavra <em><strong>m&#233;dia</strong></em>: ela &#233; a mentira mais cara do capacity planning. A segunda &#233; que o recorde n&#227;o foi surpresa &#8212; ele estava no calend&#225;rio. Volte a esse detalhe quando chegarmos &#224; se&#231;&#227;o 04.</p><p>Aquele mesmo minuto de pico foi visto de tr&#234;s lugares diferentes. O usu&#225;rio viu uma tela de confirma&#231;&#227;o em dois segundos e n&#227;o pensou mais no assunto. O SRE de plant&#227;o viu um gr&#225;fico subindo e uma m&#233;trica de satura&#231;&#227;o chegando perto de um limiar. E o Banco Central viu um n&#250;mero que, no fim do m&#234;s, vira nota do participante &#8212; com peso, com hist&#243;rico e com consequ&#234;ncia.</p><p>Este artigo &#233; sobre tudo o que existe entre essas tr&#234;s vis&#245;es. E ele parte de duas assimetrias que quase nenhum material sobre Kubernetes menciona, porque quase nenhum material sobre Kubernetes foi escrito pensando em um arranjo de pagamentos regulado:<strong><span> </span></strong></p><div class="callout-block" data-callout="true"><h4>As duas assimetrias do Pix</h4><p><strong>1. O SLO n&#227;o &#233; escolhido pelo time de SRE &#8212; ele &#233; herdado da norma.</strong> Em produto comum, o time negocia a meta de disponibilidade com o Product Owner. No Pix, a meta chega pronta, por categoria de participante, e o descumprimento tem consequ&#234;ncia regulat&#243;ria.</p><p><strong>2. A capacidade n&#227;o &#233; escolhida pelo produto &#8212; ela &#233; imposta pelo tr&#225;fego de entrada.</strong> Voc&#234; decide quantos Pix seus clientes enviam. Voc&#234; n&#227;o decide quantos Pix eles recebem.</p><p></p></div><div><hr></div><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong><span>01 / FUNDAMENTOS</span></strong></h2><h2><strong>Vocabul&#225;rio sem vergonha</strong></h2><p><a href="https://claude.ai/code/artifact/103d3200-0e6d-419c-acce-45a8c0cd0578">Se voc&#234; j&#225; opera EKS h&#225; cinco anos, leia esta se&#231;&#227;o mesmo assim &#8212; n&#227;o pelas defini&#231;&#245;es, mas pela terceira, sobre a palavra </a><em><a href="https://claude.ai/code/artifact/103d3200-0e6d-419c-acce-45a8c0cd0578">provisionar</a></em><a href="https://claude.ai/code/artifact/103d3200-0e6d-419c-acce-45a8c0cd0578">, que &#233; onde a maioria das reuni&#245;es de arquitetura descarrilha.</a></p><h3><strong>Cluster</strong></h3><p>Pense num condom&#237;nio residencial. Ele tem duas metades que quase nunca se confundem: a <strong>administra&#231;&#227;o</strong> e os <strong>pr&#233;dios</strong>.</p><p><strong>A administra&#231;&#227;o</strong> guarda a planta do empreendimento, decide qual apartamento vago vai receber qual morador, mant&#233;m o cadastro de quem est&#225; onde e passa o dia conferindo se a realidade bate com a planta. Ela n&#227;o mora em lugar nenhum e n&#227;o carrega m&#243;vel. Ela decide. <strong>Os pr&#233;dios</strong>, por sua vez, n&#227;o decidem nada &#8212; eles t&#234;m parede, encanamento, elevador e capacidade finita. &#201; neles que a vida acontece.</p><p>Um cluster Kubernetes &#233; exatamente isso. O <strong>control plane</strong> &#233; a administra&#231;&#227;o: API server, etcd, scheduler e controller manager. Ele guarda o estado desejado &#8212; &#8220;quero 28 r&#233;plicas deste servi&#231;o&#8221; &#8212; e trabalha sem parar para que a realidade se pare&#231;a com esse desejo. O <strong>data plane</strong> s&#227;o os pr&#233;dios: os n&#243;s, que no seu caso s&#227;o inst&#226;ncias EC2, com CPU e mem&#243;ria finitas, rodando a carga de verdade.</p><p>No EKS, a AWS opera a administra&#231;&#227;o do condom&#237;nio por voc&#234;, com escrit&#243;rios em bairros diferentes da cidade &#8212; m&#250;ltiplas zonas de disponibilidade. Voc&#234; n&#227;o v&#234; essa administra&#231;&#227;o, n&#227;o a ajusta e n&#227;o a demite &#224;s tr&#234;s da manh&#227;. O que &#233; seu, o que voc&#234; dimensiona e o que voc&#234; eventualmente derruba s&#227;o os pr&#233;dios.</p><div class="callout-block" data-callout="true"><h4><strong><span>Por que essa divis&#227;o importa &#224;s 3h da manh&#227;</span></strong></h4><p>Quando um pod n&#227;o sobe, a pergunta certa quase nunca &#233; &#8220;o control plane est&#225; com problema?&#8221;. A administra&#231;&#227;o recebeu o pedido, anotou na planta e est&#225; tentando alocar. O que costuma faltar &#233; <strong>pr&#233;dio</strong>: n&#227;o h&#225; n&#243; com espa&#231;o, n&#227;o h&#225; IP livre na subnet, ou o morador n&#227;o consegue entrar (imagem errada, segredo ausente, configura&#231;&#227;o inv&#225;lida).</p><p>Um pod em <code>Pending</code> &#233; um problema de capacidade ou de agendamento. Um pod em <code>CrashLoopBackOff</code> &#233; um problema da sua aplica&#231;&#227;o. Nenhum dos dois &#233; da AWS. Essa separa&#231;&#227;o economiza uma hora de investiga&#231;&#227;o em quase todo incidente.</p></div><h3><strong>N&#243;, pod, container, r&#233;plica</strong></h3><p>A analogia que funciona bem em treinamento interno: o <strong>n&#243;</strong> &#233; o pr&#233;dio, o <strong>pod</strong> &#233; o apartamento (com seu pr&#243;prio endere&#231;o de rede), o <strong>container</strong> &#233; o morador, e a <strong>r&#233;plica</strong> &#233; a quantidade de apartamentos id&#234;nticos que voc&#234; alugou. Escalar horizontalmente &#233; alugar mais apartamentos. Escalar verticalmente &#233; derrubar a parede e fazer um apartamento maior &#8212; com o inquilino dentro.</p><h3><strong>Provisionar (a palavra que causa confus&#227;o)</strong></h3><p>O mercado usa &#8220;provisionar&#8221; para tr&#234;s coisas diferentes, e o mal-entendido custa horas de reuni&#227;o:</p><ul><li><p><strong>Provisionar infraestrutura</strong> &#8212; criar capacidade f&#237;sica: subir n&#243;s, aumentar o node group, pedir inst&#226;ncias &#224; AWS. Leva <em>minutos</em>.</p></li><li><p><strong>Provisionar aloca&#231;&#227;o</strong> &#8212; reservar CPU e mem&#243;ria para um pod dentro de um n&#243; que j&#225; existe. &#201; o campo <code>resources.requests</code>. &#201; contabilidade do scheduler, n&#227;o capacidade real. Leva <em>milissegundos</em>.</p></li><li><p><strong>Provisionar atendimento</strong> &#8212; decidir quantas r&#233;plicas do servi&#231;o existem. &#201; o <code>replicas</code> ou o HPA. Leva <em>segundos</em>.</p></li></ul><p>Quando algu&#233;m diz &#8220;o servi&#231;o est&#225; mal provisionado&#8221;, pergunte qual dos tr&#234;s. Na pr&#225;tica, a maioria dos incidentes de capacidade em banco &#233; do tipo 1 chegando atrasado para atender o tipo 3 &#8212; e voltaremos a isso na se&#231;&#227;o 05.</p><h3><strong>EKS, ECS ou Fargate</strong></h3><p>Tr&#234;s op&#231;&#245;es, tr&#234;s conversas diferentes. A compara&#231;&#227;o honesta n&#227;o &#233; sobre qual &#233; melhor, &#233; sobre o que cada uma cobra de voc&#234;:</p><p><strong>EKS </strong><span>&#8212; Kubernetes gerenciado</span></p><ul><li><p><strong>Ganha em:</strong> ecossistema maduro (HPA, KEDA, Karpenter, service mesh), portabilidade e facilidade de contratar gente que j&#225; conhece.</p></li><li><p><strong>Custa em:</strong> complexidade operacional real e a necessidade de um time de plataforma dedicado &#8212; n&#227;o &#233; algo que um time de produto opera nas horas vagas.</p></li><li><p><strong>Onde os bancos usam:</strong> core transacional. Inicia&#231;&#227;o, liquida&#231;&#227;o, consumidores do SPI.</p></li></ul><p><strong>ECS sobre EC2 </strong><span>&#8212; orquestra&#231;&#227;o nativa da AWS</span></p><ul><li><p><strong>Ganha em:</strong> simplicidade operacional e integra&#231;&#227;o direta com IAM, ALB e CloudWatch, sem camadas de tradu&#231;&#227;o.</p></li><li><p><strong>Custa em:</strong> acoplamento ao provedor e menos primitivas de escalonamento avan&#231;ado quando o cen&#225;rio aperta.</p></li><li><p><strong>Onde os bancos usam:</strong> servi&#231;os de apoio, backoffice e integra&#231;&#245;es internas.</p></li></ul><p><strong>Fargate </strong><span>&#8212; computa&#231;&#227;o sem gest&#227;o de n&#243;</span></p><ul><li><p><strong>Ganha em:</strong> zero gest&#227;o de inst&#226;ncia e isolamento por task.</p></li><li><p><strong>Custa em:</strong> pr&#234;mio por vCPU, tempo de partida de task maior e &#8212; o ponto que pega &#8212; <strong>aus&#234;ncia de DaemonSet</strong>.</p></li><li><p><strong>Onde os bancos usam:</strong> jobs, cargas espor&#225;dicas e ambientes de teste.</p></li></ul><div class="callout-block" data-callout="true"><h4><strong><span>Aten&#231;&#227;o regulat&#243;ria</span></strong></h4><p>A aus&#234;ncia de DaemonSet no Fargate n&#227;o &#233; detalhe est&#233;tico. Agentes de seguran&#231;a, coletores de log e sondas de conformidade em banco costumam rodar como DaemonSet &#8212; um por n&#243;. Migrar um servi&#231;o cr&#237;tico para Fargate sem redesenhar essa camada significa perder evid&#234;ncia de auditoria. J&#225; vi essa conversa acontecer tarde demais, com o servi&#231;o em produ&#231;&#227;o.</p></div><h2><strong><span>02 / DIMENSIONAMENTO</span></strong></h2><h2><strong>Horizontal e vertical: qual custa mais, qual derruba o cluster</strong></h2><p>Todo mundo sabe a defini&#231;&#227;o. Poucos times sabem a consequ&#234;ncia.</p><h3><strong>Vertical (scale-up): o pod fica maior</strong></h3><p>Voc&#234; aumenta CPU e mem&#243;ria da mesma inst&#226;ncia do servi&#231;o. Tr&#234;s problemas:</p><ul><li><p><strong>Teto f&#237;sico.</strong> Existe uma maior inst&#226;ncia dispon&#237;vel, e depois dela n&#227;o h&#225; mais para onde ir.</p></li><li><p><strong>Custo superlinear e ocioso.</strong> Voc&#234; paga o tamanho do pico durante os vales. &#192;s tr&#234;s da manh&#227;, quando o Pix cai para uma fra&#231;&#227;o do volume, aquela inst&#226;ncia gigante continua na fatura.</p></li><li><p><strong>Rein&#237;cio.</strong> Este &#233; o ponto cr&#237;tico. Aplicar um novo tamanho a um pod significa <em>recriar o pod</em>. O VPA em modo <code>Auto</code> mata e recria pods para redimension&#225;-los. Em um servi&#231;o que participa da liquida&#231;&#227;o de pagamentos, isso &#233; matar processos no meio de transa&#231;&#245;es &#8212; e o resultado n&#227;o &#233; um erro 500, &#233; uma inconsist&#234;ncia cont&#225;bil.</p></li></ul><h3><strong>Horizontal (scale-out): mais r&#233;plicas do mesmo pod</strong></h3><p>&#201; a &#250;nica forma de escala que tamb&#233;m entrega toler&#226;ncia a falha: se voc&#234; tem 28 r&#233;plicas em tr&#234;s zonas, perder uma zona &#233; perder um ter&#231;o da capacidade, n&#227;o o servi&#231;o. Por isso &#233; a escolha padr&#227;o. Mas ela tem custos escondidos que ningu&#233;m coloca no slide:</p><ul><li><p>Cada r&#233;plica &#233; <strong>mais um pool de conex&#245;es</strong> contra o banco de dados.</p></li><li><p>Cada r&#233;plica &#233; <strong>mais um heap de JVM</strong> &#8212; o overhead de mem&#243;ria por inst&#226;ncia &#233; duplicado, n&#227;o dividido.</p></li><li><p>Cada r&#233;plica &#233; <strong>mais um handshake mTLS</strong> a ser negociado com a infraestrutura do Banco Central.</p></li><li><p>Cada r&#233;plica &#233; <strong>mais um endere&#231;o IP</strong> consumido da sua subnet.</p></li></ul><h3><strong>E qual delas custa mais?</strong></h3><p>O t&#237;tulo desta se&#231;&#227;o faz duas perguntas, e a primeira tem resposta mais curta do que parece: <strong>depende de qual custo voc&#234; est&#225; olhando</strong>, e a maioria dos times olha s&#243; um.</p><p><strong>Na fatura da nuvem, a vertical costuma custar mais</strong>, por dois motivos que se somam. Inst&#226;ncias grandes raramente t&#234;m pre&#231;o proporcional &#8212; o salto de uma fam&#237;lia para a seguinte cobra pr&#234;mio por densidade. E, pior, voc&#234; paga o tamanho do pico vinte e quatro horas por dia, inclusive nas seis horas em que o Pix roda a uma fra&#231;&#227;o do volume. A escala horizontal devolve capacidade quando o tr&#225;fego cai; a vertical n&#227;o devolve nada.</p><p><strong>No custo total de propriedade, a conta &#224;s vezes se inverte.</strong> Vinte e oito r&#233;plicas significam vinte e oito pools de conex&#227;o, vinte e oito heaps de JVM com seu overhead fixo, vinte e oito conjuntos de conex&#245;es TLS e vinte e oito vezes o custo do sidecar de malha, se voc&#234; usa um. Em servi&#231;os com overhead fixo alto por inst&#226;ncia, chega um ponto em que espalhar sai mais caro do que adensar &#8212; e esse ponto aparece na conta de mem&#243;ria antes de aparecer na de CPU.</p><p>A regra &#250;til: <strong>a vertical desperdi&#231;a capacidade, a horizontal desperdi&#231;a overhead.</strong> Voc&#234; escolhe qual desperd&#237;cio prefere pagar. Mas s&#243; uma das duas pode te deixar sem servi&#231;o &#224;s onze da manh&#227; de uma sexta-feira &#8212; e &#233; essa a pergunta que realmente importa.</p><h3><strong>E qual delas derruba o cluster?</strong></h3><p>A resposta contraintuitiva: <strong>a horizontal</strong>. E quase nunca por esgotar CPU. Ela derruba por efeito de segunda ordem &#8212; o autoscaler funcionou perfeitamente e quebrou outra coisa. As mortes reais, em ordem de frequ&#234;ncia:</p><h4><strong><span>Cinco formas de o autoscaling derrubar sua plataforma</span></strong></h4><p><strong>1. Exaust&#227;o de IP no VPC CNI.</strong> No EKS, cada pod consome um IP real da subnet. O HPA sobe de 28 para 200 r&#233;plicas, o bloco CIDR acaba, e os pods ficam presos em <code>ContainerCreating</code>. N&#227;o &#233; s&#243; o seu servi&#231;o: <em>nenhum</em> pod novo sobe no cluster. Mitiga&#231;&#227;o: planejamento de CIDR desde o dia zero, <em>prefix delegation</em>, subnets dedicadas por cluster.</p><p><strong>2. Exaust&#227;o do pool de conex&#245;es.</strong> 28 r&#233;plicas &#215; 20 conex&#245;es = 560. Escalou para 90 r&#233;plicas: 1.800 conex&#245;es contra um banco que aceita 800. O autoscaling derrubou o banco de dados, e o banco de dados derrubou tudo. Mitiga&#231;&#227;o: pooler externo, ou pool por r&#233;plica dimensionado <em>em fun&#231;&#227;o do m&#225;ximo de r&#233;plicas</em>, n&#227;o do atual.</p><p><strong>3. Limite de ENI e de pods por inst&#226;ncia.</strong> Cada tipo de inst&#226;ncia EC2 suporta um n&#250;mero finito de interfaces de rede e, portanto, de pods. Escalar r&#233;plicas sem conferir esse teto gera pods pendentes com n&#243;s aparentemente vazios.</p><p><strong>4. Thundering herd de inicializa&#231;&#227;o.</strong> Quarenta pods Java subindo simultaneamente, cada um fazendo warmup de JIT, carregando cache e abrindo conex&#245;es. O pico de CPU da <em>partida</em> &#233; maior que o pico do tr&#225;fego que motivou a escala. O cluster entra em espiral: mais carga &#8594; mais escala &#8594; mais partidas &#8594; mais carga.</p><p><strong>5. HPA sem </strong><code>maxReplicas</code><strong> sensato.</strong> Combinado com Cluster Autoscaler, isso escala at&#233; estourar a quota da conta AWS ou o or&#231;amento do trimestre. O incidente n&#227;o &#233; t&#233;cnico; &#233; uma liga&#231;&#227;o da &#225;rea financeira.</p><div class="callout-block" data-callout="true"><p><strong>Veredito pr&#225;tico</strong></p><p>Use a escala vertical <em>uma vez</em>, offline, com teste de carga, para descobrir o tamanho certo de um pod. Use a escala horizontal <em>sempre</em>, em produ&#231;&#227;o, para responder a tr&#225;fego. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">O erro comum &#233; o inverso: chutar o tamanho do pod e ajustar r&#233;plicas por sensa&#231;&#227;o.</mark></p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/pix-318-milhoes-de-transacoes-em?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/pix-318-milhoes-de-transacoes-em?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p><h2><strong><span>03 / TR&#193;FEGO</span></strong></h2><h2><strong>O que voc&#234; escala e o que voc&#234; n&#227;o escala</strong></h2><p>Aqui est&#225; a diferen&#231;a entre dimensionar um e-commerce e dimensionar um participante do Pix.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!yp_L!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!yp_L!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png 424w, https://substackcdn.com/image/fetch/$s_!yp_L!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png 848w, https://substackcdn.com/image/fetch/$s_!yp_L!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png 1272w, https://substackcdn.com/image/fetch/$s_!yp_L!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!yp_L!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png" width="684" height="334.2583222370173" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:367,&quot;width&quot;:751,&quot;resizeWidth&quot;:684,&quot;bytes&quot;:38023,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/213050705?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!yp_L!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png 424w, https://substackcdn.com/image/fetch/$s_!yp_L!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png 848w, https://substackcdn.com/image/fetch/$s_!yp_L!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png 1272w, https://substackcdn.com/image/fetch/$s_!yp_L!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F40f7ea81-8fcf-427e-b919-4b51516cc17d_751x367.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A frase para levar da se&#231;&#227;o: <strong>voc&#234; pode enfileirar o que sai; n&#227;o pode enfileirar o que entra.</strong> Se o Pix recebido n&#227;o &#233; processado dentro do tempo, isso n&#227;o &#233; uma fila crescendo &#8212; &#233; um indicador regulat&#243;rio sendo violado, com nome e CNPJ.</p><p>E h&#225; um detalhe de engenharia que muda completamente o modelo mental de quem vem do mundo web: <strong>a comunica&#231;&#227;o com o SPI n&#227;o &#233; </strong><em><strong>push</strong></em><strong>, &#233; </strong><em><strong>pull</strong></em>. </p><p>O Banco Central n&#227;o bate na porta do seu servidor quando chega um Pix para o seu cliente. Em vez disso, o SPI mant&#233;m uma <strong>fila de sa&#237;da por participante</strong> &#8212; uma caixa postal onde as mensagens destinadas a voc&#234; se acumulam &#8212; e a responsabilidade de drenar essa caixa &#233; inteiramente sua. Voc&#234; inicia a leitura dessa fila com uma requisi&#231;&#227;o <code>GET</code>, e a interface devolve no m&#225;ximo dez mensagens por vez, em formato multipart. Se voc&#234; parar de buscar, nada avisa ningu&#233;m: a caixa simplesmente enche, e o tempo continua correndo contra o seu indicador.</p><p>Al&#233;m disso, a pr&#243;pria interface aplica controle de tr&#225;fego com saldo de tokens individualizado por participante &#8212; se o saldo zera, voc&#234; recebe <code>HTTP 429</code> com o cabe&#231;alho <code>Retry-After</code>.</p><div class="callout-block" data-callout="true"><h4><strong><span>Consequ&#234;ncia de arquitetura</span></strong></h4><p>Voc&#234; n&#227;o dimensiona um servidor HTTP esperando requisi&#231;&#245;es do Bacen. Voc&#234; dimensiona <strong>consumidores em la&#231;o</strong>: quantas threads fazem pull simult&#226;neo, com que intervalo, com qual paralelismo de processamento por lote de dez mensagens, e como voc&#234; desduplica e ordena o que chega. &#201; um problema de consumidor de fila, n&#227;o de API p&#250;blica. A m&#233;trica de escala correta, portanto, n&#227;o &#233; CPU nem requisi&#231;&#245;es por segundo &#8212; &#233; <strong>profundidade de fila / lag de processamento</strong>.</p></div><h2><strong><span>04 / A CONTA</span></strong></h2><h2><strong>Do volume nacional ao n&#250;mero de r&#233;plicas</strong></h2><p>At&#233; aqui: vocabul&#225;rio e restri&#231;&#245;es. Daqui em diante, a conta.</p><p>Esta &#233; a parte que quase nunca &#233; escrita, e &#233; a &#250;nica que separa dimensionamento de adivinha&#231;&#227;o. Vamos usar um banco fict&#237;cio com <strong>5% de participa&#231;&#227;o</strong> no volume nacional, num dia nacional de <strong>300 milh&#245;es de transa&#231;&#245;es</strong> &#8212; n&#250;meros redondos e inventados, escolhidos para voc&#234; refazer a conta com os seus.</p><p>Aqui est&#225; a cadeia inteira, de uma vez. Os oito degraus que separam um n&#250;mero p&#250;blico do Banco Central de um valor no seu reposit&#243;rio de infraestrutura. As subse&#231;&#245;es seguintes justificam cada um deles.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;8e120245-9f47-4152-861f-37762d6a354d&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">DEGRAU                                     OPERA&#199;&#195;O              RESULTADO
&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
1. Volume nacional do dia                                    300.000.000
2. Participa&#231;&#227;o do banco                   &#215; 5%               15.000.000 tx/dia
3. TPS m&#233;dio do dia                        &#247; 86.400                  174 tps
4. Fator intradi&#225;rio (vale da madrugada)   &#215; 2,5                     434 tps
5. Fator de calend&#225;rio (5&#186; dia &#250;til)       &#215; 1,5                     651 tps
6. Fan-out interno (8 chamadas por Pix)    &#215; 8                     5.208 rps
7. Capacidade medida por pod               &#247; 250 rps                20,8 pods
8. Margem operacional                      + 33%             28 R&#201;PLICAS
&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
Nenhum desses n&#250;meros &#233; meu. Todos s&#227;o seus &#8212; e todos devem estar
versionados junto com a infraestrutura, com a data da &#250;ltima revis&#227;o.</code></pre></div><p>Repare no que essa tabela permite. Quando o volume nacional crescer 20%, voc&#234; n&#227;o refaz o dimensionamento: voc&#234; troca o degrau 1 e l&#234; o resultado. Quando algu&#233;m adicionar uma chamada nova ao fluxo de pagamento, o degrau 6 vira 9 e o n&#250;mero de r&#233;plicas sobe sozinho. &#201; isso que separa um dimensionamento de um chute com apar&#234;ncia de rigor.</p><p>Agora, degrau por degrau.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!BIf0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!BIf0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png 424w, https://substackcdn.com/image/fetch/$s_!BIf0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png 848w, https://substackcdn.com/image/fetch/$s_!BIf0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png 1272w, https://substackcdn.com/image/fetch/$s_!BIf0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!BIf0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png" width="1257" height="532" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a831d269-891b-4984-8ac9-878184570169_1257x532.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:532,&quot;width&quot;:1257,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:41928,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/213050705?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!BIf0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png 424w, https://substackcdn.com/image/fetch/$s_!BIf0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png 848w, https://substackcdn.com/image/fetch/$s_!BIf0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png 1272w, https://substackcdn.com/image/fetch/$s_!BIf0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa831d269-891b-4984-8ac9-878184570169_1257x532.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">A curva intradi&#225;ria do Pix tem dois vales e dois picos. Dimensionar pela m&#233;dia significa estar subdimensionado por aproximadamente doze horas por dia &#8212; justamente as doze horas em que existe gente acordada para reclamar.</figcaption></figure></div><h3><strong>Degraus 4 e 5: os dois fatores de pico</strong></h3><p>Um erro comum &#233; multiplicar tudo por um n&#250;mero m&#225;gico. S&#227;o dois fatores distintos, medem coisas diferentes e se comp&#245;em:</p><ul><li><p><strong>Fator intradi&#225;rio (degrau 4)</strong> &#8212; a raz&#227;o entre o pico do dia e a m&#233;dia <em>daquele mesmo dia</em>. Existe por causa do vale da madrugada que voc&#234; acabou de ver na curva. Tipicamente entre <strong>2&#215; e 3&#215;</strong>; usei 2,5 na conta.</p></li><li><p><strong>Fator de calend&#225;rio (degrau 5)</strong> &#8212; a raz&#227;o entre um dia excepcional e um dia comum. Janela de sal&#225;rio, dia 10, sexta-feira, v&#233;spera de feriado, Black Friday, dezembro com d&#233;cimo terceiro. Tipicamente <strong>1,5&#215; a 2&#215;</strong>; usei 1,5.</p></li></ul><p>Volte agora aos dois recordes do primeiro par&#225;grafo, porque eles ensinam a parte dif&#237;cil do degrau 5. Em dezembro de 2025, o prazo legal de sal&#225;rio caiu na sexta, dia 5, e o pico foi no dia 5. Em setembro de 2026, o mesmo prazo caiu num s&#225;bado, as empresas anteciparam, e o pico foi na sexta, dia 4 &#8212; que n&#227;o era o quinto dia &#250;til por contagem nenhuma. Some ainda o feriado da Independ&#234;ncia na segunda seguinte, e um contador ing&#234;nuo de dias &#250;teis apontaria para o dia 8.</p><div class="callout-block" data-callout="true"><h4><strong><span>O erro que faz a pr&#233;-escala chegar quatro dias atrasada</span></strong></h4><p>Se o seu calend&#225;rio de capacidade tem uma regra do tipo &#8220;escalar no quinto dia &#250;til&#8221;, ele vai errar o dia em todo m&#234;s em que o prazo cair em s&#225;bado, domingo ou v&#233;spera de feriado. E vai errar para o lado pior: escalando <em>depois</em> do pico j&#225; ter passado.</p><p>A regra correta &#233; <strong>o &#250;ltimo dia banc&#225;rio antes do prazo</strong>, o que exige l&#243;gica de fim de semana e de feriado banc&#225;rio, n&#227;o um contador. &#201; uma fun&#231;&#227;o de dez linhas que ningu&#233;m escreve &#8212; e que vale mais, em disponibilidade, do que boa parte do YAML deste artigo.</p></div><p>A leitura maior &#233; essa: <strong>isso n&#227;o &#233; volatilidade, &#233; sazonalidade.</strong> E sazonalidade se coloca no calend&#225;rio, n&#227;o no relat&#243;rio de incidente.</p><h3><strong>Degrau 6, o que todo mundo pula: fan-out</strong></h3><p>Um Pix n&#227;o &#233; uma requisi&#231;&#227;o. &#201; uma orquestra&#231;&#227;o. Quando o time de produto diz &#8220;651 transa&#231;&#245;es por segundo&#8221;, o time de plataforma precisa traduzir isso para o n&#250;mero real de chamadas que atravessam a malha interna. No fluxo de envio, cada transa&#231;&#227;o costuma disparar algo pr&#243;ximo destas oito:</p><ol><li><p>Consulta da chave no DICT</p></li><li><p>Motor antifraude e verifica&#231;&#227;o de limites</p></li><li><p>Consulta e reserva de saldo</p></li><li><p>Envio da ordem de pagamento ao SPI (pacs.008)</p></li><li><p>Recebimento e processamento do retorno (pacs.002)</p></li><li><p>Efetiva&#231;&#227;o e lan&#231;amento cont&#225;bil</p></li><li><p>Notifica&#231;&#227;o ao usu&#225;rio</p></li><li><p>Publica&#231;&#227;o de evento para consumidores internos e webhooks</p></li></ol><p>Da&#237; o multiplicador 8 do degrau 6 &#8212; e da&#237; os <strong>5.208 requisi&#231;&#245;es por segundo</strong> que o resto do dimensionamento usa. Oito &#233; o n&#250;mero deste exemplo, n&#227;o uma constante do mercado: o seu pode ser seis ou catorze, e a &#250;nica forma de saber &#233; medir. Cada uma dessas chamadas, al&#233;m disso, precisa do seu pr&#243;prio c&#225;lculo de capacidade, porque elas n&#227;o t&#234;m a mesma lat&#234;ncia nem o mesmo custo. E uma delas &#8212; a primeira &#8212; n&#227;o pode ser dimensionada por este m&#233;todo de jeito nenhum, pelo motivo que a Parte 2 explica.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!70b0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!70b0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png 424w, https://substackcdn.com/image/fetch/$s_!70b0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png 848w, https://substackcdn.com/image/fetch/$s_!70b0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png 1272w, https://substackcdn.com/image/fetch/$s_!70b0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!70b0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png" width="787" height="436" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:436,&quot;width&quot;:787,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:48948,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/213050705?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!70b0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png 424w, https://substackcdn.com/image/fetch/$s_!70b0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png 848w, https://substackcdn.com/image/fetch/$s_!70b0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png 1272w, https://substackcdn.com/image/fetch/$s_!70b0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F041fa423-79a2-482a-bf26-70cb92c2268d_787x436.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">O fan-out &#233; o multiplicador silencioso do dimensionamento. Cada chamada interna precisa do seu pr&#243;prio c&#225;lculo de capacidade &#8212; e o servi&#231;o de consulta ao DICT, especificamente, n&#227;o pode ser dimensionado por este m&#233;todo, pelo motivo que a Parte 2 explica.</figcaption></figure></div><h4><strong><span>Lei de Little, em uma frase</span></strong></h4><p><code>concorr&#234;ncia = throughput &#215; lat&#234;ncia</code>. Se voc&#234; processa 5.200 requisi&#231;&#245;es por segundo e cada uma leva 120 ms, existem cerca de <strong>624 requisi&#231;&#245;es vivas ao mesmo tempo</strong> dentro do seu sistema. &#201; esse n&#250;mero &#8212; n&#227;o o de requisi&#231;&#245;es por segundo &#8212; que dimensiona threads, tamanho de pool de conex&#245;es e filas internas. E note a implica&#231;&#227;o perversa: se a lat&#234;ncia dobrar por causa de uma depend&#234;ncia lenta, a concorr&#234;ncia dobra <em>sem que o tr&#225;fego mude</em>. &#201; assim que uma lentid&#227;o externa vira esgotamento de thread pool interno.</p><h3><strong>Degrau 7: quanto um pod aguenta</strong></h3><p>Este &#233; o &#250;nico degrau que n&#227;o se calcula &#8212; se mede. Voc&#234; sobe um pod isolado, aplica carga crescente e encontra o ponto onde o percentil 99 come&#231;a a subir antes de a CPU saturar. No exemplo, esse ponto ficou em <strong>250 requisi&#231;&#245;es por segundo com p99 de 90 ms, num pod de 1 vCPU e 2 GB</strong>. Da&#237; 5.208 &#247; 250 = <strong>20,8 pods</strong>.</p><p>Se voc&#234; pular esse teste e chutar a capacidade do pod, todos os sete degraus anteriores viram decora&#231;&#227;o. &#201; o elo que transforma a cadeia em engenharia.</p><h3><strong>Degrau 8: a margem, e por que ela &#233; 33% e n&#227;o outra coisa</strong></h3><p>Vinte e um pods atendem o pico <em>se nada der errado</em>. Como algo sempre d&#225; errado, a margem cobre tr&#234;s riscos distintos, e vale explicitar cada um em vez de esconder todos atr&#225;s de um n&#250;mero redondo:</p><ul><li><p><strong>Erro de estimativa.</strong> Os degraus 4, 5 e 6 s&#227;o aproxima&#231;&#245;es. Se o fan-out real for 9 em vez de 8, voc&#234; precisa de 23 pods, n&#227;o 21.</p></li><li><p><strong>Tempo de rea&#231;&#227;o.</strong> Como a se&#231;&#227;o 05 vai mostrar, o HPA leva segundos e o provisionamento de n&#243; leva minutos. A margem &#233; o que sustenta o servi&#231;o durante esse intervalo.</p></li><li><p><strong>Falha parcial.</strong> Pods morrem, n&#243;s s&#227;o reciclados, um rolling update est&#225; sempre acontecendo em algum lugar.</p></li></ul><p>Somando: 21 &#215; 1,33 &#8776; <strong>28 r&#233;plicas</strong>.</p><div class="callout-block" data-callout="true"><h4><strong><span>O que 28 r&#233;plicas N&#195;O cobrem &#8212; e isso precisa estar escrito</span></strong></h4><p>Lembra da frase da se&#231;&#227;o 02, de que com 28 r&#233;plicas em tr&#234;s zonas perder uma zona &#233; perder um ter&#231;o da capacidade? Fa&#231;a a conta at&#233; o fim: com uma AZ fora, sobram cerca de 19 pods servindo, e voc&#234; precisava de 21 s&#243; para o pico. <strong>28 r&#233;plicas n&#227;o sobrevivem &#224; perda de uma zona no hor&#225;rio de pico sem degradar o percentil 99.</strong></p><p>Para sobreviver de verdade, a conta &#233; 21 &#215; 3/2 = <strong>32 r&#233;plicas</strong> &#8212; porque as duas zonas restantes precisam carregar sozinhas o que tr&#234;s carregavam. A diferen&#231;a entre 28 e 32 &#233; aproximadamente 15% de custo de computa&#231;&#227;o daquele servi&#231;o.</p><p>Escolher 28 &#233; leg&#237;timo. Escolher 32 &#233; leg&#237;timo. O que n&#227;o &#233; leg&#237;timo &#233; escolher sem saber que escolheu. Essa &#233; uma <strong>decis&#227;o expl&#237;cita de risco contra custo</strong>, e ela pertence a um documento de arquitetura com data e respons&#225;vel &#8212; n&#227;o a um campo de YAML que algu&#233;m preencheu de mem&#243;ria. Se a sua institui&#231;&#227;o est&#225; na categoria A de disponibilidade, o assunto da Parte 2, esse par&#225;grafo vale uma reuni&#227;o.</p></div><h5><strong><span>Pense comigo</span></strong></h5><blockquote><p>Voc&#234; sabe qual &#233; o fan-out real do seu fluxo de pagamento? N&#227;o o desenhado no diagrama de arquitetura &#8212; o medido, com trace distribu&#237;do, em produ&#231;&#227;o, no hor&#225;rio de pico.</p><p>Na maioria dos times em que perguntei, o n&#250;mero medido era entre 30% e 100% maior que o n&#250;mero desenhado. Retries, chamadas de enriquecimento esquecidas, uma consulta de saldo feita duas vezes por dois servi&#231;os diferentes. Cada ponto percentual disso &#233; hardware na fatura e lat&#234;ncia no percentil 99 que o regulador l&#234;.</p></blockquote><h2><strong><span>05 / EXECU&#199;&#195;O</span></strong></h2><h2><strong>Do n&#250;mero ao YAML</strong></h2><p>Agora os 28 pods viram configura&#231;&#227;o. Cada valor abaixo vem de uma conta, n&#227;o de um exemplo copiado da documenta&#231;&#227;o.</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;7d6d4e51-2197-47b7-b1e5-1abdd54940ab&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml"># Servi&#231;o de inicia&#231;&#227;o de Pix &#8212; dimensionado para 5.208 rps de pico
apiVersion: apps/v1
kind: Deployment
metadata:
  name: pix-iniciacao
spec:
  replicas: 28                       # degrau 8: 20,8 pods + 33% de margem
  strategy:
    type: RollingUpdate
    rollingUpdate:
      maxSurge: 25%
      maxUnavailable: 0              # 24x7x365: nunca reduzir capacidade durante deploy
  template:
    spec:
      terminationGracePeriodSeconds: 60   # &gt; transa&#231;&#227;o mais longa do fluxo
      topologySpreadConstraints:
        - maxSkew: 1
          topologyKey: topology.kubernetes.io/zone
          whenUnsatisfiable: DoNotSchedule
          labelSelector:
            matchLabels: { app: pix-iniciacao }
      containers:
        - name: app
          resources:
            requests:
              cpu: "800m"            # medido: 250 rps por pod a 800m
              memory: "1536Mi"
            limits:
              memory: "1536Mi"       # sempre. OOMKill &gt; n&#243; inst&#225;vel
              # cpu: deliberadamente ausente &#8212; ver nota sobre CFS
          env:
            - name: JAVA_TOOL_OPTIONS
              value: "-XX:MaxRAMPercentage=70.0 -XX:+UseG1GC"
          startupProbe:              # cobre o warmup da JVM
            httpGet: { path: /health/startup, port: 8080 }
            periodSeconds: 5
            failureThreshold: 24     # at&#233; 120s para subir
          readinessProbe:            # N&#195;O consulta depend&#234;ncia externa
            httpGet: { path: /health/ready, port: 8080 }
            periodSeconds: 5
          lifecycle:
            preStop:
              exec:
                command: ["sh","-c","sleep 15"]   # drenar antes de morrer</code></pre></div><h3><strong>Por que n&#227;o h&#225; limite de CPU</strong></h3><p>O limite de CPU no Kubernetes &#233; implementado como cota do CFS do kernel: o container &#233; <em>throttled</em> quando estoura a cota da janela, mesmo que haja CPU sobrando no n&#243;. Em um servi&#231;o sens&#237;vel a lat&#234;ncia, isso significa introduzir pausas artificiais exatamente no percentil que o Banco Central mede. A pr&#225;tica dominante em plataformas transacionais: <strong>defina </strong><code>requests</code><strong> de CPU para garantir escalonamento justo, e n&#227;o defina </strong><code>limits</code><strong> de CPU</strong> &#8212; ou defina com folga grande. Mem&#243;ria &#233; o oposto: sempre limite, porque mem&#243;ria n&#227;o &#233; compress&#237;vel e um pod vazando derruba o n&#243; inteiro.</p><div class="callout-block" data-callout="true"><h4><strong><span>E quando a pol&#237;tica da plataforma obriga limits?</span></strong></h4><p>Em muitos bancos, o time de plataforma exige <code>limits</code> de CPU em todos os workloads, por controle de custo, por isolamento entre inquilinos do cluster ou por exig&#234;ncia de auditoria. &#201; uma decis&#227;o defens&#225;vel e provavelmente voc&#234; n&#227;o vai revert&#234;-la com um argumento sobre CFS. O que fazer ent&#227;o:</p><p><strong>Dimensione o limite com folga real</strong> &#8212; algo entre duas e tr&#234;s vezes o <code>requests</code>, n&#227;o o mesmo valor. Limite igual ao request &#233; o cen&#225;rio de throttling garantido. <strong>Me&#231;a antes de discutir</strong>: a m&#233;trica <code>container_cpu_cfs_throttled_seconds_total</code> mostra exatamente quanto tempo o seu pod passou parado esperando cota, e um gr&#225;fico dela ao lado do p99 encerra a conversa mais r&#225;pido do que qualquer argumento te&#243;rico. E, se o throttling for real e a pol&#237;tica inegoci&#225;vel, <strong>negocie uma exce&#231;&#227;o para o caminho cr&#237;tico de liquida&#231;&#227;o</strong> em vez de para o cluster inteiro &#8212; exce&#231;&#227;o estreita e justificada costuma passar onde a ampla n&#227;o passa.</p></div><h4><strong><span>A armadilha do readinessProbe</span></strong></h4><p>Se o seu <code>readinessProbe</code> verifica a sa&#250;de de uma depend&#234;ncia externa &#8212; o DICT, por exemplo &#8212; uma instabilidade moment&#226;nea do Banco Central marca <strong>todos os seus pods como n&#227;o prontos ao mesmo tempo</strong>. O balanceador remove todo mundo. Voc&#234; acabou de converter uma degrada&#231;&#227;o parcial de terceiro em indisponibilidade total sua, por conta pr&#243;pria, e o indicador que vai para o regulador &#233; o seu.</p><p>Readiness responde: &#8220;este processo consegue atender?&#8221;. Liveness responde: &#8220;este processo precisa ser reiniciado?&#8221;. Nenhum dos dois responde &#8220;minhas depend&#234;ncias est&#227;o bem?&#8221; &#8212; isso &#233; trabalho de circuit breaker e de painel, n&#227;o de sonda.</p><h3><strong>A m&#233;trica de escala certa</strong></h3><p>CPU &#233; uma p&#233;ssima m&#233;trica de HPA para servi&#231;o que passa a maior parte do tempo esperando I/O &#8212; que &#233; exatamente o caso de qualquer servi&#231;o de pagamento. Um pod pode estar com 30% de CPU e saturado de threads bloqueadas. As duas op&#231;&#245;es que funcionam:</p><ul><li><p><strong>M&#233;trica custom via Prometheus Adapter</strong> &#8212; requisi&#231;&#245;es por segundo por pod, ou lat&#234;ncia no percentil 95, para os servi&#231;os de entrada s&#237;ncrona.</p></li><li><p><strong>KEDA com lag de fila</strong> &#8212; para os consumidores do SPI e para qualquer coisa orientada a evento. Como vimos na se&#231;&#227;o 03, o modelo do SPI &#233; pull; a profundidade da fila &#233; o sinal honesto.</p></li></ul><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;yaml&quot;,&quot;nodeId&quot;:&quot;0a561ac4-d543-42f7-aa91-977d55c7b39c&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-yaml">behavior:
  scaleUp:
    stabilizationWindowSeconds: 0    # subir imediatamente
    policies:
      - type: Percent
        value: 100                   # dobrar
        periodSeconds: 30
  scaleDown:
    stabilizationWindowSeconds: 600  # descer s&#243; ap&#243;s 10 min est&#225;veis
    policies:
      - type: Percent
        value: 10
        periodSeconds: 60</code></pre></div><p>A regra &#233; assim&#233;trica de prop&#243;sito: <strong>subir r&#225;pido, descer devagar</strong>. O custo de uma r&#233;plica a mais por dez minutos &#233; centavos. O custo de uma r&#233;plica a menos durante um pico &#233; um indicador.</p><h3><strong>O gap de dois minutos</strong></h3><p>Este &#233; o problema que separa quem leu a documenta&#231;&#227;o de quem operou o cluster num quinto dia &#250;til.</p><p>Os n&#250;meros, em sequ&#234;ncia. O HPA avalia m&#233;tricas a cada <strong>15 segundos</strong> e, com a janela de estabiliza&#231;&#227;o zerada, decide escalar em algo entre <strong>15 e 30 segundos</strong>. Ele cria os pods imediatamente &#8212; e &#233; a&#237; que o problema come&#231;a, porque criar um pod n&#227;o &#233; o mesmo que ter onde coloc&#225;-lo. Se n&#227;o h&#225; n&#243; com espa&#231;o, o pod fica em <code>Pending</code>. Provisionar um n&#243; novo leva cerca de <strong>40 a 60 segundos com Karpenter</strong> e tipicamente <strong>2 a 3 minutos com o Cluster Autoscaler cl&#225;ssico</strong>, somando decis&#227;o, chamada &#224; API da AWS, boot da inst&#226;ncia, registro no cluster e download de imagem.</p><p>Ou seja: entre o tr&#225;fego subir e a capacidade nova existir de verdade, passam-se de um a tr&#234;s minutos. Nesse intervalo, os pods novos existem no <code>etcd</code> e n&#227;o existem em lugar nenhum. <strong>O pico do Pix n&#227;o espera tr&#234;s minutos.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IL79!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IL79!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png 424w, https://substackcdn.com/image/fetch/$s_!IL79!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png 848w, https://substackcdn.com/image/fetch/$s_!IL79!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png 1272w, https://substackcdn.com/image/fetch/$s_!IL79!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IL79!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png" width="1039" height="376" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:376,&quot;width&quot;:1039,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24369,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/213050705?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IL79!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png 424w, https://substackcdn.com/image/fetch/$s_!IL79!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png 848w, https://substackcdn.com/image/fetch/$s_!IL79!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png 1272w, https://substackcdn.com/image/fetch/$s_!IL79!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb31732b8-2c43-4328-8fed-9d4af3af4b0b_1039x376.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a><figcaption class="image-caption">Karpenter reduz esse intervalo para algo perto de 40 a 60 segundos; o Cluster Autoscaler cl&#225;ssico costuma levar de 2 a 3 minutos. Nos dois casos, o intervalo &#233; maior que a paci&#234;ncia de um pico de Pix.</figcaption></figure></div><p>As tr&#234;s respostas usadas em produ&#231;&#227;o para fechar esse intervalo:</p><ul><li><p><strong>Pods de headroom.</strong> Um Deployment de pods que n&#227;o fazem nada, com <code>priorityClass</code> de valor negativo, ocupando espa&#231;o em n&#243;s j&#225; provisionados. Quando o servi&#231;o real precisa escalar, o scheduler <em>preempta</em> os pods de headroom instantaneamente e usa a capacidade que j&#225; estava quente. Voc&#234; paga capacidade ociosa em troca de tempo de rea&#231;&#227;o &#8212; e em pagamento, tempo de rea&#231;&#227;o &#233; o produto.</p></li><li><p><strong>Pr&#233;-escala por calend&#225;rio.</strong> Um CronJob que aumenta <code>minReplicas</code> do HPA antes das janelas conhecidas: &#250;ltimo dia banc&#225;rio antes do prazo de sal&#225;rio, dia 10, sextas, v&#233;spera de feriado, Black Friday, dezembro. Repare que a primeira dessas janelas &#233; calculada, n&#227;o &#233; uma data fixa &#8212; como a se&#231;&#227;o 04 mostrou, uma regra ing&#234;nua de &#8220;quinto dia &#250;til&#8221; erra o dia justamente nos meses em que o pico &#233; maior. Sazonalidade previs&#237;vel n&#227;o deveria ser tratada como surpresa.</p></li><li><p><strong>Karpenter com node pools pr&#233;-aquecidos</strong> e diversidade de tipos de inst&#226;ncia, para n&#227;o depender de um &#250;nico tipo estar dispon&#237;vel na AZ no momento exato do pico.</p></li></ul><div class="callout-block" data-callout="true"><h4><strong><span>Pr&#225;tica de mercado</span></strong></h4><p>Os times de plataforma dos bancos brasileiros que operam Pix em volume relevante mant&#234;m um <strong>calend&#225;rio de capacidade</strong> versionado junto com a infraestrutura, e um <strong>freeze de mudan&#231;as</strong> nas janelas cr&#237;ticas. N&#227;o &#233; burocracia: a maioria dos incidentes graves em ambiente transacional n&#227;o &#233; causada por excesso de tr&#225;fego, &#233; causada por <em>mudan&#231;a</em> feita perto do excesso de tr&#225;fego. O gr&#225;fico mostra capacidade; a causa raiz &#233; o deploy.</p></div><h3><strong>Morrer direito</strong></h3><p>Um detalhe que s&#243; aparece quando o dinheiro &#233; real. Quando o Kubernetes decide encerrar um pod, ele envia <code>SIGTERM</code> e, em paralelo, remove o pod dos endpoints do servi&#231;o. Essas duas coisas n&#227;o s&#227;o at&#244;micas: existe uma janela em que o pod j&#225; recebeu o sinal de t&#233;rmino e ainda est&#225; recebendo tr&#225;fego. Da&#237; o <code>preStop</code> com uma pausa &#8212; ele d&#225; tempo para a remo&#231;&#227;o propagar antes de a aplica&#231;&#227;o come&#231;ar a se desligar. Depois disso, o <code>terminationGracePeriodSeconds</code> precisa ser maior que a transa&#231;&#227;o mais longa em voo.</p><p>Sem isso, um rolling update rotineiro &#224;s 14h de uma sexta-feira interrompe liquida&#231;&#245;es no meio. E o efeito n&#227;o &#233; um erro que o usu&#225;rio reenvia &#8212; &#233; uma transa&#231;&#227;o em estado indeterminado que algu&#233;m vai conciliar manualmente na segunda-feira.</p><h2>06 / FECHAMENTO</h2><h2><strong>O que a Parte 1 comprou</strong></h2><p>Se voc&#234; seguiu a escada, agora existe uma cadeia audit&#225;vel entre um n&#250;mero p&#250;blico do Banco Central e um valor no seu reposit&#243;rio de infraestrutura. Quando algu&#233;m perguntar &#8220;por que 28 r&#233;plicas?&#8221;, a resposta n&#227;o &#233; &#8220;foi o que a gente configurou&#8221;. S&#227;o oito degraus, cada um com uma premissa nomeada, revis&#225;vel quando o volume mudar &#8212; e ele vai mudar, todo trimestre. Dois recordes nacionais em nove meses deveriam bastar como aviso.</p><p>Um checklist do que revisar segunda de manh&#227;:</p><ul><li><p>Qual &#233; o fan-out <strong>medido</strong> do seu fluxo de pagamento, n&#227;o o desenhado?</p></li><li><p>Algu&#233;m j&#225; mediu quantos rps um pod seu aguenta, ou o degrau 7 &#233; um chute?</p></li><li><p>A sua margem do degrau 8 cobre a perda de uma zona no pico &#8212; e, se n&#227;o cobre, isso est&#225; escrito em algum lugar com data e respons&#225;vel?</p></li><li><p>Seu <code>maxReplicas</code> multiplicado pelo tamanho do pool de conex&#245;es cabe no seu banco de dados?</p></li><li><p>Seu bloco CIDR suporta o n&#250;mero m&#225;ximo de pods que o HPA pode criar?</p></li><li><p>Seu <code>readinessProbe</code> depende de terceiro?</p></li><li><p>Existe headroom, ou o pico vai esperar o n&#243;?</p></li><li><p>O <code>terminationGracePeriodSeconds</code> &#233; maior que a sua transa&#231;&#227;o mais longa?</p></li></ul><h4><strong><span>Pense comigo</span></strong></h4><blockquote><p>Toda a matem&#225;tica desta Parte 1 assume que capacidade &#233; algo que voc&#234; compra. Voc&#234; calcula, provisiona, escala e o problema cede.</p><p>Na Parte 2 n&#243;s vamos encontrar o componente do ecossistema Pix onde essa premissa simplesmente n&#227;o vale &#8212; onde adicionar r&#233;plicas n&#227;o aumenta nada, porque o limite n&#227;o &#233; o seu hardware, &#233; uma cota definida em norma. E onde a decis&#227;o entre um microservi&#231;o dedicado e uma fun&#231;&#227;o Lambda deixa de ser prefer&#234;ncia arquitetural e vira uma conta de custo e de percentil.</p><p><strong>Na Parte 2:</strong> como a API do DICT realmente funciona, o algoritmo de baldes e fichas do Banco Central, por que consulta de chave n&#227;o escala como servi&#231;o normal, microservi&#231;o dedicado versus Lambda com a conta feita, a r&#233;gua de disponibilidade por categoria de participante e o que muda no dia a dia dos times de engenharia e de SRE quando o SLO vem do regulador.</p><p><strong>Fontes prim&#225;rias para quem quiser conferir os n&#250;meros:</strong> Manual de Tempos do Pix, Manual Operacional do DICT e Manual das Interfaces de Comunica&#231;&#227;o, todos publicados pelo Banco Central; estat&#237;sticas do Pix no portal do BCB; reposit&#243;rios <code>bacen/pix-api</code> e <code>bacen/pix-dict-api</code> no GitHub. Os manuais s&#227;o revisados com frequ&#234;ncia &#8212; sempre confira a vers&#227;o vigente antes de usar um n&#250;mero em decis&#227;o de arquitetura.</p></blockquote><p><strong>Obrigado por ler at&#233; o final, at&#233; o pr&#243;ximo!</strong></p>]]></content:encoded></item><item><title><![CDATA[Quando Orientação a Objetos Estraga Seu Modelo de Dados]]></title><description><![CDATA[Voc&#234; vai trocar de framework tr&#234;s vezes nos pr&#243;ximos dez anos. A tabela de lan&#231;amentos que voc&#234; criou em uma segunda-feira em 2009 vai sobreviver a todas elas &#8212; e a todos os seus sucessores.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/quando-orientacao-a-objetos-estraga</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/quando-orientacao-a-objetos-estraga</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Fri, 04 Sep 2026 13:44:51 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/857129f9-26a1-42d5-8efe-43bdc190e3c8_2768x1848.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="pullquote"><p><strong>O DBA foi embora e deixou a chave com voc&#234;</strong></p></div><p>Em 2013, o fluxo era claro. Voc&#234; recebia um documento &#8212; quase sempre um PDF com um DER exportado do Erwin &#8212; e implementava. As tabelas j&#225; existiam. Os nomes das colunas j&#225; tinham sido decididos por algu&#233;m que passou os &#250;ltimos quinze anos brigando por integridade referencial. Se voc&#234; quisesse um &#237;ndice, abria um chamado. Se quisesse uma coluna nova, abria um chamado e esperava o comit&#234; de mudan&#231;as da quinta-feira.</p><p>Aquilo era lento, burocr&#225;tico e frequentemente irritante. Mas havia ali uma coisa que perdemos sem perceber: <strong>existia um adulto na sala cuja &#250;nica fun&#231;&#227;o era proteger os dados de n&#243;s.</strong></p><p>Hoje, o fluxo &#233; outro. A squad &#233; aut&#244;noma. Voc&#234; provisiona o banco com Terraform, escolhe entre PostgreSQL e DynamoDB numa thread do Slack que dura quinze minutos, define a chave de parti&#231;&#227;o no meio de um refinamento e sobe a migration junto com o PR da feature. O DBA virou SRE de plataforma, ou virou &#8220;time de dados&#8221; que cuida do lakehouse, ou simplesmente n&#227;o existe mais no organograma. Ningu&#233;m revisa seu modelo. Ningu&#233;m pergunta por que aquele campo &#233; <code>VARCHAR(255)</code> guardando um valor monet&#225;rio.</p><p>N&#243;s ganhamos velocidade &#8212; muita. E ganhamos tamb&#233;m uma responsabilidade que a maioria de n&#243;s nunca foi treinada para exercer: <strong>traduzir requisito de neg&#243;cio em estrutura de dados</strong>. N&#227;o &#233; uma tarefa acess&#243;ria do desenvolvimento. &#201; <em>a</em> decis&#227;o de arquitetura mais duradoura que um engenheiro toma.</p><div class="callout-block" data-callout="true"><h4><strong><span>A assimetria que ningu&#233;m te contou</span></strong></h4><p>Refatorar uma classe custa uma tarde. Trocar de framework custa um algumas semanas. Corrigir um modelo de dados errado em produ&#231;&#227;o, com sete anos de hist&#243;rico, integra&#231;&#245;es downstream, relat&#243;rios regulat&#243;rios e tr&#234;s sistemas legados lendo direto do seu banco, custa <strong>um programa inteiro</strong> &#8212; com gerente de projeto, comit&#234; e risco de compliance.</p></div><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#201; por isso que a r&#233;gua da modelagem precisa ser mais alta que a r&#233;gua do c&#243;digo. C&#243;digo ruim voc&#234; reescreve. </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Dado ruim voc&#234; </mark></strong><em><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">carrega</mark></strong></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">.</mark></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>O pecado original: quando orienta&#231;&#227;o a objetos vira modelagem de dados</strong></h2><p>Antes dos princ&#237;pios, precisamos falar de um vi&#233;s que a nossa forma&#231;&#227;o instalou e que atrapalha ativamente o racioc&#237;nio sobre dados.</p><p><strong>A maioria de n&#243;s aprendeu a modelar </strong><em><strong>objetos</strong></em><strong> antes de aprender a modelar </strong><em><strong>dados</strong></em><strong>.</strong> E aprendeu que a classe &#233; o ponto de partida. Ent&#227;o o caminho natural virou: escrevo a entidade, anoto com <code>@Entity</code>, deixo o Hibernate gerar o DDL, e pronto &#8212; &#8220;o modelo est&#225; feito&#8221;.</p><p>S&#243; que n&#227;o est&#225;. O que existe ali &#233; uma <strong>proje&#231;&#227;o acidental de decis&#245;es de c&#243;digo sobre uma estrutura de armazenamento permanente</strong>. Ningu&#233;m decidiu nada sobre os dados. O schema foi um efeito colateral.</p><h4><strong>Objetos e dados s&#227;o coisas diferentes</strong></h4><p>Comece pelo <strong>tempo de vida</strong>. Um objeto existe por milissegundos: nasce numa requisi&#231;&#227;o, &#233; serializado, morre no garbage collector. O dado que ele representa existe por d&#233;cadas &#8212; vai sobreviver ao servi&#231;o que o criou, &#224; linguagem em que foi escrito e provavelmente &#224; empresa que o gerou na forma atual.</p><p>Depois, a <strong>identidade</strong>. Objeto tem identidade por refer&#234;ncia: dois objetos com os mesmos atributos podem ser coisas distintas, e voc&#234; resolve isso sobrescrevendo <code>equals()</code>. Dado tem identidade por chave, e essa chave precisa ser est&#225;vel, &#250;nica e significativa para o mundo externo &#8212; porque outros sistemas v&#227;o apontar para ela.</p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Vem ent&#227;o a invers&#227;o mais importante, e a que quase ningu&#233;m enuncia: </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">objeto existe para esconder estado; dado existe para expor estado</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">. </mark>Encapsulamento &#233; a virtude central da orienta&#231;&#227;o a objetos &#8212; o mundo l&#225; fora n&#227;o deve saber como o saldo &#233; calculado. Mas o banco de dados tem a fun&#231;&#227;o oposta: ele precisa ser consult&#225;vel, audit&#225;vel, agreg&#225;vel por gente que nunca vai ler o seu c&#243;digo. Aplicar reflexo de encapsulamento na modelagem de dados produz esquemas opacos, com campos cr&#237;pticos<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a> cujo significado s&#243; o servi&#231;o dono conhece.</p><p>Some a isso a <strong>quantidade de consumidores</strong>. O seu objeto tem exatamente um consumidor: o seu c&#243;digo. O seu dado tem N &#8212; o time de BI, o relat&#243;rio regulat&#243;rio, o servi&#231;o de antifraude, o pipeline do lakehouse, o analista que vai abrir um cliente SQL &#224;s onze da noite para investigar um incidente, e o pr&#243;ximo time daqui a quatro anos. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Modelar para um consumidor conhecido &#233; f&#225;cil; modelar para consumidores que voc&#234; nunca vai conhecer exige outro tipo de rigor.</mark></p><p>E, por fim, o <strong>crit&#233;rio de otimiza&#231;&#227;o</strong>. Objeto &#233; otimizado para comportamento: <em><strong>coes&#227;o, baixo acoplamento, facilidade de mudan&#231;a</strong></em>.<mark data-color="#fce5cd" style="background-color: rgb(252, 229, 205); color: rgb(0, 0, 0);"> Dado &#233; otimizado para verdade e para perguntas: integridade, rastreabilidade, lat&#234;ncia de consulta</mark>. S&#227;o eixos diferentes &#8212; e otimizar um deles n&#227;o melhora o outro por acidente.</p><p>O <em><a href="https://agiledata.org/essays/impedancemismatch.html">object-relational impedance mismatch</a></em> n&#227;o &#233; um detalhe de mapeamento. &#201; um sintoma de que os dois mundos respondem a perguntas diferentes. Quando voc&#234; deixa o modelo de objetos ditar o modelo de dados, tr&#234;s estragos previs&#237;veis acontecem:</p><p><strong>1. O UPDATE destrutivo.</strong><span> Este &#233; o mais caro de todos. Em OO, mutar estado &#233; natural:</span></p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;885528c2-6d0f-4846-8ac5-4963df3319a4&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">// parece limpo. &#201; uma bomba-rel&#243;gio.
public class Conta {
    private BigDecimal saldo;

    public void debitar(BigDecimal valor) {
        this.saldo = this.saldo.subtract(valor);
    }
}</code></pre></div><p>Mapeado direto, isso vira <code>UPDATE conta SET saldo = ? WHERE id = ?</code>. E a&#237;, tr&#234;s meses depois, chega uma contesta&#231;&#227;o de MED sobre um Pix de R$ 4.200 e algu&#233;m pergunta: <em>&#8220;qual era o saldo dessa conta &#224;s 14h32 do dia 12?&#8221;</em>. A resposta honesta &#233;: n&#227;o sabemos. N&#243;s sobrescrevemos.</p><p><strong>2. A heran&#231;a que vaza para a tabela.</strong> <code>SINGLE_TABLE</code> produz aquela tabela com 60 colunas onde 40 s&#227;o nulas dependendo da linha. <code>JOINED</code> produz JOINs em toda leitura para servir uma hierarquia que s&#243; existe porque quer&#237;amos reaproveitar c&#243;digo. Heran&#231;a &#233; ferramenta de re&#250;so de comportamento. Ela raramente descreve uma verdade do neg&#243;cio.</p><p><strong>3. O modelo an&#234;mico invertido.</strong> A classe vira um saco de getters e setters, a regra vaga nos services &#8212; e como a classe n&#227;o tem sem&#226;ntica, a tabela tamb&#233;m n&#227;o tem. Fica um <code>status</code> gen&#233;rico, um <code>tipo</code> gen&#233;rico, um <code>valor</code> sem moeda. O dom&#237;nio desaparece dos dois lados ao mesmo tempo.</p><h4><strong><span>Ajuste de mentalidade</span></strong></h4><p>N&#227;o &#233; &#8220;OO &#233; ruim&#8221;. &#201; que <strong>o modelo de dom&#237;nio e o modelo de persist&#234;ncia s&#227;o dois artefatos distintos</strong>, com prop&#243;sitos distintos, e &#233; saud&#225;vel que eles n&#227;o sejam id&#234;nticos. O objeto responde <em>&#8220;o que esse conceito faz?&#8221;</em>. O dado responde <em>&#8220;o que aconteceu, e como eu provo?&#8221;</em>. Quando voc&#234; aceita que s&#227;o perguntas diferentes, os cinco princ&#237;pios abaixo deixam de ser teoria.</p><h4><strong>Os cinco princ&#237;pios</strong></h4><ol><li><p>O modelo de dados &#233; o modelo do neg&#243;cio</p></li><li><p>Fatos s&#227;o imut&#225;veis; estado &#233; derivado</p></li><li><p>Modele para as perguntas, n&#227;o para os dados</p></li><li><p>Consist&#234;ncia tem fronteiras &#8212; e voc&#234; as desenha</p></li><li><p>Todo schema evolui; <em>schema-less</em> &#233; uma mentira confort&#225;vel</p></li></ol><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/quando-orientacao-a-objetos-estraga?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/quando-orientacao-a-objetos-estraga?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p><h2><strong><span>Princ&#237;pio 01: </span>O modelo de dados &#233; o modelo do neg&#243;cio</strong></h2><p>Se o pessoal de opera&#231;&#245;es diz &#8220;devolu&#231;&#227;o&#8221;, o jur&#237;dico diz &#8220;devolu&#231;&#227;o&#8221;, o Bacen diz &#8220;devolu&#231;&#227;o&#8221; &#8212; e a sua tabela chama <code>TB_MOV_CAD_02</code> com uma flag <code>TP_MOV = 'D'</code> &#8212;, voc&#234; n&#227;o modelou dados. <strong>Voc&#234; criou um dicion&#225;rio paralelo que existe na cabe&#231;a de tr&#234;s pessoas, das quais duas j&#225; pediram demiss&#227;o. &#128579;</strong></p><p>Modelagem come&#231;a nos substantivos e verbos do dom&#237;nio, n&#227;o nos tipos de coluna. E isso n&#227;o &#233; preciosismo est&#233;tico: nome &#233; a interface de leitura do seu dado, e ela dura mais que qualquer API.</p><h4><strong><span>Li&#231;&#227;o 1.1 </span>&#8594;<span> </span>Nome de coluna &#233; contrato permanente, n&#227;o vari&#225;vel local</strong></h4><p>Renomear uma vari&#225;vel &#233; Shift+F6. Renomear uma coluna em produ&#231;&#227;o &#233;: migration, dual-write, atualizar as views, avisar o time de BI, atualizar os DAGs do lakehouse, atualizar o relat&#243;rio regulat&#243;rio, descobrir que existe um <code>SELECT *</code> num job COBOL de 2008 e negociar a janela de mudan&#231;a.</p><p>Na pr&#225;tica, ningu&#233;m renomeia. O nome errado fica. E o pior: <strong>o nome errado ensina errado</strong>. Todo desenvolvedor que chegar nos pr&#243;ximos oito anos vai aprender o dom&#237;nio lendo o seu schema, e vai aprender torto.</p><p>Escreva o nome pensando em quem vai ler daqui a cinco anos, n&#227;o em quem est&#225; digitando agora.</p><h4><strong><span>Li&#231;&#227;o 1.2 </span>&#8594;<span> </span>N&#227;o ache conceitos distintos sob um nome gen&#233;rico</strong></h4><p>Este &#233; o erro mais comum e o mais destrutivo em sistemas financeiros. A palavra &#8220;transa&#231;&#227;o&#8221; parece cobrir tudo &#8212; ent&#227;o cria-se <code>tb_transacao</code> com um <code>tipo</code> e um <code>status</code>, e enfia-se o mundo inteiro l&#225; dentro.</p><p>S&#243; que num cart&#227;o de cr&#233;dito, quatro coisas absolutamente diferentes acontecem com o mesmo dinheiro:</p><ul><li><p><strong>Autoriza&#231;&#227;o</strong> &#8212; a bandeira reserva limite. N&#227;o h&#225; dinheiro se movendo. Pode expirar sozinha.</p></li><li><p><strong>Captura</strong> &#8212; o lojista confirma a venda. Agora existe uma obriga&#231;&#227;o.</p></li><li><p><strong>Liquida&#231;&#227;o</strong> &#8212; o dinheiro efetivamente troca de m&#227;os entre institui&#231;&#245;es, em D+N.</p></li><li><p><strong>Faturamento</strong> &#8212; a obriga&#231;&#227;o entra num ciclo de cobran&#231;a do portador.</p></li></ul><p>Cada uma tem ciclo de vida pr&#243;prio, prazo pr&#243;prio, efeito cont&#225;bil pr&#243;prio e regra de estorno pr&#243;pria. Achatar as quatro em uma tabela com <code>status IN ('AUTORIZADA','CAPTURADA','LIQUIDADA','FATURADA')</code> parece elegante no dia 1 e vira um inferno no dia 400 &#8212; porque uma autoriza&#231;&#227;o pode ser parcialmente capturada, uma captura pode ser liquidada em duas parcelas de agenda, e o modelo n&#227;o tem onde colocar isso.</p><p><strong>Regra pr&#225;tica:</strong> se dois &#8220;estados&#8221; de uma entidade t&#234;m ciclos de vida independentes, cardinalidade diferente entre si, ou donos diferentes no neg&#243;cio &#8212; eles n&#227;o s&#227;o estados. S&#227;o entidades diferentes.</p><h4><strong><span>Li&#231;&#227;o 1.3 </span>&#8594;<span> </span>Seu modelo precisa passar no teste do auditor</strong></h4><p>Pegue o seu DER e mostre para um analista de neg&#243;cio, um contador ou algu&#233;m de compliance. N&#227;o explique nada. Se a pessoa n&#227;o conseguir apontar onde est&#225; a devolu&#231;&#227;o de um Pix, onde est&#225; o bloqueio judicial e onde est&#225; o saldo dispon&#237;vel <em>versus</em> o saldo bloqueado, o modelo falhou &#8212; mesmo que o c&#243;digo funcione perfeitamente.</p><p>Isso n&#227;o &#233; sobre documenta&#231;&#227;o bonita. &#201; que num dom&#237;nio regulado, o seu banco de dados <strong>&#233;</strong> a evid&#234;ncia. Quando o regulador pedir a trilha de uma opera&#231;&#227;o e a resposta exigir um JOIN de seis tabelas mais o conhecimento t&#225;cito de que <code>flag_ctrl = 3</code> significa &#8220;estornado por MED&#8221;, voc&#234; n&#227;o tem um problema t&#233;cnico. Tem um problema de neg&#243;cio com prazo.</p><p><strong><span>Pense comigo &#8212; antes de nomear qualquer coisa</span></strong></p><ul><li><p><strong>Esse nome existe no vocabul&#225;rio de quem opera o neg&#243;cio?</strong> Se voc&#234; inventou o termo, provavelmente inventou tamb&#233;m o conceito &#8212; e conceito inventado n&#227;o sobrevive &#224; primeira conversa com o jur&#237;dico.</p></li><li><p><strong>Quando eu digo &#8220;transa&#231;&#227;o&#8221;, o especialista de dom&#237;nio entende a mesma coisa que eu?</strong> Pergunte assim, literalmente: <em>&#8220;para voc&#234;, o que &#233; uma transa&#231;&#227;o aqui?&#8221;</em>. Se ele come&#231;ar a responder com &#8220;depende&#8221;, voc&#234; acabou de descobrir que s&#227;o v&#225;rias entidades.</p></li><li><p><strong>Esses dois campos que eu estou juntando t&#234;m o mesmo dono e o mesmo ciclo de vida?</strong> Se um muda por decis&#227;o do cliente e o outro por evento do Bacen, eles n&#227;o pertencem &#224; mesma linha.</p></li><li><p><strong>Se eu sumir amanh&#227;, algu&#233;m entende esse schema sem me perguntar nada?</strong> Essa &#233; a &#250;nica m&#233;trica de qualidade de nomenclatura que importa.</p></li></ul><h2><strong>Princ&#237;pio 02: Fatos s&#227;o imut&#225;veis; estado &#233; derivado</strong></h2><p>Este &#233; o princ&#237;pio mais poderoso do artigo, o mais violado na pr&#225;tica, e o &#250;nico que a contabilidade resolveu <strong>no s&#233;culo XIV</strong> &#8212; enquanto n&#243;s ainda escrevemos <code>UPDATE saldo SET ...</code> em 2026.</p><p>Luca Pacioli formalizou as partidas dobradas em 1494 sobre uma ideia simples: voc&#234; n&#227;o altera o passado. Voc&#234; registra um novo lan&#231;amento. <strong>O raz&#227;o &#233; </strong><em><strong><a href="https://www.tigrisdata.com/blog/append-only-storage/">append-only</a></strong></em><strong>, e o saldo nunca &#233; armazenado como verdade &#8212; ele &#233; calculado</strong>. &#201; a arquitetura de dados mais bem-sucedida da hist&#243;ria da humanidade, e ela &#233; anterior ao computador.</p><h3><strong>Li&#231;&#227;o 2.1 &#8594; O UPDATE destrutivo apaga a &#250;nica evid&#234;ncia que voc&#234; tinha</strong></h3><p>Compare as duas modelagens de uma transfer&#234;ncia de R$ 100 entre contas internas.</p><p><strong>Modelo mut&#225;vel (o que a maioria faz):</strong></p><pre><code><code>UPDATE conta SET saldo = saldo - 100 WHERE id = 'A';
UPDATE conta SET saldo = saldo + 100 WHERE id = 'B';</code></code></pre><p>Funciona. &#201; r&#225;pido. E destruiu a informa&#231;&#227;o. N&#227;o existe mais como saber <em>por que</em> o saldo mudou, <em>quando</em>, <em>por ordem de quem</em>, ou <em>qual era antes</em>. Voc&#234; guardou o resultado e jogou fora a causa.</p><p><strong>Modelo de fatos (raz&#227;o append-only):</strong></p><pre><code><code>id     conta   natureza   valor     origem
&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
9001   A       DEBITO     100,00    TRANSF#7712
9002   B       CREDITO    100,00    TRANSF#7712</code></code></pre><p>Nada foi sobrescrito. Duas linhas novas, apontando para a mesma ordem de transfer&#234;ncia &#8212; e a soma das duas &#233; zero, que &#233; exatamente a garantia das partidas dobradas.</p><pre><code><code>-- saldo deixa de ser um campo. Vira uma pergunta.
SELECT SUM(CASE WHEN tipo = 'CREDITO' THEN valor ELSE -valor END)
FROM lancamento
WHERE conta = 'A' AND data_movimento &lt;= :data_corte;</code></code></pre><p>Repare no ganho que caiu de gra&#231;a: aquele <code>:data_corte</code> significa que voc&#234; consegue responder <em>&#8220;qual era o saldo em qualquer instante do passado&#8221;</em> &#8212; sem nenhuma estrutura adicional. No modelo mut&#225;vel, essa pergunta &#233; imposs&#237;vel a qualquer pre&#231;o.</p><h4><strong><span>&#8220;Mas isso n&#227;o &#233; lento?&#8221;</span></strong></h4><p>&#201; a obje&#231;&#227;o padr&#227;o, e ela tem resposta madura: <strong>snapshot</strong>. Voc&#234; materializa o saldo consolidado em pontos de corte (fechamento di&#225;rio, por exemplo) e soma apenas o delta desde o &#250;ltimo snapshot. O snapshot &#233; <em>cache</em>, n&#227;o verdade &#8212; pode ser descartado e reconstru&#237;do a qualquer momento a partir dos fatos. O caminho errado &#233; o inverso: guardar o saldo como verdade e tentar reconstruir os fatos depois. Isso n&#227;o tem volta.</p><h3><strong>Li&#231;&#227;o 2.2 &#8594; O fato precisa carregar o &#8220;porqu&#234;&#8221;, n&#227;o s&#243; o &#8220;quanto&#8221;</strong></h3><p>Muitos times chegam na metade do caminho: criam uma tabela de hist&#243;rico com <code>saldo_anterior</code> e <code>saldo_novo</code>. Isso &#233; melhor que nada, mas ainda n&#227;o &#233; hist&#243;ria &#8212; &#233; o <em>diff</em> sem o <em>commit message</em>.</p><p>Um fato bem modelado responde: <strong>o que aconteceu, quando, por causa de qu&#234;, por ordem de quem, e sob qual refer&#234;ncia externa</strong>.</p><pre><code><code>lancamento
&#9500;&#9472; id
&#9500;&#9472; conta_id
&#9500;&#9472; natureza          DEBITO | CREDITO
&#9500;&#9472; valor             DECIMAL(18,2) &#8212; nunca float, nunca varchar
&#9500;&#9472; evento_origem     PIX_RECEBIDO | DEVOLUCAO_MED | TARIFA_TED | ...
&#9500;&#9472; referencia_externa E2E ID do Pix, NSU, ID do contrato
&#9500;&#9472; lancamento_estornado_id aponta para o fato que este reverte
&#9500;&#9472; data_movimento    quando o dinheiro se moveu
&#9500;&#9472; data_competencia  a que per&#237;odo isso pertence
&#9492;&#9472; data_registro     quando N&#211;S soubemos</code></code></pre><p>E note a linha de estorno: no modelo de fatos, <strong>voc&#234; nunca apaga nem edita um lan&#231;amento errado</strong>. Voc&#234; lan&#231;a o contr&#225;rio e aponta para o original. &#201; exatamente o que a contabilidade chama de lan&#231;amento de retifica&#231;&#227;o, e &#233; o que permite que a resposta para <em>&#8220;esse cliente foi debitado indevidamente?&#8221;</em> seja <code>SELECT</code>, e n&#227;o arqueologia em log de aplica&#231;&#227;o.</p><h3><strong>Li&#231;&#227;o 2.3 &#8594; Tempo &#233; dimens&#227;o do dado, n&#227;o metadado de auditoria</strong></h3><p>Quase todo modelo carrega um <code>created_at</code> e acha que resolveu o tempo. N&#227;o resolveu. Em sistemas financeiros existem, no m&#237;nimo, <strong>tr&#234;s tempos independentes</strong>, e confundi-los gera diverg&#234;ncia cont&#225;bil real:</p><ul><li><p><strong>Data de movimento</strong> &#8212; quando o dinheiro efetivamente se moveu.</p></li><li><p><strong>Data de compet&#234;ncia</strong> &#8212; a que per&#237;odo o fato pertence economicamente.</p></li><li><p><strong>Data de registro (ou conhecimento)</strong> &#8212; quando o seu sistema tomou ci&#234;ncia.</p></li></ul><p>Um exemplo que todo mundo que trabalha com fechamento reconhece: em 3 de agosto, chega uma devolu&#231;&#227;o referente a uma opera&#231;&#227;o de 28 de julho. Movimento: 3/8. Compet&#234;ncia: julho. Registro: 3/8. Se o seu modelo tem uma data s&#243;, voc&#234; &#233; obrigado a escolher entre mentir para a contabilidade ou mentir para o extrato do cliente.</p><p>E h&#225; a pergunta que separa modelo maduro de modelo ing&#234;nuo: <em>&#8220;me mostre o balancete como ele estava em 31/07, do jeito que n&#243;s o v&#237;amos naquele dia&#8221;</em>. Isso &#233; modelagem <strong>bitemporal</strong> &#8212; tempo do fato &#215; tempo do conhecimento. Se voc&#234; fez UPDATE, essa pergunta n&#227;o tem resposta. Se voc&#234; guardou fatos com as tr&#234;s datas, &#233; um <code>WHERE</code>.</p><p><strong><span>Pense comigo &#8212; antes de escrever qualquer UPDATE</span></strong></p><ul><li><p><strong>Isso que eu vou sobrescrever &#233; um estado ou &#233; um fato?</strong> &#8220;Endere&#231;o do cliente&#8221; &#233; estado (talvez). &#8220;Saldo&#8221;, &#8220;status de pagamento&#8221;, &#8220;limite aprovado&#8221; quase sempre s&#227;o consequ&#234;ncias de fatos &#8212; <em><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">e fato n&#227;o se sobrescreve</mark></strong></em>.</p></li><li><p><strong>Se algu&#233;m perguntar daqui a dois anos por que esse valor mudou, eu consigo responder s&#243; com dados?</strong> Se a resposta envolve &#8220;olha o log da aplica&#231;&#227;o&#8221;, o modelo est&#225; incompleto. Log expira; dado permanece.</p></li><li><p><strong>Eu consigo reconstruir esse valor do zero se ele for corrompido?</strong> Se sim, ele &#233; derivado e n&#227;o deveria ser a fonte da verdade. Se n&#227;o, ele &#233; fato e n&#227;o deveria ser mut&#225;vel. Uma das duas coisas &#8212; nunca as duas.</p></li><li><p><strong>Quantos tempos diferentes esse fato tem?</strong> Pergunte ao neg&#243;cio: <em>&#8220;quando isso aconteceu&#8221;</em> e <em>&#8220;a que m&#234;s isso pertence&#8221;</em> s&#227;o a mesma data? Se hesitarem, voc&#234; precisa de duas colunas.</p></li></ul><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/quando-orientacao-a-objetos-estraga/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/quando-orientacao-a-objetos-estraga/comments"><span>Deixe um coment&#225;rio</span></a></p><h2><strong>Princ&#237;pio 03: Modele para as perguntas, n&#227;o para os dados</strong></h2><p>Aqui mora a discuss&#227;o relacional &#215; n&#227;o relacional &#8212; e ela &#233; muito menos religiosa do que a internet faz parecer. Normaliza&#231;&#227;o otimiza escrita e integridade. Desnormaliza&#231;&#227;o otimiza leitura. Nenhuma das duas &#233; &#8220;certa&#8221; no v&#225;cuo. A pergunta correta nunca foi <em>&#8220;qual banco eu uso?&#8221;</em>, e sim <strong>&#8220;quais perguntas esse dado precisa responder, com que lat&#234;ncia, e quantas vezes por segundo?&#8221;</strong>.</p><h3><strong>Li&#231;&#227;o 3.1 &#8594; Normaliza&#231;&#227;o e desnormaliza&#231;&#227;o s&#227;o estrat&#233;gias, n&#227;o moral</strong></h3><p>A terceira forma normal existe para garantir que cada fato seja afirmado em exatamente um lugar &#8212; o que torna imposs&#237;vel ter duas verdades contradit&#243;rias. &#201; uma prote&#231;&#227;o de <em>escrita</em>. Voc&#234; paga por ela em JOINs.</p><p>Desnormalizar &#233; o movimento contr&#225;rio: aceitar redund&#226;ncia controlada para comprar lat&#234;ncia de leitura. Voc&#234; paga por ela em risco de diverg&#234;ncia.</p><p>O erro n&#227;o &#233; escolher um dos dois. O erro &#233; escolher <strong>sem saber qual dos dois custos voc&#234; est&#225; pagando</strong>. Um sinal claro de que a decis&#227;o foi autom&#225;tica: o time normalizou at&#233; a &#250;ltima gota uma tabela que s&#243; &#233; lida por um dashboard uma vez por dia &#8212; e desnormalizou o cadastro de cliente, que &#233; escrito por seis fluxos diferentes.</p><h3><strong>Li&#231;&#227;o 3.2 &#8594; No NoSQL, o padr&#227;o de acesso vem antes da entidade &#8212; e &#233; quase irrevers&#237;vel</strong></h3><p>Esta &#233; a diferen&#231;a pr&#225;tica que muita gente descobre tarde demais.</p><p>No relacional, voc&#234; modela entidades e relacionamentos primeiro; se uma consulta nova aparecer daqui a um ano, voc&#234; cria um &#237;ndice e segue a vida. O modelo perdoa.</p><p>Num banco de chave-parti&#231;&#227;o &#8212; DynamoDB, Cassandra &#8212;, a chave <strong>&#233;</strong> a arquitetura. Ela determina onde o dado fisicamente mora, e mud&#225;-la depois significa reescrever a tabela inteira. O modelo n&#227;o perdoa.</p><pre><code><code>// extrato: "os lan&#231;amentos da conta X no per&#237;odo Y, mais recentes primeiro"
PK = "CONTA#00219-4"
SK = "LANC#2026-08-19T14:32:11Z#a3f9"

// a pergunta vira uma &#250;nica leitura sequencial, sem JOIN, sem scan:
Query(PK = "CONTA#00219-4", SK between "LANC#2026-08-01" and "LANC#2026-08-31")</code></code></pre><p>Isso &#233; lindo &#8212; <em>para essa pergunta</em>. Agora tente responder <em>&#8220;todos os lan&#231;amentos com E2E ID X, em qualquer conta&#8221;</em>, ou <em>&#8220;soma de tarifas por ag&#234;ncia no m&#234;s&#8221;</em>. Sem um &#237;ndice secund&#225;rio planejado desde o in&#237;cio, essas perguntas custam um scan da base inteira.</p><p><strong>Portanto:</strong> em NoSQL, listar os padr&#245;es de acesso <em>antes</em> de desenhar a chave n&#227;o &#233; boa pr&#225;tica opcional. &#201; a modelagem inteira.</p><h3><strong>Li&#231;&#227;o 3.3 &#8594; Um dado pode ter v&#225;rios modelos &#8212; mas s&#243; uma fonte da verdade</strong></h3><p>A sa&#237;da madura para o falso dilema &#233; aceitar que <strong>o mesmo fato banc&#225;rio quer formas diferentes para consumidores diferentes</strong>:</p><ul><li><p><strong>Raz&#227;o cont&#225;bil</strong> &#8212; relacional, normalizado, consist&#234;ncia forte, transacional. &#201; a fonte da verdade. Precisa fechar at&#233; o centavo e sobreviver a auditoria.</p></li><li><p><strong>Extrato do app</strong> &#8212; documento desnormalizado, particionado por conta e per&#237;odo, leitura em milissegundos, consist&#234;ncia eventual toler&#225;vel (o cliente aceita ver o lan&#231;amento com 300 ms de atraso; n&#227;o aceita esperar 4 segundos pela tela).</p></li><li><p><strong>Base anal&#237;tica</strong> &#8212; colunar, orientada a agrega&#231;&#227;o, atualizada por batch.</p></li></ul><p>Tr&#234;s modelos, um fato. Isso &#233; CQRS na pr&#225;tica &#8212; e n&#227;o &#233; sofistica&#231;&#227;o: &#233; reconhecer que &#8220;consultar&#8221; e &#8220;registrar&#8221; s&#227;o atividades com requisitos opostos.</p><h4><strong><span>A regra que n&#227;o pode ser quebrada</span></strong></h4><p>Proje&#231;&#245;es <strong>derivam</strong>; nunca escrevem por conta pr&#243;pria. No instante em que dois caminhos de escrita alimentam duas c&#243;pias do mesmo fato de forma independente, voc&#234; n&#227;o tem duas vis&#245;es &#8212; tem duas verdades. E quando elas divergirem (e v&#227;o divergir), n&#227;o existir&#225; crit&#233;rio t&#233;cnico para decidir qual est&#225; certa.</p><p><strong><span>Pense comigo &#8212; antes de escolher a tecnologia</span></strong></p><ul><li><p><strong>Quais s&#227;o as cinco perguntas que esse dado vai responder em produ&#231;&#227;o?</strong> Escreva as cinco antes de abrir o Terraform. Se voc&#234; n&#227;o consegue escrever nem tr&#234;s, voc&#234; ainda n&#227;o entendeu o requisito &#8212; e nenhum banco resolve isso.</p></li><li><p><strong>Qual dessas perguntas &#233; feita 10.000 vezes por minuto, e qual &#233; feita uma vez por m&#234;s?</strong> Modele para a primeira, tolere a segunda. Otimizar o caminho frio &#233; a forma mais comum de desperdi&#231;ar complexidade.</p></li><li><p><strong>Quem &#233; o dono da verdade desse dado?</strong> Se voc&#234; n&#227;o consegue apontar <em>um</em> lugar, voc&#234; j&#225; tem um problema de consist&#234;ncia &#8212; s&#243; ainda n&#227;o sabe.</p></li><li><p><strong>O que quebra se essa leitura estiver 500 ms atrasada?</strong> Se a resposta for &#8220;nada&#8221;, voc&#234; acabou de ganhar liberdade arquitetural enorme. Se for &#8220;o saldo pode ficar negativo&#8221;, voc&#234; acabou de descobrir onde precisa de consist&#234;ncia forte.</p></li></ul><h2><strong>Princ&#237;pio 04: Consist&#234;ncia tem fronteiras &#8212; e voc&#234; as desenha</strong></h2><p>Todo engenheiro sabe recitar ACID. Poucos percebem que <strong>o desenho do modelo &#233; o que decide onde o ACID pode ser aplicado</strong>. A fronteira transacional n&#227;o &#233; uma configura&#231;&#227;o de infraestrutura: &#233; uma consequ&#234;ncia direta de como voc&#234; agrupou os dados.</p><h3><strong><span>Li&#231;&#227;o 4.1 </span>&#8594; O agregado define o que cabe em um commit</strong></h3><p>Um agregado &#233; o conjunto de dados que precisa mudar junto para que uma invariante do neg&#243;cio se mantenha. &#8220;O saldo da conta nunca pode ficar abaixo do limite&#8221; &#233; uma invariante &#8212; logo, conta e seus lan&#231;amentos do momento precisam ser consistentes no mesmo instante.</p><p>O sintoma de agregado mal desenhado &#233; conhecido: voc&#234; se pega precisando de uma transa&#231;&#227;o que atravessa tr&#234;s servi&#231;os. Nesse ponto h&#225; exatamente duas sa&#237;das honestas &#8212; ou a fronteira est&#225; errada e esses dados deveriam viver juntos, ou eles s&#227;o realmente independentes e voc&#234; precisa de <strong>saga com compensa&#231;&#227;o</strong>. O que n&#227;o existe &#233; a terceira: transa&#231;&#227;o distribu&#237;da em dois bancos diferentes via <code>@Transactional</code> e f&#233;.</p><h3><strong>Li&#231;&#227;o 4.2 &#8594; Estados intermedi&#225;rios s&#227;o cidad&#227;os de primeira classe</strong></h3><p>Um Pix atravessa a fronteira da sua institui&#231;&#227;o. Entre a ordem do cliente e a confirma&#231;&#227;o do SPI existe um intervalo em que <strong>o dinheiro precisa estar contabilmente em algum lugar</strong>. Ele n&#227;o est&#225; mais totalmente dispon&#237;vel para o pagador e ainda n&#227;o &#233; do recebedor.</p><p>Modelo ing&#234;nuo:</p><pre><code><code>conta.saldo -= valor;          // e se o SPI recusar?
conta.processado = true;       // processado significa o qu&#234;, exatamente?</code></code></pre><p>Modelo honesto: o valor sai do saldo dispon&#237;vel e entra numa <strong>posi&#231;&#227;o transit&#243;ria expl&#237;cita</strong> &#8212; bloqueio, provis&#227;o, conta de tr&#226;nsito &#8212;, e s&#243; sai de l&#225; quando existe um fato terminal: liquida&#231;&#227;o confirmada, devolu&#231;&#227;o, ou expira&#231;&#227;o de prazo.</p><p>Isso muda a modelagem por completo. Voc&#234; deixa de ter um <code>boolean</code> e passa a ter uma <strong>m&#225;quina de estados com transi&#231;&#245;es registradas como fatos</strong>: <code>ORDEM_RECEBIDA &#8594; RESERVADA &#8594; ENVIADA_SPI &#8594; LIQUIDADA</code>, com ramos para <code>DEVOLVIDA</code> e <code>EXPIRADA</code>. Cada transi&#231;&#227;o &#233; uma linha. O estado atual &#233; a &#250;ltima linha &#8212; ou seja, derivado (Princ&#237;pio 2 outra vez).</p><p><strong>Teste r&#225;pido no seu modelo:</strong> se existe um campo booleano chamado <code>processado</code>, <code>enviado</code> ou <code>ok</code> em algo que atravessa rede, voc&#234; tem um estado intermedi&#225;rio n&#227;o modelado. E ele vai aparecer &#8212; geralmente &#224;s duas da manh&#227;, na forma de dinheiro que &#8220;sumiu&#8221;.</p><h3><strong>Li&#231;&#227;o 4.3 &#8594; Idempot&#234;ncia mora no schema, n&#227;o no </strong><code>if</code></h3><p>Sistemas de mensageria s&#233;rios entregam <em>at-least-once</em>. Isso significa que a mesma mensagem <strong>vai</strong> chegar duas vezes &#8212; n&#227;o &#233; hip&#243;tese, &#233; garantia. E a defesa contra isso n&#227;o &#233; c&#243;digo defensivo; &#233; uma restri&#231;&#227;o de banco.</p><pre><code><code>-- a defesa real cabe em uma linha
ALTER TABLE lancamento
  ADD CONSTRAINT uk_idempotencia UNIQUE (chave_idempotencia);</code></code></pre><p>Um <code>SELECT</code> antes do <code>INSERT</code> para &#8220;verificar se j&#225; existe&#8221; n&#227;o protege nada: entre a leitura e a escrita cabem duas inst&#226;ncias do seu servi&#231;o fazendo exatamente a mesma coisa. S&#243; a restri&#231;&#227;o no banco &#233; at&#244;mica. O c&#243;digo apenas trata a viola&#231;&#227;o e devolve sucesso &#8212; porque receber duas vezes o mesmo pedido e produzir um efeito s&#243; <em>&#233;</em> o comportamento correto.</p><p>No Pix, esse pensamento fica evidente: o <strong>E2E ID</strong> identifica unicamente a opera&#231;&#227;o ponta a ponta. Ele &#233; uma chave natural de idempot&#234;ncia que o pr&#243;prio arranjo te entrega de gra&#231;a. Times que a ignoram e criam um UUID interno pr&#243;prio descobrem, na primeira reapresenta&#231;&#227;o de mensagem, que duplicaram cr&#233;dito.</p><p><strong><span>Pense comigo &#8212; antes de desenhar a fronteira</span></strong></p><ul><li><p><strong>Qual invariante do neg&#243;cio essa opera&#231;&#227;o precisa manter </strong><em><strong>no mesmo instante</strong></em><strong>?</strong> S&#243; o que a invariante exige entra na transa&#231;&#227;o. Todo o resto pode ser eventual &#8212; e provavelmente deveria ser.</p></li><li><p><strong>Se isso falhar no meio, o dinheiro est&#225; em que lugar do meu modelo?</strong> Se a resposta for &#8220;em lugar nenhum&#8221; ou &#8220;no ar&#8221;, falta uma entidade. Dinheiro em tr&#226;nsito precisa de endere&#231;o.</p></li><li><p><strong>Se essa mensagem chegar tr&#234;s vezes, quantos efeitos eu produzo?</strong> Se a resposta depende de <em>timing</em>, voc&#234; tem uma condi&#231;&#227;o de corrida esperando volume para se manifestar.</p></li><li><p><strong>Existe uma chave natural que o dom&#237;nio j&#225; me d&#225;?</strong> E2E ID, NSU, n&#250;mero de contrato, chave da nota. Chave natural do dom&#237;nio quase sempre &#233; melhor guardi&#227; de unicidade do que qualquer UUID que voc&#234; gerar.</p></li></ul><h2><strong>Princ&#237;pio 05: Todo schema evolui; </strong><em><strong>schema-less</strong></em><strong> &#233; uma mentira confort&#225;vel</strong></h2><p>N&#227;o existe dado sem schema. Existe schema <em>n&#227;o declarado</em>. Quando algu&#233;m diz &#8220;nosso banco &#233; schema-less&#8221;, o que aconteceu de fato foi: o schema saiu do banco &#8212; onde era validado, versionado e vis&#237;vel &#8212; e foi para dentro do c&#243;digo, onde ningu&#233;m valida nada e onde existem, agora, quatro vers&#245;es diferentes rodando ao mesmo tempo em produ&#231;&#227;o.</p><h3><strong>Li&#231;&#227;o 5.1 &#8594; O schema n&#227;o sumiu; ele apenas trocou de dono</strong></h3><p>No relacional, se voc&#234; tentar inserir texto numa coluna num&#233;rica, o banco recusa. &#201; chato e &#233; maravilhoso: o erro acontece <em>na escrita</em>, com o culpado ainda em cena.</p><p>Num documento livre, o dado entra. O erro acontece meses depois, na <em>leitura</em>, quando algu&#233;m tenta somar uma cole&#231;&#227;o onde 3% dos registros gravaram <code>valor</code> como string porque uma vers&#227;o antiga do servi&#231;o serializava diferente. A&#237; o culpado n&#227;o est&#225; mais em cena &#8212; e o dado ruim j&#225; est&#225; em backup, em r&#233;plica e no lakehouse.</p><p>Escolher NoSQL &#233; leg&#237;timo. Escolher NoSQL <em>e</em> abrir m&#227;o de valida&#231;&#227;o &#233; abrir m&#227;o de qualidade de dado. <strong>Use </strong><em><strong><a href="https://www.mongodb.com/company/blog/json-schema-validation--locking-down-your-model-the-smart-way">schema validation</a></strong></em><strong><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></strong>, <strong>use contratos de dados versionados, use um </strong><em><strong>registry</strong></em><strong> &#8212; mas declare o schema em algum lugar onde ele seja verific&#225;vel por m&#225;quina, n&#227;o pela boa vontade do pr&#243;ximo desenvolvedor.</strong></p><h3><strong>Li&#231;&#227;o 5.2 &#8594; Migra&#231;&#227;o &#233; pr&#225;tica de engenharia cont&#237;nua, n&#227;o evento traum&#225;tico</strong></h3><p>O padr&#227;o que torna evolu&#231;&#227;o de schema um n&#227;o-evento &#233; o <strong>expand / contract</strong>:</p><ol><li><p><strong>Expandir</strong> &#8212; adicionar a nova estrutura, sempre opcional/nul&#225;vel. Nada quebra; a vers&#227;o antiga do c&#243;digo ignora o campo novo.</p></li><li><p><strong>Escrever nos dois</strong> &#8212; o servi&#231;o passa a gravar no formato antigo <em>e</em> no novo.</p></li><li><p><strong>Backfill</strong> &#8212; job idempotente e reprocess&#225;vel converte o hist&#243;rico, em lotes, com controle de progresso.</p></li><li><p><strong>Migrar a leitura</strong> &#8212; consumidores passam a ler o campo novo, um a um.</p></li><li><p><strong>Contrair</strong> &#8212; s&#243; ent&#227;o o campo antigo morre.</p></li></ol><p>Cada passo &#233; revers&#237;vel isoladamente, e em nenhum momento existe uma janela em que o sistema precise ficar fora do ar. Compare com o modelo tradicional &#8212; &#8220;migration na madrugada de s&#225;bado, com rollback plan e todo mundo acordado&#8221; &#8212; e fica claro qual dos dois escala num time que faz deploy todo dia.</p><h3><strong>Li&#231;&#227;o 5.3 &#8594; Saiba, no dia 1, quais mudan&#231;as ser&#227;o baratas e quais ser&#227;o caras</strong></h3><p>Nem toda evolu&#231;&#227;o custa igual. Vale ter o mapa na cabe&#231;a:</p><ul><li><p><strong>Barato:</strong> adicionar campo opcional; adicionar &#237;ndice; adicionar novo valor a um enum aberto.</p></li><li><p><strong>M&#233;dio:</strong> tornar um campo obrigat&#243;rio; extrair uma entidade nova; alterar precis&#227;o num&#233;rica.</p></li><li><p><strong>Caro:</strong> mudar o tipo de um campo com hist&#243;rico; mudar a chave de parti&#231;&#227;o; quebrar uma linha em v&#225;rias; retroagir sem&#226;ntica (&#8221;a partir de agora <code>valor</code> passa a ser l&#237;quido, n&#227;o bruto&#8221;).</p></li></ul><p>O exemplo can&#244;nico: um campo <code>valor DECIMAL(18,2)</code> parece perfeito &#8212; at&#233; o dia em que a institui&#231;&#227;o opera c&#226;mbio, ou cripto, ou pontos de fidelidade. Transformar <code>valor</code> em <code>{ montante, moeda }</code> em 400 milh&#245;es de registros &#233; um projeto de trimestre, com dual-write, backfill e reconcilia&#231;&#227;o.</p><p>Da&#237; uma heur&#237;stica que envelhece muito bem: <strong>todo dado que tem unidade deve carregar a unidade desde o primeiro dia</strong> &#8212; moeda com valor, fuso com timestamp, base com taxa. Custa uma coluna hoje. Economiza um trimestre depois.</p><h4><strong><span>Cuidado com o inverso</span></strong></h4><p>Isso n&#227;o &#233; licen&#231;a para generalizar tudo &#8220;porque um dia pode mudar&#8221;. Modelo excessivamente gen&#233;rico &#8212; o famoso EAV, com <code>chave</code> e <code>valor</code> em texto para &#8220;flexibilidade&#8221; &#8212; &#233; a forma mais eficiente j&#225; inventada de n&#227;o modelar nada e transferir o problema para todo mundo que vier depois. Antecipe unidades e dimens&#245;es reais do dom&#237;nio, n&#227;o hip&#243;teses.</p><p><strong><span>Pense comigo &#8212; antes de criar o primeiro campo</span></strong></p><ul><li><p><strong>Onde esse schema est&#225; declarado de forma que uma m&#225;quina consiga verificar?</strong> Se a resposta &#233; &#8220;na classe Java&#8221;, pergunte: qual das quatro vers&#245;es em produ&#231;&#227;o?</p></li><li><p><strong>Se eu precisar mudar isso com 300 milh&#245;es de registros, qual &#233; o plano?</strong> Fa&#231;a a pergunta hoje, enquanto a tabela est&#225; vazia e a resposta ainda &#233; barata.</p></li><li><p><strong>Esse campo tem unidade impl&#237;cita?</strong> Valor sem moeda, data sem fuso, percentual sem base. Toda unidade impl&#237;cita &#233; uma migra&#231;&#227;o futura j&#225; agendada.</p></li><li><p><strong>Consigo adicionar um campo aqui sem coordenar deploy com mais ningu&#233;m?</strong> Se n&#227;o consigo, o acoplamento n&#227;o est&#225; no c&#243;digo &#8212; est&#225; no dado, e &#233; muito mais caro de desfazer.</p></li></ul><h2><strong>O adulto na sala agora &#233; voc&#234;</strong></h2><p>Os cinco princ&#237;pios t&#234;m uma raiz comum, e ela &#233; bem simples: <strong>dados descrevem a realidade; c&#243;digo apenas a manipula</strong>. Por isso o c&#243;digo &#233; descart&#225;vel e os dados n&#227;o s&#227;o. Framework &#233; opini&#227;o &#8212; e opini&#227;o muda. Um lan&#231;amento cont&#225;bil de 2019 &#233; um fato, e fato n&#227;o muda nunca.</p><p>O DBA n&#227;o vai voltar. Aquele documento com o DER pronto n&#227;o vai chegar. A responsabilidade de fazer as perguntas certas antes de escrever a primeira linha de DDL caiu no colo de quem escreve o c&#243;digo &#8212; e a boa not&#237;cia &#233; que ela cabe em quinze minutos de conversa com quem entende do neg&#243;cio, desde que sejam os quinze minutos certos.</p><h4><strong>Cart&#227;o de bolso &#8212; as 15 li&#231;&#245;es</strong></h4><ol><li><p>Nome de coluna &#233; contrato permanente, n&#227;o vari&#225;vel local.</p></li><li><p>N&#227;o ache conceitos distintos sob um nome gen&#233;rico.</p></li><li><p>Seu modelo precisa passar no teste do auditor.</p></li><li><p>O UPDATE destrutivo apaga a &#250;nica evid&#234;ncia que voc&#234; tinha.</p></li><li><p>O fato carrega o &#8220;porqu&#234;&#8221;, n&#227;o s&#243; o &#8220;quanto&#8221;.</p></li><li><p>Tempo &#233; dimens&#227;o do dado, n&#227;o metadado.</p></li><li><p>Normalizar e desnormalizar s&#227;o estrat&#233;gias, n&#227;o moral.</p></li><li><p>Em NoSQL, o padr&#227;o de acesso vem antes da entidade.</p></li><li><p>V&#225;rios modelos, uma &#250;nica fonte da verdade.</p></li><li><p>O agregado define o que cabe em um commit.</p></li><li><p>Estados intermedi&#225;rios s&#227;o cidad&#227;os de primeira classe.</p></li><li><p>Idempot&#234;ncia mora no schema, n&#227;o no <code>if</code>.</p></li><li><p>O schema n&#227;o sumiu; trocou de dono.</p></li><li><p>Migra&#231;&#227;o &#233; pr&#225;tica cont&#237;nua, n&#227;o evento traum&#225;tico.</p></li><li><p>Saiba desde o dia 1 quais mudan&#231;as ser&#227;o caras.</p></li></ol><p>Uma &#250;ltima provoca&#231;&#227;o, para levar para o pr&#243;ximo refinamento. Antes de aceitar a pr&#243;xima hist&#243;ria com um <code>status</code> novo, fa&#231;a a pergunta que resume o artigo inteiro:</p><div class="pullquote"><p><em><strong>&#8220;Se algu&#233;m precisar provar, daqui a cinco anos, que isso aqui aconteceu exatamente assim &#8212; o meu modelo consegue provar sozinho?&#8221;</strong></em></p></div><p>Se a resposta depender do log da aplica&#231;&#227;o, da mem&#243;ria de algu&#233;m do time ou de uma planilha no drive de algu&#233;m, o modelo ainda n&#227;o est&#225; pronto. E vale a pena gastar mais quinze minutos hoje &#8212; porque o c&#243;digo que voc&#234; vai escrever depois disso &#233; descart&#225;vel de qualquer jeito.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><strong><mark>algo oculto, misterioso, enigm&#225;tico ou dif&#237;cil de compreender</mark></strong>.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>A flexibilidade dos bancos de dados orientados a documentos (como o MongoDB) &#233; incr&#237;vel: voc&#234; come&#231;a a modelar sem amarras, itera r&#225;pido e entrega funcionalidades sem a dor de cabe&#231;a inicial de migra&#231;&#245;es complexas. Mas essa liberdade tem um pre&#231;o. Conforme o sistema cresce, m&#250;ltiplos microsservi&#231;os ou desenvolvedores come&#231;am a escrever na mesma base, e a aus&#234;ncia de regras r&#237;gidas pode transformar sua cole&#231;&#227;o em um verdadeiro faroeste de dados inconsistentes.</p><p>&#201; a&#237; que entra o <strong>Schema Validation</strong>.</p><p>Trata-se de um contrato definido diretamente no banco que estabelece regras claras sobre como os documentos devem ser estruturados antes de serem persistidos. Em vez de depender exclusivamente da camada de aplica&#231;&#227;o para garantir que nada saia dos trilhos, o pr&#243;prio banco rejeita grava&#231;&#245;es que violem os padr&#245;es definidos.</p><p><strong>O que o Schema Validation resolve no dia a dia</strong></p><ul><li><p><strong>Campos obrigat&#243;rios:</strong> Garante que atributos essenciais nunca fiquem nulos ou ausentes em documentos cr&#237;ticos.</p></li><li><p><strong>Tipagem estrita:</strong> Evita bugs silenciosos de coer&#231;&#227;o de tipo (como receber uma <code>string</code> onde a aplica&#231;&#227;o espera um <code>decimal</code> ou <code>int</code>).</p></li><li><p><strong>Enums e valores permitidos:</strong> Restringe o preenchimento de campos de status e categorias a listas pr&#233;-aprovadas.</p></li><li><p><strong>Bloqueio de campos n&#227;o previstos:</strong> Impede que dados acidentais ou formatos antigos poluam novos registros (<code>additionalProperties: false</code>).</p></li></ul><p>Na pr&#225;tica, o Schema Validation une o melhor de dois mundos: mant&#233;m a agilidade do modelo orientado a documentos durante o desenvolvimento inicial e permite &#8220;travar&#8221; o modelo quando ele atinge maturidade arquitetural, garantindo governan&#231;a e integridade sem perder a flexibilidade do NoSQL.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Ataques ao Pix: R$ 15 mil de um lado, R$ 800 milhões do outro]]></title><description><![CDATA[Dois anos, mais de R$ 2 bilh&#245;es e nenhum zero-day. Como o crime recruta, planeja e executa, o que o Bacen passou a exigir &#8212; e onde o seu c&#243;digo decide o tamanho do rombo.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/ataques-ao-pix-r-15-mil-de-um-lado</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/ataques-ao-pix-r-15-mil-de-um-lado</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Thu, 27 Aug 2026 13:55:03 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/3fd1ab04-90a2-436a-b7de-5064218b18b7_6024x4024.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><p>Leitura densa (~30 min) &#183; Para quem escreve c&#243;digo que move dinheiro</p><p>Voc&#234; tem gateway, rate limit, mTLS e motor antifraude &#8212; e ainda assim a dist&#226;ncia entre uma credencial vendida num bar e as contas reserva do Banco Central &#233; feita de decis&#245;es que passaram pelo seu pull request. Sete t&#243;picos sobre como o ataque funciona de verdade.</p></div><h3><a href="https://www.cnnbrasil.com.br/nacional/sudeste/sp/ataque-hacker-suspeito-recebeu-r-15mil-em-saida-de-bar-para-passar-senhas/">Mar&#231;o de 2025. Um homem sai de um bar no Jaragu&#225;</a>, zona norte de S&#227;o Paulo. Algu&#233;m o aborda na rua e faz uma pergunta simples: &#8220;voc&#234; trabalha no TI?&#8221;</h3><p>Ele trabalhava. Era programador j&#250;nior terceirizado numa empresa que a maioria dos brasileiros nunca ouviu falar, mas por onde passavam ordens de pagamento de seis institui&#231;&#245;es financeiras. A oferta inicial foi de R$ 5 mil, entregues em dinheiro por um motoboy. Depois vieram mais R$ 10 mil, e com eles a tarefa de <em>inserir comandos</em> nos sistemas da empresa a mando de gente que ele nunca viu pessoalmente. Ele passou a trocar de chip e de aparelho a cada 15 dias.</p><p>Na madrugada de 30 de junho para 1&#186; de julho de 2025, por volta das 4h da manh&#227;, essas credenciais entraram no sistema. Durante aproximadamente tr&#234;s horas, transfer&#234;ncias sa&#237;ram das contas reserva mantidas no Banco Central<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a>. O preju&#237;zo estimado ficou entre R$ 800 milh&#245;es e R$ 1 bilh&#227;o. Uma parte virou criptoativo antes que algu&#233;m percebesse. E quem percebeu, ali&#225;s, n&#227;o foi a empresa atacada nem a v&#237;tima direta: foi uma terceira institui&#231;&#227;o que estranhou o fluxo e ligou avisando.</p><p>N&#227;o houve zero-day. N&#227;o houve estouro de buffer, inje&#231;&#227;o de SQL, desserializa&#231;&#227;o insegura, nada que caiba num relat&#243;rio de pentest. Houve <strong>uma credencial leg&#237;tima usada dentro do hor&#225;rio e do protocolo</strong>. A pr&#243;pria empresa afirmou, em nota, que &#8220;n&#227;o houve invas&#227;o direta aos sistemas&#8221;. Tecnicamente correto. E &#233; exatamente por isso que essa frase &#233; a mais perigosa do setor.</p><p>Este artigo &#233; uma tentativa de responder, com honestidade t&#233;cnica, tr&#234;s perguntas que quase nunca aparecem juntas: <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">como o crime pensa</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">, </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">onde a engenharia falha</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> e </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">o que a regula&#231;&#227;o passou a cobrar de n&#243;s, programadores</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> &#8212; n&#227;o do CISO, n&#227;o do jur&#237;dico, de n&#243;s.</mark> Porque em todos os casos dos &#250;ltimos dois anos, quando voc&#234; desce a pilha o suficiente, encontra uma decis&#227;o de design.</p><h4>O que vem por a&#237;</h4><ul><li><p><strong>O inimigo tem crach&#225;.</strong> Anatomia do vetor interno: C&amp;M, Sinqia e o padr&#227;o do &#8220;n&#227;o invadiram, logaram&#8221;.</p></li><li><p><strong>De fora para dentro.</strong> Como criminosos selecionam alvo, planejam, executam e sacam &#8212; inclusive no celular do seu cliente.</p></li><li><p><strong>Onde o c&#243;digo falha.</strong> APIs, autentica&#231;&#227;o, autoriza&#231;&#227;o, segredos, assinatura e limites: a parte que &#233; culpa nossa.</p></li><li><p><strong>O que o Bacen exige.</strong> Do discurso de pol&#237;tica &#224; evid&#234;ncia audit&#225;vel: PSTI, 14 controles m&#237;nimos, MFA, isolamento, pentest.</p></li><li><p><strong>SSDLC.</strong> Onde a norma vira c&#243;digo, teste, pipeline e pull request.</p></li><li><p><strong>Como o ataque funciona na pr&#225;tica.</strong> RCE, deserializa&#231;&#227;o, SSRF, a mentira do &#8220;estou seguro na minha linguagem&#8221; e o mito do Kali Linux. Para arquitetos e programadores.</p></li><li><p><strong>As pr&#243;ximas brechas.</strong> Pix Autom&#225;tico, aproxima&#231;&#227;o offline, parcelado, garantia, internacional, split: cada funcionalidade &#233; uma superf&#237;cie nova.</p></li></ul><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong><span>T&#243;pico 01: </span>O inimigo tem crach&#225;: anatomia do vetor interno</strong></h2><p>Vamos come&#231;ar pelo desconfort&#225;vel. Os dois maiores ataques ao ecossistema Pix n&#227;o foram invas&#245;es no sentido hollywoodiano. Foram <strong>abusos de acesso leg&#237;timo</strong>.</p><h3><strong>Caso 1 &#8212; C&amp;M Software (julho de 2025)</strong></h3><p><a href="https://g1.globo.com/tecnologia/noticia/2025/07/04/venda-de-senhas-e-ordens-falsas-de-transferencia-como-foi-ataque-a-sistema-ligado-ao-pix.ghtml">A C&amp;M &#233; um PSTI: um provedor de tecnologia que funcionava como ponte entre institui&#231;&#245;es financeiras e o Sistema de Pagamentos Brasileiro, incluindo o ambiente do Pix</a>. Institui&#231;&#245;es que n&#227;o t&#234;m conex&#227;o pr&#243;pria com as infraestruturas do Banco Central contratam algu&#233;m assim. &#201; infraestrutura cr&#237;tica com apar&#234;ncia de fornecedor de TI.</p><p>O alvo n&#227;o foram as contas correntes dos clientes finais. Foram as <strong>contas reserva das institui&#231;&#245;es no Banco Central</strong> &#8212; o dinheiro institucional, n&#227;o o do correntista. A BMP sozinha reportou preju&#237;zo de R$ 541 milh&#245;es. Credsystem e Banco Paulista tamb&#233;m foram atingidos. O Banco Central determinou o desligamento do acesso das institui&#231;&#245;es &#224;s infraestruturas operadas pela C&amp;M.</p><p>Meses depois, um grupo criminoso publicou na dark web cerca de 392 GB de dados supostamente obtidos da empresa. A Pol&#237;cia Federal, segundo seu diretor-geral, mapeou a cadeia criminosa, prendeu 25 pessoas e recuperou mais de R$ 500 milh&#245;es.</p><p>Repare no custo do ataque para o crime: <strong>R$ 15 mil</strong>.</p><h3><strong>Caso 2 &#8212; Sinqia / Evertec (agosto de 2025)</strong></h3><p>Menos de dois meses depois, a mesma classe de vetor. <a href="https://www.youtube.com/watch?v=CPgmqV_MQ6w">A Sinqia &#8212; provedora de tecnologia comprada pela porto-riquenha Evertec &#8212; detectou atividade n&#227;o autorizada no seu ambiente Pix em 29 de agosto</a>. Resultado: <strong>R$ 710 milh&#245;es</strong> em transa&#231;&#245;es B2B n&#227;o autorizadas, sendo cerca de R$ 669 milh&#245;es do HSBC e R$ 41 milh&#245;es da SCD Artta. O Banco Central conseguiu bloquear a maior parte &#8212; algo em torno de R$ 589 milh&#245;es.</p><p>O relat&#243;rio enviado pela Evertec &#224; SEC americana traz a linha que interessa a n&#243;s: os resultados preliminares da forense indicaram acesso por meio de <strong><a href="https://convergenciadigital.com.br/mercado/sinquia-ataque-hacker-levou-cerca-de-r-710-milhoes-em-transacoes-b2b-nao-autorizadas/">credenciais leg&#237;timas comprometidas de fornecedores de TI</a></strong><a href="https://convergenciadigital.com.br/mercado/sinquia-ataque-hacker-levou-cerca-de-r-710-milhoes-em-transacoes-b2b-nao-autorizadas/"> da Sinqia</a>. A empresa informou que &#8220;j&#225; encerrou o acesso&#8221; dessas contas. Depois. Sempre depois.</p><h3><strong>E n&#227;o parou a&#237;</strong></h3><p>Em janeiro de 2026, o <a href="https://www.infomoney.com.br/brasil/banco-do-nordeste-suspende-pix-temporariamente-apos-ataque-hacker/">Banco do Nordeste suspendeu o Pix</a> ap&#243;s um incidente originado em prestador de servi&#231;o de tecnologia, envolvendo uma <em>conta bols&#227;o</em> de empresa terceirizada. O impacto apareceu no balan&#231;o do 1&#186; trimestre como item n&#227;o recorrente: <strong>R$ 146,6 milh&#245;es</strong>. A FictorPay teve recursos desviados ap&#243;s a invas&#227;o da Diletta, empresa que desenvolvia seu aplicativo e a camada de comunica&#231;&#227;o com a institui&#231;&#227;o autorizada a operar o Pix em seu nome. Em mar&#231;o de 2026, o BTG identificou atividade suspeita e suspendeu opera&#231;&#245;es preventivamente.</p><p>O padr&#227;o &#233; t&#227;o consistente que d&#225; para escrever como uma assinatura:</p><pre><code><span>// Assinatura comum dos incidentes de 2025&#8211;2026</span>
alvo        : elo de infraestrutura, n&#227;o o banco
acesso      : credencial v&#225;lida (interna, terceirizada ou de fornecedor)
t&#233;cnica     : uso leg&#237;timo de fun&#231;&#227;o leg&#237;tima
janela      : madrugada / fim de semana / baixa observabilidade
alvo do $   : conta reserva ou conta bols&#227;o, n&#227;o conta de cliente
detec&#231;&#227;o    : por terceiro, por concilia&#231;&#227;o, ou tarde demais
sa&#237;da       : pulveriza&#231;&#227;o &#8594; contas de passagem &#8594; cripto/OTC &#8594; fachada
</code></pre><h4><strong><span>O que ningu&#233;m quer admitir</span></strong></h4><p>&#8220;N&#227;o houve invas&#227;o direta aos sistemas&#8221; &#233; uma frase de defesa jur&#237;dica, n&#227;o de engenharia. Do ponto de vista do sistema, <strong>n&#227;o existe diferen&#231;a entre uma credencial roubada e uma credencial vendida</strong>. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">As duas produzem requisi&#231;&#245;es perfeitamente v&#225;lidas. Se a sua arquitetura s&#243; sabe se defender de requisi&#231;&#245;es inv&#225;lidas, ela n&#227;o sabe se defender.</mark></p><p>Existe uma express&#227;o que descreve bem o fen&#244;meno: <em><a href="https://securitymiddleeastmag.com/cybersecurity/insider-as-a-service-the-next-stage-of-internal-threats/">insider as a service</a></em>. <strong>O crime organizado descobriu que comprar um humano com acesso &#233; ordens de grandeza mais barato, mais r&#225;pido e mais confi&#225;vel do que financiar pesquisa de vulnerabilidade</strong>. E ele n&#227;o precisa comprar um arquiteto s&#234;nior. Basta comprar algu&#233;m cujo perfil de acesso foi definido por conveni&#234;ncia operacional, e n&#227;o por necessidade &#8212; o que, sejamos honestos, descreve boa parte dos acessos que existem na sua empresa agora.</p><p>Aqui &#233; onde a engenharia entra. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Uma credencial vendida s&#243; vale R$ 800 milh&#245;es se, do outro lado dela, existir um caminho em que </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">um &#250;nico ator consegue originar, aprovar e liquidar</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> uma ordem de pagamento de alt&#237;ssimo valor, fora de hor&#225;rio, para um destino nunca visto, sem que nada dispare</mark>. Cada &#8220;e&#8221; dessa frase &#233; um controle que faltou. <strong>Cada controle que faltou &#233; uma decis&#227;o de design que algu&#233;m tomou &#8212; ou deixou de tomar &#8212; num refinamento de sprint</strong>.</p><h4><strong><span>Pense comigo</span></strong></h4><blockquote><p>Quantas pessoas na sua empresa conseguem, sozinhas, com uma &#250;nica credencial, fazer o sistema emitir uma mensagem de pagamento v&#225;lida para o SPI? N&#227;o a resposta da pol&#237;tica. A resposta real, incluindo terceirizados, contas de servi&#231;o herdadas, o acesso de emerg&#234;ncia que ningu&#233;m revogou e o script que roda com a credencial de algu&#233;m que saiu em 2023.</p><p>Se voc&#234; n&#227;o consegue produzir essa lista em cinco minutos, o atacante vai produzir por voc&#234;.</p></blockquote><h2><strong><span>T&#243;pico 02: </span>De fora para dentro: como o crime pensa, planeja e executa</strong></h2><p>Para defender bem, &#233; preciso parar de imaginar o atacante como um g&#234;nio de capuz e come&#231;ar a enxerg&#225;-lo como <strong>uma opera&#231;&#227;o com or&#231;amento, prazo, divis&#227;o de trabalho e an&#225;lise de retorno</strong>. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">O que descrevo abaixo &#233; a estrutura recorrente nas investiga&#231;&#245;es p&#250;blicas dos &#250;ltimos dois anos.</mark></p><h3><strong>Fase 1 &#8212; Sele&#231;&#227;o do alvo: procurar o elo, n&#227;o o cofre</strong></h3><p>Nenhum grupo racional ataca o Ita&#250; de frente. <strong>Ele ataca quem se conecta ao Ita&#250;</strong>. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">A l&#243;gica &#233; de custo: o banco grande tem SOC 24&#215;7, red team, or&#231;amento de nove d&#237;gitos; o fornecedor que faz a ponte tem uma equipe enxuta, um contrato apertado&#8230; </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">mas acesso privilegiado</mark></strong>. Como resumiu um advogado que acompanhou o caso C&amp;M, o ataque exp&#244;s que a empresa prestava servi&#231;o de tecnologia a institui&#231;&#245;es financeiras <strong>sem ser ela pr&#243;pria regulada</strong>, o que permitiu a um elo n&#227;o supervisionado comprometer contas reserva de v&#225;rias institui&#231;&#245;es ao mesmo tempo.</p><p>O n&#250;mero que sustenta essa escolha &#233; constrangedor: entre 606 institui&#231;&#245;es avaliadas pelo Banco Central, apenas <strong>37,3% faziam avalia&#231;&#227;o peri&#243;dica de risco dos seus prestadores de servi&#231;o</strong>, e menos da metade mantinha invent&#225;rio atualizado de fornecedores. O criminoso n&#227;o precisa de sorte para achar um elo fraco. Ele precisa de paci&#234;ncia para escolher qual.</p><h3><strong>Fase 2 &#8212; Reconhecimento: OSINT &#233; barato e legal</strong></h3><p>Antes de qualquer acesso, vem o mapeamento. E ele &#233; feito, em boa medida, com informa&#231;&#227;o que <em>n&#243;s mesmos</em> publicamos:</p><ul><li><p><strong>Vagas de emprego.</strong> &#8220;Vaga para pessoa desenvolvedora Java/Spring com experi&#234;ncia em SPI, ISO 20022 e integra&#231;&#227;o com Bacen.&#8221; Voc&#234; acabou de entregar a stack, o dom&#237;nio e a superf&#237;cie.</p></li><li><p><strong>LinkedIn.</strong> Quem &#233; o time, quem &#233; j&#250;nior, quem &#233; terceiro, quem acabou de entrar (e ainda n&#227;o sabe o que &#233; estranho), quem est&#225; insatisfeito.</p></li><li><p><strong>Reposit&#243;rios e pacotes p&#250;blicos.</strong> Nomes internos de servi&#231;os, endpoints, formatos de payload, ocasionalmente um segredo esquecido no hist&#243;rico do Git.</p></li><li><p><strong>Marketplaces de credenciais.</strong> Logs de <em><a href="https://redcanary.com/threat-detection-report/trends/info-stealers/">infostealer</a></em><a href="https://redcanary.com/threat-detection-report/trends/info-stealers/"> </a>vendidos aos milhares, com sess&#245;es de VPN, cookies e senhas corporativas. N&#227;o &#233; fic&#231;&#227;o: o Banco Central passou a exigir formalmente a&#231;&#245;es de intelig&#234;ncia cibern&#233;tica com monitoramento de credenciais e chaves na internet, deep e dark web &#8212; justamente porque &#233; ali que a sua senha aparece antes de aparecer no seu SIEM.</p></li><li><p><strong>Documenta&#231;&#227;o p&#250;blica do Pix.</strong> As especifica&#231;&#245;es do Bacen s&#227;o abertas, e isso &#233; bom. Mas significa que o atacante conhece o protocolo t&#227;o bem quanto voc&#234;. A vantagem competitiva dele &#233; conhecer o <em>seu</em> desvio em rela&#231;&#227;o ao protocolo.</p></li></ul><h3><strong>Fase 3 &#8212; Acesso inicial: quatro portas, todas leg&#237;timas</strong></h3><p>Nos casos recentes, o acesso inicial veio por uma destas rotas:</p><ol><li><p><strong>Recrutamento humano.</strong> Abordagem direta, valor baixo, alvo de baixa senioridade e alto acesso. O caso C&amp;M &#233; o manual.</p></li><li><p><strong>Credencial de fornecedor comprometida.</strong> O caso Sinqia. Voc&#234; endureceu a sua autentica&#231;&#227;o; a do seu fornecedor de banco de dados, n&#227;o.</p></li><li><p><strong>Comprometimento do desenvolvedor do aplicativo.</strong> O caso FictorPay/Diletta: quem escreveu a camada de integra&#231;&#227;o vira o ponto de entrada.</p></li><li><p><strong>Conta de servi&#231;o e integra&#231;&#227;o esquecida.</strong> O acesso que ningu&#233;m revogou porque ningu&#233;m sabia que existia.</p></li></ol><h3><strong>Fase 4 &#8212; Aprender o neg&#243;cio, n&#227;o o sistema</strong></h3><p>Este &#233; o ponto que mais me impressiona e o que mais deveria nos assustar. No caso C&amp;M, o funcion&#225;rio confessou que vinha <strong>inserindo comandos desde maio</strong> &#8212; o ataque s&#243; aconteceu no fim de junho. Semanas de perman&#234;ncia silenciosa.</p><p>Por qu&#234;? Porque o atacante n&#227;o estava quebrando nada. Ele estava <strong>aprendendo o fluxo</strong>: qual conta tem saldo, em que hor&#225;rio a concilia&#231;&#227;o roda, qual valor n&#227;o dispara alarme, quanto tempo demora at&#233; algu&#233;m olhar, qual mensagem o SPI aceita sem devolver erro. &#201; reconhecimento de dom&#237;nio, n&#227;o de infraestrutura.</p><div class="callout-block" data-callout="true"><p><strong><span>A assimetria que define tudo</span></strong></p><p>O atacante n&#227;o precisa entender o seu dom&#237;nio inteiro. Ele precisa entender <strong>um fluxo melhor do que voc&#234; entende</strong>. E ele tem uma vantagem estrutural: enquanto o seu time divide aten&#231;&#227;o entre roadmap, incidente de produ&#231;&#227;o, refinamento e a migra&#231;&#227;o daquele legado, ele passa semanas olhando para um &#250;nico caminho feliz procurando o que acontece quando ele &#233; seguido de um jeito n&#227;o previsto.</p></div><h3><strong>Fase 5 &#8212; Execu&#231;&#227;o: a janela</strong></h3><p>4h da manh&#227; de uma segunda-feira. Tr&#234;s horas de opera&#231;&#227;o. N&#227;o &#233; coincid&#234;ncia: &#233; escolha de <strong>janela de baixa observabilidade humana</strong>. E aqui mora a caracter&#237;stica que torna o Pix estruturalmente diferente de qualquer meio de pagamento anterior &#8212; a liquida&#231;&#227;o &#233; <strong>final e irrevers&#237;vel em segundos</strong>, 24&#215;7. No cart&#227;o existe chargeback, no boleto existe compensa&#231;&#227;o, no TED existe hor&#225;rio. No Pix, quando a mensagem &#233; liquidada, o dinheiro &#233; do outro lado.</p><p>Isso muda a matem&#225;tica da defesa de uma forma que muita gente ainda n&#227;o internalizou: <strong>qualquer controle antifraude que rode em lote, no dia seguinte, &#233; arqueologia</strong>. Ele documenta o crime; n&#227;o o impede. O tempo de decis&#227;o precisa ser da ordem da pr&#243;pria transa&#231;&#227;o. E, aqui, veja o detalhe cruel do caso C&amp;M apontado pelos especialistas: as institui&#231;&#245;es identificaram as movimenta&#231;&#245;es fraudulentas quando os recursos <em>j&#225; estavam em fase de convers&#227;o para criptoativos</em>.</p><h3><strong>Fase 6 &#8212; Sa&#237;da: o problema mais dif&#237;cil do crime</strong></h3><p>Roubar &#233; f&#225;cil; sacar &#233; caro. A cadeia de sa&#237;da usa pulveriza&#231;&#227;o em muitas contas, contas de passagem, <em>contas bols&#227;o</em> (que agregam recursos de v&#225;rios usu&#225;rios sem identifica&#231;&#227;o individualizada &#8212; instrumento historicamente explorado para lavagem), empresas de fachada e convers&#227;o em criptoativos. Na Opera&#231;&#227;o Cofre Digital, que desmontou o grupo do caso Sinqia, o Minist&#233;rio P&#250;blico de S&#227;o Paulo e a Pol&#237;cia Federal descreveram exatamente isso: <strong>empresas de fachada convertendo o valor roubado em cripto para simular origem leg&#237;tima.</strong></p><p>&#201; por isso que boa parte da resposta regulat&#243;ria recente mira o cash-out, e n&#227;o s&#243; o acesso: bloqueio de chaves Pix ligadas a fraudes, rejei&#231;&#227;o obrigat&#243;ria de pagamentos destinados a contas usadas em fraude, encerramento compuls&#243;rio de contas usadas para movimentar recursos de terceiros, regula&#231;&#227;o das prestadoras de servi&#231;os de ativos virtuais. Se voc&#234; seca o ralo, o roubo perde o sentido econ&#244;mico.</p><h3><strong>O outro &#8220;de fora para dentro&#8221;: o celular do seu cliente</strong></h3><p>Tudo o que descrevi at&#233; aqui &#233; o ataque &#224; infraestrutura. Existe um segundo front, muito maior em volume e muito menor em ticket: a fraude no <em>endpoint</em> do usu&#225;rio.</p><p>O Brasil desenvolveu uma ind&#250;stria pr&#243;pria de trojans banc&#225;rios voltados ao Pix. O <strong><a href="https://www.cleafy.com/cleafy-labs/pixpirate-a-new-brazilian-banking-trojan">PixPirate</a></strong><a href="https://www.cleafy.com/cleafy-labs/pixpirate-a-new-brazilian-banking-trojan">, documentado pela Cleafy</a> e depois pela IBM Trusteer, pertence a uma gera&#231;&#227;o de malware Android capaz de <strong>ATS &#8212; Automatic Transfer System</strong>: ele automatiza a inser&#231;&#227;o de uma transfer&#234;ncia Pix maliciosa a partir do pr&#243;prio aparelho da v&#237;tima. Abusa do servi&#231;o de acessibilidade para ganhar capacidade de RAT, monitora a atividade, rouba credenciais e &#8212; o detalhe que quebra a premissa de meio mundo &#8212; <strong>acessa, edita e apaga as mensagens SMS</strong>, inclusive as do banco, quando o segundo fator &#233; necess&#225;rio. E aprendeu a esconder o pr&#243;prio &#237;cone de um jeito novo depois que o Android 10 fechou a t&#233;cnica antiga.</p><p>Do lado do desktop, o <strong><a href="https://www.kaspersky.com.br/about/press-releases/trojan-bancario-brasileiro-gopix-evolui-e-se-consolida-como-a-ciberameaca-financeira-mais-avancada-do-pais">GoPix</a></strong><a href="https://www.kaspersky.com.br/about/press-releases/trojan-bancario-brasileiro-gopix-evolui-e-se-consolida-como-a-ciberameaca-financeira-mais-avancada-do-pais"> </a>abusa de um recurso leg&#237;timo do navegador para substituir chave Pix, c&#243;digo de boleto ou endere&#231;o de carteira no momento da transa&#231;&#227;o, com certificados forjados exibindo o cadeado de conex&#227;o segura.</p><p>A escala: entre janeiro e setembro de 2025, o Brasil registrou <strong>28 milh&#245;es de fraudes envolvendo o Pix</strong>, segundo levantamento da ADDP. Relat&#243;rio da ACI Worldwide com a GlobalData projeta perdas com golpes do Pix chegando a <strong>R$ 11 bilh&#245;es at&#233; 2028</strong>. E o Banco Central reportou crescimento de <strong>183% nos ataques cibern&#233;ticos em dois anos</strong>, com incidentes subindo cerca de 29% entre 2024 e 2025.</p><div class="callout-block" data-callout="true"><p><strong><span>A conclus&#227;o inc&#244;moda do t&#243;pico</span></strong></p><p>Fraude de infraestrutura e fraude de <em>endpoint</em> parecem problemas de times diferentes. N&#227;o s&#227;o. As duas exploram a mesma coisa: <strong>a transa&#231;&#227;o chega ao seu backend perfeitamente bem formada</strong>. Assinada, autenticada, dentro do esquema. Se a sua &#250;nica linha de defesa &#233; validar a forma da requisi&#231;&#227;o, voc&#234; j&#225; perdeu nos dois cen&#225;rios. O que separa &#233; a capacidade de julgar <em>comportamento</em>, e comportamento &#233; c&#243;digo que algu&#233;m precisa escrever.</p></div><h4><strong><span>Pense comigo</span></strong></h4><blockquote><p>Se um ator com acesso leg&#237;timo de um dos seus servi&#231;os operasse por tr&#234;s horas &#224;s 4h de um domingo, qual seria o teto de dano? Some limites por transa&#231;&#227;o, por conta, por janela, por contraparte, por destino novo. Se voc&#234; n&#227;o conseguir chegar a um n&#250;mero, &#233; porque o teto &#233; <strong>o saldo</strong>.</p><p>E a segunda parte da pergunta, mais dura: quem seria acordado, em quantos minutos, por qual alerta &#8212; e esse alerta j&#225; disparou alguma vez em simula&#231;&#227;o?</p></blockquote><h2><strong><span>T&#243;pico 03: </span>Onde o c&#243;digo falha: APIs, autentica&#231;&#227;o, autoriza&#231;&#227;o e segredos</strong></h2><p>Chegamos &#224; parte que &#233; nossa. N&#227;o do compliance, n&#227;o do jur&#237;dico: nossa, de quem abre o editor e escreve o m&#233;todo. Vou listar os padr&#245;es de falha que, na minha experi&#234;ncia com integra&#231;&#245;es de core banking e Pix, aparecem com mais frequ&#234;ncia &#8212; e que explicam tecnicamente como &#8220;credencial leg&#237;tima&#8221; vira &#8220;R$ 800 milh&#245;es&#8221;.</p><h3><strong>1. Autenticar n&#227;o &#233; autorizar</strong></h3><p>&#201; a falha n&#250;mero um do OWASP API Security Top 10 (<em>Broken Object Level Authorization</em>) e a n&#250;mero cinco (<em>Broken Function Level Authorization</em>), e continua sendo a campe&#227; em revis&#227;o de c&#243;digo no setor financeiro. O servi&#231;o valida o token com rigor, confirma assinatura, expira&#231;&#227;o, emissor &#8212; e ent&#227;o <strong>usa o identificador de conta que veio no corpo da requisi&#231;&#227;o</strong>.</p><pre><code><span>// O padr&#227;o que mata</span>
@PostMapping(&#8221;/pagamentos&#8221;)
ResponseEntity&lt;Void&gt; pagar(@AuthenticationPrincipal Cliente c,
                           @RequestBody OrdemPix ordem) {
    <span>// token v&#225;lido &#10004;  cliente autenticado &#10004;</span>
    <span>// e a conta de origem? veio do payload. de quem?</span>
    pixService.iniciar(ordem);
}</code></pre><p>A pergunta que precisa ser feita em toda revis&#227;o de PR de endpoint monet&#225;rio: <strong>quem provou que a conta de origem pertence ao principal autenticado?</strong> Se a resposta &#233; &#8220;o front-end s&#243; manda a conta certa&#8221;, voc&#234; tem um controle de seguran&#231;a implementado em JavaScript rodando no dispositivo do atacante.</p><p>A varia&#231;&#227;o institucional do mesmo erro &#233; pior: o servi&#231;o de integra&#231;&#227;o aceita o ISPB de origem no payload e n&#227;o valida contra a identidade do certificado ou do canal mTLS que originou a chamada. Ou seja: um participante consegue emitir em nome de outro.</p><h3><strong>2. Confian&#231;a impl&#237;cita dentro do per&#237;metro</strong></h3><p>&#8220;Est&#225; dentro da VPC<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a>, &#233; confi&#225;vel.&#8221; Essa frase construiu a maior parte das arquiteturas de microsservi&#231;os que existem hoje em bancos brasileiros, e &#233; exatamente a premissa que o vetor interno destr&#243;i. Se o atacante entra com credencial leg&#237;tima, <strong>ele </strong><em><strong>j&#225; est&#225; dentro da VPC</strong></em>. Toda chamada servi&#231;o-a-servi&#231;o sem autentica&#231;&#227;o m&#250;tua, sem identidade de carga de trabalho e sem autoriza&#231;&#227;o pr&#243;pria &#233; um degrau gr&#225;tis.</p><p>N&#227;o &#224; toa, <a href="https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Resolu%C3%A7%C3%A3o%20CMN&amp;numero=5274">a nova regula&#231;&#227;o passou a exigir </a><strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Resolu%C3%A7%C3%A3o%20CMN&amp;numero=5274">isolamento f&#237;sico e l&#243;gico dos ambientes Pix e STR dos demais sistemas</a></strong>, com inst&#226;ncias dedicadas e apartadas em nuvem. Traduzindo para arquitetura: o ambiente que fala com o SPI n&#227;o compartilha rede, credencial, plano de controle nem imagem base com o resto da casa. E, sim, isso quebra a sua conveni&#234;ncia de deploy.</p><h3><strong>3. Segredos: a chave privada &#233; o sistema</strong></h3><p>Num arranjo em que mensagens s&#227;o assinadas digitalmente, <strong>quem controla a chave de assinatura controla o dinheiro</strong>. Perguntas que doem:</p><ul><li><p>A chave privada de assinatura de mensagens do SPI est&#225; em HSM<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> &#8212; o hardware dedicado que assina sem nunca deixar a chave sair de dentro dele&#8212; ou num arquivo <code>.p12</code> num volume montado?</p></li><li><p>Quantos humanos conseguem extrair essa chave? E quantos <em>fornecedores</em>?</p></li><li><p>Existe rota&#231;&#227;o automatizada de certificados, ou o processo &#233; &#8220;abre um chamado 30 dias antes do vencimento&#8221;?</p></li><li><p>Se um certificado precisar ser revogado &#224;s 4h da manh&#227;, quanto tempo leva? Minutos ou o pr&#243;ximo dia &#250;til?</p></li></ul><p>A resolu&#231;&#227;o mais recente do CMN foi cir&#250;rgica aqui: monitoramento de credenciais e certificados digitais, especialmente no SPI/Pix, com <strong>veda&#231;&#227;o de acesso de terceiros &#224;s chaves privadas associadas &#224; assinatura de mensagens</strong>. Isso n&#227;o &#233; burocracia: &#233; a descri&#231;&#227;o literal do que faltou nos incidentes de 2025.</p><h3><strong>4. A janela entre autorizar e assinar</strong></h3><p>Este &#233; o ponto mais sutil do t&#243;pico, e o que mais gera aquele olhar de &#8220;pera&#237;, como assim?&#8221; quando explico em revis&#227;o de c&#243;digo. Vou por partes, porque a sutileza &#233; justamente onde mora o perigo.</p><p>Comece pela pergunta: <strong>o que a assinatura digital realmente garante?</strong> Ela garante que a mensagem <em>n&#227;o foi alterada depois de assinada</em> e que veio de quem det&#233;m a chave. Repare no &#8220;depois de assinada&#8221;. A assinatura &#233; uma fotografia: ela congela o conte&#250;do <strong>no exato instante em que &#233; tirada</strong>. Tudo o que aconteceu com a mensagem <em>antes</em> desse instante, ela fotografa fielmente &#8212; inclusive uma adultera&#231;&#227;o. A assinatura n&#227;o sabe distinguir o valor certo do valor trocado; ela s&#243; certifica que &#8220;este foi o conte&#250;do no momento da foto&#8221;.</p><p>Agora o fluxo real de um pagamento no seu sistema, do clique &#224; liquida&#231;&#227;o. Entre duas etapas espec&#237;ficas existe uma janela que quase ningu&#233;m protege:</p><pre><code><span>Fluxo de uma ordem de pagamento &#8212; onde mora o risco</span>

  [1] Cliente autentica e pede o Pix
       &#9474;
  [2] DOM&#205;NIO AUTORIZA  &#8592; aqui o valor &#233; R$ 100, chave = padaria@cpf
       &#9474;                   (esta &#233; a &#8220;verdade&#8221; do neg&#243;cio)
       &#9474;
       &#9660;
  &#9556;&#9552;&#9552;&#9552; JANELA DE RISCO &#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9559;
  &#9553;  a ordem viaja por: fila, enricher, mapeador ISO,  &#9553;
  &#9553;  interceptador, retry, aquele &#8220;ajuste&#8221; de &#250;ltima   &#9553;
  &#9553;  hora... cada salto pode tocar no conte&#250;do         &#9553;
  &#9562;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9565;
       &#9474;
       &#9660;
  [3] MENSAGEM &#201; ASSINADA  &#8592; e se aqui o valor virou R$ 100.000
       &#9474;                      e a chave virou conta@laranja?
       &#9474;
  [4] Enviada ao SPI &#8594; liquidada &#8594; irrevers&#237;vel
</code></pre><p>O ataque &#233; este: entre o passo [2] (o dom&#237;nio autoriza &#8220;R$ 100 para a padaria&#8221;) e o passo [3] (a mensagem &#233; assinada), a ordem <strong>viaja</strong>. Ela passa por uma fila, por um servi&#231;o que a enriquece com dados, por um mapeador que a converte para o formato ISO 20022, talvez por um interceptador, talvez por um <em>retry</em>. Cada um desses saltos &#233; c&#243;digo que <em>toca</em> na mensagem. Se qualquer um deles &#8212; comprometido por um atacante, ou simplesmente mal escrito &#8212; <strong>trocar o valor para R$ 100.000 e a chave de destino para uma conta laranja</strong>, o que acontece no passo [3]?</p><p>A mensagem adulterada &#233; assinada. Com perfei&#231;&#227;o criptogr&#225;fica. O SPI recebe uma ordem <strong>impec&#225;vel</strong>: assinatura v&#225;lida, emissor leg&#237;timo, formato correto. Ele liquida. E o dinheiro foi. A assinatura fez exatamente o trabalho dela &#8212; certificou o conte&#250;do no instante da foto. O problema &#233; que, naquele instante, o conte&#250;do <strong>j&#225; estava errado</strong>, e a assinatura n&#227;o tem como saber disso. Ela protege contra altera&#231;&#227;o <em>depois</em> de assinar; ningu&#233;m a projetou para proteger contra altera&#231;&#227;o <em>antes</em>.</p><h4><strong><span>A frase que resume o buraco</span></strong></h4><p>Assinar garante que ningu&#233;m mexeu na mensagem <strong>depois</strong> da assinatura. N&#227;o garante que ela chegou &#237;ntegra <strong>at&#233;</strong> a assinatura. Se o atacante troca o valor no meio do caminho, voc&#234; n&#227;o assina uma fraude por acidente &#8212; voc&#234; assina uma fraude <em>com autoridade</em>, e entrega a ela o seu selo oficial.</p><p>&#201; exatamente esse buraco que a nova regra do Bacen fecha ao exigir <strong>valida&#231;&#227;o de integridade fim a fim antes da assinatura digital</strong>. A implementa&#231;&#227;o, na pr&#225;tica, &#233; mais simples do que parece:</p><pre><code><span>Selo de integridade &#8212; a defesa</span>

  [2] DOM&#205;NIO AUTORIZA
       &#9474;  gera um selo: hash(valor + chave_destino + ISPB + ...)
       &#9474;  o selo viaja GRUDADO na ordem, calculado na FONTE da verdade
       &#9660;
  &#9556;&#9552;&#9552;&#9552; JANELA DE RISCO &#9552;&#9552;&#9552;&#9559;  &#8592; se algu&#233;m alterar o valor aqui,
  &#9562;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9552;&#9565;    o hash n&#227;o vai mais bater
       &#9474;
       &#9660;
  [3] ANTES DE ASSINAR:
       recalcula hash(valor + chave + ISPB) dos campos atuais
       &#9484;&#9472; bate com o selo?  &#8594; assina e envia  &#10004;
       &#9492;&#9472; divergiu?         &#8594; ABORTA. n&#227;o assina nada.  &#10006;
</code></pre><p>No passo [2], o ponto onde o dom&#237;nio autoriza &#8212; a <strong>fonte da verdade</strong> do neg&#243;cio &#8212;, voc&#234; calcula um selo: um hash dos campos financeiros que n&#227;o podem mudar (valor, chave de destino, ISPB, identificador da ordem). Esse selo viaja grudado na ordem por toda a janela de risco. No passo [3], imediatamente antes de assinar, voc&#234; <strong>recalcula</strong> o hash dos campos como eles est&#227;o <em>agora</em> e compara com o selo original. Se bateu, os campos financeiros s&#227;o id&#234;nticos aos que o dom&#237;nio autorizou: assina e envia. Se divergiu, algu&#233;m mexeu no caminho: <strong>aborta, n&#227;o assina nada</strong>, e dispara alerta.</p><p>&#201; barato, &#233; determin&#237;stico, e transforma a janela de risco de um espa&#231;o cego numa zona vigiada. E, mesmo sendo simples de descrever, &#233; <strong>rar&#237;ssimo</strong> de encontrar implementado &#8212; porque exige que algu&#233;m, l&#225; no refinamento, tenha enxergado que o intervalo entre autorizar e assinar era um lugar onde o dinheiro podia ser trocado. Que &#233;, no fim, o assunto do artigo inteiro.</p><h3><strong>5. Aus&#234;ncia de limites: o cofre sem porta</strong></h3><p>Antes dos detalhes, o conceito &#8212; porque &#8220;limite&#8221; soa como uma configura&#231;&#227;o chata de produto, e &#233; na verdade um dos controles de seguran&#231;a mais poderosos que existem em pagamento.</p><p>Todos os outros controles deste t&#243;pico tentam <strong>impedir</strong> que o ataque aconte&#231;a: autoriza&#231;&#227;o impede o acesso indevido, integridade impede a adultera&#231;&#227;o, isolamento impede o movimento lateral. Limite &#233; diferente. Limite parte de uma premissa humilde e realista: <strong>um dia um deles vai falhar</strong>. Algu&#233;m vai vender uma credencial, uma depend&#234;ncia vai ter um CVE, um endpoint vai passar sem revis&#227;o. Quando isso acontecer, o limite &#233; o que decide se voc&#234; vai ter um problema ou uma manchete.</p><p>Ele n&#227;o pergunta &#8220;esta opera&#231;&#227;o &#233; leg&#237;tima?&#8221;. Ele pergunta <strong>&#8220;quanto dano esta opera&#231;&#227;o pode causar antes que algu&#233;m perceba?&#8221;</strong>. &#201; conten&#231;&#227;o, n&#227;o preven&#231;&#227;o. &#201; a porta corta-fogo do pr&#233;dio: n&#227;o impede o inc&#234;ndio, impede que ele consuma tudo.</p><p>E &#233; aqui que est&#225; o ponto que d&#225; t&#237;tulo ao trecho: <strong>se voc&#234; n&#227;o define um teto expl&#237;cito, o seu sistema tem um teto impl&#237;cito &#8212; o saldo dispon&#237;vel.</strong> Nenhum sistema &#233; ilimitado; ele s&#243; descobre o pr&#243;prio limite quando a conta zera. Foi literalmente o que aconteceu nas contas reserva: n&#227;o havia nada dizendo &#8220;esta institui&#231;&#227;o n&#227;o movimenta mais que X por hora de madrugada&#8221;, ent&#227;o o teto foi o que havia na conta. Um cofre sem porta n&#227;o &#233; um cofre com seguran&#231;a fraca. &#201; uma sala.</p><h4><strong>O erro de posicionamento: o limite est&#225; no lugar errado</strong></h4><p>Quase todo time responde &#8220;claro que temos rate limit&#8221; &#8212; e tem mesmo, no API gateway, contando requisi&#231;&#245;es por IP ou por token. Isso &#233; <em>throttling de infraestrutura</em>: protege o servi&#231;o de cair, evita abuso de for&#231;a bruta. &#201; &#250;til e n&#227;o &#233; do que estamos falando.</p><p>O atacante dos casos de 2025 n&#227;o faria dez mil requisi&#231;&#245;es. Ele faria <strong>algumas dezenas de opera&#231;&#245;es perfeitamente v&#225;lidas, dentro de qualquer rate limit de gateway</strong>, cada uma com um valor alto, para destinos diferentes, &#224;s 4h da manh&#227;. O gateway v&#234; tr&#225;fego normal&#237;ssimo &#8212; porque o gateway conta <em>requisi&#231;&#245;es</em>, e o crime acontece em <em>reais</em>.</p><p>Da&#237; a regra de arquitetura: <strong>limite de infraestrutura mora no gateway; limite de dano mora no dom&#237;nio.</strong> O gateway n&#227;o conhece conta, saldo, contraparte, hist&#243;rico nem perfil de risco &#8212; e s&#227;o exatamente essas as dimens&#245;es que importam. Se todo o seu controle de limite est&#225; na borda, voc&#234; tem prote&#231;&#227;o contra volume e nenhuma contra valor.</p><h4><strong>O que o Bacen passou a exigir &#8212; e por qu&#234;</strong></h4><p>Vale entender que o regulador chegou nessa conclus&#227;o pelo caminho mais caro poss&#237;vel: vendo o dano acontecer. Boa parte do pacote de 2025&#8211;2026 &#233;, na pr&#225;tica, <strong>o Banco Central impondo por norma os limites que os sistemas n&#227;o tinham por design</strong>. Os principais:</p><ul><li><p><strong>Teto de R$ 15 mil por transa&#231;&#227;o de Pix e TED</strong> (Resolu&#231;&#245;es BCB n&#186; 496, 497 e 498) para institui&#231;&#245;es de pagamento n&#227;o autorizadas e para as que se conectam &#224; RSFN via PSTI. Repare no racioc&#237;nio: o regulador n&#227;o conseguia garantir a maturidade de seguran&#231;a daquele grupo, ent&#227;o limitou o dano poss&#237;vel de cada opera&#231;&#227;o. &#201; conten&#231;&#227;o pura &#8212; e &#233; exatamente o tipo de trava que deveria ter existido no c&#243;digo.</p></li><li><p><strong>Limites por transa&#231;&#227;o conforme perfil de risco e comportamento do cliente</strong> (Resolu&#231;&#227;o BCB n&#186; 506). Note a mudan&#231;a de filosofia: n&#227;o &#233; mais um teto &#250;nico para todo mundo, &#233; um teto <em>que a institui&#231;&#227;o precisa calcular</em> com base em quem &#233; o cliente e como ele costuma operar. Isso deixou de ser uma configura&#231;&#227;o e virou um requisito de sistema &#8212; algu&#233;m precisa modelar perfil, manter hist&#243;rico e aplicar a decis&#227;o em tempo de transa&#231;&#227;o.</p></li><li><p><strong>Limita&#231;&#227;o de dispositivo n&#227;o cadastrado</strong>, regra mais antiga e ainda v&#225;lida na sua l&#243;gica: transa&#231;&#245;es a partir de aparelho desconhecido t&#234;m teto reduzido, justamente para conter fraude de <em>endpoint</em> &#8212; aquele malware ATS que descrevi no T&#243;pico 02 opera do dispositivo da v&#237;tima.</p></li><li><p><strong>Bloqueio e rejei&#231;&#227;o por destino</strong> (Resolu&#231;&#245;es BCB n&#186; 501 e 506): obriga&#231;&#227;o de rejeitar pagamentos destinados a contas marcadas como usadas em fraude, e de bloquear chaves de fraudadores. &#201; um limite baseado em <em>para onde o dinheiro vai</em>, n&#227;o em quanto.</p></li><li><p><strong>Sistema de alerta de movimenta&#231;&#245;es at&#237;picas no SPI</strong>, desenvolvido pelo pr&#243;prio BC, e o <strong>MED 2.0</strong> (Resolu&#231;&#227;o BCB n&#186; 493) com rastreio e bloqueio em camadas sucessivas de contas &#8212; reconhecimento expl&#237;cito de que o dinheiro precisa ser contido enquanto ainda est&#225; em tr&#226;nsito.</p></li><li><p><strong>Exclus&#227;o do Pix</strong> em casos de maior potencial lesivo e reincid&#234;ncia (Resolu&#231;&#227;o BCB n&#186; 507): o limite &#250;ltimo, aplicado ao participante inteiro.</p></li></ul><p>Traduzindo o recado regulat&#243;rio em uma frase: <strong>o Bacen deixou de confiar que cada institui&#231;&#227;o calibraria sozinha o pr&#243;prio raio de dano</strong>. Quem j&#225; tinha limites bem desenhados sentiu pouco. Quem n&#227;o tinha, descobriu os seus por decreto &#8212; e com n&#250;meros escolhidos por outra pessoa.</p><h4><strong>Como a engenharia implementa isso de verdade</strong></h4><p>Agora a parte pr&#225;tica. Um sistema de limites maduro trabalha em <strong>camadas cumulativas</strong> &#8212; cada uma respondendo a uma pergunta diferente sobre a mesma opera&#231;&#227;o:</p><pre><code><span>As camadas de conten&#231;&#227;o &#8212; todas avaliadas ANTES de autorizar</span>

 valor      &#8594; esta transa&#231;&#227;o sozinha passa do teto da conta/perfil?
 ac&#250;mulo    &#8594; somada &#224;s &#250;ltimas 1h / 24h, passa do teto da janela?
 velocidade &#8594; quantas opera&#231;&#245;es esta conta fez nos &#250;ltimos N minutos?
 destino    &#8594; chave criada h&#225; minutos? contraparte nunca vista?
 contexto   &#8594; hor&#225;rio at&#237;pico p/ este cliente? dispositivo novo?
 agregado   &#8594; e o PARTICIPANTE? quanto saiu da institui&#231;&#227;o nesta hora?
                &#9492;&#9472;&#9472; a camada que faltou nas contas reserva
</code></pre><p>Alguns pontos de implementa&#231;&#227;o que fazem diferen&#231;a entre um limite decorativo e um limite real:</p><ul><li><p><strong>Janela deslizante, n&#227;o janela de calend&#225;rio.</strong> Um limite &#8220;por dia&#8221; que zera &#224; meia-noite entrega ao atacante uma janela dobrada: ele opera das 23h &#224;s 01h e usa dois dias de cota em duas horas. Janela deslizante (as &#250;ltimas 24 horas corridas) elimina essa brecha. E vale ter <strong>janelas noturnas mais apertadas</strong>: o comportamento leg&#237;timo &#224;s 4h da manh&#227; &#233; raro, e foi exatamente esse hor&#225;rio que os atacantes escolheram.</p></li><li><p><strong>O limite agregado do participante &#233; o que ningu&#233;m implementa.</strong> Todo mundo limita o cliente. Quase ningu&#233;m limita a <em>institui&#231;&#227;o</em> &#8212; quanto pode sair, no total, por hora, daquele ambiente. &#201; a camada que teria transformado R$ 800 milh&#245;es em um alerta na primeira dezena de milh&#245;es. Se voc&#234; opera contas reserva ou conta bols&#227;o, esse &#233; provavelmente o item mais valioso desta lista inteira.</p></li><li><p><strong>Falha fechada, n&#227;o aberta.</strong> Se o servi&#231;o que avalia limites estiver indispon&#237;vel, o que o seu c&#243;digo faz? Se a resposta for &#8220;deixa passar para n&#227;o derrubar o produto&#8221;, voc&#234; tem um <em>bypass</em> de seguran&#231;a que o atacante pode induzir derrubando aquele servi&#231;o. Opera&#231;&#227;o de alto valor com motor de limite fora do ar deve ser <strong>recusada</strong>, n&#227;o liberada.</p></li><li><p><strong>Contador com estado consistente.</strong> Limite avaliado sobre dado desatualizado (r&#233;plica com atraso, cache velho) &#233; limite que autoriza duas vezes o mesmo teto quando as requisi&#231;&#245;es chegam em paralelo. Contador at&#244;mico e transacional, com a mesma disciplina que voc&#234; aplica a saldo.</p></li><li><p><strong>Al&#231;ada e segunda pessoa.</strong> Acima de certos valores, o controle n&#227;o deveria ser &#8220;bloqueia ou libera&#8221;, mas &#8220;exige aprova&#231;&#227;o adicional&#8221;. Dupla cust&#243;dia &#233; o controle banc&#225;rio mais antigo que existe, e continua sendo o mais eficaz contra ator &#250;nico &#8212; inclusive contra credencial comprada.</p></li><li><p><strong>Limite ultrapassado &#233; evento de seguran&#231;a.</strong> Recusar n&#227;o basta: precisa gerar alerta correlacionado. Uma conta batendo no teto repetidamente &#224;s 4h n&#227;o &#233; um cliente frustrado, &#233; um sinal. Se o seu sistema recusa em sil&#234;ncio, voc&#234; jogou fora a informa&#231;&#227;o mais valiosa que tinha.</p></li></ul><div class="callout-block" data-callout="true"><p><strong><span>O menor commit de maior impacto do setor</span></strong></p><p>A diferen&#231;a entre um incidente de R$ 2 milh&#245;es e um de R$ 800 milh&#245;es n&#227;o est&#225; numa tecnologia sofisticada de detec&#231;&#227;o, num modelo de IA ou num fornecedor caro. Est&#225;, muitas vezes, em <strong>uma compara&#231;&#227;o com um n&#250;mero, avaliada no lugar certo do dom&#237;nio, com o contador certo por tr&#225;s</strong>. &#201; provavelmente o melhor retorno por linha de c&#243;digo que existe em sistemas de pagamento &#8212; e continua sendo tratado como configura&#231;&#227;o de produto, n&#227;o como arquitetura de seguran&#231;a.</p></div><h3><strong>6. Logs que n&#227;o servem para nada</strong></h3><p>Quase todo mundo loga. Quase ningu&#233;m consegue responder, em quinze minutos, a pergunta &#8220;o que essa credencial fez nas &#250;ltimas seis horas, ponta a ponta?&#8221;. Os defeitos cl&#225;ssicos:</p><ul><li><p>Log sem identificador de correla&#231;&#227;o que atravesse todos os saltos (o <code>endToEndId</code> existe exatamente para isso &#8212; use como chave de correla&#231;&#227;o e n&#227;o s&#243; como campo de neg&#243;cio).</p></li><li><p>Log de aplica&#231;&#227;o separado de log de acesso, separado de log de infraestrutura, sem tempo sincronizado nem consulta unificada.</p></li><li><p>Reten&#231;&#227;o curta demais para uma forense que descobre o incidente semanas depois. A norma agora manda guardar resultados de teste de intrus&#227;o e planos de a&#231;&#227;o por <strong>cinco anos</strong>; a sua trilha de auditoria precisa ter a mesma ambi&#231;&#227;o.</p></li><li><p>Detec&#231;&#227;o baseada em relat&#243;rio, n&#227;o em fluxo. Se o alerta nasce de um <em>job</em> das 6h, voc&#234; tem um sistema de hist&#243;ria, n&#227;o de defesa.</p></li></ul><h3><strong>7. A porta dos fundos operacional</strong></h3><p>Todo sistema financeiro tem endpoints administrativos: reprocessar, estornar manualmente, for&#231;ar concilia&#231;&#227;o, ajustar saldo, liberar bloqueio. Eles nascem no calor de um incidente, entram sem revis&#227;o de seguran&#231;a, ficam para sempre e frequentemente n&#227;o t&#234;m MFA, n&#227;o t&#234;m dupla aprova&#231;&#227;o, n&#227;o t&#234;m limite e n&#227;o t&#234;m trilha. &#201; o presente perfeito para uma credencial comprada.</p><p>Por isso a exig&#234;ncia de <strong>autentica&#231;&#227;o multifatorial para acessos administrativos aos ambientes Pix e STR</strong> n&#227;o &#233; detalhe operacional: &#233; reconhecimento regulat&#243;rio de que a porta dos fundos &#233; a porta preferida.</p><h3><strong>8. O dom&#237;nio como controle de seguran&#231;a</strong></h3><p>Fecho o t&#243;pico com a parte que costuma ser tratada como &#8220;modelagem&#8221; e que, na verdade, &#233; defesa. Os estados s&#227;o a sua rede de prote&#231;&#227;o. Estados terminais precisam ser <em>de fato</em> terminais e imut&#225;veis; transi&#231;&#245;es precisam ser expl&#237;citas e verific&#225;veis; a fonte da verdade do que foi liquidado &#233; o extrato do SPI, n&#227;o a sua tabela.</p><p>Um n&#250;mero de exemplo: concilia&#231;&#227;o cont&#237;nua contra a infraestrutura oficial detecta diverg&#234;ncia em minutos. Concilia&#231;&#227;o em D+1 detecta em horas &#8212; depois da convers&#227;o em cripto. A escolha entre as duas parece uma decis&#227;o de efici&#234;ncia. &#201; uma decis&#227;o de seguran&#231;a.</p><div class="callout-block" data-callout="true"><p><strong><span>Para colar no monitor</span></strong></p><p>Toda API que origina dinheiro &#233; uma superf&#237;cie de ataque com CNPJ. Ela n&#227;o precisa estar exposta na internet para ser explorada &#8212; precisa apenas ser alcan&#231;&#225;vel por algu&#233;m que conseguiu um crach&#225;.</p></div><h4><strong><span>Pense comigo</span></strong></h4><blockquote><p>Abra agora o endpoint de inicia&#231;&#227;o de pagamento do seu servi&#231;o. Percorra o caminho de cima para baixo e marque, linha por linha, onde est&#227;o: (a) a valida&#231;&#227;o de que a origem pertence ao autenticado; (b) o teto de valor; (c) o selo de integridade que sobrevive at&#233; a assinatura; (d) o identificador de correla&#231;&#227;o; (e) o ponto onde uma anomalia dispararia alerta em tempo real.</p><p>Se faltar mais de dois desses cinco, voc&#234; pode ter s&#233;rios problemas no futuro!</p></blockquote><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/ataques-ao-pix-r-15-mil-de-um-lado/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/ataques-ao-pix-r-15-mil-de-um-lado/comments"><span>Deixe um coment&#225;rio</span></a></p><h2><strong><span>T&#243;pico 04: </span>Governan&#231;a fraca &#233; d&#237;vida t&#233;cnica com juros: o que o Bacen passou a exigir</strong></h2><p>At&#233; 2024, o arcabou&#231;o de seguran&#231;a cibern&#233;tica do sistema financeiro brasileiro girava em torno de duas normas de 2021 &#8212; a Resolu&#231;&#227;o CMN n&#186; 4.893 (para institui&#231;&#245;es financeiras) e a Resolu&#231;&#227;o BCB n&#186; 85 (para institui&#231;&#245;es de pagamento). Eram normas <strong>principiol&#243;gicas</strong>: exigiam pol&#237;tica, governan&#231;a, plano de resposta a incidentes, gest&#227;o de terceiros. Boas normas. Audit&#225;veis por documento.</p><p>Depois de 2025, o regulador mudou de idioma. Saiu do &#8220;voc&#234; tem pol&#237;tica?&#8221; e entrou no <strong>&#8220;me mostre o controle funcionando&#8221;</strong>. Vale a pena entender o pacote, porque cada item dele vira, mais cedo ou mais tarde, um card no seu board.</p><h3><strong>A resposta ao ponto cego: os PSTIs entram no radar</strong></h3><p>A li&#231;&#227;o mais dura do caso C&amp;M foi que uma empresa n&#227;o regulada podia comprometer contas reserva de v&#225;rias institui&#231;&#245;es simultaneamente. A resposta veio na <strong>Resolu&#231;&#227;o BCB n&#186; 498/2025</strong>, endurecida pela <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Resolu%C3%A7%C3%A3o%20BCB&amp;numero=547">Resolu&#231;&#227;o BCB n&#186; 547/2026</a></strong>, que disciplina o credenciamento dos Provedores de Servi&#231;os de Tecnologia da Informa&#231;&#227;o para acesso &#224; Rede do Sistema Financeiro Nacional. O que ela trouxe:</p><ul><li><p><strong>Capital m&#237;nimo de R$ 15 milh&#245;es</strong> e exig&#234;ncias ampliadas de governan&#231;a e gest&#227;o de riscos.</p></li><li><p>Comprova&#231;&#227;o de capacidade t&#233;cnico-operacional segundo padr&#245;es do Deinf/BCB.</p></li><li><p><strong>Certifica&#231;&#227;o t&#233;cnica</strong> das solu&#231;&#245;es usadas por institui&#231;&#245;es para integrar, por interfaces eletr&#244;nicas, aos servi&#231;os do provedor.</p></li><li><p><strong>A&#231;&#245;es de intelig&#234;ncia cibern&#233;tica</strong>, incluindo monitoramento de informa&#231;&#245;es de interesse &#8212; clientes, chaves, credenciais, vulnerabilidades &#8212; na internet aberta e fechada.</p></li><li><p>Sujei&#231;&#227;o a medidas cautelares e ao descredenciamento em caso de descumprimento.</p></li></ul><p>O efeito de mercado foi imediato e vale registrar sem romantismo: o segmento encolheu. Stark Infra e Topaz (Stefanini) oficializaram a sa&#237;da; ABBC, C&amp;M Software e a pr&#243;pria Sinqia/Evertec entraram em processo de sa&#237;da ordenada. A barreira de entrada subiu, e com ela a concentra&#231;&#227;o. Seguran&#231;a tem custo, e o custo aparece em algum lugar.</p><p>Em paralelo, as <strong>Resolu&#231;&#245;es BCB n&#186; 496, 497 e 498</strong> impuseram <strong>limite de R$ 15 mil por transa&#231;&#227;o de Pix e TED</strong> para institui&#231;&#245;es de pagamento n&#227;o autorizadas e para aquelas conectadas &#224; RSFN via PSTI &#8212; uma trava de dano aplicada por regula&#231;&#227;o, exatamente do tipo que o c&#243;digo deveria ter e n&#227;o tinha. E a <strong>Resolu&#231;&#227;o BCB n&#186; 494</strong> antecipou de dezembro de 2029 para maio de 2026 o prazo para que IPs n&#227;o autorizadas pe&#231;am autoriza&#231;&#227;o de funcionamento.</p><h3><strong>Os controles m&#237;nimos: o que virou obrigat&#243;rio para todos n&#243;s</strong></h3><p>Em 18 de dezembro de 2025, a <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Resolu%C3%A7%C3%A3o%20CMN&amp;numero=5274">Resolu&#231;&#227;o CMN n&#186; 5.274/2025</a></strong> (alterando a 4.893) e a <strong><a href="https://www.matera.com/br/blog/resolucoes-bcb-538-cmn-5274-seguranca-cibernetica/">Resolu&#231;&#227;o BCB n&#186; 538/2025</a></strong> (alterando a 85) reescreveram a pol&#237;tica de seguran&#231;a cibern&#233;tica com prazo de adequa&#231;&#227;o em <strong>1&#186; de mar&#231;o de 2026</strong>. Elas detalham controles m&#237;nimos mandat&#243;rios: autentica&#231;&#227;o; criptografia; preven&#231;&#227;o e detec&#231;&#227;o de intrus&#227;o; preven&#231;&#227;o de vazamento de informa&#231;&#245;es; prote&#231;&#227;o contra software malicioso; rastreabilidade; gest&#227;o de backup; avalia&#231;&#227;o e corre&#231;&#227;o de vulnerabilidades; controles de acesso; perfis de configura&#231;&#227;o segura (<em>hardening</em>); prote&#231;&#227;o de rede; gest&#227;o de certificados digitais; <strong>requisitos de seguran&#231;a para integra&#231;&#227;o por interfaces eletr&#244;nicas (APIs)</strong>; e a&#231;&#245;es de intelig&#234;ncia no ambiente cibern&#233;tico.</p><p>Repare que dois desses itens s&#227;o explicitamente sobre o que fazemos no editor: <em><a href="https://www.totvs.com/blog/negocios/hardening/">hardening</a></em><a href="https://www.totvs.com/blog/negocios/hardening/"> </a>e seguran&#231;a de APIs. E h&#225; um detalhe que muda o escopo de forma decisiva: os controles se aplicam tamb&#233;m a <strong>sistemas de terceiros executados com recursos computacionais da pr&#243;pria institui&#231;&#227;o</strong>. Aquele componente do fornecedor que roda no seu cluster agora &#233; problema seu, formalmente.</p><h3><strong>Os refor&#231;os espec&#237;ficos de Pix e STR</strong></h3><p>Esta &#233; a parte que qualquer pessoa que escreve c&#243;digo de pagamento deveria saber de cor:</p><ul><li><p><strong>MFA obrigat&#243;rio</strong> para acessos administrativos aos ambientes Pix e STR.</p></li><li><p><strong>Isolamento f&#237;sico e l&#243;gico</strong> dos ambientes Pix e STR dos demais sistemas; em nuvem, inst&#226;ncias dedicadas e apartadas.</p></li><li><p><strong>Monitoramento de credenciais e certificados digitais</strong>, especialmente no SPI, com veda&#231;&#227;o de acesso de terceiros &#224;s chaves privadas de assinatura de mensagens.</p></li><li><p><strong>Valida&#231;&#227;o de integridade fim a fim</strong> antes da assinatura digital das transa&#231;&#245;es.</p></li><li><p>Comunica&#231;&#227;o eletr&#244;nica na RSFN qualificada como <strong>&#8220;servi&#231;o relevante&#8221;</strong> para fins de contrata&#231;&#227;o, elevando o n&#237;vel de gest&#227;o de risco e supervis&#227;o.</p></li><li><p><strong>Teste de intrus&#227;o anual</strong> por profissionais independentes, com documenta&#231;&#227;o de resultados e planos de a&#231;&#227;o mantidos &#224; disposi&#231;&#227;o do BC por cinco anos.</p></li><li><p><strong>Cyber Threat Intelligence</strong> como controle m&#237;nimo &#8212; n&#227;o mais &#8220;apoio t&#233;cnico&#8221;, mas requisito formal conectado ao SOC/CSIRT e &#224; gest&#227;o de credenciais.</p></li></ul><h3><strong>E do lado do dinheiro: fechar o ralo do cash-out</strong></h3><p>O outro eixo do pacote, apresentado pelo presidente do Banco Central ao Senado em maio de 2026, ataca a sa&#237;da dos recursos: sistema de alerta de movimenta&#231;&#245;es at&#237;picas no SPI; <strong><a href="https://www.jusbrasil.com.br/artigos/resolucao-bcb-n-493-2025-e-o-med-20-melhoria-no-rastreio-dos-valores-em-uma-fraude/5624341765">MED 2.0</a></strong><a href="https://www.jusbrasil.com.br/artigos/resolucao-bcb-n-493-2025-e-o-med-20-melhoria-no-rastreio-dos-valores-em-uma-fraude/5624341765"> (Resolu&#231;&#227;o BCB n&#186; 493)</a>, que amplia a identifica&#231;&#227;o de contas usadas em fraude e a devolu&#231;&#227;o &#224;s v&#237;timas; <strong>bot&#227;o de contesta&#231;&#227;o</strong> do MED (Instru&#231;&#227;o Normativa BCB n&#186; 589), para acelerar o bloqueio na conta do fraudador; bloqueio de chaves Pix de fraudadores e rejei&#231;&#227;o obrigat&#243;ria de pagamentos destinados a contas usadas em fraude (Resolu&#231;&#245;es BCB n&#186; 506 e 501); limites por transa&#231;&#227;o conforme perfil e comportamento do cliente; encerramento compuls&#243;rio de contas usadas para movimentar recursos de terceiros &#8212; o cerco &#224;s contas bols&#227;o; <strong>BC Protege+</strong> (Resolu&#231;&#227;o BCB n&#186; 475), que impede abertura de conta ou inclus&#227;o de titular sem autoriza&#231;&#227;o pr&#233;via; e multas dosadas por capacidade econ&#244;mica, com <strong>exclus&#227;o do Pix</strong> em casos de maior potencial lesivo e reincid&#234;ncia.</p><h3><strong>O alerta que fecha o c&#237;rculo</strong></h3><p>Em novembro de 2025, o Comit&#234; de Estabilidade Financeira do Banco Central colocou na ata algo que soa como um <em>code review</em> do setor inteiro: preocupa&#231;&#227;o com a &#8220;crescente depend&#234;ncia&#8221; de servi&#231;os de terceiros e com o <strong>&#8220;uso disseminado&#8221; de APIs</strong>, em muitos casos sem avalia&#231;&#227;o peri&#243;dica de riscos nem monitoramento operacional adequado.</p><p>Os n&#250;meros que sustentam o alerta: <strong>72,6%</strong> das institui&#231;&#245;es usam integra&#231;&#245;es por API, mas apenas cerca de <strong>29%</strong> revisam regularmente os riscos associadas a essas conex&#245;es. &#201; a dist&#226;ncia entre construir e sustentar, expressa em percentual.</p><h4><strong><span>A tradu&#231;&#227;o para quem escreve c&#243;digo</span></strong></h4><p>N&#227;o existe mais &#8220;requisito de seguran&#231;a&#8221; como categoria separada de &#8220;requisito funcional&#8221;. MFA em rota administrativa, isolamento de ambiente, integridade antes da assinatura, rastreabilidade ponta a ponta e limite por perfil de risco s&#227;o <strong>comportamentos do sistema</strong> &#8212; com dono, teste automatizado, evid&#234;ncia e data. Se um deles n&#227;o tem um teste que falha quando algu&#233;m o remove, ele n&#227;o existe: existe uma frase num documento.</p><h4><strong><span>Pense comigo</span></strong></h4><blockquote><p>Pegue a &#250;ltima funcionalidade de Pix que o seu time entregou. Quais dos 14 controles m&#237;nimos ela tocou? Consegue apontar o <em>pull request</em> em que cada um foi endere&#231;ado, ou o teste que o cobre?</p><p>E a vers&#227;o executiva da mesma pergunta, que &#233; a que o Bacen faz: se o supervisor pedir a evid&#234;ncia amanh&#227;, voc&#234; entrega um log ou uma apresenta&#231;&#227;o?</p></blockquote><h2><strong><span>T&#243;pico 05: </span>SSDLC: onde a norma vira c&#243;digo</strong></h2><p>Chegamos ao ponto do artigo em que preciso ser direto sobre uma coisa: <strong><a href="https://www.ibm.com/br-pt/think/topics/secure-software-development-life-cycle">SSDLC </a>n&#227;o teria impedido que aquele funcion&#225;rio vendesse a credencial</strong>. Nenhum ciclo de desenvolvimento seguro impede a fraqueza humana, o aliciamento, a necessidade financeira.</p><p>O que o SSDLC faz &#233; diferente e mais importante: <strong>ele determina quanto vale a credencial vendida</strong>. Num sistema com privil&#233;gio m&#237;nimo, segrega&#231;&#227;o real, limites expl&#237;citos, integridade fim a fim e detec&#231;&#227;o em tempo de transa&#231;&#227;o, aquela mesma credencial valeria uma consulta a extrato e um alerta imediato. Num sistema sem isso, valeu R$ 800 milh&#245;es. O ataque &#233; o mesmo. O design &#233; que decide o resultado.</p><p>Vamos por fase, com o que d&#225; para come&#231;ar na segunda-feira.</p><h3><strong>Requisitos: escreva o caso de abuso junto com a hist&#243;ria de usu&#225;rio</strong></h3><p>Para cada &#8220;como pagador, quero iniciar um Pix por chave&#8221;, escreva a irm&#227; g&#234;mea maligna: &#8220;como fraudador com credencial v&#225;lida, quero iniciar um Pix por chave <em>a partir da conta de outro cliente</em>&#8221;, &#8220;&#8230;<em>de madrugada, para uma chave criada h&#225; dois minutos</em>&#8221;, &#8220;&#8230;<em>mil vezes em sessenta segundos</em>&#8221;.</p><p>Caso de abuso n&#227;o &#233; paranoia: &#233; requisito. E ele tem uma propriedade excelente &#8212; vira teste automatizado quase sem tradu&#231;&#227;o. Uma hist&#243;ria de usu&#225;rio produz um teste de caminho feliz; um caso de abuso produz um teste que precisa <strong>falhar com 403</strong>.</p><p>No mesmo movimento, amarre requisito regulat&#243;rio ao backlog com identificador. &#8220;RN-Pix-014: integridade fim a fim antes da assinatura (CMN 5.274)&#8221;. Assim a rastreabilidade regulat&#243;ria n&#227;o &#233; um exerc&#237;cio arqueol&#243;gico de auditoria: ela nasce no seu backlog.</p><h3><strong>Design: modelagem de amea&#231;as no fluxo, n&#227;o no diagrama bonito</strong></h3><p>Aplique <a href="https://www.everpuredata.com/br/knowledge/stride-threat-model.html">STRIDE</a><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a><a href="https://www.everpuredata.com/br/knowledge/stride-threat-model.html"> </a>ao fluxo real de inicia&#231;&#227;o e liquida&#231;&#227;o, com as fronteiras de confian&#231;a <strong>desenhadas explicitamente</strong> &#8212; incluindo a fronteira com o PSTI, com o BaaS, com o app desenvolvido por terceiro. Nos casos de 2025, era exatamente essa fronteira que estava impl&#237;cita.</p><pre><code><span>// Modelagem de amea&#231;as aplicada ao fluxo Pix</span>
S  Spoofing        &#8594; servi&#231;o A se passa por servi&#231;o B?  mTLS + identidade de carga
T  Tampering       &#8594; algu&#233;m altera valor/destino entre autorizar e assinar?  selo de integridade
R  Repudiation     &#8594; d&#225; para reconstruir quem fez o qu&#234;?  endToEndId como correla&#231;&#227;o
I  Info disclosure &#8594; chave privada / chaves Pix / dados cadastrais expostos?
D  Denial          &#8594; esgotamento de conex&#227;o com o SPI derruba o produto?
E  Elevation       &#8594; operador vira originador? conta de servi&#231;o vira admin?</code></pre><p>Duas perguntas de design que valem mais que uma semana de ferramenta: </p><ul><li><p><strong>Onde est&#225; o ponto &#250;nico em que um ator consegue fechar o ciclo sozinho?</strong> </p></li><li><p><strong>E</strong> <strong>o que acontece se este componente for hostil?</strong> </p></li></ul><p>Fa&#231;a a segunda pergunta apontando para o seu pr&#243;prio servi&#231;o de integra&#231;&#227;o. &#201; desconfort&#225;vel, e &#233; o exerc&#237;cio certo.</p><h3><strong>Codifica&#231;&#227;o: autoriza&#231;&#227;o centralizada, tudo mais &#233; detalhe</strong></h3><ul><li><p><strong>Autoriza&#231;&#227;o como componente, n&#227;o como </strong><code>if</code><strong> espalhado.</strong> Um ponto de decis&#227;o, test&#225;vel isoladamente, chamado por todos os fluxos monet&#225;rios. Espalhar autoriza&#231;&#227;o por controllers &#233; garantir que um deles ficar&#225; para tr&#225;s.</p></li><li><p><strong>Negar por padr&#227;o.</strong> Rota nova nasce fechada. Perfil novo nasce sem permiss&#227;o. Campo novo no payload nasce rejeitado por valida&#231;&#227;o de esquema estrita &#8212; nada de <em>schema</em> permissivo aceitando propriedades desconhecidas em mensagem financeira.</p></li><li><p><strong>Idempot&#234;ncia de verdade.</strong> Chave de idempot&#234;ncia com escopo, TTL e persist&#234;ncia; reenvio devolve o mesmo resultado, n&#227;o um segundo pagamento. Em sistema irrevers&#237;vel, idempot&#234;ncia &#233; controle de seguran&#231;a.</p></li><li><p><strong>Segredos fora do c&#243;digo e fora do humano.</strong> Cofre com credencial din&#226;mica, chave de assinatura em HSM, nenhum segredo est&#225;tico em vari&#225;vel de ambiente de longa dura&#231;&#227;o.</p></li><li><p><strong>Depend&#234;ncias s&#227;o superf&#237;cie.</strong> SCA e SBOM no pipeline. A cadeia de suprimentos do <em>software</em> merece o mesmo rigor que passamos a exigir da cadeia de fornecedores &#8212; e &#233; o mesmo tipo de risco.</p></li></ul><h3><strong>Revis&#227;o de c&#243;digo: algu&#233;m precisa olhar com a lente errada</strong></h3><p>Revis&#227;o de c&#243;digo no Brasil, na m&#233;dia, verifica legibilidade, cobertura e regra de neg&#243;cio. Quase nunca verifica autoriza&#231;&#227;o. Uma mudan&#231;a pequena e barata: em reposit&#243;rios que tocam dinheiro, exija <strong>revisor de seguran&#231;a designado</strong> (via <code>CODEOWNERS</code>) para qualquer altera&#231;&#227;o em rota monet&#225;ria, em autoriza&#231;&#227;o, em assinatura ou em limites. E d&#234; ao revisor uma lista curta, do tipo: &#8220;esta mudan&#231;a altera quem pode fazer o qu&#234;? altera um teto? altera o que &#233; assinado? altera o que &#233; logado?&#8221;.</p><h3><strong>Pipeline: onde o discurso encontra o bloqueio</strong></h3><ul><li><p><a href="https://checkmarx.com/learn/sca/sca-sast-dast/">SAST, DAST, SCA</a><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a> e <em><a href="https://docs.github.com/pt/code-security/concepts/secret-security/secret-scanning">secret scanning</a></em> <strong>bloqueantes</strong> nas rotas cr&#237;ticas &#8212; n&#227;o informativos, n&#227;o &#8220;corrige depois&#8221;.</p></li><li><p><strong>Segrega&#231;&#227;o de fun&#231;&#227;o no deploy</strong>: quem faz o merge n&#227;o &#233; quem aprova a promo&#231;&#227;o para o ambiente Pix. Isso &#233; o equivalente, na engenharia, &#224; dupla cust&#243;dia que os bancos exigem no caixa h&#225; um s&#233;culo.</p></li><li><p><strong>Acesso a produ&#231;&#227;o ef&#234;mero e sob demanda</strong>, com justificativa e trilha. Acesso permanente de desenvolvedor a produ&#231;&#227;o de pagamento &#233; a vers&#227;o institucional da credencial vendida no bar.</p></li><li><p>Assinatura e proveni&#234;ncia de artefatos, para que o bin&#225;rio que roda seja comprovadamente o que saiu do reposit&#243;rio revisado.</p></li></ul><h3><strong>Testes: os que quase ningu&#233;m escreve</strong></h3><p>Al&#233;m de unidade e integra&#231;&#227;o, tr&#234;s fam&#237;lias que deveriam ser obrigat&#243;rias em qualquer sistema Pix:</p><ol><li><p><strong>Teste de autoriza&#231;&#227;o negativa</strong> &#8212; o &#8220;teste do vizinho&#8221;: o token do cliente A tentando operar a conta do cliente B precisa retornar 403, e isso precisa ser um teste automatizado que roda em toda build. Cada objeto sens&#237;vel, cada fun&#231;&#227;o administrativa.</p></li><li><p><strong>Teste de limite e velocidade</strong> &#8212; o sistema precisa recusar a opera&#231;&#227;o 101 na janela, o valor acima do teto, o destino rec&#233;m-criado acima do valor de al&#231;ada. Se n&#227;o existe teste, o limite vai ser removido por algu&#233;m, em produ&#231;&#227;o, num incidente, e ningu&#233;m vai notar.</p></li><li><p><strong>Teste de detec&#231;&#227;o</strong> &#8212; este &#233; o mais esquecido. Injete um padr&#227;o an&#244;malo em ambiente controlado e verifique que o alerta <em>dispara</em>, chega ao destino certo e aciona quem deve. Alerta que nunca foi testado &#233; alerta que n&#227;o existe.</p></li></ol><h3><strong>Observabilidade e conten&#231;&#227;o: detec&#231;&#227;o &#233; requisito funcional</strong></h3><p>Se h&#225; uma li&#231;&#227;o operacional dos casos recentes, &#233; esta: <strong>a detec&#231;&#227;o chegou tarde e veio de fora</strong>. Na C&amp;M, o aviso partiu de uma terceira institui&#231;&#227;o. Em v&#225;rios casos, a percep&#231;&#227;o veio quando o dinheiro j&#225; virava cripto.</p><p>O que precisa existir, e ser c&#243;digo:</p><ul><li><p>Correla&#231;&#227;o por <code>endToEndId</code> atravessando todos os servi&#231;os e chegando ao painel.</p></li><li><p>Detec&#231;&#227;o em tempo quase real de anomalia de volume, hor&#225;rio, contraparte, destino novo e concentra&#231;&#227;o &#8212; no <em>dom&#237;nio</em>, n&#227;o s&#243; no gateway.</p></li><li><p><em><strong>Kill switch</strong></em> por produto, por participante, por tipo de opera&#231;&#227;o &#8212; implementado, documentado e <em>ensaiado</em>. O Banco Central desligou o acesso da C&amp;M &#224; infraestrutura em horas. A pergunta &#233;: <strong>quanto tempo o </strong><em><strong>seu</strong></em><strong> desligamento leva?</strong></p></li><li><p>Concilia&#231;&#227;o cont&#237;nua contra a fonte oficial, com alarme por diverg&#234;ncia.</p></li><li><p>Revoga&#231;&#227;o de credencial e certificado em minutos, automatizada. &#8220;J&#225; encerramos o acesso&#8221; precisa ser um bot&#227;o, n&#227;o um projeto.</p></li></ul><h3><strong>E o peda&#231;o cultural, que &#233; o mais dif&#237;cil</strong></h3><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">O programador j&#250;nior terceirizado da C&amp;M tinha aquele acesso porque, em algum momento, foi </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">mais f&#225;cil conceder do que desenhar</mark></strong>. Ningu&#233;m acordou querendo criar um risco de R$ 1 bilh&#227;o; algu&#233;m s&#243; precisava destravar um chamado numa sexta-feira &#224; noite.</p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Privil&#233;gio m&#237;nimo n&#227;o &#233; uma </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">decis&#227;o de arquitetura</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> que algu&#233;m precisa tomar no refinamento, defender no </mark><em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">planning</mark></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> e sustentar quando o prazo apertar.</mark> E &#233; a&#237;, exatamente a&#237;, que a mentalidade de conformidade que o Bacen exige deixa de ser burocracia e vira of&#237;cio: entender que o requisito regulat&#243;rio &#233; a codifica&#231;&#227;o de um dano real que j&#225; aconteceu com algu&#233;m, escrito por quem teve que pagar a conta.</p><p><strong><span>A tese do t&#243;pico, em uma frase:</span></strong></p><blockquote><p>O SSDLC n&#227;o impede que vendam uma credencial. Ele impede que uma credencial vendida valha oitocentos milh&#245;es de reais.</p></blockquote><h4><strong><span>Pense comigo</span></strong></h4><blockquote><p>Quando foi a &#250;ltima vez que uma build do seu reposit&#243;rio falhou <strong>por motivo de seguran&#231;a</strong> &#8212; um teste de autoriza&#231;&#227;o quebrado, um segredo detectado, um limite removido, uma depend&#234;ncia vulner&#225;vel barrando o merge?</p><p>Se a resposta for &#8220;nunca&#8221;, existem duas explica&#231;&#245;es poss&#237;veis: o seu c&#243;digo &#233; perfeito, ou o seu pipeline n&#227;o est&#225; olhando. Uma delas &#233; muito mais prov&#225;vel que a outra.</p></blockquote><h2><strong><span>T&#243;pico 06 &#183; </span>&#8220;Mas o que isso tem a ver comigo?&#8221; Como o ataque funciona na pr&#225;tica&#8230;</strong></h2><p>Se voc&#234; chegou at&#233; aqui trabalhando diretamente com sistemas que operam Pix, &#233; bem prov&#225;vel que uma obje&#231;&#227;o esteja formada na sua cabe&#231;a: <em>&#8220;t&#225;, mas eu n&#227;o tenho o problema da C&amp;M. Meu ecossistema tem camadas. API gateway na borda, rate limit, WAF, motor antifraude, mTLS entre servi&#231;os. Um atacante que n&#227;o trabalha aqui dentro simplesmente n&#227;o passa.&#8221;</em></p><p>&#201; uma obje&#231;&#227;o leg&#237;tima, e &#233; exatamente a que eu quero desmontar &#8212; n&#227;o com ret&#243;rica, mas mostrando o mecanismo. Porque o Pix, voc&#234; sabe melhor que ningu&#233;m, <strong>n&#227;o &#233; uma simples transfer&#234;ncia instant&#226;nea</strong>. &#201; um sistema distribu&#237;do, com integra&#231;&#227;o s&#237;ncrona a uma infraestrutura cr&#237;tica, assinatura de mensagens, cust&#243;dia de chaves e liquida&#231;&#227;o irrevers&#237;vel. Cada uma dessas propriedades &#233; uma camada de prote&#231;&#227;o &#8212; e cada camada &#233; software, e software tem defeito.</p><p>A pergunta honesta n&#227;o &#233; &#8220;minhas camadas me protegem?&#8221;. &#201; <strong>&#8220;o que acontece quando uma delas tem uma falha cr&#237;tica, e o atacante encontra essa falha antes de mim?&#8221;</strong>. Vou responder isso de forma transparente. O objetivo aqui &#233; 100% defensivo: voc&#234; n&#227;o consegue defender um sistema contra um ataque que n&#227;o entende.</p><h3><strong>Primeiro, mate uma ilus&#227;o: o gateway n&#227;o &#233; uma parede</strong></h3><p>API gateway, <a href="https://www.cloudflare.com/pt-br/learning/ddos/glossary/web-application-firewall-waf/">WAF</a> e rate limit s&#227;o <strong>redutores de ru&#237;do</strong>, n&#227;o paredes. Eles filtram o &#243;bvio: a inje&#231;&#227;o de SQL trivial, o scanner barulhento, o pico de requisi&#231;&#245;es. O que eles n&#227;o fazem &#8212; porque n&#227;o conseguem &#8212; &#233; entender a sem&#226;ntica do seu dom&#237;nio. Um WAF n&#227;o sabe que a conta de origem daquela ordem de pagamento n&#227;o pertence ao token que a enviou. Para o WAF, &#233; uma requisi&#231;&#227;o HTTPS bem formada, com um JSON v&#225;lido, dentro do rate limit. Ela passa.</p><p><a href="https://www.azion.com/en/learning/websec/what-are-waf-bypass-attacks/">Pior: WAF &#233; regra, e regra se contorna. A pr&#243;pria comunidade de seguran&#231;a documenta abertamente como ofuscar payloads para passar por WAF em vulnerabilidades famosas</a>. Trate o gateway como o que ele &#233;: um quebra-molas que reduz a velocidade do tr&#225;fego hostil. N&#227;o como o port&#227;o do cofre.</p><h3><strong>O frontend mobile n&#227;o &#233; seu. &#201; do atacante.</strong></h3><p>Aqui mora um erro conceitual que sustenta metade das vulnerabilidades que vejo. O time trata o app como &#8220;nosso cliente confi&#225;vel&#8221;. N&#227;o &#233;. Assim que o APK &#233; publicado, ele est&#225; <strong>na m&#227;o do advers&#225;rio</strong>, para ser desmontado com calma.</p><p>O que um atacante extrai de um app mobile em uma tarde, com ferramentas p&#250;blicas e gratuitas:</p><ul><li><p>Todos os <strong>endpoints</strong> que o app chama &#8212; a sua superf&#237;cie de API, documentada de gra&#231;a pelo seu pr&#243;prio bin&#225;rio.</p></li><li><p>Segredos <em>hardcoded</em>: API keys, tokens est&#225;ticos, credenciais de servi&#231;os de terceiros que algu&#233;m &#8220;s&#243; deixou aqui pra testar&#8221;.</p></li><li><p>A l&#243;gica de neg&#243;cio embutida &#8212; inclusive as valida&#231;&#245;es que voc&#234; achou que eram &#8220;de seguran&#231;a&#8221; e que rodam <strong>no dispositivo dele</strong>.</p></li></ul><p>E o <em>certificate pinning</em>, que muitos tratam como a defesa definitiva? &#201; bypass&#225;vel. Com o dispositivo sob controle do atacante (root/jailbreak, emulador, instrumenta&#231;&#227;o din&#226;mica), o pinning cai. A li&#231;&#227;o de arquitetura &#233; dura e inegoci&#225;vel: <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">toda decis&#227;o de seguran&#231;a que importa acontece no servidor.</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> O app &#233; uma sugest&#227;o de interface, n&#227;o um guardi&#227;o. Se a sua autoriza&#231;&#227;o depende de o cliente &#8220;se comportar&#8221;, ela n&#227;o existe.</mark></p><div class="callout-block" data-callout="true"><p><strong><span>Regra de ouro do cliente hostil</span></strong></p><p>Assuma que o atacante fala com a sua API <strong>sem passar pelo seu app</strong>. Direto, com uma ferramenta de linha de comando, montando os pacotes na m&#227;o, com o token que ele extraiu ou capturou. Se a sua API s&#243; est&#225; segura porque &#8220;o app n&#227;o deixaria fazer isso&#8221;, a sua API n&#227;o est&#225; segura.</p></div><h3><strong>Agora o cora&#231;&#227;o: o que &#233; &#8220;acesso remoto por vulnerabilidade&#8221; (RCE)</strong></h3><p><a href="https://www.cloudflare.com/learning/security/what-is-remote-code-execution/">Remote Code Execution</a> &#233; o santo graal do atacante: fazer <strong>o seu servidor executar c&#243;digo dele</strong>. N&#227;o roubar um dado, n&#227;o burlar uma tela &#8212; rodar comandos dentro do seu processo, com os privil&#233;gios do seu processo. E o mecanismo, por tr&#225;s de toda a m&#237;stica, &#233; quase sempre o mesmo princ&#237;pio:</p><div class="pullquote"><p style="text-align: center;">o sistema confunde <strong>dado</strong> com <strong>instru&#231;&#227;o</strong>.</p></div><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Voc&#234; espera receber um dado (um nome, um valor, um cabe&#231;alho, um objeto serializado). Mas em algum ponto do caminho, aquele dado &#233; interpretado como </mark><em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">c&#243;digo</mark></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">, ou usado para decidir </mark><em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">qual c&#243;digo</mark></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> carregar. Toda a fam&#237;lia RCE &#233; varia&#231;&#227;o disso.</mark> Vamos aos vetores reais, com o que voc&#234; deve procurar no <strong>seu</strong> c&#243;digo &#8212; o <em>sink</em>, o ponto onde o dado perigoso &#233; consumido.</p><p><strong>Deserializa&#231;&#227;o insegura.</strong> Um objeto serializado carrega, junto, o <em>tipo</em> dele. Se voc&#234; desserializa dado que veio de fora e existe, no seu classpath, uma sequ&#234;ncia de classes que pode ser encadeada (o que a comunidade chama de <em>gadget chain</em>), o simples ato de desserializar dispara execu&#231;&#227;o. Voc&#234; n&#227;o chamou nenhum m&#233;todo malicioso; a m&#225;quina de desserializa&#231;&#227;o chamou por voc&#234;. O <em>sink</em><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-6" href="#footnote-6" target="_self">6</a> a ca&#231;ar: qualquer <code>readObject</code>, <code>ObjectInputStream</code>, ou binder de JSON/XML configurado para instanciar tipos arbitr&#225;rios a partir do payload.</p><p><strong>Inje&#231;&#227;o de express&#227;o / template.</strong> Frameworks modernos avaliam express&#245;es (SpEL no Spring, OGNL no Struts, template engines em qualquer linguagem). Se entrada do usu&#225;rio chega a um avaliador de express&#227;o, ela deixa de ser texto e vira programa. Foi essa a natureza do <strong>Spring4Shell</strong> (CVE-2022-22965): manipula&#231;&#227;o de <em>data binding</em> alcan&#231;ava o carregador de classes e permitia carregar c&#243;digo malicioso. O <em>sink</em>: <code>parseExpression</code> com string do usu&#225;rio, template renderizado com input n&#227;o confi&#225;vel, <code>Runtime.exec</code>/<code>ProcessBuilder</code> alimentado por par&#226;metro.</p><p><strong>Uma linha de log.</strong> O <strong>Log4Shell</strong> (<a href="https://www.kaspersky.com.br/blog/log4shell-critical-vulnerability-in-apache-log4j/18633/">CVE-2021-44228, CVSS 10.0</a>) &#233; o exemplo mais did&#225;tico e mais assustador da hist&#243;ria recente. A biblioteca de log processava strings controladas pelo usu&#225;rio de forma insegura: uma express&#227;o de <em>lookup</em> dentro de uma mensagem logada fazia o Log4j <strong>buscar e executar c&#243;digo de um servidor remoto</strong>. Como quase toda aplica&#231;&#227;o loga entrada do usu&#225;rio &#8212; um cabe&#231;alho HTTP, um User-Agent, um campo de formul&#225;rio &#8212;, <strong>bastava enviar a string certa num header para comprometer o servidor</strong>. A v&#237;tima nem precisava clicar. E o ponto que importa para n&#243;s: ningu&#233;m <em>escreveu</em> c&#243;digo inseguro. A inseguran&#231;a estava numa <strong>depend&#234;ncia transitiva</strong> que a maioria dos times nem sabia que tinha. Anos depois, em 2025, ainda h&#225; servi&#231;os vulner&#225;veis por depend&#234;ncia oculta e remedia&#231;&#227;o incompleta.</p><p><strong>SSRF &#8212; o vetor que ama a nuvem.</strong> <em><a href="https://portswigger.net/web-security/ssrf">Server-Side Request Forgery</a></em>: voc&#234; faz o servidor da v&#237;tima realizar uma requisi&#231;&#227;o para um destino escolhido pelo atacante. Parece inofensivo at&#233; voc&#234; lembrar onde o seu servi&#231;o roda. Numa nuvem, existe um <em>endpoint</em> de metadados interno que entrega <strong>credenciais tempor&#225;rias da identidade da m&#225;quina</strong>. Um SSRF bem posicionado transforma &#8220;meu servi&#231;o busca uma imagem nessa URL&#8221; em &#8220;meu servi&#231;o acabou de entregar as credenciais de nuvem dele para o atacante&#8221;. Dali, o advers&#225;rio se move lateralmente &#8212; e &#233; por isso que o isolamento de ambiente Pix que o Bacen passou a exigir n&#227;o &#233; firula: &#233; a diferen&#231;a entre um servi&#231;o comprometido e o ambiente de assinatura comprometido.</p><h3><strong>Como o &#8220;acesso remoto&#8221; realmente se estabelece</strong></h3><p>Suponha que o atacante conseguiu executar <em>um</em> comando via um desses vetores. Como isso vira uma sess&#227;o interativa, um &#8220;controle&#8221; do servidor? Vamos para o conceito &#8212; e, dentro dele, a sua melhor defesa.</p><p>Seu firewall provavelmente bloqueia conex&#245;es <em>de entrada</em> n&#227;o autorizadas. Ent&#227;o o atacante inverte o fluxo: faz o <strong>seu servidor iniciar uma conex&#227;o de sa&#237;da</strong> para uma m&#225;quina que ele controla, e usa esse canal para enviar comandos e receber respostas. &#201; o conceito de <em>reverse shell</em>: o servidor comprometido &#8220;liga de volta&#8221; para o atacante. Ele funciona porque, na esmagadora maioria das arquiteturas, <strong>ningu&#233;m filtra o tr&#225;fego de sa&#237;da</strong>. Entrada &#233; paranoia; sa&#237;da &#233; festa aberta.</p><div class="callout-block" data-callout="true"><p><strong><span>O controle que quebra a corrente inteira</span></strong></p><p><strong>Egress filtering.</strong> Se o seu ambiente de pagamento s&#243; pode abrir conex&#245;es de sa&#237;da para uma lista curt&#237;ssima e expl&#237;cita de destinos (o SPI, o seu banco de dados, o seu cofre), ent&#227;o: a RCE n&#227;o consegue &#8220;ligar de volta&#8221; para o atacante; o Log4Shell n&#227;o consegue buscar o c&#243;digo remoto; o SSRF n&#227;o consegue exfiltrar; o dado roubado n&#227;o consegue sair. Um &#250;nico controle de rede neutraliza metade da cadeia. &#201; de longe o item de melhor custo-benef&#237;cio desta lista, e &#233; o mais negligenciado &#8212; porque bloquear a sa&#237;da d&#225; trabalho e quebra conveni&#234;ncias. De novo: &#233; o ponto.</p></div><h3><strong>A pergunta que todo mundo faz: &#8220;no .NET/Node/Go estou livre?&#8221;</strong></h3><p><strong>N&#227;o</strong>. E preciso ser direto sobre isso porque &#233; uma cren&#231;a perigosa. A vulnerabilidade <strong>n&#227;o mora na linguagem; mora no padr&#227;o</strong> &#8212; na fronteira onde dado n&#227;o confi&#225;vel vira instru&#231;&#227;o, decis&#227;o ou requisi&#231;&#227;o. Toda linguagem cruza essa fronteira. O que muda &#233; o <em>nome</em> da falha, n&#227;o a exist&#234;ncia dela.</p><ul><li><p><strong>Java / Spring.</strong> Deserializa&#231;&#227;o (a fam&#237;lia de gadgets &#233; vasta), SpEL/OGNL, e a exposi&#231;&#227;o gigantesca a depend&#234;ncias do ecossistema JVM &#8212; Log4Shell e Spring4Shell foram eventos de escala planet&#225;ria. Uma an&#225;lise da Veracode apontou que a maioria esmagadora das empresas com muitas aplica&#231;&#245;es usa Java, e uma fatia enorme rodava vers&#245;es vulner&#225;veis. N&#227;o porque programam mal; porque a superf&#237;cie &#233; imensa.</p></li><li><p><strong>.NET.</strong> &#8220;&#201; Microsoft, &#233; seguro.&#8221; A mesma fam&#237;lia existe com outros nomes: desserializa&#231;&#227;o perigosa (<code>BinaryFormatter</code>, <code>TypeNameHandling</code> permissivo no serializador JSON instanciando tipos do payload), abuso de <em>view state</em> quando a prote&#231;&#227;o de integridade est&#225; mal configurada, inje&#231;&#227;o de comando via <code>Process.Start</code>. Mesmo princ&#237;pio, sotaque diferente.</p></li><li><p><strong>Node.js.</strong> A aus&#234;ncia de tipagem forte abre vetores pr&#243;prios: <strong>prototype pollution</strong> &#8212; o atacante injeta propriedades no prot&#243;tipo base dos objetos e altera o comportamento da aplica&#231;&#227;o inteira, &#224;s vezes escalando para RCE; <code>eval</code> e o m&#243;dulo <code>vm</code> alimentados por input; inje&#231;&#227;o de comando via <code>child_process</code>. E o npm &#233; o maior tapete vermelho de <em>supply chain</em> que existe: milhares de depend&#234;ncias transitivas, e ataques reais de pacote malicioso injetado numa biblioteca popular.</p></li><li><p><strong>Go.</strong> Este &#233; o mais interessante porque carrega a fama de &#8220;seguro por design&#8221;. E &#233; verdade <em>em uma dimens&#227;o</em>: por ser <em>memory-safe</em>, Go elimina classes inteiras de vulnerabilidade de corrup&#231;&#227;o de mem&#243;ria (buffer overflow e afins) que assombram C/C++. Mas <strong>seguran&#231;a de mem&#243;ria n&#227;o &#233; seguran&#231;a de l&#243;gica</strong>. Em Go voc&#234; continua vulner&#225;vel a SSRF, path traversal, inje&#231;&#227;o de comando via <code>os/exec</code>, e a um cl&#225;ssico espec&#237;fico: usar <code>text/template</code> onde deveria usar <code>html/template</code>, abrindo inje&#231;&#227;o. E a cadeia de suprimentos morde igual &#8212; um m&#243;dulo comprometido &#233; um m&#243;dulo comprometido, n&#227;o importa a eleg&#226;ncia da linguagem.</p></li></ul><h2><strong><span>A conclus&#227;o que d&#243;i</span></strong></h2><p>Nenhuma linguagem te salva. Go reduz uma <em>fam&#237;lia</em> de bugs; n&#227;o reduz a fam&#237;lia que mais importa em pagamento, que &#233; a <strong>l&#243;gica de autoriza&#231;&#227;o e a confus&#227;o dado/instru&#231;&#227;o</strong>. Trocar de stack para &#8220;ficar seguro&#8221; &#233; como trocar de fechadura numa porta que ningu&#233;m tranca. O trabalho &#233; o mesmo em todas: identidade de carga de trabalho, entrada tratada como hostil, depend&#234;ncias vigiadas, sa&#237;da filtrada, privil&#233;gio m&#237;nimo.</p><h3><strong>Ent&#227;o o atacante precisa de um Kali Linux?</strong></h3><p>Essa pergunta merece uma resposta honesta porque a mitologia atrapalha a defesa. Kali Linux &#233; s&#243; uma distribui&#231;&#227;o Linux com um monte de ferramenta de seguran&#231;a pr&#233;-instalada. N&#227;o &#233; uma arma m&#225;gica; &#233; conveni&#234;ncia. As mesmas ferramentas rodam em qualquer sistema. O que importa entender &#233; a <strong>economia real do ataque em 2026</strong>, e ela &#233; bem menos glamurosa que o imagin&#225;rio do g&#234;nio de capuz:</p><ul><li><p><strong>A maior parte &#233; automatizada.</strong> Scanners de vulnerabilidade conhecida (os chamados <em>n-days</em>) varrem a internet inteira procurando servi&#231;os n&#227;o corrigidos. Quando um CVE cr&#237;tico sai, existe <em>proof of concept</em> p&#250;blico, em m&#233;dia, <strong>em duas semanas</strong> &#8212; e a explora&#231;&#227;o em massa come&#231;a antes de a maioria das empresas ter corrigido. O atacante n&#227;o descobriu a falha; ele baixou o exploit e apontou para o seu IP.</p></li><li><p><strong>Descobrir o alvo &#233; trivial.</strong> Existem motores de busca que indexam dispositivos e servi&#231;os expostos na internet. O atacante pesquisa &#8220;servi&#231;os rodando a vers&#227;o X do framework Y&#8221; e recebe uma lista. O reconhecimento que antes levava semanas hoje &#233; uma query.</p></li><li><p><strong>A barreira de entrada despencou.</strong> Ferramentas de intercepta&#231;&#227;o e teste de API, frameworks de explora&#231;&#227;o e cat&#225;logos de <em>templates</em> de vulnerabilidade s&#227;o gratuitos, documentados e cheios de tutorial. E a IA generativa reduziu ainda mais o custo de adaptar um exploit ou escrever o script de sa&#237;da.</p></li></ul><p>Ou seja: o atacante <strong>n&#227;o precisa ser um especialista de sistemas</strong> &#8212; que era exatamente a sua intui&#231;&#227;o no come&#231;o deste t&#243;pico, e ela est&#225; certa pela raz&#227;o oposta &#224; que voc&#234; imaginava. Ele n&#227;o precisa ser g&#234;nio porque <strong>a genialidade foi commoditizada</strong>. O especialista descobre a falha uma vez e publica; mil operadores medianos a exploram em escala. O seu advers&#225;rio mais prov&#225;vel n&#227;o &#233; um <em>hacker</em> brilhante mirando voc&#234; especificamente. &#201; um scanner automatizado que encontrou a sua depend&#234;ncia desatualizada &#224;s 3h da manh&#227;, no meio de uma varredura de milh&#245;es de IPs, e um humano que, ao ver o alvo financeiro, resolveu olhar com aten&#231;&#227;o.</p><h3><strong>Fechando o circuito: por que isso &#233; problema seu, e n&#227;o do CISO</strong></h3><p>Junte as pe&#231;as e veja como um caminho &#8220;de fora para dentro&#8221; alcan&#231;a o mesmo lugar que a credencial comprada da C&amp;M alcan&#231;ou por dentro:</p><pre><code><span>// A corrente &#8212; cada elo &#233; uma decis&#227;o de engenharia</span>
1. app mobile revela endpoint de integra&#231;&#227;o (cliente n&#227;o &#233; confi&#225;vel)
2. endpoint roda depend&#234;ncia com CVE cr&#237;tico n&#227;o corrigido
3. requisi&#231;&#227;o forjada dispara RCE (dado vira instru&#231;&#227;o)
4. servidor abre conex&#227;o de sa&#237;da &#8212; ningu&#233;m filtra egress
5. atacante l&#234; identidade da carga de trabalho / material de chave
6. ambiente N&#195;O isolado &#8594; move lateral at&#233; o ponto de assinatura
7. agora ele emite mensagem V&#193;LIDA para o SPI
   &#8594; e voc&#234; voltou ao T&#243;pico 01: &#8220;n&#227;o houve invas&#227;o&#8221;, s&#243; uso leg&#237;timo
</code></pre><p>Repare que o passo 7 &#233; <strong>id&#234;ntico</strong> ao resultado do insider. Por isso os dois cap&#237;tulos deste artigo &#8212; o de dentro e o de fora &#8212; convergem: ambos terminam com <em>uma mensagem perfeitamente v&#225;lida sendo assinada e liquidada</em>. <strong>A diferen&#231;a &#233; s&#243; o caminho at&#233; a chave</strong>. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">E cada elo dessa corrente &#8212; do 1 ao 6 &#8212; &#233; algo que </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">voc&#234;</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">, arquiteto ou programador, tem o poder de cortar: tratar o mobile como hostil, corrigir depend&#234;ncia com SLA de horas, procurar os </mark><em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">sinks</mark></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> no code review, filtrar a sa&#237;da, dar identidade m&#237;nima &#224; carga de trabalho, isolar o ambiente Pix.</mark></p><p>O CISO define a pol&#237;tica. Quem quebra a corrente &#233; quem escreve a linha. &#201; por isso que &#8220;o que isso tem a ver comigo?&#8221; tem uma &#250;nica resposta honesta: <strong>tudo</strong>. A dist&#226;ncia entre uma vulnerabilidade num pacote e uma mensagem fraudulenta no SPI &#233; feita, elo por elo, das decis&#245;es que voc&#234; toma antes do <em>merge</em>.</p><h4><strong><span>Pense comigo</span></strong></h4><blockquote><p>Tr&#234;s perguntas concretas, para responder hoje: </p><p>(1) Voc&#234; tem invent&#225;rio das suas depend&#234;ncias &#8212; inclusive as <strong>transitivas</strong> &#8212; e um alerta que dispara quando uma delas ganha um CVE cr&#237;tico? Se um novo Log4Shell sair amanh&#227;, quanto tempo at&#233; voc&#234; <em>saber</em> se est&#225; exposto? </p><p>(2) O seu ambiente de pagamento pode abrir conex&#227;o de sa&#237;da para qualquer lugar, ou s&#243; para uma lista expl&#237;cita? </p><p>(3) Se a sua aplica&#231;&#227;o for comprometida por RCE, a identidade que ela carrega alcan&#231;a a chave de assinatura e o SPI &#8212; ou o raio de dano para na borda do servi&#231;o?</p><p>Se voc&#234; n&#227;o gostou de nenhuma das tr&#234;s respostas, boas not&#237;cias: as tr&#234;s s&#227;o c&#243;digo e configura&#231;&#227;o. Nenhuma delas &#233; sorte.</p></blockquote><h2><strong><span>T&#243;pico 07: </span>Cada funcionalidade nova &#233; uma superf&#237;cie nova</strong></h2><p>O Pix n&#227;o &#233; um produto pronto. &#201; uma <strong>plataforma em agenda evolutiva permanente</strong>, e cada item dessa agenda muda o modelo de amea&#231;as de quem se integra a ele. O padr&#227;o hist&#243;rico &#233; implac&#225;vel: toda funcionalidade que remove atrito para o usu&#225;rio transfere a decis&#227;o para uma camada autom&#225;tica &#8212; e onde voc&#234; tira um humano, precisa colocar um controle.</p><p>Vamos olhar o que vem e onde, na minha leitura, mora o risco.</p><h3><strong>Pix Autom&#225;tico: o consentimento vira a nova credencial</strong></h3><p>Pagamentos recorrentes com <strong>autoriza&#231;&#227;o &#250;nica</strong> e d&#233;bitos subsequentes sem autentica&#231;&#227;o transa&#231;&#227;o a transa&#231;&#227;o. &#201; d&#233;bito autom&#225;tico democratizado &#8212; e &#233;, tecnicamente, a cria&#231;&#227;o de um <em>mandato de longa dura&#231;&#227;o</em>. J&#225; &#233; obrigat&#243;rio nos casos em que o recebedor de autoriza&#231;&#227;o de d&#233;bito interbanc&#225;rio &#233; pessoa jur&#237;dica ou entidade n&#227;o autorizada pelo BC, e a partir de outubro de 2026 alcan&#231;a contas sal&#225;rio.</p><p>O risco muda de lugar: deixa de ser &#8220;roubar uma transa&#231;&#227;o&#8221; e passa a ser <strong>&#8220;obter ou adulterar um mandato&#8221;</strong>. As perguntas de design que o seu time precisa responder antes de subir isso:</p><ul><li><p>Qual o escopo do consentimento &#8212; valor m&#225;ximo, periodicidade, prazo, contraparte fixa? Um mandato sem teto &#233; um cheque em branco recorrente.</p></li><li><p>Quem pode <strong>alterar</strong> um mandato existente, e isso exige nova autentica&#231;&#227;o forte? Alterar &#233; mais perigoso que criar.</p></li><li><p>Como se revoga, em quanto tempo, e o que acontece com a cobran&#231;a em voo?</p></li><li><p>Existe prote&#231;&#227;o contra <em>replay</em> de autoriza&#231;&#227;o e contra reuso de consentimento em contexto diferente do original?</p></li><li><p>O consentimento &#233; audit&#225;vel ponta a ponta &#8212; quem, quando, de qual dispositivo, com qual evid&#234;ncia?</p></li></ul><h3><strong>Pix por aproxima&#231;&#227;o e o modelo offline: a premissa s&#237;ncrona quebra</strong></h3><p>O Pix por aproxima&#231;&#227;o (NFC, via carteiras digitais) j&#225; est&#225; dispon&#237;vel. O que vem depois &#233; mais interessante do ponto de vista de risco: o <strong>QR pagador / Pix offline</strong>, previsto de 2027 em diante, em que o pagador consegue concluir a opera&#231;&#227;o <em>sem conex&#227;o com a internet</em> no momento.</p><p>Isso quebra a premissa que sustenta boa parte da defesa atual: a valida&#231;&#227;o s&#237;ncrona contra a infraestrutura antes da liquida&#231;&#227;o. Numa opera&#231;&#227;o offline, alguma coisa &#233; gerada e apresentada localmente, e a reconcilia&#231;&#227;o acontece depois. As perguntas ficam evidentes: como se previne duplicidade e <em>replay</em> de um token gerado no dispositivo? Qual a janela de validade? Quem assume o risco de saldo insuficiente entre a apresenta&#231;&#227;o e a liquida&#231;&#227;o? Como se detecta um dispositivo comprometido gerando tokens em s&#233;rie &#8212; lembrando que j&#225; existe malware brasileiro fazendo fraude no pr&#243;prio aparelho da v&#237;tima?</p><h3><strong>Pix Parcelado padronizado: pagamento vira cr&#233;dito</strong></h3><p>Quando o Bacen padronizar o Pix Parcelado, o instrumento deixa de ser s&#243; liquida&#231;&#227;o e passa a carregar <strong>origina&#231;&#227;o de cr&#233;dito</strong>. Isso importa muito para o modelo de amea&#231;as: entra fraude de origina&#231;&#227;o, entra KYC como superf&#237;cie de ataque, entra o golpe de engenharia social que convence a v&#237;tima a &#8220;parcelar&#8221; um valor que ela n&#227;o deve. Times que hoje tratam Pix como transporte v&#227;o precisar herdar controles de cr&#233;dito &#8212; an&#225;lise, al&#231;ada, pol&#237;tica, contesta&#231;&#227;o.</p><h3><strong>Pix em garantia: receb&#237;veis futuros como colateral</strong></h3><p>Usar receb&#237;veis futuros de Pix como garantia de empr&#233;stimo cria uma nova classe de objeto sens&#237;vel: <strong>o v&#237;nculo entre o fluxo futuro e o credor</strong>. Integridade e imutabilidade desse registro passam a ter valor econ&#244;mico direto, e todo novo intermedi&#225;rio na cadeia de registro e trava &#233; um novo elo &#8212; com tudo o que aprendemos sobre elos nos &#250;ltimos dois anos.</p><h3><strong>Pix Internacional: dois modelos de confian&#231;a se encontrando</strong></h3><p>A ambi&#231;&#227;o para 2027 &#233; interligar o Pix a sistemas de pagamentos instant&#226;neos de outros pa&#237;ses. Toda ponte entre esquemas com <strong>modelos de confian&#231;a, requisitos de identidade e regimes de san&#231;&#227;o diferentes</strong> &#233;, historicamente, o lugar onde a fraude se instala &#8212; porque a diferen&#231;a de premissas entre os dois lados vira ambiguidade, e ambiguidade vira explora&#231;&#227;o. Some a isso jurisdi&#231;&#227;o, preven&#231;&#227;o &#224; lavagem, listas restritivas e resolu&#231;&#227;o de disputa transfronteiri&#231;a.</p><h3><strong>Intermedi&#225;rios, split e duplicata: dinheiro sendo dividido dentro da mensagem</strong></h3><p>A agenda prev&#234; especifica&#231;&#245;es para identificar <strong>m&#250;ltiplos recebedores numa mesma transa&#231;&#227;o</strong> (o caso dos marketplaces), o <strong>split tribut&#225;rio</strong> separando automaticamente a parcela de tributos no momento do pagamento, a <strong>cobran&#231;a h&#237;brida</strong> com QR obrigat&#243;rio em boletos digitais e a <strong>duplicata liquidada via Pix</strong>.</p><p>Traduzindo para risco: l&#243;gica de divis&#227;o de valores passa a viver dentro da mensagem de pagamento. Todo lugar em que o sistema calcula percentuais, arredonda centavos e decide quem recebe o qu&#234; &#233; um lugar onde manipula&#231;&#227;o de um &#250;nico campo desvia dinheiro <em>sem parecer uma fraude</em> &#8212; parece um erro de c&#225;lculo. E erro de c&#225;lculo &#233; a fraude que menos dispara alarme.</p><h3><strong>O campo de descri&#231;&#227;o, que ningu&#233;m trata como entrada n&#227;o confi&#225;vel</strong></h3><p>O Bacen j&#225; sinalizou que criar&#225; regras para inibir o uso do campo &#8220;Descri&#231;&#227;o&#8221; do Pix para amea&#231;as e mensagens ofensivas. Do nosso lado, esse campo &#233; o que sempre foi: <strong>entrada controlada pelo atacante que trafega entre institui&#231;&#245;es e chega a interfaces de terceiros</strong>. Inje&#231;&#227;o, XSS armazenado em pain&#233;is internos, <em>phishing</em> por conte&#250;do, abuso de canal. Se o seu extrato renderiza descri&#231;&#227;o sem sanitiza&#231;&#227;o, voc&#234; tem um vetor.</p><h3><strong>IA nos dois lados do balc&#227;o</strong></h3><p>O Banco Central trabalha em ferramentas antifraude com intelig&#234;ncia artificial, indicador de probabilidade de fraude no Pix, alerta de golpe e melhorias no bloqueio cautelar. Do outro lado, deepfake j&#225; aparece em golpes de identidade e a automa&#231;&#227;o reduz o custo marginal do ataque a quase zero. A corrida vai ser entre modelos, e o lado que perder ser&#225; o que tiver <strong>pior telemetria</strong> &#8212; porque modelo bom com dado ruim &#233; chute com sotaque estat&#237;stico.</p><h4><strong><span>O gr&#225;fico que ningu&#233;m desenha</span></strong></h4><p>Coloque num mesmo eixo duas curvas: a velocidade com que a superf&#237;cie do Pix cresce (agenda evolutiva do Bacen, trimestre a trimestre) e a velocidade com que a maturidade de seguran&#231;a do seu time cresce (sprints com item de seguran&#231;a entregue). Se a primeira sobe mais r&#225;pido que a segunda, voc&#234; n&#227;o est&#225; estagnado &#8212; <strong>est&#225; regredindo em termos relativos</strong>, que &#233; o &#250;nico termo que importa quando existe um advers&#225;rio.</p><h4><strong><span>Pense comigo</span></strong></h4><blockquote><p>Quando o Pix Autom&#225;tico entrou no roadmap da sua empresa, quem escreveu o modelo de amea&#231;as do mandato de consentimento? Quem definiu escopo, revoga&#231;&#227;o, prote&#231;&#227;o contra replay e trilha de auditoria da autoriza&#231;&#227;o?</p><p>Se a resposta for &#8220;ningu&#233;m, a gente implementou a especifica&#231;&#227;o&#8221;, esse &#233; o pr&#243;ximo ticket. Especifica&#231;&#227;o define o que o sistema <em>deve</em> fazer. Modelo de amea&#231;as define o que ele <strong>n&#227;o pode permitir</strong> &#8212; e &#233; sempre a segunda lista que o atacante l&#234;.</p></blockquote><h2><strong>Para fechar: o ataque n&#227;o come&#231;ou num terminal</strong></h2><p>Volto ao bar no Jaragu&#225;, porque &#233; ali que est&#225; a li&#231;&#227;o.</p><p>O maior ataque cibern&#233;tico da hist&#243;ria do sistema financeiro brasileiro come&#231;ou com uma conversa de cal&#231;ada e R$ 5 mil em esp&#233;cie entregues por um motoboy. N&#227;o come&#231;ou com um <em>exploit</em>. Come&#231;ou com uma pergunta &#8212; &#8220;voc&#234; trabalha no TI?&#8221; &#8212; feita a uma pessoa cujo n&#237;vel de acesso tinha sido definido por conveni&#234;ncia.</p><p>Entre aquela conversa e as contas reserva no Banco Central havia uma dist&#226;ncia inteira de sistema: autentica&#231;&#227;o, autoriza&#231;&#227;o, segrega&#231;&#227;o, limites, integridade, trilha, detec&#231;&#227;o, conten&#231;&#227;o. Cada uma dessas camadas era uma chance de transformar R$ 800 milh&#245;es em um alerta de madrugada. Cada uma delas foi, em algum momento, um card que algu&#233;m priorizou ou despriorizou.</p><p>&#201; por isso que insisto: <strong>seguran&#231;a em pagamentos n&#227;o &#233; um departamento, &#233; uma propriedade emergente do jeito como o sistema foi constru&#237;do</strong>. Ela emerge de decis&#245;es pequenas, tomadas por gente como n&#243;s, em reuni&#245;es que ningu&#233;m lembra, sobre coisas que pareciam detalhes de implementa&#231;&#227;o.</p><p>Tr&#234;s perguntas para levar para a segunda-feira:</p><ol><li><p><strong>Qual &#233; o teto de dano</strong> de uma credencial leg&#237;tima comprometida no seu sistema, expresso em reais? Se voc&#234; n&#227;o sabe o n&#250;mero, esse &#233; o primeiro trabalho.</p></li><li><p><strong>Quanto tempo</strong> entre o primeiro comportamento an&#244;malo e o primeiro humano acionado? Se a resposta depende de um relat&#243;rio matinal, voc&#234; est&#225; fazendo arqueologia.</p></li><li><p><strong>Onde est&#225; o ponto &#250;nico</strong> em que um ator fecha o ciclo sozinho &#8212; originar, aprovar, liquidar? Ele existe. Ele sempre existe. A quest&#227;o &#233; se voc&#234; o encontrou antes.</p></li></ol><p>O Bacen fez a parte dele: transformou dano real em norma verific&#225;vel, e norma verific&#225;vel em prazo. Os criminosos fizeram a parte deles: profissionalizaram o recrutamento, mapearam a cadeia de fornecedores e descobriram que o elo mais fraco quase nunca &#233; apenas o banco. Mas sim humano.</p><p>Fico por aqui neste artigo e at&#233; o pr&#243;ximo! &#10084;&#65039;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h3><strong>Fontes e leituras</strong></h3><ul><li><p>Banco Central do Brasil &#8212; Resolu&#231;&#245;es BCB n&#186; 493, 494, 495, 496, 497, 498, 501, 505, 506, 507, 517, 518, 538 e 547; Resolu&#231;&#245;es CMN n&#186; 4.893, 5.251, 5.261 e 5.274; Instru&#231;&#245;es Normativas BCB n&#186; 589 e 661.</p></li><li><p>Banco Central do Brasil &#8212; 63&#170; Ata do Comit&#234; de Estabilidade Financeira (Comef), novembro de 2025, sobre depend&#234;ncia de terceiros e uso disseminado de APIs.</p></li><li><p>Finsiders Brasil &#8212; &#8220;Um ano ap&#243;s ataque &#224; C&amp;M: o que mudou na seguran&#231;a do setor financeiro?&#8221; e &#8220;BC alerta para uso disseminado de APIs no setor financeiro&#8221;.</p></li><li><p>Grant Thornton Brasil &#8212; an&#225;lise dos controles m&#237;nimos da Resolu&#231;&#227;o CMN n&#186; 5.274/2025 e da Resolu&#231;&#227;o BCB n&#186; 538/2025.</p></li><li><p>Evertec &#8212; comunicado &#224; SEC sobre o incidente no ambiente Pix da Sinqia (agosto de 2025).</p></li><li><p>CNN Brasil, Estad&#227;o/Isto&#201; Dinheiro, Seu Dinheiro, InfoMoney, Exame e Ag&#234;ncia Brasil &#8212; cobertura dos casos C&amp;M Software, Sinqia, Banco do Nordeste e Opera&#231;&#227;o Cofre Digital.</p></li><li><p>IBM Trusteer e Cleafy &#8212; an&#225;lises t&#233;cnicas do PixPirate e da gera&#231;&#227;o de trojans Android com capacidade ATS.</p></li><li><p>Kaspersky &#8212; relat&#243;rio sobre o trojan GoPix e substitui&#231;&#227;o de chaves Pix e c&#243;digos de boleto no navegador.</p></li><li><p>ACI Worldwide e GlobalData &#8212; Relat&#243;rio Scamscope, proje&#231;&#245;es de perdas com fraudes em pagamentos em tempo real.</p></li><li><p>OWASP &#8212; API Security Top 10 e Application Security Verification Standard (ASVS).</p></li></ul><p></p><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><span>As </span><strong>contas reservas Pix</strong><span> s&#227;o mantidas por institui&#231;&#245;es financeiras diretamente no Banco Central (BC). Seu prop&#243;sito principal &#233; a </span><strong>compensa&#231;&#227;o e liquida&#231;&#227;o de opera&#231;&#245;es</strong><span> dentro do Sistema de Pagamentos Brasileiro (SPB). Pense nelas como um grande &#8220;caixa&#8221; central onde os bancos depositam recursos para garantir que todas as transa&#231;&#245;es, incluindo aquelas feitas via Pix, sejam processadas de forma eficiente e segura.</span></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p><a href="https://aws.amazon.com/pt/vpc/">Virtual Private Cloud</a></p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><strong>De onde vem o HSM</strong></p><p>A analogia mais fiel &#233; a de um <strong>cart&#243;rio com um carimbo oficial trancado num cofre que n&#227;o abre</strong>. Voc&#234; nunca pega o carimbo emprestado: entrega o documento pela portinhola, algu&#233;m carimba l&#225; dentro e devolve o documento carimbado. Se um ladr&#227;o invade a sala, ele at&#233; consegue carimbar alguns documentos enquanto est&#225; l&#225; &#8212; mas <strong>n&#227;o leva o carimbo embora</strong> para carimbar o que quiser, para sempre. &#201; essa a diferen&#231;a entre uma chave em HSM e uma chave num arquivo: com o arquivo, o atacante copia o carimbo e vai embora; com o HSM, ele perde tudo no instante em que voc&#234; fecha a porta.</p><p>A origem &#233; apropriada &#224; hist&#243;ria deste artigo. O HSM nasceu em <strong>1972</strong>, das m&#227;os do engenheiro eg&#237;pcio-americano <strong>Mohamed Atalla</strong> &#8212; o mesmo que coinventou o transistor MOSFET, base de toda a eletr&#244;nica moderna. O aparelho, apelidado de <em>&#8220;Atalla Box&#8221;</em>, foi criado para um problema id&#234;ntico ao que discutimos aqui: proteger o segredo que move dinheiro. Ele cifrava as senhas (PINs) e as mensagens dos primeiros caixas eletr&#244;nicos, isolando as chaves do sistema operacional que as usava. Atalla ficou conhecido como &#8220;o pai do PIN&#8221;, e suas caixas chegaram a proteger a maioria das transa&#231;&#245;es de ATM do mundo. Meio s&#233;culo depois, o princ&#237;pio &#233; o mesmo &#8212; a chave nasce no hardware e nunca sai. O que mudou foi o tamanho do rombo quando algu&#233;m ignora isso.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>O acr&#244;nimo STRIDE (Spoofing, Tampering, Repudiation, Information Disclosure, Denial of Service (DoS) e Elevation of Privilege) &#233; uma abordagem sistem&#225;tica &#224; seguran&#231;a que ajuda as equipes de desenvolvimento a pensar como invasores para proteger seus sistemas antes que ocorram viola&#231;&#245;es.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>A principal diferen&#231;a entre SAST e SCA reside no foco: o SAST analisa o c&#243;digo-fonte de uma aplica&#231;&#227;o em busca de vulnerabilidades de seguran&#231;a no c&#243;digo propriet&#225;rio, enquanto o SCA (Software Composition Analysis) busca vulnerabilidades em bibliotecas de terceiros e componentes de c&#243;digo aberto. O SAST ajuda a identificar falhas de codifica&#231;&#227;o, enquanto o SCA visa os riscos provenientes de depend&#234;ncias, garantindo a seguran&#231;a tanto do c&#243;digo personalizado quanto do c&#243;digo externo.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-6" href="#footnote-anchor-6" class="footnote-number" contenteditable="false" target="_self">6</a><div class="footnote-content"><p><strong>Sink</strong> &#233; o ponto onde um dado que veio de fora &#233; consumido por uma opera&#231;&#227;o com poder de causar dano &#8212; executar c&#243;digo, avaliar express&#227;o, instanciar objeto, fazer requisi&#231;&#227;o, acessar arquivo ou montar consulta.</p></div></div>]]></content:encoded></item><item><title><![CDATA[Dívida Técnica: A Conta Que Você Já Está Pagando Parcelada]]></title><description><![CDATA[Cinco pilares para transformar risco t&#233;cnico em decis&#227;o de neg&#243;cio &#8212; com evid&#234;ncia, com honestidade intelectual e sem prometer o que voc&#234; n&#227;o pode entregar.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/divida-tecnica-a-conta-que-voce-ja</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/divida-tecnica-a-conta-que-voce-ja</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Thu, 20 Aug 2026 16:47:34 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fa6c63c6-2a5f-4784-86aa-fc72b5d7bab3_6000x4000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Voc&#234; j&#225; viveu essa cena. O time levanta que o motor de cobran&#231;a precisa ser reestruturado. Algu&#233;m monta um slide. A palavra &#8220;refatora&#231;&#227;o&#8221; aparece tr&#234;s vezes, &#8220;acoplamento&#8221; aparece duas, e h&#225; um diagrama com muitas setas. A reuni&#227;o termina com um &#8220;faz sentido, vamos avaliar no pr&#243;ximo trimestre&#8221; &#8212; que &#233; a forma corporativa de dizer n&#227;o sem ter que justificar.</p><p>A explica&#231;&#227;o confort&#225;vel para o que aconteceu &#233; que a diretoria n&#227;o entende de tecnologia. A explica&#231;&#227;o honesta &#233; mais desconfort&#225;vel: <strong>o pedido foi feito numa moeda que ningu&#233;m do outro lado da mesa sabe converter.</strong></p><p>O problema raramente &#233; indiferen&#231;a. Executivos aprovam investimentos dif&#237;ceis o tempo todo &#8212; trocam o core banc&#225;rio, compram empresa, aceitam tr&#234;s anos de payback. O que eles n&#227;o fazem &#233; aprovar algo que chega descrito em qualidade de c&#243;digo, cobertura de teste e framework depreciado, sem nenhuma tradu&#231;&#227;o para receita, custo, risco ou posi&#231;&#227;o competitiva. E h&#225; uma falha ainda mais comum: apresentar um caso gen&#233;rico e &#250;nico para todo mundo ao mesmo tempo, obrigando cada executivo a fazer a tradu&#231;&#227;o sozinho &#8212; o que, na pr&#225;tica, termina em decis&#227;o nenhuma.</p><p>A escala do problema n&#227;o &#233; anedota. Numa pesquisa da McKinsey com 50 CIOs de institui&#231;&#245;es financeiras e empresas de tecnologia com receita acima de US$ 1 bilh&#227;o, os entrevistados estimaram que a d&#237;vida t&#233;cnica representa entre 20% e 40% do valor de todo o parque tecnol&#243;gico antes da deprecia&#231;&#227;o, e relataram que de 10% a 20% do or&#231;amento nominalmente destinado a produtos novos acaba desviado para resolver problemas decorrentes dela. Do outro lado da r&#233;gua, o CISQ estimou o custo da m&#225; qualidade de software nos Estados Unidos em US$ 2,41 trilh&#245;es, com a d&#237;vida t&#233;cnica acumulada em torno de US$ 1,52 trilh&#227;o.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Pilar 01: Recupere o significado da met&#225;fora antes de us&#225;-la</strong></h2><p>Voc&#234; n&#227;o consegue precificar um conceito que cada pessoa na sala entende de um jeito. E &#8220;d&#237;vida t&#233;cnica&#8221; virou exatamente isso: um guarda-chuva onde cabe desde uma falha arquitetural que impede a empresa de lan&#231;ar produto at&#233; o desgosto de algu&#233;m com o nome de um pacote.</p><h3><strong>Li&#231;&#227;o 1.1 &#8594; A met&#225;fora nasceu num banco, e nasceu para falar com executivos</strong></h3><p>Ward Cunningham cunhou o termo em 1992, num relat&#243;rio de experi&#234;ncia apresentado na OOPSLA sobre o WyCash &#8212; um sistema de gest&#227;o de portf&#243;lio. Ele estava cercado de gente de finan&#231;as, precisava justificar tempo investido em melhorar c&#243;digo existente, e escolheu deliberadamente uma linguagem que aquela audi&#234;ncia j&#225; dominava.</p><p>Repare na ironia: <strong>a met&#225;fora foi inventada como ferramenta de comunica&#231;&#227;o com o C-level, dentro de uma empresa financeira.</strong> N&#243;s &#233; que a sequestramos e transformamos em jarg&#227;o interno de engenharia.</p><p>E h&#225; uma segunda perda, mais s&#233;ria. Numa entrevista de 2009, Cunningham voltou ao tema para dizer que a met&#225;fora nunca foi sobre c&#243;digo bagun&#231;ado, e sim sobre o custo de operar com um entendimento incompleto do dom&#237;nio. Michael Feathers formulou isso da maneira mais &#250;til que j&#225; vi: d&#237;vida t&#233;cnica &#233; a dist&#226;ncia acumulada entre o seu entendimento do problema e o entendimento que o sistema reflete. Voc&#234; come&#231;a codificando o que entendeu; depois aprende mais; se o c&#243;digo n&#227;o acompanha o aprendizado, voc&#234; passa a trope&#231;ar num descompasso conceitual toda vez que mexe ali.</p><p>Essa defini&#231;&#227;o &#233; infinitamente mais defens&#225;vel numa reuni&#227;o do que &#8220;o c&#243;digo est&#225; ruim&#8221;. Porque ela &#233; verific&#225;vel: <em>quantas vezes, no &#250;ltimo trimestre, uma regra de neg&#243;cio nova exigiu contornar o modelo em vez de us&#225;-lo?</em></p><h3><strong>Li&#231;&#227;o 1.2 &#8594; Classifique a d&#237;vida antes de cobr&#225;-la</strong></h3><p>Nem toda d&#237;vida tem a mesma origem, e a origem muda completamente quem deve responder por ela.</p><p>Steve McConnell, num artigo da IEEE Software de 2007, prop&#244;s a primeira taxonomia s&#233;ria: d&#237;vida n&#227;o intencional, produzida por desconhecimento ou processo falho; d&#237;vida de curto prazo, assumida deliberadamente por um ganho imediato; e d&#237;vida de longo prazo, acumulada pela evolu&#231;&#227;o natural do sistema. Foi ele tamb&#233;m quem tornou expl&#237;cito o par principal/juros.</p><p><a href="https://martinfowler.com/bliki/TechnicalDebtQuadrant.html">Martin Fowler foi al&#233;m em 2009 com o </a><strong><a href="https://martinfowler.com/bliki/TechnicalDebtQuadrant.html">quadrante de d&#237;vida t&#233;cnica</a></strong>, cruzando dois eixos:</p><pre><code><code>                    PRUDENTE              IMPRUDENTE
                &#9484;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9516;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9488;
   DELIBERADA   &#9474; "Entregamos agora,  &#9474; "N&#227;o temos tempo    &#9474;
                &#9474;  refatoramos no     &#9474;  para desenhar       &#9474;
                &#9474;  pr&#243;ximo ciclo"     &#9474;  isso direito"       &#9474;
                &#9500;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9532;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9508;
   INADVERTIDA  &#9474; "Agora entendemos   &#9474; "O que &#233; camada     &#9474;
                &#9474;  como deveria       &#9474;  de dom&#237;nio?"        &#9474;
                &#9474;  ter sido feito"    &#9474;                      &#9474;
                &#9492;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9524;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9496;</code></code></pre><p>Por que isso importa numa conversa com gest&#227;o? Porque muda o pedido. D&#237;vida <strong>deliberada e prudente</strong> &#233; uma promessa que a empresa fez a si mesma e agora precisa honrar &#8212; voc&#234; est&#225; cobrando uma decis&#227;o anterior, n&#227;o pedindo um favor. D&#237;vida <strong>inadvertida e prudente</strong> &#233; aprendizado do dom&#237;nio; &#233; o quadrante mais honesto e o mais f&#225;cil de defender, porque ningu&#233;m errou. J&#225; d&#237;vida <strong>imprudente</strong>, deliberada ou n&#227;o, &#233; um problema de processo ou de capacita&#231;&#227;o &#8212; e apresent&#225;-la como se fosse trade-off estrat&#233;gico &#233; desonesto e, pior, n&#227;o resolve, porque a mesma d&#237;vida vai voltar em seis meses pela mesma porta.</p><h3><strong>Li&#231;&#227;o 1.3 &#8594; Diga em voz alta qual d&#237;vida voc&#234; n&#227;o vai pagar</strong></h3><p>Este &#233; o movimento mais contraintuitivo do artigo e talvez o de maior retorno.</p><p>Quem chega no f&#243;rum de arquitetura pedindo para zerar a d&#237;vida est&#225; pedindo para n&#227;o ser levado a s&#233;rio &#8212; porque zerar d&#237;vida significaria alocar todo o recurso dispon&#237;vel em remedia&#231;&#227;o, sem construir nada que diferencie a empresa. A pr&#243;pria McKinsey &#233; expl&#237;cita ao dizer que o objetivo n&#227;o &#233; chegar a zero, e a orienta&#231;&#227;o pr&#225;tica &#233; que raramente compensa reduzir abaixo de algo em torno de 10%: parte da d&#237;vida simplesmente nunca vale o custo de pagar.</p><p>Ent&#227;o inverta. Leve uma lista de <strong>tr&#234;s itens que o seu time decidiu conscientemente n&#227;o corrigir</strong>, com a raz&#227;o de cada um: &#8220;esse m&#243;dulo &#233; feio mas n&#227;o &#233; tocado h&#225; dois anos e n&#227;o &#233; caminho cr&#237;tico&#8221;, &#8220;essa duplica&#231;&#227;o &#233; intencional para desacoplar dois times&#8221;, &#8220;esse servi&#231;o vai ser desligado em 2027, n&#227;o vamos investir nele&#8221;.</p><p><strong><span>O efeito pr&#225;tico</span></strong></p><p>No instante em que voc&#234; mostra o que decidiu <em>n&#227;o</em> fazer, o seu pedido deixa de ser prefer&#234;ncia est&#233;tica e passa a ser prioriza&#231;&#227;o. Voc&#234; para de ser o engenheiro que sempre quer reescrever tudo e vira a pessoa que j&#225; filtrou o ru&#237;do antes de ocupar o tempo da lideran&#231;a. Isso n&#227;o &#233; t&#233;cnica de ret&#243;rica &#8212; &#233; o subproduto natural de ter feito o trabalho de an&#225;lise de verdade.</p><p><strong><span>Pense comigo &#8212; antes de dizer a palavra &#8220;d&#237;vida&#8221;</span></strong></p><ul><li><p><strong>Isso atrapalha o neg&#243;cio ou atrapalha a mim?</strong> A pergunta honesta &#233;: se ningu&#233;m mais mexer nesse c&#243;digo nos pr&#243;ximos dois anos, alguma coisa acontece? Se a resposta &#233; n&#227;o, voc&#234; tem uma prefer&#234;ncia, n&#227;o uma d&#237;vida.</p></li><li><p><strong>Em que quadrante isso est&#225;?</strong> Se for deliberada e prudente, quem prometeu pagar? Quando? Est&#225; registrado em algum lugar, ou s&#243; na mem&#243;ria de quem estava na sala?</p></li><li><p><strong>Qual &#233; a dist&#226;ncia entre o sistema e o meu entendimento atual do dom&#237;nio?</strong> Conte as vezes em que uma regra nova precisou de gambiarra em vez de encaixar no modelo. Esse n&#250;mero &#233; a sua evid&#234;ncia.</p></li><li><p><strong>O que eu vou dizer que N&#195;O vamos corrigir?</strong> Se voc&#234; n&#227;o consegue nomear tr&#234;s itens, voc&#234; ainda n&#227;o priorizou &#8212; s&#243; listou inc&#244;modos.</p></li></ul><h4><strong><span>Pontos altos do Pilar 1</span></strong></h4><ol><li><p>A met&#225;fora nasceu para conversar com executivos, dentro de uma empresa financeira. N&#243;s a transformamos em jarg&#227;o de engenheiro.</p></li><li><p>D&#237;vida &#233; a dist&#226;ncia entre o sistema e o entendimento atual do dom&#237;nio &#8212; n&#227;o a feiura do c&#243;digo.</p></li><li><p>Levar uma lista do que voc&#234; decidiu <em>n&#227;o</em> corrigir transforma o pedido de prefer&#234;ncia em prioriza&#231;&#227;o.</p></li></ol><h2><strong>Pilar 02: Traduza para a moeda de quem decide</strong></h2><p>O diretor n&#227;o precisa entender o problema t&#233;cnico. Precisa entender a <strong>unidade de medida</strong>. E, felizmente, a unidade certa j&#225; existe e &#233; justamente a que ele usa todos os dias.</p><h3><strong>Li&#231;&#227;o 2.1 &#8594; Separe principal de juros &#8212; e apresente os dois</strong></h3><p>A refer&#234;ncia aqui &#233; o livro de Philippe Kruchten, Robert Nord e Ipek Ozkaya, <em>Managing Technical Debt: Reducing Friction in Software Development</em> (SEI Series, 2019), que dedica um cap&#237;tulo inteiro &#224; distin&#231;&#227;o entre principal e juros, custo e valor, d&#237;vida potencial e d&#237;vida efetiva.</p><p>A tradu&#231;&#227;o &#233; direta:</p><ul><li><p><strong>Principal</strong> &#8212; o custo de remediar. &#201; um n&#250;mero &#250;nico, em horas ou reais, com data.</p></li><li><p><strong>Juros</strong> &#8212; o arrasto recorrente que voc&#234; paga todo m&#234;s por <em>n&#227;o</em> ter remediado. &#201; um n&#250;mero por per&#237;odo.</p></li></ul><p>Quase todo engenheiro apresenta s&#243; o principal: &#8220;s&#227;o seis semanas de duas pessoas&#8221;. E &#233; exatamente por isso que o pedido &#233; arquivado &#8212; porque, isolado, o principal &#233; puro custo. Sem os juros ao lado, voc&#234; est&#225; pedindo para a empresa gastar seis semanas em troca de nada vis&#237;vel.</p><p>Com os juros, a conversa muda de natureza. Deixa de ser &#8220;quero gastar&#8221; e passa a ser &#8220;estamos gastando, s&#243; que parcelado e sem abater o saldo devedor&#8221;.</p><h3><strong>Li&#231;&#227;o 2.2 &#8594; Os juros s&#227;o invis&#237;veis por natureza &#8212; o seu trabalho &#233; dar rosto a eles</strong></h3><p>Juros de d&#237;vida t&#233;cnica n&#227;o aparecem em nenhuma linha de or&#231;amento. Eles se escondem dentro de custos que j&#225; foram normalizados pela organiza&#231;&#227;o: plant&#227;o, concilia&#231;&#227;o manual, retrabalho, incidente, prazo de entrega que ningu&#233;m mais questiona porque &#8220;sempre foi assim&#8221;.</p><p>Vamos a um caso concreto do nosso mundo. Motor de cobran&#231;a sem reprocessamento idempotente:</p><pre><code><code>PRINCIPAL
  6 semanas &#215; 2 engenheiros ................... R$  96.000

JUROS (por ano, hoje)
  Concilia&#231;&#227;o manual p&#243;s-falha
    4h/semana &#215; 52 &#215; R$ 120/h ................. R$  24.960
  Plant&#227;o acionado por reprocesso duplicado
    ~2 incidentes/m&#234;s &#215; 5h &#215; 2 pessoas ........ R$  28.800
  Retrabalho de atendimento (estorno indevido)
    ~35 casos/m&#234;s &#215; 40 min &#215; R$ 45/h .......... R$  12.600
  Janela de entrega travada
    3 iniciativas adiadas no ano (ver adiante)
  &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
  Subtotal mensur&#225;vel ......................... R$  66.360/ano</code></code></pre><p>Duas coisas acontecem quando voc&#234; apresenta assim. Primeiro, o principal deixa de ser caro: R$ 96 mil contra R$ 66 mil por ano &#233; <strong>payback em pouco mais de dezoito meses</strong>, e todo diretor sabe ler payback. Segundo &#8212; e mais importante &#8212; voc&#234; acabou de tornar vis&#237;vel um gasto que a empresa j&#225; vinha fazendo sem saber que fazia.</p><p><strong><span>A frase que funciona</span></strong></p><div class="callout-block" data-callout="true"><p>&#8220;A gente j&#225; pagou o principal dessa d&#237;vida uma vez e meia nos &#250;ltimos doze meses. S&#243; que parcelado, em plant&#227;o e concilia&#231;&#227;o manual &#8212; e sem abater um centavo do saldo devedor.&#8221;</p></div><p>N&#227;o &#233; ret&#243;rica. &#201; a descri&#231;&#227;o literal do que est&#225; acontecendo.</p><h3><strong>Li&#231;&#227;o 2.3 &#8594; Quantifique o custo de atraso &#8212; porque a intui&#231;&#227;o alheia sobre ele &#233; p&#233;ssima</strong></h3><p>Repare que na conta acima ficou uma linha em aberto: &#8220;3 iniciativas adiadas no ano&#8221;. Essa linha costuma ser a maior de todas, e quase ningu&#233;m a preenche.</p><p>O conceito &#233; o <strong>Custo de Atraso</strong>, formulado por Donald Reinertsen em <em>The Principles of Product Development Flow</em> (2009). Ele mede o impacto econ&#244;mico do tempo sobre a realiza&#231;&#227;o de valor: receita n&#227;o realizada, custo de oportunidade e riscos que se acumulam enquanto algo espera na fila. O conselho dele &#233; c&#233;lebre &#8212; se voc&#234; s&#243; puder quantificar uma coisa, que seja o custo de atraso.</p><p>E h&#225; dois dados no trabalho de Reinertsen que valem ouro numa reuni&#227;o:</p><ul><li><p>Cerca de 85% dos gestores de produto n&#227;o conhecem o pr&#243;prio custo de atraso.</p></li><li><p>Quando estimam por intui&#231;&#227;o, as estimativas divergem entre si numa propor&#231;&#227;o de at&#233; 50 para 1.</p></li></ul><p>Traduzindo: <strong>quem chega com esse n&#250;mero calculado tem uma vantagem informacional enorme sobre todo mundo que chega com sensa&#231;&#227;o</strong> &#8212; inclusive sobre pessoas hierarquicamente acima. N&#227;o porque seja mais inteligente, mas porque fez uma conta que os outros presumiram imposs&#237;vel.</p><p>O caso mais citado &#233; o da Maersk Line: uma funcionalidade que ficou 38 semanas parada em filas, com custo de atraso superior a US$ 200 mil por semana &#8212; perto de US$ 8 milh&#245;es em receita n&#227;o realizada por algo que ningu&#233;m percebia como custo, porque fila n&#227;o aparece em relat&#243;rio.</p><p>Para ordenar iniciativas concorrentes, Reinertsen prop&#245;e o <strong>CD3</strong>: custo de atraso dividido pela dura&#231;&#227;o. &#201; a r&#233;gua que responde &#224; pergunta que o seu gestor realmente tem &#8212; n&#227;o &#8220;isso &#233; importante?&#8221;, mas &#8220;isso &#233; mais importante do que as outras sete coisas que est&#227;o na minha mesa?&#8221;.</p><p><strong><span>Aplicando ao dom&#237;nio banc&#225;rio</span></strong></p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Uma integra&#231;&#227;o de Pix travada por um m&#234;s n&#227;o custa &#8220;um m&#234;s de time&#8221;. Custa a receita de tarifa n&#227;o capturada nesse m&#234;s, mais o custo de aquisi&#231;&#227;o dos clientes que abriram conta em outro lugar, mais o risco de multa se houver prazo regulat&#243;rio envolvido.</mark> Quando voc&#234; monta essa conta, descobre com frequ&#234;ncia que o custo de atraso &#233; uma ordem de grandeza maior do que o custo do trabalho &#8212; e essa &#233; a informa&#231;&#227;o que muda a decis&#227;o.</p><p><strong><span>Pense comigo &#8212; antes de apresentar um n&#250;mero</span></strong></p><ul><li><p><strong>Eu estou apresentando principal, juros, ou os dois?</strong> Se for s&#243; o principal, voc&#234; est&#225; pedindo custo puro. Sem os juros, a resposta racional do outro lado &#233; n&#227;o.</p></li><li><p><strong>Onde os juros est&#227;o escondidos hoje?</strong> Plant&#227;o, concilia&#231;&#227;o manual, retrabalho de atendimento, prazo inflado. Procure em or&#231;amentos que j&#225; existem e ningu&#233;m questiona.</p></li><li><p><strong>Quanto custa cada semana de atraso?</strong> Receita n&#227;o capturada, custo de oportunidade, exposi&#231;&#227;o regulat&#243;ria. Se voc&#234; nunca fez essa conta, fa&#231;a uma vez &#8212; o resultado costuma surpreender voc&#234; antes de surpreender o gestor.</p></li><li><p><strong>Isso &#233; mais importante do que as outras coisas na mesa dele?</strong> Essa &#233; a pergunta real. Responder a ela com CD3 &#233; mais honesto do que responder com convic&#231;&#227;o.</p></li></ul><h4><strong><span>Pontos altos do Pilar 2</span></strong></h4><ol><li><p>Principal e juros &#233; a estrutura mental que a &#225;rea financeira j&#225; domina. Use a linguagem dela em vez de inventar a sua.</p></li><li><p>Juros de d&#237;vida t&#233;cnica se pagam parcelados e invis&#237;veis &#8212; dar rosto a eles &#233; metade do trabalho de convencimento.</p></li><li><p>Quantifique o custo de atraso: a intui&#231;&#227;o alheia sobre ele erra por ordens de grandeza, e quem calculou tem vantagem informacional real.</p></li></ol><h2><strong>Pilar 03: Me&#231;a sem fingir precis&#227;o</strong></h2><p>Vamos para o pilar que separa o profissional maduro do vendedor interno. Porque o Pilar 2 tem um efeito colateral perigoso: quando voc&#234; aprende a produzir n&#250;meros, fica tentador produzi-los com uma confian&#231;a que os dados n&#227;o sustentam.</p><p>E diretor experiente detecta falsa certeza. Uma vez detectada, tudo que voc&#234; disser depois entra com desconto &#8212; inclusive o que era verdade.</p><h3><strong>Li&#231;&#227;o 3.1 &#8594; Medir &#233; reduzir incerteza, n&#227;o elimin&#225;-la</strong></h3><p>A refer&#234;ncia &#233; <a href="https://www.lesswrong.com/posts/ybYBCK9D7MZCcdArB/how-to-measure-anything">Douglas Hubbard, </a><em><a href="https://www.lesswrong.com/posts/ybYBCK9D7MZCcdArB/how-to-measure-anything">How to Measure Anything: Finding the Value of &#8220;Intangibles&#8221; </a>in Business</em>. A tese central liberta o engenheiro do impasse mais comum: &#8220;n&#227;o d&#225; para medir isso&#8221;.</p><p>Hubbard trata medi&#231;&#227;o como <strong>redu&#231;&#227;o de incerteza baseada em observa&#231;&#227;o</strong> &#8212; n&#227;o como obten&#231;&#227;o de certeza. E faz quest&#227;o de distinguir as duas coisas: reduzir incerteza &#233; poss&#237;vel, elimin&#225;-la n&#227;o &#233;, porque o mundo &#233; complexo demais. A consequ&#234;ncia pr&#225;tica &#233; enorme: mesmo uma redu&#231;&#227;o modesta de incerteza j&#225; tem valor econ&#244;mico, e organiza&#231;&#245;es deixam muito valor na mesa ao operar por intui&#231;&#227;o quando t&#233;cnicas simples dariam clareza suficiente para escolher melhor.</p><p>Ele tamb&#233;m &#233; direto sobre por que as pessoas acham que algo n&#227;o pode ser medido, e nenhuma das raz&#245;es tem a ver com o objeto: ou a defini&#231;&#227;o de medi&#231;&#227;o est&#225; mal compreendida, ou o objeto da medi&#231;&#227;o n&#227;o foi bem definido, ou os m&#233;todos dispon&#237;veis s&#227;o desconhecidos. Na esmagadora maioria dos casos em engenharia, o problema &#233; o segundo &#8212; &#8220;a d&#237;vida do motor de cobran&#231;a&#8221; n&#227;o &#233; mensur&#225;vel, mas &#8220;horas de plant&#227;o por m&#234;s atribu&#237;veis a reprocesso duplicado&#8221; &#233; trivial.</p><h3><strong>Li&#231;&#227;o 3.2 &#8594; Decomponha, depois fale em faixas de 90%</strong></h3><p>Duas t&#233;cnicas espec&#237;ficas, ambas do arsenal de Hubbard, e ambas aplic&#225;veis na pr&#243;xima segunda-feira.</p><p><strong>Decomposi&#231;&#227;o.</strong> S&#243; quebrar uma estimativa muito incerta em componentes menores j&#225; produz uma melhora substancial na estimativa &#8212; antes mesmo de coletar qualquer dado novo. Foi o que fizemos na tabela de juros do Pilar 2: ningu&#233;m sabe responder &#8220;quanto custa essa d&#237;vida&#8221;, mas todo mundo consegue estimar horas de plant&#227;o, n&#250;mero de incidentes e custo-hora.</p><p><strong>Intervalo de confian&#231;a de 90%.</strong> Em vez de um n&#250;mero, voc&#234; declara uma faixa que tem 90% de chance de conter o valor real. E h&#225; um teste simples para calibrar os limites: um intervalo de 90% implica 5% de chance de o valor real estar acima do teto e 5% abaixo do piso. Ent&#227;o pergunte-se se voc&#234; est&#225; 95% seguro de que o valor real fica abaixo do teto que escreveu. Se n&#227;o estiver, suba o teto. Fa&#231;a o mesmo com o piso.</p><pre><code><code>&#10007;  "Isso vai economizar R$ 480 mil por ano."</code></code></pre><pre><code><code>&#10003;  "Entre R$ 320 mil e R$ 700 mil por ano, com 90% de confian&#231;a.
    A maior fonte de incerteza &#233; o volume de incidentes &#8212;
    trabalhamos com 18 a 30 por trimestre; se ficar perto de 18,
    o retorno vai para a ponta de baixo da faixa."</code></code></pre><p>A segunda vers&#227;o parece mais fraca. N&#227;o &#233;. Ela &#233; <strong>a &#250;nica das duas que sobrevive a uma auditoria dos n&#250;meros daqui a um ano</strong> &#8212; e, portanto, a &#250;nica que constr&#243;i credibilidade em vez de gast&#225;-la.</p><p>H&#225; ainda um efeito que vale mencionar: pessoas n&#227;o s&#227;o naturalmente boas em produzir intervalos calibrados, mas essa &#233; uma habilidade trein&#225;vel, com melhora mensur&#225;vel por repeti&#231;&#227;o e feedback. Vale exercitar em time, comparando estimativas passadas com resultados reais.</p><h3><strong>Li&#231;&#227;o 3.3 &#8594; Use evid&#234;ncia de sistema, n&#227;o evid&#234;ncia de opini&#227;o &#8212; e pare de medir o que n&#227;o muda a decis&#227;o</strong></h3><p>N&#250;meros que voc&#234; produziu sozinho, sobre o seu pr&#243;prio pedido, t&#234;m um problema &#243;bvio de credibilidade. Por isso vale ancorar parte do argumento em m&#233;tricas que o time j&#225; coleta e que n&#227;o foram inventadas para essa reuni&#227;o.</p><p>As m&#233;tricas DORA &#8212; lead time para mudan&#231;a, frequ&#234;ncia de deploy, taxa de falha de mudan&#231;a e tempo de restaura&#231;&#227;o de servi&#231;o, popularizadas em <em>Accelerate</em>, de Nicole Forsgren, Jez Humble e Gene Kim &#8212; funcionam bem porque descrevem o comportamento do sistema ao longo do tempo, n&#227;o a sua opini&#227;o sobre ele. &#8220;A taxa de falha de mudan&#231;a nesse servi&#231;o &#233; 3&#215; a m&#233;dia dos demais, h&#225; seis trimestres&#8221; &#233; um fato observ&#225;vel.</p><p>Com uma ressalva que aumenta a sua credibilidade justamente por ser dita em voz alta: <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">DORA &#233; indicador de sistema e de time, n&#227;o instrumento de avalia&#231;&#227;o individual</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">. Se voc&#234; n&#227;o disser isso, algu&#233;m em algum momento vai usar o seu gr&#225;fico para avaliar pessoas, e o dano ser&#225; atribu&#237;do a voc&#234;.</mark></p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Por fim, o crit&#233;rio que economiza semanas de an&#225;lise, tamb&#233;m de Hubbard: </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">se a informa&#231;&#227;o n&#227;o tem influ&#234;ncia sobre a decis&#227;o, ela n&#227;o tem valor.</mark></strong> Antes de o time gastar duas sprints instrumentando algo, pergunte qual o limiar em que aquele n&#250;mero mudaria a escolha. Se n&#227;o existe limiar &#8212; se o &#8220;sim&#8221; ou &#8220;n&#227;o&#8221; ser&#225; o mesmo em qualquer resultado &#8212; a medi&#231;&#227;o &#233; curiosidade, n&#227;o evid&#234;ncia.</p><p><strong><span>Pense comigo &#8212; antes de dizer que tem certeza</span></strong></p><ul><li><p><strong>Eu consigo decompor isso em tr&#234;s coisas que eu de fato observo?</strong> Se n&#227;o consigo, o problema n&#227;o &#233; falta de dado &#8212; &#233; que a vari&#225;vel ainda n&#227;o foi definida direito.</p></li><li><p><strong>Estou 95% seguro de que o valor real fica abaixo do meu teto?</strong> Se hesitei, o teto est&#225; baixo demais. Suba antes que a realidade suba por mim.</p></li><li><p><strong>O que eu explicitamente n&#227;o sei aqui?</strong> Declarar isso primeiro tira do outro lado o trabalho de descobrir depois &#8212; e &#233; a diferen&#231;a entre ser corrigido e ser desacreditado.</p></li><li><p><strong>Se esse n&#250;mero der o contr&#225;rio do que eu espero, a minha recomenda&#231;&#227;o muda?</strong> Se n&#227;o muda, eu n&#227;o estou medindo. Estou procurando muni&#231;&#227;o.</p></li></ul><h4><strong><span>Pontos altos do Pilar 3</span></strong></h4><ol><li><p>Medir &#233; reduzir incerteza, n&#227;o elimin&#225;-la &#8212; e reduzir j&#225; &#233; suficiente para decidir melhor.</p></li><li><p>Fale em faixas de 90%. Ponto &#250;nico &#233; promessa disfar&#231;ada de an&#225;lise, e ela vence com juros.</p></li><li><p>Declarar o que voc&#234; n&#227;o sabe aumenta, n&#227;o diminui, o peso do que voc&#234; sabe.</p></li></ol><h2><strong>Pilar 04: Classifique a decis&#227;o antes de pedir a decis&#227;o</strong></h2><p>Grande parte da fric&#231;&#227;o entre engenharia e gest&#227;o vem de um erro de enquadramento: tratamos decis&#245;es muito diferentes com o mesmo protocolo. Coisas triviais viram reuni&#227;o de arquitetura; coisas irrevers&#237;veis s&#227;o resolvidas numa thread de Slack de quinze minutos.</p><h3><strong>Li&#231;&#227;o 4.1 &#8594; Porta de uma via ou porta de duas vias</strong></h3><p>A formula&#231;&#227;o mais &#250;til que o mercado produziu sobre isso est&#225; na carta de acionistas da Amazon. Decis&#245;es Tipo 1 s&#227;o portas de uma via: consequentes e irrevers&#237;veis ou quase, e devem ser tomadas com m&#233;todo, lentid&#227;o, delibera&#231;&#227;o e consulta. Decis&#245;es Tipo 2 s&#227;o portas de duas vias: revers&#237;veis, e podem &#8212; devem &#8212; ser tomadas rapidamente por indiv&#237;duos ou grupos pequenos.</p><p>O diagn&#243;stico que acompanha a ideia &#233; o que interessa: &#224; medida que organiza&#231;&#245;es crescem, elas tendem a aplicar o processo pesado de Tipo 1 &#224; maioria das decis&#245;es, inclusive &#224;s de Tipo 2. O resultado &#233; lentid&#227;o, avers&#227;o a risco irrefletida, experimenta&#231;&#227;o insuficiente e, por consequ&#234;ncia, menos inven&#231;&#227;o. <strong> </strong></p><p>Aplicando ao nosso of&#237;cio, com a nuance honesta: em software, quase nada &#233; porta de uma via de verdade, porque quase tudo pode ser mudado com trabalho suficiente. A pergunta pr&#225;tica n&#227;o &#233; &#8220;&#233; revers&#237;vel?&#8221;, e sim <strong>&#8220;&#233; revers&#237;vel a que custo?&#8221;</strong>.</p><pre><code><code>PORTA DE UMA VIA (ou quase)          PORTA DE DUAS VIAS
&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;        &#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;&#9472;
Modelo de dados do raz&#227;o             Biblioteca de cliente HTTP
Chave de parti&#231;&#227;o no DynamoDB        Estrat&#233;gia de cache
Provedor de core banking             Formato de log
Contrato de API p&#250;blica              Ferramenta de observabilidade
Esquema de chaves de idempot&#234;ncia    Framework de teste</code></code></pre><p>E aqui est&#225; o movimento que muda a reuni&#227;o: <strong>declare a classifica&#231;&#227;o antes de pedir a decis&#227;o</strong>. &#8220;Essa &#233; revers&#237;vel, sugiro decidirmos hoje e revisarmos em 60 dias&#8221; devolve poder ao gestor e elimina o teatro de aprova&#231;&#227;o. &#8220;Essa &#233; de uma via, custa aproximadamente X para reverter, e por isso trouxe tr&#234;s alternativas com as consequ&#234;ncias de cada uma&#8221; comunica seriedade sem drama.</p><h3><strong>Li&#231;&#227;o 4.2 &#8594; Instrumente a decis&#227;o: ADR, registro de d&#237;vida e pre-mortem</strong></h3><p>Tr&#234;s ferramentas concretas que transformam conversa de corredor em evid&#234;ncia audit&#225;vel.</p><p><strong><a href="https://martinfowler.com/bliki/ArchitectureDecisionRecord.html">ADR &#8212; Architecture Decision Record</a>.</strong> Formato proposto por <a href="https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions">Michael Nygard</a>: contexto, decis&#227;o, consequ&#234;ncias. O campo que quase todo mundo esquece &#233; o das alternativas descartadas e por qu&#234;. &#201; esse campo que protege voc&#234; da pergunta &#8220;por que ningu&#233;m pensou nisso?&#8221; dois anos depois, feita por algu&#233;m que n&#227;o estava na sala. Um ADR de meia p&#225;gina &#233; o registro mais barato de responsabilidade compartilhada que existe.</p><p><strong>Registro de d&#237;vida t&#233;cnica.</strong> Kruchten, Nord e Ozkaya recomendam manter um registro expl&#237;cito, com os itens de d&#237;vida armazenados nas mesmas ferramentas de trabalho do time e dentro de um &#250;nico backlog. A l&#243;gica &#233; simples e boa: d&#237;vida, assim como defeito e funcionalidade, representa trabalho a ser feito &#8212; ent&#227;o precisa ser vis&#237;vel e planej&#225;vel, n&#227;o viver numa planilha paralela que s&#243; engenheiro abre.</p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Isso resolve, de quebra, o problema da Li&#231;&#227;o 1.2: quando a d&#237;vida deliberada &#233; registrada no momento em que &#233; contra&#237;da, cobrar o pagamento depois deixa de ser opini&#227;o e passa a ser confer&#234;ncia.</mark></p><p><strong>Pre-mortem.</strong> T&#233;cnica de <a href="https://www.researchgate.net/publication/3229642_Performing_a_Project_Premortem">Gary Klein</a>: re&#250;na o time, declare que estamos um ano no futuro e o projeto fracassou, e pe&#231;a a aut&#243;psia. Funciona porque inverte o incentivo social &#8212; em vez de precisar ter coragem para levantar um risco na frente do patrocinador, a pessoa &#233; convidada a faz&#234;-lo. Numa cultura em que ningu&#233;m quer ser o pessimista da sala, isso muda o que chega &#224; mesa.</p><h3><strong>Li&#231;&#227;o 4.3 &#8594; Saber perder &#233; parte da t&#233;cnica</strong></h3><p>Voc&#234; vai perder discuss&#245;es. Vai apresentar a conta certa, com a faixa calibrada e o custo de atraso, e mesmo assim a decis&#227;o vai ser outra &#8212; por raz&#245;es que talvez nem lhe contem, porque nem toda informa&#231;&#227;o &#233; sua para ter.</p><p>O complemento indispens&#225;vel dos Tipos 1 e 2 &#233; o <strong>disagree and commit</strong>: seguir adiante mesmo sem consenso, especialmente quando a decis&#227;o &#233; revers&#237;vel e o custo de estar errado &#233; baixo. Mas a pr&#225;tica exige duas coisas que costumam ser omitidas:</p><ul><li><p><strong>Registrar a discord&#226;ncia sem sabotar a execu&#231;&#227;o.</strong> Um par&#225;grafo no ADR &#8212; &#8220;esta op&#231;&#227;o foi escolhida contra a recomenda&#231;&#227;o da engenharia, pelas raz&#245;es X; o risco identificado &#233; Y; sugerimos revisar em Z&#8221; &#8212; &#233; registro, n&#227;o &#233; passivo-agressividade. O que &#233; passivo-agressivo &#233; executar mal para provar o ponto.</p></li><li><p><strong>N&#227;o cobrar a fatura depois.</strong> Quando o risco se materializa, a tenta&#231;&#227;o de dizer &#8220;eu avisei&#8221; &#233; enorme e o retorno &#233; zero. O profissional que ganha autoridade de verdade &#233; o que aparece com a solu&#231;&#227;o e deixa o registro falar sozinho &#8212; ele est&#225; l&#225;, datado, e todo mundo sabe.</p></li></ul><p>Perder bem &#233; o que compra o direito de ser ouvido na pr&#243;xima. E vai haver pr&#243;xima.</p><p><strong><span>Pense comigo &#8212; antes de marcar a reuni&#227;o</span></strong></p><ul><li><p><strong>Essa porta &#233; de uma via ou de duas &#8212; e reverter custa quanto?</strong> Se for barata de reverter, talvez voc&#234; n&#227;o precise de aprova&#231;&#227;o nenhuma. Precise de um prazo para reavaliar.</p></li><li><p><strong>Estou pedindo decis&#227;o ou pedindo cobertura?</strong> S&#227;o coisas diferentes, e mistur&#225;-las &#233; o que faz reuni&#245;es de arquitetura durarem duas horas sem concluir nada.</p></li><li><p><strong>Se isso der errado em um ano, qual ser&#225; a explica&#231;&#227;o?</strong> Fa&#231;a o pre-mortem antes de a decis&#227;o sair, n&#227;o depois do incidente.</p></li><li><p><strong>Se eu perder essa, eu executo bem mesmo assim?</strong> Se a resposta honesta for &#8220;n&#227;o com vontade&#8221;, o problema deixou de ser t&#233;cnico.</p></li></ul><h4><strong><span>Pontos altos do Pilar 4</span></strong></h4><ol><li><p>Diga se a porta &#233; de uma via ou de duas antes de pedir aprova&#231;&#227;o &#8212; isso define o processo inteiro e devolve poder a quem decide.</p></li><li><p>Tratar decis&#227;o revers&#237;vel como irrevers&#237;vel &#233; a forma mais cara de parecer prudente.</p></li><li><p>ADR, registro de d&#237;vida e pre-mortem convertem mem&#243;ria de corredor em evid&#234;ncia datada.</p></li></ol><h2><strong>Pilar 05: Construa cr&#233;dito antes de precisar dele</strong></h2><p>Vamos ser expl&#237;citos sobre o que este pilar n&#227;o &#233;. N&#227;o &#233; sobre puxar conversa no cafezinho, n&#227;o &#233; sobre virar amigo do diretor, n&#227;o &#233; sobre pol&#237;tica de corredor. &#201; sobre uma coisa s&#243;, e ela &#233; mensur&#225;vel: <strong>hist&#243;rico de previs&#245;es cumpridas</strong>.</p><p>H&#225; uma leitura inc&#244;moda que sustenta o pilar inteiro: d&#237;vida t&#233;cnica n&#227;o &#233;, na origem, um problema t&#233;cnico. Ela se manifesta como rigidez no sistema, mas &#233; subproduto de problemas de processo e comunica&#231;&#227;o, com raiz na cultura da organiza&#231;&#227;o. Empresas muito siladas &#8212; onde engenharia, produto e design trabalham isolados &#8212; acumulam muito mais d&#237;vida, porque decis&#245;es de trade-off tomadas no c&#243;digo ficam a camadas de dist&#226;ncia da necessidade real do cliente. Onde falta confian&#231;a, sobra d&#237;vida.</p><h3><strong>Li&#231;&#227;o 5.1 &#8594; Um caso por interlocutor, n&#227;o um caso gen&#233;rico para todos</strong></h3><p>Este &#233; o erro mais caro e o mais f&#225;cil de corrigir. O mesmo problema t&#233;cnico precisa de argumentos diferentes conforme quem est&#225; do outro lado:</p><ul><li><p><strong>CFO / diretor financeiro</strong> &#8212; risco financeiro quantificado, payback, custo evitado. Fale em principal, juros e prazo de retorno.</p></li><li><p><strong>Diretor comercial / de receita</strong> &#8212; churn, capacidade do time de vendas continuar vendendo, prazo de entrega prometido a cliente.</p></li><li><p><strong>Produto</strong> &#8212; prote&#231;&#227;o do roadmap. O argumento vencedor &#233;: quantas entregas suas eu destravo, e quando.</p></li><li><p><strong>Risco e compliance</strong> &#8212; exposi&#231;&#227;o regulat&#243;ria, rastreabilidade, prazo de resposta a demanda de auditoria. Num banco, esse costuma ser o interlocutor mais poderoso e o menos procurado por engenheiros.</p></li></ul><p>Apresentar um &#250;nico caso gen&#233;rico para todos ao mesmo tempo obriga cada pessoa a traduzir sozinha para a pr&#243;pria realidade &#8212; e o resultado quase sempre &#233; decis&#227;o nenhuma. N&#227;o porque discordem, mas porque ningu&#233;m se sentiu diretamente endere&#231;ado.</p><h3><strong>Li&#231;&#227;o 5.2 &#8594; Pense em campanha, n&#227;o em reuni&#227;o &#250;nica &#8212; e volte com o n&#250;mero prometido</strong></h3><p>Ningu&#233;m aprova investimento estrutural na primeira exposi&#231;&#227;o ao tema. Vale pensar como publicit&#225;rio pensa: alcance e frequ&#234;ncia. Quantas pessoas o argumento atinge, e quantas vezes.</p><p>Na pr&#225;tica, isso significa que o tema aparece em tr&#234;s ou quatro contextos antes de virar pedido formal &#8212; uma men&#231;&#227;o no relat&#243;rio mensal, um dado no p&#243;s-mortem de um incidente, uma conversa de dez minutos com produto, um slide de contexto numa apresenta&#231;&#227;o sobre outra coisa. Quando o pedido formal chega, ele n&#227;o &#233; novidade para ningu&#233;m. &#201; a conclus&#227;o de algo que a organiza&#231;&#227;o j&#225; vinha percebendo.</p><p><strong><span>A parte que quase todo mundo pula</span></strong></p><p>Se voc&#234; pediu aprova&#231;&#227;o prometendo &#8220;reduzir em 40% o tempo de resposta a incidente de liquida&#231;&#227;o&#8221;, volte em 90 dias com <strong>esse n&#250;mero exato</strong>, na mesma linguagem, mesmo que ele tenha ficado em 22%.</p><p>Sem isso, a pr&#243;xima proposta parte do zero &#8212; e a organiza&#231;&#227;o aprende que investimento em engenharia &#233; um lugar para onde recursos v&#227;o e do qual nenhuma not&#237;cia volta. Ter entregado 22% e reportado honestamente constr&#243;i mais cr&#233;dito do que ter entregado 40% e nunca ter contado.</p><h3><strong>Li&#231;&#227;o 5.3 &#8594; N&#227;o use medo como alavanca &#8212; e reconhe&#231;a que parte da desconfian&#231;a &#233; merecida</strong></h3><p>O apelo catastrofista funciona uma vez. &#8220;Se n&#227;o fizermos isso, o sistema vai cair&#8221; produz aten&#231;&#227;o imediata na primeira ocorr&#234;ncia. Na segunda, voc&#234; &#233; o engenheiro que grita lobo. Na terceira, o risco real que voc&#234; trouxer vai ser descontado exatamente como os anteriores &#8212; e o custo desse desconto n&#227;o &#233; seu, &#233; da empresa.</p><p>Existe uma vers&#227;o mais honesta e mais eficaz: apresentar probabilidade e consequ&#234;ncia separadas. &#8220;A chance de isso causar indisponibilidade no pr&#243;ximo semestre &#233; baixa, algo entre 10% e 20%. Mas, se acontecer durante fechamento, o impacto &#233; alto e n&#227;o temos plano de conting&#234;ncia.&#8221; Isso &#233; informa&#231;&#227;o. O outro &#233; chantagem emocional com sotaque t&#233;cnico.</p><p>E cabe uma dose de honestidade coletiva para fechar o pilar. Parte da resist&#234;ncia que encontramos n&#227;o &#233; preconceito contra tecnologia &#8212; &#233; hist&#243;rico. N&#243;s j&#225; pedimos reescrita completa que n&#227;o entregou nada. J&#225; chamamos prefer&#234;ncia de arquiteto de necessidade de neg&#243;cio. J&#225; estimamos tr&#234;s meses para algo que levou onze e nunca voltamos para explicar por qu&#234;. Se formos honestos, temos uma parcela real de responsabilidade pela dificuldade que temos hoje em ser levados a s&#233;rio.</p><p>A boa not&#237;cia &#233; que cr&#233;dito se reconstr&#243;i exatamente do mesmo jeito que se perde: uma previs&#227;o por vez.</p><p><strong><span>Pense comigo &#8212; antes de pedir qualquer coisa</span></strong></p><ul><li><p><strong>Qual foi a &#250;ltima previs&#227;o que eu fiz para essa pessoa, e ela se cumpriu?</strong> Se voc&#234; n&#227;o lembra, ela lembra. Essa &#233; a sua linha de cr&#233;dito.</p></li><li><p><strong>Estou falando com o interlocutor certo, na moeda dele?</strong> Em banco, risco e compliance frequentemente t&#234;m mais poder de destravar or&#231;amento do que tecnologia &#8212; e quase nunca s&#227;o procurados.</p></li><li><p><strong>Esta &#233; a primeira vez que essa pessoa ouve falar disso?</strong> Se for, o pedido &#233; prematuro. Primeiro o tema precisa existir; depois ele vira decis&#227;o.</p></li><li><p><strong>Estou usando risco como informa&#231;&#227;o ou como press&#227;o?</strong> Se a frase precisa de tom dram&#225;tico para funcionar, o n&#250;mero por tr&#225;s dela provavelmente &#233; fraco.</p></li></ul><h4><strong><span>Pontos altos do Pilar 5</span></strong></h4><ol><li><p>Credibilidade &#233; ativo acumulado, n&#227;o argumento apresentado. Ela se constr&#243;i muito antes da reuni&#227;o em que voc&#234; vai precisar dela.</p></li><li><p>Um caso por interlocutor. O caso gen&#233;rico obriga cada um a traduzir sozinho, e o resultado &#233; decis&#227;o nenhuma.</p></li><li><p>Voltar com o n&#250;mero prometido &#8212; mesmo quando ele decepciona &#8212; vale mais do que superar a meta em sil&#234;ncio.</p></li></ol><h2><strong>O que muda na segunda-feira</strong></h2><p>Os cinco pilares n&#227;o s&#227;o cinco assuntos. S&#227;o uma sequ&#234;ncia &#250;nica, e ela cabe em cinco perguntas feitas na ordem certa, antes de abrir qualquer slide:</p><h4><strong>A linha de racioc&#237;nio, em cinco perguntas</strong></h4><ol><li><p><strong>O que &#233; essa d&#237;vida, na defini&#231;&#227;o precisa?</strong> Dist&#226;ncia entre o sistema e o entendimento atual do dom&#237;nio &#8212; n&#227;o est&#233;tica. E em que quadrante ela est&#225;.</p></li><li><p><strong>Qual &#233; o principal e quais s&#227;o os juros, em reais e por per&#237;odo?</strong> Incluindo o custo de atraso das iniciativas que ela trava.</p></li><li><p><strong>Qual &#233; a minha faixa de 90%, e o que eu explicitamente n&#227;o sei?</strong> Ponto &#250;nico &#233; promessa; faixa &#233; an&#225;lise.</p></li><li><p><strong>Essa porta &#233; de uma via ou de duas, e reverter custa quanto?</strong> Isso define quem decide e com que velocidade.</p></li><li><p><strong>Eu tenho cr&#233;dito para pedir isso &#8212; e vou voltar com qual n&#250;mero, em quanto tempo?</strong></p></li></ol><p>Repare no que essas perguntas t&#234;m em comum: nenhuma delas &#233; sobre convencer. Todas s&#227;o sobre <strong>fazer o trabalho de an&#225;lise antes de ocupar o tempo de outra pessoa</strong>. A persuas&#227;o, quando aparece, &#233; consequ&#234;ncia &#8212; n&#227;o m&#233;todo.</p><p>E vale terminar situando isso no momento que estamos vivendo. Se a IA de fato funciona como amplificador &#8212; acelerando quem tem fundamento e multiplicando o caos de quem n&#227;o tem &#8212;, ent&#227;o a capacidade de traduzir risco t&#233;cnico em decis&#227;o de neg&#243;cio deixou de ser um diferencial de senioridade. Virou requisito. Times v&#227;o produzir c&#243;digo mais r&#225;pido do que a organiza&#231;&#227;o consegue avaliar, e a pessoa capaz de dizer, com n&#250;mero e com faixa de confian&#231;a, <em>onde vale a pena desacelerar</em>, passa a ser a mais valiosa da sala.</p><p>Quem s&#243; souber descrever o problema em termos de engenharia vai continuar tendo o pedido arquivado. S&#243; que agora mais r&#225;pido.</p><p>A boa not&#237;cia &#8212; e &#233; uma not&#237;cia boa de verdade &#8212; &#233; que nada aqui exige talento especial. Exige uma planilha, uma conversa de minutos com quem entende do neg&#243;cio, e a disciplina de voltar em noventa dias com o resultado. &#201; trabalho, n&#227;o dom.</p><h4><strong>Para ir al&#233;m</strong></h4><ul><li><p><strong>Kruchten, Nord &amp; Ozkaya</strong> &#8212; <em>Managing Technical Debt: Reducing Friction in Software Development</em> (SEI Series, 2019). A refer&#234;ncia definitiva sobre principal, juros e registro de d&#237;vida.</p></li><li><p><strong>Donald Reinertsen</strong> &#8212; <em>The Principles of Product Development Flow</em> (2009). Custo de atraso e CD3.</p></li><li><p><strong>Douglas Hubbard</strong> &#8212; <em>How to Measure Anything</em>. Decomposi&#231;&#227;o, intervalos calibrados e valor da informa&#231;&#227;o.</p></li><li><p><strong>Forsgren, Humble &amp; Kim</strong> &#8212; <em><a href="https://ciandt.com/br/pt-br/article/4-key-metrics-quatro-metricas-chave-em-eficiencia-digital">Accelerate</a></em><a href="https://ciandt.com/br/pt-br/article/4-key-metrics-quatro-metricas-chave-em-eficiencia-digital">. As quatro m&#233;tricas e a evid&#234;ncia emp&#237;rica por tr&#225;s delas.</a></p></li><li><p><strong>Kim, Behr &amp; Spafford</strong> &#8212; <em>The Phoenix Project</em>. Romance sobre um l&#237;der de TI que aprende justamente a traduzir problema de tecnologia em termo de neg&#243;cio. Boa porta de entrada.</p></li><li><p><strong>Michael Feathers</strong> &#8212; <em><a href="https://michaelfeathers.silvrback.com/toward-a-galvanizing-definition-of-technical-debt">Toward a Galvanizing Definition of Technical Debt</a></em><a href="https://michaelfeathers.silvrback.com/toward-a-galvanizing-definition-of-technical-debt"> (ensaio). A defini&#231;&#227;o que salva a met&#225;fora.</a></p></li><li><p><strong>Martin Fowler</strong> &#8212; <em>TechnicalDebtQuadrant</em> (bliki) e <strong>Steve McConnell</strong> &#8212; <em>Managing Technical Debt</em> (IEEE Software, 2007).</p></li><li><p><strong>Relat&#243;rios</strong> &#8212; <a href="https://www.mckinsey.com/capabilities/tech-and-ai/our-insights/tech-debt-reclaiming-tech-equity">DORA 2025 (State of AI-assisted Software Development), CISQ 2022 (Cost of Poor Software Quality), McKinsey (Tech debt: Reclaiming tech equity).</a></p></li></ul><p></p><p></p>]]></content:encoded></item><item><title><![CDATA[O Que Ninguém Te Conta Sobre Integrar com o Pix]]></title><description><![CDATA[As tr&#234;s mensagens que governam o SPI, o estado que nenhuma delas resolve, e o que sobra para o seu c&#243;digo.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/o-que-ninguem-te-conta-sobre-integrar</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/o-que-ninguem-te-conta-sobre-integrar</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Wed, 19 Aug 2026 16:51:44 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/b56e132e-4251-4eea-87c6-a830a9ecc12b_4000x6000.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>S&#227;o 23h47 de um s&#225;bado. O cliente abre o app, faz um Pix de R$ 380 para o irm&#227;o, a tela gira, e aparece a mensagem mais odiada do sistema financeiro brasileiro: <strong>&#8220;Estamos processando sua transa&#231;&#227;o.&#8221;</strong></p><p>O saldo dele j&#225; foi debitado. O irm&#227;o n&#227;o recebeu nada. O suporte n&#227;o sabe dizer se o dinheiro saiu. E o time de plant&#227;o abre o dashboard e encontra uma linha no banco de dados com <code>status = 'PENDENTE'</code> e um <code>updated_at</code> de quarenta minutos atr&#225;s.</p><p>A pergunta que todo mundo faz nesse momento &#233; a errada. A pergunta errada &#233; <em>&#8220;qual foi o bug?&#8221;</em>. A pergunta certa &#233;: <strong>&#8220;o que o meu sistema sabe, o que o SPI sabe, e por que essas duas coisas divergiram?&#8221;</strong></p><p>Porque na esmagadora maioria das vezes n&#227;o h&#225; bug nenhum. H&#225; um sistema distribu&#237;do funcionando exatamente como especificado, e um c&#243;digo local que foi escrito assumindo garantias que o arranjo do Pix nunca ofereceu.</p><p>Este artigo &#233; sobre essas garantias &#8212; as que existem, as que n&#227;o existem, e o que o Banco Central exige que voc&#234; fa&#231;a no meio do caminho.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>Pense comigo: se o seu c&#243;digo n&#227;o respeita isso&#8230;</h3><p>Se o seu sistema representa uma transa&#231;&#227;o Pix como uma linha com um campo <code>status</code> que sofre <code>UPDATE</code>, voc&#234; j&#225; perdeu antes de come&#231;ar. N&#227;o porque seja feio, mas porque voc&#234; acaba de destruir a &#250;nica coisa que te salva numa auditoria ou num incidente: <strong>a capacidade de responder </strong><em><strong>quando</strong></em><strong> e </strong><em><strong>por causa de qual mensagem</strong></em><strong> aquela transa&#231;&#227;o mudou de estado</strong>.</p><p>Um campo <code>status</code> &#233; uma fotografia. O que o Pix exige de voc&#234; &#233; um filme. E filme se guarda em log de transi&#231;&#245;es, n&#227;o em coluna sobrescrita.</p><h2>Assincronismo n&#227;o &#233; escolha de arquitetura &#8212; &#233; imposi&#231;&#227;o do arranjo</h2><p>Existe um mal-entendido confort&#225;vel que circula em muito time: o de que o Pix &#8220;&#233; ass&#237;ncrono porque o banco escolheu processar em fila&#8221;. N&#227;o &#233;. O assincronismo est&#225; na especifica&#231;&#227;o, n&#227;o na sua implementa&#231;&#227;o.</p><p>O fluxo interbanc&#225;rio funciona assim: o PSP do pagador envia uma ordem de pagamento ao SPI. Em algum momento &#8212; que pode ser em 200 milissegundos ou pode n&#227;o acontecer nunca &#8212; chega de volta uma <em>outra</em> mensagem, independente, com o veredito. N&#227;o existe um <code>HTTP 200</code> que signifique &#8220;liquidado&#8221;. N&#227;o existe <code>BEGIN TRANSACTION &#8230; COMMIT</code> atravessando tr&#234;s institui&#231;&#245;es e a rede do Banco Central. N&#227;o existe rollback.</p><p>Vale a analogia do correio registrado. Voc&#234; n&#227;o liga para o destinat&#225;rio e espera na linha at&#233; ele confirmar que recebeu. Voc&#234; posta a carta, recebe um comprovante de postagem, e dias depois chega um aviso de recebimento &#8212; <strong>ou n&#227;o chega</strong>. O comprovante de postagem prova que voc&#234; postou. N&#227;o prova que entregou. Confundir os dois &#233; a origem da maior parte dos incidentes graves de Pix.</p><p>H&#225; ainda um detalhe do cat&#225;logo que costuma pegar times desprevenidos: a granularidade da mensagem <strong>n&#227;o &#233;</strong> necessariamente a granularidade da transa&#231;&#227;o. Uma mesma mensagem de ordem de pagamento pode carregar m&#250;ltiplas opera&#231;&#245;es agregadas, e o mesmo vale para a mensagem de resposta. Qualquer modelo mental que assuma &#8220;uma mensagem = um pagamento&#8221; vai quebrar no primeiro cen&#225;rio de agrega&#231;&#227;o.</p><h3>Pense comigo: se o seu c&#243;digo n&#227;o respeita isso&#8230;</h3><p>Se voc&#234; escreveu um <code>service</code> que chama o gateway do SPI e faz <code>await</code> na resposta esperando o resultado final da liquida&#231;&#227;o, o seu c&#243;digo tem uma premissa embutida que a rede n&#227;o honra. O que vai acontecer, inevitavelmente:</p><ul><li><p>a <em>thread</em> vai estourar timeout antes do arranjo;</p></li><li><p>o seu <code>catch</code> vai capturar uma exce&#231;&#227;o de rede e traduzir como &#8220;falhou&#8221;;</p></li><li><p>o seu c&#243;digo vai estornar o d&#233;bito do cliente;</p></li><li><p>e tr&#234;s segundos depois o SPI vai confirmar a liquida&#231;&#227;o daquela transa&#231;&#227;o, que <em>realmente aconteceu</em>.</p></li></ul><p>Resultado: o cliente recebeu o dinheiro de volta <em>e</em> o benefici&#225;rio recebeu o valor. Voc&#234; acabou de criar dinheiro. Isso n&#227;o &#233; um bug de borda &#8212; &#233; a consequ&#234;ncia l&#243;gica e previs&#237;vel de tratar aus&#234;ncia de resposta como negativa de resposta.</p><p><strong>Regra que vale gravar:</strong> em sistema de pagamento, <em>timeout n&#227;o &#233; resultado</em>. Timeout &#233; aus&#234;ncia de informa&#231;&#227;o, e aus&#234;ncia de informa&#231;&#227;o exige consulta, n&#227;o decis&#227;o.</p><h2>As tr&#234;s <code>pacs</code>: o vocabul&#225;rio completo do Pix</h2><p>Aqui est&#225; o fato que quase todo conte&#250;do sobre Pix apresenta de forma difusa, e que na verdade &#233; enxuto a ponto de caber em um par&#225;grafo.</p><p>O SPI usa o padr&#227;o internacional ISO 20022, e a fam&#237;lia <code>pacs</code> &#8212; de <em>Payments Clearing and Settlement</em> &#8212; tem <strong>exatamente tr&#234;s mensagens</strong> no arranjo do Pix. Tr&#234;s. O pr&#243;prio SPI exp&#245;e um endpoint de cat&#225;logo que lista as vers&#245;es de mensagem vigentes, e na fam&#237;lia <code>pacs</code> aparecem <code>pacs.002</code>, <code>pacs.004</code> e <code>pacs.008</code>. Nada al&#233;m disso.</p><h3><code>pacs.008</code> &#8212; a inten&#231;&#227;o</h3><p><em>FIToFICustomerCreditTransfer.</em> &#201; a ordem de pagamento: o PSP do pagador declarando ao SPI que quer transferir um valor de uma conta para outra. Carrega o identificador fim a fim da transa&#231;&#227;o, os dados de pagador e recebedor, o valor, a finalidade, e &#8212; detalhe operacionalmente importante &#8212; um campo de prioridade de liquida&#231;&#227;o.</p><p>O que ela <strong>&#233;</strong>: uma declara&#231;&#227;o de inten&#231;&#227;o assinada digitalmente.<br>O que ela <strong>n&#227;o &#233;</strong>: prova de que o dinheiro saiu.</p><p>Um ponto que rende decis&#227;o de arquitetura: a prioridade preenchida nessa mensagem determina por qual canal de transmiss&#227;o a opera&#231;&#227;o vai trafegar, e <strong>essa escolha se propaga para todas as mensagens seguintes da mesma opera&#231;&#227;o</strong>. Ou seja, a decis&#227;o tomada no momento da inicia&#231;&#227;o amarra o comportamento do ciclo inteiro. N&#227;o &#233; um par&#226;metro cosm&#233;tico.</p><h3><code>pacs.002</code> &#8212; o veredito</h3><p><em>FIToFIPaymentStatusReport.</em> &#201; a mensagem que efetivamente muda o estado do mundo. Ela referencia a mensagem original e carrega o status: aceito e liquidado, ou rejeitado com um c&#243;digo de motivo padronizado.</p><p>&#201; a oper&#225;ria da rede, e por dois motivos que costumam confundir:</p><ol><li><p><strong>Ela aparece em mais de um fluxo.</strong> Responde &#224; ordem de pagamento, mas tamb&#233;m responde &#224; mensagem de devolu&#231;&#227;o. Se o seu <em>handler</em> assume que toda <code>pacs.002</code> se refere a um pagamento original, ele vai processar errado o retorno de uma devolu&#231;&#227;o.</p></li><li><p><strong>Os c&#243;digos de rejei&#231;&#227;o n&#227;o s&#227;o todos iguais.</strong> O padr&#227;o ISO define motivos de naturezas muito diferentes: conta encerrada, conta bloqueada, transa&#231;&#227;o proibida para aquele tipo de conta, liquida&#231;&#227;o abortada por timeout, transa&#231;&#227;o interrompida por timeout no agente do credor, agente do credor fora do ar.</p></li></ol><p>Colocar tudo isso num &#250;nico <code>if (status != OK) marcarComoFalha()</code> &#233; jogar fora a informa&#231;&#227;o mais cara que o arranjo te entrega. &#8220;Conta encerrada&#8221; &#233; erro de neg&#243;cio definitivo &#8212; voc&#234; informa o cliente e encerra. &#8220;Timeout no agente do credor&#8221; &#233; erro de infraestrutura de terceiro &#8212; pode ser retent&#225;vel, pode n&#227;o ser, e a decis&#227;o depende de voc&#234; saber se houve liquida&#231;&#227;o. S&#227;o dois mundos.</p><h3><code>pacs.004</code> &#8212; o arrependimento</h3><p><em>PaymentReturn.</em> &#201; a devolu&#231;&#227;o. Segundo o Cat&#225;logo de Servi&#231;os do SFN, quando o usu&#225;rio recebedor identifica que a transfer&#234;ncia foi feita de forma indevida e quer devolver o valor ao pagador, entram em cena a <code>pacs.004</code> e a <code>pacs.002</code> correspondente.</p><p>E aqui est&#225; a assimetria mais importante do arranjo inteiro, aquela que sustenta a tese deste artigo:</p><blockquote><p><strong>N&#227;o existe mensagem de cancelamento no Pix.</strong> Voc&#234; n&#227;o desfaz um pagamento. Voc&#234; faz outro, no sentido inverso, com v&#237;nculo ao original.</p></blockquote><p>Em vocabul&#225;rio de engenharia: o Pix n&#227;o tem <em>rollback</em>, tem <em>evento compensat&#243;rio</em>. Quem j&#225; trabalhou com Event Sourcing reconhece o padr&#227;o na hora &#8212; voc&#234; n&#227;o apaga o evento errado do log, voc&#234; emite um evento que o compensa, e ambos ficam registrados para sempre. O Bacen desenhou o arranjo assim de prop&#243;sito: em sistema financeiro, apagar hist&#243;ria &#233; pior do que registrar erro.</p><p>H&#225; ainda uma restri&#231;&#227;o operacional espec&#237;fica dessa mensagem. A devolu&#231;&#227;o <strong>sempre</strong> trafega pelo canal prim&#225;rio de transmiss&#227;o, mesmo tendo campo de prioridade &#8212; e as respostas decorrentes dela tamb&#233;m. Isto &#233;, mesmo que a opera&#231;&#227;o original tenha ido pelo canal secund&#225;rio, a devolu&#231;&#227;o n&#227;o vai. Se a sua camada de transporte roteia mensagem por prioridade de forma gen&#233;rica, ela vai violar essa regra sem avisar ningu&#233;m.</p><h3>As vizinhas que ningu&#233;m apresenta</h3><p>Falar s&#243; das tr&#234;s <code>pacs</code> d&#225; uma vis&#227;o incompleta do ciclo. Ao redor delas existe um conjunto que costuma ser implementado &#224;s pressas, e &#233; exatamente onde moram os incidentes silenciosos:</p><ul><li><p><code>admi.002</code><strong> &#8212; erro n&#227;o terminativo.</strong> Comunica&#231;&#227;o de erro que <em>n&#227;o encerra</em> a transa&#231;&#227;o. Se o seu c&#243;digo trata isso como rejei&#231;&#227;o, voc&#234; marca como morta uma transa&#231;&#227;o que ainda est&#225; viva. Analogia: o gar&#231;om voltou &#224; mesa dizendo que n&#227;o entendeu o pedido. Isso n&#227;o significa que a cozinha recusou o prato.</p></li><li><p><code>admi.004</code><strong> &#8212; avisos sobre a opera&#231;&#227;o do SPI.</strong> &#201; a mensagem que fala do sistema, n&#227;o da sua transa&#231;&#227;o.</p></li><li><p><strong>Fam&#237;lia </strong><code>camt</code><strong> &#8212; concilia&#231;&#227;o e conta.</strong> A <code>camt.060</code> vai do PSP para o SPI como requisi&#231;&#227;o, e a resposta vem em <code>camt.052</code>, <code>camt.053</code> ou <code>camt.054</code>, conforme a natureza do que se pediu. H&#225; ainda <code>camt.014</code> para avisos de altera&#231;&#227;o de dados cadastrais de participante e <code>camt.025</code> para resultado de grava&#231;&#227;o. Este &#233; o material bruto da sua concilia&#231;&#227;o &#8212; e, como veremos mais pra frente, &#233; tamb&#233;m o &#250;nico lugar de onde voc&#234; consegue descobrir o que realmente aconteceu com uma transa&#231;&#227;o.</p></li><li><p><strong>Fam&#237;lia </strong><code>REDA</code><strong> &#8212; gest&#227;o de participantes indiretos.</strong> <code>REDA.014</code> e <code>REDA.031</code> do PSP para o SPI, <code>REDA.016</code> e <code>REDA.017</code> no sentido inverso. Se voc&#234; opera como liquidante ou depende de um, esse &#233; o vocabul&#225;rio que descreve a sua rela&#231;&#227;o com ele.</p></li><li><p><code>pibr.001</code><strong> e </strong><code>pibr.002</code><strong> &#8212; teste de ECO.</strong> Mensagens propriet&#225;rias brasileiras, o <em>heartbeat</em> da sua conex&#227;o com a rede. Elas respondem &#224; pergunta &#8220;o canal est&#225; vivo?&#8221;, que &#233; diferente de &#8220;minha transa&#231;&#227;o foi processada?&#8221;.</p></li><li><p><strong>Fam&#237;lia </strong><code>LPI</code> &#8212; mensagens propriet&#225;rias de movimenta&#231;&#227;o de saldo da Conta PI.</p></li></ul><h3>E onde &#233; que a <code>pacs</code> <em>n&#227;o</em> aparece</h3><p>Ponto de confus&#227;o frequente, principalmente em quem entrou no Pix pela porta de um BaaS: <strong>as </strong><code>pacs</code><strong> s&#243; existem entre o participante e o SPI.</strong></p><p>O DICT &#8212; o diret&#243;rio de chaves &#8212; &#233; REST/JSON. A API Pix que voc&#234; usa para gerar cobran&#231;a, consultar um <code>pix</code> recebido ou receber webhook tamb&#233;m &#233; REST/JSON. N&#227;o h&#225; uma linha de ISO 20022 ali. Quem integra com um provedor nunca v&#234; uma <code>pacs</code> na vida: v&#234; o JSON que algu&#233;m traduziu por ele.</p><p>Isso significa que existe, obrigatoriamente, uma camada de tradu&#231;&#227;o entre o vocabul&#225;rio do arranjo e o vocabul&#225;rio do seu dom&#237;nio. Se essa camada n&#227;o for expl&#237;cita e isolada &#8212; uma <em>Anti-Corruption Layer</em> de manual &#8212;, ela existe assim mesmo, s&#243; que espalhada por dezenove classes do seu core.</p><h3>Pense comigo: se o seu c&#243;digo n&#227;o respeita isso&#8230;</h3><p>Suponha que o seu <em>handler</em> de mensagens tenha esta forma, que &#233; a forma mais comum que existe:</p><pre><code><code>if (mensagem.status == "ACSC") { confirmar(); }
else { falhar(); }</code></code></pre><p>Esse <code>else</code> est&#225; engolindo, no m&#237;nimo:</p><ul><li><p>rejei&#231;&#245;es de neg&#243;cio definitivas (conta encerrada) &#8212; que deveriam informar o cliente com mensagem espec&#237;fica;</p></li><li><p>rejei&#231;&#245;es de infraestrutura de terceiro (agente do credor fora do ar) &#8212; que podem ser reapresent&#225;veis;</p></li><li><p>abortos por timeout de liquida&#231;&#227;o &#8212; que exigem <strong>consulta</strong>, n&#227;o conclus&#227;o;</p></li><li><p>erros n&#227;o terminativos que nem sequer s&#227;o rejei&#231;&#227;o.</p></li></ul><p>Quatro tratamentos radicalmente diferentes, achatados em uma palavra. Toda vez que algu&#233;m pergunta &#8220;por que nosso Pix tem tanto estorno indevido?&#8221;, a resposta costuma estar nesse <code>else</code>.</p><h2>A m&#225;quina de estados que o arranjo exige de voc&#234;</h2><p>Agora podemos montar o modelo. Do lado do PSP pagador, o ciclo de vida m&#237;nimo honesto de uma transa&#231;&#227;o &#233;:</p><pre><code><code>INICIADA
   &#8594; VALIDADA
   &#8594; DEBITADA / SALDO_RESERVADO
   &#8594; ENVIADA_AO_SPI
   &#8594; { LIQUIDADA | REJEITADA | INDETERMINADA }
   &#8594; [ DEVOLVIDA ]
</code></code></pre><p>Tr&#234;s observa&#231;&#245;es que transformam isso de diagrama bonito em decis&#227;o de engenharia.</p><ol><li><p><strong>Os estados finais n&#227;o s&#227;o dois, s&#227;o tr&#234;s.</strong> Todo mundo modela sucesso e falha. Quase ningu&#233;m modela o terceiro, que &#233; o &#250;nico que realmente d&#243;i. Voltaremos a ele no pr&#243;ximo t&#243;pico.</p></li><li><p><code>LIQUIDADA</code><strong> n&#227;o &#233; estado terminal.</strong> Uma transa&#231;&#227;o liquidada pode receber devolu&#231;&#227;o depois &#8212; por erro operacional, por acordo entre as partes ou por mecanismo regulat&#243;rio. Quem modelou liquida&#231;&#227;o como estado absorvente vai precisar reabrir o modelo, e reabrir m&#225;quina de estados em produ&#231;&#227;o &#233; caro.</p></li><li><p><strong>Terceira: transi&#231;&#245;es precisam ser fatos registrados, n&#227;o muta&#231;&#245;es.</strong> Cada mudan&#231;a de estado deve guardar: o estado anterior, o estado novo, o identificador da mensagem que causou a mudan&#231;a, o timestamp do arranjo e o timestamp local. Sem isso voc&#234; n&#227;o consegue responder &#224; &#250;nica pergunta que importa numa apura&#231;&#227;o de incidente: <em>&#8220;em que instante n&#243;s soubemos disso, e por qu&#234;?&#8221;</em></p></li></ol><p>A analogia que costuma destravar a conversa com o time de produto &#233; a do livro-raz&#227;o cont&#225;bil. Contador nunca apaga lan&#231;amento errado &#8212; ele faz um lan&#231;amento de estorno. O livro cresce, nunca encolhe, e o saldo &#233; sempre derivado da soma dos lan&#231;amentos. Se contabilidade descobriu isso h&#225; seiscentos anos e o Bacen desenhou o Pix sobre esse mesmo princ&#237;pio, talvez o seu <code>UPDATE transacao SET status = 'OK'</code> mere&#231;a uma segunda olhada.</p><h3>Pense comigo:</h3><p>Imagine o cen&#225;rio real: chega uma <code>pacs.002</code> de confirma&#231;&#227;o e, dois segundos depois, chega uma segunda c&#243;pia da mesma mensagem &#8212; o que, como veremos mais pra frente, &#233; comportamento previsto do canal de comunica&#231;&#227;o, n&#227;o anomalia.</p><p>Com um campo <code>status</code> mut&#225;vel, as duas execu&#231;&#245;es s&#227;o indistingu&#237;veis. Com m&#225;quina de estados e transi&#231;&#245;es registradas, a segunda mensagem encontra uma transi&#231;&#227;o j&#225; existente para aquele identificador, &#233; reconhecida como reentrega e descartada. N&#227;o &#233; eleg&#226;ncia arquitetural &#8212; &#233; a diferen&#231;a entre creditar o cliente uma vez ou duas.</p><p>E o corol&#225;rio, que &#233; o cora&#231;&#227;o do t&#243;pico seguinte: <strong>a sua m&#225;quina de estados precisa ter uma transi&#231;&#227;o leg&#237;tima para &#8220;eu n&#227;o sei&#8221;</strong>. Se o &#250;nico caminho que sai de <code>ENVIADA_AO_SPI</code> vai para sucesso ou falha, voc&#234; for&#231;ou o sistema a mentir quando ele n&#227;o sabe. E sistema que mente sobre dinheiro cria preju&#237;zo cont&#225;bil.</p><h2>O estado indeterminado &#8212; o que fazer quando a resposta n&#227;o vem</h2><p>Este &#233; o t&#243;pico que separa quem j&#225; operou Pix em produ&#231;&#227;o de quem leu sobre Pix.</p><p>O SPI opera controle de timeout em dois pontos distintos do fluxo. O primeiro cobre o intervalo entre o PSP pagador receber a ordem do cliente e efetivamente envi&#225;-la ao SPI &#8212; e &#233; o pr&#243;prio PSP quem declara esse valor na mensagem. O segundo cobre o intervalo entre o SPI receber a ordem e a liquida&#231;&#227;o efetiva, o que <strong>inclui a valida&#231;&#227;o e o retorno por parte do PSP recebedor</strong>. Quando o controle foi ativado, em 2020, esses valores foram estabelecidos provisoriamente em cinco e dez minutos, respectivamente. Os n&#250;meros vigentes devem ser conferidos no cat&#225;logo atual antes de qualquer implementa&#231;&#227;o &#8212; o que n&#227;o muda &#233; a exist&#234;ncia dos dois controles e o que eles significam para voc&#234;.</p><p>Repare no escopo do segundo: ele engloba o comportamento de <em>outra institui&#231;&#227;o</em>. O seu rel&#243;gio de incerteza depende da disponibilidade de um terceiro sobre o qual voc&#234; n&#227;o tem controle, visibilidade nem SLA contratual. Essa &#233; a defini&#231;&#227;o operacional de depend&#234;ncia distribu&#237;da.</p><p>A consequ&#234;ncia &#233; esta: <strong>existe um intervalo de tempo real, mensur&#225;vel, em que a sua transa&#231;&#227;o j&#225; saiu do seu dom&#237;nio e voc&#234; n&#227;o sabe o desfecho.</strong> N&#227;o &#233; falha. &#201; o estado normal do sistema entre dois pontos.</p><h3>A idempot&#234;ncia n&#227;o &#233; conven&#231;&#227;o sua &#8212; &#233; norma, e tem janela</h3><p>O Cat&#225;logo de Servi&#231;os do SFN estabelece que tanto o ciclo b&#225;sico de transfer&#234;ncia quanto o de devolu&#231;&#227;o operam sob o princ&#237;pio da idempot&#234;ncia, com <strong>janela de 24 horas</strong>.</p><p>Leia de novo, porque isso muda a pol&#237;tica de retentativa do seu sistema inteiro. Reapresentar a mesma opera&#231;&#227;o, com o mesmo identificador, dentro da janela, &#233; comportamento previsto pela especifica&#231;&#227;o &#8212; n&#227;o &#233; gambiarra e n&#227;o gera duplicidade. Fora da janela, a mesma opera&#231;&#227;o passa a ser uma <em>transa&#231;&#227;o nova</em>, com efeito financeiro novo.</p><p>O que essa regra implica na pr&#225;tica:</p><ul><li><p>o identificador fim a fim &#233; a sua chave de idempot&#234;ncia. N&#227;o invente outra, n&#227;o use o seu ID interno como &#226;ncora com o arranjo;</p></li><li><p>ele precisa ser gerado <strong>antes</strong> do envio e persistido junto com a inten&#231;&#227;o. Gerar identificador no momento do envio significa que uma queda entre a persist&#234;ncia e o envio te deixa sem chave para consultar;</p></li><li><p>a sua pol&#237;tica de retentativa precisa conhecer o rel&#243;gio de 24 horas. Um <em>job</em> de reprocessamento que varre transa&#231;&#245;es antigas sem essa no&#231;&#227;o &#233; uma m&#225;quina de duplicar pagamento com pinta de rotina de manuten&#231;&#227;o.</p></li></ul><h3>Reentrega &#233; comportamento especificado, n&#227;o anomalia</h3><p>O guia de implementa&#231;&#227;o do canal de comunica&#231;&#227;o &#233; expl&#237;cito quanto a um ponto que muda a forma de escrever o consumidor: <strong>a aus&#234;ncia de confirma&#231;&#227;o de leitura no tempo adequado pode fazer com que a mesma mensagem seja disponibilizada novamente</strong>, onerando tanto os sistemas do Banco Central quanto os do pr&#243;prio participante. O guia tamb&#233;m trata do ritmo de leitura do endpoint de recep&#231;&#227;o, com tempo m&#225;ximo recomendado na ordem de 200 milissegundos no canal prim&#225;rio e intervalo maior no canal secund&#225;rio.</p><p>Traduzindo para o vocabul&#225;rio que usamos todo dia: <strong>a entrega &#233; </strong><em><strong>at-least-once</strong></em><strong>.</strong> Voc&#234; vai receber mensagem repetida. N&#227;o ocasionalmente, n&#227;o em cen&#225;rio de desastre &#8212; no funcionamento normal, sempre que a confirma&#231;&#227;o atrasar.</p><p>Quem trabalha com Kafka, SQS ou qualquer broker moderno j&#225; sabe onde isso vai dar: <em>exactly-once</em> no transporte &#233; fic&#231;&#227;o, e a &#250;nica defesa real &#233; processamento idempotente no consumidor. A diferen&#231;a &#233; que aqui o payload duplicado n&#227;o &#233; um evento de analytics. &#201; trezentos e oitenta reais.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/o-que-ninguem-te-conta-sobre-integrar?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/o-que-ninguem-te-conta-sobre-integrar?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p><h3>A pol&#237;tica que separa os times maduros dos demais</h3><p>Diante de uma transa&#231;&#227;o indeterminada, existem tr&#234;s condutas poss&#237;veis. Duas est&#227;o erradas:</p><ul><li><p><strong>Errado &#8212; concluir como falha e estornar o cliente.</strong> Se a liquida&#231;&#227;o ocorreu, voc&#234; criou dinheiro.</p></li><li><p><strong>Errado &#8212; reapresentar cegamente sem controle de janela.</strong> Fora das 24 horas, voc&#234; paga duas vezes.</p></li><li><p><strong>Certo &#8212; consultar.</strong> Manter a transa&#231;&#227;o em estado indeterminado expl&#237;cito, com o cliente informado com honestidade, e resolver por reconcilia&#231;&#227;o contra a fonte da verdade &#8212; que n&#227;o &#233; o seu banco de dados.</p></li></ul><h3>&#8220;Consultar&#8221; onde, exatamente?</h3><p>Essa &#233; a pergunta que separa o conselho bonito da engenharia de verdade. Falar &#8220;consulte o SPI&#8221; &#233; f&#225;cil at&#233; algu&#233;m pedir o endpoint. Ent&#227;o vamos ao que existe de fato.</p><p><strong>Primeiro fato, e ele reorganiza tudo: n&#227;o existe uma consulta de situa&#231;&#227;o de transa&#231;&#227;o.</strong> A fam&#237;lia <code>pacs</code> tem tr&#234;s mensagens &#8212; ordem, veredito e devolu&#231;&#227;o &#8212; e nenhuma delas serve para perguntar &#8220;qual o status do meu identificador fim a fim?&#8221;. N&#227;o h&#225; o equivalente a um <code>GET /transacao/{e2e}/status</code>. Quem procura por isso no cat&#225;logo n&#227;o vai achar, e isso n&#227;o &#233; omiss&#227;o: &#233; decorr&#234;ncia do desenho.</p><p>O que existe &#233; a fam&#237;lia <code>camt</code>, e o Cat&#225;logo de Servi&#231;os do SFN &#233; preciso quanto ao papel dela: s&#227;o mensagens que permitem <em>consultas diversas relacionadas &#224; Conta PI</em>. A <code>camt.060</code> &#233; a requisi&#231;&#227;o, enviada do PSP para o SPI, e pode ser respondida com <code>camt.052</code>, <code>camt.053</code> ou <code>camt.054</code>, a depender da natureza do que foi pedido. O roteiro de participa&#231;&#227;o direta no SPI detalha os tr&#234;s caminhos que o participante precisa demonstrar que sabe percorrer:</p><ul><li><p><code>camt.060</code> &#8594; <code>camt.053</code>: saldo de fechamento de um dia anterior;</p></li><li><p><code>camt.060</code> &#8594; <code>camt.054</code>: detalhes de um lan&#231;amento espec&#237;fico;</p></li><li><p><code>camt.060</code> &#8594; <code>camt.052</code>: indica&#231;&#227;o do arquivo que cont&#233;m a rela&#231;&#227;o de lan&#231;amentos, seguida do download desse arquivo.</p></li></ul><h4>A invers&#227;o mental que isso exige</h4><p>Voc&#234; n&#227;o pergunta ao SPI <em>&#8220;essa transa&#231;&#227;o liquidou?&#8221;</em>. Voc&#234; pergunta <em>&#8220;existe lan&#231;amento correspondente na minha Conta PI?&#8221;</em>.</p><p>A diferen&#231;a n&#227;o &#233; sem&#226;ntica, &#233; de modelo. No Pix, <strong>liquida&#231;&#227;o &#233; um fato cont&#225;bil, e o status da sua transa&#231;&#227;o &#233; uma propriedade </strong><em><strong>derivada</strong></em><strong> desse fato</strong> &#8212; n&#227;o o contr&#225;rio. A mensagem de veredito &#233; a notifica&#231;&#227;o do fato; o lan&#231;amento na Conta PI <em>&#233;</em> o fato. Quando as duas coisas divergem, o lan&#231;amento ganha, sempre.</p><p>A analogia do dia a dia: quando voc&#234; n&#227;o tem certeza se uma compra passou, voc&#234; n&#227;o liga para a loja perguntando o status do pedido. Voc&#234; abre o extrato e olha se saiu do seu saldo. O extrato n&#227;o &#233; a opini&#227;o de ningu&#233;m sobre o que aconteceu &#8212; &#233; o que aconteceu.</p><h4>E aqui vem a parte que quebra a arquitetura ing&#234;nua</h4><p><strong>A consulta tamb&#233;m &#233; ass&#237;ncrona.</strong> A <code>camt.060</code> &#233; uma mensagem postada no canal, n&#227;o uma chamada REST s&#237;ncrona. Voc&#234; envia a pergunta e a resposta chega depois, por outra mensagem, pelo mesmo endpoint de recep&#231;&#227;o que j&#225; entrega as suas <code>pacs</code>. E no caminho da rela&#231;&#227;o de lan&#231;amentos h&#225; ainda um passo adicional de <em>download</em> de arquivo.</p><p>A consequ&#234;ncia &#233; dura e vale escrever em negrito: <strong>n&#227;o existe nenhuma forma de resolver uma transa&#231;&#227;o indeterminada dentro do ciclo de requisi&#231;&#227;o do cliente.</strong> Nenhuma. Se o seu desenho previa &#8220;se der timeout, consulta o SPI e responde ao app&#8221;, esse desenho n&#227;o &#233; implement&#225;vel. A consulta &#233; um segundo fluxo ass&#237;ncrono, e o cliente vai precisar ser notificado depois.</p><h4>Os quatro recursos, em ordem de uso</h4><ol><li><p><strong>Esperar a reentrega.</strong> Antes de qualquer coisa, lembre que o canal reentrega mensagem n&#227;o confirmada. Uma fatia relevante dos &#8220;sumi&#231;os&#8221; &#233;, na verdade, consumidor que confirmou a leitura e falhou ao processar depois &#8212; o arranjo cumpriu a parte dele, voc&#234; &#233; que perdeu a mensagem. Confirmar leitura antes de persistir o fato &#233; um dos bugs mais caros e mais comuns dessa integra&#231;&#227;o.</p></li><li><p><strong>Consultar o lan&#231;amento</strong> via <code>camt.060</code>, com resposta em <code>camt.054</code>. &#201; a consulta cir&#250;rgica, para uma transa&#231;&#227;o espec&#237;fica.</p></li><li><p><strong>Varrer a rela&#231;&#227;o de lan&#231;amentos</strong> via <code>camt.060</code> &#8594; <code>camt.052</code> &#8594; download do arquivo. &#201; a varredura em lote, base da concilia&#231;&#227;o transacional. &#201; aqui que voc&#234; descobre transa&#231;&#245;es que existem no SPI e n&#227;o existem na sua base &#8212; as &#243;rf&#227;s, que a consulta cir&#250;rgica jamais encontraria, porque voc&#234; n&#227;o sabe que elas existem para perguntar por elas.</p></li><li><p><strong>O SPB-Web, m&#243;dulo SPI.</strong> Interface humana, com autentica&#231;&#227;o de segundo fator e consultas tarifadas conforme a tabela de servi&#231;os do Sisbacen. N&#227;o serve ao seu microsservi&#231;o &#8212; serve ao analista de plant&#227;o &#224;s 3h da manh&#227;. Mas precisa estar no <em>runbook</em>, com acesso provisionado <em>antes</em> do incidente, e n&#227;o durante.</p></li></ol><h4>O que isso significa em desenho de servi&#231;o</h4><p>Junte as pe&#231;as e o formato aparece sozinho: a reconcilia&#231;&#227;o n&#227;o &#233; um <code>retry</code> dentro do servi&#231;o de pagamento. &#201; <strong>um servi&#231;o pr&#243;prio</strong>, com responsabilidade &#250;nica, que:</p><ul><li><p>consome transa&#231;&#245;es que entraram em estado indeterminado e envelheceram al&#233;m do limite do arranjo;</p></li><li><p>emite <code>camt.060</code> respeitando o custo e o volume dessas consultas &#8212; consulta n&#227;o &#233; gr&#225;tis, nem em tarifa nem em capacidade;</p></li><li><p>correlaciona a resposta ass&#237;ncrona de volta ao identificador fim a fim original &#8212; o que exige guardar a correla&#231;&#227;o entre a sua requisi&#231;&#227;o e a transa&#231;&#227;o investigada, porque a resposta vai chegar desacoplada;</p></li><li><p>emite um evento de resolu&#231;&#227;o que reabre a m&#225;quina de estados naquele ponto exato;</p></li><li><p>e exp&#245;e como m&#233;trica de neg&#243;cio a quantidade e a idade das transa&#231;&#245;es n&#227;o resolvidas.</p></li></ul><p>Quem tenta encaixar isso dentro do servi&#231;o que processa pagamento acaba com um componente que faz duas coisas com perfis de lat&#234;ncia, criticidade e escala incompat&#237;veis. Separar n&#227;o &#233; purismo de arquitetura &#8212; &#233; a &#250;nica forma de a reconcilia&#231;&#227;o n&#227;o competir por recurso com o caminho cr&#237;tico justamente no pior momento, que &#233; quando as duas coisas acontecem juntas.</p><h3>Pense comigo: se o seu c&#243;digo n&#227;o respeita isso&#8230;</h3><p>H&#225; uma regra de dimensionamento que decorre de tudo isso e que quase nenhum time aplica conscientemente: <strong>os seus timeouts internos precisam ser menores que os do arranjo, nunca maiores.</strong></p><p>Se o seu <em>circuit breaker</em> abre depois do timeout do SPI, ele nunca protege nada &#8212; s&#243; te informa de um desastre que j&#225; aconteceu. Se o timeout do seu cliente HTTP interno &#233; maior que o do arranjo, voc&#234; segura <em>threads</em> esperando resposta de uma opera&#231;&#227;o que o SPI j&#225; abortou. Em hor&#225;rio de pico, isso n&#227;o degrada um servi&#231;o: derruba o pool de conex&#245;es e leva junto tudo o que compartilha aquele recurso.</p><p>Timeout, aqui, n&#227;o &#233; configura&#231;&#227;o de infraestrutura. &#201; decis&#227;o de arquitetura derivada de norma.</p><h2>O outro lado do balc&#227;o: onde fica a fronteira do irrevers&#237;vel</h2><p>Trocamos de cadeira. Agora a mensagem chega at&#233; voc&#234;.</p><p>Existe uma pergunta que aparece em toda discuss&#227;o de arquitetura de Pix recebido, e que eu quero desarmar logo de in&#237;cio, porque ela est&#225; mal formulada: <em>&#8220;a gente credita o cliente antes ou depois de responder ao SPI?&#8221;</em></p><p>Parece um dilema aberto, com dois lados defens&#225;veis. N&#227;o &#233;. O arranjo j&#225; respondeu essa pergunta por voc&#234;, e quem trata isso como escolha livre normalmente est&#225; prestes a descobrir o motivo da forma cara.</p><h3>Por que a ordem n&#227;o &#233; negoci&#225;vel</h3><p>O PSP recebedor tem duas obriga&#231;&#245;es simult&#226;neas: disponibilizar os recursos ao usu&#225;rio e responder ao arranjo. A segunda &#233; medida. O Manual de Tempos do Pix estabelece indicadores apurados por percentil, com apura&#231;&#227;o obrigat&#243;ria por todos os participantes &#8212; diretos e indiretos &#8212;, e esses indicadores comp&#245;em o &#205;ndice Geral de ANS. A ordem de grandeza dos tempos &#233; de poucos segundos: s&#243; para ilustrar o padr&#227;o de rigor, no ciclo de autoriza&#231;&#227;o do Pix Autom&#225;tico h&#225; indicador apurado a percentil de 95% com limite de tr&#234;s segundos.</p><p>Some a isso o &#237;ndice m&#237;nimo de disponibilidade de 99,9% previsto no arcabou&#231;o da Resolu&#231;&#227;o BCB n&#186; 195, e o quadro fica claro: o seu compromisso com a rede &#233; medido em segundos, na cauda da distribui&#231;&#227;o, e auditado.</p><p>Agora responda honestamente: <strong>o seu core banc&#225;rio consegue creditar uma conta dentro desse or&#231;amento de tempo, no percentil 99, &#224;s 20h de uma sexta-feira, durante um pico de volume, com o motor antifraude no caminho?</strong></p><p>Se a resposta &#233; &#8220;quase sempre&#8221;, voc&#234; acabou de amarrar um compromisso regulat&#243;rio medido por percentil &#224; parte mais lenta e mais compartilhada da sua infraestrutura. E percentil n&#227;o perdoa &#8220;quase sempre&#8221; &#8212; percentil &#233; exatamente a m&#233;trica desenhada para capturar o &#8220;quase&#8221;.</p><h3>O que o mercado convergiu a fazer</h3><p>O padr&#227;o dominante entre bancos e provedores de BaaS n&#227;o &#233; um meio-termo diplom&#225;tico. &#201; este, e &#233; bem definido:</p><ol><li><p><strong>Valida&#231;&#227;o s&#237;ncrona, r&#225;pida e determin&#237;stica.</strong> A conta existe? Est&#225; apta a receber? Est&#225; bloqueada? O tipo de conta aceita aquela opera&#231;&#227;o? As regras de antifraude que precisam ser s&#237;ncronas passaram? &#201; daqui que saem as rejei&#231;&#245;es leg&#237;timas, com o c&#243;digo de motivo espec&#237;fico que discutimos no anteriormente.</p></li><li><p><strong>Aceite ao arranjo assim que a valida&#231;&#227;o passa.</strong> Esse &#233; o ponto de n&#227;o retorno.</p></li><li><p><strong>Cr&#233;dito ao cliente como consequ&#234;ncia garantida</strong>, disparado por mecanismo dur&#225;vel &#8212; <em>outbox</em>, fila com garantia de entrega, o que a sua stack oferecer &#8212; e n&#227;o dentro do mesmo bloco transacional que processa a mensagem do arranjo.</p></li></ol><p>Repare no que isso significa: entre o aceite e o cr&#233;dito efetivo existe uma janela, curta mas real, em que a rede j&#225; considera o dinheiro entregue e o seu cliente ainda n&#227;o o v&#234;. Essa janela n&#227;o &#233; um defeito da implementa&#231;&#227;o. &#201; o pre&#231;o estrutural de cumprir um SLA externo com um recurso interno mais lento &#8212; e todo participante do Pix paga esse pre&#231;o, saiba ele disso ou n&#227;o.</p><h3>A pergunta certa, ent&#227;o, &#233; outra</h3><p>Se a ordem j&#225; est&#225; definida, o que sobra para voc&#234; decidir? Sobra a &#250;nica coisa que ainda importa: <strong>o que precisa estar verificado antes do aceite.</strong></p><p>Vamos com um exemplo bem simples. Duas coisas que o seu sistema faz quando um Pix chega:</p><ul><li><p>verificar se a conta do destinat&#225;rio existe;</p></li><li><p>enviar a notifica&#231;&#227;o no celular dele.</p></li></ul><p>A primeira pode dizer &#8220;n&#227;o&#8221;. Se a conta n&#227;o existe, a opera&#231;&#227;o n&#227;o acontece. A segunda nunca diz &#8220;n&#227;o&#8221; &#8212; ela s&#243; executa. Se a notifica&#231;&#227;o falhar, o dinheiro entrou do mesmo jeito.</p><p>&#201; essa a diferen&#231;a que organiza o fluxo inteiro:</p><blockquote><p><strong>O que pode recusar vem antes do aceite. O que apenas executa vem depois.</strong></p></blockquote><p>O motivo &#233; direto: aceitar &#233; prometer. No instante em que voc&#234; responde &#8220;aceito&#8221; ao arranjo, voc&#234; assumiu uma d&#237;vida com o cliente. Tudo que voc&#234; deixou para depois desse momento, voc&#234; j&#225; prometeu &#8212; e n&#227;o tem mais como voltar atr&#225;s.</p><p>Colocar uma regra que pode recusar <em>depois</em> do aceite cria uma situa&#231;&#227;o sem sa&#237;da. Voc&#234; j&#225; disse sim para a rede e vai precisar dizer n&#227;o para o cliente. A &#250;nica forma de sair disso &#233; devolver o dinheiro.</p><h4>Na pr&#225;tica: o que fica de cada lado</h4><p><strong>Antes do aceite</strong> ficam as verifica&#231;&#245;es que podem recusar. Elas precisam ser r&#225;pidas e ter resposta certa:</p><ul><li><p>a conta existe e est&#225; ativa;</p></li><li><p>a conta n&#227;o est&#225; bloqueada &#8212; por decis&#227;o judicial, administrativa ou cautelar;</p></li><li><p>o tipo de conta aceita aquela opera&#231;&#227;o;</p></li><li><p>a opera&#231;&#227;o n&#227;o estoura limite cadastral ou regulat&#243;rio do titular;</p></li><li><p>as checagens de conformidade que n&#227;o podem esperar, como listas restritivas.</p></li></ul><p>Repare que os c&#243;digos de rejei&#231;&#227;o do padr&#227;o separam &#8220;conta encerrada&#8221; de &#8220;conta bloqueada&#8221;. N&#227;o &#233; preciosismo: s&#227;o situa&#231;&#245;es diferentes, e o cliente do outro lado precisa ouvir coisas diferentes.</p><p><strong>Depois do aceite</strong> fica tudo que &#233; consequ&#234;ncia do dinheiro que j&#225; entrou:</p><ul><li><p>notifica&#231;&#227;o ao usu&#225;rio;</p></li><li><p>categoriza&#231;&#227;o do lan&#231;amento;</p></li><li><p>atualiza&#231;&#227;o de extrato e das telas de saldo;</p></li><li><p>contabilidade anal&#237;tica;</p></li><li><p>programa de pontos, atualiza&#231;&#227;o de limites, esteiras de an&#225;lise.</p></li></ul><p>Nenhuma dessas etapas pode recusar a opera&#231;&#227;o. Se alguma delas <em>puder</em>, ela est&#225; no lado errado &#8212; e o seu sistema tem um problema esperando o dia certo para aparecer.</p><h4>O teste de uma pergunta</h4><p>Para cada regra do seu fluxo, pergunte:</p><p><em>&#8220;Se essa verifica&#231;&#227;o falhar depois que eu j&#225; aceitei, o que eu fa&#231;o?&#8221;</em></p><p>Se a &#250;nica resposta for &#8220;devolver o dinheiro&#8221;, ela precisa ser s&#237;ncrona. Ponto.</p><p>A devolu&#231;&#227;o existe e &#233; leg&#237;tima. Mas ela &#233; um mecanismo de exce&#231;&#227;o. Usar devolu&#231;&#227;o como rotina, para compensar uma decis&#227;o de arquitetura, &#233; transformar exce&#231;&#227;o em processo &#8212; e isso sempre cobra a conta quando o volume cresce.</p><p>&#201; exatamente por isso que o antifraude s&#237;ncrono &#233; o assunto mais espinhoso desse fluxo. Ele &#233;, por defini&#231;&#227;o, uma regra que pode recusar. Ou ele cabe no tempo dispon&#237;vel, ou precisa ser redesenhado para atuar em outro momento, com outro mecanismo. N&#227;o existe uma terceira op&#231;&#227;o confort&#225;vel.</p><h3>O espelho do outro lado: o que fazer com o saldo de quem paga</h3><p>Agora gire a cadeira de volta. A mesma l&#243;gica existe do lado de quem envia &#8212; s&#243; que ali tem uma complica&#231;&#227;o a mais: <strong>o dinheiro est&#225; saindo da conta de algu&#233;m antes de existir qualquer certeza.</strong></p><p>Voltemos ao Jo&#227;o, que quer mandar R$ 380. Ele tem saldo, e voc&#234; j&#225; validou tudo. A pergunta &#233;: <strong>o que voc&#234; faz com esses R$ 380 no momento em que envia a ordem?</strong></p><p>Existem tr&#234;s respostas poss&#237;veis, e duas delas d&#227;o errado.</p><h4>Op&#231;&#227;o 1: n&#227;o mexer no saldo agora</h4><p>Voc&#234; envia a ordem e deixa o saldo do Jo&#227;o intacto at&#233; a confirma&#231;&#227;o chegar.</p><p>Problema: nos pr&#243;ximos segundos, nada impede que ele pague uma conta, fa&#231;a outro Pix ou passe o cart&#227;o. Quando a confirma&#231;&#227;o chegar, o dinheiro pode n&#227;o estar mais l&#225;. Voc&#234; mandou ao arranjo uma ordem sem lastro.</p><h4>Op&#231;&#227;o 2: debitar de vez</h4><p>Voc&#234; tira os R$ 380 da conta, como pagamento efetuado, e envia.</p><p>Problema: se a opera&#231;&#227;o for recusada, voc&#234; precisa fazer um estorno. Estorno &#233; um novo lan&#231;amento cont&#225;bil, com rastro, com possibilidade de falha e com custo operacional. Agora multiplique isso pelo n&#250;mero de rejei&#231;&#245;es leg&#237;timas que uma institui&#231;&#227;o recebe por dia.</p><h4>Op&#231;&#227;o 3: reservar o saldo &#8212; e &#233; essa que funciona</h4><p>Os R$ 380 saem da <em>disponibilidade</em> do Jo&#227;o, mas ainda n&#227;o viram pagamento. Ficam numa posi&#231;&#227;o intermedi&#225;ria: ele n&#227;o pode gastar, e voc&#234; n&#227;o contabilizou nada.</p><p>Depois, uma de duas coisas acontece:</p><ul><li><p>chegou a confirma&#231;&#227;o &#8594; a reserva vira pagamento efetivado;</p></li><li><p>chegou a recusa &#8594; a reserva &#233; liberada e o saldo volta a ficar dispon&#237;vel.</p></li></ul><p>Simples assim. E o ponto elegante: <strong>na recusa n&#227;o existe estorno.</strong> N&#227;o h&#225; lan&#231;amento de compensa&#231;&#227;o, n&#227;o h&#225; dinheiro que circulou e voltou. Contabilmente, aquele valor nunca chegou a sair da conta.</p><h4>Tr&#234;s cuidados com a reserva</h4><p><strong>1. Reservar e registrar precisam acontecer juntos.</strong> Mesma transa&#231;&#227;o de banco, mesma unidade de trabalho. Se voc&#234; reserva e n&#227;o registra, cria saldo preso sem dono. Se voc&#234; registra e n&#227;o reserva, envia ordem sem lastro. Os dois acontecem em produ&#231;&#227;o; o segundo custa dinheiro.</p><p><strong>2. A reserva vem antes do envio.</strong> A ordem correta &#233;: validar, reservar, gravar a inten&#231;&#227;o com o identificador fim a fim, e s&#243; ent&#227;o enviar. Qualquer invers&#227;o abre uma janela em que o arranjo conhece uma opera&#231;&#227;o que o seu sistema ainda n&#227;o conhece.</p><p><strong>3. Toda reserva tem dono e idade.</strong> Ela pertence a uma transa&#231;&#227;o identific&#225;vel e tem um rel&#243;gio rodando. Reserva &#243;rf&#227;, sem transa&#231;&#227;o correspondente, &#233; dinheiro do cliente preso por um bug silencioso. &#201; um dos achados mais constrangedores que uma concilia&#231;&#227;o pode produzir.</p><h4>Quando a recusa chega, a reserva se paga</h4><p>A institui&#231;&#227;o do outro lado respondeu: conta encerrada, conta bloqueada, tipo de conta incompat&#237;vel, agente indispon&#237;vel. O que voc&#234; faz?</p><p>Libera a reserva. S&#243; isso.</p><p>E note uma coisa: <strong>a recusa &#233; o melhor tipo de falha que existe.</strong> &#201; informa&#231;&#227;o clara, chegando em tempo, permitindo uma decis&#227;o certa. &#201; o cen&#225;rio que voc&#234; quer.</p><p>Aqui aquela classifica&#231;&#227;o de c&#243;digos de rejei&#231;&#227;o mostra o valor. &#8220;Essa conta n&#227;o existe mais, confira os dados&#8221; &#233; uma mensagem que o cliente consegue agir. &#8220;Erro ao processar&#8221; n&#227;o &#233;.</p><h4>E quando n&#227;o chega resposta nenhuma?</h4><p>Este &#233; o momento em que a reserva deixa de ser boa pr&#225;tica e vira prote&#231;&#227;o contra preju&#237;zo.</p><p>Voltamos ao estado indeterminado. A tenta&#231;&#227;o natural &#233; liberar a reserva, afinal &#8220;n&#227;o deu certo&#8221;. <strong>N&#227;o libere.</strong></p><p>Enquanto voc&#234; n&#227;o souber se houve liquida&#231;&#227;o, aquele valor precisa continuar indispon&#237;vel. A conta &#233; simples:</p><ul><li><p>cliente com saldo bloqueado por alguns minutos &#8594; uma reclama&#231;&#227;o;</p></li><li><p>cliente com saldo liberado numa transa&#231;&#227;o que liquidou &#8594; preju&#237;zo direto, sem contrapartida.</p></li></ul><p>Da&#237; sai uma regra que vale levar escrita para o refinamento:</p><blockquote><p><strong>Reserva s&#243; &#233; liberada por recusa expl&#237;cita ou por reconcilia&#231;&#227;o conclusiva.</strong> Nunca por timeout. Nunca por decis&#227;o local. Nunca por rotina de limpeza.</p></blockquote><p>Ali&#225;s, &#8220;job que limpa reservas antigas&#8221; &#233; uma das rotinas mais perigosas que existem em sistema de pagamento. Ela parece higiene. &#201; vazamento.</p><h4>O saldo que quase ningu&#233;m verifica</h4><p>Um &#250;ltimo detalhe, que pega at&#233; gente experiente: <strong>existem dois saldos nessa hist&#243;ria, n&#227;o um.</strong></p><p>Um &#233; o saldo do Jo&#227;o, no seu core banc&#225;rio. O outro &#233; o saldo da <em>sua institui&#231;&#227;o</em> na conta que ela mant&#233;m no Banco Central &#8212; e &#233; de l&#225; que a liquida&#231;&#227;o sai de fato.</p><p>S&#227;o sistemas diferentes, &#225;reas diferentes, controles diferentes. O Jo&#227;o pode ter saldo de sobra e a opera&#231;&#227;o ser recusada assim mesmo, porque o dinheiro que faltou n&#227;o era o dele.</p><p>Esse assunto &#233; grande o suficiente para merecer um texto pr&#243;prio, e &#233; um dos que ficaram para a sequ&#234;ncia. Por ora, guarde a consequ&#234;ncia pr&#225;tica: <strong>quando o seu Pix come&#231;ar a recusar em massa sem que nada tenha mudado no seu c&#243;digo, esse &#233; o segundo lugar para olhar.</strong></p><h3>E no BaaS, a assimetria &#233; ainda mais forte</h3><p>Vale um par&#225;grafo espec&#237;fico, porque &#233; onde mora o mal-entendido mais comum do mercado.</p><p>Quando voc&#234; &#233; cliente de um BaaS, <strong>o participante do arranjo &#233; o provedor, n&#227;o voc&#234;.</strong> Quem valida, quem aceita e quem responde ao SPI &#233; ele. O cr&#233;dito na conta do seu cliente final acontece depois, por outro trilho, sob a sua responsabilidade contratual.</p><p>Consequ&#234;ncia pr&#225;tica que derruba fintech: o <em>webhook</em> de Pix recebido <strong>n&#227;o &#233; uma solicita&#231;&#227;o de autoriza&#231;&#227;o</strong>. &#201; a notifica&#231;&#227;o de um fato j&#225; consumado por outra institui&#231;&#227;o. N&#227;o existe &#8220;recusar no webhook&#8221;. Times que desenham o fluxo assumindo que podem barrar um recebimento naquele ponto descobrem, geralmente em produ&#231;&#227;o, que a decis&#227;o foi tomada em uma camada &#224; qual eles n&#227;o t&#234;m acesso.</p><p>Isso n&#227;o &#233; limita&#231;&#227;o do provedor &#8212; &#233; o desenho do arranjo. E &#233; mais um caso em que a camada de tradu&#231;&#227;o entre o vocabul&#225;rio do Pix e o vocabul&#225;rio do seu dom&#237;nio esconde uma diferen&#231;a sem&#226;ntica profunda por tr&#225;s de um JSON de apar&#234;ncia inofensiva.</p><h3>Pense comigo: se o seu c&#243;digo n&#227;o respeita isso&#8230;</h3><p>Cen&#225;rio concreto, e eu recomendo lev&#225;-lo tal e qual para o pr&#243;ximo refinamento:</p><p><em>O aceite foi enviado ao SPI. O cr&#233;dito foi enfileirado. O core banc&#225;rio fica indispon&#237;vel por vinte minutos.</em></p><p>O que o seu sistema faz?</p><ul><li><p>Se a resposta &#233; <strong>&#8220;a fila segura e reprocessa quando o core voltar&#8221;</strong> &#8212; &#243;timo, &#233; exatamente para isso que ela existe. Mas confirme: a mensagem foi persistida antes da confirma&#231;&#227;o de leitura no canal? Ou voc&#234; confirmou primeiro e perdeu o fato?</p></li><li><p>Se a resposta &#233; <strong>&#8220;as retentativas estouram e a mensagem vai para a fila de erro&#8221;</strong> &#8212; voc&#234; tem um passivo silencioso. Algu&#233;m aceitou dinheiro em nome do cliente e o cliente nunca o recebeu. Isso n&#227;o gera alarme, n&#227;o gera exce&#231;&#227;o, n&#227;o aparece em nenhum <em>dashboard</em> de erro. S&#243; aparece na concilia&#231;&#227;o.</p></li><li><p>Se a resposta &#233; <strong>&#8220;n&#227;o sei&#8221;</strong> &#8212; essa &#233; a resposta mais comum e a mais honesta, e ela vale um item de backlog hoje.</p></li></ul><p>A pergunta operacional que fecha o bloco: <strong>quem, no seu sistema, &#233; o dono da decis&#227;o de cr&#233;dito?</strong> Se o cr&#233;dito acontece dentro do mesmo bloco que processa a mensagem do arranjo, sem separa&#231;&#227;o, voc&#234; amarrou o seu compromisso com o Bacen &#224; disponibilidade do seu core. Um <em>lock</em> em tabela de saldo, uma indisponibilidade moment&#226;nea, um <em>deploy</em> mal cronometrado &#8212; e o que era problema interno vira descumprimento de indicador perante o arranjo.</p><p>Aceitar &#233; irrevers&#237;vel. Creditar &#233; garantido. S&#227;o duas afirma&#231;&#245;es diferentes, e o seu c&#243;digo precisa saber disso.</p><h2>A verdade mora no extrato, n&#227;o na sua base</h2><p>Chegamos &#224; afirma&#231;&#227;o mais desconfort&#225;vel do artigo, e a que d&#225; t&#237;tulo &#224; tese:</p><blockquote><p>O seu banco de dados n&#227;o &#233; a fonte da verdade sobre o dinheiro. A Conta PI, no SPI, &#233;.</p></blockquote><p>O seu banco de dados &#233; uma <em>r&#233;plica local, eventualmente consistente, de um estado que vive no Banco Central</em>. Toda a engenharia de um participante do Pix &#233;, no fundo, um exerc&#237;cio cont&#237;nuo de converg&#234;ncia entre essas duas vis&#245;es.</p><p>&#201; por isso que o arranjo oferece mensagens espec&#237;ficas para isso: consulta de saldo da Conta PI e detalhamento de lan&#231;amentos via a fam&#237;lia <code>camt</code>. Elas n&#227;o s&#227;o acess&#243;rio. S&#227;o o instrumento pelo qual voc&#234; descobre o que realmente aconteceu.</p><h3>Duas concilia&#231;&#245;es, n&#227;o uma</h3><p>Times maduros mant&#234;m duas, e elas falham por motivos diferentes:</p><ul><li><p><strong>Concilia&#231;&#227;o transacional:</strong> cada identificador fim a fim que existe do meu lado existe do lado do SPI, com o mesmo desfecho? Detecta transa&#231;&#227;o &#243;rf&#227;, estado divergente, mensagem perdida. &#201; o que resolve o caso do cliente.</p></li><li><p><strong>Concilia&#231;&#227;o cont&#225;bil:</strong> o saldo da minha Conta PI bate com o meu raz&#227;o interno? Detecta erro sist&#234;mico, lan&#231;amento duplicado, valor divergente. &#201; o que resolve o problema do balan&#231;o.</p></li></ul><p>A primeira pode estar perfeita e a segunda quebrada &#8212; basta um lan&#231;amento interno com valor errado numa transa&#231;&#227;o que existe corretamente dos dois lados. E a segunda pode fechar com a primeira furada, se dois erros se compensarem. Quem roda s&#243; uma delas dorme tranquilo por motivo errado.</p><h3>Pense comigo: se o seu c&#243;digo n&#227;o respeita isso&#8230;</h3><p>Se a concilia&#231;&#227;o no seu sistema &#233; um <em>script</em> que algu&#233;m roda quando o financeiro reclama, ela n&#227;o &#233; concilia&#231;&#227;o &#8212; &#233; arqueologia.</p><p>Concilia&#231;&#227;o em sistema de pagamento instant&#226;neo precisa ser cidad&#227; de primeira classe: rodando continuamente, com m&#233;trica de diverg&#234;ncia exposta, com alerta quando o n&#250;mero de transa&#231;&#245;es indeterminadas cresce acima da linha de base. A diverg&#234;ncia entre o seu estado e o do SPI &#233;, ela pr&#243;pria, um <em>golden signal</em> &#8212; e provavelmente o mais importante que o seu produto tem.</p><p>O teste &#225;cido: se o SPI e a sua base discordarem agora, em quanto tempo voc&#234; fica sabendo? Se a resposta for &#8220;quando o cliente ligar&#8221;, voc&#234; tem um problema de arquitetura, n&#227;o de suporte.</p><h2>O que levar para o refinamento de amanh&#227;</h2><p>Vamos amarrar o racioc&#237;nio.</p><p>O Pix n&#227;o &#233; dif&#237;cil por causa do certificado digital ou da assinatura. Isso &#233; trabalho, e trabalho conhecido &#8212; qualquer time competente resolve em algumas <em>sprints</em>. O Pix &#233; dif&#237;cil por um motivo bem menos glamouroso e muito mais estrutural:</p><blockquote><p>Ele &#233; um sistema distribu&#237;do de alt&#237;ssima criticidade, com entrega <em>at-least-once</em>, sem transa&#231;&#227;o distribu&#237;da, sem <em>rollback</em>, com a fonte da verdade fora do seu dom&#237;nio e com o estado do mundo chegando at&#233; voc&#234; por mensagens que podem atrasar, duplicar ou n&#227;o chegar.</p></blockquote><p>Nenhuma dessas propriedades &#233; acidental. Todas est&#227;o na especifica&#231;&#227;o, e cada uma tem consequ&#234;ncia direta no seu c&#243;digo.</p><h3>O fio que costura tudo</h3><p>Vamos revistar o que j&#225; conversamos:</p><ul><li><p>N&#227;o existe resposta s&#237;ncrona &#8212; logo, o seu modelo precisa representar espera.</p></li><li><p>S&#243; existem tr&#234;s <code>pacs</code>, e nenhuma delas cancela nada &#8212; logo, o seu modelo precisa representar compensa&#231;&#227;o, n&#227;o desfazimento.</p></li><li><p>Erro tem naturezas diferentes, incluindo erro que n&#227;o encerra a transa&#231;&#227;o &#8212; logo, o seu modelo precisa representar mais do que sucesso e falha.</p></li><li><p>A resposta pode n&#227;o vir, e a consulta tamb&#233;m &#233; ass&#237;ncrona &#8212; logo, o seu modelo precisa representar o &#8220;eu n&#227;o sei&#8221; como estado leg&#237;timo, e resolv&#234;-lo em outro fluxo.</p></li><li><p>Aceitar e creditar s&#227;o compromissos distintos &#8212; logo, o seu modelo precisa representar a fronteira entre o que &#233; irrevers&#237;vel e o que &#233; apenas garantido.</p></li><li><p>A verdade mora na Conta PI, n&#227;o na sua base &#8212; logo, o seu modelo precisa representar a possibilidade de estar errado, e um processo cont&#237;nuo que o corrija.</p></li></ul><p>Um campo <code>status</code> n&#227;o carrega. Ele n&#227;o tem onde guardar &#8220;n&#227;o sei&#8221;, n&#227;o tem onde guardar &#8220;por qual mensagem&#8221;, n&#227;o tem onde guardar &#8220;quando&#8221;, e n&#227;o sobrevive &#224; pr&#243;xima norma que acrescentar um estado.</p><p><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">E vai acrescentar. Esse &#233; o ponto que fecha o argumento: este &#233; um dom&#237;nio que evolui por norma externa, em cad&#234;ncia que voc&#234; n&#227;o controla.</mark></strong> A agenda regulat&#243;ria dos pr&#243;ximos anos vai continuar inserindo estados no ciclo de vida da sua transa&#231;&#227;o, e a pergunta que decide o custo disso n&#227;o &#233; &#8220;quanto c&#243;digo vou escrever?&#8221;, e sim <strong>&#8220;acrescentar um estado no meu sistema &#233; uma tarefa de meio dia ou um projeto de um trimestre?&#8221;</strong>.</p><p>M&#225;quina de estados expl&#237;cita, com transi&#231;&#245;es declaradas e registradas, &#233; o que mant&#233;m essa resposta em &#8220;meio dia&#8221;. Dezenove <code>if</code> espalhados por doze classes &#233; o que a transforma em trimestre.</p><h3>A jornada invis&#237;vel &#8212; e por que algu&#233;m vai pedir para v&#234;-la</h3><p>Existe um argumento a favor da m&#225;quina de estados que raramente aparece em discuss&#227;o t&#233;cnica, porque ele n&#227;o &#233; sobre c&#243;digo. &#201; sobre <em>prova</em>.</p><p>Pense no que o usu&#225;rio final enxerga de uma transfer&#234;ncia: uma tela dizendo &#8220;enviando&#8221; e outra dizendo &#8220;conclu&#237;do&#8221;. Duas telas. Entre elas, aconteceram oito, dez, doze fatos distintos &#8212; valida&#231;&#227;o, reserva de saldo, envio ao arranjo, liquida&#231;&#227;o, notifica&#231;&#227;o, cr&#233;dito, confirma&#231;&#227;o &#8212; cada um em um instante diferente, cada um podendo falhar por um motivo diferente.</p><p>Essa &#233; a <strong>jornada invis&#237;vel</strong>. Ela existe quer voc&#234; a registre ou n&#227;o. A &#250;nica diferen&#231;a &#233; se ela fica recuper&#225;vel depois.</p><h4>Tr&#234;s pessoas v&#227;o bater na sua porta pedindo essa linha do tempo</h4><p><strong>O atendimento, hoje.</strong> O cliente ligou perguntando do dinheiro dele. A pergunta do atendente n&#227;o &#233; &#8220;deu erro?&#8221; &#8212; &#233; <em>&#8220;o que eu digo para ele agora?&#8221;</em>. E a resposta correta depende inteiramente de onde a transa&#231;&#227;o parou: &#8220;j&#225; saiu e o outro banco est&#225; processando&#8221; &#233; uma conversa completamente diferente de &#8220;n&#227;o chegamos a enviar&#8221;. Com um campo <code>status = 'PENDENTE'</code>, as duas situa&#231;&#245;es s&#227;o indistingu&#237;veis, e o atendente &#233; obrigado a improvisar. Improviso em conversa sobre dinheiro do cliente &#233; como se produz reclama&#231;&#227;o em ouvidoria.</p><p><strong>O engenheiro, no incidente.</strong> Duzentas transa&#231;&#245;es travadas. A pergunta &#250;til n&#227;o &#233; &#8220;quantas falharam?&#8221;, &#233; <em>&#8220;todas elas pararam no mesmo ponto?&#8221;</em>. Se pararam, voc&#234; tem uma causa comum e uma corre&#231;&#227;o. Se est&#227;o espalhadas por estados diferentes, voc&#234; tem outro problema, provavelmente mais grave. Sem transi&#231;&#245;es registradas, essa distin&#231;&#227;o &#8212; que &#233; a diferen&#231;a entre resolver em vinte minutos e resolver em seis horas &#8212; simplesmente n&#227;o &#233; obt&#237;vel.</p><p><strong>O auditor, meses depois.</strong> E esse &#233; o mais implac&#225;vel dos tr&#234;s, porque ele n&#227;o aceita &#8220;acho que&#8221;.</p><h4>Auditoria: o &#244;nus da prova &#233; seu</h4><p>Em sistema financeiro regulado, a assimetria &#233; essa: <strong>aus&#234;ncia de registro n&#227;o &#233; interpretada como aus&#234;ncia de problema, &#233; interpretada contra voc&#234;.</strong> &#8220;N&#227;o temos esse dado&#8221; e &#8220;fizemos errado&#8221; produzem, na pr&#225;tica, o mesmo desfecho.</p><p>E a demanda por essa prova n&#227;o &#233; hipot&#233;tica. Ela &#233; rotina do arranjo:</p><ul><li><p>Uma contesta&#231;&#227;o via mecanismo de devolu&#231;&#227;o exige demonstrar o que foi feito e quando &#8212; e, com o MED em sua vers&#227;o atual, com prazos e etapas que s&#227;o eles pr&#243;prios medidos.</p></li><li><p>Os indicadores do Manual de Tempos s&#227;o apurados <em>pelo participante</em> e reportados. Voc&#234; n&#227;o &#233; medido pelo que acha que levou: voc&#234; &#233; medido pelo que registrou. Sem marca&#231;&#227;o de tempo confi&#225;vel em cada etapa, voc&#234; n&#227;o tem sequer como calcular o pr&#243;prio indicador &#8212; muito menos defend&#234;-lo.</p></li><li><p>Uma apura&#231;&#227;o de fraude vai reconstituir o trajeto de um valor. A sua parte dessa reconstitui&#231;&#227;o &#233; a sua linha do tempo.</p></li></ul><p>H&#225; um detalhe t&#233;cnico aqui que o pr&#243;prio Manual de Tempos explicita e que vale internalizar: a diferen&#231;a entre um instante medido de um lado e o seu espelho medido do outro decorre tanto dos rel&#243;gios distintos dos dois agentes quanto da solu&#231;&#227;o tecnol&#243;gica adotada na comunica&#231;&#227;o, que determina os pontos de aferi&#231;&#227;o. Traduzindo: <strong>o rel&#243;gio do arranjo e o seu rel&#243;gio n&#227;o s&#227;o o mesmo rel&#243;gio</strong>, e a diverg&#234;ncia entre eles &#233; esperada e mensur&#225;vel.</p><p>Da&#237; a recomenda&#231;&#227;o de guardar <em>as duas</em> marca&#231;&#245;es de tempo em cada transi&#231;&#227;o &#8212; a do arranjo e a local. Uma serve para reconstruir a verdade externa; a outra, para reconstruir a sua. Quando divergirem, e v&#227;o divergir, voc&#234; quer estar com as duas na m&#227;o, e n&#227;o descobrindo naquele momento que s&#243; guardou uma.</p><h4>A analogia</h4><p>M&#225;quina de estados com transi&#231;&#245;es registradas &#233; o prontu&#225;rio m&#233;dico da transa&#231;&#227;o. Ningu&#233;m apaga uma entrada do prontu&#225;rio porque o diagn&#243;stico mudou &#8212; acrescenta-se uma nova entrada, datada, assinada, e a sequ&#234;ncia inteira permanece leg&#237;vel. &#201; essa sequ&#234;ncia que permite a outro profissional, meses depois, entender o que foi feito e por qu&#234;.</p><p>O campo <code>status</code> sobrescrito &#233; o oposto disso: &#233; um prontu&#225;rio com uma linha s&#243;, sempre atualizada, sempre no presente, sem mem&#243;ria. Serve para saber o estado atual do paciente. N&#227;o serve para absolutamente mais nada.</p><h4>O b&#244;nus que ningu&#233;m pede e todo mundo usa</h4><p>E h&#225; um efeito colateral generoso: uma vez que as transi&#231;&#245;es existem como fatos datados, voc&#234; ganha de gra&#231;a o que teria custado caro construir depois. A contagem de transa&#231;&#245;es em cada estado vira funil de neg&#243;cio. A idade m&#233;dia em cada estado vira alerta operacional. O crescimento da fila de indeterminadas vira o <em>golden signal</em> de que falamos anteriormente. A taxa de rejei&#231;&#227;o por c&#243;digo vira o term&#244;metro da sa&#250;de da sua integra&#231;&#227;o.</p><p>Nada disso precisa ser instrumentado separadamente. Tudo isso j&#225; est&#225; no log de transi&#231;&#245;es, esperando algu&#233;m consultar. &#201; a mesma estrutura respondendo, simultaneamente, ao suporte, ao plant&#227;o, ao produto e ao regulador &#8212; e essa &#233;, no fim, a melhor defesa que existe para o custo de implement&#225;-la direito.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/o-que-ninguem-te-conta-sobre-integrar/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/o-que-ninguem-te-conta-sobre-integrar/comments"><span>Deixe um coment&#225;rio</span></a></p><h3>O checklist</h3><p>O que eu levaria para o pr&#243;ximo refinamento, na ordem em que eu levaria:</p><ul><li><p><strong>Log de transi&#231;&#245;es de estado</strong>, n&#227;o campo <code>status</code> mut&#225;vel. Cada transi&#231;&#227;o guarda estado anterior, estado novo, a mensagem que a causou e <em>as duas</em> marca&#231;&#245;es de tempo &#8212; a do arranjo e a local. Os rel&#243;gios s&#227;o diferentes por natureza; guardar s&#243; um deles &#233; abrir m&#227;o da metade da prova.</p></li><li><p><strong>Identificador fim a fim gerado e persistido antes do envio</strong>, usado como chave de idempot&#234;ncia com o arranjo &#8212; nunca o seu ID interno.</p></li><li><p><strong>Pol&#237;tica de retentativa consciente da janela de 24 horas</strong> estabelecida pelo Cat&#225;logo. Dentro dela, reapresentar &#233; seguro; fora dela, &#233; pagamento novo.</p></li><li><p><strong>Consumidor idempotente por padr&#227;o.</strong> Reentrega de mensagem &#233; comportamento previsto do canal, n&#227;o anomalia.</p></li><li><p><strong>Confirmar leitura s&#243; depois de persistir o fato</strong>, nunca antes. Esse &#233; o bug mais barato de evitar e o mais caro de descobrir.</p></li><li><p><strong>Timeout interno estritamente menor que o do arranjo.</strong> <em>Circuit breaker</em> que abre depois do SPI n&#227;o protege nada.</p></li><li><p><strong>C&#243;digos de rejei&#231;&#227;o classificados por natureza de tratamento</strong> &#8212; neg&#243;cio definitivo, infraestrutura de terceiro, timeout, n&#227;o terminativo &#8212; e nunca achatados em um <code>else</code>.</p></li><li><p><strong>Estado indeterminado expl&#237;cito no modelo</strong>, resolvido por consulta &#224; Conta PI e jamais por suposi&#231;&#227;o.</p></li><li><p><strong>Reserva de saldo at&#244;mica com o registro da inten&#231;&#227;o</strong>, liberada apenas por rejei&#231;&#227;o expl&#237;cita ou reconcilia&#231;&#227;o conclusiva &#8212; nunca por timeout e nunca por <em>job</em> de limpeza.</p></li><li><p><strong>Reconciliador como servi&#231;o pr&#243;prio</strong>, ass&#237;ncrono, com correla&#231;&#227;o entre a requisi&#231;&#227;o e a transa&#231;&#227;o investigada.</p></li><li><p><strong>Fronteira do aceite documentada:</strong> tudo que pode gerar recusa &#233; s&#237;ncrono; tudo que &#233; execu&#231;&#227;o &#233; ass&#237;ncrono e garantido.</p></li><li><p><strong>Dupla concilia&#231;&#227;o</strong> &#8212; transacional e cont&#225;bil &#8212; como processo cont&#237;nuo, com a diverg&#234;ncia exposta como m&#233;trica de neg&#243;cio.</p></li></ul><h3>Fica para a pr&#243;xima</h3><p>Ficaram de fora tr&#234;s assuntos que merecem artigo pr&#243;prio, e que s&#227;o a continua&#231;&#227;o natural deste: <strong>a gest&#227;o de liquidez da Conta PI</strong> &#8212; o incidente de Pix que n&#227;o tem uma linha de c&#243;digo quebrada; <strong>os indicadores do Manual de Tempos</strong> e o que muda quando voc&#234; &#233; medido por percentil e n&#227;o por m&#233;dia; e <strong>o MED 2.0</strong>, que acrescentou &#224; m&#225;quina de estados um caso que quase ningu&#233;m modelou: recurso liquidado, creditado e indispon&#237;vel.</p><p>Se este texto foi &#250;til, esses tr&#234;s v&#234;m na sequ&#234;ncia. Fico por aqui neste artigo! At&#233; o pr&#243;ximo!</p><p><strong>Fontes prim&#225;rias para quem quiser se aprofundar:</strong><span> Cat&#225;logo de Servi&#231;os do SFN &#8212; Volume VI (Pagamentos Instant&#226;neos); Manual das Interfaces de Comunica&#231;&#227;o; Manual de Tempos do Pix; Guia de Implementa&#231;&#227;o do Canal Secund&#225;rio de Transmiss&#227;o de Mensagens; Roteiro para Participa&#231;&#227;o Direta no SPI e Abertura de Conta PI; e os Informes SPI publicados pelo Banco Central. Os valores num&#233;ricos de timeout e os indicadores de tempo s&#227;o revisados periodicamente &#8212; confira sempre a vers&#227;o vigente antes de implementar.</span></p>]]></content:encoded></item><item><title><![CDATA[O Pix Fala uma Língua que Seu Core Bank Não Entende: Por que Você Precisa de um Intérprete (Anti-Corruption Layer)]]></title><description><![CDATA[O ACL &#233; o diplomata que impede a guerra entre dois dom&#237;nios que nunca deveriam se conhecer t&#227;o de perto.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/o-pix-fala-uma-lingua-que-seu-core</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/o-pix-fala-uma-lingua-que-seu-core</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Tue, 28 Jul 2026 13:17:23 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/24f9b252-cdb8-4dc2-9f56-7ac5a784b6a6_2752x1536.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><p>Pra quem usa, &#233; um toque e pronto. Pra quem constr&#243;i, &#233; conciliar um protocolo de liquida&#231;&#227;o em tempo real, irrevog&#225;vel, regulado pelo Banco Central, com um n&#250;cleo banc&#225;rio que muitas vezes ainda pensa em lote, em ledger cont&#225;bil, e &#224;s vezes nem tem autonomia total sobre a pr&#243;pria arquitetura. Este artigo &#233; sobre o tradutor que existe entre esses dois mundos &#8212; e por que ele n&#227;o &#233; opcional.</p></div><p>Existem dois pa&#237;ses vizinhos. Um &#233; jovem, fala uma l&#237;ngua nova, padronizada, criada em comit&#234;, com gram&#225;tica r&#237;gida e sem exce&#231;&#245;es &#8212; tudo precisa ser dito de um jeito espec&#237;fico, ou a mensagem simplesmente n&#227;o &#233; aceita na fronteira. O outro &#233; antigo, tem s&#233;culos de tratados, dialetos regionais, express&#245;es que s&#243; fazem sentido se voc&#234; souber a hist&#243;ria por tr&#225;s delas. Os dois precisam negociar dinheiro entre si, todos os dias, milh&#245;es de vezes por dia, em menos de dez segundos por negocia&#231;&#227;o. Essa &#233; a situa&#231;&#227;o de qualquer banco que precisa fazer o Pix conversar com o seu core banc&#225;rio.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Pix parece simples &#8212; at&#233; voc&#234; olhar por tr&#225;s do app</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Pro usu&#225;rio, Pix &#233; abrir o app, apontar a c&#226;mera ou colar uma chave, confirmar, e pronto &#8212; o dinheiro j&#225; est&#225; do outro lado. Essa experi&#234;ncia de dez segundos &#233;, de prop&#243;sito, projetada para esconder toda a complexidade que ela carrega por tr&#225;s.</p></div><h3><strong>Pra entender o Pix, primeiro lembre da TED</strong></h3><p>Se voc&#234; tem mais de trinta anos, provavelmente j&#225; viveu isso: precisar mandar dinheiro pra algu&#233;m numa sexta-feira &#224; tarde e torcer para o rel&#243;gio cooperar. Antes do Pix existir, a <strong><a href="https://www.bcb.gov.br/meubc/faqs/p/o-que-e-transferencia-eletronica-disponivel-ted">TED</a></strong><a href="https://www.bcb.gov.br/meubc/faqs/p/o-que-e-transferencia-eletronica-disponivel-ted"> &#8212; Transfer&#234;ncia Eletr&#244;nica Dispon&#237;vel</a>, criada em 2002 &#8212; era basicamente a &#250;nica forma de mandar dinheiro entre bancos diferentes de forma eletr&#244;nica. E a TED, para quem n&#227;o lembra mais ou nunca precisou lidar com ela, tinha um detalhe brutal: <strong>ela tinha hora para fechar</strong>.</p><p>Feita at&#233; por volta das 17h (no hor&#225;rio de Bras&#237;lia, variando um pouco por institui&#231;&#227;o), o dinheiro chegava no mesmo dia &#250;til &#8212; &#224;s vezes em minutos, &#224;s vezes em algumas horas. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Passou desse hor&#225;rio, mesmo que fosse 17h01?</mark> <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">A transfer&#234;ncia ficava &#8220;engavetada&#8221; at&#233; o pr&#243;ximo dia &#250;til</mark>. Numa sexta &#224; tarde, isso significava esperar at&#233; segunda-feira. Um profissional aut&#244;nomo que recebia o pagamento de um cliente numa sexta de manh&#227;, mas cujo cliente s&#243; conseguia fazer a transfer&#234;ncia &#224; noite, simplesmente n&#227;o via aquele dinheiro at&#233; a semana seguinte &#8212; mesmo o dinheiro j&#225; &#8220;existindo&#8221;, j&#225; tendo sa&#237;do da conta de algu&#233;m.</p><h3><strong>Por que errar uma letra desfazia a transfer&#234;ncia inteira</strong></h3><p>Tinha outro problema, t&#227;o irritante quanto o hor&#225;rio: para fazer uma TED, era preciso informar banco, ag&#234;ncia, conta e CPF ou CNPJ do destinat&#225;rio &#8212; e, em muitos casos, o nome precisava bater exatamente com o que o banco de destino tinha cadastrado. N&#227;o existia nenhum tipo de &#8220;atalho&#8221;: nenhuma chave curta, nenhum QR code, nenhum diret&#243;rio central que resolvesse quem era quem. Bastava errar um d&#237;gito da ag&#234;ncia, ou digitar o nome com uma abrevia&#231;&#227;o diferente da que o banco de destino esperava, para a transfer&#234;ncia ser recusada e devolvida &#8212; e o processo inteiro precisava recome&#231;ar do zero, muitas vezes horas depois, arriscando cair fora da janela das 17h de novo.</p><h3><strong>A tecnologia por tr&#225;s da TED &#8212; e por que ela tinha hora pra fechar</strong></h3><p>A TED era processada pelo <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/str">STR</a></strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/str"> &#8212; Sistema de Transfer&#234;ncia de Reservas</a>, a infraestrutura de liquida&#231;&#227;o do pr&#243;prio Banco Central &#8212; ou, para valores menores, pela infraestrutura da <strong><a href="https://www.bcb.gov.br/content/publicacoes/ref/200305/RELESTAB2003-PortuguesCapitulo3.pdf">CIP</a></strong><a href="https://www.bcb.gov.br/content/publicacoes/ref/200305/RELESTAB2003-PortuguesCapitulo3.pdf"> (C&#226;mara Interbanc&#225;ria de Pagamentos)</a>, atrav&#233;s de um sistema chamado <strong><a href="https://www.enfin.com.br/termo/sitraf-sistema-de-transferencia-de-fundos-7fwhrdm3">SITRAF</a></strong>. O detalhe importante aqui: tanto o STR quanto o SITRAF foram desenhados para operar dentro de uma <strong>janela de expediente banc&#225;rio</strong> &#8212; o SITRAF, por exemplo, funcionava historicamente s&#243; entre 6h30 e 17h, em dias &#250;teis. N&#227;o &#233; que o dinheiro &#8220;demorasse&#8221; tecnicamente para se mover; &#233; que a pr&#243;pria infraestrutura de liquida&#231;&#227;o por tr&#225;s da TED s&#243; ficava de portas abertas durante o hor&#225;rio comercial, replicando no sistema banc&#225;rio eletr&#244;nico a mesma l&#243;gica de uma ag&#234;ncia f&#237;sica que fecha as portas &#224;s 16h.</p><p><strong>Ou seja: o problema nunca foi a internet ser lenta. Era a arquitetura de liquida&#231;&#227;o ter sido desenhada, d&#233;cadas atr&#225;s, em torno do conceito de &#8220;expediente banc&#225;rio&#8221; </strong>&#8212; um conceito que fazia todo sentido quando transfer&#234;ncias eram f&#237;sicas, feitas na boca do caixa, e que continuou entalado dentro da infraestrutura eletr&#244;nica muito depois de deixar de fazer sentido para o usu&#225;rio final.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rzpY!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rzpY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png 424w, https://substackcdn.com/image/fetch/$s_!rzpY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png 848w, https://substackcdn.com/image/fetch/$s_!rzpY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png 1272w, https://substackcdn.com/image/fetch/$s_!rzpY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rzpY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png" width="1099" height="291" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:291,&quot;width&quot;:1099,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:31105,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/208334856?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rzpY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png 424w, https://substackcdn.com/image/fetch/$s_!rzpY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png 848w, https://substackcdn.com/image/fetch/$s_!rzpY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png 1272w, https://substackcdn.com/image/fetch/$s_!rzpY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F202e602c-a75e-4a95-9220-3b7749d0d93e_1099x291.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>O que o Pix realmente resolveu</strong></h3><p>Comparar Pix com TED s&#243; como &#8220;ficou mais r&#225;pido&#8221; subestima o tamanho da mudan&#231;a. O Pix resolveu os dois problemas na raiz, n&#227;o na superf&#237;cie: o <strong>DICT</strong> eliminou a necessidade de decorar banco, ag&#234;ncia, conta e nome exato &#8212; uma chave (CPF, e-mail, telefone ou chave aleat&#243;ria) resolve tudo isso automaticamente, direto no diret&#243;rio do Banco Central, o que tamb&#233;m elimina boa parte do erro humano que devolvia TEDs. E o <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/ISO20022">SPI</a></strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/ISO20022"> </a>foi desenhado, desde o primeiro dia, para n&#227;o ter janela de expediente &#8212; opera de fato 24 horas, 365 dias por ano, sem &#8220;fechar &#224;s 17h&#8221; e sem pausar no fim de semana ou feriado. N&#227;o &#233; a mesma infraestrutura da TED, otimizada. &#201; uma infraestrutura nova, desenhada para um pressuposto que a TED nunca teve: dinheiro n&#227;o deveria ter hor&#225;rio de expediente.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3pqj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3pqj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png 424w, https://substackcdn.com/image/fetch/$s_!3pqj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png 848w, https://substackcdn.com/image/fetch/$s_!3pqj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png 1272w, https://substackcdn.com/image/fetch/$s_!3pqj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3pqj!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png" width="1080" height="575.6043956043956" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/da60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:776,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1080,&quot;bytes&quot;:385757,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/208334856?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3pqj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png 424w, https://substackcdn.com/image/fetch/$s_!3pqj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png 848w, https://substackcdn.com/image/fetch/$s_!3pqj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png 1272w, https://substackcdn.com/image/fetch/$s_!3pqj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fda60f0cf-b5f8-4835-8162-a27b1f1a853c_2333x1244.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Vale registrar tamb&#233;m que o Pix n&#227;o parou de evoluir depois do lan&#231;amento: o <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/pix-saque-troco">Pix Saque</a></strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/pix-saque-troco"> e o </a><strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/pix-saque-troco">Pix Troco</a></strong>, criados posteriormente, levaram esse mesmo racioc&#237;nio para o dinheiro f&#237;sico &#8212; permitindo sacar esp&#233;cie em casas lot&#233;ricas, padarias e supermercados usando o mesmo protocolo. &#201; mais uma pista de que o Pix n&#227;o &#233; uma funcionalidade fechada e est&#225;tica; &#233; uma plataforma regulat&#243;ria em expans&#227;o constante &#8212; o que, como vamos ver mais adiante, &#233; parte do motivo pelo qual conectar um core banc&#225;rio a ela n&#227;o pode ser um projeto &#8220;de uma vez s&#243;&#8221;.</p><p>Por tr&#225;s do toque no app de hoje existe um protocolo de liquida&#231;&#227;o regulado pelo Banco Central &#8212; o <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/sistemapagamentosinstantaneos">SPI</a></strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/sistemapagamentosinstantaneos"> </a>&#8212; operando em modelo <strong><a href="https://www.investopedia.com/terms/r/rtgs.asp">RTGS</a></strong><a href="https://www.investopedia.com/terms/r/rtgs.asp"> (liquida&#231;&#227;o bruta em tempo real)</a>: cada transa&#231;&#227;o &#233; processada individualmente, de forma definitiva e <strong>irrevog&#225;vel</strong>, 24 horas por dia, 365 dias por ano. Existe o <strong>DICT</strong>, o diret&#243;rio que resolve uma chave Pix na conta de destino. Existe o <strong>ICOM</strong>, a interface pela qual as mensagens trafegam &#8212; em XML, seguindo o padr&#227;o internacional <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/ISO20022">ISO 20022</a></strong>. E existe, do outro lado dessa cadeia inteira, o n&#250;cleo banc&#225;rio &#8212; o sistema que efetivamente debita e credita a conta do cliente, sustenta o hist&#243;rico, gera o extrato, e responde por cada centavo perante o regulador.</p><p>O problema &#233; que esse n&#250;cleo, na maioria dos bancos, n&#227;o nasceu pensando em nada disso. Nasceu pensando em lan&#231;amento cont&#225;bil, em fechamento de expediente, em lote processado &#224; noite &#8212; na mesma l&#243;gica de janela de opera&#231;&#227;o que limitava a TED. &#201; a colis&#227;o entre um protocolo desenhado do zero para o tempo real e um sistema que carrega d&#233;cadas de decis&#245;es pensadas para um mundo que fechava &#224;s 16h.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!sddM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!sddM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png 424w, https://substackcdn.com/image/fetch/$s_!sddM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png 848w, https://substackcdn.com/image/fetch/$s_!sddM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png 1272w, https://substackcdn.com/image/fetch/$s_!sddM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!sddM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png" width="1090" height="276" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:276,&quot;width&quot;:1090,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:39944,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/208334856?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!sddM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png 424w, https://substackcdn.com/image/fetch/$s_!sddM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png 848w, https://substackcdn.com/image/fetch/$s_!sddM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png 1272w, https://substackcdn.com/image/fetch/$s_!sddM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F291a5b88-da16-4bec-86c4-9013c258fed2_1090x276.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="callout-block" data-callout="true"><p><strong><span>N&#227;o podemos aceitar</span></strong></p><p>Tratar &#8220;o Pix j&#225; est&#225; pronto, &#233; s&#243; consumir a API do Banco Central&#8221; como uma frase completa. Ela descreve o protocolo &#8212; n&#227;o descreve o trabalho de fazer esse protocolo conversar com o que j&#225; existe dentro do banco.</p><p><strong><span>Como precisamos pensar</span></strong></p><p>Diante de qualquer integra&#231;&#227;o nova com um protocolo regulado, a pergunta n&#227;o &#233; &#8220;como eu chamo essa API&#8221;, &#233; &#8220;qual dos dois lados vai ter que ceder terreno quando um conceito de um simplesmente n&#227;o existir no vocabul&#225;rio do outro?&#8221;</p><p><span>Q</span>uantos sistemas diferentes voc&#234; imagina que uma &#250;nica transa&#231;&#227;o Pix atravessa hoje, entre o toque no app e o dinheiro efetivamente aparecer na conta do outro lado?</p></div><blockquote><p><strong><span>&#129504; Exerc&#237;cio de racioc&#237;nio</span></strong></p><p>Antes de continuar, responda mentalmente duas coisas: primeiro, por que uma infraestrutura de liquida&#231;&#227;o (STR/SITRAF) precisaria ter &#8220;hor&#225;rio de expediente&#8221; se o dinheiro em si &#233; s&#243; um registro eletr&#244;nico, sem nenhuma limita&#231;&#227;o f&#237;sica real? Segundo &#8212; e essa &#233; a que vamos carregar pelo resto do artigo: quantos sistemas diferentes voc&#234; imagina que uma &#250;nica transa&#231;&#227;o Pix atravessa hoje, entre o toque no app e o dinheiro efetivamente aparecer na conta do outro lado?</p><p><em>Sobre a primeira: o hor&#225;rio nunca foi uma limita&#231;&#227;o t&#233;cnica real &#8212; era uma decis&#227;o de design herdada de um tempo em que &#8220;transfer&#234;ncia&#8221; significava um funcion&#225;rio de banco processando algo manualmente. Guarde sua resposta para a segunda pergunta &#8212; vamos revisitar ela no fechamento do artigo, depois de percorrer os dois idiomas envolvidos.</em></p></blockquote><h2><strong>O que &#233;, de fato, uma Anti-Corruption Layer</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Uma Anti-Corruption Layer n&#227;o &#233; um cabo com dois plugues diferentes nas pontas. &#201; uma embaixada inteira: existe gente cuja fun&#231;&#227;o exclusiva &#233; garantir que nenhum conceito mal traduzido de um pa&#237;s vire um incidente diplom&#225;tico no outro.</p></div><p>Formalmente, uma <strong><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/acl.html">Anti-Corruption Layer (ACL)</a></strong> &#233; uma camada intermedi&#225;ria colocada entre dois <em>Bounded Contexts</em> &#8212; dois dom&#237;nios com seus pr&#243;prios modelos e linguagens &#8212; cuja fun&#231;&#227;o &#233; traduzir o que atravessa essa fronteira, para que nenhum dos dois lados precise adotar os conceitos do outro dentro de casa. O nome n&#227;o &#233; acidental: &#8220;anti-corrup&#231;&#227;o&#8221; porque o papel dela &#233; literalmente impedir que o modelo de dom&#237;nio de um sistema seja corrompido &#8212; contaminado, distorcido &#8212; pelos conceitos e pela estrutura de dados do outro.</p><div class="pullquote"><p>&#8220;Essa camada traduz as solicita&#231;&#245;es que um subsistema faz para o outro subsistema.&#8221;- <span>Microsoft Azure Architecture Center &#8212; cat&#225;logo oficial de padr&#245;es de arquitetura, que credita a origem do conceito a Eric Evans.</span></p></div><p>Repare que a defini&#231;&#227;o n&#227;o fala em &#8220;converter formato de dado&#8221; &#8212; fala em traduzir <em>requisi&#231;&#245;es</em> entre dois mundos que n&#227;o compartilham a mesma vis&#227;o da realidade. Uma ACL entre Pix e core bank n&#227;o troca s&#243; o nome de um campo; ela decide o que significa, dentro do vocabul&#225;rio do core, um evento que nasceu com um significado espec&#237;fico no vocabul&#225;rio do Banco Central.</p><div class="callout-block" data-callout="true"><p><strong><span>N&#227;o podemos aceitar</span></strong></p><p>Achar que &#8220;ACL&#8221; &#233; s&#243; um apelido chique para qualquer camada de integra&#231;&#227;o. Ela existe para um problema espec&#237;fico: proteger a integridade sem&#226;ntica do seu dom&#237;nio contra um modelo externo que voc&#234; n&#227;o controla.</p><p><strong><span>Como precisamos pensar</span></strong></p><p>A pergunta certa n&#227;o &#233; &#8220;como eu conecto os dois sistemas&#8221; &#8212; &#233; &#8220;o que, no vocabul&#225;rio do sistema externo, eu N&#195;O quero que vaze para dentro do meu dom&#237;nio, custe o que custar?&#8221;</p></div><blockquote><p><strong><span>&#129504; Exerc&#237;cio de racioc&#237;nio</span></strong></p><p>Pense num conceito do seu dom&#237;nio atual &#8212; de qualquer sistema que voc&#234; mantenha hoje &#8212; que, se fosse traduzido ao p&#233; da letra pra outro sistema, mudaria de significado no caminho. Guarde esse exemplo: vamos usar esse tipo exato de armadilha no t&#243;pico, <strong>O que uma ACL faz exatamente</strong>, com um caso real do Pix.</p></blockquote><h2><strong>De onde veio o conceito</strong></h2><p>A Anti-Corruption Layer n&#227;o &#233; inven&#231;&#227;o recente de arquiteto de fintech. Ela foi descrita formalmente por <strong>Eric Evans</strong> em 2003, no livro que fundou toda uma escola de pensamento em arquitetura de software: <em><a href="https://www.amazon.com.br/Domain-Driven-Design-Tackling-Complexity-Software/dp/0321125215">Domain-Driven Design: Tackling Complexity in the Heart of Software</a></em>. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Est&#225; na parte do livro dedicada a rela&#231;&#245;es entre Bounded Contexts &#8212; ao lado de outras estrat&#233;gias como Shared Kernel, Customer/Supplier, Conformist e </mark><a href="https://ddd-practitioners.com/home/glossary/bounded-context/bounded-context-relationship/open-host-service/"><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Open Host Service</mark></a><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">, cada uma descrevendo um jeito diferente de dois dom&#237;nios coexistirem.</mark></p><p>O motivo de mencionar isso n&#227;o &#233; curiosidade hist&#243;rica. &#201; que Evans n&#227;o prop&#244;s a ACL como a &#250;nica op&#231;&#227;o &#8212; ele catalogou v&#225;rias, cada uma adequada a uma rela&#231;&#227;o de poder e confian&#231;a diferente entre os dois lados. <strong><a href="https://ddd-practitioners.com/home/glossary/bounded-context/bounded-context-relationship/conformist/">Conformist</a></strong>, por exemplo, <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#233; quando um lado simplesmente aceita o modelo do outro sem tradu&#231;&#227;o nenhuma &#8212; funciona quando voc&#234; n&#227;o tem escolha e o modelo externo &#233; bom o suficiente para conviver com ele. </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">A ACL &#233; o oposto</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">: voc&#234; escolhe pagar o custo de traduzir porque n&#227;o pode se dar ao luxo de deixar o modelo externo ditar as regras dentro de casa.</mark></p><div class="callout-block" data-callout="true"><p><strong><span>N&#227;o podemos aceitar</span></strong></p><p>Escolher entre ACL e Conformist s&#243; porque &#8220;&#233; o que todo mundo usa&#8221; &#8212; as duas resolvem o mesmo problema de formas opostas, e escolher errado tem consequ&#234;ncias diferentes.</p><p><strong><span>Como precisamos pensar</span></strong></p><p>Pergunte quem tem menos a perder se o modelo do outro lado mudar sem aviso. Se a resposta for &#8220;eu tenho muito a perder&#8221;, ACL. Se for &#8220;tanto faz, o modelo deles j&#225; &#233; bom o suficiente&#8221;, Conformist pode ser mais barato e igualmente correto.</p></div><blockquote><p><strong><span>&#129504; Exerc&#237;cio de racioc&#237;nio</span></strong></p><p>Por que a rela&#231;&#227;o <strong>Conformist</strong> &#8212; aceitar o modelo do Banco Central sem tradu&#231;&#227;o nenhuma, direto dentro do core &#8212; n&#227;o &#233; uma op&#231;&#227;o segura para um banco, mesmo sendo tecnicamente mais barata de implementar?</p><p><em>Pense em quem regula o qu&#234;: o Banco Central pode mudar a especifica&#231;&#227;o do Pix (e j&#225; mudou, v&#225;rias vezes, com Pix Autom&#225;tico e Pix por Aproxima&#231;&#227;o). O core do banco carrega obriga&#231;&#245;es cont&#225;beis e regulat&#243;rias que n&#227;o nasceram do Pix e n&#227;o podem depender do ritmo de mudan&#231;a dele.</em></p></blockquote><h2><strong>O que uma ACL faz exatamente</strong></h2><h4>Tradu&#231;&#227;o de modelo, n&#227;o s&#243; de dado</h4><p>Aqui est&#225; o caso real que prova por que &#8220;traduzir campo por campo&#8221; n&#227;o &#233; suficiente. O SPI opera em RTGS: toda liquida&#231;&#227;o &#233; <strong>individual, definitiva e irrevog&#225;vel</strong>. Isso tem uma implica&#231;&#227;o direta e pouco intuitiva &#8212; quando um Pix precisa ser devolvido, o sistema n&#227;o desfaz a transa&#231;&#227;o original. Ele cria uma <strong>transa&#231;&#227;o nova</strong>, no sentido contr&#225;rio. Devolu&#231;&#227;o de Pix n&#227;o &#233; estorno; &#233; um segundo pagamento.</p><p>Agora imagine um core bank cujo modelo cont&#225;bil tradicional trata &#8220;devolu&#231;&#227;o&#8221; como sin&#244;nimo de &#8220;estornar o lan&#231;amento original&#8221; &#8212; apagar ou reverter o que foi feito, como sempre se fez com outros meios de pagamento. Se a ACL simplesmente mapear o campo <code>tipo_opera&#231;&#227;o: devolu&#231;&#227;o</code> do Pix para <code>tipo_lan&#231;amento: estorno</code> do core, sem traduzir o <em>conceito</em>, o relat&#243;rio de concilia&#231;&#227;o enviado ao Banco Central vai registrar dois lan&#231;amentos que deveriam ser positivos e distintos como se um deles nunca tivesse existido de verdade &#8212; e a concilia&#231;&#227;o regulat&#243;ria simplesmente n&#227;o bate.</p><p>&#201; exatamente esse tipo de armadilha que a ACL existe para evitar: ela n&#227;o troca o nome do campo, ela garante que o <strong>conceito</strong> &#8220;devolu&#231;&#227;o Pix&#8221; chegue ao core como o que ele realmente &#233; &#8212; um novo lan&#231;amento de cr&#233;dito, n&#227;o uma revers&#227;o &#8212; porque s&#243; assim o modelo cont&#225;bil do banco continua correto.</p><blockquote><p>&#8220;A camada anti-corrup&#231;&#227;o atua como mediadora, traduzindo e adaptando entre os dois sistemas &#8212; evitando que a complexidade de um vaze para dentro do outro.&#8221; - <span>Par&#225;frase de Sam Newman, Monolith to Microservices (2019), sobre o uso da ACL na integra&#231;&#227;o entre sistemas legados e novos servi&#231;os</span></p></blockquote><div class="callout-block" data-callout="true"><p><strong><span>N&#227;o podemos aceitar</span></strong></p><p>Um &#8220;de-para&#8221; de campos apresentado como se fosse uma integra&#231;&#227;o completa. Nome de campo igual n&#227;o significa conceito igual &#8212; e &#233; exatamente a&#237; que a concilia&#231;&#227;o regulat&#243;ria quebra silenciosamente.</p><p><strong><span>Como precisamos pensar</span></strong></p><p>Para cada evento que atravessa a fronteira, pergunte: &#8220;esse conceito, no meu dom&#237;nio, significa exatamente a mesma coisa que significa do outro lado &#8212; ou s&#243; parece significar, porque o nome do campo &#233; parecido?&#8221;</p></div><blockquote><p><strong><span>&#129504; Exerc&#237;cio de racioc&#237;nio</span></strong></p><p>Se o core tratar uma devolu&#231;&#227;o Pix como estorno cont&#225;bil tradicional &#8212; desfazendo o lan&#231;amento original em vez de criar um novo &#8212; o que exatamente quebra no relat&#243;rio de concilia&#231;&#227;o enviado ao Banco Central?</p><p><em>O volume total de transa&#231;&#245;es reportado n&#227;o bate com o volume real processado pelo SPI, porque uma opera&#231;&#227;o que devia aparecer como dois lan&#231;amentos positivos (o Pix original + a devolu&#231;&#227;o como novo pagamento) aparece como se o primeiro nunca tivesse acontecido.</em></p></blockquote><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/o-pix-fala-uma-lingua-que-seu-core/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/o-pix-fala-uma-lingua-que-seu-core/comments"><span>Deixe um coment&#225;rio</span></a></p><h2><strong>ACL n&#227;o &#233; Adapter</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Um Adapter &#233; um dicion&#225;rio de bolso: troca palavra por palavra, r&#225;pido, sem entender o contexto. Uma ACL &#233; um int&#233;rprete profissional: entende os dois idiomas de verdade, sabe quando uma express&#227;o n&#227;o existe do outro lado, e reformula a frase inteira para preservar o que ela realmente quer dizer.</p></div><p><strong><span>&#9997;&#65039; Nota pessoal</span></strong></p><blockquote><p><em>Sendo sincero: essa diferen&#231;a n&#227;o foi &#243;bvia pra mim da primeira vez que estudei DDD. Os dois nomes se pareciam demais, os dois &#8220;traduzem alguma coisa entre dois sistemas&#8221;, e por um bom tempo eu tratei os dois como sin&#244;nimos, s&#243; com nomes diferentes. S&#243; entendi de verdade quando parei de pensar em &#8220;o que cada um faz&#8221; e comecei a pensar em &#8220;o que cada um protege&#8221; &#8212; e &#233; esse o &#226;ngulo que eu quero te poupar de precisar descobrir sozinho, do jeito mais dif&#237;cil, revisando c&#243;digo em produ&#231;&#227;o.</em></p></blockquote><div class="pullquote"><p>&#8220;Converter a interface de uma classe em outra interface que os clientes esperam.&#8221; - <span>Gamma, Helm, Johnson &amp; Vlissides (&#8221;Gang of Four&#8221;) &#8212; Design Patterns: Elements of Reusable Object-Oriented Software (1994), defini&#231;&#227;o do padr&#227;o Adapter</span></p></div><p>&#201; uma defini&#231;&#227;o precisa, e propositalmente estreita: <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">o Adapter resolve um problema de </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">interface</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> &#8212; assinatura de m&#233;todo, formato de chamada, tipo de dado esperado.</mark> &#201; um padr&#227;o estrutural, quase sempre aplicado classe a classe, ou servi&#231;o a servi&#231;o, num escopo pequeno e bem definido. Ele responde uma pergunta simples: &#8220;esses dois peda&#231;os de c&#243;digo conseguem trocar bytes entre si?&#8221;</p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">A ACL opera em outro n&#237;vel &#8212; mais alto, mais amplo, e por isso mais dif&#237;cil de enxergar de primeira. Ela protege um </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Bounded Context inteiro</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">: n&#227;o s&#243; o formato do dado que entra, mas o que aquele dado </mark><em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">significa</mark></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> depois que entra</mark>. Na pr&#225;tica, uma ACL quase sempre <em>usa</em> um ou mais Adapters por dentro &#8212; junto com Facades e Translators &#8212; <mark data-color="#ff9900" style="background-color: rgb(255, 153, 0); color: rgb(0, 0, 0);">mas a diferen&#231;a que realmente importa n&#227;o &#233; t&#233;cnica, &#233; de prop&#243;sito. Um Adapter garante que duas pe&#231;as de software conseguem se falar.</mark><mark data-color="#d0e0e3" style="background-color: rgb(208, 224, 227); color: rgb(0, 0, 0);"> </mark><strong><mark data-color="#ff9900" style="background-color: rgb(255, 153, 0); color: rgb(0, 0, 0);">Uma ACL garante que essa conversa n&#227;o distorce o significado do que est&#225; sendo dito</mark></strong><mark data-color="#ff9900" style="background-color: rgb(255, 153, 0); color: rgb(0, 0, 0);">.</mark></p><h3><strong>O mesmo caso da devolu&#231;&#227;o Pix, visto pelos dois padr&#245;es</strong></h3><p>Vale voltar ao exemplo do T&#243;pico anterior para deixar a diferen&#231;a concreta, n&#227;o abstrata:</p><h4><strong>S&#243; com Adapter</strong></h4><p>O c&#243;digo converte <code>tipo_opera&#231;&#227;o: devolu&#231;&#227;o</code> (Pix) em <code>tipo_lan&#231;amento: estorno</code> (core) sem erro nenhum. A chamada funciona, os tipos batem, os testes t&#233;cnicos passam.</p><p><strong>O problema:</strong> o Adapter n&#227;o tem como saber que &#8220;devolu&#231;&#227;o&#8221; e &#8220;estorno&#8221; significam coisas diferentes no mundo real &#8212; <strong>ele s&#243; sabe converter formato</strong>. A distor&#231;&#227;o de significado passa despercebida.</p><h4><strong>Com ACL</strong></h4><p>A camada reconhece que &#8220;devolu&#231;&#227;o Pix&#8221; &#233; um conceito de neg&#243;cio espec&#237;fico &#8212; uma transa&#231;&#227;o nova, nunca uma revers&#227;o &#8212; e traduz isso para o lan&#231;amento cont&#225;bil correto: <strong>um cr&#233;dito novo, n&#227;o um estorno do lan&#231;amento original.</strong></p><p><strong>O resultado:</strong> o core recebe o conceito certo, a concilia&#231;&#227;o com o Banco Central bate, e ningu&#233;m precisa descobrir o erro tr&#234;s meses depois, numa auditoria.</p><h3><strong>Por que todo programador precisa saber diferenciar os dois</strong></h3><p>N&#227;o &#233; purismo acad&#234;mico de nomenclatura. A diferen&#231;a muda tr&#234;s coisas concretas no seu dia a dia:</p><p><strong>Primeiro, muda a falsa sensa&#231;&#227;o de seguran&#231;a.</strong> Se voc&#234; constr&#243;i um Adapter e o chama de ACL, o c&#243;digo vai parecer perfeitamente saud&#225;vel &#8212; compila, os testes passam, a chamada funciona. Mas o dom&#237;nio n&#227;o est&#225; protegido de verdade, porque ningu&#233;m traduziu o <em>conceito</em>, s&#243; o formato. &#201; o mesmo tipo de bug silencioso que j&#225; vimos antes neste artigo: n&#227;o gera erro, n&#227;o gera exce&#231;&#227;o, s&#243; gera um dado errado que ningu&#233;m percebe at&#233; algu&#233;m comparar com a realidade.</p><p><strong>Segundo, muda quem consegue revisar o c&#243;digo com seguran&#231;a.</strong> Um Adapter pode ser revisado por qualquer pessoa que entenda as duas interfaces t&#233;cnicas envolvidas. Uma ACL s&#243; pode ser revisada de verdade por algu&#233;m que entenda os dois dom&#237;nios de neg&#243;cio &#8212; nesse caso, tanto as regras do Pix quanto o modelo cont&#225;bil do banco. Se sua revis&#227;o de c&#243;digo s&#243; valida &#8220;os tipos batem&#8221;, ela nunca vai pegar um erro de ACL.</p><p><strong>Terceiro, muda o que voc&#234; constr&#243;i quando o requisito parece simples.</strong> &#8220;S&#243; precisamos traduzir esse campo&#8221; &#233;, quase sempre, a frase que introduz um Adapter disfar&#231;ado de ACL &#8212; e o disfarce s&#243; &#233; descoberto quando o conceito de um dos dois lados muda, e a tradu&#231;&#227;o simplista, que nunca protegeu significado nenhum, desmorona silenciosamente.</p><div class="callout-block" data-callout="true"><p><strong><span>N&#227;o podemos aceitar</span></strong></p><p>Chamar de &#8220;ACL&#8221; qualquer classe que s&#243; troca o formato de um payload &#8212; isso resolve incompatibilidade de interface, mas n&#227;o protege dom&#237;nio nenhum, e chamar do nome errado esconde uma lacuna real de prote&#231;&#227;o.</p><p><strong><span>Como precisamos pensar</span></strong></p><p>Pergunte em qual n&#237;vel o problema realmente est&#225;: se &#233; &#8220;os dois sistemas n&#227;o conseguem trocar bytes&#8221;, &#233; Adapter. Se &#233; &#8220;os dois sistemas discordam sobre o que um mesmo conceito de neg&#243;cio significa&#8221;, &#233; ACL &#8212; e Adapter sozinho nunca vai resolver isso.</p></div><blockquote><p><strong><span>&#129504; Exerc&#237;cio de racioc&#237;nio</span></strong></p><p>Se voc&#234; trocar s&#243; a assinatura do m&#233;todo que chama o DICT &#8212; sem tocar em como &#8220;chave Pix&#8221; &#233; modelada dentro do seu dom&#237;nio de conta corrente &#8212; voc&#234; resolveu um problema de Adapter ou de ACL? E se amanh&#227; o conceito de &#8220;chave&#8221; mudar sutilmente pro Bacen (como j&#225; aconteceu com o Pix Autom&#225;tico, que introduziu recorr&#234;ncia), seu Adapter sozinho aguenta essa mudan&#231;a sem vazar inconsist&#234;ncia pro resto do core?</p><p><em>Trocar a assinatura &#233; Adapter. Mas o Adapter sozinho n&#227;o tem onde guardar a decis&#227;o de &#8220;o que fazer quando o conceito muda&#8221; &#8212; essa decis&#227;o precisa morar na camada que entende o dom&#237;nio dos dois lados, que &#233; exatamente o papel da ACL.</em></p></blockquote><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/o-pix-fala-uma-lingua-que-seu-core/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/o-pix-fala-uma-lingua-que-seu-core/comments"><span>Deixe um coment&#225;rio</span></a></p><h2><strong>O idioma do Core Bank: contas, ledgers e o peso da hist&#243;ria</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>O core banc&#225;rio &#233; o pa&#237;s que construiu suas leis ao longo de d&#233;cadas, uma emenda de cada vez, e ainda opera boa parte da burocracia em expediente &#8212; mesmo que a fachada do pr&#233;dio tenha sido reformada recentemente.</p></div><p>Alguns termos valem explica&#231;&#227;o, porque sustentam o resto do racioc&#237;nio. <strong>Ledger</strong> &#233; o livro-raz&#227;o &#8212; o registro definitivo de todos os lan&#231;amentos financeiros de uma conta, seguindo o princ&#237;pio de <strong>partida dobrada</strong> (todo d&#233;bito tem uma contrapartida de cr&#233;dito em algum lugar). <strong>Lan&#231;amento</strong> &#233; cada entrada individual nesse livro. Tradicionalmente, bancos <strong>processam em lote</strong> (batch): uma janela espec&#237;fica da madrugada onde o dia &#233; fechado, os saldos s&#227;o consolidados, e o sistema &#8212; muitas vezes ainda rodando sobre <strong>mainframe</strong>, em <strong>COBOL</strong> &#8212; assume que n&#227;o h&#225; mais nada acontecendo at&#233; o pr&#243;ximo expediente.</p><p>A realidade de 2026 &#233; mais confusa do que esse retrato sugere. Plataformas modernas de core banking v&#234;m substituindo sistemas monol&#237;ticos legados por arquiteturas de microsservi&#231;os, nuvem e mensageria em tempo real &#8212; mas essa transi&#231;&#227;o &#233; gradual, e a maioria dos bancos vive hoje num est&#225;gio intermedi&#225;rio: parte do core j&#225; modernizada, parte ainda presa ao modelo antigo. Para complicar, muitas institui&#231;&#245;es &#8212; especialmente fintechs e bancos menores &#8212; nem operam seu pr&#243;prio core: <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">dependem de provedores de </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">BaaS</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> (Banking as a Service), o que significa que nem sempre t&#234;m autonomia total para mudar a arquitetura por conta pr&#243;pria, mesmo quando sabem exatamente o que precisaria mudar.</mark></p><div class="callout-block" data-callout="true"><p><strong><span>N&#227;o podemos aceitar</span></strong></p><p>Tratar &#8220;o core &#233; legado&#8221; como desculpa gen&#233;rica e permanente. &#201; um fato operacional real &#8212; mas vira desculpa quando usado pra justificar nunca investir em desacoplar as duas realidades.</p><p><strong><span>Como precisamos pensar</span></strong></p><p>Pergunte n&#227;o &#8220;quando o core vai ser modernizado&#8221;, mas &#8220;o que precisa existir HOJE para que o Pix n&#227;o precise esperar essa moderniza&#231;&#227;o acontecer para funcionar direito?&#8221; &#8212; &#233; exatamente a pergunta que a ACL responde.</p></div><h2><strong>O idioma do Pix: SPI, DICT, ISO 20022 e liquida&#231;&#227;o sem volta</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>O Pix &#233; o pa&#237;s que nasceu com constitui&#231;&#227;o escrita num &#250;nico fim de semana, por um comit&#234; que decidiu que tudo seria padronizado, sem exce&#231;&#227;o, e que o expediente de atendimento seria vinte e quatro horas por dia, todos os dias do ano, para sempre.</p></div><p>O <strong>SPI</strong> opera em modelo <strong><a href="https://www.bcb.gov.br/en/financialstability/spi_en?ano=2023">RTGS &#8212; Real-Time Gross Settlement</a></strong>: cada transa&#231;&#227;o &#233; liquidada individualmente, no momento em que acontece, sem espera por lote nem compensa&#231;&#227;o multilateral. Uma vez confirmada, a liquida&#231;&#227;o &#233; <strong>definitiva e irrevog&#225;vel</strong> &#8212; a infraestrutura do Banco Central n&#227;o desfaz uma transa&#231;&#227;o depois de liquidada. O <strong>DICT</strong> &#233; o diret&#243;rio que resolve uma chave Pix (CPF, e-mail, telefone, chave aleat&#243;ria) na conta de destino, consultado antes de cada pagamento. E as mensagens entre participantes trafegam pela <strong>ICOM</strong>, seguindo o padr&#227;o internacional <strong>ISO 20022</strong>, em XML &#8212; o mesmo padr&#227;o usado por sistemas de pagamento em diversos pa&#237;ses, o que abre a porta para interoperabilidade internacional no futuro.</p><p>A institui&#231;&#227;o que se conecta diretamente a essa infraestrutura &#8212; <strong>participante direto</strong>, o chamado &#8220;Pix Direto&#8221; &#8212; precisa cumprir requisitos t&#233;cnicos r&#237;gidos de disponibilidade, lat&#234;ncia e seguran&#231;a. N&#227;o existe meio-termo: ou a mensagem est&#225; no formato certo, dentro do prazo certo, ou ela simplesmente n&#227;o &#233; aceita.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TG-I!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png 424w, https://substackcdn.com/image/fetch/$s_!TG-I!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png 848w, https://substackcdn.com/image/fetch/$s_!TG-I!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png 1272w, https://substackcdn.com/image/fetch/$s_!TG-I!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TG-I!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png" width="1062" height="215.49226569608734" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:223,&quot;width&quot;:1099,&quot;resizeWidth&quot;:1062,&quot;bytes&quot;:22578,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/208334856?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TG-I!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png 424w, https://substackcdn.com/image/fetch/$s_!TG-I!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png 848w, https://substackcdn.com/image/fetch/$s_!TG-I!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png 1272w, https://substackcdn.com/image/fetch/$s_!TG-I!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2fbd1eb1-3e24-4774-a866-ebd31853a5d9_1099x223.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><div class="callout-block" data-callout="true"><p><strong><span>N&#227;o podemos aceitar</span></strong></p><p>Achar que &#8220;irrevog&#225;vel&#8221; &#233; s&#243; um detalhe t&#233;cnico de rodap&#233;. &#201; uma restri&#231;&#227;o de neg&#243;cio com peso jur&#237;dico &#8212; molda como devolu&#231;&#227;o, disputa e fraude precisam ser tratadas em todo o resto do sistema.</p><p><strong><span>Como precisamos pensar</span></strong></p><p>Toda vez que um conceito do Pix parecer &#8220;s&#243; mais um campo&#8221;, pergunte se ele carrega uma restri&#231;&#227;o regulat&#243;ria por tr&#225;s &#8212; porque no Pix, quase sempre carrega.</p></div><blockquote><p><strong><span>&#129504; Exerc&#237;cio de racioc&#237;nio</span></strong></p><p>O Bacen exige liquida&#231;&#227;o em menos de dez segundos, ponta a ponta. Se o seu core precisa de tr&#234;s segundos s&#243; para abrir uma transa&#231;&#227;o tradicional de banco de dados relacional, quanto tempo sobra para todo o resto do fluxo &#8212; valida&#231;&#227;o, DICT, ACL, resposta ao app?</p><p><em>Pouqu&#237;ssimo &#8212; e &#233; exatamente por isso que a integra&#231;&#227;o n&#227;o pode ser &#8220;chamar o core como se chama qualquer outra API interna&#8221;. A ACL, nesse contexto, tamb&#233;m carrega a responsabilidade de decidir o que pode ser respondido de forma otimista ao SPI antes mesmo do core confirmar de verdade.</em></p></blockquote><h2><strong>Onde a ACL entra: desacoplar, proteger e sobreviver &#224;s duas evolu&#231;&#245;es</strong></h2><p>A ACL senta exatamente na fronteira entre os dois idiomas descritos nos dois t&#243;picos anteriores. Do lado do Pix, ela recebe eventos em ISO 20022, com sem&#226;ntica de liquida&#231;&#227;o irrevog&#225;vel e restri&#231;&#245;es regulat&#243;rias espec&#237;ficas. Do lado do core, ela precisa entregar algo que o ledger entenda &#8212; um lan&#231;amento cont&#225;bil coerente, respeitando partida dobrada, sem quebrar a l&#243;gica que o banco usa h&#225; anos. E o caminho inverso tamb&#233;m existe: quando o core precisa responder algo ao Pix (confirma&#231;&#227;o, saldo, status), a ACL traduz de volta, sem expor os detalhes internos do ledger para fora dos muros do banco.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Yapl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Yapl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png 424w, https://substackcdn.com/image/fetch/$s_!Yapl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png 848w, https://substackcdn.com/image/fetch/$s_!Yapl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png 1272w, https://substackcdn.com/image/fetch/$s_!Yapl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Yapl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png" width="1096" height="298" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:298,&quot;width&quot;:1096,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30439,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/208334856?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Yapl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png 424w, https://substackcdn.com/image/fetch/$s_!Yapl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png 848w, https://substackcdn.com/image/fetch/$s_!Yapl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png 1272w, https://substackcdn.com/image/fetch/$s_!Yapl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa6fa4060-6ae8-4a3e-9632-8b25e44b7ff1_1096x298.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>O ganho estrat&#233;gico &#233; que os dois lados passam a poder evoluir em velocidades diferentes, sem que uma mudan&#231;a de um force uma reescrita imediata do outro. O Banco Central j&#225; mudou a especifica&#231;&#227;o do Pix v&#225;rias vezes desde 2020 &#8212; Pix Autom&#225;tico, Pix por Aproxima&#231;&#227;o, Pix Parcelado &#8212; e cada mudan&#231;a dessas bateria direto no core, sem aviso, se n&#227;o existisse uma camada absorvendo o impacto. Do mesmo jeito, o banco pode modernizar seu core aos poucos &#8212; migrando de mainframe para microsservi&#231;os, por exemplo &#8212; sem que essa transi&#231;&#227;o interna ponha em risco a conformidade regulat&#243;ria do lado do Pix.</p><h3><strong>Antes de escrever a primeira linha: as perguntas que um arquiteto pragm&#225;tico faz</strong></h3><p>Adicionar uma ACL n&#227;o deveria ser reflexo autom&#225;tico s&#243; porque &#8220;&#233; o padr&#227;o certo pra integra&#231;&#227;o com sistema externo&#8221;. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#201; uma decis&#227;o de investimento &#8212; toda ACL bem constru&#237;da custa tempo de design, disciplina de manuten&#231;&#227;o, e mais um componente para algu&#233;m entender daqui a tr&#234;s anos</mark>. O arquiteto pragm&#225;tico n&#227;o pergunta &#8220;existe um padr&#227;o pra isso?&#8221; &#8212; <strong>pergunta se o problema espec&#237;fico que est&#225; na sua frente realmente exige essa prote&#231;&#227;o</strong>. Quatro perguntas ajudam a decidir com crit&#233;rio, n&#227;o por reflexo:</p><p><strong><span>1 &#183; Quem &#233; dono do modelo do outro lado?</span></strong></p><p>O banco n&#227;o escreve, n&#227;o revisa e n&#227;o vota na especifica&#231;&#227;o do SPI, do DICT ou da ICOM &#8212; quem decide &#233; o Banco Central, sozinho, e o banco s&#243; recebe a mudan&#231;a quando ela j&#225; est&#225; definida.</p><p><em>Por qu&#234; isso importa: quando voc&#234; n&#227;o tem nenhuma influ&#234;ncia sobre o modelo externo, aceitar esse modelo dentro de casa significa aceitar mudan&#231;as futuras que voc&#234; nunca vai poder negociar. Isso &#233;, sozinho, um sinal forte de que uma ACL vale o investimento.</em></p><p><strong><span>2 &#183; O que exatamente se perde se o conceito externo entrar sem tradu&#231;&#227;o?</span></strong></p><p>No caso do Pix, j&#225; vimos: tratar devolu&#231;&#227;o como estorno quebra a concilia&#231;&#227;o regulat&#243;ria. Esse &#233; um custo real, mensur&#225;vel, com consequ&#234;ncia jur&#237;dica &#8212; n&#227;o uma prefer&#234;ncia est&#233;tica de &#8220;c&#243;digo mais limpo&#8221;.</p><p><em>Por qu&#234; isso importa: se a resposta for &#8220;nada muito grave, s&#243; fica um pouco menos elegante&#8221;, talvez o problema n&#227;o justifique uma ACL &#8212; s&#243; um Adapter simples j&#225; resolve, como vimos anteriormente.</em></p><p><strong><span>3 &#183; O ritmo de mudan&#231;a dos dois lados &#233; genuinamente diferente?</span></strong></p><p>O Pix evolui no calend&#225;rio regulat&#243;rio do Banco Central. O core evolui no calend&#225;rio de risco e investimento do banco. Nenhum dos dois espera o outro &#8212; cada mudan&#231;a de um lado pode acontecer sem qualquer coordena&#231;&#227;o com o outro.</p><p><em>Por qu&#234; isso importa: quando dois sistemas mudam por raz&#245;es diferentes, em ritmos diferentes, decididos por pessoas diferentes, &#233; exatamente a defini&#231;&#227;o de dois Bounded Contexts distintos que Evans descreveu &#8212; e &#233; a&#237; que uma fronteira expl&#237;cita, traduzida, compensa o esfor&#231;o de mant&#234;-la.</em></p><p><strong><span>4 &#183; Existe irreversibilidade envolvida?</span></strong></p><p>A liquida&#231;&#227;o Pix &#233; definitiva. N&#227;o existe &#8220;desfazer&#8221; no protocolo &#8212; s&#243; uma nova opera&#231;&#227;o no sentido contr&#225;rio. Isso significa que um erro de tradu&#231;&#227;o n&#227;o &#233; um bug que se corrige com um patch silencioso; ele j&#225; aconteceu no mundo real, com dinheiro de verdade, de forma irrevog&#225;vel.</p><div class="callout-block" data-callout="true"><p><em>Por qu&#234; isso importa: quanto mais irrevers&#237;vel a consequ&#234;ncia de um erro de tradu&#231;&#227;o, menor a margem para &#8220;vamos simplificar por enquanto e ajustar depois&#8221;. &#201; esse tipo de restri&#231;&#227;o, vinda diretamente do desenho t&#233;cnico do sistema &#8212; n&#227;o de prefer&#234;ncia arquitetural &#8212; que transforma a ACL de boa pr&#225;tica em necessidade estrutural.</em></p></div><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Reparar nessas quatro perguntas juntas explica por que, no caso espec&#237;fico do Pix, a resposta quase sempre aponta para o mesmo lugar: modelo controlado por terceiro regulador, custo real de tradu&#231;&#227;o malfeita, ritmos de mudan&#231;a desacoplados, e consequ&#234;ncia irrevers&#237;vel em caso de erro.</mark> N&#227;o &#233; o arquiteto &#8220;gostando&#8221; de DDD &#8212; &#233; o pr&#243;prio desenho t&#233;cnico do sistema, com suas restri&#231;&#245;es de regula&#231;&#227;o e liquida&#231;&#227;o, empurrando a decis&#227;o nessa dire&#231;&#227;o. &#201; isso que significa dizer que o system design mostra a necessidade da camada: a decis&#227;o nasce das restri&#231;&#245;es reais do sistema, n&#227;o de uma prefer&#234;ncia de estilo de quem desenhou.</p><div class="callout-block" data-callout="true"><p><strong><span>N&#227;o podemos aceitar</span></strong></p><p>Tratar a ACL como um projeto &#8220;de uma vez s&#243;&#8221; que termina quando entra em produ&#231;&#227;o. Ela existe justamente porque os dois lados continuam mudando &#8212; e precisa evoluir junto, para sempre, enquanto os dois mundos continuarem diferentes.</p><p><strong><span>Como precisamos pensar</span></strong></p><p>A pergunta permanente de quem mant&#233;m essa camada n&#227;o &#233; &#8220;ela est&#225; funcionando hoje&#8221;, &#233; &#8220;ela ainda protege os dois lados da &#250;ltima mudan&#231;a que o Bacen fez &#8212; ou j&#225; ficou desatualizada silenciosamente?&#8221;</p></div><h2><strong>Quando voc&#234; N&#195;O precisa de uma ACL</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Construir uma embaixada para conversar com o vizinho que fala a mesma l&#237;ngua que voc&#234;, sobre assuntos que nenhum dos dois lados considera s&#233;rio o suficiente para gerar conflito, &#233; desperd&#237;cio de verba p&#250;blica &#8212; vira burocracia que ningu&#233;m pediu.</p></div><p>Depois de um artigo inteiro argumentando a favor da ACL, &#233; importante fechar com o contrapeso: nem toda integra&#231;&#227;o externa precisa dela, e aplicar esse padr&#227;o por reflexo &#8212; &#8220;&#233; boa pr&#225;tica, ent&#227;o uso sempre&#8221; &#8212; cria exatamente o tipo de complexidade acidental que a ACL deveria evitar, s&#243; que num lugar onde ningu&#233;m precisava dela.</p><ol><li><p><strong><span>Sinal de que talvez voc&#234; n&#227;o precise</span></strong></p><p>Os dois sistemas s&#227;o mantidos pelo mesmo time, ou por times que se comunicam de perto e evoluem juntos por decis&#227;o consciente. N&#227;o existe fronteira real de propriedade &#8212; existe s&#243; uma separa&#231;&#227;o t&#233;cnica de deploy.</p></li><li><p><strong><span>Sinal de que talvez voc&#234; n&#227;o precise</span></strong></p><p>O modelo externo j&#225; &#233; bom o suficiente para conviver com ele sem tradu&#231;&#227;o &#8212; &#233; exatamente a rela&#231;&#227;o que Evans chama de <strong>Conformist</strong>, mencionada no T&#243;pico 3: voc&#234; aceita o modelo do outro de prop&#243;sito, porque traduzir custaria mais do que adotar.</p></li><li><p><strong><span>Sinal de que talvez voc&#234; n&#227;o precise</span></strong></p><p>O erro de tradu&#231;&#227;o, se acontecer, &#233; barato de corrigir &#8212; um relat&#243;rio interno que pode ser reprocessado, um dado que pode ser recalculado, nada com efeito jur&#237;dico ou financeiro irrevers&#237;vel.</p></li><li><p><strong><span>Sinal de que talvez voc&#234; n&#227;o precise</span></strong></p><p>A integra&#231;&#227;o &#233; estreita e est&#225;vel: um &#250;nico endpoint, um formato que n&#227;o muda h&#225; anos, sem sinal de que vai mudar. Nesse caso, um Adapter simples j&#225; resolve o problema todo &#8212; sem precisar de uma camada de prote&#231;&#227;o de dom&#237;nio inteira por tr&#225;s dele.</p></li></ol><p>Um exemplo concreto, propositalmente mais simples que o resto do artigo: um banco constr&#243;i um painel interno, s&#243; para uso do pr&#243;prio time de opera&#231;&#245;es, que espelha alguns dados do Pix para fins de acompanhamento visual &#8212; sem escrever de volta em lugar nenhum, sem alimentar o ledger, sem nenhuma consequ&#234;ncia regulat&#243;ria se um campo estiver levemente desatualizado por alguns minutos. Nesse caso, um Adapter que converte o formato de resposta da API para o formato que o painel espera j&#225; &#233; solu&#231;&#227;o completa. Construir uma ACL inteira, com Facade, Translator e um modelo de dom&#237;nio interno espelhando o do Pix, seria esfor&#231;o de arquitetura para um problema que nunca teve o peso que justificasse esse esfor&#231;o.</p><div class="callout-block" data-callout="true"><p><strong><span>N&#227;o podemos aceitar</span></strong></p><p>Usar &#8220;boa pr&#225;tica de DDD&#8221; como justificativa autom&#225;tica para adicionar camada de indire&#231;&#227;o em qualquer integra&#231;&#227;o &#8212; inclusive nas que n&#227;o carregam risco real de corrup&#231;&#227;o de dom&#237;nio nem consequ&#234;ncia irrevers&#237;vel.</p><p><strong><span>Como precisamos pensar</span></strong></p><p>Antes de desenhar a prote&#231;&#227;o, me&#231;a o que ela protege. Se o pior cen&#225;rio de erro de tradu&#231;&#227;o &#233; baixo, revers&#237;vel e barato de corrigir, o custo de manter uma ACL provavelmente supera o risco que ela evitaria.</p></div><blockquote><p><strong><span>&#129504; Exerc&#237;cio de racioc&#237;nio</span></strong></p><p>Pense numa integra&#231;&#227;o que voc&#234; mant&#233;m hoje, ou j&#225; manteve, que tem uma camada de tradu&#231;&#227;o &#8220;por precau&#231;&#227;o&#8221; &#8212; constru&#237;da seguindo o padr&#227;o certo, mas que, olhando com honestidade, nunca precisou proteger nada de fato grave. Pelas quatro perguntas do T&#243;pico 8, ela deveria continuar existindo do jeito que est&#225;, ou poderia ser simplificada para um Adapter comum?</p><p><em>Se a resposta genu&#237;na for &#8220;nunca vi essa camada realmente evitar um problema s&#233;rio&#8221;, isso n&#227;o &#233; motivo de vergonha &#8212; &#233; sinal de que o julgamento arquitetural na &#233;poca pode ter pesado mais o padr&#227;o do que o risco real. Vale revisitar, n&#227;o vale se cobrar por isso.</em></p></blockquote><h2><strong>Voltando ao n&#250;mero que voc&#234; guardou</strong></h2><p>L&#225; no inicio, pedi para voc&#234; guardar um n&#250;mero &#8212; quantos sistemas uma &#250;nica transa&#231;&#227;o Pix atravessa entre o toque no app e o dinheiro chegar do outro lado. Depois de passar pelo SPI, pelo DICT, pela ICOM, pela ACL traduzindo nos dois sentidos, pelo core com seu ledger e sua partida dobrada, e frequentemente por camadas adicionais de preven&#231;&#227;o a fraude e notifica&#231;&#227;o &#8212; &#233; prov&#225;vel que o n&#250;mero real seja maior do que o que voc&#234; escreveu. A maioria das pessoas subestima, porque a experi&#234;ncia de dez segundos no app foi desenhada, de prop&#243;sito, para esconder exatamente essa dist&#226;ncia.</p><p>&#201; esse o ponto central do artigo: <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Pix n&#227;o &#233; simples porque o problema &#233; simples &#8212; &#233; simples porque algu&#233;m, em algum lugar da arquitetura, est&#225; absorvendo toda a complexidade de duas l&#237;nguas que n&#227;o deveriam, por padr&#227;o, conseguir se entender.</mark> <strong>A Anti-Corruption Layer &#233; essa pe&#231;a</strong>. N&#227;o &#233; burocracia arquitetural nem exagero de quem gosta de over-engineering &#8212; &#233; a diferen&#231;a entre um banco que sobrevive &#224; pr&#243;xima mudan&#231;a regulat&#243;ria do Pix sem reescrever o core, e um banco que descobre, na pior hora poss&#237;vel, que os dois mundos nunca estavam de fato traduzidos &#8212; s&#243; coincidentemente compat&#237;veis, at&#233; deixarem de ser.</p><blockquote><p><strong>Duas l&#237;nguas nunca v&#227;o virar uma s&#243;. O trabalho do arquiteto n&#227;o &#233; for&#231;ar essa fus&#227;o &#8212; &#233; construir, e manter viva, a embaixada entre elas.</strong></p></blockquote><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p><strong>Guia r&#225;pido &#8212; os 8 pontos</strong></p><ul><li><p><strong>Simplicidade aparente:</strong> o app esconde SPI, DICT, ICOM e ISO 20022 atr&#225;s de um toque de dez segundos.</p></li><li><p><strong>ACL:</strong> camada que traduz entre dois Bounded Contexts, protegendo o modelo de dom&#237;nio &#8212; n&#227;o s&#243; o formato do dado.</p></li><li><p><strong>Origem:</strong> Eric Evans, Domain-Driven Design (2003) &#8212; uma entre v&#225;rias estrat&#233;gias catalogadas para rela&#231;&#227;o entre contextos.</p></li><li><p><strong>O que ela faz:</strong> traduz conceito, n&#227;o campo &#8212; devolu&#231;&#227;o Pix &#233; transa&#231;&#227;o nova, nunca estorno.</p></li><li><p><strong>ACL &#8800; Adapter:</strong> Adapter resolve interface; ACL protege sem&#226;ntica de um dom&#237;nio inteiro.</p></li><li><p><strong>Core Bank:</strong> ledger, partida dobrada, lote, legado, e muitas vezes sem autonomia total (BaaS).</p></li><li><p><strong>Pix:</strong> RTGS, liquida&#231;&#227;o irrevog&#225;vel, 24&#215;365, sem toler&#226;ncia a mensagem fora do padr&#227;o.</p></li><li><p><strong>A embaixada:</strong> a ACL permite que os dois mundos evoluam em velocidades diferentes, para sempre.</p></li><li><p><strong>Quando evitar:</strong> mesmo time, modelo externo j&#225; bom o suficiente (Conformist), erro barato de corrigir, ou integra&#231;&#227;o estreita e est&#225;vel &#8212; nesses casos, um Adapter simples j&#225; basta.</p></li></ul>]]></content:encoded></item><item><title><![CDATA[System Design: As partes difíceis!]]></title><description><![CDATA[Arquitetura de software &#233; algo que voc&#234; n&#227;o encontra no Google.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/system-design-as-partes-dificeis</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/system-design-as-partes-dificeis</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Fri, 24 Jul 2026 13:32:16 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/f8cb9bdb-3601-4ad7-9cc1-4ca329067f1f_7952x5304.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="pullquote"><p style="text-align: center;">Arquitetura de software n&#227;o &#233; um diagrama bonito no Miro. &#201; a disciplina de fazer sistemas legados e modernos coexistirem, tribos com prazos e apetite a risco diferentes cooperarem, e mudan&#231;as acontecerem sem derrubar o que j&#225; est&#225; em produ&#231;&#227;o &#8212; sem esperar que a empresa inteira se alinhe de uma vez, porque isso nunca vai acontecer.</p></div><p>O cont&#234;iner &#233; padronizado em portos mar&#237;timos &#8212; qualquer navio, de qualquer pa&#237;s, encaixa no mesmo guindaste. Ningu&#233;m precisa negociar o formato da carga toda vez que um navio novo atraca. Arquitetura de sistemas num ecossistema corporativo fechado busca exatamente isso: n&#227;o uniformizar os navios, mas padronizar o ponto de encaixe entre eles.</p><p>Em bancos, isso &#233; mais dif&#237;cil do que parece. N&#227;o existe &#8220;uma arquitetura&#8221; &#8212; existem dezenas de squads, cada uma com seu hist&#243;rico t&#233;cnico, seu apetite a risco e seu calend&#225;rio de entrega, quase nunca alinhados entre si. Os seis t&#243;picos abaixo que vamos destacar, mapeiam como o engenheiro pragm&#225;tico desenha para essa realidade, sem esperar um alinhamento geral que nunca vai vir.</p><h3>Por que System Design continua sendo essencial</h3><p>C&#243;digo resolve o problema de hoje. System Design decide se o sistema de hoje ainda vai fazer sentido daqui a dois anos, quando o volume triplicar, quando tr&#234;s squads novas precisarem integrar com ele, ou quando a regra de neg&#243;cio que parecia simples virar uma dezena de exce&#231;&#245;es que ningu&#233;m previu no desenho original. &#201; a diferen&#231;a entre escrever uma fun&#231;&#227;o e decidir onde essa fun&#231;&#227;o mora, quem pode cham&#225;-la, o que acontece quando ela falha, e o que sobra do sistema se ela falhar de vez.</p><p>&#201; tamb&#233;m uma das poucas disciplinas de engenharia onde n&#227;o existe resposta certa memoriz&#225;vel &#8212; s&#243; existem trade-offs bem ou mal avaliados para um contexto espec&#237;fico. Um desenho excelente para uma startup de dez pessoas pode ser irrespons&#225;vel dentro de um banco regulado, e um padr&#227;o consolidado num banco pode ser exagero puro numa aplica&#231;&#227;o que nunca vai passar de mil usu&#225;rios. &#201; exatamente por isso que System Design n&#227;o se aprende uma vez e fica pronto &#8212; se aprende revisitando decis&#245;es antigas, comparando o que o desenho previu com o que a produ&#231;&#227;o realmente cobrou, e ajustando o julgamento a cada novo sistema.</p><p>As &#8220;partes dif&#237;ceis&#8221; deste artigo n&#227;o s&#227;o as ferramentas ou os padr&#245;es em si &#8212; Gateway, Strangler Fig, Anti-Corruption Layer s&#227;o s&#243; nomes para ideias relativamente simples. A parte dif&#237;cil de verdade &#233; decidir <strong>quando</strong> cada uma se aplica, <strong>o que ela custa</strong> em troca do que resolve, e <strong>como propor</strong> isso dentro de uma organiza&#231;&#227;o que n&#227;o vai parar para esperar voc&#234; ter certeza absoluta. &#201; esse o tipo de julgamento que s&#243; se constr&#243;i estudando os erros &#8212; os seus e os de quem j&#225; passou por eles antes.</p><p>Em sistemas financeiros, isso &#233; mais dif&#237;cil do que parece. N&#227;o existe &#8220;uma arquitetura&#8221; &#8212; existem dezenas de squads, cada uma com seu hist&#243;rico t&#233;cnico, seu apetite a risco e seu calend&#225;rio de entrega. Os t&#243;picos abaixo mapeiam como o engenheiro pragm&#225;tico desenha para essa realidade, sem esperar um alinhamento geral que nunca vai vir.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Resili&#234;ncia de sistema: blindando o usu&#225;rio do atrito s&#237;ncrono vs. ass&#237;ncrono</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Se um navio quebra ao tentar atracar, ele deve bloquear apenas aquele atracadouro espec&#237;fico &#8212; o resto do porto precisa continuar operando e recebendo navios normalmente.</p></div><h4>&#127974; No mundo real do banco</h4><p>Imagine o cen&#225;rio: a <strong>squad de cart&#245;es</strong> roda sobre um sistema legado s&#237;ncrono via HTTP &#8212; pesado, engessado e com tempo de resposta imprevis&#237;vel. J&#225; a <strong>squad de investimentos</strong> opera em uma arquitetura moderna orientada a eventos, com Kafka e Redis, garantindo baixa lat&#234;ncia e alta vaz&#227;o. Sem uma camada de prote&#231;&#227;o, qualquer engasgo no legado se propaga em cascata e derruba a experi&#234;ncia do fluxo moderno, mesmo que ele n&#227;o tenha culpa nenhuma.</p><p>A resposta arquitetural &#233; a combina&#231;&#227;o de dois pap&#233;is complementares: o <strong>API Gateway (ou BFF)</strong> e o <strong>Circuit Breaker</strong>.</p><p>Se o Gateway atua como a Capitania dos Portos &#8212; controlando, autenticando e roteando o tr&#225;fego de entrada &#8212;, o <em>Circuit Breaker</em> &#233; a trava de emerg&#234;ncia do atracadouro. Quando o backend s&#237;ncrono legado ultrapassa o limite de lat&#234;ncia ou come&#231;a a acumular erros, o <em>Circuit Breaker</em> interrompe as chamadas para aquele servi&#231;o espec&#237;fico (<em>abre o circuito</em>).</p><p>Em vez de deixar a requisi&#231;&#227;o pendurada at&#233; dar <em>timeout</em>, o Gateway responde imediatamente com um estado degradado controlado (um <em>fallback</em> expl&#237;cito), permitindo que o fluxo de alta performance do resto do banco continue operando de forma intocada e transparente para o cliente.</p><h3><strong>Os trade-offs que valem a pena pesar</strong></h3><p>Nenhuma prote&#231;&#227;o &#233; de gra&#231;a. Um gateway com circuit breaker &#233; mais um componente rodando 24 horas por dia, mais um ponto que pode falhar, mais um painel que algu&#233;m precisa monitorar &#224;s 3 da manh&#227;. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">A pergunta que separa a decis&#227;o pragm&#225;tica da decis&#227;o s&#243; te&#243;rica n&#227;o &#233; &#8220;isso &#233; uma boa pr&#225;tica?&#8221; &#8212; &#233; &#8220;o custo de operar essa camada extra &#233; menor do que o custo de uma falha em cascata acontecer sem ela?&#8221;</mark>. Em sistemas que movem dinheiro, quase sempre a resposta &#233; sim. Mas vale medir, n&#227;o assumir: se o legado raramente falha e o impacto de uma falha isolada &#233; baixo, talvez o investimento em resili&#234;ncia valha mais em outro ponto do sistema.</p><p><strong>Existe tamb&#233;m um risco de m&#233;dio prazo: um circuit breaker bem constru&#237;do &#233; t&#227;o bom em esconder problemas que pode virar desculpa para nunca resolver a causa raiz</strong>. A squad dona do legado para de sentir a dor da pr&#243;pria lentid&#227;o, porque algu&#233;m mais construiu uma rede de seguran&#231;a em volta dela. Isso &#233; &#243;timo para o usu&#225;rio no curto prazo e perigoso para a arquitetura no longo prazo, se ningu&#233;m tiver a conversa seguinte.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!_P4K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!_P4K!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png 424w, https://substackcdn.com/image/fetch/$s_!_P4K!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png 848w, https://substackcdn.com/image/fetch/$s_!_P4K!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png 1272w, https://substackcdn.com/image/fetch/$s_!_P4K!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!_P4K!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png" width="952" height="1059.8846153846155" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1621,&quot;width&quot;:1456,&quot;resizeWidth&quot;:952,&quot;bytes&quot;:606003,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/207662283?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!_P4K!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png 424w, https://substackcdn.com/image/fetch/$s_!_P4K!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png 848w, https://substackcdn.com/image/fetch/$s_!_P4K!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png 1272w, https://substackcdn.com/image/fetch/$s_!_P4K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3807a1e0-da2a-4b03-951a-4b9c3f8071ee_1971x2195.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3><strong>Como comunicar isso</strong></h3><p>Essa &#233; a conversa que costuma faltar: alinhar com a squad legada, antes de construir a prote&#231;&#227;o, o que exatamente conta como &#8220;degradado aceit&#225;vel&#8221; para o produto &#8212; porque essa n&#227;o &#233; uma decis&#227;o s&#243; de engenharia, &#233; uma decis&#227;o de experi&#234;ncia do cliente que produto e neg&#243;cio precisam validar junto. E &#233; preciso deixar claro, desde o in&#237;cio, que o gateway n&#227;o &#233; um substituto permanente para resolver a d&#237;vida t&#233;cnica de origem &#8212; &#233; um paliativo com prazo de validade.</p><p><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>ACEITE &#8594; </span></mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>que o legado vai continuar inst&#225;vel por um tempo &#8212; a prioridade n&#227;o &#233; consert&#225;-lo agora, &#233; isolar o impacto dele nos demais fluxos.</span></mark></p><p><strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>N&#195;O ACEITE &#8594; </span></mark></strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>que a instabilidade do legado defina, de forma permanente e silenciosa, o teto de performance de tudo que depende dele.</span></mark></p><p><strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>NEGOCIE &#8594; </span></mark></strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>com a squad legada um horizonte de tempo &#8212; ou pelo menos um crit&#233;rio &#8212; para reduzir a d&#237;vida t&#233;cnica de origem, e n&#227;o apenas mascar&#225;-la indefinidamente.</span></mark></p><h2><strong>Rompendo silos: contratos de integra&#231;&#227;o (API First) como acordo &#250;nico</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>&#201; a especifica&#231;&#227;o do cont&#234;iner padr&#227;o do porto: qualquer squad que a siga sabe, de antem&#227;o, exatamente como sua carga vai ser recebida do outro lado &#8212; sem precisar perguntar.</p></div><h4>&#127974; No mundo real do banco</h4><p>Sem padroniza&#231;&#227;o, cada squad reinventa a integra&#231;&#227;o &#224; sua maneira &#8212; acopladas, redundantes, cada uma com seu pr&#243;prio jeito de nomear campos e tratar erros. Alinhar como um fluxo de cobran&#231;a vai trafegar dados entre 4 ou 5 squads, sem contrato pr&#233;vio, vira semanas de reuni&#227;o s&#243; para decidir formato de payload.</p><h3><strong>Os trade-offs que valem a pena pesar</strong></h3><p>Desenhar um contrato antes de codar tem um custo real e imediato: algu&#233;m precisa parar, pensar no formato dos dados, revisar com as squads consumidoras, e s&#243; depois come&#231;ar a implementar. Isso parece mais lento no primeiro dia. O ganho aparece no dia 90, quando cinco squads j&#225; evolu&#237;ram seus pr&#243;prios sistemas em paralelo, sem precisar de uma &#250;nica reuni&#227;o de sincroniza&#231;&#227;o entre elas. <strong>O erro comum &#233; tentar fazer o contrato perfeito logo de cara &#8212; incluindo todo campo que algu&#233;m &#8220;pode precisar um dia&#8221;</strong>. Isso trava a entrega e ainda assim raramente acerta o que o futuro realmente vai pedir. Um contrato bom o suficiente para come&#231;ar, com espa&#231;o claro para evoluir, bate um contrato &#8220;completo&#8221; que demorou meses para nascer.</p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">H&#225; tamb&#233;m uma tens&#227;o permanente entre autonomia e coer&#234;ncia: contratos r&#237;gidos demais sufocam a squad que precisa se mover r&#225;pido; contratos frouxos demais devolvem o caos que o contrato deveria eliminar. Encontrar esse meio-termo &#233;, na pr&#225;tica, uma decis&#227;o de governan&#231;a &#8212; n&#227;o s&#243; uma decis&#227;o t&#233;cnica.</mark></p><h3><strong>Como comunicar isso</strong></h3><p>Tratar a revis&#227;o do contrato como uma negocia&#231;&#227;o de verdade ajuda mais do que buscar consenso un&#226;nime entre cinco squads &#8212; que raramente existe e, quando existe, geralmente &#233; porque o contrato virou gen&#233;rico demais para servir a algu&#233;m direito. Na pr&#225;tica, isso significa tr&#234;s coisas. <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Primeiro</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">, </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">existe um dono t&#233;cnico com a palavra final</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">:</mark> algu&#233;m &#8212; uma pessoa ou uma tribo de plataforma &#8212; que decide o formato do contrato quando as squads n&#227;o chegam a um acordo sozinhas, para que a decis&#227;o n&#227;o fique travada esperando unanimidade. <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Segundo</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">, </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">esse dono</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">ouve cada squad pelo que ela realmente precisa</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">, </mark>n&#227;o pela lista inteira de &#8220;seria bom ter&#8221; que toda squad tende a trazer quando &#233; convidada a opinar &#8212; o contrato cresce demais e fica lento de aprovar quando tenta agradar todo pedido. <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Terceiro, fica combinado desde o in&#237;cio que o contrato</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">vai evoluir por vers&#245;es</mark></strong> &#8212; campos s&#227;o adicionados, comportamentos s&#227;o ajustados &#8212; em vez de algu&#233;m tentar prever, na v1, tudo que qualquer squad vai precisar nos pr&#243;ximos dois anos.</p><p>E existe uma regra que sustenta tudo isso no tempo, e que precisa ser dita em voz alta, sem deixar margem para interpreta&#231;&#227;o: se uma squad decide &#8220;integrar por fora&#8221; do contrato &#8212; por exemplo, combinando informalmente com outra squad um formato de dado que n&#227;o est&#225; documentado, s&#243; porque parece mais r&#225;pido resolver assim agora &#8212; <strong>isso n&#227;o &#233; um atalho inofensivo</strong>. <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#201; a reintrodu&#231;&#227;o, em miniatura, do exato problema que o contrato existe para resolver: um acordo que s&#243; duas pessoas conhecem, n&#227;o versionado, que pode quebrar sem aviso na pr&#243;xima mudan&#231;a de qualquer um dos dois lados</mark></strong>. Se essa regra for tratada como sugest&#227;o em vez de compromisso, ela vai ser ignorada exatamente nos momentos de mais press&#227;o de prazo &#8212; que &#233; justamente quando o contrato mais precisa se sustentar.</p><p><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>ACEITE &#8594; </span></mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>que o primeiro contrato n&#227;o vai satisfazer 100% das necessidades de todo mundo &#8212; ele s&#243; precisa ser bom o suficiente para todos come&#231;arem a trabalhar em paralelo.</span></mark></p><p><strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>N&#195;O ACEITE &#8594; </span></mark></strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>squads integrando &#8220;por fora&#8221; do contrato porque parece mais r&#225;pido no momento &#8212; isso devolve o caos que o contrato existe para evitar.</span></mark></p><p><strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>NEGOCIE &#8594; </span></mark></strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>um processo leve e com prazo definido para propor mudan&#231;as no contrato, com um dono t&#233;cnico que decide &#8212; sem virar nem burocracia travada, nem terra sem lei.</span></mark></p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!VNgk!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png 424w, https://substackcdn.com/image/fetch/$s_!VNgk!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png 848w, https://substackcdn.com/image/fetch/$s_!VNgk!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png 1272w, https://substackcdn.com/image/fetch/$s_!VNgk!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!VNgk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png" width="728" height="250.9598811292719" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c76d97f2-9317-4bdb-ba10-300418753f62_673x232.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:232,&quot;width&quot;:673,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:28098,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/207662283?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!VNgk!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png 424w, https://substackcdn.com/image/fetch/$s_!VNgk!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png 848w, https://substackcdn.com/image/fetch/$s_!VNgk!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png 1272w, https://substackcdn.com/image/fetch/$s_!VNgk!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc76d97f2-9317-4bdb-ba10-300418753f62_673x232.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/system-design-as-partes-dificeis?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/system-design-as-partes-dificeis?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p><h2><strong>Falando a l&#237;ngua do neg&#243;cio: ROI de componentes reutiliz&#225;veis vs. prazos r&#237;gidos</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>N&#227;o defenda Clean Architecture ou CQRS porque &#8220;o c&#243;digo fica elegante&#8221;. Defenda porque a pr&#243;xima feature vai sair em dias, n&#227;o em meses.</p></div><h4><strong><span>&#127974; No mundo real do banco</span></strong></h4><p>Diretoria vive sob entregas regulat&#243;rias e metas trimestrais, e enxerga refatora&#231;&#227;o grande como perda de tempo. Isolar a regra de neg&#243;cio central de componentes de infraestrutura reutiliz&#225;veis &#8212; autentica&#231;&#227;o, auditoria, resili&#234;ncia j&#225; homologados pelo time de seguran&#231;a &#8212; significa que um produto de cr&#233;dito novo pluga a l&#243;gica de neg&#243;cio em cima de uma funda&#231;&#227;o que j&#225; passou por todo o ciclo de aprova&#231;&#227;o.</p><p><span>O argumento certo aqui n&#227;o &#233; est&#233;tico, &#233; de </span><strong>reaproveitamento de funda&#231;&#227;o j&#225; auditada</strong><span>: quanto do esfor&#231;o da squad vai para regra de neg&#243;cio nova versus quanto vai para reconstruir autentica&#231;&#227;o, log de auditoria e retry do zero, s&#243; para passar pelo mesmo comit&#234; de seguran&#231;a de novo.</span></p><h3><strong>Os trade-offs que valem a pena pesar</strong></h3><p>Construir uma funda&#231;&#227;o reutiliz&#225;vel custa tempo antes de gerar qualquer retorno vis&#237;vel &#8212; a primeira feature que usa essa funda&#231;&#227;o sempre parece mais lenta do que teria sido escrevendo tudo do zero, porque o investimento est&#225; indo para algo que s&#243; paga dividendo na segunda, terceira e d&#233;cima feature. <strong>Esse &#233; exatamente o tipo de trade-off que a lideran&#231;a de banco, sob press&#227;o trimestral, tende a rejeitar instintivamente</strong> &#8212; <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">e &#233; papel do engenheiro pragm&#225;tico traduzir isso em uma conta que fa&#231;a sentido para quem decide or&#231;amento: quanto tempo essa funda&#231;&#227;o economiza a partir da segunda vez que for usada</mark>.</p><p><strong>Existe tamb&#233;m um risco na dire&#231;&#227;o oposta: centralizar demais.</strong> Um m&#243;dulo compartilhado de autentica&#231;&#227;o, auditoria e resili&#234;ncia que toda squad depende vira, tamb&#233;m, <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">um ponto &#250;nico de gargalo &#8212; se ele precisa mudar, todo mundo que depende dele precisa validar, testar e &#224;s vezes esperar. Isso reintroduz, em menor escala, o mesmo problema de coordena&#231;&#227;o que a funda&#231;&#227;o reutiliz&#225;vel deveria resolver.</mark> A linha entre &#8220;o que &#233; genuinamente comum a todas as squads&#8221; e &#8220;o que s&#243; parece comum, mas na pr&#225;tica cada squad precisa de uma varia&#231;&#227;o&#8221; &#233; uma decis&#227;o que vale a pena revisitar, n&#227;o assumir uma vez e esquecer.</p><h3><strong>Como comunicar isso</strong></h3><p>Com a lideran&#231;a, o discurso t&#233;cnico de &#8220;arquitetura limpa&#8221; convence pouco &#8212; o que convence &#233; mostrar, em tempo, o custo de repetir autentica&#231;&#227;o e auditoria em cada produto novo versus o custo de construir uma vez e reaproveitar. Com as squads que v&#227;o consumir a funda&#231;&#227;o, vale alinhar desde o in&#237;cio que &#8220;reutiliz&#225;vel&#8221; n&#227;o &#233; sin&#244;nimo de &#8220;engessado&#8221;: <strong>precisa existir um canal claro para squads pedirem ajustes, sem que cada pedido vire uma fila de meses</strong>.</p><p><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>ACEITE &#8594; </span></mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>que construir a funda&#231;&#227;o reutiliz&#225;vel primeiro atrasa a primeira entrega que depende dela &#8212; o retorno aparece a partir da segunda vez que ela &#233; usada, n&#227;o da primeira.</span></mark></p><p><strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>N&#195;O ACEITE &#8594; </span></mark></strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>press&#227;o para pular a funda&#231;&#227;o &#8220;s&#243; dessa vez, por causa do prazo&#8221; &#8212; isso quase sempre vira pr&#225;tica permanente, n&#227;o exce&#231;&#227;o.</span><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></mark></p><p><strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>NEGOCIE &#8594; </span></mark></strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>o escopo m&#237;nimo real da funda&#231;&#227;o &#8212; o que &#233; genuinamente comum a todas as squads &#8212; separado do que cada squad deveria manter por conta pr&#243;pria.</span></mark></p><h2><strong>Gest&#227;o de mudan&#231;a: evolu&#231;&#227;o arquitetural sem quebrar o legado</strong></h2><p>Mudar uma API central ou um modelo de dados em produ&#231;&#227;o causa p&#226;nico real: sistemas core t&#234;m impactos imprevis&#237;veis e caros quando quebram. Desligar o mon&#243;lito de uma vez, ou trocar o banco de dados da empresa do dia para a noite, nunca &#233; uma op&#231;&#227;o s&#233;ria.</p><p>O padr&#227;o consolidado para isso &#233; o <strong><a href="https://martinfowler.com/bliki/StranglerFigApplication.html">Strangler Fig Pattern</a></strong>: uma camada de roteamento direciona tr&#225;fego progressivamente do sistema legado para o novo, funcionalidade por funcionalidade, at&#233; o legado &#8220;murchar&#8221; e poder ser desligado com seguran&#231;a. Combinado com <strong>versionamento estrito de API</strong> e <strong>feature toggles</strong> no backend, squads mais lentas continuam na vers&#227;o est&#225;vel enquanto squads &#225;geis avan&#231;am &#8212; o risco nunca fica concentrado num &#250;nico momento.</p><h3><strong>Os trade-offs que valem a pena pesar</strong></h3><p>Migra&#231;&#227;o gradual custa mais caro enquanto dura: por um per&#237;odo, a empresa est&#225; pagando para operar e monitorar dois sistemas ao mesmo tempo &#8212; o legado e o novo &#8212; em vez de um s&#243;. &#201; um custo real, n&#227;o hipot&#233;tico, e vale ser dito em voz alta para quem aprova or&#231;amento, em vez de deixar que a conta apare&#231;a como surpresa meses depois. O outro lado da moeda &#233; que o corte &#250;nico, &#8220;big bang&#8221;, &#233; mais barato no papel e infinitamente mais arriscado na pr&#225;tica &#8212; &#233; exatamente o tipo de risco que sistemas core de banco n&#227;o podem se dar ao luxo de correr.</p><p>Existe um risco menos falado sobre a estrat&#233;gia gradual: gradualismo sem prazo tende a nunca terminar. Sem um crit&#233;rio objetivo de conclus&#227;o, &#8220;estamos migrando aos poucos&#8221; pode se tornar o estado permanente do sistema &#8212; os dois ambientes convivendo indefinidamente, cada um exigindo manuten&#231;&#227;o, nenhum sendo desligado. Isso n&#227;o &#233; uma falha do padr&#227;o Strangler Fig; &#233; uma falha de quem n&#227;o definiu, desde o in&#237;cio, quando a migra&#231;&#227;o est&#225; oficialmente terminada.</p><h3><strong>Como comunicar isso</strong></h3><p>Em bancos, mudan&#231;as em sistemas core normalmente n&#227;o s&#227;o s&#243; uma decis&#227;o de engenharia &#8212; times de compliance, risco e auditoria costumam ter que aprovar o plano tamb&#233;m, e traz&#234;-los para a conversa s&#243; no fim, depois que a arquitetura j&#225; est&#225; desenhada, &#233; um dos jeitos mais eficientes de travar tudo por semanas. &#128517;</p><p> <strong>Vale desenhar o plano de migra&#231;&#227;o junto com essas &#225;reas desde o in&#237;cio, n&#227;o depois</strong>. E antes de come&#231;ar a migrar qualquer coisa, vale deixar por escrito, com quem tem poder de aprovar, quais s&#227;o os crit&#233;rios objetivos que marcam o fim da migra&#231;&#227;o &#8212; <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">percentual de tr&#225;fego est&#225;vel, tempo sem incidente, o que for &#8212; para que &#8220;gradual&#8221; tenha um destino, n&#227;o vire um estado permanente</mark>.</p><p><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>ACEITE &#8594; </span></mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>que a migra&#231;&#227;o vai custar mais caro operacionalmente enquanto os dois sistemas rodam em paralelo &#8212; &#233; o pre&#231;o de evitar um corte &#250;nico e arriscado.</span></mark></p><p><strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>N&#195;O ACEITE &#8594; </span></mark></strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>uma migra&#231;&#227;o sem data ou crit&#233;rio de conclus&#227;o &#8212; &#8220;gradual&#8221; n&#227;o pode virar sin&#244;nimo de &#8220;eterno&#8221;, com dois sistemas vivendo para sempre em paralelo.</span></mark></p><p><strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>NEGOCIE &#8594; </span></mark></strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>os crit&#233;rios objetivos de quando o legado pode ser desligado &#8212; e quem, al&#233;m de engenharia, precisa validar esse plano antes de come&#231;ar.</span></mark></p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!R317!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png 424w, https://substackcdn.com/image/fetch/$s_!R317!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png 848w, https://substackcdn.com/image/fetch/$s_!R317!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png 1272w, https://substackcdn.com/image/fetch/$s_!R317!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!R317!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png" width="681" height="156" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:156,&quot;width&quot;:681,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12543,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/207662283?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!R317!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png 424w, https://substackcdn.com/image/fetch/$s_!R317!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png 848w, https://substackcdn.com/image/fetch/$s_!R317!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png 1272w, https://substackcdn.com/image/fetch/$s_!R317!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6b0edc80-fd68-4037-ad7a-38b9fe6de8cf_681x156.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/system-design-as-partes-dificeis/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/system-design-as-partes-dificeis/comments"><span>Deixe um coment&#225;rio</span></a></p><h2><strong>Navegando a assimetria t&#233;cnica entre squads</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Um porto moderno n&#227;o exige que toda carga chegue de navio. Se ela chega de carro&#231;a, o porto constr&#243;i um ponto de recep&#231;&#227;o espec&#237;fico para isso &#8212; e &#233; essa estrutura, n&#227;o a carro&#231;a, que se encarrega de transformar a carga no formato que o resto do porto sabe manusear.</p></div><h4><strong><span>&#127974; No mundo real do banco</span></strong></h4><p>Enquanto uma squad opera microsservi&#231;os escal&#225;veis na nuvem, outra ainda est&#225; presa a um mainframe ou a servidores locais que s&#243; exportam arquivo plano. Exigir que essa squad reescreva toda a infraestrutura para falar a linguagem de eventos do seu ecossistema &#233;, na pr&#225;tica, uma utopia que trava a entrega indefinidamente.</p><p>A sa&#237;da pragm&#225;tica &#233; uma camada de tradu&#231;&#227;o &#8212; <strong><a href="https://refactoring.guru/design-patterns/adapter">Adapter Pattern</a></strong> ou <strong><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/acl.html">Anti-Corruption Layer</a></strong>: voc&#234; recebe o dado exatamente como o outro time consegue enviar (arquivo flat, HTTP s&#237;ncrono simples), e a pr&#243;pria camada de adapta&#231;&#227;o o transforma no formato que seu ecossistema espera. A dor do neg&#243;cio &#233; resolvida sem exigir que a squad vizinha refatore nada.</p><h3><strong>Os trade-offs que valem a pena pesar</strong></h3><p>Toda camada de adapta&#231;&#227;o carrega, embutido, <strong>um acordo silencioso</strong>: <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">voc&#234; est&#225; aceitando manter uma complexidade que tecnicamente n&#227;o &#233; sua, para n&#227;o travar a integra&#231;&#227;o numa depend&#234;ncia que voc&#234; n&#227;o controla</mark>. Isso &#233; generosidade arquitetural com um custo real &#8212; <strong>mais um componente para debugar quando algo d&#225; errado, mais uma tradu&#231;&#227;o entre formatos que pode esconder um bug sutil, mais um lugar onde investigar um problema antes de chegar &#224; causa raiz</strong>. Vale entrar nessa consciente do pre&#231;o, n&#227;o como um favor gratuito.</p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">O risco mais comum com esse tipo de ponte &#233; ela ser tratada como &#8220;provis&#243;ria&#8221; no discurso e permanente na pr&#225;tica</mark>. <strong>Times mudam, gente sai, documenta&#231;&#227;o envelhece</strong> &#8212; e um Adapter Pattern criado como solu&#231;&#227;o tempor&#225;ria para destravar uma integra&#231;&#227;o pode continuar rodando, sem dono e sem ningu&#233;m lembrando por que ele existe, anos depois do problema original ter deixado de fazer sentido.</p><h3><strong>Como comunicar isso</strong></h3><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">O combinado mais importante aqui n&#227;o &#233; t&#233;cnico, &#233; de propriedade: quem mant&#233;m essa camada de tradu&#231;&#227;o daqui a um ano, quando a pessoa que a escreveu j&#225; tiver mudado de time?</mark> Vale deixar isso expl&#237;cito por escrito no momento da cria&#231;&#227;o, junto com a condi&#231;&#227;o sob a qual a camada deixa de ser necess&#225;ria &#8212; por exemplo, quando a squad legada eventualmente modernizar sua pr&#243;pria infraestrutura. Sem esse combinado, o adaptador vira &#243;rf&#227;o, e &#243;rf&#227;os t&#233;cnicos s&#227;o exatamente o tipo de d&#237;vida que ningu&#233;m prioriza resolver at&#233; que quebrem em produ&#231;&#227;o.</p><p><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>ACEITE &#8594; </span></mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"><span>carregar, temporariamente, uma complexidade que n&#227;o &#233; originalmente sua, para destravar uma integra&#231;&#227;o que de outra forma ficaria travada indefinidamente.</span></mark></p><p><strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>N&#195;O ACEITE &#8594; </span></mark></strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>que essa camada fique sem dono claro &#8212; &#8220;solu&#231;&#227;o tempor&#225;ria&#8221; sem respons&#225;vel definido &#233; a receita para ela virar permanente e esquecida.</span></mark></p><p><strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>NEGOCIE &#8594; </span></mark></strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>por escrito quem mant&#233;m o adaptador e sob que condi&#231;&#227;o ele deixa de ser necess&#225;rio &#8212; n&#227;o deixe isso impl&#237;cito na cabe&#231;a de quem o construiu.</span></mark></p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5uhC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png 424w, https://substackcdn.com/image/fetch/$s_!5uhC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png 848w, https://substackcdn.com/image/fetch/$s_!5uhC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png 1272w, https://substackcdn.com/image/fetch/$s_!5uhC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5uhC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png" width="728" height="147.63636363636363" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:145,&quot;width&quot;:715,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:13072,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/207662283?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5uhC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png 424w, https://substackcdn.com/image/fetch/$s_!5uhC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png 848w, https://substackcdn.com/image/fetch/$s_!5uhC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png 1272w, https://substackcdn.com/image/fetch/$s_!5uhC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff51a5eba-480c-4f70-b155-fb9d72910aa6_715x145.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><h2><strong>Engenharia de conflitos: vencendo a burocracia dos comit&#234;s com PoC</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Um resultado de teste de carga vale mais do que uma hora de debate conceitual sobre qual arquitetura &#233; &#8220;melhor&#8221;.</p></div><h4><strong><span>&#127974; No mundo real do banco</span></strong></h4><p>F&#243;runs de arquitetura corporativos tendem ao conservadorismo &#8212; apresenta&#231;&#245;es te&#243;ricas e diagramas complexos morrem na praia por medo do risco, n&#227;o por falta de m&#233;rito t&#233;cnico. Debater os benef&#237;cios de uma arquitetura ass&#237;ncrona em abstrato raramente convence arquitetos que carregam a responsabilidade por sistemas core.</p><p>A t&#225;tica pragm&#225;tica: isolar o cen&#225;rio, construir uma <strong>PoC funcional simplificada</strong> e rodar <strong>testes de carga locais</strong> que provem resili&#234;ncia sob estresse &#8212; com n&#250;meros reais de lat&#234;ncia e custo. M&#233;tricas concretas pesam mais num comit&#234; do que qualquer argumento conceitual, por melhor que seja constru&#237;do.</p><h3><strong>Os trade-offs que valem a pena pesar</strong></h3><p>Construir uma PoC tamb&#233;m tem custo: dias que n&#227;o est&#227;o sendo gastos em compromissos j&#225; assumidos no roadmap, e esse tempo precisa ser justificado como investimento, n&#227;o como desvio de foco. Existe tamb&#233;m um risco de sedu&#231;&#227;o com os pr&#243;prios n&#250;meros: uma PoC roda num cen&#225;rio controlado, isolado, quase sempre mais simples do que a produ&#231;&#227;o real &#8212; e &#233; f&#225;cil, apresentar um resultado bom demais para um contexto que n&#227;o reflete todas as condi&#231;&#245;es que o sistema vai enfrentar de verdade. <strong>A PoC reduz incerteza; ela n&#227;o elimina risco, e tratar os dois como sin&#244;nimos &#233; onde comit&#234;s bem-intencionados tomam decis&#245;es erradas com confian&#231;a excessiva.</strong></p><p>Vale lembrar tamb&#233;m que uma PoC convincente &#233;, por defini&#231;&#227;o, um argumento parcial &#8212; <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">ela mostra o caso em que a proposta funciona bem, quase nunca o caso em que ela falha</mark>. Apresentar isso com honestidade intelectual, incluindo o que n&#227;o foi testado, custa um pouco de for&#231;a ao argumento no curto prazo e compra muito mais credibilidade da pr&#243;xima vez que voc&#234; precisar convencer o mesmo comit&#234;.</p><h3><strong>Como comunicar isso</strong></h3><p>O erro mais comum &#233; construir a PoC primeiro e s&#243; depois perguntar ao comit&#234; o que teria sido suficiente para convenc&#234;-lo. Isso convida a um &#8220;n&#227;o foi bem isso que eu queria ver&#8221; depois de dias de trabalho j&#225; feitos. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Definir com quem vai decidir, antes de come&#231;ar, qual crit&#233;rio objetivo de lat&#234;ncia, custo ou taxa de erro conta como evid&#234;ncia suficiente evita esse desgaste &#8212; e transforma a apresenta&#231;&#227;o de &#8220;conven&#231;a-me&#8221; em &#8220;aqui est&#225; o que combinamos verificar, e aqui est&#225; o resultado&#8221;.</mark></p><p><strong><mark data-color="#fff2cc" style="background-color: rgb(255, 242, 204); color: rgb(0, 0, 0);"><span>ACEITE &#8594; </span></mark></strong><mark data-color="#fff2cc" style="background-color: rgb(255, 242, 204); color: rgb(0, 0, 0);"><span>que uma PoC reduz incerteza, n&#227;o elimina risco &#8212; ela &#233; evid&#234;ncia para apoiar uma decis&#227;o, n&#227;o uma garantia absoluta de que nada vai dar errado.</span></mark></p><p><strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>N&#195;O ACEITE &#8594; </span></mark></strong><mark data-color="#f4cccc" style="background-color: rgb(244, 204, 204); color: rgb(0, 0, 0);"><span>uma exig&#234;ncia de garantia total antes de qualquer teste pr&#225;tico &#8212; isso trava qualquer evolu&#231;&#227;o arquitetural indefinidamente, e nenhuma PoC do mundo vai satisfazer esse padr&#227;o.</span></mark></p><p><strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>NEGOCIE &#8594; </span></mark></strong><mark data-color="#00ff00" style="background-color: rgb(0, 255, 0); color: rgb(0, 0, 0);"><span>o crit&#233;rio de sucesso da PoC antes de constru&#237;-la, com quem vai decidir &#8212; para que o resultado seja avaliado contra um combinado, n&#227;o contra uma expectativa que s&#243; apareceu depois.</span></mark></p><h2><strong>O que muda quando voc&#234; para de negociar tudo e come&#231;a a propor</strong></h2><p>Repare no que os seis t&#243;picos t&#234;m em comum, al&#233;m do tema. Nenhum deles se resolve com uma reuni&#227;o geral de alinhamento &#8212; porque essa reuni&#227;o, num ecossistema com dezenas de squads e apetites a risco diferentes, simplesmente n&#227;o acontece de forma &#250;til. E nenhum deles se resolve s&#243; com a decis&#227;o t&#233;cnica certa, sem a conversa certa acontecendo antes, durante ou depois dela. Gateway, contrato, funda&#231;&#227;o reutiliz&#225;vel, Strangler Fig, Adapter Pattern, PoC &#8212; cada um &#233;, ao mesmo tempo, uma escolha de arquitetura e uma proposta de negocia&#231;&#227;o com um p&#250;blico espec&#237;fico: a squad legada, o comit&#234; de seguran&#231;a, a diretoria, o time vizinho tecnicamente atr&#225;s, os arquitetos conservadores.</p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#201; por isso que o exerc&#237;cio de </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">aceite / n&#227;o aceite / negocie</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> vale mais do que os padr&#245;es espec&#237;ficos deste artigo</mark>. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Amanh&#227; voc&#234; vai enfrentar um problema de arquitetura que n&#227;o est&#225; listado aqui &#8212; e a pergunta que realmente ajuda n&#227;o &#233; &#8220;qual &#233; o padr&#227;o certo&#8221;, </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#233; &#8220;o que dessa situa&#231;&#227;o eu deveria simplesmente aceitar como restri&#231;&#227;o, o que eu n&#227;o deveria aceitar sob hip&#243;tese nenhuma, e o que d&#225; pra resolver com uma negocia&#231;&#227;o bem colocada, no lugar certo, com a pessoa certa&#8221;</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">.</mark> Esse &#233; o h&#225;bito de pensamento que sobrevive &#224; tecnologia espec&#237;fica.</p><p>Vale tamb&#233;m gerenciar a pr&#243;pria expectativa. Nada disso conserta um ambiente corporativo fechado da noite para o dia, e ningu&#233;m deveria prometer isso. O que muda, com o tempo, &#233; a sua posi&#231;&#227;o dentro dele: cada contrato que se sustenta, cada migra&#231;&#227;o que termina no prazo combinado, cada PoC que vira decis&#227;o real, constr&#243;i um cr&#233;dito de confian&#231;a que facilita a pr&#243;xima proposta. Engenheiros pragm&#225;ticos n&#227;o vencem a burocracia corporativa numa tacada s&#243; &#8212; eles acumulam pequenas vit&#243;rias verific&#225;veis at&#233; que propor deixe de ser um combate e vire, simplesmente, o processo padr&#227;o.</p><blockquote><p><strong>Voc&#234; n&#227;o muda o porto inteiro numa noite. Voc&#234; padroniza um atracadouro de cada vez &#8212; e deixa que o resto do porto perceba, aos poucos, que vale a pena seguir o mesmo padr&#227;o.</strong></p></blockquote><p>Fico por aqui nesse artigo! At&#233; o pr&#243;ximo! &#10084;&#65039;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><strong><span>Nota &#8212; sobre o custo real de dizer n&#227;o</span></strong></p><p>&#201; f&#225;cil escrever &#8220;n&#227;o aceite&#8221; numa frase. Na pr&#225;tica, dizer n&#227;o sob press&#227;o de prazo custa algo de verdade: o desconforto imediato de sentir que o chefe vai te ver como o obst&#225;culo, o medo &#8212; nada irracional &#8212; de que isso pese contra voc&#234; numa avalia&#231;&#227;o, num corte de equipe, na sua estabilidade. Ter a evid&#234;ncia t&#233;cnica certa n&#227;o faz esse desconforto desaparecer. Ele &#233; real, e faz sentido ser real: todo mundo tem boleto pra pagar no fim do m&#234;s, e ningu&#233;m deveria fingir que carreira e sobreviv&#234;ncia n&#227;o pesam nessa equa&#231;&#227;o.</p><p>Tr&#234;s coisas ajudam a dizer n&#227;o de um jeito que protege voc&#234;, n&#227;o s&#243; o sistema:</p><ol><li><p><strong>Nunca diga n&#227;o sozinho &#8212; diga n&#227;o com uma alternativa junto.</strong> &#8220;N&#227;o d&#225; pra pular a funda&#231;&#227;o, mas d&#225; pra entregar X em 2 semanas construindo s&#243; a parte essencial dela&#8221; muda a conversa de resist&#234;ncia para proposta. Quem ouve isso lida com uma op&#231;&#227;o, n&#227;o com uma recusa.</p></li><li><p><strong>Separe a decis&#227;o da pessoa que pediu.</strong> Deixe claro que o risco &#233; t&#233;cnico, n&#227;o uma desconfian&#231;a do prazo ou da lideran&#231;a dela: &#8220;n&#227;o &#233; sobre n&#227;o confiar no seu cronograma, &#233; que esse atalho espec&#237;fico volta como retrabalho maior daqui a dois meses&#8221; &#8212; isso tira o &#8220;n&#227;o&#8221; do territ&#243;rio pessoal.</p></li><li><p><strong>Nem toda press&#227;o exige recusa total.</strong> &#192;s vezes o mais seguro para voc&#234; &#8212; e ainda assim honesto tecnicamente &#8212; &#233; aceitar o atalho uma vez, mas registrar por escrito o risco assumido e um prazo pra resolver depois. Isso protege sua carreira no curto prazo sem esconder o problema, e evita que voc&#234; precise ser o &#250;nico a bancar o &#8220;n&#227;o&#8221; toda vez.</p></li></ol><p>Busque o equil&#237;brio! </p></div></div>]]></content:encoded></item><item><title><![CDATA[Cachear ou não cachear: Eis a questão do Engenheiro Pragmático]]></title><description><![CDATA["Existem apenas duas coisas dif&#237;ceis na Ci&#234;ncia da Computa&#231;&#227;o: invalida&#231;&#227;o de cache, nomear coisas..." &#8212; Phil Karlton]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/cachear-ou-nao-cachear-eis-a-questao</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/cachear-ou-nao-cachear-eis-a-questao</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Wed, 22 Jul 2026 13:09:46 GMT</pubDate><enclosure url="https://substackcdn.com/image/fetch/$s_!eeaL!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F033b4aa8-5924-4a31-af48-6efdc2faf85c_608x608.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Cache &#233;, ao mesmo tempo, uma das ferramentas mais poderosas e uma das mais mal utilizadas em engenharia de software. Resolve problemas reais de lat&#234;ncia e custo &#8212; e, usado sem crit&#233;rio, cria bugs silenciosos que s&#243; aparecem em produ&#231;&#227;o, sob carga, no pior momento poss&#237;vel. Este artigo &#233; sobre saber a diferen&#231;a.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>De onde nasceu a ideia de cache (uma breve viagem &#224; CPU)</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><p>O processador do seu computador &#233; absurdamente mais r&#225;pido que a mem&#243;ria RAM que alimenta ele de dados. &#201; como ter um leitor que devora uma p&#225;gina por segundo, mas cujo bibliotec&#225;rio pessoal leva v&#225;rios segundos para trazer cada p&#225;gina nova.</p><p>O cache n&#227;o nasceu em nenhum banco de dados nem em nenhum framework web &#8212; nasceu na arquitetura de hardware, nos anos 60, para resolver exatamente esse gargalo de velocidade entre processador e mem&#243;ria. A solu&#231;&#227;o foi guardar uma c&#243;pia dos dados mais usados numa mem&#243;ria menor, mais cara e muito mais r&#225;pida, fisicamente mais perto do processador &#8212; o que hoje chamamos de <a href="https://www.youtube.com/watch?v=7yrK_9PderQ">L1, L2 e L3 cache</a>, cada n&#237;vel um pouco maior e um pouco mais lento que o anterior, at&#233; chegar na RAM.</p><p>O ponto-chave para carregar deste t&#243;pico: o problema que resolvemos hoje com Redis na frente de um banco de dados &#233;, estruturalmente, <strong>o mesmo problema</strong> que engenheiros de hardware resolveram sessenta anos atr&#225;s. Muda a escala, muda o vocabul&#225;rio, mas a ideia central &#8212; guardar uma c&#243;pia r&#225;pida perto de quem precisa, para n&#227;o pagar o pre&#231;o de buscar na fonte lenta toda vez &#8212; &#233; a mesma.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B5BS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B5BS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png 424w, https://substackcdn.com/image/fetch/$s_!B5BS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png 848w, https://substackcdn.com/image/fetch/$s_!B5BS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png 1272w, https://substackcdn.com/image/fetch/$s_!B5BS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B5BS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png" width="1139" height="575" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:575,&quot;width&quot;:1139,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:142696,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:false,&quot;topImage&quot;:true,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/207909690?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!B5BS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png 424w, https://substackcdn.com/image/fetch/$s_!B5BS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png 848w, https://substackcdn.com/image/fetch/$s_!B5BS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png 1272w, https://substackcdn.com/image/fetch/$s_!B5BS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc14b1b12-1332-4ef1-a00a-3979f7f2b017_1139x575.png 1456w" sizes="100vw" fetchpriority="high"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>Cache no mundo real: a mesa do desenvolvedor e a estante de livros</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Ir at&#233; a biblioteca (o banco de dados) buscar um livro toda vez que voc&#234; precisa de uma informa&#231;&#227;o &#233; lento &#8212; tem que se levantar, caminhar, procurar na estante, voltar. Deixar o livro aberto na sua pr&#243;pria mesa de trabalho (o cache) &#233; r&#225;pido: a informa&#231;&#227;o j&#225; est&#225; ali, ao alcance da m&#227;o.</p></div><p>A analogia &#233; boa justamente porque o problema dela tamb&#233;m &#233; real. Se a sua mesa acumula livros demais, voc&#234; perde espa&#231;o e se confunde sobre qual livro tem qual informa&#231;&#227;o &#8212; o equivalente a um cache sem pol&#237;tica de despejo, crescendo sem limite. E existe um risco mais sutil: voc&#234; pode estar lendo, na sua mesa, uma edi&#231;&#227;o desatualizada de um livro, enquanto a biblioteca j&#225; recebeu a edi&#231;&#227;o nova. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#201; exatamente isso que chamamos de </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">dado obsoleto (</mark><a href="https://www.ibm.com/think/topics/stale-data"><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">stale data</mark></a><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">)</mark></strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> &#8212; e &#233; o problema mais dif&#237;cil de resolver em qualquer sistema de cache, porque a mesa n&#227;o avisa sozinha quando o livro dela ficou velho.</mark></p><h3><strong>Por que dado obsoleto &#233; o bug mais trai&#231;oeiro que existe</strong></h3><p>Vale se demorar aqui, porque stale data tem uma caracter&#237;stica que nenhum outro tipo comum de bug tem: <strong>ele n&#227;o gera erro nenhum</strong>. N&#227;o lan&#231;a exce&#231;&#227;o, n&#227;o aparece em log de falha, n&#227;o derruba requisi&#231;&#227;o nenhuma. Ele s&#243; entrega, com toda a confian&#231;a de uma resposta bem-sucedida, um dado que j&#225; deixou de ser verdade. Um sistema servindo dado obsoleto parece, de fora, exatamente t&#227;o saud&#225;vel quanto um sistema servindo dado correto &#8212; a &#250;nica forma de perceber a diferen&#231;a &#233; comparar com a fonte da verdade, coisa que ningu&#233;m faz em toda requisi&#231;&#227;o, porque isso anularia o pr&#243;prio prop&#243;sito de ter cache.</p><h3><strong>Pontos de aten&#231;&#227;o que todo programador precisa carregar</strong></h3><p><strong>1. A janela entre escrever e invalidar n&#227;o &#233; instant&#226;nea</strong></p><p>Existe sempre um intervalo de tempo &#8212; que pode ser milissegundos, mas existe &#8212; entre o momento em que o banco confirma a escrita e o momento em que a chave correspondente &#233; de fato removida do cache. Qualquer leitura que aconte&#231;a exatamente nesse intervalo recebe o dado antigo, mesmo que a escrita j&#225; tenha sido conclu&#237;da com sucesso.</p><p><strong>2. Nem todo caminho de escrita passa pelo mesmo lugar</strong></p><p>Um job em lote, uma migra&#231;&#227;o manual rodada direto no banco, uma corre&#231;&#227;o feita por outra squad em outro servi&#231;o &#8212; qualquer escrita que n&#227;o passe pelo caminho de c&#243;digo &#8220;oficial&#8221; da aplica&#231;&#227;o simplesmente n&#227;o sabe que precisa invalidar cache nenhum. O dado muda na fonte, e o cache nunca fica sabendo.</p><p><strong>3. Cache em camadas m&#250;ltiplas multiplica o problema</strong></p><p>CDN, cache do navegador, cache da aplica&#231;&#227;o, cache de query do pr&#243;prio banco &#8212; &#233; comum ter v&#225;rias camadas de cache empilhadas, cada uma com sua pr&#243;pria janela de expira&#231;&#227;o. Invalidar a camada de aplica&#231;&#227;o n&#227;o invalida a CDN; o usu&#225;rio pode continuar vendo dado velho mesmo depois de o seu c&#243;digo ter feito tudo certo.</p><p><strong>4. Cache distribu&#237;do nem sempre invalida em todo lugar de uma vez</strong></p><p>Em arquiteturas com m&#250;ltiplas inst&#226;ncias, m&#250;ltiplas regi&#245;es ou r&#233;plicas de cache, invalidar uma chave numa inst&#226;ncia n&#227;o garante que ela tamb&#233;m sumiu nas outras &#8212; dependendo da topologia, pode existir um atraso de propaga&#231;&#227;o, e diferentes usu&#225;rios, batendo em r&#233;plicas diferentes, veem respostas diferentes ao mesmo tempo.</p><p><strong>5. Teste normal quase nunca revela o problema</strong></p><p>Um teste automatizado t&#237;pico faz uma escrita, espera, depois l&#234; &#8212; sequencialmente, sem concorr&#234;ncia real. Isso nunca exercita a janela de corrida que causa stale data em produ&#231;&#227;o, porque produ&#231;&#227;o tem m&#250;ltiplas requisi&#231;&#245;es concorrentes que o teste, sozinho, n&#227;o reproduz. &#8220;Passou nos testes&#8221; n&#227;o &#233; evid&#234;ncia de que a invalida&#231;&#227;o est&#225; correta sob carga real.</p><p>Nenhum desses pontos significa que stale data &#233; imposs&#237;vel de mitigar &#8212; significa que ele nunca &#233; resolvido por uma &#250;nica linha de c&#243;digo chamando <code>invalidar()</code>. Exige pensar, para cada dado cacheado, em todos os caminhos que podem mud&#225;-lo, em todas as camadas onde uma c&#243;pia dele pode estar guardada, e em quanto tempo de inconsist&#234;ncia o neg&#243;cio realmente tolera.</p><h2><strong>O que o cache &#233; (e o que ele definitivamente N&#195;O &#233;)</strong></h2><p>Cache &#233; uma camada tempor&#225;ria, vol&#225;til e de alt&#237;ssima velocidade para dados de leitura frequente. Essa defini&#231;&#227;o &#233; curta de prop&#243;sito &#8212; porque a parte mais importante deste t&#243;pico <strong>est&#225; no que ele n&#227;o &#233;</strong>, e que engenheiros sob press&#227;o de prazo tendem a esquecer.</p><p><strong>&#10003; O que ele &#233;</strong></p><ul><li><p>Uma camada tempor&#225;ria e vol&#225;til &#8212; pode desaparecer, e a aplica&#231;&#227;o precisa sobreviver a isso.</p></li><li><p>Otimizado para leitura de alt&#237;ssima frequ&#234;ncia sobre dado que muda pouco.</p></li><li><p>Uma c&#243;pia &#8212; nunca a fonte da verdade.</p></li></ul><p><strong>&#10007; O que ele n&#227;o &#233;</strong></p><ul><li><p>N&#227;o &#233; banco de dados prim&#225;rio &#8212; nunca use para persist&#234;ncia crucial que n&#227;o pode simplesmente sumir.</p></li><li><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">N&#227;o &#233; solu&#231;&#227;o para query ruim ou &#237;ndice faltando &#8212; cache esconde o sintoma, n&#227;o cura a causa.</mark></p></li><li><p>N&#227;o &#233; rem&#233;dio para c&#243;digo lento &#8212; mascarar lat&#234;ncia de algoritmo ineficiente com cache &#233; s&#243; acumular d&#233;bito t&#233;cnico com juros.</p></li></ul><p>Vale grifar o segundo item da coluna <strong>o que ele n&#227;o &#233;</strong>, porque &#233; o erro mais comum e mais caro: colocar um cache na frente de uma query lenta resolve o sintoma imediatamente e esconde o problema real por meses &#8212; at&#233; o dia em que o cache expira no pior momento poss&#237;vel, e a query lenta original volta a bater, sem aviso, direto na cara de todo mundo.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Os 5 grandes benef&#237;cios do cache</strong></h2><h4>O lado bom:</h4><p><strong>1. Redu&#231;&#227;o dr&#225;stica de lat&#234;ncia</strong></p><p>Respostas na casa dos milissegundos, &#224;s vezes sub-milissegundos &#8212; uma diferen&#231;a de ordem de magnitude frente a uma consulta que precisa tocar disco.</p><p><strong>2. Al&#237;vio de carga no banco de dados</strong></p><p>Libera conex&#245;es e CPU do banco principal, deixando mais capacidade dispon&#237;vel justamente para as opera&#231;&#245;es de escrita, que n&#227;o t&#234;m como ser cacheadas da mesma forma.</p><p><strong>3. Economia de custo de infraestrutura</strong></p><p>Menos processamento repetitivo em inst&#226;ncias pagas por hora ou por uso &#8212; cada leitura evitada no banco &#233;, literalmente, dinheiro que deixou de ser gasto.</p><p><strong>4. Maior vaz&#227;o (throughput)</strong></p><p>O sistema suporta muito mais requisi&#231;&#245;es simult&#226;neas, porque a maioria delas nunca chega a competir pelos mesmos recursos escassos do banco.</p><p><strong>5. Resili&#234;ncia parcial</strong></p><p>Em alguns cen&#225;rios, se o banco prim&#225;rio oscilar, o cache ainda consegue servir leituras tempor&#225;rias &#8212; um amortecedor, n&#227;o uma garantia, mas que j&#225; evitou muita indisponibilidade total.</p><h2><strong>Os 5 perigos mortais do cache (a parte que d&#243;i em produ&#231;&#227;o)</strong></h2><h4>O lado ruim:</h4><p>Esta &#233; a se&#231;&#227;o que a comunidade s&#234;nior mais valoriza &#8212; n&#227;o porque os perigos sejam segredo, mas porque s&#243; quem j&#225; apanhou de cada um deles em produ&#231;&#227;o sabe o quanto eles doem na pr&#225;tica.</p><p><strong>1. Invalida&#231;&#227;o de cache &#8212; &#8220;a coisa mais dif&#237;cil da computa&#231;&#227;o&#8221;</strong></p><p>Manter o dado do cache sincronizado com a fonte da verdade &#233;, de forma consolidada na ind&#250;stria, um dos problemas mais dif&#237;ceis de acertar de verdade. N&#227;o &#233; falta de biblioteca ou de padr&#227;o &#8212; &#233; que &#8220;quando exatamente esse dado deixou de ser v&#225;lido&#8221; &#233; uma pergunta genuinamente dif&#237;cil quando m&#250;ltiplos caminhos de escrita podem afetar o mesmo dado cacheado.</p><p><strong>2. Cache Stampede / Thundering Herd</strong></p><p>Quando um item muito acessado expira, e milhares de requisi&#231;&#245;es simult&#226;neas &#8212; que estavam todas contando com aquele cache &#8212; batem direto no banco de dados ao mesmo tempo, tentando repopular a mesma chave. O banco, que estava tranquilo um segundo antes, recebe um pico brutal e pode simplesmente cair.</p><p><strong>3. Inconsist&#234;ncia de dados (stale data)</strong></p><p>O usu&#225;rio atualiza o pr&#243;prio perfil, mas continua vendo os dados antigos na tela seguinte &#8212; porque a chave de cache correspondente n&#227;o foi invalidada no momento da escrita. &#201; o bug mais confuso de explicar para um usu&#225;rio, porque para ele parece que o sistema simplesmente &#8220;esqueceu&#8221; o que ele acabou de fazer.</p><p><strong>4. Consumo descontrolado de mem&#243;ria (OOM)</strong></p><p>Salvar payloads gigantescos no cache, sem uma pol&#237;tica clara de despejo (como LRU &#8212; descartar o item menos recentemente usado), transforma uma ferramenta de otimiza&#231;&#227;o em uma bomba-rel&#243;gio de Out Of Memory esperando o momento certo para explodir.</p><p><strong>5. Acoplamento e ponto &#250;nico de falha</strong></p><p>Tratar o servidor de cache como infal&#237;vel &#233; o erro mais silencioso da lista. Quando ele cai &#8212; e ele vai cair, cedo ou tarde &#8212; a aplica&#231;&#227;o inteira quebra, porque ningu&#233;m escreveu o caminho de c&#243;digo que lida com um cache miss em massa e repentino.</p><blockquote><p>&#8220;A invalida&#231;&#227;o de cache &#233; t&#227;o dif&#237;cil que n&#243;s criamos o &#8216;Time to Live&#8217; (TTL) s&#243; para o tempo resolver o problema que a gente n&#227;o soube resolver na arquitetura.&#8221;</p></blockquote><h2><strong>Quando arquitetos e devs DEVEM cachear</strong></h2><p>&#8220;Dados que mudam pouco e s&#227;o lidos muito&#8221; &#233; o conselho que todo mundo j&#225; ouviu &#8212; e &#233; verdadeiro, mas raso demais para decidir de verdade. Na pr&#225;tica, cachear n&#227;o &#233; uma escolha bin&#225;ria de sim ou n&#227;o: &#233; uma posi&#231;&#227;o num espectro, definida por dois eixos independentes que precisam ser medidos, n&#227;o estimados de cabe&#231;a.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/cachear-ou-nao-cachear-eis-a-questao/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/cachear-ou-nao-cachear-eis-a-questao/comments"><span>Deixe um coment&#225;rio</span></a></p><h3><strong>A matriz que substitui o &#8220;achismo&#8221; por decis&#227;o</strong></h3><p>Cruze <strong>frequ&#234;ncia de leitura</strong> com <strong>frequ&#234;ncia de mudan&#231;a</strong> e a decis&#227;o fica muito mais clara do que qualquer regra geral:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!rlr2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!rlr2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png 424w, https://substackcdn.com/image/fetch/$s_!rlr2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png 848w, https://substackcdn.com/image/fetch/$s_!rlr2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png 1272w, https://substackcdn.com/image/fetch/$s_!rlr2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!rlr2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png" width="702" height="370" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/48e624b3-0b68-4756-84fa-845ef569647b_702x370.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:370,&quot;width&quot;:702,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:35082,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/207909690?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!rlr2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png 424w, https://substackcdn.com/image/fetch/$s_!rlr2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png 848w, https://substackcdn.com/image/fetch/$s_!rlr2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png 1272w, https://substackcdn.com/image/fetch/$s_!rlr2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F48e624b3-0b68-4756-84fa-845ef569647b_702x370.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>O eixo horizontal n&#227;o deveria ser preenchido por intui&#231;&#227;o &#8212; &#233; dado real de APM, de log de query, de m&#233;trica de acesso. &#8220;Eu acho que esse dado muda pouco&#8221; e &#8220;esse dado muda, em m&#233;dia, uma vez a cada 40 minutos, medido nos &#250;ltimos 30 dias&#8221; s&#227;o afirma&#231;&#245;es completamente diferentes, e s&#243; a segunda deveria autorizar uma decis&#227;o de cache em produ&#231;&#227;o.</p><h3><strong>Cen&#225;rio 1 &#8212; dados de alta leitura, baix&#237;ssima escrita</strong></h3><p>Exemplos concretos: a lista de pa&#237;ses e estados de um formul&#225;rio de cadastro, a tabela de tarifas de um banco atualizada uma vez por m&#234;s, a configura&#231;&#227;o de feature flags, as categorias de navega&#231;&#227;o de um e-commerce. &#201; o cen&#225;rio mais cl&#225;ssico e mais seguro para cachear &#8212; mas &#8220;seguro&#8221; n&#227;o significa &#8220;sem perguntas&#8221;.</p><p><strong><span>Pergunta do arquiteto</span></strong></p><p><em>&#8220;Qual &#233; a raz&#227;o real entre leituras e escritas desse dado &#8212; 10 para 1? 10 mil para 1? Eu medi isso, ou estou assumindo que &#233; alto porque parece &#243;bvio?&#8221;</em></p><p><strong><span>Pergunta do engenheiro</span></strong></p><p><em>&#8220;Quem, al&#233;m do meu pr&#243;prio servi&#231;o, escreve nesse dado &#8212; um job em lote, um script de outro time, uma tela de administra&#231;&#227;o separada? Esse caminho de escrita tamb&#233;m invalida o cache, ou s&#243; o meu c&#243;digo lembra de fazer isso?&#8221;</em></p><h3><strong>Cen&#225;rio 2 &#8212; c&#225;lculos computacionalmente caros que raramente mudam</strong></h3><p>Exemplos concretos: um score de cr&#233;dito calculado a partir de m&#250;ltiplas fontes de dados, um relat&#243;rio de fechamento cont&#225;bil do m&#234;s anterior, o resultado de um modelo de recomenda&#231;&#227;o treinado uma vez por dia, um dashboard de m&#233;tricas agregadas que varre milh&#245;es de linhas para gerar um &#250;nico n&#250;mero.</p><p><strong><span>Pergunta do engenheiro</span></strong></p><p><em>&#8220;Esse c&#225;lculo &#233; determin&#237;stico &#8212; o mesmo input sempre produz o mesmo output &#8212; ou depende de algum estado externo que pode mudar entre uma chamada e outra, tornando o resultado cacheado enganoso mesmo dentro do TTL?&#8221;</em></p><p><strong><span>Pergunta do arquiteto</span></strong></p><p><em>&#8220;Existe uma forma de recalcular esse valor de forma incremental, conforme os dados de origem mudam, em vez de recalcular do zero e depender de cache para esconder o custo? &#192;s vezes o cache &#233; usado para mascarar um algoritmo que deveria ser redesenhado, n&#227;o otimizado com uma camada por cima.&#8221;</em></p><h3><strong>Cen&#225;rio 3 &#8212; prote&#231;&#227;o de APIs de terceiros</strong></h3><p>Exemplos concretos: consulta de CEP e endere&#231;o, cota&#231;&#227;o de moeda de um provedor externo, verifica&#231;&#227;o de score num bureau de cr&#233;dito como Serasa ou SPC, geolocaliza&#231;&#227;o a partir de IP. Aqui o cache deixa de ser s&#243; otimiza&#231;&#227;o de performance e vira, tamb&#233;m, controle de custo e de risco de bloqueio.</p><p><strong><span>Pergunta do arquiteto</span></strong></p><p><em>&#8220;Esse dado muda com a frequ&#234;ncia que eu estou assumindo? A cota&#231;&#227;o do d&#243;lar muda a cada segundo no mercado, mas a minha aplica&#231;&#227;o realmente precisa dessa granularidade, ou um cache de sessenta segundos j&#225; &#233; impercept&#237;vel para o caso de uso real?&#8221;</em></p><p><strong><span>Pergunta do engenheiro</span></strong></p><p><em>&#8220;Se o provedor externo cair e, ao mesmo tempo, meu cache expirar, existe um plano B &#8212; um valor default, uma resposta degradada &#8212; ou a minha aplica&#231;&#227;o simplesmente quebra em cascata porque dependeu de duas coisas saud&#225;veis ao mesmo tempo?&#8221;</em></p><h2><strong>O estudo da feature: por que entender o neg&#243;cio antes de cachear</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Decidir cachear um dado n&#227;o &#233; uma escolha puramente t&#233;cnica &#8212; &#233; uma escolha sobre o quanto de desatualiza&#231;&#227;o o neg&#243;cio tolera, e essa resposta n&#227;o mora no c&#243;digo, mora na cabe&#231;a de quem entende o produto.</p></div><p>A pergunta que todo engenheiro deveria fazer antes de configurar um <a href="https://redis.io/docs/latest/commands/ttl/">TTL </a>n&#227;o &#233; <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#8220;quanto tempo esse dado pode ficar em cache?&#8221; &#8212; &#233; </mark><strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#8220;qual o impacto se o cliente vir esse dado desatualizado por X segundos?&#8221;</mark></strong>. Se o saldo de uma conta banc&#225;ria atrasar dois segundos por causa de cache, isso &#233; inaceit&#225;vel &#8212; <strong>dinheiro &#233; o tipo de dado onde inconsist&#234;ncia tem consequ&#234;ncia direta e mensur&#225;vel.</strong> <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Se o estoque de um produto na Black Friday atrasar dois segundos, o preju&#237;zo pode ser igualmente real: venda de item que j&#225; esgotou, cliente frustrado, estorno, reputa&#231;&#227;o.</mark></p><p><strong>Essa pergunta n&#227;o tem resposta t&#233;cnica &#8212; tem resposta de produto</strong>. &#201; por isso que decidir cachear, e por quanto tempo, <strong>exige uma conversa expl&#237;cita com quem entende o impacto de neg&#243;cio daquele dado espec&#237;fico, n&#227;o uma decis&#227;o unilateral de engenharia tomada s&#243; olhando para o gr&#225;fico de lat&#234;ncia</strong>.</p><h3><strong>Por que invalidar uma chave simples &#233; f&#225;cil, e invalidar uma chave derivada n&#227;o &#233;</strong></h3><p>Vale um exemplo concreto do primeiro perigo da lista, porque a diferen&#231;a entre os dois casos &#233; onde a maioria dos bugs de cache realmente nasce:</p><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;5111b9b2-863b-446d-9db7-4d3de9a5e350&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">invalida&#231;&#227;o simples &#8212; um dado, uma chave

// atualizar o perfil de um usu&#225;rio
atualizarUsuario(usuario)
cache.invalidar(`usuario:${usuario.id}`)  // direto, &#243;bvio, uma chave</code></pre></div><div class="highlighted_code_block" data-attrs="{&quot;language&quot;:&quot;plaintext&quot;,&quot;nodeId&quot;:&quot;ff437c70-feaf-4d6e-9fd7-4d97c207f3c2&quot;}" data-component-name="HighlightedCodeBlockToDOM"><pre class="shiki"><code class="language-plaintext">invalida&#231;&#227;o derivada &#8212; um dado, dezenas de chaves poss&#237;veis

// atualizar UM produto, que aparece em v&#225;rias listagens cacheadas
atualizarProduto(produto)
cache.invalidar(`produto:${produto.id}`)          // f&#225;cil
cache.invalidar(`categoria:${produto.categoriaId}`)  // esse produto est&#225; numa listagem por categoria...
cache.invalidar(`vitrine:destaques`)             // ...que tamb&#233;m aparece na vitrine...
cache.invalidar(`busca:*termo-relacionado*`)     // ...e em resultados de busca. Esqueceu um? Bug sile</code></pre></div><p>A primeira invalida&#231;&#227;o &#233; trivial. A segunda &#233; onde &#8220;a coisa mais dif&#237;cil da computa&#231;&#227;o&#8221; ganha o apelido: cada novo lugar onde aquele dado aparece, de forma direta ou agregada, &#233; mais uma chave que algu&#233;m precisa lembrar de invalidar &#8212; e esquecer uma &#250;nica delas n&#227;o gera erro nenhum, s&#243; um dado desatualizado silencioso em produ&#231;&#227;o.</p><h2><strong>Estrat&#233;gias cl&#225;ssicas de caching</strong></h2><p><strong>Cache-Aside (Lazy Loading)</strong></p><p>O c&#243;digo busca no cache primeiro; se n&#227;o encontrar, busca no banco e popula o cache para a pr&#243;xima vez. &#201; a estrat&#233;gia mais comum, mais simples de implementar, e a que d&#225; mais controle expl&#237;cito ao pr&#243;prio c&#243;digo da aplica&#231;&#227;o.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2xeO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2xeO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png 424w, https://substackcdn.com/image/fetch/$s_!2xeO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png 848w, https://substackcdn.com/image/fetch/$s_!2xeO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png 1272w, https://substackcdn.com/image/fetch/$s_!2xeO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2xeO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png" width="724" height="1078" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1078,&quot;width&quot;:724,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:171710,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/207909690?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2xeO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png 424w, https://substackcdn.com/image/fetch/$s_!2xeO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png 848w, https://substackcdn.com/image/fetch/$s_!2xeO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png 1272w, https://substackcdn.com/image/fetch/$s_!2xeO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe2ba622c-e766-41e8-97f0-0804aa20e954_724x1078.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong><span>&#10003; Pr&#243;s</span></strong></p><ul><li><p>S&#243; entra em cache o dado que &#233; efetivamente pedido &#8212; sem gastar mem&#243;ria com o que ningu&#233;m l&#234;.</p></li><li><p>O cache pode cair inteiro sem quebrar a aplica&#231;&#227;o: pior caso, tudo vira miss e volta a bater no banco.</p></li></ul><p><strong><span>&#10007; Contras &#8212; aten&#231;&#227;o no debug</span></strong></p><ul><li><p>A primeira requisi&#231;&#227;o depois de um miss &#233; sempre mais lenta &#8212; se acontecer em massa (stampede), o pico atinge o banco de uma vez.</p></li><li><p>A responsabilidade de invalidar fica espalhada pelo c&#243;digo da aplica&#231;&#227;o &#8212; f&#225;cil esquecer um caminho de escrita, <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">gerando stale data silencioso</mark>.</p></li></ul><blockquote><p><strong><span>Pergunta antes de adotar</span></strong></p><p><em>&#8220;Quantos lugares no c&#243;digo escrevem esse dado &#8212; e cada um deles sabe que precisa invalidar a mesma chave, ou s&#243; o caminho &#8216;principal&#8217; lembra disso?&#8221;</em></p></blockquote><p><strong>Write-Through / Write-Behind</strong></p><p>A escrita passa pelo cache antes de ir para o banco (Write-Through, s&#237;ncrono e mais seguro) ou &#233; enviada ao banco de forma ass&#237;ncrona depois (Write-Behind, mais r&#225;pido para quem escreve, com uma janela de risco se o cache cair antes de persistir).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!2nqj!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!2nqj!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png 424w, https://substackcdn.com/image/fetch/$s_!2nqj!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png 848w, https://substackcdn.com/image/fetch/$s_!2nqj!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png 1272w, https://substackcdn.com/image/fetch/$s_!2nqj!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!2nqj!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png" width="872" height="641.707351891506" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1031,&quot;width&quot;:1401,&quot;resizeWidth&quot;:872,&quot;bytes&quot;:240415,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/207909690?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!2nqj!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png 424w, https://substackcdn.com/image/fetch/$s_!2nqj!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png 848w, https://substackcdn.com/image/fetch/$s_!2nqj!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png 1272w, https://substackcdn.com/image/fetch/$s_!2nqj!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd99961dd-90a7-40bd-bb3d-0bb6e5020b62_1401x1031.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong><span>&#10003; Pr&#243;s</span></strong></p><ul><li><p>Cache e banco tendem a ficar sincronizados por constru&#231;&#227;o &#8212; muito menor chance de stale data comparado ao Cache-Aside.</p></li><li><p>Write-Behind reduz a lat&#234;ncia percebida pelo cliente, j&#225; que a confirma&#231;&#227;o n&#227;o espera o banco responder.</p></li></ul><p><strong><span>&#10007; Contras &#8212; aten&#231;&#227;o no debug</span></strong></p><ul><li><p>Write-Through adiciona lat&#234;ncia a toda escrita, porque agora ela depende da sa&#250;de de dois sistemas em vez de um.</p></li><li><p>Write-Behind cria uma janela real de perda de dado: se o cache cair antes de persistir no banco, a escrita &#8220;confirmada&#8221; nunca chegou l&#225;.</p></li></ul><blockquote><p><strong><span>Pergunta antes de adotar</span></strong></p><p><em>&#8220;Se eu escolher Write-Behind, e o cache cair no meio da janela ass&#237;ncrona, que dado exatamente eu aceito perder &#8212; e algu&#233;m do neg&#243;cio j&#225; concordou explicitamente com essa perda?&#8221;</em></p></blockquote><p><strong>Read-Through</strong></p><p>A aplica&#231;&#227;o s&#243; conversa com o cache &#8212; nunca diretamente com o banco &#8212; e &#233; o pr&#243;prio cache que se encarrega de buscar a fonte quando necess&#225;rio. Centraliza a l&#243;gica de busca,<mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> mas cria mais depend&#234;ncia da camada de cache estar sempre saud&#225;vel</mark>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Bbhb!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Bbhb!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png 424w, https://substackcdn.com/image/fetch/$s_!Bbhb!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png 848w, https://substackcdn.com/image/fetch/$s_!Bbhb!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png 1272w, https://substackcdn.com/image/fetch/$s_!Bbhb!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Bbhb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png" width="757" height="812" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:812,&quot;width&quot;:757,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:133949,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/207909690?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Bbhb!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png 424w, https://substackcdn.com/image/fetch/$s_!Bbhb!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png 848w, https://substackcdn.com/image/fetch/$s_!Bbhb!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png 1272w, https://substackcdn.com/image/fetch/$s_!Bbhb!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd3c9899d-9dfc-48bd-bfee-3bfdadfcd57b_757x812.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong><span>&#10003; Pr&#243;s</span></strong></p><ul><li><p>A l&#243;gica de &#8220;buscar na fonte se n&#227;o tiver em cache&#8221; fica centralizada numa &#250;nica camada, em vez de duplicada em cada ponto do c&#243;digo que l&#234; esse dado.</p></li><li><p>Simplifica bastante o c&#243;digo da aplica&#231;&#227;o, que passa a conhecer s&#243; uma interface &#8212; o cache &#8212; em vez de duas.</p></li></ul><p><strong><span>&#10007; Contras &#8212; aten&#231;&#227;o no debug</span></strong></p><ul><li><p>Cria uma depend&#234;ncia mais forte: se o cache cair, a aplica&#231;&#227;o n&#227;o sabe mais falar diretamente com o banco, porque nunca foi desenhada para isso.</p></li><li><p>Debugar um dado errado &#233; mais opaco &#8212; a l&#243;gica de busca mora dentro do provedor de cache, fora da vis&#227;o direta do c&#243;digo da aplica&#231;&#227;o.</p></li></ul><blockquote><p><strong><span>Pergunta antes de adotar</span></strong></p><p><em>&#8220;Se a camada de cache ficar indispon&#237;vel, minha aplica&#231;&#227;o tem algum caminho alternativo para falar com o banco &#8212; ou o Read-Through virou, sem perceber, um ponto &#250;nico de falha disfar&#231;ado de otimiza&#231;&#227;o?&#8221;</em></p></blockquote><h2><strong>O trade-off operacional: mais um componente na sua infra</strong></h2><h4><strong><span>Em termos simples</span></strong></h4><div class="callout-block" data-callout="true"><p>Todo componente novo que voc&#234; adiciona &#224; arquitetura &#233;, tamb&#233;m, mais uma coisa que pode quebrar &#224;s tr&#234;s da manh&#227; &#8212; e algu&#233;m precisa saber operar, monitorar e pagar por ela.</p></div><p>Adicionar Redis ou Memcached &#224; arquitetura n&#227;o &#233; uma decis&#227;o isolada de c&#243;digo &#8212; &#233; adicionar mais uma inst&#226;ncia para monitorar, mais um painel de m&#233;tricas para manter, custo real de transfer&#234;ncia de rede, gerenciamento de chaves em escala, e uma preocupa&#231;&#227;o adicional de seguran&#231;a sobre o que est&#225; sendo guardado em mem&#243;ria (e se precisa de criptografia). Nenhum desses custos aparece no c&#243;digo da feature; todos eles aparecem na opera&#231;&#227;o, semanas ou meses depois.</p><p><strong>&#10003; Vale a pena quando</strong></p><p>O ganho de lat&#234;ncia e a redu&#231;&#227;o de carga no banco s&#227;o mensur&#225;veis e relevantes para o neg&#243;cio &#8212; e existe capacidade real de operar mais um componente de infraestrutura com o mesmo cuidado que se d&#225; ao banco principal.</p><p><strong>&#10007; N&#227;o vale a pena quando</strong></p><p>O problema real &#233; uma query sem &#237;ndice, um algoritmo ineficiente, ou um volume de tr&#225;fego que nem justifica a complexidade operacional extra &#8212; cachear, nesse caso, s&#243; adiciona um componente novo para quebrar sem resolver a causa raiz.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/cachear-ou-nao-cachear-eis-a-questao/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/cachear-ou-nao-cachear-eis-a-questao/comments"><span>Deixe um coment&#225;rio</span></a></p><h2><strong>O que dizem os que j&#225; pagaram o pre&#231;o do cache</strong></h2><p>Tudo o que foi dito at&#233; aqui &#233; an&#225;lise t&#233;cnica &#8212; matriz de decis&#227;o, TTL, pol&#237;tica de invalida&#231;&#227;o. Mas existe outra fonte de evid&#234;ncia que vale a pena ouvir: o que engenheiros com d&#233;cadas de sistemas em produ&#231;&#227;o, e mais de uma cicatriz de cache mal calibrado, escreveram sobre o assunto depois de j&#225; terem pago a conta. <strong>Tr&#234;s vozes, tr&#234;s &#226;ngulos diferentes do mesmo aviso.</strong></p><blockquote><p>&#8220;Existem apenas duas coisas dif&#237;ceis em Ci&#234;ncia da Computa&#231;&#227;o: invalida&#231;&#227;o de cache e dar nomes &#224;s coisas.&#8221;<em> - <span>Phil Karlton</span></em></p></blockquote><p>A frase j&#225; tem mais de trinta anos e virou clich&#234; de tanto ser repetida &#8212; o que, paradoxalmente, &#233; a prova de que ela nunca deixou de ser verdadeira. Karlton n&#227;o estava fazendo piada sobre dificuldade t&#233;cnica; estava apontando que tanto nomear algo quanto invalidar um cache exigem a mesma coisa dif&#237;cil: modelar corretamente uma realidade que muda com o tempo. Um nome errado e uma chave de cache n&#227;o invalidada s&#227;o o mesmo tipo de erro &#8212; a representa&#231;&#227;o parou de corresponder &#224; realidade, e ningu&#233;m percebeu no momento em que isso aconteceu.</p><blockquote><p><em>&#8220;Cuidado ao colocar caches em muitos lugares!&#8221; - <span>Sam Newman &#8212; Building Microservices</span></em></p></blockquote><p>Newman escreve isso depois de descrever d&#233;cadas de sistemas distribu&#237;dos que ele pr&#243;prio ajudou a construir &#8212; e o aviso conecta direto com o que j&#225; conversamos nesse artigo: cache em camadas m&#250;ltiplas (CDN, aplica&#231;&#227;o, banco) n&#227;o soma prote&#231;&#227;o, multiplica a chance de alguma camada ficar com dado velho enquanto as outras j&#225; atualizaram. Ele tamb&#233;m escreve, no mesmo livro, que entender o caminho completo do dado &#8212; da fonte at&#233; o destino final &#8212; &#233; o que realmente revela a complexidade de um cache, n&#227;o a implementa&#231;&#227;o da chave em si. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">A li&#231;&#227;o pr&#225;tica &#233;: antes de adicionar mais uma camada de cache, pergunte quantas j&#225; existem no caminho desse dado, porque provavelmente voc&#234; n&#227;o &#233; a primeira pessoa a pensar nisso.</mark></p><blockquote><p><em>&#8220;...t&#227;o diferente do comportamento das vari&#225;veis &#8203;&#8203;em um programa normal de thread &#250;nica.&#8221; - <span>Martin Kleppmann &#8212; Designing Data-Intensive Applications</span></em></p></blockquote><p>Kleppmann usa essa frase para descrever por que consist&#234;ncia eventual &#233; dif&#237;cil para desenvolvedores. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">A maioria dos bugs de cache nasce porque o engenheiro, mentalmente, ainda est&#225; raciocinando como se o sistema fosse uma &#250;nica vari&#225;vel numa &#250;nica thread &#8212; muda aqui, l&#234; ali, sempre na ordem esperada</mark>. Um sistema com cache nunca &#233; isso: <strong>&#233; sempre m&#250;ltiplas c&#243;pias do mesmo dado, em momentos diferentes de atualiza&#231;&#227;o, sendo lidas por m&#250;ltiplos consumidores ao mesmo tempo</strong>. O bug n&#227;o &#233; a implementa&#231;&#227;o; &#233; o modelo mental que n&#227;o corresponde &#224; arquitetura real.</p><p>O fio que conecta os tr&#234;s: nenhum deles trata cache como uma decis&#227;o puramente local, resolvida no momento em que a chave &#233; escrita. Os tr&#234;s tratam como uma decis&#227;o sist&#234;mica, cujo pre&#231;o &#233; <strong>pago depois</strong> &#8212; n&#227;o pelo engenheiro que configurou o TTL numa tarde tranquila, mas por quem estiver de plant&#227;o no dia em que a suposi&#231;&#227;o de &#8220;isso est&#225; sempre atualizado o suficiente&#8221; deixar de ser verdade.</p><h2><strong>O checklist do engenheiro pragm&#225;tico</strong></h2><p>Antes de configurar a primeira chave de cache, quatro perguntas honestas valem mais do que qualquer benchmark de performance:</p><ol><li><p><span>O banco de dados </span><strong><span>j&#225; est&#225; otimizado</span></strong><span> &#8212; &#237;ndices no lugar, queries revisadas &#8212; antes de adicionar uma camada de cache por cima do problema?</span></p></li><li><p><span>A m&#233;trica real de acesso (leitura vs. escrita) </span><strong><span>justifica</span></strong><span> o uso de cache para esse dado espec&#237;fico, ou &#233; uma suposi&#231;&#227;o n&#227;o verificada?</span></p></li><li><p><span>O time </span><strong><span>definiu o TTL ideal</span></strong><span> para cada chave, com base no impacto de neg&#243;cio de um dado desatualizado &#8212; n&#227;o num n&#250;mero redondo escolhido de forma arbitr&#225;ria?</span></p></li><li><p><span>A aplica&#231;&#227;o </span><strong><span>sobrevive</span></strong><span> se o cache cair agora mesmo &#8212; </span><em><span>graceful degradation</span></em><span> real, testada, n&#227;o apenas assumida?</span></p></li></ol><p><strong>Cache n&#227;o &#233; uma pergunta de &#8220;como implementar&#8221;. &#201; uma pergunta de &#8220;o que meu sistema, meu neg&#243;cio e minha opera&#231;&#227;o est&#227;o dispostos a pagar&#8221; &#8212; em lat&#234;ncia tolerada, em complexidade operacional, e em confian&#231;a de que tudo continua de p&#233; no dia em que o cache, inevitavelmente, cair.</strong></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><ul><li><p>Fundamentos de hierarquia de mem&#243;ria e cache em arquitetura de computadores (L1/L2/L3, d&#233;cadas de 1960&#8211;70)</p></li><li><p>Padr&#245;es consolidados da ind&#250;stria: Cache-Aside, Write-Through, Write-Behind, Read-Through</p></li><li><p>Cache Stampede / Thundering Herd &#8212; padr&#227;o de falha documentado em sistemas de alta escala</p></li><li><p>Phil Karlton &#8212; aforismo &#8220;two hard things in computer science&#8221; (via Martin Fowler&#8217;s bliki)</p></li><li><p>Sam Newman &#8212; Building Microservices: Designing Fine-Grained Systems</p></li><li><p>Martin Kleppmann &#8212; Designing Data-Intensive Applications</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Por que os engenheiros mais valiosos do mercado gastam mais tempo conversando do que codando]]></title><description><![CDATA[Independentemente do qu&#227;o brilhante seja a sua arquitetura ou qu&#227;o limpo seja o seu c&#243;digo, se o software n&#227;o resolver o problema certo do neg&#243;cio, ele falhou. &#8212; Eric Evans, no livro DDD]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/por-que-os-engenheiros-mais-valiosos</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/por-que-os-engenheiros-mais-valiosos</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Fri, 17 Jul 2026 20:12:32 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/2fc675f2-df74-4052-a158-6c5182efb5ac_5472x3648.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<div class="callout-block" data-callout="true"><p>H&#225; uma cena que se repete em quase toda empresa de tecnologia: algu&#233;m recebe uma tarefa, abre o editor e come&#231;a a digitar antes de terminar de entender o problema. Duas semanas depois, o time descobre que construiu a coisa errada &#8212; com perfei&#231;&#227;o t&#233;cnica. Este texto &#233; sobre o profissional que faz o oposto.</p></div><p>Existe um mito silencioso na engenharia de software: o de que o valor de um profissional se mede pela velocidade com que ele produz c&#243;digo. &#201; um mito confort&#225;vel, porque c&#243;digo &#233; vis&#237;vel, mensur&#225;vel, version&#225;vel. Conversa n&#227;o aparece no <em>git blame</em>.</p><p>Mas observe qualquer engenheiro s&#234;nior de verdade em a&#231;&#227;o &#8212; n&#227;o o que tem mais anos de casa, mas o que as empresas disputam a peso de ouro &#8212; e voc&#234; vai notar um padr&#227;o contraintuitivo: ele passa uma fra&#231;&#227;o desproporcional do tempo perguntando, parafraseando, desenhando no quadro, discordando educadamente, at&#233; <em>ent&#227;o</em> abrir o editor. E quando abre, escreve r&#225;pido, porque j&#225; sabe exatamente o que est&#225; construindo e por qu&#234;.</p><h2><strong>O c&#243;digo &#233; o efeito, n&#227;o a causa</strong></h2><p>O que diferencia esse perfil n&#227;o &#233; talento de digita&#231;&#227;o. &#201; que ele enxerga o fluxo de valor de ponta a ponta &#8212; do problema de neg&#243;cio at&#233; o resultado que o usu&#225;rio sente &#8212; e trata o c&#243;digo como <strong>um meio para chegar l&#225;</strong>, n&#227;o como o objetivo em si. Isso muda a ordem das opera&#231;&#245;es: primeiro alinhar o que &#233; valioso e por qu&#234;, depois desenhar o caminho t&#233;cnico, s&#243; ent&#227;o escrever.</p><div class="pullquote"><p>&#8220;Bons programadores escrevem c&#243;digo que os humanos conseguem entender.&#8221;</p><p><span>Martin Fowler &#8212; Refactoring: Improving the Design of Existing Code</span></p></div><p>Fowler escreveu isso pensando em legibilidade de c&#243;digo, mas a ideia vale um passo antes: escrever para ser entendido tamb&#233;m &#233; o que se faz numa conversa de alinhamento. Quem s&#243; escreve para a m&#225;quina compilar tende a pular a etapa de escrever &#8212; falado ou em texto &#8212; para as pessoas entenderem.</p><h2><span>A pergunta que todo mundo j&#225; fez</span></h2><p><em><span>Se conversar n&#227;o gera commits, por que isso deveria contar como trabalho de verdade?</span></em></p><p>Porque comunica&#231;&#227;o malfeita &#233; o maior gerador de retrabalho que existe &#8212; e retrabalho &#233; c&#243;digo tamb&#233;m, s&#243; que descartado. <strong>Frederick Brooks</strong>, arquiteto de software do IBM System/360 e autor de <em>The Mythical Man-Month</em>, mostrou que projetos grandes n&#227;o falham por falta de gente digitando: falham porque o custo de coordena&#231;&#227;o cresce mais r&#225;pido que o n&#250;mero de pessoas. Ele chamou de &#8220;<a href="https://arxiv.org/abs/1811.04315">conceitual integrity</a>&#8221; <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">a ideia de que um sistema bom precisa vir de poucas mentes alinhadas &#8212; n&#227;o porque comit&#234;s s&#227;o burros, mas porque cada nova pessoa multiplica os caminhos de comunica&#231;&#227;o necess&#225;rios para todo mundo entender a mesma coisa</mark>.</p><p>Na vis&#227;o dele, publicada em 1975 e ainda citada hoje, <span>&#8220;conceptual integrity is the most important consideration in system design&#8221;</span> &#8212; a integridade conceitual &#233; a considera&#231;&#227;o mais importante no design de um sistema. <strong>Conversa &#233; como essa integridade se propaga entre pessoas antes de virar c&#243;digo.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FOAU!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FOAU!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png 424w, https://substackcdn.com/image/fetch/$s_!FOAU!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png 848w, https://substackcdn.com/image/fetch/$s_!FOAU!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png 1272w, https://substackcdn.com/image/fetch/$s_!FOAU!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FOAU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png" width="721" height="319" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:319,&quot;width&quot;:721,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:22150,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/197857043?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FOAU!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png 424w, https://substackcdn.com/image/fetch/$s_!FOAU!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png 848w, https://substackcdn.com/image/fetch/$s_!FOAU!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png 1272w, https://substackcdn.com/image/fetch/$s_!FOAU!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4708df15-db2d-4e92-ab56-93741fc6c80a_721x319.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2><strong>O pre&#231;o do mal-entendido</strong></h2><p><strong>Barry Boehm</strong> estudou projetos reais na TRW e na IBM e chegou a uma conclus&#227;o que hoje parece &#243;bvia mas continua sendo ignorada na pr&#225;tica: um mal-entendido descoberto na fase de requisitos custa uma fra&#231;&#227;o do que custa descobri-lo depois do sistema estar em produ&#231;&#227;o &#8212; em an&#225;lises citadas por engenheiros da NASA, essa diferen&#231;a chega &#224; ordem de cem vezes. A curva ficou menos exponencial com pr&#225;ticas modernas de entrega cont&#237;nua, mas a dire&#231;&#227;o da seta nunca mudou: <em>quanto antes voc&#234; entende errado, mais barato &#233; corrigir</em>.</p><p><strong>Kent Beck</strong>, criador do Extreme Programming, levou essa ideia a s&#233;rio de um jeito diferente: em vez de tentar acertar tudo de uma vez no papel, ele prop&#244;s achatar a curva com ciclos de feedback curt&#237;ssimos &#8212; testes antes do c&#243;digo, integra&#231;&#227;o constante, conversas frequentes no lugar de documentos grossos escritos uma &#250;nica vez. A li&#231;&#227;o comum aos dois: o problema nunca foi documentar demais ou de menos, foi <em>quando</em> voc&#234; descobre que entendeu errado.</p><blockquote><p>&#8220;...&#233; sempre um problema de pessoas.&#8221; - <span>Kent Beck &#8212; Extreme Programming Explained</span></p></blockquote><p>Beck escreveu isso sobre um contexto espec&#237;fico &#8212; n&#227;o importa o que o cliente diga que &#233; o problema t&#233;cnico, na raiz costuma haver um desalinhamento entre pessoas. <strong>&#201; por isso que times que conversam bem corrigem defeitos de entendimento antes que eles virem defeitos de c&#243;digo.</strong></p><h4><span>Pergunta seguinte</span></h4><p><em><span>Ent&#227;o a solu&#231;&#227;o &#233; documentar tudo antes de sair codando?</span></em></p><p>N&#227;o exatamente &#8212; &#233; pensar em prosa antes de pensar em sintaxe. A Amazon institucionalizou isso com o processo interno conhecido como <em><a href="https://aws.amazon.com/pt/blogs/smb/working-backwards-to-drive-customer-experience-and-smb-innovation-forward/">Working Backwards</a></em>: antes de qualquer linha de c&#243;digo, o time escreve um comunicado de imprensa fict&#237;cio e uma lista de perguntas frequentes descrevendo o produto j&#225; pronto, do ponto de vista do cliente. Jeff Bezos resumiu a l&#243;gica dizendo que a empresa <span>&#8220;start[s] with the customer and work[s] backwards&#8221;</span> &#8212; come&#231;a pelo cliente e trabalha de tr&#225;s para frente.</p><p>O caso mais citado &#233; o da AWS: Bezos e Andy Jassy identificaram a oportunidade em 2004, mas os servi&#231;os fundacionais (S3 e EC2) s&#243; foram lan&#231;ados em 2006 &#8212; mais de um ano foi gasto escrevendo, revisando e discutindo esses documentos antes de qualquer infraestrutura ser constru&#237;da. A empresa tamb&#233;m substituiu apresenta&#231;&#245;es de slides por memorandos narrativos de at&#233; seis p&#225;ginas nas reuni&#245;es de decis&#227;o, exatamente porque escrever em prosa for&#231;a a explicitar rela&#231;&#245;es de causa e efeito que um slide deixa passar batido.</p><h2><strong>Por que isso n&#227;o &#233; burocracia</strong></h2><p>Vale uma ressalva cient&#237;fica aqui. O pr&#243;prio Brooks fez uma compara&#231;&#227;o reveladora: a f&#237;sica lida com sistemas t&#227;o complexos quanto o software, mas o f&#237;sico trabalha com f&#233; de que existem princ&#237;pios unificadores por tr&#225;s do caos &#8212; a complexidade da natureza &#233;, em certo sentido, honesta. A complexidade de um sistema de software, ao contr&#225;rio, &#233; muitas vezes arbitr&#225;ria: nasce de decis&#245;es humanas, de interfaces que mudam de equipe para equipe, <strong><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">de contexto que n&#227;o est&#225; escrito em lugar nenhum a n&#227;o ser na cabe&#231;a de quem participou da conversa original.</mark></strong></p><p>&#201; por isso que engenheiros de alto n&#237;vel conversam antes: n&#227;o para seguir processo, mas porque a complexidade arbitr&#225;ria do software s&#243; existe na cabe&#231;a das pessoas at&#233; algu&#233;m a transformar em linguagem compartilhada &#8212; falada, escrita ou desenhada. <em><strong>C&#243;digo &#233; a &#250;ltima tradu&#231;&#227;o dessa cadeia, n&#227;o a primeira.</strong></em></p><h2><strong>T&#233;cnicas para n&#227;o errar a especifica&#231;&#227;o, o entendimento e os testes</strong></h2><h3>Especifica&#231;&#227;o</h3><ul><li><p><strong>Reescreva o pedido com suas pr&#243;prias palavras</strong> antes de abrir o editor, e mande de volta para quem pediu confirmar. Se a par&#225;frase n&#227;o bater, o requisito n&#227;o estava claro &#8212; s&#243; parecia estar.</p></li><li><p><strong>Pergunte pelo que n&#227;o deve acontecer</strong>, n&#227;o s&#243; pelo que deve. Casos de borda e comportamento n&#227;o-funcional (performance, seguran&#231;a, limites) raramente aparecem no pedido original.</p></li><li><p><strong>Escreva um mini &#8220;PR/FAQ&#8221; antes de uma POC</strong>: qual pergunta este experimento responde e o que provaria que ele falhou. Sem isso, toda POC &#8220;funciona&#8221; &#8212; porque ningu&#233;m definiu o que seria fracasso.</p></li></ul><h3>Entendimento</h3><ul><li><p><strong>Em sistemas legados, mapeie o comportamento atual antes de mudar</strong> (testes de caracteriza&#231;&#227;o / golden master). Documente o que o sistema <em>faz</em> hoje, n&#227;o o que a documenta&#231;&#227;o diz que ele deveria fazer &#8212; raramente s&#227;o a mesma coisa.</p></li><li><p><strong>Traga algu&#233;m de fora da equipe t&#233;cnica</strong> (produto, suporte, dados) para ouvir sua leitura do problema antes de estimar prazo. Se voc&#234; n&#227;o consegue explicar em termos simples, ainda n&#227;o entendeu o suficiente para estimar.</p></li><li><p><strong>Em reuni&#245;es de kickoff, seja o &#250;ltimo a falar sobre solu&#231;&#227;o.</strong> Alinhar o problema antes da solu&#231;&#227;o evita que a primeira ideia t&#233;cnica vire &#226;ncora da conversa inteira.</p></li></ul><h3>Testes</h3><ul><li><p><strong>Escreva o teste que evidencia a inten&#231;&#227;o</strong> &#8212; dado, quando, ent&#227;o &#8212; antes ou junto da implementa&#231;&#227;o, mesmo sem seguir TDD &#224; risca. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Um teste escrito depois tende a confirmar o c&#243;digo, n&#227;o a inten&#231;&#227;o original</mark>.</p></li><li><p><strong>Em MVPs, escreva por escrito o que deliberadamente n&#227;o ser&#225; testado</strong> (non-goals). <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Isso transforma d&#233;bito t&#233;cnico invis&#237;vel em decis&#227;o expl&#237;cita</mark>, revis&#225;vel mais tarde.</p></li><li><p><strong>Revis&#227;o de c&#243;digo deve avaliar se o c&#243;digo cumpre o que a conversa combinou</strong>, n&#227;o s&#243; se est&#225; limpo ou idiom&#225;tico. Sintaxe correta n&#227;o &#233; o mesmo que inten&#231;&#227;o correta.</p></li></ul><h2><strong>3 pr&#225;ticas para levar &#224; pr&#243;xima conversa com seu gestor ou PM</strong></h2><p>Tudo isso pode soar abstrato se voc&#234; ainda n&#227;o tem uma d&#233;cada de bagagem para &#8220;simplesmente perceber&#8221; quando uma conversa evitaria um problema. </p><p>As tr&#234;s pr&#225;ticas abaixo n&#227;o exigem experi&#234;ncia &#8212; s&#243; um script pronto e a coragem de us&#225;-lo antes de abrir o editor.</p><h3>1 &#183; Antes de come&#231;ar &#8212; traduza o pedido em uma frase que se responde com &#8220;sim&#8221; ou &#8220;n&#227;o&#8221;</h3><p>Voc&#234; n&#227;o precisa entender o neg&#243;cio a fundo para fazer isso. Precisa s&#243; devolver o que ouviu, em uma frase curta, e esperar confirma&#231;&#227;o antes de escrever a primeira linha de c&#243;digo.</p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">&#8220;Antes de come&#231;ar, s&#243; confirmando: o objetivo &#233; </mark><em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">[o que voc&#234; entendeu]</mark></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">, para que </mark><em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">[resultado que a pessoa quer]</mark></em><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);"> aconte&#231;a. &#201; isso mesmo?&#8221;</mark></p><p>Se a resposta vier r&#225;pida e direta, voc&#234; entendeu. Se vier uma corre&#231;&#227;o &#8212; &#8220;n&#227;o bem, na verdade...&#8221; &#8212; voc&#234; acabou de evitar dias de trabalho na dire&#231;&#227;o errada, pelo custo de uma mensagem.</p><h3>2 &#183; Durante a conversa &#8212; pergunte pelo cen&#225;rio de fracasso, n&#227;o s&#243; pelo de sucesso</h3><p>PMs e gestores costumam descrever o que a funcionalidade deve fazer. Raramente descrevem o que ela n&#227;o pode quebrar, porque isso parece &#243;bvio para quem pensa no produto o dia inteiro. N&#227;o &#233; &#243;bvio para quem est&#225; chegando agora &#8212; e perguntar por isso n&#227;o exige jarg&#227;o t&#233;cnico.</p><p><mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">"O que aconteceria se essa parte desse errado, ou se ela simplesmente n&#227;o existisse? Isso me ajuda a saber o que eu n&#227;o posso quebrar sem perceber."</mark></p><p>Essa pergunta funciona em qualquer n&#237;vel de senioridade porque desloca a conversa do &#8220;o que construir&#8221; para &#8220;o que importa de verdade&#8221; &#8212; que &#233; exatamente o tipo de informa&#231;&#227;o que normalmente s&#243; aparece depois, em produ&#231;&#227;o.</p><h3>3 &#183; Depois da conversa &#8212; feche com um resumo escrito de tr&#234;s linhas</h3><p>N&#227;o precisa ser um documento. Precisa existir, e precisa ser curto o bastante para algu&#233;m corrigir em quinze segundos, no mesmo dia &#8212; n&#227;o descobrir o mal-entendido na revis&#227;o final.</p><p><strong>Entendi:</strong><span> </span><em>[o problema, em uma frase]</em><br><strong>Vou fazer:</strong><span> </span><em>[o que voc&#234; vai construir]</em><br><strong>Pode dar errado se:</strong><span> </span><em>[a maior d&#250;vida ou risco que voc&#234; identificou]</em></p><p>Esse mini-resumo &#233; a vers&#227;o rascunho do memorando narrativo da Amazon citado antes: for&#231;a voc&#234; a organizar o pensamento em texto simples, e d&#225; para quem pediu a tarefa uma chance barata de corrigir o rumo &#8212; exatamente o ponto da curva de custo em que a corre&#231;&#227;o ainda &#233; quase de gra&#231;a.</p><div class="pullquote"><p>&#8220;A &#250;nica maneira de ir r&#225;pido &#233; ir bem.&#8221;</p><p><span>Robert C. Martin &#8212; Clean Architecture</span></p></div><p>Vale para c&#243;digo, e vale para o que vem antes dele. <mark data-color="#ffff00" style="background-color: rgb(255, 255, 0); color: rgb(0, 0, 0);">Pular a conversa para &#8220;ganhar tempo&#8221; costuma custar mais tempo depois &#8212; s&#243; que espalhado, invis&#237;vel, disfar&#231;ado de imprevisto</mark>.</p><p>O perfil mais disputado do mercado hoje n&#227;o &#233; o que escreve mais r&#225;pido. &#201; o que entende o fluxo de valor de ponta a ponta &#8212; do problema de neg&#243;cio ao resultado sentido pelo usu&#225;rio &#8212; <strong>e usa o c&#243;digo como meio, n&#227;o como fim</strong>. Conversar antes de codar n&#227;o &#233; perder tempo: &#233; onde esse profissional realmente trabalha.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Sua vez</strong></h2><p>Qual foi a vez em que uma conversa de dez minutos evitou uma semana de retrabalho? Ou o contr&#225;rio &#8212; o que aconteceu quando o time codificou antes de entender? Conte abaixo.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/por-que-os-engenheiros-mais-valiosos/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/por-que-os-engenheiros-mais-valiosos/comments"><span>Deixe um coment&#225;rio</span></a></p><p></p><ul><li><p>Frederick P. Brooks Jr. &#8212; <em>The Mythical Man-Month: Essays on Software Engineering</em> (1975/1995)</p></li><li><p>Colin Bryar &amp; Bill Carr &#8212; <em>Working Backwards</em>; processo PR/FAQ da Amazon (workingbackwards.com)</p></li><li><p>Barry Boehm &#8212; <em>Software Engineering Economics</em> (1981); s&#237;ntese em relat&#243;rio da NASA sobre custo de erro</p></li><li><p>Kent Beck &#8212; <em>Extreme Programming Explained</em> (2000), sobre o achatamento da curva de custo de mudan&#231;a e mal-entendidos como problema de pessoas</p></li><li><p>Martin Fowler &#8212; <em>Refactoring: Improving the Design of Existing Code</em> (1999)</p></li><li><p>Robert C. Martin &#8212; <em>Clean Architecture: A Craftsman&#8217;s Guide to Software Structure and Design</em> (2017)</p></li></ul>]]></content:encoded></item><item><title><![CDATA[Arquitetura Self-Healing em Sistemas De Crédito e Cobrança]]></title><description><![CDATA[Tudo d&#225; errado, o tempo todo.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/arquitetura-self-healing-em-sistemas</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/arquitetura-self-healing-em-sistemas</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Tue, 20 Jan 2026 13:02:10 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fd4127d1-c4f1-48ad-b9fd-8081cd233421_800x500.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Imagine o seguinte cen&#225;rio: hoje &#233; o 5&#186; dia de atraso da fatura de cart&#227;o de cr&#233;dito de um cliente. Pelas regras de neg&#243;cio do banco, o cart&#227;o deveria ser bloqueado automaticamente para impedir novas compras.</p><p>No mundo ideal dos diagramas de arquitetura, o fluxo &#233; lindo:</p><ol><li><p>O Core Banc&#225;rio detecta o atraso.</p></li><li><p>Um evento <code>CreditCardOverdue</code> &#233; publicado no barramento.</p></li><li><p>O microsservi&#231;o de Cart&#245;es consome a mensagem e muda o status para <code>BLOCKED</code>.</p></li></ol><p>Mas n&#243;s somos programadores pragm&#225;ticos e sabemos que o mundo real &#233; ca&#243;tico. E se o <em>consumer</em> de Cart&#245;es estiver fora do ar justo nesse milissegundo? E se a mensagem cair numa <em>Dead Letter Quee</em> (DLQ) e for esquecida l&#225;? E se o payload do evento tiver um caractere inv&#225;lido que quebra o parser (a temida <em>Poison Pill</em>)?</p><p><strong>O resultado &#233; o sil&#234;ncio.</strong></p><p>O evento falha, o sistema n&#227;o tenta de novo, e o contrato entra no que chamamos de <strong>State Drift</strong> (Desvio de Estado). Para o banco de dados, aquele cliente ainda est&#225; <code>ACTIVE</code>. Ele vai ao shopping amanh&#227; e gasta mais R$ 5.000,00. O banco aumentou sua exposi&#231;&#227;o ao risco por uma falha.</p><p>Pior ainda: o cenario avan&#231;a 200 dias. Esse cliente nunca pagou. Pela regula&#231;&#227;o do Banco Central, esse contrato deveria ser marcado como Preju&#237;zo (<em>Write-off</em>) para fins cont&#225;beis e fiscais. Se o seu sistema depende exclusivamente de um evento disparado no dia 200 para fazer isso, e esse evento falha... parab&#233;ns, seu banco agora tem um passivo cont&#225;bil incorreto e est&#225; sujeito a multas regulat&#243;rias.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><p>Arquiteturas Orientadas a Eventos s&#227;o excelentes para escalabilidade e desacoplamento, mas elas <strong>n&#227;o garantem a consist&#234;ncia final dos dados ao longo do tempo</strong>.</p><p>Para sistemas financeiros cr&#237;ticos, precisamos de mais. Precisamos de uma arquitetura de <strong><a href="https://www.geeksforgeeks.org/system-design/self-healing-systems-system-design/">Self-Healing</a></strong>. Precisamos de sistemas que n&#227;o apenas &#8220;reajam&#8221; a eventos, mas que proativamente &#8220;busquem e corrijam&#8221; estados inconsistentes baseados na passagem do tempo.</p><p>Neste artigo, vamos explorar como desenhar uma estrat&#233;gia robusta de recupera&#231;&#227;o de estado para ciclos de cr&#233;dito. Vamos sair do &#8220;Happy Path&#8221; e desenhar o &#8220;Safety Path&#8221;: cron jobs inteligentes, m&#225;quinas de estado resilientes e estrat&#233;gias de reconcilia&#231;&#227;o que garantem que, mesmo que o <em>broker</em> pegue fogo, o balan&#231;o do banco feche no final do m&#234;s.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Assine! &#201; gratuito!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>O que &#233; Self-Healing?</h3><p>Se voc&#234; jogou qualquer RPG ou MMO nos &#250;ltimos 20 anos, o termo &#8220;Self-Healing&#8221; provavelmente evoca a imagem de um personagem regenerando sua barra de vida automaticamente ou um cl&#233;rigo lan&#231;ando um feiti&#231;o de cura. No mundo dos jogos, &#233; a mec&#226;nica que permite ao jogador continuar na batalha sem precisar parar o tempo todo.</p><p>Na engenharia de software, o conceito &#233; surpreendentemente similar, mas a inspira&#231;&#227;o original n&#227;o veio dos videogames. Veio da <strong>Biologia</strong>.</p><h4>A Biologia como Benchmark</h4><p>No mundo real, o melhor exemplo de um sistema <em>Self-Healing</em> &#233; o seu pr&#243;prio corpo. Quando voc&#234; faz um pequeno corte no dedo, voc&#234; n&#227;o precisa abrir um chamado no Jira para o seu c&#233;rebro, nem rodar um script manual para iniciar a coagula&#231;&#227;o. O seu sistema imunol&#243;gico detecta a &#8220;falha&#8221; (o corte), isola a &#225;rea e inicia processos autom&#225;ticos de reparo para restaurar a integridade da pele.</p><p>Se o corpo humano dependesse de interven&#231;&#227;o consciente para curar cada micro-les&#227;o, n&#227;o sobreviver&#237;amos um dia. Sistemas distribu&#237;dos complexos chegaram a esse mesmo est&#225;gio: eles s&#227;o grandes demais para serem &#8220;curados&#8221; manualmente por humanos &#224;s 3 da manh&#227;.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vSce!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vSce!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!vSce!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!vSce!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!vSce!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vSce!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6240850,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183054970?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vSce!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!vSce!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!vSce!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!vSce!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1b2b73c7-ca2d-4c8c-9899-cb05898e161e_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>O Manifesto da IBM</h4><p>O termo ganhou for&#231;a corporativa no in&#237;cio dos anos 2000, quando a IBM lan&#231;ou seu manifesto sobre <strong>Computa&#231;&#227;o Auton&#244;mica</strong> (<a href="https://www.ibm.com/docs/en/db2/11.5.x?topic=servers-autonomic-computing-overview">Autonomic Computing</a>).</p><p>A IBM percebeu que a complexidade dos sistemas de computa&#231;&#227;o estava crescendo mais r&#225;pido do que a capacidade humana de gerenci&#225;-los. Eles propuseram que os sistemas deveriam ter quatro propriedades inspiradas no sistema nervoso aut&#244;nomo:</p><ol><li><p><strong>Self-Configuration:</strong> Configurar-se sozinho.</p></li><li><p><strong>Self-Optimization:</strong> Otimizar recursos sozinho.</p></li><li><p><strong>Self-Protection:</strong> Defender-se de ataques.</p></li><li><p><strong>Self-Healing:</strong> Detectar, diagnosticar e reparar problemas causados por falhas internas ou externas.</p></li></ol><p>Portanto, <em>Self-Healing</em> n&#227;o &#233; apenas &#8220;tentar de novo&#8221;. &#201; a capacidade de um sistema perceber que n&#227;o est&#225; saud&#225;vel (est&#225; inconsistente) e tomar medidas para voltar ao estado normal sem acordar o engenheiro de plant&#227;o.</p><h3>O &#8220;Diagn&#243;stico Financeiro&#8221;: Meu sistema precisa de Self-Healing?</h3><p>Implementar <em>Self-Healing</em> (como jobs de reconcilia&#231;&#227;o de GL, retentativas inteligentes ou estornos autom&#225;ticos) custa caro. Aumenta a complexidade do c&#243;digo e exige manuten&#231;&#227;o. N&#227;o use um canh&#227;o para matar uma mosca.</p><p>Para saber se o seu microsservi&#231;o financeiro precisa dessa arquitetura, fa&#231;a a si mesmo estas 4 perguntas (O &#8220;Teste da Dor Financeira&#8221;):</p><p><strong>1. A inconsist&#234;ncia gera impacto no P&amp;L ou risco regulat&#243;rio?</strong></p><ul><li><p><strong>Contexto:</strong> Erros de UI tem um menor impacto; erros de financeiros s&#227;o fatais.</p></li><li><p><strong>Exemplo</strong>: Se o avatar do usu&#225;rio n&#227;o carregar, o m&#225;ximo que acontece &#233; uma experi&#234;ncia visual ruim. Ningu&#233;m processa o banco por isso. Mas o jogo muda quando entramos na camada regulat&#243;ria: se o c&#225;lculo de IOF falhar silenciosamente e voc&#234; recolher a menos para a Receita, ou se um contrato inadimplente n&#227;o sofrer a <strong>Baixa Cont&#225;bil (Write-off)</strong> no momento exigido pelas normas vigentes (como a <a href="https://www.deloitte.com/br/pt/services/audit-assurance/perspectives/resolucao-cmn-4966.html">Resolu&#231;&#227;o CMN 4.966</a>), voc&#234; estar&#225; mantendo um ativo 'podre' inflando artificialmente o balan&#231;o. Nesse cen&#225;rio, o erro de software deixa de ser um bug no Jira e vira risco de conformidade, colocando na reta n&#227;o s&#243; a reputa&#231;&#227;o da empresa, mas o CPF dos diretores estatut&#225;rios.</p></li><li><p>Se o erro afeta o balan&#231;o cont&#225;bil ou relat&#243;rios para o regulador (BACEN/CVM), a cura <em>precisa</em> ser autom&#225;tica.</p></li></ul><p><strong>2. A opera&#231;&#227;o manual escala com o volume transacional?</strong></p><ul><li><p><strong>Contexto:</strong> O custo do Backoffice vs. Engenharia.</p></li><li><p><strong>Exemplo:</strong> Se 5 TEDs falham por dia, um analista de N2 pode corrigir na m&#227;o acessando o banco de dados. Mas se voc&#234; processa PIX e tem uma taxa de falha de 0.1% em dia de folha de pagamento (milhares de erros/hora), tentar corrigir isso manualmente vai colapsar seu time de atendimento e gerar milhares de reclama&#231;&#245;es no Banco Central.</p></li><li><p>Se o volume de falhas excede a capacidade humana de digitar <code>UPDATE</code> no banco em tempo real, voc&#234; precisa de automa&#231;&#227;o.</p></li></ul><p><strong>3. O erro gera Passivo Fantasma?</strong></p><ul><li><p><strong>Contexto:</strong> O perigo do &#8220;Sucesso Falso&#8221;.</p></li><li><p><strong>Exemplo:</strong> O sistema de eventos diz que o cart&#227;o foi bloqueado, mas o processadora de cart&#245;es n&#227;o recebeu o comando. O cliente continua comprando. Para o seu banco de dados, est&#225; tudo certo; para o caixa do banco, h&#225; um vazamento de dinheiro invis&#237;vel ocorrendo agora.</p></li><li><p>Se o estado financeiro pode divergir da realidade sem gerar um <em>Exception</em> imediato, voc&#234; precisa de um &#8220;Auditor Rob&#244;&#8221; (Worker) rodando de tempos em tempos.</p></li></ul><p><strong>4. Existe uma Janela de Clearing ou SLA Regulat&#243;rio R&#237;gido?</strong></p><ul><li><p><strong>Contexto:</strong> O tempo n&#227;o perdoa em finan&#231;as.</p></li><li><p><strong>Exemplo:</strong> Voc&#234; tem at&#233; as 16h para enviar a remessa de boletos ou at&#233; as 18h30 para fechar o caixa do STR (Sistema de Transfer&#234;ncia de Reservas). Se o sistema travar &#224;s 15h50, voc&#234; n&#227;o tem 20 minutos para esperar um humano investigar logs. O sistema precisa tentar rotas alternativas ou degradar graciosamente para garantir o envio.</p></li><li><p>Se perder a janela de tempo significa multa ou travar o dinheiro do banco at&#233; o dia seguinte, a recupera&#231;&#227;o deve ser instant&#226;nea.</p></li></ul><p>Se o erro permite que voc&#234; espere at&#233; a <em>daily</em> do dia seguinte para resolver, n&#227;o gaste engenharia com isso. <strong>Mas se o erro faz o banco perder dinheiro, inflacionar o balan&#231;o artificialmente ou falhar com o BACEN enquanto voc&#234; dorme, o </strong><em><strong>Self-Healing</strong></em><strong> &#233; obrigat&#243;rio</strong>.</p><h3>A Auditoria de Inconsist&#234;ncia (Como encontrar os &#8220;Fantasmas&#8221; no seu Banco)</h3><p>Se voc&#234; passou pelo &#8220;Teste da Dor&#8221; e desconfia que seu sistema precisa de cura autom&#225;tica, o pr&#243;ximo passo n&#227;o &#233; abrir o VS Code, mas sim abrir o <strong>Jira</strong> e o <strong>seu banco de dados em produ&#231;&#227;o</strong>.</p><p>Para justificar o investimento em uma arquitetura de <em>Self-Healing</em> para a diretoria, <strong>voc&#234; precisa de dados</strong>. A equipe de engenharia deve conduzir uma investiga&#231;&#227;o em tr&#234;s frentes para mapear a fragilidade atual do sistema.</p><p>Chamamos isso de <strong>&#8220;A Ca&#231;a aos Fantasmas de Estado&#8221;</strong>.</p><h4>1. A An&#225;lise de &#8220;Toil&#8221; (Trabalho Penoso)</h4><p>O conceito de <em><a href="https://sre.google/sre-book/eliminating-toil/">Toil</a></em><a href="https://sre.google/sre-book/eliminating-toil/"> (do SRE do Google)</a> refere-se ao trabalho manual, repetitivo e t&#225;tico. Pe&#231;a para o seu Tech Lead ou coordenador de suporte levantar os chamados dos &#250;ltimos 6 meses. Procure por padr&#245;es nos tickets fechados:</p><ul><li><p><strong>A &#8220;Pasta M&#225;gica&#8221; de Scripts:</strong> O time tem uma pasta compartilhada com scripts SQL chamados <code>update_status_travado.sql</code> ou <code>destravar_pix.sql</code>? Se existe um script oficial para corrigir dados na m&#227;o, isso &#233; um grito de socorro por <em>Self-Healing</em>.</p></li><li><p><strong>Tickets de &#8220;Sincronia&#8221;:</strong> Chamados com t&#237;tulos como <em>&#8220;Cliente pagou mas n&#227;o consta no app&#8221;</em> ou <em>&#8220;Fatura cancelada ainda aparece como aberta&#8221;</em>.</p></li><li><p><strong>A Regra dos 20%:</strong> Se a sua equipe gasta mais de 20% do sprint executando corre&#231;&#245;es manuais de dados (n&#227;o de c&#243;digo, mas de <em>dados</em>), a automa&#231;&#227;o n&#227;o &#233; luxo, &#233; sobreviv&#234;ncia.</p></li></ul><h4>2. A &#8220;Query Ca&#231;a-Fantasmas&#8221; (Data Integrity Probe)</h4><p>N&#227;o espere o cliente reclamar. A engenharia deve executar queries explorat&#243;rias para encontrar <strong>estados imposs&#237;veis</strong> que existem silenciosamente no banco de dados hoje.</p><p>Crie queries que busquem viola&#231;&#245;es da sua M&#225;quina de Estados:</p><ul><li><p><strong>O Limbo do Processamento:</strong></p></li></ul><pre><code><code>SELECT count(*) FROM transacoes
WHERE status = 'PROCESSING'
AND created_at &lt; NOW() - INTERVAL '24 HOURS';</code></code></pre><ul><li><p><em>Diagn&#243;stico:</em> Em um sistema financeiro, nada deve ficar &#8220;processando&#8221; por 24 horas. Ou pagou, ou falhou. Se retornou linhas, voc&#234; tem &#8220;fantasmas&#8221; que precisam de cura.</p></li><li><p><strong>O Zumbi Financeiro:</strong></p></li></ul><pre><code><code>SELECT count(*) FROM contratos
WHERE dias_atraso &gt; 60
AND status = 'ACTIVE';</code></code></pre><ul><li><p><em>Diagn&#243;stico:</em> O cliente deve h&#225; dois meses e o contrato est&#225; ativo? O evento de bloqueio falhou e ningu&#233;m viu.</p></li></ul><h4>3. O Mapeamento de Depend&#234;ncias Cegas</h4><p>Desenhe a arquitetura e circule em vermelho todos os pontos onde seu sistema <strong>espera passivamente</strong> por uma resposta externa.</p><ul><li><p><strong>Webhooks de Pagamento:</strong> Se o gateway n&#227;o chamar o seu webhook de confirma&#231;&#227;o (ou se o servi&#231;o de bloquear), a transa&#231;&#227;o morre?</p></li><li><p><strong>Arquivos de Retorno (CNAB):</strong> Se o arquivo do banco n&#227;o chegar at&#233; as 10h, o processo para?</p></li></ul><div class="pullquote"><p><em>Se o seu sistema desenhado depende da &#8220;sorte&#8221; de receber um evento externo e n&#227;o tem um processo ativo que vai l&#225; perguntar &#8220;e a&#237;, j&#225; processou?&#8221;, voc&#234; encontrou o ponto exato onde deve implementar o primeiro mecanismo de reconcilia&#231;&#227;o.</em></p></div><h4>O Resultado da An&#225;lise</h4><p>Ao final dessa auditoria, a equipe deve ter uma tabela simples:</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!pKkg!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png 424w, https://substackcdn.com/image/fetch/$s_!pKkg!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png 848w, https://substackcdn.com/image/fetch/$s_!pKkg!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png 1272w, https://substackcdn.com/image/fetch/$s_!pKkg!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!pKkg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png" width="654" height="197" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:197,&quot;width&quot;:654,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:28930,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183054970?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!pKkg!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png 424w, https://substackcdn.com/image/fetch/$s_!pKkg!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png 848w, https://substackcdn.com/image/fetch/$s_!pKkg!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png 1272w, https://substackcdn.com/image/fetch/$s_!pKkg!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fef975b90-33d3-4dba-992a-f079eaf76bc9_654x197.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><p>Com essa tabela, a decis&#227;o deixa de ser t&#233;cnica (&#8221;queremos fazer porque &#233; legal&#8221;) e vira financeira (&#8221;queremos fazer para recuperar 14 horas de engenharia e destravar R$ 45 mil&#8221;).</p><h2>A Ilus&#227;o do "Event-Driven Puro"</h2><p>N&#243;s, arquitetos e engenheiros de software, fomos seduzidos por uma promessa arquitetural: <strong>&#8220;Desacople tudo. Use eventos. O sistema ser&#225; reativo e perfeito.&#8221;</strong></p><p>Em diagramas de arquitetura, setas ligando o <em>Producer</em> ao <em>Consumer</em> via Kafka ou RabbitMQ representam certeza. Na pr&#225;tica, elas representam esperan&#231;a. Em sistemas financeiros, especialmente em Cobran&#231;a e Cr&#233;dito, a esperan&#231;a n&#227;o &#233; uma estrat&#233;gia vi&#225;vel.</p><h4>O Dia 5</h4><p>Vamos analisar a anatomia de um desastre silencioso. Imagine que seu motor de cr&#233;dito (Core Banking) dispara o evento <code>PaymentOverdue</code> exatamente no 5&#186; dia de atraso do cliente. A regra de neg&#243;cio &#233; clara: <strong>&#8220;Se atrasar 5 dias, bloqueie o cart&#227;o imediatamente.&#8221;</strong></p><p>O evento &#233; publicado. O <em>offset</em> &#233; commitado. O Core Banking &#8220;lavou as m&#227;os&#8221;, crente de que fez sua parte. Mas do outro lado, no microsservi&#231;o de Cart&#245;es, o <em>Consumer</em> encontrou um problema:</p><ol><li><p><strong>A &#8220;<a href="https://www.confluent.io/blog/spring-kafka-can-your-kafka-consumers-handle-a-poison-pill/">Poison Pill</a>&#8221;:</strong> O payload do evento veio com um caractere invis&#237;vel no campo <code>contract_id</code>, causando uma <em>Exception</em> de deserializa&#231;&#227;o que trava o processamento daquele lote.</p></li><li><p><strong>Falha de Infra:</strong> O pod do Kubernetes estava reiniciando por <a href="https://kubernetes.io/docs/concepts/configuration/manage-resources-containers/">OOM (Out of Memory)</a> bem naquele segundo.</p></li><li><p><strong>Bug Silencioso:</strong> O c&#243;digo consumiu a mensagem, mas falhou ao gravar no banco devido a um <em>Deadlock</em>, e a configura&#231;&#227;o de <em>retry</em> estava mal implementada.</p></li></ol><p><strong>O Resultado</strong> &#233; que o evento se perdeu. Para o sistema, o cliente ainda est&#225; &#8220;Ativo&#8221;. Para o cliente, o cart&#227;o continua passando na maquininha.</p><h4>PDD e Metas Estouradas</h4><p>Aqui &#233; onde o c&#243;digo afeta o b&#244;nus da diretoria e a sa&#250;de do banco.</p><p>Passam-se 6, 7, 8 dias. O cliente, que j&#225; n&#227;o pagou a fatura anterior, continua comprando (aumentando a exposi&#231;&#227;o do banco ao risco). Chamamos isso de <a href="https://www.thebanker.com/content/0ec3d3f5-62bc-4aa1-8202-9cccb6ebc2a3">Exposi&#231;&#227;o Sombra</a>. O sistema de Risco acha que a exposi&#231;&#227;o &#233; X, mas ela &#233; X + R$ 5.000,00 que o cliente gastou <em>depois</em> do dia em que deveria ter sido bloqueado.</p><p>Quando o time de dados rodar o relat&#243;rio mensal de <strong><a href="https://www.serasaexperian.com.br/conteudos/pdd-provisao-para-devedores-duvidosos/">PDD (Provis&#227;o para Devedores Duvidosos)</a></strong> ou PCL, os n&#250;meros n&#227;o bater&#227;o.</p><ul><li><p><strong>Impacto no Time:</strong> Sua squad para de desenvolver features novas. O Sprint vai para o lixo. Voc&#234;s entram em &#8220;War Room&#8221; para fazer queries manuais no banco, tentando descobrir <em>quais</em> clientes deveriam estar bloqueados e n&#227;o est&#227;o.</p></li><li><p><strong>Impacto na Meta:</strong> O tempo gasto investigando &#8220;fantasmas&#8221; (incid&#234;ncias sem logs de erro &#243;bvios) &#233; exponencialmente maior do que corrigir um bug que estoura um alerta 500 no Datadog.</p></li></ul><p>Arquiteturas puramente orientadas a eventos sofrem de um otimismo excessivo. Elas assumem que a mensagem <em><strong>vai</strong></em> chegar.</p><p>Para dormir tranquilo, precisamos adotar uma postura pessimista: <strong>&#8220;O evento vai falhar. E quando ele falhar, quem vai limpar a bagun&#231;a?&#8221;</strong></p><p>Eventos s&#227;o excelentes para <strong>Real-Time</strong> (velocidade). Mas Jobs de Varredura s&#227;o necess&#225;rios para <strong>Consist&#234;ncia</strong> (garantia).</p><p>Se o evento &#233; o carteiro que entrega a conta, precisamos de algu&#233;m que passe depois verificando se a conta foi realmente paga. Precisamos de um &#8220;Varredor de Estados&#8221;.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><h2>O "State Sweeper" &#8211; A Rede de Seguran&#231;a Invis&#237;vel</h2><p>O que exatamente &#233; esse <strong>&#8220;State Sweeper&#8221;</strong> (Varredor de Estado)?</p><p>Em uma defini&#231;&#227;o simples:</p><blockquote><p><em>O State Sweeper &#233; um componente arquitetural proativo que periodicamente interroga a base de dados em busca de inconsist&#234;ncias temporais ou l&#243;gicas que escaparam do fluxo padr&#227;o de eventos.</em></p></blockquote><p>Enquanto a arquitetura orientada a eventos (EDA) &#233; <strong>Reativa</strong> (o sistema espera algo acontecer para agir), o State Sweeper &#233; <strong>Proativo</strong> <strong>(o sistema vai verificar se algo </strong><em><strong>deveria</strong></em><strong> ter acontecido, mas n&#227;o aconteceu)</strong>.</p><h4>Por que ele &#233; obrigat&#243;rio em Event-Driven?</h4><p>N&#243;s amamos arquiteturas orientadas a eventos porque elas desacoplam servi&#231;os. O Servi&#231;o A grita &#8220;Cobran&#231;a Criada!&#8221; e n&#227;o se importa com quem vai ouvir.</p><p>Mas essa liberdade tem um custo: <strong>O Sil&#234;ncio</strong>. Se o evento se perde, ou se o consumidor falha silenciosamente, n&#227;o h&#225; &#8220;erro&#8221;. H&#225; apenas o vazio. O sistema n&#227;o quebra; ele simplesmente para de evoluir o estado daquele contrato.</p><ul><li><p>O cliente n&#227;o &#233; bloqueado.</p></li><li><p>A divida n&#227;o &#233; transmitida para outros servi&#231;os.</p></li><li><p>O nome n&#227;o vai para o Serasa.</p></li></ul><p>O <em>State Sweeper</em> &#233; necess&#225;rio porque ele n&#227;o confia na infraestrutura de mensageria. Ele confia apenas na <strong>passagem do tempo</strong> e no <strong>estado persistido</strong>. Ele &#233; a garantia final de que a &#8220;Consist&#234;ncia Eventual&#8221; vai realmente acontecer, mesmo que seja &#8220;na marra&#8221;.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!XymR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!XymR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png 424w, https://substackcdn.com/image/fetch/$s_!XymR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png 848w, https://substackcdn.com/image/fetch/$s_!XymR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png 1272w, https://substackcdn.com/image/fetch/$s_!XymR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!XymR!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png" width="944" height="844.1538461538462" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1302,&quot;width&quot;:1456,&quot;resizeWidth&quot;:944,&quot;bytes&quot;:822220,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183054970?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!XymR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png 424w, https://substackcdn.com/image/fetch/$s_!XymR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png 848w, https://substackcdn.com/image/fetch/$s_!XymR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png 1272w, https://substackcdn.com/image/fetch/$s_!XymR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F77381534-24ab-4416-bf22-fc00e2e0eaa7_3634x3250.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>A Complexidade Oculta: Por que &#233; dif&#237;cil desenvolver?</h4><p>Muitos gerentes podem olhar para isso e dizer: <em>&#8220;Ah, &#233; s&#243; uma rotina que roda a cada diariamente, pede para o estagi&#225;rio fazer.&#8221;</em></p><p>Isso &#233; um grande engano! Desenvolver um <em>Sweeper</em> eficiente e seguro exige um n&#237;vel de <strong>pensamento cr&#237;tico e an&#225;lise de neg&#243;cio</strong> que geralmente s&#243; encontramos em engenheiros s&#234;niores ou especialistas.</p><p>Por que n&#227;o &#233; simples?</p><p><strong>1. Exige Dom&#237;nio Absoluto das Regras de Neg&#243;cio</strong> Voc&#234; n&#227;o pode escrever um <em>Sweeper</em> <strong>se n&#227;o entender profundamente o ciclo de vida da entidade</strong>. &#9888;&#65039;</p><ul><li><p><em>Pergunta Dif&#237;cil:</em> &#8220;Se o contrato est&#225; atrasado h&#225; 200 dias, eu devo mandar para cobran&#231;a externa? Mas e se ele tiver uma flag de &#8216;Renegocia&#231;&#227;o em Andamento&#8217;? E se for um dia n&#227;o-&#250;til? E se o cliente tiver uma liminar judicial?&#8221;</p></li><li><p>O <em>Sweeper</em> toma decis&#245;es. Se a regra estiver errada, ele vai aplicar a regra errada em massa (ex: bloquear 50 mil clientes indevidamente em 1 minuto).</p></li></ul><p><strong>2. O Dilema da Concorr&#234;ncia (Race Conditions)</strong> E se o <em>Sweeper</em> rodar exatamente no mesmo milissegundo em que o evento real chegou atrasado?</p><ul><li><p>Voc&#234; precisa desenhar mecanismos de travamento (Locks) ou verifica&#231;&#245;es de vers&#227;o (Optimistic Locking) para garantir que o <em>Sweeper</em> e o <em>Consumer</em> n&#227;o tentem modificar o mesmo contrato ao mesmo tempo, corrompendo o saldo.</p></li></ul><p><strong>3. O Perigo da &#8220;Avalanche&#8221;</strong> Se o seu sistema ficar fora do ar por 2 horas, voc&#234; acumular&#225; milhares de pend&#234;ncias.</p><ul><li><p>Um <em>Sweeper</em> mal desenhado vai tentar corrigir tudo de uma vez assim que o sistema voltar, criando um ataque DDoS contra o seu pr&#243;prio banco de dados ou contra as APIs de parceiros. &#201; preciso desenhar <strong><a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/cloud-design-patterns/retry-backoff.html">backoff</a></strong>, <strong>pagina&#231;&#227;o</strong> e <strong>limita&#231;&#227;o de taxa (<a href="https://www.redhat.com/en/blog/pros-and-cons-throttling">throttling</a>)</strong>.</p></li></ul><p><strong>4. Visibilidade e Rastreabilidade</strong> Quando um humano corrige um erro, ele deixa um log. Quando um rob&#244; corrige 500 erros, quem fica sabendo?</p><ul><li><p>Desenvolver um <em>Sweeper</em> exige criar eventos espec&#237;ficos (ex: <code>ContractAutoHealed</code>) para que os analistas saibam a diferen&#231;a entre &#8220;O sistema funcionou normalmente&#8221; e &#8220;O sistema falhou, mas o rob&#244; consertou&#8221;.</p></li></ul><p>O <em>State Sweeper</em> n&#227;o &#233; um &#8220;tapa-buraco&#8221;. &#201; um componente de <strong>Resili&#234;ncia de Neg&#243;cio</strong>. Ele exige que a equipe de engenharia pare de pensar apenas em c&#243;digo (syntax) e comece a pensar em comportamento sist&#234;mico (semantics) e modos de falha.</p><h3>System Design com Sweepers em Arquiteturas Distribu&#237;das</h3><p>Aqui chegamos ao &#8220;n&#243;&#8221; arquitetural que separa os juniores dos seniores.</p><p>No mundo monol&#237;tico, o <em>Sweeper</em> seria um script simples que atualizaria todas as tabelas. Mas n&#243;s estamos no mundo dos <strong>Microsservi&#231;os</strong>. O Servi&#231;o de Cobran&#231;a (Collection Service) &#233; a autoridade sobre <em>quem est&#225; sendo cobrado</em>, mas ele <strong>n&#227;o &#233; o dono</strong> do Contrato de Cr&#233;dito original (quem manda l&#225; &#233; o <em>Credit Core</em>).</p><p>Isso cria um dilema: O Servi&#231;o de Cobran&#231;a sabe que o cliente est&#225; atrasado h&#225; 205 dias (violando a regra de neg&#243;cio de 200 dias), mas ele n&#227;o pode simplesmente dar um <code>UPDATE</code> na tabela do Core.</p><p>Ent&#227;o, como desenhar um <em>Sweeper</em> que imp&#245;e a regra sem quebrar o desacoplamento? A resposta est&#225; em atuar como um <strong>Auditor Ativo</strong>.</p><h4>O Cen&#225;rio: A Regra dos 200 Dias</h4><p>A regra &#233; clara: <em>&#8220;Contratos com mais de 200 dias de atraso devem ser retirados da carteira interna e enviados para uma Assessoria de Cobran&#231;a Externa (Recovery).&#8221;</em></p><p>Se o evento do &#8220;Dia 200&#8221; falhar, o contrato fica esquecido na base de Cobran&#231;a com status <code>OVERDUE</code>. O banco perde dinheiro a cada dia.</p><h4>Passo 1: A Detec&#231;&#227;o Cir&#250;rgica (NoSQL Strategy)</h4><p>Primeiro, o <em>Sweeper</em> (um Lambda ou CronJob) precisa encontrar os infratores na <strong>sua pr&#243;pria base</strong> (Collection Service). N&#227;o vamos fazer <code>Scan</code>. Vamos usar a estrat&#233;gia de <strong>&#205;ndice de Exce&#231;&#227;o</strong> no DynamoDB.</p><ul><li><p><strong>Tabela:</strong> <code>CollectionCases</code></p></li><li><p><strong>GSI (&#205;ndice):</strong> <code>GSI_Compliance_Audit</code></p><ul><li><p><strong>PK:</strong> <code>CollectionStatus</code> (Ex: <code>OVERDUE</code>)</p></li><li><p><strong>SK:</strong> <code>DaysInDelay</code> (Num&#233;rico)</p></li></ul></li></ul><p><strong>A Query do Sweeper:</strong> Ele pergunta ao DynamoDB: <em>&#8220;Liste apenas os itens onde Status = &#8216;OVERDUE&#8217; E DaysInDelay &gt;= 200&#8221;</em>. Isso &#233; extremamente perform&#225;tico. Se o sistema estiver saud&#225;vel, retorna zero. Se houver falha no fluxo de eventos, retorna exatamente os contratos problem&#225;ticos.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wq98!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wq98!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!wq98!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!wq98!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!wq98!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wq98!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1495373,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183054970?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wq98!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!wq98!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!wq98!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!wq98!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0311c8b1-7b10-43f7-b718-db0342b93ac8_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Passo 2: O &#8220;Sanity Check&#8221; (Consulta de Seguran&#231;a)</h4><p>Aqui entra a intelig&#234;ncia distribu&#237;da. Antes de tomar uma atitude dr&#225;stica, o <em>Sweeper</em> precisa garantir que sua vis&#227;o de mundo n&#227;o est&#225; distorcida. Pode ser que o cliente tenha renegociado a d&#237;vida no Core h&#225; 10 minutos e o evento de &#8220;Renegocia&#231;&#227;o&#8221; ainda n&#227;o chegou na Cobran&#231;a.</p><p>O <em>Sweeper</em> faz uma chamada s&#237;ncrona leve (HTTP GET) na API do Core: <code>GET /credit-core/contracts/{id}/status</code></p><ul><li><p><strong>Se o Core disser &#8220;Renegociado/Pago&#8221;:</strong> O Sweeper aborta e dispara um alerta de inconsist&#234;ncia de dados (para corrigir sua base local depois).</p></li><li><p><strong>Se o Core disser &#8220;Atrasado&#8221;:</strong> O sinal verde est&#225; dado. A regra dos 200 dias foi violada e a d&#237;vida &#233; v&#225;lida.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kxap!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kxap!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png 424w, https://substackcdn.com/image/fetch/$s_!kxap!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png 848w, https://substackcdn.com/image/fetch/$s_!kxap!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png 1272w, https://substackcdn.com/image/fetch/$s_!kxap!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kxap!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png" width="872" height="926.2943396226415" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1126,&quot;width&quot;:1060,&quot;resizeWidth&quot;:872,&quot;bytes&quot;:244617,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183054970?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kxap!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png 424w, https://substackcdn.com/image/fetch/$s_!kxap!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png 848w, https://substackcdn.com/image/fetch/$s_!kxap!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png 1272w, https://substackcdn.com/image/fetch/$s_!kxap!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb07b0701-b77b-45f5-ad4d-e8aeb605517a_1060x1126.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Passo 3: A &#8220;Arbitragem Local&#8221; e o Disparo do Evento</h4><p>O <em>Sweeper</em> n&#227;o ordena que o Core mude. Ele ordena que o <strong>Servi&#231;o de Cobran&#231;a</strong> tome a atitude correta para proteger o banco.</p><ol><li><p><strong>Atualiza&#231;&#227;o da Base Local (Autocura):</strong> O Sweeper executa um <code>UpdateItem</code> na tabela <code>CollectionCases</code>:</p><ul><li><p><strong>De:</strong> <code>OVERDUE</code></p></li><li><p><strong>Para:</strong> <code>SENT_FOR_COLLECTION</code> </p></li><li><p><strong>Metadata:</strong> Adiciona um campo <code>UpdatedBy: "SWEEPER_BOT"</code> para rastreabilidade.</p></li></ul></li><li><p><strong>Comunica&#231;&#227;o ao Ecossistema (O Grito):</strong> Como o contrato mudou de estado, o Servi&#231;o de Cobran&#231;a precisa avisar o mundo. Ele publica um evento enriquecido no barramento (Kafka/SNS):</p><p><strong>Evento:</strong> <code>Collection.CaseSentToExternalRecovery</code></p></li></ol><pre><code><code>{
  "contractId": "XYZ-123",
  "daysInDelay": 205,
  "outstandingBalance": 5000.00,
  "reason": "RULE_200_DAYS_LIMIT",
  "trigger": "AUTO_HEALING_WORKER", // Campo crucial para m&#233;tricas!
  "occurredAt": "2024-01-20T10:00:00Z"
}</code></code></pre><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ATUN!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ATUN!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png 424w, https://substackcdn.com/image/fetch/$s_!ATUN!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png 848w, https://substackcdn.com/image/fetch/$s_!ATUN!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png 1272w, https://substackcdn.com/image/fetch/$s_!ATUN!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ATUN!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png" width="970" height="719.5899347623485" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:796,&quot;width&quot;:1073,&quot;resizeWidth&quot;:970,&quot;bytes&quot;:334061,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183054970?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ATUN!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png 424w, https://substackcdn.com/image/fetch/$s_!ATUN!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png 848w, https://substackcdn.com/image/fetch/$s_!ATUN!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png 1272w, https://substackcdn.com/image/fetch/$s_!ATUN!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8b8cd49b-9255-4eff-b782-1b923cf2ca7c_1073x796.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>O Resultado Arquitetural</h4><p>Veja a eleg&#226;ncia dessa solu&#231;&#227;o:</p><ol><li><p><strong>Respeito aos Limites:</strong> O Servi&#231;o de Cobran&#231;a n&#227;o invadiu o Core. Ele resolveu o problema <strong>do seu lado</strong> (mudou para <code>SENT_FOR_COLLECTION</code>) e avisou os parceiros.</p></li><li><p><strong>Integra&#231;&#227;o Reativa:</strong> Quando a Assessoria de Cobran&#231;a escutar esse evento, ela cadastra a d&#237;vida l&#225;. Quando o Core escutar esse evento, ele marca o contrato como &#8220;Em Preju&#237;zo&#8221;.</p></li><li><p><strong>Observabilidade:</strong> Gra&#231;as ao campo <code>trigger: AUTO_HEALING_WORKER</code>, voc&#234; pode criar um dashboard no Datadog/Grafana. Se o n&#250;mero de eventos disparados pelo <em>Sweeper</em> subir, voc&#234; sabe que seu fluxo <em>real-time</em> (Kafka) est&#225; quebrado, antes mesmo do Diretor reclamar.</p></li></ol><p>O <em>Sweeper</em> aqui n&#227;o &#233; um &#8220;consertador de banco de dados&#8221;. Ele &#233; um <strong>agente de neg&#243;cio automatizado</strong> que garante que as regras financeiras sejam cumpridas, mesmo quando a infraestrutura falha.</p><h2>Antes do C&#243;digo: A Mentalidade Orientada a Perguntas</h2><p>Agora que voc&#234; viu uma ponta do <em>Self-Healing</em> em a&#231;&#227;o, pare um pouco. Antes de abrir a IDE e come&#231;ar a codar Lambdas ou desenhar &#237;ndices no DynamoDB, voc&#234; precisa de algo que nenhuma ferramenta pode te dar: <strong>Contexto de Neg&#243;cio</strong>.</p><p>Uma arquitetura de autocura n&#227;o nasce de requisitos t&#233;cnicos, ela nasce de <strong>dores operacionais</strong>. Para modelar os dados corretamente e facilitar a introdu&#231;&#227;o dessas rotinas de cura, Tech Leads e Arquitetos precisam adotar uma postura de investiga&#231;&#227;o.</p><h4>1. A Mina de Ouro: O Time de Suporte (N1/N2)</h4><p>Se voc&#234; quer saber onde seu sistema sangra, n&#227;o olhe para o Datadog agora. <strong>Olhe para a fila de chamados.</strong> O time que atende o cliente provavelmente j&#225; tem uma &#8220;Wiki&#8221; ou um caderno de anota&#231;&#245;es com procedimentos manuais para resolver problemas que a engenharia considera &#8220;pontuais&#8221;, mas que na verdade s&#227;o cr&#244;nicos.</p><p>Voc&#234; precisa se perguntar:</p><ul><li><p>&#8220;Por que o atendente precisa verificar manualmente se a fatura do dia 15 fechou?&#8221;</p></li><li><p>&#8220;Quais scripts o time de Opera&#231;&#245;es roda toda sexta-feira para &#8216;ajustar a base&#8217;?&#8221;</p></li><li><p>&#8220;Quantas vezes um cliente ligou reclamando que pagou o acordo, mas continua recebendo SMS de cobran&#231;a?&#8221;</p></li></ul><p>Cada um desses &#8220;procedimentos manuais&#8221; &#233; um <strong>candidato a automa&#231;&#227;o via Self-Healing</strong>. O <em>Sweeper</em> nada mais &#233; do que a codifica&#231;&#227;o do conhecimento t&#225;tico do time de suporte, rodando 24/7.</p><h4>2. O Custo da Inconsist&#234;ncia (O Risco Jur&#237;dico)</h4><p>Estados inconsistentes em sistemas financeiros n&#227;o s&#227;o apenas &#8220;bugs&#8221;. S&#227;o passivos judiciais.</p><ul><li><p><strong>Cobran&#231;a Indevida:</strong> Um cliente que renegociou a d&#237;vida, mas o evento se perdeu e ele voltou para a r&#233;gua de cobran&#231;a, &#233; um processo ganho no Juizado de Pequenas Causas.</p></li><li><p><strong>Bloqueio Indevido:</strong> Um cart&#227;o bloqueado porque a fatura consta em aberto (quando j&#225; foi paga) gera uma experi&#234;ncia terr&#237;vel e danos morais.</p></li></ul><p>O objetivo do <em>Self-Healing</em> &#233; mitigar esse risco. &#201; garantir que a <strong>realidade do cliente</strong> (ele pagou) seja igual &#224; <strong>realidade do sistema</strong> (o status &#233; PAID), independente de falhas na rede. Inconsist&#234;ncia &#233; inaceit&#225;vel.</p><h4>3. Access Patterns: Seja Orientado a Perguntas</h4><p>Aqui entra a virada de chave mental. Para criar um sistema que se cura, voc&#234; precisa modelar seus dados baseado nas perguntas de auditoria, n&#227;o apenas nas perguntas de navega&#231;&#227;o.</p><p>N&#227;o adianta apenas perguntar: <em>&#8220;Como recupero o contrato do cliente X?&#8221;</em>. Voc&#234; deve perguntar: <em>&#8220;Como o sistema identifica, em meio a 10 milh&#245;es de contratos, quais deveriam ter fechado ontem e n&#227;o fecharam?&#8221;</em></p><p>Se voc&#234; n&#227;o tiver essa resposta na ponta da l&#237;ngua (e modelada no seu banco de dados), voc&#234; n&#227;o tem observabilidade, voc&#234; tem cegueira.</p><ul><li><p>Se o neg&#243;cio diz: &#8220;Faturas acima de R$ 5k n&#227;o podem ficar abertas por mais de 30 dias&#8221;, sua arquitetura deve responder &#224; pergunta: <em>&#8220;Onde est&#227;o as faturas &gt; 5k com days_delayed &gt; 30?&#8221;</em> de forma instant&#226;nea.</p></li></ul><p>O sucesso do <em>Self-Healing</em> depende 20% de tecnologia (Lambdas, Streams, Queues) e 80% de entender profundamente as regras de neg&#243;cio para converter &#8220;Dores de Suporte&#8221; em &#8220;Consultas de Auditoria&#8221;.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share&quot;,&quot;text&quot;:&quot;Compartilhar Chronicles of a Pragmatic Programmer&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Compartilhar Chronicles of a Pragmatic Programmer</span></a></p><h2>Conclus&#227;o</h2><p>H&#225; uma verdade desconfort&#225;vel que todo Tech Lead aprende eventualmente: <strong>em sistemas distribu&#237;dos, a esperan&#231;a n&#227;o &#233; uma estrat&#233;gia de engenharia.</strong></p><p>N&#243;s gastamos horas desenhando diagramas perfeitos no Miro, onde setas conectam microsservi&#231;os em uma dan&#231;a sincronizada. Mas em produ&#231;&#227;o, redes oscilam, brokers engasgam, deploys introduzem bugs e parceiros externos ficam fora do ar. A entropia sempre vence.</p><p>A arquitetura de <em>Self-Healing</em> n&#227;o &#233; sobre construir sistemas que nunca falham; isso &#233; imposs&#237;vel. &#201; sobre construir sistemas que <strong>falham com eleg&#226;ncia e se recuperam com autonomia</strong>.</p><p>Ao adotar essa estrat&#233;gia no seu ciclo de cr&#233;dito e cobran&#231;a, voc&#234; muda o jogo:</p><ol><li><p><strong>Tecnicamente</strong>, voc&#234; para de tratar inconsist&#234;ncias de dados como &#8220;incidentes de suporte&#8221; e passa a trat&#225;-las como um estado esperado que o pr&#243;prio software resolve.</p></li><li><p><strong>Culturalmente</strong>, voc&#234; for&#231;a sua equipe a sair do piloto autom&#225;tico do &#8220;Happy Path&#8221; e a desenvolver uma obsess&#227;o saud&#225;vel pelos padr&#245;es de acesso e pelas regras de neg&#243;cio.</p></li><li><p><strong>Financeiramente</strong>, voc&#234; protege o ativo mais valioso do banco: a integridade do balan&#231;o e a conformidade regulat&#243;ria.</p></li></ol><p>Implementar <em>Sweepers</em>, garantir idempot&#234;ncia e desenhar para a falha d&#225; trabalho. Exige mais c&#243;digo, mais testes e mais infraestrutura. Mas o retorno desse investimento &#233; a moeda mais valiosa para quem opera sistemas cr&#237;ticos: <strong>a tranquilidade</strong>.</p><p>&#201; a diferen&#231;a entre ser acordado &#224;s 3 da manh&#227; porque &#8220;o fechamento n&#227;o bateu&#8221; ou chegar no escrit&#243;rio &#224;s 9h, abrir o dashboard e ver que o sistema detectou 50 falhas durante a madrugada &#8212; e corrigiu todas elas sozinho, enquanto voc&#234; dormia.</p><p>No fim do dia, programar para o sucesso &#233; f&#225;cil. O que separa os sistemas fr&#225;geis dos resilientes &#233; o quanto nos preparamos para o inevit&#225;vel fracasso.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p><strong>Nota sobre Regula&#231;&#227;o Banc&#225;ria e Risco de Compliance:</strong> No Brasil, a classifica&#231;&#227;o de risco de cr&#233;dito e o provisionamento (PDD) s&#227;o regidos historicamente pela <strong>Resolu&#231;&#227;o CMN 2.682/1999</strong>, que determina o n&#237;vel de provis&#227;o com base nos dias de atraso (ex: atrasos superiores a 180 dias exigem provisionamento de 100%). Mais recentemente, a <strong>Resolu&#231;&#227;o CMN 4.966/2021</strong> (vigente a partir de 2025 para a maioria das institui&#231;&#245;es) alinhou as normas locais ao padr&#227;o internacional IFRS 9, focando na &#8220;Perda Esperada&#8221;. Falhas sist&#234;micas que impe&#231;am o reconhecimento cont&#225;bil desses estados violam a integridade das demonstra&#231;&#245;es financeiras, sujeitando a institui&#231;&#227;o a multas administrativas severas (conforme <strong>Lei n&#186; 13.506/2017</strong>) e exig&#234;ncias de capital adicional por Risco Operacional.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Desafios de Arquitetura e Resiliência em Sistemas de Cobrança]]></title><description><![CDATA["A primeira regra &#233; n&#227;o perder dinheiro. A segunda regra &#233; n&#227;o esquecer a primeira." &#8211; Warren Buffett]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/desafios-de-arquitetura-e-resiliencia</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/desafios-de-arquitetura-e-resiliencia</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Mon, 12 Jan 2026 18:32:12 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/ec75b9b1-e350-4dab-a9bf-d98ef6960087_420x300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Para um arquiteto de software, poucas coisas s&#227;o t&#227;o elegantes quanto um diagrama de microsservi&#231;os bem desenhado: eventos fluindo assincronamente via Kafka, desacoplamento total, escalabilidade horizontal e bancos de dados otimizados para leitura e escrita. &#201; o &#8220;Estado da Arte&#8221;.</p><p>Mas, quando aplicamos essa arquitetura moderna ao <strong>cr&#233;dito </strong>&#8212; e especificamente &#224; Engenharia de Cobran&#231;a &#8212; o &#8220;Estado da Arte&#8221; colide violentamente com o regras e normas!</p><p>Na cobran&#231;a, a <strong>Consist&#234;ncia Eventual</strong> n&#227;o &#233; apenas um detalhe t&#233;cnico; &#233; um risco jur&#237;dico. Um atraso de 500 milissegundos na ingest&#227;o de um evento de pagamento pode significar um SMS de amea&#231;a enviado indevidamente a um cliente que acabou de pagar a fatura. O resultado? Dano moral, reclama&#231;&#227;o no BACEN e preju&#237;zo reputacional.</p><p>Como programadores, fomos treinados para lidar com <em>deadlocks</em>, <em>lat&#234;ncia</em> e <em>throughput</em>. Mas, na trincheira do sistema financeiro brasileiro, precisamos lidar com vari&#225;veis muito mais r&#237;gidas: a <strong>Resolu&#231;&#227;o CMN 4.966</strong>, o arquivo <strong>DOC 3040 (SCR)</strong>, a <strong>Lei do Superendividamento</strong> e janelas de processamento <em>batch</em> que parecem ter parado nos anos 90.</p><p>N&#227;o basta que o c&#243;digo compile e os testes passem. O sistema precisa garantir que o &#8220;<a href="https://aws.amazon.com/pt/blogs/aws-brasil/blockchain-ou-ledger-centralizado-onde-e-quando-usa-los/">Ledger</a>&#8221; (livro raz&#227;o) sobreviva ao caos de um sistema distribu&#237;do, conciliando a velocidade instant&#226;nea do Pix com a lentid&#227;o burocr&#225;tica dos arquivos de remessa para o Serasa.</p><p>Neste artigo, vamos sair do &#8220;Happy Path&#8221; da arquitetura e mergulhar nos desafios reais de desenhar sistemas de Cobran&#231;a e Cr&#233;dito.</p><p>Bem-vindos ao mundo onde a <strong>exatid&#227;o</strong> <strong>vale mais que a performance</strong>.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>As Regras do Jogo</h3><p>Vamos ser honestos: a maioria de n&#243;s, quando pensa em &#8220;requisitos de sistema&#8221;, pensa em lat&#234;ncia, <em>throughput</em>, escalabilidade e disponibilidade. Se o servidor respondeu em 20ms, o trabalho est&#225; feito, certo?</p><p><strong>Errado. </strong>No mundo financeiro, um sistema perform&#225;tico que ignora a legisla&#231;&#227;o &#233; apenas uma m&#225;quina muito eficiente de gerar processos judiciais. Antes de escrevermos a primeira linha de c&#243;digo para cobrar algu&#233;m, precisamos entender o campo minado regulat&#243;rio onde estamos pisando.</p><p>O desafio t&#233;cnico aqui n&#227;o &#233; apenas &#8220;cobrar&#8221;. &#201; cobrar de forma cir&#250;rgica, respeitando limites invis&#237;veis que, se cruzados, transformam sua arquitetura em um passivo milion&#225;rio.</p><p>Vamos dissecar os tr&#234;s grandes &#8220;chef&#245;es&#8221; que seu c&#243;digo precisa derrotar antes de mandar aquele SMS.</p><div class="pullquote"><p><strong>&#8220;O risco vem de voc&#234; n&#227;o saber o que est&#225; fazendo.&#8221;</strong> &#8211; <em>Warren Buffett</em> </p></div><h4>O Limite do Ass&#233;dio: Por que voc&#234; precisa de um Global Rate Limiter</h4><p>O Artigo 42 do C&#243;digo de Defesa do Consumidor (CDC) &#233; claro: <em>&#8220;Na cobran&#231;a de d&#233;bitos, o consumidor inadimplente n&#227;o ser&#225; exposto a rid&#237;culo, nem ser&#225; submetido a qualquer tipo de constrangimento ou amea&#231;a.&#8221;</em></p><p>O problema &#233; que, em uma arquitetura de microsservi&#231;os, temos m&#250;ltiplos &#8220;atores&#8221; independentes tentando recuperar o dinheiro:</p><ol><li><p>O microsservi&#231;o de <strong>SMS</strong> quer mandar um lembrete.</p></li><li><p>O sistema de <strong>Discador Autom&#225;tico (Dialer)</strong> quer ligar.</p></li><li><p>O servi&#231;o de <strong>Push Notification</strong> quer vibrar o celular.</p></li><li><p>O parceiro externo (Assessoria) quer mandar um WhatsApp.</p></li></ol><p>Se cada um desses sistemas trabalhar de forma isolada (o famoso &#8220;desacoplamento&#8221;), seu cliente receber&#225; 15 contatos em uma manh&#227;. Isso configura <strong>ass&#233;dio</strong>.</p><p>Al&#233;m disso, temos a <strong><a href="https://cmsarquivos.autorregulacaobancaria.com.br/Arquivos/documentos/PDF/Normativo%20de%20Relacionamento%20com%20o%20Consumidor%20Potencialmente%20Vulner%C3%A1vel%20-%20aprovada%20CAR%2031_03_21.pdf">Autorregula&#231;&#227;o da FEBRABAN (SARB 024)</a></strong>, que estabelece normas de conduta, e o &#8220;N&#227;o Perturbe&#8221;.</p><p>Como garantir que o <em>Service A</em> saiba que o <em>Service B</em> j&#225; ligou para o cliente h&#225; 10 minutos?</p><p>Aqui morre a ideia de sistemas totalmente independentes. Voc&#234; precisa implementar um Global Rate Limiter<strong> Centrado no Cliente</strong>. N&#227;o estamos falando de limitar requisi&#231;&#245;es por segundo (RPS) para proteger sua API. Estamos falando de limitar a <strong>fric&#231;&#227;o humana</strong>.</p><p>Voc&#234; precisar&#225; de uma estrutura de dados compartilhada (provavelmente um Redis ou DynamoDB com TTL) que funcione como o &#8220;Guarda de Tr&#226;nsito&#8221; da sua r&#233;gua de cobran&#231;a:</p><ul><li><p><em>Chave:</em> <code>interaction:limit:{cpf}:{data}</code></p></li><li><p><em>Regra:</em> Se <code>count &gt; 3</code>, rejeite qualquer tentativa de contato, n&#227;o importa o canal.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!AOI-!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!AOI-!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!AOI-!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!AOI-!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!AOI-!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!AOI-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png" width="1456" height="778" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:778,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7595422,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183951977?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!AOI-!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!AOI-!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!AOI-!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!AOI-!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9e19bbe6-7124-41ff-bcb7-c2c96a372f4d_2816x1504.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Seu c&#243;digo precisa ter a intelig&#234;ncia de dizer: <em>&#8220;Eu poderia mandar esse SMS agora e ele custaria R$ 0,05. Mas o risco jur&#237;dico de mand&#225;-lo ap&#243;s uma liga&#231;&#227;o telef&#244;nica custa R$ 5.000,00 em danos morais. <strong>Drop the packet.</strong>&#8220;</em></p><h4>O Rel&#243;gio Biol&#243;gico do Cron Job</h4><p>Voc&#234; configurou seu <em>Cron Job</em> para rodar &#224;s 08:00 da manh&#227;. Lindo. Mas onde est&#225; o seu cliente?</p><p>Se o seu servidor est&#225; em <em>us-east-1</em> (Virg&#237;nia) e s&#227;o 08:00 UTC, que horas s&#227;o para o cliente que mora no Acre (-2h de Bras&#237;lia)? Ou pior: hoje &#233; feriado estadual em S&#227;o Paulo?</p><p>A <strong>Lei Estadual 17.334 de S&#227;o Paulo</strong> (e similares em outros estados) pro&#237;be liga&#231;&#245;es de telemarketing e cobran&#231;a em feriados e fora do hor&#225;rio comercial (geralmente 8h &#224;s 20h nos dias &#250;teis).</p><p>O erro do programador j&#250;nior &#233; validar o hor&#225;rio do servidor (<code>DateTime.Now</code>). O acerto do engenheiro s&#234;nior &#233; validar o hor&#225;rio do <strong>DDD do cliente</strong>.</p><p>Sua arquitetura de agendamento precisa ser enriquecida com metadados geogr&#225;ficos. Antes de disparar a <em>command</em> para o discador, seu sistema deve consultar uma tabela de feriados nacionais, estaduais e municipais. Se hoje &#233; anivers&#225;rio da cidade do cliente, seu sistema deve ficar em sil&#234;ncio. Um <em>Cron</em> burro gera multas inteligentes.</p><h4>A Lei do Superendividamento (Lei 14.181/2021)</h4><p>Esta &#233; a legisla&#231;&#227;o mais recente e a que mais impacta a l&#243;gica de &#8220;D&#233;bito Autom&#225;tico&#8221; e recupera&#231;&#227;o de cr&#233;dito.</p><p>A lei introduziu o conceito de <strong>&#8220;<a href="https://www.serasa.com.br/limpa-nome-online/blog/lei-do-superendividamento/">M&#237;nimo Existencial</a>&#8221;</strong>. Em termos simples: o banco n&#227;o pode limpar a conta do cliente para cobrir uma d&#237;vida se isso deixar a pessoa sem dinheiro para comer.</p><p>Antigamente, a l&#243;gica do sistema era predadora:</p><pre><code><code>if saldo_conta &gt; 0:
    debitar_divida(saldo_conta) # Pega tudo o que der!</code></code></pre><p>Hoje, esse c&#243;digo &#233; ilegal sob certas condi&#231;&#245;es. <a href="https://www.gov.br/mj/pt-br/assuntos/seus-direitos/consumidor/defesadoconsumidor/Superendividamento">A Lei 14.181 exige que as institui&#231;&#245;es previnam o superendividamento e garantam a dignidade do consumidor.</a></p><p><strong>O Impacto no C&#243;digo:</strong> Seu motor de cobran&#231;a n&#227;o pode mais olhar apenas para o <code>Saldo</code> e para a <code>D&#237;vida</code>. Ele precisa ter acesso (ou estimativa) da <code>Renda</code> e do <code>Comprometimento de Renda</code> desse cliente. A l&#243;gica de <em>Business Rule</em> precisa evoluir para: <em>&#8220;Se eu debitar essa parcela agora, vou consumir mais de X% da renda mensal declarada desse CPF? Se sim, aborte ou debite um valor parcial.&#8221;</em></p><p>Isso exige uma orquestra&#231;&#227;o de dados muito mais complexa. O servi&#231;o de Cobran&#231;a precisa consultar o servi&#231;o de Cadastro/Renda em tempo real (ou ter esses dados cacheados) antes de efetuar o <em>settlement</em> da parcela.</p><h2>A Matem&#225;tica da Perda</h2><p>Se o t&#243;pico anterior tratava de como <em>falar</em> com o cliente, este trata de como <em>contabilizar</em> o cliente. E aqui reside uma das maiores armadilhas para times de engenharia que migram de sistemas legados para arquiteturas modernas.</p><p>Durante d&#233;cadas, o sistema financeiro brasileiro operou sob a l&#243;gica da <strong>Resolu&#231;&#227;o 2.682/99</strong>. Para um programador, essa resolu&#231;&#227;o era um sonho. Ela era determin&#237;stica, baseada em regras r&#237;gidas (&#8221;Hard Rules&#8221;). O algoritmo era basicamente uma escada simples:</p><pre><code><code>// A L&#243;gica Antiga (Resolu&#231;&#227;o 2.682) - O sonho antigo do dev
if (atraso &lt;= 14) return Rating.A;
else if (atraso &lt;= 30) return Rating.B;
// ...
else if (atraso &gt; 360) return Status.Prejuizo; // Write-off
</code></code></pre><p>Era confort&#225;vel. Voc&#234; esperava o problema acontecer (o atraso) para ent&#227;o reagir contabilmente. Cham&#225;vamos isso de <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Instru%C3%A7%C3%A3o%20Normativa%20BCB&amp;numero=493">Perda Incorrida</a></strong> (<em>Incurred Loss</em>). O banco s&#243; admitia que perdeu dinheiro quando o dinheiro j&#225; tinha sumido h&#225; um ano.</p><p>Mas, em 2025 (com a plena vig&#234;ncia da <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Resolu%C3%A7%C3%A3o%20CMN&amp;numero=4966">Resolu&#231;&#227;o CMN 4.966/2021</a></strong>), o jogo mudou. O Brasil adotou o padr&#227;o internacional <a href="https://www.ifrs.org/content/dam/ifrs/publications/pdf-standards/portugese-brazilian/2012/issued/part-a/ifrs-9-financial-instruments-pt.pdf?bypass=on">IFRS 9</a>. A l&#243;gica de &#8220;Perda Incorrida&#8221; morreu. Bem-vindos &#224; era da <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Instru%C3%A7%C3%A3o%20Normativa%20BCB&amp;numero=577">Perda Esperada</a></strong> (<em><a href="https://www.ifrs.org/content/dam/ifrs/project/fi-impairment/exposure-draft-2013/webcast/expected-credit-losses-slides.pdf">Expected Loss</a></em>).</p><h4>Por que isso MUDA a sua arquitetura?</h4><p>Porque o provisionamento cont&#225;bil (quanto dinheiro o banco precisa separar para cobrir calotes) n&#227;o &#233; mais um reflexo do <em>passado</em>, mas uma proje&#231;&#227;o do <em>futuro</em>.</p><p>Seu sistema n&#227;o pode mais ser apenas um reposit&#243;rio passivo de transa&#231;&#245;es. <strong>Ele precisa ser um motor de c&#225;lculo de risco</strong>. A resolu&#231;&#227;o divide a vida do contrato em tr&#234;s est&#225;gios (Stages), e o seu c&#243;digo precisa saber transitar o cliente entre eles dinamicamente:</p><ul><li><p><strong>Est&#225;gio 1 (O cliente &#8220;bom&#8221;):</strong> O cliente pagou em dia. Tudo certo? N&#227;o. Pela norma, voc&#234; j&#225; precisa calcular a probabilidade dele n&#227;o pagar nos pr&#243;ximos 12 meses e provisionar um percentual disso. O sistema precisa consumir modelos de risco mesmo para quem nunca atrasou.</p></li><li><p><strong>Est&#225;gio 2 (O sinal amarelo):</strong> Aqui a complexidade explode. Se o &#8220;Risco de Cr&#233;dito&#8221; aumentou significativamente (ex: o score do cliente caiu no Bureau, ou ele atrasou meros 5 dias), voc&#234; n&#227;o provisiona mais 12 meses. Voc&#234; provisiona a perda esperada para a <strong>vida inteira</strong> do contrato.</p><ul><li><p><em>Desafio T&#233;cnico:</em> Seu sistema precisa detectar &#8220;Gatilhos de Risco&#8221; (Triggers) externos, n&#227;o apenas olhar para a sua pr&#243;pria tabela de faturas.</p></li></ul></li><li><p><strong>Est&#225;gio 3 (O Ativo Problem&#225;tico):</strong> Esque&#231;a os 360 dias da regra antiga. O novo &#8220;game over&#8221; &#233; <strong>90 dias</strong>.</p><ul><li><p>Se o cliente atrasar mais de 90 dias, o contrato &#233; considerado &#8220;Ativo Problem&#225;tico&#8221; (<em>Default</em>). A engenharia precisa marcar esse contrato como deteriorado muito antes. Se o seu <em>job</em> de <em>Write-off<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></em> ainda roda procurando por <code>days_overdue &gt; 360</code>, seu banco est&#225; carregando &#8220;lixo cont&#225;bil&#8221; no balan&#231;o por 270 dias a mais do que deveria. Isso distorce o lucro l&#237;quido e atrai a f&#250;ria dos auditores.</p></li></ul></li></ul><h4>Documento 3040 (SCR)</h4><p>Toda essa l&#243;gica complexa desemboca em um &#250;nico ponto cr&#237;tico de falha: o <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/scrdoc3040">Documento 3040</a></strong> (<a href="https://www.bcb.gov.br/estabilidadefinanceira/scr">o famoso SCR - Sistema de Informa&#231;&#245;es de Cr&#233;dito do Banco Central</a>).</p><p>Para o desenvolvedor desavisado, o 3040 &#233; &#8220;apenas um XML chato&#8221; que precisa ser gerado e enviado via FTP at&#233; o 9&#186; dia &#250;til do m&#234;s seguinte. <a href="https://www.bcb.gov.br/content/estabilidadefinanceira/Leiaute_de_documentos/scrdoc3040/SCR_InstrucoesDePreenchimento_Doc3040.pdf">Para o banco e para o cliente, o 3040 &#233; a </a><strong><a href="https://www.bcb.gov.br/content/estabilidadefinanceira/Leiaute_de_documentos/scrdoc3040/SCR_InstrucoesDePreenchimento_Doc3040.pdf">verdade absoluta</a></strong>.</p><p>Aqui est&#225; o cen&#225;rio de pesadelo que acontece quando a consist&#234;ncia eventual falha na gera&#231;&#227;o desse arquivo:</p><ol><li><p>O cliente paga a d&#237;vida no dia 28.</p></li><li><p>Seu microsservi&#231;o de <em>Ingestion</em> recebe o pagamento, mas falha ao atualizar a tabela de &#8220;Estado Cont&#225;bil&#8221; (usada para gerar o relat&#243;rio).</p></li><li><p>O <em>job</em> do 3040 roda no dia 5 do m&#234;s seguinte, l&#234; a tabela desatualizada e envia para o BACEN que aquele cliente ainda est&#225; em &#8220;Preju&#237;zo&#8221;.</p></li><li><p>O cliente tenta financiar um apartamento em outro banco e &#233; negado porque &#8220;consta preju&#237;zo no SCR&#8221;.</p></li><li><p>O cliente processa o seu banco. E ele <strong>vai ganhar</strong>.</p></li></ol><p>Ent&#227;o<strong> </strong>o SCR n&#227;o &#233; um log. <strong>O SCR &#233; a reputa&#231;&#227;o financeira do cidad&#227;o</strong>. A gera&#231;&#227;o desse arquivo n&#227;o pode ser baseada em um <code>SELECT</code> simples em uma r&#233;plica de leitura com <em>lag</em>. Ela exige processos de <strong>Reconcilia&#231;&#227;o Pr&#233;via</strong>.</p><p>Antes de gerar o XML para o BACEN, seu sistema deve rodar uma rotina de &#8220;Sanidade de Dados&#8221;:</p><ul><li><p><em>Check:</em> O saldo cont&#225;bil bate com a soma das parcelas abertas?</p></li><li><p><em>Check:</em> Existem pagamentos processados no Gateway que n&#227;o foram baixados no Core?</p></li><li><p><em>Check:</em> H&#225; clientes marcados como &#8220;Preju&#237;zo&#8221; que receberam pagamentos recentes?</p></li></ul><p>Se a resposta for &#8220;sim&#8221; para qualquer inconsist&#234;ncia, o sistema deve alertar um humano ou auto-corrigir <strong>antes</strong> de enviar o arquivo. Enviar dados errados para o BACEN n&#227;o &#233; um <em>bug</em>, <strong>&#233; uma infra&#231;&#227;o regulat&#243;ria sujeita a multas e, em casos extremos, interven&#231;&#227;o</strong>.</p><p>Na engenharia de cr&#233;dito, a exatid&#227;o dos dados &#233; a &#250;nica <em>feature</em> que realmente importa no final do m&#234;s.</p><h2>Negativa&#231;&#227;o, Bureaus e o Legado que Sobrevive</h2><p>Se o provisionamento &#233; silencioso e cont&#225;bil, a <strong>Negativa&#231;&#227;o</strong> &#233; barulhenta e destrutiva.</p><p>Quando voc&#234; marca um cliente como inadimplente no seu banco de dados, o problema &#233; interno. Mas quando voc&#234; envia esse nome para os Bureaus de Cr&#233;dito (Serasa, Boa Vista, SPC), voc&#234; est&#225; acionando a &#8220;op&#231;&#227;o nuclear&#8221;. Voc&#234; est&#225; <strong>efetivamente</strong> impedindo aquela pessoa de alugar um apartamento, financiar um carro ou obter um cart&#227;o de cr&#233;dito em <em>qualquer outro lugar</em>.</p><p>Para um Engenheiro de Software, integrar com APIs RESTful modernas &#233; lazer. Mas integrar com o ecossistema de prote&#231;&#227;o ao cr&#233;dito &#233; arqueologia.</p><h4>A Sopa de Letrinhas (PEFIN e REFIN) e os Arquivos Posicionais</h4><p>Esque&#231;a JSON, gRPC ou GraphQL. A linguagem universal da negativa&#231;&#227;o no Brasil ainda &#233; o <strong>Arquivo de Texto Posicional</strong> (Flat File).</p><ul><li><p><strong>O Que S&#227;o:</strong> Estamos falando de arquivos <code>.txt</code> onde a posi&#231;&#227;o exata do caractere define o dado. Do caractere 1 ao 11 &#233; o CPF; do 12 ao 20 &#233; o valor; do 21 ao 28 &#233; a data. Se voc&#234; inserir um espa&#231;o em branco extra na linha 4000, o arquivo inteiro &#233; rejeitado e nenhuma negativa&#231;&#227;o acontece naquele dia.</p></li><li><p><strong>Os Protocolos:</strong> Esses arquivos geralmente viajam via <strong><a href="https://aws.amazon.com/pt/what-is/sftp/">SFTP</a></strong> ou ferramentas corporativas jur&#225;ssicas como o <strong><a href="https://www.worldofintegration.com/SMF/category/communication-protocols/connect-direct-cd">ConnectDirect (C:D)</a></strong>. N&#227;o existe <code>HTTP 200 OK</code>. Existe &#8220;O arquivo foi depositado na pasta&#8221;. Se ele foi processado com sucesso? <strong>Voc&#234; s&#243; vai saber amanh&#227;, lendo o arquivo de Retorno</strong>.</p></li><li><p><strong><a href="https://www.jusbrasil.com.br/artigos/pefin-e-refin-entenda-a-diferenca-entre-os-principais-registros-de-inadimplencia/5348344045">PEFIN vs. REFIN</a>:</strong></p><ul><li><p><em>PEFIN (Pend&#234;ncia Financeira):</em> Usado para d&#237;vidas gerais (varejo, telecom, etc.).</p></li><li><p><em>REFIN (Pend&#234;ncia Banc&#225;ria):</em> Exclusivo para d&#237;vidas com institui&#231;&#245;es financeiras. Seu sistema precisa saber exatamente qual layout usar, ou a remessa volta.</p></li></ul></li></ul><p>Voc&#234; construiu uma arquitetura linda, <em>Event-Driven</em>, rodando no Kubernetes, que cospe eventos o dia todo. Mas se voc&#234; utiliza o Serasa, ele trabalha em <strong>Batch (posso afirmar isso pois trabalho com sistemas que geram esses arquivos posicionais do sistema </strong><em><strong>Sisconvem</strong></em><strong>)</strong>. Eles n&#227;o querem um evento a cada cliente que atrasa. Eles querem um arquivo, uma vez por dia (geralmente na madrugada), consolidando tudo. O seu desafio &#233; criar uma <strong>janela de agrega&#231;&#227;o</strong> robusta: voc&#234; precisa acumular o estado do dia, gerar esse arquivo monol&#237;tico sem corromper os dados e transmiti-lo com seguran&#231;a.</p><h4>A Regra de Ouro: Os 5 Dias da &#8220;Baixa&#8221;</h4><p>Aqui &#233; onde a tecnologia encontra o tribunal.</p><p>Segundo o C&#243;digo de Defesa do Consumidor e a S&#250;mula do STJ, ap&#243;s o cliente pagar a d&#237;vida, a institui&#231;&#227;o financeira tem um prazo m&#225;ximo de <strong>5 dias &#250;teis</strong> para retirar o nome dele dos &#243;rg&#227;os de prote&#231;&#227;o (dar a &#8220;Baixa&#8221;).<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><p><strong>Onde mora o perigo para o Engenheiro:</strong> Imagine o seguinte cen&#225;rio de &#8220;Tempestade Perfeita&#8221;:</p><ol><li><p><strong>Sexta-feira, 16:00:</strong> O cliente paga a d&#237;vida via Pix. Seu microsservi&#231;o de <em>Payment</em> processa e emite o evento <code>DebtSettled</code>.</p></li><li><p><strong>Sexta-feira, 23:00:</strong> Seu <em>Cron Job</em> que gera o arquivo de &#8220;Exclus&#227;o de Negativa&#231;&#227;o&#8221; para o Serasa falha silenciosamente (talvez um erro de mem&#243;ria ou timeout na conex&#227;o SFTP).</p></li><li><p><strong>S&#225;bado e Domingo:</strong> Ningu&#233;m olha os logs.</p></li><li><p><strong>Segunda-feira:</strong> Feriado banc&#225;rio. O batch n&#227;o roda.</p></li><li><p><strong>Ter&#231;a-feira:</strong> O time descobre a falha. O arquivo &#233; gerado e enviado &#224; noite.</p></li><li><p><strong>Quarta-feira:</strong> O Bureau processa o arquivo.</p></li></ol><p>Voc&#234; est&#225; perigosamente perto de estourar o prazo. Se o processamento do Bureau atrasar mais um dia, o nome do cliente continua sujo ilegalmente.</p><p>Se esse cliente tentar um financiamento na quarta-feira e for negado, ele tira um print da tela, processa seu banco por <strong><a href="https://www.tjdft.jus.br/institucional/imprensa/campanhas-e-produtos/direito-facil/edicao-semanal/dano-moral-in-re-ipsa201d">Danos Morais In Re Ipsa</a></strong> (dano presumido, que n&#227;o precisa de prova do preju&#237;zo) e ganha. A falha do seu <em>Cron Job</em> acabou de custar R$ 10.000,00 ao banco.</p><p>Aqui est&#225; o desenvolvimento do t&#243;pico, conectando a dor financeira (o preju&#237;zo de R$ 10k) &#224; solu&#231;&#227;o arquitetural (FSM), escrito com a profundidade t&#233;cnica que voc&#234; pediu.</p><h2>Por que a Ignor&#226;ncia do Dom&#237;nio &#233; o seu Maior Bug</h2><p>Depois de ler sobre leis, arquivos posicionais e regras do Banco Central, voc&#234;, engenheiro de software, pode estar se perguntando: <em>&#8220;Por que eu preciso saber a diferen&#231;a entre PEFIN e REFIN? Eu fui contratado para escrever Go/Java/C#, configurar Kubernetes e otimizar queries, n&#227;o para ser advogado.&#8221;</em></p><p>A resposta &#233; dura, mas necess&#225;ria: <strong>Em sistemas financeiros, n&#227;o existe separa&#231;&#227;o entre &#8220;C&#243;digo&#8221; e &#8220;Regra de Neg&#243;cio&#8221;. O c&#243;digo &#201; a regra.</strong></p><p>Se voc&#234; desenha uma arquitetura sem entender as restri&#231;&#245;es legais e as limita&#231;&#245;es dos parceiros legados, voc&#234; n&#227;o est&#225; criando um sistema moderno; voc&#234; est&#225; criando uma <strong>m&#225;quina de gerar passivos trabalhistas e c&#237;veis em alta velocidade</strong>.</p><p>Vou descrever 3 motivos pelos quais o Engenheiro de Software precisam dominar o neg&#243;cio antes de sair codificando que nem loucos <strong>&#128514;</strong> :</p><h4>1. Voc&#234; &#233; o Tradutor da Lei (Domain-Driven Design na Veia)</h4><p>O documento que o BACEN publica &#233; um PDF escrito por advogados e economistas. O documento que o servidor executa &#233; um bin&#225;rio compilado. Quem faz a tradu&#231;&#227;o de um para o outro? <strong>Voc&#234;.</strong></p><p>Se voc&#234; n&#227;o entender o que significa &#8220;baixa de pagamento em 5 dias&#8221;, voc&#234; vai implementar uma fila de baixa com prioridade baixa. Quando o sistema ficar sobrecarregado, essa fila vai atrasar. O c&#243;digo funcionou (n&#227;o deu erro 500), a infraestrutura aguentou, mas a <strong>lei foi quebrada</strong>. O engenheiro precisa praticar o verdadeiro DDD (Domain-Driven Design). As entidades do seu c&#243;digo (<code>OverdueDebt</code>, <code>GracePeriod</code>, <code>ProvisioningBatch</code>) devem refletir exatamente os termos regulat&#243;rios. A ignor&#226;ncia do dom&#237;nio gera &#8220;bugs conceituais&#8221; que nenhum teste unit&#225;rio pega, mas que a auditoria do BACEN pega em segundos.</p><h4>2. A Ilus&#227;o da &#8220;Caixa Preta&#8221; e o Respeito ao Legado</h4><p>&#201; tentador olhar para arquivos posicionais e mainframes como &#8220;dinossauros&#8221; que devem ser ignorados ou encapsulados numa caixa preta m&#225;gica. Mas a realidade &#233; que <strong>o dinheiro do pa&#237;s roda nesses trilhos</strong>.</p><p>Ignorar como um arquivo CNAB funciona n&#227;o &#233; &#8220;ser moderno&#8221;, &#233; ser imprudente.</p><ul><li><p>Se voc&#234; n&#227;o sabe que o arquivo tem um limite f&#237;sico de tamanho ou hor&#225;rio de corte, voc&#234; vai desenhar um sistema de <em>streaming</em> que acumula dados infinitamente e estoura a janela de envio na madrugada.</p></li><li><p>Entender o legado te d&#225; a humildade necess&#225;ria para construir sistemas de <strong>Resili&#234;ncia Adaptativa</strong>: <em>&#8220;Meu sistema voa baixo em Kafka, mas o parceiro anda de bicicleta. Eu preciso construir um freio (Backpressure) eficiente para n&#227;o atropelar quem recebe meus dados.&#8221;</em></p></li></ul><h4>3. Resili&#234;ncia Financeira &gt; Resili&#234;ncia T&#233;cnica</h4><p>Em um e-commerce, se o servi&#231;o de &#8220;Recomenda&#231;&#227;o de Produtos&#8221; cair, o usu&#225;rio compra menos. &#201; ruim, mas aceit&#225;vel. Em cr&#233;dito, se o servi&#231;o de &#8220;C&#225;lculo de Juros&#8221; tiver um bug de arredondamento ou cair durante o processamento do lote noturno:</p><ol><li><p>Voc&#234; cobra o valor errado de 500.000 pessoas.</p></li><li><p>Voc&#234; &#233; obrigado por lei a devolver o dobro do que cobrou indevidamente (Repeti&#231;&#227;o de Ind&#233;bito).</p></li></ol><p>A resili&#234;ncia aqui n&#227;o &#233; apenas sobre o sistema estar &#8220;Online&#8221; (Uptime). &#201; sobre o sistema estar <strong>Correto</strong> (Data Integrity). Engenheiros de cr&#233;dito precisam ser paranoicos. Precisamos de <em><a href="https://aws.amazon.com/what-is/dead-letter-queue/">Dead Letter Queues</a></em> n&#227;o s&#243; para reprocessar, mas para auditar. Precisamos de idempot&#234;ncia n&#227;o s&#243; para evitar erro de banco, mas para evitar cobrar duas vezes. A arquitetura precisa ser desenhada assumindo que <strong>errar custa milh&#245;es</strong>, n&#227;o apenas lat&#234;ncia.</p><p><strong>Vamos analisar estrategias que podem nos ajudar a superar esses desafios!</strong></p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share&quot;,&quot;text&quot;:&quot;Compartilhar Chronicles of a Pragmatic Programmer&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/?utm_source=substack&amp;utm_medium=email&amp;utm_content=share&amp;action=share"><span>Compartilhar Chronicles of a Pragmatic Programmer</span></a></p><h2>Do &#8220;Boolean&#8221; &#224; M&#225;quina de Estados (Finite State Machine)</h2><p>Lembra daquele cen&#225;rio catastr&#243;fico que desenhamos&#8230; Aquele em que um simples <em>Cron Job</em> falha, o cliente processa o banco por danos morais e voc&#234; perde judicialmente?</p><p>A raiz desse problema raramente &#233; &#8220;o servidor caiu&#8221;. A raiz geralmente &#233; uma <strong>gest&#227;o de estado pobre</strong>.</p><p>Quando constru&#237;mos sistemas de cobran&#231;a, a tenta&#231;&#227;o de modelar o status de uma fatura como um simples campo no banco de dados (<code>is_paid: boolean</code> ou <code>status: string</code>) &#233; imensa. &#201; r&#225;pido, &#233; f&#225;cil e funciona nos testes unit&#225;rios. </p><p>Para evitar o preju&#237;zo financeiro e o caos jur&#237;dico, precisamos parar de tratar estados como &#8220;labels&#8221; e come&#231;ar a trat&#225;-los como um <strong><a href="https://medium.com/programadores-ajudando-programadores/os-grafos-e-os-algoritmos-697c1fd4a416">Grafo Direcionado</a></strong>.</p><h4>A Fal&#225;cia do &#8220;Pago vs. N&#227;o Pago&#8221;</h4><p>Engenheiros adoram bin&#225;rios. Zero ou Um. True ou False.</p><p><strong>O problema &#233; que a vida financeira de um cliente n&#227;o &#233; bin&#225;ria. Ela &#233; um espectro de matizes cinzentos regulados por leis e regras de neg&#243;cio.</strong></p><p>Vamos olhar para a realidade. Um contrato de cr&#233;dito (<code>CreditContract</code>) e uma cobran&#231;a (<code>BillingCollection</code>) possuem ciclos de vida distintos, mas entrela&#231;ados. Tentar resolver isso com <code>if (atraso &gt; 5)</code> espalhados pelo c&#243;digo <strong>&#233; pedir para ter bugs</strong>.</p><p>Considere a complexidade real de um Contrato de Cr&#233;dito:</p><ol><li><p><strong>Em Pr&#233;-An&#225;lise:</strong> O cliente pediu, o motor de risco est&#225; rodando.</p></li><li><p><strong>Pendente de Assinatura:</strong> Aprovado, mas falta o aceite formal (aqui entra a esteira de formaliza&#231;&#227;o).</p></li><li><p><strong>Assinado/Ativo:</strong> O dinheiro caiu na conta, o contrato est&#225; vigente.</p></li></ol><p>At&#233; aqui, &#233; o &#8220;Happy Path&#8221;. Mas e quando a primeira fatura vence e o pagamento n&#227;o cai?</p><p>O contrato continua Ativo, mas o estado da cobran&#231;a muda. E &#233; aqui que a M&#225;quina de Estados (FSM) salva sua vida.</p><h4>Modelando a Inadimpl&#234;ncia com FSM</h4><p>Precisamos entender que cliente n&#227;o vira inadimplente no minuto seguinte ao vencimento. Existe uma jornada de estados que seu sistema precisa respeitar rigorosamente para n&#227;o quebrar a lei (lembra do T&#243;pico 1?):</p><ol><li><p><strong>Estado </strong><code>Overdue_Grace</code><strong> (Atraso T&#233;cnico):</strong> O vencimento foi ontem. O cliente n&#227;o pagou. O estado muda para <code>Overdue</code>, mas entra numa sub-categoria de <em>Grace Period</em> (ex: 3 dias).</p><ul><li><p><em>A&#231;&#227;o Permitida:</em> Enviar &#8220;Lembrete amig&#225;vel&#8221; (Push).</p></li><li><p><em>A&#231;&#227;o Bloqueada:</em> Negativar no Serasa ou Bloquear o Cart&#227;o. A FSM <strong>pro&#237;be</strong> a transi&#231;&#227;o para <code>Blocked</code> ou <code>Negative_List</code> enquanto estiver na <em>Grace Window</em>.</p></li></ul></li><li><p><strong>Estado </strong><code>Overdue_Collection</code><strong> (Cobran&#231;a Ativa):</strong> Passou o per&#237;odo de gra&#231;a.</p><ul><li><p><em>Gatilho:</em> <code>DaysPastDue &gt; 5</code>.</p></li><li><p><em>Transi&#231;&#227;o de Contrato:</em> Aqui, a FSM do contrato escuta o evento da cobran&#231;a e transiciona o contrato de <code>Active</code> para <code>Blocked</code> (Bloqueio de novas compras).</p></li><li><p><em>Transi&#231;&#227;o de Cobran&#231;a:</em> O sistema come&#231;a a calcular multas e juros di&#225;rios. O <em>dialer</em> &#233; autorizado a ligar.</p></li></ul></li><li><p><strong>Estado </strong><code>Legal_Collection</code><strong> (Ajuizado):</strong> O atraso superou 60 ou 90 dias (dependendo da pol&#237;tica).</p><ul><li><p><em>A&#231;&#227;o:</em> O contrato sai da esfera amig&#225;vel e vai para um Escrit&#243;rio de Cobran&#231;a Terceirizado.</p></li><li><p><em>Guard Clause (Cl&#225;usula de Guarda):</em> O sistema <strong>n&#227;o pode</strong> aceitar um pagamento parcial simples via app sem antes entender se ocorreu uma renegocia&#231;&#227;o&#8230; Se o cliente paga um valor avulso "por fora" via App, ele pode estar quebrando um acordo jur&#237;dico que est&#225; sendo desenhado pelo escrit&#243;rio parceiro, al&#233;m de ignorar outros calculos. Nesse estado, a FSM deve bloquear a gera&#231;&#227;o de boletos padr&#227;o e for&#231;ar o cliente para o fluxo de "Renegocia&#231;&#227;o/Acordo".. A FSM bloqueia pagamentos padr&#227;o e exige uma renegocia&#231;&#227;o.</p></li></ul></li></ol><h4>Por que tudo isso pode evitar preju&#237;zo?</h4><p>A beleza da M&#225;quina de Estados &#233; o <strong>Determinismo</strong> e a <strong>Idempot&#234;ncia</strong>.</p><p>Imagine o cen&#225;rio do &#8220;Race Condition&#8221; do pagamento:</p><ul><li><p>O sistema recebe o evento <code>PaymentReceived</code>.</p></li><li><p>A FSM tenta transicionar o estado de <code>Overdue</code> para <code>Paid</code>.</p></li><li><p><strong>Sucesso:</strong> O contrato &#233; desbloqueado (<code>Blocked</code> -&gt; <code>Active</code>).</p></li><li><p><strong>1 segundo depois:</strong> O mesmo evento chega novamente (duplicidade no Kafka ou retry do Gateway).</p></li><li><p><strong>A M&#225;gica:</strong> A FSM olha o estado atual. J&#225; &#233; <code>Paid</code>. A transi&#231;&#227;o <code>Paid -&gt; Paid</code> &#233; uma opera&#231;&#227;o nula (ou idempotente). O sistema ignora.</p></li></ul><p>Sem FSM, o c&#243;digo imperativo tradicional (<code>if status == 'Overdue'</code>) poderia tentar desbloquear o contrato duas vezes, ou pior, somar o saldo do cliente duas vezes, gerando um cr&#233;dito indevido que o cliente vai gastar e voc&#234; vai perder.</p><h4>Clareza para o Neg&#243;cio (A Linguagem Ub&#237;qua)</h4><p>Por fim, abandonar as <em>strings</em> e <em>n&#250;meros m&#225;gicos</em> traz sanidade mental para a equipe.</p><p>Ningu&#233;m sabe o que status = 4 significa. O analista de suporte n&#227;o sabe, o PM n&#227;o sabe e o dev novo vai ter medo de mexer.</p><p>Mas quando voc&#234; modela seus estados explicitamente no c&#243;digo (seja usando Enums ricos em Java/C# ou Types em TS), voc&#234; cria um gloss&#225;rio vivo:</p><ul><li><p><code>CONTRACT_STATUS.WAITING_SIGNATURE</code></p></li><li><p><code>CONTRACT_STATUS.ACTIVE_IN_ARREARS</code> (Ativo, mas devendo)</p></li><li><p><code>CONTRACT_STATUS.WRITTEN_OFF</code> (Preju&#237;zo contabilizado)</p></li></ul><p>Isso permite que analistas de neg&#243;cio desenhem r&#233;guas de cobran&#231;a baseadas em estados reais, e n&#227;o em suposi&#231;&#245;es. Eles podem dizer: <em>&#8220;Se o cliente entrar em </em><code>ACTIVE_IN_ARREARS</code><em> por mais de 10 dias, dispare o evento para o CRM&#8221;</em>.</p><p>N&#227;o deixe o estado do seu cliente ser definido por um booleano solto no banco de dados. Construa uma M&#225;quina de Estados que reflita a jornada legal e financeira do contrato. &#201; a diferen&#231;a entre um sistema que &#8220;acha&#8221; que o cliente deve e um sistema que sabe exatamente em qual etapa da r&#233;gua de cobran&#231;a o cliente est&#225; &#8212; e o que &#233; legalmente permitido fazer com ele naquele momento.</p><p>Agora chegou o momento de falarmos sobre <strong>Anti-Corruption Layer</strong></p><h2><strong>Anti Corruption Layer Pattern</strong></h2><p>Para entender o <strong><a href="https://medium.com/solutions-architecture-patterns/anti-corruption-layer-pattern-bd75e1f2be7f">Anti Corruption Layer Pattern</a></strong>, imagine que a sua empresa (o Sistema de Cobran&#231;a) &#233; uma frota gigantesca de <strong>Motoboys</strong>.</p><ul><li><p>Eles s&#227;o r&#225;pidos.</p></li><li><p>Eles chegam a todo momento (Real-time).</p></li><li><p>Eles trazem pacotes pequenos (Eventos de Pagamento, Atrasos).</p></li><li><p>Eles falam g&#237;ria, s&#227;o &#225;geis e mudam de rota f&#225;cil.</p></li></ul><p>Agora, imagine que o Sistema Legado (Serasa, Bacen, Mainframes) &#233; um <strong>Navio Cargueiro Transatl&#226;ntico</strong>.</p><ul><li><p>Ele &#233; lento e pesado.</p></li><li><p>Ele s&#243; atraca no porto uma vez por dia, &#224;s 02:00 da manh&#227; (Janela Batch).</p></li><li><p>Ele s&#243; aceita cont&#234;ineres de a&#231;o padronizados e soldados (Arquivos Posicionais).</p></li><li><p>Ele n&#227;o muda de rota. O capit&#227;o &#233; r&#237;gido e segue regras de 1990.</p></li></ul><h4>Onde est&#225; o Problema? (A Corrup&#231;&#227;o)</h4><p>Se voc&#234; tentar mandar seus motoboys entregarem os pacotes diretamente no navio, o caos acontece:</p><ol><li><p><strong>Imped&#226;ncia:</strong> O motoboy chega &#224;s 10:00 da manh&#227;. O navio n&#227;o est&#225; l&#225;. O motoboy fica esperando? Ele larga o pacote na &#225;gua? (Perda de Dados).</p></li><li><p><strong>Protocolo:</strong> O motoboy tenta jogar o pacote no conv&#233;s. O capit&#227;o grita: <em>&#8220;Aqui s&#243; entra Cont&#234;iner ISO 668!&#8221;</em>. O motoboy n&#227;o sabe o que &#233; isso.</p></li><li><p><strong>Cat&#225;strofe:</strong> Se 50.000 motoboys tentarem subir na prancha do navio ao mesmo tempo, a prancha quebra (DDoS no parceiro).</p></li></ol><h4>Onde est&#225; o ACL? (O P&#225;tio de Log&#237;stica)</h4><p>O <strong>Anti-Corruption Layer</strong> &#233; o <strong>Armaz&#233;m/P&#225;tio do Porto</strong> que fica entre a rua (seus microsservi&#231;os) e o mar (o legado).</p><ol><li><p><strong>Recep&#231;&#227;o (Ingest&#227;o):</strong> O ACL recebe os motoboys o dia todo. Ele diz: <em>&#8220;Obrigado, pode ir embora, eu assumo daqui&#8221;</em>. (Desacoplamento).</p></li><li><p><strong>Organiza&#231;&#227;o (Agrega&#231;&#227;o):</strong> O ACL pega os pacotes pequenos, traduz as etiquetas (De: &#8220;Cliente Pagou&#8221; Para: &#8220;C&#243;digo 06&#8221;) e vai enchendo um cont&#234;iner virtual estacionado no p&#225;tio.</p></li><li><p><strong>Despacho (Batch):</strong> &#192;s 02:00 da manh&#227;, quando o Navio encosta, o ACL pega aquele cont&#234;iner perfeitamente arrumado e coloca no navio de uma s&#243; vez.</p></li></ol><p><strong>Por que &#8220;Anti-Corrup&#231;&#227;o&#8221;?</strong> Porque ele impede que a burocracia do Navio contamine a agilidade dos Motoboys. Com o ACL, os motoboys continuam sendo motoboys, e o navio continua sendo um navio. O ACL protege a <strong>integridade</strong> do seu modelo de dom&#237;nio moderno contra a rigidez do modelo antigo.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!eO_o!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!eO_o!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!eO_o!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!eO_o!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!eO_o!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!eO_o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/f43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9497184,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183951977?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!eO_o!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!eO_o!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!eO_o!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!eO_o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ff43f5d51-ae98-4e8c-8144-65a14c3b7cae_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TW1r!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TW1r!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png 424w, https://substackcdn.com/image/fetch/$s_!TW1r!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png 848w, https://substackcdn.com/image/fetch/$s_!TW1r!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png 1272w, https://substackcdn.com/image/fetch/$s_!TW1r!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TW1r!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png" width="962" height="334.98214285714283" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:507,&quot;width&quot;:1456,&quot;resizeWidth&quot;:962,&quot;bytes&quot;:120251,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183951977?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TW1r!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png 424w, https://substackcdn.com/image/fetch/$s_!TW1r!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png 848w, https://substackcdn.com/image/fetch/$s_!TW1r!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png 1272w, https://substackcdn.com/image/fetch/$s_!TW1r!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F6281b8c6-26c4-44c4-a8f6-cdbaf4e5735b_1757x612.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>O que mais precisamos entender?</h3><p>Existe um mito perigoso no desenvolvimento moderno: a ideia de que <em>&#8220;Se meu sistema &#233; Real-Time, tudo ao meu redor deve ser tratado como Real-Time&#8221;</em>.</p><p>Voc&#234; desenha seus microsservi&#231;os usando arquitetura Hexagonal, domina o Kafka, usa gRPC para comunica&#231;&#227;o interna e seu <em>Core Domain</em> &#233; puro, falando a &#8220;Linguagem Ub&#237;qua&#8221; do neg&#243;cio. Mas, na hora de sair para o mundo real (Serasa, Bacen, Bancos Legados), voc&#234; d&#225; de cara com um muro de concreto: o <strong>Batch Processing</strong> e os <strong>Arquivos Posicionais</strong>.</p><p>&#201; aqui que muitos projetos falham. Eles tentam conectar o Kafka direto no FTP do parceiro, criando &#8220;gambiarras&#8221; no c&#243;digo de neg&#243;cio para formatar strings. O resultado? Seu dom&#237;nio fica sujo, acoplado a regras de formata&#231;&#227;o arcaicas e imposs&#237;vel de manter.</p><p>A solu&#231;&#227;o do <em>Domain-Driven Design</em> (DDD) para isso &#233; o <strong>ACL</strong>. Mas em Finan&#231;as, o ACL n&#227;o &#233; apenas um tradutor; ele &#233; uma <strong>Represa de Caos</strong>.</p><h4>O Cen&#225;rio Real: A Tempestade da Madrugada (Billing Cycle vs. Contexto de Cobran&#231;a)</h4><p>Para entender onde a arquitetura quebra, vamos olhar para a cronologia de uma madrugada em uma Fintech.</p><p><strong>1. O Gatilho (Upstream):</strong> &#192;s 02:00 da manh&#227;, o time de <strong>Faturas</strong> roda o <em>Cron Job</em> de fechamento de ciclo. O barramento de eventos &#233; inundado com 40.000 mensagens de <code>InvoiceClosed</code>.</p><p><strong>2. A Ingest&#227;o e Contextualiza&#231;&#227;o (O &#8220;C&#233;rebro&#8221;):</strong> O sistema de Cobran&#231;a n&#227;o reage cegamente. Ele consome esses eventos e os salva em sua base interna. Ele roda uma rotina de verifica&#231;&#227;o para cada fatura fechada:</p><ul><li><p><strong>Check de Contrato:</strong> <em>&#8220;Esse cliente j&#225; possui um contrato em estado de Cobran&#231;a?&#8221;</em></p><ul><li><p><em>Se SIM:</em> A nova fatura &#233; apenas anexada &#224; d&#237;vida existente. Voc&#234; precisa ainda assim atualizar o saldo devedor.</p></li><li><p><em>Se N&#195;O:</em> O sistema olha a <strong>Data de Vencimento</strong>. <em>&#8220;Vence em 10 dias? Ok, agendar lembrete preventivo. Nada a fazer agora.&#8221;</em></p></li></ul></li></ul><p><strong>3. </strong>A Varredura dos &#8220;Veteranos&#8221; e a Granularidade da D&#237;vida</p><p>Enquanto a ingest&#227;o das faturas novas acontece, uma outra <em>thread</em> do sistema roda a varredura di&#225;ria de inadimplentes cr&#237;ticos. A query encontra 30.000 clientes com atraso.</p><p>Aqui, o sistema precisa tomar uma decis&#227;o crucial sobre o que enviar ao Bureau (Serasa/Boa Vista). N&#227;o basta olhar o valor total; &#233; preciso olhar a <strong>anatomia da d&#237;vida</strong>. O sistema cruza os dados:</p><ul><li><p><em>&#8220;Desses 30 mil, 28 mil j&#225; s&#227;o veteranos (negativados).&#8221;</em></p></li><li><p><em>&#8220;O valor da d&#237;vida mudou? Sim, os juros di&#225;rios rodaram.&#8221;</em></p></li><li><p><em>&#8220;O status mudou? Sim, o cliente pagou a parcela 3 de 10, mas a 4 venceu hoje.&#8221;</em></p></li></ul><p>Para manter a fidelidade do risco de cr&#233;dito, a arquitetura precisa suportar o <strong>Envio de Remessa Di&#225;ria de Manuten&#231;&#227;o</strong>. N&#227;o &#233; redund&#226;ncia; &#233; atualiza&#231;&#227;o de saldo. O mercado precisa saber que a d&#237;vida de R$ 1.000,00 de ontem virou R$ 1.001,50 hoje.</p><blockquote><p><strong>&#9888;&#65039; Nota de Engenharia: A Complexidade do Parcelado e o Teto do BACEN</strong></p><p>&#201; aqui que muitos motores de cobran&#231;a falham. Uma &#8220;fatura de R$ 500,00&#8221; n&#227;o &#233; uma entidade at&#244;mica simples.</p><ol><li><p><strong>A Granularidade da Compra:</strong> O sistema precisa distinguir um cliente que gastou R$ 500,00 &#224; vista (Rotativo puro) de um cliente que tem uma parcela de R$ 50,00 vencendo (Parcelado). Se o cliente pagou R$ 50,00, ele quitou a parcela, mas o saldo devedor total continua alto. O motor de cobran&#231;a deve saber exatamente <em>qual</em> parte da d&#237;vida est&#225; em atraso para calcular os juros corretamente sobre o principal, e n&#227;o sobre o total.</p></li><li><p><strong>O &#8220;Hard Cap&#8221; do BACEN (Lei 14.690):</strong> Desde 2024, vigora a regra que limita os juros e encargos a <strong>100% do valor original da d&#237;vida</strong>.</p><ul><li><p><em>O Desafio T&#233;cnico:</em> Seu algoritmo de c&#225;lculo de juros (<code>InterestService</code>) n&#227;o pode mais ser uma fun&#231;&#227;o exponencial infinita (<code>Principal * (1+taxa)^tempo</code>).</p></li><li><p><em>A Trava:</em> Ele precisa ter um <code>Guard Clause</code> matem&#225;tico: <code>TotalDebt = Min(CalculatedValue, OriginalValue * 2)</code>.</p></li></ul></li></ol><p>Se o seu sistema de cobran&#231;a continuar rodando juros &#8220;eternamente&#8221; sobre uma d&#237;vida antiga, voc&#234; estar&#225; gerando um arquivo de negativa&#231;&#227;o <strong>ilegal</strong>. O ACL ou o Core precisam garantir que o valor enviado ao Serasa nunca ultrapasse esse teto regulat&#243;rio, sob pena de multas pesadas.</p></blockquote><h4>4. O Gargalo (A Tempestade Perfeita)</h4><p>Com essa complexidade de c&#225;lculo rodando para 30.000 clientes simultaneamente, somada &#224; necessidade de gerar arquivos posicionais precisos, temos um pico de processamento massivo.</p><p>Se voc&#234; tentar abrir uma conex&#227;o direta SFTP para escrever cada uma dessas atualiza&#231;&#245;es individualmente, o firewall do parceiro bloquear&#225; seu IP por comportamento abusivo (<em>Denial of Service</em>). &#201; aqui que a arquitetura precisa do padr&#227;o de Agrega&#231;&#227;o.</p><blockquote><p><strong>&#9889; Dica de Performance: Constru&#231;&#227;o In-Memory e Streams</strong></p><p>N&#227;o cometa o erro de escrever esse arquivo em disco local (como um volume EBS ou temp folder) antes de enviar. I/O de disco &#233; lento, custa IOPS e aumenta a lat&#234;ncia.</p><p>A abordagem s&#234;nior &#233; utilizar <strong>Memory Streams</strong> (ex: <code>MemoryStream</code> em .NET ou <code>ByteArrayOutputStream</code> em Java).</p><ol><li><p>O <em>Job</em> l&#234; os 30.000 registros agregados do banco.</p></li><li><p>O arquivo posicional (CNAB/Serasa) &#233; montado inteiramente na mem&#243;ria RAM do container (buffer).</p></li><li><p><strong>No momento do envio:</strong> Quando a conex&#227;o SFTP &#233; aberta, o arquivo <strong>j&#225; est&#225; pronto</strong>.</p></li></ol><p>O upload se torna uma opera&#231;&#227;o &#250;nica e at&#244;mica de <em>streaming</em> de bytes da mem&#243;ria para a rede. Isso reduz o tempo de conex&#227;o aberta (&#8221;handshake overhead&#8221;) de minutos para segundos e evita corrup&#231;&#227;o de arquivo por falha de escrita em disco.</p></blockquote><h4>O Erro: Conex&#227;o Direta (O Suic&#237;dio do I/O)</h4><p>Se para esses 2.000 clientes cr&#237;ticos (mais as atualiza&#231;&#245;es de saldo dos outros 28.000) voc&#234; tentar abrir uma conex&#227;o direta SFTP com o Serasa a cada evento processado, voc&#234; ser&#225; bloqueado. O legado n&#227;o suporta o &#8220;rajad&#227;o&#8221; (burst) que sua arquitetura moderna gera.</p><h4>O Padr&#227;o de Agrega&#231;&#227;o (Aggregation Pattern)</h4><p>Imagine o ACL como um microsservi&#231;o isolado que assina o t&#243;pico collection.negative_list_candidates. A responsabilidade dele n&#227;o &#233; decidir quem deve, mas sim preparar a remessa de forma eficiente.</p><ol><li><p><strong>Ingest&#227;o Idempotente:</strong> O ACL consome os eventos que passaram pelo filtro da varredura.</p></li><li><p><strong>O &#8220;Bucket&#8221; (Persist&#234;ncia Tempor&#225;ria):</strong> Em vez de tentar gerar o arquivo final na hora (o que travaria o I/O), o ACL grava esses eventos em um banco de dados de alta escrita e baixa lat&#234;ncia (<em>Key-Value</em>).</p><ul><li><p><em>Tecnologia Sugerida:</em> <strong>DynamoDB</strong> (com TTL) ou <strong>Redis</strong> (com persist&#234;ncia AOF).</p></li></ul></li></ol><p>Mesmo que a varredura tenha falhado e enviado o mesmo cliente duas vezes (ex: um erro de retry no Kafka), o ACL protege o parceiro.</p><p>Ele faz um Upsert na tabela de agrega&#231;&#227;o usando o CPF como chave.</p><ul><li><p>Evento 1 (02:01): &#8220;Negativar Jo&#227;o por R$ 1.000&#8221;.</p></li><li><p>Evento 2 (02:03 - Duplicado): &#8220;Negativar Jo&#227;o por R$ 1.000&#8221;.</p></li><li><p><strong>Resultado no Bucket:</strong> Apenas <strong>uma linha</strong> &#233; mantida.</p></li></ul><pre><code><code>// Exemplo conceitual do Item no DynamoDB do ACL
{
  "PK": "DATE_2025-10-10", // Partition Key: Data da Remessa
  "SK": "CPF_12345678900", // Sort Key: Cliente
  "Action": "INCLUDE_NEGATIVE_LIST", // Ou "UPDATE_AMOUNT"
  "Amount": 1500.50,
  "SourceEventId": "evt_998877",
  "LastUpdated": "2025-10-10T02:03:00Z" // Garante que a &#250;ltima vers&#227;o prevale&#231;a
}
</code></code></pre><p>Isso transforma um fluxo ca&#243;tico, rajado e potencialmente duplicado em uma <strong>fotografia est&#225;tica, &#250;nica e organizada</strong>, pronta para ser extra&#237;da e enviada em um &#250;nico arquivo Batch seguro.</p><h3>A Janela de Corte: Onde a Engenharia perde para o Rel&#243;gio</h3><p>Agora entramos em aguas onde lidamos com o tempo e a tradu&#231;&#227;o. &#201; aqui que acontecem os erros mais sutis de <em>Race Condition</em>.</p><h4>O Pesadelo da Meia-Noite (23:59:59 vs 00:00:01)</h4><p>Sistemas de mensageria (Kafka/SQS) n&#227;o garantem ordem exata de processamento em escala massiva.</p><p>Voltando ao nosso cen&#225;rio: O Cron Job de faturas rodou perto da meia-noite.</p><ul><li><p><strong>O Problema:</strong> O evento <code>InvoiceClosed</code> foi gerado &#224;s 23:59:58 (D-0). Devido &#224; carga massiva de 40.000 mensagens, seu consumidor s&#243; processou essa mensagem &#224;s 00:00:05 (D+1).</p></li></ul><p>Se o seu ACL usar o <code>System.Now()</code> (hora do servidor) para decidir em qual arquivo de remessa colocar esse dado, voc&#234; errou.</p><ul><li><p>O evento pertence ao dia fiscal anterior.</p></li><li><p>Se voc&#234; coloc&#225;-lo no arquivo de &#8220;Hoje&#8221;, voc&#234; pode estar negativando um cliente um dia antes do permitido legalmente, ou perdendo o prazo de baixa.</p></li></ul><h4>Data L&#243;gica vs. Data F&#237;sica</h4><p>O ACL nunca deve confiar no rel&#243;gio da m&#225;quina para decidir a &#8220;Janela de Corte&#8221;. Ele deve confiar na Data do Evento (Event Time) ou na Data Cont&#225;bil que veio no payload da mensagem.</p><p>A arquitetura precisa suportar janelas de toler&#226;ncia:</p><ol><li><p>O <em>Job</em> que gera o arquivo f&#237;sico roda &#224;s 02:00 da manh&#227;.</p></li><li><p>Ele faz uma <em>query</em> no Bucket do ACL buscando tudo o que tem <code>ReferenceDate == D-0</code>.</p></li><li><p>Isso captura at&#233; mesmo as mensagens que chegaram com atraso devido ao pico de processamento, garantindo a integridade do dia fiscal.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4TgC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4TgC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png 424w, https://substackcdn.com/image/fetch/$s_!4TgC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png 848w, https://substackcdn.com/image/fetch/$s_!4TgC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png 1272w, https://substackcdn.com/image/fetch/$s_!4TgC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4TgC!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png" width="1006" height="715.1167582417582" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1035,&quot;width&quot;:1456,&quot;resizeWidth&quot;:1006,&quot;bytes&quot;:195937,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183951977?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4TgC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png 424w, https://substackcdn.com/image/fetch/$s_!4TgC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png 848w, https://substackcdn.com/image/fetch/$s_!4TgC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png 1272w, https://substackcdn.com/image/fetch/$s_!4TgC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc846e228-1e20-43da-b59f-d41a46cd81d8_1597x1135.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>A Tradu&#231;&#227;o de Protocolos: Por que precisamos de um &#8220;Diplomata&#8221;?</h4><p>Por fim, a fun&#231;&#227;o cl&#225;ssica dessa layer: <strong>Tradu&#231;&#227;o</strong>.</p><p>Seu sistema interno fala &#8220;Linguagem Ub&#237;qua&#8221;. Voc&#234; tem classes ricas como <code>CreditContract</code> e <code>CustomerScore</code>.</p><p>O sistema do parceiro fala &#8220;Layout CNAB 400&#8221; ou &#8220;Layout Serasa 600&#8221;. Eles falam em posi&#231;&#245;es de caracteres: &#8220;Posi&#231;&#227;o 104 a 105: C&#243;digo da Ocorr&#234;ncia (02 = Entrada, 06 = Baixa)&#8221;.</p><p><strong>O perigo da contamina&#231;&#227;o: Sem um ACL, voc&#234; come&#231;a a sujar seu dom&#237;nio com c&#243;digos externos:</strong></p><pre><code><code>public enum StatusDivida {
    Aberto = 1,
    Pago = 2,
    Serasa_Codigo_H = 3 // &lt;--- ISSO &#201; CONTAMINA&#199;&#195;O!
}</code></code></pre><p>O ACL atua como uma roupa de prote&#231;&#227;o. O Core emite eventos puros (DebtPaid). Ele recebe, olha para um mapa de tradu&#231;&#227;o (De/Para) e diz: &#8220;Para o dom&#237;nio, isso &#233; &#8216;Pago&#8217;. Para o Layout V2 da Serasa, isso significa escrever &#8216;06&#8217; na posi&#231;&#227;o 104.&#8221;</p><p>Isso garante que, quando a Serasa mudar o layout (e eles mudam), voc&#234; s&#243; mexe no corruption layer. O cora&#231;&#227;o do seu sistema de cr&#233;dito continua batendo, limpo e intocado.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!B8wO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!B8wO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png 424w, https://substackcdn.com/image/fetch/$s_!B8wO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png 848w, https://substackcdn.com/image/fetch/$s_!B8wO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png 1272w, https://substackcdn.com/image/fetch/$s_!B8wO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!B8wO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png" width="1456" height="1087" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:1087,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6606991,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183951977?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!B8wO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png 424w, https://substackcdn.com/image/fetch/$s_!B8wO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png 848w, https://substackcdn.com/image/fetch/$s_!B8wO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png 1272w, https://substackcdn.com/image/fetch/$s_!B8wO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa9d7a77b-a2b8-4d94-89ec-9659fe3a9e62_2400x1792.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>&#8220;Trust No One&#8221;: Reconcilia&#231;&#227;o, Shadow Accounting e a Arte do Self-Healing</h2><p>Se voc&#234; sair deste artigo com apenas uma li&#231;&#227;o, que seja esta: <strong>Em cr&#233;dito, nunca confie cegamente nos seus pr&#243;prios eventos.</strong></p><p>Voc&#234; pode ter 99.99% de disponibilidade no Kafka e testes unit&#225;rios cobrindo 100% do c&#243;digo. Ainda assim, em algum lugar, um bit vai virar, um <em>timeout</em> vai ocorrer na borda ou um arquivo ser&#225; corrompido no SFTP. Quando isso acontece, o seu banco de dados diz &#8220;A&#8221;, o extrato banc&#225;rio diz &#8220;B&#8221; e o Serasa diz &#8220;C&#8221;.</p><p>Quem tem raz&#227;o? Como evitar que essa disson&#226;ncia cognitiva vire um processo judicial? A resposta est&#225; na <strong>Reconcilia&#231;&#227;o Ofensiva</strong>.</p><h4>O Conceito de &#8220;Shadow Accounting&#8221; (A Prova Real)</h4><p>A primeira camada de defesa &#233; admitir que o seu microsservi&#231;o de Cobran&#231;a n&#227;o &#233; o dono da verdade financeira. Ele &#233; apenas uma &#8220;vis&#227;o&#8221; do que aconteceu. A verdade est&#225; no dinheiro.</p><p>O <strong>Shadow Accounting</strong> &#233; uma t&#233;cnica onde voc&#234; mant&#233;m um livro-raz&#227;o paralelo (um <em>Ledger</em>) que n&#227;o responde a eventos de usu&#225;rio (como &#8220;cliquei em pagar&#8221;), mas apenas a <strong>fatos financeiros consumados</strong>.</p><ul><li><p><strong>O Cen&#225;rio de Risco:</strong> Seu sistema recebe um evento <code>PaymentAuthorized</code> do Gateway de Cart&#227;o. O App mostra &#8220;Pago&#8221;. O cliente fica feliz.</p></li><li><p><strong>A Falha:</strong> O adquirente (a maquininha) falha na liquida&#231;&#227;o financeira dois dias depois. O dinheiro nunca caiu na conta do banco.</p></li><li><p><strong>A Defesa:</strong> O <em>Shadow Ledger</em> s&#243; d&#225; baixa na d&#237;vida cont&#225;bil quando processa o <strong>Arquivo de Retorno (CNAB)</strong> do banco confirmando o dep&#243;sito. Enquanto o dinheiro n&#227;o &#8220;pingar&#8221;, a d&#237;vida est&#225; tecnicamente aberta no <em>Shadow</em>, mesmo que a UI mostre &#8220;Em processamento&#8221;. Isso evita que voc&#234; libere limite de cr&#233;dito com dinheiro fantasma.</p></li></ul><h4>Jobs de Reconcilia&#231;&#227;o D+1</h4><p>Em Finan&#231;as, o dia s&#243; termina quando a reconcilia&#231;&#227;o roda. Voc&#234; precisa de processos <em>Batch</em> (olha eles a&#237; de novo!) que rodam na madrugada (D+1) para fazer o &#8220;Three-Way Match&#8221; (Triangula&#231;&#227;o):</p><ol><li><p><strong>Fonte 1 (Interna):</strong> O que meu banco de dados diz? (Ex: Cliente X pagou R$ 100).</p></li><li><p><strong>Fonte 2 (Banc&#225;ria):</strong> O que o arquivo CNAB do banco diz? (Ex: Recebemos R$ 100 do CPF X).</p></li><li><p><strong>Fonte 3 (Externa/Bureau):</strong> O que o log de envio para o Serasa diz? (Ex: Enviamos comando de &#8220;Baixa&#8221; para o CPF X?).</p></li></ol><p><strong>A L&#243;gica do Job:</strong></p><ul><li><p>Se (1 == 2) e (3 == &#8220;Negativado&#8221;): <strong>ALERTA CR&#205;TICO</strong>. O dinheiro entrou, mas n&#227;o avisamos o Serasa. Risco Legal Iminente.</p></li><li><p>Se (1 == &#8220;Aberto&#8221;) e (2 == &#8220;Pago&#8221;): <strong>Inconsist&#234;ncia de Ingest&#227;o</strong>. O cliente pagou, mas perdemos o evento. O sistema deve acionar o <em>Self-Healing</em>.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!e4F2!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!e4F2!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png 424w, https://substackcdn.com/image/fetch/$s_!e4F2!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png 848w, https://substackcdn.com/image/fetch/$s_!e4F2!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png 1272w, https://substackcdn.com/image/fetch/$s_!e4F2!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!e4F2!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png" width="774" height="838.9657039711192" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1201,&quot;width&quot;:1108,&quot;resizeWidth&quot;:774,&quot;bytes&quot;:183949,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/183951977?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!e4F2!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png 424w, https://substackcdn.com/image/fetch/$s_!e4F2!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png 848w, https://substackcdn.com/image/fetch/$s_!e4F2!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png 1272w, https://substackcdn.com/image/fetch/$s_!e4F2!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F39ee8978-5654-467e-841e-35186f7206d4_1108x1201.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Monitoramento de SLA Legal</h4><p>Voc&#234; precisa configurar no seu Datadog, CloudWatch ou Prometheus m&#233;tricas de neg&#243;cio que monitorem o &#8220;Risco Jur&#237;dico&#8221;:</p><ul><li><p><strong>M&#233;trica:</strong> <code>time_since_payment_settlement</code> vs <code>bureau_status</code>.</p></li><li><p><strong>O Alerta:</strong> N&#227;o quero saber se o pod caiu. Quero um alerta que diga:</p></li></ul><blockquote><p><em>&#8220;ALERTA CR&#205;TICO: O Cliente XPTO pagou h&#225; <strong>96 horas</strong> (4 dias &#250;teis) e a flag </em><code>negative_in_bureau</code><em> ainda &#233; TRUE. Restam 24h para o prazo legal de 5 dias expirar. Risco de Dano Moral.&#8221;</em></p></blockquote><p>Esse tipo de monitoramento d&#225; ao time de opera&#231;&#245;es a chance de agir <em>antes</em> do cliente chamar o advogado.</p><h4>Self-Healing: O Sistema Imunol&#243;gico</h4><p>Por fim, o &#225;pice da engenharia: a <strong>Autocura</strong>. Se o seu Job de Reconcilia&#231;&#227;o encontrou uma falha (ex: Cliente pagou, mas a baixa no Serasa n&#227;o foi enviada), <strong>por que acordar um engenheiro &#224;s 3 da manh&#227; para rodar um </strong><em><strong>script</strong></em><strong> manual?</strong></p><p>O sistema deve ser capaz de disparar a corre&#231;&#227;o:</p><ol><li><p>O Job detecta: <code>Status=PAID</code> no DB, mas <code>Bureau=NEGATIVE</code> no Log.</p></li><li><p>O Job emite um evento de compensa&#231;&#227;o: <code>ForceBureauDischarge</code> (For&#231;ar Baixa).</p></li><li><p>O ACL recebe esse evento, gera uma linha no arquivo de remessa de emerg&#234;ncia e envia.</p></li><li><p>O sistema loga: <em>&#8220;Inconsist&#234;ncia detectada e corrigida automaticamente pelo Agente de Reconcilia&#231;&#227;o em 03:15 AM.&#8221;</em></p></li></ol><p>Sistemas de cobran&#231;a resilientes n&#227;o s&#227;o aqueles que nunca falham. S&#227;o aqueles que <strong>falham, percebem que falharam e se consertam</strong> antes que o mundo exterior perceba. Isso &#233; o que diferencia um software banc&#225;rio de um projeto de fim de semana.</p><h2>Conclus&#227;o</h2><p>Ao longo deste artigo, viajamos do glamour da Arquitetura Orientada a Eventos para a realidade &#225;rida dos arquivos posicionais e das resolu&#231;&#245;es do Banco Central.</p><p>Se existe uma mensagem que eu gostaria que voc&#234; levasse para o seu pr&#243;ximo <em>Daily</em>, &#233; esta: <strong>Em Bancos, a tecnologia n&#227;o &#233; neutra.</strong></p><p>Quando constru&#237;mos sistemas de cobran&#231;a, n&#227;o estamos apenas movendo bytes de um t&#243;pico Kafka para um banco NoSQL. Estamos manipulando a reputa&#231;&#227;o financeira de pessoas reais.</p><ul><li><p>Um <em>bug</em> de concorr&#234;ncia no e-commerce faz algu&#233;m comprar dois sapatos.</p></li><li><p>Um <em>bug</em> de concorr&#234;ncia na cobran&#231;a faz algu&#233;m ser negativado indevidamente, perder o score de cr&#233;dito e ter um financiamento imobili&#225;rio negado.</p></li></ul><p>A verdadeira senioridade na engenharia de software financeira n&#227;o se mede pelo n&#250;mero de <em>frameworks</em> que voc&#234; domina, mas pela sua capacidade de:</p><ol><li><p><strong>Respeitar o Legado:</strong> Entender que o arquivo Batch do Serasa, por mais arcaico que pare&#231;a, &#233; a &#8220;API&#8221; mais importante do mercado.</p></li><li><p><strong>Traduzir a Regula&#231;&#227;o:</strong> Transformar leis abstratas (CDC, Resolu&#231;&#227;o 4.966) em c&#243;digo determin&#237;stico e test&#225;vel (FSM, ACL).</p></li><li><p><strong>Desconfiar do Sucesso:</strong> Construir sistemas paranoicos, que se auto-auditam (Reconcilia&#231;&#227;o) e se auto-corrigem (Self-Healing) antes que o erro chegue ao cliente.</p></li></ol><p>O desafio de construir o &#8220;Banco do Futuro&#8221; n&#227;o &#233; apenas tecnol&#243;gico; &#233; um desafio de<strong> risco e precis&#227;o</strong>. A consist&#234;ncia pode ser eventual, mas a responsabilidade &#233; imediata.</p><p>Que seu c&#243;digo seja limpo, sua arquitetura seja robusta e, acima de tudo, que seus <em>Jobs</em> de baixa de negativa&#231;&#227;o nunca falhem numa sexta-feira &#224; noite.</p><p>At&#233; a pr&#243;xima!</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/desafios-de-arquitetura-e-resiliencia?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Esta publica&#231;&#227;o &#233; p&#250;blica, ent&#227;o sinta-se &#224; vontade para compartilh&#225;-la.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/desafios-de-arquitetura-e-resiliencia?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/desafios-de-arquitetura-e-resiliencia?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Write-off: <em><strong>per&#237;odo quando n&#227;o h&#225; expectativa de recupera&#231;&#227;o</strong></em>.</p></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><h4>1. S&#250;mula 548 do Superior Tribunal de Justi&#231;a (STJ)</h4><p>Esta &#233; a fonte mais direta e citada:</p><blockquote><p><strong>&#8220;Incumbe ao credor a exclus&#227;o do registro da d&#237;vida em nome do devedor no cadastro de inadimplentes no prazo de cinco dias &#250;teis, a partir do integral e efetivo pagamento do d&#233;bito.&#8221;</strong></p></blockquote><ul><li><p>A S&#250;mula deixa claro que a responsabilidade &#233; do <strong>credor</strong> (o banco/fintech) e n&#227;o do bureau (Serasa/Boa Vista). Se o seu sistema falhar no envio do arquivo, o banco n&#227;o pode culpar o parceiro.</p></li></ul><h4>2. Artigo 43, &#167; 3&#186; do C&#243;digo de Defesa do Consumidor (Lei 8.078/1990)</h4><p>O CDC &#233; a lei que d&#225; sustenta&#231;&#227;o &#224; S&#250;mula. O artigo atualizado diz:</p><blockquote><p><strong>&#8220;O consumidor, sempre que encontrar inexatid&#227;o em seus dados e cadastros, poder&#225; exigir sua imediata corre&#231;&#227;o, devendo o arquivista, no prazo de cinco dias &#250;teis, comunicar a altera&#231;&#227;o aos eventuais destinat&#225;rios das informa&#231;&#245;es incorretas.&#8221;</strong></p></blockquote><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Engenharia de Crédito: Implementando Máquinas de Estado]]></title><description><![CDATA[Aprenda a blindar sistemas financeiros usando M&#225;quinas de Estados (FSM). Entenda a diferen&#231;a entre Motor de Regras e FSM, implemente o Transactional Outbox e garanta consist&#234;ncia de dados em alta escala.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-credito-implementando</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-credito-implementando</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Mon, 05 Jan 2026 16:03:43 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d9752fef-06b6-4778-a5a6-2488d0e0feaa_2464x1728.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Se voc&#234; trabalha como engenheiro de software h&#225; algum tempo, provavelmente j&#225; se deparou com uma tabela no banco de dados que possui uma coluna chamada <code>status</code>. No in&#237;cio, eram apenas dois: <code>PENDENTE</code> e <code>APROVADO</code>. Meses depois, a coluna aceitava <code>CANCELADO</code>, <code>BLOQUEADO</code>, <code>EM_ANALISE</code>, <code>ESTORNADO</code> e uma dezena de outros valores que brotaram conforme o neg&#243;cio crescia.</p><p>O problema real n&#227;o s&#227;o os nomes em si, mas o que acontece entre os estados.</p><p>Voc&#234; j&#225; viu um contrato <code>CANCELADO</code> receber um evento de <code>PAGAMENTO_CONFIRMADO</code> e, do nada, voltar a ser <code>ATIVO</code>? Ou um cliente inadimplente conseguir realizar um saque porque o motor de decis&#227;o emitiu o bloqueio, mas o sistema de destino n&#227;o sabia se &#8220;podia&#8221; acatar aquela ordem naquele momento?</p><p>Em sistemas financeiros, a consist&#234;ncia n&#227;o &#233; apenas uma &#8220;boa pr&#225;tica&#8221; &#8212; &#233; uma quest&#227;o de sobreviv&#234;ncia e conformidade jur&#237;dica. Quando falamos de <strong>Arquiteturas Orientadas a Eventos (EDA)</strong>, onde tudo acontece de forma ass&#237;ncrona e distribu&#237;da, confiar que uma simples atualiza&#231;&#227;o de coluna no banco manter&#225; a integridade do seu dom&#237;nio &#233; um erro perigoso.</p><p>Neste artigo, vamos mergulhar na origem da necessidade das <strong><a href="https://www.spiceworks.com/soft-tech/what-is-fsm/">M&#225;quinas de Estado Finitas (FSM)</a></strong>. Vamos sair do b&#225;sico &#8220;banco de dados como espelho de status&#8221; e entender como transformar o ciclo de vida de um contrato de cr&#233;dito &#8212; com todas as suas complexidades de bloqueios, liquida&#231;&#245;es e raz&#245;es judiciais &#8212; em um fluxo determin&#237;stico, audit&#225;vel e, acima de tudo, pragm&#225;tico.</p><p>Se voc&#234; quer parar de debugar &#8220;estados imposs&#237;veis&#8221; e come&#231;ar a construir sistemas que se defendem sozinhos, este texto &#233; para voc&#234;.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>Teoria dos Aut&#244;matos</h3><p>Antes de falarmos sobre Kafka, Microsservi&#231;os ou Banco de Dados, precisamos dar um passo atr&#225;s. O que vamos discutir aqui n&#227;o &#233; uma &#8220;tend&#234;ncia&#8221; nova da engenharia de software; &#233; um fundamento que existe muito antes da primeira linha de c&#243;digo web ser escrita.</p><p>A solu&#231;&#227;o para a complexidade de estados reside na <strong><a href="https://cs.stanford.edu/people/eroberts/courses/soco/projects/2004-05/automata-theory/basics.html">Teoria dos Aut&#244;matos</a></strong>, um ramo da ci&#234;ncia da computa&#231;&#227;o e matem&#225;tica.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><p>O conceito de <strong><a href="https://medium.com/@jeffcoh23/an-oh-so-gentle-introduction-to-finite-state-machines-df02117034e3">M&#225;quina de Estados Finitos (FSM)</a></strong> foi formalizado nas d&#233;cadas de 1940 e 1950. Pioneiros como <strong>George H. Mealy</strong> e <strong>Edward F. Moore</strong> desenvolveram modelos matem&#225;ticos para descrever o comportamento de circuitos l&#243;gicos sequenciais. Eles precisavam de uma forma de garantir que, dada uma entrada X, o sistema reagisse de forma previs&#237;vel Y.</p><h3>O Conceito Matem&#225;tico (Simplificado)</h3><p>Formalmente, uma M&#225;quina de Estados Finita Determin&#237;stica &#233; definida por uma 5-tupla (<em><strong>Q, &#931;, &#916;, Q<sub>0</sub>, F</strong></em>). N&#227;o se assuste com os s&#237;mbolos, eles s&#227;o exatamente o que voc&#234; programa no dia a dia:</p><ol><li><p>Q: Um conjunto finito de <strong>Estados</strong> (ex: <code>SIGNED</code>, <code>BLOCKED</code>).</p></li><li><p>&#931;: Um alfabeto finito de entrada, que chamaremos de <strong>Gatilhos/Eventos</strong> (ex: <code>PAGAMENTO_RECEBIDO</code>).</p></li><li><p>&#916;: A <strong>Fun&#231;&#227;o de Transi&#231;&#227;o</strong>. &#201; a regra que diz: &#916;(estado_atual, evento) &#8594; proximo_estado.</p></li><li><p>q_0: O <strong>Estado Inicial</strong> (onde tudo come&#231;a, ex: <code>PRE_ANALYSIS</code>).</p></li><li><p><em>F</em>: O conjunto de <strong>Estados Finais</strong> (onde o processo termina, ex: <code>CLOSED</code>).</p></li></ol><p>Entender isso &#233; vital porque transforma &#8220;regras de neg&#243;cio soltas&#8221; em uma equa&#231;ao matem&#225;tica que n&#227;o permite ambiguidade.</p><h3>A Catraca do Metr&#244;</h3><p>Para tirar essa matem&#225;tica do papel e torn&#225;-la tang&#237;vel, esque&#231;a o c&#243;digo e pense em uma <strong>catraca de metr&#244;</strong> (ou torniquete). Ela &#233; a implementa&#231;&#227;o f&#237;sica perfeita dessa tupla matem&#225;tica.</p><p>A catraca possui apenas dois estados (Q):</p><ol><li><p><strong>Bloqueada (Locked)</strong></p></li><li><p><strong>Desbloqueada (Unlocked)</strong></p></li></ol><p>E aceita duas a&#231;&#245;es ou inputs do mundo externo (&#931;):</p><ol><li><p><strong>Inserir Moeda</strong></p></li><li><p><strong>Empurrar a Barra</strong></p></li></ol><p>A &#8220;magia&#8221; &#8212; e a seguran&#231;a do sistema &#8212; n&#227;o est&#225; nos estados, mas na <strong>Fun&#231;&#227;o de Transi&#231;&#227;o (</strong>&#916;<strong>)</strong>:</p><ul><li><p>Se voc&#234; <strong>Empurrar</strong> enquanto est&#225; <strong>Bloqueada</strong>, a fun&#231;&#227;o retorna o pr&#243;prio estado <strong>Bloqueada</strong>. Nada acontece.</p></li><li><p>Se voc&#234; <strong>Inserir Moeda</strong> enquanto est&#225; <strong>Bloqueada</strong>, a fun&#231;&#227;o muda o estado para <strong>Desbloqueada</strong>.</p></li><li><p>Se voc&#234; <strong>Empurrar</strong> enquanto est&#225; <strong>Desbloqueada</strong>, voc&#234; passa, e a m&#225;quina transita automaticamente de volta para <strong>Bloqueada</strong>.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!IsBR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!IsBR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!IsBR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!IsBR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!IsBR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!IsBR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5341138,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/182564456?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!IsBR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!IsBR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!IsBR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!IsBR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fec09bb1e-1b67-4dc8-adf5-6bea1cec605a_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Se a catraca fosse implementada como a maioria dos nossos sistemas legados (o modelo de &#8220;banco de dados espelho&#8221;), ela n&#227;o teria regras. Seria poss&#237;vel for&#231;ar a a&#231;&#227;o &#8220;Passar&#8221; sem inserir a moeda, bastando um desenvolvedor rodar um update manual: <code>UPDATE catraca SET travada = false</code>.</p><p>Na vida real, a barreira f&#237;sica da catraca impede essa fraude. No software, a <strong>FSM &#233; essa barreira f&#237;sica</strong>.</p><p>Adotar FSMs em sistemas de cr&#233;dito marca uma mudan&#231;a profunda na postura do engenheiro:</p><ul><li><p><strong>Mentalidade CRUD:</strong> <em>&#8220;Quais dados eu preciso salvar agora?&#8221;</em> (Foco na persist&#234;ncia passiva).</p></li><li><p><strong>Mentalidade FSM:</strong> <em>&#8220;Dado que o contrato est&#225; no estado Q, o evento &#931; pertence ao alfabeto aceito? Se sim, qual &#233; a transi&#231;&#227;o </em>&#916; <em>obrigat&#243;ria?&#8221;</em> (Foco no comportamento e integridade).</p></li></ul><p>Ao adotar essa postura, sa&#237;mos do modo <strong>reativo</strong> (corrigir dados corrompidos no banco porque algu&#233;m fez o que n&#227;o devia) para o modo <strong>preventivo</strong> (a arquitetura torna matematicamente imposs&#237;vel o erro acontecer).</p><h2>Por que precisamos de FSM em Sistemas de Cr&#233;dito?</h2><p>Se voc&#234; est&#225; construindo um aplicativo de lista de tarefas e um bug permite que o usu&#225;rio marque uma tarefa como &#8220;conclu&#237;da&#8221; duas vezes, o pior que acontece &#233; uma falha visual. &#201; chato, mas inofensivo.</p><p>No mundo financeiro, entretanto, a toler&#226;ncia ao erro &#233; zero. <strong>Em sistemas de cr&#233;dito, um erro de transi&#231;&#227;o de estado &#233; sin&#244;nimo de dinheiro perdido ou processo judicial.</strong></p><p>Antes de escrevermos a primeira linha de c&#243;digo, precisamos entender o risco. O banco de dados n&#227;o pode ser apenas um dep&#243;sito de informa&#231;&#245;es; a arquitetura precisa atuar como um <strong>Escudo de Integridade</strong>. &#201; aqui que a M&#225;quina de Estados (FSM) deixa de ser um luxo e vira uma obriga&#231;&#227;o.</p><h3>O Caos das Colunas de Status e o Banco de Dados como &#8220;Espelho&#8221;</h3><p>No in&#237;cio de qualquer projeto, a simplicidade &#233; sedutora. Voc&#234; modela a entidade <code>CreditContract</code>, cria uma tabela e adiciona uma coluna inofensiva: <code>status</code> (seja uma <code>string</code> ou um <code>enum</code>).</p><p>A regra de neg&#243;cio parece clara: &#8220;Quando o cliente assinar, mude o status para <code>SIGNED</code>&#8220;. No c&#243;digo, isso se traduz em um simples update:</p><pre><code><code>UPDATE contracts SET status = 'SIGNED' WHERE id = 123;</code></code></pre><p>O problema &#233; que, nessa abordagem, o seu banco de dados atua como um <strong>espelho passivo</strong>. Ele reflete cegamente qualquer coisa que a aplica&#231;&#227;o ordene. Ele n&#227;o questiona. Se um bug, uma <em>race condition</em> ou um estagi&#225;rio rodando um script manual tentar mudar um contrato de <code>CANCELLED</code> (Cancelado) para <code>SIGNED</code> (Assinado), o banco aceitar&#225; silenciosamente.</p><p>Parab&#233;ns, voc&#234; acabou de criar um <strong>estado imposs&#237;vel</strong>. Um contrato que n&#227;o existe mais acabou de &#8220;renascer&#8221;, gerando inconsist&#234;ncia cont&#225;bil e potencial fraude. A dor aqui n&#227;o &#233; apenas t&#233;cnica; &#233; a perda de confian&#231;a na integridade dos dados.</p><h3>O Custo do &#8220;Estado Imposs&#237;vel&#8221;</h3><p>Imagine um contrato de cr&#233;dito que foi quitado pelo cliente. O status atual &#233; <code>CLOSED</code> (Encerrado). O cliente est&#225; feliz e com o &#8220;nome limpo&#8221;.</p><p>Por&#233;m, em uma arquitetura de microsservi&#231;os distribu&#237;da, uma mensagem antiga de atraso &#8212; que ficou presa em uma fila de retentativa (DLQ) por 5 dias &#8212; finalmente &#233; processada. Esse evento diz: <em>&#8220;Bloqueie este contrato por inadimpl&#234;ncia&#8221;</em>.</p><p>Sem uma FSM:</p><ol><li><p>O sistema recebe o evento.</p></li><li><p>Atualiza o status de <code>CLOSED</code> para <code>BLOCKED</code>.</p></li><li><p>Dispara gatilhos que negativam o nome do cliente nos &#243;rg&#227;os de prote&#231;&#227;o ao cr&#233;dito.</p></li><li><p><strong>Resultado:</strong> Um processo judicial por danos morais contra a sua empresa, causado por um cliente que j&#225; havia pago tudo.</p></li></ol><p>Com uma FSM:</p><ol><li><p>O sistema recebe o evento de bloqueio.</p></li><li><p>A FSM consulta a configura&#231;&#227;o: <em>&#8220;Existe uma transi&#231;&#227;o v&#225;lida de </em><code>CLOSED</code><em> para </em><code>BLOCKED</code><em>?&#8221;</em></p></li><li><p>A resposta &#233; <strong>N&#195;O</strong>. Um contrato encerrado &#233; imut&#225;vel em rela&#231;&#227;o a bloqueios.</p></li><li><p>A FSM rejeita a transi&#231;&#227;o, loga o erro e descarta a mensagem silenciosamente ou alerta o time de sustenta&#231;&#227;o. O cliente permanece protegido.</p></li></ol><p>&#201; bom lembrar que a fraude em sistemas de cr&#233;dito nem sempre vem de ataques externos; muitas vezes ela explora brechas na l&#243;gica de fluxo.</p><p>Em um fluxo de origina&#231;&#227;o de cr&#233;dito, a ordem das etapas &#233; sagrada: <code>PRE_ANALYSIS</code> (Score/KYC) &#8594; <code>PENDING_SIGNED</code> (Aguardando Assinatura) &#8594; <code>SIGNED</code> (Efetivado).</p><p>Um fraudador (ou um bug cr&#237;tico) poderia tentar chamar diretamente a API de efetiva&#231;&#227;o (<code>/confirm-contract</code>), tentando pular a etapa de an&#225;lise de risco (<code>PRE_ANALYSIS</code>), for&#231;ando o contrato a nascer j&#225; no estado <code>SIGNED</code>.</p><p>Se o seu sistema apenas &#8220;salva o status&#8221; que chega na requisi&#231;&#227;o, voc&#234; acabou de conceder cr&#233;dito sem an&#225;lise. A FSM atua como um porteiro implac&#225;vel: ela garante que o estado <code>SIGNED</code> <strong>s&#243; pode ser alcan&#231;ado</strong> se o estado anterior for estritamente <code>PENDING_SIGNED</code>. Tentar pular etapas resulta em uma exce&#231;&#227;o imediata.</p><h3>&#8220;Em produ&#231;&#227;o, surpresa &#233; o inimigo&#8221;</h3><p>Sistemas financeiros precisam ser determin&#237;sticos. A FSM for&#231;a o time de engenharia e produto a mapear <strong>exatamente</strong> o que pode acontecer.</p><ul><li><p>Pode cancelar um contrato que j&#225; est&#225; bloqueado judicialmente?</p></li><li><p>Pode renegociar uma d&#237;vida que ainda n&#227;o foi assinada?</p></li></ul><p>Ao definir essas regras na FSM, voc&#234; remove a ambiguidade. Voc&#234; tira a l&#243;gica de neg&#243;cio de <code>ifs</code> espalhados por 10 classes diferentes e a centraliza em uma defini&#231;&#227;o formal. Isso evita que, em produ&#231;&#227;o, um cen&#225;rio n&#227;o testado coloque o sistema em um estado inconsistente que exija interven&#231;&#227;o manual no banco de dados (o famoso &#8220;update na base&#8221; que todo DBA odeia).</p><h2>Anatomia de uma M&#225;quina de Estados (E o que ela N&#195;O &#233;)</h2><p>Agora que entendemos a motiva&#231;&#227;o, vamos descer para o n&#237;vel de engenharia. O que comp&#245;e, tecnicamente, uma M&#225;quina de Estados? N&#227;o estamos falando de bibliotecas espec&#237;ficas, mas dos quatro elementos universais que formam a espinha dorsal desse padr&#227;o.</p><h3>Os 4 Elementos Fundamentais</h3><ol><li><p><strong>Estado (State):</strong> &#201; uma fotografia do momento. Representa a situa&#231;&#227;o qualitativa do Contrato de Cr&#233;dito naquele instante exato.</p><ul><li><p><em>Exemplo:</em> <code>SIGNED</code> (Assinado/Vigente). Enquanto o contrato est&#225; neste estado, o sistema entende que ele &#233; saud&#225;vel. O estado persiste at&#233; que um fato novo ocorra.</p></li></ul></li><li><p><strong>Gatilho (Trigger):</strong> &#201; o est&#237;mulo externo ou a inten&#231;&#227;o de mudan&#231;a. &#201; o &#8220;fato&#8221; que ocorreu no mundo real (ou no sistema) e que bate &#224; porta da m&#225;quina.</p><ul><li><p><em>Exemplo:</em> <code>INADIMPLENCIA_DETECTADA</code>. Note que o gatilho n&#227;o &#233; a mudan&#231;a em si, mas a notifica&#231;&#227;o de que algo aconteceu.</p></li></ul></li><li><p><strong>Transi&#231;&#227;o (Transition):</strong> &#201; a regra at&#244;mica de movimento. &#201; a ponte que conecta dois estados, governada por uma l&#243;gica determin&#237;stica: <em>Se estou no Estado A e recebo o Gatilho X, vou para o Estado B.</em></p><ul><li><p><em>Exemplo:</em> De <code>SIGNED</code> &#8594; para &#8594; <code>BLOCKED</code>.</p></li></ul></li><li><p><strong>A&#231;&#227;o (Action):</strong> &#201; o efeito colateral. &#201; a tarefa que o sistema deve agendar ou registrar durante a mudan&#231;a.</p><ul><li><p><em>Exemplo:</em> Ao entrar em <code>BLOCKED</code>, a a&#231;&#227;o &#233; gerar e persistir o evento de dom&#237;nio <code>ContractBlocked</code> (que notificar&#225; outros sistemas).</p></li></ul></li></ol><h3>O Motor de Decis&#227;o vs. A M&#225;quina de Estados</h3><p>Aqui reside a maior confus&#227;o arquitetural em sistemas de cr&#233;dito. &#201; fundamental distinguir quem <strong>pensa</strong> de quem <strong>garante</strong>.</p><ul><li><p><strong>Motor de Decis&#227;o (Rule Engine):</strong> &#201; o <strong>Estrategista</strong>. Ele analisa vari&#225;veis complexas (saldo, dias de atraso, score de cr&#233;dito, taxa Selic) e emite uma <strong>Inten&#231;&#227;o</strong> (Comando): <em>&#8220;Este cliente deve ser bloqueado&#8221;</em>.</p></li><li><p><strong>M&#225;quina de Estados (FSM):</strong> &#201; o <strong>Guardi&#227;o do Fluxo</strong>. Ela n&#227;o recalcula o risco do cliente. Ela recebe a inten&#231;&#227;o e valida a integridade: <em>&#8220;Dado que o contrato est&#225; no estado atual, &#233; tecnicamente poss&#237;vel e permitido transitar para BLOQUEADO?&#8221;</em>.</p></li></ul><h4>Por que precisamos dessa separa&#231;&#227;o?</h4><p>Se o Motor de Decis&#227;o j&#225; &#8220;decidiu&#8221;, por que a FSM precisa validar novamente? Porque a FSM enxerga riscos de consist&#234;ncia que o Motor ignora:</p><ol><li><p><strong>Integridade do Ciclo de Vida (Imutabilidade):</strong> O Motor pode estar analisando dados de segundos atr&#225;s. Se, nesse meio tempo, o cliente cancelou a conta, o contrato est&#225; <code>CANCELLED</code>. A FSM impede que um contrato cancelado vire bloqueado. Ela garante que n&#227;o se altere o status de um processo j&#225; encerrado.</p></li><li><p><strong>Concorr&#234;ncia (Race Conditions):</strong> Em alta escala, comandos chegam simultaneamente. Se uma ordem judicial de bloqueio chegar milissegundos antes de um bloqueio por atraso, a FSM arbitra a colis&#227;o, garantindo que o estado final respeite a prioridade correta e n&#227;o seja sobrescrito indevidamente.</p></li><li><p><strong>Preven&#231;&#227;o de Atalhos (Bypass):</strong> A FSM assegura o rito processual. Se um erro de integra&#231;&#227;o tentar pular de <code>PRE_ANALYSIS</code> direto para <code>SIGNED</code>, a FSM barra o movimento porque a etapa obrigat&#243;ria de assinatura foi ignorada.</p></li></ol><blockquote><p><strong>A Diferen&#231;a Fundamental:</strong> O Motor de Decis&#227;o determina que o contrato <strong>deve</strong> ser bloqueado (baseado em pol&#237;tica). A M&#225;quina de Estados determina se o contrato <strong>pode</strong> ser bloqueado (baseado em estrutura e ciclo de vida).</p></blockquote><h4>Na Pr&#225;tica: Responsabilidades Limpas</h4><p>Para manter a arquitetura s&#227;, evite colocar l&#243;gica de neg&#243;cio dentro da FSM:</p><ul><li><p><strong>Errado (na FSM):</strong> <code>if (diasAtraso &gt; 10 &amp;&amp; divida &gt; 1000) ...</code></p></li><li><p><strong>Certo (na FSM):</strong> Receber o gatilho <code>INADIMPLENCIA_CRITICA</code> (j&#225; processado pelo motor) e apenas validar a transi&#231;&#227;o.</p></li></ul><p>O Motor &#233; <strong>Estrat&#233;gico</strong> (muda com as regras de neg&#243;cio). A M&#225;quina &#233; <strong>Operacional</strong> (garante a consist&#234;ncia do banco de dados).</p><p>Agora que estabelecemos as fronteiras, vamos dissecar cada um desses elementos, come&#231;ando pelo mais incompreendido: o <strong>Estado</strong>.</p><h2>Dissecando os Elementos: STATE (Estado)</h2><p>Quando a equipe de produto chega com um requisito dizendo: <em>&#8220;O contrato precisa ter um status de &#8216;Em An&#225;lise de Risco&#8217;&#8221;</em>, o instinto do desenvolvedor &#233; abrir o arquivo de <code>Enums</code> e adicionar <code>IN_RISK_ANALYSIS</code>. Pare agora mesmo.</p><p>Antes de definir um <strong>Estado</strong>, o engenheiro precisa fazer uma pergunta fundamental ao neg&#243;cio:</p><blockquote><p><em>&#8220;O contrato se comporta de maneira diferente neste momento?&#8221;</em></p></blockquote><p>Se a resposta for &#8220;N&#227;o, &#233; s&#243; para a gente saber que est&#225; l&#225;&#8221;, ent&#227;o isso provavelmente n&#227;o &#233; um estado de uma FSM, &#233; apenas uma tag, um log ou um atributo informativo.</p><p><em><strong>Um Estado representa um modo de comportamento.</strong></em></p><ul><li><p>No estado <code>SIGNED</code> (Assinado), o contrato aceita pagamentos.</p></li><li><p>No estado <code>BLOCKED</code> (Bloqueado), o contrato <strong>rejeita</strong> pagamentos.</p></li><li><p>Se o comportamento mudou, o estado mudou.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!5EQ5!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!5EQ5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!5EQ5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!5EQ5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!5EQ5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!5EQ5!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png" width="904" height="483.04395604395603" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:778,&quot;width&quot;:1456,&quot;resizeWidth&quot;:904,&quot;bytes&quot;:5527485,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/182564456?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!5EQ5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!5EQ5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!5EQ5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!5EQ5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F151a3c6e-203a-4297-a5ba-a277df8914cc_2816x1504.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Onde a M&#225;quina de Estados &#8220;Mora&#8221;?</h3><p>Muitos arquitetos imaginam que, para ter uma M&#225;quina de Estados, precisam subir um cluster novo, criar uma &#8220;State API&#8221; ou configurar algo complexo na AWS. Vamos desmistificar isso.</p><p>A FSM n&#227;o &#233; necessariamente um microsservi&#231;o isolado. Ela &#233;, na maioria das vezes, uma <strong>propriedade comportamental da sua Entidade de Dom&#237;nio</strong>.</p><p>Existem duas formas principais de implementar, e o Tech Lead precisa saber escolher a ferramenta certa para o problema certo:</p><h4>Cen&#225;rio A: O Estado &#8220;No C&#243;digo&#8221; (Bibliotecas / In-Memory)</h4><p>Este &#233; o padr&#227;o ouro para <strong>Entidades de Dom&#237;nio</strong> (como o nosso Contrato de Cr&#233;dito). A FSM &#233; uma biblioteca que roda dentro do seu microsservi&#231;o.</p><ul><li><p><strong>Onde fica o dado?</strong> Em uma coluna simples <code>status</code> (string/enum) na tabela do seu banco de dados (Postgres, DynamoDB, Mongo).</p></li><li><p><strong>Quem gerencia?</strong> O c&#243;digo da sua API, utilizando bibliotecas leves como <strong>Stateless (.NET)</strong>, <strong>XState (Node.js)</strong> ou <strong>Spring State Machine (Java)</strong>.</p></li><li><p><strong>Como funciona?</strong> A FSM &#233; &#8220;Stateless&#8221; (sem mem&#243;ria). Quando a API recebe um request:</p><ol><li><p>Busca o contrato no banco (ex: <code>status: 'SIGNED'</code>).</p></li><li><p>Instancia a FSM na mem&#243;ria configurada com esse estado inicial.</p></li><li><p>Tenta disparar o gatilho.</p></li><li><p>Se a FSM permitir, atualiza o objeto e salva no banco.</p></li><li><p>A FSM morre da mem&#243;ria ao fim do request.</p></li></ol></li><li><p><strong>Para quem &#233;?</strong> Para gerenciar o ciclo de vida de entidades que precisam de resposta r&#225;pida (milissegundos) e alta integridade transacional.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0XDI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0XDI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!0XDI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!0XDI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!0XDI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0XDI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6155556,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/182564456?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0XDI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!0XDI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!0XDI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!0XDI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1ee0c9c0-e329-41f4-82e9-f30fb340c0ac_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Cen&#225;rio B: O Estado &#8220;Na Infraestrutura&#8221; (Workflow Engines)</h4><p>Aqui, o Estado &#233; gerenciado por um servi&#231;o de nuvem, como <strong>AWS Step Functions</strong> ou <strong>Temporal.io</strong>.</p><ul><li><p><strong>Onde fica o dado?</strong> O pr&#243;prio servi&#231;o de nuvem mant&#233;m o &#8220;ponteiro&#8221; de em qual etapa o processo est&#225;.</p></li><li><p><strong>Quem gerencia?</strong> A plataforma de Cloud. Voc&#234; desenha o fluxo visualmente.</p></li><li><p><strong>Para quem &#233;?</strong> Para <strong>Processos de Longa Dura&#231;&#227;o (Sagas)</strong>.</p><ul><li><p><em>Exemplo:</em> Um fluxo de Cobran&#231;a que envia um e-mail, espera 3 dias, verifica o pagamento e, se n&#227;o pagar, envia SMS. Isso n&#227;o &#233; o estado do contrato, &#233; o estado de um <em>processo</em> que atua sobre o contrato.</p></li></ul></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!FjUh!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!FjUh!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!FjUh!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!FjUh!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!FjUh!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!FjUh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5912801,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/182564456?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!FjUh!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!FjUh!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!FjUh!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!FjUh!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa3e418bf-a532-4efe-ba4e-98856ebb1282_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>O &#8220;State&#8221; &#233; uma Lambda?</h3><p><strong>N&#227;o.</strong> Confundir <em>computa&#231;&#227;o</em> com <em>estado</em> &#233; um erro prim&#225;rio.</p><ul><li><p><strong>O Estado &#233; passivo:</strong> &#8220;O contrato est&#225; <code>AGUARDANDO_ANALISE</code>&#8220;.</p></li><li><p><strong>A Lambda &#233; o trabalhador (Worker):</strong> Ela executa a an&#225;lise.</p></li></ul><p>A FSM &#233; o maestro que diz: <em>&#8220;J&#225; que estou no estado &#8216;AGUARDANDO_ANALISE&#8217; e chegou o evento &#8216;INICIAR&#8217;, vou invocar a Lambda X para trabalhar&#8221;</em>.</p><h3>Responsabilidades: Evitando a Explos&#227;o de Estados</h3><p>Para fechar o conceito, precisamos definir o que o Estado <strong>N&#195;O</strong> deve fazer.</p><ol><li><p><strong>O Estado n&#227;o guarda contexto:</strong> O estado &#233; <code>BLOCKED</code>. O motivo do bloqueio, o valor da d&#237;vida ou a data s&#227;o <strong>atributos</strong> do contrato, n&#227;o do estado.</p><ul><li><p><em>Errado:</em> Criar estados como <code>BLOCKED_POR_FALTA_PAGAMENTO</code>, <code>BLOCKED_JUDICIAL</code>, <code>BLOCKED_FRAUDE</code>. Isso gera uma explos&#227;o combinat&#243;ria imposs&#237;vel de manter.</p></li><li><p><em>Certo:</em> Tenha um estado <code>BLOCKED</code> e uma coluna auxiliar <code>reason_code</code> ou <code>block_type</code>.</p></li></ul></li><li><p><strong>O Estado n&#227;o realiza l&#243;gica complexa:</strong> Ele n&#227;o sabe &#8220;como&#8221; calcular juros compostos. Ele apenas sabe que, enquanto estiver no estado <code>BILLING</code>, o c&#225;lculo de juros &#233; permitido.</p></li></ol><p>Encare a FSM do Cen&#225;rio A como uma &#8220;camada de prote&#231;&#227;o l&#243;gica&#8221; ao redor da sua classe de Dom&#237;nio. Ela n&#227;o &#233; um servidor, &#233; um guarda-costas que vive no seu c&#243;digo e protege seu banco de dados de escritas inv&#225;lidas.</p><h2>O Gatilho (Trigger)</h2><p>Se o <strong>Estado</strong> &#233; uma fotografia est&#225;tica do seu contrato em um momento no tempo, o <strong>Gatilho (ou Trigger)</strong> &#233; o movimento. Ele &#233; a energia cin&#233;tica. Sem gatilhos, sua m&#225;quina de estados &#233; apenas um diagrama bonito desenhado no Gliffy ou no Miro, mas completamente in&#250;til na pr&#225;tica.</p><p>Para muitos desenvolvedores, o gatilho parece ser a parte mais simples: <em>&#8220;Ah, &#233; s&#243; o nome do m&#233;todo que eu chamo&#8221;</em>. Mas &#233; aqui que a arquitetura come&#231;a a se provar robusta ou fr&#225;gil. Vamos dissecar o que comp&#245;e um gatilho de verdade.</p><h3>O Que &#233;?</h3><p>Formalmente, o Gatilho &#233; um <strong>sinal</strong>. &#201; uma solicita&#231;&#227;o externa que diz &#224; m&#225;quina: <em>&#8220;Ei, algo aconteceu (ou queremos que aconte&#231;a). Verifique se voc&#234; pode mudar de estado por causa disso.&#8221;</em><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><p>No nosso sistema de cr&#233;dito, o contrato n&#227;o acorda de manh&#227; e decide virar <code>BLOCKED</code> sozinho. Algo precisa empurr&#225;-lo. Esse empurr&#227;o &#233; o gatilho.</p><h3>Comando (Inten&#231;&#227;o) vs. Evento (Fato)</h3><p>Em Arquiteturas Orientadas a Eventos (EDA), a sem&#226;ntica do gatilho muda o jogo. Precisamos separar duas categorias:</p><p><strong>1. O Gatilho como Comando (Inten&#231;&#227;o):</strong> &#201; imperativo. Ocorre quando um usu&#225;rio ou sistema <strong>quer</strong> que algo mude.</p><ul><li><p><em>Exemplo:</em> O usu&#225;rio clica no bot&#227;o &#8220;Renegociar D&#237;vida&#8221; no app.</p></li><li><p><em>Gatilho:</em> <code>SolicitarRenegociacao</code>.</p></li><li><p><em>Natureza:</em> A FSM tem todo o direito de rejeitar. Se o contrato estiver <code>BLOCKED_JUDICIALLY</code> (Bloqueio Judicial), a m&#225;quina diz: <em>&#8220;N&#227;o. Rejeito seu comando&#8221;</em>.</p></li></ul><p><strong>2. O Gatilho como Evento (Fato):</strong> &#201; pret&#233;rito. Ocorre quando algo <strong>j&#225; aconteceu</strong> e a FSM precisa reagir para manter a consist&#234;ncia.</p><ul><li><p><em>Exemplo:</em> O gateway de pagamento enviou um webhook confirmando que o dinheiro caiu na conta.</p></li><li><p><em>Gatilho:</em> <code>PagamentoConfirmado</code>.</p></li><li><p><em>Natureza:</em> A FSM n&#227;o pode &#8220;negar&#8221; que o pagamento ocorreu. O dinheiro j&#225; saiu da conta do cliente. A FSM deve aceitar o gatilho e transitar para <code>PAID</code> ou, se estiver num estado onde isso &#233; imposs&#237;vel (como <code>CANCELLED</code>), deve iniciar um processo de reembolso ou logar uma inconsist&#234;ncia grave.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!h7kn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!h7kn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!h7kn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!h7kn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!h7kn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!h7kn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png" width="1456" height="778" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:778,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5743462,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/182564456?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!h7kn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png 424w, https://substackcdn.com/image/fetch/$s_!h7kn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png 848w, https://substackcdn.com/image/fetch/$s_!h7kn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png 1272w, https://substackcdn.com/image/fetch/$s_!h7kn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8c811a99-4cab-4504-a4fb-6d1da14ba41c_2816x1504.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>O Gatilho carrega &#8220;Bagagem&#8221; (Payload)</h3><p>Um erro cl&#225;ssico de iniciantes &#233; tratar o Gatilho como uma simples <em>flag</em> booleana (ex: &#8220;pagou&#8221; ou &#8220;n&#227;o pagou&#8221;). Para a FSM do <strong>Contrato de Cr&#233;dito</strong>, o pagamento de uma fatura &#233; um fato externo que traz dados cruciais.</p><p>Um gatilho robusto deve transportar o <strong>Contexto (Payload)</strong> necess&#225;rio para que a transi&#231;&#227;o do Contrato ocorra de forma isolada, sem que a FSM precise &#8220;adivinhar&#8221; o que aconteceu no m&#243;dulo de faturamento.</p><h3>Reagindo &#224; Fatura</h3><p>Imagine que o seu contrato est&#225; no estado <code>BLOCKED</code> (Bloqueado) porque havia uma pend&#234;ncia. O cliente paga uma fatura. Para a FSM decidir se o <strong>Contrato</strong> volta a ser <code>SIGNED</code> (Ativo/Saud&#225;vel), ela precisa dos dados do ciclo:</p><ul><li><p><strong>Gatilho:</strong> <code>PAGAMENTO_FATURA_RECEBIDO</code></p></li><li><p><strong>Payload:</strong></p></li></ul><pre><code><code>{
  "fatura_id": "ciclo_jan_2026",
  "valor_pago": 150.00,
  "valor_total_fatura": 150.00,
  "status_final_fatura": "TOTAL_PAID"
}
</code></code></pre><h3>Por que isso &#233; vital para a Arquitetura?</h3><p>A M&#225;quina de Estados utiliza esse payload para alimentar as <strong>Guards (Guardas)</strong>. Com o contexto na m&#227;o, a l&#243;gica de roteamento do contrato fica protegida:</p><ul><li><p><strong>Regra:</strong> &#8220;Se o contrato est&#225; <code>BLOCKED</code> e o gatilho traz <code>status_final_fatura == TOTAL_PAID</code>, transite o contrato para <code>SIGNED</code>.&#8221;</p></li><li><p><strong>Regra:</strong> &#8220;Se o gatilho traz <code>status_final_fatura == PARTIAL_PAID</code>, mantenha o contrato em <code>BILLING</code> (Em Cobran&#231;a).&#8221;<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p></li></ul><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-credito-implementando?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Esta publica&#231;&#227;o &#233; p&#250;blica, ent&#227;o sinta-se &#224; vontade para compartilh&#225;-la.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-credito-implementando?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-de-credito-implementando?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p></div><h3>O Benef&#237;cio: FSM &#8220;Stateless&#8221; e de Alta Performance</h3><p>O objetivo fundamental da FSM n&#227;o &#233; gerenciar o saldo da fatura &#8212; isso &#233; fun&#231;&#227;o do m&#243;dulo de faturamento. A miss&#227;o da FSM &#233; validar se o <strong>Contrato de Cr&#233;dito</strong> pode mudar de est&#225;gio.</p><ol><li><p><strong>Eliminamos Queries Pesadas:</strong> A FSM n&#227;o precisa abrir conex&#245;es com a API de faturamento para perguntar &#8220;o saldo dessa fatura est&#225; zerado?&#8221;. Ela recebe essa informa&#231;&#227;o como um fato no payload.</p></li><li><p><strong>Reduzimos o Acoplamento:</strong> A FSM do contrato n&#227;o conhece as regras de c&#225;lculo de juros da fatura. Ela apenas reage ao resultado final enviado pelo gatilho.</p></li><li><p><strong>Garantimos Determinismo:</strong> O estado do contrato muda com base em fatos imut&#225;veis entregues no momento do disparo.</p></li></ol><h3>O Tempo como Gatilho</h3><p>Em sistemas de cr&#233;dito, o tempo &#233; o gatilho mais implac&#225;vel. Muitas mudan&#231;as no contrato n&#227;o v&#234;m de uma a&#231;&#227;o do cliente, mas do rel&#243;gio.</p><ul><li><p><strong>Cen&#225;rio:</strong> O contrato est&#225; <code>SIGNED</code>. A fatura venceu ontem e n&#227;o houve pagamento.</p></li><li><p><strong>Quem dispara?</strong> Um componente externo de <strong>Scheduler</strong> (AWS EventBridge, Hangfire ou um Cron Job).</p></li><li><p><strong>A Arquitetura:</strong> O Scheduler identifica a fatura vencida e &#8220;empurra&#8221; o gatilho <code>FATURA_VENCIDA_NAO_PAGA</code> contra a FSM do Contrato.</p></li></ul><p>A FSM recebe o sinal, verifica que o contrato est&#225; em um estado que permite cobran&#231;a e transita o contrato automaticamente para <code>BILLING</code> ou <code>BLOCKED</code>. Para a FSM, o tempo &#233; apenas mais um ator externo enviando sinais.</p><h3>Lidando com Gatilhos &#8220;Fora de Hora&#8221;</h3><p>O que acontece se a FSM receber um sinal que n&#227;o faz sentido para o estado atual do <strong>Contrato</strong>? (Ex: Receber um aviso de pagamento para um contrato que j&#225; foi <code>CANCELLED</code>).</p><p>Existem duas abordagens de design para garantir a resili&#234;ncia:</p><ol><li><p><strong>Abordagem Estrita (Throw Exception):</strong> &#218;til para capturar erros de l&#243;gica em desenvolvimento. Se tentarmos disparar <code>EFETUAR_SAQUE</code> em um contrato <code>BLOCKED</code>, o sistema lan&#231;a um <code>InvalidTransitionException</code>.</p></li><li><p><strong>Abordagem Resiliente (Idempot&#234;ncia/Ignore):</strong> Essencial em sistemas distribu&#237;dos (Kafka/RabbitMQ). Se um evento de pagamento for processado duas vezes (replay), a FSM olha para o contrato, v&#234; que ele j&#225; est&#225; <code>SIGNED</code> e simplesmente ignora o segundo gatilho de forma silenciosa e segura.</p></li></ol><p>O Gatilho &#233; a <em><strong>&#250;nica</strong></em> API da sua M&#225;quina de Estados. Nunca altere o estado do contrato diretamente no banco de dados.</p><p><strong>Errado:</strong> <code>update contrato set status = 'CANCELLED'</code></p><p><strong>Certo:</strong> <code>contratoFsm.Fire(Triggers.SOLICITAR_CANCELAMENTO)</code></p><p>Ao for&#231;ar todas as mudan&#231;as atrav&#233;s de Gatilhos e Payloads, voc&#234; garante que o ciclo de vida do cr&#233;dito seja audit&#225;vel, previs&#237;vel e, acima de tudo, protegido contra estados imposs&#237;veis.</p><h2>A Transi&#231;&#227;o (Transition)</h2><p>A Transi&#231;&#227;o &#233; o componente que define as regras de movimenta&#231;&#227;o entre estados. Em uma M&#225;quina de Estados (FSM), ela &#233; muito mais do que uma seta em um diagrama; &#233; a <strong>camada de aplica&#231;&#227;o das regras de neg&#243;cio</strong>.</p><p>Enquanto o Estado representa a situa&#231;&#227;o qualitativa do Contrato (ex: <code>SIGNED</code>) e o Gatilho representa a inten&#231;&#227;o de mudan&#231;a (ex: <code>PAGAMENTO_RECEBIDO</code>), a Transi&#231;&#227;o &#233; a l&#243;gica que governa se &#8212; e como &#8212; essa mudan&#231;a pode ocorrer. Ela &#233; fundamental porque centraliza o controle de fluxo, garantindo que o Contrato nunca assuma um comportamento indefinido.</p><p>Vamos dissecar os quatro mecanismos que tornam a transi&#231;&#227;o uma ferramenta poderosa de arquitetura:</p><h4>1. Permiss&#227;o Expl&#237;cita (Whitelisting)</h4><p>Diferente de c&#243;digos procedurais onde tudo &#233; permitido at&#233; que se escreva uma valida&#231;&#227;o para proibir, a FSM opera pelo princ&#237;pio da <strong>Permiss&#227;o Expl&#237;cita</strong>. Se uma transi&#231;&#227;o n&#227;o estiver mapeada, ela simplesmente n&#227;o existe.</p><ul><li><p><strong>Cen&#225;rio:</strong> Um contrato est&#225; no estado <code>CANCELLED</code> (Cancelado definitivamente). O sistema recebe o gatilho <code>SOLICITAR_AUMENTO_LIMITE</code>.</p></li><li><p><strong>Comportamento:</strong> A FSM consulta a defini&#231;&#227;o do estado <code>CANCELLED</code>. Como n&#227;o h&#225; nenhuma seta de sa&#237;da configurada para esse gatilho a partir deste estado, a solicita&#231;&#227;o &#233; rejeitada automaticamente.</p></li></ul><p>A pr&#243;pria arquitetura garante a integridade do contrato, impedindo opera&#231;&#245;es inv&#225;lidas sem a necessidade de espalhar <code>if (status != CANCELLED)</code> por todo o c&#243;digo.</p><h4>2. Cl&#225;usulas de Guarda (Guards): Roteamento Condicional</h4><p>Uma transi&#231;&#227;o nem sempre &#233; linear. Frequentemente, o destino do Contrato depende dos dados contidos no <em>payload</em> do gatilho (como o status da fatura). Para gerenciar isso, utilizamos <strong>Guards</strong> &#8212; express&#245;es booleanas avaliadas antes da transi&#231;&#227;o ocorrer.</p><p>Imagine um contrato que est&#225; no estado <code>BLOCKED</code> (Bloqueado por inadimpl&#234;ncia). O sistema recebe o gatilho <code>PROCESSAMENTO_FATURA_CONCLUIDO</code>. A FSM precisa decidir se desbloqueia o contrato ou se ele permanece bloqueado.</p><p>A transi&#231;&#227;o analisa o <em>payload</em> da fatura:</p><ul><li><p><strong>Guard A:</strong> <code>Se payload.status_fatura == "TOTALLY_PAID"</code> &#8594; O Contrato transita para <code>SIGNED</code> (Desbloqueia e volta a ficar saud&#225;vel).</p></li><li><p><strong>Guard B:</strong> <code>Se payload.status_fatura == "PARTIALLY_PAID"</code> &#8594; O Contrato transita (ou permanece via auto-transi&#231;&#227;o) em <code>BLOCKED</code> (Pois a pend&#234;ncia n&#227;o foi totalmente sanada).</p></li></ul><p>Note que <code>PARTIALLY_PAID</code> &#233; o status da <strong>fatura</strong> (input), mas o estado do <strong>contrato</strong> (output) permanece <code>BLOCKED</code>. A Guard usa o dado da fatura para decidir o destino do contrato.</p><h4>3. Irreversibilidade e Auditoria (Forward Recovery)</h4><p>Em sistemas orientados a eventos, as transi&#231;&#245;es geram efeitos colaterais. Quando um contrato transita para <code>BLOCKED</code>, eventos s&#227;o disparados para suspender o cart&#227;o na processadora e notificar o cliente. Por isso, n&#227;o podemos simplesmente &#8220;desfazer&#8221; uma mudan&#231;a voltando o estado no banco de dados.</p><p>Se um contrato foi bloqueado indevidamente, reverter o estado manualmente corrompe a realidade, pois o cart&#227;o j&#225; foi bloqueado na ponta. A abordagem correta &#233; o <strong>Forward Recovery</strong> (Recupera&#231;&#227;o para Frente): cria-se uma transi&#231;&#227;o corretiva (ex: <code>DESBLOQUEIO_ADMINISTRATIVO</code>) que move o contrato de <code>BLOCKED</code> para <code>SIGNED</code>. Isso preserva a trilha de auditoria: o hist&#243;rico mostrar&#225; que o contrato foi bloqueado e, segundos depois, desbloqueado administrativamente.</p><h4>4. Auto-Transi&#231;&#227;o (Self-Transition): Reatividade sem Mudan&#231;a</h4><p>Existem cen&#225;rios onde o Contrato deve reagir a um evento, mas seu estado qualitativo n&#227;o muda.</p><ul><li><p><strong>Cen&#225;rio:</strong> O contrato est&#225; em <code>BLOCKED</code>. Ocorre uma nova tentativa de pagamento que falha. O gatilho <code>FALHA_PAGAMENTO</code> &#233; disparado.</p></li><li><p><strong>A&#231;&#227;o:</strong> O contrato permanece em <code>BLOCKED</code> (a transi&#231;&#227;o aponta para o pr&#243;prio estado).</p></li></ul><p>Apesar de o estado n&#227;o mudar, a transi&#231;&#227;o dispara uma <strong>A&#231;&#227;o (Action)</strong>: &#8220;Registrar a falha no hist&#243;rico de cobran&#231;a&#8221; ou &#8220;Enviar Push Notification de falha&#8221;. A Auto-Transi&#231;&#227;o permite processar regras de neg&#243;cio mantendo a estabilidade do estado atual do contrato.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!4ziK!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!4ziK!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png 424w, https://substackcdn.com/image/fetch/$s_!4ziK!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png 848w, https://substackcdn.com/image/fetch/$s_!4ziK!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png 1272w, https://substackcdn.com/image/fetch/$s_!4ziK!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!4ziK!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png" width="830" height="674.945054945055" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:1184,&quot;width&quot;:1456,&quot;resizeWidth&quot;:830,&quot;bytes&quot;:379873,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/182564456?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!4ziK!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png 424w, https://substackcdn.com/image/fetch/$s_!4ziK!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png 848w, https://substackcdn.com/image/fetch/$s_!4ziK!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png 1272w, https://substackcdn.com/image/fetch/$s_!4ziK!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F7ab9f3d3-1ab7-4b9e-a1d3-f0f2b931c1df_1603x1303.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Pausa para Esclarecer: Transi&#231;&#227;o &#8800; Motor de Decis&#227;o &#8800; A&#231;&#227;o</h3><p>Se voc&#234; leu o t&#243;pico anterior e pensou:<br>&#8220;Espere, isso parece muito com um Motor de Regras (Rule Engine)&#8221; ou<br>&#8220;Parece que a pr&#243;pria transi&#231;&#227;o faz o trabalho&#8221; &#8212; voc&#234; n&#227;o est&#225; sozinho.</p><p>Essa &#233; exatamente a fronteira onde uma boa arquitetura come&#231;a a ficar confusa.</p><p>Para n&#227;o transformarmos nossa M&#225;quina de Estados em um <strong>&#8220;Deus Ex Machina&#8221; que decide, executa e orquestra tudo</strong>, precisamos tra&#231;ar duas linhas vermelhas muito claras no ch&#227;o.</p><h3>1. Transi&#231;&#227;o n&#227;o &#233; Motor de Decis&#227;o (Decision Engine)</h3><p>Existe uma diferen&#231;a <strong>vital</strong> de responsabilidade, complexidade e momento de execu&#231;&#227;o.</p><p><strong>Motor de Decis&#227;o (o &#8220;C&#233;rebro&#8221;)</strong><br>&#201; quem faz o trabalho pesado, consulta bureaus de cr&#233;dito (Serasa/SPC), calcula scores de risco, aplica modelos estat&#237;sticos e decide se um cliente <strong>merece</strong> ou n&#227;o cr&#233;dito. Ele &#233; focado no <strong>&#8220;POR QU&#202;&#8221;</strong>.</p><p><strong>Transi&#231;&#227;o da FSM (o &#8220;Guarda de Tr&#226;nsito&#8221;)</strong><br>&#201; focada na <strong>ESTRUTURA</strong> do fluxo.<br>Ela n&#227;o calcula score.<br>Ela apenas valida algo como:</p><blockquote><p>&#8220;O score que chegou no payload &#233; maior que 500?<br>Se sim, essa transi&#231;&#227;o &#233; permitida.&#8221;</p></blockquote><p>A Transi&#231;&#227;o <strong>n&#227;o decide o mundo</strong>.<br>Ela apenas valida se o mundo <strong>j&#225; decidido</strong> permite o pr&#243;ximo passo.</p><p><strong>Regra de Ouro:</strong><br>Se a l&#243;gica demora mais do que alguns milissegundos, envolve c&#225;lculos complexos ou precisa chamar APIs externas, ela <strong>n&#227;o pertence &#224; Transi&#231;&#227;o (Guard)</strong>.</p><p>Esse trabalho deve ser feito <strong>antes</strong>, por um servi&#231;o dedicado, e o resultado passado para a FSM apenas para validar o roteamento.</p><h3>2. Transi&#231;&#227;o n&#227;o &#233; A&#231;&#227;o</h3><p>A Transi&#231;&#227;o &#233; apenas a autoriza&#231;&#227;o de movimento.<br>Ela &#233; o carimbo no passaporte.</p><p>Mas carimbar o passaporte <strong>n&#227;o faz o avi&#227;o decolar</strong>.</p><p>A Transi&#231;&#227;o diz apenas:</p><blockquote><p>&#8220;Ok. O contrato saiu de SIGNED e agora est&#225; BLOCKED.&#8221;</p></blockquote><p>Mas isso levanta perguntas importantes:</p><ul><li><p>Quem bloqueia o cart&#227;o de cr&#233;dito?</p></li><li><p>Quem envia o e-mail de &#8220;Sinto muito&#8221;?</p></li><li><p>Quem torna esse fato observ&#225;vel para o resto do sistema?</p></li></ul><p>A Transi&#231;&#227;o &#233; o <strong>Mapa</strong>.<br>A A&#231;&#227;o &#233; o <strong>registro das consequ&#234;ncias da viagem</strong>.</p><p>Se a Transi&#231;&#227;o responde apenas &#8220;posso ir ou n&#227;o?&#8221;, ent&#227;o onde ficam os efeitos que realmente impactam o mundo externo?</p><p>&#201; exatamente aqui que entram as <strong>Actions (A&#231;&#245;es)</strong>.</p><h2>Actions (A&#231;&#245;es): O Dilema do &#8220;Falar vs. Escrever&#8221;</h2><p>Chegamos ao &#250;ltimo elemento fundamental da M&#225;quina de Estados. Se a Transi&#231;&#227;o &#233; a burocracia que autoriza a mudan&#231;a, <strong>a </strong><em><strong>Action</strong></em><strong> &#233; a materializa&#231;&#227;o dur&#225;vel dessa decis&#227;o.</strong></p><p>&#201; o momento em que a FSM deixa de apenas avaliar e passa a registrar as consequ&#234;ncias.</p><p>Quando um contrato transita de <code>SIGNED</code> para <code>BLOCKED</code>, a FSM n&#227;o deve sair &#8220;falando&#8221; com outros servi&#231;os. Ela n&#227;o bloqueia cart&#245;es diretamente, n&#227;o envia e-mails e n&#227;o chama o Kafka. Mas ela precisa tornar esse fato observ&#225;vel.</p><p>A pergunta que separa desenvolvedores juniores de seniores n&#227;o &#233; &#8220;o que fazer&#8221;, mas sim:</p><blockquote><p><em>&#8220;Como tornar essa decis&#227;o observ&#225;vel sem quebrar a consist&#234;ncia dos dados?&#8221;</em></p></blockquote><h4>O Jeito Ing&#234;nuo (O Perigo do Dual Write)</h4><p>O instinto inicial &#233; escrever um c&#243;digo sequencial que parece inofensivo:</p><ol><li><p><strong>Atualiza o banco:</strong> <code>UPDATE contrato SET status = 'BLOCKED'</code></p></li><li><p><strong>Publica no Kafka:</strong> <code>producer.send(ContractBlockedEvent)</code></p></li></ol><p>O problema &#233; simples e cruel: <strong>redes falham.</strong> Se o passo 1 funcionar (o banco commitar), mas o passo 2 falhar (timeout no Kafka, erro de DNS), voc&#234; criou uma cat&#225;strofe silenciosa:</p><ul><li><p><strong>No Banco:</strong> O cliente est&#225; bloqueado.</p></li><li><p><strong>No Mundo Real:</strong> O sistema de cart&#245;es nunca ficou sabendo.</p></li><li><p><strong>Resultado:</strong> O cliente continua gastando, gerando preju&#237;zo financeiro.</p></li></ul><p>Esse cen&#225;rio &#233; o cl&#225;ssico <strong>Dual Write Problem</strong>: tentar escrever em dois sistemas de armazenamento diferentes (Banco e Broker) sem uma transa&#231;&#227;o que una os dois.</p><h4>Onde Est&#225; o Erro?</h4><p>Aqui mora o perigo. Ao colocar uma chamada de rede (Kafka, HTTP, gRPC) dentro do fluxo da FSM &#8212; especialmente dentro de uma transa&#231;&#227;o de banco &#8212; voc&#234; est&#225; acoplando a <strong>integridade dos seus dados</strong> &#224; <strong>instabilidade da rede externa</strong>.</p><p>Se o banco salva, mas a rede falha, a FSM acredita que o trabalho foi feito. Mas a notifica&#231;&#227;o se perdeu.</p><h4>Transactional Outbox</h4><p>Uma FSM madura adota uma postura defensiva. Ela n&#227;o tenta entregar mensagens; ela apenas <strong>registra decis&#245;es</strong>.</p><p>A FSM n&#227;o deve ser o &#8220;motoboy&#8221; que entrega a carta. Ela deve ser o &#8220;Escritur&#225;rio&#8221; que carimba e arquiva o documento oficial. O fluxo robusto &#233;:</p><ol><li><p>A FSM decide a mudan&#231;a de estado.</p></li><li><p>A FSM monta o evento (o payload JSON).</p></li><li><p>A FSM salva o <strong>Novo Status</strong> E o <strong>Evento na tabela Outbox</strong> na <strong>mesma transa&#231;&#227;o de banco de dados (ACID)</strong>.</p></li></ol><p>Por estarem na mesma transa&#231;&#227;o local, ou os dois s&#227;o salvos, ou nenhum &#233;. &#201; fisicamente imposs&#237;vel o contrato mudar de estado sem que o evento correspondente seja gravado.</p><h4>Quem Fala com o Mundo?</h4><p>A FSM encerra sua responsabilidade ao escrever a verdade no banco. A entrega do evento ao Kafka &#233; delegada a um processo separado &#8212; um <strong>Worker</strong>, <strong>Relay</strong> ou ferramenta de <strong>CDC (Change Data Capture)</strong>.</p><p>Esse componente &#8220;Carteiro&#8221;:</p><ol><li><p>L&#234; a tabela <code>OUTBOX_EVENTS</code>.</p></li><li><p>Recupera os eventos pendentes.</p></li><li><p>Publica no Kafka.</p></li><li><p>Se o Kafka estiver fora do ar, ele espera e tenta novamente (Retry).</p></li></ol><p><strong>Resultado:</strong></p><ul><li><p>Se o Kafka cair, o evento continua seguro no banco.</p></li><li><p>Nenhuma informa&#231;&#227;o se perde.</p></li><li><p>A entrega acontece assim que a infraestrutura voltar.</p></li></ul><p>Ao separar claramente a <strong>Decis&#227;o</strong> (Motor), o <strong>Roteamento</strong> (Transi&#231;&#245;es) e o <strong>Registro</strong> (Action + Outbox), a M&#225;quina de Estados deixa de ser fr&#225;gil. Ela n&#227;o executa o mundo; ela declara a verdade do dom&#237;nio, garantindo que o estado do contrato e os eventos observados pelos outros sistemas estejam sempre em eventual, por&#233;m garantida, sintonia.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!HzKp!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!HzKp!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png 424w, https://substackcdn.com/image/fetch/$s_!HzKp!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png 848w, https://substackcdn.com/image/fetch/$s_!HzKp!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!HzKp!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!HzKp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png" width="728" height="401.5" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1456,&quot;resizeWidth&quot;:728,&quot;bytes&quot;:12959961,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/182564456?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!HzKp!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png 424w, https://substackcdn.com/image/fetch/$s_!HzKp!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png 848w, https://substackcdn.com/image/fetch/$s_!HzKp!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!HzKp!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fae03bb48-3a6f-498f-be23-80822212ac89_2784x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>O &#8220;Porqu&#234;&#8221; Importa: Status vs. Status Reason (Motivo)</h2><p>Agora que a nossa M&#225;quina de Estados (FSM) est&#225; s&#243;lida, vamos enfrentar um problema de design que apanha 9 em cada 10 equipas de engenharia: a <strong>Explos&#227;o de Estados</strong>.</p><p>Imagine que est&#225; a desenhar o estado de <strong>Cancelamento</strong>. O neg&#243;cio diz-lhe:</p><blockquote><p><em>&#8220;Precisamos de saber se o contrato foi cancelado pelo cliente, por fraude&#8230;&#8221;</em></p></blockquote><p>O instinto inicial do programador &#233; criar tr&#234;s estados:</p><ol><li><p><code>CANCELLED_BY_CUSTOMER</code></p></li><li><p><code>CANCELLED_BY_FRAUD</code></p></li><li><p><code>CANCELLED_BY_PARTNER</code></p></li></ol><p><strong>N&#227;o fa&#231;a isto.</strong> Se seguir este caminho, a sua FSM vai transformar-se numa teia de aranha imposs&#237;vel de manter. Para cada um destes tr&#234;s estados, teria de duplicar as transi&#231;&#245;es, as <em>actions</em> e os <em>guards</em>.</p><h2>A Solu&#231;&#227;o: Dividir para Conquistar</h2><p>Para manter a sanidade da arquitetura &#8212; e da equipe &#8212; precisamos separar duas perguntas que parecem semelhantes, mas <strong>n&#227;o s&#227;o</strong>:</p><ul><li><p><strong>Onde estou?</strong> &#8594; Isso define o <strong>comportamento</strong> do sistema</p></li><li><p><strong>Por que estou aqui?</strong> &#8594; Isso define o <strong>contexto de neg&#243;cio</strong></p></li></ul><p>Misturar essas duas coisas &#233; o caminho mais curto para uma FSM ileg&#237;vel.</p><h3>O Status Define o Comportamento</h3><p>O <strong>Status</strong> deve representar apenas o <strong>ciclo de vida macrosc&#243;pico</strong> do contrato.<br>Ele responde a uma pergunta simples:</p><blockquote><p>&#8220;O que este contrato pode ou n&#227;o fazer agora?&#8221;</p></blockquote><p>Se um contrato est&#225; em <code>CANCELLED</code>, o comportamento &#233; o mesmo, <strong>independentemente do motivo</strong>:</p><ul><li><p>N&#227;o gera juros</p></li><li><p>N&#227;o aceita pagamentos</p></li><li><p>N&#227;o envia faturas</p></li></ul><p>Do ponto de vista do sistema, n&#227;o importa <em>por que</em> ele foi cancelado.<br>O comportamento &#233; &#250;nico.</p><p>Por isso, <strong>tenha apenas um estado </strong><code>CANCELLED</code>.</p><p>Criar <code>CANCELLED_BY_CLIENT</code>, <code>CANCELLED_BY_FRAUD</code>, <code>CANCELLED_BY_BANK</code> n&#227;o adiciona comportamento &#8212; apenas confus&#227;o.</p><h3>O Reason Define a Regra de Neg&#243;cio (a Nuance)</h3><p>O <strong>Status Reason</strong> &#233; um campo sat&#233;lite.<br>Ele n&#227;o muda o comportamento base do estado, mas <strong>explica a causa raiz</strong> que levou at&#233; ele.</p><p>&#201; aqui que a granularidade de neg&#243;cio vive.</p><p>Esse campo &#233; usado para:</p><ul><li><p>autorizar ou bloquear transi&#231;&#245;es futuras</p></li><li><p>aplicar regras espec&#237;ficas</p></li><li><p>justificar decis&#245;es regulat&#243;rias ou jur&#237;dicas</p></li></ul><p>Ele <strong>n&#227;o cria novos estados</strong>, ele <strong>informa decis&#245;es</strong>.</p><h3>Exemplo Pr&#225;tico: Estado BLOCKED</h3><p>Vamos olhar para o estado <code>BLOCKED</code>.</p><p>O comportamento &#233; claro:</p><ul><li><p>cr&#233;dito suspenso</p></li><li><p>opera&#231;&#245;es bloqueadas</p></li><li><p>risco controlado</p></li></ul><p>Mas o <strong>motivo</strong> do bloqueio importa &#8212; e muito.</p><h3>Cen&#225;rio A</h3><ul><li><p><strong>Status:</strong> <code>BLOCKED</code></p></li><li><p><strong>Reason:</strong> <code>LACK_OF_PAYMENT</code></p></li></ul><p><strong>Implica&#231;&#227;o:</strong><br>Se chegar um evento <code>PAYMENT_RECEIVED</code>, a FSM pode desbloquear automaticamente e retornar para <code>SIGNED</code>.</p><h3>Cen&#225;rio B</h3><ul><li><p><strong>Status:</strong> <code>BLOCKED</code></p></li><li><p><strong>Reason:</strong> <code>JUDICIAL_ORDER</code></p></li></ul><p><strong>Implica&#231;&#227;o: </strong>Se chegar o mesmo evento <code>PAYMENT_RECEIVED</code>, a FSM <strong>n&#227;o pode</strong> desbloquear.</p><p>O dinheiro entra. A d&#237;vida diminui. Mas o contrato continua bloqueado at&#233; que um comando jur&#237;dico expl&#237;cito seja recebido.</p><h3>Por que isso &#233; melhor do que m&#250;ltiplos status?</h3><p>Porque voc&#234; mant&#233;m:</p><ul><li><p><strong>Poucos estados</strong></p></li><li><p><strong>Comportamentos claros</strong></p></li><li><p><strong>FSM leg&#237;vel</strong></p></li><li><p><strong>Regras concentradas</strong></p></li><li><p><strong>Evolu&#231;&#227;o sem explos&#227;o de complexidade</strong></p></li></ul><p>O Status responde <em>&#8220;o que o sistema faz&#8221;</em>.<br>O Reason responde <em>&#8220;sob quais condi&#231;&#245;es ele pode mudar&#8221;</em>.</p><p>Separar essas responsabilidades n&#227;o &#233; confus&#227;o &#8212; &#233; <strong>design consciente</strong>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!doG0!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!doG0!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!doG0!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!doG0!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!doG0!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!doG0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png" width="1408" height="752" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:752,&quot;width&quot;:1408,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:1014374,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/182564456?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!doG0!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png 424w, https://substackcdn.com/image/fetch/$s_!doG0!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png 848w, https://substackcdn.com/image/fetch/$s_!doG0!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png 1272w, https://substackcdn.com/image/fetch/$s_!doG0!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbbccd80a-4ebb-487c-85f7-d648a8ecb51a_1408x752.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><h2>FSM + Event-Driven: A Arquitetura da Consist&#234;ncia</h2><p>Agora que definimos a mec&#226;nica interna, precisamos olhar para fora. Como a FSM se comporta como o cora&#231;&#227;o de uma arquitetura distribu&#237;da?</p><h4>A FSM como a Fonte &#218;nica da Verdade (SSOT)</h4><p>Em microsservi&#231;os, o maior risco arquitetural &#233; o <strong>Split Brain</strong> (C&#233;rebro Dividido): o Servi&#231;o de Cobran&#231;a acredita que o contrato est&#225; ativo, enquanto o Servi&#231;o de Risco acredita que ele est&#225; bloqueado. Isso ocorre quando a mudan&#231;a de estado e a notifica&#231;&#227;o dessa mudan&#231;a n&#227;o s&#227;o at&#244;micas.</p><p>A FSM resolve isso centralizando a autoridade de escrita. Ela opera sob o princ&#237;pio de <strong>Single Writer</strong>: apenas a m&#225;quina de estados tem permiss&#227;o para alterar a coluna <code>status</code> do contrato. O fluxo de confian&#231;a &#233; garantido pela transacionalidade:</p><ol><li><p><strong>Valida&#231;&#227;o:</strong> O gatilho entra, as <em>Guards</em> validam e a Transi&#231;&#227;o &#233; aprovada.</p></li><li><p><strong>Atomicidade (O Pulo do Gato):</strong> Na <strong>mesma transa&#231;&#227;o de banco de dados</strong> (ACID), a FSM realiza duas opera&#231;&#245;es:</p><ul><li><p>Atualiza o estado do contrato (<code>UPDATE contracts SET status...</code>).</p></li><li><p>Insere o evento na tabela de sa&#237;da (<code>INSERT INTO outbox...</code>).</p></li></ul></li><li><p><strong>Garantia:</strong> &#201; matematicamente imposs&#237;vel o estado mudar sem que o evento seja gerado. O evento <code>ContractBlockedEvent</code> n&#227;o &#233; um &#8220;aviso&#8221;; &#233; um fato imut&#225;vel que reflete a realidade persistida.</p></li></ol><h4>Orquestra&#231;&#227;o Local, Coreografia Global</h4><p>Arquitetos frequentemente debatem entre Coreografia (servi&#231;os reagem a eventos soltos) e Orquestra&#231;&#227;o (um maestro comanda tudo). A FSM introduz um padr&#227;o h&#237;brido eficiente: <strong>Orquestra&#231;&#227;o Intra-Servi&#231;o e Coreografia Inter-Servi&#231;os</strong>.</p><ul><li><p><strong>Internamente (Orquestra&#231;&#227;o):</strong> A FSM &#233; tirana. Ela orquestra rigidamente o ciclo de vida dentro do <em>Bounded Context</em> de Cr&#233;dito. Ela n&#227;o permite que etapas sejam puladas. Ela garante a consist&#234;ncia imediata do dom&#237;nio.</p></li><li><p><strong>Externamente (Coreografia):</strong> Uma vez que a transi&#231;&#227;o ocorre e o evento &#233; publicado, a FSM n&#227;o se importa com quem vai consumi-lo. O Servi&#231;o de Marketing pode consumir o evento para enviar um e-mail, e o Jur&#237;dico para abrir um processo.</p></li></ul><p>Isso desacopla os dom&#237;nios. A FSM garante que o sinal emitido &#233; puro e confi&#225;vel, permitindo que os consumidores reajam livremente sem acoplar a l&#243;gica de neg&#243;cio central.</p><h4>Auditabilidade e Causalidade (Forensics)</h4><p>Logs de aplica&#231;&#227;o (como no Datadog ou Splunk) mostram <em>que</em> um erro ocorreu. A M&#225;quina de Estados mostra <em>por que</em> o sistema chegou naquele estado.</p><p>Em sistemas financeiros auditados, saber o estado atual n&#227;o basta. Precisamos da <strong>Causalidade</strong>. Ao persistir o hist&#243;rico de transi&#231;&#245;es (<code>state_transitions</code> table), criamos uma trilha forense:</p><ul><li><p><em>Estado Anterior:</em> <code>SIGNED</code></p></li><li><p><em>Gatilho:</em> <code>PAGAMENTO_FALHOU</code> (Payload: motivo=&#8221;Saldo Insuficiente&#8221;)</p></li><li><p><em>Novo Estado:</em> <code>RETRY_PAYMENT</code></p></li><li><p><em>Timestamp:</em> 14:00:01</p></li></ul><p>Isso transforma a depura&#231;&#227;o. Em vez de adivinhar o comportamento do sistema cruzando logs de dez microsservi&#231;os, voc&#234; olha para a tabela de transi&#231;&#245;es e v&#234; a narrativa exata da decis&#227;o tomada pela m&#225;quina. Isso n&#227;o &#233; apenas m&#233;trica para PMs; &#233; ferramenta de <em><a href="https://www.ibm.com/docs/en/om-jvm/5.4.0?topic=support-introduction-troubleshooting">troubleshooting</a></em> para engenheiros e prova de conformidade para auditores.</p><h2>A Diferen&#231;a entre C&#243;digo e Arquitetura</h2><p>Chegamos ao fim desta jornada. Se voc&#234; leu at&#233; aqui, percebeu que implementar uma M&#225;quina de Estados em um sistema financeiro n&#227;o &#233; um preciosismo acad&#234;mico. &#201; uma estrat&#233;gia de sobreviv&#234;ncia.</p><p>Ao longo deste texto, desconstru&#237;mos a ilus&#227;o de que &#8220;basta uma coluna de status e alguns IFs&#8221;. Vimos que a complexidade do cr&#233;dito &#8212; juros, atrasos, bloqueios judiciais &#8212; n&#227;o pode ficar espalhada em <em>Controllers</em> ou <em>Services</em>. Ela precisa de um lar. E esse lar &#233; dividido em duas casas: o <strong>Motor de Decis&#227;o</strong>, que pensa a estrat&#233;gia, e a <strong>M&#225;quina de Estados</strong>, que protege a integridade.</p><h3>O Que Levar Daqui</h3><p>Se este artigo mudar apenas tr&#234;s coisas na sua pr&#243;xima implementa&#231;&#227;o, que sejam estas:</p><ol><li><p><strong>A FSM &#233; o Guardi&#227;o:</strong> Ela &#233; o &#250;nico componente do seu sistema que tem o direito de dizer &#8220;N&#195;O&#8221;. Quando o Motor, o Frontend ou o CEO pedirem uma mudan&#231;a de status, a FSM tem a autoridade de barrar se aquilo violar o ciclo de vida do contrato. Ela impede o caos dos &#8220;Estados Imposs&#237;veis&#8221;.</p></li><li><p><strong>O Gatilho &#233; a &#218;nica Porta de Entrada:</strong> Acabou a era do <code>UPDATE contracts SET status = ...</code>. A partir de hoje, voc&#234; interage com o contrato atrav&#233;s de inten&#231;&#245;es claras (<code>PAGAMENTO_RECEBIDO</code>, <code>FALHA_DETECTADA</code>). Isso garante que nenhuma regra seja burlada e que todo evento deixe um rastro audit&#225;vel.</p></li><li><p><strong>A Transi&#231;&#227;o &#233; Regra de Neg&#243;cio, n&#227;o Seta:</strong> Uma transi&#231;&#227;o n&#227;o &#233; apenas um caminho de A para B. Com o uso de <em>Guards</em> e <em>Payloads</em>, a transi&#231;&#227;o torna-se o local onde a l&#243;gica vital reside. Ela decide se o dinheiro recebido &#233; suficiente para quitar ou apenas amortizar. Ela centraliza a intelig&#234;ncia do fluxo.</p></li></ol><p>A maior barreira para adotar este padr&#227;o n&#227;o &#233; t&#233;cnica, &#233; mental. O desenvolvedor j&#250;nior foca na <strong>Persist&#234;ncia</strong>: <em>&#8220;Preciso salvar que o cliente pagou.&#8221;</em> O arquiteto s&#234;nior foca no <strong>Comportamento</strong>: <em>&#8220;Dado que o cliente pagou, quais caminhos s&#227;o v&#225;lidos e quais garantias eu preciso oferecer?&#8221;</em></p><p>Adotar M&#225;quinas de Estados &#233; aceitar pagar um pre&#231;o inicial maior na modelagem para comprar <strong>tranquilidade a longo prazo</strong>. &#201; a diferen&#231;a entre passar suas noites corrigindo dados inconsistentes no banco de dados ou dormir tranquilo, sabendo que sua arquitetura matematicamente pro&#237;be que o erro aconte&#231;a.</p><p>O c&#243;digo muda, as regras de neg&#243;cio mudam, mas a integridade dos dados do seu cliente &#233; inegoci&#225;vel. Construa sistemas que respeitem isso.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><div><hr></div><h2>FAQ &amp; Trade-offs</h2><p>Se voc&#234; acompanhou at&#233; aqui, &#233; prov&#225;vel que algumas luzes de alerta tenham acendido na sua cabe&#231;a. Arquitetura de software &#233; feita de <em>trade-offs</em> (trocas), e implementar uma FSM resiliente com Outbox traz complexidades que n&#227;o podem ser ignoradas.</p><p>Vamos antecipar as d&#250;vidas (e evitar discuss&#245;es te&#243;ricas infinitas no Code Review ou no LinkedIn):</p><h3>1. &#8220;Isso &#233; Event Sourcing?&#8221;</h3><p><strong>Resposta Curta:</strong> N&#227;o.</p><p><strong>A Nuance:</strong> Muita gente confunde os conceitos.</p><ul><li><p><strong>Event Sourcing:</strong> O estado da aplica&#231;&#227;o &#233; derivado <em>exclusivamente</em> da re-execu&#231;&#227;o de todos os eventos passados. Voc&#234; n&#227;o tem uma coluna <code>status</code> no banco; voc&#234; tem um log de eventos.</p></li><li><p><strong>FSM + Outbox (O que propomos aqui):</strong> O estado continua sendo persistido da maneira tradicional (Snapshot State). Voc&#234; tem a coluna <code>status</code> atualizada via <code>UPDATE</code>. A tabela de eventos (Outbox) serve apenas para <strong>integra&#231;&#227;o</strong> com outros sistemas, n&#227;o para reconstruir o estado interno.</p></li></ul><blockquote><p>&#8220;O Outbox Pattern serve para garantir que o mundo externo saiba o que aconteceu, sem que voc&#234; precise mudar a forma como seu banco de dados relacional funciona.&#8221;</p></blockquote><h3>2. &#8220;O Outbox garante que eu receba a mensagem apenas uma vez?&#8221;</h3><p><strong>Resposta Curta:</strong> Absolutamente n&#227;o. E isso &#233; perigoso.</p><p><strong>A Nuance:</strong> O Outbox resolve o problema de <em>perder</em> mensagens, mas introduz a possibilidade de <em>duplicar</em> mensagens. O worker que l&#234; a tabela Outbox pode falhar logo ap&#243;s enviar para o Kafka, mas antes de marcar a mensagem como &#8220;enviada&#8221; no banco. Quando ele reiniciar, ele enviar&#225; a mesma mensagem novamente.</p><p>Isso &#233; o que chamamos de garantia <strong><a href="https://www.cloudcomputingpatterns.org/at_least_once_delivery/">At-Least-Once Delivery</a></strong> (Entrega pelo menos uma vez). <strong>Consequ&#234;ncia:</strong> Todos os consumidores (servi&#231;os de Cart&#227;o, Cobran&#231;a, etc.) <strong>DEVEM ser Idempotentes</strong>. Se o servi&#231;o de Cart&#227;o receber o evento <code>ContractBlocked</code> duas vezes, na segunda vez ele deve olhar e dizer: <em>&#8220;Ok, j&#225; est&#225; bloqueado, n&#227;o vou fazer nada&#8221;</em>, e n&#227;o travar ou gerar erro.</p><h3>3. &#8220;Por que a FSM n&#227;o pode chamar uma API externa (HTTP) para decidir?&#8221;</h3><p><strong>D&#250;vida:</strong> <em>&#8220;Se o cliente est&#225; atrasado h&#225; 150 dias, por que a FSM n&#227;o faz um GET na API de Calend&#225;rio ou na API de Score para saber o que fazer?&#8221;</em></p><p><strong>Resposta:</strong> Porque isso viola o isolamento e acopla a disponibilidade. Se a API de Score estiver fora do ar, sua FSM trava e voc&#234; n&#227;o consegue mais transitar contratos. A FSM deve ser autocontida.</p><ul><li><p><strong>Errado:</strong> FSM pergunta: &#8220;Quantos dias de atraso?&#8221;</p></li><li><p><strong>Correto:</strong> Um Scheduler externo calcula os dias e envia o gatilho: <code>Fire(ATRASO_150_DIAS_DETECTADO)</code>. A FSM n&#227;o calcula, ela reage. Ela n&#227;o pensa, ela executa regras pr&#233;-definidas.</p></li></ul><h3>4. &#8220;Por que tanta complexidade com Tabela Outbox? O Kafka quase nunca cai.&#8221;</h3><p><strong>A Realidade:</strong> &#8220;Quase nunca&#8221; em sistemas financeiros de alta escala significa &#8220;vai cair na Black Friday&#8221;.</p><p><em><strong>O custo de implementar o Outbox &#233; o custo de configurar um worker extra</strong></em>. O custo de <strong>n&#227;o</strong> <strong>implementar &#233; ter dados inconsistentes (dinheiro saindo da conta sem registro) </strong>e ter que gastar semanas rodando scripts manuais de corre&#231;&#227;o no banco de dados para conciliar sistemas.</p><p>Em arquitetura, voc&#234; paga o pre&#231;o da complexidade agora para n&#227;o pagar o pre&#231;o do caos &#224;s 3 da manh&#227; de um domingo.</p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Simplificando, a teoria dos aut&#244;matos lida com a l&#243;gica da computa&#231;&#227;o em rela&#231;&#227;o a m&#225;quinas simples, chamadas de <strong>aut&#244;matos</strong> . Atrav&#233;s dos aut&#244;matos, os cientistas da computa&#231;&#227;o conseguem entender como as m&#225;quinas computam fun&#231;&#245;es e resolvem problemas e, mais importante, o que significa uma fun&#231;&#227;o ser definida como <em>comput&#225;vel</em> ou uma quest&#227;o ser descrita como <em>decid&#237;vel</em> .</p><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p><strong>O Bot&#227;o do Elevador</strong></p><p>Para visualizar a fun&#231;&#227;o do Gatilho, imagine que voc&#234; est&#225; dentro de um elevador subindo entre o 10&#186; e o 20&#186; andar.</p><p>O elevador est&#225; no Estado: <code>EM_MOVIMENTO</code>. Voc&#234; decide apertar o bot&#227;o <strong>&#8220;Abrir Porta&#8221;</strong> (Gatilho).</p><p>O painel recebe o sinal el&#233;trico do bot&#227;o (a solicita&#231;&#227;o externa). No entanto, a porta <strong>n&#227;o se abre</strong>. O sistema central do elevador verifica o estado atual (<code>EM_MOVIMENTO</code>), consulta suas regras de seguran&#231;a e decide ignorar o gatilho.</p><p><strong>A li&#231;&#227;o:</strong> O Gatilho &#233; apenas o pedido (a inten&#231;&#227;o). Ele n&#227;o tem poder absoluto. Quem tem a palavra final sobre se a porta abre ou n&#227;o &#233; a combina&#231;&#227;o do <strong>Estado Atual</strong> com as <strong>Regras de Transi&#231;&#227;o</strong>.</p><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><strong>A Fronteira entre &#8220;Guard&#8221; e &#8220;Motor de Regras&#8221;</strong></p><p>Leitores atentos podem notar uma aparente contradi&#231;&#227;o. No t&#243;pico <strong>&#8220;Anatomia de uma M&#225;quina de Estados&#8221;</strong>, afirmamos que a FSM n&#227;o deve conter l&#243;gicas como <code>if (diasAtraso &gt; 10...)</code>. Agora, sugerimos que ela compare <code>valor_pago</code> com <code>valor_total</code>. Qual &#233; a diferen&#231;a?</p><p>A diferen&#231;a &#233; <strong>C&#225;lculo vs. Roteamento</strong>.</p><ol><li><p><strong>O que a FSM N&#195;O faz (C&#225;lculo/Pol&#237;tica):</strong> A FSM n&#227;o decide <em>qual</em> &#233; a taxa de juros, nem <em>se</em> 10 dias de atraso &#233; ruim ou bom. Isso &#233; pol&#237;tica de cr&#233;dito (vari&#225;vel) e pertence ao Motor de Decis&#227;o.</p></li><li><p><strong>O que a FSM DEVE fazer (Roteamento/Fato):</strong> A FSM recebe fatos. Comparar se <code>150.00 == 150.00</code> n&#227;o &#233; uma regra de neg&#243;cio complexa; &#233; uma <strong>valida&#231;&#227;o de integridade estrutural</strong>.</p><ul><li><p>A Guard pergunta: <em>&#8220;O valor que chegou preenche o buraco total da d&#237;vida?&#8221;</em></p></li><li><p>Se Sim -&gt; Caminho A (<code>SIGNED</code>).</p></li><li><p>Se N&#227;o -&gt; Caminho B (<code>PARTIALLY_PAID</code>).</p></li></ul></li></ol><p><strong>Por que enviar os valores no Payload?</strong> Mesmo que tenhamos o campo <code>status_final_fatura</code>, enviar os valores brutos (<code>150.00</code>) serve como <strong>Prova de Auditoria</strong> dentro do evento. Se houver um bug no faturamento que envie <code>status: TOTAL_PAID</code> mas com <code>valor: 0.00</code>, uma Guard bem escrita na FSM pode pegar essa inconsist&#234;ncia (<code>valor_pago != valor_total</code>) e travar a transi&#231;&#227;o, salvando o sistema de um falso positivo.</p><div><hr></div></div></div>]]></content:encoded></item><item><title><![CDATA[Como a Teoria das Filas explica latências 'inexplicáveis'em produção.]]></title><description><![CDATA[Por que sua fila s&#243; cresce? A matem&#225;tica ignorada que derruba arquiteturas de eventos.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/por-que-sua-fila-so-cresce-a-matematica</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/por-que-sua-fila-so-cresce-a-matematica</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Fri, 19 Dec 2025 20:13:53 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/64ee530c-63f1-4533-9815-5c62996abf20_2432x1728.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>A maioria de n&#243;s sabe como colocar uma mensagem na fila. Se voc&#234; trabalha com backend hoje, provavelmente j&#225; configurou um t&#243;pico no Kafka, subiu uma inst&#226;ncia do RabbitMQ, provisionou uma fila no SQS ou configurou workers no BullMQ. Sabemos implementar a biblioteca, configurar a <em>connection string</em> e ver a m&#225;gica do desacoplamento acontecer.</p><p>Mas paramos por a&#237;?</p><p>Saber a sintaxe de uma ferramenta &#233; apenas a ponta do iceberg. O verdadeiro desafio n&#227;o &#233; fazer a mensagem chegar, mas entender o comportamento matem&#225;tico de como essa fila cresce, esvazia ou colapsa sob press&#227;o.</p><p>Por muito tempo, eu achei que garantir a entrega da mensagem era o suficiente. At&#233; que enfrentei gargalos que n&#227;o faziam sentido, lat&#234;ncias que explodiam sem aviso e consumidores que travavam justamente quando o sistema estava ocioso. A ferramenta estava certa, o c&#243;digo estava limpo, mas a arquitetura estava falhando.</p><p>Foi nesse momento que precisei dar um passo atr&#225;s e estudar <strong>Teoria das Filas</strong> (<em><a href="https://github.com/joelparkerhenderson/queueing-theory">Queueing Theory</a></em>). N&#227;o a vers&#227;o acad&#234;mica e abstrata, mas a aplica&#231;&#227;o pr&#225;tica que define se o seu sistema distribu&#237;do vai sobreviver &#224; Black Friday ou se vai se afogar em <em>timeouts</em>.</p><p>Neste artigo, vamos falar sobre a &#8220;matem&#225;tica oculta&#8221; que rege o comportamento dos seus microsservi&#231;os e filas e que, surpreendentemente, poucos artigos abordam.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/por-que-sua-fila-so-cresce-a-matematica?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Esta publica&#231;&#227;o &#233; p&#250;blica, ent&#227;o sinta-se &#224; vontade para compartilh&#225;-la.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/por-que-sua-fila-so-cresce-a-matematica?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/por-que-sua-fila-so-cresce-a-matematica?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p></div><h2>Teoria das Filas</h2><p>A <strong><a href="https://www.bigbinary.com/blog/understanding-queueing-theory">Teoria das Filas</a></strong><a href="https://www.bigbinary.com/blog/understanding-queueing-theory"> (</a><em><a href="https://www.bigbinary.com/blog/understanding-queueing-theory">Queueing Theory</a></em><a href="https://www.bigbinary.com/blog/understanding-queueing-theory">)</a> n&#227;o nasceu no Vale do Sil&#237;cio. Ela surgiu em Copenhague, em 1909, criada por um engenheiro e matem&#225;tico chamado <strong><a href="https://mathshistory.st-andrews.ac.uk/Biographies/Erlang/">Agner Krarup Erlang</a></strong>. Ele trabalhava na companhia telef&#244;nica local e tinha um problema de neg&#243;cios muito claro: <em><a href="https://queue-it.com/blog/queuing-theory/">quantas linhas telef&#244;nicas s&#227;o necess&#225;rias para garantir que as pessoas consigam falar, sem que a empresa v&#225; &#224; fal&#234;ncia instalando cabos in&#250;teis?</a></em></p><p>Substitua &#8220;linhas telef&#244;nicas&#8221; por &#8220;t&#243;picos no Kafka&#8221; ou &#8220;workers no Kubernetes&#8221;, e o problema de 1909 &#233; exatamente o mesmo problema que voc&#234; enfrenta hoje ao desenhar uma arquitetura distribu&#237;da.</p><h4>Os Elementos Fundamentais</h4><p>Para um engenheiro de software, qualquer sistema de fila &#8212; seja uma simples <code>ArrayQueue</code> em mem&#243;ria ou um cluster complexo de RabbitMQ &#8212; pode ser reduzido a tr&#234;s componentes matem&#225;ticos b&#225;sicos (<a href="https://lamfo-unb.github.io/2018/07/06/Otimiza%C3%A7%C3%A3o-de-Filas/">o modelo de Kendall</a>):</p><ol><li><p><strong>A Fonte de Chegada (&#955; - Lambda):</strong> &#201; a taxa na qual as requisi&#231;&#245;es chegam ao sistema. No nosso mundo, s&#227;o os <em>Producers</em> gerando eventos, cliques de usu&#225;rios ou webhooks de terceiros. A caracter&#237;stica mais importante aqui &#233; a <strong>distribui&#231;&#227;o</strong>: elas chegam em intervalos fixos (previs&#237;vel) ou aleat&#243;rios (<a href="https://michaelis.uol.com.br/busca?id=YmOL">estoc&#225;stico</a>)?</p></li><li><p><strong>A Fila (Buffer):</strong> O local onde as requisi&#231;&#245;es aguardam. Ela pode ser finita (tem um limite e depois disso rejeita dados) ou infinita (aceita dados at&#233; a mem&#243;ria do servidor estourar).</p></li><li><p><strong>O Mecanismo de Servi&#231;o (&#181; - Mu):</strong> &#201; a capacidade de processamento. S&#227;o seus <em>Consumers</em> ou <em>Workers</em>. A taxa de servi&#231;o &#233; quantos itens por segundo voc&#234; consegue eliminar da fila.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wXac!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wXac!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!wXac!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!wXac!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!wXac!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wXac!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/c083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7828866,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wXac!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!wXac!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!wXac!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!wXac!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fc083d08e-9e98-4412-a9b9-fabea716fb9d_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Por que precisamos de filas? (A analogia do Amortecedor)</h4><p>Se viv&#234;ssemos em um mundo perfeito onde o processamento de um dado (<strong>&#181;</strong>) fosse sempre mais r&#225;pido que a chegada dele (<strong>&#955;</strong>), filas seriam in&#250;teis.</p><p>Precisamos de filas porque o mundo real &#233; ass&#237;ncrono e &#8220;rajado&#8221; (bursty).<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!elTS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!elTS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!elTS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!elTS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!elTS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!elTS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/fdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4729358,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!elTS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!elTS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!elTS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!elTS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Ffdce136c-6099-4d0c-8e16-4784f1d83391_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>A fila atua como um amortecedor de choques. Imagine um carro passando por um buraco: a mola (fila) absorve o impacto imediato (pico de tr&#225;fego) e dissipa a energia lentamente ao longo do tempo (processamento pelos workers). Sem a fila, o impacto seria transferido diretamente para o chassi (banco de dados), quebrando o sistema.</p><h4>Por que precisamos entender isso?</h4><p>Aqui est&#225; o ponto cego da nossa profiss&#227;o: <strong>nossa intui&#231;&#227;o linear falha em sistemas <a href="https://www.dicio.com.br/estocastico/">estoc&#225;sticos</a>.</strong></p><p>A maioria dos programadores opera sob a l&#243;gica de que &#8220;se eu dobrar o n&#250;mero de servidores, eu corto a lat&#234;ncia pela metade&#8221; ou &#8220;se meu servidor est&#225; em 80% de CPU, ainda tenho 20% de folga&#8221;.</p><p>Estudar Teoria das Filas nos ensina que essas afirma&#231;&#245;es s&#227;o falsas. Sistemas de filas se comportam de maneira <strong>n&#227;o-linear</strong>. Pequenas varia&#231;&#245;es na taxa de chegada ou no tempo de servi&#231;o n&#227;o causam pequenos aumentos na fila; elas podem causar colapsos catastr&#243;ficos.</p><p>Entender o b&#225;sico dessa teoria transforma voc&#234; de um desenvolvedor que &#8220;aumenta a m&#225;quina quando o site cai&#8221; para um engenheiro que sabe <strong>matematicamente</strong> onde est&#225; o gargalo e, mais importante, consegue prever a falha antes que ela aconte&#231;a.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>O &#8220;Joelho da Curva&#8221; e o Mito dos 100% de CPU</h3><p>Se voc&#234; j&#225; teve que justificar custos de infraestrutura para um gerente financeiro ou um CTO focado em <em>cutting costs</em>, j&#225; ouviu a frase:</p><blockquote><p><em>&#8220;Nossos servidores est&#227;o rodando com apenas 40% de CPU. Estamos jogando dinheiro fora! Quero ver isso rodando perto de 100% para valer o que pagamos na AWS.&#8221;</em></p></blockquote><p>Na cabe&#231;a de quem olha planilhas, 100% de utiliza&#231;&#227;o &#233; efici&#234;ncia m&#225;xima. Na matem&#225;tica dos sistemas distribu&#237;dos, <strong>100% de utiliza&#231;&#227;o &#233; um estado de falha.</strong></p><p>Para entender por que buscar a &#8220;utiliza&#231;&#227;o total&#8221; &#233; suic&#237;dio arquitetural, precisamos primeiro definir o que estamos tentando proteger: a Lat&#234;ncia.</p><h4>O que &#233; Lat&#234;ncia?</h4><p>Muitos devs confundem lat&#234;ncia com &#8220;tempo de processamento&#8221;. N&#227;o s&#227;o a mesma coisa.</p><p>Imagine que voc&#234; vai a uma cafeteria. O barista leva exatos <strong>30 segundos</strong> para fazer seu caf&#233;.</p><ul><li><p><strong>Cen&#225;rio A (Vazia):</strong> Voc&#234; chega, pede, espera 30s. Sai com o caf&#233;.</p><ul><li><p>Tempo total: 30s.</p></li></ul></li><li><p><strong>Cen&#225;rio B (Cheia):</strong> Existem 4 pessoas na sua frente. O barista ainda leva os mesmos 30s para fazer cada caf&#233;.</p><ul><li><p>Voc&#234; espera 2 minutos na fila + 30s do seu caf&#233;.</p></li><li><p>Tempo total: 2 minutos e 30s.</p></li></ul></li></ul><p><strong>Lat&#234;ncia &#233; o Tempo Total.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!QfXE!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!QfXE!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!QfXE!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!QfXE!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!QfXE!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!QfXE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9868565,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!QfXE!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!QfXE!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!QfXE!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!QfXE!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd89f7e0c-1d5e-44ad-b669-a7668703838c_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;Lat&#234;ncia = \\text{Tempo de Espera na Fila} + \\text{Tempo de Servi&#231;o}&quot;,&quot;id&quot;:&quot;NLFPVWXUMH&quot;}" data-component-name="LatexBlockToDOM"></div><p>O tempo de servi&#231;o (fazer o caf&#233;) costuma ser fixo. O que mata sua performance &#233; o <strong>Tempo de Espera</strong>. E &#233; aqui que entra a F&#243;rmula de Kingman para explicar como a fila se comporta quando a loja enche.</p><h4>A F&#243;rmula de Kingman</h4><p>A intui&#231;&#227;o humana &#233; <strong>linear</strong>. N&#243;s pensamos: <em>&#8220;Se com 50% de ocupa&#231;&#227;o a fila demora 1 minuto, com 100% de ocupa&#231;&#227;o deve demorar 2 minutos&#8221;</em>.</p><p><strong>Errado.</strong></p><p>A F&#243;rmula de Kingman (uma aproxima&#231;&#227;o para filas G/G/1) nos mostra que a rela&#231;&#227;o entre ocupa&#231;&#227;o e tempo de espera &#233; <strong>exponencial</strong>.</p><p>Sem entrar no c&#225;lculo complexo, a parte da f&#243;rmula que importa para n&#243;s &#233; o &#8220;Multiplicador de Utiliza&#231;&#227;o&#8221;:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\text{Tempo na Fila} \\approx \\left( \\frac{\\text{Utiliza&#231;&#227;o}}{1 - \\text{Utiliza&#231;&#227;o}} \\right)&quot;,&quot;id&quot;:&quot;ZKGQGBGAYH&quot;}" data-component-name="LatexBlockToDOM"></div><p>Vamos rodar os n&#250;meros desse multiplicador para ver o estrago:</p><ol><li><p><strong>50% de Utiliza&#231;&#227;o:</strong></p><ol><li><p>Espera &#233; &#8220;1x&#8221;. O sistema est&#225; saud&#225;vel.</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(\\rho = 0.5):\n\\frac{0.5}{0.5} = 1\n&quot;,&quot;id&quot;:&quot;CNNKWWBNCF&quot;}" data-component-name="LatexBlockToDOM"></div></li></ol></li><li><p><strong>80% de Utiliza&#231;&#227;o</strong></p><ol><li><p>A lat&#234;ncia <strong>quadruplicou</strong>, embora a carga n&#227;o tenha dobrado.</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(\\rho = 0.8):\n\\frac{0.8}{0.2} = 4&quot;,&quot;id&quot;:&quot;MLHXSVIJTW&quot;}" data-component-name="LatexBlockToDOM"></div></li></ol></li><li><p><strong>90% de Utiliza&#231;&#227;o</strong></p><ol><li><p>A lat&#234;ncia ficou <strong>9x</strong> maior</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(\\rho = 0.9):\n\\frac{0.9}{0.1} = 9&quot;,&quot;id&quot;:&quot;CUKBGHPXCI&quot;}" data-component-name="LatexBlockToDOM"></div></li></ol></li><li><p><strong>95% de Utiliza&#231;&#227;o</strong></p><ol><li><p>Lat&#234;ncia <strong>19x</strong> maior</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;(\\rho = 0.95):\n\\frac{0.95}{0.05} = 19&quot;,&quot;id&quot;:&quot;DBEITTJSIT&quot;}" data-component-name="LatexBlockToDOM"></div></li></ol></li></ol><p>Isso &#233; insano! Para 99% isso salta para 99x mais lat&#234;ncia, um verdadeiro pesadelo para a engenheria!</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3sPC!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3sPC!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!3sPC!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!3sPC!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!3sPC!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3sPC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:9520646,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3sPC!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!3sPC!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!3sPC!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!3sPC!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faea91457-7b68-4fda-b487-fc27e4b21871_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Esse ponto de inflex&#227;o (circulado em azul) &#233; o <strong>&#8220;Joelho da Curva&#8221;</strong>.</p><p>&#201; a mesma l&#243;gica do tr&#226;nsito na estrada:</p><ul><li><p>Com 50% de carros na pista, todos andam a 100km/h.</p></li><li><p>Com 70%, come&#231;a a ficar lento, mas anda.</p></li><li><p>Quando atinge 90% (satura&#231;&#227;o), <strong>um &#250;nico carro</strong> freando causa um engarrafamento quilom&#233;trico e a velocidade cai para 0km/h.</p></li></ul><h4>O Suic&#237;dio dos 100%</h4><p>Quando seu gerente pede &#8220;100% de CPU&#8221;, ele est&#225; matematicamente pedindo <strong>lat&#234;ncia infinita</strong>.</p><p>Em um sistema de filas (Kafka, RabbitMQ), operar acima do &#8220;Joelho da Curva&#8221; (geralmente 70-80%) significa que voc&#234; perdeu a capacidade de absorver a <strong>variabilidade</strong> (os <em>bursts</em> que falamos antes).</p><p>Se seus workers est&#227;o rodando a 90% de ocupa&#231;&#227;o constante:</p><ol><li><p>Qualquer micro-aumento de tr&#225;fego faz a fila crescer exponencialmente.</p></li><li><p>Qualquer solu&#231;o do sistema (um <em>Garbage Collector</em> do Java que para o mundo por 200ms) cria um backlog que o sistema <strong>nunca</strong> conseguir&#225; recuperar, pois n&#227;o h&#225; &#8220;gordura&#8221; (tempo ocioso) para processar o atraso.</p></li></ol><p><strong>A li&#231;&#227;o &#233; a seguinte:</strong> Aqueles 20-30% de CPU ociosa que o financeiro quer cortar n&#227;o s&#227;o desperd&#237;cio. Eles s&#227;o o seu <strong>seguro contra lat&#234;ncia exponencial</strong>. S&#227;o o espa&#231;o necess&#225;rio para que a fila possa esvaziar depois de uma rajada. Sem isso, seu sistema &#233; uma bomba-rel&#243;gio esperando o primeiro pico de tr&#225;fego para explodir.</p><h3>O &#8220;Dia do Fechamento&#8221; e a Fal&#225;cia dos 10%</h3><p>Vamos sair da lousa e ir para a &#8220;War Room&#8221; de uma Fintech fict&#237;cia com 10 milh&#245;es de clientes.</p><p>Imagine esse cen&#225;rio.</p><p>Todo dia 05 &#233; o &#8220;Dia do Fechamento&#8221;. O sistema Core Banking dispara um gatilho que gera o fechamento de faturas. Isso n&#227;o &#233; um fluxo constante; &#233; um canh&#227;o. Em uma janela de 4 horas, 10 milh&#245;es de eventos <code>InvoiceClosed</code> s&#227;o publicados no Kafka.</p><p><strong>A Arquitetura:</strong></p><ul><li><p><strong>Producer:</strong> Core Legacy (Mainframe) empurrando dados.</p></li><li><p><strong>Broker:</strong> Kafka (particionado corretamente).</p></li><li><p><strong>Consumer:</strong> Um microsservi&#231;o de &#8220;Fidelidade&#8221; que escuta esse t&#243;pico, calcula pontos e atualiza o saldo do usu&#225;rio no SQL Database.</p></li><li><p><strong>Capacidade (Service Rate - &#956;):</strong> Seus workers aguentam processar <strong>800 mensagens/segundo</strong> de forma est&#225;vel.</p></li></ul><p><strong>O Hist&#243;rico (Meses Anteriores):</strong></p><p>A taxa de chegada (&#955;) durante o pico do dia 05 costuma oscilar em torno de 600 msg/s.</p><p>Vamos aplicar a f&#243;rmula de utiliza&#231;&#227;o:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\rho = \\frac{600}{800} = 0.75 \\text{ (75% de Utiliza&#231;&#227;o)}&quot;,&quot;id&quot;:&quot;ENHHOHXTLV&quot;}" data-component-name="LatexBlockToDOM"></div><p>Segundo Kingman, em 75%, estamos antes do &#8220;joelho&#8221;. O sistema fica um pouco lento, a fila sobe um pouco, mas drena r&#225;pido. O time de infra comemora: <em>&#8220;O sistema &#233; robusto!&#8221;</em>.</p><h4>O Elemento Ca&#243;tico: A &#8220;Pequena&#8221; Mudan&#231;a</h4><p>Neste m&#234;s, o time de Produtos lan&#231;ou uma feature nova: &#8220;Cashback Turbinado&#8221;.</p><p>Agora, para cada fatura fechada, o consumer precisa fazer uma valida&#231;&#227;o extra em uma API externa de parceiros para ver se o cliente tem direito a b&#244;nus.</p><p>Os desenvolvedores mediram em Staging: essa chamada adiciona apenas 30ms ao processamento.</p><p>&#8220;&#201; s&#243; 30ms a mais, e o Kafka aguenta tudo, certo? Vamos subir.&#8221;</p><p>O que ningu&#233;m calculou foi que esses 30ms reduziram a capacidade de processamento dos workers (<strong>&#956;</strong>) de 800 msg/s para <strong>640 msg/s</strong>.</p><h4>O Desastre Matem&#225;tico</h4><p>Chega o dia 05. O canh&#227;o dispara. A entrada (&#955;) continua sendo 600 msg/s. Mas a capacidade (<strong>&#956;</strong>) caiu para 640 msg/s.</p><p>Nova Utiliza&#231;&#227;o:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\rho = \\frac{600}{640} = 0.9375 \\text{ (93.7% de Utiliza&#231;&#227;o)}&quot;,&quot;id&quot;:&quot;PSFZLSIDFZ&quot;}" data-component-name="LatexBlockToDOM"></div><p>Na cabe&#231;a do gerente: &#8220;Ainda estamos abaixo de 100%, vai funcionar.&#8221;</p><p>Na matem&#225;tica de Kingman, veja o multiplicador de espera:</p><ul><li><p><strong>M&#234;s passado (75%):</strong> </p><ul><li><p> Lat&#234;ncia gerenci&#225;vel.</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\frac{0.75}{0.25} = 3x&quot;,&quot;id&quot;:&quot;PCFERUTANQ&quot;}" data-component-name="LatexBlockToDOM"></div><p></p></li></ul></li><li><p><strong>Este m&#234;s (93.7%):</strong> </p><ul><li><p>A lat&#234;ncia mais que quadruplicou!</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\frac{0.93}{0.07} \\approx 13.2x&quot;,&quot;id&quot;:&quot;IPUVUBCGDF&quot;}" data-component-name="LatexBlockToDOM"></div><p></p></li></ul></li></ul><h4>O Efeito Cascata</h4><ol><li><p><strong>10:00 AM:</strong> O processamento come&#231;a. Como a utiliza&#231;&#227;o est&#225; em 93%, qualquer micro-varia&#231;&#227;o (rede, GC do Java) empurra a utiliza&#231;&#227;o para 99% ou 100% momentaneamente.</p></li><li><p><strong>10:15 AM:</strong> A fila do Kafka (Consumer Lag) come&#231;a a acumular. N&#227;o s&#227;o mais mil mensagens, s&#227;o milh&#245;es acumuladas.</p></li><li><p><strong>10:30 AM (O Colapso):</strong> Como o processamento est&#225; demorando 13x mais, ocorreram <em>timeouts</em> de banco de dados no Consumer.</p></li><li><p>A Tempestade de Retentativas (<a href="https://bytesizeddesign.substack.com/p/understanding-retry-storms-what-they">Retry Storm</a>): Aqui o sistema morre.</p><p>O consumer falha ao processar a mensagem devido ao timeout. O que ele faz? Devolve a mensagem para a fila (Nack/Retry).</p><p>Agora, sua taxa de chegada n&#227;o &#233; apenas as novas faturas. &#201; a soma de:</p><ol><li><p></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\lambda_{novas} + \\lambda_{retries}&quot;,&quot;id&quot;:&quot;ZLSXTPGSDK&quot;}" data-component-name="LatexBlockToDOM"></div><p>Sua utiliza&#231;&#227;o efetiva passa de 100%.</p></li></ol></li></ol><p><strong>Resultado</strong>: O Kafka continua aceitando mensagens (ele &#8220;aguenta tudo&#8221;), mas seus consumers est&#227;o travados. O atraso na atualiza&#231;&#227;o de pontos passa de &#8220;alguns segundos&#8221; para &#8220;5 horas&#8221;. O suporte come&#231;a a receber chamados de clientes que pagaram a fatura mas n&#227;o viram os pontos cair.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!vW-K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!vW-K!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!vW-K!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!vW-K!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!vW-K!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!vW-K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:11749774,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!vW-K!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!vW-K!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!vW-K!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!vW-K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8d6c57ff-7f41-4a3e-81a2-3c07b8d207b0_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>O que a matematica nos ensina?</h4><p>A matem&#225;tica previu esse desastre. A mudan&#231;a parecia pequena (apenas 10-15% de perda de performance), mas como o sistema j&#225; operava perto do &#8220;Joelho da Curva&#8221; (75%), esse empurr&#227;o foi suficiente para jog&#225;-lo na zona vertical da exponencial (93%), onde a lat&#234;ncia tende ao infinito e o <em>backlog</em> se torna irrecuper&#225;vel.</p><p>Se o time conhecesse Teoria das Filas, eles saberiam que <strong>n&#227;o se adiciona complexidade a um sistema que j&#225; roda a 75% de carga sem antes escalar a infraestrutura (aumentar &#956;) para trazer a utiliza&#231;&#227;o de volta para baixo do joelho.</strong></p><h3>A Lei de Little: A Calculadora de Capacidade que Voc&#234; Ignora</h3><p>Agora que sabemos que operar perto de 100% &#233; suic&#237;dio (gra&#231;as ao Kingman), surge a pergunta inevit&#225;vel: <em>&#8220;Ok, ent&#227;o quantos servidores ou consumers eu preciso exatamente para n&#227;o deixar meu sistema explodir?&#8221;</em></p><p>&#201; aqui que a maioria dos arquitetos recorre ao &#8220;chut&#244;metro&#8221;: <em>&#8220;Ah, coloca uns 10 pods no Kubernetes e v&#234; no que d&#225;.&#8221;</em></p><p>Pare. Existe uma f&#243;rmula para isso.</p><p>Ela se chama <em><strong>Lei de Little</strong></em> (<a href="https://corporatefinanceinstitute.com/resources/data-science/littles-law/">Little&#8217;s Law</a>) e &#233; surpreendentemente simples, mas brutalmente honesta.</p><h4>A F&#243;rmula M&#225;gica</h4><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;L = \\lambda W&quot;,&quot;id&quot;:&quot;NWPXPLZTKX&quot;}" data-component-name="LatexBlockToDOM"></div><p>Onde:</p><ul><li><p><strong>L (Length):</strong> O n&#250;mero m&#233;dio de itens dentro do sistema (Processando + Fila). Em termos de software: <strong>Concorr&#234;ncia Necess&#225;ria</strong>.</p></li><li><p><strong>Lambda:</strong> A taxa de chegada (Requests por segundo).</p></li><li><p><strong>W (Wait):</strong> O tempo m&#233;dio que uma requisi&#231;&#227;o leva para ser processada (Lat&#234;ncia).</p></li></ul><p>Parece abstrato? Vamos trazer para a sua realidade.</p><h4>Um Exerc&#237;cio de Racioc&#237;nio</h4><p>Imagine que voc&#234; tem um worker consumindo mensagens do RabbitMQ.</p><p>Vamos aos fatos do seu sistema:</p><ol><li><p><strong>Fato 1:</strong> Seu sistema recebe <strong>100 requisi&#231;&#245;es por segundo</strong> constantes.</p></li><li><p><strong>Fato 2:</strong> Seu c&#243;digo &#233; razoavelmente otimizado e leva <strong>200 milissegundos (0.2s)</strong> para processar cada mensagem (fazer o parse, salvar no banco e dar <em>ack</em>).</p></li></ol><p>A pergunta &#233;: <strong>Quantas threads ou conex&#245;es simult&#226;neas voc&#234; precisa para manter o sistema est&#225;vel (sem a fila crescer)?</strong></p><p>N&#227;o chute. Calcule.</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;L = 100 \\text{ req/s} \\times 0.2 \\text{}\n\nL = 20&quot;,&quot;id&quot;:&quot;DYSDHMSEGI&quot;}" data-component-name="LatexBlockToDOM"></div><p><strong>Resposta:</strong> Voc&#234; precisa, matematicamente, de capacidade para processar <strong>20 mensagens simultaneamente</strong> o tempo todo.</p><h4>Por que isso muda o jogo?</h4><p>Agora, vamos aplicar a realidade da engenharia de software nesse n&#250;mero &#8220;20&#8221;.</p><p><strong>Cen&#225;rio A: O Programador Otimista</strong></p><p>Voc&#234; configurou seu consumer para rodar com 10 threads (ou 10 pods single-threaded), porque &#8220;10 parece um n&#250;mero bom&#8221;.</p><p>O que acontece? A Lei de Little exige 20. Voc&#234; tem 10.</p><p>A cada segundo, 100 chegam, mas voc&#234; s&#243; consegue dar vaz&#227;o a 50 (10 threads / 0.2s).</p><p><strong>Saldo</strong>: 50 mensagens acumulam na fila por segundo. Em um minuto, voc&#234; tem 3.000 mensagens de lag. Em uma hora, 180.000. <a href="https://docs.aws.amazon.com/eks/latest/best-practices/windows-oom.html">O Out of Memory (OOM)</a> &#233; inevit&#225;vel.</p><p><strong>Cen&#225;rio B: O Programador que Entende a Lei</strong></p><p>Voc&#234; sabe que o n&#250;mero m&#225;gico &#233; 20. Mas voc&#234; se lembra do &#8220;Joelho da Curva&#8221; (n&#227;o queremos rodar a 100%). Ent&#227;o, voc&#234; aplica uma margem de seguran&#231;a de 30%.</p><p>Voc&#234; provisiona 26 a 30 threads.</p><p>Resultado: O sistema roda suave, a fila fica zerada e, se houver um pequeno pico, voc&#234; tem gordura para queimar.</p><h4>A Dura Verdade sobre &#8220;Otimizar C&#243;digo&#8221;</h4><p>A Lei de Little nos ensina uma li&#231;&#227;o de humildade sobre performance.</p><p>Muitas vezes, tentamos resolver problemas de fila acumulada mexendo no c&#243;digo.</p><p><em>&#8220;Vou otimizar essa query para ganhar 10ms!&#8221;</em></p><p>Se o seu d&#233;ficit de concorr&#234;ncia for grande (ex: voc&#234; tem 10 threads mas precisa de 50), ganhar 10ms no tempo de processamento &#233; como tentar esvaziar o oceano com um balde.</p><p>&#192;s vezes, a solu&#231;&#227;o n&#227;o &#233; c&#243;digo (reduzir a latencia). A solu&#231;&#227;o &#233; infraestrutura (aumentar a capacidade para atingir a <strong>concorr&#234;ncia necess&#225;ria</strong>).</p><p><em><strong>Ent&#227;o o que podemos aprender?</strong></em></p><p>Devemos considerar a Lei de Little quando desenhamos uma arquitetura? Absolutamente.</p><p>Ela transforma o <a href="https://www.cncf.io/blog/2019/10/08/kubernetes-patterns-capacity-planning/">Capacity Planning</a> de uma arte obscura de adivinha&#231;&#227;o em uma ci&#234;ncia exata. Antes de subir qualquer sistema para produ&#231;&#227;o, fa&#231;a a conta: </p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;\\lambda \\times W&quot;,&quot;id&quot;:&quot;GJNXNZYCAA&quot;}" data-component-name="LatexBlockToDOM"></div><p>Se a sua infraestrutura n&#227;o suportar o resultado dessa multiplica&#231;&#227;o, seu sistema j&#225; nasceu morto.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!7vbB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!7vbB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!7vbB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!7vbB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!7vbB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!7vbB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/aa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:11148011,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!7vbB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!7vbB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!7vbB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!7vbB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Faa1707a8-416d-433d-bcc5-24f321eeea1e_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h2>Bufferbloat: Por que sua DLQ n&#227;o vai te salvar</h2><p>Se perguntarmos para qualquer desenvolvedor como ele lida com falhas em sistemas de mensageria, a resposta padr&#227;o &#233;: <em>&#8220;Temos uma Dead Letter Queue (DLQ) configurada. Se der erro, reprocessamos.&#8221;</em></p><p>Isso &#233; &#243;timo para erros de sintaxe ou falhas l&#243;gicas. Mas a DLQ &#233; in&#250;til contra o maior inimigo da performance em alta escala: o <strong><a href="https://queue.acm.org/detail.cfm?id=2071893">Bufferbloat</a></strong>.</p><p>Enquanto a DLQ cuida das mensagens que &#8220;morreram&#8221; (deram exce&#231;&#227;o), o Bufferbloat cria um ex&#233;rcito de <strong>&#8220;Mensagens Zumbis&#8221;</strong>: requisi&#231;&#245;es que est&#227;o tecnicamente vivas na fila, consumindo recursos, mas que j&#225; n&#227;o servem para mais nada.</p><h4>O Problema das Filas Infinitas (Unbounded Queues)</h4><p>A maioria das bibliotecas de clientes (seja em Java, Node ou Python) e configura&#231;&#245;es padr&#227;o de brokers (como RabbitMQ cl&#225;ssico) tendem a tratar filas como infinitas. A premissa ing&#234;nua &#233;: <em>&#8220;Se o consumidor est&#225; lento, guarde tudo na mem&#243;ria. N&#227;o rejeite nada. Eventualmente processaremos.&#8221;</em></p><p>O artigo cl&#225;ssico <em>&#8220;<a href="https://www.rabbitmq.com/blog/2014/01/23/preventing-unbounded-buffers-with-rabbitmq">Preventing Unbounded Buffers with RabbitMQ&#8221;</a></em> nos alerta sobre a f&#237;sica b&#225;sica desse problema: <strong>Producers quase sempre s&#227;o mais r&#225;pidos que Consumers.</strong> Se voc&#234; tem um producer injetando 1.000 req/s e um consumer drenando 900 req/s, essa diferen&#231;a n&#227;o desaparece. Ela se acumula na RAM.</p><p>Vamos aplicar a l&#243;gica matem&#225;tica para ver onde isso quebra.</p><h4>A Matem&#225;tica do &#8220;Trabalho Zumbi&#8221;</h4><p>Imagine o cen&#225;rio de um pico de tr&#225;fego (<em>burst</em>):</p><ol><li><p>Sua fila acumulou <strong>5.000 mensagens</strong>.</p></li><li><p>Seu worker leva <strong>100ms</strong> para processar cada uma (persist&#234;ncia, regras de neg&#243;cio).</p></li><li><p><strong>C&#225;lculo de Lat&#234;ncia na Cauda:</strong></p><ol><li><p></p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;5.000 \\text{ msgs} \\times 0.1s = 500 \\text{ segundos}&quot;,&quot;id&quot;:&quot;PJTFIKDJBH&quot;}" data-component-name="LatexBlockToDOM"></div></li></ol></li></ol><p>Isso significa que a &#250;ltima mensagem da fila vai esperar <strong>mais de 8 minutos</strong> para ser atendida.</p><p>Agora, olhe para o contrato com seu cliente (SLA ou Timeout):</p><ul><li><p>O aplicativo m&#243;vel do usu&#225;rio tem um <strong>timeout de 10 segundos</strong>.</p></li><li><p>O gateway de pagamento espera resposta em <strong>30 segundos</strong>.</p></li><li><p>A paci&#234;ncia humana dura <strong>1 minuto</strong>.</p></li></ul><p>O Resultado L&#243;gico:</p><p>Quando seu servidor pegar a mensagem n&#186; 5.000 (daqui a 8 minutos), o cliente j&#225; recebeu um TimeOut Exception, j&#225; fechou o app ou j&#225; clicou em &#8220;Tentar Novamente&#8221; (criando duplicidade).</p><p>Mas o servidor n&#227;o sabe disso. Ele vai gastar CPU, mem&#243;ria e I/O de banco de dados para processar uma requisi&#231;&#227;o cujo &#8220;dono&#8221; j&#225; foi embora.</p><p>Isso &#233; desperd&#237;cio puro. Em engenharia de performance, chamamos isso de queda de <strong>Goodput</strong> (vaz&#227;o &#250;til). Seu servidor est&#225; trabalhando a 100% de carga, mas entregando 0% de valor.</p><h4>Por que isso acontece em sistemas Ass&#237;ncronos?</h4><p>Aqui reside a confus&#227;o comum: <em>&#8220;Mas se &#233; ass&#237;ncrono, por que o tempo importa?&#8221;</em></p><p>O assincronismo desacopla a conex&#227;o (o c&#243;digo n&#227;o trava), mas n&#227;o elimina a expectativa temporal.</p><p>A informa&#231;&#227;o tem prazo de validade.</p><ul><li><p>Se o e-mail de &#8220;Redefinir Senha&#8221; demora 10 minutos por causa da fila, o usu&#225;rio j&#225; solicitou outro token, invalidando o anterior.</p></li></ul><p>Filas infinitas transformam problemas de <strong>capacidade</strong> (falta de hardware) em problemas de <strong>lat&#234;ncia</strong> (demora infinita), o que &#233; muito pior para o neg&#243;cio.</p><h4>Backpressure e Load Shedding</h4><p>Para resolver isso, precisamos parar de ter medo de rejeitar trabalho. Engenharia robusta exige aplicar <strong>Load Shedding</strong> (Derrubar Carga) e <strong>Backpressure</strong> (Contrapress&#227;o).</p><p><strong>1. Limite o Buffer (Bounded Queues)</strong></p><p>Baseado no artigo do RabbitMQ, podemos  limitar o tamanho da fila com base no tempo m&#225;ximo toler&#225;vel.</p><p>Se seu timeout &#233; 5s e voc&#234; processa em 0.1s:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;Capacidade M&#225;xima = \\frac{5}{0.1} = 50&quot;,&quot;id&quot;:&quot;IQTPTIXCUQ&quot;}" data-component-name="LatexBlockToDOM"></div><p>Configure sua fila (x-max-length no RabbitMQ ou buffer channel no Go) para 50.</p><p>Se chegar a 51&#170; mensagem: Rejeite na entrada.</p><p>&#201; melhor retornar um erro 503 Service Unavailable em 10 milissegundos (permitindo que o cliente tente outro servidor ou mostre um aviso amig&#225;vel) do que aceitar a requisi&#231;&#227;o e deix&#225;-la apodrecer por 8 minutos.</p><p><strong>2. Use QoS e Prefetch (Backpressure)</strong></p><p>No lado do consumidor, nunca use configura&#231;&#245;es que &#8220;puxam tudo&#8221; (unlimited prefetch). Isso transfere o buffer do Broker para a RAM da aplica&#231;&#227;o, causando Out of Memory.</p><p>Configure o <a href="https://www.rabbitmq.com/docs/consumer-prefetch">basic.qos</a> (prefetch count) para um n&#250;mero pequeno (ex: 10 ou 20). Isso sinaliza ao RabbitMQ: &#8220;S&#243; me mande mais trabalho quando eu terminar esse&#8221;. Isso mant&#233;m a fila no disco do Broker, n&#227;o na mem&#243;ria vol&#225;til da aplica&#231;&#227;o.</p><p><strong>3. Time-To-Live na Mensagem</strong></p><p>Uma defesa final contra zumbis &#233; configurar um <a href="https://www.rabbitmq.com/docs/ttl">TTL</a> nas mensagens. Se a mensagem ficar na fila por mais de 10 segundos (seu timeout de neg&#243;cio), o pr&#243;prio Broker a descarta ou envia para <a href="https://aws.amazon.com/what-is/dead-letter-queue/">DLQ</a>.</p><p>Assim, seu consumidor nunca desperdi&#231;a ciclos de CPU processando dados expirados.</p><p>Filas n&#227;o s&#227;o &#8220;armazenamento infinito gratuito&#8221;. <strong>Elas s&#227;o amortecedores de choque tempor&#225;rios.</strong> Permitir que elas cres&#231;am indefinidamente &#233; uma falha de design que garante lat&#234;ncia alta e desperd&#237;cio de recursos. A verdadeira resili&#234;ncia vem de saber processar o que &#233; poss&#237;vel e rejeitar o que n&#227;o &#233;, mantendo o sistema responsivo para quem ainda pode ser atendido.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><h2>Head-of-Line Blocking</h2><p>Se voc&#234; j&#225; foi ao supermercado comprar apenas uma garrafa de &#225;gua e acabou preso atr&#225;s de algu&#233;m fazendo a compra do m&#234;s com dois carrinhos cheios, voc&#234; j&#225; sofreu de <strong>Head-of-Line (HoL) Blocking</strong>.</p><p>Em arquitetura de eventos, n&#243;s amamos filas FIFO (First-In, First-Out - O primeiro que entra &#233; o primeiro que sai). &#201; justo, &#233; previs&#237;vel.</p><p>Mas o <a href="https://aws.amazon.com/pt/blogs/compute/solving-complex-ordering-challenges-with-amazon-sqs-fifo-queues/">FIFO</a> tem uma falha fatal: ele assume que todas as requisi&#231;&#245;es consomem o mesmo tempo.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!jK_o!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!jK_o!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!jK_o!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!jK_o!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!jK_o!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!jK_o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/bc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5546233,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!jK_o!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!jK_o!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!jK_o!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!jK_o!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbc971dfb-60d2-4613-9c07-9a48f9d4f98c_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>O Problema na Pr&#225;tica</h4><p>Imagine que voc&#234; tem um t&#243;pico no Kafka chamado processamento-pagamentos.</p><p>L&#225; dentro, voc&#234; mistura dois tipos de eventos:</p><ol><li><p><strong>Pagamento Pix (Tipo A):</strong> R&#225;pido. Leva <strong>50ms</strong> para processar.</p></li><li><p><strong>Fechamento de Relat&#243;rio Cont&#225;bil (Tipo B):</strong> Pesado. Leva <strong>5 segundos</strong> para processar.</p></li></ol><p>Se um lote de 100 &#8220;Relat&#243;rios&#8221; entrar na fila, e logo atr&#225;s vier um &#8220;Pix&#8221;, o Pix vai ter que esperar 500 segundos (100 $\times$ 5s) apenas para chegar a vez dele.</p><p>Para o usu&#225;rio do Pix, o sistema travou.</p><p>Isso &#233; o Bloqueio da Cabe&#231;a da Fila. As tarefas r&#225;pidas est&#227;o ref&#233;ns das tarefas lentas que est&#227;o na frente (&#8221;Head&#8221;).</p><p>Na engenharia, chamamos essa tarefa lenta de <strong>&#8220;<a href="https://docs.aws.amazon.com/wellarchitected/latest/saas-lens/noisy-neighbor.html">Noisy Neighbor</a>&#8221; (Vizinho Barulhento)</strong>. Ele monopoliza o worker e degrada a performance de todo mundo que mora na mesma fila.</p><h4>Sharding</h4><p>Como o supermercado resolve isso? Caixa R&#225;pido (at&#233; 10 volumes).</p><p>Na arquitetura, fazemos o mesmo. N&#227;o misture cargas heterog&#234;neas no mesmo t&#243;pico.</p><p>Se voc&#234; tem processos leves e pesados, crie filas distintas:</p><ul><li><p><code>pagamentos-fast-lane</code>: S&#243; para Pix e Cart&#227;o.</p></li><li><p><code>relatorios-slow-lane</code>: S&#243; para batch e arquivos pesados.</p></li></ul><p>Aloque workers dedicados para cada fila. Assim, se o sistema de relat&#243;rios engasgar, o Pix continua voando. Isso &#233; <strong>Bulkheading</strong> (compartimenta&#231;&#227;o) aplicado a filas.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!1O7D!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!1O7D!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!1O7D!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!1O7D!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!1O7D!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!1O7D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6190186,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!1O7D!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!1O7D!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!1O7D!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!1O7D!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F84d9443d-3230-48c1-bd41-a809506d1413_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>O Efeito &#8220;Thundering Herd&#8221;: Por que consertar o servidor pode derrub&#225;-lo de novo</h3><p>Chegamos ao pesadelo final da arquitetura distribu&#237;da. &#201; aquele momento em que voc&#234; resolve o problema raiz, mas, paradoxalmente, essa solu&#231;&#227;o causa um colapso ainda maior.</p><p>Imagine a cena cl&#225;ssica de &#8220;Sexta-feira &#224; noite&#8221;: O banco de dados principal caiu. O DBA est&#225; trabalhando freneticamente para reinici&#225;-lo. Enquanto isso, sua arquitetura orientada a eventos est&#225; fazendo exatamente o que foi projetada para fazer: <strong>resili&#234;ncia</strong>. Os <em>Producers</em> continuam vendendo, os usu&#225;rios continuam clicando, e o Kafka/RabbitMQ est&#225; absorvendo esse impacto, acumulando mensagens.</p><p>A fila, que normalmente opera com 100 mensagens (quase <em>real-time</em>), agora tem <strong>500.000 mensagens</strong> represadas. Seus 50 microsservi&#231;os (<em>Consumers</em>) est&#227;o rodando, mas estagnados. Eles pegam uma mensagem, tentam conectar no banco, recebem <em>timeout</em>, devolvem a mensagem para a fila (Nack) e tentam de novo.</p><p>Finalmente, no Slack, algu&#233;m digita a frase fatal:</p><blockquote><p><em>&#8220;O Banco reiniciou! Podem liberar o tr&#225;fego!&#8221;</em></p></blockquote><p>Dois segundos depois, o banco cai novamente. Desta vez, o travamento &#233; t&#227;o severo que o servidor nem responde ao SSH. Voc&#234; acabou de ser atropelado pelo <strong><a href="https://distributed-computing-musings.com/2025/08/thundering-herd-problem-preventing-the-stampede/">Efeito Manada (Thundering Herd Problem)</a></strong><a href="https://distributed-computing-musings.com/2025/08/thundering-herd-problem-preventing-the-stampede/">.</a></p><h4>A F&#237;sica do Colapso: Por que o Banco &#8220;Frio&#8221; &#233; Fr&#225;gil?</h4><p>Para entender por que isso acontece, precisamos analisar o estado f&#237;sico do seu banco de dados no momento do <em>reboot</em>.</p><p>Um banco de dados em opera&#231;&#227;o normal &#233; &#8220;quente&#8221;. Suas p&#225;ginas de dados mais acessadas est&#227;o na mem&#243;ria RAM (Buffer Pool), seus planos de execu&#231;&#227;o de query est&#227;o cacheados e o pool de conex&#245;es est&#225; est&#225;vel. Um banco que acabou de reiniciar &#233; <strong>&#8220;frio&#8221;</strong>.</p><ol><li><p><strong>Caches Vazios:</strong> Toda query precisa ir buscar dados no disco f&#237;sico (I/O), que &#233; 100x mais lento que a RAM.</p></li><li><p><strong>Overhead de Conex&#227;o:</strong> Aceitar uma nova conex&#227;o TCP e autenticar um usu&#225;rio custa CPU.</p></li></ol><p>No exato milissegundo em que o banco volta online, seus 50 consumers (que estavam em <em>loop</em> de retry) percebem simultaneamente que a porta 5432 abriu. Eles olham para a fila de 500.000 mensagens e entram em modo de p&#226;nico: <em>&#8220;Tenho trabalho atrasado!&#8221;</em>.</p><p>Todos os 50 consumers, rodando talvez 20 threads cada (1.000 threads no total), tentam abrir conex&#245;es e disparar <code>INSERTs</code> complexos ao mesmo tempo. O banco, que ainda est&#225; tentando carregar o sistema operacional, recebe um golpe de ar&#237;ete de 1.000 requisi&#231;&#245;es simult&#226;neas exigindo I/O de disco. A CPU bate 100% instantaneamente, os <em>locks</em> de tabela travam tudo e o banco morre sufocado antes mesmo de processar a primeira query.</p><h4>A Solu&#231;&#227;o 1: &#8220;Slow Start&#8221;</h4><p>Para evitar isso, precisamos impedir que os consumers operem em capacidade m&#225;xima imediatamente ap&#243;s uma falha. Precisamos de um &#8220;aquecimento&#8221;.</p><p>Na engenharia de redes, existe um algoritmo famoso no protocolo TCP/IP chamado <strong><a href="https://developer.mozilla.org/en-US/docs/Glossary/TCP_slow_start">TCP Slow Start</a></strong> (Partida Lenta). Quando seu computador conecta no Google, ele n&#227;o sabe a velocidade real da sua internet. Se ele enviar todos os pacotes de uma vez, ele pode congestionar a rede e perder tudo. <strong>Como o TCP resolve:</strong> Ele envia 1 pacote. Se chegar o <em>Ack</em> (confirma&#231;&#227;o), ele envia 2. Se chegarem, envia 4, depois 8, crescendo exponencialmente at&#233; encontrar o limite da banda.</p><p><strong>Aplicando aos seus Consumers:</strong> Devemos roubar essa ideia. Quando seu sistema detecta que o banco voltou (ap&#243;s um per&#237;odo de erro), o consumer <strong>n&#227;o</strong> deve processar o <em>prefetch</em> m&#225;ximo (ex: 50 msgs). Ele deve entrar em modo <strong>Slow Start</strong>:</p><ol><li><p><strong>Fase 1:</strong> Processe apenas 1 mensagem. Espere 1 segundo. Sucesso?</p></li><li><p><strong>Fase 2 :</strong> Processe 5 mensagens. Sucesso?</p></li><li><p><strong>Fase 3:</strong> Volte para a capacidade total.</p></li></ol><p>Isso d&#225; ao banco de dados os preciosos segundos necess&#225;rios para popular o cache da mem&#243;ria RAM e estabilizar as conex&#245;es, transformando um &#8220;tsunami&#8221; em uma &#8220;mar&#233; que sobe devagar&#8221;.</p><h4>A Solu&#231;&#227;o 2: Jitter</h4><p>O segundo problema &#233; a <strong>Sincronia Artificial</strong>. Computadores s&#227;o rel&#243;gios perfeitos. Se voc&#234; programou seus consumers para tentar reconectar a cada 5 segundos (<code>retry_interval = 5000ms</code>), o que acontece?</p><ul><li><p>Consumer A bate no banco &#224;s 10:00:05.000</p></li><li><p>Consumer B bate no banco &#224;s 10:00:05.000</p></li><li><p>Consumer C bate no banco &#224;s 10:00:05.000</p></li></ul><p>Eles formam uma onda de choque peri&#243;dica. Mesmo que o banco aguente a m&#233;dia, ele n&#227;o aguenta o pico sincronizado.</p><p>Para quebrar essa sincronia, introduzimos o <strong><a href="https://developer.mozilla.org/en-US/docs/Glossary/Jitter">Jitter</a></strong> (Ru&#237;do/Aleatoriedade). Nunca use tempos fixos em sistemas distribu&#237;dos. A f&#243;rmula de espera deve ser:</p><div class="latex-rendered" data-attrs="{&quot;persistentExpression&quot;:&quot;Wait = \\text{BaseInterval} + \\text{Random}(0, \\text{JitterRange})&quot;,&quot;id&quot;:&quot;MSVOPUGVZZ&quot;}" data-component-name="LatexBlockToDOM"></div><p>Exemplo: <code>5000ms + Random(0, 1000ms)</code>.</p><ul><li><p>Consumer A tenta em 5.1s</p></li><li><p>Consumer B tenta em 5.4s</p></li><li><p>Consumer C tenta em 5.8s</p></li></ul><p>Parece uma mudan&#231;a boba, mas matematicamente, isso &#8220;espalha&#8221; a carga ao longo do tempo. Em vez de 1.000 marteladas num &#250;nico milissegundo, voc&#234; tem uma &#8220;chuva&#8221; constante de requisi&#231;&#245;es distribu&#237;das ao longo de um segundo inteiro.</p><p>O Efeito &#8220;<a href="https://www.youtube.com/watch?v=pFBCgFzS2W8">Thundering Herd</a>&#8221; nos ensina que recuperar um sistema &#233; t&#227;o perigoso quanto a queda dele. Se seus consumidores s&#227;o gananciosos e tentam limpar a fila o mais r&#225;pido poss&#237;vel assim que a depend&#234;ncia volta, eles agem como um ataque DDoS n&#227;o intencional (Friendly Fire).</p><div id="youtube2-pFBCgFzS2W8" class="youtube-wrap" data-attrs="{&quot;videoId&quot;:&quot;pFBCgFzS2W8&quot;,&quot;startTime&quot;:null,&quot;endTime&quot;:null}" data-component-name="Youtube2ToDOM"><div class="youtube-inner"><iframe src="https://www.youtube-nocookie.com/embed/pFBCgFzS2W8?rel=0&amp;autoplay=0&amp;showinfo=0&amp;enablejsapi=0" frameborder="0" loading="lazy" gesture="media" allow="autoplay; fullscreen" allowautoplay="true" allowfullscreen="true" width="728" height="409"></iframe></div></div><p>Implementar <strong>Slow Start</strong> e <strong>Jitter</strong> n&#227;o &#233; sobre deixar o sistema lento; &#233; sobre modera&#231;&#227;o estrat&#233;gica. &#192;s vezes, para ir r&#225;pido (limpar a fila), voc&#234; precisa, obrigatoriamente, come&#231;ar devagar.</p><h3>Como implementar Jitter e Slow Start na vida real</h3><p>Falar de &#8220;algoritmos de TCP&#8221; &#233; fascinante, mas voc&#234; n&#227;o vai reescrever o kernel do Linux dentro do seu microsservi&#231;o Java ou Node.js. Como trazemos essa f&#237;sica para a nossa arquitetura de eventos de forma pragm&#225;tica?</p><h4>Caminho 1: O &#8220;Jitter&#8221; est&#225; nas Bibliotecas de Resili&#234;ncia (N&#227;o fa&#231;a na m&#227;o!)</h4><p>Voc&#234; n&#227;o precisa escrever <code>Thread.sleep(Random.Next())</code>. Isso &#233; amador e dif&#237;cil de testar. A ind&#250;stria j&#225; resolveu isso com o padr&#227;o <strong>Exponential Backoff with Jitter</strong>.</p><p>A maioria das linguagens modernas possui bibliotecas de resili&#234;ncia robustas que j&#225; trazem isso pronto. Seu trabalho &#233; <strong>configurar</strong>, n&#227;o codar.</p><ul><li><p><strong>No mundo .NET:</strong> Use o <strong>Polly</strong>. Ele tem uma pol&#237;tica nativa: <code>WaitAndRetryAsync</code> com <code>BackoffType.Exponential</code> e, crucialmente, uma contribui&#231;&#227;o de <code>Jitter</code>.</p></li><li><p><strong>No mundo Java:</strong> Use o <strong>Resilience4j</strong>. Configure o <code>IntervalFunction.ofExponentialRandomBackoff()</code>.</p></li><li><p><strong>No mundo Python:</strong> Use o <strong>Tenacity</strong>. Configure <code>wait=wait_random_exponential(multiplier=1, max=60)</code>.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!iJuO!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!iJuO!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!iJuO!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!iJuO!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!iJuO!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!iJuO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/b8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6271362,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!iJuO!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!iJuO!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!iJuO!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!iJuO!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fb8feacdb-1fdf-4744-94e0-7af3b06d2af9_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li></ul><p><strong>O que acontece na pr&#225;tica:</strong> Quando o banco falha, a biblioteca assume o controle. Ela calcula matematicamente: &#8220;Vou tentar em 2.1s... falhou. Agora em 4.3s... falhou. Agora em 8.7s&#8221;. Isso garante o espalhamento da manada sem voc&#234; escrever uma linha de l&#243;gica matem&#225;tica.</p><h4>Caminho 2: Implementando &#8220;Slow Start&#8221; com Circuit Breaker (O Estado &#8220;Half-Open&#8221;)</h4><p>O padr&#227;o <strong>Circuit Breaker</strong> (Disjuntor) &#233; a implementa&#231;&#227;o mais realista do conceito de Slow Start para 90% das aplica&#231;&#245;es.</p><p>Imagine o Circuit Breaker como um interruptor inteligente entre seu Consumer e o Banco de Dados.</p><ol><li><p><strong>Estado Fechado (Closed):</strong> Tudo normal. O tr&#225;fego passa.</p></li><li><p><strong>Estado Aberto (Open):</strong> O banco caiu. O Disjuntor &#8220;abre&#8221; e corta a energia. O Consumer para de tentar bater no banco instantaneamente (fail fast).</p></li><li><p><strong>O Pulo do Gato: Estado Meio-Aberto (Half-Open):</strong> Ap&#243;s um tempo (ex: 30s), o disjuntor n&#227;o fecha de vez. Ele entra em modo <strong>&#8220;Meio-Aberto&#8221;</strong>. Ele permite que <strong>apenas uma requisi&#231;&#227;o</strong> (ou um n&#250;mero muito pequeno) passe. &#201; a &#8220;Sonda&#8221;.</p><ul><li><p>Se essa requisi&#231;&#227;o falhar: Ele abre de novo imediatamente.</p></li><li><p>Se essa requisi&#231;&#227;o tiver sucesso: Ele fecha e libera o fluxo total.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3Lx7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3Lx7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!3Lx7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!3Lx7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!3Lx7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3Lx7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6093935,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3Lx7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!3Lx7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!3Lx7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!3Lx7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdc06dbb-0e24-4b4a-801a-0c0913478448_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li></ul></li></ol><p><strong>Por que isso resolve?</strong> O estado <em>Half-Open</em> <strong>&#233;</strong> o Slow Start. Ele impede que as 1.000 threads tentem reconectar simultaneamente. Apenas uma &#8220;cobaia&#8221; testa o banco. Se o banco ainda estiver frio ou caindo, s&#243; uma requisi&#231;&#227;o falha, n&#227;o a manada inteira.</p><h4>Caminho 3: Slow Start via Controle de Vaz&#227;o (Reinicializa&#231;&#227;o Estrat&#233;gica)</h4><p>Se sua biblioteca n&#227;o permite mudar o <code>prefetch</code> com o carro andando (a maioria exige reinicializar o consumidor), use a estrat&#233;gia de <strong>Pause &amp; Resume</strong>.</p><ol><li><p><strong>Detector de P&#226;nico:</strong> Ao capturar exce&#231;&#245;es consecutivas do banco, seu c&#243;digo pausa o <em>Listener</em> (ex: <code>listener.stop()</code> no Spring ou <code>consumer.pause()</code> no Kafka).</p></li><li><p><strong>Reconfigura&#231;&#227;o:</strong> Altere a configura&#231;&#227;o do prefetch para <strong>1</strong> na mem&#243;ria.</p></li><li><p><strong>Religar:</strong> Reinicie o <em>Listener</em>. Ele voltar&#225; consumindo 1 mensagem por vez.</p></li><li><p><strong>Rampa:</strong> Conforme os erros somem, reinicie o listener novamente com prefetch 10, depois 50.</p></li></ol><p>Embora pare&#231;a agressivo &#8220;parar e voltar&#8221;, isso &#233; infinitamente melhor do que deixar o consumidor bater no banco 1.000 vezes por segundo.</p><h4>Kubernetes e Auto-Scaling</h4><p>Cuidado com o &#8220;Fogo Amigo&#8221; da infraestrutura. Se voc&#234; usa <strong><a href="https://kubernetes.io/docs/concepts/workloads/autoscaling/">KEDA</a></strong><a href="https://kubernetes.io/docs/concepts/workloads/autoscaling/"> (Kubernetes Event-driven Autoscaling</a>), ele vai ver a fila de 500.000 mensagens e tentar subir 100 Pods de uma vez. Isso &#233; o oposto de Slow Start.</p><p><strong>A Solu&#231;&#227;o:</strong> Configure janelas de estabiliza&#231;&#227;o (<em>stabilization windows</em>) no HPA (Horizontal Pod Autoscaler) ou limites de velocidade de <em>scale-up</em>. Obrigue o Kubernetes a subir novos pods devagar, dando tempo para o banco &#8220;respirar&#8221; antes de ter 100 inst&#226;ncias novas atacando ele.</p><h3>Resumo</h3><p>N&#227;o tente reinventar a matem&#225;tica.</p><ol><li><p>Use <strong>Bibliotecas de Resili&#234;ncia</strong> para o Jitter (Polly, Resilience4j).</p></li><li><p>Use <strong>Circuit Breakers</strong> para o controle de fluxo &#8220;Tudo ou Nada&#8221; com seguran&#231;a (Half-Open).</p></li><li><p>Use <strong>Controle de Prefetch</strong> se precisar de uma rampa suave e personalizada.</p></li></ol><p>Implementar isso &#233; a diferen&#231;a entre um sistema que cai e volta sozinho, e um sistema que precisa ser reiniciado manualmente &#224;s 3 da manh&#227; porque entrou em loop de autodestrui&#231;&#227;o.</p><div><hr></div><h3>A Grande Ilus&#227;o: Desacoplamento Espacial vs. Acoplamento Temporal</h3><p>Antes de encerrarmos, precisamos destruir o mito mais perigoso sobre Arquitetura Orientada a Eventos: <strong>A ideia de que filas resolvem todos os problemas de depend&#234;ncia.</strong></p><p>Quando voc&#234; troca uma chamada HTTP s&#237;ncrona (REST) por uma mensagem ass&#237;ncrona (Kafka/RabbitMQ), voc&#234; ganha <strong>Desacoplamento Espacial</strong>.</p><ul><li><p><em>Significado:</em> O servi&#231;o A n&#227;o precisa saber o IP do servi&#231;o B, nem se o servi&#231;o B est&#225; online naquele exato segundo. Se o B cair, o A continua aceitando pedidos. Isso &#233; &#243;timo.</p></li></ul><p>Mas o que os vendedores de cursos esquecem de avisar &#233; que voc&#234; acabou de criar um <strong>Acoplamento Temporal</strong>.</p><h4>O Que &#233; Acoplamento Temporal?</h4><p>Mesmo que os servi&#231;os n&#227;o estejam conectados por um cabo de rede direto, eles est&#227;o conectados pelo <strong>fluxo de dados</strong>. Se o seu <em>Producer</em> (Vendas) gera 1.000 eventos por segundo, e o seu <em>Consumer</em> (Nota Fiscal) s&#243; processa 500, o sistema est&#225; <strong>logicamente quebrado</strong>.</p><ul><li><p>O cliente comprou, mas a nota n&#227;o sai.</p></li><li><p>O produto saiu do estoque, mas o site ainda mostra dispon&#237;vel.</p></li></ul><p>Voc&#234; trocou um &#8220;Erro 500&#8221; (que &#233; vis&#237;vel e barulhento) por um &#8220;Lag de 4 horas&#8221; (que &#233; silencioso e mortal).</p><h4>Os Gargalos Silenciosos</h4><p>Em REST, se o sistema fica lento, o cliente reclama na hora (<em>Lat&#234;ncia alta</em>). Em Eventos, se o sistema fica lento, a fila absorve o impacto silenciosamente (<em>Lag</em>). O gerente olha o dashboard: &#8220;CPU ok, Mem&#243;ria ok, Zero erros&#8221;. Enquanto isso, o neg&#243;cio est&#225; sangrando. Dados est&#227;o ficando obsoletos (Stale Data) e decis&#245;es est&#227;o sendo tomadas com base em informa&#231;&#245;es velhas.</p><h4>Por que a Teoria das Filas &#233; o Pr&#233;-Requisito?</h4><p>&#201; por isso que estudar <strong>Teoria das Filas</strong> n&#227;o &#233; um preciosismo acad&#234;mico; &#233; um pr&#233;-requisito de sobreviv&#234;ncia.</p><p>Se voc&#234; implementa RabbitMQ ou Kafka sem entender a <strong>Lei de Little</strong> ou a <strong>F&#243;rmula de Kingman</strong>, voc&#234; est&#225; pilotando um avi&#227;o no meio da neblina sem alt&#237;metro.</p><ul><li><p>Voc&#234; acha que est&#225; voando alto (fila aceitando tudo), mas est&#225; a segundos de bater na montanha (Bufferbloat e OOM).</p></li><li><p>Voc&#234; acha que adicionar mais consumidores resolve tudo, mas n&#227;o previu o <em>Thundering Herd</em> que vai derrubar seu banco de dados.</p></li></ul><p>Arquitetura Orientada a Eventos &#233; poderosa, mas ela exige maturidade matem&#225;tica. Antes de dar <code>docker run rabbitmq</code>, entenda que voc&#234; n&#227;o est&#225; apenas subindo um container; <strong>voc&#234; est&#225; introduzindo um componente probabil&#237;stico e estoc&#225;stico na sua infraestrutura</strong>. Se voc&#234; n&#227;o souber calcular os limites dele, ele vai definir os limites do seu neg&#243;cio &#8212; da pior maneira poss&#237;vel.</p><h2>Conclus&#227;o</h2><p>Ao longo deste artigo, viajamos de Copenhague em 1909 (com Agner Erlang) at&#233; os <em>Data Centers</em> modernos distribu&#237;dos em Kubernetes. Passamos por f&#243;rmulas matem&#225;ticas, curvas exponenciais e estrat&#233;gias de defesa como <em>Load Shedding</em> e <em>Jitter</em>.</p><p>Mas o que, de fato, podemos levar para a nossa pr&#243;xima <em>Daily</em> ou <em>Architecture Review</em>?</p><p>A li&#231;&#227;o fundamental &#233; que <strong>ferramentas n&#227;o resolvem problemas de f&#237;sica.</strong> Voc&#234; pode usar o broker mais r&#225;pido do mundo (Kafka, Redpanda, RabbitMQ), escrito em Rust, Go ou C++. Nada disso importa se voc&#234; violar a <strong>Lei de Little</strong> ou ignorar a <strong>F&#243;rmula de Kingman</strong>. Se a taxa de chegada for maior que a de sa&#237;da, ou se a utiliza&#231;&#227;o ro&#231;ar os 100%, a lat&#234;ncia <em>vai</em> subir, a mem&#243;ria <em>vai</em> estourar e o sistema <em>vai</em> colapsar. A matem&#225;tica &#233; implac&#225;vel; voc&#234; n&#227;o negocia com ela.</p><p>A diferen&#231;a entre um programador s&#234;nior e um verdadeiro arquiteto de software n&#227;o est&#225; em saber decorar a API do RabbitMQ. Est&#225; em entender que:</p><ol><li><p><strong>Filas Infinitas s&#227;o uma Mentira:</strong> A mem&#243;ria &#233; finita e a paci&#234;ncia do usu&#225;rio &#233; curta. Aceitar trabalho que voc&#234; n&#227;o pode entregar a tempo (Bufferbloat) n&#227;o &#233; resili&#234;ncia, &#233; neglig&#234;ncia. A coragem de dizer &#8220;N&#227;o&#8221; (rejeitar carga) &#233; o que salva o sistema.</p></li><li><p><strong>O &#8220;Happy Path&#8221; &#233; Irrelevante:</strong> Qualquer sistema funciona quando tudo vai bem. A engenharia de verdade acontece no caos: quando o banco cai, quando a <em>Manada Estourada</em> volta, quando o <em>Vizinho Barulhento</em> entope o t&#243;pico. Projetamos para a falha, n&#227;o para o sucesso.</p></li><li><p><strong>Observabilidade &#233; Obrigat&#243;ria:</strong> Voc&#234; n&#227;o pode corrigir o &#8220;Joelho da Curva&#8221; se n&#227;o souber onde ele est&#225;. M&#233;tricas de <em>Consumer Lag</em>, <em>Processing Time</em> e <em>Error Rate</em> n&#227;o s&#227;o &#8220;b&#244;nus&#8221;, s&#227;o os batimentos card&#237;acos da sua aplica&#231;&#227;o.</p></li></ol><p>Arquitetura Orientada a Eventos &#233; poderosa, mas perigosa. Ela nos d&#225; a ilus&#227;o de desacoplamento infinito, mas esconde acoplamentos temporais e gargalos silenciosos.</p><p>Ao desenhar seu pr&#243;ximo microsservi&#231;o, lembre-se: <strong>resili&#234;ncia n&#227;o &#233; algo que voc&#234; instala via </strong><code>npm install</code><strong> ou </strong><code>docker run</code><strong>. Resili&#234;ncia &#233; o resultado de decis&#245;es matem&#225;ticas conscientes.</strong></p><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>&#8220;Dizer que o tr&#225;fego &#233; &#8216;rajado&#8217; (<em>bursty</em>) significa que a carga de trabalho chega em picos imprevis&#237;veis e violentos, seguidos de per&#237;odos de calmaria. &#201; o oposto de um fluxo constante. &#201; por isso que dimensionar servidores baseando-se na &#8216;m&#233;dia de requisi&#231;&#245;es&#8217; &#233; um erro fatal: a m&#233;dia esconde os picos, e s&#227;o os picos que derrubam sua aplica&#231;&#227;o.&#8221;</p><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Voc&#234; deve estar se perguntando: <em>&#8220;Ok, entendi que a fila enche. Mas qual &#233; a diferen&#231;a pr&#225;tica entre derrubar a carga e fazer contrapress&#227;o? Eu uso um ou o outro?&#8221;</em></p><p>A resposta de engenharia robusta &#233;: <strong>Voc&#234; usa os dois juntos.</strong> Eles s&#227;o o freio e o cinto de seguran&#231;a do seu sistema.</p><p>Para visualizar, imagine o seguinte.</p><ol><li><p><strong>Backpressure (Contrapress&#227;o):</strong> &#201; o <strong>sinal de pare</strong>. Quando o <em>Consumer</em> (sua aplica&#231;&#227;o) diz: <em>&#8220;Estou engasgado, pare de me mandar &#225;gua!&#8221;</em>. No RabbitMQ, isso &#233; feito via <code>QoS / Prefetch</code>. O Consumer para de buscar mensagens e elas come&#231;am a acumular na Fila. O objetivo aqui &#233; <strong>proteger a Aplica&#231;&#227;o</strong> de travar por falta de mem&#243;ria.</p></li><li><p><strong>Load Shedding (Derrubar Carga):</strong> &#201; a <strong>v&#225;lvula de escape</strong>. Como a contrapress&#227;o fez a &#225;gua acumular na Fila, agora a Fila est&#225; perigosamente cheia. Se continuar entrando &#225;gua, o Broker (RabbitMQ) vai explodir. O Load Shedding entra em a&#231;&#227;o para abrir uma v&#225;lvula e jogar o excesso fora (no lixo ou DLQ) antes que o reservat&#243;rio estoure. O objetivo aqui &#233; <strong>proteger o Broker e a Lat&#234;ncia</strong>.</p></li></ol><h4>Como eles trabalham juntos</h4><ul><li><p>O <strong>Worker</strong> fica sobrecarregado (CPU 100%).</p></li><li><p>Ele aplica <strong>Backpressure</strong> no RabbitMQ (via configura&#231;&#227;o de QoS/Prefetch), dizendo: <em>&#8220;N&#227;o me mande mais nada at&#233; eu terminar este lote&#8221;</em>.</p></li><li><p>Como o RabbitMQ parou de entregar para o Worker, as mensagens come&#231;am a empilhar na <strong>Fila</strong>.</p></li><li><p>A Fila atinge seu limite m&#225;ximo (ex: 1.000 msgs).</p></li><li><p>O RabbitMQ aplica <strong>Load Shedding</strong>: Como a fila est&#225; cheia e o Worker n&#227;o aceita mais nada, o RabbitMQ come&#231;a a <strong>rejeitar/dropar</strong> as novas mensagens que chegam do Producer (ou joga as mais antigas fora), salvando o servidor de cair.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!PpOl!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!PpOl!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!PpOl!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!PpOl!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!PpOl!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!PpOl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/cdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5518336,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181627983?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!PpOl!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!PpOl!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!PpOl!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!PpOl!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fcdf59543-f00d-4c95-a4fd-336609d52074_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Um breve mergulho no DynamoDB]]></title><description><![CDATA[Por que sua mentalidade Relacional est&#225; matando a performance da sua aplica&#231;&#227;o (e como resolver).]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/um-breve-mergulho-dynamodb</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/um-breve-mergulho-dynamodb</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Mon, 15 Dec 2025 13:33:02 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/d62396c1-45f6-4428-80d8-2644aad99922_420x300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Hoje o papo &#233; sobre um gigante que, muito provavelmente, voc&#234; j&#225; cruzou ou vai cruzar na sua jornada de desenvolvimento na nuvem: o <strong>Amazon DynamoDB</strong>.</p><p>Sendo bem sincero com voc&#234;s: a primeira vez que a gente mexe com DynamoDB, a sensa&#231;&#227;o pode ser um pouco... desconfort&#225;vel. A maioria de n&#243;s vem daquela escola cl&#225;ssica dos bancos relacionais (o bom e velho SQL), onde tudo &#233; tabelinha normalizada e os JOINs resolvem magicamente qualquer problema.</p><p>A&#237; voc&#234; chega no Dynamo e descobre que JOIN n&#227;o existe, que modelar dados parece um quebra-cabe&#231;a e que voc&#234; precisa saber exatamente como vai consultar o dado <em>antes</em> mesmo de cri&#225;-lo. D&#225; um n&#243; na cabe&#231;a de qualquer um no come&#231;o, n&#233;?</p><p>Eu sei, a AWS tem uma documenta&#231;&#227;o oficial extremamente robusta. Tem de tudo l&#225;, &#233; uma b&#237;blia t&#233;cnica. Mas, justamente por ser t&#227;o completa, &#224;s vezes a gente se sente tentando beber &#225;gua de um hidrante: &#233; tanta informa&#231;&#227;o, limite e configura&#231;&#227;o que fica dif&#237;cil filtrar o que &#233; teoria do que &#233; &#8220;vida real&#8221;.</p><p><strong>O meu objetivo com este artigo &#233; facilitar o seu caminho.</strong></p><p>N&#227;o quero apenas replicar o manual. Quero ter uma conversa franca sobre como usar essa ferramenta no dia a dia, focando no que realmente importa, nas &#8220;cicatrizes&#8221; de batalha e nas li&#231;&#245;es que a gente costuma aprender da maneira mais dif&#237;cil.</p><p>Vamos percorrer juntos t&#243;picos essenciais para dominar esse banco, passando por:</p><ul><li><p>A arte de escolher <strong>Partition Keys (PK) e Sort Keys (SK)</strong>;</p></li><li><p>O famoso (e &#224;s vezes pol&#234;mico) <strong>Single Table Design</strong>;</p></li><li><p>A matem&#225;tica real dos <strong>custos de leitura e escrita</strong>;</p></li><li><p>O lado obscuro dos <strong>GSIs</strong> e as armadilhas perigosas do comando <strong>Scan</strong>;</p></li><li><p>E, claro, como lidar com <strong>transa&#231;&#245;es e dados gigantes</strong>;</p></li></ul><p>Ent&#227;o, pega um caf&#233;, ajusta a cadeira e bora conhecer o DynamoDB.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3>A Anatomia da Chave Prim&#225;ria: Onde tudo come&#231;a (e onde tudo pode terminar mal)</h3><p>Se voc&#234; vem do mundo dos bancos relacionais, a &#8220;Chave Prim&#225;ria&#8221; (Primary Key) geralmente &#233; algo trivial: um campo <code>ID</code> autoincremental (<code>1, 2, 3...</code>) ou um UUID que voc&#234; cria apenas para garantir unicidade e fazer alguns <code>JOINs</code> depois.</p><p>No DynamoDB, a Chave Prim&#225;ria &#233; <strong>muito mais</strong> do que um identificador &#250;nico. Ela &#233; o destino, o mapa e o motor de performance do seu dado.</p><p>Para come&#231;ar, precisamos entender que existem dois tipos de Chaves no DynamoDB:</p><ol><li><p><strong>Chave Simples:</strong> Composta apenas pela <strong>Partition Key (PK)</strong>.</p></li><li><p><strong>Chave Composta:</strong> Composta pela <strong>Partition Key (PK)</strong> + <strong>Sort Key (SK)</strong>.</p></li></ol><p>Vamos dissecar cada uma delas e entender por que essa escolha tira o sono de muito arquiteto de software.</p><h3>A Anatomia da Chave Prim&#225;ria: O &#8220;GPS&#8221; Matem&#225;tico do DynamoDB</h3><p>Para dominar o DynamoDB, voc&#234; precisa esquecer como um banco SQL funciona.</p><p>No SQL, quando voc&#234; busca um <code>ID=50</code>, o banco vai at&#233; um &#237;ndice (geralmente uma &#225;rvore B-Tree), percorre os galhos da &#225;rvore procurando onde est&#225; o 50, pega o ponteiro e vai at&#233; o disco. &#201; uma <strong>busca</strong>.</p><p>No DynamoDB, a Partition Key (PK) n&#227;o serve para &#8220;buscar&#8221;. Ela serve para <strong>calcular</strong>.</p><h4>O que &#233; a Partition Key (PK) realmente?</h4><p>O nome t&#233;cnico dela &#233; <strong>Hash Key</strong>. E esse nome n&#227;o &#233; &#224; toa. A PK &#233; a entrada de uma <strong>Fun&#231;&#227;o de Hash</strong>.</p><p>O processo interno &#233; o seguinte:</p><ol><li><p>Voc&#234; envia o dado: <code>PK = &#8220;USER#123&#8221;</code>.</p></li><li><p>O DynamoDB pega essa string e passa por uma fun&#231;&#227;o criptogr&#225;fica interna.</p></li><li><p>O resultado &#233; um n&#250;mero hexadecimal gigante (o hash).</p></li><li><p>O DynamoDB olha para esse n&#250;mero e diz: <em>&#8220;O hash resultante cai no intervalo que pertence &#224; <strong>Parti&#231;&#227;o 3</strong>&#8220;</em>.</p></li></ol><h4>Por que isso muda tudo?</h4><p>Isso elimina a necessidade de percorrer &#237;ndices.</p><ul><li><p>&#8203;<strong>No SQL (B-Tree):</strong> O banco precisa navegar pelos galhos da &#225;rvore de &#237;ndice. Quanto mais dados (1 milh&#227;o vs 1 bilh&#227;o), mais profunda &#233; a &#225;rvore e mais &#8220;saltos&#8221; o banco precisa dar para achar o endere&#231;o do dado. A lat&#234;ncia aumenta ligeiramente com o tamanho.</p></li><li><p>&#8203;<strong>No DynamoDB (Hash Table):</strong> O c&#225;lculo do Hash tem complexidade constante O(1). Mas o segredo &#233; o que acontece depois: esse hash funciona como uma <strong>coordenada direta</strong>. O Request Router da AWS usa esse hash para ir <strong>diretamente</strong> ao n&#243; de armazenamento correto. Ele n&#227;o &#8220;procura&#8221; o dado; ele sabe onde est&#225;.</p></li></ul><p> &#201; por isso que a lat&#234;ncia do DynamoDB &#233; previs&#237;vel. N&#227;o importa se sua tabela tem 1 GB ou 100 TB, o n&#250;mero de etapas para chegar ao dado &#233; o mesmo. A PK n&#227;o &#233; apenas um ID, &#233; o endere&#231;o f&#237;sico matem&#225;tico do item.</p><h4>A Escalabilidade Horizontal</h4><p>Imagine que o DynamoDB n&#227;o &#233; um &#8220;supercomputador gigante&#8221;, mas sim um ex&#233;rcito de milhares de computadores pequenos (n&#243;s).</p><p>A Partition Key &#233; a ferramenta de <strong>Sharding (Fragmenta&#231;&#227;o)</strong> autom&#225;tica.</p><ul><li><p>Se todos os seus usu&#225;rios tiverem a PK <code>BRASIL</code>, todos os dados cair&#227;o no mesmo computador. Esse computador vai lotar e travar.</p></li><li><p>Se cada usu&#225;rio tiver uma PK &#250;nica (<code>USER#A</code>, <code>USER#B</code>...), o algoritmo de hash vai espalhar matematicamente esses dados entre os milhares de computadores dispon&#237;veis.</p></li></ul><p><strong>Resumo:</strong> A PK n&#227;o &#233; apenas um ID. Ela &#233; a instru&#231;&#227;o de roteamento que garante que seu dado seja distribu&#237;do uniformemente pelo cluster da AWS, evitando que um &#250;nico servidor sofra sobrecarga.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Pg8K!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Pg8K!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Pg8K!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Pg8K!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Pg8K!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Pg8K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:114918,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!Pg8K!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg 424w, https://substackcdn.com/image/fetch/$s_!Pg8K!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg 848w, https://substackcdn.com/image/fetch/$s_!Pg8K!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!Pg8K!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5f1fbfcf-fc18-46db-97fe-6c849616cbfb_1024x559.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Como escolher bem sua Primary Key (PK)?</h4><p>A regra de ouro aqui &#233;: <strong>Alta Cardinalidade</strong>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><p>Voc&#234; quer uma PK que tenha muitos valores &#250;nicos diferentes.</p><ul><li><p><strong>Escolha Ruim:</strong> <code>Status</code> (Valores: &#8220;Pendente&#8221;, &#8220;Aprovado&#8221;, &#8220;Cancelado&#8221;).</p><ul><li><p><em>Por que &#233; ruim?</em> Voc&#234; s&#243; ter&#225; 3 parti&#231;&#245;es l&#243;gicas. Se voc&#234; tiver 1 milh&#227;o de pedidos e todos estiverem como &#8220;Aprovado&#8221;, todo o tr&#225;fego de leitura e escrita vai martelar um &#250;nico n&#243; do banco. Isso cria uma <strong>Hot Partition</strong> (<em>vamos ter um t&#243;pico para falar sobre isso</em>). O DynamoDB vai engasgar e limitar suas requisi&#231;&#245;es (Throttling).</p></li></ul></li><li><p><strong>Escolha Boa:</strong> <code>UserID</code> ou <code>DeviceID</code> (Valores: Milh&#245;es de UUIDs diferentes).</p><ul><li><p><em>Por que &#233; boa?</em> Os dados e o tr&#225;fego s&#227;o distribu&#237;dos uniformemente por todo o cluster da AWS.</p></li></ul></li></ul><h3>O que &#233; a Sort Key (SK)?</h3><p><em>Tamb&#233;m conhecida como: Range Key.</em></p><p>Se a PK &#233; o &#8220;Corredor&#8221; do armaz&#233;m, a <strong>Sort Key</strong> &#233; a organiza&#231;&#227;o da prateleira dentro desse corredor.</p><p>Quando voc&#234; usa uma Chave Composta (PK + SK), o DynamoDB faz algo m&#225;gico: ele garante que todos os itens que compartilham a mesma PK ser&#227;o armazenados <strong>fisicamente juntos e ordenados</strong> pelo valor da SK.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!P0mS!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!P0mS!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg 424w, https://substackcdn.com/image/fetch/$s_!P0mS!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg 848w, https://substackcdn.com/image/fetch/$s_!P0mS!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!P0mS!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!P0mS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:166103,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!P0mS!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg 424w, https://substackcdn.com/image/fetch/$s_!P0mS!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg 848w, https://substackcdn.com/image/fetch/$s_!P0mS!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!P0mS!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F05e38823-ee24-4b28-a23a-76edbefe6fb8_1024x559.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Por que precisamos dela?</h4><p>Precisamos dela para Modelar Relacionamentos 1:N e para Queries Complexas.</p><p>Sem a SK, voc&#234; s&#243; conseguiria buscar um item pelo ID exato (GetItem). Com a SK, voc&#234; ganha superpoderes de pesquisa dentro de uma parti&#231;&#227;o.</p><h4>Como a SK est&#225; relacionada &#224; PK?</h4><p>A rela&#231;&#227;o &#233; de hierarquia e agrupamento.</p><ul><li><p><strong>PK:</strong> Agrupa os dados (Ex: &#8220;Tudo sobre o usu&#225;rio Jo&#227;o&#8221;).</p></li><li><p><strong>SK:</strong> Identifica o dado espec&#237;fico dentro do grupo (Ex: &#8220;Perfil&#8221;, &#8220;Pedido #1&#8221;, &#8220;Pedido #2&#8221;).</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!MbRG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!MbRG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg 424w, https://substackcdn.com/image/fetch/$s_!MbRG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg 848w, https://substackcdn.com/image/fetch/$s_!MbRG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!MbRG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!MbRG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6021565,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!MbRG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg 424w, https://substackcdn.com/image/fetch/$s_!MbRG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg 848w, https://substackcdn.com/image/fetch/$s_!MbRG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!MbRG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7291a65-f1e8-4621-9f75-6f83f5a269e5_2816x1536.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Melhores pr&#225;ticas para usar Sort Keys (SK)</h3><p>Aqui &#233; onde a arte da modelagem acontece. Uma SK bem escolhida permite que voc&#234; recupere exatamente o que precisa com uma &#250;nica requisi&#231;&#227;o, gastando o m&#237;nimo de dinheiro.</p><h4>1. Use Sort Keys para criar Hierarquias</h4><p>Imagine que voc&#234; est&#225; armazenando dados geogr&#225;ficos.</p><ul><li><p><strong>PK:</strong> <code>Country#BRASIL</code></p></li><li><p><strong>SK:</strong> <code>State#SP#City#CAMPINAS</code></p></li></ul><p>Isso permite que voc&#234; use a fun&#231;&#227;o <code>begins_with</code> na sua query.</p><ul><li><p>Quero todas as cidades de SP? Query onde PK &#233; <code>Country#BRASIL</code> e SK come&#231;a com <code>State#SP</code>. O DynamoDB l&#234; apenas esse bloco sequencial de dados. &#201; extremamente r&#225;pido.</p></li></ul><h4>2. Use Sort Keys para Versionamento</h4><p>Em sistemas de auditoria ou controle de documentos.</p><ul><li><p><strong>PK:</strong> <code>DOC#123</code></p></li><li><p><strong>SK:</strong> <code>v1</code>, <code>v2</code>, <code>v3</code>...</p></li></ul><p>Voc&#234; pode fazer uma Query pedindo para listar em ordem decrescente (ScanIndexForward = false) e limitar a 1 item (<code>Limit = 1</code>) para pegar sempre a vers&#227;o mais atual do documento instantaneamente.</p><h4>3. Datas ISO 8601 s&#227;o suas melhores amigas</h4><p>Se o seu acesso &#233; temporal (ex: extrato banc&#225;rio, logs, hist&#243;rico de pedidos), use datas no formato ISO (<code>YYYY-MM-DDThh:mm:ss</code>).</p><ul><li><p><strong>PK:</strong> <code>CONTA#12345</code></p></li><li><p><strong>SK:</strong> <code>2023-10-27T14:30:00</code></p></li></ul><p>Como a SK ordena o dado fisicamente, strings ISO ordenam cronologicamente de forma perfeita. Isso permite queries do tipo: <em>&#8220;Me d&#234; todas as transa&#231;&#245;es da conta 12345 que aconteceram <strong>entre</strong> dia 01 e dia 15&#8221;</em>.</p><h4>4. O &#8220;Pulo do Gato&#8221;: Nomes Gen&#233;ricos (Generic Names)</h4><p>Se voc&#234; pretende usar <strong>Single Table Design</strong> (que falaremos adiante), pare de batizar suas colunas de chave como <code>UserId</code> ou <code>OrderId</code>. No DynamoDB, a pr&#225;tica de mercado &#233; chamar as colunas simplesmente de <strong>PK</strong> e <strong>SK</strong>.</p><p><strong>Por que fazer isso?</strong> Porque isso desacopla o esquema f&#237;sico do significado l&#243;gico, permitindo armazenar tipos de dados completamente diferentes na mesma tabela.</p><ul><li><p>Na linha 1, a coluna <code>PK</code> pode guardar um Usu&#225;rio.</p></li><li><p>Na linha 2, a <em>mesma</em> coluna <code>PK</code> pode guardar um Pedido.</p></li></ul><p><strong>&#8220;Mas eu quero usar o CPF como chave, posso?&#8221;</strong> Com certeza! O segredo est&#225; nos <strong>prefixos</strong>. Voc&#234; n&#227;o cria uma coluna chamada <code>CPF</code>, voc&#234; usa a coluna <code>PK</code> e preenche o valor com <code>CLIENTE#11122233344</code>.</p><p>Ao fazer isso, voc&#234; organiza a casa: o prefixo (<code>CLIENTE#</code> ou <code>PEDIDO#</code>) diz <em>o que</em> &#233; aquele dado, e o sufixo (o CPF ou UUID) diz <em>quem</em> &#233; aquele dado. Tudo isso morando harmoniosamente na mesma coluna gen&#233;rica.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!uDf5!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png 424w, https://substackcdn.com/image/fetch/$s_!uDf5!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png 848w, https://substackcdn.com/image/fetch/$s_!uDf5!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png 1272w, https://substackcdn.com/image/fetch/$s_!uDf5!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!uDf5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png" width="722" height="143" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:143,&quot;width&quot;:722,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:26302,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!uDf5!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png 424w, https://substackcdn.com/image/fetch/$s_!uDf5!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png 848w, https://substackcdn.com/image/fetch/$s_!uDf5!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png 1272w, https://substackcdn.com/image/fetch/$s_!uDf5!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F1f6b7e31-6ae2-4a44-98b1-65bd849ea1c1_722x143.png 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><p><strong>A regra de ouro da modelagem:</strong></p><blockquote><p><em>&#8220;Projete sua PK para espalhar os dados uniformemente (evitar hot spots). Projete sua SK para agrupar os dados que voc&#234; precisa ler juntos (query eficiente).&#8221;</em></p></blockquote><div><hr></div><p>No pr&#243;ximo t&#243;pico, vamos pegar esses conceitos e aplicar no <strong>Single Table Design</strong>, onde vamos aprender a colocar Usu&#225;rios, Pedidos e Produtos tudo nessa estrutura de PK e SK que acabamos de conversar.</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/um-breve-mergulho-dynamodb?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/um-breve-mergulho-dynamodb?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p><h3>O Dilema do Single Table Design: Uma tabela para todos governar</h3><p>Se voc&#234; mostrasse um diagrama de <strong>Single Table Design (STD)</strong> para um DBA de banco relacional nos anos 90, ele provavelmente teria um ataque card&#237;aco.</p><p>A ideia de colocar <strong>Usu&#225;rios</strong>, <strong>Pedidos</strong>, <strong>Produtos</strong> e <strong>Notas Fiscais</strong> tudo misturado dentro de uma &#250;nica tabela (<code>MinhaApp</code>) parece violar todas as leis de organiza&#231;&#227;o que aprendemos na faculdade. Parece ca&#243;tico. Parece errado.</p><p>Mas, no DynamoDB, isso n&#227;o s&#243; &#233; &#8220;certo&#8221;, como &#233; o estado da arte da performance. Para entender o porqu&#234;, precisamos entender de onde essa loucura nasceu.</p><h4>De onde nasceu esse conceito?</h4><p>No mundo SQL, a normaliza&#231;&#227;o de dados (criar v&#225;rias tabelas separadas) existe para <strong>economizar espa&#231;o em disco</strong>. Antigamente, armazenamento era caro. Ent&#227;o, voc&#234; salvava o nome &#8220;Jo&#227;o&#8221; na tabela <code>Usuarios</code> e apenas referenciava o ID dele na tabela <code>Pedidos</code>. Para ver o nome do Jo&#227;o junto com o pedido, o banco de dados fazia um <strong>JOIN</strong> &#8212; uma opera&#231;&#227;o de CPU pesada que &#8220;cola&#8221; as tabelas na hora da leitura.</p><p><strong>O Problema:</strong> Quando voc&#234; tem milh&#245;es de usu&#225;rios e bilh&#245;es de pedidos, os JOINs come&#231;am a ficar lentos. O banco precisa pular de um lugar para outro no disco para montar a resposta.</p><p>O DynamoDB foi criado para rodar na escala da Amazon.com (o e-commerce). Eles perceberam que <strong>espa&#231;o em disco &#233; barato, mas tempo de CPU &#233; caro</strong>.</p><p>O Single Table Design nasceu de uma premissa simples: <strong>E se a gente j&#225; deixasse os dados &#8220;pr&#233;-juntados&#8221; no disco?</strong></p><p>Em vez de gastar CPU para fazer JOIN na hora que o cliente pede (Leitura), n&#243;s organizamos os dados fisicamente juntos na hora de salvar (Escrita). O STD resolve o problema da lat&#234;ncia de rede e processamento: ele elimina a necessidade de fazer m&#250;ltiplas requisi&#231;&#245;es para buscar dados relacionados.</p><h4>Como funciona na pr&#225;tica: A Arte da &#8220;Sobrecarga&#8221; (Key Overloading)</h4><p>Lembra que falamos sobre nomes gen&#233;ricos (<code>PK</code> e <code>SK</code>)? &#201; aqui que eles brilham. No STD, n&#243;s usamos uma t&#233;cnica chamada <strong><a href="https://urielbitton.substack.com/p/how-to-satisfy-multiple-access-patterns">Key Overloading</a></strong> (Sobrecarga de Chave). Isso significa que a coluna <code>SK</code> vai ter significados diferentes dependendo da linha.</p><p>Vamos visualizar o cen&#225;rio cl&#225;ssico de E-commerce: <strong>&#8220;Quero buscar um Cliente e ver todos os pedidos dele de uma vez s&#243;&#8221;</strong>.</p><p>No SQL, seriam duas tabelas e um JOIN. No DynamoDB Single Table, modelamos assim:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!w8cf!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!w8cf!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg 424w, https://substackcdn.com/image/fetch/$s_!w8cf!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg 848w, https://substackcdn.com/image/fetch/$s_!w8cf!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!w8cf!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!w8cf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:6698856,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!w8cf!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg 424w, https://substackcdn.com/image/fetch/$s_!w8cf!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg 848w, https://substackcdn.com/image/fetch/$s_!w8cf!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!w8cf!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F391c394c-0f8c-4451-872e-87f2e9a6be96_2816x1536.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Observe a m&#225;gica acontecendo na primeira coluna da imagem. Veja que o Perfil (METADATA), as Transa&#231;&#245;es (TRANSACAO#...) e at&#233; a Fatura (FATURA#...) compartilham rigorosamente a mesma Partition Key: CLIENTE#12345.</p><p>&#8203;Isso cria o que a chave lateral da imagem destaca como "Parti&#231;&#227;o do Cliente Jo&#227;o" &#8212; tecnicamente chamado de Item Collection (Cole&#231;&#227;o de Itens). O DynamoDB garante que todos esses dados, embora sejam entidades diferentes (JSONs com atributos distintos), sejam armazenados fisicamente vizinhos, "colados" uns nos outros no mesmo servidor.</p><p>&#8203;Agora, olhe para o diagrama SQL riscado &#224; direita da imagem. Para obter esses dados no modelo antigo, voc&#234; precisaria de m&#250;ltiplos JOINs entre tabelas espalhadas. Aqui, para buscar o perfil do Jo&#227;o, seu extrato e suas faturas, voc&#234; faz apenas uma Query:</p><p>&#8203;Condi&#231;&#227;o: PK = "CLIENTE#12345"</p><p>&#8203;Custo: 1 &#250;nica viagem de rede (ida e volta).</p><p>&#8203;O banco l&#234; esse "bloco azul" sequencialmente e te devolve tudo de uma vez. &#201; uma performance imbat&#237;vel. Voc&#234; acabou de "emular" os JOINs complexos de Clientes, Transa&#231;&#245;es e Faturas com custo computacional quase zero.</p><h4>O Lado Amargo: STD n&#227;o &#233; bala de prata</h4><p>Depois de ler isso, a vontade &#233; sair convertendo tudo para Single Table. <strong>Calma.</strong></p><p>O Single Table Design tem um &#8220;custo cognitivo&#8221; alto. Ele &#233; r&#237;gido.</p><ul><li><p><strong>A curva de aprendizado &#233; alta:</strong> &#201; dif&#237;cil explicar para novos desenvolvedores do time por que a tabela est&#225; &#8220;misturada&#8221;.</p></li><li><p><strong>Rigidez nas Consultas:</strong> Voc&#234; precisa desenhar a tabela baseado nas perguntas (<a href="https://docs.aws.amazon.com/prescriptive-guidance/latest/dynamodb-data-modeling/step3.html">Access Patterns</a>)<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a> que voc&#234; tem hoje. Se daqui a 6 meses o chefe chegar e disser: <em>&#8220;Agora quero filtrar pedidos pelo tipo de cart&#227;o de cr&#233;dito&#8221;</em>, e voc&#234; n&#227;o previu isso na sua SK, voc&#234; ter&#225; um problema s&#233;rio. Adicionar novos padr&#245;es de acesso em uma Single Table j&#225; populada &#233; doloroso.</p></li><li><p><strong>Analytics e BI:</strong> Ferramentas de BI (como Tableau, PowerBI) odeiam Single Table Design. Elas esperam tabelas normalizadas. Tentar rodar queries anal&#237;ticas (&#8221;Qual a m&#233;dia de vendas por estado?&#8221;) em uma STD &#233; ineficiente e caro.</p></li></ul><p>Use <strong>Multi-Table</strong> (uma tabela por entidade) se voc&#234; est&#225; prototipando, se seu produto muda toda semana, ou se voc&#234; precisa de flexibilidade para queries ad-hoc que voc&#234; ainda nem sabe quais ser&#227;o. N&#227;o h&#225; vergonha nenhuma em usar m&#250;ltiplas tabelas no DynamoDB se isso simplificar sua vida e a performance for aceit&#225;vel.</p><div class="captioned-button-wrap" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/um-breve-mergulho-dynamodb?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;}" data-component-name="CaptionedButtonToDOM"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Esta publica&#231;&#227;o &#233; p&#250;blica, ent&#227;o sinta-se &#224; vontade para compartilh&#225;-la.</p></div><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/um-breve-mergulho-dynamodb?utm_source=substack&utm_medium=email&utm_content=share&action=share&quot;,&quot;text&quot;:&quot;Compartilhar&quot;}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/um-breve-mergulho-dynamodb?utm_source=substack&utm_medium=email&utm_content=share&action=share"><span>Compartilhar</span></a></p></div><h2>Lidando com Itens Gigantes e a Arte do Particionamento Vertical (Case Real)</h2><p>O DynamoDB imp&#245;e um limite r&#237;gido: <strong>um &#250;nico item n&#227;o pode exceder 400KB</strong>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a></p><p>Pode parecer muito. Afinal, 400KB de texto puro &#233; quase um livro. Mas no dia a dia, especialmente quando trazemos v&#237;cios de outros bancos (Relacional ou Documentais como MongoDB), esse limite chega mais r&#225;pido do que imaginamos.</p><p>Vou ser transparente com voc&#234;s: <strong>eu bati de frente com essa parede.</strong></p><p>No in&#237;cio, minha mentalidade ainda estava presa ao modelo relacional. Eu olhava para um &#8220;Cliente&#8221; e queria guardar <em>tudo</em> sobre ele num &#250;nico registro gigante. Hist&#243;rico de simula&#231;&#245;es, contratos, logs de auditoria... eu ia anexando tudo num JSON aninhado que s&#243; crescia. O resultado? Itens obesos, lentid&#227;o na rede e o risco iminente de estourar o limite do banco.</p><p>Eu precisei dar um passo atr&#225;s. Parei de codar, respirei fundo e voltei para a prancheta de <strong>Access Patterns</strong>. Entendi que o segredo n&#227;o &#233; guardar tudo junto, mas guardar de forma que eu consiga <em>ler</em> apenas o necess&#225;rio.</p><p>Foi a&#237; que apliquei o <strong>Particionamento Vertical</strong> em um case real de sistema financeiro no qual trabalhei, e &#233; essa modelagem que quero compartilhar com voc&#234;s.</p><h4>Segrega&#231;&#227;o e Temporalidade na SK</h4><p>O desafio era gerenciar dados de cr&#233;dito onde um cliente (identificado por CPF) poderia ter contratos, fazer dezenas de simula&#231;&#245;es e registrar promessas de pagamento.</p><p>Em vez de criar um item gigante do tipo &#8220;Dossi&#234; do Cliente&#8221;, quebrei esse cliente em v&#225;rias linhas pequenas na mesma tabela, usando a <strong>Sort Key (SK)</strong> para organizar a bagun&#231;a.</p><p>A estrutura ficou assim:</p><ul><li><p><strong>Partition Key (PK):</strong> <code>BANCO#CPF</code> (Ex: <code>SANTANDER#11122233344</code>)</p><ul><li><p><em>Por que?</em> Garante o isolamento entre institui&#231;&#245;es (Multi-tenant) e usa o CPF para dar a alta cardinalidade que o DynamoDB precisa.</p></li></ul></li><li><p><strong>Sort Key (SK):</strong> Aqui est&#225; a m&#225;gica da segrega&#231;&#227;o.</p><ol><li><p><strong>Contratos:</strong> CONTRACT<code>#091213#001</code>, CONTRACT<code>#091213#002</code>...</p></li><li><p><strong>Simula&#231;&#245;es:</strong> <code>SIMULATION#2023-12-01#UUID</code></p></li><li><p><strong>Promessas:</strong> <code>PROMISE#2023-12-05#UUID</code></p></li></ol></li></ul><p>Veja como isso fica no banco:</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!Hy7H!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!Hy7H!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Hy7H!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Hy7H!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Hy7H!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!Hy7H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/be49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:4406492,&quot;alt&quot;:&quot;&quot;,&quot;title&quot;:&quot;&quot;,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" title="" srcset="https://substackcdn.com/image/fetch/$s_!Hy7H!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!Hy7H!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!Hy7H!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!Hy7H!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fbe49b79a-ac49-4f85-ba9d-ac446a60aefe_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>O que conseguimos alcan&#231;ar?</h4><ol><li><p><strong>Fim dos Itens Gigantes:</strong> Cada simula&#231;&#227;o &#233; uma nova linha de apenas 1KB ou 2KB. Eu posso ter 1 milh&#227;o de simula&#231;&#245;es para esse cliente e nunca vou estourar o limite de 400KB do item, porque s&#227;o <em>itens diferentes</em> (Item Collection).</p></li><li><p><strong>Ordena&#231;&#227;o Temporal Autom&#225;tica:</strong> Ao colocar a DATA ISO 8601 na SK (<code>SIMULATION#2023...</code>), como falamos no t&#243;pico anterior <a href="https://aws.amazon.com/pt/blogs/database/effective-data-sorting-with-amazon-dynamodb/">o DynamoDB ordena cronologicamente.</a></p><ul><li><p><em>Query Poderosa:</em> &#8220;Quero as &#250;ltimas 5 simula&#231;&#245;es&#8221;. Basta fazer uma Query com <code>ScanIndexForward=False</code> e <code>Limit=5</code>. O banco nem l&#234; o resto.</p></li></ul></li><li><p><strong>Idempot&#234;ncia com UUID:</strong> Perceba que finalizei a SK com <code>#UUID</code>. Isso evita colis&#227;o se o cliente gerar duas simula&#231;&#245;es no mesmo milissegundo. Cada evento &#233; &#250;nico.</p></li><li><p><strong>Query H&#237;brida:</strong> Se eu quiser saber &#8220;Tudo sobre o cliente&#8221;, fa&#231;o uma Query apenas pela PK. O DynamoDB traz contratos, simula&#231;&#245;es e promessas num pacote s&#243;, mas estruturado.</p></li></ol><h4>E quando o item &#233; <em>realmente</em> um bin&#225;rio pesado? (O Padr&#227;o Claim Check)</h4><p>Mesmo com essa modelagem, as vezes precisamos guardar um PDF do contrato ou uma imagem de comprovante. Nesses casos, n&#227;o tente for&#231;ar a barra guardando Base64 no DynamoDB.</p><p>Siga a recomenda&#231;&#227;o oficial da AWS e use o padr&#227;o<a href="https://nikiforovall.blog/dotnet/aws/2024/05/27/aws-claim-check-dotnet.html"> </a><strong><a href="https://nikiforovall.blog/dotnet/aws/2024/05/27/aws-claim-check-dotnet.html">Claim Check</a></strong>:</p><ol><li><p>Salve o arquivo pesado no <strong>Amazon S3</strong> (que &#233; feito para isso e custa muito menos).</p></li><li><p>Salve no DynamoDB apenas o <strong>link</strong> (a URL do S3) e os metadados.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!oDgH!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!oDgH!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!oDgH!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!oDgH!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!oDgH!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!oDgH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png" width="1024" height="559" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:559,&quot;width&quot;:1024,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:969056,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!oDgH!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png 424w, https://substackcdn.com/image/fetch/$s_!oDgH!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png 848w, https://substackcdn.com/image/fetch/$s_!oDgH!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png 1272w, https://substackcdn.com/image/fetch/$s_!oDgH!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F2edf2550-2584-4469-bdc4-0eef8f1cf020_1024x559.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Li&#231;&#227;o Aprendida:</strong> O DynamoDB n&#227;o &#233; um dep&#243;sito de arquivos, nem um JSON infinito. Ele &#233; um motor de indexa&#231;&#227;o e recupera&#231;&#227;o r&#225;pida. Ao quebrar seus dados verticalmente, voc&#234; transforma um problema de &#8220;limite de tamanho&#8221; em uma solu&#231;&#227;o de &#8220;alta performance&#8221;.</p><div><hr></div><p>Agora vamos mudar um pouco o foco e entrar em um outro lado&#8230; Chegamos na parte onde os engenheiros viram contadores.</p><p>Ao contr&#225;rio de um banco de dados tradicional (RDS/SQL) onde voc&#234; paga pelo &#8220;tamanho da m&#225;quina&#8221; (CPU e RAM) e pode usar o quanto aguentar, no DynamoDB voc&#234; paga pelo <strong>tr&#225;fego</strong>. Voc&#234; paga por cada leitura e cada escrita.</p><p>Vamos colocar isso na ponta do l&#225;pis, porque a matem&#225;tica do DynamoDB &#233; implac&#225;vel e cheia de &#8220;pegadinhas&#8221;.</p><h3>Custo Real: O que s&#227;o WCU e RCU?</h3><p>Para a AWS, n&#227;o importa se sua query demorou 1ms ou 100ms. O que importa &#233; o <strong>tamanho do dado</strong> que voc&#234; mexeu. Para medir isso, eles criaram duas &#8220;moedas&#8221;:</p><ol><li><p><strong>WCU (Write Capacity Unit):</strong> Unidade de Capacidade de Escrita.</p></li><li><p><strong>RCU (Read Capacity Unit):</strong> Unidade de Capacidade de Leitura.</p></li></ol><h4>A Matem&#225;tica &#8220;Cruel&#8221; do Arredondamento</h4><p>Aqui mora o perigo. A AWS cobra por blocos, e o arredondamento &#233; <strong>sempre para cima</strong>.</p><p><strong>Para Escrita (WCU):</strong></p><ul><li><p><strong>Regra:</strong> 1 WCU permite escrever um item de at&#233; <strong>1 KB</strong> por segundo.</p></li><li><p><strong>O Cen&#225;rio:</strong> Voc&#234; tem um JSON de usu&#225;rio super otimizado com <code>900 bytes</code>.</p><ul><li><p>Custo: <strong>1 WCU</strong>.</p></li></ul></li><li><p><strong>O Problema:</strong> Voc&#234; adicionou uma descri&#231;&#227;o nova e o JSON foi para <code>1.1 KB</code>.</p><ul><li><p>Custo: <strong>2 WCUs</strong>.</p></li><li><p><em>Li&#231;&#227;o:</em> Por causa de <strong>0.1 KB</strong> (100 bytes), sua conta dobrou. O DynamoDB n&#227;o cobra &#8220;1.1 WCU&#8221;. &#201; 1 ou 2.</p></li></ul></li></ul><p><strong>Para Leitura (RCU):</strong></p><ul><li><p><strong>Regra:</strong> 1 RCU permite ler um item de at&#233; <strong>4 KB</strong> por segundo (no modo <em>Strongly Consistent</em>).</p></li><li><p>Se voc&#234; ler um item de <code>4.1 KB</code>, voc&#234; pagar&#225; <strong>2 RCUs</strong>.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!KybG!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!KybG!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!KybG!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!KybG!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!KybG!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!KybG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12743039,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!KybG!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!KybG!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!KybG!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!KybG!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F8174dc84-9370-4e8e-a7a3-8a36f9d0ab43_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>Provisioned vs On-Demand: Onde est&#225; o risco?</h3><p><a href="https://docs.aws.amazon.com/wellarchitected/latest/serverless-applications-lens/capacity.html">A AWS te d&#225; duas formas de comprar essas moedas</a>. A escolha errada aqui pode derrubar sua aplica&#231;&#227;o ou falir seu or&#231;amento.</p><h4>1. Mode: On-Demand (Sob Demanda)</h4><p>&#201; o modelo &#8220;Uber&#8221;. Voc&#234; n&#227;o combina nada antes. Se tiver tr&#225;fego, o DynamoDB escala instantaneamente para atender.</p><ul><li><p><strong>Vantagem:</strong> Zero dor de cabe&#231;a com planejamento. &#201; perfeito para startups ou cargas de trabalho imprevis&#237;veis (ex: um pico de acesso repentino).</p></li><li><p><strong>Desvantagem:</strong> &#201; o pre&#231;o &#8220;Premium&#8221;. O custo por requisi&#231;&#227;o chega a ser <strong>5 a 7 vezes mais caro</strong> do que o modo provisionado otimizado.</p></li><li><p><strong>Pre&#231;o (Estimativa base US-East-1):</strong></p><ul><li><p>Escrita: 1.25 por milh&#227;o de unidades.</p></li><li><p>Leitura: 0.25 por milh&#227;o de unidades.</p></li><li><p><em>Pode parecer centavos, mas em sistemas com alto throughput, isso vira milhares de d&#243;lares r&#225;pido.</em></p></li></ul></li></ul><h4>2. Mode: Provisioned (Provisionado)</h4><p>&#201; o modelo &#8220;Aluguel&#8221;. Voc&#234; diz para a AWS: <em>&#8220;Eu prometo que vou precisar de 100 Writes por segundo e 50 Reads por segundo&#8221;</em>, e ela deixa essa capacidade reservada para voc&#234;, 24 horas por dia.</p><ul><li><p><strong>Vantagem:</strong> &#201; <strong>muito</strong> mais barato se voc&#234; tiver fluxo constante.</p></li><li><p><strong>Desvantagem:</strong> Voc&#234; paga pela capacidade reservada, <strong>usando ou n&#227;o</strong>. Se voc&#234; reservar 100 WCUs e seu site ficar vazio na madrugada, voc&#234; paga pelas 100 WCUs do mesmo jeito (a menos que configure Auto-Scaling, que tem um delay para reagir).</p></li></ul><p><strong>O Custo na Ponta do L&#225;pis (Baseado US East N. Virginia):</strong> Olhando a tabela oficial, o custo &#233; cobrado <strong>por hora</strong> para cada unidade que voc&#234; provisiona:</p><ul><li><p><strong>WCU (Write):</strong> 0.00065 por hora.</p></li><li><p><strong>RCU (Read):</strong> 0.00013 por hora.</p></li></ul><p><strong>&#8220;Isso &#233; barato ou caro?&#8221;</strong> Vamos fazer uma conta de padaria para comparar com o On-Demand. Imagine uma aplica&#231;&#227;o que precisa sustentar <strong>10 escritas por segundo</strong> constantes durante um m&#234;s inteiro (720 horas).</p><ul><li><p><strong>No Provisioned:</strong></p><ul><li><p>10 WCUs x 0.00065 x 720 horas = <strong>$4.68 d&#243;lares/m&#234;s</strong>.</p></li></ul></li><li><p><strong>No On-Demand (estimado):</strong></p><ul><li><p>10 escritas/seg x 60 seg x 60 min x 720 horas = 25.9 milh&#245;es de escritas.</p></li><li><p>25.9 milh&#245;es x $1.25 (pre&#231;o m&#233;dio por milh&#227;o) = <strong>32.37 d&#243;lares/m&#234;s</strong>.</p></li></ul></li></ul><p>O Provisioned custou quase <strong>7x menos</strong> nesse cen&#225;rio de carga constante. Por isso, grandes empresas fogem do On-Demand assim que o tr&#225;fego estabiliza. O On-Demand &#233; excelente para picos, mas o Provisioned &#233; quem paga as contas do dia a dia.</p><h2>O Perigo do Provisioned: &#8220;Podemos acabar n&#227;o persistindo dados?&#8221;</h2><p>A resposta t&#233;cnica &#233;: <strong>O banco n&#227;o perde, mas sua aplica&#231;&#227;o pode desistir.</strong></p><p>Imagine que voc&#234; configurou (provisionou) <strong>10 WCUs</strong> (10 escritas por segundo). De repente, sua aplica&#231;&#227;o recebe um lote de <strong>50 requisi&#231;&#245;es simult&#226;neas</strong>.</p><ol><li><p>O DynamoDB aceita as primeiras 10.</p></li><li><p>Para as outras 40, o DynamoDB devolve um erro HTTP 400: <code>ProvisionedThroughputExceededException</code>. Isso &#233; o famoso <strong><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/TroubleshootingThrottling.html">Throttling</a> (em breve explico mais)</strong>.</p></li><li><p><strong>O que acontece agora?</strong></p><ul><li><p>O SDK da AWS (que voc&#234; usa no c&#243;digo) &#233; inteligente. Ele captura esse erro e tenta de novo automaticamente (Retries) usando <em>Exponential Backoff</em> (espera um pouco e tenta, espera mais um pouco e tenta...).</p></li><li><p>Se o tr&#225;fego continuar alto por muito tempo, o SDK vai cansar de tentar e vai estourar o erro na cara da sua aplica&#231;&#227;o.</p></li></ul></li><li><p><strong>O Resultado:</strong> Se voc&#234; n&#227;o tiver um tratamento de erro (<code>try/catch</code>) para salvar esse dado em uma fila morta (DLQ) ou tentar mais tarde, <strong>sim, o dado ser&#225; perdido pela aplica&#231;&#227;o</strong>, porque o banco bateu a porta na sua cara.</p></li></ol><blockquote><p><strong>Auto-Scaling no Provisioned:</strong> A AWS tem um &#8220;Auto-Scaling&#8221; para o modo provisionado, mas ele <strong>n&#227;o &#233; instant&#226;neo</strong>. Ele demora alguns minutos para reagir. Se o pico for muito r&#225;pido (ex: 1 minuto de loucura), o Auto-Scaling n&#227;o chega a tempo de evitar os erros.</p></blockquote><h3>Boas Pr&#225;ticas</h3><p>Como trabalhar de forma inteligente com essas regras?</p><h4>1. Nomes de Atributos contam no Custo!</h4><p>Essa &#233; chocante para muitos. No DynamoDB, o nome da coluna &#233; salvo junto com o valor em <strong>cada linha</strong>.</p><ul><li><p><strong>Ruim:</strong> <code>{ &#8220;CustomerIdentificationNumberOfTheSystem&#8221;: 123 }</code></p><ul><li><p>Voc&#234; est&#225; gastando WCU para salvar essa string gigante de chave milh&#245;es de vezes.</p></li></ul></li><li><p><strong>Bom:</strong> <code>{ &#8220;id&#8221;: 123 }</code> ou <code>{ &#8220;cin&#8221;: 123 }</code></p><ul><li><p>Em milh&#245;es de registros, encurtar nomes de atributos economiza gigabytes e muitas WCUs.</p></li></ul></li></ul><h4>2. Eventual Consistency (O desconto de 50%)</h4><p>Por padr&#227;o, leituras podem ser &#8220;Eventualmente Consistentes&#8221;. Isso significa que se voc&#234; gravar um dado agora e ler 1 milissegundo depois, pode ser que pegue o dado antigo.</p><ul><li><p>Se sua aplica&#231;&#227;o aceita isso (ex: listar coment&#225;rios de um blog, hist&#243;rico antigo), use <strong><a href="https://www.alexdebrie.com/posts/dynamodb-eventual-consistency/">Eventual Consistency</a></strong>.</p></li><li><p>Leituras eventuais custam <strong>metade</strong> do pre&#231;o (0.5 RCU por 4KB). &#201; um desconto de 50% na fatura apenas mudando um par&#226;metro na query.</p></li></ul><h4>3. Cuidado com &#205;ndices (GSIs)</h4><p>Cada GSI que voc&#234; cria &#233;, na pr&#225;tica, uma nova tabela oculta. Se voc&#234; insere um item na tabela principal e esse item precisa ser replicado para 3 &#237;ndices, voc&#234; paga a escrita na tabela principal <strong>+</strong> a escrita no &#205;ndice 1 <strong>+</strong> a escrita no &#205;ndice 2 <strong>+</strong> a escrita no &#205;ndice 3. <strong>Um insert pode custar 4x mais caro se voc&#234; tiver muitos &#237;ndices. Pode ficar confuso agora, mas vamos ter um t&#243;pico dedicado para conversar mais.</strong></p><h4>4. O Mito do &#8220;Capacity Planning&#8221; Perfeito</h4><p>Se voc&#234; tem tr&#225;fego previs&#237;vel (ex: processamento em lote toda madrugada), use <strong>Provisioned</strong>. Se seu tr&#225;fego &#233; err&#225;tico e voc&#234; tem medo de <code>Throttling</code>, comece com <strong>On-Demand</strong>. Voc&#234; pode alternar entre os modos uma vez a cada 24 horas.</p><ul><li><p><em>Estrat&#233;gia:</em> Muitos times come&#231;am em On-Demand para aprender o padr&#227;o de uso e, depois de um m&#234;s, mudam para Provisioned ajustado para economizar.</p></li></ul><h2>Query vs. Scan: O Her&#243;i e o Vil&#227;o</h2><p>A diferen&#231;a entre Query e Scan n&#227;o &#233; apenas t&#233;cnica; &#233; a diferen&#231;a entre uma aplica&#231;&#227;o que responde em milissegundos custando centavos e uma aplica&#231;&#227;o que trava o banco e estoura o cart&#227;o de cr&#233;dito da empresa.</p><p>Muitos desenvolvedores que v&#234;m do SQL (onde um SELECT * FROM tabela &#233; comum) tendem a usar o Scan inadvertidamente. Vamos entender por que isso &#233; um erro grave em produ&#231;&#227;o.</p><p>Se o DynamoDB fosse um filme de a&#231;&#227;o, a <strong>Query</strong> seria o atirador de elite: silencioso, preciso e letal. O <strong>Scan</strong> seria o Rambo com uma metralhadora: atira para todo lado, faz muito barulho, gasta toda a muni&#231;&#227;o, mas eventualmente acerta o alvo (depois de destruir o cen&#225;rio inteiro &#128514;).</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!mDJI!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!mDJI!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!mDJI!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!mDJI!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!mDJI!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!mDJI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:13740661,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!mDJI!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!mDJI!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!mDJI!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!mDJI!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5aff14fd-088b-4dfe-a511-c64d9b1c0f8d_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Para dominar o DynamoDB, voc&#234; precisa entender profundamente a mec&#226;nica dessas duas opera&#231;&#245;es.</p><h4>O Her&#243;i: <code>Query</code> (A Opera&#231;&#227;o Cir&#250;rgica)</h4><p>A opera&#231;&#227;o de <strong>Query</strong> &#233; a forma &#8220;correta&#8221; de buscar dados em escala. Ela &#233; projetada para encontrar itens com base na <strong>Chave Prim&#225;ria (PK)</strong>.</p><ul><li><p><strong>Como funciona:</strong> Quando voc&#234; executa uma Query, voc&#234; <strong>obrigatoriamente</strong> fornece a PK. O DynamoDB usa sua fun&#231;&#227;o de hash interna e vai direto para a parti&#231;&#227;o f&#237;sica onde aquele dado reside. Se voc&#234; usar tamb&#233;m a Sort Key (SK), ele vai direto para o ponto exato da lista ordenada.</p></li><li><p><strong>Por que &#233; eficiente:</strong> O DynamoDB l&#234; apenas os itens que satisfazem sua chave. Se sua parti&#231;&#227;o tem 100 itens e voc&#234; pediu 5, ele l&#234; e cobra apenas por esses 5 (arredondando para blocos de 4KB).</p></li><li><p><strong>O Limite:</strong> Uma &#250;nica requisi&#231;&#227;o de Query pode retornar no m&#225;ximo <strong>1 MB</strong> de dados. Se houver mais dados, o DynamoDB devolve uma <code>LastEvaluatedKey</code> (pagina&#231;&#227;o) para voc&#234; fazer a pr&#243;xima requisi&#231;&#227;o e continuar de onde parou.</p></li></ul><blockquote><p>Imagine um livro gigantesco (a tabela). A <code>Query</code> &#233; ir direto ao <strong>&#205;ndice Remissivo</strong>, ver que o t&#243;pico &#8220;Dinossauros&#8221; est&#225; na p&#225;gina 42, abrir a p&#225;gina 42 e ler. Tempo gasto: segundos.</p></blockquote><h4>O Vil&#227;o: <code>Scan</code> (A Arrast&#227;o de Dados)</h4><p>O <strong>Scan</strong> &#233; a opera&#231;&#227;o mais simples de escrever, mas a mais perigosa de rodar.</p><ul><li><p><strong>Como funciona:</strong> O Scan ignora completamente as chaves e &#237;ndices. Ele abre a tabela na primeira p&#225;gina e come&#231;a a ler <strong>item por item</strong>, linha por linha, at&#233; chegar no final da tabela (ou atingir o limite de 1MB).</p></li><li><p><strong>O Desastre da Escalabilidade:</strong> Se sua tabela tem 100 itens, o Scan &#233; r&#225;pido. Se ela tem 100 milh&#245;es de itens, o Scan vai tentar ler os 100 milh&#245;es.</p></li><li><p><strong>O Custo:</strong> Voc&#234; paga por <strong>cada item lido</strong>, mesmo que voc&#234; jogue o item fora depois.</p></li></ul><blockquote><p>Imagine o mesmo livro gigantesco. O <code>Scan</code> &#233; voc&#234; ignorar o &#237;ndice e come&#231;ar a ler o livro da p&#225;gina 1 at&#233; a &#250;ltima p&#225;gina procurando a palavra &#8220;Dinossauros&#8221;. Voc&#234; vai demorar dias (lat&#234;ncia alta) e vai cansar muito (gasto de RCU).</p></blockquote><h3>O Grande Perigo: A Ilus&#227;o do <code>FilterExpression</code></h3><p>Aqui est&#225; a <strong>&#8220;Li&#231;&#227;o Aprendida&#8221;</strong> mais valiosa deste t&#243;pico. &#201; onde 90% dos iniciantes caem.</p><p>Voc&#234; pode pensar: <em>&#8220;Ah, mas o Scan tem um par&#226;metro chamado </em><code>FilterExpression</code><em>. Se eu usar isso para filtrar apenas os usu&#225;rios ativos, eu vou economizar dinheiro, certo?&#8221;</em></p><p><strong>ERRADO. E perigosamente errado.</strong></p><p>&#201; crucial entender a ordem de execu&#231;&#227;o do DynamoDB:</p><ol><li><p><strong>Leitura do Disco:</strong> O DynamoDB vai ao disco e l&#234; o dado (Scan).</p></li><li><p><strong>Cobran&#231;a (O Tax&#237;metro):</strong> A AWS <strong>cobra</strong> as RCUs baseadas no dado que foi lido do disco.</p></li><li><p><strong>Filtragem:</strong> S&#243; <em>agora</em> o DynamoDB aplica o seu <code>FilterExpression</code> e descarta o que n&#227;o serve.</p></li><li><p><strong>Retorno:</strong> Ele te devolve apenas o que sobrou.</p></li></ol><p><strong>Cen&#225;rio Real de Pesadelo:</strong></p><ul><li><p><strong>Sua Tabela:</strong> 1 milh&#227;o de usu&#225;rios.</p></li><li><p><strong>Seu Objetivo:</strong> Achar os 10 usu&#225;rios que moram no &#8220;Acre&#8221;.</p></li><li><p><strong>Sua A&#231;&#227;o:</strong> Fazer um Scan com <code>FilterExpression = &#8220;Estado = :acre&#8221;</code>.</p></li></ul><p><strong>O Resultado:</strong></p><ol><li><p>O DynamoDB l&#234; <strong>1 milh&#227;o</strong> de itens.</p></li><li><p>Voc&#234; paga o consumo de RCU para ler <strong>1 milh&#227;o</strong> de itens (centenas de d&#243;lares se for frequente).</p></li><li><p>O DynamoDB joga fora 999.990 itens.</p></li><li><p>Ele te entrega os 10 itens do Acre.</p></li></ol><p>Para a sua aplica&#231;&#227;o (backend), pareceu que vieram s&#243; 10 itens. Mas a fatura da AWS vai cobrar a leitura da tabela inteira. O filtro economiza apenas <strong>banda de rede</strong> (transfer&#234;ncia de dados), mas n&#227;o economiza <strong>processamento nem dinheiro</strong>.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a></p><h3>Quando usar Scan ent&#227;o?</h3><p>O Scan n&#227;o &#233; in&#250;til. Ele tem seu lugar, mas geralmente &#233; em processos de background, nunca no fluxo s&#237;ncrono do usu&#225;rio (aquela tela de loading girando).</p><p>Use Scan para:</p><ol><li><p><strong>Backups e Exporta&#231;&#245;es:</strong> &#8220;Preciso jogar tudo para o S3 (Data Lake) para rodar Analytics&#8221;.</p></li><li><p><strong>Migra&#231;&#245;es:</strong> &#8220;Preciso ler tudo para mudar um formato de dado&#8221;.</p></li><li><p><strong>Tabelas de Configura&#231;&#227;o Pequenas:</strong> Tabelas com menos de 100 itens que raramente mudam.</p></li></ol><p><strong>Dica de Ouro:</strong> Se voc&#234; precisa fazer um Scan frequente porque n&#227;o sabe a PK, voc&#234; modelou errado. A solu&#231;&#227;o n&#227;o &#233; otimizar o Scan, &#233; criar um <strong>Global Secondary Index (GSI)</strong> para transformar esse padr&#227;o de acesso em uma <strong>Query</strong>.</p><h3>O Segredo dos &#205;ndices Esparsos (Sparse Indexes): Menos &#233; Mais</h3><p>Muitos desenvolvedores acham que um &#205;ndice (GSI) &#233; apenas uma c&#243;pia completa da tabela principal, reordenada por outra coluna. E, na maioria das vezes, &#233; assim que usamos.</p><p>Mas o DynamoDB tem uma regra peculiar e poderosa sobre &#237;ndices:</p><blockquote><p><strong>&#8220;Se um item n&#227;o possui o atributo definido como Chave do &#205;ndice, ele N&#195;O &#233; copiado para o &#205;ndice.&#8221;</strong></p></blockquote><p>Em bancos SQL, se voc&#234; cria um &#237;ndice numa coluna que est&#225; nula (<code>NULL</code>), o banco indexa o nulo. No DynamoDB, o item &#233; simplesmente <strong>ignorado</strong>. Ele n&#227;o existe no mundo do &#237;ndice.</p><p>&#201; aqui que nasce o <strong><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-indexes-general-sparse-indexes.html">Sparse Index</a></strong>.</p><h4>O que &#233; um &#205;ndice Esparso?</h4><p>&#201; um GSI intencionalmente &#8220;incompleto&#8221;. &#201; um &#237;ndice que cont&#233;m apenas uma pequena fra&#231;&#227;o dos itens da sua tabela principal.</p><p>Em vez de indexar <em>todos</em> os 1 milh&#227;o de usu&#225;rios, voc&#234; cria um &#237;ndice que cont&#233;m <em>apenas</em> os usu&#225;rios que atendem a um crit&#233;rio espec&#237;fico.</p><h4>O Cen&#225;rio Real: &#8220;Ache os Admins&#8221;</h4><p>Imagine que voc&#234; tem uma tabela de Usu&#225;rios com <strong>10 milh&#245;es de registros</strong>. Desses, apenas <strong>500 s&#227;o Administradores</strong>.</p><p>Seu chefe pede: <em>&#8220;Crie uma tela que lista todos os Admins do sistema.&#8221;</em></p><ul><li><p><strong>Abordagem Ing&#234;nua (Scan):</strong></p><ul><li><p>Voc&#234; faz um <code>Scan</code> na tabela de 10 milh&#245;es de itens com <code>FilterExpression = &#8220;Role = &#8216;ADMIN&#8217;&#8221;</code>.</p></li><li><p><strong>Custo:</strong> Voc&#234; paga para ler 10 milh&#245;es de itens. Caro e lento.</p></li></ul></li><li><p><strong>Abordagem Padr&#227;o (GSI Comum):</strong></p><ul><li><p>Voc&#234; cria um GSI na coluna <code>Role</code>.</p></li><li><p>O &#237;ndice ter&#225; 10 milh&#245;es de itens (9.999.500 &#8220;USER&#8221; e 500 &#8220;ADMIN&#8221;).</p></li><li><p>Melhor que o Scan, mas voc&#234; paga armazenamento por 10 milh&#245;es de r&#233;plicas e paga WCU (escrita) toda vez que qualquer usu&#225;rio comum for atualizado.</p></li></ul></li><li><p><strong>Sparse Index:</strong></p><ul><li><p>Voc&#234; cria um atributo novo chamado <code>IsAdmin</code>.</p></li><li><p>Para os usu&#225;rios comuns, voc&#234; <strong>n&#227;o cria</strong> esse atributo (deixa ele inexistente).</p></li><li><p>Para os administradores, voc&#234; grava: <code>&#8220;IsAdmin&#8221;: &#8220;Sim&#8221;</code>.</p></li><li><p>Voc&#234; cria um GSI onde a <strong>PK &#233; </strong><code>IsAdmin</code>.</p></li></ul></li></ul><p><strong>A M&#225;gica:</strong> Como 99.9% dos usu&#225;rios <strong>n&#227;o t&#234;m</strong> o atributo <code>IsAdmin</code>, o DynamoDB <strong>n&#227;o copia</strong> esses usu&#225;rios para o &#237;ndice. O seu GSI ter&#225; apenas <strong>500 itens</strong>.</p><ul><li><p><strong>Resultado da Query:</strong> Voc&#234; faz um <code>Scan</code> nesse &#237;ndice esparso.</p></li><li><p><strong>Custo:</strong> Voc&#234; paga para ler apenas 500 itens. &#201; quase de gra&#231;a.</p></li><li><p><strong>Performance:</strong> Instant&#226;nea.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!xdcq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!xdcq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg 424w, https://substackcdn.com/image/fetch/$s_!xdcq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg 848w, https://substackcdn.com/image/fetch/$s_!xdcq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!xdcq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!xdcq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7453349,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/jpeg&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!xdcq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg 424w, https://substackcdn.com/image/fetch/$s_!xdcq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg 848w, https://substackcdn.com/image/fetch/$s_!xdcq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg 1272w, https://substackcdn.com/image/fetch/$s_!xdcq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F0846b08a-3733-4954-a00f-caafd45fb1a7_2816x1536.jpeg 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>Outros Casos de Uso</h4><ol><li><p><strong>Pedidos em Aberto:</strong></p><ul><li><p>Tabela de Pedidos tem milh&#245;es de hist&#243;ricos. Pedidos abertos s&#227;o poucos.</p></li><li><p>Crie um atributo <code>StatusAberto</code>. Remova esse atributo quando o pedido for entregue. O &#237;ndice se &#8220;limpa&#8221; sozinho.</p></li></ul></li><li><p><strong>Soft Delete (Lixeira):</strong></p><ul><li><p>Em vez de apagar o dado, voc&#234; marca com <code>DeletedAt: &#8220;2023-10-27&#8221;</code>.</p></li><li><p>Crie um &#237;ndice na chave <code>DeletedAt</code>.</p></li><li><p>Seu &#237;ndice funciona como uma &#8220;Lixeira&#8221; isolada. Itens ativos (sem essa data) n&#227;o ocupam espa&#231;o l&#225;.</p></li></ul></li><li><p><strong>Mensagens N&#227;o Lidas:</strong></p><ul><li><p>App de chat. A maioria das mensagens j&#225; foi lida. Crie um &#237;ndice apenas nas que t&#234;m flag <code>Unread</code>.</p></li></ul></li></ol><p>Imagine que voc&#234; tem <strong>5.000 contatos</strong> na sua agenda telef&#244;nica (Tabela Principal). Tem todo mundo l&#225;: amigos, ex-colegas, a pizzaria do bairro, o suporte da internet, etc.</p><p>Voc&#234; quer ligar para sua <strong>M&#227;e</strong>.</p><ul><li><p><strong>Sem &#205;ndice Esparso (Scan na Tabela):</strong> Voc&#234; abre a lista completa (A-Z) e rola a tela para baixo passando por <em>todos</em> os 5.000 nomes at&#233; achar &#8220;M&#227;e&#8221;. &#201; demorado e chato.</p></li><li><p><strong>Com &#205;ndice Esparso (A aba &#8220;Favoritos&#8221;):</strong> Voc&#234; clica na estrelinha &#8220;Favorito&#8221; apenas no contato da sua m&#227;e e do seu irm&#227;o. Quando voc&#234; clica na aba <strong>Favoritos</strong> (que &#233; o nosso &#205;ndice Es</p><p>parso), o celular ignora os outros 4.998 contatos. Ele s&#243; te mostra os 2 que voc&#234; marcou.</p></li></ul><p>No DynamoDB, o &#8220;clicar na estrelinha&#8221; &#233; adicionar o atributo. Se o contato n&#227;o tem estrelinha, ele nem aparece na lista de Favoritos. Na imagem abaixo tem um breve resumo.</p><div class="captioned-image-container"><figure><div class="image-link image2" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!N6YY!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp 424w, https://substackcdn.com/image/fetch/$s_!N6YY!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp 848w, https://substackcdn.com/image/fetch/$s_!N6YY!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp 1272w, https://substackcdn.com/image/fetch/$s_!N6YY!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!N6YY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp" width="708" height="107" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/4677b861-0f55-467b-8929-2922a8b45153_708x107.webp&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:107,&quot;width&quot;:708,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:7404,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/webp&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!N6YY!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp 424w, https://substackcdn.com/image/fetch/$s_!N6YY!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp 848w, https://substackcdn.com/image/fetch/$s_!N6YY!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp 1272w, https://substackcdn.com/image/fetch/$s_!N6YY!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F4677b861-0f55-467b-8929-2922a8b45153_708x107.webp 1456w" sizes="100vw" loading="lazy"></picture><div></div></div></div></figure></div><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!T9S7!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!T9S7!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!T9S7!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!T9S7!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!T9S7!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!T9S7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/d460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:13502071,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!T9S7!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!T9S7!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!T9S7!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!T9S7!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fd460a7c7-7b89-4dce-bac0-2414d0d19c5c_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h3>O &#8220;Lado Obscuro&#8221; dos Global Secondary Indexes (GSIs)</h3><p>At&#233; agora, falamos dos GSIs como se fossem a salva&#231;&#227;o da lavoura: &#8220;Quer buscar por e-mail? Cria um GSI! Quer buscar por status? Cria um GSI!&#8221;.</p><p>Eles s&#227;o incr&#237;veis, sim. Mas eles t&#234;m um pre&#231;o. E se voc&#234; n&#227;o configurar direito, esse pre&#231;o vem em duas faturas: na <strong>financeira</strong> (AWS Cost Explorer) e na <strong>t&#233;cnica</strong> (bugs de sincronia).</p><blockquote><p><strong>Talvez eu soe repetitivo em alguns pontos aqui, mas acredite: &#233; melhor ler isso duas vezes agora do que descobrir na sexta-feira &#224; noite com a produ&#231;&#227;o parada</strong>.</p></blockquote><h4>1. A Verdade Nua: Um GSI &#233; outra Tabela &#8220;Clonada&#8221;</h4><p>Quando voc&#234; clica no bot&#227;o &#8220;Criar &#205;ndice&#8221;, a AWS n&#227;o faz m&#225;gica. Ela cria, nos bastidores, <strong>uma tabela inteiramente nova e separada</strong>, gerenciada automaticamente por ela.</p><p>Isso significa que:</p><ul><li><p>Cada vez que voc&#234; <strong>grava</strong> (<code>PutItem</code>) na sua tabela principal, o DynamoDB precisa replicar esse dado para a tabela do GSI.</p></li><li><p><strong>Consequ&#234;ncia:</strong> Voc&#234; paga a escrita <strong>duas vezes</strong>. Uma na tabela principal e outra no &#237;ndice. Se tiver 5 &#237;ndices, voc&#234; paga 6 escritas.</p></li></ul><h4>2. Attribute Projection</h4><p>Aqui est&#225; a configura&#231;&#227;o mais perigosa da AWS. Quando voc&#234; cria um GSI, ele pergunta: <em>&#8220;Quais atributos voc&#234; quer copiar para este &#237;ndice?&#8221;</em></p><p>Op&#231;&#245;es:</p><ol><li><p><strong>KEYS_ONLY (Apenas Chaves):</strong> Copia apenas a PK e a SK.</p><ul><li><p><em>Custo:</em> M&#237;nimo. O item fica min&#250;sculo.</p></li><li><p><em>Uso:</em> Perfeito para verificar exist&#234;ncia ou buscar IDs.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!0ofM!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!0ofM!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!0ofM!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!0ofM!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!0ofM!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!0ofM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5224243,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!0ofM!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!0ofM!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!0ofM!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!0ofM!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa088f9a5-7739-4d3d-8dd1-7be4e49637eb_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li></ul></li><li><p><strong>INCLUDE (Incluir Espec&#237;ficos):</strong> Copia as chaves + alguns campos que voc&#234; escolher (ex: <code>Nome</code>, <code>Data</code>).</p><ul><li><p><em>Custo:</em> M&#233;dio.</p></li><li><p><em>Uso:</em> O ideal. Voc&#234; leva apenas o que precisa para exibir na tela de listagem.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!kb70!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!kb70!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!kb70!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!kb70!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!kb70!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!kb70!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5564815,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!kb70!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!kb70!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!kb70!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!kb70!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F28ce4930-0c64-4d1f-a53e-002158ec02ef_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div></li></ul></li><li><p><strong>ALL (Todos - O Perigo):</strong> Copia o item inteiro.</p><ul><li><p><em>Custo:</em> <strong>Dobro</strong>.</p></li><li><p><em>O Erro Comum:</em> Na pressa, o desenvolvedor marca <code>ALL</code> &#8220;s&#243; para garantir&#8221; que n&#227;o vai faltar dado.</p></li><li><p><em>Resultado:</em> Se seu item tem 4KB, voc&#234; grava 4KB na tabela principal e 4KB no &#237;ndice. Voc&#234; acabou de duplicar sua conta de armazenamento e de WCU sem necessidade.</p></li></ul></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!TVqT!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!TVqT!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!TVqT!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!TVqT!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!TVqT!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!TVqT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5808482,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!TVqT!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!TVqT!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!TVqT!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!TVqT!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9ca500e8-c13b-426a-9f68-89a0462bade5_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><blockquote><p><strong>Alerta:</strong> Se voc&#234; escolher <code>KEYS_ONLY</code> e depois sua aplica&#231;&#227;o tentar ler um atributo que <em>n&#227;o</em> est&#225; no &#237;ndice (ex: o endere&#231;o do usu&#225;rio), o DynamoDB vai cobrar extra para buscar esse dado na tabela principal (isso se chama <em>Fetch</em>). Portanto, projete com sabedoria: nem de menos (que exige fetch), nem de mais (que custa caro).<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-5" href="#footnote-5" target="_self">5</a></p></blockquote><h4>3. O &#8220;Delay&#8221; Fantasma: Consist&#234;ncia Eventual</h4><p>Diferente da tabela principal, onde voc&#234; pode pedir consist&#234;ncia forte (&#8221;quero o dado mais recente agora!&#8221;), <strong>GSIs s&#227;o sempre eventualmente consistentes</strong>.</p><p>A replica&#231;&#227;o da tabela principal para o GSI &#233; ass&#237;ncrona. Geralmente leva milissegundos, mas pode levar segundos em momentos de falha ou carga alta.</p><p><strong>O Cen&#225;rio do Bug &#8220;Imposs&#237;vel&#8221;:</strong></p><ol><li><p>Usu&#225;rio edita o perfil e muda o nome de &#8220;Jo&#227;o&#8221; para &#8220;Jos&#233;&#8221;.</p></li><li><p>Aplica&#231;&#227;o salva na Tabela Principal (Sucesso).</p></li><li><p>A aplica&#231;&#227;o imediatamente redireciona o usu&#225;rio para a lista de perfis.</p></li><li><p>A lista de perfis consulta o <strong>GSI</strong>.</p></li><li><p><strong>O Bug:</strong> O GSI ainda n&#227;o recebeu a atualiza&#231;&#227;o. A tela mostra &#8220;Jo&#227;o&#8221;. O usu&#225;rio acha que o sistema quebrou e clica em salvar de novo.</p></li></ol><p><strong>Como mitigar?</strong> Nunca use GSIs para fluxos que exigem leitura imediata ap&#243;s a escrita (Read-after-Write). Se o usu&#225;rio acabou de salvar, leia da Tabela Principal usando a PK, nunca do &#237;ndice.</p><div><hr></div><p>Chegamos a um momento decisivo. Se voc&#234; trabalha em uma Fintech, E-commerce ou qualquer sistema onde o dado representa <strong>dinheiro</strong> ou <strong>estoque finito</strong>, este cap&#237;tulo &#233; o mais importante da sua leitura.</p><p>Durante muito tempo, o maior argumento dos cr&#237;ticos de NoSQL era: <em>&#8220;Bancos n&#227;o relacionais n&#227;o t&#234;m transa&#231;&#245;es ACID. Voc&#234; n&#227;o pode confiar neles para opera&#231;&#245;es financeiras.&#8221;</em></p><p>Bom, essa afirma&#231;&#227;o envelheceu mal. O DynamoDB suporta, sim, transa&#231;&#245;es ACID (Atomicidade, Consist&#234;ncia, Isolamento e Durabilidade) completas entre m&#250;ltiplos itens e tabelas. Mas, como tudo na AWS, isso n&#227;o acontece &#8220;automaticamente&#8221; e tem um pre&#231;o espec&#237;fico que voc&#234; precisa calcular.</p><div><hr></div><h3>Transa&#231;&#245;es ACID: O Protocolo do &#8220;Tudo ou Nada&#8221;</h3><p>Imagine o cen&#225;rio cl&#225;ssico de transfer&#234;ncia banc&#225;ria:</p><ol><li><p>Debitar R$ 100,00 da conta da <strong>Maria</strong>.</p></li><li><p>Creditar R$ 100,00 na conta do <strong>Jo&#227;o</strong>.</p></li></ol><p>Num banco SQL tradicional, voc&#234; abriria uma <code>BEGIN TRANSACTION</code>, rodaria os dois updates e daria <code>COMMIT</code>. Se a luz acabasse no meio, o <code>ROLLBACK</code> garantiria que o dinheiro n&#227;o sumiria.</p><p>No DynamoDB, n&#243;s usamos a API <code>TransactWriteItems</code>. Ela permite agrupar at&#233; <strong>100 opera&#231;&#245;es</strong> (Put, Update, Delete ou ConditionCheck) em um &#250;nico pacote at&#244;mico.</p><p>A regra &#233; bin&#225;ria: <strong>Ou todas as opera&#231;&#245;es do pacote t&#234;m sucesso, ou nenhuma acontece.</strong> Se a conta da Maria tiver saldo, mas a conta do Jo&#227;o tiver sido bloqueada no mesmo milissegundo, a transa&#231;&#227;o falha e o dinheiro da Maria permanece intacto.</p><h4>O Custo da Seguran&#231;a: Por que custa o dobro?</h4><p>Aqui est&#225; a matem&#225;tica que voc&#234; precisa explicar para o seu gerente financeiro.</p><p>Uma opera&#231;&#227;o de escrita normal (<code>PutItem</code>) custa 1 WCU (para itens at&#233; 1KB). Uma opera&#231;&#227;o transacional (<code>TransactWriteItems</code>) custa <strong>2 WCUs</strong>.</p><p><strong>Por que o dobro?</strong> Porque o DynamoDB precisa realizar um protocolo de <strong>Two-Phase Commit</strong> (Commit em Duas Fases) para garantir a integridade.</p><ol><li><p><strong>Fase 1: Prepara&#231;&#227;o (Prepare Phase):</strong> O DynamoDB vai at&#233; todos os n&#243;s onde os itens da transa&#231;&#227;o vivem e pergunta: <em>&#8220;Voc&#234;s podem realizar essa escrita? As condi&#231;&#245;es (ConditionCheck) s&#227;o v&#225;lidas? Algu&#233;m est&#225; bloqueando esse item?&#8221;</em>. O banco reserva o item.</p></li><li><p><strong>Fase 2: Confirma&#231;&#227;o (Commit Phase):</strong> Se todos os n&#243;s responderam &#8220;SIM&#8221;, o DynamoDB escreve os dados de fato e libera o bloqueio.</p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!3LFd!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!3LFd!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png 424w, https://substackcdn.com/image/fetch/$s_!3LFd!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png 848w, https://substackcdn.com/image/fetch/$s_!3LFd!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!3LFd!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!3LFd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png" width="1456" height="803" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:803,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:5773585,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!3LFd!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png 424w, https://substackcdn.com/image/fetch/$s_!3LFd!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png 848w, https://substackcdn.com/image/fetch/$s_!3LFd!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!3LFd!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F75abf797-e3d7-4eaf-afad-2226c3367082_2784x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Voc&#234; paga 1 WCU pela prepara&#231;&#227;o e 1 WCU pela escrita. Total: 2 WCUs. O mesmo vale para leituras transacionais (<code>TransactGetItems</code>): custam o dobro de RCUs para garantir que voc&#234; est&#225; lendo o dado isolado e perfeitamente consistente naquele instante.</p><h3>ConditionCheck</h3><p>Muitas vezes, voc&#234; n&#227;o quer escrever um dado, voc&#234; s&#243; quer garantir que uma condi&#231;&#227;o &#233; verdadeira antes de mexer em <em>outro</em> dado.</p><p>Imagine um sistema de estoque:</p><ul><li><p><strong>A&#231;&#227;o:</strong> O cliente comprou o &#250;ltimo iPhone.</p></li><li><p><strong>Opera&#231;&#227;o Transacional:</strong></p><ol><li><p><code>UpdateItem</code>: Tabela <code>Pedidos</code> (Criar pedido do Cliente).</p></li><li><p><code>UpdateItem</code>: Tabela <code>Estoque</code> (Diminuir quantidade em -1).</p></li><li><p><code>ConditionCheck</code>: Tabela <code>Produtos</code> (Verificar se <code>Status</code> == &#8216;ATIVO&#8217;).</p></li></ol></li></ul><p>Se o produto estiver inativo, o <code>ConditionCheck</code> falha. <a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html">Como ele faz parte do pacote transacional, </a><strong><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html">o pedido n&#227;o &#233; criado e o estoque n&#227;o &#233; baixado</a></strong><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html">. O </a><code>ConditionCheck</code><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/transaction-apis.html"> &#233; o guardi&#227;o da sua regra de neg&#243;cio.</a></p><h3>O Pesadelo da Rede e a Solu&#231;&#227;o: Idempot&#234;ncia (<code>ClientRequestToken</code>)</h3><p>Aqui entra um problema que tira o sono de quem desenvolve APIs financeiras. Imagine que seu backend manda a transa&#231;&#227;o para a AWS: <em>&#8220;Transfere 50 reais&#8221;</em>. A AWS processa, transfere o dinheiro, e manda a resposta: <em>&#8220;Sucesso! 200 OK&#8221;</em>. <strong>Mas... a sua internet piscou.</strong> O cabo de rede soltou. O pacote de resposta se perdeu no caminho.</p><p>Para o seu backend, deu <strong>Timeout</strong>. Voc&#234; n&#227;o sabe se o dinheiro foi transferido ou n&#227;o. O que sua aplica&#231;&#227;o faz? <strong>Tenta de novo (Retry).</strong> Se voc&#234; n&#227;o se proteger, o DynamoDB vai receber o segundo pedido e transferir <em>mais</em> 50 reais. O cliente pagou duas vezes.</p><p><strong>Como resolver? Use o </strong><code>ClientRequestToken</code><strong>.</strong></p><p>O <code>ClientRequestToken</code> &#233; uma string &#250;nica (geralmente um UUID) que voc&#234; gera no <strong>seu c&#243;digo</strong> antes de chamar o DynamoDB.</p><ol><li><p>Voc&#234; envia a transa&#231;&#227;o com Token: <code>abc-123</code>.</p></li><li><p>O DynamoDB processa e salva internamente que o Token <code>abc-123</code> j&#225; foi executado.</p></li><li><p>A resposta falha na rede.</p></li><li><p>Voc&#234; envia a transa&#231;&#227;o <em>de novo</em> com o <strong>mesmo</strong> Token: <code>abc-123</code>.</p></li><li><p>O DynamoDB olha e diz: <em>&#8220;Opa, eu j&#225; fiz isso 1 segundo atr&#225;s. N&#227;o vou debitar de novo. Toma aqui a resposta de Sucesso que voc&#234; perdeu.&#8221;</em></p></li></ol><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!ecPq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!ecPq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!ecPq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!ecPq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!ecPq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!ecPq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:12228073,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!ecPq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!ecPq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!ecPq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!ecPq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F9c47a56e-449b-4994-a176-ecaf1350ca1e_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p></p><blockquote><p><strong>Dica de Implementa&#231;&#227;o:</strong> Garanta que esse Token seja gerado o mais &#8220;alto&#8221; poss&#237;vel na sua arquitetura (de prefer&#234;ncia no frontend ou no in&#237;cio da controller da API) e passado adiante at&#233; o banco. Isso garante idempot&#234;ncia ponta a ponta.</p></blockquote><h3>Cuidados na API e Tratamento de Erros</h3><p>Quando uma transa&#231;&#227;o falha, o DynamoDB lan&#231;a uma exce&#231;&#227;o espec&#237;fica: <code>TransactionCanceledException</code>.</p><p>Dentro dessa exce&#231;&#227;o, existe um campo chamado <code>CancellationReasons</code> (Raz&#245;es do Cancelamento). Isso &#233; uma lista que corresponde &#224; ordem dos itens que voc&#234; tentou gravar.</p><p>Se voc&#234; enviou 3 itens na transa&#231;&#227;o e a lista de raz&#245;es voltar assim: <code>[None, &#8220;ConditionalCheckFailed&#8221;, None]</code></p><p>Isso significa que:</p><ul><li><p>O Item 1 estava OK.</p></li><li><p><strong>O Item 2 falhou na checagem de condi&#231;&#227;o.</strong></p></li><li><p>O Item 3 estava OK.</p></li></ul><p>Mas, lembre-se: como &#233; uma transa&#231;&#227;o, <strong>nada foi salvo</strong>. Seu c&#243;digo precisa ser inteligente o suficiente para ler esse erro, entender <em>qual</em> condi&#231;&#227;o falhou (ex: &#8220;Sem saldo&#8221; ou &#8220;Sem estoque&#8221;) e devolver uma mensagem amig&#225;vel para o usu&#225;rio, em vez de um simples &#8220;Erro 500&#8221;.</p><h3>Evitando &#8220;Hot Partitions&#8221;: Quando todo mundo tenta passar na mesma porta</h3><p>Lembra daquela nossa analogia do Ped&#225;gio com 100 cabines? Vamos resgat&#225;-la porque ela explica perfeitamente o fen&#244;meno da &#8220;Parti&#231;&#227;o Quente&#8221; (<a href="https://aws.amazon.com/pt/blogs/database/part-2-scaling-dynamodb-how-partitions-hot-keys-and-split-for-heat-impact-performance/">Hot Partition</a>).</p><p>Voc&#234; contratou o DynamoDB e ele te deu 100 cabines (parti&#231;&#245;es) para distribuir seus carros (dados). O ideal &#233; que os carros se espalhem e usem as 100 cabines ao mesmo tempo.</p><p>Mas, se voc&#234; escolher a <strong>Partition Key</strong> <strong>errada</strong>, voc&#234; pode acabar for&#231;ando <strong>todos</strong> os carros a tentarem passar apenas pela <strong>Cabine N&#250;mero 1</strong>, enquanto as outras 99 ficam vazias.</p><p>O resultado? Engarrafamento monstro na Cabine 1. O guarda de tr&#226;nsito (DynamoDB) come&#231;a a barrar os carros. Isso &#233; o que chamamos de <strong>Hot Partition</strong>.</p><h4>O Erro Cl&#225;ssico: Datas ou Sequenciais na PK</h4><p>Aqui entra a confus&#227;o comum:</p><blockquote><p><em>&#8220;No in&#237;cio, dissemos que Data na Sort Key (SK) &#233; &#243;timo. Agora estou dizendo que Data na Partition Key (PK) pode ser um desastre.&#8221;</em></p></blockquote><p><strong>A Diferen&#231;a Vital:</strong></p><ul><li><p><strong>Data na SK (Sort Key):</strong> &#211;timo! Porque voc&#234; j&#225; escolheu a cabine (pelo CPF do cliente, por exemplo) e agora est&#225; apenas organizando a fila dentro daquela cabine.</p></li><li><p><strong>Data na PK (Partition Key):</strong> Perigoso! Porque a PK define <strong>qual cabine</strong> usar.</p></li></ul><p><strong>O Exemplo no Sistema Financeiro:</strong> Imagine que voc&#234; tem uma tabela de <strong>&#8220;Logs de Transa&#231;&#245;es do Dia&#8221;</strong> para auditoria. Muitos desenvolvedores pensam: <em>&#8220;Vou agrupar tudo pela data de hoje.&#8221;</em></p><ul><li><p><strong>PK Escolhida:</strong> <code>DATA#2023-12-13</code> (A data de hoje).</p></li><li><p><strong>O Cen&#225;rio:</strong> Voc&#234; tem 50 microsservi&#231;os bombardeando essa tabela com logs de Pix, TED, Boletos e Pagamentos.</p></li><li><p><strong>O Problema:</strong> Como hoje &#233; dia 13, <strong>100% das escritas</strong> ter&#227;o a mesma PK (<code>DATA#2023-12-13</code>).</p><ul><li><p>Isso significa que <strong>100% do tr&#225;fego</strong> vai para uma &#250;nica parti&#231;&#227;o f&#237;sica.</p></li><li><p>Voc&#234; tem o poder de processamento de todo o cluster da AWS &#224; sua disposi&#231;&#227;o, mas est&#225; usando a capacidade de apenas um n&#243;.</p></li></ul></li></ul><p>O mesmo vale para <strong>IDs Sequenciais Simples</strong>. Se sua PK for <code>ORDEM#1000</code>, <code>ORDEM#1001</code>, <code>ORDEM#1002</code>... e essas ordens s&#227;o criadas muito r&#225;pido, o DynamoDB pode alocar esses n&#250;meros sequenciais vizinhos na mesma parti&#231;&#227;o f&#237;sica, criando um ponto de calor.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!89vR!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!89vR!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!89vR!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!89vR!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!89vR!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!89vR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png" width="1456" height="794" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:794,&quot;width&quot;:1456,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:13739496,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/181480442?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!89vR!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png 424w, https://substackcdn.com/image/fetch/$s_!89vR!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png 848w, https://substackcdn.com/image/fetch/$s_!89vR!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png 1272w, https://substackcdn.com/image/fetch/$s_!89vR!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F3c9b498f-5a6b-4b79-8724-5d922f9edd45_2816x1536.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><h4>O Fantasma do &#8220;Throttling&#8221;</h4><p>Quando voc&#234; cria esse &#8220;ponto de calor&#8221;, o DynamoDB levanta uma placa de &#8220;Pare&#8221;. Isso &#233; o <strong>Throttling</strong>.</p><p>Tecnicamente, voc&#234; recebe um erro <code>ProvisionedThroughputExceededException</code>.</p><ul><li><p><strong>Traduzindo:</strong> &#8220;Voc&#234; excedeu a velocidade permitida nesta cabine espec&#237;fica&#8221;.</p></li><li><p><strong>Consequ&#234;ncia:</strong> Sua API come&#231;a a tomar erro 400/500, transa&#231;&#245;es financeiras falham e o sistema fica inst&#225;vel, mesmo que voc&#234; esteja pagando por uma capacidade total gigantesca (que est&#225; ociosa nas outras parti&#231;&#245;es).</p></li></ul><h4>A Solu&#231;&#227;o: Write Sharding (Dividir para Conquistar)</h4><p>Se voc&#234; <strong>realmente</strong> precisa agrupar dados por dia (ou qualquer chave com alta concentra&#231;&#227;o de escrita), voc&#234; precisa usar a t&#233;cnica de <strong><a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-indexes-gsi-sharding.html">Write Sharding</a></strong>.</p><p>A ideia &#233; adicionar um &#8220;sufixo aleat&#243;rio&#8221; ao final da sua PK para espalhar os dados.</p><p><strong>Como funciona:</strong> Em vez de gravar tudo em <code>DATA#2023-12-13</code>, sua aplica&#231;&#227;o sorteia um n&#250;mero de 1 a 10 (ou 1 a N) e adiciona na chave.</p><ul><li><p><code>DATA#2023-12-13_1</code> (Vai para a parti&#231;&#227;o A)</p></li><li><p><code>DATA#2023-12-13_2</code> (Vai para a parti&#231;&#227;o B)</p></li><li><p>...</p></li><li><p><code>DATA#2023-12-13_10</code> (Vai para a parti&#231;&#227;o J)</p></li></ul><p><strong>O Resultado:</strong> Agora voc&#234; tem 10 cabines do ped&#225;gio trabalhando simultaneamente para processar o dia 13. Voc&#234; multiplicou sua capacidade de escrita por 10.</p><p><strong>Trade-off:</strong> Na hora de ler, voc&#234; n&#227;o pode buscar apenas por <code>DATA#2023-12-13</code>. Voc&#234; ter&#225; que fazer 10 queries em paralelo (uma para cada sufixo <code>_1</code> a <code>_10</code>) e juntar os resultados na sua aplica&#231;&#227;o. D&#225; mais trabalho para ler? Sim. Mas &#233; o &#250;nico jeito de escalar escritas massivas sem gerar Hot Partitions.</p><p><strong>Resumo:</strong></p><blockquote><p>Use datas para <strong>ordenar</strong> (Sort Key), n&#227;o para <strong>agrupar</strong> (Partition Key) volumes massivos. Se precisar agrupar por data, quebre o dia em pedacinhos (Sharding) para n&#227;o engarrafar o ped&#225;gio.</p></blockquote><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas publica&#231;&#245;es e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2>Conclus&#227;o</h2><p>Ufa! Quanta coisa, n&#233;?</p><p>Se voc&#234; chegou at&#233; aqui, respire fundo. N&#243;s acabamos de percorrer o caminho que separa quem apenas &#8220;usa&#8221; o DynamoDB de quem realmente <strong>domina</strong> a ferramenta.</p><p>Eu sei que, em alguns momentos, a vontade &#233; voltar correndo para o conforto do SQL, onde um <code>JOIN</code> resolve tudo e o <code>SELECT *</code> n&#227;o custa os olhos da cara. Mas a verdade &#233; que o desconforto que voc&#234; sentiu lendo sobre <strong>Single Table Design</strong>, <strong>Invers&#227;o de &#205;ndices</strong> e <strong>C&#225;lculo de WCU</strong> &#233; apenas o som da sua &#8220;mentalidade relacional&#8221; quebrando para dar lugar a algo novo.</p><p>O que vimos aqui n&#227;o foi apenas um manual de &#8220;como apertar bot&#245;es na AWS&#8221;. Foi um guia de sobreviv&#234;ncia baseado em cicatrizes reais de produ&#231;&#227;o:</p><ol><li><p>Aprendemos que <strong>Access Patterns</strong> n&#227;o s&#227;o opcionais: eles s&#227;o o projeto da sua casa. Sem eles, voc&#234; constr&#243;i uma mans&#227;o sem portas.</p></li><li><p>Vimos que <strong>Single Table Design</strong> pode parecer ca&#243;tico, mas &#233; a orquestra&#231;&#227;o perfeita para performance quando bem executado (lembra do nosso case financeiro com Contratos e Simula&#231;&#245;es?).</p></li><li><p>Entendemos que o <strong>Bolso</strong> (custos de WCU/RCU) &#233; o melhor arquiteto: t&#233;cnicas como <strong>Sparse Indexes</strong> e o padr&#227;o <strong>Claim Check com S3</strong> salvam or&#231;amentos.</p></li><li><p>E, principalmente, descobrimos que o DynamoDB &#233; implac&#225;vel com erros de modelagem, mas incrivelmente generoso em <strong>escala e velocidade</strong> quando voc&#234; joga pelas regras dele.</p></li></ol><p><strong>O meu conselho final para voc&#234; &#233;:</strong> N&#227;o tente aplicar as 10 t&#233;cnicas amanh&#227; cedo no sistema cr&#237;tico da empresa. Comece pequeno. Pegue aquele microsservi&#231;o novo, aquela tabela de logs, aquele sistema de notifica&#231;&#245;es. Abra o Excel, desenhe seus Access Patterns, discuta com seu time se o CPF deve ir na PK ou se &#233; melhor usar um UUID.</p><p>O DynamoDB &#233; como uma Ferrari: se voc&#234; tentar dirigir como se fosse um carro popular voc&#234; vai bater no primeiro poste. Mas, se voc&#234; aprender a pilotar... n&#227;o existe limite de velocidade para o que voc&#234; pode construir.</p><p>Espero que este artigo tenha facilitado o seu caminho e evitado algumas das pedras nas quais eu tropecei. &#128517; </p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>N&#227;o se assuste com a palavra. <strong>Cardinalidade &#233; apenas uma medida de &#8220;variedade&#8221;.</strong></p><p>Imagine que voc&#234; est&#225; chegando em um ped&#225;gio gigante com 100 cabines (parti&#231;&#245;es do banco de dados).</p><ul><li><p><strong>Baixa Cardinalidade (O Pesadelo):</strong> &#201; como se voc&#234; tivesse uma chave chamada <code>Status</code> com apenas 2 valores: &#8220;Aprovado&#8221; ou &#8220;Pendente&#8221;.</p><ul><li><p><em>Resultado:</em> Mesmo tendo 100 cabines dispon&#237;veis, todos os seus carros (dados) v&#227;o tentar passar apenas pelas cabines 1 e 2. O resto fica vazio. Gera engarrafamento, lentid&#227;o e o banco rejeita seus dados.</p></li></ul></li><li><p><strong>Alta Cardinalidade (O Sonho):</strong> &#201; quando voc&#234; usa chaves com milh&#245;es de valores &#250;nicos, como <code>UserID</code>, <code>CPF</code> ou <code>UUID</code>.</p><ul><li><p><em>Resultado:</em> Os carros se espalham perfeitamente. Tem carro passando na cabine 1, na 50, na 99... O tr&#226;nsito flui, ningu&#233;m pega fila e seu banco voa baixo.</p></li></ul></li></ul><p><strong>Resumo:</strong> No DynamoDB, queremos que os dados sejam &#8220;anti-sociais&#8221;. Quanto mais espalhados eles estiverem, melhor. Por isso, amamos alta cardinalidade.</p><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>Em tradu&#231;&#227;o livre, s&#227;o os <strong>Padr&#245;es de Acesso</strong>.</p><p>Basicamente, um Access Pattern &#233; <strong>uma lista detalhada de todas as perguntas</strong> que sua aplica&#231;&#227;o far&#225; ao banco de dados.</p><p>No modelo relacional (SQL), voc&#234; pode se dar ao luxo de criar as tabelas primeiro e depois &#8220;descobrir&#8221; como vai consult&#225;-las usando <code>JOINs</code> e <code>WHEREs</code> complexos. O banco se vira para te responder.</p><p>No DynamoDB, essa flexibilidade n&#227;o existe (ou custa muito caro via <code>Scan</code>). Voc&#234; precisa saber a pergunta <strong>antes</strong> de construir a tabela.</p><p><strong>Como mapear um Access Pattern na pr&#225;tica?</strong> Antes de escrever uma linha de c&#243;digo, voc&#234; deve abrir uma planilha (o Excel &#233; o melhor amigo do arquiteto NoSQL) e listar cada funcionalidade do sistema:</p><ol><li><p><strong>Access Pattern 1:</strong> &#8220;Buscar usu&#225;rio pelo Email.&#8221;</p></li><li><p><strong>Access Pattern 2:</strong> &#8220;Listar todos os pedidos de um usu&#225;rio ordenados por data.&#8221;</p></li><li><p><strong>Access Pattern 3:</strong> &#8220;Buscar detalhes de um produto pelo SKU.&#8221;</p></li></ol><p><strong>Por que isso define a estrutura?</strong> Cada pergunta dita a regra da sua Chave Prim&#225;ria:</p><ul><li><p>Para atender o <strong>Padr&#227;o 1</strong>, sua PK precisa ser o Email.</p></li><li><p>Para atender o <strong>Padr&#227;o 2</strong>, sua PK precisa ser o UserID e sua SK precisa ser a Data do Pedido.</p></li></ul><p><strong>A Regra:</strong></p><blockquote><p><em>&#8220;Se n&#227;o estiver no seu documento de Access Patterns, sua aplica&#231;&#227;o n&#227;o vai conseguir buscar com performance. No DynamoDB, voc&#234; modela as queries, n&#227;o os dados.&#8221;</em></p></blockquote><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p><strong>O que s&#227;o 400KB na ponta do l&#225;pis?</strong></p><p>&#8203;Tecnicamente, 1 KB = 1024 bytes.</p><p>Portanto, o limite do DynamoDB &#233; 409.600 bytes.</p><p>&#8203;Em uma codifica&#231;&#227;o de texto padr&#227;o da web (UTF-8), onde cada caractere comum (letras e n&#250;meros) ocupa 1 byte, isso equivale a 400.000 caracteres.</p><p>Se considerarmos que uma p&#225;gina m&#233;dia de um livro impresso tem cerca de 2.500 caracteres (com espa&#231;os), um &#250;nico item do DynamoDB pode armazenar:</p><p>&#8203;160 p&#225;ginas de texto puro.</p><p>&#8203;Ou seja, guardar um item de 400KB &#233; o equivalente a tentar enfiar metade do livro "Harry Potter e a Pedra Filosofal". &#201; muita coisa para processar em tempo real.</p><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>Muitos leitores podem estar questionando: <em>&#8220;Ent&#227;o o FilterExpression &#233; in&#250;til?&#8221;</em> N&#227;o! Ele &#233; &#250;til para limpar o payload de retorno (economizar banda de rede), mas ele &#233; <strong>financeiramente in&#250;til</strong> para economizar leitura no banco.</p><p><strong>A Regra de Ouro:</strong></p><blockquote><p><em>&#8220;O DynamoDB cobra pelo que ele l&#234; do disco, n&#227;o pelo que ele entrega para voc&#234;.&#8221;</em></p></blockquote><p>Se o seu filtro descarta dados, voc&#234; est&#225; pagando para ler lixo. Aqui est&#227;o as 3 alternativas arquiteturais para substituir um <code>FilterExpression</code> pesado:</p><h4>1. Mova o dado para a Sort Key (SK)</h4><p>Se voc&#234; vive filtrando por Data ou Status, esses dados devem fazer parte da sua Chave de Ordena&#231;&#227;o.</p><ul><li><p><strong>Antes (Errado):</strong> <code>Query</code> na PK &#8220;USUARIO#123&#8221; e <code>FilterExpression</code> &#8220;Data &gt; 2023-01-01&#8221;. (O banco l&#234; todo o hist&#243;rico do usu&#225;rio e filtra depois).</p></li><li><p><strong>Depois (Correto):</strong> Coloque a data na SK (<code>PEDIDO#2023-01-01</code>). Use a condi&#231;&#227;o <code>KeyConditionExpression</code> com o operador <code>begins_with</code> ou <code>between</code>. (O banco vai direto no bloco de dados de 2023 e ignora o resto. Voc&#234; paga apenas pelo que pediu).</p></li></ul><h4>2. Use &#205;ndices Esparsos (Sparse Indexes)</h4><p>Imagine que voc&#234; quer buscar apenas os pedidos com <code>Status = &#8220;CANCELADO&#8221;</code>, mas eles representam s&#243; 1% da sua tabela.</p><ul><li><p><strong>A T&#233;cnica:</strong> Crie um GSI (Global Secondary Index) onde a PK &#233; o <code>PedidoID</code> e a SK &#233; <code>StatusCancelado</code>.</p></li><li><p><strong>O Truque:</strong> No DynamoDB, se um item n&#227;o tem o atributo usado na chave do &#237;ndice, <strong>ele n&#227;o &#233; copiado para o &#237;ndice</strong>.</p></li><li><p><strong>O Resultado:</strong> Seu &#237;ndice ter&#225; apenas os 1% de pedidos cancelados. Fazer um <code>Scan</code> nesse &#237;ndice min&#250;sculo &#233; extremamente r&#225;pido e barato. Voc&#234; transformou um &#8220;filtro&#8221; em uma &#8220;tabela VIP&#8221; s&#243; com os dados que te interessam.</p></li></ul><h4>3. Filtre no lado da Aplica&#231;&#227;o (Se o volume for pequeno)</h4><p>Se voc&#234; j&#225; fez uma Query eficiente pela PK+SK e retornou 50 itens, e agora quer filtrar apenas os que s&#227;o &#8220;cor azul&#8221;, fa&#231;a isso no c&#243;digo da sua aplica&#231;&#227;o (Node.js, Python, C#).</p><ul><li><p><strong>Por que?</strong> A CPU do seu servidor de aplica&#231;&#227;o (Lambda/EC2) geralmente &#233; mais barata e escal&#225;vel que a RCU do DynamoDB. N&#227;o gaste a intelig&#234;ncia do banco para fazer filtros triviais em conjuntos de dados pequenos.</p></li></ul><p><strong>Resumo:</strong> Use <code>KeyConditionExpression</code> (Chaves) para cortar o grosso dos dados. Use <code>FilterExpression</code> (Atributos) apenas para aparar as arestas finais.</p><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-5" href="#footnote-anchor-5" class="footnote-number" contenteditable="false" target="_self">5</a><div class="footnote-content"><p>Antes de demonizar completamente a proje&#231;&#227;o <code>ALL</code>, precisamos entender o custo de <strong>n&#227;o</strong> ter o dado no &#237;ndice.</p><p>Imagine que voc&#234; projetou apenas as chaves (<code>KEYS_ONLY</code>) para economizar na escrita. Sua query no GSI retorna a lista de pedidos, mas agora seu frontend precisa mostrar o <strong>Valor Total</strong> do pedido, e esse dado <strong>n&#227;o</strong> est&#225; no &#237;ndice.</p><p>O que acontece?</p><ol><li><p>O DynamoDB l&#234; o &#237;ndice (Gasto de RCU).</p></li><li><p>O DynamoDB percebe que falta o &#8220;Valor Total&#8221;.</p></li><li><p>Ele &#233; obrigado a ir na Tabela Principal buscar o resto do item. Isso se chama <strong>Fetch</strong>.</p></li><li><p><strong>O Custo:</strong> Voc&#234; paga <strong>2 Leituras</strong> (uma no &#237;ndice, uma na tabela) e a lat&#234;ncia dobra.</p></li></ol><p><strong>Quando usar </strong><code>ALL</code><strong> (e pagar o pre&#231;o sorrindo):</strong> Se o seu Access Pattern &#233; <strong>intensivo em leitura (Read-Heavy)</strong> e o dado raramente muda (pouca escrita), projetar <code>ALL</code> &#233; vantajoso. Voc&#234; paga mais caro uma &#250;nica vez na escrita, mas economiza metade do custo e do tempo em todas as milh&#245;es de leituras subsequentes, pois o &#237;ndice j&#225; entrega o &#8220;pacote completo&#8221; sem precisar bater na tabela principal.</p><p><strong>Resumo do C&#225;lculo:</strong></p><ul><li><p><strong>Muita Escrita / Pouca Leitura:</strong> Fuja do <code>ALL</code>. Use <code>KEYS_ONLY</code> ou <code>INCLUDE</code>.</p></li><li><p><strong>Muita Leitura / Pouca Escrita:</strong> O <code>ALL</code> pode ser seu melhor amigo para performance.</p></li></ul><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Princípios Essenciais de Engenharia para Sistemas Financeiros]]></title><description><![CDATA[Em sistemas financeiros, n&#227;o existe "quase". Um c&#225;lculo tem que ser exato, repet&#237;vel e, acima de tudo, estar em conformidade com a lei.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-financeira</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-financeira</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Thu, 25 Sep 2025 16:55:44 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/fd05bdd5-8252-4717-9079-2d5aafccd573_420x300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Se tem uma coisa que todo programador descobre cedo ou tarde &#233; que <strong>mexer com dinheiro n&#227;o &#233; s&#243; c&#243;digo, &#233; responsabilidade</strong>.</p><p>Em um e-commerce, se um bug repete um item no carrinho, o cliente se irrita &#8212; mas a loja pode estornar.</p><p> Mas em um sistema financeiro, se um bug duplica uma liquida&#231;&#227;o, o banco pode tomar um preju&#237;zo milion&#225;rio em segundos (e voc&#234; vira o &#8220;nome da reuni&#227;o&#8221; no dia seguinte &#128517;).</p><p>&#201; por isso que os sistemas financeiros t&#234;m uma engenharia diferente: eles n&#227;o s&#227;o apenas APIs que salvam dados em tabelas, mas <strong>m&#225;quinas de precis&#227;o</strong>, onde cada centavo precisa ter origem, destino e motivo documentados.</p><p>Neste artigo, eu quero compartilhar <strong>princ&#237;pios que aprendi na pr&#225;tica</strong> construindo e mantendo sistemas de cr&#233;dito, cobran&#231;a, Pix e afins. S&#227;o li&#231;&#245;es que todo engenheiro de software pode aplicar para entender por que, no mundo financeiro, <strong>imutabilidade, idempot&#234;ncia e auditoria</strong> n&#227;o s&#227;o buzzwords &#8212; s&#227;o sobreviv&#234;ncia. &#128579;</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Inscreva-se gratuitamente!</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h3><strong>Princ&#237;pio n&#186; 1: O Passado &#233; Imut&#225;vel</strong></h3><p>Antes de falarmos de bancos de dados, faturas ou regulamenta&#231;&#245;es, precisamos falar de um conceito que vive no cora&#231;&#227;o do nosso c&#243;digo: a <strong>Imutabilidade</strong>.</p><h4><strong>A Origem do Conceito: Da Palavra &#224; Programa&#231;&#227;o Funcional</strong></h4><p>A palavra "imut&#225;vel" vem do Latim <code>immutabilis</code>: <code>im-</code> (prefixo de nega&#231;&#227;o) + <code>mutabilis</code> (mut&#225;vel, que pode ser mudado). A defini&#231;&#227;o &#233; literal: <strong>algo que n&#227;o pode ser alterado.</strong></p><p>Na engenharia de software, essa ideia n&#227;o &#233; nova. Ela &#233; um dos pilares da <strong>programa&#231;&#227;o funcional (PF)</strong>, um paradigma popularizado por linguagens como Lisp e Haskell. A principal motiva&#231;&#227;o da PF &#233; escrever c&#243;digo mais previs&#237;vel e livre de surpresas, e a imutabilidade &#233; a sua principal ferramenta para isso.</p><p>A filosofia &#233; simples: uma vez que um dado (um objeto, uma vari&#225;vel) &#233; criado, seu estado nunca mais deve mudar. Se voc&#234; precisa de uma vers&#227;o modificada, voc&#234; n&#227;o altera o original; voc&#234; cria um novo dado a partir do original com a altera&#231;&#227;o desejada.</p><h4><strong>Por que Precisamos nos Atentar a Isso no C&#243;digo?</strong></h4><p>A resposta curta &#233;: para evitar <strong>efeitos colaterais (</strong><em><strong>side effects</strong></em><strong>)</strong>.</p><p>Um efeito colateral ocorre quando uma fun&#231;&#227;o, al&#233;m de retornar um valor, modifica algum estado fora de seu escopo &#8212; como alterar um objeto que foi passado para ela como par&#226;metro. Isso transforma seu programa em um campo minado de bugs dif&#237;ceis de rastrear.</p><p>Imagine um objeto <code>usuario</code> que &#233; compartilhado por v&#225;rias partes do seu sistema. Uma fun&#231;&#227;o <code>A</code> o utiliza para exibir o nome na tela. Ao mesmo tempo, uma fun&#231;&#227;o <code>B</code> &#233; chamada para atualizar o e-mail desse usu&#225;rio. Se a fun&#231;&#227;o <code>B</code> <strong>muta</strong> (altera) diretamente o objeto <code>usuario</code> original, a fun&#231;&#227;o <code>A</code> pode, de repente e sem aviso, passar a exibir um dado diferente ou se comportar de forma inesperada. Agora multiplique isso por centenas de fun&#231;&#245;es e objetos e voc&#234; tem o caos.</p><p>Trabalhar com imutabilidade no c&#243;digo nos for&#231;a a uma disciplina que torna o software:</p><ul><li><p><strong>Previs&#237;vel:</strong> Sem efeitos colaterais, o fluxo de dados se torna expl&#237;cito e f&#225;cil de seguir.</p></li><li><p><strong>Mais F&#225;cil de Debugar:</strong> Se um dado est&#225; errado, voc&#234; n&#227;o precisa se perguntar "quem alterou isso?". Em vez disso, voc&#234; pode rastrear a sequ&#234;ncia de transforma&#231;&#245;es que criou aquele novo dado.</p></li><li><p><strong>Seguro para Concorr&#234;ncia:</strong> Em sistemas que executam m&#250;ltiplas tarefas ao mesmo tempo (<em>multithreading</em>), garantir que ningu&#233;m est&#225; alterando um dado enquanto outro est&#225; lendo &#233; um dos maiores desafios. Dados imut&#225;veis resolvem essa classe de problemas de forma elegante.</p></li></ul><h4><strong>Imutabilidade na Pr&#225;tica: O Exemplo em TypeScript</strong></h4><p>Vamos trazer a teoria para a pr&#225;tica com nosso exemplo de fatura, focando apenas no que acontece com os objetos na mem&#243;ria do programa.</p><pre><code><code>// Um objeto representando o estado inicial de uma fatura
const faturaOriginal = { id: 1, valor: 500, status: "FECHADA" };

// &#10060; JEITO ERRADO: Mutando o objeto. A informa&#231;&#227;o original &#233; destru&#237;da.
function estornarValorErrado(faturaParaEstornar) {
  faturaParaEstornar.valor = 400; // Alterando o objeto original!
  faturaParaEstornar.status = "AJUSTADA";
  return faturaParaEstornar;
}

// &#9989; JEITO CERTO: Criando um novo objeto. O original &#233; preservado.
function estornarValorCerto(faturaOriginal) {
  // Usamos o spread operator (...) para criar uma c&#243;pia e depois sobrescrevemos as propriedades
  const faturaCorrigida = {
    ...faturaOriginal,
    valor: 400,
    status: "AJUSTADA"
  };
  return faturaCorrigida;
}</code></code></pre><p>No primeiro caso (<strong>ERRADO</strong>), a fun&#231;&#227;o <code>estornarValorErrado</code> recebe o objeto <code>faturaOriginal</code> e "rabisca por cima" de suas propriedades. O estado original (<code>valor: 500</code>) &#233; perdido para sempre na mem&#243;ria. Qualquer outra parte do programa que tinha uma refer&#234;ncia a esse objeto ver&#225; a mudan&#231;a inesperadamente.</p><p>No segundo caso (<strong>CERTO</strong>), a fun&#231;&#227;o <code>estornarValorCerto</code> respeita a imutabilidade. Ela cria um objeto <code>faturaCorrigida</code> completamente novo, deixando o <code>faturaOriginal</code> intacto. Agora temos dois estados distintos e claros na mem&#243;ria. Criamos um hist&#243;rico, em vez de corromp&#234;-lo.</p><h3><strong>O Teste de Estresse da Imutabilidade: Cancelamentos e Estornos na Fatura Fechada</strong></h3><p>A teoria da imutabilidade &#233; poderosa, mas como ela sobrevive ao caos do mundo real? Vamos para a trincheira com um cen&#225;rio cl&#225;ssico: o cliente fez uma compra, a fatura fechou e, <em>depois</em>, ele cancela essa compra. E agora? A imutabilidade quebra? O valor simplesmente some do registro passado?</p><p>A resposta &#233; um sonoro <strong>n&#227;o</strong>. E &#233; aqui que a robustez do princ&#237;pio se revela. <strong>A Regra de Ouro do Mercado: Uma Fatura Fechada N&#227;o &#233; Alterada. Ponto.</strong></p><p>Pense na fatura fechada como um <strong>contrato selado</strong> ou um <strong>snapshot de dados imut&#225;vel</strong>. &#201; um registro consolidado daquele per&#237;odo de faturamento que j&#225; foi comunicado para m&#250;ltiplos sistemas: a contabilidade do banco, a processadora de pagamentos (adquirente) e a bandeira (Visa/Mastercard). Alterar esse registro retroativamente criaria um caos de concilia&#231;&#227;o em toda a cadeia de pagamentos.</p><p>Ent&#227;o, como o mercado resolve isso? O sistema n&#227;o executa um <code>UPDATE</code> ou <code>DELETE</code> em um registro passado. Em vez disso, ele insere (<code>INSERT</code>) um <strong>novo registro financeiro</strong> para representar a corre&#231;&#227;o.</p><p>O cancelamento de uma compra, o recebimento de Cashback ou o estorno de um IOF (em compras internacionais) n&#227;o altera a fatura fechada. Ele se torna um <strong>lan&#231;amento a cr&#233;dito</strong> na sua pr&#243;xima fatura &#8212; aquela que est&#225; atualmente aberta e recebendo novas transa&#231;&#245;es.<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-1" href="#footnote-1" target="_self">1</a></p><p>Vamos a um exemplo pr&#225;tico para ficar claro:</p><ul><li><p><strong>05 de Outubro:</strong> Voc&#234; compra um fone de R$ 500. A cobran&#231;a &#233; registrada para a sua fatura de Outubro.</p></li><li><p><strong>10 de Outubro:</strong> Sua fatura de Outubro fecha com um total de, digamos, R$ 1.200 (incluindo o fone). Este registro de R$ 1.200 agora &#233; considerado imut&#225;vel.</p></li><li><p><strong>12 de Outubro:</strong> Voc&#234; devolve o fone, e a loja aprova o cancelamento.</p></li><li><p><strong>13 de Outubro:</strong> A operadora do seu cart&#227;o recebe a confirma&#231;&#227;o do estorno. O que o sistema faz?</p><ul><li><p>Ele <strong>n&#227;o</strong> reabre ou modifica o registro da fatura de Outubro.</p></li><li><p>Ele gera um <strong>novo evento financeiro</strong>: um "Cr&#233;dito de Confian&#231;a" ou "Estorno" no valor de R$ 500, associado &#224; sua fatura de Novembro (a que est&#225; aberta).</p></li></ul></li></ul><p><strong>O resultado final para o cliente:</strong></p><ul><li><p>Voc&#234; vai pagar os R$ 1.200 da fatura de Outubro normalmente.</p></li><li><p>Na sua fatura de Novembro, antes mesmo de voc&#234; fazer qualquer nova compra, j&#225; existir&#225; um lan&#231;amento positivo: <code>+ R$ 500,00</code>. Esse cr&#233;dito ir&#225; abater dos seus novos gastos de Novembro. Se voc&#234; gastar R$ 700 em Novembro, sua fatura fechar&#225; em apenas R$ 200.</p></li></ul><p>O mesmo vale para o estorno de IOF: a cobran&#231;a original do imposto permanece no registro da fatura fechada, e um lan&#231;amento de cr&#233;dito do mesmo valor &#233; criado para a fatura seguinte.</p><p>Este cen&#225;rio n&#227;o quebra a imutabilidade; ele a refor&#231;a. O sistema respeita a integridade dos dados passados e, para corrigi-los, cria <strong>novos registros audit&#225;veis</strong> no presente.</p><p>A hist&#243;ria financeira, portanto, n&#227;o &#233; reescrita. Ela &#233; continuamente expandida, com cada novo evento &#8212; seja uma compra ou um estorno &#8212; adicionando um novo fato a uma linha do tempo clara, transparente e, o mais importante, imut&#225;vel.</p><h2>A<strong>l&#233;m do C&#243;digo: A Imutabilidade como Cultura e Arquitetura</strong></h2><p>J&#225; estabelecemos que o passado &#233; imut&#225;vel. Mas uma coisa &#233; entender o conceito no conforto de um exemplo de c&#243;digo; outra, bem diferente, &#233; resistir &#224; tenta&#231;&#227;o de quebrar essa regra quando o sistema est&#225; em chamas em uma sexta-feira &#224; tarde.</p><p>&#201; nesse momento que a imutabilidade deixa de ser um detalhe t&#233;cnico e vira uma quest&#227;o de cultura, processo e, em &#250;ltima inst&#226;ncia, de sobreviv&#234;ncia para o neg&#243;cio.</p><h4><strong>A Tenta&#231;&#227;o do </strong><code>UPDATE</code><strong> em Produ&#231;&#227;o: O Atalho para o Inferno</strong></h4><p>Agora, sejamos brutalmente sinceros. Todo desenvolvedor j&#225; esteve nessa situa&#231;&#227;o: um bug, uma cobran&#231;a errada, um status que n&#227;o virou. E a vozinha do mal sussurra no seu ouvido:</p><p><em>&#8220;Ih, o job rodou com o valor errado... deixa eu s&#243; dar um </em><code>UPDATE</code><em> rapidinho direto no banco de produ&#231;&#227;o pra consertar. Ningu&#233;m vai notar.&#8221;</em></p><p>Voc&#234; abre o DataGrip, digita o comando, aperta <code>Ctrl+Enter</code> e... ufa. O valor na tela est&#225; correto. Problema resolvido, deploy salvo. Certo?</p><p><strong>Errado.</strong> O que voc&#234; acabou de fazer foi plantar uma bomba-rel&#243;gio no cora&#231;&#227;o do sistema. No curto prazo, parece que funcionou. Mas no m&#233;dio e longo prazo, come&#231;a o inferno:</p><ul><li><p><strong>Na segunda-feira,</strong> o time financeiro te liga desesperado: a concilia&#231;&#227;o com o parceiro de pagamentos n&#227;o fecha. Aquele valor que voc&#234; "corrigiu" n&#227;o bate com o que foi reportado.</p></li><li><p><strong>Na ter&#231;a,</strong> o cliente abre um chamado no Reclame Aqui com prints: o extrato dele mudou magicamente de um dia para o outro. A confian&#231;a dele no seu sistema acabou de ir para o lixo.</p></li><li><p><strong>Na quarta,</strong> um auditor interno te pergunta: "Pode me mostrar a trilha e a aprova&#231;&#227;o para essa altera&#231;&#227;o no lan&#231;amento XYZ?". A resposta &#233; um sil&#234;ncio constrangedor. N&#227;o existe trilha.</p></li><li><p><strong>No m&#234;s seguinte,</strong> o regulador descobre a discrep&#226;ncia. Aquele <code>UPDATE</code> inocente de 30 segundos agora se transformou em um problema de reputa&#231;&#227;o, regulat&#243;rio e, potencialmente, de milh&#245;es.</p></li></ul><p>O atalho r&#225;pido virou o caminho mais longo e doloroso.</p><h4><strong>Construindo a Prova de Balas: As Ferramentas da Imutabilidade</strong></h4><p>Se n&#227;o podemos usar o atalho, qual &#233; o caminho certo? &#201; construir sistemas onde o <code>UPDATE</code> destrutivo seja arquiteturalmente imposs&#237;vel ou, no m&#237;nimo, muito dif&#237;cil. A engenharia nos d&#225; v&#225;rias ferramentas para isso:</p><ul><li><p><strong>O Mantra do </strong><em><strong>Append-Only</strong></em><strong>:</strong> Esta &#233; a filosofia central. Nada de apagar ou editar registros financeiros. Se algo est&#225; errado, voc&#234; <strong>insere um novo registro</strong> que corrige o anterior. Um pagamento errado? N&#227;o se edita a linha original; cria-se uma nova transa&#231;&#227;o (um estorno, um cr&#233;dito) que anula o efeito da primeira.</p></li><li><p><strong>A Borracha que n&#227;o Apaga (</strong><em><strong>Soft Deletes</strong></em><strong>):</strong> Precisa "apagar" um registro? Jamais use o <code>DELETE</code>. Em vez disso, adicione uma coluna <code>status</code> ou <code>cancelado_em</code> e marque aquele registro como inativo. Ele continua l&#225; para fins hist&#243;ricos e de auditoria, mas a sua aplica&#231;&#227;o sabe que n&#227;o deve mais consider&#225;-lo v&#225;lido.</p></li><li><p><strong>A M&#225;quina do Tempo do Versionamento:</strong> Este padr&#227;o &#233; para cen&#225;rios onde a pr&#243;pria entidade financeira evolui. N&#227;o se aplica a um simples estorno de compra na fatura do cart&#227;o (que vira cr&#233;dito na fatura seguinte), mas sim a casos como:</p><ul><li><p><strong>Renegocia&#231;&#227;o de D&#237;vida:</strong> <code>fatura_v1</code> &#233; a original, n&#227;o paga. O sistema a marca como <code>'RENEGOCIADA'</code> e cria um novo <code>acordo_v1</code>, com novas regras e parcelas. O passado n&#227;o foi alterado, foi sucedido.</p></li><li><p><strong>Corre&#231;&#227;o de Faturas B2B:</strong> <code>fatura_empresa_v1</code> &#233; emitida com um erro. Ela &#233; marcada como <code>'CANCELADA'</code> e uma <code>fatura_empresa_v2</code>, com um novo n&#250;mero, &#233; emitida corretamente, referenciando a original. O hist&#243;rico completo da jornada daquele d&#233;bito fica preservado.</p></li></ul></li><li><p><strong>O Livro do Cart&#243;rio no Banco de Dados (</strong><em><strong><a href="https://www.architecture-weekly.com/p/building-your-own-ledger-database">Ledgers</a></strong></em><strong>):</strong> <a href="https://www.architecture-weekly.com/p/building-your-own-ledger-database">Use tabelas de hist&#243;rico ou estruturas de </a><em><a href="https://www.architecture-weekly.com/p/building-your-own-ledger-database">ledger</a></em>. S&#227;o tabelas projetadas para nunca serem alteradas, apenas receberem novas inser&#231;&#245;es. Elas frequentemente t&#234;m colunas como <code>valid_from</code> e <code>valid_to</code>, permitindo que voc&#234; reconstrua o estado exato do sistema em qualquer ponto do passado.</p></li><li><p><strong>A Verdade Absoluta do </strong><em><strong>Event Sourcing</strong></em><strong>:</strong> A forma mais pura de imutabilidade. O estado de uma entidade (como o saldo de uma conta) n&#227;o &#233; um n&#250;mero salvo em uma coluna. Ele &#233; o <strong>resultado da aplica&#231;&#227;o de todos os eventos</strong> (cr&#233;ditos e d&#233;bitos) que j&#225; aconteceram naquela conta desde sua cria&#231;&#227;o. A trilha de auditoria e o estado s&#227;o a mesma coisa.</p></li></ul><h4><strong>A Verdade Desconfort&#225;vel que Ningu&#233;m Gosta de Ouvir</strong></h4><p>Sejamos francos: <strong>imutabilidade &#233; chata, mas necess&#225;ria.</strong> &#128064;</p><p>Ela &#233; o atrito, a burocracia que te impede de tomar aquele "atalho r&#225;pido" que salvaria seu deploy &#224;s 18h da sexta-feira. Ela te for&#231;a a pensar mais, a planejar a corre&#231;&#227;o, a criar um novo tipo de transa&#231;&#227;o de ajuste.</p><p>Mas &#233; essa "chatice" que garante que:</p><ul><li><p>O cliente <strong>confie</strong> no extrato dele.</p></li><li><p>O auditor <strong>n&#227;o te destrua</strong> na reuni&#227;o trimestral.</p></li><li><p>O banco <strong>n&#227;o quebre</strong> porque algu&#233;m tentou "consertar" uma inconsist&#234;ncia no susto.</p></li></ul><p>&#201; exatamente como o cart&#243;rio: um processo que pode parecer lento, burocr&#225;tico e &#224;s vezes irritante. Mas sem ele, qualquer um poderia falsificar um documento de im&#243;vel e reescrever a pr&#243;pria hist&#243;ria.</p><p>Sem a disciplina da imutabilidade, que se estende do c&#243;digo &#224; opera&#231;&#227;o, qualquer sistema financeiro vira exatamente isso: <strong>um caos sem mem&#243;ria confi&#225;vel.</strong> E confian&#231;a &#233; a &#250;nica moeda que realmente importa.</p><h2><strong>Princ&#237;pio n&#186; 2: Idempot&#234;ncia &#233; Sobreviv&#234;ncia</strong></h2><p>Se imutabilidade &#233; a regra de ouro para proteger o passado, <strong>idempot&#234;ncia &#233; o escudo contra o caos do presente.</strong></p><p>Vamos ser realistas: em sistemas financeiros, n&#227;o existe esse mundo perfeito em que cada requisi&#231;&#227;o acontece uma &#250;nica vez, na ordem correta, sem nunca dar erro. O que acontece na pr&#225;tica &#233; muito mais ca&#243;tico:</p><ul><li><p>A internet oscila.</p></li><li><p>O app do cliente trava.</p></li><li><p>O servidor d&#225; <em>timeout</em>.</p></li><li><p>O parceiro financeiro demora para responder.</p></li><li><p>O cliente fica ansioso e clica cinco vezes em &#8220;Pagar Boleto&#8221;.</p></li></ul><p>E adivinha quem sofre com isso? O seu sistema. Se voc&#234; n&#227;o projetou as opera&#231;&#245;es para serem idempotentes, cada um desses eventos vira uma dor de cabe&#231;a: pagamentos duplicados, lan&#231;amentos repetidos, estornos manuais, clientes furiosos e preju&#237;zo no balan&#231;o.</p><h3><strong>O Tratamento de Duplicidades &#233; a Regra</strong></h3><p>Em sistemas de pagamentos, a premissa &#233; que falhas de comunica&#231;&#227;o e reprocessamentos v&#227;o acontecer. Por isso, os protocolos s&#227;o desenhados para lidar com isso de forma segura.</p><p>No <strong>Pix</strong>, por exemplo, o tratamento de retransmiss&#245;es &#233; parte essencial do protocolo. <a href="https://iso20022ref.com/pacs/pacs.008/">Se o Banco A envia uma ordem de pagamento</a> (<code>pacs.008</code>) <a href="https://iso20022ref.com/pacs/pacs.002/">e n&#227;o recebe a confirma&#231;&#227;o</a> (<code>pacs.002</code>) dentro do tempo esperado, ele <strong>deve</strong> reenviar a mesma mensagem. Isso n&#227;o &#233; um loop infinito e desesperado; &#233; um processo controlado com regras de <em>timeout</em> e um n&#250;mero limitado de tentativas. A seguran&#231;a reside no fato de que a mensagem retransmitida carrega o mesmo identificador &#250;nico, o <code>EndToEndId</code>. O sistema de destino, ao ver aquele ID repetido, sabe que n&#227;o &#233; uma nova transa&#231;&#227;o, e a processa apenas uma vez.</p><p>Em outros sistemas, a duplicidade surge de formas diferentes. No <strong>boleto</strong>, a falha &#233; na ponta: um cliente pode pagar a mesma conta duas vezes por engano. No <strong>d&#233;bito autom&#225;tico</strong>, o problema pode ser um <em>job</em> de processamento que falha no meio e &#233; reiniciado.</p><p>Ou seja: a vida real &#233; um festival de tentativas m&#250;ltiplas, sejam elas do protocolo, do usu&#225;rio ou de um sistema interno. Por isso, sistemas financeiros precisam ser projetados para suportar chamadas duplicadas sem causar efeitos colaterais.</p><h3><strong>Idempot&#234;ncia na Pr&#225;tica</strong></h3><p>Idempot&#234;ncia significa: <strong>n&#227;o importa quantas vezes voc&#234; executar a mesma opera&#231;&#227;o, o resultado final do sistema precisa ser sempre o mesmo.</strong></p><p>Imagine que um cliente mandou pagar R$ 200 para a conta X:</p><ul><li><p><strong>Na primeira requisi&#231;&#227;o:</strong> O servidor executa a transa&#231;&#227;o, debita R$ 200 da conta e salva o resultado.</p></li><li><p><strong>Nas requisi&#231;&#245;es seguintes (id&#234;nticas):</strong> O servidor identifica que a opera&#231;&#227;o j&#225; foi conclu&#237;da com sucesso. Ele <strong>n&#227;o executa um novo d&#233;bito</strong> e apenas retorna a resposta original.</p></li></ul><p>Dessa forma, o estado final &#233; sempre o mesmo: a conta foi debitada uma &#250;nica vez em R$ 200. O sistema n&#227;o pode sair debitando R$ 200, R$ 400, R$ 600 s&#243; porque recebeu a mesma instru&#231;&#227;o repetida. Isso significa que o sistema adotou mecanismos para evitar erros graves de duplica&#231;&#227;o indevidas. <em>(leia a nota)</em><a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-2" href="#footnote-2" target="_self">2</a></p><h3><strong>Estrat&#233;gias Usadas no Mercado</strong></h3><ul><li><p><strong>Chave de Idempot&#234;ncia (Idempotency Key):</strong> Muito usada em APIs de pagamento (Stripe, Adyen, Mercado Pago). Cada requisi&#231;&#227;o do cliente (<code>POST</code>, <code>PUT</code>) traz um cabe&#231;alho com uma chave &#250;nica. O servidor guarda o resultado da opera&#231;&#227;o atrelado a essa chave. Se a requisi&#231;&#227;o repetir com a mesma chave, o sistema n&#227;o processa de novo &#8212; apenas devolve a resposta que j&#225; foi computada.</p></li></ul><pre><code><code>// Exemplo em pseudo-c&#243;digo
if (cache.hasKey(request.idempotencyKey)) {
  return cache.getResult(request.idempotencyKey);
} else {
  const result = processPayment(request);
  cache.save(request.idempotencyKey, result);
  return result;
}</code></code></pre><ul><li><p><strong>EndToEndId no Pix:</strong> No arranjo do Pix, cada transa&#231;&#227;o carrega um identificador &#250;nico de ponta a ponta. Seu objetivo principal &#233; garantir unicidade e rastreabilidade. Ele funciona como o <strong>CPF da transa&#231;&#227;o</strong>: se um Pix n&#227;o caiu, &#233; com esse ID que bancos e reguladores localizam o evento. Como efeito colateral positivo, ele &#233; a chave de idempot&#234;ncia natural do sistema, evitando duplicidades em cen&#225;rios de retransmiss&#227;o</p></li><li><p><strong>Controle de Unicidade no Banco de Dados:</strong> Uso de <em>constraints</em> <code>UNIQUE</code> em colunas cr&#237;ticas (ex: <code>transaction_id</code>). Se duas <em>threads</em> tentarem gravar a mesma opera&#231;&#227;o, s&#243; a primeira passa; a segunda recebe um erro controlado de viola&#231;&#227;o de chave, que a aplica&#231;&#227;o sabe como tratar.</p></li><li><p><strong>Locks Otimistas ou Pessimistas:</strong> Em sistemas de liquida&#231;&#227;o ou concilia&#231;&#227;o, &#233; preciso controlar concorr&#234;ncia para garantir que a mesma opera&#231;&#227;o n&#227;o seja aplicada duas vezes. Pode ser feito via <code>SELECT FOR UPDATE</code> (pessimista) ou via <code>versioning</code> (otimista).</p></li><li><p><strong>Mensageria com Deduplica&#231;&#227;o:</strong> Em arquiteturas ass&#237;ncronas, a entrega de mensagens &#233;, por padr&#227;o, <em>at-least-once</em> (pelo menos uma vez), o que significa que mensagens podem chegar repetidas. A aplica&#231;&#227;o consumidora precisa estar preparada para lidar com isso, usando uma das estrat&#233;gias acima para deduplicar<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-3" href="#footnote-3" target="_self">3</a> o processamento.</p></li></ul><h3><strong>Determinismo: A Alma da Idempot&#234;ncia</strong></h3><p>No fundo, o que buscamos &#233; <strong>determinismo</strong>: <a href="https://nolongerset.com/idempotence-vs-determinism/">dado o mesmo input, o sistema precisa produzir sempre o mesmo output, sem surpresas</a>. Se o cliente pediu para pagar o boleto 123 no valor de R$ 500, esse pedido n&#227;o pode virar uma loteria em que cada nova chamada resulta em um d&#233;bito adicional. Determinismo &#233; o que d&#225; previsibilidade. E previsibilidade &#233; o que d&#225; confian&#231;a.</p><h4><strong>E Quando N&#227;o Pensamos Nisso?</strong></h4><p>Hist&#243;rias de terror n&#227;o faltam:</p><ul><li><p>Cliente que recebe o Pix duas vezes porque a deduplica&#231;&#227;o falhou.</p></li><li><p>Boleto pago em duplicidade, exigindo estorno manual.</p></li><li><p>Sistema de d&#233;bito autom&#225;tico que rodou o job duas vezes, cobrando o cliente em dobro.</p></li></ul><p>Cada falha dessas gera:</p><ol><li><p><strong>Trabalho operacional extra</strong> (equipes de backoffice corrigindo manualmente).</p></li><li><p><strong>Desgaste com o cliente</strong> (ningu&#233;m esquece quando o banco cobra em duplicidade).</p></li><li><p><strong>Risco regulat&#243;rio</strong> (o BACEN pode punir reincid&#234;ncias graves).</p></li></ol><h4><strong>A Verdade Nua e Crua</strong></h4><p>Idempot&#234;ncia &#233; um daqueles conceitos que parecem detalhe t&#233;cnico, mas, na pr&#225;tica, s&#227;o quest&#227;o de <strong>sobreviv&#234;ncia</strong>. O cliente nunca vai aceitar a desculpa: <em>&#8220;Ah, foi porque o sistema chamou o endpoint duas vezes.&#8221;</em></p><p>Ele s&#243; vai ver o d&#233;bito duplicado e pensar: <em>&#8220;Meu banco n&#227;o &#233; confi&#225;vel.&#8221; </em>E confian&#231;a, em sistemas financeiros, &#233; tudo.</p><div class="pullquote"><p>&#8220;Se o cliente nota duas cobran&#231;as, ele n&#227;o pensa em idempot&#234;ncia: ele pensa em incompet&#234;ncia.&#8221;</p></div><h2><strong>Princ&#237;pio n&#186; 3: Consist&#234;ncia &#233; o Ch&#227;o, Disponibilidade &#233; o Teto</strong></h2><p>Agora vamos para o cora&#231;&#227;o da fera, a escolha de todo arquiteto de software financeiro: <strong>Consist&#234;ncia vs. Disponibilidade.</strong></p><p>Se idempot&#234;ncia era sobre n&#227;o processar a mesma coisa duas vezes, consist&#234;ncia &#233; sobre garantir que o estado do sistema fa&#231;a sentido o tempo todo. &#201; a garantia de que o saldo na conta &#233; <em>o saldo</em>, e n&#227;o uma vers&#227;o aproximada, otimista ou desatualizada dele.</p><h4><strong>O Dilema do Mundo Real: O Pagamento que Demora</strong></h4><p>Imagine a cena: seu usu&#225;rio abre o app para pagar um boleto de R$ 500. O saldo dele &#233; de R$ 501. Ele clica em "Pagar". A rodinha come&#231;a a girar... e gira... e gira.</p><p>O que est&#225; acontecendo por tr&#225;s das cortinas? Nosso sistema est&#225;, provavelmente, travando uma batalha &#233;pica para garantir a consist&#234;ncia. Ele precisa:</p><ol><li><p>Verificar se o saldo de R$ 501 &#233; real e est&#225; dispon&#237;vel.</p></li><li><p><strong>Travar</strong> esse saldo para que nenhuma outra opera&#231;&#227;o (um d&#233;bito autom&#225;tico, outra compra no cart&#227;o) o consuma no mesmo instante.</p></li><li><p>Efetivar o d&#233;bito de R$ 500.</p></li><li><p>Liberar a trava, deixando o novo saldo de R$ 1.</p></li><li><p>Confirmar o pagamento do boleto.</p></li></ol><p>Essa "rodinha girando" &#233; o sintoma da <strong>Disponibilidade</strong> sendo sacrificada. O sistema n&#227;o est&#225; 100% dispon&#237;vel para dar uma resposta instant&#226;nea. Por qu&#234;? Porque ele est&#225; priorizando a <strong>Consist&#234;ncia</strong>.</p><p>A alternativa seria um pesadelo: o sistema, para ser super r&#225;pido (alta disponibilidade), poderia ler o saldo de R$ 501, e enquanto processa o boleto, uma outra transa&#231;&#227;o de Pix de R$ 10 entra e &#233; aprovada. Resultado: o boleto &#233; pago, o Pix &#233; pago, e a conta do cliente fica com um saldo negativo de R$ 9. O sistema mentiu sobre a realidade financeira. E isso, no mundo do dinheiro, &#233; o pecado original.</p><p>Agora, voc&#234; pode estar pensando: "Mas e o cheque especial? Os bancos deixam o saldo ficar negativo!". Perfeito. Mas a pegadinha &#233; que, para decidir se o cliente <em>pode</em> entrar no cheque especial de forma controlada, o sistema ainda precisa daquele mesmo processo de <strong>trava e consist&#234;ncia</strong>! Sem ele, o sistema n&#227;o saberia se o saldo final seria -R$ 9 (dentro do limite) ou -R$ 999 (muito al&#233;m do limite), e poderia aprovar uma opera&#231;&#227;o que levaria a uma perda gigantesca. A consist&#234;ncia &#233; o que permite ao banco tomar uma decis&#227;o de risco de forma segura.</p><h4><strong>O Segredo dos Bancos Modernos</strong></h4><p>Ent&#227;o os sistemas banc&#225;rios s&#227;o lentos o tempo todo? N&#227;o exatamente. A verdadeira maestria da arquitetura financeira est&#225; em aplicar essa consist&#234;ncia rigorosa apenas onde ela &#233; inegoci&#225;vel, adotando um modelo h&#237;brido.</p><ul><li><p><strong>O Core Transacional (O Cofre):</strong> No momento em que o dinheiro realmente muda de m&#227;os &#8211; o pagamento do boleto, a transfer&#234;ncia via Pix, a compra no cart&#227;o &#8211; o sistema vai obrigatoriamente buscar a informa&#231;&#227;o no "ch&#227;o". Ele opera no modo <strong>CP (Consist&#234;ncia &gt; Disponibilidade)</strong>. Ele vai usar travas, filas e tudo o que for preciso para garantir que a opera&#231;&#227;o seja at&#244;mica e correta. <strong>Se isso levar 300 milissegundos a mais, que seja</strong>.</p></li><li><p><strong>As Camadas de Experi&#234;ncia (A Vitrine):</strong> Mas e o saldo que aparece no topo da tela inicial do seu app? Aquele que voc&#234; consulta rapidamente? Essa informa&#231;&#227;o n&#227;o precisa, necessariamente, vir do "cofre" a cada segundo. Ela pode vir de uma c&#243;pia de leitura mais r&#225;pida, um cache que opera no modo </p></li><li><p><strong>AP (Disponibilidade &gt; Consist&#234;ncia)</strong>. Nesse caso, o sistema aceita uma "consist&#234;ncia eventual". Se o saldo na tela inicial estiver 1 ou 2 segundos desatualizado, n&#227;o h&#225; problema. Mas no instante em que voc&#234; clica em "Pagar", o sistema muda de postura e vai direto ao cofre para garantir a verdade absoluta.</p></li></ul><p>A intelig&#234;ncia do design est&#225; em saber quando agir como um contador paranoico e quando agir como um servidor de cache ultrarr&#225;pido. E essa decis&#227;o &#233; o que separa um sistema financeiro robusto e escal&#225;vel de um mon&#243;lito lento ou, pior, de um sistema r&#225;pido e perigosamente inconsistente.</p><h4><strong>Entra em Cena o Teorema CAP: A Lei da F&#237;sica dos Sistemas Distribu&#237;dos</strong></h4><p>Todo programador que trabalha com sistemas distribu&#237;dos (ou seja, basicamente todo mundo hoje em dia) j&#225; ouviu falar do <strong><a href="https://docs.aws.amazon.com/pt_br/whitepapers/latest/availability-and-beyond-improving-resilience/cap-theorem.html">Teorema CAP</a></strong>. Ele n&#227;o &#233; uma "boa pr&#225;tica", &#233; uma lei, como a gravidade.</p><p>Ele diz que, de tr&#234;s garantias, voc&#234; s&#243; pode escolher duas em um sistema distribu&#237;do que sofra uma falha de rede:</p><ul><li><p><strong>C</strong>onsistency (Consist&#234;ncia): Todos os n&#243;s do sistema enxergam a mesma vers&#227;o dos dados ao mesmo tempo. Uma leitura sempre retorna o dado mais recentemente escrito.</p></li><li><p><strong>A</strong>vailability (Disponibilidade): Toda requisi&#231;&#227;o recebe uma resposta (que n&#227;o seja um erro), mesmo que alguns n&#243;s do sistema estejam fora do ar. O sistema est&#225; sempre "de p&#233;".</p></li><li><p><strong>P</strong>artition Tolerance (Toler&#226;ncia a Parti&#231;&#245;es): O sistema continua funcionando mesmo que a comunica&#231;&#227;o entre seus n&#243;s seja interrompida (uma "parti&#231;&#227;o" de rede).</p></li></ul><p>A pegadinha? A vida real <strong>garante</strong> que parti&#231;&#245;es de rede v&#227;o acontecer. O cabo de rede vai ser desconectado, o data center vai perder conex&#227;o. Ent&#227;o, a Toler&#226;ncia a Parti&#231;&#245;es (P) n&#227;o &#233; uma escolha, &#233; um fato da vida.</p><p>A verdadeira escolha que voc&#234;, programador, precisa fazer &#233; entre <strong>C</strong> e <strong>A</strong>.</p><ul><li><p><strong>Sistemas AP (Availability + Partition Tolerance):</strong> Priorizam a disponibilidade. Se um n&#243; n&#227;o consegue falar com o outro para saber o dado mais recente, ele responde com o melhor dado que tem no momento. &#201; melhor responder com um dado talvez antigo do que n&#227;o responder.</p><ul><li><p><em>Exemplo:</em> Uma rede social. Se voc&#234; n&#227;o v&#234; o &#250;ltimo post de um amigo por alguns segundos, tudo bem. O feed carregar &#233; mais importante que ele estar 100% atualizado.</p></li></ul></li><li><p><strong>Sistemas CP (Consistency + Partition Tolerance):</strong> Priorizam a consist&#234;ncia. Se um n&#243; n&#227;o consegue garantir que tem a vers&#227;o mais correta e atualizada do dado, ele <strong>recusa a opera&#231;&#227;o</strong>. &#201; melhor dar um erro ou fazer o cliente esperar do que retornar um dado inconsistente.</p><ul><li><p><em>Exemplo:</em> <strong>Qualquer sistema de liquida&#231;&#227;o financeira.</strong></p></li></ul></li></ul><p><strong>Princ&#237;pio fundamental: Em uma transa&#231;&#227;o financeira, a consist&#234;ncia vale mais que a experi&#234;ncia instant&#226;nea.</strong> Um Pix que demora 3 segundos &#233; irritante. Um Pix que debita sua conta mas n&#227;o chega no destino (ou chega duas vezes) <strong>destr&#243;i a confian&#231;a</strong>.</p><h4><strong>Estrat&#233;gias para Garantir a Consist&#234;ncia</strong></h4><p>Ent&#227;o, como as institui&#231;&#245;es financeiras fazem isso na pr&#225;tica? Elas usam um arsenal de ferramentas e estrat&#233;gias testadas em batalha.</p><ol><li><p><strong>Locks Pessimistas no Banco de Dados (</strong><code>SELECT FOR UPDATE</code><strong>)</strong></p><ul><li><p><strong>Como funciona:</strong> &#201; a estrat&#233;gia mais direta. Antes de modificar uma linha cr&#237;tica (como o saldo de uma conta), o sistema diz ao banco de dados: "Ei, vou mexer nesta linha. Tranque-a e n&#227;o deixe mais ningu&#233;m nem mesmo ler o valor dela at&#233; eu terminar e confirmar (commit)".</p></li><li><p><strong>Vis&#227;o do programador:</strong> &#201; literalmente uma linha de c&#243;digo a mais no seu SQL. A <code>SELECT ... FOR UPDATE</code> garante um bloqueio exclusivo.</p></li><li><p><strong>Trade-off:</strong> &#201; a forma mais forte de consist&#234;ncia, mas tamb&#233;m a que mais prejudica a performance e a concorr&#234;ncia. Se muitas transa&#231;&#245;es tentam acessar a mesma conta, elas formam uma fila, esperando a libera&#231;&#227;o do lock. &#201; por isso que &#233; usado cirurgicamente, apenas no momento exato da liquida&#231;&#227;o.</p></li></ul></li><li><p><strong>Transa&#231;&#245;es ACID e N&#237;veis de Isolamento Fortes</strong></p><ul><li><p><strong>Como funciona:</strong> O bom e velho banco de dados relacional (Postgres, Oracle, etc.) &#233; o melhor amigo da consist&#234;ncia. As garantias <strong>ACID</strong> (Atomicidade, Consist&#234;ncia, Isolamento, Durabilidade) s&#227;o a base. Em especial, o <strong>Isolamento</strong> &#233; crucial. Configurar o n&#237;vel de isolamento para <code>SERIALIZABLE</code> ou <code>REPEATABLE READ</code> garante que uma transa&#231;&#227;o n&#227;o veja a "sujeira" (dados n&#227;o commitados) de outra.</p></li><li><p><strong>Vis&#227;o do programador:</strong> &#201; entender profundamente como seu banco de dados funciona e n&#227;o usar os n&#237;veis de isolamento mais baixos (como <code>READ UNCOMMITTED</code>) em opera&#231;&#245;es cr&#237;ticas s&#243; para ganhar performance.</p></li></ul></li><li><p><strong>Two-Phase Commit (2PC - "Commit em Duas Fases")</strong></p><ul><li><p><strong>Como funciona:</strong> E quando a transa&#231;&#227;o envolve mais de um sistema, como debitar de um banco de dados e registrar em um sistema de liquida&#231;&#227;o separado? O 2PC &#233; um protocolo para garantir consist&#234;ncia distribu&#237;da.</p><ul><li><p><strong>Fase 1 (Vota&#231;&#227;o):</strong> O coordenador da transa&#231;&#227;o pergunta a todos os participantes: "Voc&#234;s est&#227;o prontos para commitar? Prometem que n&#227;o v&#227;o falhar?". Todos precisam responder "sim" e se preparar.</p></li><li><p><strong>Fase 2 (Commit):</strong> Se todos disseram "sim", o coordenador manda a ordem: "Ok, podem commitar!". Se algu&#233;m falhou ou disse "n&#227;o", ele manda: "Abortem tudo!".</p></li></ul><p><strong><br>Trade-off:</strong> Pense no 2PC como uma cerim&#244;nia de casamento de alt&#237;ssima seguran&#231;a, onde o &#8220;sim&#8221; &#233; irrevers&#237;vel. O coordenador &#233; o juiz, e os sistemas participantes s&#227;o os noivos.</p><ul><li><p><strong>Fase 1 (A Promessa):</strong> O juiz (coordenador) pergunta a cada noivo (participante) se eles aceitam o compromisso. Ao dizerem &#8220;sim&#8221;, eles n&#227;o est&#227;o apenas concordando; eles est&#227;o se trancando em um estado de &#8220;prometido&#8221;. Eles n&#227;o podem mais sair correndo do altar ou aceitar outra proposta. Seus recursos (suas vidas!) est&#227;o bloqueados.</p></li><li><p><strong>Fase 2 (A Declara&#231;&#227;o):</strong> O juiz ent&#227;o oficializa o casamento para o mundo.<br></p></li></ul><p>O problema fatal acontece se o <strong>juiz desmaiar depois que ambos disseram &#8220;sim&#8221;, mas antes de declar&#225;-los casados. </strong>Os noivos ficam parados no altar. Eles n&#227;o est&#227;o solteiros, mas tamb&#233;m n&#227;o est&#227;o oficialmente casados. Est&#227;o em um limbo, com seus recursos (seu estado civil) travados, esperando uma decis&#227;o que n&#227;o vem. Para resolver, seria preciso encontrar um novo juiz que pudesse consultar as anota&#231;&#245;es do anterior para decidir se conclui a cerim&#244;nia ou se anula tudo.<br><br>&#201; exatamente por isso que o 2PC &#233; considerado <strong>fr&#225;gil</strong>: a queda do coordenador em um momento cr&#237;tico cria um cen&#225;rio de bloqueio indefinido que &#233; um pesadelo para resolver em produ&#231;&#227;o, exigindo interven&#231;&#227;o manual e complexa. Ele garante a atomicidade, mas a um custo muito alto de disponibilidade e resili&#234;ncia (para entender alternativas leia a nota ao rodape).<a class="footnote-anchor" data-component-name="FootnoteAnchorToDOM" id="footnote-anchor-4" href="#footnote-4" target="_self">4</a></p></li></ul></li></ol><h4><strong>O Modelo H&#237;brido: O Segredo dos Bancos Modernos</strong></h4><p>Nenhum sistema financeiro moderno &#233; puramente CP ou puramente AP. Eles s&#227;o inteligentes. Eles aplicam o rigor da consist&#234;ncia onde o dinheiro realmente muda de m&#227;os e relaxam para a disponibilidade nas camadas de experi&#234;ncia do usu&#225;rio.</p><ul><li><p><strong>O Core Transacional (Liquida&#231;&#227;o):</strong> &#201; o cofre. Aqui, tudo &#233; <strong>CP</strong>. Usam-se locks pessimistas, transa&#231;&#245;es seriais, tudo para garantir que o livro-raz&#227;o (ledger) seja perfeito. Se essa parte ficar lenta ou indispon&#237;vel por alguns segundos para evitar inconsist&#234;ncia, que assim seja. O preju&#237;zo de um erro aqui &#233; grande demais.</p></li><li><p><strong>Os Servi&#231;os Perif&#233;ricos (Experi&#234;ncia):</strong> S&#227;o as vitrines da loja. O extrato, o hist&#243;rico de transa&#231;&#245;es, o envio de notifica&#231;&#245;es. Aqui, o sistema pode ser <strong>AP</strong>. Ele l&#234; de r&#233;plicas do banco de dados, usa caches, sistemas de mensageria (como Kafka). Se o extrato do seu app demorar 2 segundos para mostrar o &#250;ltimo Pix que voc&#234; fez, n&#227;o &#233; o fim do mundo. &#201; uma "consist&#234;ncia eventual". O sistema eventualmente ficar&#225; consistente, mas ele prioriza te dar uma resposta r&#225;pida agora.</p></li></ul><p>Essa separa&#231;&#227;o &#233; a estrat&#233;gia mais inteligente e realista: <strong>ser paranoico com a consist&#234;ncia no centro da opera&#231;&#227;o e focar na disponibilidade nas bordas.</strong></p><p>Como programador, seu trabalho &#233; saber em que parte do sistema voc&#234; est&#225;. Voc&#234; est&#225; mexendo no cofre ou na vitrine? A resposta para essa pergunta define se seu c&#243;digo deve fazer o cliente esperar ou arriscar mostrar um dado um segundo desatualizado. E no mundo das finan&#231;as, essa &#233; uma das decis&#245;es mais importantes que voc&#234; vai tomar.</p><h2><strong>Princ&#237;pio n&#186; 4: Auditoria como Feature, n&#227;o &#233; algo opcional!</strong></h2><p>Vamos ser brutalmente honestos. Para muitos de n&#243;s, programadores, "log" &#233; aquela coisa que a gente cospe no console pra debugar um problema em produ&#231;&#227;o. &#201; um mon&#243;logo ca&#243;tico de <code>INFO</code>, <code>WARN</code> e <code>ERROR</code> que a gente s&#243; olha quando a casa j&#225; est&#225; pegando fogo. A gente pensa em log como um curativo, um rem&#233;dio para o sintoma.</p><p>Em um sistema financeiro, essa mentalidade n&#227;o &#233; s&#243; errada, &#233; perigosa.</p><p>Aqui, a trilha de auditoria n&#227;o &#233; um log de debug. <strong>&#201; a caixa-preta do avi&#227;o.</strong> &#201; um registro imut&#225;vel e detalhado de cada evento significativo que acontece no sistema. N&#227;o &#233; algo que voc&#234; adiciona depois porque o time de Compliance pediu. &#201; um requisito funcional, uma <em>feature</em> t&#227;o importante quanto a pr&#243;pria transfer&#234;ncia de dinheiro.</p><p>O princ&#237;pio &#233; simples e inegoci&#225;vel: <strong>cada centavo precisa de um "por qu&#234;" e um "quando" rastre&#225;veis.</strong></p><h4><strong>A Grande Diferen&#231;a: Log de Debug vs. Trilha de Auditoria</strong></h4><p>Para a coisa ficar clara, vamos separar os dois mundos:</p><ul><li><p><strong>Log de Debug:</strong></p><ul><li><p><strong>Prop&#243;sito:</strong> Ajudar o desenvolvedor a entender o fluxo do c&#243;digo.</p></li><li><p><strong>Audi&#234;ncia:</strong> Voc&#234; e seu time de engenharia.</p></li><li><p><strong>Formato:</strong> Geralmente texto livre, bagun&#231;ado. Ex: <code>&#8220;Entrou no m&#233;todo de pagamento, valor: 100.50&#8221;</code>.</p></li><li><p><strong>Ciclo de Vida:</strong> Tempor&#225;rio. Pode ser sobrescrito, rotacionado, deletado.</p></li><li><p><strong>Valor de Neg&#243;cio:</strong> Indireto. Ajuda a consertar bugs.</p></li></ul></li><li><p><strong>Trilha de Auditoria:</strong></p><ul><li><p><strong>Prop&#243;sito:</strong> Prover um registro formal e legal de todas as a&#231;&#245;es. &#201; evid&#234;ncia.</p></li><li><p><strong>Audi&#234;ncia:</strong> Suporte ao cliente, time de Risco, Compliance, auditores externos (como o Banco Central) e, em &#250;ltima inst&#226;ncia, a Justi&#231;a.</p></li><li><p><strong>Formato:</strong> Altamente estruturado (JSON &#233; seu melhor amigo aqui). Ex: </p></li></ul></li></ul><pre><code>{
   "timestamp":"...",
   "event_type":"ACCOUNT_DEBITED",
   "actor":{
      "user_id":123,
      "origin_ip":"..."
   },
   "entity":{
      "account_id":456
   },
   "state_change":{
      "balance_before":1000.00,
      "balance_after":800.01,
      "amount":199.99
   },
   "context":{
      "transaction_id":"...",
      "correlation_id":"..."
   }
}</code></pre><ul><li><p><strong>Ciclo de Vida:</strong> Permanente e imut&#225;vel. Deve ser armazenado em local seguro por anos (a regulamenta&#231;&#227;o exige!).</p></li><li><p><strong>Valor de Neg&#243;cio:</strong> Direto. Constr&#243;i confian&#231;a, mitiga risco, resolve disputas e cumpre a lei.</p></li></ul><p>Seu <code>console.log("deu pau")</code> n&#227;o serve para explicar a um auditor por que um saldo ficou negativo. A trilha de auditoria, sim.</p><h4><strong>"Logar Tudo, Sempre": O que significa "Tudo"?</strong></h4><p>"Logar tudo" n&#227;o &#233; sair cuspindo cada linha de c&#243;digo. &#201; sobre registrar cada <strong>evento de neg&#243;cio com significado</strong>. Em uma institui&#231;&#227;o financeira, isso se resume a algumas perguntas fundamentais:</p><ol><li><p><strong>O QU&#202; aconteceu? (O Evento e a Mudan&#231;a de Estado)</strong></p><ul><li><p>N&#227;o basta dizer "o pagamento foi aprovado". Voc&#234; precisa registrar a <strong>mudan&#231;a de estado</strong> completa. O "antes" e o "depois".</p></li><li><p><strong>Exemplo:</strong> Para um d&#233;bito, voc&#234; precisa registrar o saldo <em>antes</em> da opera&#231;&#227;o e o saldo <em>depois</em>. Se um auditor (ou um cliente) questionar a transa&#231;&#227;o, voc&#234; tem a prova matem&#225;tica de que o saldo foi calculado corretamente. Sem o "antes", a prova fica incompleta.</p></li></ul></li><li><p><strong>QUEM fez? (O Ator)</strong></p><ul><li><p>Toda a&#231;&#227;o precisa de um dono. Foi o cliente <code>ID 123</code> pelo aplicativo? Foi o agente de suporte <code>ID 789</code> pela ferramenta de backoffice? Foi um <code>SYSTEM_JOB</code> que rodou de madrugada?</p></li><li><p>Essa rastreabilidade &#233; crucial para investiga&#231;&#245;es de fraude. Se uma opera&#231;&#227;o suspeita foi feita, a primeira pergunta &#233;: quem estava autenticado naquela sess&#227;o?</p></li></ul></li><li><p><strong>QUANDO e ONDE aconteceu? (O Contexto)</strong></p><ul><li><p><strong>Timestamp:</strong> Preciso, em UTC para evitar dores de cabe&#231;a com fuso hor&#225;rio.</p></li><li><p><strong>Origem:</strong> Endere&#231;o de IP, geolocaliza&#231;&#227;o, <code>User-Agent</code> do navegador, ID do dispositivo m&#243;vel. Se uma conta de um cliente de Campinas tem uma transa&#231;&#227;o originada de um IP do leste europeu, o sistema de fraude precisa saber disso.</p></li></ul></li><li><p><strong>POR QU&#202; aconteceu? (A Causa e a Correla&#231;&#227;o)</strong></p><ul><li><p>Em um mundo de microsservi&#231;os, uma &#250;nica a&#231;&#227;o do usu&#225;rio (como "pagar a fatura") pode gerar uma cascata de dezenas de eventos em sistemas diferentes. Como voc&#234; conecta tudo isso?</p></li><li><p><strong>Correlation ID:</strong> Gere um ID &#250;nico no in&#237;cio da requisi&#231;&#227;o (no API Gateway, por exemplo) e propague-o em todas as chamadas de servi&#231;o subsequentes e em cada linha de log/auditoria. Quando der um problema, voc&#234; pode filtrar por esse ID e ver a hist&#243;ria completa daquela transa&#231;&#227;o, ponta a ponta, atrav&#233;s de todos os microsservi&#231;os. &#201; como dar um n&#250;mero de RG para cada requisi&#231;&#227;o.</p></li></ul></li></ol><h4><strong>Estrat&#233;gias Confi&#225;veis: Como Implementar Isso Sem Enlouquecer</strong></h4><p>Construir uma trilha de auditoria robusta n&#227;o precisa ser um pesadelo. A ind&#250;stria j&#225; tem padr&#245;es para isso.</p><ol><li><p><strong>Logging Estruturado e Centralizado (O M&#237;nimo Vi&#225;vel)</strong></p><ul><li><p><strong>A Pr&#225;tica:</strong> Abandone logs em texto puro. Use JSON. Sua linguagem de programa&#231;&#227;o com certeza tem bibliotecas para isso (Serilog para .NET, Logback para Java, Winston para Node.js).</p></li><li><p><strong>A Infraestrutura:</strong> Envie esses logs estruturados para uma plataforma centralizada de logging, como o <strong>ELK Stack (Elasticsearch, Logstash, Kibana)</strong>, Splunk ou Datadog. A ideia &#233; ter um &#250;nico lugar onde voc&#234; pode buscar, filtrar e criar dashboards sobre tudo o que acontece no seu ecossistema. <code>grep</code> em 20 servidores diferentes n&#227;o &#233; uma estrat&#233;gia, &#233; um ato de desespero.</p></li></ul></li><li><p><strong>Change Data Capture (CDC) com "Shadow Tables" (A Estrat&#233;gia Pragm&#225;tica)</strong></p><ul><li><p><strong>A Pr&#225;tica:</strong> Para cada tabela cr&#237;tica no seu banco de dados (ex: <code>contas</code>), crie uma tabela espelho (<code>contas_historico</code>). Use <em>triggers</em> de banco de dados ou ferramentas como <strong>Debezium</strong> para que toda <code>INSERT</code>, <code>UPDATE</code> ou <code>DELETE</code> na tabela principal gere automaticamente uma nova linha na tabela de hist&#243;rico, registrando o dado antigo, o dado novo, quem fez e quando.</p></li><li><p><strong>Vantagem:</strong> Desacopla a l&#243;gica de auditoria da sua aplica&#231;&#227;o principal. O desenvolvedor do servi&#231;o de contas n&#227;o precisa se lembrar de logar tudo; a auditoria acontece a n&#237;vel de banco de dados, de forma transparente e garantida.</p></li></ul></li><li><p><strong>Event Sourcing</strong></p><ul><li><p><strong>A Pr&#225;tica:</strong> Essa &#233; a mudan&#231;a de paradigma mais radical. Em vez de armazenar o <em>estado atual</em> das coisas (ex: o saldo atual da conta &#233; R$ 50), voc&#234; armazena a <strong>sequ&#234;ncia completa de eventos que levaram a esse estado</strong>.</p></li><li><p><strong>Exemplo:</strong> <code>[Conta Criada com saldo 0], [Dep&#243;sito de R$100], [Compra de R$20], [Pagamento de Fatura de R$30]</code>. O saldo atual (R$ 50) &#233; apenas uma consequ&#234;ncia, uma proje&#231;&#227;o desses eventos.</p></li><li><p><strong>Vantagem:</strong> <strong>A trilha de auditoria &#201; o seu sistema</strong>. N&#227;o h&#225; como o estado e a auditoria ficarem inconsistentes, porque eles s&#227;o a mesma coisa. &#201; a fonte da verdade mais pura que existe.</p></li><li><p><strong>Desvantagem:</strong> &#201; um padr&#227;o de arquitetura complexo (geralmente anda de m&#227;os dadas com CQRS) e exige uma forma de pensar diferente. N&#227;o &#233; para qualquer projeto, mas para o <em>core</em> de um sistema de ledger, manter o a jornada do usu&#225;rio &#233; essencial.</p></li></ul></li></ol><h4><strong>Por Que Isso Salva a Sua Pele (e seu Emprego)</strong></h4><p>No final do dia, uma trilha de auditoria impec&#225;vel &#233; o que permite que voc&#234; durma &#224; noite. &#201; ela que vai responder &#224;s perguntas inevit&#225;veis:</p><ul><li><p><strong>Do Suporte:</strong> "O cliente disse que o Pix n&#227;o caiu. O que aconteceu?"</p></li><li><p><strong>Do Time de Fraude:</strong> "Temos uma transa&#231;&#227;o suspeita. Qual a origem e os passos exatos que o fraudador deu?"</p></li><li><p><strong>Do Auditor do Banco Central:</strong> "Me forne&#231;a um relat&#243;rio de todas as opera&#231;&#245;es acima de X valor, autorizadas pelo gerente Y, no &#250;ltimo trimestre."</p></li><li><p><strong>De Voc&#234; Mesmo, Daqui a Seis Meses:</strong> "Meu Deus, por que esse saldo est&#225; assim? O que aconteceu com essa conta?"</p></li></ul><p>Sem as respostas para essas perguntas, seu sistema n&#227;o &#233; apenas um sistema com bugs. <strong>&#201; um sistema que n&#227;o &#233; confi&#225;vel. E em finan&#231;as, a falta de confian&#231;a n&#227;o &#233; um problema t&#233;cnico, &#233; uma senten&#231;a de morte.</strong></p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Inscreva-se gratuitamente para receber novas postagens e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>Princ&#237;pio n&#186; 5: Precis&#227;o Matem&#225;tica &#233; Inegoci&#225;vel</strong></h2><p>Em sistemas financeiros, n&#227;o existe "quase". Um c&#225;lculo tem que ser exato, repet&#237;vel e, acima de tudo, estar em conformidade com a lei.</p><p>O princ&#237;pio aqui &#233; direto e assustador: <strong>1 centavo fora de lugar, repetido em escala, vira fraude ou multa.</strong></p><h4><strong>O Pecado Capital: Usar </strong><code>float</code><strong> ou </strong><code>double</code><strong> para Dinheiro</strong></h4><p>Todo programador, em algum momento da carreira, j&#225; caiu nesta armadilha. Voc&#234; abre o console do navegador e digita:</p><pre><code><code>0.1 + 0.2
// Resultado esperado: 0.3
// Resultado real: 0.30000000000000004
</code></code></pre><p>O que &#233; essa aberra&#231;&#227;o? &#201; a consequ&#234;ncia de como os computadores representam n&#250;meros de ponto flutuante (tipos <code>float</code> e <code>double</code>) usando o <a href="https://learn.microsoft.com/pt-br/cpp/build/ieee-floating-point-representation?view=msvc-170">padr&#227;o IEEE 754</a>. Eles trabalham em bin&#225;rio (base 2), e muitas fra&#231;&#245;es decimais simples (como 0.1) n&#227;o t&#234;m uma representa&#231;&#227;o bin&#225;ria finita e exata. &#201; como tentar escrever 1/3 como um decimal finito; voc&#234; acaba com 0.33333...</p><p>Para a maioria das aplica&#231;&#245;es (jogos, gr&#225;ficos, ci&#234;ncia), essa pequena imprecis&#227;o &#233; irrelevante. Para dinheiro, &#233; um desastre. Imagine essa diferen&#231;a de <code>0.00000000000000004</code> sendo somada ou subtra&#237;da em milh&#245;es de transa&#231;&#245;es de juros de cart&#227;o de cr&#233;dito. Ao final do dia, o balan&#231;o da empresa n&#227;o vai fechar. Algu&#233;m ganhou ou perdeu fra&#231;&#245;es de centavos, e isso, em contabilidade, &#233; o apocalipse.</p><h4><strong>A Matem&#225;tica de Precis&#227;o Decimal</strong></h4><p>Para resolver isso, a computa&#231;&#227;o financeira se baseia em tipos de dados de <strong>Decimal de Precis&#227;o Arbitr&#225;ria</strong> (tamb&#233;m conhecidos como <code>BigDecimal</code> ou <code>Numeric</code>).</p><ul><li><p><strong>Como funciona?</strong> Em vez de representar o n&#250;mero em bin&#225;rio de ponto flutuante, esses tipos geralmente o armazenam como um n&#250;mero inteiro grande e um fator de escala. Pense em <code>199.99</code> sendo armazenado internamente como o inteiro <code>19999</code> e uma instru&#231;&#227;o para colocar a v&#237;rgula duas casas da direita. Isso preserva a exatid&#227;o decimal que usamos no mundo real.</p></li><li><p><strong>Suporte Nativo:</strong> Muitas linguagens usadas no <em>backend</em> financeiro tratam isso como um cidad&#227;o de primeira classe:</p><ul><li><p><strong>Java:</strong> <code>BigDecimal</code></p></li><li><p><strong>C#:</strong> <code>decimal</code></p></li><li><p><strong>Python:</strong> O m&#243;dulo <code>Decimal</code></p></li><li><p><strong>Ruby:</strong> <code>BigDecimal</code></p></li><li><p><strong>Bancos de Dados:</strong> Tipos <code>NUMERIC(precision, scale)</code> ou <code>DECIMAL</code> s&#227;o o padr&#227;o para colunas de dinheiro.</p></li></ul></li></ul><h4><strong>O Desafio em TypeScript/JavaScript</strong></h4><p>No ecossistema JavaScript, a situa&#231;&#227;o &#233; mais delicada. Nativamente, <strong>todos os n&#250;meros s&#227;o do tipo </strong><code>number</code>, que &#233; um <code>double</code> de 64 bits. N&#227;o existe um tipo decimal nativo.</p><p>Tentar contornar isso "multiplicando por 100 e trabalhando com centavos como inteiros" &#233; uma gambiarra perigosa. Ela funciona para somas e subtra&#231;&#245;es simples, mas quebra espetacularmente em c&#225;lculos de juros, amortiza&#231;&#227;o ou qualquer opera&#231;&#227;o que envolva multiplica&#231;&#227;o e divis&#227;o complexas.</p><p><strong>Como trabalhar com decimal em TypeScript da forma correta?</strong></p><p>Voc&#234; <strong>deve</strong> usar uma biblioteca robusta e bem testada pela comunidade. As mais confi&#225;veis s&#227;o:</p><ol><li><p><code>Decimal.js</code><strong> (ou </strong><code>decimal.js-light</code><strong>):</strong> Geralmente a mais recomendada. &#201; completa, configur&#225;vel (precis&#227;o, regras de arredondamento) e tem uma API clara.</p></li><li><p><code>Big.js</code><strong>:</strong> Mais leve e r&#225;pida, mas com menos funcionalidades. &#211;tima para casos de uso mais simples.</p></li><li><p><code>bignumber.js</code><strong>:</strong> Outra biblioteca muito popular e poderosa.</p></li></ol><p><strong>Exemplo pr&#225;tico em TypeScript:</strong></p><pre><code><code>import { Decimal } from 'decimal.js';

// O JEITO ERRADO (usando number nativo)
const valorErrado1 = 0.1;
const valorErrado2 = 0.2;
const resultadoErrado = valorErrado1 + valorErrado2;
console.log(resultadoErrado); // 0.30000000000000004

// O JEITO CERTO (usando Decimal.js)
// Note que passamos os valores como string para garantir que n&#227;o haja perda de precis&#227;o na entrada
const valorCerto1 = new Decimal('0.1');
const valorCerto2 = new Decimal('0.2');
const resultadoCerto = valorCerto1.plus(valorCerto2);
console.log(resultadoCerto.toString()); // "0.3" - Exato!
</code></code></pre><h3><strong>N&#250;meros ou Strings para Dinheiro em APIs JSON?</strong></h3><p>Esta &#233; uma das grandes discuss&#245;es em design de APIs financeiras. Se o JSON suporta o tipo <code>number</code>, por que a pr&#225;tica de mercado insiste em usar <code>string</code> para representar dinheiro, como em <code>{"valor": "199.99"}</code>?</p><p>A resposta &#233; <strong>design defensivo</strong>. A decis&#227;o n&#227;o &#233; sobre o que &#233; semanticamente "puro", mas sobre o que &#233; mais seguro em um ecossistema complexo e heterog&#234;neo.</p><p>O risco &#233; a <strong>"contamina&#231;&#227;o por ponto flutuante"</strong>: no momento em que um sistema no caminho interpreta seu n&#250;mero como um <code>double</code>, a precis&#227;o original pode ser perdida para sempre. Os culpados mais comuns s&#227;o:</p><ol><li><p><strong>Clientes em JavaScript:</strong> Se sua API retorna <code>{"valor": 199.99}</code>, o <code>JSON.parse()</code> no navegador ou no Node.js ir&#225;, inevitavelmente, criar um <code>number</code> do tipo <code>double</code>. A precis&#227;o j&#225; foi corrompida na chegada. Qualquer c&#225;lculo feito no frontend a partir dali carregar&#225; a imprecis&#227;o.</p></li><li><p><strong>Sistemas Intermedi&#225;rios:</strong> Uma API Gateway, um servi&#231;o de cache ou uma plataforma de logging podem processar seu JSON no meio do caminho. Se essas ferramentas n&#227;o forem configuradas para lidar com decimais de alta precis&#227;o, elas podem ler o <code>number</code> e reescrev&#234;-lo, potencialmente alterando seu valor em n&#237;vel bin&#225;rio.</p></li><li><p><strong>Consumidores Poliglotas:</strong> Voc&#234; n&#227;o tem controle sobre a stack de tecnologia de todos que consumir&#227;o sua API. Uma equipe pode usar uma linguagem que trata n&#250;meros JSON como <code>double</code> por padr&#227;o.</p></li></ol><p>A representa&#231;&#227;o como <strong>string &#233; inequ&#237;voca</strong>. <code>"199.99"</code> &#233; um texto. Ela for&#231;a o desenvolvedor do outro lado a fazer uma convers&#227;o expl&#237;cita e intencional para <strong>um tipo decimal seguro em sua linguagem, em vez de aceitar passivamente um </strong><code>number</code><strong> que j&#225; pode estar "contaminado"</strong>.</p><p><strong>Ent&#227;o apenas refor&#231;ando:</strong></p><ul><li><p>Para <strong>APIs p&#250;blicas ou ecossistemas heterog&#234;neos</strong>, usar <strong>strings</strong> para valores monet&#225;rios &#233; o padr&#227;o-ouro da ind&#250;stria para garantir m&#225;xima seguran&#231;a e interoperabilidade.</p></li><li><p>Para <strong>APIs internas e controladas</strong>, onde voc&#234; garante que todo o ecossistema (produtor e consumidor) trata n&#250;meros JSON com tipos decimais de forma consistente, o uso do tipo <code>number</code> &#233; vi&#225;vel, mas exige maior governan&#231;a.</p></li></ul><h3><strong>As Regras do Jogo: O que o BACEN Exige?</strong></h3><p>Beleza, agora a coisa fica s&#233;ria. Pensa no Banco Central como se fosse o arquiteto de uma API que voc&#234; &#233; obrigado a consumir. N&#227;o d&#225; para inventar os par&#226;metros ou mandar os dados do jeito que voc&#234; quer. A matem&#225;tica do seu sistema financeiro tem um "contrato" a seguir, e fugir dele n&#227;o gera um <code>400 Bad Request</code>, gera multa.</p><p>Vamos ver os "endpoints" mais cr&#237;ticos dessa API regulat&#243;ria.</p><h4><strong>1. O "Pre&#231;o de Verdade": Custo Efetivo Total (CET)</strong></h4><p>Sabe o "pre&#231;o de verdade" de um empr&#233;stimo ou financiamento? &#201; isso que &#233; o <a href="https://www.bcb.gov.br/cidadaniafinanceira/tiposemprestimo">CET</a>. N&#227;o &#233; s&#243; aquela taxa de juros bonitinha que o marketing adora anunciar. O BACEN, que n&#227;o &#233; bobo nem nada, exige que voc&#234; mostre <strong>TUDO</strong> o que o cliente vai pagar, de forma percentual. Isso inclui os juros, claro, mas tamb&#233;m todas as tarifas, o IOF, seguros e at&#233; o custo do cafezinho, se ele for obrigat&#243;rio na opera&#231;&#227;o.</p><p><strong>A documenta&#231;&#227;o oficial?</strong> A "b&#237;blia" que voc&#234; precisa ter aberta na sua segunda tela &#233; a <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Resolu%C3%A7%C3%A3o%20CMN&amp;numero=5113">Resolu&#231;&#227;o CMN N&#186; 5.113</a>, de 21 de dezembro de 2023</strong>.</p><p><strong>O que isso significa pro seu c&#243;digo?</strong> Voc&#234; ter&#225; que implementar a f&#243;rmula de juros compostos que o BACEN define para achar essa taxa. &#201; uma conta que precisa ser cravada, porque pode ter certeza que o fiscal do outro lado vai rodar o mesmo c&#225;lculo para ver se bate.</p><h4><strong>2. Arredondamento: O </strong><code>Math.round()</code><strong> Proibido</strong></h4><p>Agora, um cl&#225;ssico onde muito dev j&#250;nior escorrega. Primeira regra do clube da programa&#231;&#227;o financeira: <strong>esquece o </strong><code>Math.round()</code><strong> padr&#227;o da sua linguagem.</strong> Ele simplesmente n&#227;o serve aqui.</p><p>No Brasil, a regra do jogo para arredondar dinheiro se chama <strong>ABNT NBR 5891</strong>. &#201; o &#250;nico <code>round()</code> que o BACEN confia. O resumo da &#243;pera &#233;:</p><ul><li><p>Se o d&#237;gito a ser jogado fora for de 0 a 4, beleza, fica como est&#225;.</p></li><li><p>Se for de 6 a 9, arredonda pra cima.</p></li><li><p><strong>A pegadinha est&#225; no 5:</strong> Se o n&#250;mero a ser descartado &#233; exatamente 5, voc&#234; n&#227;o arredonda pra cima sempre. Voc&#234; arredonda para o algarismo par mais pr&#243;ximo. Por exemplo, <code>2,35</code> vira <code>2,4</code>, mas <code>2,45</code> tamb&#233;m vira <code>2,4</code>.</p></li></ul><p><strong>Por que essa complica&#231;&#227;o?</strong> &#201; um detalhe nerd genial para evitar vi&#233;s estat&#237;stico. Em milh&#245;es de opera&#231;&#245;es, o arredondamento tradicional "sempre para cima no 5" faria o banco (ou o cliente) ganhar/perder dinheiro. <strong>Arredondar para o par mais pr&#243;ximo equilibra o jogo.</strong></p><h4><strong>3. Quita&#231;&#227;o Antecipada: O Cliente Quer Pagar Antes, e Agora?</strong></h4><p>Outra situa&#231;&#227;o batata: o cliente juntou uma grana e quer quitar o financiamento. Ele pode? Sim. Ele tem desconto? <strong>Com certeza.</strong></p><p>E quem manda nisso &#233; a <strong><a href="https://www.bcb.gov.br/estabilidadefinanceira/exibenormativo?tipo=Resolu%C3%A7%C3%A3o%20CMN&amp;numero=5112">Resolu&#231;&#227;o CMN N&#186; 5.112, de 21 de dezembro de 2023</a></strong>. Ela garante que o cliente tem o direito de ter os juros das parcelas futuras abatidos.</p><p><strong>A l&#243;gica &#233; justa:</strong> se o banco n&#227;o vai mais correr o risco daquele tempo todo, voc&#234; n&#227;o tem que pagar os juros correspondentes.</p><p><strong>O que isso significa pro seu c&#243;digo?</strong> Seu sistema precisa ter uma fun&#231;&#227;o para "trazer a valor presente" as parcelas futuras. A conta usa a mesma taxa de juros do contrato, e, de novo, a matem&#225;tica financeira tem que ser exata, sem margem para interpreta&#231;&#227;o.</p><h4><strong>4. Transpar&#234;ncia: O "Esp&#237;rito da Lei"</strong></h4><p>Por fim, um tema que costura tudo isso: <strong>transpar&#234;ncia</strong>. N&#227;o adianta fazer a matem&#225;tica certa se voc&#234; a exibe de um jeito que confunde o cliente.</p><p>O BACEN n&#227;o tem uma &#250;nica regra para isso, mas sim um princ&#237;pio que aparece em todas as normas que a gente citou. As taxas precisam ser claras (mensal, anual, CET), os custos precisam estar discriminados, e o cliente precisa conseguir, de forma f&#225;cil, entender o que est&#225; pagando.</p><p>&#201; a camada de "UX" da regula&#231;&#227;o. Seu sistema n&#227;o precisa s&#243; ser matematicamente perfeito, ele precisa ser honesto na apresenta&#231;&#227;o.</p><h3><strong>E a Norma Internacional? A ISO 20022 como "Contrato Universal"</strong></h3><p>At&#233; agora, falamos sobre garantir a precis&#227;o <em>dentro</em> do nosso sistema. Mas de que adianta nossa calculadora ser perfeita se, ao enviar o resultado para outro banco, a mensagem se perde na tradu&#231;&#227;o?</p><p>Antes de padr&#245;es como a <a href="https://www.bcb.gov.br/estabilidadefinanceira/ISO20022">ISO 20022</a>, o mundo financeiro era uma Torre de Babel. Cada banco, cada sistema, tinha seu pr&#243;prio formato de arquivo, seu pr&#243;prio jeito de representar uma data ou um valor. A integra&#231;&#227;o era um pesadelo de parsers customizados, e cada um desses "tradutores" era um ponto de falha em potencial para a precis&#227;o dos dados.</p><p>A ISO 20022 chega para ser o "dicion&#225;rio global" das finan&#231;as. Olhando para o cen&#225;rio global, ela &#233; o padr&#227;o que define a troca de mensagens financeiras (o Pix, por exemplo, &#233; baseado nela). A norma n&#227;o dita <em>como calcular</em>, mas dita <strong>como representar e transmitir os dados de forma inequ&#237;voca.</strong></p><h4><strong>O que isso significa na pr&#225;tica para o seu c&#243;digo?</strong></h4><ol><li><p><strong>Fim da Ambiguidade (Al&#233;m do Dinheiro):</strong> A norma define, para cada moeda, o n&#250;mero de casas decimais que ela suporta (ex: 'BRL' tem 2, 'USD' tem 2, mas 'CLF', um &#237;ndice chileno, tem 4). Mas vai muito al&#233;m: ela padroniza <strong>tudo</strong>. Formatos de data (<code>ISODateTime</code>), c&#243;digos de pa&#237;s, estrutura de endere&#231;os, tipos de identificadores. Voc&#234; n&#227;o precisa mais adivinhar se a data do cliente vir&#225; como <code>DD/MM/AAAA</code> ou <code>AAAA-MM-DD</code>. O padr&#227;o dita o formato, eliminando uma classe inteira de bugs de parsing.</p></li><li><p><strong>Estrutura Previs&#237;vel (O DNA do PIX):</strong> Toda a comunica&#231;&#227;o do Pix com o SPI (Sistema de Pagamentos Instant&#226;neos) acontece via mensagens XML estruturadas sob a ISO 20022. A mensagem de uma ordem de pagamento, por exemplo, &#233; a <code>pacs.008</code>. Dentro dela, h&#225; campos rigidamente definidos para cada peda&#231;o da informa&#231;&#227;o: quem paga, quem recebe, o valor exato (<code>IntrBkSttlmAmt</code>), o identificador da transa&#231;&#227;o (<code>EndToEndId</code>), etc. Seu sistema pode n&#227;o gerar esse XML diretamente, mas o banco ou PSP para o qual voc&#234; se conecta, com certeza o faz. Uma falha de precis&#227;o ou formato em qualquer um desses campos causa a rejei&#231;&#227;o da transa&#231;&#227;o pelo SPI.</p></li><li><p><strong>Contratos de Dados Alinhados:</strong> Ao construir um sistema que lida com Pix, a melhor pr&#225;tica &#233; alinhar seus pr&#243;prios modelos de dados (suas classes, <code>structs</code> ou interfaces) com os conceitos da ISO 20022. Se a norma define um <code>EndToEndId</code>, seu objeto <code>Transacao</code> tamb&#233;m deve ter um <code>endToEndId</code>. Isso torna o mapeamento entre seu sistema e o mundo exterior muito mais simples, robusto e menos propenso a erros.</p></li></ol><p>A <a href="https://www.iso20022.org/about-iso-20022">ISO 20022</a> for&#231;a os sistemas a concordarem sobre a precis&#227;o e o formato dos valores que est&#227;o trocando, evitando erros de interpreta&#231;&#227;o entre bancos que rodam em tecnologias, linguagens e at&#233; pa&#237;ses diferentes. Ela &#233; a garantia de que um valor, calculado com precis&#227;o decimal no seu sistema, n&#227;o ser&#225; corrompido ou mal interpretado ao viajar pela rede financeira global.</p><p>Galera quanta coisa vimos at&#233; aqui hein! &#128517;</p><p class="button-wrapper" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-financeira/comments&quot;,&quot;text&quot;:&quot;Deixe um coment&#225;rio&quot;,&quot;action&quot;:null,&quot;class&quot;:null}" data-component-name="ButtonCreateButton"><a class="button primary" href="https://chroniclesofapragmaticprogrammer.substack.com/p/engenharia-financeira/comments"><span>Deixe um coment&#225;rio</span></a></p><h2><strong>A Ponta do Iceberg</strong></h2><p>Exploramos cinco princ&#237;pios que formam a espinha dorsal de qualquer sistema financeiro robusto. Cobrimos o b&#225;sico, o inegoci&#225;vel, o alicerce sobre o qual softwares confi&#225;veis s&#227;o constru&#237;dos.</p><p>Mas n&#227;o se engane: esta &#233; apenas a ponta do iceberg. O universo da engenharia de software para finan&#231;as &#233; vasto e profundo. Ficaram de fora temas cruciais como estrat&#233;gias de seguran&#231;a em m&#250;ltiplas camadas, o design de processos de concilia&#231;&#227;o cont&#225;bil, a complexidade dos sistemas de mensageria e a arquitetura para suportar baix&#237;ssima lat&#234;ncia em cen&#225;rios de alta frequ&#234;ncia.</p><p>Quem sabe em um pr&#243;ximo artigo, n&#227;o mergulhamos nesses t&#243;picos mais avan&#231;ados? Por agora, o mais importante &#233; consolidar o que vimos at&#233; aqui.</p><h4><strong>O que n&#243;s, como programadores, aprendemos?</strong></h4><p>Se pud&#233;ssemos resumir essa conversa em alguns pontos altos, o que levar&#237;amos para nossa pr&#243;xima <code>feature</code>, nosso pr&#243;ximo servi&#231;o ou nossa pr&#243;xima <code>startup</code>?</p><ol><li><p><strong>Aprendemos a respeitar o tempo.</strong> Vimos que o <strong>passado &#233; imut&#225;vel</strong> e n&#227;o pode ser reescrito. Nosso trabalho &#233; registrar a hist&#243;ria financeira, n&#227;o alter&#225;-la. Entendemos que o <strong>presente &#233; ca&#243;tico e repetitivo</strong>, e por isso a <strong>idempot&#234;ncia</strong> &#233; nosso escudo, garantindo que a instabilidade do mundo real n&#227;o quebre a l&#243;gica do nosso sistema.</p></li><li><p><strong>Aprendemos a fazer escolhas dif&#237;ceis.</strong> Diante do dilema entre estar sempre dispon&#237;vel e estar sempre correto, entendemos que, para dinheiro, a <strong>consist&#234;ncia</strong> n&#227;o &#233; uma op&#231;&#227;o, &#233; uma obriga&#231;&#227;o. Aprendemos a travar opera&#231;&#245;es e a fazer o cliente esperar um segundo a mais para garantir que a verdade do sistema seja absoluta.</p></li><li><p><strong>Aprendemos a ser accountable (respons&#225;veis).</strong> Descobrimos que a <strong>auditoria</strong> n&#227;o &#233; um luxo ou um pedido chato do time de compliance, mas uma feature essencial. Nossa responsabilidade n&#227;o termina quando a transa&#231;&#227;o &#233; processada; ela continua na nossa capacidade de provar, de forma irrefut&#225;vel, o "qu&#234;", o "quem", o "quando" e o "porqu&#234;" de cada centavo que se moveu.</p></li><li><p><strong>Aprendemos a matem&#225;tica de verdade.</strong> Deixamos de lado a conveni&#234;ncia perigosa dos <code>floats</code> e <code>doubles</code> e abra&#231;amos a <strong>precis&#227;o matem&#225;tica</strong> dos tipos decimais. Entendemos que <code>0.1 + 0.2</code> <em>precisa</em> ser exatamente <code>0.3</code>, e que as regras do jogo, como o arredondamento, n&#227;o s&#227;o definidas por n&#243;s, mas por reguladores como o Banco Central.</p></li></ol><p>No fim das contas, a grande li&#231;&#227;o &#233; uma mudan&#231;a de mentalidade. Construir software financeiro &#233; menos sobre criar algoritmos complexos e mais sobre gerenciar risco e construir confian&#231;a. Em um aplicativo comum, um bug &#233; uma inconveni&#234;ncia. Em um aplicativo financeiro, um bug &#233; uma crise que pode abalar o bem mais precioso que uma institui&#231;&#227;o tem: a confian&#231;a do seu cliente.</p><p>O c&#243;digo que escrevemos n&#227;o move apenas dados em um banco de dados; ele move o sustento, os sonhos e o futuro financeiro das pessoas. E essa &#233; uma responsabilidade que vai muito al&#233;m de qualquer framework ou linguagem de programa&#231;&#227;o.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Obrigado por ler Cr&#244;nicas de um Programador Pragm&#225;tico! Inscreva-se gratuitamente para receber novas postagens e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><p></p><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-1" href="#footnote-anchor-1" class="footnote-number" contenteditable="false" target="_self">1</a><div class="footnote-content"><p>Se voc&#234; j&#225; se perguntou <em>&#8220;como o banco faz pra saber em qual fatura vai cair o estorno de uma compra ou o cashback?&#8221;</em>, a resposta est&#225; em duas palavrinhas que todo programador precisa entender: <strong>eventos</strong> e <strong>ledger</strong>.</p><p>Na pr&#225;tica, quando um ciclo de fatura &#233; fechado, ele se torna um <strong>snapshot imut&#225;vel</strong>. &#201; como tirar aquela foto de fam&#237;lia no Natal: ningu&#233;m vai poder entrar ou sair depois do clique. O sistema registra todos os d&#233;bitos daquele per&#237;odo e congela o saldo.</p><p>S&#243; que a vida real continua. Voc&#234; cancela uma compra, recebe um cashback, o IOF de uma compra internacional &#233; estornado&#8230; E a&#237;? O banco n&#227;o reabre a foto antiga. Ele cria <strong>novos eventos</strong> no ledger.</p><p>Pense no ledger como um di&#225;rio cont&#225;bil distribu&#237;do:</p><ul><li><p>Cada transa&#231;&#227;o vira uma linha, com <strong>tipo (cr&#233;dito/d&#233;bito)</strong>, <strong>data</strong> e <strong>refer&#234;ncia</strong>.</p></li><li><p>Quando a fatura do m&#234;s passado foi fechada, o ledger marcou todas as transa&#231;&#245;es dela como &#8220;consolidadas&#8221;.</p></li><li><p>Se um cr&#233;dito chega depois (cancelamento, cashback, estorno), esse evento &#233; registrado no ledger e <strong>mapeado para o ciclo atual</strong>, que est&#225; em aberto.</p></li></ul><p>Ou seja: o cancelamento nunca &#8220;apaga&#8221; o lan&#231;amento anterior. Ele gera uma nova linha compensat&#243;ria que vai aparecer como cr&#233;dito na sua pr&#243;xima fatura.</p><p>A Pismo (e outros processadores modernos) tratam tudo como <strong>eventos financeiros imut&#225;veis</strong>. Cada compra aprovada gera um evento de d&#233;bito. Cada estorno, um evento de cr&#233;dito. A fatura nada mais &#233; do que uma vis&#227;o agregada do ledger dentro de um intervalo de tempo.</p><p>Isso tem algumas vantagens enormes:</p><ul><li><p><strong>Auditabilidade</strong>: voc&#234; sempre consegue reconstruir qualquer fatura passada s&#243; olhando os eventos daquele per&#237;odo.</p></li><li><p><strong>Idempot&#234;ncia</strong>: o sistema n&#227;o depende de &#8220;atualizar linhas&#8221;, mas de adicionar novos eventos.</p></li><li><p><strong>Clareza cont&#225;bil</strong>: cr&#233;dito e d&#233;bito ficam registrados como fatos distintos, e n&#227;o como &#8220;edi&#231;&#245;es de fatura&#8221;.</p></li></ul><p>No fim das contas, &#233; por isso que um estorno nunca mexe na fatura fechada: porque do ponto de vista de sistemas financeiros, <strong>n&#227;o existe &#8220;editar&#8221; &#8212; s&#243; adicionar novos eventos ao ledger</strong>.</p><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-2" href="#footnote-anchor-2" class="footnote-number" contenteditable="false" target="_self">2</a><div class="footnote-content"><p>O fluxo funciona assim pessoal:</p><ol><li><p><strong>Gera&#231;&#227;o da Chave:</strong> O cliente (o aplicativo, o site, etc.) que vai fazer a requisi&#231;&#227;o gera um identificador &#250;nico para aquela opera&#231;&#227;o. Geralmente &#233; um UUID (Identificador &#218;nico Universal).</p><ul><li><p><code>Idempotency-Key: a1b2c3d4-e5f6-7890-1234-567890abcdef</code></p></li></ul></li><li><p><strong>Primeira Requisi&#231;&#227;o:</strong></p><ul><li><p>O cliente envia a requisi&#231;&#227;o de pagamento (e.g., <code>POST /pagamentos</code>) incluindo essa chave em um cabe&#231;alho (header).</p></li><li><p>O servidor recebe a requisi&#231;&#227;o.</p></li><li><p>Ele verifica em seu banco de dados ou cache: &#8220;Eu j&#225; vi essa chave <code>a1b2c3d4-...</code> antes?&#8221;</p></li><li><p>A resposta &#233; <strong>n&#227;o</strong>.</p></li><li><p>Ent&#227;o, o servidor <strong>processa o pagamento</strong>, debita os R$ 200, e <strong>salva a chave de idempot&#234;ncia</strong> junto com o resultado da opera&#231;&#227;o.</p></li><li><p>Ele retorna uma resposta de sucesso (ex: <code>201 Created</code>).</p></li></ul></li><li><p><strong>Requisi&#231;&#227;o Repetida (Retransmiss&#227;o por Falha de Rede):</strong></p><ul><li><p>O cliente, por algum motivo (ex: timeout de rede), n&#227;o recebeu a resposta de sucesso e tenta novamente, enviando a <strong>exatamente mesma requisi&#231;&#227;o</strong>, com a <strong>mesma Chave de Idempot&#234;ncia</strong>.</p></li><li><p>O servidor recebe a requisi&#231;&#227;o.</p></li><li><p>Ele verifica novamente: &#8220;Eu j&#225; vi essa chave <code>a1b2c3d4-...</code> antes?&#8221;</p></li><li><p>A resposta agora &#233; <strong>sim</strong>.</p></li><li><p>O servidor <strong>N&#195;O processa o pagamento novamente</strong>. Em vez disso, ele busca o resultado salvo da primeira vez e retorna a <strong>mesma resposta de sucesso</strong> (<code>201 Created</code>) que enviou anteriormente.</p></li></ul></li></ol><p>Para o cliente, o resultado &#233; o mesmo nas duas vezes. <strong>Para o sistema, a opera&#231;&#227;o cr&#237;tica (o d&#233;bito) s&#243; aconteceu uma vez.</strong></p><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-3" href="#footnote-anchor-3" class="footnote-number" contenteditable="false" target="_self">3</a><div class="footnote-content"><p>A palavra <strong>&#8220;<a href="https://www.infopedia.pt/dicionarios/lingua-portuguesa/deduplicar">deduplicar</a>&#8221;</strong> significa, literalmente, <strong>remover as duplicatas</strong>.</p><p>&#201; o processo de identificar e eliminar c&#243;pias repetidas ou redundantes de um mesmo dado, item ou, como no nosso exemplo anterior, de uma mesma <strong>opera&#231;&#227;o</strong>.</p><p>O objetivo principal da deduplica&#231;&#227;o &#233; garantir que cada &#8220;coisa&#8221; exista apenas uma vez, de forma &#250;nica.</p><div><hr></div></div></div><div class="footnote" data-component-name="FootnoteToDOM"><a id="footnote-4" href="#footnote-anchor-4" class="footnote-number" contenteditable="false" target="_self">4</a><div class="footnote-content"><p>Para isso temos <a href="https://docs.aws.amazon.com/pt_br/prescriptive-guidance/latest/modernization-data-persistence/saga-pattern.html">Saga </a>que &#233; a alternativa mais famosa e discutida porque representa uma mudan&#231;a de filosofia fundamental (trocar consist&#234;ncia imediata por eventual), mas existem outras estrat&#233;gias no nosso cinto de ferramentas, algumas que at&#233; podem ser combinadas com Sagas para torn&#225;-las mais robustas.</p><p>Vamos dar uma olhada no card&#225;pio de op&#231;&#245;es. &#128513;</p><h3>1. <a href="https://docs.aws.amazon.com/pt_br/prescriptive-guidance/latest/cloud-design-patterns/transactional-outbox.html">Padr&#227;o Transactional Outbox</a> (O &#8220;Carteiro Confi&#225;vel&#8221;)</h3><p>Esse padr&#227;o n&#227;o &#233; tanto uma alternativa completa &#224; Saga, mas uma t&#233;cnica crucial para faz&#234;-la funcionar de forma confi&#225;vel. Ele resolve um problema espec&#237;fico: &#8220;Como eu garanto que, ap&#243;s salvar algo no meu banco de dados, a mensagem para o pr&#243;ximo servi&#231;o ser&#225; <em>realmente</em> enviada, mesmo que a minha aplica&#231;&#227;o caia no meio do caminho?&#8221;.</p><p><strong>Como funciona:</strong> Em vez de fazer duas coisas separadas (salvar no banco e depois publicar no Kafka/RabbitMQ), voc&#234; faz uma &#250;nica opera&#231;&#227;o at&#244;mica:</p><ol><li><p><strong>Dentro da mesma transa&#231;&#227;o do banco de dados</strong>, voc&#234; salva a sua entidade de neg&#243;cio (ex: <code>Pedido</code>) E insere uma linha em uma tabela chamada <code>outbox_events</code> com a mensagem que voc&#234; quer enviar (ex: um evento <code>PedidoCriado</code>).</p></li><li><p>Um processo separado e ass&#237;ncrono monitora essa tabela <code>outbox_events</code>. Quando ele v&#234; uma nova linha, ele a publica no message broker e, s&#243; depois de ter certeza que a mensagem foi enviada, ele marca a linha como &#8220;processada&#8221;.</p></li></ol><p><strong>Trade-off:</strong> Voc&#234; garante o envio da mensagem (&#8221;at-least-once delivery&#8221;), mas adiciona uma pequena lat&#234;ncia, pois o envio n&#227;o &#233; mais instant&#226;neo. &#201; a escolha pragm&#225;tica para garantir que os passos da sua Saga (ou qualquer comunica&#231;&#227;o ass&#237;ncrona) n&#227;o se percam no caminho.</p><h3>2. TCC (Try-Confirm/Cancel)</h3><p>Este &#233; um pouco pr&#243;ximo do 2PC e da Saga, mas com uma abordagem de &#8220;reserva&#8221;. Tente pensar nele como reservar uma mesa em um restaurante.</p><p><strong>Como funciona em tr&#234;s fases:</strong></p><ol><li><p><strong>Try (Tentar/Reservar):</strong> O orquestrador da transa&#231;&#227;o pede a cada servi&#231;o para <em>reservar</em> os recursos necess&#225;rios, mas sem efetiv&#225;-los. Ex: o servi&#231;o de estoque n&#227;o diminui o contador do produto, mas cria uma &#8220;reserva&#8221; para aquele item, tornando-o indispon&#237;vel para outras transa&#231;&#245;es.</p></li><li><p><strong>Confirm (Confirmar):</strong> Se todas as fases &#8220;Try&#8221; foram bem-sucedidas, o orquestrador manda uma mensagem de &#8220;Confirm&#8221; para todos os servi&#231;os. Agora sim, eles efetivam a transa&#231;&#227;o (a reserva no estoque vira uma baixa definitiva).</p></li><li><p><strong>Cancel (Cancelar):</strong> Se qualquer fase &#8220;Try&#8221; falhar, o orquestrador manda uma mensagem de &#8220;Cancel&#8221; para todos os servi&#231;os que j&#225; tinham feito a reserva, pedindo para que liberem os recursos.</p></li></ol><p><strong>Diferen&#231;a da Saga:</strong> Na Saga, voc&#234; efetiva a opera&#231;&#227;o e depois compensa se algo der errado. No TCC, voc&#234; primeiro reserva tudo e s&#243; efetiva no final. Isso significa que os recursos ficam bloqueados por mais tempo, o que pode ser um problema de concorr&#234;ncia, mas evita o estado de &#8220;inconsist&#234;ncia tempor&#225;ria&#8221; da Saga, o que pode ser mais simples para o neg&#243;cio entender.</p><h3>3. Reconcilia&#231;&#227;o Peri&#243;dica (O &#8220;Auditor Noturno&#8221;)</h3><p>Essa abordagem &#233; a mais pragm&#225;tica e ainda &#233; comum no mundo financeiro. A ideia &#233;: aceite que os sistemas <em>v&#227;o</em> ficar inconsistentes em algum momento e, em vez de tentar evitar isso a todo custo em tempo real, crie um processo para detectar e corrigir as discrep&#226;ncias.</p><p><strong>Como funciona:</strong></p><ul><li><p>Os servi&#231;os operam de forma mais otimista, talvez com menos garantias transacionais.</p></li><li><p>Periodicamente (toda noite, de hora em hora), um processo de <strong>reconcilia&#231;&#227;o</strong> (ou &#8220;batch&#8221;) &#233; executado. Ele l&#234; os dados de diferentes sistemas, compara-os, encontra as diverg&#234;ncias e gera as transa&#231;&#245;es de ajuste necess&#225;rias para colocar tudo em ordem.</p></li></ul><p><strong>Exemplo cl&#225;ssico:</strong> A concilia&#231;&#227;o de extratos banc&#225;rios. O banco n&#227;o trava o mundo a cada transa&#231;&#227;o; ele processa um volume gigantesco de opera&#231;&#245;es e, no final do dia, roda processos pesados para garantir que todas as contas fechem.</p><p><strong>Trade-off:</strong> &#201; a abordagem com a menor sobrecarga transacional, mas a consist&#234;ncia &#233; a mais &#8220;eventual&#8221; de todas, podendo levar horas para ser alcan&#231;ada. &#201; perfeita para cen&#225;rios que n&#227;o exigem consist&#234;ncia em tempo real.</p><p></p></div></div>]]></content:encoded></item><item><title><![CDATA[Pare Um Pouco de Escrever Código. Comece a Fazer Perguntas Antes.]]></title><description><![CDATA[As respostas te colocam em movimento, apenas as perguntas garantem que voc&#234; est&#225; se movendo na dire&#231;&#227;o certa.]]></description><link>https://chroniclesofapragmaticprogrammer.substack.com/p/pare-um-pouco-de-escrever-codigo</link><guid isPermaLink="false">https://chroniclesofapragmaticprogrammer.substack.com/p/pare-um-pouco-de-escrever-codigo</guid><dc:creator><![CDATA[Rafael Miguel]]></dc:creator><pubDate>Tue, 23 Sep 2025 14:24:21 GMT</pubDate><enclosure url="https://substack-post-media.s3.amazonaws.com/public/images/552a1166-1564-45ac-9281-4d09c705cd4d_420x300.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Por que dedicar um artigo inteiro para falar sobre algo t&#227;o aparentemente b&#225;sico como "fazer perguntas"?</p><p>Bom, vou ser sincero com voc&#234;. Nos &#250;ltimos anos, venho notando uma tend&#234;ncia que me preocupa: <strong>a capacidade de algumas equipes de engenharia de software de realmente </strong><em><strong>questionar</strong></em><strong> as coisas parece estar diminuindo</strong>. O sil&#234;ncio em reuni&#245;es de planejamento, a aceita&#231;&#227;o passiva de tarefas mal descritas, o foco total na velocidade da entrega... <strong>tudo isso tem um custo, e ele costuma chegar na forma de retrabalho, frustra&#231;&#227;o e bugs em produ&#231;&#227;o</strong>.</p><p>E eu fico me perguntando: <strong>por que ser&#225; que isso acontece? &#129300;</strong></p><p>Ser&#225; que o ambiente da empresa ou da equipe &#233; t&#243;xico, e as pessoas t&#234;m medo de falar? Ser&#225; falta de maturidade do time? Ou talvez a equipe seja muito nova de carreira? Ser&#225; que n&#227;o est&#227;o engajados o suficiente com o produto para se importarem com o "porqu&#234;"?</p><p><strong>Ou ser&#225; que ca&#237;mos numa armadilha mais sutil?</strong> <strong>A de que, na nossa busca incessante por "agilidade" e "entrega cont&#237;nua", simplesmente nos esquecemos de ensinar &#8212; e de valorizar &#8212; a pausa para a pergunta certa</strong>. Aquela pergunta que valida um comportamento, que desafia uma premissa, que evita um erro <code>500</code> numa sexta-feira &#224; noite.</p><p>&#201; por isso que estamos aqui. Este artigo &#233; um convite para resgatar essa habilidade que considero essencial. Juntos, vamos explorar por que essa capacidade de questionar se tornou o verdadeiro diferencial na carreira de um programador, muito al&#233;m de qualquer framework ou linguagem da moda. Vamos desafiar a ideia de que perguntar &#233; "coisa de j&#250;nior" e mostrar como essa necessidade, na verdade, s&#243; aumenta com a senioridade.</p><div class="subscription-widget-wrap-editor" data-attrs="{&quot;url&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/subscribe?&quot;,&quot;text&quot;:&quot;Inscreva-se&quot;,&quot;language&quot;:&quot;pt-br&quot;}" data-component-name="SubscribeWidgetToDOM"><div class="subscription-widget show-subscribe"><div class="preamble"><p class="cta-caption">Inscreva-se gratuitamente para receber novas postagens e apoiar meu trabalho.</p></div><form class="subscription-widget-subscribe"><input type="email" class="email-input" name="email" placeholder="Digite seu e-mail&#8230;" tabindex="-1"><input type="submit" class="button primary" value="Inscreva-se"><div class="fake-input-wrapper"><div class="fake-input"></div><div class="fake-button"></div></div></form></div></div><h2><strong>A Pergunta Como Ferramenta de Escava&#231;&#227;o</strong></h2><p>Pense na sua caixa de ferramentas como programador. Voc&#234; tem seu teclado, seu editor de c&#243;digo, seu debugger... Mas e se a sua ferramenta mais poderosa n&#227;o for nenhuma dessas? <strong>E se for uma p&#225;?</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!flTq!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!flTq!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!flTq!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!flTq!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!flTq!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!flTq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png" width="420" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/e695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:420,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:20431,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/171795862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!flTq!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!flTq!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!flTq!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!flTq!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fe695b7c7-c09f-4f9d-8645-15e28773670f_420x300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>Todo dia, recebemos tarefas que s&#227;o como a superf&#237;cie de um terreno. O pedido diz: "Construa um muro aqui". <strong>Nossa rea&#231;&#227;o imediata, quase um reflexo, &#233; come&#231;ar a empilhar tijolos de c&#243;digo. Mas... o que h&#225; por baixo dessa superf&#237;cie?</strong></p><p>A mentalidade de executor se contenta em trabalhar na superf&#237;cie. A tarefa diz "crie um bot&#227;o", voc&#234; cria o bot&#227;o. Miss&#227;o cumprida. Mas um verdadeiro engenheiro-escavador sabe que o valor raramente est&#225; na superf&#237;cie. Ele est&#225; enterrado, escondido sob camadas de suposi&#231;&#245;es.</p><p>Vamos ao exemplo real, no setor de preven&#231;&#227;o a fraudes de um banco. A tarefa que chega para voc&#234; &#233;: <strong>"Para cada transa&#231;&#227;o que nosso sistema marca como </strong><code>SUSPECT_FRAUD</code><strong>, precisamos que um evento seja publicado em um t&#243;pico do Kafka."</strong></p><p>O executor, ao ouvir "Kafka", j&#225; pensa no producer, na serializa&#231;&#227;o dos dados, e come&#231;a a codificar. Mas voc&#234;, o escavador, pega sua p&#225; &#8211; a pergunta &#8211; e d&#225; a primeira cavada para entender o motivo por tr&#225;s da ferramenta:</p><blockquote><p>"Legal, podemos fazer isso. S&#243; para eu entender o objetivo: qual &#233; a dor que voc&#234;s est&#227;o tentando resolver com esses eventos? O que o processo atual de voc&#234;s n&#227;o atende?"</p></blockquote><p>A resposta pode ser: "A equipe de analistas de fraude est&#225; com problemas para avaliar as transa&#231;&#245;es suspeitas. A query que eles rodam direto no banco de dados para buscar o status <code>SUSPECT_FRAUD</code> &#233; muito lenta e demora demais."</p><p>Faz sentido. A escava&#231;&#227;o continua, agora um pouco mais fundo, para entender o plano deles:</p><blockquote><p>"Perfeito, entendi a dor. E como o time de analistas planeja consumir esses eventos do t&#243;pico do Kafka? Voc&#234;s j&#225; t&#234;m uma API ou um servi&#231;o preparado para processar esse fluxo, ou a ideia &#233; buscar manualmente no t&#243;pico?"</p></blockquote><p>E &#233; aqui que a sua picareta atinge a rocha. A resposta vem com uma certa hesita&#231;&#227;o: <em>"Bem, a gente pensou em usar uma ferramenta para visualizar o t&#243;pico e procurar as transa&#231;&#245;es l&#225; dentro..."</em></p><p><strong>Aha! &#128517;</strong></p><p>Voc&#234; percebe? O problema real n&#227;o era a falta de dados no Kafka. O problema real, a "dor" enterrada, era <strong>a falta de uma ferramenta eficiente e perform&#225;tica para os analistas acessarem e trabalharem com os dados de transa&#231;&#245;es suspeitas.</strong> Eles estavam prestes a trocar um processo lento e manual (query no banco) por outro processo igualmente lento e manual (pesquisar em um t&#243;pico bruto do Kafka).</p><p>E aqui &#233; preciso ser ainda mais realista. Mesmo que o t&#243;pico seja <em>apenas</em> para transa&#231;&#245;es de suspeita de fraude, em um banco com m&#250;ltiplos produtos &#8212; cart&#227;o de cr&#233;dito, transfer&#234;ncias, PIX, investimentos &#8212; o volume ainda seria gigantesco. A sua pr&#243;xima pergunta de escava&#231;&#227;o poderia ser: </p><p><em>"E o que acontece depois que um analista identifica uma dessas transa&#231;&#245;es no t&#243;pico?"</em>. </p><p>A resposta revelaria ainda mais camadas do problema.</p><p>Voc&#234; descobriria que <strong>alguns casos precisam de uma an&#225;lise manual e minuciosa</strong>, onde o analista cruza dados. Outros poderiam <strong>gerar processos autom&#225;ticos</strong>, como um bloqueio preventivo ou o envio de uma notifica&#231;&#227;o. <strong>Veja como as perguntas promovem o entendimento real do problema!</strong> A solu&#231;&#227;o n&#227;o &#233; apenas "mostrar os dados". <em>A solu&#231;&#227;o precisa suportar diferentes fluxos de trabalho</em>, <strong>priorizar casos</strong> e permitir que os analistas gerenciem suas filas de an&#225;lise.</p><p>Com a funda&#231;&#227;o do problema finalmente exposta, a conversa muda de "como publicar no Kafka?" para um universo de solu&#231;&#245;es muito mais inteligentes:</p><ul><li><p>"E se, em vez de voc&#234;s terem o trabalho de consumir o t&#243;pico, n&#243;s cri&#225;ssemos uma <strong>API dedicada e otimizada para a &#225;rea de fraudes</strong>? Ela poderia ser a consumidora desses eventos e j&#225; entregar os dados prontos para an&#225;lise."</p></li><li><p>"Ou talvez possamos construir um <strong>pequeno painel de alertas que seja 'alimentado' por esses eventos, j&#225; mostrando os casos mais cr&#237;ticos em ordem de prioridade</strong> e permitindo que o analista 'reivindique' um caso para si?"</p></li><li><p>"Dando um passo atr&#225;s, j&#225; investigamos por que a query original no banco &#233; t&#227;o lenta? Talvez uma <strong>otimiza&#231;&#227;o de &#237;ndice</strong> resolva 80% do problema com 10% do esfor&#231;o."</p></li></ul><p>Muitos acreditam que esse tipo de questionamento "n&#227;o &#233; trabalho deles". Mas essa &#233; a diferen&#231;a entre engenharia e simples constru&#231;&#227;o. <strong>Um construtor ergue a parede onde mandam. Um engenheiro pergunta se a funda&#231;&#227;o aguenta e se a parede est&#225; no lugar certo.</strong></p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!wYsV!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!wYsV!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!wYsV!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!wYsV!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!wYsV!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!wYsV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png" width="420" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:420,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:23458,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/171795862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!wYsV!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!wYsV!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!wYsV!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!wYsV!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F301b3839-4bd7-4dba-a359-a3cb35ff11ac_420x300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>No final, o executor entrega o producer do Kafka. A equipe de fraude agora tem um novo problema: um mar de eventos que eles n&#227;o conseguem usar. O programador entregou a tarefa, mas a dor continua a mesma. O escavador iniciou uma discuss&#227;o que resolveu a lentid&#227;o e melhorou o fluxo de trabalho do analista. Ele entregou valor real. Ent&#227;o a li&#231;&#227;o &#233;&#8230; Pegue sua p&#225;. Fa&#231;a a primeira pergunta. <strong>Comece a escavar. &#128579;</strong></p><div class="pullquote"><p>Por que dedicar um artigo inteiro para falar sobre algo t&#227;o aparentemente b&#225;sico como "fazer perguntas"? Porque a pergunta certa economiza semanas de c&#243;digo. A pergunta errada gera meses de d&#237;vida t&#233;cnica.</p></div><p><strong>Agora a conversa &#233; com voc&#234;s iniciantes e programadores juniors!</strong></p><h2><strong>Porque alguns programadores juniors temem fazer perguntas?</strong></h2><p>Depois de entendermos que as perguntas s&#227;o nossa melhor ferramenta de escava&#231;&#227;o, surge uma quest&#227;o desconfort&#225;vel: por que justamente aqueles que mais precisam escavar &#8212; os programadores em in&#237;cio de carreira &#8212; s&#227;o frequentemente os mais silenciosos?</p><p>Se voc&#234; &#233; um j&#250;nior, talvez sinta um n&#243; na garganta antes de clicar "enviar" naquela pergunta no chat da equipe. A m&#227;o treme um pouco, voc&#234; rel&#234; a frase dez vezes, apaga, reescreve. Se voc&#234; &#233; um s&#234;nior, talvez j&#225; tenha se perguntado por que o novo contratado, t&#227;o inteligente e promissor, parece t&#227;o relutante em pedir ajuda, preferindo &#224;s vezes ficar travado por horas a fio.</p><p>Afinal, de quem &#233; a culpa? &#201; do j&#250;nior, que n&#227;o tem "atitude"? As perguntas que ele quer fazer s&#227;o "bestas"? Ou ser&#225; que n&#243;s, como ind&#250;stria, estamos errando em algum ponto fundamental?</p><p>A verdade &#233; que esse medo raramente &#233; "culpa" do j&#250;nior. Ele &#233; um sintoma, o resultado de uma combina&#231;&#227;o poderosa de press&#245;es internas e, principalmente, de falhas no ambiente ao seu redor.</p><p>O primeiro vil&#227;o &#233; um inimigo interno que todos n&#243;s conhecemos bem: a <strong>S&#237;ndrome do Impostor</strong>. O programador j&#250;nior, rec&#233;m-chegado, muitas vezes se sente como um fraude que, por algum milagre, conseguiu passar pelo processo seletivo. Ele olha para os colegas mais experientes, que discutem arquiteturas complexas com uma fluidez assustadora, e pensa:</p><blockquote><p><em>&#8220;Todos aqui s&#227;o g&#234;nios. Eu n&#227;o sei nada. Se eu fizer essa pergunta, eles v&#227;o achar que cometeram um erro ao me contratar."</em> </p></blockquote><p>Nessa cabe&#231;a, o objetivo prim&#225;rio n&#227;o &#233; aprender, mas <em>sobreviver e provar seu valor</em>. E, na sua l&#243;gica, fazer uma pergunta &#233; o equivalente a levantar uma placa de neon escrito "EU N&#195;O SOU BOM O SUFICIENTE". Obviamente isso n&#227;o &#233; verdade! Pense comigo caro leitor! Existem perguntas "bestas"? Vamos ser brutalmente honestos: <strong>a &#250;nica pergunta verdadeiramente besta &#233; aquela que n&#227;o foi feita e que resultou em um bug em produ&#231;&#227;o ou em tr&#234;s dias de trabalho desperdi&#231;ado.</strong> </p><p>O que parece "&#243;bvio" para um s&#234;nior &#233; um universo desconhecido para o j&#250;nior. Ele n&#227;o tem o contexto hist&#243;rico das decis&#245;es, das regras de neg&#243;cio. Ele n&#227;o conhece os jarg&#245;es e as siglas internas da empresa. Uma pergunta como:</p><blockquote><p>"Desculpe, o que voc&#234;s querem dizer com 'servi&#231;o legado'?" </p></blockquote><p><strong>N&#227;o &#233; boba; &#233; um pedido por um mapa para que ele possa se orientar no novo territ&#243;rio.</strong></p><p>E &#233; aqui que a ind&#250;stria e a cultura da equipe entram em cena. Muitas vezes, o ambiente n&#227;o s&#243; falha em encorajar perguntas, como ativamente as puni. <strong>Quantos de n&#243;s j&#225; n&#227;o vimos uma pergunta em um f&#243;rum como o Stack Overflow ser recebida com respostas r&#237;spidas ou ser fechada por ser "duplicada"?</strong> Essa cultura do "descubra sozinho a qualquer custo", esse batismo de fogo onde sofrer em sil&#234;ncio &#233; visto como um rito de passagem, ensina uma li&#231;&#227;o terr&#237;vel para quem est&#225; come&#231;ando: <strong>"Perguntar &#233; perigoso. Fique quieto."</strong></p><p>A vontade de um j&#250;nior perguntar &#233; o term&#244;metro mais preciso da <strong>seguran&#231;a psicol&#243;gica</strong> de uma equipe. Pense em dois cen&#225;rios:</p><ul><li><p><strong>Equipe A:</strong> O j&#250;nior pergunta algo. O s&#234;nior responde com um suspiro, ou com um passivo-agressivo "Isso est&#225; na documenta&#231;&#227;o, voc&#234; leu?", ou pior, ignora. A mensagem &#233; clara: "Voc&#234; me fez perder tempo". A li&#231;&#227;o que o j&#250;nior aprende? "Nunca mais pergunte nada a essa pessoa."</p></li><li><p><strong>Equipe B:</strong> O j&#250;nior pergunta. O s&#234;nior para o que est&#225; fazendo e diz: "&#211;tima pergunta. Eu tamb&#233;m tive essa d&#250;vida no come&#231;o. Deixa eu te mostrar como eu costumo pensar sobre isso...". A mensagem &#233;: "Sua curiosidade &#233; bem-vinda aqui". A li&#231;&#227;o? "Este &#233; um lugar seguro para aprender."</p></li></ul><p>Portanto, o medo n&#227;o &#233; uma falha de car&#225;ter do j&#250;nior. &#201; o reflexo de um sistema. &#201; a s&#237;ndrome do impostor batendo de frente com uma cultura que, muitas vezes, valoriza a autossufici&#234;ncia cega em detrimento da colabora&#231;&#227;o inteligente.</p><p><strong>Para voc&#234;, j&#250;nior:</strong> entenda que sua pergunta n&#227;o &#233; um sinal de fraqueza, mas de engajamento. <strong>O risco do sil&#234;ncio &#233; sempre, sempre maior</strong>. <strong>Aprenda a formular bem sua pergunta (mostrando o contexto e o que voc&#234; j&#225; tentou), respire fundo e mande</strong>.</p><p><strong>E para voc&#234;, s&#234;nior ou l&#237;der:</strong> a responsabilidade &#233; sua. Sua rea&#231;&#227;o &#224; primeira, segunda, d&#233;cima pergunta de um novato definir&#225; o padr&#227;o de comunica&#231;&#227;o e a velocidade de aprendizado de toda a sua equipe. <strong>Acolha a d&#250;vida. Celebre a curiosidade</strong>. Lembre-se que o sil&#234;ncio de um j&#250;nior n&#227;o &#233; um sinal de que tudo est&#225; bem; na maioria das vezes, &#233; um sinal de alerta.</p><h2><strong>Por Que a Experi&#234;ncia Pode se Tornar uma Armadilha de Suposi&#231;&#245;es</strong></h2><p>Se voc&#234; tem alguma experi&#234;ncia profissional, conhece a sensa&#231;&#227;o. Naquele momento em que um problema complexo surge, enquanto todos ainda tentam entender o cen&#225;rio, seu c&#233;rebro dispara na frente. Como um veterano que reconhece um som &#224; dist&#226;ncia, voc&#234; ouve a descri&#231;&#227;o e... clique. O padr&#227;o se encaixa.</p><p>Essa capacidade de reconhecer padr&#245;es &#233; o superpoder que a experi&#234;ncia nos d&#225;. &#201; o que nos torna r&#225;pidos e eficientes. N&#227;o precisamos mais percorrer cada rua de um problema; nossa mente construiu supervias que nos levam diretamente de um desafio (Ponto A) para uma solu&#231;&#227;o comprovada (Ponto B).</p><p>Mas essa mesma superpot&#234;ncia tem um lado sombrio: <strong>a armadilha das suposi&#231;&#245;es</strong>. A l&#243;gica &#233; simples: quanto mais estradas pavimentadas voc&#234; tem na mente, menor a disposi&#231;&#227;o para explorar trilhas desconhecidas.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!8JNB!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!8JNB!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!8JNB!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!8JNB!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!8JNB!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!8JNB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png" width="420" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/a7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:420,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:17768,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/171795862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!8JNB!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!8JNB!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!8JNB!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!8JNB!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fa7c1049a-72ee-4d8c-9098-58597234b00b_420x300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>&#201; por isso que a experi&#234;ncia, muitas vezes, nos cega para as perguntas certas. Se seu c&#233;rebro j&#225; decidiu a natureza do problema, suas perguntas servir&#227;o apenas para confirmar sua rota preferida, e n&#227;o para explorar se voc&#234; est&#225; no mapa certo. Enquanto isso, a causa real pode estar em um detalhe que voc&#234; nem considerou, porque sua "sensa&#231;&#227;o" inicial apontava para outro lugar. Sua experi&#234;ncia, em vez de iluminar o caminho, colocou um par de viseiras.</p><p>O grande desafio para o profissional experiente &#233; entender que nem todo problema novo &#233; uma reedi&#231;&#227;o de um antigo. Para encontrar a nuance que muda tudo, as perguntas de sempre n&#227;o servem. &#201; preciso um novo m&#233;todo, que come&#231;a com o sil&#234;ncio. Antes de fazer a primeira pergunta, voc&#234; precisa de tr&#234;s coisas: <strong>observar, escutar e entender.</strong></p><ul><li><p><strong>Observar:</strong> Antes de saltar para sua conclus&#227;o padr&#227;o, apenas observe os fatos. Quais s&#227;o os detalhes que sua mente apressada filtrou? O que est&#225; ali, escondido &#224; vista de todos, que n&#227;o se encaixa no seu padr&#227;o mental?</p></li><li><p><strong>Escutar:</strong> Escute de verdade. Escute quem descreve a frustra&#231;&#227;o do cliente. Escute o colega com menos experi&#234;ncia, mas com um par de olhos frescos. A pe&#231;a que falta no quebra-cabe&#231;a raramente vir&#225; da sua pr&#243;pria cabe&#231;a; ela vir&#225; da perspectiva de outra pessoa.</p></li><li><p><strong>Entender:</strong> S&#243; depois de observar e escutar, voc&#234; pode juntar as pe&#231;as. "Ok, minha experi&#234;ncia aponta para uma causa comum. Mas eu escutei que o problema s&#243; acontece em uma situa&#231;&#227;o muito espec&#237;fica e observei um detalhe que parecia inofensivo." &#201; nesse momento que voc&#234; percebe que sua supervia neural te levaria para a cidade errada.</p></li></ul><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!OpQn!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!OpQn!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!OpQn!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!OpQn!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!OpQn!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!OpQn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png" width="420" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:420,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:30889,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/171795862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!OpQn!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!OpQn!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!OpQn!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!OpQn!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F5a6e7dc7-0c52-4145-b105-c187d9e6bf53_420x300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>S&#243; ent&#227;o voc&#234; est&#225; pronto para perguntar. E suas perguntas ser&#227;o completamente diferentes. Elas n&#227;o servir&#227;o mais para confirmar um vi&#233;s, mas para explorar a anomalia que voc&#234; descobriu.</p><p>Em vez de uma pergunta que fecha o mundo ("J&#225; verificamos a causa X de sempre?"), sua pergunta o expande: "O que essa situa&#231;&#227;o espec&#237;fica tem de diferente que poderia explicar essa anomalia que notamos?".</p><p>O verdadeiro valor de um profissional experiente n&#227;o est&#225; no repert&#243;rio de respostas prontas, mas na sabedoria de saber quando n&#227;o us&#225;-las. Est&#225; na disciplina de abordar cada desafio com uma "mente de principiante", mas armado com a capacidade de um mestre para observar, escutar e, s&#243; ent&#227;o,<strong> fazer a pergunta que ningu&#233;m mais pensou em fazer</strong>. &#201; isso que impede que a experi&#234;ncia se torne uma gaiola dourada.</p><h2><strong>A Humildade Intelectual de Perguntar o "&#211;bvio"</strong></h2><p>Imagine a cena: voc&#234; est&#225; em uma reuni&#227;o (f&#237;sica ou virtual) cheia de engenheiros inteligentes. A conversa est&#225; voando alto, repleta de acr&#244;nimos, termos t&#233;cnicos e refer&#234;ncias a sistemas que voc&#234; mal conhece. A arquitetura de uma nova solu&#231;&#227;o est&#225; sendo desenhada no quadro branco, as setas e caixas se multiplicando.</p><p>No meio de tudo isso, uma d&#250;vida fundamental come&#231;a a se formar na sua cabe&#231;a. N&#227;o &#233; uma d&#250;vida complexa sobre a implementa&#231;&#227;o, mas algo muito mais simples, quase infantil. Algo como: <em>"Espera um pouco... por que mesmo estamos fazendo tudo isso? Qual &#233; o problema original que deu in&#237;cio a essa discuss&#227;o toda?"</em></p><p>E ent&#227;o, quase que instantaneamente, uma outra voz sussurra no seu ouvido. &#201; a voz do nosso velho amigo, o ego. E ele diz: <em>"Fique quieto. N&#227;o pergunte isso. Todo mundo aqui j&#225; entendeu. Se voc&#234; perguntar isso agora, vai parecer o &#250;nico que est&#225; perdido. V&#227;o achar que voc&#234; n&#227;o &#233; t&#227;o s&#234;nior quanto pensam."</em></p><p>Essa batalha interna &#233; uma das mais importantes e silenciosas na carreira de um desenvolvedor. Vivemos em uma cultura de engenharia que, muitas vezes, glorifica o conhecimento. Ser "s&#234;nior" ou "especialista" est&#225; diretamente ligado &#224; nossa identidade profissional. Nosso ego, em sua tentativa de nos proteger, cria uma armadura de compet&#234;ncia. E fazer uma pergunta "b&#225;sica" parece ser o mesmo que apontar uma rachadura nessa armadura.</p><p><strong>&#201; aqui que o ego se torna perigoso. Ele muda o objetivo do jogo. O objetivo deixa de ser "encontrar a melhor solu&#231;&#227;o para o problema" e passa a ser "n&#227;o parecer burro na frente dos meus colegas".</strong></p><p><strong>Quando o ego est&#225; no comando, ele impacta diretamente a forma e a liberdade com que fazemos perguntas.</strong> Em vez de buscar clareza, <strong>o ego busca valida&#231;&#227;o</strong>. As perguntas que ele permite s&#227;o aquelas que demonstram o que j&#225; sabemos: perguntas complexas, cheias de jarg&#227;o, que sutilmente mostram nosso dom&#237;nio sobre um assunto. Ele nos impede de levantar a m&#227;o e simplesmente dizer: "Desculpe, eu me perdi. Podemos recapitular o objetivo principal?".</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!008U!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!008U!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!008U!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!008U!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!008U!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!008U!,w_2400,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png" width="498" height="355.7142857142857" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/ced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:false,&quot;imageSize&quot;:&quot;large&quot;,&quot;height&quot;:300,&quot;width&quot;:420,&quot;resizeWidth&quot;:498,&quot;bytes&quot;:9384,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/171795862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:&quot;center&quot;,&quot;offset&quot;:false}" class="sizing-large" alt="" srcset="https://substackcdn.com/image/fetch/$s_!008U!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!008U!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!008U!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!008U!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2Fced34025-ff44-40c8-a1e8-c03a1f513dae_420x300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p>O mais ir&#244;nico e tr&#225;gico dessa situa&#231;&#227;o &#233; que, naquela sala onde voc&#234; est&#225; com medo de perguntar, &#233; quase garantido que metade das pessoas tem exatamente a mesma d&#250;vida "boba" que voc&#234;. &#201; um fen&#244;meno conhecido como <strong>"ignor&#226;ncia pluralista": um grupo de pessoas se conforma com uma norma (neste caso, o sil&#234;ncio) porque cada indiv&#237;duo assume, erroneamente, que &#233; o &#250;nico que pensa diferente.</strong></p><p>Todos ficam em sil&#234;ncio, cada um em sua ilha de incerteza, com medo de ser o &#250;nico a n&#227;o entender. <strong>E &#233; nesse sil&#234;ncio coletivo que nascem as piores decis&#245;es t&#233;cnicas</strong>. &#201; assim que projetos de milh&#245;es de d&#243;lares s&#227;o iniciados com base em premissas falhas que ningu&#233;m teve a coragem de questionar.</p><p>A ferramenta para quebrar esse ciclo perigoso &#233; a <strong>humildade intelectual</strong>.</p><p><em><strong>Humildade intelectual n&#227;o &#233; se achar inferior. &#201; simplesmente a capacidade de desapegar do seu ego e priorizar a clareza e o resultado coletivo acima da sua imagem pessoal</strong></em>. &#201; a sabedoria de entender que n&#227;o h&#225; pergunta "boba" quando a estabilidade de um sistema, o dinheiro da empresa e meses de trabalho da sua equipe est&#227;o em jogo.</p><p>O programador verdadeiramente s&#234;nior, aquele que transcendeu a necessidade de parecer inteligente, <strong>&#233; quem geralmente tem a coragem de parar o trem</strong>. &#201; ele quem diz, com calma e sem vergonha:</p><blockquote><p><strong>"Pessoal, estou notando alguns pontos vagos&#8230; podemos dar um passo atr&#225;s por um segundo? S&#243; para garantir que estamos todos na mesma p&#225;gina. Qual &#233;, exatamente, o problema de neg&#243;cio que estamos tentando resolver aqui?"</strong></p></blockquote><p>Observe o poder dessa pergunta. Ela n&#227;o soa como fraqueza. Ela soa como algu&#233;m procurando clareza! Ela d&#225; a todas as outras pessoas na sala a permiss&#227;o para tamb&#233;m serem vulner&#225;veis e admitirem suas pr&#243;prias d&#250;vidas. Em um instante, a press&#227;o para "saber de tudo" se dissipa, e o foco do grupo volta para o que realmente importa. Quase sempre, o que se ouve depois de uma pergunta dessas &#233; um suspiro coletivo de al&#237;vio.</p><p>Na sua pr&#243;xima reuni&#227;o, quando aquela d&#250;vida fundamental surgir, reconhe&#231;a a voz do seu ego tentando te silenciar. Agrade&#231;a a ele por tentar te proteger. E ent&#227;o, respire fundo, levante a m&#227;o e fa&#231;a a pergunta "&#243;bvia" mesmo assim. Voc&#234; n&#227;o ser&#225; lembrado como aquele que "n&#227;o sabia", mas como aquele que teve a coragem de garantir que todos estivessem construindo a coisa certa, juntos. E essa &#233; uma marca de lideran&#231;a que ego nenhum pode comprar.</p><h2><strong>O Impacto Direto das Perguntas no Seu C&#243;digo</strong></h2><p>At&#233; agora, n&#243;s conversamos sobre a arte de perguntar como uma <em>soft skill</em>, uma ferramenta para o crescimento profissional, para a din&#226;mica de equipe e para a estrat&#233;gia de neg&#243;cio. <strong>Mas e o c&#243;digo? Como essa habilidade (ou a falta dela) realmente encontra o teclado?</strong> Como o sil&#234;ncio de um j&#250;nior medroso ou as suposi&#231;&#245;es de um s&#234;nior apressado se materializam em <code>if/else</code>, em estruturas de dados, em chamadas de API?</p><p>A verdade &#233; esta: <strong>cada linha de c&#243;digo que voc&#234; escreve &#233; a resposta final a uma s&#233;rie de perguntas.</strong> <em><strong>A diferen&#231;a &#233; se essas perguntas foram feitas em voz alta, de forma colaborativa e intencional, ou se foram apenas respondidas silenciosamente, pela sua pr&#243;pria suposi&#231;&#227;o.</strong></em></p><p>A qualidade do seu c&#243;digo &#233; um eco direto da qualidade dessas perguntas. Vamos ver como isso impacta seu dia a dia, em cada fase do desenvolvimento.</p><h4><strong>1. Antes de Escrever a Primeira Linha (A Fase da Arquitetura)</strong></h4><p>&#201; aqui que as perguntas t&#234;m o maior poder de alavancagem. O sil&#234;ncio nesta fase n&#227;o cria bugs, ele cria algo muito pior: <strong>d&#237;vida t&#233;cnica arquitet&#244;nica</strong>, a mais cara e dolorosa de todas.</p><p>Imagine que chega uma tarefa simples: "Adicionar campo 'endere&#231;o' ao perfil do usu&#225;rio".</p><ul><li><p><strong>O desenvolvedor silencioso</strong> parte da suposi&#231;&#227;o. Ele abre o modelo <code>User</code>, adiciona um campo <code>address</code> como uma string (<code>VARCHAR(255)</code>) no banco de dados, cria um campo de texto livre no formul&#225;rio e, em poucas horas, a tarefa est&#225; "conclu&#237;da". Simples. R&#225;pido. E terrivelmente m&#237;ope.</p></li><li><p><strong>O desenvolvedor questionador</strong> faz uma pausa. Ele gasta vinte minutos fazendo perguntas que v&#227;o definir a funda&#231;&#227;o da funcionalidade:</p><ul><li><p><em><strong>"Este endere&#231;o ser&#225; usado para qu&#234;? Apenas para exibi&#231;&#227;o, para cobran&#231;a de faturas ou para entrega de produtos?"</strong></em></p><ul><li><p><strong>Impacto no c&#243;digo:</strong> A resposta aqui define tudo. Se for para entrega, talvez um usu&#225;rio precise de m&#250;ltiplos endere&#231;os (casa, trabalho). Isso transforma um simples campo de texto em uma nova tabela <code>addresses</code> no banco, com uma rela&#231;&#227;o <code>one-to-many</code> com o usu&#225;rio. O c&#243;digo que voc&#234; escrever&#225; ser&#225; completamente diferente.</p></li></ul></li><li><p><em><strong>"Precisamos de dados estruturados (CEP, rua, n&#250;mero, cidade) ou um campo &#250;nico &#233; suficiente?"</strong></em></p><ul><li><p><strong>Impacto no c&#243;digo:</strong> Essa pergunta define a complexidade do seu back-end (valida&#231;&#245;es, modelo de dados) e do seu front-end (m&#250;ltiplos campos de formul&#225;rio). A falta dessa pergunta leva a um campo de texto livre que, em seis meses, precisar&#225; ser migrado com scripts complexos e dolorosos quando o time de marketing decidir que precisa segmentar usu&#225;rios por cidade.</p></li></ul></li><li><p><em><strong>"Vamos nos integrar com algum servi&#231;o externo, como c&#225;lculo de frete ou valida&#231;&#227;o de CEP?"</strong></em></p><ul><li><p><strong>Impacto no c&#243;digo:</strong> A resposta "sim" aqui introduz a necessidade de pensar em chamadas de API, tratamento de falhas de servi&#231;os de terceiros e talvez at&#233; em um padr&#227;o de design como o "Adapter" para isolar essa integra&#231;&#227;o.</p></li></ul></li></ul></li></ul><p>O sil&#234;ncio nesta fase te leva a escrever um c&#243;digo que funciona hoje, mas que &#233; uma bomba-rel&#243;gio para o futuro. As perguntas certas te levam a escrever um c&#243;digo que talvez seja um pouco mais complexo hoje, mas que &#233; robusto, escal&#225;vel e preparado para a realidade do neg&#243;cio.</p><h4><strong>2. Durante o Desenvolvimento (A Fase da L&#243;gica e dos Edge Cases)</strong></h4><p>&#201; aqui que o sil&#234;ncio escreve os bugs mais comuns e as piores experi&#234;ncias para o usu&#225;rio. Voc&#234; est&#225; no meio do c&#243;digo, encontra uma bifurca&#231;&#227;o, um cen&#225;rio n&#227;o previsto na tarefa.</p><ul><li><p><strong>O desenvolvedor silencioso</strong> toma uma decis&#227;o unilateral. "O que acontece se o upload do arquivo do usu&#225;rio falhar no meio do caminho?". Ele pensa: "Ah, vou s&#243; retornar um erro 500. Ningu&#233;m vai nem notar". Ou: "O que acontece se a API externa que eu consumo demorar 10 segundos para responder?". Ele sup&#245;e: "O usu&#225;rio que espere."</p></li><li><p><strong>O desenvolvedor questionador</strong> entende que ele n&#227;o tem todas as respostas. Ele para e pergunta ao time (PM, Designer):</p><ul><li><p><em><strong>"Quando a API externa demorar a responder, qual &#233; a experi&#234;ncia ideal? Mostramos uma anima&#231;&#227;o de 'loading'? Definimos um 'timeout' de 5 segundos e mostramos uma mensagem de erro amig&#225;vel?"</strong></em></p><ul><li><p><strong>Impacto no c&#243;digo:</strong> Isso se traduz em c&#243;digo para <code>try/catch</code>, <code>Promises</code>, <code>async/await</code>, estados de <code>isLoading</code> no front-end e <code>setTimeout</code>. A falta da pergunta resulta em uma interface travada, que frustra o usu&#225;rio.</p></li></ul></li><li><p><em><strong>"Se o upload do arquivo falhar, qual mensagem exatamente mostramos? 'Erro ao fazer upload' ou algo mais &#250;til como 'Arquivo muito grande. O limite &#233; de 5MB'?"</strong></em></p><ul><li><p><strong>Impacto no c&#243;digo:</strong> Isso se traduz em tratamento de erro granular. Em vez de um bloco <code>catch</code> gen&#233;rico, voc&#234; escreve l&#243;gicas para diferentes tipos de erro, fornecendo feedback &#250;til que transforma um momento de falha em um momento de ajuda para o usu&#225;rio.</p></li></ul></li></ul></li></ul><p>O sil&#234;ncio nesta fase cria um software que s&#243; funciona no "caminho feliz". As perguntas constroem um software robusto, resiliente e que respeita o tempo e a intelig&#234;ncia do usu&#225;rio, mesmo quando as coisas d&#227;o errado.</p><h4><strong>3. Na Fase de Testes (A Fase da Valida&#231;&#227;o e Seguran&#231;a)</strong></h4><p>&#201; aqui que o sil&#234;ncio escreve as vulnerabilidades. Testar n&#227;o &#233; apenas confirmar que o c&#243;digo faz o que deveria, mas perguntar ativamente: "Como eu posso quebrar isso?".</p><ul><li><p><strong>O desenvolvedor silencioso</strong> testa o fluxo &#243;bvio. Ele preenche o formul&#225;rio com dados v&#225;lidos e clica em "salvar". Funciona. "Teste passou".</p></li><li><p><strong>O desenvolvedor questionador</strong> veste o chap&#233;u de um usu&#225;rio mal-intencionado ou simplesmente desastrado. Ele se torna a pr&#243;pria m&#225;quina de fazer perguntas, que se transformam em casos de teste:</p><ul><li><p><em><strong>"O que acontece se eu injetar um script </strong></em><code>&lt;script&gt;alert('XSS')&lt;/script&gt;</code><em><strong> no campo de endere&#231;o?"</strong></em> -&gt; <strong>Impacto no c&#243;digo:</strong> Isso te for&#231;a a implementar sanitiza&#231;&#227;o de inputs no back-end para prevenir ataques de Cross-Site Scripting.</p></li><li><p><em><strong>"O que acontece se eu enviar o mesmo formul&#225;rio 100 vezes em um segundo?"</strong></em> -&gt; <strong>Impacto no c&#243;digo:</strong> Isso te leva a pensar em <em>rate limiting</em> e prote&#231;&#227;o contra ataques de nega&#231;&#227;o de servi&#231;o.</p></li><li><p><em><strong>"Eu, como usu&#225;rio A, consigo, de alguma forma, atualizar o endere&#231;o do usu&#225;rio B apenas trocando o ID na chamada da API?"</strong></em> -&gt; <strong>Impacto no c&#243;digo:</strong> Isso te obriga a escrever l&#243;gicas de autoriza&#231;&#227;o robustas, garantindo que um usu&#225;rio s&#243; possa ver e editar seus pr&#243;prios dados.</p></li></ul></li></ul><p>No fim das contas, o c&#243;digo n&#227;o &#233; um artefato isolado. Ele &#233; a materializa&#231;&#227;o f&#237;sica das conversas, decis&#245;es e, principalmente, das perguntas que foram (ou n&#227;o) feitas ao longo de seu ciclo de vida. Fazer perguntas n&#227;o &#233; uma formalidade burocr&#225;tica. &#201; a pr&#225;tica de engenharia mais concreta e eficiente que existe. &#201; o que transforma c&#243;digo fr&#225;gil em software de verdade.</p><h2><strong>O Term&#244;metro da Boa Pergunta: Como Saber se Voc&#234; Est&#225; no Caminho Certo</strong></h2><p>Depois de tudo isso, a d&#250;vida mais importante talvez seja esta: </p><blockquote><p>"Ok, eu entendi a import&#226;ncia. Mas no calor da batalha do dia a dia, no meio de uma reuni&#227;o apressada, como eu sei se a pergunta que estou prestes a fazer &#233; boa? Existe algum sinal? Algum teste r&#225;pido para validar minha pr&#243;pria curiosidade?"</p></blockquote><p>N&#227;o existe uma f&#243;rmula m&#225;gica, um <code>if/else</code> que separe uma pergunta ruim de uma boa. Mas, com base em tudo que conversamos, existem sinais claros, sintomas positivos que aparecem quando sua habilidade de questionar est&#225; amadurecendo.</p><p>Pense neles como um "term&#244;metro" para medir a qualidade e o impacto da sua curiosidade. Se voc&#234; notar que suas perguntas est&#227;o causando os efeitos abaixo, parab&#233;ns, voc&#234; est&#225; no caminho certo.</p><p><strong>Sinal 1: Suas perguntas geram mais contexto, n&#227;o apenas um "sim" ou "n&#227;o".</strong> Uma pergunta fraca busca apenas uma confirma&#231;&#227;o. "O bot&#227;o deve ser azul?". A resposta &#233; "sim" ou "n&#227;o" e a conversa morre. Uma pergunta forte busca a inten&#231;&#227;o por tr&#225;s da decis&#227;o. "<em><strong>Qual &#233; o crit&#233;rio para a escolha da cor do bot&#227;o? Estamos seguindo um guia de design ou h&#225; uma raz&#227;o de acessibilidade?</strong></em>". A primeira pergunta te d&#225; um pixel. A segunda te d&#225; o <em>prop&#243;sito</em>, o que te permite tomar dezenas de outras decis&#245;es de forma aut&#244;noma no futuro. Se suas perguntas est&#227;o abrindo a conversa em vez de fech&#225;-la, seu term&#244;metro est&#225; subindo.</p><p><strong>Sinal 2: Elas movem a conversa do "Como" para o "Porqu&#234;".</strong> Esse &#233; um dos sinais mais claros de que voc&#234; est&#225; escavando. Se a maioria das suas perguntas &#233; sobre detalhes de implementa&#231;&#227;o ("Devo usar um <code>POST</code> ou <code>PUT</code> neste endpoint?"), voc&#234; est&#225; operando no n&#237;vel da execu&#231;&#227;o. &#201; importante, mas n&#227;o &#233; onde o maior valor &#233; gerado. Quando suas perguntas come&#231;am a evoluir para "<em><strong>Por que o usu&#225;rio precisa ver est&#225; p&#225;gina em primeiro lugar? Qual &#233; o fluxo de trabalho que estamos tentando facilitar aqui?</strong></em>", voc&#234; transcendeu a tarefa. Voc&#234; est&#225; operando no n&#237;vel da estrat&#233;gia.</p><p><strong>Sinal 3: Elas te for&#231;am a pensar (e a falar) sobre cen&#225;rios de falha.</strong> Se voc&#234; se pega sendo a pessoa que pergunta "<em><strong>Ok, e o que acontece se essa chamada de API falhar?</strong></em>", "<em><strong>Qual &#233; o comportamento esperado se o usu&#225;rio inserir um texto com mais de 500 caracteres aqui?</strong></em>", "<em><strong>Como o sistema reage se este servi&#231;o de terceiro estiver offline?</strong></em>", comemore. Voc&#234; deixou de ser um programador otimista para se tornar um engenheiro resiliente. Voc&#234; est&#225; construindo software para o mundo real, um lugar ca&#243;tico e imprevis&#237;vel. Voc&#234; est&#225; ativamente ca&#231;ando bugs antes mesmo de eles terem a chance de nascer.</p><p><strong>Sinal 4: Elas, &#224;s vezes, pausam a reuni&#227;o por um instante.</strong></p><p>Isso pode parecer contraintuitivo, afinal, ningu&#233;m quer ser a pessoa que "atrasa" uma reuni&#227;o. Mas quando sua pergunta causa uma pausa, um sil&#234;ncio moment&#226;neo onde as pessoas se olham e o fluxo r&#225;pido da conversa &#233; interrompido, isso geralmente &#233; um sinal de ouro.</p><p>Imagine o cen&#225;rio: a equipe est&#225; discutindo uma nova funcionalidade para permitir que os usu&#225;rios editem um tipo de documento importante no sistema. Todos est&#227;o alinhados, a interface parece clara, a tarefa parece simples. O trem est&#225; andando a todo vapor.</p><p>&#201; nesse momento que voc&#234; levanta a m&#227;o e diz, com calma:</p><blockquote><p>"Pessoal, s&#243; para eu entender a fundo. A gente est&#225; falando sobre permitir que o usu&#225;rio edite e salve novas vers&#245;es deste documento, certo? Mas, ao revisar a estrutura do nosso banco de dados, eu n&#227;o encontrei nenhuma tabela de hist&#243;rico ou uma forma de versionamento para o documento. <strong>Como vamos conseguir auditar quem alterou o qu&#234; e quando? Ou, mais importante, como vamos conseguir reverter para uma vers&#227;o anterior se um erro grave for cometido?"</strong></p></blockquote><p><strong>Sil&#234;ncio.</strong></p><p>As pessoas piscam e se olham. O Product Manager para de digitar. O Tech Lead fica com um olhar de d&#250;vida...</p><p>O que acabou de acontecer aqui? Voc&#234; n&#227;o fez uma pergunta para ser "do contra". Voc&#234; conectou uma necessidade de neg&#243;cio fundamental (auditoria, seguran&#231;a, recupera&#231;&#227;o de desastres) com uma realidade t&#233;cnica que estava sendo completamente ignorada (a aus&#234;ncia de uma estrutura de dados para suportar isso).</p><p>A "simples" tarefa de "permitir a edi&#231;&#227;o" acabou de revelar uma necessidade muito maior e mais complexa de um sistema de versionamento. Sem a sua pergunta, a equipe provavelmente construiria a funcionalidade. Meses depois, um cliente alteraria um contrato por engano, pediria para "voltar &#224; vers&#227;o de ontem" e o p&#226;nico se instalaria ao perceberem que isso era imposs&#237;vel. O custo para consertar isso em produ&#231;&#227;o, com dados reais j&#225; sendo modificados, seria dez vezes maior.</p><p>Essa pausa que a sua pergunta causou n&#227;o foi um atraso; foi a economia de talvez um m&#234;s inteiro de retrabalho e dor de cabe&#231;a no futuro. Voc&#234; n&#227;o interrompeu o fluxo; voc&#234; o salvou de correr em dire&#231;&#227;o a um penhasco. Quando sua pergunta faz todos pararem para pensar em uma consequ&#234;ncia que ningu&#233;m havia considerado, voc&#234; n&#227;o est&#225; atrasando a reuni&#227;o. Voc&#234; est&#225; sendo o engenheiro mais valioso da sala.</p><div class="captioned-image-container"><figure><a class="image-link image2 is-viewable-img" target="_blank" href="https://substackcdn.com/image/fetch/$s_!X-n3!,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png" data-component-name="Image2ToDOM"><div class="image2-inset"><picture><source type="image/webp" srcset="https://substackcdn.com/image/fetch/$s_!X-n3!,w_424,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!X-n3!,w_848,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!X-n3!,w_1272,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!X-n3!,w_1456,c_limit,f_webp,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png 1456w" sizes="100vw"><img src="https://substackcdn.com/image/fetch/$s_!X-n3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png" width="420" height="300" data-attrs="{&quot;src&quot;:&quot;https://substack-post-media.s3.amazonaws.com/public/images/333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png&quot;,&quot;srcNoWatermark&quot;:null,&quot;fullscreen&quot;:null,&quot;imageSize&quot;:null,&quot;height&quot;:300,&quot;width&quot;:420,&quot;resizeWidth&quot;:null,&quot;bytes&quot;:24536,&quot;alt&quot;:null,&quot;title&quot;:null,&quot;type&quot;:&quot;image/png&quot;,&quot;href&quot;:null,&quot;belowTheFold&quot;:true,&quot;topImage&quot;:false,&quot;internalRedirect&quot;:&quot;https://chroniclesofapragmaticprogrammer.substack.com/i/171795862?img=https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png&quot;,&quot;isProcessing&quot;:false,&quot;align&quot;:null,&quot;offset&quot;:false}" class="sizing-normal" alt="" srcset="https://substackcdn.com/image/fetch/$s_!X-n3!,w_424,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png 424w, https://substackcdn.com/image/fetch/$s_!X-n3!,w_848,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png 848w, https://substackcdn.com/image/fetch/$s_!X-n3!,w_1272,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png 1272w, https://substackcdn.com/image/fetch/$s_!X-n3!,w_1456,c_limit,f_auto,q_auto:good,fl_progressive:steep/https%3A%2F%2Fsubstack-post-media.s3.amazonaws.com%2Fpublic%2Fimages%2F333b3c4e-b55c-4588-b431-106acb3ea832_420x300.png 1456w" sizes="100vw" loading="lazy"></picture><div class="image-link-expand"><div class="pencraft pc-display-flex pc-gap-8 pc-reset"><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container restack-image buttonBase-GK1x3M"><svg aria-hidden="true" width="20" height="20" viewBox="0 0 20 20" fill="none" stroke-width="1.5" stroke="var(--color-fg-primary)" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg" class="icon-noB79L"><g><path d="M2.53001 7.81595C3.49179 4.73911 6.43281 2.5 9.91173 2.5C13.1684 2.5 15.9537 4.46214 17.0852 7.23684L17.6179 8.67647M17.6179 8.67647L18.5002 4.26471M17.6179 8.67647L13.6473 6.91176M17.4995 12.1841C16.5378 15.2609 13.5967 17.5 10.1178 17.5C6.86118 17.5 4.07589 15.5379 2.94432 12.7632L2.41165 11.3235M2.41165 11.3235L1.5293 15.7353M2.41165 11.3235L6.38224 13.0882"></path></g></svg></button><button tabindex="0" type="button" class="pencraft pc-reset pencraft icon-container view-image buttonBase-GK1x3M"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-maximize2 lucide-maximize-2 icon-noB79L"><polyline points="15 3 21 3 21 9"></polyline><polyline points="9 21 3 21 3 15"></polyline><line x1="21" x2="14" y1="3" y2="10"></line><line x1="3" x2="10" y1="21" y2="14"></line></svg></button></div></div></div></a></figure></div><p><strong>Sinal 5: Elas frequentemente resultam em c&#243;digo mais simples (ou em nenhum c&#243;digo).</strong> Aqui est&#225; um sinal contraintuitivo, mas poderoso. As melhores perguntas muitas vezes levam &#224; <em>remo&#231;&#227;o</em> de complexidade. Perguntas como "<em><strong>N&#243;s realmente precisamos construir esse sistema de cache customizado agora, ou podemos come&#231;ar sem ele e medir a performance real em produ&#231;&#227;o?</strong></em>" ou "<em><strong>Existe alguma ferramenta que j&#225; faz isso e que poder&#237;amos usar, em vez de construir do zero?</strong></em>" s&#227;o incrivelmente valiosas. Se suas perguntas est&#227;o ativamente combatendo o <em>over-engineering</em> e levando a solu&#231;&#245;es mais simples, voc&#234; est&#225; gerando um valor imenso para a sua equipe.</p><p><strong>Sinal 6: Voc&#234; come&#231;a a entender melhor o trabalho dos seus colegas (n&#227;o-t&#233;cnicos).</strong> Quando voc&#234; pratica o questionamento profundo, um efeito colateral maravilhoso acontece: <strong>voc&#234; come&#231;a a entender de verdade as dores e os processos do time de Marketing, Vendas, Financeiro ou Suporte</strong>. Se, ao final de uma discuss&#227;o sobre uma <em>feature</em>, voc&#234; consegue explicar para outra pessoa <em><strong>por que</strong></em> o time de vendas precisa daquilo, sua pergunta foi excelente. Voc&#234; est&#225; construindo pontes entre os silos da empresa, n&#227;o apenas software.</p><p>Saber se voc&#234; est&#225; fazendo as perguntas certas n&#227;o &#233; sobre ter uma epifania a cada questionamento. &#201; sobre observar os resultados. &#201; uma pr&#225;tica cont&#237;nua de usar a curiosidade como uma ferramenta para gerar clareza, simplicidade e valor. E a boa not&#237;cia &#233; que, quanto mais voc&#234; pratica, mais afiado e confi&#225;vel seu "term&#244;metro" se torna.</p><h2><strong>Testes de Unidade, a M&#225;quina de Fazer Perguntas</strong></h2><p>Para muitos programadores, a palavra "teste" evoca uma sensa&#231;&#227;o de... <em><strong>dever</strong></em>. Uma tarefa a ser feita no final, quase como arrumar a bagun&#231;a depois da festa. &#201; uma formalidade para aumentar aquele n&#250;mero de "cobertura de c&#243;digo" e deixar o pipeline de CI/CD feliz. Se essa &#233; a sua vis&#227;o, eu te convido a suspend&#234;-la por alguns minutos.</p><p><strong>E se eu te dissesse que essa &#233; a forma mais superficial e menos interessante de enxergar os testes?</strong> E se, na verdade, os testes de unidade fossem a ferramenta de <em>design</em> mais poderosa que voc&#234; tem &#224; sua disposi&#231;&#227;o? E se eles n&#227;o fossem sobre <em>verificar</em> o c&#243;digo, mas sobre <em>descobri-lo</em> e <em>defini-lo</em>?</p><p>&#201; exatamente isso. <strong>Escrever um bom teste de unidade &#233; um exerc&#237;cio estruturado de fazer perguntas incrivelmente precisas sobre o seu c&#243;digo, antes mesmo que ele exista</strong>. O teste n&#227;o &#233; o fiscal do seu c&#243;digo; ele &#233; o seu arquiteto, <strong>o seu parceiro de di&#225;logo mais exigente</strong>.</p><p>Ao sentar para escrever um teste, voc&#234; &#233; for&#231;ado a parar de pensar de forma abstrata e a come&#231;ar a fazer perguntas brutalmente concretas.</p><h4><strong>Primeira pergunta, a mais fundamental: "O que, exatamente, voc&#234; deveria fazer?"</strong></h4><p>Antes mesmo de escrever <code>function calcularDesconto(...){</code>, voc&#234; escreve o esqueleto do seu teste: <code>it('deve calcular o desconto corretamente para clientes VIP em produtos eleg&#237;veis')</code>. O simples ato de dar um nome claro e descritivo ao seu teste te obriga a definir a <strong>responsabilidade &#250;nica</strong> da sua fun&#231;&#227;o. Se o nome do teste come&#231;a a ficar complicado e cheio de "E" (<code>it('deve calcular o desconto E aplicar a taxa de frete E enviar um email')</code>), voc&#234; acaba de receber o feedback mais claro poss&#237;vel: sua fun&#231;&#227;o est&#225; tentando abra&#231;ar o mundo. O teste te for&#231;a, desde o in&#237;cio, a criar componentes pequenos, focados e coesos, n&#227;o por dogma acad&#234;mico, mas por pura praticidade.</p><h4><strong>Segunda pergunta: "Com o que vamos trabalhar e o que voc&#234; vai me devolver?"</strong></h4><p>Um teste precisa de inputs concretos e espera um output exato. <code>expect(calcularDesconto({ cliente: 'VIP', preco: 200.00 })).toBe(20.00);</code>. Este pequeno trecho de c&#243;digo &#233; uma das coisas mais poderosas que voc&#234; pode fazer. Voc&#234; est&#225; definindo o <strong>contrato</strong> da sua fun&#231;&#227;o de forma inequ&#237;voca. N&#227;o h&#225; espa&#231;o para ambiguidade. Voc&#234; est&#225; especificando, em c&#243;digo, o comportamento esperado. &#201; o fim do "ah, eu n&#227;o sabia que essa fun&#231;&#227;o podia receber um objeto sem a propriedade 'cliente'". O teste define as regras do jogo.</p><h4><strong>Terceira pergunta: "E se tudo der certo? Como &#233; o seu melhor dia?"</strong></h4><p>Estes s&#227;o os seus testes de "caminho feliz". Voc&#234; escreve cen&#225;rios para os casos de uso mais comuns e importantes. <code>it('deve retornar 0 para clientes n&#227;o-VIP')</code>, <code>it('deve calcular o desconto corretamente para clientes normais acima de R$500')</code>. Esses testes constroem a funda&#231;&#227;o da sua l&#243;gica, garantindo que o comportamento principal est&#225; s&#243;lido como uma rocha.</p><p><strong>E quando o universo conspirar contra voc&#234;?</strong></p><p>O teste se torna seu "advogado", for&#231;ando voc&#234; a pensar nos "caminhos tristes", nos cen&#225;rios ca&#243;ticos do mundo real que tantas vezes ignoramos. Seu conjunto de testes come&#231;a a fazer um interrogat&#243;rio implac&#225;vel:</p><ul><li><p>"O que acontece se eu te passar <code>null</code> como cliente? Voc&#234; explode com um <code>TypeError</code> ou retorna <code>0</code> de forma elegante?" -&gt; <code>it('deve retornar 0 quando o cliente for nulo')</code>.</p></li><li><p>"E se o pre&#231;o do produto for um n&#250;mero negativo? Voc&#234; o aceita e calcula um desconto negativo bizarro, ou lan&#231;a uma exce&#231;&#227;o de argumento inv&#225;lido?" -&gt; <code>it('deve lan&#231;ar um erro para pre&#231;os negativos')</code>.</p></li><li><p>"E se o objeto do cliente vier sem a propriedade 'tipo'? Voc&#234; assume um padr&#227;o ou quebra?" -&gt; <code>it('deve tratar um cliente sem tipo como n&#227;o-VIP')</code>.</p></li><li><p>"E se eu te passar um pre&#231;o gigantesco que cause um estouro de n&#250;mero? E se o tipo do cliente for uma string vazia?"</p></li></ul><p>Cada um desses testes &#233; uma <strong>pergunta de design</strong>. A resposta que voc&#234; dar&#225; a ela ser&#225; uma decis&#227;o consciente, que ser&#225; implementada no seu c&#243;digo. Voc&#234; n&#227;o est&#225; mais deixando esses casos extremos ao acaso; voc&#234; est&#225; ativamente projetando o comportamento da sua fun&#231;&#227;o para lidar com o caos.</p><p>&#201; isso que transforma o desenvolvimento de software. Voc&#234; deixa de operar no modo "eu acho que funciona" e passa para o modo "eu posso provar que funciona nestes cen&#225;rios". &#201; uma mudan&#231;a de um desenvolvimento baseado na esperan&#231;a para uma engenharia baseada em evid&#234;ncias.</p><h2><strong>A Pergunta Final!</strong></h2><p>Vimos que <strong>as perguntas s&#227;o as melhores ferramentas de escava&#231;&#227;o que um programador possui</strong>. Vimos como o medo, muitas vezes, silencia o enorme potencial de quem est&#225; come&#231;ando; como a experi&#234;ncia, ironicamente, pode criar viseiras que nos impedem de ver novas solu&#231;&#245;es; e como a humildade para questionar o "&#243;bvio" &#233;, na verdade, uma das maiores demonstra&#231;&#245;es de for&#231;a e lideran&#231;a que existem.</p><p>Agora, vamos juntar todas essas pe&#231;as. Afinal, depois de tanta conversa, como transformamos essa reflex&#227;o em um plano de a&#231;&#227;o? Como garantimos que, na pr&#243;xima segunda-feira, nossa postura ser&#225; diferente?</p><p>Muitos buscam uma "f&#243;rmula m&#225;gica" para a pergunta perfeita, um template que possam copiar e colar. A verdade &#233; que as melhores perguntas n&#227;o nascem de uma f&#243;rmula, mas de uma postura. Uma postura que se sustenta em tr&#234;s a&#231;&#245;es fundamentais que devem vir <em>antes</em> de qualquer questionamento: <strong>Observar, Escutar e Entender.</strong></p><p>&#201; por isso que estes tr&#234;s pilares s&#227;o t&#227;o cruciais. Eles s&#227;o a mat&#233;ria-prima da boa pergunta. Sem eles, suas perguntas ser&#227;o baseadas em "achismos". Com eles, suas perguntas ser&#227;o baseadas em evid&#234;ncias.</p><ul><li><p><strong>Observar</strong> &#233; o ato de desligar o piloto autom&#225;tico. &#201; ler a tarefa com olhos de detetive, n&#227;o de executor. &#201; notar a pequena inconsist&#234;ncia na documenta&#231;&#227;o que ningu&#233;m viu. A observa&#231;&#227;o te d&#225; os <strong>fatos</strong>.</p></li><li><p><strong>Escutar</strong> &#233; o superpoder de absorver informa&#231;&#227;o sem o filtro do seu pr&#243;prio ego. &#201; ouvir a frustra&#231;&#227;o na voz do cliente, a hesita&#231;&#227;o na explica&#231;&#227;o do Product Manager. A escuta te d&#225; as <strong>emo&#231;&#245;es</strong> e as <strong>dores</strong> por tr&#225;s dos fatos.</p></li><li><p><strong>Entender</strong> &#233; a pausa estrat&#233;gica para conectar os pontos. "Ok, <em>observei</em> que a performance cai aqui (o fato), e <em>escutei</em> que o cliente s&#243; reclama disso no final do m&#234;s (a dor). <em>Entendi!</em> O problema est&#225; no volume de dados do fechamento mensal."</p></li></ul><p>S&#243; depois dessa s&#237;ntese &#233; que a pergunta matadora &#8212; a pergunta certa &#8212; pode ser formulada, porque agora ela nasce de um contexto rico. Com essa base, vamos direcionar a conversa final para os diferentes momentos da carreira.</p><h4><strong>Para Seniores, Tech Leads e Especialistas: A Autoan&#225;lise Como Ferramenta de Crescimento</strong></h4><p>Se voc&#234; est&#225; em uma posi&#231;&#227;o de lideran&#231;a ou &#233; uma refer&#234;ncia t&#233;cnica, sua responsabilidade transcende o c&#243;digo; voc&#234; &#233; um guardi&#227;o do contexto e um catalisador da clareza para toda a equipe. Sua autoan&#225;lise deve ser um h&#225;bito constante. Pergunte-se com honestidade:</p><ul><li><p><em><strong>"Eu estou realmente me preparando para o jogo?"</strong></em> Antes de uma reuni&#227;o de refinamento, eu dedico tempo para ler a documenta&#231;&#227;o e pensar nas poss&#237;veis ambiguidades, ou chego "de peito aberto", reagindo apenas ao que me &#233; apresentado e ocupando o tempo da reuni&#227;o com d&#250;vidas que eu poderia ter resolvido antes?</p></li><li><p><em><strong>"Eu estou afiando meu machado?"</strong></em> Qual foi a &#250;ltima vez que dediquei tempo para melhorar a maneira como analiso uma tarefa, tentando enxergar al&#233;m da solu&#231;&#227;o &#243;bvia e conectar os pontos com a estrat&#233;gia do neg&#243;cio? Ou estou apenas confiando nos meus reflexos e na experi&#234;ncia passada, correndo o risco de cair no "Vi&#233;s do Especialista"?</p></li><li><p><em><strong>"Minhas perguntas s&#227;o um farol ou uma neblina?"</strong></em> Quando eu questiono algo, estou colaborando com perguntas objetivas que guiam a equipe para uma solu&#231;&#227;o ("<em>J&#225; consideramos o impacto disso na performance do dashboard X?</em>")? Ou estou apenas trazendo mais d&#250;vidas e incertezas sem oferecer um caminho ("<em>Isso tudo me parece muito complexo e arriscado.</em>")? A primeira &#233; lideran&#231;a; a segunda pode ser paralisia por an&#225;lise.</p></li></ul><h4><strong>Para os Juniores: Navegando o Medo e Encontrando Seus Aliados</strong></h4><p>Para voc&#234; que est&#225; no in&#237;cio da jornada, a mensagem final &#233; clara: o medo de perguntar n&#227;o &#233; uma falha sua. Mas super&#225;-lo &#233; uma das primeiras e mais importantes vit&#243;rias da sua carreira. E voc&#234; n&#227;o precisa fazer isso sozinho.</p><ul><li><p><strong>Seja um Observador de Pessoas:</strong> Nas reuni&#245;es, ative seu modo detetive. Quem s&#227;o os seniores que fazem perguntas claras e com humildade? Quem tem paci&#234;ncia para explicar um conceito pela segunda vez? Quem reage bem &#224;s d&#250;vidas dos outros? Essas pessoas s&#227;o seus "portos seguros". Mapeie seus aliados e se aproxime deles. A maioria dos bons seniores adora ensinar.</p></li><li><p><strong>Abrace a "Pergunta Boba" de uma vez por todas:</strong> Eu te garanto, leitor: n&#227;o existe pergunta boba. O que existe &#233; um time inteiro construindo uma funcionalidade por tr&#234;s semanas com base numa premissa completamente errada, porque ningu&#233;m teve a coragem de fazer a pergunta "boba" que desmascararia o problema logo no in&#237;cio.</p></li></ul><p><strong>Entenda a Regra da Repeti&#231;&#227;o:</strong> &#201; aqui que mora o bom senso. H&#225; uma diferen&#231;a colossal entre:</p><ul><li><p><strong>Fazer 70 vezes a mesma pergunta sobre um fato:</strong> Isso n&#227;o demonstra curiosidade, mas falta de esfor&#231;o para anotar, pesquisar e reter a informa&#231;&#227;o que j&#225; lhe foi passada. Mostra que voc&#234; n&#227;o est&#225; fazendo a sua parte para estudar o assunto e melhorar.</p></li><li><p><strong>Fazer a mesma perguntas algumas vezes ao longo de um projeto complexo e do mundo real:</strong> Isso &#233; completamente normal, esperado e at&#233; desej&#225;vel! O contexto muda, os detalhes se aprofundam. Fazer a mesma pergunta em diferentes est&#225;gios do projeto mostra que voc&#234; est&#225; engajado e tentando conectar os pontos.</p></li></ul><p>E aqui vai um segredo final: <strong>os bons seniores e l&#237;deres </strong><em><strong>querem</strong></em><strong> que voc&#234; pergunte. O sil&#234;ncio de um j&#250;nior &#233; aterrorizante</strong>. Significa uma de duas coisas: ou ele &#233; um g&#234;nio que entendeu tudo de primeira (raro), <strong>ou ele est&#225; completamente perdido e com medo</strong>, pois n&#227;o foi dado espa&#231;o para d&#250;vidas! E no final est&#225; cavando um buraco de retrabalho que s&#243; ser&#225; descoberto daqui a duas semanas. <strong>Um s&#234;nior deveria ter medo do dia em que nenhum j&#250;nior busca mais a sua ajuda, pois isso significa que a cultura de aprendizado e seguran&#231;a da equipe est&#225; quebrada e que o ego est&#225; falando mais alto</strong>.</p><p>No fim, a habilidade de fazer perguntas &#233; a habilidade de aprender, de colaborar e de liderar, tudo em um s&#243; pacote. &#201; a ferramenta que nos impede de construir catedrais magn&#237;ficas no terreno errado. A jornada para se tornar um questionador melhor come&#231;a com uma &#250;nica decis&#227;o: a de valorizar a clareza acima da certeza, e a humildade acima do ego.</p><p>Obrigado por ler at&#233; o final e at&#233; o pr&#243;ximo artigo! &#128406;&#127996;</p>]]></content:encoded></item></channel></rss>