Zmienne środowiskowe to dynamiczne wartości przekazywane aplikacji na etapie uruchamiania w celu skonfigurowania jej działania bez modyfikacji kodu. Umożliwiają rozdzielenie konfiguracji środowisk programistycznych, testowych i produkcyjnych. Według Twelve-Factor App, 2025, konfiguracja powinna być przechowywana w zmiennych środowiskowych, a nie w kodzie. Zmienne środowiskowe zapewniają bezpieczne zarządzanie kluczami API, adresami URL backendu i flagami funkcjonalności.
Najważniejsze
Zmienne środowiskowe to para klucz-wartość, dostępna dla procesu aplikacji przez API systemu operacyjnego. Są przekazywane do procesu podczas jego tworzenia i istnieją tylko przez czas jego działania. W przeciwieństwie do parametrów konfiguracji wbudowanych w kod źródłowy, zmienne środowiskowe nie wymagają ponownej kompilacji, aby zmienić wartości. To fundamentalna zasada Twelve-Factor App, która zapewnia wyraźny podział między kodem a konfiguracją.
W programowaniu mobilnym zmienne środowiskowe rozwiązują problem różnych konfiguracji dla środowisk: programista używa lokalnego serwera, tester — staging, użytkownicy — produkcja. Zamiast przechowywać trzy adresy URL backendu w kodzie z instrukcjami warunkowymi if-else, programista przekazuje jeden URL przez zmienną środowiskową na etapie budowania. Upraszcza to kod i eliminuje ryzyko przypadkowego użycia serwera produkcyjnego w środowisku testowym.
Główną zaletą jest bezpieczeństwo: poufne dane nie trafiają do repozytorium kodu. Klucze API, sekrety Firebase, tokeny dostępu do backendu i certyfikaty są ładowane przez CI/CD bezpośrednio do środowiska budowania. Jeśli osoba nieuprawniona uzyska dostęp do repozytorium kodu, nie znajdzie tam sekretów, ponieważ są one przechowywane w zabezpieczonych magazynach systemu CI i przekazywane tylko na etapie budowania pliku binarnego.
Projekty mobilne mają co najmniej trzy środowiska: development, staging i produkcja. Każde środowisko wymaga własnego zestawu konfiguracji: adres URL serwera, nazwa pakietu, schemat podpisu i certyfikaty powiadomień push. Bez zmiennych środowiskowych programista musi ręcznie zmieniać konfigurację przed każdym budowaniem, co prowadzi do błędów: zapomniany klucz produkcyjny w kompilacji testowej może spowodować wysyłanie powiadomień do rzeczywistych użytkowników lub zużycie płatnego API.
Zmienne środowiskowe umożliwiają przełączanie backendu bez zmiany kodu: wystarczy zastąpić wartość w zmiennej API_BASE_URL. Flagi funkcjonalności (feature flags) są zarządzane przez zmienne typu FEATURE_CHAT_ENABLED=true, co pozwala włączać nowe funkcje w staging bez wpływu na produkcję. Dla każdego środowiska tworzony jest własny plik .env, który jest ładowany na etapie budowania.
class AppConfig {
static final String apiBaseUrl =
const String.fromEnvironment('API_BASE_URL',
defaultValue: 'http://localhost:8080');
}
Zakodowane na sztywno klucze to powszechna luka w aplikacjach mobilnych. Osoba nieuprawniona dekompiluje APK lub IPA za pomocą narzędzi takich jak jadx lub Hopper i wyodrębnia sekrety z pliku binarnego. Nawet obfuskacja nie chroni literałów stringów — są łatwe do znalezienia w kodzie po dekompilacji. Zmienne środowiskowe rozwiązują ten problem, przekazując klucze na etapie budowania przez CI/CD, gdzie są maskowane w logach.
object Config {
val apiKey: String =
System.getenv("API_KEY") ?: throw
IllegalStateException("API_KEY not set")
}
Zmienne środowiskowe są integrowane z pipeline'ami budowania: GitHub Actions, GitLab CI, Bitrise i CircleCI obsługują tajne zmienne, które nie są wyświetlane w logach. Na etapie budowania CI podstawia odpowiednie wartości w zależności od gałęzi lub tagu: dla gałęzi develop używane jest staging, dla tagu v* — produkcja. Automatyzuje to proces i eliminuje czynnik ludzki, gwarantując, że każde budowanie otrzymuje poprawny zestaw konfiguracji.
Plik .env to standardowy sposób przechowywania zmiennych środowiskowych w formacie KEY=VALUE. Nie jest dołączany do repozytorium, zamiast tego do repozytorium dodawany jest .env.example z szablonem wszystkich zmiennych i pustymi wartościami. Każdy programista tworzy własny plik .env z ustawieniami lokalnymi, nie wpływając na konfigurację innych członków zespołu. Dla różnych środowisk używane są osobne pliki: .env.dev, .env.stage, .env.prod.
# .env.example — szablon dla programistów
API_BASE_URL=http://localhost:8080
FEATURE_CHAT_ENABLED=true
SENTRY_DSN=
Dla projektów mobilnych istnieją wyspecjalizowane biblioteki do pracy z plikami .env:
Ustawienia gałęzi w CI/CD umożliwiają podstawianie różnych plików .env: .env.dev dla serwerów testowych, .env.stage dla przedpremierowych i .env.prod dla publikacji w sklepach aplikacji. Pliki z sekretami są ładowane z bezpiecznego magazynu (Vault, AWS Secrets Manager) i nie są przechowywane w repozytorium. Gwarantuje to, że nawet przy kompromitacji systemu kontroli wersji sekrety pozostają chronione.
Ekostystem iOS używa plików xcconfig do zarządzania zmiennymi na poziomie budowania. Są one podłączane do schematów Xcode i umożliwiają nadpisywanie wartości dla konfiguracji Debug i Release. Pliki xcconfig obsługują dziedziczenie: można utworzyć bazowy plik z wspólnymi ustawieniami i specyficzne pliki dla każdego środowiska.
Pliki xcconfig przechowują zmienne w formacie KEY = VALUE i są podłączane do schematu budowania w Xcode przez ustawienia Configuration. Zmienne z xcconfig są dostępne w Info.plist przez składnię $(VARIABLE_NAME), co pozwala używać różnych identyfikatorów pakietu i nazw aplikacji dla różnych schematów. Do szybkiej identyfikacji środowiska do nazwy aplikacji dodawany jest sufiks Dev lub Staging.
# Config/Dev.xcconfig — konfiguracja programistyczna
API_BASE_URL = http://localhost:3000
BUNDLE_ID_SUFFIX = .dev
APP_DISPLAY_NAME = MyApp Dev
Do dostępu w runtime do zmiennych w iOS używany jest plik Configuration.swift, który odczytuje wartości z Info.plist przez Bundle.main.object(forInfoDictionaryKey:). To podejście gwarantuje, że zmienne są określane na etapie budowania i dostępne dla aplikacji natychmiast po uruchomieniu. Wartości są odczytywane raz podczas inicjalizacji modułu i cachowane dla szybkiego dostępu przez cały cykl życia aplikacji.
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 obsługuje zmienne środowiskowe przez BuildConfig — automatycznie generowaną klasę, której pola są określane w pliku build.gradle modułu. BuildConfig jest tworzony na etapie kompilacji dla każdego flavora i typu budowania osobno. Pozwala to mieć różne wartości dla debug i release bez używania instrukcji warunkowych w kodzie, co zwiększa wydajność i bezpieczeństwo.
Pola BuildConfig są ustawiane przez buildConfigField w defaultConfig lub w konkretnych buildTypes. Dla każdego środowiska tworzony jest osobny buildType lub productFlavor. Zapewnia to ścisłą izolację konfiguracji: debug używa lokalnego serwera, release — produkcji. Pola BuildConfig są statycznie typowane, co eliminuje błędy przy odwołaniach do nich w kodzie.
// 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\""
}
}
}
Plik gradle.properties w katalogu głównym projektu przechowuje globalne zmienne Gradle. Są one dostępne we wszystkich modułach przez składnię $variableName i używane do określania wersji zależności, flag budowania i kluczy API. W przeciwieństwie do BuildConfig, gradle.properties działa tylko na etapie konfiguracji Gradle, a nie w runtime aplikacji. Dlatego hasła i klucze API określone w gradle.properties nie są widoczne w zdekompilowanym kodzie, ponieważ są używane tylko do generowania BuildConfig na etapie kompilacji.
# gradle.properties
SENTRY_DSN=https://key@sentry.io/project
MAPS_API_KEY=AIzaSy...
Do bezpiecznego przekazywania sekretów w projektach Android zaleca się używanie local.properties (wykluczonego z VCS) lub ładowanie wartości z zmiennych CI/CD do build.gradle przez System.getenv(). Gwarantuje to, że klucze nie trafią do repozytorium. Podczas publikacji w Google Play Console upewnij się, że wszystkie klucze debugowe zostały zastąpione wersjami produkcyjnymi przez różne buildTypes lub productFlavors z odpowiednimi wartościami BuildConfig.
Często zadawane pytania
Tak, Flutter obsługuje zmienne środowiskowe przez pakiet flutter_dotenv do dostępu w runtime lub przez kanały natywne dla zmiennych platformowych. W Dart dostępny jest również konstruktor String.fromEnvironment do przekazywania wartości na etapie kompilacji przez --dart-define, co jest preferowanym sposobem dla projektów Flutter.
BuildConfig to klasa Java z typowanymi polami, wygenerowana na etapie kompilacji dla każdego buildType i flavora. gradle.properties to plik tekstowy z parami klucz-wartość, dostępny dla wszystkich modułów Gradle na etapie konfiguracji budowania. BuildConfig działa w runtime aplikacji, gradle.properties — tylko w skryptach Gradle.
Dodaj .env do pliku .gitignore swojego repozytorium. Do repozytorium commituj tylko .env.example z pustymi wartościami i opisem każdej zmiennej. Dla CI/CD używaj zaszyfrowanych sekretów w ustawieniach GitHub Actions, GitLab CI lub Bitrise, które są maskowane w logach i niedostępne do odczytu po zakończeniu budowania.
Większość systemów CI obsługuje tajne zmienne środowiskowe. W GitHub Actions są to Secrets, w GitLab CI — CI/CD Variables, w Bitrise — Secrets. Na etapie budowania są przekazywane do skryptu budowania przez process.env lub System.getenv(). Tajne zmienne nie są wyświetlane w logach budowania i są niedostępne w forka repozytorium.
Feature flags to zmienne boolowskie sterujące włączaniem lub wyłączaniem funkcjonalności bez ponownej kompilacji kodu. Przykład: FEATURE_NEW_PAYMENT=true włącza nowy system płatności w staging do testowania. W produkcji ta sama flaga jest ustawiona na false do pełnego wdrożenia backendu. Pozwala to bezpiecznie wdrażać zmiany etapami i wycofywać je w przypadku problemów.
Podsumowanie
Opracujemy aplikację mobilną pod klucz
IT Sectr tworzy aplikacje na iOS i Androida dla startupów i firm od 2017 roku. Doradzimy Ci i zaproponujemy najlepsze rozwiązanie.
Przeczytaj również