Переменные окружения vs удалённая конфигурация: когда что использовать
«Просто положи в переменную окружения» — частый совет. И часто он правильный — переменные окружения просты, безопасны и поддерживаются повсеместно. Но иногда это неподходящий инструмент.
В этой статье мы сравним переменные окружения с удалённой конфигурацией и установим чёткие рекомендации, ког да использовать каждый подход.
Как работают переменные окружения
Переменные окружения — это пары ключ-значение, доступные процессу:
const databaseUrl = process.env.DATABASE_URL
const apiKey = process.env.API_KEY
const logLevel = process.env.LOG_LEVEL ?? 'INFO'Они устанавливаются вне вашего кода — в shell-скриптах, файлах Docker Compose или настройках облачной платформы:
# docker-compose.yml
services:
app:
environment:
- DATABASE_URL=postgresql://localhost/mydb
- LOG_LEVEL=DEBUGПеременные окружения:
- Читаются один раз при старте процесса
- Статичны на протяжении жизни процесса
- Ограничены одним процессом (и его дочерними)
Как работает удалённая конфигурация
Удалённая конфигурация получает значения с внешнего сервера:
import { Replane } from '@replanejs/sdk'
interface Configs {
'rate-limit': number
'new-feature-enabled': boolean
}
const replane = new Replane<Configs>()
await replane.connect({
sdkKey: process.env.REPLANE_SDK_KEY!,
baseUrl: 'https://cloud.replane.dev',
})
const rateLimit = replane.get('rate-limit')
const featureEnabled = replane.get('new-feature-enabled')Удалённая конфигурация:
- Читается во время работы (постоянно обновляется)
- Динамична (меняется без перезапуска)
- Общая для всех экземпляров
В чём ключевое отличие между переменными окружения и удалённой конфигурацией?
Когда использовать переменные окружения
Переменные окружения — правильный выбор для:
Секреты и учётные данные
API-ключи, пароли баз данных и ключи шифрования должны храниться в переменных окружения (или менеджере секретов):
// Хорошо: секреты в переменных окружения
const stripeKey = process.env.STRIPE_API_KEY
const dbPassword = process.env.DATABASE_PASSWORDПочему? Секреты чувствительны и должны:
- Никогда не появляться в панелях управления или логах
- Ротироваться через безопасные каналы
- Иметь строгий контроль доступа
Системы удалённой конфигурации не предназначены для управления секретами.
Строки подключения и эндпоинты
URL баз данных и эндпоинты сервисов редко меняются во время работы:
// Хорошо: информация о подключении в переменных окружения
const databaseUrl = process.env.DATABASE_URL
const redisUrl = process.env.REDIS_URL
const apiGateway = process.env.API_GATEWAY_URLЭти значения привязаны к инфраструктуре. При их изменении обычно нужно перезапустить соединения в любом случае.
Настройки, специфичные для среды
Настройки, различающиеся между dev/staging/production, но не меняющиеся внутри среды:
// Хорошо: конфигурация среды в переменных окружения
const environment = process.env.ENVIRONMENT ?? 'development'
const debugMode = process.env.DEBUG === 'true'Конфигурация времени сборки
Настройки, нужные при сборке (не во время работы):
# Сборка с определёнными настройками
NEXT_PUBLIC_API_URL=https://api.example.com npm run buildКогда использовать удалённую конфигурацию
Удалённая конфигурация — правильный выбор для:
Feature flags
Переключатели, которые дол жны меняться мгновенно без деплоя:
// Хорошо: feature flags в удалённой конфигурации
if (replane.get('new-checkout-enabled')) {
showNewCheckout()
}Вы хотите включить функцию для 10% пользователей, следить за метриками, затем увеличить до 100% — всё без деплоя.
Операционные параметры
Настройки, которые вы подстраиваете под реальные условия:
// Хорошо: операционная настройка в удалённой конфигурации
const rateLimit = replane.get('api-rate-limit')
const cacheTtl = replane.get('cache-ttl-seconds')
const batchSize = replane.get('worker-batch-size')При всплеске трафика может потребоваться снизить лимиты. При замедлении базы данных — увеличить таймауты. Удалённая конфигурация позволяет реагировать за секунды.
Kill switches
Аварийные элементы управления для отключения функций:
// Хорошо: kill switch в удалённой конфигурации
if (replane.get('payments-enabled', { default: true })) {
processPayment()
} else {
showMaintenanceMessage()
}Когда платежи ломаются в 2 часа ночи, вы переключаете флаг вместо аварийного деплоя.
Настройки для пользователей или клиентов
Разные клиенты получают разные значения:
// Хорошо: конфигурация по клиентам в удалённой конфигурации
const maxUsers = replane.get('max-users', {
context: { tenant: tenant.id }
})Enterprise-клиенты могут получить 10 000 пользователей, а бесплатный тариф — 10.
Где хранить пароль подключения к базе данных?
Структура принятия решений
Вот простая схема для выбора места хранения конфигурации:
Это секрет? ──────Да──────► Переменная окружения
│ (или менеджер секретов)
Нет
▼
Должно меняться ──Да──► Удалённая конфигурация
без перезапуска?
│
Нет
▼
Переменная окруженияИли в виде таблицы:
| Тип конфигурации | Где хранить | |------------------|-------------| | API-ключи, пароли | Переменная окружения / Менеджер секретов | | URL баз данных | Переменная окружения | | Feature flags | Удалённая конфигурация | | Лимиты, таймауты | Удалённая конфигурация | | Kill switches | Удалённая конфигурация | | Настройки сборки | Переменная окружения | | Настройки по пользователям | Удалённая конфигурация |
Комбинирование подходов
На практике вы будете использовать оба. Вот типичный паттерн:
import { Replane } from '@replanejs/sdk'
// Статическая конфигурация из окружения (секреты, подключения)
const DATABASE_URL = process.env.DATABASE_URL!
const STRIPE_KEY = process.env.STRIPE_API_KEY!
const ENVIRONMENT = process.env.ENVIRONMENT ?? 'development'
interface Configs {
'rate-limit': number
'feature-enabled': boolean
}
// Сам SDK-ключ берётся из переменной окружения
const replane = new Replane<Configs>()
await replane.connect({
sdkKey: process.env.REPLANE_SDK_KEY!,
baseUrl: 'https://cloud.replane.dev',
})
// Динамическая конфигурация из удалённого источника (функции, настройки)
function getRateLimit(): number {
return replane.get('rate-limit', { default: 100 })
}
function isFeatureEnabled(name: keyof Configs, userContext?: Record<string, unknown>): boolean {
if (userContext) {
return replane.get(name, { context: userContext, default: false }) as boolean
}
return replane.get(name, { default: false }) as boolean
}Обратите внимание, что даже SDK-ключ удалённой конфигурации берётся из переменной окружения — вы используете переменные окружения для действительно статичных, секретных значений.