TIPS 46 · INTERRUTTORE

Desliga a funcionalidade sem voltar a publicar

Como ponho uma funcionalidade nova atrás de um interruptor que ligo e desligo no telemóvel, sem uma versão nova: onde o guardo, os prompts e as três regras.

O que é e porque importa

Um feature flag, em português um interruptor, é um sim ou não que a app lê enquanto está a correr. Se estiver ligado, o utilizador vê a funcionalidade nova. Se estiver desligado, vê a de antes. O código dos dois caminhos já está dentro da app: o interruptor só escolhe qual usar.

O problema que resolve é simples. Publicas uma funcionalidade, algo parte-se, e para a tirar tens de fazer outra alteração, outro deploy, talvez esperar pela revisão da loja. Com o interruptor mudas um valor e a funcionalidade desaparece. Depois corriges com calma.

Para quem constrói com o Claude Code, conta ainda mais: publicas muitas vezes e nem sempre leste cada linha. Poder desligar o que acabaste de publicar, sem mexer no código, tira-te muita ansiedade no dia do lançamento.

1. Onde os guardo

Uso uma tabela «flags» na base de dados que a app já usa: uma linha por interruptor, com nome, ligado sim ou não, percentagem e lista de utilizadores. A app lê-a no arranque e de vez em quando enquanto está aberta. Mudo um valor e a funcionalidade liga-se ou desliga-se sem uma versão nova. Quem já tem a app aberta recebe a mudança na leitura seguinte dos flags.

A única condição verdadeira é esta: o valor tem de estar num sítio que consigas mudar sem um deploy. Os caminhos principais são três.

  • Uma tabela na base de dados. É o que eu faço: nada de novo para aprender, e podes mudá-la também a partir de uma página tua.
  • Um serviço dedicado a feature flags ou a configuração remota. Dá-te um painel já pronto, percentagens e estatísticas. Para começar, a tabela chega.
  • Uma variável de ambiente. Serve no servidor, mas em muitos alojamentos mudá-la exige um novo deploy ou um reinício: verifica como o teu alojamento a aplica. Numa app móvel normalmente fica fixada na build.

Primeiro peço ao Claude Code a tabela e um único ponto do código por onde passam todas as perguntas sobre os interruptores, em vez de vinte verificações espalhadas. Com o Claude Code, tudo o que vem a seguir monta-se em poucos minutos.

O prompt
Cria uma tabela flags com nome, ligado sim ou não, percentagem e lista de utilizadores, e uma única função que a app usa para saber se um interruptor está ligado para o utilizador atual.

2. A funcionalidade nasce desligada

Cada funcionalidade nova entra no código já atrás do seu interruptor, desligado.

O prompt
Põe a funcionalidade X atrás de um interruptor chamado nova-x, desligado por defeito, lido da tabela flags.

Depois publico assim. Para os utilizadores não muda nada: o código novo está lá, mas não arranca. É a primeira das três regras: nenhum valor na base de dados consegue fazer aparecer uma funcionalidade que não está na app instalada.

Nas apps para iPhone esta é a segunda regra, e a Apple escreve-a de forma clara: as funcionalidades novas têm de ser descritas nas notas para a revisão e estar acessíveis a quem revê, e as escondidas são proibidas. Se publicares uma funcionalidade desligada, escreve-o nas notas e explica como vê-la.

3. Ligar por fases

Nunca ligo para todos de uma vez. Primeiro para mim, depois para uma dezena de utilizadores, depois para todos.

O prompt
Faz com que nova-x se possa ligar para uma lista de utilizadores e para uma percentagem, sempre a mesma para o mesmo utilizador.

A parte «sempre a mesma para o mesmo utilizador» conta. Se a app tirar à sorte sempre que abre, a mesma pessoa vê a funcionalidade hoje e amanhã não. Normalmente calcula-se um número fixo a partir do id do utilizador: se o número ficar abaixo da percentagem, a funcionalidade está ligada. Assim, quando sobes a percentagem, quem já a tinha continua a tê-la.

Entre uma fase e a seguinte vejo os erros e as mensagens dos utilizadores. Se estiver tudo tranquilo, subo.

4. Se a leitura falhar, fica tudo desligado

O interruptor já é um kill switch: se a funcionalidade der problemas, desligo-a e volta a versão antiga. Mas há um caso em que ninguém pensa: e se a app não conseguir ler os flags? Base de dados lenta, sem rede, uma tabela renomeada por engano.

O prompt
Se a leitura dos flags falhar, considera todos os interruptores desligados e mostra a versão antiga.

Assim, se a leitura falhar, a app mostra sozinha a versão que já funcionava. Ligar tudo quando não sabe é a forma mais rápida de mostrar a todos uma funcionalidade a meio.

Depois há o atraso: quem tem a app aberta vê a funcionalidade até ela voltar a ler os flags, e também os serviços dedicados costumam guardar os valores em cache. Quanto tenho de esperar, pergunto.

O prompt
Diz-me quanto tempo demora, no pior dos casos, até um utilizador com a app já aberta ver um interruptor que acabei de desligar, e onde se decide esse tempo.

5. A tabela só a mudo eu, e no telemóvel

Para não ter de abrir a base de dados no computador, uso uma página de admin no telemóvel. Mas antes da página vem a proteção: esconder um botão não chega. Quem pode alterar a tabela tem de ser decidido pelo servidor; caso contrário, alguém mais esperto liga e desliga as tuas funcionalidades sem passar pela página. Por isso o prompt faz as duas coisas por esta ordem.

O prompt
Protege a tabela flags no servidor: a app pode lê-la, mas só a minha conta a pode alterar. Depois cria uma página de admin, só para mim, com um interruptor para cada linha.

6. A limpeza

Um interruptor ligado para todos há semanas, e que está a correr bem, já não serve. Se ficar lá, é código antigo que ninguém testa, e uma linha que mais cedo ou mais tarde alguém desliga por engano. Além disso, com a regra do ponto 4, se a leitura falhar a app volta à versão antiga até numa funcionalidade que todos usam.

O prompt
Lista os interruptores ligados para todos há mais de um mês e tira-os do código, deixando a funcionalidade ligada.

Antes de publicar, confirmo que a funcionalidade continua ligada e tiro também a linha da tabela.

As três regras

  • O interruptor só liga código que já está na versão publicada: a funcionalidade tem de ser publicada desligada, primeiro.
  • Desligar código já publicado não exige revisão; mas as lojas não aceitam funcionalidades escondidas para ligar depois sem revisão. Os flags servem para desligar e para ligar por fases, não para fazer passar às escondidas funcionalidades novas.
  • Nada de secreto nos flags: quem os lê é a app, por isso quem quiser vê-os.

O nome também conta: «desconto-secreto-parceiros» já diz demasiado. Chaves e palavras-passe ficam no servidor.

Os erros mais comuns

  • Ligar o flag antes de publicar a funcionalidade: não acontece nada, porque o código não está lá. Primeiro publica desligado, depois liga.
  • Considerar ligado um flag que não se consegue ler: se a leitura falhar, o valor certo é desligado.
  • Tirar à sorte sempre que a app abre: a percentagem tem de dar sempre o mesmo resultado para o mesmo utilizador.
  • Proteger só a página de admin: a tabela também só deve aceitar alterações da tua conta.
  • Nunca tirar os interruptores antigos: os que estão ligados para todos há mais de um mês devem sair do código.

Checklist

  • Tabela flags criada, uma única função para a ler
  • A funcionalidade nova está publicada desligada
  • Percentagem estável para o mesmo utilizador
  • Se a leitura falhar, tudo desligado
  • Sei quanto demora a desligar para quem tem a app aberta
  • Só a minha conta pode mudar os flags
  • De vez em quando tiro os flags ligados há mais de um mês

Perguntas frequentes

É o mesmo que voltar à versão anterior?

Não. Voltar atrás põe a app toda como estava, e numa app móvel isso quer dizer outra versão e outra revisão. O interruptor desliga só essa funcionalidade, e o resto fica como está.

Funciona também numa app que já está na loja?

Sim, e é aí que mais ajuda, porque cada versão nova passa pela revisão e nem todos atualizam logo. Mas só vale para o código que já está dentro da versão instalada: um flag não pode acrescentar uma funcionalidade que a app não tem.

← Todos os guias