DEV Community

Guilherme Marucchi
Guilherme Marucchi

Posted on

# Feature Flags em React/Next.js: Guia Completo com ConfigCat e Context API

Introdução

Se você já passou por alguma dessas situações, este artigo é pra você:

  • Precisou fazer rollback de um deploy e isso bloqueou toda a equipe de subir código
  • Teve conflitos intermináveis na branch develop porque várias features estavam sendo desenvolvidas ao mesmo tempo
  • Quis testar uma funcionalidade em produção apenas com um grupo específico de usuários antes de liberar pra todos
  • Descobriu um bug crítico em produção e precisou esperar um novo deploy pra resolver

Feature Flags (ou Feature Toggles) resolvem todos esses problemas. Neste guia, vou mostrar passo a passo como implementei feature flags em uma aplicação Next.js usando ConfigCat como serviço externo e React Context API como camada de abstração — uma arquitetura desacoplada, escalável e pronta pra produção.


O que são Feature Flags?

Feature Flags são interruptores que permitem ligar e desligar funcionalidades da sua aplicação sem precisar fazer deploy. O código da feature já está em produção, mas só é executado quando a flag está ativa.

Pense assim: é como ter um painel de controle onde você decide, em tempo real, o que cada usuário vê na aplicação.

O que você ganha com isso?

  • Deploy contínuo sem medo — suba o código com a feature desligada
  • Teste em produção com segurança — ligue apenas pro seu usuário
  • Kill switch instantâneo — desligou a flag, a feature some em segundos
  • Rollout gradual — libere pra 10%, depois 50%, depois 100%
  • Sem conflitos de branch — todo mundo sobe pra main/develop, a flag controla quem vê o quê

Por que ConfigCat?

Existem várias ferramentas de feature flags no mercado (LaunchDarkly, Unleash, Split.io). Escolhi o ConfigCat por:

  1. Interface intuitiva — dashboard simples e direto ao ponto
  2. Suporte nativo a React — SDK com Provider e hooks prontos
  3. Targeting avançado — controle por usuário, atributos customizados, porcentagem
  4. Plano gratuito generoso — suficiente pra começar
  5. Polling automático — atualiza os valores das flags periodicamente sem você precisar fazer nada

Arquitetura da Solução

A decisão mais importante da implementação: não acoplar a aplicação ao ConfigCat.

Se amanhã você precisar trocar de ferramenta, só altera o Provider. Nenhum componente da aplicação sabe que o ConfigCat existe.

Estrutura de pastas

src/
├── utils/consts/
│   └── featureFlags.ts          # Constantes das flags + SDK keys
├── providers/feature-flags/
│   └── featureFlagsContext.tsx   # Context + Provider + sincronização
└── pages/
    └── _app.tsx                 # Provider na árvore de componentes
Enter fullscreen mode Exit fullscreen mode

Fluxo da arquitetura

ConfigCat (serviço externo)
    ↓ polling a cada N segundos
ConfigCatReactProvider (engine — busca, cache, polling)
    ↓
ConfigCatSync (sincroniza user + busca flags boolean e string + escuta mudanças)
    ↓ armazena no useState
FeatureFlagsContext.Provider (Context próprio do projeto)
    ↓ expõe via useContext
Componentes (consomem com getFlag ou getStringFlag, não sabem que ConfigCat existe)
Enter fullscreen mode Exit fullscreen mode

Implementação Passo a Passo

1. Instalação

npm install configcat-react
Enter fullscreen mode Exit fullscreen mode

2. Constantes tipadas

Primeiro, crie as constantes das flags e as SDK keys. A implementação suporta dois tipos de flags: boolean (liga/desliga) e string (valor variável, útil para A/B testing e conteúdo dinâmico).

// src/utils/consts/featureFlags.ts

export const featureFlags = {
  SHOW_DASHBOARD: 'showDashboard',
  ENABLE_NEW_CHECKOUT: 'enableNewCheckout'
} as const

export const stringFeatureFlags = {
  ACTIVE_PROMOTION: 'activePromotion'
} as const

export type FeatureFlagKey = (typeof featureFlags)[keyof typeof featureFlags]
export type StringFeatureFlagKey = (typeof stringFeatureFlags)[keyof typeof stringFeatureFlags]

export const configCatSdkKeys = {
  TEST: '<your-configcat-sdk-key-test>',
  PRODUCTION: '<your-configcat-sdk-key-production>'
} as const
Enter fullscreen mode Exit fullscreen mode

Por que tipar? O TypeScript garante que você não vai digitar o nome errado de uma flag. Se tentar usar uma string que não existe no objeto, o compilador avisa.

Por que dois objetos separados? Flags boolean e string têm contratos de retorno diferentes — { isEnabled, isLoading } vs { value, isLoading }. Separar os objetos permite tipar cada método (getFlag e getStringFlag) de forma precisa, sem abrir mão de type safety.

Por que as SDK keys estão no código e não em .env? As SDK keys do ConfigCat são públicas por design — elas só permitem leitura das flags, não alteração. Colocá-las no código junto com uma função de detecção de ambiente (como veremos a seguir) elimina a necessidade de gerenciar arquivos .env por ambiente.

Agora exporte as constantes no barrel do projeto pra que fiquem acessíveis:

// src/utils/consts/index.ts
import { featureFlags, stringFeatureFlags, configCatSdkKeys } from './featureFlags'

const Consts = {
  // ... outras constantes do projeto
  featureFlags,
  stringFeatureFlags,
  configCatSdkKeys
}

export default Consts
Enter fullscreen mode Exit fullscreen mode

E exporte os tipos:

// src/utils/index.ts
export type { FeatureFlagKey, StringFeatureFlagKey } from './consts/featureFlags'
Enter fullscreen mode Exit fullscreen mode

3. Detecção de ambiente

Em vez de depender de variáveis de ambiente (.env.local, .env.production), use uma função que detecta o ambiente em runtime:

// src/lib/getIsProduction.ts

const hostsProduction = [
  'app.meudominio.com.br',
  'app-stg.meudominio.com.br'
]

const getIsProduction = () =>
  process.env.BUILD_ENV === 'production' ||
  (typeof window !== 'undefined' &&
    hostsProduction.includes(window.location.host))

export default getIsProduction
Enter fullscreen mode Exit fullscreen mode

Isso permite que o Provider escolha a SDK key correta automaticamente, sem depender de arquivos .env.

4. O Provider — coração da implementação

Aqui é onde tudo acontece. O Provider tem 3 responsabilidades:

  1. Inicializar o ConfigCat com a SDK key correta
  2. Sincronizar os dados do usuário logado com o ConfigCat (pra targeting)
  3. Buscar todas as flags e expor via Context próprio do projeto
// src/providers/feature-flags/featureFlagsContext.tsx
'use client'

import {
  createContext,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useState
} from 'react'

import {
  ConfigCatProvider as ConfigCatReactProvider,
  useConfigCatClient,
  User
} from 'configcat-react'

import { getIsProduction } from 'lib'

import { Consts, FeatureFlagKey, StringFeatureFlagKey } from 'utils'

import { UserContext } from 'providers/user/userContext'

type FlagState = {
  value: boolean | string
  loading: boolean
}

type FeatureFlagsContextProps = {
  flags: Record<string, FlagState>
  getFlag: (key: FeatureFlagKey) => { isEnabled: boolean; isLoading: boolean }
  getStringFlag: (key: StringFeatureFlagKey) => { value: string; isLoading: boolean }
}

const defaultFlagState = { isEnabled: false, isLoading: true }
const defaultStringFlagState = { value: '', isLoading: true }

export const FeatureFlagsContext = createContext<FeatureFlagsContextProps>({
  flags: {},
  getFlag: () => defaultFlagState,
  getStringFlag: () => defaultStringFlagState
})
Enter fullscreen mode Exit fullscreen mode

Detalhe importante: o defaultFlagState retorna isEnabled: false e isLoading: true. O defaultStringFlagState retorna value: '' e isLoading: true. Enquanto as flags não carregaram, nenhuma feature controlada por flag aparece — comportamento seguro por padrão.

ConfigCatSync — o componente que faz a mágica

const ConfigCatSync = ({ children }: { children: React.ReactNode }) => {
  const { user } = useContext(UserContext)
  const client = useConfigCatClient()
  const [flags, setFlags] = useState<Record<string, FlagState>>({})

  // Sincroniza dados do usuário logado com o ConfigCat
  useEffect(() => {
    if (!client) return

    if (user) {
      const configCatUser = new User(user.email, user.email, undefined, {
        role: user.role || '',
        plan: user.plan || '',
        is_admin: user.isAdmin ? '1' : '0'
        // Adicione quantos atributos precisar pro targeting
      })

      client.setDefaultUser(configCatUser)
    } else {
      client.clearDefaultUser()
    }
  }, [client, user])

  // Busca todas as flags e escuta mudanças
  useEffect(() => {
    if (!client) return

    const fetchAllFlags = async () => {
      const booleanKeys = Object.values(Consts.featureFlags)
      const stringKeys = Object.values(Consts.stringFeatureFlags)

      const booleanEntries = await Promise.all(
        booleanKeys.map(async (key) => {
          const value = await client.getValueAsync(key, false)
          return [key, { value: Boolean(value), loading: false }] as const
        })
      )

      const stringEntries = await Promise.all(
        stringKeys.map(async (key) => {
          const value = await client.getValueAsync(key, '')
          return [key, { value: String(value), loading: false }] as const
        })
      )

      setFlags(Object.fromEntries([...booleanEntries, ...stringEntries]))
    }

    fetchAllFlags()

    const handler = () => {
      fetchAllFlags()
    }

    client.on('configChanged', handler)

    return () => {
      client.off('configChanged', handler)
    }
  }, [client, user])

  const getFlag = useCallback(
    (key: FeatureFlagKey) => {
      const flag = flags[key]
      if (!flag) return defaultFlagState
      return { isEnabled: Boolean(flag.value), isLoading: flag.loading }
    },
    [flags]
  )

  const getStringFlag = useCallback(
    (key: StringFeatureFlagKey) => {
      const flag = flags[key]
      if (!flag) return defaultStringFlagState
      return { value: String(flag.value), isLoading: flag.loading }
    },
    [flags]
  )

  const contextValue = useMemo(
    () => ({ flags, getFlag, getStringFlag }),
    [flags, getFlag, getStringFlag]
  )

  return (
    <FeatureFlagsContext.Provider value={contextValue}>
      {children}
    </FeatureFlagsContext.Provider>
  )
}
Enter fullscreen mode Exit fullscreen mode

O que está acontecendo aqui:

  • O primeiro useEffect sincroniza os dados do usuário logado com o ConfigCat. Isso permite targeting por atributos (ex: "mostrar só pra admins")
  • O segundo useEffect busca todas as flags de uma vez — boolean e string separadamente — e armazena no useState. Note que user faz parte das dependências: quando o usuário troca (ex: login/logout), as flags são rebuscadas automaticamente com o novo contexto de targeting
  • O client.on('configChanged', handler) escuta mudanças: quando o ConfigCat detecta uma atualização via polling, o handler rebusca todas as flags automaticamente, sem refresh da página
  • O useMemo no contextValue evita re-renders desnecessários nos componentes consumidores

O Provider principal

type FeatureFlagsProviderProps = {
  children: React.ReactNode
}

const FeatureFlagsProvider = ({ children }: FeatureFlagsProviderProps) => {
  const sdkKey = getIsProduction()
    ? Consts.configCatSdkKeys.PRODUCTION
    : Consts.configCatSdkKeys.TEST

  return (
    <ConfigCatReactProvider
      sdkKey={sdkKey}
      options={{ pollIntervalSeconds: 3600 }}
    >
      <ConfigCatSync>{children}</ConfigCatSync>
    </ConfigCatReactProvider>
  )
}

export default FeatureFlagsProvider
Enter fullscreen mode Exit fullscreen mode

Sobre o pollIntervalSeconds: o padrão do ConfigCat é 60 segundos. Usamos 3600 (1 hora) porque, na maioria dos casos, flags não precisam propagar imediatamente — e intervalos curtos consomem quota do plano desnecessariamente. Se o seu cenário exigir propagação mais rápida (ex: kill switch urgente), reduzir para 60 ou 120 é razoável. O importante é calibrar conforme a necessidade real, não usar o menor valor possível.

5. Adicionar na árvore de componentes

// src/pages/_app.tsx
import FeatureFlagsProvider from 'providers/feature-flags/featureFlagsContext'

function App({ Component, pageProps }) {
  return (
    <UserProvider>
      <FeatureFlagsProvider>
        <Component {...pageProps} />
      </FeatureFlagsProvider>
    </UserProvider>
  )
}
Enter fullscreen mode Exit fullscreen mode

Ordem importa: o FeatureFlagsProvider precisa estar dentro do UserProvider, porque o ConfigCatSync consome o UserContext pra sincronizar os dados do usuário.


Configurando no Dashboard do ConfigCat

Criando uma flag boolean

  1. Acesse o dashboard do ConfigCat
  2. Crie uma feature flag do tipo On/Off Toggle com o nome exato que você definiu na constante (ex: showDashboard)
  3. Configure o valor padrão como OFF

Imagem do dashboard do ConfigCat mostrando a criação de uma flag boolean

Criando uma flag string

  1. No dashboard, crie uma feature flag do tipo Text
  2. Use o nome exato da constante em stringFeatureFlags (ex: activePromotion)
  3. Configure o valor padrão como string vazia ou um valor seguro

Imagem do dashboard do ConfigCat mostrando a criação de uma flag string

Configurando ambientes

O ConfigCat separa ambientes por SDK key. Cada ambiente tem sua própria key:

  • Test/Development — use a key de teste
  • Production — use a key de produção

Isso significa que você pode ter a flag ON em teste e OFF em produção ao mesmo tempo.

Imagem dos ambientes Test e Production


Consumindo nos Componentes

Exemplo 1: Controlar visibilidade de um item no menu

import { useContext, useMemo } from 'react'

import { Consts } from 'utils'

import { FeatureFlagsContext } from 'providers/feature-flags/featureFlagsContext'

const Menu = ({ loggedUser }) => {
  const { getFlag } = useContext(FeatureFlagsContext)
  const { isEnabled: showDashboard } = getFlag(
    Consts.featureFlags.SHOW_DASHBOARD
  )

  const menuItems = useMemo(() => {
    const base = [
      { label: 'Parcelas', url: '/app/parcelas' },
      { label: 'Recibos', url: '/app/recibos' }
    ]

    if (loggedUser.isAdmin && showDashboard) {
      base.unshift({ label: 'Dashboard', url: '/app/dashboard' })
    }

    return base
  }, [loggedUser, showDashboard])

  return (
    <nav>
      <ul>
        {menuItems.map((item) => (
          <li key={item.url}>
            <a href={item.url}>{item.label}</a>
          </li>
        ))}
      </ul>
    </nav>
  )
}
Enter fullscreen mode Exit fullscreen mode

Perceba: o componente importa FeatureFlagsContext — não sabe nada sobre ConfigCat. Se amanhã trocar de ferramenta, este componente não muda.

Exemplo 2: Proteger uma página inteira com redirect

import { useContext, useEffect } from 'react'

import { useRouter } from 'next/router'

import { Consts } from 'utils'

import { FeatureFlagsContext } from 'providers/feature-flags/featureFlagsContext'

const DashboardPage = () => {
  const { push } = useRouter()
  const { getFlag } = useContext(FeatureFlagsContext)
  const { isEnabled: showDashboard, isLoading } = getFlag(
    Consts.featureFlags.SHOW_DASHBOARD
  )

  useEffect(() => {
    if (!isLoading && !showDashboard) {
      push('/app')
    }
  }, [isLoading, showDashboard, push])

  if (isLoading) return null

  return <div>Conteúdo do Dashboard</div>
}
Enter fullscreen mode Exit fullscreen mode

Por que checar isLoading? Sem essa checagem, o componente faria redirect imediatamente no primeiro render (porque isEnabled começa como false enquanto as flags carregam). O isLoading garante que o redirect só acontece depois que o ConfigCat respondeu.

Exemplo 3: Exibição condicional de um componente

import { useContext } from 'react'

import { Consts } from 'utils'

import { FeatureFlagsContext } from 'providers/feature-flags/featureFlagsContext'

const HomePage = () => {
  const { getFlag } = useContext(FeatureFlagsContext)
  const { isEnabled: enableNewCheckout } = getFlag(
    Consts.featureFlags.ENABLE_NEW_CHECKOUT
  )

  return (
    <div>
      <h1>Bem-vindo</h1>
      {enableNewCheckout ? <NewCheckout /> : <LegacyCheckout />}
    </div>
  )
}
Enter fullscreen mode Exit fullscreen mode

Exemplo 4: Flag string para conteúdo dinâmico

Flags string são ideais quando você precisa não só de ligar/desligar, mas de controlar qual variante exibir — A/B testing, banners sazonais, temas, e afins.

import { useContext } from 'react'

import { Consts } from 'utils'

import { FeatureFlagsContext } from 'providers/feature-flags/featureFlagsContext'

const PromoBanner = () => {
  const { getStringFlag } = useContext(FeatureFlagsContext)
  const { value: activePromotion, isLoading } = getStringFlag(
    Consts.stringFeatureFlags.ACTIVE_PROMOTION
  )

  if (isLoading || !activePromotion) return null

  return <Banner type={activePromotion} />
}
Enter fullscreen mode Exit fullscreen mode

No dashboard do ConfigCat, você configura o valor da flag como 'black-friday', 'cyber-monday' ou string vazia (desligado). Zero deploy pra trocar a campanha ativa.


User Targeting — A Mágica de Verdade

A parte mais poderosa das feature flags é o targeting. Com os dados do usuário sincronizados no ConfigCatSync, você pode criar regras no dashboard como:

Targeting por email (testar com seu próprio usuário)

IF User.email IS ONE OF ["dev@empresa.com"]
THEN serve: ON
To everyone else: OFF
Enter fullscreen mode Exit fullscreen mode

Imagem de flag com Targeting por email

Targeting por atributo (liberar pra admins)

IF User.is_admin EQUALS "1"
THEN serve: ON
To everyone else: OFF
Enter fullscreen mode Exit fullscreen mode

Rollout por porcentagem

10% dos usuários: ON
90% dos usuários: OFF
Enter fullscreen mode Exit fullscreen mode

Isso permite um rollout gradual: comece com 10%, monitore, aumente pra 50%, e finalmente 100%.

Imagem de flag com Targeting por atributo e Rollout por porcentagem


Testes

Mock do ConfigCat para Jest

Como o ConfigCat é uma dependência externa, precisamos mocká-lo nos testes. O mock deve cobrir todos os símbolos usados pelo featureFlagsContext.tsx:

// __mocks__/configcat-react.js
module.exports = {
  ConfigCatProvider: ({ children }) => children,
  useConfigCatClient: () => ({
    getValueAsync: (key, defaultValue) => Promise.resolve(defaultValue),
    setDefaultUser: () => {},
    clearDefaultUser: () => {},
    on: () => {},
    off: () => {}
  }),
  User: class User {
    constructor() {}
  }
}
Enter fullscreen mode Exit fullscreen mode

Configurar no jest.config.js

module.exports = {
  moduleNameMapper: {
    '^configcat-react$': '<rootDir>/__mocks__/configcat-react.js'
  }
}
Enter fullscreen mode Exit fullscreen mode

Mock do Context nos testes de componentes

Na prática, a maioria dos testes de componentes não precisa exercitar o Provider inteiro — basta mockar o Context diretamente. Inclua getStringFlag junto com getFlag:

jest.mock('providers/feature-flags/featureFlagsContext', () => ({
  FeatureFlagsContext: {
    _currentValue: {
      flags: {},
      getFlag: () => ({ isEnabled: true, isLoading: false }),
      getStringFlag: () => ({ value: 'default', isLoading: false })
    }
  }
}))
Enter fullscreen mode Exit fullscreen mode

Isso faz com que todas as flags boolean retornem true e as string retornem 'default' nos testes — comportamento padrão que geralmente você quer ao testar o caminho feliz.

Para testar o cenário com flag desligada:

jest.mock('providers/feature-flags/featureFlagsContext', () => ({
  FeatureFlagsContext: {
    _currentValue: {
      flags: {},
      getFlag: () => ({ isEnabled: false, isLoading: false }),
      getStringFlag: () => ({ value: '', isLoading: false })
    }
  }
}))
Enter fullscreen mode Exit fullscreen mode

Para testar o estado de carregamento:

jest.mock('providers/feature-flags/featureFlagsContext', () => ({
  FeatureFlagsContext: {
    _currentValue: {
      flags: {},
      getFlag: () => ({ isEnabled: false, isLoading: true }),
      getStringFlag: () => ({ value: '', isLoading: true })
    }
  }
}))
Enter fullscreen mode Exit fullscreen mode

Casos de Uso Reais

1. Deploy Seguro

Você desenvolveu uma feature nova. Em vez de esperar pra fazer merge só quando estiver 100% pronta:

  1. Suba o código com a flag OFF
  2. No dashboard, ligue apenas pro seu email
  3. Teste em produção com dados reais
  4. Quando estiver pronto, ligue pra todos

Zero conflito de branch. Zero rollback.

2. Kill Switch

Feature causando problemas em produção?

  1. Abra o dashboard do ConfigCat
  2. Desligue a flag
  3. Em até uma hora (ou o intervalo de polling configurado), a feature desaparece

Sem deploy. Sem rollback. Sem estresse.

3. Apresentação para Stakeholders

Quer mostrar uma feature nova só pros diretores antes de liberar?

  1. Crie uma regra de targeting com os emails dos diretores
  2. Ligue a flag
  3. Eles veem a feature, o resto dos usuários não

4. Rollout Gradual

Feature nova que pode impactar performance?

  • Dia 1: 10% dos usuários
  • Dia 3: 50% dos usuários
  • Dia 5: 100% dos usuários

Se em qualquer momento algo der errado, volte pra 0%.

5. Campanha ou Conteúdo Dinâmico (flag string)

Você tem um banner que muda conforme a campanha ativa. Em vez de fazer deploy a cada troca:

  1. Crie uma flag string activePromotion no ConfigCat
  2. Configure o valor como 'black-friday' quando a campanha começa
  3. Mude para '' (vazio) quando terminar

O componente reage automaticamente quando o polling detecta a mudança — sem deploy, sem redeploy, sem acionar o time de infra.


Boas Práticas

1. Nomenclatura consistente

Use camelCase nas flags (padrão do ConfigCat) e UPPER_SNAKE_CASE nas constantes:

// ✅ Bom — claro e descritivo
featureFlags = {
  SHOW_DASHBOARD: 'showDashboard',
  ENABLE_NEW_CHECKOUT: 'enableNewCheckout'
}

stringFeatureFlags = {
  ACTIVE_PROMOTION: 'activePromotion'
}

// ❌ Ruim — ambíguo
featureFlags = {
  DASH: 'dash',
  CHECKOUT: 'checkout'
}
Enter fullscreen mode Exit fullscreen mode

2. Escolha o tipo certo de flag

Use boolean quando... Use string quando...
Ligar/desligar uma feature Controlar qual variante exibir
Proteger uma rota Selecionar um tema ou layout
Feature em rollout gradual A/B testing com múltiplas variantes
Kill switch de emergência Conteúdo dinâmico (banners, campanhas)

3. Limpeza de flags antigas

Flags são código temporário. Quando a feature estiver 100% liberada e estável:

  1. Remova a flag do código (o if com getFlag ou o componente com getStringFlag)
  2. Remova a constante do featureFlags ou stringFeatureFlags
  3. Delete a flag do dashboard do ConfigCat

Flag esquecida é dívida técnica.

4. Comportamento seguro por padrão

O defaultFlagState retorna isEnabled: false e o defaultStringFlagState retorna value: ''. Se o ConfigCat estiver fora do ar ou demorar pra responder, nenhuma feature nova aparece — o usuário vê a versão estável da aplicação.

5. Use isLoading em páginas protegidas

Sempre cheque isLoading antes de tomar decisões como redirect:

// ✅ Bom — espera carregar antes de decidir
const { isEnabled, isLoading } = getFlag(Consts.featureFlags.SHOW_DASHBOARD)

useEffect(() => {
  if (!isLoading && !isEnabled) {
    push('/app')
  }
}, [isLoading, isEnabled, push])

// ❌ Ruim — redireciona antes de saber o valor real
const { isEnabled } = getFlag(Consts.featureFlags.SHOW_DASHBOARD)

useEffect(() => {
  if (!isEnabled) {
    push('/app') // Vai redirecionar no primeiro render!
  }
}, [isEnabled, push])
Enter fullscreen mode Exit fullscreen mode

Vantagens da Arquitetura

Desacoplamento total

// Componentes não conhecem ConfigCat
import { FeatureFlagsContext } from 'providers/feature-flags/featureFlagsContext'

const { getFlag, getStringFlag } = useContext(FeatureFlagsContext)
Enter fullscreen mode Exit fullscreen mode

Fácil migração

Para trocar ConfigCat por outra ferramenta (ex: LaunchDarkly), você altera apenas o ConfigCatSync e o FeatureFlagsProvider. Os componentes continuam usando useContext(FeatureFlagsContext) + getFlag/getStringFlag — não mudam nada.

Estado centralizado

Todas as flags são buscadas uma vez e compartilhadas via Context. Se 10 componentes usam a mesma flag, o ConfigCat é consultado uma vez só — não 10 vezes.

Reatividade automática

O listener configChanged garante que quando uma flag muda no dashboard, todos os componentes que a consomem atualizam automaticamente, sem refresh da página. Isso vale tanto para flags boolean quanto string.

Dois tipos de flag, um contrato unificado

O Context expõe getFlag e getStringFlag com contratos claros e tipados. Componentes nunca lidam com any ou precisam fazer cast — o TypeScript guia o uso correto desde o autocompletar.


Conclusão

Feature Flags transformaram a forma como fazemos deploy. Com ConfigCat como engine e React Context API como camada de abstração, conseguimos:

  • ✅ Deploy contínuo sem medo
  • ✅ Testes em produção com segurança
  • ✅ Kill switch instantâneo
  • ✅ Targeting avançado por usuário
  • ✅ Suporte a flags boolean e string
  • ✅ Estado centralizado e reativo
  • ✅ Arquitetura desacoplada — pronta pra trocar de ferramenta
  • ✅ TypeScript garantindo type safety nas flags

O investimento de criar a camada de abstração com Context API vale muito a pena. O código fica limpo, testável e preparado pro futuro.


Indo Além — O que este guia não cobre

O que implementamos aqui é uma base sólida e pronta pra produção. Mas feature flags como conceito têm muito mais profundidade. Se você quiser continuar explorando:

Outros tipos de valor
Além de boolean e string, ferramentas como ConfigCat suportam flags do tipo int, double e até JSON. Isso abre espaço para casos como configurar limites dinâmicos (rate limit, tamanho de paginação, timeout de requisição) ou passar objetos de configuração inteiros sem deploy.

Flags no servidor
A implementação deste artigo é inteiramente client-side. Em aplicações Next.js com uso intenso de SSR ou Server Components, você pode — e muitas vezes deve — avaliar flags no servidor, antes de renderizar a página. Isso evita flashes de conteúdo (o usuário nunca vê o estado "carregando") e permite decisões de roteamento ainda na borda.

Testes com flags em múltiplos estados no CI
Uma flag desligada em produção não significa que o código por trás dela está livre de bugs. Estratégias de CI/CD mais avançadas rodam o pipeline com a flag ligada e desligada, garantindo que ambos os caminhos sejam validados antes do merge.

Gestão do ciclo de vida das flags
Flags acumuladas viram dívida técnica silenciosa. Existem abordagens para rastrear a idade de cada flag, definir uma data de expiração desde a criação, e automatizar alertas quando uma flag passa do prazo esperado — antes que ela vire um if esquecido no código.

Plataformas de experimentação
Feature flags são o passo zero de um experimento. Ferramentas como LaunchDarkly, Split.io e GrowthBook vão além: medem o impacto estatístico de cada variante, calculam significância e ajudam a tomar decisões de produto baseadas em dados — não em intuição.

Cada um desses tópicos rende um artigo próprio. O importante é que a arquitetura que construímos aqui já está preparada para evoluir em qualquer dessas direções — o desacoplamento via Context API garante isso.


Recursos


Escrito por Guilherme Marucchi — Meu LinkedIn

Top comments (0)