---
title: Tutarlılık
source: 'https://academia.sh/tr/kurslar/temiz-kod/tutarlilik'
course: 'Temiz Kod'
language: tr
updated: '2026-08-17T18:10:37+00:00'
license: 'CC BY-SA 4.0'
---

# Tutarlılık

Aynı işin projede tek biçimde yapılmasının ölçülmesi: üç ayrı işi üç ayrı biçimde yapan bir pakette biçim sayısını bulan tarama, tekilleştirmeden sonra ölçümün tekrarlanması ve biçim birleştirmenin çıktılarda ve hata davranışında yol açtığı değişikliklerin sayılması.

Aktör düzeninde her kararın bir sahibi oldu. Kitaplıkta yine de rahatsız edici bir şey
kalıyor: bir dosyada tutar `toFixed` ile yazılıyor, başka bir dosyada elle yuvarlanıp
başına para birimi ekleniyor; bir yerde bulunamama hata atarak, başka bir yerde boş değer
dönerek bildiriliyor.

Bunların hiçbiri yanlış değil. Üçü de çalışıyor, üçü de sınamalardan geçiyor. Yalnız
**farklılar** ve farklılığın bedeli okuyanın üzerinde birikir: her dosyada aynı sorunun
cevabı yeniden aranır. Bu ders farklılığı sayıya bağlıyor — aynı iş kaç ayrı biçimde
yapılıyor.

## Ölçüt: İş Başına Biçim Sayısı

Bir kod tabanında tekrarlanan işler vardır: para yazmak, bulunamama durumunu bildirmek,
bir modülü dışa açmak. Her iş için o kod tabanında kullanılan farklı yazım biçimlerinin
sayısı, tutarlılığın ölçüsüdür. Hedef her zaman birdir; hangi biçim olduğu ikinci
sorudur.

Sayı doğrudan bir maliyete karşılık gelir. Bir iş üç biçimde yapılıyorsa, o işe yeni bir
yerde ihtiyaç duyan kişi üç örnek görür ve hangisini izleyeceğine karar vermek zorunda
kalır. Kararı verirken çoğunlukla en son baktığı dosyayı örnek alır; böylece biçim sayısı
azalmaz, dağılım rastlantısallaşır.

## Üç İş, Üç Ayrı Biçim

Paketteki üç modül üç ayrı iş yapıyor: ücret satırı, etiket satırı, günlük toplam. Üçü de
tutar yazıyor, üçü de bulunamama durumunu bildiriyor, üçü de bir şey dışa açıyor.

```sh
mkdir -p karisik tekil
```

```js
// karisik/ucret.mjs — ucret satiri
export function ucretSatiri(gonderi, tarife) {
  const kademe = tarife.kademeler.find((k) => gonderi.agirlikKg <= k.ustSinirKg);
  if (kademe === undefined) throw new RangeError("kademe bulunamadi");
  return `${gonderi.kod} ${(kademe.kiloBasiUcret * gonderi.agirlikKg).toFixed(2)} TL`;
}
```

```js
// karisik/etiket.mjs — etiket satiri
export const etiketSatiri = (gonderi, bolgeAdlari) => {
  const bolge = bolgeAdlari[gonderi.bolgeKodu];
  if (bolge === undefined) return null;
  return `${gonderi.kod} ${bolge} ${gonderi.beyanDegeri.toFixed(2)}TL`;
};
```

```js
// karisik/rapor.mjs — gunluk toplam
function gunlukToplam(gonderiler, gun) {
  const gunun = gonderiler.filter((g) => g.gun === gun);
  if (gunun.length === 0) return { hata: "o gune ait gonderi yok" };
  const toplam = Math.round(gunun.reduce((t, g) => t + g.ucret, 0) * 100) / 100;
  return `${gun}. gun TL ${toplam}`;
}

export { gunlukToplam };
```

Üç dosya birbirinden bağımsız yazılmış gibi duruyor ve öyle de olabilir: her biri farklı
bir zamanda, farklı bir kişi tarafından, o günkü alışkanlıkla yazılmıştır. Sonuç, aynı
paketin içinde üç ayrı yazım geleneğidir.

## Tekilleştirilmiş Sürüm

İkinci sürümde her iş için tek bir biçim seçildi: tutar `toFixed(2)` ile yazılıp para
birimi bir boşlukla ekleniyor, bulunamama bir alan hatası atarak bildiriliyor, dışa açma
bildirimin başında yapılıyor.

```js
// tekil/ucret.mjs — ucret satiri
export function ucretSatiri(gonderi, tarife) {
  const kademe = tarife.kademeler.find((k) => gonderi.agirlikKg <= k.ustSinirKg);
  if (kademe === undefined) throw new RangeError("kademe bulunamadi");
  return `${gonderi.kod} ${(kademe.kiloBasiUcret * gonderi.agirlikKg).toFixed(2)} TL`;
}
```

```js
// tekil/etiket.mjs — etiket satiri
export function etiketSatiri(gonderi, bolgeAdlari) {
  const bolge = bolgeAdlari[gonderi.bolgeKodu];
  if (bolge === undefined) throw new RangeError("bolge bulunamadi");
  return `${gonderi.kod} ${bolge} ${gonderi.beyanDegeri.toFixed(2)} TL`;
}
```

```js
// tekil/rapor.mjs — gunluk toplam
export function gunlukToplam(gonderiler, gun) {
  const gunun = gonderiler.filter((g) => g.gun === gun);
  if (gunun.length === 0) throw new RangeError("o gune ait gonderi yok");
  return `${gun}. gun ${gunun.reduce((t, g) => t + g.ucret, 0).toFixed(2)} TL`;
}
```

## Biçim Tarayıcı

Tarayıcı her iş için tanınacak biçimlerin örüntülerini taşır, dizindeki dosyalarda hangi
biçimlerin göründüğünü bulur ve iş başına kaç ayrı biçim kullanıldığını sayar.

```js
// bicim-tara.mjs — ayni isin kac ayri bicimde yapildigini sayar
import { readFileSync, readdirSync } from "node:fs";

const ISLER = {
  "para bicimi": [["toFixed + bosluk + TL", /toFixed\(2\)\} TL/], ["toFixed + bitisik TL", /toFixed\(2\)\}TL/],
    ["Math.round + onek TL", /Math\.round\(/]],
  "bulunamama": [["hata atma", /throw new \w*Error/], ["bos deger", /return null/],
    ["hata nesnesi", /return \{ hata:/]],
  "disa aktarma": [["export function", /^export function /m], ["export const ok", /^export const \w+ = \(/m],
    ["sonda export bloku", /^export \{/m]],
};

for (const dizin of process.argv.slice(2)) {
  const dosyalar = readdirSync(dizin).filter((a) => a.endsWith(".mjs")).sort();
  const metin = dosyalar.map((a) => [a, readFileSync(`${dizin}/${a}`, "utf8")]);
  console.log(dizin);
  let toplam = 0;
  for (const [is, bicimler] of Object.entries(ISLER)) {
    const bulunan = bicimler
      .map(([ad, oruntu]) => [ad, metin.filter(([, m]) => oruntu.test(m)).map(([d]) => d)])
      .filter(([, dosyalar]) => dosyalar.length > 0);
    toplam += bulunan.length;
    console.log(`  ${is.padEnd(14)} bicim=${bulunan.length}`);
    for (const [ad, d] of bulunan) console.log(`    ${ad.padEnd(22)} ${d.join(", ")}`);
  }
  console.log(`  toplam bicim=${toplam}  (is basina 1 hedefleniyor, hedef=${Object.keys(ISLER).length})`);
}
```

```sh
node bicim-tara.mjs karisik tekil
```

```
karisik
  para bicimi    bicim=3
    toFixed + bosluk + TL  ucret.mjs
    toFixed + bitisik TL   etiket.mjs
    Math.round + onek TL   rapor.mjs
  bulunamama     bicim=3
    hata atma              ucret.mjs
    bos deger              etiket.mjs
    hata nesnesi           rapor.mjs
  disa aktarma   bicim=3
    export function        ucret.mjs
    export const ok        etiket.mjs
    sonda export bloku     rapor.mjs
  toplam bicim=9  (is basina 1 hedefleniyor, hedef=3)
tekil
  para bicimi    bicim=1
    toFixed + bosluk + TL  etiket.mjs, rapor.mjs, ucret.mjs
  bulunamama     bicim=1
    hata atma              etiket.mjs, rapor.mjs, ucret.mjs
  disa aktarma   bicim=1
    export function        etiket.mjs, rapor.mjs, ucret.mjs
  toplam bicim=3  (is basina 1 hedefleniyor, hedef=3)
```

Toplam biçim sayısı dokuzdan üçe indi ve üç, iş sayısına eşit — yani her iş tek biçimde
yapılıyor. Dosya listeleri de tersine döndü: önce her biçimin tek bir dosyası vardı,
şimdi tek biçimin üç dosyası var.

## Tekilleştirmenin Bedeli

Biçim birleştirmek serbest bir işlem değildir. Para biçimini birleştirmek çıktı metnini,
bulunamama biçimini birleştirmek hata davranışını değiştirir. Değişikliklerin sayılması
gerekir.

```js
// karsilastir.mjs — tekillestirmenin ciktilara ve bulunamama davranisina etkisi
import { ucretSatiri as kUcret } from "./karisik/ucret.mjs";
import { etiketSatiri as kEtiket } from "./karisik/etiket.mjs";
import { gunlukToplam as kToplam } from "./karisik/rapor.mjs";
import { ucretSatiri as tUcret } from "./tekil/ucret.mjs";
import { etiketSatiri as tEtiket } from "./tekil/etiket.mjs";
import { gunlukToplam as tToplam } from "./tekil/rapor.mjs";

const TARIFE = { kademeler: [{ ustSinirKg: 5, kiloBasiUcret: 26 }, { ustSinirKg: 30, kiloBasiUcret: 17 }] };
const BOLGELER = { 1: "Ayni Sehir", 2: "Komsu Bolge" };
const GONDERI = { kod: "GN-4172", agirlikKg: 2.4, bolgeKodu: 2, beyanDegeri: 1500, gun: 3, ucret: 62.4 };
const LISTE = [GONDERI, { ...GONDERI, kod: "GN-4173", ucret: 51.2 }];

const dene = (fn) => {
  try {
    const sonuc = fn();
    return typeof sonuc === "object" && sonuc !== null ? JSON.stringify(sonuc) : String(sonuc);
  } catch (hata) {
    return `${hata.constructor.name}: ${hata.message}`;
  }
};

const DURUMLAR = [
  ["ucret", () => kUcret(GONDERI, TARIFE), () => tUcret(GONDERI, TARIFE)],
  ["etiket", () => kEtiket(GONDERI, BOLGELER), () => tEtiket(GONDERI, BOLGELER)],
  ["toplam", () => kToplam(LISTE, 3), () => tToplam(LISTE, 3)],
  ["etiket/yok", () => kEtiket({ ...GONDERI, bolgeKodu: 9 }, BOLGELER),
    () => tEtiket({ ...GONDERI, bolgeKodu: 9 }, BOLGELER)],
  ["toplam/yok", () => kToplam(LISTE, 7), () => tToplam(LISTE, 7)],
];

for (const [ad, karisik, tekil] of DURUMLAR) {
  const a = dene(karisik), b = dene(tekil);
  console.log(`${ad.padEnd(11)} ${a === b ? "ayni  " : "degisti"} ${a.padEnd(34)} -> ${b}`);
}
```

```
ucret       ayni   GN-4172 62.40 TL                   -> GN-4172 62.40 TL
etiket      degisti GN-4172 Komsu Bolge 1500.00TL      -> GN-4172 Komsu Bolge 1500.00 TL
toplam      degisti 3. gun TL 113.6                    -> 3. gun 113.60 TL
etiket/yok  degisti null                               -> RangeError: bolge bulunamadi
toplam/yok  degisti {"hata":"o gune ait gonderi yok"}  -> RangeError: o gune ait gonderi yok
```

Beş durumun dördü değişti. Üçü sunum kararıdır: para birimi eki, ondalık hane sayısı ve
sıralama artık her yerde aynı. Biri davranış kararıdır: iki fonksiyon artık boş değer ya
da hata nesnesi yerine hata atıyor ve bunları çağıran her yer buna göre güncellenmelidir.

Bu, tutarlılık çalışmasının gerçek maliyetidir. Ölçüm işi mekanikleştirir ama kararı
vermez; tarayıcı kaç biçim olduğunu söyler, hangisinin standart olacağını söylemez.
`3. gun TL 113.6` satırının `3. gun 113.60 TL` olması bir seçimdir ve seçim yapıldıktan
sonra tek başına geri alınamaz.

## Standardı Belirlemek

Standardın hangisi olacağına dair üç ölçüt işe yarar.

**Yaygınlık.** Kod tabanında zaten en çok kullanılan biçim, en az değişiklikle
standartlaştırılabilendir. Tarayıcının dosya listesi bu bilgiyi doğrudan veriyor.

**Dışarıya bakan yüzeyle uyum.** Bir biçim kullanıcıya ya da başka bir sisteme görünüyorsa,
onun beklentisi iç tercihten önce gelir. Para biçimi fatura çıktısında görünüyorsa
standardı belirleyen şey muhasebenin biçimidir.

**Yanlış kullanıma dirençlilik.** Bulunamama örneğinde hata atmak, boş değer döndürmekten
daha dirençlidir; önceki derslerden birinde ölçülmüştü — boş değer, hatayı üretildiği
yerden uzağa taşıyor.

Ölçütlerin hiçbiri "daha güzel"i içermez. Tutarlılık tartışmasının çıkmaza girdiği yer,
biçimlerin estetik olarak karşılaştırılmasıdır; oysa asıl kazanç biçimin **tek** olmasından
gelir, hangisi olduğundan değil. Karar verildikten sonra iş mekanikleşir: tarayıcı
kod tabanında çalıştırılır, biçim sayısı birden büyük olan her iş listelenir.

Tekilleştirilmiş sürümde bir şey hâlâ eksik. Üç dosya aynı para biçimini üç kez yazıyor;
biçim tek ama yazım üç yerde tekrarlanıyor. Bunlar ayrı sorunlardır: biri aynı işin farklı
biçimlerde yapılması, öteki aynı bilginin birden çok yerde durmasıdır.

## Özet

- Tutarlılığın ölçüsü, bir işin kod tabanında kaç ayrı biçimde yapıldığıdır; hedef her
  zaman birdir, hangi biçim olduğu ikinci sorudur.
- Üç modüllü pakette para biçimi, bulunamama bildirimi ve dışa aktarma üçer biçimdeydi;
  toplam dokuz biçim üçe indi ve iş sayısına eşitlendi.
- Tekilleştirme bedelsiz değil: beş durumun dördü değişti; üçü sunum kararı, biri hata
  davranışı kararıydı ve çağıran her yeri ilgilendirir.
- Standardı seçerken yaygınlık, dışarıya bakan yüzeyle uyum ve yanlış kullanıma
  dirençlilik ölçütleri kullanılır; estetik karşılaştırma tartışmayı bitirmez.
- Aynı biçimin tek olması ile o biçimin tek yerde yazılması ayrı sorunlardır; ikincisi
  tekilleştirmeden sonra da yerinde duruyor.

## Sonraki Adım

Bu konudaki kararların hepsi **yerel**di: bir adın ne söylediği, bir imzanın neyi kabul
ettiği, bir modülün neyi dışa açtığı, bir dosyanın kime hizmet ettiği, bir işin hangi
biçimde yapıldığı. Her biri tek bir dosyaya bakılarak verilebilir ve tek bir dosyaya
bakılarak denetlenebilir.

Kod tabanı büyüdükçe bu ölçeğe sığmayan bir maliyet doğar. Aynı bilgi birden çok yerde
durmaya başlar ve bir tanesi güncellendiğinde ötekiler geride kalır. Bugün gerekmeyen bir
esneklik bugünden kurulur ve gerekmediği ortaya çıkana kadar her değişikliğin üzerine
binen bir yük olur. Bir kararın değiştirilmesi tek bir dosyaya değil, kaç yere dokunduğu
önceden bilinmeyen bir kümeye yayılır. Sonraki konu bu üç maliyeti ele alıyor ve
yinelemenin türlerini ayırt etmekle başlıyor: aynı bilginin iki yerde durması ile iki
parçanın tesadüfen birbirine benzemesi aynı şey değildir.
