Feature flags в TypeScript: как включать фичи без деплоя

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Когда деплой — это боль: почему разработчики обожают feature flags

Представьте: вы неделю пилили новую фичу. Покрыли тестами, прогнали код-ревью, нажали кнопку деплоя. И тут — бабах! — в продакшене всё ломается. Пользователи в ярости, менеджер дёргается, вы лихорадочно откатываете версию. Знакомо?

А теперь представьте другой сценарий: вы включаете новую фичу только для 5% пользователей. Смотрите метрики. Видите, что всё окей. Постепенно увеличиваете процент до 100%. Никакого стресса, никаких откатов, только спокойная уверенность.

Это магия feature flags (они же feature toggles, фича-флаги, флаги функций). В TypeScript проектах эта техника особенно хороша благодаря статической типизации и строгому контролю типов. Сегодня разберёмся, как внедрить feature flags в ваш проект, чтобы вы спали спокойно, а фичи включались без боли.

Что такое feature flags простыми словами

Feature flag — это условный переключатель в коде, который позволяет включить или выключить функциональность без изменения кода и повторного деплоя. Выглядит это примерно так:

if (featureFlags.isEnabled('new-checkout')) {
  // новый код
} else {
  // старый код
}

Всё гениальное просто, правда? Но за этой простотой скрывается мощный инструмент, который используют Google, Netflix, Facebook и другие гиганты. Они не деплоят каждую мелочь — они управляют поведением приложения через централизованную панель управления.

Когда feature flags реально нужны

  • Canary release — выкатка новой фичи на малую часть пользователей
  • Dark launch — скрытое тестирование, когда фича есть, но пользователи её не видят
  • A/B тестирование — сравнение двух версий функциональности
  • Kill switch — аварийное отключение проблемного кода без деплоя
  • Permit testing — тестирование на внутренних пользователях или по ролям

В TypeScript проектах feature flags приобретают особый шарм: компилятор проверяет, что вы правильно используете флаги, не путаете типы и не забываете обработать все состояния.

Архитектура системы feature flags

Прежде чем писать код, давайте разберёмся, из чего состоит грамотная система управления функциями.

Базовые компоненты

  1. Хранилище флагов — где лежат значения (JSON, база данных, внешний сервис)
  2. Провайдер — модуль, который загружает и кэширует флаги
  3. Клиентская часть — код, который проверяет состояние флагов
  4. Панель управления — интерфейс для изменения флагов в реальном времени

Простейшая реализация на TypeScript

Начнём с самого базового примера. Создадим тип для флагов и простой сервис:

// types/feature-flags.ts
export type FeatureFlag = 
  | 'new-checkout'
  | 'dark-mode'
  | 'recommendations-v2'
  | 'beta-chat';

export interface FeatureFlagConfig {
  flag: FeatureFlag;
  enabled: boolean;
  rolloutPercentage?: number;
  userIds?: string[];
}

Теперь напишем сам сервис:

// services/feature-flag.service.ts
export class FeatureFlagService {
  private flags: Map<FeatureFlag, FeatureFlagConfig> = new Map();

  constructor(initialFlags: FeatureFlagConfig[]) {
    initialFlags.forEach(flag => {
      this.flags.set(flag.flag, flag);
    });
  }

  isEnabled(flag: FeatureFlag, userId?: string): boolean {
    const config = this.flags.get(flag);
    if (!config) return false;

    if (!config.enabled) return false;

    // Проверка по конкретным пользователям
    if (config.userIds && userId) {
      return config.userIds.includes(userId);
    }

    // Rollout по проценту пользователей
    if (config.rolloutPercentage !== undefined && userId) {
      const hash = this.hashCode(userId);
      return (hash % 100) < config.rolloutPercentage;
    }

    return true;
  }

  private hashCode(str: string): number {
    let hash = 0;
    for (let i = 0; i < str.length; i++) {
      const char = str.charCodeAt(i);
      hash = ((hash << 5) - hash) + char;
      hash |= 0;
    }
    return Math.abs(hash);
  }
}

Это минимальная рабочая система. Но в реальном проекте вы, скорее всего, захотите использовать готовые решения, чтобы не изобретать велосипед.

Готовые библиотеки для TypeScript

На рынке есть несколько зрелых решений, которые отлично работают с TypeScript.

1. Unleash

Open-source решение с собственным сервером и отличным TypeScript SDK. Позволяет управлять флагами через веб-интерфейс, поддерживает стратегии активации (по проценту, по IP, по роли).

import { UnleashClient } from 'unleash-client';

const unleash = new UnleashClient({
  url: 'https://your-unleash-instance.com/api',
  appName: 'my-typescript-app',
  environment: 'production',
});

if (unleash.isEnabled('new-checkout', { userId: '123' })) {
  // новый код
}

2. LaunchDarkly

Корпоративный стандарт. Мощный, быстрый, с кучей интеграций. Для TypeScript есть отличный SDK с полной поддержкой типов.

import { init } from 'launchdarkly-node-server-sdk';

const client = init('sdk-key-12345');

const flagValue = await client.variation(
  'new-checkout',
  { key: 'user-123', name: 'John' },
  false // значение по умолчанию
);

if (flagValue) {
  // новый код
}

3. Микро-решение на файлах

Если не хотите поднимать сервер, можно хранить флаги в JSON-файлах и перезагружать их при изменении. Отлично подходит для небольших проектов.

// flags.json
{
  "new-checkout": {
    "enabled": true,
    "rolloutPercentage": 30
  },
  "dark-mode": {
    "enabled": false
  }
}
// services/file-based-flags.ts
import * as fs from 'fs';

export class FileBasedFlags {
  private flags: Record<string, any>;

  constructor(private filePath: string) {
    this.flags = this.loadFlags();
    fs.watchFile(filePath, () => {
      this.flags = this.loadFlags();
    });
  }

  isEnabled(flag: string): boolean {
    return this.flags[flag]?.enabled ?? false;
  }

  private loadFlags(): Record<string, any> {
    const raw = fs.readFileSync(this.filePath, 'utf-8');
    return JSON.parse(raw);
  }
}

Продвинутые техники работы с feature flags

Когда базовая система

Блоги

Рекомендаций по книгам