Контекст
Приняли решение использовать Trunk-Based Development с Feature Toggles. Нам необходимо выбрать конкретную реализацию инфраструктуры для управления флагами.
Ограничения среды
- Air-gapped Kubernetes кластер в Yandex Cloud — нет выхода в интернет
- Docker-образы загружаются через внутренний Container Registry
- Никакие решения с license check / phone-home не работают
- Стек: Java (Spring Boot), React, Envoy API Gateway, GitLab CI/CD, ArgoCD, Helm Charts
- Секреты: Yandex Lockbox + External Secrets Operator
Требования
- Уровень сложности флагов: kill switches (boolean on/off) + targeting по контексту
- Три окружения: dev, stage, prod
- Надёжность: если система флагов недоступна, приложения продолжают работать
- Минимум дополнительной инфраструктуры
Рассмотренные варианты
1. Unleash OSS
Описание: Самый зрелый open source feature flag сервер. TypeScript/Node.js, PostgreSQL, Web UI. Apache 2.0.
Плюсы: 12k+ GitHub stars, 10+ лет разработки, большое community. Helm Chart, Java SDK, React SDK (через Edge proxy). Prometheus метрики. Работает air-gapped (Apache 2.0, нет license checks).
Минусы:
- OSS ограничен 1 проектом и 2 окружениями (dev + prod). Эти лимиты нельзя обойти через env vars. Для трёх окружений (dev/stage/prod) потребуется либо workaround, либо отдельный инстанс.
- Edge OSS (proxy для React) достигает End of Life 31 декабря 2026. После этого — без security-обновлений. Enterprise Edge — платный.
- Нет SSO, нет fine-grained RBAC, нет change requests в OSS.
- Enterprise: $75/seat/month (minimum 5 seats = $4,500/год).
Вердикт: Стратегический риск — Edge EOL убивает React-сценарий через 10 месяцев.
2. FeatBit OSS
Описание: Open source feature flag платформа. C#/.NET, PostgreSQL + Redis, Web UI. MIT лицензия.
Плюсы: Unlimited проекты и окружения бесплатно. RBAC, change requests, scheduled changes — бесплатно. Java SDK, React SDK, WebSocket real-time. Работает air-gapped (MIT, нет license checks).
Минусы:
- Bus factor: 1-2 активных мейнтейнера. Проект может прекратить развитие.
- C#/.NET стек — наша команда не может дебажить server-side код.
- 5-6 Docker-образов (UI, API Server, Evaluation Server, Data Analytics, Redis, PostgreSQL) — больше компонентов для air-gapped registry.
- Документация слабая. Маленькое community (~2.5k stars).
- SSO — только Enterprise ($3,999/год).
Вердикт: Много фич бесплатно, но высокий risk чужого стека и bus factor.
3. Flipt v2
Описание: Git-native feature flag решение. Go, flags хранятся в Git как YAML. Fair Core License.
Плюсы: Git-native — идеально для GitOps (GitLab + ArgoCD). Modern UI. OpenFeature провайдеры. OFREP-совместимость.
Минусы:
- Fair Core License (не open source). Бесплатная версия делает license check через интернет при старте. В air-gapped — не запускается.
- Air-gapped режим — Pro фича ($1,000/год).
- GitLab integration (merge proposals) — тоже Pro.
Вердикт: Не работает в air-gapped без оплаты.
4. flagd + ConfigMap + OpenFeature SDK (GitOps)
Описание: CNCF-проект flagd — лёгкий daemon для evaluation флагов. Флаги хранятся как YAML/JSON в Git-репозитории, синхронизируются в Kubernetes через ConfigMap и ArgoCD. Приложения используют vendor-neutral OpenFeature SDK.
Плюсы:
- Apache 2.0 — нет license checks, нет phone-home, нет ограничений.
- CNCF-стандарт (OpenFeature — Incubating project). Vendor-neutral API.
- Минимум зависимостей: никаких БД, Redis, отдельных серверов. Только ConfigMap.
- Наш существующий workflow: Git → GitLab MR → ArgoCD → ConfigMap → flagd.
- In-Process режим: evaluation engine встраивается прямо в JVM, не нужен sidecar.
- 2 Docker-образа максимум (flagd, если sidecar) или 0 дополнительных (in-process).
- Evaluation в наносекундах (из памяти JVM).
- Если flagd недоступен — приложение работает с последним кэшем.
Минусы:
- Нет UI — управление только через Git (YAML) и kubectl.
- Нет готового решения для React (нет Edge proxy). Требуется BFF-endpoint в Java-сервисе.
- Изменение флага = коммит + MR + merge + ArgoCD sync (минуты, не секунды).
- Нет встроенного audit log, RBAC, SSO (audit = Git history, RBAC = GitLab permissions).
Вердикт: Идеально для текущего этапа — zero cost, максимальная надёжность, встраивается в существующий GitOps workflow.
5. Собственная разработка (Spring Boot + React UI)
Описание: Evaluation engine на Java (Spring Boot), Admin UI на React, PostgreSQL для хранения. Полный контроль.
Плюсы: Полный контроль. Наш стек — можем дебажить и развивать. UI для нетехнических пользователей. Нет внешних зависимостей от чужих roadmap.
Минусы:
- Недели-месяцы разработки вместо решения за день.
- Поддержка: баги, security-патчи, SDK-обновления — всё на нас.
- Reinventing the wheel для solved problem.
Вердикт: Преждевременно. Может быть рассмотрено в будущем, если появится реальная потребность в UI.
Решение
Выбран: Вариант 4 — flagd + ConfigMap + OpenFeature SDK (GitOps-подход).
Архитектура
┌─────────────────────────────────────────────────────────┐
│ GitLab Repository │
│ │
│ feature-flags/ │
│ ├── dev/ │
│ │ └── flags.json │
│ ├── stage/ │
│ │ └── flags.json │
│ └── prod/ │
│ └── flags.json │
└───────────────────────┬─────────────────────────────────┘
│ ArgoCD sync
▼
┌─────────────────────────────────────────────────────────┐
│ Kubernetes (air-gapped) │
│ │
│ ┌─────────────────────────────┐ │
│ │ ConfigMap: feature-flags │ │
│ │ (per environment) │ │
│ └──────────────┬──────────────┘ │
│ │ │
│ ┌────────────┼────────────┐ │
│ ▼ ▼ ▼ │
│ ┌──────┐ ┌──────┐ ┌──────────┐ │
│ │Svc A │ │Svc B │ │React BFF │──▶ React App │
│ │ │ │ │ │ │ │
│ │flagd │ │flagd │ │flagd │ │
│ │in- │ │in- │ │in-proc + │ │
│ │proc │ │proc │ │/api/flags│ │
│ └──────┘ └──────┘ └──────────┘ │
└─────────────────────────────────────────────────────────┘
Формат файла флагов
{
"$schema": "https://flagd.dev/schema/v0/flags.json",
"flags": {
"new-payment-engine": {
"state": "ENABLED",
"variants": {
"on": true,
"off": false
},
"defaultVariant": "off",
"targeting": {
"if": [
{ "in": ["user-123", "user-456", { "var": "userId" }] },
"on",
"off"
]
}
},
"maintenance-mode": {
"state": "ENABLED",
"variants": {
"on": true,
"off": false
},
"defaultVariant": "off"
}
}
}
Java SDK — интеграция в микросервис
Maven зависимости:
<!-- OpenFeature SDK (vendor-neutral API) -->
<dependency>
<groupId>dev.openfeature</groupId>
<artifactId>sdk</artifactId>
</dependency>
<!-- flagd Provider (in-process evaluation) -->
<dependency>
<groupId>dev.openfeature.contrib.providers</groupId>
<artifactId>flagd</artifactId>
</dependency>
Spring Boot конфигурация:
@Configuration
public class FeatureFlagConfig {
@Bean
public OpenFeatureAPI openFeatureAPI() {
OpenFeatureAPI api = OpenFeatureAPI.getInstance();
// In-process: evaluation внутри JVM, флаги из файла
FlagdProvider provider = new FlagdProvider(
FlagdOptions.builder()
.resolverType(Config.Resolver.IN_PROCESS)
.offlineFlagSourcePath("/etc/feature-flags/flags.json")
.build()
);
api.setProviderAndWait(provider);
return api;
}
@Bean
public Client featureFlagClient(OpenFeatureAPI api) {
return api.getClient();
}
}
Использование в коде:
@RestController
public class PaymentController {
private final Client featureFlags;
@PostMapping("/pay")
public ResponseEntity<?> processPayment(@RequestBody PaymentRequest req) {
boolean newEngine = featureFlags.getBooleanValue(
"new-payment-engine", false,
new MutableContext(req.getUserId())
);
if (newEngine) {
return newPaymentService.process(req);
} else {
return oldPaymentService.process(req);
}
}
}
React — через BFF endpoint
В одном из Java-сервисов (API Gateway или BFF):
@RestController
@RequestMapping("/api/flags")
public class FeatureFlagController {
private final Client featureFlags;
@GetMapping
public Map<String, Boolean> getFlags(@RequestParam String userId) {
MutableContext ctx = new MutableContext(userId);
return Map.of(
"new-payment-engine", featureFlags.getBooleanValue("new-payment-engine", false, ctx),
"dark-mode", featureFlags.getBooleanValue("dark-mode", false, ctx),
"maintenance-mode", featureFlags.getBooleanValue("maintenance-mode", false, ctx)
);
}
}
React:
const { data: flags } = useSWR(`/api/flags?userId=${userId}`, fetcher);
if (flags?.['new-payment-engine']) {
return <NewCheckout />;
} else {
return <OldCheckout />;
}
Helm — монтирование ConfigMap
# values.yaml
featureFlags:
enabled: true
configMapName: feature-flags
# deployment.yaml
volumes:
- name: feature-flags
configMap:
name: {{ .Values.featureFlags.configMapName }}
volumeMounts:
- name: feature-flags
mountPath: /etc/feature-flags
readOnly: true
Workflow изменения флага
1. Разработчик редактирует feature-flags/prod/flags.json в GitLab
2. Создаёт Merge Request
3. Code review (кто approve — тот несёт ответственность)
4. Merge в main
5. ArgoCD обнаруживает изменение → обновляет ConfigMap
6. flagd in-process подхватывает новый файл (file watcher)
7. Следующий запрос пользователя видит новое значение флага
Время от merge до применения: ~1-3 минуты (ArgoCD sync interval).
Почему не UI-решения
Проведён детальный анализ всех основных решений с UI. Ключевые выводы:
Последствия
Положительные
- Запуск за день: ConfigMap + OpenFeature SDK — всё, что нужно
- $0 стоимость: нет лицензий, подписок, license checks
- Air-gapped native: никаких обращений к внешним сервисам
- Vendor-neutral: OpenFeature SDK позволяет заменить flagd на любой другой бэкенд (Unleash, Flipt, собственный) без изменения бизнес-кода
- Минимум инфраструктуры: нет дополнительных БД, серверов, sidecar-ов
- Git = audit log: вся история изменений флагов в Git (кто, когда, что, зачем, через MR)
- Git = RBAC: GitLab permissions контролируют, кто может мержить изменения флагов
- Git = approval workflow: MR review = change request
- Знакомый workflow: GitLab MR → ArgoCD — команда уже это использует каждый день
Отрицательные
- Нет UI для нетехнических пользователей. PM не может сам нажать кнопку.
- Изменение = коммит. Минуты, не секунды. Для экстренного отключения — медленнее, чем кнопка в UI.
- BFF для React — ручная работа. Каждый новый флаг для фронтенда нужно добавить в endpoint.
Митигация
- Если появится потребность в UI — можно мигрировать на Unleash/FeatBit/собственное решение, заменив только OpenFeature Provider. Бизнес-код останется без изменений.
- Для экстренного отключения: kubectl edit configmap feature-flags работает за секунды без MR.
- BFF endpoint можно сделать динамическим (возвращает все флаги из файла, без хардкода).
План внедрения
Страховка на собеседовании
Знание есть, но стресс мешает?
Бесплатное сообщество для прокачки карьеры в IT
Подпишись на https://t.me/IT_Interview_Partner_Bot
Подпишись на https://t.me/LyakhovEugene