Web, backend ve mobilde dev, staging ve prod için URL'leri, anahtarları ve özellik bayraklarını koddan çıkaran, güvenli ve tekrarlanabilir ortam yapılandırma desenleri.

Sabitlenmiş yapılandırma ilk günlerde makul görünür. Sonra bir staging ortamına, ikinci bir API'ye veya hızlı bir özellik anahtarına ihtiyaç duyarsınız ve “basit” değişiklik sürüm riskine dönüşür. Çözüm basit: ortam değerlerini kaynak dosyalarından çıkarın ve öngörülebilir bir düzende saklayın.
Yaygın sorun çıkaranlar kolayca görülür:
“Prod için sadece değiştir” alışkanlığı son dakikacı düzenlemelere yol açar. Bu düzenlemeler genellikle incelemeyi, testleri ve tekrarlanabilirliği atlar. Bir kişi bir URL değiştirir, başka biri bir anahtar değiştirir ve artık temel bir soruya cevap veremezsiniz: bu derlemeyle tam olarak hangi yapılandırma çıktı?
Yaygın bir senaryo: yeni bir mobil sürümü staging'e karşı oluşturursunuz, sonra biri yayın öncesi URL'yi prod'a çevirir. Backend ertesi gün tekrar değişir ve geri almak zorunda kalırsınız. Eğer URL sabitlenmişse, geri alma başka bir uygulama güncellemesi demektir. Kullanıcılar bekler, destek talepleri artar.
Amaç burada web, Go backend ve Flutter mobil uygulaması genelinde işe yarayan basit bir şemadır:
Dev, staging ve prod aynı uygulamanın üç farklı yerde çalışıyormuş gibi hissettirmeli. Amaç davranışı değil, değerleri değiştirmektir.
Değişmesi gerekenler uygulamanın çalıştığı yere veya kullanıcılar kim olduğuna bağlı olan her şeydir: temel URL'ler ve host adları, kimlik bilgileri, sandbox vs gerçek entegrasyonlar ve prod'da daha sıkı güvenlik ayarları gibi güvenlik kontrolleri.
Aynı kalması gerekenler ise mantık ve parçalar arasındaki sözleşmedir. API yolları, istek ve yanıt şekilleri, özellik isimleri ve temel iş kuralları ortama göre değişmemeli. Staging farklı davranırsa, prod'un güvenli bir provası olmaktan çıkar.
“Yeni ortam” ile “yeni yapılandırma değeri” arasındaki pratik kural: izole bir sisteme (ayrı veri, erişim ve risk) ihtiyaç duyduğunuzda yeni bir ortam oluşturun. Sadece farklı uç noktalar veya farklı sayılar gerekiyorsa, yeni bir yapılandırma değeri ekleyin.
Örnek: yeni bir arama sağlayıcısını test etmek istiyorsunuz. Küçük bir grup için güvenli şekilde etkinleştirilebiliyorsa, bir staging ortamı tutun ve bir özellik bayrağı ekleyin. Ayrı bir veritabanı ve sıkı erişim kontrolleri gerekiyorsa, o zaman yeni bir ortam mantıklıdır.
İyi bir kurulum bir işi iyi yapar: yanlışlıkla bir dev URL'si, test anahtarı veya bitmemiş bir özellik göndermeyi zorlaştırır.
Her uygulama (web, backend, mobil) için aynı üç katmanı kullanın:
Kafa karışıklığını önlemek için her uygulama için bir tek gerçek kaynağı seçin ve ona sadık kalın. Örneğin backend başlatıldığında çevre değişkenlerinden okur, web uygulaması build zamanlı değişkenlerden veya dağıtım sırasında oluşturulan küçük bir runtime config dosyasından okur, mobil uygulama build zamanında seçilen küçük bir ortam dosyasından okur. Her uygulama içinde tutarlılık, aynı mekanizmayı her yerde zorlamaktan daha önemlidir.
Basit, yeniden kullanılabilir bir şema şöyle görünür:
Her yapılandırma öğesine ne olduğunu, nerede uygulandığını ve hangi tür olduğunu cevaplayan net bir isim verin.
Pratik bir konvansiyon:
Böylece kimse “BASE_URL”in React uygulaması mı, Go servisi mi yoksa Flutter uygulaması mı olduğunu tahmin etmek zorunda kalmaz.
React kodu kullanıcının tarayıcısında çalışır; gönderdiğiniz her şey okunabilir. Amaç basit: gizli değerleri sunucuda tutmak, tarayıcının yalnızca API base URL gibi “güvenli” ayarları veya uygulama adını görmesine izin vermek.
Build-zamanı yapılandırma bundle oluşturulurken enjekte edilir. Nadir değişen ve açığa çıkarılması güvenli olan değerler için uygundur.
Runtime yapılandırma uygulama başladığında yüklenir (örneğin uygulama ile birlikte sunulan küçük bir JSON dosyasından veya enjekte edilen bir globalden). Dağıtımdan sonra değiştirmek isteyebileceğiniz değerler için daha iyidir, örneğin API base URL'i ortamlar arasında değiştirmek.
Basit kural: değiştirmek için UI'yi yeniden derlememek istiyorsanız runtime yapın.
Geliştiriciler için yerel bir dosya tutun (commit etmeyin) ve gerçek değerleri deploy hattınızda ayarlayın.
.env.local (gitignored) içinde VITE_API_BASE_URL=http://localhost:8080 gibi kullanınVITE_API_BASE_URLi ortam değişkeni olarak ayarlayın veya deploy sırasında oluşturulan runtime config dosyasına koyunRuntime örneği (uygulamanızın yanında servis edilen):
{ \"apiBaseUrl\": \"https://api.staging.example.com\", \"features\": { \"newCheckout\": false } }
Sonra bunu başlangıçta bir kez yükleyip tek bir yerde tutun:
export async function loadConfig() {
const res = await fetch('/config.json', { cache: 'no-store' });
return res.json();
}
React env değişkenlerindeki her şeyi açık kabul edin. Parolaları, özel API anahtarlarını veya veritabanı URL'lerini web uygulamasına koymayın.
Güvenli örnekler: API base URL, Sentry DSN (public), build versiyonu ve basit özellik bayrakları.
Backend yapılandırması, tiplenmiş, ortam değişkenlerinden yüklenen ve sunucu trafik kabul etmeden önce doğrulanan bir yapı olduğunda daha güvenli kalır.
Başlamak için backend'in çalışması için gerekenleri belirleyin ve bu değerleri açık hale getirin. Tipik “zorunlu” değerler:
APP_ENV (dev, staging, prod)HTTP_ADDR (örneğin :8080)DATABASE_URL (Postgres DSN)PUBLIC_BASE_URL (callback'ler ve linkler için)API_KEY (üçüncü parti servis için)Sonra bunları bir struct'a yükleyin ve eksik veya yanlış formatlı bir şey varsa hızlıca hata verin. Bu sayede kısmi dağıtım sonrası değil, saniyeler içinde sorunları bulursunuz.
package config
import (
\"errors\"
\"net/url\"
\"os\"
\"strings\"
)
type Config struct {
Env string
HTTPAddr string
DatabaseURL string
PublicBaseURL string
APIKey string
}
func Load() (Config, error) {
c := Config{
Env: mustGet(\"APP_ENV\"),
HTTPAddr: getDefault(\"HTTP_ADDR\", \":8080\"),
DatabaseURL: mustGet(\"DATABASE_URL\"),
PublicBaseURL: mustGet(\"PUBLIC_BASE_URL\"),
APIKey: mustGet(\"API_KEY\"),
}
return c, c.Validate()
}
func (c Config) Validate() error {
if c.Env != \"dev\" && c.Env != \"staging\" && c.Env != \"prod\" {
return errors.New(\"APP_ENV must be dev, staging, or prod\")
}
if _, err := url.ParseRequestURI(c.PublicBaseURL); err != nil {
return errors.New(\"PUBLIC_BASE_URL must be a valid URL\")
}
if !strings.HasPrefix(c.DatabaseURL, \"postgres://\") {
return errors.New(\"DATABASE_URL must start with postgres://\")
}
return nil
}
func mustGet(k string) string {
v, ok := os.LookupEnv(k)
if !ok || strings.TrimSpace(v) == \"\" {
panic(\"missing env var: \" + k)
}
return v
}
func getDefault(k, def string) string {
if v, ok := os.LookupEnv(k); ok && strings.TrimSpace(v) != \"\" {
return v
}
return def
}
Bu, veritabanı DSN'lerini, API anahtarlarını ve callback URL'lerini koddan ve gitten uzak tutar. Barındırılan ortamlarda bu env var'ları ortam başına enjekte edersiniz, böylece dev, staging ve prod tek bir satır bile değiştirmeden farklı olabilir.
Flutter uygulamaları genellikle iki katman yapılandırma ister: build-zamanı flavor'lar (ne gönderdiğiniz) ve runtime ayarlar (uygulamanın yeni bir sürüm olmadan değiştirebilecekleri). Bunları ayırmak “sadece bir URL değişikliği”nin acil bir yeniden derlemeye dönüşmesini engeller.
Üç flavor oluşturun: dev, staging, prod. Flavorlar build-zamanı sabit olması gereken şeyleri kontrol etsin: uygulama adı, bundle id, signing, analytics projesi ve debug araçlarının açık olup olmadığı gibi.
Sonra sadece --dart-define (veya CI) ile duyarsız varsayılanları geçirin, böylece bunları kodda asla sabitlemezsiniz:
ENV=stagingDEFAULT_API_BASE=https://api-staging.example.comCONFIG_URL=https://config.example.com/mobile.jsonDart'ta bunları String.fromEnvironment ile okuyup başlangıçta basit bir AppConfig nesnesi oluşturun.
Küçük uç nokta değişiklikleri için yeniden derlemek istemiyorsanız, API base URL'i sabit olarak ele almayın. Uygulama başlatıldığında küçük bir config dosyası çekin (ve cache'leyin). Flavor sadece konfigürasyonu nereden çekeceğini belirlesin.
Pratik bölünme:
Backend'inizi taşırsanız, remote config'i güncelleyip yeni base URL'i gösterirsiniz. Mevcut kullanıcılar bir sonraki açılışta bunu alır; son cached değere güvenli bir yedek var.
Özellik bayrakları kademeli dağıtımlar, A-B testleri, hızlı kill switch'ler ve staging'de riskli değişiklikleri prod'ta açmadan önce test etmek için yararlıdır. Güvenlik kontrollerinin yerine geçmezler. Eğer bir bayrak korunması gereken bir şeyi koruyorsa, o bir bayrak değil, bir yetkilendirme kuralıdır.
Her bayrağı bir API gibi ele alın: net bir isim, bir sahibi ve bir bitiş tarihi olsun.
Bayrak ON olduğunda ne olduğunu ve ürünün hangi kısmını etkilediğini söyleyen isimler kullanın. Basit bir şema:
feature.checkout_new_ui_enabled (müşteri tarafı özellik)ops.payments_kill_switch (acil kapatma anahtarı)exp.search_rerank_v2 (deney)release.api_v3_rollout_pct (kademeli dağıtım)debug.show_network_logs (diagnostik)Pozitif boole'leri (..._enabled) tercih edin. Arama ve denetim için sabit bir önek tutun.
Servis down olduğunda uygulamanız kararlı sürüm gibi davranmalı: güvenli varsayılanlarla başlatın.
Gerçekçi bir model: backend'de yeni bir uç nokta yayınlayın, eskisini çalışır tutun ve release.api_v3_rollout_pct ile trafiği yavaşça taşıyın. Hatalar yükselirse, hotfix olmadan geri alın.
Bayrak yığını oluşmasını önlemek için kurallar:
“Gizli” bir sızıntı olduğunda zarar verecek her şeydir. API tokenları, veritabanı parolaları, OAuth client secret'ları, imzalama anahtarları (JWT), webhook secret'ları ve özel sertifikalar gibi. Gizli olmayanlar: API base URL'ler, build numaraları, özellik bayrakları veya public analytics ID'leri.
Gizli değerleri diğer ayarlardan ayırın. Geliştiriciler güvenli yapılandırmayı serbestçe değiştirebilmeli, gizli değerler ise sadece runtime'da ve sadece gerektiği yerde enjekte edilmeli.
Geliştirmede gizliler yerel ve değiştirilebilir olmalı. .env dosyası veya işletim sistemi keychain'i kullanın ve sıfırlamayı kolay yapın. Asla commit etmeyin.
Staging ve prod'da gizliler özel bir secret store'da olmalı, kod repo'da, chat log'larda veya mobil uygulamalara gömülü şekilde olmamalı.
Rotasyon, bir anahtarı takas edip eski istemcilerin hâlâ onu kullandığını unutunca başarısız olur. Bir örtüşme penceresi planlayın.
Bu örtüşme yaklaşımı API anahtarları, webhook secret'ları ve imzalama anahtarları için işe yarar. Beklenmedik kesintileri önler.
Bir staging API'niz ve yeni bir prod API'niz var. Amaç trafiği aşamalar halinde taşımak ve bir sorun görünürse hızlıca geri almak. Uygulamanın API base URL'i config'ten okunuyorsa bu daha kolaydır.
Her yerde API URL'ini deploy-zamanı değeri gibi ele alın. Web uygulamasında (React) genellikle build-zamanı değeri veya runtime config dosyasıdır. Mobilde (Flutter) genellikle flavor + remote config'tir. Backend'te (Go) runtime env var'dır. Önemli kısım tutarlılık: kod tek bir değişken adı (örneğin API_BASE_URL) kullanır ve URL'yi bileşenlerin, servislerin veya ekranların içine gömmez.
Güvenli bir kademeli dağıtım şöyle olabilir:
Doğrulama büyük ölçüde uyumsuzlukları erken yakalamaktır. Gerçek kullanıcı değişiklikten önce, health endpoint'lerinin cevap verdiğini, auth akışlarının çalıştığını ve aynı test hesabının bir temel senaryoyu baştan sona tamamlayabildiğini doğrulayın.
Çoğu prod yapılandırma hatası sıkıcıdır: bir staging değeri kalmış, bir bayrak varsayılanı ters, veya bir bölge için API anahtarı eksik. Hızlı bir kontrol bunların çoğunu yakalar.
Deploy etmeden önce üç şeyin hedef ortama uygun olduğunu doğrulayın: endpoint'ler, gizliler ve varsayılanlar.
Sonra hızlı bir smoke testi yapın. Bir gerçek kullanıcı akışı seçip baştan sona çalıştırın, temiz bir kurulum veya temiz bir tarayıcı profili kullanın ki cache'lenmiş token'lara bel bağlamayın.
Pratik bir alışkanlık: staging'i farklı değerlerle prod gibi ele alın. Bu, aynı yapılandırma şeması, aynı doğrulama kuralları ve aynı dağıtım şekli anlamına gelir. Sadece değerler farklı olmalı, yapı değil.
Çoğu yapılandırma kesintisi egzotik değildir. Basit hatalardır; yapılandırma dosyaları, build adımları ve panolar arasında dağılmış olduğunda ve kimse “Bu uygulama şu anda hangi değerleri kullanacak?” sorusuna cevap veremediğinde ortaya çıkar. İyi bir kurulum bu soruyu kolaylaştırır.
Yaygın tuzak: runtime değerlerini build-zamanı yerlere koymak. API base URL'i bir React build'ine gömerseniz, her ortam için yeniden derlemeniz gerekir. Sonra biri yanlış artefaktı deploy eder ve prod staging'e işaret eder.
Daha güvenli kural: build'e sadece gerçekten yayın sonrası değişmeyecek değerleri koyun (örneğin uygulama versiyonu). Ortam detaylarını (API URL'leri, özellik anahtarları, analytics endpoint'leri) mümkünse runtime tutun ve tek gerçek kaynağı belirgin yapın.
Bu, varsayılanların “yardımsever” ama güvensiz olduğu zaman olur. Bir mobil uygulama config okuyamazsa dev API'yi varsayılan yapabilir veya backend bir env var eksikse yerel veritabanına düşebilir. Bu küçük bir yapılandırma hatasını tam bir kesintiye dönüştürür.
İki alışkanlık yardımcı olur:
Gerçekçi örnek: Cuma gecesi bir release çıkar, ve prod build yanlışlıkla bir staging ödeme anahtarını içerir. Her şey “çalışır” görünür ama ödemeler sessizce başarısız olur. Çözüm yeni bir ödeme kütüphanesi değil. Üretimde staging anahtarlarını reddeden doğrulama.
Prod ile eşleşmeyen staging yanlış güven sağlar. Farklı veritabanı ayarları, eksik background job'lar veya ekstra özellik bayrakları hataların sadece yayın sonrası görünmesine neden olur.
Staging'i yakın tutmak için aynı yapılandırma şemasını, aynı doğrulama kurallarını ve aynı dağıtım şeklini kopyalayın. Sadece değerler farklı olsun, yapı değil.
Amaç havalı araçlar değil. Amaç sıkıcı tutarlılık: aynı isimler, aynı tipler, aynı kurallar dev, staging ve prod boyunca. Yapılandırma öngörülebilir olunca yayınlar riskli hissetmez.
Öncelikle kısa ama spesifik bir yapılandırma sözleşmesini tek bir yerde yazın: her anahtarın adı, tipi (string, number, boolean), nereden gelebileceği (env var, remote config, build-zamanı) ve varsayılanı. İstemcide asla ayarlanamayacak değerler için notlar ekleyin (örneğin özel API anahtarları). Bu sözleşmeyi bir API gibi ele alın: değişiklikler inceleme gerektirsin.
Sonra hataların erken ortaya çıkmasını sağlayın. Eksik bir API base URL'in en iyi keşfedileceği yer CI'dir, dağıtımdan sonra değil. Uygulamanızın aynı şekilde yüklediği yapılandırmayı yükleyen ve kontrol eden otomatik doğrulama ekleyin:
Son olarak, bir yapılandırma değişikliği yanlış olduğunda kurtarmayı kolaylaştırın. Çalışanı anlık görüntüleyin, tek bir şeyi değiştirin, hızlıca doğrulayın ve geri alma yolu tutun.
Eğer Koder.ai (koder.ai) gibi bir platformla build ve deploy yapıyorsanız, aynı kurallar geçerlidir: ortam değerlerini build ve hosting için girdiler olarak ele alın, gizlileri dışarı aktarılan kaynağa koymayın ve göndermeden önce yapılandırmayı doğrulayın. Bu tutarlılık redeploy'ları ve geri alımları rutin hissettirir.
Yapılandırma belgelenmiş, doğrulanmış ve geri alınabilir olduğunda, kesintilerin kaynağı olmaktan çıkar ve gönderme sürecinin normal bir parçası haline gelir.