
Feature Flags no Frontend: Suba o Código, Lance Quando Estiver Pronto
Você fez o merge. A funcionalidade está em produção. Mas o negócio ainda não está pronto para lançar.
Sem feature flags, as opções são: segurar o merge ou aceitar o risco. Com feature flags, você sobe o código e ativa quando estiver pronto.
O Que É uma Feature Flag?
Uma feature flag (também chamada de feature toggle) é uma condicional que controla se uma funcionalidade está visível ou ativa.
if (flags.novoCheckout) {
return <NovoFluxoCheckout />
}
return <CheckoutAntigo />
A funcionalidade existe em produção. Ninguém vê até você ativar a flag.
Por Que Isso Importa
| Sem flags | Com flags |
|---|---|
| Aguardar para fazer merge | Merge a qualquer momento |
| Deploys arriscados | Rollouts graduais |
| Todos os usuários recebem a mudança | Você controla quem vê o quê |
| Rollback = novo deploy | Rollback = virar o switch |
Times que entregam com frequência usam feature flags para separar deploy de lançamento.
A Implementação Mais Simples
Comece com variáveis de ambiente. Sem bibliotecas, sem complexidade.
// lib/flags.ts
export const flags = {
novoCheckout: process.env.NEXT_PUBLIC_FLAG_NOVO_CHECKOUT === 'true',
betaDashboard: process.env.NEXT_PUBLIC_FLAG_BETA_DASHBOARD === 'true',
}
// components/Checkout.tsx
import { flags } from '@/lib/flags'
export default function Checkout() {
if (flags.novoCheckout) {
return <NovoFluxoCheckout />
}
return <CheckoutAntigo />
}
Defina a variável no seu pipeline de CI/CD ou no .env.local:
NEXT_PUBLIC_FLAG_NOVO_CHECKOUT=true
Funciona bem para toggles por ambiente (staging vs produção). A desvantagem: mudar uma flag exige um novo deploy.
Flags em Runtime com React Context
Para flags que mudam sem redeploy, busque-as em runtime e exponha via context.
// lib/flags-context.tsx
'use client'
import { createContext, useContext } from 'react'
type Flags = {
novoCheckout: boolean
betaDashboard: boolean
}
const FlagsContext = createContext<Flags>({ novoCheckout: false, betaDashboard: false })
export function FlagsProvider({ flags, children }: { flags: Flags; children: React.ReactNode }) {
return <FlagsContext.Provider value={flags}>{children}</FlagsContext.Provider>
}
export function useFlags() {
return useContext(FlagsContext)
}
Busque as flags no servidor e injete no layout raiz:
// app/[locale]/layout.tsx
import { FlagsProvider } from '@/lib/flags-context'
async function getFlags() {
const res = await fetch('https://sua-api-de-flags.com/flags', { next: { revalidate: 60 } })
return res.json()
}
export default async function RootLayout({ children }) {
const flags = await getFlags()
return (
<html>
<body>
<FlagsProvider flags={flags}>{children}</FlagsProvider>
</body>
</html>
)
}
Qualquer componente pode ler as flags sem prop drilling:
'use client'
import { useFlags } from '@/lib/flags-context'
export default function Header() {
const { betaDashboard } = useFlags()
return <nav>{betaDashboard && <a href="/beta">Beta Dashboard</a>}</nav>
}
Ferramentas de Terceiros
Quando você precisa de segmentação por usuário, testes A/B ou uma interface para gerenciar flags sem tocar no código, use um serviço dedicado.
| Ferramenta | Pontos Fortes |
|---|---|
| LaunchDarkly | Enterprise, segmentação avançada |
| GrowthBook | Open-source, A/B testing integrado |
| Unleash | Self-hostável, bom para times |
| Vercel Flags | Integração nativa com Next.js |
| Flagsmith | Simples, boa camada gratuita |
Exemplo com Vercel Flags
import { flag } from '@vercel/flag/next'
export const novoCheckoutFlag = flag<boolean>({
key: 'novo-checkout',
defaultValue: false,
decide() {
return false
},
})
// app/checkout/page.tsx
import { novoCheckoutFlag } from '@/flags'
export default async function CheckoutPage() {
const mostrarNovoCheckout = await novoCheckoutFlag()
return mostrarNovoCheckout ? <NovoFluxoCheckout /> : <CheckoutAntigo />
}
Erros Comuns
1. Deixar flags no código para sempre
// ❌ Flags se acumulam e ninguém remove
if (flags.funcionalidadeAntigaDoQ1) { ... }
if (flags.experimentoDoAnoPassado) { ... }
// ✅ Defina uma data de limpeza ao criar a flag
// TODO: remover esta flag após 2026-07-01
if (flags.novoCheckout) { ... }
2. Aninhar flags dentro de flags
// ❌ Ilegível e difícil de depurar
if (flags.funcionalidadeA) {
if (flags.funcionalidadeB) {
if (flags.funcionalidadeC) { ... }
}
}
Refatore: uma flag por funcionalidade, composta no nível do componente.
3. Expor flags somente de servidor para o cliente
// ❌ Exportado para o cliente — vaza configurações internas
export const flags = {
adminPanel: process.env.ADMIN_FEATURE_FLAG === 'true',
}
Use NEXT_PUBLIC_ apenas para flags seguras de expor. Mantenha toggles sensíveis no servidor.
4. Sem valor padrão
// ❌ Quebra se flags for undefined
if (flags.novoCheckout) { ... }
// ✅ Padrão seguro
const { novoCheckout = false } = useFlags()
Quando Usar Feature Flags
Use quando:
- A funcionalidade é grande e abrange vários PRs
- O negócio precisa controlar o timing do lançamento
- Você quer um rollout gradual (10% → 50% → 100%)
- Está rodando um experimento A/B
- A funcionalidade é arriscada e você precisa de um kill switch
Evite quando:
- A funcionalidade é pequena e isolada
- Você está adicionando flags para tudo (aumenta a manutenção)
- A flag nunca é removida (vira código morto)
O Que Fazer Agora
- Adicione um simples
lib/flags.tsao seu projeto atual - Identifique uma funcionalidade em andamento que se beneficiaria de uma flag
- Defina uma política de limpeza de flags (tempo máximo de vida, responsável, PR de remoção)
- Se o seu time entrega com frequência, avalie GrowthBook ou Vercel Flags
Feature flags separam quando você faz deploy de quando os usuários veem.
Isso não é apenas um truque de deployment — é uma disciplina que torna times mais rápidos e lançamentos mais seguros.