---
title: 'Yerel Geliştirme Ortamı'
source: 'https://academia.sh/tr/kurslar/sunucu-temelleri/yerel-gelistirme-ortami'
course: 'Sunucu Tarafı Temelleri'
language: tr
updated: '2026-08-17T18:06:53+00:00'
license: 'CC BY-SA 4.0'
---

# Yerel Geliştirme Ortamı

Bağımlı servislerin geliştiricinin makinesinde ayağa kaldırılması: başlatma sırasının zincir oluşturması, hazır olma denetiminin bağlantı denemesi ve geri çekilmeyle kurulması, sabit beklemenin neden bir tahmin olduğu ve aynı ortamın iki makinede sürüm sabitleme ile tohum verisi kurallarından yeniden üretilmesi.

Önceki ders sunucunun diske yazdığı ilk kalıcı veriyi ortaya çıkardı ve kayıtların bellekte
durmasının bir eksik olduğunu söyledi. Gerçek kurulumda o kayıtlar bir veritabanında,
arama ise ayrı bir serviste durur. Kütüphane ödünç uygulaması artık tek başına çalışan bir
süreç değildir: yanında **bağımlı servisler** (dependent services) ister.

Bu, geliştiricinin makinesinde yeni bir soruna yol açar. Uygulamayı çalıştırmak için önce
başka programları çalıştırmak gerekir, onların da kendi hazırlık işleri vardır ve
uygulamanın onlara ne zaman güvenebileceği belli değildir. Bu ders üç soruyu yanıtlar:
servisler hangi sırayla başlar, bir servisin hazır olduğu nasıl anlaşılır ve aynı ortam iki
ayrı makinede nasıl birebir aynı kurulur?

## Bağımlılıklar Bir Zincir Kurar

Yerel ortamın kararları tek bir dosyada toplanır. Bu dosya bir **ortam bildirimi**
(environment manifest) olarak çalışır: bağlantı noktaları, şema sürümü, çalışma zamanının
en az sürümü ve tohum verisi kümeleri.

```json
{
  "calisma_zamani": { "ad": "node", "en_az_ana_surum": 22 },
  "sema_surumu": 3,
  "tohum_kumesi": "kucuk",
  "tohum_satir": { "kucuk": 150000, "buyuk": 1000000 },
  "baglanti_noktalari": { "veritabani": 8431, "arama": 8432, "uygulama": 8433 }
}
```

İlk bağımlılık katalog verisini tutan servistir. Davranışındaki ayrıntı bu dersin
merkezidir: bağlantı noktasını hemen açar, ama şema ve tohum verisi yazılmadan sorgu kabul
etmez. Ayrıca kendi durumunu bildiren bir **sağlık ucu** (health endpoint) sunar.

```js
// veritabani.mjs — katalog verisini tutan servis; hazir olmadan sorgu kabul etmez
import { createServer } from "node:http";
import { DatabaseSync } from "node:sqlite";
import { readFileSync, rmSync } from "node:fs";

const ortam = JSON.parse(readFileSync("ortam.json", "utf8"));
const kume = process.env.TOHUM_KUMESI ?? ortam.tohum_kumesi;
const satir = ortam.tohum_satir[kume];
const kapi = ortam.baglanti_noktalari.veritabani;

rmSync("kutuphane.db", { force: true });
const db = new DatabaseSync("kutuphane.db");
let hazir = false;

const json = (yanit, kod, govde) => {
  const metin = JSON.stringify(govde);
  yanit.setHeader("Content-Type", "application/json; charset=utf-8");
  yanit.setHeader("Content-Length", Buffer.byteLength(metin));
  yanit.writeHead(kod).end(metin);
};

createServer((istek, yanit) => {
  yanit.sendDate = false;
  const adres = new URL(istek.url, "http://yerel");
  if (adres.pathname === "/saglik")
    return json(yanit, hazir ? 200 : 503, { hazir, sema: ortam.sema_surumu });
  if (!hazir) return json(yanit, 503, { hata: "hazir_degil" });
  if (adres.pathname === "/kitaplar") {
    const yazar = adres.searchParams.get("yazar") ?? "Yazar 42";
    const satirlar = db.prepare(
      "SELECT isbn, ad, yazar FROM kitap WHERE yazar = ? ORDER BY isbn").all(yazar);
    return json(yanit, 200, { sayi: satirlar.length, kitaplar: satirlar });
  }
  return json(yanit, 404, { hata: "bulunamadi" });
}).listen(kapi, "127.0.0.1", () => {
  console.log(`veritabani :${kapi} acildi (hazir degil)`);
  // Hazirlik isi baglanti noktasi acildiktan sonra, parcalar halinde yurur: boylece
  // hazir olmayan sunucu istekleri yanitsiz birakmaz, durumunu bildiren bir yanit dondurur.
  const basladi = Date.now();
  db.exec("CREATE TABLE kitap (isbn TEXT PRIMARY KEY, ad TEXT NOT NULL, yazar TEXT NOT NULL)");
  const ekle = db.prepare("INSERT INTO kitap (isbn, ad, yazar) VALUES (?, ?, ?)");
  db.exec("BEGIN");
  let yazilan = 0;
  const parcaYaz = () => {
    for (const son = Math.min(yazilan + 10000, satir); yazilan < son; yazilan++)
      ekle.run(`978-${String(yazilan).padStart(10, "0")}`,
               `Kitap ${yazilan}`, `Yazar ${yazilan % 500}`);
    if (yazilan < satir) return setImmediate(parcaYaz);
    db.exec("COMMIT");
    db.exec("CREATE INDEX ix_yazar ON kitap(yazar)");
    hazir = true;
    console.log(`veritabani hazir: ${kume} tohum, ${satir} satir, ${Date.now() - basladi} ms`);
  };
  setImmediate(parcaYaz);
});
```

İkinci bağımlılık arama servisidir ve zinciri kuran şey onun açılışıdır: dizinini
veritabanından okuyarak kurar, bu yüzden veritabanı hazır değilse hiç başlayamaz.

```js
// arama.mjs — ikinci HTTP servisi; dizinini acilista veritabanindan okur
import { createServer } from "node:http";
import { readFileSync } from "node:fs";

const ortam = JSON.parse(readFileSync("ortam.json", "utf8"));
const { veritabani, arama } = ortam.baglanti_noktalari;

const yanit = await fetch(`http://127.0.0.1:${veritabani}/kitaplar?yazar=Yazar%2042`)
  .catch((e) => { console.error(`arama: veritabanina ulasilamadi (${e.cause?.code ?? e.name})`);
                  process.exit(1); });
if (!yanit.ok) { console.error(`arama: veritabani ${yanit.status} verdi, dizin kurulamadi`);
                 process.exit(1); }

const { kitaplar } = await yanit.json();
const dizin = new Map(kitaplar.map((k) => [k.ad.toLowerCase(), k.isbn]));

createServer((istek, cikti) => {
  cikti.sendDate = false;
  const terim = new URL(istek.url, "http://yerel").searchParams.get("q")?.toLowerCase() ?? "";
  const bulunan = [...dizin].filter(([ad]) => ad.includes(terim)).map(([ad, isbn]) => ({ ad, isbn }));
  const govde = JSON.stringify({ dizinBoyu: dizin.size, bulunan });
  cikti.setHeader("Content-Type", "application/json; charset=utf-8");
  cikti.setHeader("Content-Length", Buffer.byteLength(govde));
  cikti.writeHead(200).end(govde);
}).listen(arama, "127.0.0.1", () => console.log(`arama :${arama} hazir, dizin ${dizin.size} kayit`));
```

Zincirin son halkası, ikisini birden kullanan ödünç uygulamasıdır.

```js
// uygulama.mjs — odunc uygulamasi; iki bagimli servisi de kullanir
import { createServer } from "node:http";
import { readFileSync } from "node:fs";

const ortam = JSON.parse(readFileSync("ortam.json", "utf8"));
const { veritabani, arama, uygulama } = ortam.baglanti_noktalari;

createServer(async (istek, cikti) => {
  cikti.sendDate = false;
  const q = new URL(istek.url, "http://yerel").searchParams.get("q") ?? "kitap 42";
  let kod = 200, govde;
  try {
    const a = await fetch(`http://127.0.0.1:${arama}/?q=${encodeURIComponent(q)}`).then((r) => r.json());
    const v = await fetch(`http://127.0.0.1:${veritabani}/saglik`).then((r) => r.json());
    govde = { sorgu: q, eslesme: a.bulunan.length, dizinBoyu: a.dizinBoyu, veritabani: v };
  } catch (e) {
    kod = 503;
    govde = { hata: "bagimli_servis_yok", ayrinti: e.cause?.code ?? e.name };
  }
  const metin = JSON.stringify(govde);
  cikti.setHeader("Content-Type", "application/json; charset=utf-8");
  cikti.setHeader("Content-Length", Buffer.byteLength(metin));
  cikti.writeHead(kod).end(metin);
}).listen(uygulama, "127.0.0.1", () => console.log(`uygulama :${uygulama} hazir`));
```

Katalog verisinin burada kendi yazdığımız bir süreçte durması, dersin konusunu görünür
kılmak içindir; yerini bir veritabanı sunucusu aldığında davranış aynı kalır. Açılışta
yapılan hazırlık işi de aynı yerde durur: dosya biçiminin denetlenmesi, kütüğün onarılması,
dizinlerin açılması.

## Hazır Olmak, Başlamış Olmaktan Farklıdır

Bir süreci başlatmak ile o sürecin iş görebilir olması iki ayrı olaydır ve aralarında bir
pencere vardır. **Hazır olma denetimi** (readiness check) bu pencereyi bekleyen bir
kapıdır: bağımlı servise denenerek sorulur, olumsuz yanıtta beklenir ve yeniden denenir.
Bekleme süresi her başarısız denemede iki katına çıkar — Uygulama Mimarisi kursunda
tanıtılan **üstel geri çekilme** (exponential backoff) burada da geçerlidir: ilk denemeler
sık, sonrakiler seyrek olur, böylece hızlı açılan bir servis hemen yakalanır, yavaş açılan
bir servis boşuna yoklanmaz.

```js
// bekle.mjs — hazir olma denetimi. Kullanim: node bekle.mjs <kapi|saglik> <baglanti-noktasi> [ad]
import { connect } from "node:net";
const [kip, kapi, ad = "servis"] = process.argv.slice(2);

const kapiAcikMi = (p) => new Promise((coz) => {
  const s = connect({ port: Number(p), host: "127.0.0.1" });
  s.once("connect", () => { s.destroy(); coz(true); });
  s.once("error", () => coz(false));
});
const saglikliMi = async (p) =>
  fetch(`http://127.0.0.1:${p}/saglik`).then((r) => r.status === 200).catch(() => false);

const dene = kip === "kapi" ? kapiAcikMi : saglikliMi;
const basladi = Date.now();
let gecikme = 20;      // ilk bekleme, ms; her basarisiz denemede iki katina cikar
let deneme = 0;
while (Date.now() - basladi < 5000) {
  deneme++;
  if (await dene(kapi)) {
    console.log(`  ${ad}: hazir (${kip} kipi, ${deneme} deneme, ${Date.now() - basladi} ms)`);
    process.exit(0);
  }
  await new Promise((c) => setTimeout(c, gecikme));
  gecikme = Math.min(gecikme * 2, 500);
}
console.error(`  ${ad}: 5 s icinde hazir olmadi`);
process.exit(1);
```

İki kip arasındaki fark ölçümün konusudur. **Bağlantı noktası denetimi** sorunun yalnız bir
kısmını sorar: birisi bu adreste dinliyor mu? **Sağlık denetimi** asıl soruyu sorar: bu
servis iş görebiliyor mu? Başlatma betiği dört ayrı geçidi karşılaştırabilecek biçimde
yazılır.

```bash
#!/usr/bin/env bash
# ayaga-kaldir.sh — bagimli servisleri sirayla ayaga kaldirir.
# Kullanim: ./ayaga-kaldir.sh <yok|sabit|kapi|saglik>;  TOHUM_KUMESI=kucuk|buyuk
kip="${1:-saglik}"; kume="${TOHUM_KUMESI:-kucuk}"
pids=(); temizle() { kill "${pids[@]}" 2>/dev/null; wait 2>/dev/null; }; trap temizle EXIT
echo "== gecit=$kip  tohum=$kume =="

TOHUM_KUMESI="$kume" node veritabani.mjs & pids+=($!)
case "$kip" in
  yok)    ;;                                                 # gecit yok
  sabit)  sleep 0.5 ;;                                       # sabit bekleme: bir tahmin
  kapi)   node bekle.mjs kapi   8431 veritabani || exit 1 ;; # baglanti kabul ediyor mu
  saglik) node bekle.mjs saglik 8431 veritabani || exit 1 ;; # is gorebiliyor mu
esac

node arama.mjs & pids+=($!)
node bekle.mjs kapi 8432 arama || { echo "  -> zincir kirildi: arama ayaga kalkmadi"; exit 1; }

node uygulama.mjs & pids+=($!)
node bekle.mjs kapi 8433 uygulama > /dev/null
printf 'istek -> '
curl -sS --max-time 5 -w ' (HTTP %{http_code})\n' "http://127.0.0.1:8433/?q=kitap%2042"
```

## Ölçüm: Dört Geçit

```bash
for k in yok sabit kapi saglik; do ./ayaga-kaldir.sh $k; echo; done
```

```
== gecit=yok  tohum=kucuk ==
arama: veritabanina ulasilamadi (ECONNREFUSED)
veritabani :8431 acildi (hazir degil)
veritabani hazir: kucuk tohum, 150000 satir, 128 ms
  arama: 5 s icinde hazir olmadi
  -> zincir kirildi: arama ayaga kalkmadi

== gecit=sabit  tohum=kucuk ==
veritabani :8431 acildi (hazir degil)
veritabani hazir: kucuk tohum, 150000 satir, 136 ms
arama :8432 hazir, dizin 300 kayit
  arama: hazir (kapi kipi, 2 deneme, 23 ms)
uygulama :8433 hazir
istek -> {"sorgu":"kitap 42","eslesme":3,"dizinBoyu":300,"veritabani":{"hazir":true,"sema":3}} (HTTP 200)

== gecit=kapi  tohum=kucuk ==
veritabani :8431 acildi (hazir degil)
  veritabani: hazir (kapi kipi, 2 deneme, 23 ms)
arama: veritabani 503 verdi, dizin kurulamadi
veritabani hazir: kucuk tohum, 150000 satir, 136 ms
  arama: 5 s icinde hazir olmadi
  -> zincir kirildi: arama ayaga kalkmadi

== gecit=saglik  tohum=kucuk ==
veritabani :8431 acildi (hazir degil)
veritabani hazir: kucuk tohum, 150000 satir, 129 ms
  veritabani: hazir (saglik kipi, 4 deneme, 177 ms)
arama :8432 hazir, dizin 300 kayit
  arama: hazir (kapi kipi, 2 deneme, 23 ms)
uygulama :8433 hazir
istek -> {"sorgu":"kitap 42","eslesme":3,"dizinBoyu":300,"veritabani":{"hazir":true,"sema":3}} (HTTP 200)
```

Süreler makineye bağlıdır; arka plandaki üç sürecin yazdığı satırların sırası da her
çalıştırmada değişir. Değişmeyen, hangi geçidin zinciri kurduğudur.

**Geçit yokken** arama servisi veritabanının bağlantı noktası açılmadan istek yapar ve
bağlantı reddedilir. Süreç çıkar, ondan sonrası hiç kurulmaz. Başlatma sırası bir tercih
değil, bir zorunluluktur.

**Bağlantı noktası denetimi** en öğretici satırı üretir. Geçit 23 milisaniyede, ikinci
denemede geçmiştir — çünkü bağlantı noktası gerçekten açıktır. Ama veritabanı o anda hâlâ
tohum verisini yazmaktadır ve aramaya `503` döndürür. Zincir yine kırılır. Dinleyen bir
bağlantı noktası, iş gören bir servis demek değildir.

**Sağlık denetimi** aynı işi 177 milisaniyede ve dört denemede yapar: geçit, servisin kendi
bildirdiği hazır olma durumunu bekler. Zincir kurulur, uygulama iki bağımlılığını da
kullanarak yanıt üretir.

## Sabit Bekleme Bir Tahmindir

Sabit bekleme küçük tohum kümesinde çalıştı. Bu, doğru olduğunu değil, tahminin tuttuğunu
gösterir. Aynı betik daha büyük bir tohum kümesiyle çalıştırıldığında tahmin bozulur.

```bash
for k in sabit saglik; do TOHUM_KUMESI=buyuk ./ayaga-kaldir.sh $k; echo; done
```

```
== gecit=sabit  tohum=buyuk ==
veritabani :8431 acildi (hazir degil)
arama: veritabani 503 verdi, dizin kurulamadi
veritabani hazir: buyuk tohum, 1000000 satir, 860 ms
  arama: 5 s icinde hazir olmadi
  -> zincir kirildi: arama ayaga kalkmadi

== gecit=saglik  tohum=buyuk ==
veritabani :8431 acildi (hazir degil)
veritabani hazir: buyuk tohum, 1000000 satir, 868 ms
  veritabani: hazir (saglik kipi, 7 deneme, 1179 ms)
arama :8432 hazir, dizin 2000 kayit
  arama: hazir (kapi kipi, 3 deneme, 64 ms)
uygulama :8433 hazir
istek -> {"sorgu":"kitap 42","eslesme":23,"dizinBoyu":2000,"veritabani":{"hazir":true,"sema":3}} (HTTP 200)
```

Yarım saniyelik bekleme, hazırlık 860 milisaniye sürdüğü için yetmez. Sağlık denetimi ise
aynı betikte, hiçbir değişiklik yapılmadan 1179 milisaniye bekler ve geçer. Sabit bekleme
iki yönde de yanılır: kısa olduğunda zinciri kırar, uzun olduğunda her açılışta boşa süre
harcar. Denetim ise beklemeyi ölçüme bağlar.

## Aynı Ortam İki Makinede

Zincirin kurulması yetmez; aynı zincirin başka bir makinede aynı sonucu vermesi gerekir.
Bunu bozan iki şey vardır: farklı çalışma zamanı sürümleri ve her kurulumda farklı çıkan
tohum verisi.

Birincisi **sürüm sabitlemesiyle** karşılanır: bildirimde yazan en az sürüm, açılışta
çalışan sürümle karşılaştırılır ve uyuşmazlık sessizce geçilmez. İkincisi bir kuralla
karşılanır: **tohum verisi girdidir, üretilen bir şey değildir**. Kimlikler, tarihler ve
ilişkiler tohumun parçası olarak yazılır; çalışma anından ya da rastgelelikten alınmaz.

```js
// tohum.mjs — tohum verisini yazar. Kullanim: node tohum.mjs <dosya> <sabit|serbest>
import { DatabaseSync } from "node:sqlite";
import { rmSync } from "node:fs";

const [dosya, kural = "sabit"] = process.argv.slice(2);
rmSync(dosya, { force: true });
const db = new DatabaseSync(dosya);
db.exec(`CREATE TABLE uye (id INTEGER PRIMARY KEY, ad TEXT NOT NULL, kayit TEXT NOT NULL);
         CREATE TABLE odunc (id INTEGER PRIMARY KEY, uye INTEGER NOT NULL, isbn TEXT NOT NULL)`);

const UYELER = ["Ayse Kaya", "Baris Demir", "Ceren Yildiz", "Deniz Aksoy"];
const ISBNLER = ["978-0000000001", "978-0000000002", "978-0000000003"];
const uyeEkle = db.prepare("INSERT INTO uye (id, ad, kayit) VALUES (?, ?, ?)");
const oduncEkle = db.prepare("INSERT INTO odunc (id, uye, isbn) VALUES (?, ?, ?)");

UYELER.forEach((ad, i) => {
  // sabit kural: tarih tohumun parcasidir. serbest kural: calisma anindan gelir.
  const kayit = kural === "sabit" ? `2024-01-0${i + 1}` : new Date().toISOString();
  uyeEkle.run(i + 1, ad, kayit);
});
ISBNLER.forEach((isbn, i) => {
  const uye = kural === "sabit"
    ? (i % UYELER.length) + 1
    : 1 + Math.floor(Math.random() * UYELER.length);
  oduncEkle.run(i + 1, uye, isbn);
});
db.close();
```

Kuralın tutup tutmadığı ölçülebilir: aynı tohum iki kez yazılır ve veri içeriğinin
**sağlaması** (checksum) karşılaştırılır. Sağlama dosya düzenine değil, tablo içeriğine
bakar.

```js
// dogrula.mjs — calisma zamani surumunu bildirime gore denetler, tohum sagalamasini yazar
import { execFileSync } from "node:child_process";
import { readFileSync } from "node:fs";

const ortam = JSON.parse(readFileSync("ortam.json", "utf8"));
const anaSurum = Number(process.versions.node.split(".")[0]);
const uygun = anaSurum >= ortam.calisma_zamani.en_az_ana_surum;
console.log(`calisma zamani: ${ortam.calisma_zamani.ad} ${anaSurum}` +
  ` (bildirilen en az ${ortam.calisma_zamani.en_az_ana_surum}) -> ${uygun ? "uygun" : "UYGUN DEGIL"}`);
if (!uygun) process.exit(1);

const sagalama = (dosya) =>
  execFileSync("sqlite3", [dosya, ".sha3sum"], { encoding: "utf8" }).trim().slice(0, 16);
for (const dosya of process.argv.slice(2)) console.log(`  ${dosya}: ${sagalama(dosya)}`);
```

```bash
for kural in sabit serbest; do
  for n in 1 2; do node tohum.mjs "$kural-$n.db" "$kural"; done
done
node dogrula.mjs sabit-1.db sabit-2.db serbest-1.db serbest-2.db
```

```
calisma zamani: node 24 (bildirilen en az 22) -> uygun
  sabit-1.db: adc75c8f1262a1f5
  sabit-2.db: adc75c8f1262a1f5
  serbest-1.db: 1ee804800e30af0a
  serbest-2.db: bb90ce9eec315bec
```

Sağlamalar kısaltılarak yazılmıştır ve serbest kuralın ürettiği iki değer her çalıştırmada
başkadır; sürüm satırı da çalıştıran makinenin çalışma zamanına bağlıdır. Okunan sonuç
şudur: sabit kural iki kurulumda aynı sağlamayı vermiştir, serbest kural vermemiştir. Aynı
ayrım iki makine arasında da geçerlidir. Serbest kuralla kurulan bir ortamda "bende
çalışıyor" cümlesi doğrulanamaz bir iddiaya dönüşür, çünkü iki makinedeki veri aynı
değildir.

## Özet

- Bağımlı servisler bir zincir kurar: her halka kendinden öncekinin hazır olmasını
  gerektirir; geçit konmadığında ilk halkanın açılış gecikmesi tüm zinciri kırar.
- Bağlantı noktasının açık olması hazır olmak demek değildir; ölçümde bağlantı denetimi 23
  milisaniyede geçmiş, servis o anda hâlâ hazırlık işindeyken bağımlı servise `503`
  döndürmüştür.
- Hazır olma denetimi servisin kendi bildirdiği duruma bakar ve başarısız denemeden sonra
  bekleme süresini iki katına çıkararak yeniden dener.
- Sabit bekleme bir tahmindir: tohum verisi büyüdüğünde aynı yarım saniye yetmemiş, sağlık
  denetimi ise değiştirilmeden gereken süre kadar beklemiştir.
- Ortamın iki makinede aynı kurulması iki kurala dayanır: çalışma zamanı sürümünün
  bildirimle karşılaştırılması ve tohum verisinin çalışma anından değil tohumun kendisinden
  gelmesi; ikincisi sağlamanın eşitliğiyle doğrulanır.

## Kurs Kapanışı

Bu kurs bir isteğin sunucu tarafındaki yolculuğunu baştan sona kurdu. Başlangıçta sorulan
soru "istemcinin karşısındaki taraf neyi üstlenir" idi; yanıt üç sorumlulukta toplandı —
kalıcılık, iş kuralları, tümleştirme — ve bu sorumlulukların istemciye devredilemeyeceği
ölçülerek gösterildi. Ardından "sunucu" tek bir program olmaktan çıktı: web sunucusu,
uygulama sunucusu ve vekillik ayrı roller olarak ayrıldı, istek yaşam döngüsü ile ara
katman zinciri bu roller içindeki sırayı verdi.

İkinci bölüm uygulamanın kendisini ayağa kaldırdı: yapılandırmanın ortamdan okunması,
gizli değerlerin ayrılması, yapılandırılmış kütük ve ortak hata katmanı. Üçüncü bölüm
uygulamanın dışarıya ne verdiğini ele aldı: dosyanın doğrulayıcı ve saklama yönergesiyle
sunulması, görünümün şablondan üretilmesi, gelen dosyanın sınırlarla kabul edilmesi ve
yanındaki servislerin yerelde ayağa kaldırılması.

Kurs boyunca tekrarlanan tek bir akıl yürütme vardı: **istemciden gelen hiçbir değer,
sunucunun kendi denetiminden geçmediği sürece karar sayılmaz.** Bildirilen uzunluk, bildirilen
içerik türü, gönderilen dosya adı, iletilen istemci adresi — hepsi birer iddiadır. Sunucunun
işi bu iddiaları kendi ölçümüyle karşılamaktır.

Buraya kadar sunucunun dışarıya verdiği şey ya bir dosya ya da bir belgeydi: insanın
okuyacağı içerik. Kütüphane servisinin asıl tüketicileri ise programlardır — tarayıcıdaki
uygulama, şubelerdeki uçbirimler, raporlama işleri. Programların birbirine bağlandığı yerde
biçim değil **sözleşme** belirleyicidir: hangi kaynaklar vardır, adları nedir, hangi
alanları taşır, bir alan kaldırıldığında karşı taraf ne olur? Sıradaki kurs — Web API
Tasarımı — bu sözleşmeyi kurar. İlk konusu, aynı işi karşılayan farklı API biçemlerini
kaynak temelli, uzak yordam çağrısı temelli ve sorgu temelli yaklaşımlar olarak ayırıp
aralarındaki ödünleşimleri ölçer.
