Feature Flags

Feature Flags em Apps Mobile: um Guia Prático

Feature flags em apps mobile: kill switch, rollout escalonado, cache de remote config e como não ser barrado na revisão da App Store no iOS e Android.

Ilustração de um celular cercado por interruptores geométricos flutuantes, alguns ligados e outros apenas contornados, em tons de verde escuro e turquesa

Feature flags em apps mobile resolvem um problema que o desenvolvimento web não tem: depois que um build vai para a App Store ou para o Google Play, você não edita mais aquele código, só consegue construir em volta dele. Uma feature flag já enviada dentro de um binário revisado liga ou desliga um comportamento a partir do seu servidor, em minutos, sem nova submissão, sem nova revisão e sem nova instalação. Essa propriedade sozinha, mudar comportamento sem enviar código novo, é o que faz das flags a espinha dorsal de kill switch, rollout escalonado e configuração remota no iOS e no Android.

Este guia fica especificamente no lado mobile dessa história: por que o tempo de revisão das lojas torna inviável a mentalidade de “é só publicar um hotfix”, como um kill switch se comporta de fato num aparelho que já tem o código antigo instalado, a diferença real entre o rollout escalonado da loja e o controlado por flag, como Firebase Remote Config, os SDKs mobile do LaunchDarkly, o ConfigCat e o Unleash tratam cache e aparelhos sem rede de formas diferentes, e as armadilhas de revisão e de fragmentação que pegam times que copiam o manual da web sem ajustar.

Por que app nativo não faz hotfix como página web

Na web, uma publicação está a um push de distância de todo visitante. No mobile, uma mudança de código precisa sobreviver a um percurso que a web não tem equivalente: construir o binário, submeter, esperar a revisão humana ou automatizada, e esperar de novo enquanto a loja entrega a atualização aos aparelhos instalados aos poucos. A própria Apple publica que, em média, 90% das submissões são revisadas em menos de 24 horas, na página de App Review. É uma média, não um compromisso, e a própria formulação deixa 10% das submissões fora dessa janela sem prazo declarado. O Google Play documenta a sua parte de forma ainda menos determinística: atualizações de apps existentes são processadas e publicadas assim que possível, mas certos apps passam por revisão estendida, que pode levar até 7 dias ou mais em casos excepcionais, segundo a Central de Ajuda do Play Console. Nenhuma das duas lojas promete um prazo de que você possa depender no meio de um incidente, e é exatamente essa a questão.

Mesmo depois da aprovação, a atualização não chega a todo mundo na hora. A documentação do App Store Connect da própria Apple descreve um lançamento em fases que espalha a atualização de versão por 7 dias em passos diários fixos: 1%, 2%, 5%, 10%, 20%, 50% e 100% dos aparelhos elegíveis com atualização automática ligada. O rollout escalonado do Google Play funciona pelo mesmo princípio, mas dá ao desenvolvedor controle direto da porcentagem, ajustável de 1% até 100%, e só pode ser usado em atualizações, nunca numa primeira submissão.

Atraso de lançamento: binário novo contra virada de flag remotaUma publicação web vai ao ar na hora. Uma mudança de código nativo precisa de build, revisão da loja de um a sete dias e depois um lançamento em fases de até mais sete dias até alcançar todo aparelho. Uma flag já enviada dentro de um binário aprovado pode ser virada em minutos para todo aparelho que já está naquele binário, sem envolver a loja.dia 0dia 7dia 14Publicação webno ar para todo mundo em minutosBinário novo (mudança de código)buildrevisão 1-7dlançamento em fases até 100%, mais 7 diasFlag dentro de um binário já aprovadovirada em minutos, para todo aparelho já naquele build
Revisão e lançamento em fases valem para um binário novo. Uma flag que já vive dentro de um binário aprovado se move independente dos dois, e é exatamente esse o motivo de usar uma.

Essa distância entre “quero isso desligado agora” e “a correção precisa passar pela loja de novo” é justamente o que a feature flag existe para fechar. Nada na flag pula a revisão de código novo: ela apenas move a decisão do que mostrar para um interruptor que a loja já aprovou, de forma que virá-lo depois não exige refazer o percurso acima.

O que uma flag muda de fato num aparelho que já tem o código antigo

Uma feature flag mobile não baixa código novo. A diretriz 2.5.2 da Apple é explícita ao dizer que um app não pode baixar, instalar ou executar código que introduza ou altere recursos ou funcionalidades do app, com uma exceção estreita para apps educacionais que ensinam ou permitem ao estudante testar código, e apenas se o fonte ficar totalmente visível e editável pelo usuário. O que a flag muda é um booleano, um número ou um pequeno JSON que uma condicional já compilada dentro do app lê para escolher um caminho. O código dos dois caminhos, o ligado e o desligado, foi enviado junto no mesmo binário revisado; a flag só decide, em tempo de execução, qual deles roda para um aparelho.

Essa distinção também é o que mantém o comportamento controlado por flag do lado certo da diretriz 2.3.1. A orientação da Apple, repetida nos fóruns de desenvolvedores da própria empresa, é que um recurso pode ser ligado remotamente depois do lançamento desde que tenha sido informado à revisão e disponibilizado para teste antes da aprovação, de preferência por uma porta documentada nas notas de revisão. Uma flag que revela em silêncio um recurso não informado depois que o app passa pela revisão é exatamente a divergência entre o que os revisores viram e o que os usuários experimentam depois que a 2.3.1 existe para pegar.

Kill switch: o superpoder específico do mobile

Um kill switch é uma flag cujo trabalho inteiro é desligar na hora um recurso, ou uma dependência de terceiro inteira, sem novo lançamento. No mobile isso importa mais do que na web justamente por causa do atraso acima: se um SDK de pagamento se comporta mal, um provedor de mapas cai ou uma tela nova quebra numa classe específica de aparelho, esperar por uma revisão acelerada, mesmo pelo caminho de emergência mais rápido da Apple, é coisa de horas na melhor hipótese. Um kill switch já ligado no código antes de o incidente acontecer transforma esse tempo de resposta em minutos.

O mesmo mecanismo de rollout que sustenta o kill switch também sustenta a expansão gradual, só que rodando na direção oposta: em vez de ampliar a exposição de 1% rumo a 100%, um incidente a recolhe de onde estiver direto para 0%, para todo aparelho que já roda aquele binário, independente de a que porcentagem a própria loja tenha chegado no lançamento em fases dela.

Um rollout controlado por flag anda nas duas direçõesQuatro anéis concêntricos representam um rollout de flag expandindo de 1 por cento para 10, 50 e 100 por cento dos aparelhos que já estão num binário aprovado. Diferente do lançamento em fases da loja, essa porcentagem também pode ser recolhida direto para zero a qualquer momento, que é o que um kill switch faz durante um incidente.1%10%50%100%expandirkill switch: volta a 0% em minutossem revisão da lojanas duas direções
A mesma alavanca de porcentagem que expande o rollout é a que o recolhe durante um incidente. Nenhuma das duas direções toca a loja, porque o código dos dois estados já foi enviado no binário aprovado.

Duas porcentagens de rollout, controladas por duas partes diferentes

Esta é a parte que derruba times vindo da web para o mobile: um mesmo app pode ter duas porcentagens de rollout independentes rodando ao mesmo tempo, e confundi-las leva a ler o sinal errado. Uma pertence à loja; a outra pertence ao seu backend.

Rollout escalonado da loja Rollout por feature flag
Quem controla a porcentagem Apple ou Google (a Apple define automaticamente ao longo de 7 dias; o Google deixa você definir na mão) Seu backend ou o painel do fornecedor de flags
O que ele controla Quais aparelhos recebem o binário novo Qual comportamento fica ativo dentro de um binário que já tem todo o código
Volta atrás na hora? Só pausando a distribuição; aparelhos que já atualizaram ficam com aquele binário Sim, um kill switch derruba a exposição a 0% em minutos nos aparelhos já atualizados
Prazo típico Apple: 1%, 2%, 5%, 10%, 20%, 50%, 100% fixos em 7 dias. Google: incrementos escolhidos pelo desenvolvedor de 1% a 100% De minutos a dias, inteiramente a critério de quem opera
Preso a uma versão do app? Sim, por definição Não, pode valer em toda versão ainda capaz de ler aquela flag

Um erro comum é ler uma mudança de métrica durante um rollout sem saber qual dessas duas porcentagens estava se movendo na hora. Se o lançamento em fases da loja ainda está subindo, você também está misturando usuários que atualizaram em momentos diferentes e podem estar rodando caminhos de código distintos além da própria flag, um confundimento que a mudança de flag sozinha não explica.

Ferramentas de configuração remota: onde as principais diferem de verdade

“Configuração remota” e “feature flag” são usadas quase como sinônimos no mobile, mas as ferramentas por trás diferem em modelo de hospedagem, experimentação nativa e, principalmente, no comportamento quando o aparelho está sem conexão.

Ferramenta Modelo Leitura de teste A/B nativa Comportamento em cache e sem rede
Firebase Remote Config Gerenciada, parte do Firebase do Google Via Google Analytics e Firebase A/B Testing, por cima dos valores do Remote Config O intervalo padrão de busca em produção é de 12 horas; buscar com mais frequência que a janela configurada devolve uma exceção no cliente em vez de valor novo, e o SDK sempre avalia primeiro a última configuração em cache
SDKs mobile do LaunchDarkly (iOS e Android) SaaS comercial, conexão em streaming quando o app está em primeiro plano Nativa, frequentista e bayesiana, dentro da mesma plataforma Recebe atualizações em tempo real com o app em primeiro plano; o SDK de iOS não suporta busca em segundo plano, então apps iOS em background não recebem atualização ao vivo, enquanto o Android volta a fazer sondagem automaticamente em segundo plano
ConfigCat SaaS gerenciado com configuração servida por CDN, SDKs para iOS, Android e Kotlin Multiplatform Não nativa; a documentação foca na entrega da flag e deixa a leitura estatística para uma ferramenta externa Três modos explícitos de sondagem (automática, carregamento preguiçoso e manual) mais um modo offline dedicado, que serve apenas de um cache local pré-carregado e nunca chama a rede
Unleash (com Unleash Edge) Núcleo open source, auto-hospedado ou nuvem Enterprise; o Edge é uma camada de cache e avaliação na borda, à frente dos SDKs Não nativa; o guia da própria empresa passa por divisão e eventos de exposição e entrega a leitura de significância a uma ferramenta de análise externa O Edge avalia localmente e, segundo a documentação da própria Unleash, uma instância pode servir de dezenas a centenas de milhares de requisições por segundo a partir do cache, com o modo de streaming reduzindo o atraso de replicação de um intervalo de sondagem para quase tempo real

Nenhuma dessas quatro é universalmente certa para um app mobile. Um time já dentro do ecossistema Firebase ou Google ganha configuração remota, análise e uma leitura básica de experimento sem adicionar fornecedor. Um time que quer um motor estatístico nativo e maduro, e aceita pagar por isso, costuma parar no LaunchDarkly. Um time pequeno que quer um SDK enxuto e comportamento de sondagem previsível, sem dependência funda de plataforma, tende a preferir o ConfigCat. Um time que já roda Unleash nos serviços web e quer as mesmas flags no app, sem mandar dado de usuário final mais longe do que o necessário, é para quem o Unleash Edge foi construído.

Cache e comportamento sem rede: a parte que a maioria dos guias pula

Todas as ferramentas acima compartilham uma regra inegociável: um SDK mobile nunca pode travar a interface esperando uma chamada de rede que talvez não termine. O aparelho pode perder conexão no meio, ficar em modo avião ou simplesmente iniciar do zero antes de a busca resolver, e o app ainda tem que desenhar alguma coisa.

Como um SDK de flag mobile resolve um valor na abertura do appNa partida a frio o app verifica se já existe valor da flag em cache no aparelho. Se existir, renderiza na hora com o valor em cache. Se não existir, pede o valor mais recente ao serviço de configuração remota; em caso de sucesso guarda e aplica o novo valor, e em caso de falha ou sem rede cai no valor padrão fixo dentro do binário.Partida do appavalia a flagem cache noaparelho?simRenderiza na horacom o valor em cachenão ou vencidoPede o valor mais recenteao serviço de configuração remotaSucessoguarda e aplicaSem rede ou timeoutcai no padrão fixodentro do binário
O ramo da direita é de onde vem a maior parte dos bugs de flag em mobile. Toda flag precisa de um padrão fixo no próprio binário, porque nunca há garantia de que a busca na rede termine antes de a tela precisar ser desenhada.

As ferramentas acima codificam essa regra de formas diferentes e compatíveis. O Firebase Remote Config limita buscas repetidas (cinco por hora em versões mais antigas do SDK, mais permissivo nas novas) e só troca a configuração em memória depois de uma busca somada a uma chamada explícita de ativação, então uma busca lenta ou falha nunca trava a renderização, ela apenas mantém o que o app já tinha. Os SDKs mobile do LaunchDarkly mantêm um armazenamento persistente no aparelho, de forma que uma partida a frio já tem os últimos valores conhecidos antes de qualquer ida à rede terminar. O modo offline do ConfigCat leva isso ao extremo lógico: o app pode rodar inteiramente contra um cache local pré-carregado e nunca chamar a rede, útil para um build que precisa funcionar num ambiente genuinamente desconectado. Em todos os casos, a disciplina que importa é a mesma: envie um padrão sensato para cada flag dentro do binário, porque a rede é a única coisa que nenhuma dessas ferramentas consegue garantir.

Combinando feature flags com um teste A/B de verdade no mobile

Um rollout por flag diz qual porcentagem de aparelhos vê um comportamento. Ele não diz, sozinho, se aquele comportamento melhorou alguma coisa. Como o nosso guia sobre feature flags x teste A/B detalha, a flag é um mecanismo de entrega e o teste é um método de medição, e o mobile não muda essa fronteira, só acrescenta duas dobras.

A primeira dobra é qual camada fecha o ciclo estatístico. Como a tabela acima mostra, o LaunchDarkly calcula significância de forma nativa sobre os mesmos dados de flag; o Firebase Remote Config normalmente se apoia no Firebase A/B Testing ou no Google Analytics para essa leitura; ConfigCat e Unleash entregam o sinal bruto de exposição para a stack de análise que você já roda. Antes de tratar uma porcentagem de rollout como resultado pronto, confirme de que lado dessa linha a sua stack está, a mesma distinção que o guia de como implementar teste A/B server-side percorre para experimentos de backend em geral.

A segunda dobra é específica do mobile: fragmentação de versão do app. Um rollout que atravessa aparelhos em várias versões instaladas ao mesmo tempo está comparando mais do que a flag, porque builds antigos podem carregar correções diferentes, interface diferente ou um conjunto de caminhos de código completamente distinto. A leitura mais limpa vem de um rollout de flag rodando dentro de uma única versão estável do app, depois que o lançamento em fases da loja para aquela versão já terminou de subir, e não enquanto as duas porcentagens se movem juntas. O nosso guia de teste A/B em apps mobile trata do desenho do experimento nesse ambiente, e o de rollout progressivo e canary release cobre a disciplina de métrica de guardrail que mantém uma expansão escalonada honesta, no mobile ou em qualquer lugar.

Armadilhas que pegam especificamente times mobile

Armadilha Por que acontece Correção
Rejeição na App Store por recurso escondido A flag revela funcionalidade que os revisores nunca viram, violando a diretriz 2.3.1 Informe o recurso e uma forma de acioná-lo nas notas para revisão antes de submeter, mesmo que ele vá no ar desligado
Confundir flag com a exceção de download de código A diretriz 2.5.2 proíbe baixar e executar código novo; uma flag que busca lógica nova, e não apenas um valor, cruza essa linha Só envie comportamento totalmente compilado no binário revisado; a flag escolhe entre caminhos, não busca caminhos novos
Ler resultado atravessando versões do app Um rollout cobre vários binários instalados com código diferente, embaralhando a comparação Leia resultado apenas dentro de uma versão estável do app, depois que o rollout dela na loja terminou
Nenhum padrão fixo para a flag O app assume que a chamada de rede sempre resolve antes de desenhar Toda flag precisa de um valor padrão seguro enviado dentro do próprio binário, conforme a lógica de cache acima
Limite de busca durante o QA Teste manual rápido esbarra no limite do fornecedor (o intervalo mínimo de busca do Firebase, por exemplo) e o QA vê valor velho Use intervalo de busca reduzido apenas em builds de desenvolvimento, nunca em produção, como a maioria dos fornecedores documenta explicitamente
Flags órfãs em binários antigos Uma flag removida do backend continua sendo avaliada por usuários presos numa versão antiga que nunca atualizou Mantenha uma data de desativação documentada e confirme que o valor padrão nos binários antigos continua seguro por tempo indeterminado

Faça isso automático na Donnu

A Donnu A/B não é um SDK mobile e não avalia flags dentro de um aparelho: é um motor de experimentação web, e não vai fingir o contrário. Mas a dor que este guia percorre, expandir com segurança, vigiar um guardrail e só confiar num resultado quando ele é estatisticamente real, é exatamente a mesma dor que o seu time web sente assim que um rollout mobile deixa de ser “sobe e observa” e vira “dá para provar que isso ajudou”. Se a superfície que o seu rollout toca inclui uma página web, um fluxo de checkout ou um site de marketing ao lado do app, a Donnu entrega a amostra calculada, a leitura bayesiana honesta e o dado isolado por conta que uma porcentagem bruta de rollout não fornece sozinha. Comece um teste grátis de 14 dias para a metade web dessa história e combine com a ferramenta de flag mobile deste guia que já controla o seu app.

Leia também: Feature Flags: o guia completo · Feature Flags x Teste A/B · Rollout progressivo e canary release · Ferramentas de feature flag comparadas · Teste A/B em apps mobile · Read in English

Referências

Perguntas frequentes

Uma feature flag pode fazer meu app ser rejeitado na App Store?
A flag em si não reprova um app, mas o uso que se faz dela pode. As diretrizes de revisão da Apple (2.3.1) exigem que toda funcionalidade seja descrita com precisão nas notas para revisão e fique acessível aos revisores, então um recurso escondido atrás de uma flag e ligado só depois da aprovação viola essa regra, a menos que a Apple tenha sido avisada e tenha recebido uma forma de testar antes. Separadamente, a diretriz 2.5.2 proíbe baixar e executar código novo depois da aprovação, então a flag só é segura quando liga um caminho de código que já foi enviado dentro do binário revisado, nunca quando busca lógica nova no seu servidor.
Qual a diferença entre rollout escalonado da loja e rollout por feature flag?
O rollout escalonado (o lançamento em fases da Apple ou o rollout escalonado do Google Play) controla quantos aparelhos instalados recebem o novo binário, e está preso à versão do app: depois que um aparelho atualiza, ele fica com aquele código até a próxima atualização, e voltar atrás significa publicar outra versão. O rollout por feature flag controla quantos daqueles aparelhos já atualizados veem determinado comportamento ligado, e ele vive no seu servidor, então dá para subir, baixar ou desligar em minutos, para todo aparelho que já roda aquele binário, sem envolver a loja.
Preciso de Firebase Remote Config, LaunchDarkly ou ConfigCat se o app já usa lançamento em fases?
Normalmente sim, porque resolvem problemas diferentes. O lançamento em fases protege contra um binário quebrado chegar a todo mundo de uma vez, mas depois que a versão está publicada você não muda o comportamento dela sem nova submissão e novo ciclo de revisão. Uma ferramenta de flag remota permite mudar o comportamento dentro de um binário que já passou pela revisão, na hora e sem a loja no caminho, que é exatamente o que um kill switch ou um rollout ágil exigem.
O que acontece com uma feature flag quando o aparelho está sem rede?
Ela cai no que ficou em cache da última busca bem-sucedida ou, se o app nunca buscou com sucesso, num valor padrão fixo dentro do binário. É por isso que toda integração de flag em mobile precisa de um padrão seguro escrito no código para cada flag: Firebase Remote Config, LaunchDarkly, ConfigCat e os clientes do Unleash Edge avaliam primeiro a partir do cache local, e nenhum deles pode travar o app esperando uma chamada de rede que talvez nunca complete.
Dá para rodar um teste A/B de verdade com feature flag em app mobile?
Dá, mas a ferramenta de flag normalmente cuida só da divisão e do evento de exposição, não da leitura estatística. O LaunchDarkly e, do lado web, ferramentas como o PostHog calculam significância de forma nativa sobre os próprios dados de flag, mas muitos arranjos focados em mobile (Firebase Remote Config com Google Analytics, ou Unleash com uma ferramenta de análise externa) só emitem o sinal bruto e esperam que um motor estatístico separado declare o vencedor. Confirme de que lado dessa linha a sua stack está antes de tratar uma porcentagem de rollout como se fosse experimento concluído.
Vale ligar uma flag mobile a uma métrica de guardrail?
Vale, e no mobile isso é ainda mais importante do que na web, porque a reversão pela loja é lenta. Antes de expandir a exposição, defina duas ou três métricas que, se piorarem, disparam o kill switch independentemente do que a métrica principal mostrar: taxa de crash, tempo de partida do app e, quando houver compra dentro do app, taxa de reembolso na loja. O rollout por flag torna a reversão rápida, mas só se alguém tiver decidido antes qual número justifica puxar o gatilho.