# Kentsel Dönüşüm #1: Risk Tespiti – Ajana Önce Okut, Yazdırma

> Eski bir projeyi ajana emanet etmeden önce ona tek satır yazdırmayın, okutun. "Salt okuma" her araçta başka bir şey; o yüzden röntgen atılabilir bir kopyada çekilir ve sonunda kopyanın değişmediği kanıtlanır. Altınkaynak deposundan on bir bulgu ve bir sıra.

- Yazar: [Halit Yeşil](https://halityesil.com/hakkimda/)
- Yayın tarihi: 2026-10-17
- Dil: Türkçe
- Kaynak adres: https://halityesil.com/kentsel-donusum-1-risk-tespiti/
- İngilizce sürümü: https://halityesil.com/en/urban-renewal-1-ai-refactoring-survey.md
- Seri: [Kentsel Dönüşüm](https://halityesil.com/seri/kentsel-donusum/), bölüm 1
- Kategori: Yazılım Mimarisi
- Etiketler: AI Ajanları, Claude Code, Codex, Cursor, Git, Kentsel Dönüşüm, Legacy Kod, OpenCode, php, Refactoring
- Lisans: https://creativecommons.org/licenses/by-nc/4.0/
- Atıf: Halit Yeşil. “Kentsel Dönüşüm #1: Risk Tespiti – Ajana Önce Okut, Yazdırma”. halityesil.com, 2026-10-17. https://halityesil.com/kentsel-donusum-1-risk-tespiti/

---

Vibe Coder serisinde boş arsaya bina dikiyoruz: yeni proje, temiz şema, ajanla sıfırdan. Bu yeni seri öbür tarafı anlatıyor. Arsa dolu, binada insanlar oturuyor, tesisat duvarın içinden geçiyor ve projenin çizimlerini kimse bulamıyor. **Yapay zeka ile refactoring** dediğimiz iş çoğu zaman bu: eski bir projeyi yıkmadan yenilemek. Serinin adı da buradan geliyor.

Tezi baştan koyayım. Kodlama ajanı, gördüğüm en hızlı müteahhit. Ama statik raporu siz vermezseniz kaçak kat çıkar: istemediğiniz değişiklik, sormadan büyüyen kapsam, kimsenin satır satır okumadığı bir diff. O yüzden ilk bölümde ajana tek satır yazdırmıyoruz. Önce okutuyoruz.

Demo binamız da belli: 2020'de yazdığım [Altınkaynak döviz kuru kütüphanesi](https://halityesil.com/php-ile-altinkaynak-doviz-kuru-cekmek/) ([github.com/HalitYesil/altinkaynak](https://github.com/HalitYesil/altinkaynak)). SOAP servisinden döviz ve altın fiyatı çeken, on PHP dosyası ve 679 satırlık, MIT lisanslı küçük bir kütüphane. Son commit 6 Eylül 2020. PHP 8.3.6 ile `php -l` hepsinde temiz geçiyor; yani bina ayakta, sadece yaşlı. Seri boyunca bu binayı gerçekten dönüştüreceğiz.

## Neden önce okutmak?

Ajana "şu projeyi refactor et" dediğinizde ilk gördüğü sorunu düzeltmeye girişir. Bu kötü niyet değil, tarif: ondan iş istediniz, o da iş üretiyor. Sorun şu ki biri düzeltilirken öbür dokuz sorun görünmez kalıyor. Önceliği kim belirledi? Dosyaların okunma sırası.

Kentsel dönüşümde müteahhit binaya girmeden önce risk tespit raporu çıkar ve raporu yazan kişi kolon kesmez. Bizdeki karşılığı: ajan önce kod tabanının röntgenini çeker, her bulguyu kanıtıyla yazar, hiçbir dosyaya dokunmaz. Hangi bulgunun ne zaman ele alınacağına raporu okuyan insan karar verir.

Kulağa basit geliyor. Zor olan "hiçbir dosyaya dokunmaz" kısmı, çünkü araçların "salt okuma" dediği şey her araçta aynı şey değil.

## "Salt okuma" her araçta başka bir şey

Dört ayrı ekosistemden dört aracın planlama ve soru kiplerini kendi dokümanlarından okudum (15 Ekim 2026):

- **Claude Code (Anthropic), plan modu.** `claude --permission-mode plan` ya da oturum içinde Shift+Tab. [Dokümana göre](https://code.claude.com/docs/en/permission-modes) Claude dosyaları okuyor, keşif için kabuk komutları çalıştırıyor ve plan yazıyor, ama kaynağınızı düzenlemiyor; düzenleme siz planı onaylayana kadar kapalı. Kabuk komutları ise oturuma göre ya auto mode sınıflandırıcısından ya da sizin onayınızdan geçiyor.
- **Codex (OpenAI), `--sandbox read-only`.** [Dokümana göre](https://learn.chatgpt.com/docs/sandboxing) ajan dosyaları inceleyebiliyor ama onay almadan dosya düzenleyemiyor ve komut çalıştıramıyor. Farkı, sınırın işletim sistemi seviyesinde çizilmesi: macOS'te Seatbelt, Linux ve WSL2'de bubblewrap.
- **Cursor, Ask modu.** Dosya düzenlemesi araç katmanında kapalı; kabuk komutları ise kısmen modele verilen talimata dayanıyor. Bunu ben söylemiyorum. 8 Mayıs 2026'da bir kullanıcı, Ask modundaki ajanın yerel Postgres veritabanında `docker exec` ile `ALTER TABLE` ve `UPDATE` çalıştırdığını [Cursor forumunda bildirdi](https://forum.cursor.com/t/agent-ran-database-command-in-ask-mode/160071) (Cursor 3.2.21, model Kimi K2.5). Cursor ekibinin yanıtı açık: bilinen bir mimari sınır, Ask modunu araç katmanında daha sıkı uygulamak için çalışıyorlar, tarih yok.
- **OpenCode (açık kaynak), Plan ajanı.** Tab ile geçiliyor. [Dokümana göre](https://opencode.ai/docs/agents/) dosya düzenlemeleri ve bash komutları varsayılan olarak "ask", yani her biri onayınızı bekliyor.

| Araç | Kip | Dosya yazma | Komut çalıştırma | Sınırı kim çiziyor |
|---|---|---|---|---|
| Claude Code | `plan` | Plan onayına kadar kapalı | Sınıflandırıcı ya da sizin onayınız | Aracın izin sistemi |
| Codex | `--sandbox read-only` | Kapalı | Onaya bağlı | İşletim sistemi kum havuzu |
| Cursor | Ask | Araç katmanında kapalı | Kısmen model talimatına bağlı | Araç + model |
| OpenCode | Plan | Her biri onaya | Her biri onaya | Aracın izin sistemi |

**Acı Gerçek:** Dosya sistemine yazmayı kapatmak ile yan etkiyi kapatmak ayrı işler. Ajanın ulaşabildiği veritabanı, kuyruk ya da uzak servis kipin dışında kalabiliyor; Cursor örneğinde olan tam olarak bu.

Benim bundan çıkardığım kural kipten bağımsız: **risk tespiti atılabilir bir kopyada yapılır ve sonunda kopyanın değişmediği kanıtlanır.** Kip ilk savunma hattı, kanıt ikincisi. Kopyada `.env` dosyası, gerçek veritabanı bilgisi ya da anahtar olmaz; röntgen için kodun kendisi yeter. [Vibe Coder 4.2](https://halityesil.com/vibe-coder-4-2-agents-md-claude-md/)'de anlattığımız kancalar üçüncü hat olabilir, ama ilk ikisi olmadan değil.

```bash
#!/usr/bin/env bash
# risk-tespiti.sh: ajanı atılabilir bir kopyada çalıştır, sonunda hiçbir şeyin değişmediğini kanıtla.
set -euo pipefail

DEPO=${1:?Kullanım: risk-tespiti.sh <depo-adresi>}
KOPYA="$(mktemp -d)/bina"

git clone --quiet "$DEPO" "$KOPYA"
cd "$KOPYA"
BAS=$(git rev-parse HEAD)

echo "Kopya: $KOPYA (HEAD ${BAS:0:7})"
echo "Ajanı bu klasörde salt okuma kipinde başlatın, raporu dosyaya değil ekrana yazdırın. Bitince Enter."
read -r _

if [ -n "$(git status --porcelain --ignored)" ] || [ "$(git rev-parse HEAD)" != "$BAS" ]; then
  echo "Kaçak kat: rapor sırasında kopyada değişiklik olmuş." >&2
  git status --short --ignored >&2
  exit 1
fi

echo "Temiz: ajan okudu, yazmadı."
```

Betiği altinkaynak üzerinde iki senaryoyla denedim. Kopyaya dokunmadan Enter'a basınca "Temiz" deyip 0 koduyla çıktı. Beklerken kopyadaki README'ye bir satır ekleyip yanına bir `rapor.md` bırakınca `M README.md` ve `?? rapor.md` satırlarını basıp 1 ile çıktı. `--ignored` bayrağı bilerek orada: ajan `vendor/` gibi git'in görmezden geldiği bir klasöre yazsa bile yakalansın diye. Raporu dosyaya değil ekrana yazdırmamın sebebi de bu; rapor dosyası bile değişiklik sayılıyor.

## Görev metni: rapor istiyoruz, yama değil

[Vibe Coder 4.1](https://halityesil.com/vibe-coder-4-1-mega-prompting/)'de "medyum seansı yerine yazılı şartname" demiştik. Risk tespitinin şartnamesi kısa, ama bir şeyi çok net söylemeli: çıktı bir rapor, yama değil.

```markdown
Görev: Bu depo için risk tespit raporu hazırla. Hiçbir dosyayı değiştirme,
yeni dosya oluşturma, paket kurma, ağ isteği yapma. Raporu yalnızca bu
sohbete yaz.

Her bulgu için:
- Bulgu: tek cümle
- Kanıt: dosya:satır (en fazla 3 satır alıntı; şifre ve anahtar değerlerini maskele)
- Risk: neyi bozabilir, kimi etkiler
- Dokunma maliyeti: düzeltmek başka neyi kırabilir
- Güven: kesin / şüpheli (şüpheliyse nasıl doğrulanır)

Düzeltme kodu yazma. Sıralamayı ben yapacağım.

Son bölüm: Bu depoyu okuyarak GÖREMEDİĞİN şeyler (çalışma zamanı, dış servis, gerçek veri).
```

En çok işe yarayan madde sonuncusu. Okuyarak bilinemeyecek olanı ayrı yazdırmak ajanı "her şeyi gördüm" tonundan çekiyor, size de deney listesini veriyor.

## Altınkaynak'ın röntgeni

Depoyu yalnızca okuma komutlarıyla (`git log`, `grep`, `php -l`, `git ls-files --eol`) satır satır taradım. Bulgular, kanıtlarıyla:

**1. Servis adresi `http://`.** `src/Altinkaynak.php` 33. satırda WSDL adresi TLS'siz. Servise giden her istek, kimlik bilgisiyle birlikte düz metin.

**2. Kimlik bilgisi kaynak kodda.** Aynı dosyanın 34 ve 35. satırlarında servis kullanıcı adı ve şifresi sabit değer olarak duruyor. Herkese açık bir servis bilgisi gibi görünse de ilke değişmiyor: sır depoya girmez. Değerleri buraya da yazmıyorum.

**3. Yanlış yazılmış seçenek.** `SoapClient` kurulurken verilen seçeneklerden biri sessizce boşa gidiyor:

```php
<?php
// src/Altinkaynak.php, 167-170. satırlar (girinti sadeleştirildi)
$this->Service = new \SoapClient($this->getServiceURL(), [
    "trace"     => 1,
    "exception" => 1, // PHP'de böyle bir seçenek yok; doğrusu "exceptions"
]);
```

[PHP dokümanında](https://www.php.net/manual/en/soapclient.construct.php) `exception` diye bir seçenek yok; doğrusu `exceptions` ve varsayılanı zaten `true`. Kod bugün doğru davranıyor, ama tesadüfen. **Gizli Hazine:** okuyana "hata yönetimini ayarladım" diyen ama hiçbir şey yapmayan bir satır. Raporun "dokunma maliyeti" sütununun sıfır olduğu nadir bulgulardan.

**4. Üç kez aynı metot.** `GetCurrency` (62), `GetGold` (92) ve `GetMain` (122) neredeyse birebir aynı: servis çağrısı, XML ayrıştırma, aynı döngü. Fark yalnızca çağrılan metodun adı. Altın listesi bile `GetCurrencyResponse` nesnesine dolduruluyor.

**5. Ölü kod.** `Response` sınıfında SOAP yanıtını nesneye eşleyen bir `decorateResponse()` var, ama sınıflar her seferinde `null` ile kuruluyor; eşleme hiç çalışmıyor, alanlar elle dolduruluyor. `AltinkaynakException` tanımlı, hiçbir yerde fırlatılmıyor.

**6. Para `float`.** Alış ve satış `floatval()` ile okunuyor (76, 106 ve 136. satırlar). Kuru ekranda göstermek için sorun çıkarmayabilir; bu sayılarla hesap yapılıyorsa çıkarır.

**7. Örneklerde kaçışsız çıktı.** Üç örnek dosyanın 50. satırında servisten gelen açıklama `<?= $Currency->desc; ?>` ile HTML'e kaçışsız basılıyor; aynı dosyaların başında `display_errors` açılıyor. Dışarıdan gelen veri, ham hâliyle sayfada.

**8. Tip yok, test yok.** `declare(strict_types=1)` yok, özellikler ve parametreler tipsiz, depoda tek bir test dosyası yok.

**9. Başka projeden kalan yorum.** Elle yazılmış autoloader'ın açıklama satırı "Register the autoloader for the Imflexi Gear SDK classes" diyor (`src/Autoload.php` 8. satır). Başka bir projeden taşınmış, adı değişmemiş. Risk tespitinin en insani bulgusu: kopyala-yapıştırın parmak izi.

**10. CI hiç çalışamazdı.** `.github/workflows/php.yml` ilk adımda `composer validate` çalıştırıyor, ama depoda `composer.json` yok. Aynı komutu deponun kökünde çalıştırdım: Composer 2.8.12 `./composer.json not found.` deyip 3 koduyla çıkıyor. Deponun Actions sayfasında ise kayıtlı tek bir çalıştırma görünmüyor. Yani bu bina kırmızı yanan bir alarmla değil, hiç bağlanmamış bir alarmla yaşamış. İkincisi daha tehlikeli: kırmızıyı görürsünüz, sessizliği görmezsiniz.

**11. Satır sonları karışık.** Raporun en sinsi bulgusu bu. `git ls-files --eol` çıktısına göre on PHP dosyasının onu da depoda CRLF (Windows satır sonu) ile kayıtlı; GitHub'ın web arayüzünde açılan iş akışı, README ve LICENSE ise LF. `.gitattributes` yok.

Neden önemli olduğunu ölçtüm. Bir kopyada 169. satırdaki `exception`'ı `exceptions` yapan tek kelimelik düzeltmeyi yaptım ve dosyayı LF satır sonuyla kaydettim, bir editörün ya da dosyayı baştan yazan bir ajanın yapabileceği gibi. `git diff --stat` sonucu: **238 ekleme, 238 silme.** Dosyanın tamamı. Aynı değişikliğe `git diff --ignore-cr-at-eol` ile bakınca 1 satır.

Bir kelimelik düzeltme 476 satırlık incelemeye dönüşüyor ve asıl değişiklik o kalabalığın içinde kayboluyor. Kaçak kat çoğu zaman böyle çıkar: kötü niyetle değil, gürültüyle.

### Raporun göremediği

Okuyarak bilinemeyecek iki şey vardı; ikisine de dışarıdan baktım (15 Ekim 2026).

Birincisi servisin kendisi. Kütüphanenin konuştuğu `data.altinkaynak.com` SOAP adresi ne `http://` ne `https://` ile açıldı, tarayıcı hata sayfası verdi. Altınkaynak'ın kendi [web servisleri sayfası](https://altinkaynak.com/Araclar/Servisler) ise bugün yalnızca iki adres listeliyor: `https://static.altinkaynak.com/public/Currency` ve `https://static.altinkaynak.com/public/Gold`. Döviz ucunu açtım: kimlik bilgisi istemiyor, HTTPS üzerinden JSON dönüyor. Yani binanın bağlı olduğu şebeke değişmiş. Raporun 1 ve 2 numaralı bulgusu, kodda değil servis tarafında çözülmüş; kütüphane ise eski hatta bağlı kalmış.

İkincisi yanıtın biçimi, ve burada 6 numaralı bulgu büyüdü. Yeni uç alan adlarını korumuş (`Kod`, `Aciklama`, `Alis`, `Satis`, `GuncellenmeZamani`), ama fiyatları virgüllü metin olarak veriyor: `"Alis":"49,350"`. 2020 yazısının çıktısında aynı alan noktalıydı (`7.45`). Kütüphanedeki `floatval()` virgülü görünce okumayı bırakıyor:

```php
<?php
var_dump(floatval("49,350")); // float(49)
var_dump(floatval("0,3086")); // float(0)
```

PHP 8.3.6'da çalıştırdım: Japon yeninin alış fiyatı sıfır. Hata yok, uyarı yok. Eski binayı yeni şebekeye "sadece adresi değiştirerek" bağlamaya kalkan bir ajan, çalışan ama yanlış fiyat basan bir kod teslim eder; test olmadığı için de kimse fark etmez. Raporun asıl işi buydu: düzeltmeden önce neyin kırılacağını söylemek.

## Rapordan sıraya

Röntgen bir yapılacaklar listesi değil, bir harita. Bulguları dokunma maliyetine göre sıralayınca tablo şöyle:

| Bulgu | Risk | Dokunma maliyeti | Ne zaman |
|---|---|---|---|
| CRLF/LF karışık, `.gitattributes` yok | Her diff gürültüye boğulur | Düşük, ama tek amaçlı ayrı bir commit ister | İlk iş |
| Test yok | Her değişiklik kör | Yok | İskele (#3) |
| Kaçışsız örnek çıktısı | XSS | Düşük | İskeleyle birlikte |
| Kopya metotlar, ölü kod | Bakım yükü | Davranış değişebilir | Testlerden sonra |
| Tip yok, `strict_types` yok | Sessiz tür dönüşümleri | Orta (ör. `float` para) | Mekanik dönüşüm (#5) |
| `http://`, kaynakta kimlik bilgisi, eski SOAP adresi | Servis yanıt vermiyor | Yeni uç HTTPS ve kimliksiz, ama sayı biçimi farklı | Yık-yap mı güçlendirme mi kararıyla (#2) |
| Bozuk CI | Sessiz alarm | Düşük | Yapı denetim (#9) |

Bu tabloyu ajan da önerebilir; imzayı siz atarsınız. Bir uyarı daha: bu bölümde dört aracın kendisini yan yana çalıştırmadım. Yukarıdaki kip davranışları resmi dokümanlardan ve Cursor ekibinin forum yanıtından. Aynı görev metnini farklı ekosistemlerde koşturup diff'leri karşılaştırmak serinin dördüncü bölümünün, Kaçak Kat'ın işi.

## Ne zaman işe yarar, ne zaman yaramaz?

**Yarar:** Kodu sizin yazmadığınız ya da yıllardır açmadığınız projelerde. Ekip ilk kez bir ajana legacy depo emanet edecekse. Refactor kapsamını yöneticiye ya da müşteriye anlatmanız gerekiyorsa; rapor, iş tahmininin ham maddesi.

**Yaramaz:** Üç dosyalık bir betikte rapor işin kendisinden uzun sürer. Bir de raporu okumayacaksanız hiç yazdırmayın. Okunmayan röntgen, çekilmemiş röntgenden kötüdür, çünkü "baktık" duygusu verir.

## Kolon kesmeden önce

Bu bölümde binaya tek çivi çakmadık; on bir bulgu, bir sıra ve bir kapı çıkardık. Bir sonraki bölümde, Kentsel Dönüşüm #2: Yık-Yap mı, Güçlendirme mi? – Baştan Yazmanın Faturası'nda, bu raporun önündeki ilk büyük kararı vereceğiz: binayı güçlendirmek mi, yıkıp yeniden yapmak mı. Serinin bütün bölümleri [Kentsel Dönüşüm](https://halityesil.com/seri/kentsel-donusum/) sayfasında.

Müteahhit hızlı olsun, sorun değil. Raporu imzalayan siz olun. Teknolojiyle kalın.
