---
title: 'Basit Tut ve Sık Yeniden Düzenle'
source: 'https://academia.sh/tr/kurslar/temiz-kod/basit-tut-ve-sik-yeniden-duzenle'
course: 'Temiz Kod'
language: tr
updated: '2026-08-17T18:10:37+00:00'
license: 'CC BY-SA 4.0'
---

# Basit Tut ve Sık Yeniden Düzenle

Aynı ücretlendirme gereksiniminin tek adımda tasarlanmış bir çözümle ve beş küçük adımla evrilen bir çözümle karşılanması, her adımda değişen satır sayısının ölçülmesi ve kırmızı bir testin işaret ettiği şüpheli bölgenin karşılaştırılması.

Önceki derste iki değişiklik yapıldı: bir tablo tek dosyaya taşındı, bir fonksiyon ikiye
ayrıldı. İkisi de küçüktü ve ikisi de mevcut davranışı korudu. Bu ders, adımın küçük
olmasının neyi ucuzlattığını sayıyla gösterir.

İlkenin adı **basit tut** (keep it simple): bir gereksinim, onu karşılayan en az yapıyla
karşılanır; eksik kalan yapı sonradan, gerçekten gerektiğinde eklenir. İlkeye ikinci bir
cümle eşlik eder — **sık yeniden düzenle**. Az yapıyla ilerlemek, biriken yapıyı düzenleme
işini erteleme hakkı vermez; düzenleme her adımın parçasıdır.

Test odaklı geliştirme konusunda adım boyu **geri bildirim süresi** açısından ele
alınmıştı: kırmızıdan yeşile geçen turun uzunluğu ölçülmüştü. Burada aynı büyüklük başka
bir açıdan ölçülür: bir test kırmızıya döndüğünde hatanın **hangi satır kümesinde**
arandığı.

## Gereksinim

Gönderi ücretlendirme kitaplığına dört kurallı bir ücret hesabı ekleniyor.

1. Taban ücret, ağırlık kademesinden okunur.
2. Taban ücret gönderim bölgesinin katsayısıyla çarpılır.
3. Sonuç asgari ücretin altına düşemez.
4. Hacim ağırlığı gerçek ağırlıktan büyükse kademe hacim ağırlığından okunur.

Kademe tablosu önceki derste kurulmuştu ve değişmeden kullanılıyor.

```js
// tarife.mjs — onceki dersten devralinan kademe tablosu
export const KADEME = [
  { ustGram: 1000, kurus: 4990, ad: 'kucuk' },
  { ustGram: 5000, kurus: 7490, ad: 'orta' },
  { ustGram: 10000, kurus: 9900, ad: 'buyuk' },
  { ustGram: 20000, kurus: 12900, ad: 'cok-buyuk' },
  { ustGram: Infinity, kurus: 24900, ad: 'agir' },
];

export function kademe(gram) {
  return KADEME.find((k) => gram <= k.ustGram);
}
```

## Tek Adımda Tam Tasarım

Dört kural birden okunduğunda akla gelen ilk çözüm hepsini tek yazımda karşılamaktır.

```js
// ucret.mjs — tek adimda yazilmis tam surum
import { kademe } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };
const ASGARI_KURUS = 6990;

export function gonderiUcreti(g) {
  const hacimGram = Math.ceil((g.en * g.boy * g.yukseklik) / 3);
  const etkinGram = Math.max(g.gram, hacimGram);
  const taban = kademe(etkinGram).kurus;
  const asgarili = Math.max(taban, ASGARI_KURUS);
  return Math.round(asgarili * BOLGE_KATSAYI[g.bolge]);
}
```

Dört kuralın dördü de birer testle sabitleniyor.

```js
// ucret.test.mjs — dort kuralin tamami
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { gonderiUcreti } from './ucret.mjs';

const kutu = { en: 20, boy: 15, yukseklik: 10 };

test('kademe ucreti agirliktan okunur', () => {
  assert.equal(gonderiUcreti({ ...kutu, gram: 3000, bolge: 'B1' }), 7490);
});

test('bolge katsayisi taban ucreti carpar', () => {
  assert.equal(gonderiUcreti({ ...kutu, gram: 3000, bolge: 'B3' }), 11984);
});

test('asgari ucret bolge carpimindan sonra uygulanir', () => {
  assert.equal(gonderiUcreti({ ...kutu, gram: 500, bolge: 'B2' }), 6990);
});

test('hacim agirligi gercek agirliktan buyukse o kullanilir', () => {
  assert.equal(gonderiUcreti({ en: 40, boy: 30, yukseklik: 30, gram: 500, bolge: 'B2' }), 16125);
});
```

```sh
node --test --test-reporter=tap ucret.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
printf 'ucret.mjs satir sayisi: %s\n' "$(wc -l < ucret.mjs | tr -d ' ')"
```

```
ok 1 - kademe ucreti agirliktan okunur
ok 2 - bolge katsayisi taban ucreti carpar
not ok 3 - asgari ucret bolge carpimindan sonra uygulanir
ok 4 - hacim agirligi gercek agirliktan buyukse o kullanilir
# tests 4
# pass 3
# fail 1
ucret.mjs satir sayisi: 13
```

Üçüncü test kırmızı: asgari ücret bölge çarpımından önce uygulanmış, dolayısıyla asgari
tutar da bölge katsayısıyla çarpılmış. Hatanın kendisi tek satırlık, ama koşumun işaret
ettiği şüpheli bölge dosyanın tamamı: dört kural aynı yazımda eklendiği için testin
hangi kural yüzünden düştüğü koşumdan okunamaz. Şüpheli satır sayısı 13.

## Aynı Gereksinim, Beş Adım

Aynı dört kural, her biri kendi testiyle kapanan adımlara bölünüyor. İlk adım yalnız
birinci kuralı karşılıyor.

```js
// ucret.mjs — adim 1: yalniz agirlik kademesi
import { kademe } from './tarife.mjs';

export function gonderiUcreti(g) {
  return kademe(g.gram).kurus;
}
```

```sh
: > onceki.mjs
cat > ucret.test.mjs <<'SON'
// ucret.test.mjs — adim 1
import { test } from 'node:test';
import assert from 'node:assert/strict';
import { gonderiUcreti } from './ucret.mjs';

const kutu = { en: 20, boy: 15, yukseklik: 10 };

test('kademe ucreti agirliktan okunur', () => {
  assert.equal(gonderiUcreti({ ...kutu, gram: 3000, bolge: 'B1' }), 7490);
});
SON
node --test --test-reporter=tap ucret.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
printf 'bu adimda degisen satir: %s\n' "$(diff onceki.mjs ucret.mjs | grep -c '^[<>]')"
cp ucret.mjs onceki.mjs
```

```
ok 1 - kademe ucreti agirliktan okunur
# tests 1
# pass 1
# fail 0
bu adimda degisen satir: 6
```

İkinci adım bölge katsayısını ekliyor. Değişen tek şey çarpım.

```js
// ucret.mjs — adim 2: bolge katsayisi
import { kademe } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };

export function gonderiUcreti(g) {
  return Math.round(kademe(g.gram).kurus * BOLGE_KATSAYI[g.bolge]);
}
```

```sh
cat >> ucret.test.mjs <<'SON'

test('bolge katsayisi taban ucreti carpar', () => {
  assert.equal(gonderiUcreti({ ...kutu, gram: 3000, bolge: 'B3' }), 11984);
});
SON
node --test --test-reporter=tap ucret.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
printf 'bu adimda degisen satir: %s\n' "$(diff onceki.mjs ucret.mjs | grep -c '^[<>]')"
cp ucret.mjs onceki.mjs
```

```
ok 1 - kademe ucreti agirliktan okunur
ok 2 - bolge katsayisi taban ucreti carpar
# tests 2
# pass 2
# fail 0
bu adimda degisen satir: 6
```

Üçüncü adım asgari ücreti ekliyor ve bu adımda tam olarak tek adımlık tasarımdaki karar
verilmiş oluyor: asgari ücret çarpımdan önce mi sonra mı uygulanacak? İlk deneme öncekini
seçiyor.

```js
// ucret.mjs — adim 3: asgari ucret (ilk deneme)
import { kademe } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };
const ASGARI_KURUS = 6990;

export function gonderiUcreti(g) {
  const taban = Math.max(kademe(g.gram).kurus, ASGARI_KURUS);
  return Math.round(taban * BOLGE_KATSAYI[g.bolge]);
}
```

```sh
cat >> ucret.test.mjs <<'SON'

test('asgari ucret bolge carpimindan sonra uygulanir', () => {
  assert.equal(gonderiUcreti({ ...kutu, gram: 500, bolge: 'B2' }), 6990);
});
SON
node --test --test-reporter=tap ucret.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
printf 'bu adimda degisen satir: %s\n' "$(diff onceki.mjs ucret.mjs | grep -c '^[<>]')"
```

```
ok 1 - kademe ucreti agirliktan okunur
ok 2 - bolge katsayisi taban ucreti carpar
not ok 3 - asgari ucret bolge carpimindan sonra uygulanir
# tests 3
# pass 2
# fail 1
bu adimda degisen satir: 6
```

Kırmızıya dönen test aynı, hata aynı. Değişen şey aramanın nerede yapılacağı: bu adımda
değişen satır sayısı 6. Tek adımlık tasarımda aynı kırmızı, 13 satırlık bir dosyanın
tamamını şüpheli bırakıyordu ve o 13 satır içinde geçen dört kural birbirine karışmış
durumdaydı. Burada şüpheli bölge yalnız asgari ücret satırlarıdır.

```js
// ucret.mjs — adim 3: asgari ucret carpimdan sonra
import { kademe } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };
const ASGARI_KURUS = 6990;

export function gonderiUcreti(g) {
  const bolgeli = Math.round(kademe(g.gram).kurus * BOLGE_KATSAYI[g.bolge]);
  return Math.max(bolgeli, ASGARI_KURUS);
}
```

```sh
node --test --test-reporter=tap ucret.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
printf 'bu adimda degisen satir: %s\n' "$(diff onceki.mjs ucret.mjs | grep -c '^[<>]')"
cp ucret.mjs onceki.mjs
```

```
ok 1 - kademe ucreti agirliktan okunur
ok 2 - bolge katsayisi taban ucreti carpar
ok 3 - asgari ucret bolge carpimindan sonra uygulanir
# tests 3
# pass 3
# fail 0
bu adimda degisen satir: 6
```

Dördüncü adım hacim ağırlığını ekliyor.

```js
// ucret.mjs — adim 4: hacim agirligi
import { kademe } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };
const ASGARI_KURUS = 6990;

export function gonderiUcreti(g) {
  const hacimGram = Math.ceil((g.en * g.boy * g.yukseklik) / 3);
  const etkinGram = Math.max(g.gram, hacimGram);
  const bolgeli = Math.round(kademe(etkinGram).kurus * BOLGE_KATSAYI[g.bolge]);
  return Math.max(bolgeli, ASGARI_KURUS);
}
```

```sh
cat >> ucret.test.mjs <<'SON'

test('hacim agirligi gercek agirliktan buyukse o kullanilir', () => {
  assert.equal(gonderiUcreti({ en: 40, boy: 30, yukseklik: 30, gram: 500, bolge: 'B2' }), 16125);
});
SON
node --test --test-reporter=tap ucret.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
printf 'bu adimda degisen satir: %s\n' "$(diff onceki.mjs ucret.mjs | grep -c '^[<>]')"
printf 'gonderiUcreti govdesi: %s satir\n' "$(awk '/^export function gonderiUcreti/,/^}/' ucret.mjs | wc -l | tr -d ' ')"
cp ucret.mjs onceki.mjs
```

```
ok 1 - kademe ucreti agirliktan okunur
ok 2 - bolge katsayisi taban ucreti carpar
ok 3 - asgari ucret bolge carpimindan sonra uygulanir
ok 4 - hacim agirligi gercek agirliktan buyukse o kullanilir
# tests 4
# pass 4
# fail 0
bu adimda degisen satir: 6
gonderiUcreti govdesi: 6 satir
```

## Beşinci Adım: Yeniden Düzenleme

Dört kural karşılandı ve testler yeşil. İlkenin ikinci yarısı burada devreye girer:
biriken yapı, bir sonraki gereksinim gelmeden önce düzenlenir. Ücret fonksiyonu iki ayrı
soyutlama düzeyini taşıyor — ağırlığın nasıl belirlendiği ile ücretin nasıl hesaplandığı.
Ağırlık belirleme kendi adını alıyor.

```js
// ucret.mjs — adim 5: etkin agirlik ayiklandi
import { kademe } from './tarife.mjs';

const BOLGE_KATSAYI = { B1: 1.0, B2: 1.25, B3: 1.6 };
const ASGARI_KURUS = 6990;

export function etkinAgirlik(g) {
  return Math.max(g.gram, Math.ceil((g.en * g.boy * g.yukseklik) / 3));
}

export function gonderiUcreti(g) {
  const bolgeli = Math.round(kademe(etkinAgirlik(g)).kurus * BOLGE_KATSAYI[g.bolge]);
  return Math.max(bolgeli, ASGARI_KURUS);
}
```

```sh
node --test --test-reporter=tap ucret.test.mjs | grep -E '^ *(ok|not ok|# (tests|pass|fail))'
printf 'bu adimda degisen satir: %s\n' "$(diff onceki.mjs ucret.mjs | grep -c '^[<>]')"
printf 'gonderiUcreti govdesi: %s satir\n' "$(awk '/^export function gonderiUcreti/,/^}/' ucret.mjs | wc -l | tr -d ' ')"
```

```
ok 1 - kademe ucreti agirliktan okunur
ok 2 - bolge katsayisi taban ucreti carpar
ok 3 - asgari ucret bolge carpimindan sonra uygulanir
ok 4 - hacim agirligi gercek agirliktan buyukse o kullanilir
# tests 4
# pass 4
# fail 0
bu adimda degisen satir: 10
gonderiUcreti govdesi: 4 satir
```

Bu adımda hiçbir kural eklenmedi, hiçbir test değişmedi ve değişen satır sayısı önceki
adımlardan büyük çıktı. Yeniden düzenleme adımlarında bu olağandır: ölçü değişen satır
sayısı değil, koşumun yeşil kalmasıdır. Ücret fonksiyonunun gövdesi altı satırdan dört
satıra indi ve ağırlık belirleme kuralı artık kendi adıyla ayrıca sınanabilir hâle geldi.

## Adım Boyunun Ölçüsü

İki yol aynı dosyada bitti; ölçülen büyüklükler farklı.

| Ölçüt | Tek adım | Beş adım |
|---|---|---|
| Koşum sayısı | 1 | 6 |
| Yeşil biten koşum | 0 | 5 |
| Kırmızıda şüpheli satır | 13 | 6 |
| Bir adımda değişen satır | 13 | 6 (yeniden düzenlemede 10) |

Tek adımlık yolun tek koşumu kırmızı bitti ve hatanın hangi kuralda olduğu koşumdan
okunamadı; kırmızı olan test asgari ücret testiydi ama asgari ücret satırı, hacim ağırlığı
ve bölge katsayısı satırlarıyla aynı yazımda eklenmişti. Beş adımlık yolda kırmızı tek bir
kuralın eklendiği adımda göründü.

Ölçünün alan karşılığı şudur: bir gereksinim ne kadar çok kural içeriyorsa, hepsini tek
yazımda karşılamanın maliyeti kural sayısıyla değil, kuralların **birbirine göre sıraları**
sayısıyla artar. Dört kuralın uygulanma sırası için birden fazla seçenek vardır ve tek
yazımda bunların hepsi aynı anda seçilir. Adımlara bölmek bu seçimleri birer birer yapılır
hâle getirir.

Basit tut ilkesinin sık yapılan yanlış okunuşu, "yapıyı hiç kurma" biçimindedir. Beşinci
adım bunun tersini gösterir: yapı kurulur, ama gereksinim geldikten **sonra** ve testler
yeşilken kurulur. İlkenin yasakladığı şey yapı değil, gereksinimin önüne geçen yapıdır.

## Özet

- Basit tut ilkesi bir gereksinimi karşılayan en az yapıyla ilerlemeyi, sık yeniden
  düzenleme ise biriken yapıyı her adımın sonunda düzenlemeyi söyler.
- Aynı dört kural tek yazımda karşılandığında tek koşum kırmızı bitti ve şüpheli bölge 13
  satırdı; beş adıma bölündüğünde aynı hata 6 satırlık bir adımda göründü.
- Adımlara bölmek kuralların uygulanma sırasına ilişkin kararları birer birer alınır hâle
  getirir; tek yazımda bu kararların hepsi aynı anda ve gerekçesiz alınır.
- Yeniden düzenleme adımında değişen satır sayısı büyüyebilir; o adımın ölçüsü satır
  sayısı değil, testlerin yeşil kalmasıdır.
- İlke yapıyı yasaklamaz; gereksinimin önüne geçen yapıyı yasaklar.

## Sonraki Adım

Bu derste her adım, gelmiş bir gereksinime karşılık geldi. Gelmemiş bir gereksinim için
yazılan yapının ne kadara mal olduğu ölçülmedi. Sonraki ders ücret hesabına bugünden bir
eklenti noktası ve bir yapılandırma katmanı ekler, bunların kaç dallanma ve kaç
çalışmayan yol ürettiğini sayar, sonra gerçekten gelen gereksinimin bu yapıya uymadığını
gösterir.
