---
title: 'Ortam Yapılandırması'
source: 'https://academia.sh/tr/kurslar/olusturma-stratejileri/ortam-yapilandirmasi'
course: 'Oluşturma Stratejileri ve Altyapı'
language: tr
updated: '2026-08-17T18:09:30+00:00'
license: 'CC BY-SA 4.0'
---

# Ortam Yapılandırması

Derleme zamanı değişkenlerinin çıktıya nasıl girdiği; ikamenin ürettiği sabitler, istemci paketine geçen her değerin herkese açık olması, geçebilen ile geçemeyen değer ayrımının denetlenebilir bir kurala dönüştürülmesi ve çalışma zamanı yapılandırmasının ödünleşimi.

Bir önceki ders derlemenin iki kipini ayırdı, ama iki kipin de ortak bir varsayımı vardı:
kaynak aynı. Kuzey Yamaç Ölçüm İstasyonu arayüzü yerelde bir deneme ucundan, yayında gerçek
ölçüm hattından veri okur; günlük ayrıntısı yerelde açık, yayında kapalıdır; arşiv yazma
işlemi yerelde geçici bir dizine, yayında kalıcı bir depoya gider. Bu farkların kaynağa
yazılması aynı kaynaktan iki ayrı çıktı üretilmesi demektir ve o noktada tek bir kaynak
ağacından söz edilemez.

Farkı taşıyan şey **derleme zamanı değişkenleridir (build-time variables)**. Bu ders önce
bu değişkenlerin çıktıya nasıl girdiğini gösterir, sonra tek kritik kuralı kurar: istemci
paketine giren bir değer herkese açıktır.

## İki Yapılandırma Anı

Sunucuda çalışan bir program yapılandırmasını çalışma zamanında okur. Süreç başlarken
ortamdan bir adı sorar, değeri alır, kullanır. Değer değiştiğinde süreç yeniden başlatılır;
program yeniden derlenmez. Ortam değişkenlerinin bir dosyadan — sürüm denetimine girmeyen
bir `.env` dosyasından — yüklenmesi bu okumanın kaynağını değiştirir, zamanını değiştirmez.

Tarayıcıda çalışan kodun böyle bir kaynağı yoktur. Sayfa bir süreç ortamının içinde açılmaz;
okuyabileceği tek şey kendisine verilmiş baytlardır. Yapılandırmanın istemci koduna girmesi
için iki yol kalır ve ikisi ayrı anlarda çalışır.

**Derleme zamanı ikamesi.** Kaynaktaki bir yer tutucu, derleme sırasında değerin kendisiyle
değiştirilir. Çıktıda değişken yoktur; sabit vardır. Değer değiştiğinde yeniden derleme
gerekir.

**Çalışma zamanı okuması.** Değer çıktıya değil, belgeye ya da ayrı bir uca konur; istemci
onu ilk isteğinde alır. Değer değiştiğinde yeniden derleme gerekmez, ama bir okuma daha
gerekir.

Bu dersin ana konusu birincisidir, çünkü ikincisinin bir ön koşulu vardır: hangi değerin
istemciye hiç verilmeyeceğine önce karar edilmiş olmalıdır.

## İkamenin Yaptığı Şey

İkame bir metin işlemidir. Aşağıdaki dönüştürücü, kaynaktaki `ORTAM.<AD>` yer tutucularını
ortam tablosundaki değerlerin kod olarak yazılmış hâliyle değiştirir ve tek bir paket üretir.

```js
// ikame.mjs — derleme zamani ortam ikamesi ve ciktida deger aramasi.
import { mkdirSync, writeFileSync, readFileSync, rmSync } from "node:fs";

// Kaynak agac: uc modul, ucu de ORTAM.<AD> bicimindeki bir yer tutucu okuyor.
const KAYNAK = {
  "olcum-istemcisi.js":
    'const UC = ORTAM.KUZEY_OLCUM_UCU;\n' +
    'export const olcumleriGetir = (istasyon) =>\n' +
    '  fetch(UC + "/istasyon/" + istasyon).then((y) => y.json());\n',
  "gunluk.js":
    'const SIRA = { ayrinti: 0, bilgi: 1, uyari: 2, hata: 3 };\n' +
    'const DUZEY = ORTAM.KUZEY_GUNLUK_DUZEYI;\n' +
    'export const yaz = (duzey, ileti) =>\n' +
    '  SIRA[duzey] >= SIRA[DUZEY] ? console.log(duzey + ": " + ileti) : undefined;\n',
  "arsiv-yazici.js":
    'const ANAHTAR = ORTAM.KUZEY_YAZMA_ANAHTARI;\n' +
    'export const arsivle = (kayit) =>\n' +
    '  fetch("/arsiv", { method: "POST", headers: { "X-Anahtar": ANAHTAR },\n' +
    '                    body: JSON.stringify(kayit) });\n',
};

// .env dosyasinin icerigi: ad=deger satirlari, surume girmez.
const ENV = [
  "KUZEY_OLCUM_UCU=https://olcum.kuzey-yamac.example/v1",
  "KUZEY_GUNLUK_DUZEYI=uyari",
  "KUZEY_YAZMA_ANAHTARI=yaz-9f21c4a7e08b",
].join("\n") + "\n";

const cozumle = (metin) => Object.fromEntries(metin.split("\n")
  .filter((s) => s.includes("="))
  .map((s) => [s.slice(0, s.indexOf("=")).trim(), s.slice(s.indexOf("=") + 1).trim()]));

const ortam = cozumle(ENV);

// Ikame: ORTAM.<AD>, degerin kaynak koda yazilmis sabiti ile degistirilir.
const ikameEt = (kaynak) => kaynak.replace(/ORTAM\.([A-Z0-9_]+)/g,
  (tam, ad) => (ad in ortam ? JSON.stringify(ortam[ad]) : tam));

rmSync("cikti", { recursive: true, force: true });
mkdirSync("cikti", { recursive: true });
mkdirSync("kaynak", { recursive: true });
writeFileSync(".env.yayin", ENV);

let paket = "";
const degisen = [];
for (const [ad, govde] of Object.entries(KAYNAK)) {
  writeFileSync("kaynak/" + ad, govde);
  const cikti = ikameEt(govde);
  paket += "// " + ad + "\n" + cikti + "\n";
  govde.split("\n").forEach((satir, i) => {
    if (satir.includes("ORTAM.")) degisen.push([ad, cikti.split("\n")[i]]);
  });
}
writeFileSync("cikti/paket.js", paket);

console.log("-- ikame edilmis satirlar --");
for (const [ad, satir] of degisen) console.log("  " + ad.padEnd(20) + satir);

const govde = readFileSync("cikti/paket.js", "utf8");
console.log("-- cikti paketinde deger aramasi --");
for (const [ad, deger] of Object.entries(ortam))
  console.log("  " + ad.padEnd(22) + (govde.includes(deger) ? "GORUNUYOR" : "yok"));
```

Betiğin kendi araması dışında, üretilen dosyada bağımsız bir arama da yapılır.

```bash
#!/usr/bin/env bash
# Ikameyi calistirir, sonra uretilen paketi dogrudan tarar.
node ikame.mjs
echo "=== bagimsiz arama ==="
grep -c 'yaz-9f21c4a7e08b' cikti/paket.js
grep -n 'X-Anahtar' cikti/paket.js
```

```
-- ikame edilmis satirlar --
  olcum-istemcisi.js  const UC = "https://olcum.kuzey-yamac.example/v1";
  gunluk.js           const DUZEY = "uyari";
  arsiv-yazici.js     const ANAHTAR = "yaz-9f21c4a7e08b";
-- cikti paketinde deger aramasi --
  KUZEY_OLCUM_UCU       GORUNUYOR
  KUZEY_GUNLUK_DUZEYI   GORUNUYOR
  KUZEY_YAZMA_ANAHTARI  GORUNUYOR
=== bagimsiz arama ===
1
15:  fetch("/arsiv", { method: "POST", headers: { "X-Anahtar": ANAHTAR },
```

Çıktının söylediği şey teknik olarak sıradan, sonuçları bakımından değil: üç değerin üçü de
paketin baytlarında düz metin olarak durur. Yazma anahtarı, kaynakta bir ad arkasındaydı;
çıktıda bir dizgi sabitidir.

Bu sonuç bir kusur değil, ikamenin tanımıdır. Çıktıda değişkeni çözecek bir düzenek
bulunmadığı için değerin oraya yazılmış olması gerekir. Aynı nedenle, ikame edilen değeri
çıktıda gizleyecek bir derleme adımı da yoktur:

- **Küçültme değerleri değiştirmez.** Ad kısaltma yerel adları değiştirir; dizgi sabitleri
  olduğu gibi kalır. Üçüncü dersteki ağaç sarsma da bir dizgiyi elemek için bir neden bulmaz.
- **Kodlama gizleme değildir.** Değeri taban 64 ile ya da basit bir karıştırmayla yazmak,
  çözücüsü aynı pakette bulunduğu için değeri kaldırmaz; adım sayısını artırır.
- **Kaynak haritası ikinci bir kopyadır.** Yayında herkese açık sunulan bir kaynak haritası,
  ikame edilmiş satırların özgün hâlini de taşır.

Buradan dersin kuralı çıkar: **istemci paketine giren bir değer, sayfayı açan herkesin
elindedir.** İkameyle giren de, çalışma zamanında verilen de aynı durumdadır.

## Gizli Değer Sınırı

Kural bir yasak listesi değil, bir sınıflandırmadır. Her yapılandırma değeri iki kümeden
birine girer ve ayrım tek bir soruyla yapılır: **bu değeri bilen biri, bilmeyen birinin
yapamayacağı bir iş yapabilir mi?**

Yanıt "hayır" ise değer istemciye geçebilir. Bu küme genel yapılandırmadır: açık uçların
adresleri, günlük düzeyi, özellik anahtarlarının açık/kapalı durumu, sürüm etiketi, genel
kiracı ya da site kimliği, yerel ayar listesi. Bunları bilmek yeni bir yetki vermez; zaten
gözlemlenebilir olan şeyi söyler.

Yanıt "evet" ise değer istemciye geçemez. Bu küme gizli değerlerdir: sunucu tarafı yazma
anahtarları, imzalama sırları, veri deposu bağlantı bilgileri, yönetim uçlarının kimlik
bilgileri, üçüncü taraf hizmetlerin gizli anahtarları. Bunları bilen biri, sunucunun
yetkisiyle iş yapabilir.

Sınırın iki sonucu vardır ve ikisi de sık ihmal edilir.

**İhtiyaç bir istisna üretmez.** İstemcinin bir gizli değere ihtiyaç duyduğu görülüyorsa,
ihtiyaç duyulan şey değer değil, o değerle yapılan iştir. Doğru çözüm işi sunucuya
taşımaktır: istemci kendi kökenindeki bir uca çağrı yapar, yetkiyi oturum taşır, gizli değer
sunucuda kalır. Yukarıdaki arşiv yazıcısının doğru biçimi budur.

**Sızmış bir değer geri alınamaz.** Çıktı indirilmiş, ara önbelleklere ve tarayıcı
önbelleklerine yerleşmiştir; beşinci derste eski adların bir süre daha yaşadığı da
söylenmişti. Yayını geri almak değeri geri getirmez. Tek karşılık, değerin geçersiz
kılınması ve yenisiyle değiştirilmesidir. Bu yüzden sınır, sızıntıdan sonra düzeltilecek bir
şey değil, derlemeyi durduran bir denetimdir.

Aynı gerekçe derlemenin yapıldığı yere de uzanır. Gizli değerler bir sürekli tümleştirme
hattında ortama verilir; hattın günlüğüne yazılan her satır, o hattın çıktısını görebilen
herkese açıktır. İkameyi yapan aracın hangi adları çıktıya koyduğunu günlüğe basması,
sızıntının en sessiz biçimidir.

## Sınırı Denetlemek

Sınıflandırma bir belgede kaldığı sürece uygulanmaz. Denetlenebilir olması için üç şey
gerekir: adların kapsamının tek bir yerde bildirilmesi, bildirimin adlandırmaya yansıması ve
çıktının bildirime karşı sınanması.

Aşağıdaki denetleyici bunu beş kuralla yapar. `K1` bildirilmemiş adı, `K2` istemci
kaynağında okunan sunucu kapsamlı adı, `K3` açık önek taşımayan istemci adını, `K4` çıktı
baytlarında bulunan bir gizli değeri, `K5` ortamda karşılığı olmadığı için çıktıda kalmış
yer tutucuyu bildirir.

```js
// denetci.mjs — istemciye acik degisken ile sunucuda kalan degiskeni ayiran denetleyici.
// Kullanim: node denetci.mjs ilk | node denetci.mjs duzeltilmis
const ONEK = "KUZEY_ACIK_";

// Bildirim: her adin kapsami tek bir yerde yazilir. Denetim bu tabloya bakar.
const BILDIRIM = {
  KUZEY_ACIK_OLCUM_UCU: "istemci",
  KUZEY_ACIK_GUNLUK_DUZEYI: "istemci",
  KUZEY_ACIK_SURUM_ETIKETI: "istemci",
  KUZEY_OLCUM_UCU: "istemci",
  KUZEY_GUNLUK_DUZEYI: "istemci",
  KUZEY_YAZMA_ANAHTARI: "sunucu",
  KUZEY_IMZALAMA_SIRRI: "sunucu",
};

const YAZ = 'const ANAHTAR = ORTAM.KUZEY_YAZMA_ANAHTARI;\n' +
  'export const arsivle = (kayit) => istek("/arsiv", ANAHTAR, kayit);\n';

const SURUM = {
  ilk: {
    istemci: {
      "olcum-istemcisi.js": 'const UC = ORTAM.KUZEY_OLCUM_UCU;\n',
      "gunluk.js": 'const DUZEY = ORTAM.KUZEY_GUNLUK_DUZEYI;\n',
      "dipnot.js": 'export const dipnot = () => "surum " + ORTAM.KUZEY_ACIK_SURUM_ETIKETI;\n',
      "arsiv-yazici.js": YAZ,
    },
    sunucu: {},
    env: {
      KUZEY_OLCUM_UCU: "https://olcum.kuzey-yamac.example/v1",
      KUZEY_GUNLUK_DUZEYI: "uyari",
      KUZEY_YAZMA_ANAHTARI: "yaz-9f21c4a7e08b",
      KUZEY_IMZALAMA_SIRRI: "imza-4d70b1cc52ae",
    },
  },
  duzeltilmis: {
    istemci: {
      "olcum-istemcisi.js": 'const UC = ORTAM.KUZEY_ACIK_OLCUM_UCU;\n',
      "gunluk.js": 'const DUZEY = ORTAM.KUZEY_ACIK_GUNLUK_DUZEYI;\n',
      "dipnot.js": 'export const dipnot = () => "surum " + ORTAM.KUZEY_ACIK_SURUM_ETIKETI;\n',
      // Arsiv yazimi ayni kokendeki uca tasindi; anahtar istemci kaynaginda gecmiyor.
      "arsiv-gonderici.js": 'export const arsivle = (kayit) => istek("/uc/arsiv", kayit);\n',
    },
    sunucu: { "arsiv-yazici.js": YAZ },
    env: {
      KUZEY_ACIK_OLCUM_UCU: "https://olcum.kuzey-yamac.example/v1",
      KUZEY_ACIK_GUNLUK_DUZEYI: "uyari",
      KUZEY_ACIK_SURUM_ETIKETI: "2026.03",
      KUZEY_YAZMA_ANAHTARI: "yaz-9f21c4a7e08b",
      KUZEY_IMZALAMA_SIRRI: "imza-4d70b1cc52ae",
    },
  },
};

const okunanlar = (kaynaklar) => [...new Set(Object.values(kaynaklar)
  .flatMap((g) => [...g.matchAll(/ORTAM\.([A-Z0-9_]+)/g)].map(([, ad]) => ad)))];

const paketle = (kaynaklar, env) => Object.values(kaynaklar).join("")
  .replace(/ORTAM\.([A-Z0-9_]+)/g, (tam, ad) => (ad in env ? JSON.stringify(env[ad]) : tam));

function denetle(surum) {
  const { istemci, sunucu, env } = SURUM[surum];
  const paket = paketle(istemci, env);
  const bulgu = [];
  const ekle = (kural, ad, ileti) => bulgu.push([kural, ad, ileti]);

  for (const ad of okunanlar(istemci)) {
    if (!(ad in BILDIRIM)) ekle("K1", ad, "bildirimde yok");
    else if (BILDIRIM[ad] === "sunucu") ekle("K2", ad, "sunucu kapsamli ad istemcide okunuyor");
    else if (!ad.startsWith(ONEK)) ekle("K3", ad, "istemci kapsamli ad " + ONEK + " onekini tasimiyor");
    if (!(ad in env)) ekle("K5", ad, "ortamda tanimli degil, yer tutucu ciktida kaldi");
  }
  for (const [ad, kapsam] of Object.entries(BILDIRIM))
    if (kapsam === "sunucu" && ad in env && paket.includes(env[ad]))
      ekle("K4", ad, "degeri istemci paketinin baytlarinda bulundu");

  console.log("surum: " + surum);
  console.log("  istemci paketi: " + Object.keys(istemci).length + " modul, okunan ad: " +
    okunanlar(istemci).join(", "));
  console.log("  sunucu paketi : " + Object.keys(sunucu).length + " modul, okunan ad: " +
    (okunanlar(sunucu).join(", ") || "-"));
  for (const [kural, ad, ileti] of bulgu)
    console.log("  " + kural + "  " + ad.padEnd(26) + ileti);
  console.log("  ihlal: " + bulgu.length);
  return bulgu.length;
}

process.exitCode = denetle(process.argv[2]) > 0 ? 1 : 0;
```

```bash
#!/usr/bin/env bash
# Iki surumu de denetler ve cikis kodlarini yazar.
node denetci.mjs ilk;          echo "cikis kodu: $?"
node denetci.mjs duzeltilmis;  echo "cikis kodu: $?"
```

```
surum: ilk
  istemci paketi: 4 modul, okunan ad: KUZEY_OLCUM_UCU, KUZEY_GUNLUK_DUZEYI, KUZEY_ACIK_SURUM_ETIKETI, KUZEY_YAZMA_ANAHTARI
  sunucu paketi : 0 modul, okunan ad: -
  K3  KUZEY_OLCUM_UCU           istemci kapsamli ad KUZEY_ACIK_ onekini tasimiyor
  K3  KUZEY_GUNLUK_DUZEYI       istemci kapsamli ad KUZEY_ACIK_ onekini tasimiyor
  K5  KUZEY_ACIK_SURUM_ETIKETI  ortamda tanimli degil, yer tutucu ciktida kaldi
  K2  KUZEY_YAZMA_ANAHTARI      sunucu kapsamli ad istemcide okunuyor
  K4  KUZEY_YAZMA_ANAHTARI      degeri istemci paketinin baytlarinda bulundu
  ihlal: 5
cikis kodu: 1
surum: duzeltilmis
  istemci paketi: 4 modul, okunan ad: KUZEY_ACIK_OLCUM_UCU, KUZEY_ACIK_GUNLUK_DUZEYI, KUZEY_ACIK_SURUM_ETIKETI
  sunucu paketi : 1 modul, okunan ad: KUZEY_YAZMA_ANAHTARI
  ihlal: 0
cikis kodu: 0
```

İlk sürümün beş bulgusu üç ayrı kusur sınıfını gösterir.

**Adlandırma kusuru.** İki `K3` bulgusu bir sızıntı değildir; sızıntıyı görünmez kılan
koşuldur. Açık önek, bir adı okuyan geliştiriciye o değerin çıktıya gireceğini adın kendisi
üzerinden söyler. Önek yoksa `K2` ile `K3` arasındaki fark bir dikkat meselesine iner.

**Eksik yapılandırma.** `K5`, ortamda karşılığı olmayan bir adın ikame edilmeden çıktıda
kaldığını bildirir. Bu durumda çıktıda tanımsız bir başvuru kalır ve hata çalışma zamanında,
o satır yürütüldüğünde ortaya çıkar. Derleme zamanında yakalanabilecek bir kusurun kullanıcı
tarafında ortaya çıkması, denetimin var oluş nedenidir.

**Sınır ihlali.** `K2` ile `K4` aynı sorunun iki farklı yerden görülmüş hâlidir: biri
kaynağa, öteki çıktının baytlarına bakar. İkisi birden gerekir. Kaynak denetimi, ada
dokunmadan değeri elle yazan bir kodu yakalayamaz; bayt denetimi ise adın nereden geldiğini
söyleyemez.

Düzeltilmiş sürümde arşiv yazıcısı istemci kümesinden çıkarılıp sunucu kümesine alınmıştır.
İstemcide kalan modül aynı kökendeki bir uca çağrı yapar; anahtar hiçbir aşamada istemci
kaynağına girmez.

## Yeniden Derlemeyi Gerektirmeyen Yapılandırma

Derleme zamanı ikamesinin bir bedeli vardır ve bu bedel dağıtım konusunun sorusudur: ikame
edilen her değer, çıktıyı o ortama bağlar. Beş ortam varsa beş ayrı derleme çıktısı olur ve
önizlemede sınanan baytlar, yayına giden baytlar değildir.

Çalışma zamanı yapılandırması bu bağı çözer. Ortama göre değişen değerler pakete girmez;
belgeye küçük bir bölüm olarak ya da ayrı bir uca konur, istemci onları okuyup kullanır.
Aynı derleme çıktısı her ortamda çalışır.

Ödünleşim üç ölçüde okunur. **Derleme sayısı** ortam sayısı kadardan bire iner. Buna karşılık
**ilk görünüme kadar okunması gereken kaynak sayısı** artar; değer ayrı bir uçtan geliyorsa
bu bir ağ turu daha demektir. Değer belgeye gömülürse tur eklenmez, ama **belgenin
önbelleklenebilirliği** düşer: beşinci dersin politikasında belge zaten her kullanımda
yeniden doğrulanıyordu, dolayısıyla bu iki karar birbiriyle uyumludur.

Seçim ölçütü şudur: bir değer ortamlar arasında değişiyor ve aynı çıktının birden çok ortamda
çalışması isteniyorsa çalışma zamanında verilir; değer bütün ortamlarda aynıysa ya da
değerinin derleme kararlarını etkilemesi gerekiyorsa — örneğin ölü kod elemesinin bir
özelliği tümüyle çıkarabilmesi için — derleme zamanında ikame edilir.

İki yolun **değiştirmediği** tek şey sınırdır. Çalışma zamanında verilen bir değer de
istemciye ulaştığı anda herkese açıktır; onu belgeye yazmak, pakete yazmaktan farklı bir
güvenlik durumu üretmez.

## Özet

- Tarayıcıda çalışan kodun süreç ortamı yoktur; yapılandırma ona ya derleme zamanında ikame
  edilerek ya da çalışma zamanında okutularak ulaşır.
- İkame, yer tutucuyu değerin sabiti ile değiştirir; örnekte üç değerin üçü de çıktı
  paketinin baytlarında düz metin olarak bulunur.
- Küçültme, kodlama ve ad kısaltma değerleri gizlemez; kaynak haritası ikinci bir kopya
  taşır. İstemci paketine giren değer, sayfayı açan herkesin elindedir.
- Ayrım tek soruyla yapılır: değeri bilen biri, bilmeyen birinin yapamayacağı bir iş
  yapabiliyorsa değer gizlidir ve istemciye geçemez; iş sunucuya taşınır.
- Sınır denetlenebilir olmalıdır: kapsam bildirimi, açık önek ve hem kaynağa hem çıktı
  baytlarına bakan bir denetim, ihlalde derlemeyi durdurur.
- Çalışma zamanı yapılandırması derleme sayısını bire indirir ama bir okuma ekler; sızmış
  bir gizli değer ise ancak geçersiz kılınarak karşılanır.

## Sonraki Adım

Derleme çıktısı artık belirlenmiş durumda: parçaları, varlıkları, karmalı adları ve ortamına
bağlı sabitleriyle bir dizin. Bu dizin bir makinenin diskinde durduğu sürece kimsenin işine
yaramaz. Sıradaki soru, o baytların kullanıcının tarayıcısına hangi yolla ulaştığıdır.
En yalın yol, çıktıyı olduğu gibi kopyalayıp kullanıcılara yakın duran düğümlerden
sunmaktır: hiçbir sunucu süreci çalışmaz, her istek bir dosyaya karşılık gelir. Sonraki ders
bu yolu kurar, kenar önbelleğinde isabet ile ıskayı ölçer ve beşinci dersteki ada göre
önbellek politikasının isabet oranına dönüşümünü hesaplar.
