MÓDULO 08 · CONCEITO 05 DE 14

Feature Flags & Progressive Delivery

Deploy sem release, rollback em segundos, e controle de risco em produção — o que separa CI/CD de verdadeiro deployment pipeline

Tempo de leitura ~23 min Pré-requisito 04 · Graceful degradation Próximo 06 · Chaos engineering — fundamentos

Em 2009, John Allspaw e Paul Hammond apresentaram no Velocity Conference "10+ Deploys per Day: Dev and Ops Cooperation at Flickr". A apresentação foi um marco: pela primeira vez, uma empresa grande demonstrava que deploy frequente e operação estável não são opostos — são complementares. Um dos mecanismos centrais que tornava isso possível eram os feature flags: a separação entre o ato de fazer deploy de código e o ato de ativar uma feature para usuários.

Feature flags (também chamados de feature toggles ou feature switches) são condicionais em runtime que controlam se uma feature está ativa para um usuário, grupo de usuários, ou porcentagem do tráfego. O código está em produção — compilado, testado, deployado — mas a feature só ativa quando o flag é ligado. Isso inverte a relação entre deploy e release: você pode fazer deploy de código incompleto, fazer deploy de código de alto risco sem ativá-lo, e ativar features gradualmente em vez de tudo de uma vez.

Pete Hodgson, em seu ensaio "Feature Toggles (aka Feature Flags)" (martinfowler.com, 2017), formalizou a taxonomia de flags em quatro categorias: release toggles (controle de deploy vs release), experiment toggles (A/B testing), ops toggles (controle operacional em runtime), e permission toggles (controle de acesso por usuário/tenant). Este conceito foca nos release toggles e ops toggles — os mais relevantes para disponibilidade e resiliência — e nos padrões de progressive delivery que os release toggles habilitam.

Release toggles — separando deploy de release

Um release toggle permite que código seja deployado para produção sem ser exposto aos usuários. Isso é incrivelmente valioso por várias razões:

Primeiro, permite que branches de vida longa sejam eliminadas. Em vez de manter uma branch de feature separada por semanas até a feature estar "pronta", o código vai para main em partes menores, protegido por flag. O risco de integração — que cresce exponencialmente com o tempo de divergência — é eliminado.

Segundo, permite rollback em segundos. Se uma feature ativada em produção causa problemas, desligar o flag é instantâneo. Não é preciso reverter commit, não é preciso rodar novo deploy, não é preciso aguardar pipeline de CI/CD. O flag vai para false e a feature desaparece imediatamente.

Terceiro, permite testar em produção com usuários reais antes do release completo. "Internal" flags para funcionários da empresa, canary flags para 1% dos usuários, flags por cohort de usuário beta — todos permitem validação com tráfego real antes do release amplo.

armadilha de adoção

Feature flags acumulam dívida técnica se não forem removidos após o release. Um codebase com 200 flags antigos é difícil de raciocinar, difícil de testar (2^n combinações), e caro de manter. A disciplina de lifecycle de flags é tão importante quanto a disciplina de criá-los: defina data de expiração no momento da criação, revise flags mensalmente, remova flags de features completamente deployadas.

Ops toggles — degradação proativa em runtime

Ops toggles diferem de release toggles em propósito e lifecycle. Um release toggle é temporário — existe enquanto o release está em progresso, depois é removido. Um ops toggle pode ser permanente — é um mecanismo de controle operacional que pode permanecer no sistema indefinidamente como kill switch.

Os casos de uso de ops toggles são diretamente relevantes para resiliência:

Kill switches: um flag que desliga uma feature pesada durante picos de tráfego. "Recomendações personalizadas ligadas" pode ser desligado como preparação para Black Friday, reduzindo carga no serviço de ML que provê as recomendações. O flag é religado após o pico.

Circuit breakers manuais: quando um serviço dependente está degradado e o circuit breaker automático não foi ativado ainda (por estar abaixo do threshold), um operador pode ligar manualmente um flag que força o fallback para toda a feature que depende daquele serviço.

Degradação preventiva antes de manutenção: antes de uma janela de manutenção planejada no serviço de analytics, ligar um flag que desabilita todos os eventos de analytics sendo enviados. Os eventos se acumulam em buffer ou são descartados — dependendo da política — sem nenhum erro para o usuário.

Progressive delivery — canary e blue-green

Progressive delivery é o conjunto de técnicas para ativar uma mudança gradualmente em produção, observando métricas em cada etapa e revertendo antes que o problema se propague. Feature flags são o mecanismo; progressive delivery é o processo.

Canary release: a feature (ou versão nova do serviço) é ativada para uma pequena porcentagem do tráfego — tipicamente 1%, depois 5%, depois 10%, depois 50%, depois 100%. Em cada etapa, métricas (taxa de erro, latência, SLO consumption) são comparadas com o baseline. Se as métricas pioram além de um threshold, o canary é abortado e o tráfego volta 100% para a versão anterior. A metáfora é literal: o canário vai à frente na mina — se morrer, os mineiros recuam.

Blue-green deployment: em vez de redirecionar tráfego gradualmente, o sistema mantém duas versões em paralelo (blue = produção atual, green = nova versão). O tráfego é 100% blue até que green esteja pronto — então o switch é instantâneo (load balancer redireciona tudo para green). Rollback é igualmente instantâneo (switch de volta para blue). Blue-green é mais simples de implementar que canary mas tem custo maior: dois ambientes completos em paralelo durante o switchover.

Feature flag com percentual: mais granular que blue-green, mais simples de implementar que canary de infra. O código novo está em produção em todas as instâncias, mas só ativa para X% dos usuários. Os usuários no grupo "control" continuam vendo o comportamento antigo; os do grupo "treatment" veem o novo. Métricas por cohort permitem comparar.

Técnica Granularidade Rollback Custo de infra Quando usar
Feature flag percentual Por usuário/request Instantâneo (flip flag) Baixo Features de produto, mudanças de comportamento
Canary release Por instância/serviço Redirecionar tráfego Médio Mudanças de infra, versões de serviço
Blue-green Ambiente completo Instantâneo (switch LB) Alto (2 ambientes) Deploys com zero downtime, migração de banco

Plataformas de feature flags

LaunchDarkly: a plataforma comercial mais estabelecida. SDK em dezenas de linguagens, avaliação de flags em memória (sem latência de rede por flag), targeting por atributos de usuário (plano, país, cohort), auditoria de quem mudou qual flag quando. Caro para times pequenos, mas amortiza rápido para times que fazem deploys frequentes.

OpenFeature: especificação aberta (CNCF, 2022) que padroniza a interface de SDK de feature flags, agnóstica de provider. Com OpenFeature, o código usa uma API unificada e o provider pode ser trocado (LaunchDarkly, Flagsmith, Unleash) sem mudar o código. Resolve o lock-in de plataforma.

Flagsmith: open-source, self-hosted ou cloud, com SDK para as principais linguagens. Bom para times que precisam de controle de dados (conformidade, regulatório) ou que querem evitar dependência de terceiro.

Unleash: open-source, focado em progressive delivery. Feature ativa/inativa + estratégias de targeting (porcentagem, usuário, ambiente). Bem integrado com Kubernetes via operador.

C# — OpenFeature com LaunchDarkly provider
// setup — inicialização única no startup
using OpenFeature;
using OpenFeature.Contrib.Providers.LaunchDarkly;

var provider = new LaunchDarklyProvider(
    sdkKey: config["LaunchDarkly:SdkKey"]);
await Api.Instance.SetProviderAsync(provider);

// avaliação de flag em qualquer serviço
public class CheckoutService
{
    private readonly IFeatureClient _flags;

    public CheckoutService(IFeatureClient flags)
        => _flags = flags;

    public async Task<OrderResult> PlaceOrderAsync(
        Order order, Customer customer, CancellationToken ct)
    {
        var ctx = EvaluationContext.Builder()
            .Set("userId", customer.Id)
            .Set("plan", customer.Plan.ToString())
            .Set("country", customer.Country)
            .Build();

        // Ops toggle — degradar recomendações proativamente
        var withRecs = await _flags.GetBooleanValueAsync(
            "checkout.show_recommendations", false, ctx, ct);

        // Release toggle — nova experiência para 10% dos usuários
        var newCheckout = await _flags.GetBooleanValueAsync(
            "checkout.new_flow_v2", false, ctx, ct);

        return newCheckout
            ? await ProcessNewFlowAsync(order, withRecs, ct)
            : await ProcessLegacyFlowAsync(order, withRecs, ct);
    }
}

OpenFeature tem SDK oficial em .NET: OpenFeature NuGet. O contexto de avaliação permite targeting por atributos do usuário sem amarrar a lógica de targeting ao código do serviço.

Python — Flagsmith SDK com fallback local
from flagsmith import Flagsmith
from flagsmith.models import Flags

# inicialização — uma vez no startup
flagsmith = Flagsmith(
    environment_key=settings.FLAGSMITH_KEY,
    enable_local_evaluation=True,  # avaliação offline em cache
    environment_refresh_interval_seconds=60,
)


class CheckoutService:
    def __init__(self, flagsmith: Flagsmith):
        self._flags = flagsmith

    async def place_order(
        self, order: Order, customer: Customer
    ) -> OrderResult:
        # Flags avaliadas por identidade de usuário
        flags: Flags = await asyncio.to_thread(
            self._flags.get_identity_flags,
            identifier=str(customer.id),
            traits={"plan": customer.plan, "country": customer.country},
        )

        # Ops toggle — kill switch para recomendações
        show_recs = flags.is_feature_enabled("checkout_recommendations")

        # Release toggle — nova versão do checkout
        new_flow = flags.is_feature_enabled("checkout_new_flow_v2")

        if new_flow:
            return await self._process_new_flow(order, show_recs)
        return await self._process_legacy(order, show_recs)

    async def _process_new_flow(self, order, show_recs):
        # implementação do novo checkout
        ...

enable_local_evaluation=True baixa as regras de targeting e avalia localmente — latência de flag <1ms, sem depender de rede. Imprescindível para flags em hot paths.

Go — Unleash SDK com avaliação local
import unleash "github.com/Unleash/unleash-client-go/v4"

func init() {
    unleash.Initialize(
        unleash.WithUrl(os.Getenv("UNLEASH_URL")),
        unleash.WithAppName("checkout-service"),
        unleash.WithCustomHeaders(http.Header{
            "Authorization": {os.Getenv("UNLEASH_TOKEN")},
        }),
        unleash.WithRefreshInterval(30*time.Second),
        // cache local — avaliação sem rede
    )
    unleash.WaitForReady()
}

type CheckoutService struct{}

func (s *CheckoutService) PlaceOrder(
    ctx context.Context,
    order Order,
    customer Customer,
) (*OrderResult, error) {
    ctx2 := unleash.WithContext(ctx, unleash.Context{
        UserId:     customer.ID,
        Properties: map[string]string{
            "plan":    customer.Plan,
            "country": customer.Country,
        },
    })

    showRecs := unleash.IsEnabled("checkout.recommendations",
        unleash.WithContext(ctx2))
    newFlow := unleash.IsEnabled("checkout.new_flow_v2",
        unleash.WithContext(ctx2))

    if newFlow {
        return s.processNewFlow(ctx, order, showRecs)
    }
    return s.processLegacy(ctx, order, showRecs)
}

Unleash com SDK Go avalia flags localmente com regras sincronizadas periodicamente. WaitForReady() no init garante que o cache está populado antes de servir tráfego.

Feature flags e testes

Feature flags complicam testes: agora o comportamento de uma função muda dependendo de qual flag está ativo. A estratégia mais simples é isolar o ponto de decisão de flag em uma camada fina e injetável, e testar cada branch separadamente — sem depender de infraestrutura de flag nos testes unitários.

// Teste sem dependência de flag infrastructure
[Fact]
public async Task PlaceOrder_NewFlow_UsesNewLogic()
{
    var flags = new Mock<IFeatureClient>();
    flags.Setup(f => f.GetBooleanValueAsync("checkout.new_flow_v2",
        false, It.IsAny<EvaluationContext>(), default))
        .ReturnsAsync(true);

    var svc = new CheckoutService(flags.Object, ...);
    var result = await svc.PlaceOrderAsync(order, customer);

    Assert.Equal("new_flow", result.ProcessedBy);
}

Kill switches e responsabilidade operacional

Todo ops toggle que pode degradar o sistema precisa de documentação operacional: quem pode ligar, quem pode desligar, o que acontece quando o flag muda em cada direção, e qual é o procedimento de rollback. Um kill switch não documentado é um risco operacional — em incidentes de alta pressão, operadores ativam flags sem entender as consequências.

A melhor prática é manter um runbook de flags operacionais: nome do flag, propósito, efeito quando ativado, efeito quando desativado, owner, data de revisão. Isso transforma o flag de "segredo técnico" em ferramenta operacional compartilhada.

Como praticar

  1. Implementar um kill switch operacional. Escolha uma feature que você quer poder desligar em segundos em caso de problema. Implemente um ops toggle usando OpenFeature + um provider leve (Flagsmith ou Unleash self-hosted). Documente o runbook do flag: o que acontece quando ligado vs desligado, quem pode mudar, como verificar que o toggle está funcionando. Teste o rollback em staging.
  2. Simular um canary release. Em staging, configure uma feature com flag percentual: 0% → 10% → 50% → 100%. Para cada etapa, defina uma métrica de saúde (taxa de erro, latência P99) e um threshold de abort. Execute as etapas manualmente, injetando um erro na feature no passo de 10% para verificar que o rollback funciona.
  3. Auditar flags existentes. Liste todos os feature flags no codebase (grep de flags hardcoded, variáveis de config, toggles em banco). Classifique: release toggle (temporário), ops toggle (permanente), ou flag obsoleto (para remover). Para cada flag obsoleto, crie task de remoção. Isso calibra a intuição sobre dívida técnica de flags.

Referências para aprofundar

  1. artigo Feature Toggles (aka Feature Flags) — Pete Hodgson (martinfowler.com, 2017). A taxonomia canônica de feature toggles. Leitura obrigatória — cobre todos os tipos, trade-offs, e armadilhas em profundidade.
  2. artigo 10+ Deploys a Day: Dev and Ops Cooperation at Flickr — Allspaw & Hammond (Velocity, 2009). A apresentação que popularizou feature flags como mecanismo de continuous delivery. Slides disponíveis em slideshare.
  3. docs OpenFeature Specification — CNCF, openfeature.dev. Especificação técnica e SDKs da API aberta de feature flags. Inclui guia de migração de providers e exemplos de implementação.
  4. docs LaunchDarkly — Feature Management — launchdarkly.com/docs. Documentação da plataforma mais usada comercialmente. Útil para entender o estado da arte em feature management.
  5. artigo Progressive Delivery Explained — James Governor (RedMonk, 2018). O artigo que definiu o termo "progressive delivery" e contextualizou canary, blue-green e feature flags como partes do mesmo movimento.
  6. livro Continuous Delivery — Jez Humble & David Farley (Addison-Wesley, 2010). Capítulos sobre deployment pipeline e feature toggles — base teórica para separar deploy de release.
  7. artigo The Feature Flag Lifecycle — Honeycomb Engineering Blog, 2022. Como gerenciar o lifecycle completo de feature flags — criação, auditoria, remoção. Aborda a dívida técnica de flags acumulados.
  8. artigo How Etsy Deploys 50+ Times per Day — Etsy Engineering Blog. Caso real de operação com feature flags em escala — incluindo o sistema caseiro da Etsy antes de plataformas comerciais existirem.
  9. vídeo Feature Flags at Scale — GOTO Conferences, 2021. YouTube. Como Facebook, Netflix e Spotify usam feature flags em escala — com desafios de consistência e observabilidade.
  10. docs Unleash — Feature Toggle Service — docs.getunleash.io. Documentação do Unleash open-source — estratégias de targeting, SDKs, e integração com Kubernetes.
  11. artigo Testing with Feature Flags — ThoughtWorks, 2021. Como testar código com feature flags sem explodir o número de casos de teste — isolation patterns e test doubles para flags.
  12. artigo Blue-Green Deployments — Martin Fowler (martinfowler.com, 2010). O artigo original que formalizou o padrão blue-green. Conciso, preciso, ainda relevante.