<?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>DEV Community: Marcus Dorbação</title>
    <description>The latest articles on DEV Community by Marcus Dorbação (@marcus-dorbacao).</description>
    <link>https://dev.to/marcus-dorbacao</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.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F1551826%2Ffe987f2f-c020-44dd-83d0-8db2388ab82a.jpg</url>
      <title>DEV Community: Marcus Dorbação</title>
      <link>https://dev.to/marcus-dorbacao</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/marcus-dorbacao"/>
    <language>en</language>
    <item>
      <title>SSE (Server-Sent Events): como funciona por baixo dos panos</title>
      <dc:creator>Marcus Dorbação</dc:creator>
      <pubDate>Thu, 16 Jul 2026 15:09:26 +0000</pubDate>
      <link>https://dev.to/marcus-dorbacao/sse-server-sent-events-como-funciona-por-baixo-dos-panos-422l</link>
      <guid>https://dev.to/marcus-dorbacao/sse-server-sent-events-como-funciona-por-baixo-dos-panos-422l</guid>
      <description>&lt;p&gt;Se você já precisou que um servidor "avisasse" o navegador quando algo mudasse — sem ficar dando refresh ou fazendo polling a cada X segundos — provavelmente esbarrou em duas opções: WebSocket ou &lt;strong&gt;SSE (Server-Sent Events)&lt;/strong&gt;. Este artigo foca no segundo: o que é, e como ele funciona tecnicamente.&lt;/p&gt;

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

&lt;p&gt;SSE é um mecanismo de comunicação &lt;strong&gt;unidirecional&lt;/strong&gt; (servidor → cliente), construído sobre HTTP comum. O servidor mantém uma única conexão aberta e vai empurrando eventos de texto para o cliente conforme eles acontecem, sem que o cliente precise ficar perguntando "tem novidade?".&lt;/p&gt;

&lt;p&gt;Diferente do WebSocket, não existe handshake especial nem troca de protocolo — é uma requisição HTTP GET normal, que simplesmente nunca termina.&lt;/p&gt;

&lt;h2&gt;
  
  
  O fluxo, passo a passo
&lt;/h2&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkc8mtxe2xw0u1n045cbm.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fkc8mtxe2xw0u1n045cbm.jpg" alt=" " width="623" height="585"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  1. O cliente abre a conexão
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="nf"&gt;GET&lt;/span&gt; &lt;span class="nn"&gt;/stream&lt;/span&gt; &lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt;
&lt;span class="na"&gt;Host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;api.exemplo.com&lt;/span&gt;
&lt;span class="na"&gt;Accept&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/event-stream&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Uma requisição GET comum — nada de upgrade de protocolo.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. O servidor responde e não fecha a conexão
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="k"&gt;HTTP&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="m"&gt;1.1&lt;/span&gt; &lt;span class="m"&gt;200&lt;/span&gt; &lt;span class="ne"&gt;OK&lt;/span&gt;
&lt;span class="na"&gt;Content-Type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;text/event-stream&lt;/span&gt;
&lt;span class="na"&gt;Cache-Control&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;no-cache&lt;/span&gt;
&lt;span class="na"&gt;Connection&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s"&gt;keep-alive&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;O &lt;code&gt;Content-Type: text/event-stream&lt;/code&gt; avisa o cliente: "isso é um stream contínuo, não espere o corpo terminar".&lt;/p&gt;

&lt;h3&gt;
  
  
  3. O servidor envia eventos como texto
&lt;/h3&gt;

&lt;p&gt;Cada evento é um bloco separado por linha em branco:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;event: flag-update
data: {"flagKey": "novo-checkout", "value": true}

data: heartbeat

event: flag-update
data: {"flagKey": "cancelar-nota", "value": "variante-b"}

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

&lt;/div&gt;



&lt;p&gt;Campos possíveis:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Campo&lt;/th&gt;
&lt;th&gt;Para que serve&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;data:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O payload do evento (pode ter várias linhas)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;event:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Nome do tipo de evento — permite o cliente ter listeners diferentes por tipo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;id:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Identificador do evento, usado para retomar de onde parou após reconexão&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;retry:&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Tempo em ms que o cliente deve esperar antes de tentar reconectar&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  4. O cliente processa cada evento
&lt;/h3&gt;

&lt;p&gt;No navegador, isso é nativo via &lt;code&gt;EventSource&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;source&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;EventSource&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;/stream&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="nx"&gt;source&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;flag-update&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Atualização recebida:&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  5. Se a conexão cair, reconecta sozinho
&lt;/h3&gt;

&lt;p&gt;Esse é um dos pontos mais úteis do SSE: o &lt;code&gt;EventSource&lt;/code&gt; reconecta automaticamente, sem precisar de código extra — e reenvia o header &lt;code&gt;Last-Event-ID&lt;/code&gt; com o último &lt;code&gt;id:&lt;/code&gt; recebido, permitindo o servidor retomar exatamente de onde parou, em vez de reenviar tudo desde o início.&lt;/p&gt;

&lt;h2&gt;
  
  
  Características técnicas que valem lembrar
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Só texto&lt;/strong&gt;: o payload é sempre string, geralmente JSON serializado — não dá para mandar binário direto&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Unidirecional&lt;/strong&gt;: o cliente não consegue mandar dados de volta pela mesma conexão. Se precisar de um "ack" ou enviar algo, é uma requisição HTTP separada&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Roda sobre infraestrutura HTTP comum&lt;/strong&gt;: proxies, load balancers e CDNs já sabem lidar com isso, diferente do WebSocket, que às vezes exige suporte especial nesses componentes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Limite de conexões no HTTP/1.1&lt;/strong&gt;: navegadores limitam a ~6 conexões simultâneas por domínio; isso deixa de ser um problema no HTTP/2, que multiplexa várias streams numa única conexão TCP&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Quando faz sentido usar
&lt;/h2&gt;

&lt;p&gt;SSE é a escolha certa quando o fluxo de dados é &lt;strong&gt;só do servidor para o cliente&lt;/strong&gt; — notificações, atualizações de status, feeds em tempo real, mudanças de configuração. Se o cliente também precisa mandar dados pela mesma conexão em tempo real (ex: um chat, um jogo multiplayer), WebSocket é o caminho, porque ali a comunicação é bidirecional.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Feature Flags - Thinking</title>
      <dc:creator>Marcus Dorbação</dc:creator>
      <pubDate>Thu, 16 Jul 2026 14:51:59 +0000</pubDate>
      <link>https://dev.to/marcus-dorbacao/feature-flags-thinking-3o1f</link>
      <guid>https://dev.to/marcus-dorbacao/feature-flags-thinking-3o1f</guid>
      <description>&lt;h1&gt;
  
  
  Sistema de Feature Flags — Documentação Técnica
&lt;/h1&gt;

&lt;blockquote&gt;
&lt;p&gt;Documentação em construção. Público-alvo: devs do time (uso técnico/API).&lt;br&gt;
Sistema avançado: rollout percentual, segmentação de usuários e A/B test.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Índice
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Visão geral&lt;/li&gt;
&lt;li&gt;Core de flags&lt;/li&gt;
&lt;li&gt;Targeting e segmentação&lt;/li&gt;
&lt;li&gt;Rollout progressivo&lt;/li&gt;
&lt;li&gt;A/B testing / Experimentação&lt;/li&gt;
&lt;li&gt;SDKs e integração técnica&lt;/li&gt;
&lt;li&gt;Autenticação (SDK Key)&lt;/li&gt;
&lt;li&gt;Sincronização em tempo real&lt;/li&gt;
&lt;li&gt;Eventos de conexão&lt;/li&gt;
&lt;li&gt;Webhooks (integração externa)&lt;/li&gt;
&lt;li&gt;Fluxo completo de evaluate()&lt;/li&gt;
&lt;li&gt;Decisões de arquitetura&lt;/li&gt;
&lt;li&gt;Modelo de dados&lt;/li&gt;
&lt;li&gt;Em aberto&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Visão geral
&lt;/h2&gt;

&lt;p&gt;Sistema de feature flags construído do zero, com suporte a:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Flags booleanas e multivariáveis&lt;/li&gt;
&lt;li&gt;Segmentação por atributos de usuário&lt;/li&gt;
&lt;li&gt;Rollout percentual com bucketing consistente&lt;/li&gt;
&lt;li&gt;A/B testing com múltiplas variantes&lt;/li&gt;
&lt;li&gt;SDKs client-side e server-side&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Core de flags
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Tipos de flag&lt;/strong&gt;: boolean (on/off) e multivariável (string/número/JSON), permitindo retornar variantes diferentes, não só ligar/desligar&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ambientes&lt;/strong&gt;: dev, staging, produção — cada um com seu próprio estado de flag&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Kill switch&lt;/strong&gt;: desligar uma flag instantaneamente em produção, sem deploy&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Targeting e segmentação
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Regras de segmentação&lt;/strong&gt;: por atributo do usuário (país, plano, versão do app, device, etc.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Targeting individual&lt;/strong&gt;: ligar a flag para usuários específicos (por ID, email)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Segments&lt;/strong&gt;: listas nomeadas de usuários (ex: beta testers, funcionários internos). Terminologia alinhada ao padrão de mercado (LaunchDarkly usa "segments"; "cohort" é o termo equivalente do lado de ferramentas de analytics como Amplitude, e os dois conceitos costumam ser sincronizados entre as duas categorias de ferramenta)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Rollout progressivo
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rollout percentual&lt;/strong&gt;: liberar gradualmente para uma fatia da base (5%, 10%, 50%...)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sticky bucketing&lt;/strong&gt;: garante que o mesmo usuário sempre caia na mesma variante entre sessões&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hash consistente&lt;/strong&gt;: normalmente hash do &lt;code&gt;user ID + flag key&lt;/code&gt; para decidir o bucket&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Bucketing, definição&lt;/strong&gt;: mecanismo de dividir a base de usuários em fatias numeradas (0-99) de forma determinística e recalculada a cada requisição — não há cadastro prévio de "usuário está no grupo X" guardado em banco. A mesma entrada (&lt;code&gt;userId + flagKey&lt;/code&gt;) sempre produz a mesma saída, o que gera o efeito "sticky" sem precisar guardar estado por usuário. Incluir a &lt;code&gt;flagKey&lt;/code&gt; no hash (e não só o &lt;code&gt;userId&lt;/code&gt;) evita correlação indesejada entre o bucket de um usuário em flags diferentes&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  A/B testing / Experimentação
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Múltiplas variantes com pesos configuráveis (ex: A 33%, B 33%, C 34%)&lt;/li&gt;
&lt;li&gt;Integração com analytics: disparar eventos de exposição (quem viu qual variante)&lt;/li&gt;
&lt;li&gt;Significância estatística geralmente delegada a uma ferramenta de análise externa, mas o sistema de flags precisa expor os dados de exposição&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  SDKs e integração técnica
&lt;/h2&gt;

&lt;h3&gt;
  
  
  SDK client-side vs server-side
&lt;/h3&gt;

&lt;p&gt;A diferença não é só "onde roda o código" — é uma questão de segurança e vazamento de informação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Server-side SDK&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Roda em ambiente confiável (backend)&lt;/li&gt;
&lt;li&gt;Pode ter acesso a todas as flags, incluindo as não lançadas, regras de segmentação completas e segredos usados no targeting&lt;/li&gt;
&lt;li&gt;Comunica-se direto com a API central ou via um &lt;strong&gt;Relay Proxy&lt;/strong&gt; (serviço intermediário que reduz chamadas repetidas)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Client-side SDK&lt;/strong&gt; (browser, mobile, apps)&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Roda em ambiente não confiável — qualquer um pode inspecionar o payload&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nunca&lt;/strong&gt; deve receber a lista completa de flags. O backend avalia a flag para aquele usuário específico e envia só o payload já resolvido (ex: &lt;code&gt;flag X = true&lt;/code&gt;, &lt;code&gt;flag Y = variante B&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Isso é feito via endpoint do tipo &lt;code&gt;/sdk/eval?context={user}&lt;/code&gt;, retornando um payload minimalista&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Regra crítica&lt;/strong&gt;: nunca usar a SDK key server-side (completa) dentro de um app client — isso vazaria todas as flags e regras internas&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Avaliação local vs remota
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Abordagem&lt;/th&gt;
&lt;th&gt;Latência&lt;/th&gt;
&lt;th&gt;Frescor dos dados&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Remota (thin client)&lt;/td&gt;
&lt;td&gt;Alta — chamada de rede a cada checagem&lt;/td&gt;
&lt;td&gt;Sempre atualizado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Local (thick client, recomendada)&lt;/td&gt;
&lt;td&gt;Baixa — avaliação em memória&lt;/td&gt;
&lt;td&gt;Depende da estratégia de sync&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Estratégias de atualização do cache local:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Polling&lt;/strong&gt;: SDK pergunta "tem mudança?" a cada N segundos&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Streaming&lt;/strong&gt;: servidor empurra updates em tempo real assim que uma flag muda&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Muitos SDKs combinam os dois: streaming como canal principal + polling como fallback caso a conexão caia.&lt;/p&gt;

&lt;h3&gt;
  
  
  Fallback / default values
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Todo flag check exige um valor &lt;strong&gt;default obrigatório&lt;/strong&gt; na chamada (nunca deixar o SDK "adivinhar")&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hierarquia de fallback&lt;/strong&gt;:

&lt;ol&gt;
&lt;li&gt;Valor em cache local (último snapshot conhecido)&lt;/li&gt;
&lt;li&gt;Se nunca conectou → default fornecido no código de chamada&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;Falha do serviço de flags nunca deve travar a aplicação — deve ser silenciosa e logada&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout de inicialização&lt;/strong&gt;: tempo máximo de espera no boot para o primeiro fetch (ex: 5s); depois disso, segue com defaults e atualiza quando possível&lt;/li&gt;
&lt;li&gt;Emitir métrica/log quando o SDK cai em modo fallback, para sinalizar degradação do serviço&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Autenticação (SDK Key)
&lt;/h2&gt;

&lt;p&gt;A SDK key funciona como um &lt;strong&gt;bearer token&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight http"&gt;&lt;code&gt;&lt;span class="err"&gt;Authorization: Bearer sdk-a1b2c3d4...
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Elemento&lt;/th&gt;
&lt;th&gt;Papel&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;SDK key (bearer token)&lt;/td&gt;
&lt;td&gt;Autentica + resolve escopo (projeto/ambiente) — não participa da decisão de segmentação&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context (key, plan, country...)&lt;/td&gt;
&lt;td&gt;Fornece os dados para a decisão de segmentação e rollout&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Exemplo de request:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;POST&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;/evaluate&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="err"&gt;Headers:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"Authorization"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Bearer sdk-a1b2c3d4..."&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;

&lt;/span&gt;&lt;span class="err"&gt;Body:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flagKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cancelar-nota-fiscal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"context"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"key"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"user-12345"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"plan"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"enterprise"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"country"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"BR"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Por que não expor projectId/environment como parâmetros públicos
&lt;/h3&gt;

&lt;p&gt;Alternativa descartada: passar &lt;code&gt;?project=app-mobile&amp;amp;env=production&lt;/code&gt; abertamente, sem token.&lt;/p&gt;

&lt;p&gt;Problemas dessa abordagem:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Enumeração&lt;/strong&gt; — projectId previsível permite testar/descobrir outros projetos/ambientes&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sem autenticação&lt;/strong&gt; — impossível distinguir chamada legítima de bisbilhotagem&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Vazamento de lógica de negócio&lt;/strong&gt; — testar contexts publicamente permite reconstruir regras de segmentação por engenharia reversa&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sem revogação/rotação&lt;/strong&gt; — sem token não há "chave" para cortar acesso; seria preciso mudar a própria estrutura da API&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sem rate limiting/billing por cliente&lt;/strong&gt; — impossível medir uso ou isolar consumidores&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DoS mais fácil&lt;/strong&gt; — sem identificação, não dá para aplicar throttling seletivo&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mistura acidental de ambientes&lt;/strong&gt; — sem credencial vinculante, é mais fácil um client de produção acessar staging por engano&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Sincronização em tempo real
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Por que webhook não serve como mecanismo de sync do SDK
&lt;/h3&gt;

&lt;p&gt;Webhook exige que o &lt;strong&gt;servidor inicie uma conexão de saída até o cliente&lt;/strong&gt; — o que não funciona para a maioria dos consumidores reais de SDK:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Frontend/browser: sem endereço público&lt;/li&gt;
&lt;li&gt;App mobile: sem IP público, sem porta aberta&lt;/li&gt;
&lt;li&gt;Backend atrás de NAT/firewall corporativo: porta não exposta&lt;/li&gt;
&lt;li&gt;Serverless/Lambda: função só existe durante a execução&lt;/li&gt;
&lt;li&gt;Múltiplas réplicas atrás de load balancer: ambíguo para qual réplica enviar&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Por isso, o padrão adotado (como na maioria dos sistemas de feature flag do mercado) é o &lt;strong&gt;inverso&lt;/strong&gt;: o cliente inicia a conexão de saída (streaming ou polling) e mantém ela aberta.&lt;/p&gt;

&lt;h3&gt;
  
  
  SSE como alternativa ao WebSocket
&lt;/h3&gt;

&lt;p&gt;WebSocket é bidirecional e mais pesado que o necessário — o caso de uso aqui é apenas &lt;strong&gt;server → client&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SSE (Server-Sent Events)&lt;/strong&gt; cobre esse caso:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conexão HTTP simples, unidirecional&lt;/li&gt;
&lt;li&gt;Reconexão automática nativa&lt;/li&gt;
&lt;li&gt;Funciona sobre HTTP/1.1 comum, sem upgrade de protocolo&lt;/li&gt;
&lt;li&gt;Mais simples de implementar mantendo tempo real&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Fluxo de autenticação SSE:&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;SDK Client → API (Auth): POST /auth (org, app, ambiente)
API (Auth) → SDK Client: SDK Key (bearer token)
SDK Client → Sync Service (SSE): GET /stream (Authorization: Bearer ...)
Sync Service → SDK Client: snapshot inicial de flags
[flag alterada no painel]
Sync Service → SDK Client: evento SSE (update de flag)
SDK Client: atualiza cache local
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Eventos de conexão
&lt;/h2&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;SdkConnected&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Dispara quando o SDK abre a conexão de streaming (SSE/WebSocket) &lt;strong&gt;e&lt;/strong&gt; o token é validado com sucesso. Não é só "TCP conectou" — é "conexão autenticada e pronta para receber updates daquele escopo (org/app/ambiente)".&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;code&gt;SdkDisconnected&lt;/code&gt;
&lt;/h3&gt;

&lt;p&gt;Dispara quando a conexão cai, por um de três motivos (importante diferenciar no payload):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Fechamento gracioso&lt;/strong&gt; — shutdown, deploy, scale-down&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Timeout/heartbeat perdido&lt;/strong&gt; — SDK parou de responder ao keep-alive&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Erro de rede&lt;/strong&gt; — queda abrupta&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Diferenciar o motivo importa para observabilidade: uma queda maciça de conexões pode ser "todo mundo fez deploy ao mesmo tempo" (normal) ou "o serviço de streaming caiu" (crítico).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Pontos de atenção:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Reconexões automáticas com backoff podem gerar ruído (&lt;code&gt;SdkDisconnected → SdkConnected&lt;/code&gt; repetidos) — considerar debounce/agregação nas métricas&lt;/li&gt;
&lt;li&gt;Incluir um &lt;code&gt;connectionId&lt;/code&gt; único por sessão de streaming para correlacionar conexão/desconexão e calcular duração&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Usos práticos:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Observabilidade — quantos SDKs ativos por ambiente&lt;/li&gt;
&lt;li&gt;Detecção de degradação — queda anormal indica problema no Sync service&lt;/li&gt;
&lt;li&gt;Billing/capacity planning — se cobrança for por conexões simultâneas&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Payload — SdkConnected
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eventType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SdkConnected"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connectionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"conn_9f8a3b2c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"organizationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"applicationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sdkKeyId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sdkkey_789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sdkVersion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"js-server@2.4.1"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"transport"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sse"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connectedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-16T14:32:10Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;connectionId&lt;/code&gt;: chave de correlação entre o evento de conexão e o de desconexão — sem ele não dá pra calcular duração nem saber a qual conexão um disconnect se refere&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sdkKeyId&lt;/code&gt;: identificador do token, &lt;strong&gt;nunca a SDK key crua&lt;/strong&gt; — logar o token completo num evento de observabilidade seria vazamento de credencial em qualquer pipeline de logs/analytics&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;sdkVersion&lt;/code&gt;: linguagem + versão do SDK — essencial pra saber quais versões estão em uso ao investigar bugs ou depreciar releases antigas&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;transport&lt;/code&gt;: canal usado (&lt;code&gt;sse&lt;/code&gt;, &lt;code&gt;websocket&lt;/code&gt;, &lt;code&gt;polling-fallback&lt;/code&gt;) — ajuda a identificar se alguma fatia de clients caiu pra polling por problema de rede&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Payload — SdkDisconnected
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eventType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"SdkDisconnected"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"connectionId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"conn_9f8a3b2c"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"organizationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"org_123"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"applicationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environment"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"sdkKeyId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"sdkkey_789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"disconnectedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-16T15:10:42Z"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"durationSeconds"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="mi"&gt;2312&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"heartbeat_timeout"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reasonDetail"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"no keep-alive response in 30s"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;durationSeconds&lt;/code&gt;: calculado no momento do evento (&lt;code&gt;disconnectedAt - connectedAt&lt;/code&gt;) — poupa quem consome o evento de cruzar os dois eventos manualmente&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reason&lt;/code&gt;: enum fechado (&lt;code&gt;graceful&lt;/code&gt;, &lt;code&gt;heartbeat_timeout&lt;/code&gt;, &lt;code&gt;network_error&lt;/code&gt;) — nunca texto livre, pra permitir agregação confiável em dashboards&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reasonDetail&lt;/code&gt;: campo livre opcional, só pra debug humano — nunca usado em lógica/agregação&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Agregação de reconexões&lt;/strong&gt;: se um novo &lt;code&gt;SdkConnected&lt;/code&gt; chegar com o mesmo &lt;code&gt;sdkKeyId&lt;/code&gt; dentro de uma janela curta (ex: 5s) após um &lt;code&gt;SdkDisconnected&lt;/code&gt;, o pipeline de métricas pode agregar os dois como "reconexão" em vez de "queda + nova sessão" — evita inflar o contador de desconexões em cenários normais de instabilidade de rede.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decisão — sem IP nos eventos&lt;/strong&gt;: os eventos de conexão não carregam IP do client. Segmentação por IP, se necessária no futuro, é responsabilidade do client incluir como atributo no &lt;code&gt;context&lt;/code&gt; da chamada de &lt;code&gt;evaluate()&lt;/code&gt; — igual qualquer outro atributo (país, plano, device). O sistema de flags não infere nem captura dado de rede por conta própria; só decide com base no que recebe explicitamente.&lt;/p&gt;




&lt;h2&gt;
  
  
  Webhooks (integração externa)
&lt;/h2&gt;

&lt;p&gt;Webhook &lt;strong&gt;não é&lt;/strong&gt; o mecanismo de sincronização do SDK, mas é útil como &lt;strong&gt;feature de integração opcional&lt;/strong&gt;, quando o receptor é um serviço backend do próprio cliente, controlado por eles, com endpoint público de propósito.&lt;/p&gt;

&lt;p&gt;Exemplos de uso:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Notificar o sistema de CI/CD do cliente quando uma flag mudar em produção&lt;/li&gt;
&lt;li&gt;Sincronizar com um sistema de config interno do cliente&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Modelo proposto: URL de webhook registrada no momento da autenticação (junto com organização/aplicação/ambiente), salva em uma tabela de webhooks associada à chave/token.&lt;/p&gt;

&lt;h3&gt;
  
  
  Escopo do webhook
&lt;/h3&gt;

&lt;p&gt;O webhook usado aqui serve &lt;strong&gt;exclusivamente para notificar que uma flag mudou&lt;/strong&gt; — não carrega o novo valor no payload, apenas o sinal de que algo mudou naquela flag/ambiente. O cliente, ao receber o evento, decide se e quando revalidar o estado real via &lt;code&gt;evaluate()&lt;/code&gt;/&lt;code&gt;sdk/eval&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Escopo do registro: &lt;code&gt;Application&lt;/code&gt; + &lt;code&gt;Environment&lt;/code&gt; (não atrelado a uma &lt;code&gt;SdkKey&lt;/code&gt; específica, já que a chave de SDK tem ciclo de vida de rotação/revogação próprio, diferente da vida útil de uma integração de webhook).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;Webhook&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;applicationId&lt;/span&gt;
  &lt;span class="nx"&gt;environmentId&lt;/span&gt;
  &lt;span class="nx"&gt;url&lt;/span&gt;
  &lt;span class="nx"&gt;status&lt;/span&gt;          &lt;span class="c1"&gt;// active | disabled&lt;/span&gt;
  &lt;span class="nx"&gt;createdAt&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Payload
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"eventType"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FLAG_CHANGED"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"applicationId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"app_456"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"environmentId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"env_789"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flagKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cancelar-nota-fiscal"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"changedAt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"2026-07-17T10:15:00Z"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sinal genérico, sem o valor da flag — o cliente sempre revalida o estado real depois de receber o evento; o webhook nunca é a fonte de verdade do novo valor.&lt;/p&gt;

&lt;h3&gt;
  
  
  Por que não usar assinatura (HMAC/secret)
&lt;/h3&gt;

&lt;p&gt;Como o payload não carrega o valor da flag, forjar essa notificação não tem efeito real além de fazer o cliente revalidar seu estado à toa (uma chamada extra desnecessária, não uma injeção de dado falso). Diferente de um webhook que carregasse o valor diretamente — nesse caso a assinatura seria obrigatória. Aqui, o cliente é livre para descartar/ignorar disparos que considerar excessivos.&lt;/p&gt;

&lt;h3&gt;
  
  
  Rate limiting por webhook — com colapso de eventos
&lt;/h3&gt;

&lt;p&gt;Rate limit aqui não é sobre validade temporal da requisição — é um teto de frequência de disparo, para impedir que uma rajada de edições na mesma flag vire uma rajada de chamadas HTTP contra a &lt;code&gt;url&lt;/code&gt; cadastrada (o que tornaria o próprio serviço de flags um vetor de flood/DoS contra terceiros, caso alguém cadastre a URL de uma vítima).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Estratégia adotada&lt;/strong&gt;: dentro de uma janela de tempo (ex: 1 evento a cada N segundos por webhook), múltiplas mudanças na mesma flag são colapsadas em um único disparo — já que o payload não informa o valor, não há perda de informação relevante em notificar uma vez só "essa flag mudou" em vez de uma vez por edição.&lt;/p&gt;

&lt;h3&gt;
  
  
  Validação de URL no cadastro (anti-SSRF)
&lt;/h3&gt;

&lt;p&gt;Ao registrar um webhook, validar que a &lt;code&gt;url&lt;/code&gt; não aponta para endereços internos (&lt;code&gt;localhost&lt;/code&gt;, ranges de IP privado, endereço de metadata de nuvem como &lt;code&gt;169.254.169.254&lt;/code&gt;) — evita que o cadastro de um webhook seja usado para fazer o próprio serviço de flags acessar rede interna que não deveria alcançar.&lt;/p&gt;

&lt;h3&gt;
  
  
  Entrega e retry — WebhookDelivery
&lt;/h3&gt;

&lt;p&gt;Chamada HTTP para endpoint de terceiro pode falhar (timeout, 5xx, DNS fora do ar), então cada tentativa de entrega é registrada separadamente da configuração do webhook:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;WebhookDelivery&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;webhookId&lt;/span&gt;
  &lt;span class="nx"&gt;payload&lt;/span&gt;          &lt;span class="c1"&gt;// snapshot do corpo enviado&lt;/span&gt;
  &lt;span class="nx"&gt;attempt&lt;/span&gt;          &lt;span class="c1"&gt;// número da tentativa (1, 2, 3...)&lt;/span&gt;
  &lt;span class="nx"&gt;httpStatus&lt;/span&gt;
  &lt;span class="nx"&gt;success&lt;/span&gt;
  &lt;span class="nx"&gt;deliveredAt&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;Webhook&lt;/code&gt; (a entrega nasce vinculada ao webhook que a gerou), mas como registro histórico segue o mesmo critério do &lt;code&gt;ExposureEvent&lt;/code&gt;/&lt;code&gt;ConnectionSession&lt;/code&gt;: pode ser retido para fins de auditoria mesmo que o webhook seja depois editado ou desativado.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Política de retry (fase inicial)&lt;/strong&gt;: backoff exponencial com limite de tentativas (ex: 3 tentativas — 1min, 5min, 30min), depois marca como &lt;code&gt;failed&lt;/code&gt; definitivamente e loga para investigação manual. Sem fila de retry infinita.&lt;/p&gt;




&lt;h2&gt;
  
  
  Fluxo completo de evaluate()
&lt;/h2&gt;

&lt;p&gt;Pipeline de resolução de token → busca da flag → segmentação → rollout → resultado.&lt;/p&gt;

&lt;h3&gt;
  
  
  Visão geral do pipeline
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Request → Auth (SDK key) → Resolve escopo (org/app/env)
        → Buscar definição da flag (cache local ou remoto)
        → Flag existe e está ativa?
        → Avaliar regras (ordem de precedência)
        → Bucketing (rollout % / variante A-B)
        → Montar resultado + reason
        → Emitir evento de exposição (async)
        → Retornar resposta
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  1. Autenticação e resolução de escopo
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Validar o &lt;strong&gt;SDK key&lt;/strong&gt; (bearer token) contra o serviço de auth/cache de tokens&lt;/li&gt;
&lt;li&gt;Token inválido/revogado → &lt;code&gt;401&lt;/code&gt;, encerra o pipeline aqui (sem chegar a tocar em dados de flag)&lt;/li&gt;
&lt;li&gt;Token válido → resolve &lt;code&gt;{ organizationId, applicationId, environment }&lt;/code&gt; vinculados àquele token&lt;/li&gt;
&lt;li&gt;Esse escopo é o que delimita &lt;strong&gt;quais flags&lt;/strong&gt; podem ser buscadas no próximo passo — o context da request nunca escolhe o ambiente, só o token&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Busca da definição da flag
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Chave de busca: &lt;code&gt;(organizationId, applicationId, environment, flagKey)&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Fonte: cache local em memória (thick client) → se miss, busca remota → se a flag não existir, cai no fallback (ver seção de Fallback / default values)&lt;/li&gt;
&lt;li&gt;Flag encontrada, mas &lt;strong&gt;kill switch ligado&lt;/strong&gt; → resultado imediato = valor "off" configurado, &lt;code&gt;reason: "OFF"&lt;/code&gt;. Nenhuma regra é avaliada depois disso — ver Decisões de arquitetura sobre precedência do kill switch&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Ordem de precedência das regras
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Ordem&lt;/th&gt;
&lt;th&gt;Regra&lt;/th&gt;
&lt;th&gt;Comportamento&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Kill switch / flag off&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Curto-circuito — retorna valor off, ignora tudo abaixo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Targeting individual&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Se &lt;code&gt;context.key&lt;/code&gt; (ou email) está na lista de override individual → retorna o valor daquele override&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Segments&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Se o usuário pertence a um segment com regra própria → aplica o valor definido para aquele segment&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;4&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Regras de segmentação&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Avaliadas em ordem definida na flag (primeira regra que casar com os atributos do context vence)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;5&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Rollout percentual (fallthrough)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Se nenhuma regra acima casou, aplica o bucketing por hash consistente&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;6&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Default da flag&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Se nem o rollout resolver, usa o default definido na própria flag — não confundir com o default passado pelo SDK na chamada, que só entra em jogo se a flag inteira não for alcançável&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Cada resultado carrega um campo &lt;code&gt;reason&lt;/code&gt; (ex: &lt;code&gt;TARGET_MATCH&lt;/code&gt;, &lt;code&gt;RULE_MATCH&lt;/code&gt;, &lt;code&gt;FALLTHROUGH&lt;/code&gt;, &lt;code&gt;OFF&lt;/code&gt;, &lt;code&gt;DEFAULT&lt;/code&gt;) — essencial para debugar "por que esse usuário caiu nessa variante".&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Bucketing (rollout % e variantes A/B)
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Hash consistente: &lt;code&gt;hash(userId + flagKey) % 100&lt;/code&gt; → determina o bucket 0-99&lt;/li&gt;
&lt;li&gt;Rollout percentual: compara o bucket contra o threshold configurado (ex: 30% → buckets 0-29 entram)&lt;/li&gt;
&lt;li&gt;A/B com múltiplas variantes: o range 0-99 é dividido proporcionalmente aos pesos (A 33% = buckets 0-32, B 33% = 33-65, C 34% = 66-99)&lt;/li&gt;
&lt;li&gt;Usar sempre o &lt;strong&gt;mesmo algoritmo de hash&lt;/strong&gt; em todos os SDKs (client e server) — se um SDK usa hash diferente do outro, o mesmo usuário pode cair em variantes diferentes dependendo de onde a avaliação rodou, quebrando a sticky bucketing&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Fallback (quando a busca da flag falha)
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;Cache local (último snapshot conhecido) — se a flag existe no cache mas o serviço remoto está fora, usa o cache&lt;/li&gt;
&lt;li&gt;Se nunca conectou (sem cache algum) → default fornecido no código de chamada, &lt;code&gt;reason: "ERROR"&lt;/code&gt; ou &lt;code&gt;"CLIENT_NOT_READY"&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Logar/emitir métrica sempre que cair aqui, para não mascarar degradação do serviço&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  6. Montagem do resultado
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"value"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"variant"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"control"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RULE_MATCH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"ruleId"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"rule_beta_countries_br"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;value&lt;/code&gt;: o valor resolvido (bool, string, número ou JSON)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;variant&lt;/code&gt;: nome da variante (relevante em multivariável/A-B)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reason&lt;/code&gt;: motivo da decisão (debugging)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;ruleId&lt;/code&gt;: qual regra específica decidiu (opcional, mas ajuda muito em suporte/debug)&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;Exposição desse payload completo é restrita — ver Decisões de arquitetura.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  7. Evento de exposição
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Disparado de forma &lt;strong&gt;assíncrona&lt;/strong&gt; (não pode bloquear a resposta do &lt;code&gt;evaluate()&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;Contém: &lt;code&gt;flagKey&lt;/code&gt;, &lt;code&gt;variant&lt;/code&gt;, &lt;code&gt;context.key&lt;/code&gt;, &lt;code&gt;timestamp&lt;/code&gt;, &lt;code&gt;reason&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Vai para o pipeline de analytics — é o dado bruto que alimenta significância estatística de A/B test (calculada fora do sistema de flags)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  8. Diferença entre evaluate() single e /sdk/eval bulk
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;evaluate()&lt;/code&gt; single&lt;/strong&gt;: avalia uma flag específica para um context — típico de uso server-side, ou de debug/teste manual&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;code&gt;/sdk/eval?context=&lt;/code&gt;&lt;/strong&gt;: avalia &lt;strong&gt;todas&lt;/strong&gt; as flags do ambiente para aquele context de uma vez, retornando o payload minimalista já resolvido — é o que o client-side SDK chama no boot e a cada refresh, evitando N chamadas (uma por flag)&lt;/li&gt;
&lt;li&gt;Internamente, &lt;code&gt;/sdk/eval&lt;/code&gt; roda o mesmo pipeline acima em loop para cada flag do ambiente, mas com uma otimização: busca todas as definições de flag de uma vez (evita N round-trips ao cache/DB)&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  Decisões de arquitetura
&lt;/h2&gt;

&lt;h3&gt;
  
  
  1. Exposição de reason / ruleId no payload
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Decisão&lt;/strong&gt;: o payload completo (&lt;code&gt;reason&lt;/code&gt;, &lt;code&gt;ruleId&lt;/code&gt;) só é exposto em &lt;code&gt;evaluate()&lt;/code&gt; server-side / telas de debug autenticadas. O endpoint client-side (&lt;code&gt;/sdk/eval?context=&lt;/code&gt;) devolve só &lt;code&gt;value&lt;/code&gt; + &lt;code&gt;variant&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Motivo&lt;/strong&gt;: expor &lt;code&gt;ruleId&lt;/code&gt;/&lt;code&gt;reason&lt;/code&gt; no client permite que qualquer um inspecionando o response reconstrua a lógica de segmentação interna (ex: descobrir que existe uma regra específica por país) — o mesmo tipo de vazamento de lógica de negócio que já motivou a decisão de não expor &lt;code&gt;project&lt;/code&gt;/&lt;code&gt;env&lt;/code&gt; como parâmetros públicos.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Kill switch — precedência absoluta
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Decisão&lt;/strong&gt;: o kill switch &lt;strong&gt;não é uma regra&lt;/strong&gt; na lista de precedência de targeting/segmentação/rollout. É um curto-circuito que roda antes de qualquer avaliação de regra, e vence sempre, incondicionalmente — inclusive sobre targeting individual (ex: um QA com override pessoal não continua vendo a flag "on" se o kill switch foi acionado).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Motivo&lt;/strong&gt;: kill switch é a válvula de emergência do sistema. Se pudesse ter exceções, deixaria de cumprir sua função de parar tudo, sem exceção, sem deploy.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mecânica&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;No painel de controle, apertar o botão é uma escrita simples no mesmo lugar de outras configs da flag (&lt;code&gt;status: "killed"&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;A diferença está na prioridade de leitura: &lt;code&gt;evaluate()&lt;/code&gt; checa isso primeiro; se &lt;code&gt;killed&lt;/code&gt;, nem entra no loop de regras&lt;/li&gt;
&lt;li&gt;Propagação depende do canal de sync: SDKs com streaming (SSE) ativo recebem o update quase instantaneamente; SDKs em modo polling ou desconectados (fallback em cache local) só verão o kill switch na próxima reconexão/poll — limitação conhecida, não um bug&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Versionamento de algoritmo de hash — não implementar agora
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Decisão&lt;/strong&gt;: não implementar um registry de múltiplas versões de hash (&lt;code&gt;hashVersion&lt;/code&gt; por flag) neste momento. Se o algoritmo de hash for trocado no futuro, os buckets de usuários existentes vão simplesmente mudar — novas decisões serão produzidas a partir da nova conta, sem tentar preservar as antigas.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Motivo&lt;/strong&gt;: o sistema ainda está em fase de construção, sem testes A/B ativos que dependam de continuidade estatística entre trocas de algoritmo. Implementar registry versionado agora (com todo SDK de toda linguagem precisando reimplementar todas as versões de hash) é custo de engenharia prematuro para um problema que ainda não existe. Trocar hash é evento raro e deliberado — deve ser comunicado como &lt;strong&gt;breaking change&lt;/strong&gt;, não silenciosamente absorvido.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Nota de escopo&lt;/strong&gt;: essa decisão vale para o estágio atual do projeto. Se no futuro o sistema amadurecer com testes A/B rodando continuamente, essa decisão deve ser revisitada.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Verificação client-side nunca é autoritativa
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Cenário motivador&lt;/strong&gt;: uma interface desktop exibe um botão condicionado a uma flag. Se a conexão de streaming cair e o client não for notificado de um kill switch, o botão continua visível com a última informação em cache — até o momento em que o usuário efetivamente submete a ação.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Decisão&lt;/strong&gt;: toda flag que guarda uma ação com efeito real no backend deve ser &lt;strong&gt;re-verificada no servidor no momento da ação&lt;/strong&gt;, independente do que o client mostrou. A avaliação client-side (cache local) serve só para UX responsiva (mostrar/esconder elementos); nunca é a fonte de verdade.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Fluxo&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;Client (cache desatualizado) → mostra botão → usuário clica
        ↓
API recebe a submissão → roda evaluate() fresco, contra o estado atual
        ↓
Kill switch ativo agora → API rejeita/ignora a operação
        ↓
API responde ao client (ex: 403 / "feature indisponível")
        ↓
Client pode usar essa resposta como sinal indireto para invalidar seu cache local,
sem esperar a próxima reconexão de streaming
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Motivo&lt;/strong&gt;: mesmo princípio de nunca confiar em validação só no front-end — o client pode estar desatualizado, ou ser manipulado. A flag no client é sugestão de experiência; a flag no server é o portão real.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Formato da resposta de rejeição&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Código HTTP &lt;code&gt;403 Forbidden&lt;/code&gt; — a requisição está bem formada e autenticada, só não é permitida pelo estado atual da flag (não é &lt;code&gt;400&lt;/code&gt;, não é erro de input; não é &lt;code&gt;404&lt;/code&gt;, o recurso existe).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"FEATURE_UNAVAILABLE"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"reason"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"RULE_MATCH"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"flagKey"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"cancelar-nota-fiscal"&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;error&lt;/code&gt;: código fixo e genérico (&lt;code&gt;FEATURE_UNAVAILABLE&lt;/code&gt;) — permite o client reconhecer programaticamente que a rejeição foi por causa de uma flag, diferenciando de outros motivos de 403 que a API possa ter (ex: permissão de usuário)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;reason&lt;/code&gt;: reaproveita o mesmo enum já usado no resultado de &lt;code&gt;evaluate()&lt;/code&gt; (&lt;code&gt;OFF&lt;/code&gt;, &lt;code&gt;TARGET_MATCH&lt;/code&gt;, &lt;code&gt;RULE_MATCH&lt;/code&gt;, &lt;code&gt;FALLTHROUGH&lt;/code&gt;) — sem vocabulário novo só pra esse caso. Expor a &lt;strong&gt;categoria&lt;/strong&gt; da decisão é seguro e útil: &lt;code&gt;OFF&lt;/code&gt; sugere que é sistêmico/temporário (kill switch geral), enquanto &lt;code&gt;RULE_MATCH&lt;/code&gt;/&lt;code&gt;FALLTHROUGH&lt;/code&gt; sugere que o usuário não é elegível agora — o client pode adaptar a UX de acordo (tentar de novo mais tarde vs não insistir)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;flagKey&lt;/code&gt;: qual flag causou a rejeição — permite o client invalidar exatamente essa flag no cache local, sem esperar a próxima reconexão de streaming&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;O que fica de fora, deliberadamente&lt;/strong&gt;: &lt;code&gt;ruleId&lt;/code&gt;, &lt;code&gt;attribute&lt;/code&gt;, &lt;code&gt;operator&lt;/code&gt;, &lt;code&gt;value&lt;/code&gt; — o client sabe &lt;em&gt;que categoria&lt;/em&gt; de decisão aconteceu (ex: foi uma regra de segmentação), mas não sabe &lt;em&gt;qual&lt;/em&gt; atributo ou valor causou isso (ex: não sabe que foi porque &lt;code&gt;country != BR&lt;/code&gt;). Isso preserva a distinção entre categoria da decisão (seguro expor) e conteúdo da regra de negócio (não deve vazar), mantendo a mesma postura da Decisão 1.&lt;/p&gt;

&lt;h3&gt;
  
  
  5. ruleId sem versionamento
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Decisão&lt;/strong&gt;: &lt;code&gt;ruleId&lt;/code&gt; não é versionado. É uma referência estável à regra, cujo conteúdo pode ser editado ao longo do tempo sem gerar um novo identificador. &lt;code&gt;ExposureEvent&lt;/code&gt; e o resultado de &lt;code&gt;evaluate()&lt;/code&gt; guardam apenas o &lt;code&gt;ruleId&lt;/code&gt;, não um snapshot da condição avaliada naquele momento.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Limitação conhecida e aceita&lt;/strong&gt;: se uma regra for editada depois de gerar exposições, o histórico de auditoria não reflete com precisão o que a regra dizia no momento da avaliação — o &lt;code&gt;ruleId&lt;/code&gt; de um evento antigo passa a apontar para o conteúdo atual da regra, não para o que ela era quando o evento aconteceu.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Motivo&lt;/strong&gt;: mesma lógica já aplicada à decisão de não versionar o algoritmo de hash — construir uma trilha de auditoria histórica precisa (versionamento de regra estilo git, ou snapshot embutido no evento) é engenharia adicional para um problema que, no estágio atual do projeto, ainda não se manifestou como necessidade real. Aceitar a limitação agora é mais barato do que resolver um requisito que ainda não apareceu.&lt;/p&gt;

&lt;h3&gt;
  
  
  Resumo das decisões
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Item&lt;/th&gt;
&lt;th&gt;Decisão&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;reason&lt;/code&gt;/&lt;code&gt;ruleId&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Só em &lt;code&gt;evaluate()&lt;/code&gt; server-side/debug; nunca em &lt;code&gt;/sdk/eval&lt;/code&gt; client&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Kill switch&lt;/td&gt;
&lt;td&gt;Curto-circuito com precedência absoluta, fora da lista de regras&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Hash versionado&lt;/td&gt;
&lt;td&gt;Não implementar agora; troca de hash = breaking change documentado&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Verificação client vs server&lt;/td&gt;
&lt;td&gt;Toda ação com efeito real precisa de &lt;code&gt;evaluate()&lt;/code&gt; fresco no servidor no momento da ação&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Versionamento de &lt;code&gt;ruleId&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;Não implementado; histórico de auditoria pode ficar impreciso após edição de regras — limitação aceita&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Modelo de dados
&lt;/h2&gt;

&lt;p&gt;Entidades do domínio e suas relações, em terminologia UML (associação, composição, classe de associação).&lt;/p&gt;

&lt;h3&gt;
  
  
  Organization
&lt;/h3&gt;

&lt;p&gt;Tenant raiz — representa o cliente/empresa que usa a plataforma.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;Application&lt;/code&gt; (1 organização possui N aplicações; aplicação não existe fora de uma organização)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Application
&lt;/h3&gt;

&lt;p&gt;Um produto/app específico do cliente (ex: "app mobile", "backend web").&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;Environment&lt;/code&gt; (ambiente não existe fora de uma aplicação)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;Segment&lt;/code&gt; (segments são definidos no escopo da aplicação)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;FlagDefinition&lt;/code&gt; (a flag, como conceito, pertence à aplicação)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Environment
&lt;/h3&gt;

&lt;p&gt;Instância de ambiente (dev/staging/produção) dentro de uma aplicação.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;SdkKey&lt;/code&gt; (chave é emitida para um ambiente específico e não existe fora dele)&lt;/li&gt;
&lt;li&gt;Participa como um dos lados da &lt;strong&gt;classe de associação&lt;/strong&gt; &lt;code&gt;FlagEnvironmentConfig&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SdkKey
&lt;/h3&gt;

&lt;p&gt;Token de autenticação (bearer) que resolve o escopo org/app/ambiente.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;Webhook&lt;/code&gt; (webhook é registrado atrelado a uma chave; não existe solto)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Associação&lt;/strong&gt; com &lt;code&gt;ConnectionSession&lt;/code&gt; (a sessão referencia a chave, mas é um registro de log — pode ser retida mesmo após a chave ser revogada)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Webhook
&lt;/h3&gt;

&lt;p&gt;URL de integração externa registrada no momento da autenticação.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Existe apenas como parte de uma &lt;code&gt;SdkKey&lt;/code&gt; (composição, conforme acima)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  FlagDefinition
&lt;/h3&gt;

&lt;p&gt;A flag em si: chave, tipo (boolean/multivariável), descrição. Conceito estável entre ambientes.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;Application&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Participa como um dos lados da &lt;strong&gt;classe de associação&lt;/strong&gt; &lt;code&gt;FlagEnvironmentConfig&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  FlagEnvironmentConfig
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Classe de associação&lt;/strong&gt; entre &lt;code&gt;FlagDefinition&lt;/code&gt; e &lt;code&gt;Environment&lt;/code&gt;. Representa "esta flag, neste ambiente", carregando os atributos que variam por ambiente (&lt;code&gt;status&lt;/code&gt;/kill switch, &lt;code&gt;defaultValue&lt;/code&gt;, &lt;code&gt;hashVersion&lt;/code&gt;). É o motivo de uma flag poder estar "on" em staging e "off" em produção sem duplicar a definição da flag.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;Variant&lt;/code&gt;, &lt;code&gt;TargetingRule&lt;/code&gt;, &lt;code&gt;IndividualOverride&lt;/code&gt; (só existem no contexto de uma config específica)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Associação&lt;/strong&gt; com &lt;code&gt;SegmentTargetRule&lt;/code&gt; e &lt;code&gt;ExposureEvent&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Variant
&lt;/h3&gt;

&lt;p&gt;Uma variante de valor/peso dentro de uma config de flag (ex: "A", 33%).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;FlagEnvironmentConfig&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  TargetingRule
&lt;/h3&gt;

&lt;p&gt;Regra de segmentação ordenada, avaliada dentro de uma config.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;FlagEnvironmentConfig&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;RuleCondition&lt;/code&gt; (uma regra é feita de condições; sem elas não tem sentido)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  RuleCondition
&lt;/h3&gt;

&lt;p&gt;Condição atômica de uma regra (atributo, operador, valor — ex: &lt;code&gt;country equals "BR"&lt;/code&gt;).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;TargetingRule&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Operadores suportados (fase inicial — conjunto fechado, sem comparações como maior/menor)&lt;/strong&gt;:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operator&lt;/th&gt;
&lt;th&gt;Formato esperado (máscara)&lt;/th&gt;
&lt;th&gt;Exemplo de &lt;code&gt;value&lt;/code&gt;
&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;equals&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;escalar único (string, número ou bool)&lt;/td&gt;
&lt;td&gt;&lt;code&gt;"BR"&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;in&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;array de escalares do mesmo tipo&lt;/td&gt;
&lt;td&gt;&lt;code&gt;["BR", "PT", "AO"]&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;operatorSchema&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;equals&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;scalar&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;in&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;     &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;shape&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;array&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;O &lt;code&gt;operator&lt;/code&gt; funciona como chave que resolve a máscara de validação (&lt;code&gt;shape&lt;/code&gt;) esperada para &lt;code&gt;value&lt;/code&gt; — o motor de avaliação consulta essa máscara antes de parsear a condição, em vez de um &lt;code&gt;if/else&lt;/code&gt; fixo por operador&lt;/li&gt;
&lt;li&gt;Validação na criação da regra: o backend rejeita uma condição malformada (ex: &lt;code&gt;equals&lt;/code&gt; com um array) já no momento do cadastro, sem depender do motor de avaliação pra detectar o erro depois&lt;/li&gt;
&lt;li&gt;Extensível sem quebra: operadores futuros (&lt;code&gt;greater_than&lt;/code&gt;, &lt;code&gt;contains&lt;/code&gt;, etc.) só precisam declarar sua própria entrada em &lt;code&gt;operatorSchema&lt;/code&gt; — a estrutura de validação não muda, só cresce o dicionário
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;RuleCondition&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;id&lt;/span&gt;
  &lt;span class="nx"&gt;attribute&lt;/span&gt;       &lt;span class="c1"&gt;// ex: "country", "plan"&lt;/span&gt;
  &lt;span class="nx"&gt;operator&lt;/span&gt;        &lt;span class="c1"&gt;// "equals" | "in" — chave que resolve a máscara de shape&lt;/span&gt;
  &lt;span class="nx"&gt;value&lt;/span&gt;           &lt;span class="c1"&gt;// shape validado contra operatorSchema[operator].shape&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  IndividualOverride
&lt;/h3&gt;

&lt;p&gt;Override de valor para um usuário específico (por ID/email).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;FlagEnvironmentConfig&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Segment
&lt;/h3&gt;

&lt;p&gt;Lista/grupo nomeado de usuários (ex: beta testers, funcionários internos). Terminologia alinhada ao padrão de mercado (LaunchDarkly).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;Application&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;SegmentMember&lt;/code&gt; (a lista de membros só existe dentro do segment)&lt;/li&gt;
&lt;li&gt;Participa como um dos lados da &lt;strong&gt;classe de associação&lt;/strong&gt; &lt;code&gt;SegmentTargetRule&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SegmentMember
&lt;/h3&gt;

&lt;p&gt;Um usuário (&lt;code&gt;userKey&lt;/code&gt;) pertencente a um segment.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt; com &lt;code&gt;Segment&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  SegmentTargetRule
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Classe de associação&lt;/strong&gt; entre &lt;code&gt;Segment&lt;/code&gt; e &lt;code&gt;FlagEnvironmentConfig&lt;/code&gt;: representa "este segment direciona para este valor, nesta flag/ambiente". Necessário porque o mesmo segment pode ser usado em várias flags, e uma flag pode usar vários segments — é N:N com atributo próprio (&lt;code&gt;resultVariantId&lt;/code&gt;).&lt;/p&gt;

&lt;h3&gt;
  
  
  ExposureEvent
&lt;/h3&gt;

&lt;p&gt;Evento de telemetria: qual usuário viu qual variante, quando, por qual motivo.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Associação&lt;/strong&gt; (não composição) com &lt;code&gt;FlagEnvironmentConfig&lt;/code&gt; — registro de log que referencia a config no momento da avaliação, mas com retenção/ciclo de vida independente (a exposição histórica pode ser mantida mesmo que a flag seja depois removida)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  ConnectionSession
&lt;/h3&gt;

&lt;p&gt;Registro de uma sessão de streaming (&lt;code&gt;SdkConnected&lt;/code&gt; → &lt;code&gt;SdkDisconnected&lt;/code&gt;), com &lt;code&gt;connectionId&lt;/code&gt;, timestamps e motivo de desconexão.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Associação&lt;/strong&gt; com &lt;code&gt;SdkKey&lt;/code&gt; (mesmo raciocínio do &lt;code&gt;ExposureEvent&lt;/code&gt;: é log, não parte estrutural)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Critério geral de composição vs associação
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Composição&lt;/strong&gt;: quando o filho não tem razão de existir sem o pai e seu ciclo de vida está atrelado (regra sem flag, variante sem config, membro sem segment)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Associação&lt;/strong&gt;: para registros de telemetria/log (&lt;code&gt;ExposureEvent&lt;/code&gt;, &lt;code&gt;ConnectionSession&lt;/code&gt;) — devem sobreviver independentemente do objeto que os originou, pois são dado histórico, não estrutura viva&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Classe de associação&lt;/strong&gt;: quando a relação em si carrega atributos próprios e é N:N entre duas entidades que existem de forma independente (&lt;code&gt;FlagEnvironmentConfig&lt;/code&gt;, &lt;code&gt;SegmentTargetRule&lt;/code&gt;)&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  Diagrama de classes
&lt;/h3&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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fatw5usrtdw80cgl6vxf4.jpg" 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.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fatw5usrtdw80cgl6vxf4.jpg" alt=" " width="800" height="683"&gt;&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Em aberto
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Sem itens pendentes no momento.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>architecture</category>
      <category>systemdesign</category>
      <category>testing</category>
      <category>ai</category>
    </item>
  </channel>
</rss>
