<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: João Antônio</title>
    <description>The latest articles on Forem by João Antônio (@antjjoao).</description>
    <link>https://forem.com/antjjoao</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3578101%2F152e06d8-6369-4bbe-96c3-8bea4efb4bf2.jpeg</url>
      <title>Forem: João Antônio</title>
      <link>https://forem.com/antjjoao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/antjjoao"/>
    <language>en</language>
    <item>
      <title>Cell-Based Architecture: o por que estamos sempre tentando mitigar riscos e falhas</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Tue, 28 Apr 2026 01:21:56 +0000</pubDate>
      <link>https://forem.com/antjjoao/cell-based-architecture-o-porque-estamos-sempre-tentando-mitigar-riscos-e-falhas-21em</link>
      <guid>https://forem.com/antjjoao/cell-based-architecture-o-porque-estamos-sempre-tentando-mitigar-riscos-e-falhas-21em</guid>
      <description>&lt;p&gt;Escalar microserviços horizontalmente resolve capacidade. Mas não                                                                                                                  resolve isolamento, e essa diferença importa mais do que parece.&lt;/p&gt;

&lt;p&gt;Cell-Based Architecture é um padrão que ainda é pouco discutido&lt;br&gt;
na comunidade, mas que empresas como AWS, Slack já usam&lt;br&gt;
há anos para garantir que uma falha nunca afete todos os usuários                                                                                                                ao mesmo tempo.                                                                                                                                                                    &lt;/p&gt;

&lt;p&gt;A ideia é simples: em vez de escalar instâncias de um mesmo sistema,                                                                                                               você replica a stack inteira em unidades independentes chamadas cells, onde cada uma serve um subconjunto da sua base de usuários.&lt;/p&gt;

&lt;h2&gt;
  
  
  O problema com microserviços tradicionais
&lt;/h2&gt;

&lt;p&gt;Quando escalamos microserviços horizontalmente, normalmente fazemos isso:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64era8w0v5qf3546f4u5.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F64era8w0v5qf3546f4u5.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;O design parece resiliente. Mas na prática, um único ponto de&lt;br&gt;
  pressão afeta todos os usuários ao mesmo tempo:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Um bug em deployment? Todo mundo cai.&lt;/li&gt;
&lt;li&gt;Um cliente com tráfego absurdo? Degrada a experiência de todos
os outros, o famoso &lt;em&gt;noisy neighbor&lt;/em&gt;.
&lt;/li&gt;
&lt;li&gt;Uma falha em cascata? Propaga pelo sistema inteiro.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Escala horizontal resolve capacidade. Não resolve isolamento.&lt;br&gt;&lt;br&gt;
  E essa diferença importa muito mais do que parece.&lt;/p&gt;

&lt;h2&gt;
  
  
  O que é uma Cell
&lt;/h2&gt;

&lt;p&gt;Uma &lt;strong&gt;cell&lt;/strong&gt; é uma unidade autônoma que contém tudo que precisa&lt;br&gt;
  para servir um subconjunto de usuários. Aplicação, infraestrutura&lt;br&gt;
  e banco de dados juntos, completamente isolados das outras cells. &lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1ieedwu6cf3f7ddv6cv.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fv1ieedwu6cf3f7ddv6cv.png" alt=" " width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Cada cell é funcionalmente idêntica às outras. A diferença é que&lt;br&gt;&lt;br&gt;
  cada uma serve um pedaço diferente da base de usuários e não sabe&lt;br&gt;
  que as outras existem. &lt;/p&gt;

&lt;h2&gt;
  
  
  As quatro propriedades que importam
&lt;/h2&gt;

&lt;p&gt;### 1. Blast Radius Containment&lt;/p&gt;

&lt;p&gt;Se a Cell 2 falha, apenas os usuários entre 1M e 2M são afetados.&lt;br&gt;&lt;br&gt;
  Os outros continuam operando normalmente. O raio da explosão é&lt;br&gt;
  previsível e limitado antes mesmo do incidente acontecer.                                                                                                                          &lt;/p&gt;

&lt;p&gt;### 2. Noisy Neighbor Isolation&lt;/p&gt;

&lt;p&gt;Um cliente corporativo com milhões de requests por dia não consegue&lt;br&gt;
  degradar a experiência de outros clientes que estão em cells separadas.&lt;br&gt;
  Cada um vive no seu próprio ambiente.                                                                                                                                              &lt;/p&gt;

&lt;p&gt;### 3. Canary deployment natural                                                                                                                                                   &lt;/p&gt;

&lt;p&gt;Você faz deploy na Cell 1, monitora por 30 minutos e só então&lt;br&gt;&lt;br&gt;
  propaga para as demais. Sem feature flags complexos. O rollback&lt;br&gt;
  é cirúrgico e afeta só quem precisa.&lt;/p&gt;

&lt;p&gt;### 4. Compliance e isolamento por região&lt;/p&gt;

&lt;p&gt;Precisa garantir que dados de clientes europeus fiquem na Europa?&lt;br&gt;&lt;br&gt;
  Cells por região resolvem isso de forma estrutural, não como&lt;br&gt;
  um patch em cima de outro patch.     &lt;/p&gt;

&lt;h2&gt;
  
  
  Como o roteamento funciona
&lt;/h2&gt;

&lt;p&gt;O &lt;strong&gt;Cell Router&lt;/strong&gt; é o único componente verdadeiramente global.&lt;br&gt;
  Ele resolve para qual cell cada request deve ir:&lt;/p&gt;

&lt;p&gt;Request chega&lt;br&gt;
       |&lt;br&gt;
  Cell Router consulta: qual cell serve esse tenant_id?&lt;br&gt;&lt;br&gt;
       |&lt;br&gt;&lt;br&gt;
  Encaminha para Cell N                                                                                                                                                              &lt;/p&gt;

&lt;p&gt;O mapeamento de &lt;code&gt;tenant_id&lt;/code&gt; para &lt;code&gt;cell_id&lt;/code&gt; fica em um store leve&lt;br&gt;
  e altamente disponível. Redis ou DynamoDB funcionam bem aqui ou outro que queira.                                                                                                                      &lt;/p&gt;

&lt;p&gt;## "Mas isso não é só sharding?"                                                                                                                                                   &lt;/p&gt;

&lt;p&gt;Não exatamente, e essa é a confusão mais comum.                                                                                                                                    &lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sharding&lt;/strong&gt; é uma estratégia de banco de dados. Você distribui&lt;br&gt;
  os dados entre múltiplos nós, mas a aplicação continua sendo uma só.&lt;/p&gt;

&lt;p&gt;Mas nem tudo nesse modelo representa o melhor dos mundos. Existem partes difíceis nessas decisões arquiteturais.&lt;/p&gt;

&lt;p&gt;Por exemplo, caso seja necessário agregar dados de todas as cells, será preciso consultar e processar informações distribuídas em múltiplos bancos. Isso pode aumentar a complexidade das consultas, dos pipelines de dados e da consistência das informações.&lt;/p&gt;

&lt;p&gt;Outro ponto importante é a sobrecarga de uma cell específica. Se uma cell começar a receber mais tráfego do que o esperado, migrar usuários ou aumentar sua capacidade sem downtime pode ser bem mais complicado. Como os bancos são separados, a redistribuição de dados exige mais cuidado, planejamento e mecanismos seguros de migração.&lt;/p&gt;

&lt;p&gt;Além disso, qualquer mudança estrutural no banco, como alterações de schema, índices ou procedures, precisa ser aplicada em todos os N bancos existentes. Isso exige uma boa estratégia de versionamento, automação e rollout gradual.&lt;/p&gt;

&lt;p&gt;Ou seja, apesar de ser uma solução robusta e escalável, ela também traz novos desafios operacionais. Toda decisão arquitetural resolve alguns problemas, mas inevitavelmente adiciona outros.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>microservices</category>
      <category>systemdesign</category>
      <category>backend</category>
    </item>
    <item>
      <title>por quê?</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Wed, 04 Feb 2026 10:47:10 +0000</pubDate>
      <link>https://forem.com/antjjoao/por-que-4a77</link>
      <guid>https://forem.com/antjjoao/por-que-4a77</guid>
      <description>&lt;p&gt;O desenvolvimento de software exige, de certa forma, um esforço "cognitivo" mínimo da máquina. Diante disso, acredito que pontos como entendimento e compreensão profunda produzem soluções cada vez mais robustas para a grande gambiarra que é a combinação de estruturas lógicas que se transformam em instruções com um determinado fim. O comportamento de uma instrução é interpretado com um nível específico de precisão, não de significado.&lt;/p&gt;

&lt;p&gt;Essa grande gambiarra nasce da interpretação de sinais elétricos. Enquanto elétrons percorrem circuitos em busca de uma fuga para despejar sua energia em algum ponto final, eles se tornam prisioneiros do próprio circuito. São quase escravos de portas que permitem ou bloqueiam sua passagem, velocistas que correm sem saber para onde, sem linha de chegada, queimando todo o combustível disponível até a dissipação total.&lt;/p&gt;

&lt;p&gt;Compreender essas abstrações, assim como as interpretações existentes em cada camada de software e hardware, traz uma enorme sabedoria para a tomada de decisão. Esse entendimento nos conduz à percepção de que tudo depende de contratos fortes e de implementações feitas com o mínimo possível de brechas de sentido e de processo. Quanto mais profundo é o entendimento das camadas, menor é o espaço para ambiguidades e maior é a confiabilidade dos sistemas construídos.&lt;/p&gt;

</description>
      <category>programming</category>
    </item>
    <item>
      <title>JVM, Java Memory Model e CPU: por que funciona em x86 e quebra em ARM</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Mon, 15 Dec 2025 02:17:33 +0000</pubDate>
      <link>https://forem.com/antjjoao/jvm-java-memory-model-e-cpu-por-que-funciona-em-x86-e-quebra-em-arm-280g</link>
      <guid>https://forem.com/antjjoao/jvm-java-memory-model-e-cpu-por-que-funciona-em-x86-e-quebra-em-arm-280g</guid>
      <description>&lt;p&gt;“Mas nunca deu problema na minha máquina.”&lt;/p&gt;

&lt;p&gt;Provavelmente porque sua máquina é x86. Troca pra ARM e alguns bugs de concorrência que estavam “dormindo” aparecem.&lt;/p&gt;

&lt;p&gt;A ideia central: &lt;strong&gt;x86 costuma ser mais “conservador” na prática&lt;/strong&gt;, enquanto &lt;strong&gt;ARM permite mais reordenações&lt;/strong&gt;. Se seu código depende de comportamento “bonzinho” do hardware, ele pode sobreviver em x86 e falhar em ARM.&lt;/p&gt;




&lt;h2&gt;
  
  
  O que o Java Memory Model (JMM) realmente garante
&lt;/h2&gt;

&lt;p&gt;O JMM não fala “threads compartilham memória e pronto”. Ele define:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;visibilidade&lt;/strong&gt;: quando o que uma thread escreveu passa a ser visto por outra&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;ordem&lt;/strong&gt;: quais reordenações são permitidas&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;happens-before&lt;/strong&gt;: a relação que cria garantias reais entre threads&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Sem um &lt;em&gt;happens-before&lt;/em&gt; entre duas ações, você não tem garantia de:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ver o valor mais recente&lt;/li&gt;
&lt;li&gt;ver as coisas na ordem que você escreveu&lt;/li&gt;
&lt;li&gt;ver um objeto “pronto”&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Isso é o motivo de muitos bugs “fantasma”.&lt;/p&gt;




&lt;h2&gt;
  
  
  O erro comum: confiar na ordem do código
&lt;/h2&gt;

&lt;p&gt;Você escreve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight java"&gt;&lt;code&gt;&lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;span class="n"&gt;ready&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="o"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E imagina que outra thread, ao ver &lt;code&gt;ready == true&lt;/code&gt;, vai necessariamente ver &lt;code&gt;x == 42&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;O JMM permite que, &lt;strong&gt;sem sincronização&lt;/strong&gt;, outra thread observe:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ready == true
x == 0

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Porque:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;x pode ficar em cache/registrador&lt;/li&gt;
&lt;li&gt;as escritas podem ser reordenadas&lt;/li&gt;
&lt;li&gt;a outra thread pode ler valores antigos&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  O clássico pesado: “objeto meio construído”
&lt;/h2&gt;

&lt;p&gt;Esse é o bug mais traiçoeiro.&lt;/p&gt;

&lt;p&gt;instance = new MyObject();&lt;/p&gt;

&lt;p&gt;Isso parece uma operação, mas por baixo vira algo tipo:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;alocar memória&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;inicializar campos (construtor)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;publicar a referência (instance aponta pro objeto)&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Sem sincronização, a JVM/CPU podem efetivamente permitir que a publicação (3) aconteça antes da inicialização (2) ser visível para outras threads.&lt;/p&gt;

&lt;p&gt;Então uma segunda thread pode ver:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;if (instance != null) {
    instance.doSomething();
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;E instance não é null, mas o objeto pode estar com campos ainda em estado “default” (0/null). Em padrões como double-checked locking isso já deu dor real em produção.&lt;/p&gt;

&lt;h1&gt;
  
  
  Onde volatile entra (e por que resolve)
&lt;/h1&gt;

&lt;p&gt;Se você declara:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;private static volatile MyObject instance;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Você ganha duas coisas importantes:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Visibilidade: leitura de volatile vê o valor mais recente (não “preso” em cache local).&lt;/li&gt;
&lt;li&gt;Ordem: volatile cria barreiras que impedem certas reordenações ao redor da variável.&lt;/li&gt;
&lt;/ul&gt;

&lt;h1&gt;
  
  
  Regra que importa:
&lt;/h1&gt;

&lt;p&gt;Uma escrita em um volatile acontece-before de qualquer leitura posterior do mesmo volatile.&lt;/p&gt;

&lt;p&gt;Na prática: se a thread B leu instance (volatile) como não-nulo, ela passa a ter garantia de enxergar as escritas que aconteceram antes da thread A publicar essa referência (incluindo os writes do construtor que “prepararam” o objeto).&lt;/p&gt;

&lt;h2&gt;
  
  
  “Como funciona por baixo dos panos”
&lt;/h2&gt;

&lt;p&gt;Escrita em volatile&lt;/p&gt;

&lt;p&gt;Quando a JVM compila uma escrita em volatile, ela precisa garantir que:&lt;/p&gt;

&lt;p&gt;todas as escritas anteriores não fiquem “penduradas” e invisíveis&lt;/p&gt;

&lt;p&gt;a publicação do valor não “passe na frente” de coisas anteriores&lt;/p&gt;

&lt;p&gt;Isso é implementado inserindo memory fences/barriers ao redor do acesso (o tipo exato depende da arquitetura e do JIT).&lt;/p&gt;

&lt;p&gt;Leitura de volatile&lt;/p&gt;

&lt;p&gt;Quando a JVM compila uma leitura de volatile, ela precisa garantir que:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;você não leia um valor velho do cache/registrador&lt;/li&gt;
&lt;li&gt;leituras seguintes não sejam movidas “pra antes” dessa leitura&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Também usa barreiras e instruções com semântica apropriada.&lt;/p&gt;

&lt;h2&gt;
  
  
  x86 vs ARM: por que a diferença aparece
&lt;/h2&gt;

&lt;h1&gt;
  
  
  x86 (modelo mais forte na prática)
&lt;/h1&gt;

&lt;p&gt;Em x86, muitas reordenações são menos agressivas e a plataforma tende a “ajudar” sem você pedir. Não significa que o código está correto — significa que o bug pode não se manifestar.&lt;/p&gt;

&lt;p&gt;Resultado: muita gente escreve código sem happens-before e “passa”.&lt;/p&gt;

&lt;h1&gt;
  
  
  ARM (modelo mais fraco)
&lt;/h1&gt;

&lt;p&gt;ARM permite mais reordenação e exige sincronização explícita para garantir ordem/visibilidade. Se você não usou volatile/synchronized/locks/atomics, ARM tem mais chance de mostrar o bug.&lt;/p&gt;

&lt;p&gt;Resultado: o mesmo programa “ok” em x86 pode falhar em ARM sob carga.&lt;/p&gt;

&lt;p&gt;Regra prática (sem filosofia)&lt;/p&gt;

&lt;p&gt;Se tem compartilhamento entre threads, escolha uma estratégia:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;volatile: bom para flags/estado simples e publicação segura de referência&lt;/li&gt;
&lt;li&gt;&lt;p&gt;synchronized/Lock: bom para invariantes e operações compostas&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Atomic*: bom para operações atômicas sem lock (CAS), com custos/limites próprios&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Se você não tem happens-before, você está apostando na arquitetura e no acaso.&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>computerscience</category>
      <category>java</category>
    </item>
    <item>
      <title>Implementando preço baseado em volumetria com desconto retroativo</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Sat, 08 Nov 2025 14:22:56 +0000</pubDate>
      <link>https://forem.com/antjjoao/implementando-preco-baseado-em-volumetria-com-desconto-retroativo-5505</link>
      <guid>https://forem.com/antjjoao/implementando-preco-baseado-em-volumetria-com-desconto-retroativo-5505</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpdiqmhepns8mec23oo7.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fnpdiqmhepns8mec23oo7.png" alt="twitter post" width="800" height="407"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Vamos à ideia de trabalhar com tiers por volumetria.&lt;br&gt;
Neste exemplo, defini 3 tiers, onde os valores mínimo e máximo são inclusivos, ou seja, tanto o limite mínimo quanto o limite máximo estão dentro do tier.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0418xoapgphd67bbp1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fi0418xoapgphd67bbp1s.png" alt="structs" width="800" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Se o max for 0, quer dizer que vai do min até o “infinito”.&lt;br&gt;
Cada tier tem um unitPrice, que é o preço de cada pagamento naquela faixa.&lt;/p&gt;

&lt;p&gt;No PlanVersions, a ideia é ter várias versões de um mesmo plano convivendo ao mesmo tempo.&lt;/p&gt;

&lt;p&gt;Se rolar uma atualização de preço e você não quiser que os clientes antigos sejam impactados, o EffectiveAt entra justamente pra isso: você usa a data pra decidir qual versão do plano vale pra cada cobrança.&lt;/p&gt;

&lt;p&gt;Já o CustomerPricing é o vínculo entre o plano e o cliente — e ali você pode colocar um desconto se o cliente for especial… ou só muito gente boa mesmo kkkkk 😄&lt;/p&gt;

&lt;p&gt;aqui abaixo está o uso/exemplo:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft7jxyzb7gmg2y3oj7adf.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ft7jxyzb7gmg2y3oj7adf.png" alt=" " width="757" height="744"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;keep going until you're satisfied or dead.&lt;/p&gt;

</description>
      <category>go</category>
      <category>braziliandevs</category>
      <category>backend</category>
      <category>architecture</category>
    </item>
    <item>
      <title>equals, hashcode, hashmap</title>
      <dc:creator>João Antônio</dc:creator>
      <pubDate>Wed, 22 Oct 2025 01:18:13 +0000</pubDate>
      <link>https://forem.com/antjjoao/equals-hashcode-hashmap-5ah2</link>
      <guid>https://forem.com/antjjoao/equals-hashcode-hashmap-5ah2</guid>
      <description>&lt;h2&gt;
  
  
  O papel de equals() e hashCode() no Java
&lt;/h2&gt;

&lt;p&gt;Esses dois métodos vêm da classe Object, a base de todas as classes em Java.&lt;/p&gt;

&lt;p&gt;Eles determinam como o Java compara objetos e como eles são organizados em coleções baseadas em hash.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;equals(Object o)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Define se dois objetos são considerados “iguais”.&lt;/p&gt;

&lt;p&gt;Por padrão (em Object), equals() compara referências (endereço na memória).&lt;/p&gt;

&lt;p&gt;Classes costumam sobrescrever para comparar conteúdo.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Cpu {
    String process;
    Pessoa(String process) { this.process = process; }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true; 
        if (!(o instanceof Pessoa)) 
        Pessoa p = (Pessoa) o;
        return process.equals(p.process);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;hashCode()
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Retorna um número inteiro usado pelo algoritmo de hashing de coleções como HashMap e HashSet.&lt;/p&gt;

&lt;p&gt;Serve para descobrir rapidamente onde o objeto deve ficar (qual bucket).&lt;/p&gt;

&lt;p&gt;Objetos “iguais” segundo equals() devem ter o mesmo hashCode(),&lt;br&gt;
o contrário não é obrigatório (mesmo hash não garante igualdade).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@Override
public int hashCode() {
    return Objects.hash(nome);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Como o HashMap realmente usa isso
&lt;/h2&gt;

&lt;p&gt;Vamos entender o fluxo interno simplificado:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Você chama map.put(chave, valor).&lt;/li&gt;
&lt;li&gt;O Java calcula hash = chave.hashCode().&lt;/li&gt;
&lt;li&gt;Esse hash é usado pra determinar qual “bucket” no array interno vai armazenar a entrada.&lt;/li&gt;
&lt;li&gt;Dentro do bucket, se houver colisões (outros elementos com mesmo hash), ele compara com equals() para decidir se atualiza o valor ou adiciona outro nó.&lt;/li&gt;
&lt;li&gt;Quando você faz map.get(chave), ele repete o processo:

&lt;ul&gt;
&lt;li&gt;calcula o mesmo hashCode(),&lt;/li&gt;
&lt;li&gt;vai ao mesmo bucket,&lt;/li&gt;
&lt;li&gt;percorre e compara com equals() até achar a chave correspondente.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  O que acontece quando a chave muda
&lt;/h2&gt;

&lt;p&gt;Se você altera qualquer campo da chave depois de adicioná-la ao mapa, o hashCode() calculado na inserção não corresponde mais ao hash atual.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Resultado:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O get() procura no bucket errado, não encontra.&lt;/li&gt;
&lt;li&gt;O remove() também falha (não acha a chave antiga).&lt;/li&gt;
&lt;li&gt;A entrada continua existindo dentro de um bucket “órfão”.&lt;/li&gt;
&lt;li&gt;Isso não é um “vazamento” clássico, mas é um objeto perdido logicamente — ocupa memória, mas não é acessível.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Exemplo problemático&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;class Product{
    String name;
    int code;

    Product(String name, int code) {
        this.name= name;
        this.code= code;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Product)) return false;
        Produto p = (Product) o;
        return code == p.code;
    }

    @Override
    public int hashCode() {
        return Objects.hash(code);
    }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Agora veja o uso:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Map&amp;lt;Produto, Double&amp;gt; precos = new HashMap&amp;lt;&amp;gt;();
Produto p = new Produto("Mouse", 101);
precos.put(p, 79.9);


p.codigo = 999;

// Agora o hash mudou e o mapa não encntra mais a chave
System.out.println(precos.get(p)); //  null
System.out.println(precos.size()); //  1 (ainda existe internamente)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O objeto continua dentro do mapa, mas está “preso” em um bucket diferente e inacessível.&lt;/p&gt;

&lt;h2&gt;
  
  
  O contrato formal (equals / hashCode contract)
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;A especificação do Java diz:&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Se dois objetos são iguais segundo equals(), eles devem retornar o mesmo valor em hashCode().&lt;/p&gt;

&lt;p&gt;Se dois objetos têm hashCode() diferentes, eles não podem ser iguais.&lt;/p&gt;

&lt;p&gt;Se dois objetos têm o mesmo hashCode(), eles podem ou não ser iguais (colisão).&lt;/p&gt;

&lt;p&gt;Portanto:&lt;/p&gt;

&lt;p&gt;equals() → true ⇒ hashCode() igual&lt;br&gt;
hashCode() igual ⇒ equals() pode ser true ou false&lt;/p&gt;

&lt;p&gt;feito por João Antônio e caso encontre algum erro, adicione aqui um comentário ou me mande um e-mail.&lt;/p&gt;

</description>
      <category>algorithms</category>
      <category>java</category>
      <category>tutorial</category>
    </item>
  </channel>
</rss>
