Переменные окружения — динамические значения, которые передаются приложению на этапе запуска для настройки поведения без изменения кода. Они позволяют разделять конфигурации разработки, тестирования и продакшна. По данным Twelve-Factor App, 2025, конфигурация должна храниться в переменных окружения, а не в коде. Переменные окружения обеспечивают безопасное управление API-ключами, URL бэкенда и флагами функциональности.
Главное
Переменные окружения — это пара ключ-значение, доступная процессу приложения через API операционной системы. Они передаются в процесс при его создании и существуют только во время его работы. В отличие от параметров конфигурации, встроенных в исходный код, переменные окружения не требуют перекомпиляции для изменения значений. Это фундаментальный принцип Twelve-Factor App, который обеспечивает чёткое разделение между кодом и конфигурацией.
В мобильной разработке переменные окружения решают проблему разных конфигураций для сред: разработчик использует локальный сервер, тестировщик — staging, пользователи — production. Вместо того чтобы хранить три URL бэкенда в коде с условными операторами if-else, разработчик передаёт один URL через переменную окружения на этапе сборки. Это упрощает код и исключает риск случайного использования продакшн-сервера в тестовой среде.
Основное преимущество — безопасность: чувствительные данные не попадают в репозиторий кода. API-ключи, секреты Firebase, токены доступа к бэкенду и сертификаты загружаются через CI/CD непосредственно в окружение сборки. Если злоумышленник получит доступ к репозиторию кода, он не найдёт там секретов, так как они хранятся в защищённых хранилищах CI-системы и передаются только на этапе сборки бинарного файла.
Мобильные проекты имеют минимум три среды: development, staging и production. Каждая среда требует своего набора конфигураций: URL сервера, имя пакета, схема подписи и сертификаты push-уведомлений. Без переменных окружения разработчику приходится вручную менять конфигурацию перед каждой сборкой, что приводит к ошибкам: забытый продакшн-ключ в тестовой сборке может вызвать отправку уведомлений реальным пользователям или расход платного API.
Переменные окружения позволяют переключать бэкенд без изменения кода: достаточно заменить значение в переменной API_BASE_URL. Флаги функциональности (feature flags) управляются через переменные типа FEATURE_CHAT_ENABLED=true, что позволяет включать новые возможности в staging без влияния на продакшн. Для каждой среды создаётся свой .env файл, который загружается на этапе сборки.
class AppConfig {
static final String apiBaseUrl =
const String.fromEnvironment('API_BASE_URL',
defaultValue: 'http://localhost:8080');
}
Жёстко закодированные ключи — распространённая уязвимость мобильных приложений. Злоумышленник декомпилирует APK или IPA с помощью инструментов типа jadx или Hopper и извлекает секреты из бинарного файла. Даже обфускация не защищает строковые литералы — они легко находятся в коде после декомпиляции. Переменные окружения решают эту проблему, передавая ключи на этапе сборки через CI/CD, где они маскируются в логах.
object Config {
val apiKey: String =
System.getenv("API_KEY") ?: throw
IllegalStateException("API_KEY not set")
}
Переменные окружения интегрируются с пайплайнами сборки: GitHub Actions, GitLab CI, Bitrise и CircleCI поддерживают секретные переменные, которые не отображаются в логах. На этапе сборки CI подставляет нужные значения в зависимости от ветки или тега: для ветки develop используется staging, для тега v* — production. Это автоматизирует процесс и исключает человеческий фактор, гарантируя, что каждая сборка получает правильный набор конфигурации.
Файл .env — стандартный способ хранения переменных окружения в формате KEY=VALUE. Он не включается в репозиторий, а вместо этого в репозиторий добавляется .env.example с шаблоном всех переменных и пустыми значениями. Каждый разработчик создаёт свой .env файл с локальными настройками, не затрагивая конфигурацию других членов команды. Для разных сред используются отдельные файлы: .env.dev, .env.stage, .env.prod.
# .env.example — шаблон для разработчиков
API_BASE_URL=http://localhost:8080
FEATURE_CHAT_ENABLED=true
SENTRY_DSN=
Для мобильных проектов существуют специализированные библиотеки для работы с .env файлами:
Настройки ветвления в CI/CD позволяют подставлять разные .env файлы: .env.dev для тестовых серверов, .env.stage для предрелиза и .env.prod для публикации в магазины приложений. Файлы с секретами загружаются из безопасного хранилища (Vault, AWS Secrets Manager) и не хранятся в репозитории. Это гарантирует, что даже при компрометации системы контроля версий секреты остаются защищёнными.
iOS экосистема использует xcconfig файлы для управления переменными на уровне сборки. Они подключаются к схемам Xcode и позволяют переопределять значения для Debug и Release конфигураций. xcconfig файлы поддерживают наследование: можно создать базовый файл с общими настройками и специфические файлы для каждой среды.
Файлы xcconfig хранят переменные в формате KEY = VALUE и подключаются к схеме сборки в Xcode через настройки Configuration. Переменные из xcconfig доступны в Info.plist через синтаксис $(VARIABLE_NAME), что позволяет использовать разные идентификаторы бандла и названия приложения для разных схем. Для быстрой идентификации окружения к названию приложения добавляется суффикс Dev или Staging.
# Config/Dev.xcconfig — конфигурация разработки
API_BASE_URL = http://localhost:3000
BUNDLE_ID_SUFFIX = .dev
APP_DISPLAY_NAME = MyApp Dev
Для runtime-доступа к переменным в iOS используется файл Configuration.swift, который читает значения из Info.plist через Bundle.main.object(forInfoDictionaryKey:). Этот подход гарантирует, что переменные определяются на этапе сборки и доступны приложению сразу после запуска. Значения читаются один раз при инициализации модуля и кэшируются для быстрого доступа в течение жизненного цикла приложения.
enum AppEnvironment {
static var apiBaseURL: URL {
guard let urlString = Bundle.main
.object(forInfoDictionaryKey: "API_BASE_URL"),
let url = URL(string: urlString as! String)
else { fatalError("API_BASE_URL is not configured") }
return url
}
static var isChatEnabled: Bool {
Bundle.main.object(
forInfoDictionaryKey: "FEATURE_CHAT_ENABLED"
) as? Bool ?? false
}
}
Android поддерживает переменные окружения через BuildConfig — автоматически генерируемый класс, поля которого определяются в build.gradle файле модуля. BuildConfig создаётся на этапе компиляции для каждого флейвора и типа сборки отдельно. Это позволяет иметь разные значения для debug и release без использования условных операторов в коде, что повышает производительность и безопасность.
Поля BuildConfig задаются через buildConfigField в defaultConfig или в конкретных buildTypes. Для каждой среды создаётся отдельный buildType или productFlavor. Это обеспечивает строгую изоляцию конфигураций: debug использует локальный сервер, release — продакшн. Поля BuildConfig статически типизированы, что исключает ошибки при обращении к ним в коде.
// build.gradle (Module: app)
android {
defaultConfig {
buildConfigField "String", "API_BASE_URL",
"\"http://localhost:8080\""
}
buildTypes {
debug {
buildConfigField "String", "API_BASE_URL",
"\"http://dev.api.itsectr.com\""
}
release {
buildConfigField "String", "API_BASE_URL",
"\"https://api.itsectr.com\""
}
}
}
Файл gradle.properties в корне проекта хранит глобальные переменные Gradle. Они доступны во всех модулях через синтаксис $variableName и используются для указания версий зависимостей, флагов сборки и ключей API. В отличие от BuildConfig, gradle.properties работает только на этапе конфигурации Gradle, а не в runtime приложения. Поэтому пароли и ключи API, указанные в gradle.properties, не видны в декомпилированном коде, так как они используются только для генерации BuildConfig на этапе компиляции.
# gradle.properties
SENTRY_DSN=https://key@sentry.io/project
MAPS_API_KEY=AIzaSy...
Для безопасной передачи секретов в Android проектах рекомендуется использовать local.properties (исключён из VCS) или загружать значения из CI/CD переменных в build.gradle через System.getenv(). Это гарантирует, что ключи не попадут в репозиторий. При публикации в Google Play Console убедитесь, что все отладочные ключи заменены на продакшн-версии через разные buildTypes или productFlavors с соответствующими значениями BuildConfig.
Часто задаваемые вопросы
Да, Flutter поддерживает переменные окружения через пакет flutter_dotenv для runtime-доступа или через нативные каналы для платформенных переменных. В Dart также доступен конструктор String.fromEnvironment для передачи значений на этапе компиляции через --dart-define, что является предпочтительным способом для Flutter проектов.
BuildConfig — это Java-класс с типизированными полями, сгенерированный на этапе компиляции для каждого buildType и flavor. gradle.properties — текстовый файл с парами ключ-значение, доступный всем модулям Gradle на этапе конфигурации сборки. BuildConfig работает в runtime приложения, gradle.properties — только в скриптах Gradle.
Добавьте .env в файл .gitignore вашего репозитория. В репозиторий коммитьте только .env.example с пустыми значениями и описанием каждой переменной. Для CI/CD используйте зашифрованные секреты в настройках GitHub Actions, GitLab CI или Bitrise, которые маскируются в логах и недоступны для чтения после завершения сборки.
Большинство CI-систем поддерживают секретные переменные окружения. В GitHub Actions это Secrets, в GitLab CI — CI/CD Variables, в Bitrise — Secrets. На этапе сборки они передаются в билд-скрипт через process.env или System.getenv(). Секретные переменные не отображаются в логах сборки и недоступны в форках репозитория.
Feature flags — это булевы переменные, управляющие включением или отключением функциональности без перекомпиляции кода. Пример: FEATURE_NEW_PAYMENT=true включает новую платёжную систему в staging для тестирования. В production тот же флаг установлен в false до полного развёртывания бэкенда. Это позволяет безопасно внедрять изменения поэтапно и откатывать их при проблемах.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также