Antes de configurar, vale alinhar o que é o Azure App Configuration. Ele é um serviço gerenciado da Azure para centralizar configurações e Feature Flags fora do binário da aplicação. A diferença prática em relação ao appsettings.json é direta: você altera um valor no portal e todas as instâncias passam a enxergar a mudança, sem rebuild e sem redeploy.
Em uma aplicação com vinte réplicas atrás de um load balancer, isso deixa de ser conveniência e vira a única forma sã de manter as flags coerentes entre todas elas. Editar vinte appsettings.json na mão, ou redistribuir o serviço inteiro só para virar uma flag, é exatamente a dor que esse serviço existe para resolver.
Como o WithFeatureGate funciona por dentro
Quando implementei o suporte ao WithFeatureGate para Minimal APIs, a meta não era criar uma API diferente da que já existia no MVC. Era preservar o mesmo modelo mental: quem já usava FeatureGateAttribute deveria migrar para Minimal APIs sem reaprender o conceito. Por isso a decisão de design foi apoiar tudo em endpoint filters, não em um middleware global.
E é aí que mora uma dúvida comum: existe um middleware verificando as flags em toda requisição? Não existe, e isso é proposital. O WithFeatureGate não registra nada no pipeline de middlewares. Ele instala um endpoint filter, que só roda quando aquela rota específica é selecionada pelo roteamento. Nenhuma requisição paga o custo de avaliar uma flag de um endpoint que ela nem acessou.
Vamos acompanhar o caminho de uma requisição passo a passo:
A avaliação é delegada à infraestrutura do Feature Management, implementada sobre IVariantFeatureManager, a interface atual da biblioteca. Um detalhe que vale registrar: o gate resolve especificamente o IVariantFeatureManagerSnapshot, e não o manager simples. Na prática, isso significa que, se o handler voltar a checar a mesma flag depois de passar pelo gate, ele enxerga o mesmo resultado, porque o snapshot congela a avaliação pelo tempo de vida da requisição. É o mesmo comportamento que detalho na seção de avaliação assíncrona.
Repare no que acontece quando a flag está desligada: o filtro encerra a requisição com 404, não com 403. A escolha é deliberada. Um 403 confirmaria que o endpoint existe e está apenas bloqueado. O 404 faz o recurso desaparecer do mapa, que é o comportamento esperado para uma funcionalidade que ainda não deveria ser visível. É o mesmo princípio do FeatureGateAttribute no MVC, onde o handler padrão de features desabilitadas também responde 404.
FeatureGate e autorização: quem decide primeiro
Cedo ou tarde você vai escrever isto:
app.MapGet("/api/relatorio", GerarRelatorio)
.RequireAuthorization()
.WithFeatureGate("RelatorioAvancado");
E a pergunta surge na hora: se um usuário não autenticado bater nesse endpoint com a flag desligada, o que ele recebe? 401? 403? 404? E mais: a ordem em que eu encadeio RequireAuthorization e WithFeatureGate muda o resultado?
A resposta tranquiliza, porque ela não depende da ordem do encadeamento. RequireAuthorization e WithFeatureGate atuam em estágios diferentes do pipeline. A autorização é um middleware, que roda logo depois do roteamento. O FeatureGate é um endpoint filter, que roda mais tarde, já dentro da execução do endpoint. Então a sequência real é sempre esta:
Na prática: um usuário sem credencial válida nunca chega a ser avaliado pela flag, ele leva 401 antes disso. Um usuário autenticado mas sem permissão leva 403, também antes da flag. Só quem passa pela autorização é que descobre, via 404, que a funcionalidade está desligada.
Nota: trocar a ordem para
.WithFeatureGate(...).RequireAuthorization()não inverte nada. O middleware de autorização continua rodando antes do endpoint filter, porque essa precedência é definida pela posição de cada um no pipeline do ASP.NET Core, não pela ordem de chamada no builder. Tentar reordenar isso no fluent API é uma armadilha que rende horas de depuração.
A avaliação assíncrona das flags
A chamada IsEnabledAsync parece uma simples leitura de booleano, mas o Async no nome não é decorativo. Quando a flag tem filtros configurados, é nesse momento que cada um deles roda, e todos são assíncronos por natureza:
Nota: um filtro de Targeting pode consultar o
HttpContext, e um filtro customizado seu pode bater em um banco ou em um cache distribuído. Por isso a avaliação éawaitde verdade, não açúcar sintático. Se você precisa que o resultado da flag não mude no meio de uma requisição, mesmo que a configuração seja atualizada nesse intervalo, useIVariantFeatureManagerSnapshot, que congela o primeiro valor avaliado pelo tempo de vida da requisição.
O "tempo real" do refresh, sem a ilusão
A promessa de mudar uma flag e ver o efeito sem redeploy é real, mas a expressão "tempo real" engana. O que existe por baixo é polling, não push. Vale entender o ciclo para não esperar do mecanismo algo que ele não faz:
O intervalo padrão de cache das Feature Flags é de trinta segundos, ajustável via SetRefreshInterval. O ponto fino é que o refresh não bloqueia a requisição que o disparou. Ela ainda serve o valor antigo, e a mudança aparece nas requisições seguintes. Para a maioria dos rollouts isso é mais do que suficiente, mas é bom saber que essa janela existe.
Nota: esse refresh automático vale apenas para Feature Flags, porque
UseFeatureFlags()já as registra para atualização. Key-values comuns do App Configuration não herdam esse comportamento. Para eles você ainda precisa deConfigureRefresh()e, em geral, de uma sentinel key para sinalizar quando recarregar. Misturar os dois modelos mentais é uma fonte clássica do "mas eu mudei a config e nada aconteceu".
Segurança: a role que todo mundo esquece
Um detalhe que derruba muita gente no primeiro deploy: a Managed Identity não nasce com permissão de leitura. Atribuir a identidade ao recurso não basta. Ela precisa da role App Configuration Data Reader no escopo do seu App Configuration. Sem ela, a aplicação autentica mas recebe um 403 ao tentar ler as configurações, e o erro nem sempre é óbvio no log.
Targeting na prática
O TargetingFilter é o único filtro embutido que não entra automaticamente, então lembre de registrá-lo:
builder.Services
.AddFeatureManagement()
.WithTargeting();
O WithTargeting() registra o filtro, mas ele não adivinha quem é o usuário sozinho. Ele depende de uma implementação de ITargetingContextAccessor, responsável por dizer ao filtro qual é o usuário atual e a quais grupos ele pertence. Em aplicações web, a biblioteca já traz um acessor padrão que extrai isso do HttpContext, então o WithTargeting() sem argumentos costuma bastar. Em cenários sem HttpContext, como um worker ou um console, é você quem precisa fornecer esse contexto.
Um exemplo torna o targeting concreto. Imagine liberar uma funcionalidade para dois usuários nomeados, para todo o grupo Ring0, para metade do grupo Ring1 e para uma fração pequena do restante:
{
"id": "RelatorioAvancado",
"enabled": true,
"conditions": {
"client_filters": [
{
"name": "Microsoft.Targeting",
"parameters": {
"Audience": {
"Users": [ "ana", "bruno" ],
"Groups": [
{ "Name": "Ring0", "RolloutPercentage": 100 },
{ "Name": "Ring1", "RolloutPercentage": 50 }
],
"DefaultRolloutPercentage": 5
}
}
}
]
}
}
O caminho da avaliação fica assim:
Repare que a decisão é determinística por usuário. O mesmo usuário cai sempre no mesmo lado do percentil, porque o cálculo usa um hash estável do identificador. Um rollout de cinco por cento não sorteia gente nova a cada requisição, ele escolhe os mesmos cinco por cento de forma consistente. É isso que permite aumentar a fatia aos poucos sem nunca tirar o acesso de quem já estava dentro.
Do interruptor binário às variants
Tudo o que vimos até aqui trata a flag como um interruptor binário: ligada ou desligada. Mas o mesmo IVariantFeatureManager que avalia IsEnabledAsync também expõe GetVariantAsync, e é aí que mora o próximo nível. Em vez de responder apenas sim ou não, uma variant feature flag devolve uma configuração inteira, um tamanho, uma cor, um algoritmo, alocada por usuário, grupo ou percentil. É o que transforma Feature Flags em A/B testing de verdade. Fica para um próximo artigo, mas quando você cruzar com GetVariantAsync na assinatura de IVariantFeatureManager, já sabe de onde vem.
Quando NÃO usar Feature Flags
Feature Flags resolvem tão bem o problema de rollout que a tentação é usá-las para tudo. Vale traçar a fronteira, porque cada uso fora do propósito vira dívida técnica disfarçada de flexibilidade.
Evite Feature Flags para:
- Autorização e permissões: decidir se um usuário pode acessar algo é papel do sistema de autorização, com claims e policies. Uma flag não conhece o usuário de forma estruturada, e tratar permissão como flag espalha regra de acesso por lugares que ninguém vai auditar.
- Multi-tenancy: a separação entre tenants é arquitetura, não configuração. Resolver isso com uma flag por tenant não escala e abre brechas sutis de isolamento.
- Configuração permanente: se um valor nunca vai voltar a mudar, ele não é uma flag, é uma constante ou uma configuração comum. Flag pressupõe que existe uma decisão de ligar e desligar.
- Parâmetros de negócio: limites, taxas e prazos que mudam por regra de negócio pertencem ao domínio, não a um interruptor de release.
A regra que cabe em uma frase: Feature Flags servem para controlar a exposição de código ao longo do tempo, rollout e experimentação. No instante em que você usa uma para decidir quem pode o quê, ou para guardar um valor que define o comportamento do negócio, ela parou de ser uma flag e virou a ferramenta errada no lugar errado.
Conclusão
Fechamos o ciclo, do problema de separar deploy de lançamento até os detalhes de quem decide o quê dentro do pipeline. Vimos o Azure App Configuration centralizar as flags, o WithFeatureGate barrar o endpoint com um endpoint filter antes do handler, a autorização rodar antes dele, o refresh por polling com sua janela de trinta segundos, o targeting determinístico por usuário e a fronteira do que não deveria virar flag.
A implementação não é difícil, mas os detalhes importam, e quase todos eles moram fora do happy path: o 404 em vez de 403, a role de leitura que a Managed Identity não ganha de graça, o refresh que não é instantâneo e a tentação de resolver autorização com uma flag. Acertar esses pontos é o que separa o uso ingênuo do uso sólido.
No fundo, Feature Flags não servem só para esconder código novo. Elas separam a entrega técnica da decisão de negócio. O deploy continua sendo responsabilidade da engenharia, o momento do lançamento vira uma decisão operacional, e desligar uma funcionalidade deixa de ser um novo deploy para virar o toque de um botão.
Na próxima vez que você for amarrar o lançamento de uma feature ao próximo deploy, pare um instante: talvez o que falte não seja subir código mais rápido, e sim parar de subir código para tomar decisões que já poderiam ser apenas um clique.





Top comments (0)