Настройка Feature Flags
Feature flags (флаги функций) позволяют деплоить код без релиза функций: включать фичи для отдельных пользователей, сегментов аудитории или процентов трафика без нового деплоя.
Зачем нужны feature flags
- Trunk-based development — все работают в main/master, незаконченные фичи скрыты за флагом
- Canary releases — сначала 1% пользователей, потом 10%, потом всем
- A/B тестирование — разные версии для разных сегментов
- Kill switch — мгновенное отключение проблемной функции без деплоя
- Beta-программы — включение для конкретных аккаунтов
Self-hosted: Unleash
# Docker Compose
cat > docker-compose.yml << 'EOF'
services:
db:
image: postgres:16
environment:
POSTGRES_DB: unleash
POSTGRES_USER: unleash
POSTGRES_PASSWORD: secret
volumes:
- pg_data:/var/lib/postgresql/data
unleash:
image: unleashorg/unleash-server:latest
ports:
- "4242:4242"
environment:
DATABASE_URL: postgresql://unleash:secret@db/unleash
INIT_CLIENT_API_TOKENS: "default:development.unleash-token"
depends_on:
- db
volumes:
pg_data:
EOF
docker compose up -d
Интеграция в Next.js:
// lib/unleash.ts
import { initialize, isEnabled, getVariant } from 'unleash-client';
export const unleash = initialize({
url: 'http://unleash:4242/api',
appName: 'my-nextjs-app',
customHeaders: { Authorization: process.env.UNLEASH_TOKEN! },
});
// Использование в Server Component
export async function isFeatureEnabled(flag: string, userId?: string) {
await unleash.isReady();
return isEnabled(flag, { userId });
}
// app/page.tsx (Server Component)
import { isFeatureEnabled } from '@/lib/unleash';
export default async function HomePage() {
const showNewHero = await isFeatureEnabled('new-hero-section', userId);
return showNewHero ? <NewHeroSection /> : <OldHeroSection />;
}
Cloud: Growthbook (open source SaaS)
# Self-hosted
docker run -d \
-e MONGODB_URI=mongodb://mongo/growthbook \
-e APP_ORIGIN=http://localhost:3000 \
-p 3000:3000 \
growthbook/growthbook:latest
// SDK для React
import { GrowthBook, GrowthBookProvider, useFeatureValue } from '@growthbook/growthbook-react';
const gb = new GrowthBook({
apiHost: 'https://cdn.growthbook.io',
clientKey: process.env.NEXT_PUBLIC_GROWTHBOOK_CLIENT_KEY,
enableDevMode: process.env.NODE_ENV !== 'production',
trackingCallback: (experiment, result) => {
// Отправляем в аналитику
gtag('event', 'experiment_viewed', {
experiment_id: experiment.key,
variant_id: result.key,
});
},
});
// Провайдер
function App({ Component, pageProps }) {
return (
<GrowthBookProvider growthbook={gb}>
<Component {...pageProps} />
</GrowthBookProvider>
);
}
// Использование в компоненте
function PricingPage() {
const showAnnualPricing = useFeatureValue('annual-pricing', false);
const ctaText = useFeatureValue('cta-text', 'Get Started');
return (
<div>
<button>{ctaText}</button>
{showAnnualPricing && <AnnualPricingSection />}
</div>
);
}
Флаги с таргетингом (Posthog)
// PostHog: feature flags + аналитика в одном инструменте
import PostHog from 'posthog-node';
const client = new PostHog(process.env.POSTHOG_API_KEY!, {
host: 'https://app.posthog.com'
});
// Таргетинг по свойствам пользователя
const isEnabled = await client.isFeatureEnabled(
'new-checkout-flow',
userId,
{
// PostHog проверит против правил флага
personProperties: {
plan: user.plan, // 'pro' | 'enterprise'
country: user.country,
created_at: user.createdAt,
}
}
);
Паттерны использования
Постепенный rollout:
0% → деплой кода за флагом
1% → включаем для 1% пользователей, мониторим ошибки
10% → расширяем, мониторим метрики
50% → half rollout, финальная проверка
100% → полный rollout
→ удаляем флаг из кода (флаги-зомби — технический долг)
Жизненный цикл флага:
// Флаги должны иметь срок жизни
// Создаём с датой истечения:
// "new-onboarding-flow" → expires: 2024-03-01
// После этой даты — удалить флаг из кода
Настройка self-hosted Unleash или Growthbook с интеграцией в приложение — 1–2 рабочих дня.







