İnceleme modu açık. Kesik çizgili öğeler bilerek seçilmiş etkileşimli/anlamlı bileşenlerdir.

← Blog

Anlama Borcu: Bir Vibe Coder’ı Güçlendiren 29 Kavram

Vibe coding yapıp temel kavramları bilmemenin bir adı olsa, “anlama borcu” olurdu. İşte o borcu kapatan harita: 29 kavram, her biri Sezgi’den Usta’ya dört seviyede.

 ·  ~38 dk okuma  ·  İnteraktif

Xiaohei adlı küçük siyah yaratık, LVL1’den LVL4’e yükselen dört basamaklı bir merdiveni elinde alet çantasıyla tırmanıyor
İçindekiler 01 · Sürüm Kontrolü & İşbirliği 02 · Web & Bilgisayar Nasıl Çalışır 03 · Veri & Durum 04 · Güvenlik 05 · Dağıtım & Operasyon 06 · Kod Sağlığı & Düşünme Anlama borcun ne kadar?

Geçen gün viral olan bir tweet vardı: bir yıl boyunca AI ile birçok uygulama üretmiş bir vibe coder, mülakata gururla projelerini göstermeye gitmiş. Mülakatçı tek bir şey sormuş: “git merge ile git rebase arasındaki fark nedir?” Cevap gelmemiş. Adam Claude’un yardımı olmadan tek satır push etmemiş bile.

Bu yeni bir olgu: üretebiliyorsun ama ürettiğinin altındaki kavramsal zemin yok. Buna anlama borcu diyelim. Tıpkı teknik borç gibi: her “anlamadan ama çalıştı, devam” anında küçük bir borç birikir. Kod çalıştığı sürece faizi görünmez — ta ki bir mülakat, bir production hatası ya da AI’ın çuvalladığı bir an gelene kadar. O an borç topluca ödenmeye çağrılır.

Klasik öğrenmede zorluk bir öğrenme sinyaliydi: bir şeyi anlamadan ilerleyemezdin, o sürtünme seni kavramı öğrenmeye zorlardı. AI o sürtünmeyi sıfırladı; artık anlamadan da ilerleyebiliyorsun. Yani sorun tembellik değil — öğrenmeyi tetikleyen direnç ortadan kalktı.

Bu yazı o borcu kapatmak için bir harita. Bir vibe coder’ı gerçekten güçlendirecek, daha kaliteli vibe coding yaptıracak 29 kavramı 6 kategoride topladım. Her kavramı dört seviyede açıyorum; her seviye bir öncekinin üstüne biner ve sonrakini anlamayı kolaylaştırır. Amaç ezber değil, neden’i görmek: AI’a iş yaptırırken neyi riske attığını bilmek.

LVL 1SezgiGünlük hayattan bir benzetme. En basit çekirdek fikir.
LVL 2PratikBununla pratikte ne yaparsın, ne görürsün; komut ve akış.
LVL 3MekanizmaKaputun altında gerçekte ne oluyor, neden böyle.
LVL 4Ustaİncelik, klasik tuzak ve ne zaman hangi seçim.

Kavram haritası

Altı kategori, 29 kavram. Herhangi bir kavrama atlamak için çiplere dokun.

01

Sürüm Kontrolü & İşbirliği

Git: kodun zaman makinesi ve ekibin ortak dili — geri dönmek, paralel çalışmak, çakışmadan birleşmek.

02

Web & Bilgisayar Nasıl Çalışır

Bir istek nereye gidiyor, kim cevaplıyor, veri hangi yoldan geliyor.

03

Veri & Durum

Bilginin biçimi ve uygulamanın hafızası. Bug'ların çoğu tam da burada doğar.

Derinlik:
Acelen varsa önce her kavramın yalnızca Sezgi’sini oku.
01

Sürüm Kontrolü & İşbirliği

Git: kodun zaman makinesi ve ekibin ortak dili — geri dönmek, paralel çalışmak, çakışmadan birleşmek.

01

Commit ve repo

Projenin kayıt noktaları: geri dön, değişimi gör

Xiaohei adlı küçük siyah yaratık, dosya durumunu zaman çizgisi üzerindeki numaralı cam kavanozlara mühürleyip rafa diziyor.
Görselin işi: her commit'in, geri dönülebilen ayrı bir kayıt anının mühürlenmiş kavanozu olduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir oyun düşün: zorlu bir bölüme girmeden önce kayıt noktası oluşturursun; bir şey ters giderse o ana geri yüklersin. Kodun da böyle kayıt noktalarına ihtiyacı var, çünkü dosyayı kaydetmek bir öncekini kalıcı olarak ezer ve eski hâl kaybolur. İşte repo oyunun kayıt dosyası, commit ise tek bir kayıt noktasıdır.AI bir ekranı baştan yazdı, beğenmedin: en son commit'e geri dönüp dünkü çalışan haline tek hamlede ulaşırsın.
LVL 2 PratikPratikte projenin klasörünü bir kez git init ile repo'ya (depo) çevirirsin; sonra kaydetmek istediğin değişiklikleri git add ile staging area'ya (sahne alanı) koyar, ardından bir mesajla commit'lersin. git log ile tüm geçmişi, yani kimin ne zaman neyi değiştirdiğini görürsün; bu yüzden basit bir Kaydet butonundan çok daha güçlüdür.git add app.js sonra git commit -m "giriş formu doğrulaması eklendi"
LVL 3 MekanizmaKaputun altında her commit, o anki dosya durumunun bir snapshot'ını (anlık görüntü) mesaj, yazar ve zaman bilgisiyle birlikte paketler; bu içerikten hesaplanan benzersiz bir hash (örn. a3f9c2e) ona kimlik olur. Her commit bir önceki commit'i (parent) işaret ettiği için history (geçmiş) kopmaz bir zincire dönüşür; eski commit'ler silinmeden yenileri eklenir, işte bu yüzden eski hâle güvenle dönebilirsin.git log --onelinea3f9c2e giriş formu doğrulaması ve onun parent'ı 7b1d004 ilk kurulum
LVL 4 UstaKlasik tuzak: tek dev commit'e her şeyi tıkmak; sonradan tek bir hatayı geri almak imkânsızlaşır, bu yüzden küçük ve anlamlı commit'ler at. Çalışmanı kaybetmeden geri sarmak için git revert (geri alma işlemini yeni bir commit olarak ekler) tercih et; git reset --hard ise geçmişi yok eder ve push'ladıysan ekibin geçmişini bozar. Hangi commit'in bir bug'ı getirdiğini ararken git bisect ile geçmişte ikili arama (binary search) yaparsın.Bir özellik prod'u bozdu: git revert a3f9c2e ile o commit'i temizce geri alır, geçmişi koruyarak düzeltirsin.
Vaka · gerçek bir senaryo

Elif, bir haftalık çalışmayı hiç commit'lemeden Cursor'a büyük bir refactor yaptırdı. AI dosyanın yarısını yeniden yazınca eski çalışan hâline dönmek istedi — ama geri dönülecek tek bir commit bile yoktu. Editörü kapatıp açınca undo geçmişi de sıfırlandı ve günün emeği uçtu.

Ertesi gün her anlamlı adımdan sonra git commit atmaya başladı. Bir sonraki kötü refactor'da git revert ile 10 saniyede eski hâline döndü; commit'in bir "Kaydet" değil, geri dönülebilir bir kontrol noktası olduğunu anladı.

Vibe coding’de Küçük, anlamlı commit'lerle çalışıp geçmişe bakabildiğinde, AI'ın bozduğu bir değişikliği saniyede geri alır ve çalışan koduna asla korkmadan dokunursun.

02

Branch ve Pull Request

Paralel çalış, gözden geçir, sonra ana hatta kat

Xiaohei, ana yoldan ayrılan bir yan patikada deney yapıp sonucunu bir kapıdan ana yola geri sunuyor.
Görselin işi: Ana hattan kopup paralel çalışmanın (branch) ve değişikliği bir onay kapısından (Pull Request) geçirip ana hatta katmanın aynı fikrin iki yarısı olduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiOrtak yazdığınız bir kitabın temiz nüshasına doğrudan karalama yapmak yerine, o sayfanın bir fotokopisini alıp kendi köşende denemeler yaparsın. İşin bitince "şu değişikliği temiz nüshaya da geçirelim mi?" diye sorarsın; biri okuyup onaylayınca asıl kitaba işlenir.Temiz nüsha = main; senin fotokopin = kendi branch'in; "geçirelim mi?" sorusu = Pull Request.
LVL 2 PratikYeni bir özelliğe başlarken main'den kopup kendine ayrı bir çalışma alanı (branch, yani dal) açarsın; commit'lerini orada biriktirir, push edersin. Sonra GitHub'da yeşil "Compare & pull request" butonuna basıp değişikliğini ana hatta katma teklifi (Pull Request) oluşturursun.git checkout -b feature/login → kod yaz → git commit -m "add login"git push -u origin feature/login → GitHub'da PR aç.
LVL 3 MekanizmaBir branch aslında dosyaların kopyası değil, sadece belirli bir commit'i gösteren hareketli bir etiket (pointer); commit attıkça bu etiket ileri kayar, böylece iki dal aynı geçmişten ayrılıp paralel ilerleyebilir. Pull Request ise Git'in değil GitHub'ın sunduğu bir katmandır: iki branch arasındaki farkı (diff) gösterir, ekip yorum bırakır, onaylanınca merge ile değişiklikler main'e birleştirilir.PR'da "this branch is 12 commits ahead, 0 commits behind main" görürsün; Merge pull request'e basınca senin branch'teki commit'ler main'in tepesine eklenir.
LVL 4 UstaDoğrudan main'e yazmazsın çünkü main herkesin ve genelde prod'un (canlı ortamın) referansıdır; yarım iş oraya sızarsa herkesi bozar. Branch'ini güncel tutmak için merge yerine sık sık git rebase main tercih et (geçmişi düz tutar), PR'ları küçük aç (review kolaylaşır) ve uzun yaşayan dallarda biriken conflict (çakışma) yükünden kaçınmak için erken ve sık birleştir.Eskimiş bir branch'i güncellerken: git fetch origingit rebase origin/main → çakışmaları çöz → git push --force-with-lease.
Vaka · gerçek bir senaryo

Burak "küçük bir düzeltme" diye doğrudan main'e push etti; ama değişiklik yarım kalmıştı ve o gece otomatik deploy çıkınca canlı site açılmaz oldu. Sabah ekip arkadaşı, ortada bir PR olmadığı için neyin değiştiğini bile göremedi.

O günden sonra her iş için git checkout -b ile ayrı bir branch açıp PR üzerinden review aldılar. Yarım işler artık main'e değil, kendi dalında bekliyordu.

Vibe coding’de Branch ve PR akışını bilen bir vibe coder, AI'a yarım kalmış denemeleri güvenle ayrı bir dalda yaptırıp çalışan main'i hiç bozmadan değişiklikleri review'dan geçirerek katar.

03

Merge vs Rebase

İki dalı birleştirmenin iki farklı yolu

Xiaohei adlı küçük siyah yaratık, iki ayrı raylı oyuncak tren parçasını birleştirirken bir yanda iki rayı bağlayan ek parça koyuyor, diğer yanda kendi vagonlarını ana rayın ucuna yeniden diziyor.
Görselin işi: merge'ün iki yolu bir ek parçayla birleştirdiğini, rebase'in ise vagonları tek düz raya yeniden dizdiğini tek bakışta kavratmak.
SeviyeNe demekÖrnek
LVL 1 Sezgiİki ayrı not defterinde aynı anda yazdığını düşün; bir noktada ikisini tek deftere toplaman gerekiyor. Merge (birleştirme), iki defteri yan yana koyup araya iki taraf burada birleşti diyen bir kapak sayfası eklemek gibidir; herkesin ne zaman ne yazdığı aynen durur. Rebase (yeniden tabanlama) ise senin notlarını temiz bir kâğıda, arkadaşının notlarının hemen ardına baştan ve sırayla yeniden geçirmektir; sonuç tek ve düz bir defter olur ama notların eski sırası ve kimliği değişir.İki kişi aynı belgeyi ayrı yazmış: merge "ikisini birleştirdim" diye bir iz bırakır; rebase senin satırlarını ötekinin altına yeniden, tek sıra hâlinde dizer.
LVL 2 PratikKendi dalında çalışırken main dalı ilerlediyse, dalını güncel tutman gerekir. Merge ile git merge main dersin; iki dal da kendi commit'lerine sahip olduğu için geçmişinde bir merge commit (birleşme commit'i) belirir ve dallanma çatallanmış görünür. Rebase ile git rebase main dersin; commit'lerin sanki en baştan main'in tepesinde yazılmış gibi düz bir çizgiye dönüşür ve pull request açtığında geçmiş çok daha temiz okunur.Özellik dalını güncellemek için: git fetch origin git rebase origin/main (düz geçmiş) ya da git merge origin/main (birleşme commit'i)
LVL 3 MekanizmaHer commit bir hash (benzersiz kimlik) taşır; bu kimlik, commit'in içeriğinden ve bir önceki commit'in (parent) hash'inden hesaplanır. Yani geçmiş, birbirine zincirlenmiş kutular gibidir. Merge hiçbir kutuyu değiştirmez; sadece iki zinciri birleştiren ekstra bir kutu (iki parent'lı merge commit) yaratır, mevcut hash'ler aynı kalır. Rebase ise senin commit'lerini yeni tabanın üstünde kopyalayarak yeniden oluşturur; içerik aynı olsa bile her commit yeni bir parent'a bağlandığı için yeni bir hash alır, yani aslında geçmiş yeniden yazılır.Rebase'den sonra commit mesajın aynı kalsa da hash değişir: öncesi a1b2c3d → sonrası 9f8e7d6 (aynı değişiklik, yeni kimlik)
LVL 4 UstaAltın kural: paylaşılan veya push edilmiş bir dalda rebase yapma; çünkü hash'leri değiştirdiğin an, o eski commit'lere dayanan arkadaşının geçmişiyle seninki çakışır ve herkes ağrılı bir karmaşaya düşer. Pratik tercih şudur: henüz sadece sende olan, kimseyle paylaşılmamış kişisel dalını temizlemek için rebase kullan; başkalarının da üstüne bina ettiği ya da uzun ömürlü bir dalı birleştirirken merge kullan. Eğer rebase sonrası push şart olduysa, körlemesine --force yerine güvenli olan --force-with-lease kullan; böylece uzaktaki dalda senin görmediğin yeni bir commit varsa push reddedilir ve arkadaşının işini ezmemiş olursun.Güvenli zorla-push (sadece kendi kişisel dalında): git push --force-with-lease origin feature-x Asla yapma: ekibin paylaştığı main üzerinde git rebase + git push --force
Vaka · gerçek bir senaryo

Deniz, ekip arkadaşlarının da üstünde çalıştığı develop dalını "geçmiş temiz olsun" diye git rebase edip --force ile push etti. Sabah üç kişinin commit'leri kaybolmuş gibi göründü, herkesin local'i bozuldu, yarım gün senkronizasyonla geçti.

Mesele rebase'in kötü olması değildi; paylaşılan bir dalda geçmişi yeniden yazmasıydı. "Kendi feature branch'inde rebase, paylaşılan dalda merge" kuralını o gün ezberledi.

Vibe coding’de Dalını güncel tutarken ne zaman rebase ile temiz bir geçmiş yapacağını, ne zaman merge ile güvende kalacağını bilmek; AI'a "şu dalı main'e güvenle entegre et" derken takım arkadaşlarının çalışmasını ezen bir felaketten seni korur.

04

Merge conflict (çakışma)

Aynı satırı ikiniz değiştirdiniz; hangisi kalsın?

Xiaohei iki farklı çizimi tek bir çivi deliğine asmaya çalışırken hangisini tutacağına karar veriyor.
Görselin işi: aynı satırı iki kişi değiştirdiğinde Git'in seçimi sana bıraktığını ve senin elle karar verdiğini kavratmak.
SeviyeNe demekÖrnek
LVL 1 Sezgiİki kişi aynı alışveriş listesindeki aynı satırı farklı yazdı: biri süt, öteki ayran. Liste ikisini de tutamaz, birinin karar vermesi gerekir. İşte merge conflict (çakışma) tam olarak budur; korkulacak değil, sadece bir karar anı.Sen tarifin başlığını "Mercimek Çorbası" yaptın, arkadaşın aynı satırı "Ezogelin Çorbası" yaptı. Defter ikisini birden yazamaz; biri seçilecek.
LVL 2 PratikÇoğu zaman bir git merge ya da git pull sonrası ekranda CONFLICT yazısını görürsün ve panik yaparsın; gerek yok. Editör sana çakışan dosyada <<<<<<<, =======, >>>>>>> işaretlerini gösterir; sen hangi versiyonu istiyorsan onu bırak, bu işaretleri sil, dosyayı kaydet ve git add + git commit ile bitir.Akış: git pull → "CONFLICT in app.js" → dosyayı aç, doğru satırı seç, işaretleri temizle → git add app.jsgit commit
LVL 3 MekanizmaGit aslında satır satır karşılaştıran akıllı bir birleştiricidir; iki taraf farklı satırları değiştirdiyse otomatik birleştirir. Ama aynı satırı iki taraf da değiştirmişse, Git hangisinin doğru olduğunu bilemez, çünkü her ikisi de geçerli bir niyettir. O yüzden tahmin yapmaz; iki versiyonu yan yana işaretlerle dosyaya koyup kararı sana bırakır.<<<<<<< HEAD = senin satırın, ======= = iki versiyonu ayıran çizgi, >>>>>>> main = gelen daldaki satır. Git bu üç işareti otomatik koyar.
LVL 4 UstaKlasik tuzak: işaretleri silmeyi unutmak; <<<<<<< kalıntısı kalan kod genelde derlenmez veya prod'da bozulur, o yüzden commit'ten önce git diff --check ile kontrol et. Çakışmaları azaltmak için küçük ve sık commit at, dalını sık güncel tut; karışırsan git merge --abort ile hiçbir şey olmamış gibi başa dönebilirsin.git merge --abort birleştirmeyi tamamen iptal eder; dosyaların çakışma öncesi haline döner, hiçbir veri kaybolmaz.
Vaka · gerçek bir senaryo

Seda ilk merge conflict'ini görünce panikledi; <<<<<<< işaretlerini anlamadan editörde "Accept Incoming" deyip geçti ve farkında olmadan kendi iki günlük değişikliğini sildi. Eksik özellik haftalar sonra ortaya çıktı.

Oysa conflict sıradandı: iki kişi aynı satıra dokunmuştu. Doğru yol, iki tarafı da okuyup ikisini birleştirmek, sonra işaretleri silip commit etmekti — körlemesine bir tarafı silmek değil.

Vibe coding’de Çakışmayı anlayan bir vibe coder, ekranda CONFLICT görünce kodu silip baştan yazmak yerine doğru satırı saniyeler içinde seçip devam eder, böylece AI ile çalışırken başkasının ya da kendi başka dalının emeğini yanlışlıkla ezmez.

05

.gitignore ve sızan secret'lar

Gizli anahtarlar repo'ya değil, cebinde kalsın

Xiaohei adlı küçük siyah yaratık, bir kapı eşiğinde elindeki listeye bakarak gizli anahtarı içeri almayıp dışarıda tutarken, listede olmayan bir anahtarın kapıdan kaçıp kırmızı uyarıyla repo'ya sızmasını gösteren çizim.
Görselin işi: .gitignore'un bir kapı bekçisi gibi gizli anahtarları repo'ya sokmadan dışarıda tuttuğunu, listeye yazılmayan bir anahtarınsa içeri sızıp tehlike yarattığını kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiProjeni başkalarıyla paylaşırken evinin fotoğrafını gönderir gibisin; ama ev anahtarını da kadrajda bırakmamalısın. .gitignore, fotoğrafa hangi şeylerin asla girmeyeceğini önceden söylediğin bir not gibidir: anahtarlar, şifreler ve çöp dosyalar dışarıda kalır.Paylaşılan klasöre koymadığın "şifreler.txt" dosyası gibi: herkes görsün istemediğin şeyi en baştan ayırırsın.
LVL 2 PratikProje köküne .gitignore adında bir dosya açıp içine paylaşmak istemediğin dosya/klasör adlarını satır satır yazarsın; en başta da .env (şifre ve API key'lerin yaşadığı yer) ile node_modules (yeniden indirilebilen kocaman bağımlılık klasörü) bulunmalı. Böylece git add . yaptığında bunlar commit'e (kaydetmeye) hiç dahil olmaz..gitignore içeriği:
.env
node_modules/
*.log
LVL 3 MekanizmaGit, henüz takip etmediği bir dosyayı eklemeden önce .gitignore kurallarına bakar ve eşleşenleri untracked (izlenmeyen) bırakır; ama bu kural yalnızca dosya daha önce commit'lenmemişse işe yarar. Bir kez git push ile gönderilen secret, sonradan silsen bile git geçmişinde (history) eski commit'lerin içinde durmaya devam eder; çünkü her commit o anki halin bir fotoğrafıdır ve eski fotoğraflar geçmişte kalır.Yanlışlıkla eklenmiş bir dosyayı sadece izlemeden çıkarmak için:
git rm --cached .env
(dosya diskte kalır ama artık takip edilmez)
LVL 4 UstaSızan bir anahtarda ilk hamle geçmişi temizlemek değil, anahtarı sağlayıcı panelinden iptal edip yenisiyle değiştirmektir (rotate): public bir repo'ya düşen key saniyeler içinde bot'lar tarafından taranır, geçmişi temizlemen onu zaten çalanı durdurmaz. Sonra geçmişi git filter-repo gibi bir araçla yeniden yazar, force-push edersin; ileride hiç düşürmemek için ise commit öncesi tarayan bir pre-commit hook (gitleaks gibi) kurman en sağlam savunmadır.Sızdırılmış key'i tüm geçmişten temizleme:
git filter-repo --path .env --invert-paths
(önce key'i mutlaka provider'dan iptal et)
Vaka · gerçek bir senaryo

Mert, Stripe secret key'ini test ederken doğrudan koda yazıp commit'ledi ve repo'yu public push etti. 20 dakika içinde botlar key'i bulup hesabında deneme şarjları başlattı.

Key'i koddan silmek yetmedi — git geçmişinde duruyordu. Çözüm, key'i hemen panelden iptal edip yenisini almak, onu .env'e koyup .gitignore'a eklemekti. O günden beri hiçbir secret koda girmiyor.

Vibe coding’de Secret'ı .env'de tutup .gitignore'a yazmayı ve sızdırırsa önce key'i döndürmeyi bilen bir vibe coder, AI'ın ürettiği koddaki gömülü API key'leri fark edip prod'a taşımadan temizler, böylece faturasını ve verisini koruyan güvenli projeler çıkarır.

02

Web & Bilgisayar Nasıl Çalışır

Bir istek nereye gidiyor, kim cevaplıyor, veri hangi yoldan geliyor.

06

Client–Server ve localhost

İstemci sorar, sunucu cevaplar; localhost senin makinen

Xiaohei bir mutfak servis penceresinin bir ucundan içeri uzanıp sipariş fısıldıyor, pencerenin diğer ucundaki kutu (sunucu) ona bir tabak yanıt geri gönderiyor; pencerenin küçük kapağında 3000 numarası yazılı, Xiaohei'nin ayağının altında ise kendi tarafına geri dönen bir döngü çizili.
Görselin işi: istemcinin istek yolladığını, sunucunun aynı kapıdan (port) yanıt döndürdüğünü ve localhost'un sadece Xiaohei'nin kendi tarafına bağlı olduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir restoranda müşteri sipariş verir, mutfak hazırlar ve tabağı geri yollar. Tarayıcın müşteridir (istemci), siparişi alıp yemeği yapan mutfak ise sunucudur; sen sadece istersin, asıl işi arkadaki taraf yapar.Sen tarayıcıya adres yazarsın (sipariş), karşıdaki sunucu sayfayı pişirip geri gönderir (tabak gelir).
LVL 2 PratikAI sana bir uygulama yazdığında genelde npm run dev der ve ekranda http://localhost:3000 çıkar. Buradaki localhost kendi makinendeki sunucu demektir, 3000 ise port yani o sunucunun açık kapı numarasıdır; o adrese girince kendi bilgisayarında çalışan uygulamayı görürsün.npm run dev → "Local: http://localhost:3000" → tarayıcıda bu adresi açınca uygulaman görünür.
LVL 3 Mekanizmanpm run dev makinende bir sunucu süreci başlatır ve onu bir porta (örneğin 3000) bağlar; tarayıcın o porta bir request (istek) yollar, süreç de HTML/JSON ile response (yanıt) döner. localhost aslında 127.0.0.1 adresinin takma adıdır ve bu loopback adresi sadece senin makineni işaret eder, ağdan dışarı çıkmaz.Tarayıcı: GET http://localhost:3000/api/users → sunucu: 200 OK + JSON liste döner.
LVL 4 Usta"Bende çalışıyor" tuzağı tam olarak burada doğar: localhost herkeste kendi makinesini gösterdiği için, senin sunucun arkadaşının tarayıcısında yoktur. Paylaşmak için ya kodu gerçek bir sunucuya deploy et ya da ngrok gibi bir tünelle geçici genel bir URL aç; ayrıca aynı portu iki uygulamayla aynı anda kullanmaya çalışırsan EADDRINUSE (port zaten kullanımda) hatası alırsın.Arkadaşına link atmak için: npx ngrok http 3000 → sana https://abc123.ngrok.app gibi herkesin açabildiği bir adres verir.
Vaka · gerçek bir senaryo

Can, uygulamayı localhost:3000'de çalıştırıp "bitti" dedi ve arkadaşına http://localhost:3000 linkini gönderdi. Arkadaşında açılmadı — çünkü o adres herkesin kendi makinesini gösteriyordu.

Uygulamayı gerçekten paylaşmak için bir sunucuya deploy edip public bir adres vermek gerektiğini; localhost'un sadece "bu bilgisayar" demek olduğunu böyle öğrendi.

Vibe coding’de localhost'un sadece kendi makinen olduğunu kavrayan vibe coder, "bende çalışıyor ama deploy edince patlıyor" döngüsünü daha yaşamadan görür ve uygulamasını doğru anda gerçek sunucuya taşır.

07

HTTP: method ve status kodları

Her istek bir fiş, her cevap bir damga

Xiaohei adlı küçük siyah yaratık, üstünde method yazan bir fiş tutarak bir tezgaha uzatıyor; tezgahın arkasından üç haneli status kodlu damgalı bir cevap geri geliyor.
Görselin işi: her web isteğinin bir niyet fişi (method) gönderip karşılığında damgalı bir sonuç kodu (status) aldığını kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir restoranda garsona verdiğin siparişi düşün: ne yapmak istediğini (getir, ekle, değiştir, kaldır) söylersin; mutfak da geri dönüp ne olduğunu (tamam, yanlış masa, böyle bir yemek yok, mutfak yandı) söyler. İnternette her tıklamada tarayıcın da sunucuya tam böyle bir fiş uzatır ve karşılığında damgalı bir cevap alır."Bana 12 numaralı menüyü getir" → garson: "Tamam, geliyor." (istek + cevap)
LVL 2 PratikBir endpoint'e (sunucudaki adres) istek atarken seçtiğin method niyetini belirler: GET veri al, POST yeni kayıt oluştur, PUT/PATCH güncelle, DELETE sil. Cevabın başındaki üç haneli status kodu ise işin tutup tutmadığını söyler; AI'ya "şu API'ye istek at" dediğinde tarayıcının network sekmesinde göreceğin ilk şey budur.curl -X POST https://api.site.com/users -H 'Content-Type: application/json' -d '{"name":"Eyup"}' → cevap: 201 Created
LVL 3 MekanizmaStatus kodları rastgele değil; ilk hanesi sınıfı belirleyen bir sistem: 2xx başarı, 3xx yönlendirme (başka adrese git), 4xx senin (istemcinin) hatan, 5xx sunucunun hatası. 4xx içinde fark kritiktir: 401 "kim olduğunu bilmiyorum" (kimlik yok / login lazım), 403 "kim olduğunu biliyorum ama yetkin yok", 404 "böyle bir şey yok". 5xx gördüğünde sorun genelde sende değil, sunucudadır.Token'sız korumalı endpoint'e istek → 401 Unauthorized; başkasının verisini silmeye çalışmak → 403 Forbidden
LVL 4 UstaKlasik tuzak: POST ile PUT'u karıştırmak. POST her çağrıda yeni kayıt yaratabilir (idempotent değil), PUT ise aynı isteği on kez atsan da sonuç aynı kalır (idempotent) — bu yüzden retry/yeniden deneme mantığını GET ve PUT gibi idempotent method'lar üstüne kurmak güvenlidir. Bir diğer incelik: API yanlış status döndürüyorsa (hata olduğu halde 200) AI agent'lar "başarılı" sanıp sessizce yanlış iş yapar; bu yüzden önce cevabın gövdesine değil, status koduna bakmayı öğret.Hata durumunda 200 OK + {"error":"..."} döndüren API anti-pattern'dir; doğrusu 400 Bad Request döndürmektir
Vaka · gerçek bir senaryo

Ayşe'nin uygulaması 401 Unauthorized dönüyordu ama o, sunucu çöktü sanıp saatlerce backend loglarını karıştırdı. Oysa 401, sunucunun gayet sağlam çalıştığını, sadece isteğin token taşımadığını söylüyordu.

Status kodlarını öğrenince teşhis saniyelere indi: 4xx "sen yanlış istedin", 5xx "sunucu patladı". Artık 401 görünce önce token'a bakıyor.

Vibe coding’de İstek/cevap anatomisini ve status sınıflarını bilen vibe coder, AI'ın ürettiği kod patladığında network sekmesindeki kodu okuyup hatanın kendi tarafında mı (4xx) yoksa sunucuda mı (5xx) olduğunu saniyeler içinde anlar ve doğru yere müdahale eder.

08

API ve REST

Programların birbiriyle konuşma sözleşmesi

Xiaohei adlı küçük siyah yaratık, üzerinde adresler yazan bir posta kutusu dizisine zarf atıp karşılığında JSON etiketli paket alıyor.
Görselin işi: API çağrısının, doğru adresteki kapıya istek bırakıp düzenli bir yanıt geri almak olduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir restoranda mutfağa kendin girmezsin; garsona sipariş verirsin, o da yemeği getirir. API tam olarak budur: bir programın, başka bir programdan bir şey istemek için kullandığı resmi garson. Sen menüdeki dilden konuşursun, o da arkadaki mutfağı senin yerine yönetir.Sen: "Bugünün hava durumunu istiyorum" → garson (API) → mutfak (hava servisi) → garson sana bugünün hava bilgisini getirir.
LVL 2 PratikPratikte bir API'ye bir adrese (buna endpoint denir, yani belirli bir işlevin kapısı) istek atarsın ve geri genelde JSON (programların okuyabildiği düzenli metin) alırsın. AI'a "şu hava durumu API'sini çağır" dediğinde, arka planda bir fetch ya da curl satırı bu kapıyı çalar.curl https://api.weather.com/v1/istanbul → geri döner: { "sehir": "Istanbul", "derece": 22 }
LVL 3 MekanizmaREST, API'leri düzenlemenin en yaygın mantığıdır: her şeyi bir kaynak (users, orders gibi) olarak görür, kaynağı bir URL ile adresler ve ne yapmak istediğini HTTP method'uyla söyler. GET okur, POST yeni kayıt ekler, PUT/PATCH günceller, DELETE siler; gönderdiğin veri (gövde) genelde JSON'dur ve dönen yanıtın başında bir status code (200 başarılı, 404 bulunamadı) bulunur.POST /orders + gövde { "urun": "kahve", "adet": 2 } → yanıt 201 Created ve oluşan siparişin id'si döner.
LVL 4 UstaKlasik tuzak: üçüncü parti API çağırırken API key'i (kimlik anahtarını) doğrudan kodun içine gömmek; bu anahtar koda sızar ve başkası senin adına faturayı şişirir, onun yerine .env dosyası kullan. İncelik olarak, dış API'lerin saniyedeki istek sınırı (rate limit) ve hata dönme ihtimali vardır; bu yüzden çok fazla istek attığında dönen 429 (Too Many Requests) veya sunucu hatası olan 500 gibi durumlarda kısa bekleyip tekrar deneyen bir mantık (retry) kurmak ürünü çökmekten kurtarır.Anahtarı başlıkta gönder, kodda değil: curl -H "Authorization: Bearer $API_KEY" https://api.servis.com/v1/data (anahtar .env'den gelir).
Vaka · gerçek bir senaryo

Kerem bir hava durumu API'sine istek atıyor ama hep boş cevap alıyordu; "çalışmıyor" deyip rastgele kod parçaları denedi. Sorun basitti: dokümana göre endpoint POST ve JSON gövde beklerken o GET atıyordu.

Yani endpoint'in sözleşmesine uymuyordu. Dokümandaki örnek isteği birebir taklit edince ilk seferde çalıştı.

Vibe coding’de Bir vibe coder, API ve REST mantığını bildiğinde AI'a "şu üçüncü parti servisi POST ile çağır, anahtarı .env'e koy" gibi doğru ve güvenli yönlendirmeler verip uygulamasını dış dünyaya (ödeme, harita, hava durumu) sağlam biçimde bağlayabilir.

09

DNS ve domain çözümleme

İnternetin telefon rehberi: isimden IP'ye

Xiaohei adlı küçük siyah yaratık, bir isim etiketini elindeki delikli kutuya sokuyor ve diğer ucundan numaralı bir kart çıkıyor; akış oklarla gösteriliyor.
Görselin işi: bir ismin (domain) nasıl bir numaraya (IP) çevrildiğini, DNS'i bir çevirici kutu olarak göstererek kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir insanın adını biliyorsun ama telefon numarasını bilmiyorsun; rehbere bakıp numarayı buluyorsun. DNS (Domain Name System) de tam olarak budur: tarayıcıya yazdığın akilli-site.com gibi bir ismi alır, makinelerin gerçekten konuştuğu numaraya, yani IP adresine çevirir. Sen ismi hatırlarsın, DNS numarayı bulur.akilli-site.com → DNS → 76.76.21.21
LVL 2 PratikBir domain alıp siteni yayınladığında, domain sağlayıcısının panelinde (GoDaddy, Namecheap, Cloudflare) DNS kayıtlarını ayarlarsın: hosting'in sana verdiği IP'yi bir A record (kayıt) olarak girer, çoğu zaman www için de bir CNAME eklersin. Değişiklik anında görünmeyebilir; buna propagation (yayılma) gecikmesi denir ve TTL'e bağlı olarak bazen saatler sürebilir.Type: A · Name: @ · Value: 76.76.21.21 · TTL: 3600
LVL 3 MekanizmaTarayıcı bir ismi çözerken zincir halinde sorulur: önce resolver (genelde ISP'nin sunucusu) kendi cache'ine bakar; yoksa kök (root) → .com (TLD) → senin domain'inin yetkili nameserver'ına gider ve oradaki A kaydını okur. Her cevap bir TTL (Time To Live) süresiyle gelir; bu süre boyunca sonuç cache'lenir, bu yüzden bir kaydı değiştirsen bile eski IP, TTL bitene kadar dünyanın bir yerinde hâlâ servis edilebilir.dig akilli-site.com;; ANSWER: akilli-site.com. 3600 IN A 76.76.21.21
LVL 4 UstaKlasik tuzak: "Domain'i bağladım ama site açılmıyor" şikayetinin çoğu DNS'tir; ya yanlış nameserver, ya eski IP'li bir A kaydı, ya da yüksek TTL yüzünden henüz yayılmamış bir değişiklik. Geçiş yapacaksan IP'yi değiştirmeden önce TTL'i düşür (300 gibi) ve eski TTL süresi geçene kadar bekle; böylece kesinti riskini azaltırsın. Kök domain'i (apex) bir IP yerine bir servise yöneltmen gerekiyorsa ALIAS/ANAME ya da Cloudflare'in CNAME flattening'ini kullan, çünkü apex'te düz CNAME standartlara aykırıdır.dig +short akilli-site.com @8.8.8.8 ile Google'ın resolver'ının gördüğü IP'yi doğrula
Vaka · gerçek bir senaryo

Zeynep domaini Railway'e bağladı, 5 dakika sonra "site açılmıyor" diye panikleyip ayarları tekrar tekrar değiştirdi — her değişiklik işi daha da uzattı. Aslında bir sorun yoktu: DNS değişikliğinin yayılması (propagation) zaman alır.

Bir kahve molası sonra site kendiliğinden açıldı. TTL ve propagation'ı bilmek, olmayan bir sorunu "düzeltmeye" çalışırken işleri bozmaktan kurtarıyor.

Vibe coding’de DNS kayıtlarını ve TTL/propagation mantığını bilen bir vibe coder, deploy ettikten sonra "site açılmıyor" paniğine kapılmadan sorunun kodda mı yoksa domain ayarında mı olduğunu dakikalar içinde teşhis eder.

10

Frontend / Backend / Database

Vitrin, mutfak ve kiler: kod nerede çalışır

Xiaohei bir tezgahın arkasında garson gibi durur; soldaki müşteri penceresinden gelen sipariş fişi turuncu okla mutfaktan geçer, arkadaki kilit vurulu kiler dolabından malzeme alınır; kiler dolabına müşteri tarafından uzanan el kırmızı çarpıyla engellenmiştir.
Görselin işi: frontend'in sipariş verdiğini, backend'in işi yaptığını ve database kilerine yalnızca backend'in girebildiğini tek bakışta kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir uygulamayı restoran gibi düşün: müşterinin gördüğü vitrin ve masalar (frontend), arkadaki mutfak (backend) ve malzemelerin saklandığı kiler (database). Müşteri kilere giremez; sadece garsona sipariş verir, mutfak yemeği hazırlayıp önüne koyar.Sen (frontend) garsona "bir kahve" dersin → mutfak (backend) kileri (database) açıp çekirdeği alır, kahveyi yapar, sana servis eder. Sen ne kileri görürsün ne de tarifi.
LVL 2 PratikVibe coding yaparken kodun nerede çalıştığına dikkat edersin: tarayıcıda görünen butonlar ve renkler frontend, "şu kullanıcının verisini getir" mantığı backend, o verinin durduğu yer database. AI'a "buton ekle" dersen frontend'i, "kaydı sakla" dersen backend ile database'i değiştirir.Bir to-do uygulamasında: <button>Ekle</button> frontend'tedir; tıklayınca backend'deki POST /api/todos endpoint'i çağrılır; o da görevi database'deki todos tablosuna yazar.
LVL 3 MekanizmaFrontend kodu kullanıcının tarayıcısında çalışır, yani herkes onu görebilir; backend kodu senin kontrolündeki sunucuda çalışır, dışarıdan görünmez. İkisi arasında veri, ağ üzerinden HTTP istekleriyle (genelde JSON) gidip gelir; database'e ise yalnızca backend bağlanır.Akış: tarayıcı fetch('/api/user?id=7') ile sunucuya HTTP isteği yollar → backend, database'e SELECT * FROM users WHERE id = 7 sorgusu atar → sonucu JSON olarak tarayıcıya döner: { "name": "Ada" }.
LVL 4 UstaFrontend'e gönderdiğin her şey kullanıcının elindedir: API anahtarı, parola veya database şifresini asla frontend'e koyma, çünkü tarayıcıda herkes "View Source" ve geliştirici araçlarıyla görebilir. Sırlar (secret) backend'de bir environment variable olarak durur; database'e giden bağlantı da yalnızca sunucudan açılır. Aynı şekilde, kullanıcıdan gelen veriye asla güvenme; doğrulamayı (validation) her zaman backend'de tekrar yap.Yanlış: frontend kodunda const stripeKey = "sk_live_abc123". Doğru: anahtar backend'de process.env.STRIPE_SECRET_KEY olarak kalır; frontend sadece /api/checkout endpoint'ini çağırır, ödemeyi backend tamamlar.
Vaka · gerçek bir senaryo

Ozan, OpenAI API key'ini doğrudan React (frontend) koduna koyup uygulamayı yayınladı. Birkaç gün sonra faturada beklenmedik bir artış gördü: key, tarayıcıya inen kodun içinde açıkça görünüyordu ve biri onu kopyalamıştı.

Secret'ın backend'de (mutfakta) durması, frontend'in (vitrin) ona yalnızca kendi sunucusu üzerinden erişmesi gerektiğini pahalı yoldan öğrendi.

Vibe coding’de Hangi kodun nerede çalıştığını bilince AI'a doğru yeri tarif edersin ("butonu değil, endpoint'i düzelt") ve en kritik hatadan, secret'ı frontend'e sızdırmaktan, kaçınırsın.

03

Veri & Durum

Bilginin biçimi ve uygulamanın hafızası. Bug'ların çoğu tam da burada doğar.

11

Veri tipleri ve yapıları

Her veriye doğru kap, doğru iş

Xiaohei adlı küçük siyah yaratık, üç etiketli kabın önünde her veriyi doğru kaba yerleştiriyor; yanlış kaba giden bir parça kırmızıyla işaretli.
Görselin işi: her veri parçasının kendi doğru kabına (tipine) konması gerektiğini, yanlış kaba düşeninse hata ürettiğini kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiMutfakta her malzemenin kendi kabı vardır: un torbada, su şişede, yumurtalar viyolde durur. Veri tipleri ve yapıları da bilgiyi doğru kaba koymaktır; suyu viyole, yumurtayı torbaya koyarsan iş bozulur.İsim bir kelime (string), yaş bir sayı (number), "premium üye mi?" bir evet/hayır (boolean) kabına girer.
LVL 2 PratikTek bir değerin tipini bilmek başlangıçtır; sıra bu değerleri bir araya toplayan iki büyük yapıya gelir: sıralı bir liste olan array (dizi) ve etiketli çekmeceler gibi anahtar-değer tutan object (nesne). Sırası önemli şeyler array'e, "adı-soyadı-yaşı" gibi etiketli alanlar object'e gider.["süt", "ekmek", "yumurta"] bir alışveriş listesi (array); { ad: "Ayşe", yas: 30, aktif: true } bir kullanıcı (object).
LVL 3 MekanizmaBu iki yapı kaputun altında farklı çalışır: array sıralı bir hat olduğu için bir öğeye sırasıyla (indeksle) liste[0] diye ulaşırsın; object ise bir etiketi (anahtar) değere eşler, yani kullanici.ad demek aramadan doğrudan o çekmeceyi açmaktır. "Boş" da iki türlüdür: null bilerek konmuş boşluktur, undefined ise hiç doldurulmamış, var olmayan değerdir.const u = { ad: "Ayşe" }; burada u.ad → "Ayşe", u.yasundefined (alan hiç yok); oysa u.telefon = null dersen "telefonu yok, bilerek boş" demektir.
LVL 4 UstaBu tip ve boşluk ayrımını ciddiye almazsan iki klasik tuzağa düşersin. Birincisi tip karışması: form'dan gelen "30" bir string'tir, JavaScript'te "30" + 5 sayı toplamaz, metni birleştirir ve "305" verir; hata değil ama yanlış sonuç. İkincisi null veya undefined üzerinde alan okumaya çalışmaktır; agent'ın yazdığı kodu çökerten en sık nedendir. Bu yüzden veriyi kullanmadan önce tipini netleştir ve boş ihtimalini ele al.Number("30") + 535 (önce sayıya çevir); çökmeyi önlemek için user?.adres?.sehir ?? "bilinmiyor" gibi güvenli erişim kullan.
Vaka · gerçek bir senaryo

Buse, API'den gelen kullanıcıyı user.address.city diye okudu; çoğu kullanıcıda çalıştı ama adresi olmayan birinde uygulama Cannot read properties of undefined deyip çöktü. Sebep, address'in bazen null gelmesiydi.

Null olasılığını hesaba katıp user.address?.city yazınca çökme bitti. "Her zaman dolu sandığın alan boş gelebilir" dersini aldı.

Vibe coding’de Veriyi doğru tip ve yapıda tutmayı bilen bir vibe coder, "30 + 5 neden 305 çıktı?" ya da "cannot read properties of undefined" gibi en sık çöküşleri AI'a net tarif edip tek seferde düzelttirir.

12

JSON ve serileştirme

Programların ortak konuştuğu evrensel metin dili

Xiaohei adlı küçük siyah yaratık, canlı bir oyuncağı bir presten geçirerek düz bir metin şeridine bastırıyor; şerit bir borudan geçip diğer uçtaki ikinci Xiaohei tarafından tekrar oyuncağa dönüştürülüyor.
Görselin işi: bir nesnenin önce düz metne paketlenip (serialize) sonra karşı tarafta tekrar nesneye açıldığını (parse) tek bakışta kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiAklındaki bir mobilyayı bir arkadaşına telefonda tarif edip onun aynısını kurmasını istediğini düşün: nesnenin kendisini gönderemezsin, ama herkesin anladığı kelimelerle yazılı bir tarif gönderebilirsin. JSON (JavaScript Object Notation) tam olarak budur; bir bilgisayardaki veriyi, başka bir bilgisayarın da çözebileceği düz bir metne çevirmenin ortak yoludur.Bir kişi: {"ad": "Ada", "yas": 9, "aktif": true} — süslü parantez, çift tırnaklı anahtar, iki nokta, değer.
LVL 2 PratikPratikte iki şey yaparsın: elindeki nesneyi metne çevirirsin (buna serialize denir, yani paketleme) ve gelen metni tekrar nesneye çözersin (buna parse denir, yani açma). Bir API'ye (uzaktaki servise) istek attığında cevap çoğu zaman JSON gelir; sen de onu JSON.parse ile kullanılabilir veriye dönüştürürsün.const veri = JSON.parse(cevap) sonra console.log(veri.ad) ile alan değerine erişirsin.
LVL 3 MekanizmaKaputun altında nesnen bellekte canlı bir yapıdır; ağ üzerinden ya da diske ise yalnızca bayt dizisi (sıralı metin) geçebilir. JSON.stringify nesneyi gezip her alanı kurallı karakterlere yazar; JSON.parse ise bu metni soldan sağa okuyup yapıyı yeniden kurar. JSON sadece birkaç tip tanır: string, number, boolean, null, array, object — bu yüzden tarih (Date) veya fonksiyon doğrudan sığmaz; tarih çoğu zaman string'e çevrilir, fonksiyon ise tamamen atlanır.JSON.stringify({t: new Date()}) sonucu {"t":"2026-06-07T10:00:00.000Z"} — Date nesnesi otomatik ISO string olur.
LVL 4 UstaKlasik tuzak trailing comma'dır (son elemandan sonra fazladan virgül); JSON bunu kabul etmez ve JSON.parse anında SyntaxError fırlatır, o yüzden dışarıdan gelen her parse'ı try/catch içine al. İkinci tuzak: parse ettiğin tarih artık string'dir, gerçek tarih davranışı için elle new Date(...) ile geri çevirmen gerekir. Üçüncüsü: iç içe veride var olmayan bir alana erişmeden önce ara seviyelerin geldiğini doğrula (örn. veri?.kullanici?.ad).try { JSON.parse(metin) } catch (e) { /* bozuk JSON, kullaniciya hata goster */ }
Vaka · gerçek bir senaryo

Emre, bir API'den gelen metni JSON.parse ile çözerken uygulaması ara ara çöküyordu — her zaman değil, bazen. Meğer API hata durumunda JSON yerine düz bir HTML hata sayfası dönüyormuş; JSON.parse de onu çözemeyip patlıyormuş.

Cevabı try/catch içine alıp önce içeriğin gerçekten JSON olduğunu kontrol edince sorun bitti.

Vibe coding’de JSON'ı ve serialize/parse'ı bilen bir vibe coder, API cevaplarını korkmadan okur, "JSON.parse patladı" hatasını saniyede teşhis eder ve verisini farklı servisler arasında güvenle taşır.

13

Veritabanı: SQL vs NoSQL

Sabit tablolu defter mi, esnek kutu mu?

Xiaohei sol elinde önceden bölmeli sabit bir tepsiyi, sağ elinde her boyutta serbest kutuları tutuyor; iki sistem arasında veri taşları akıyor.
Görselin işi: SQL'in sabit-bölmeli düzeniyle NoSQL'in serbest-kutu esnekliği arasındaki temel farkı tek bakışta kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiSQL önceden çizilmiş sütunları olan bir muhasebe defteri gibidir: her satır aynı kolonları doldurmak zorunda ve satırlar birbirine atıfla bağlanır. NoSQL ise her birine istediğini koyabildiğin etiketli kutular gibidir; bir kutuda 3 şey, diğerinde 10 şey olabilir, kimse karışmaz.Defter (SQL): her müşteri satırı mutlaka ad, telefon, şehir kolonlarını doldurur. Kutu (NoSQL): bir müşteri kutusunda sadece ad+telefon, diğerinde ad+telefon+3 adres+notlar olabilir.
LVL 2 PratikPratikte SQL tarafında önce bir tablo tanımlarsın (kolonlar + tipleri), sonra INSERT / SELECT ile yazıp okursun; tablolar arası JOIN ile "şu kullanıcının tüm siparişlerini" birleştirirsin. NoSQL tarafında ise (ör. MongoDB) bir doküman (JSON benzeri obje) kaydeder, ilişkili veriyi çoğu zaman aynı dokümanın içine gömersin. Vibe coding'de "login + ödeme + ilişkiler" içeren çoğu uygulama için Postgres (SQL) güvenli varsayılandır.SQL: SELECT * FROM orders WHERE user_id = 42; — NoSQL eşdeğeri: db.users.find({ _id: 42 }) kullanıcı dokümanını getirir; siparişler zaten o dokümanın içine gömülü olduğu için ayrı sorgu gerekmez.
LVL 3 MekanizmaSQL'de bir şema (kolonlar ve tipler) vardır; her satırın primary key'i kimliğidir, bir tablodaki foreign key başka tablonun primary key'ine işaret ederek ilişkiyi kurar, böylece veri tek yerde durur (tekrarsız) ve veritabanı motoru JOIN ile satırları çalışma anında eşleştirir. NoSQL motoru ise şemayı zorlamaz: veriyi olduğu gibi dokümana yazar, hız için ilişkili veriyi çoğaltarak (denormalize) birlikte saklar, JOIN yapmadan tek okumada getirir.İki tablo: users(id PK) ve orders(id PK, user_id FK → users.id). SELECT u.name, o.total FROM users u JOIN orders o ON o.user_id = u.id; her siparişe sahibinin adını ekler — ad sadece users tablosunda bir kez durur.
LVL 4 UstaKlasik tuzak: NoSQL'i "şemasız, daha kolay" diye seçip ilişkili veriyi her yere kopyalamak — sonra bir e-postayı güncellemek için onlarca dokümanı tek tek düzeltmek gerekir ve tutarsızlık başlar; SQL'de o veri tek satırdadır. Kural: veriler birbirine bağlı, doğruluk kritik (para, sipariş, kullanıcı) ise SQL seç; veri büyük, gevşek yapılı ve devasa ölçekte (log, mesaj akışı) ise NoSQL. Redis ayrı bir tür: kalıcı ana veritabanı değil, milisaniyelik anahtar-değer cache/oturum deposu olarak SQL'in yanında kullanılır.Doğru bölüşüm: kullanıcı ve sipariş tabloları Postgres'te (foreign key + tutarlılık), oturum token'ları Redis'te SET session:abc "{...}" EX 3600 ile 1 saatlik cache olarak — biri kalıcı doğruluk, diğeri hız için.
Vaka · gerçek bir senaryo

Selin, hızlı başlamak için her şeyi şemasız bir NoSQL koleksiyonuna attı. İlk ay harika gitti; ama "kullanıcıların siparişleri" gibi ilişkili veriler büyüyünce her sorgu elle birleştirme istedi, bazı kayıtlarda price string bazılarında number oldu ve raporlar tutmadı.

İlişkili ve tutarlı veri için baştan SQL + şema seçmesi gerektiğini, sonradan taşımanın çok daha pahalı olduğunu gördü.

Vibe coding’de Hangi veritabanını seçeceğini bilen vibe coder, AI'a \"Postgres'te users ve orders tablosunu foreign key ile kur\" gibi net talimat verir ve ileride veriyi düzeltilemez tutarsızlıktan kurtarır.

14

State (durum)

Uygulamanın o anki hafızası, çoğu bug'ın kaynağı

Xiaohei adlı küçük siyah yaratık, üstünde anlık sayı yazan tek bir beyaz tahtayı tutuyor; bir oktan gelen güncelleme tahtadaki değeri değiştiriyor, ikinci bir kopya tahta ise eskimiş kalmış ve kırmızıyla işaretli.
Görselin işi: State'in uygulamanın tek güncel hafızası olduğunu ve aynı bilgiyi ikinci bir yerde tutmanın eski/bayat veri ürettiğini kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiState (durum), uygulamanın o an aklında tuttuğu her şeydir: kullanıcı giriş yaptı mı, sepette kaç ürün var, hangi sekme açık. Tıpkı bir kafedeki sipariş fişi gibi düşün; üstünde yazan o anki gerçeği gösterir ve sen ekledikçe değişir.Sepetinde 2 ürün varken bir tane daha eklersin: state "2 ürün"den "3 ürün"e geçer. Ekrandaki sayı da o state'i yansıtır.
LVL 2 PratikVibe coding yaparken state, çoğu zaman kodda tuttuğun bir değişkendir; bir butona basınca onu güncellersin, ekran da o yeni değere göre yeniden çizilir. En sık gördüğün hata, ekranın güncel state'i göstermemesi yani stale (bayat) görünümdür.React'te const [count, setCount] = useState(0) tanımlarsın; butona basınca setCount(count + 1) çağırıp ekrandaki sayıyı 1 artırırsın.
LVL 3 MekanizmaKaputun altında state, bellekte (memory) duran bir değerdir; sen onu doğrudan değiştirmek yerine bir güncelleme fonksiyonu çağırırsın, böylece sistem "state değişti" diye haber alıp ekranı yeniden render eder. Bu yüzden state'i elle değiştirmek değil, hep o fonksiyonla değiştirmek gerekir; aksi halde sistem değişiklikten haberdar olmaz ve ekran yenilenmez.Yanlış: count = count + 1 (sistem haberdar olmaz, render tetiklenmez). Doğru: setCount(count + 1) (sistem yeniden render'ı tetikler).
LVL 4 UstaBug'ların çoğu, aynı bilginin iki yerde tutulup birbirinden kopmasından çıkar; çözüm tek bir source of truth (tek doğru kaynak) belirlemek ve diğer her şeyi ondan türetmektir. Mevcut state'ten hesaplayabildiğin hiçbir şeyi ayrı bir state'te tutma; ayrıca server'dan gelen veriyi gereksizce local state'e kopyalamak yerine onu olduğu yerden okumayı tercih et, yoksa stale veriyle yaşarsın.Kullanıcının tam adını ayrı bir state'te tutma; const fullName = firstName + " " + lastName diye anlık türet ki ad değişince otomatik güncellensin.
Vaka · gerçek bir senaryo

Tolga'nın sepet sayacı bazen yanlış gösteriyordu: üstte "2 ürün" yazarken sepette 3 ürün vardı. Sebep, sayıyı iki ayrı yerde tutmasıydı — bir sayaç değişkeni ve asıl sepet listesi; biri güncellenince diğeri eski kalıyordu.

Sayıyı tek kaynaktan (sepet listesinin uzunluğundan) türetince tutarsızlık tamamen bitti. "Aynı gerçeği iki yerde tutma."

Vibe coding’de State'in nereden okunup nasıl güncellendiğini ve tek doğru kaynağı bilen vibe coder, "veri ekranda neden eski kaldı" tipi bug'ları AI'a doğru tarif edip dakikalar içinde çözer.

04

Güvenlik

En sık ve en pahalı hataların çıktığı yer; AI'ın ürettiği kodda özellikle.

15

Environment variables / .env

Şifreleri koddan ayır, dışarıda sakla

Xiaohei karakteri, üstünde KEY yazan gizli anahtarı koddan ayırıp duvardaki kilitli bir kasaya koyuyor; anahtarı kodun içine yazma yolu kırmızı çarpıyla işaretli.
Görselin işi: gizli anahtarın kodun içine değil, kodun dışındaki ayrı ve güvenli bir yere konması gerektiğini kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiEvinin anahtarını kapının üstüne yazmazsın; ayrı bir yerde, cebinde taşırsın. Secret (gizli anahtar, şifre) dediğimiz şeyler de aynen böyledir: kodun içine yazılmaz, kodun dışında ayrı bir yerde tutulur ve uygulama çalışırken oradan okur.Kapıya "anahtar paspasın altında" yazmak yerine, anahtarı yanında taşırsın; kodda da şifreyi yazmak yerine dışarıdan verirsin.
LVL 2 PratikProjenin kökünde .env adında bir dosya açar, gizli değerlerini ANAHTAR=deger satırları olarak buraya yazarsın; kodda bunlara process.env.ANAHTAR ile ulaşırsın. En kritik adım: bu dosyayı asla commit'leme, .gitignore'a ekle ki GitHub'a hiç gitmesin..env dosyası:
OPENAI_API_KEY=sk-abc123
.gitignore dosyasına ekle:
.env
Kodda kullan:
const key = process.env.OPENAI_API_KEY;
LVL 3 Mekanizma.env sihirli değil; sadece bir metin dosyası. Uygulama açılırken bir kütüphane (ör. dotenv) bu dosyayı okuyup her satırı işletim sisteminin environment'ına (ortam değişkenleri) yükler, sonra kod process.env üzerinden bunları görür. Prod'da (canlı sunucu) genelde .env dosyası bulunmaz; aynı değişkenleri platformun panelinden ayarlarsın, kod farkı bile anlamaz.Vercel/Railway panelinde Settings → Environment Variables → OPENAI_API_KEY = sk-prod-xyz eklersin; kod hâlâ aynı process.env.OPENAI_API_KEY satırını okur.
LVL 4 UstaKlasik tuzak: anahtarı bir kez bile commit'lersin, sonra silersin ama git history'de (geçmiş kayıtlarda) hâlâ durur; bu yüzden sızan bir key'i silmek yetmez, hemen rotate etmen (eskisini iptal edip yenisini üretmen) gerekir. İncelik: ekip arkadaşlarının hangi değişkenleri doldurması gerektiğini göstermek için boş değerli bir .env.example dosyası commit'lersin; ayrıca tarayıcıya giden frontend kodunda gerçek secret tutamazsın, çünkü herkes tarayıcıda görebilir.Repoyu paylaşırken commit'lediğin dosya:
.env.example
OPENAI_API_KEY=
(gerçek değer boş; sızan key olursa sağlayıcının panelinden "Revoke" deyip yenisini üretirsin)
Vaka · gerçek bir senaryo

Ahmet prod veritabanı şifresini "şimdilik" koda yazdı, sonra unuttu ve repo'yu paylaştı. Şifre artık geçmişte kalıcıydı. Daha kötüsü, test ile prod aynı gömülü şifreyi kullandığı için bir testte yanlışlıkla prod veritabanına bağlandı.

Secret'ları .env'e taşıyıp her ortama ayrı değer verince hem güvenlik açığı hem de kaza riski ortadan kalktı.

Vibe coding’de Bunu bilen vibe coder, API anahtarını koda gömüp GitHub'a sızdırmak yerine .env + .gitignore ile ayırır; böylece repo'sunu rahatça paylaşabilir ve faturasını patlatacak ya da hesabını çaldıracak bir sızıntıdan kurtulur.

16

Authentication vs Authorization

Kim olduğun başka, neye iznin olduğu başka

Xiaohei iki ayrı kapıdan geçiyor: ilk kapıda kimliğini gösteriyor, ikinci kapıda anahtarının o odayı açıp açmadığı kontrol ediliyor.
Görselin işi: giriş yapmanın (kim olduğun) tek başına yetmediğini, ikinci bir izin kapısının (neye hakkın olduğu) her seferinde ayrıca kontrol edildiğini kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir otele girdiğinde resepsiyon önce kim olduğunu kontrol eder (kimliğin, rezervasyonun) ve sana bir kart verir. Ama o kart her kapıyı açmaz: neye iznin olduğu ayrı bir şeydir; kart sadece kendi odanı ve havuzu açar, başkasının odasını ya da personel deposunu açmaz. İçeri girmiş olmak, her yere girebilirsin demek değildir.Otel kartın lobiye ve 304 numaralı odana girer; ama 305'e ya da personel deposuna girmez.
LVL 2 PratikVibe coder olarak authentication (authn = kimsin) tarafında login ekranı, parola, Google ile giriş veya bir token görürsün. authorization (authz = neye iznin var) tarafında ise rol/izin kontrolü yaparsın: bu kullanıcı admin mi, bu butonu görmeli mi, bu sayfaya girebilir mi. İkisi ayrı kapıdır: önce kim olduğunu doğrularsın (authn), sonra ne yapabileceğine bakarsın (authz).Giriş yapan herkes /dashboard görür; ama silme butonu sadece if (user.role === 'admin') ise gösterilir.
LVL 3 MekanizmaAuthn aşamasında sunucu parolayı doğrular ve sana imzalı bir kimlik verir; çoğu zaman bir session çerezi ya da JWT token (içinde kim olduğun yazılı, imzayla mühürlü olduğu için içeriği değiştirilemeyen bir kart). Her sonraki istekte bu token gönderilir. Authz ise her endpoint'te ayrıca çalışır: "bu token kime ait?" sorusunu "bu kişinin bu işlemi yapmaya yetkisi var mı?" diye genişletip kullanıcının rol/izinlerine bakar.Authorization: Bearer <token> başlığı kimliği taşır; sunucu DELETE /api/users/5 için ayrıca rolün admin mi diye bakar.
LVL 4 UstaKlasik tuzak: authn'i yapıp authz'i unutmak; yani giriş yapan herkesi her şeye yetkili sanmak. En sinsi hali IDOR'dur: kullanıcı URL'deki id'yi 5'ten 6'ya değiştirip başkasının verisini çeker; çünkü kod "giriş var mı?" diye baktı ama "bu kayıt bu kişiye mi ait?" diye bakmadı. Kuralın: yetki kontrolünü her zaman sunucuda ve kaynak sahipliği düzeyinde yap; frontend'de butonu gizlemek güvenlik değildir.GET /api/invoices/6 için sunucu şunu zorunlu kılar: WHERE id = 6 AND owner_id = currentUser.id — eşleşme yoksa 403 döner.
Vaka · gerçek bir senaryo

Defne'nin uygulaması girişi (authentication) kontrol ediyordu ama yetkiyi (authorization) değil. Bir kullanıcı URL'deki /orders/123/orders/124 yapınca başkasının siparişini görebiliyordu.

Çünkü sistem "giriş yapmış mı" diye bakıyor, "bu kayıt senin mi" diye sormuyordu. Her veri erişimine sahiplik kontrolü eklenince açık kapandı. Giriş yapmak, her şeye yetkili olmak değildir.

Vibe coding’de İkisini ayırmayı bilen vibe coder, "giriş yaptı demek her şeyi yapabilir" hatasına düşmez ve her endpoint'e kaynak-sahipliği kontrolü koyarak en yaygın güvenlik açıklarından biri olan IDOR'u daha kod yazarken kapatır.

17

Hashing vs Encryption

Hash tek yön gider, encryption geri döner

Xiaohei bir parolayı tek yönlü bir kıyma makinesinden geçiriyor; çıkan kıyma geri çevrilemiyor, yanında ise ikinci bir Xiaohei anahtarla açılan kilitli bir kutuyu açıyor.
Görselin işi: hash'in tek yönlü ve geri döndürülemez, encryption'ın ise anahtarla geri açılabilir çift yönlü olduğunu tek bakışta kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiHash bir eti kıymaya çevirmek gibidir: kıymaya bakıp orijinal eti geri yapamazsın, ama aynı eti aynı makineden tekrar geçirirsen yine aynı kıyma çıkar; yani tek yönlü bir parmak izidir, geri açmak için değil karşılaştırmak için kullanılır. Encryption ise bir mektubu kilitli kutuya koymaktır: doğru anahtarla kutuyu açıp mektubu olduğu gibi geri okursun, yani çift yönlüdür.Parola: kıymaya çevir, sakla, bir daha asla geri açma. Kredi kartı no: kilitli kutuya koy, gerektiğinde anahtarla aç.
LVL 2 PratikPratikte kullanıcı parolasını veritabanına yazarken onu düz metin değil, bcrypt gibi bir kütüphaneyle hash'leyip saklarsın; girişte yeni parolayı tekrar hash'leyip saklanan değerle karşılaştırırsın, asla geri çözmezsin. Saklanan veriyi sonradan olduğu gibi geri okuman gerekiyorsa (örneğin bir API token'ı), o zaman hash değil encryption kullanırsın çünkü onu anahtarla geri açman gerekecek.const hash = await bcrypt.hash(password, 12) ile sakla, girişte await bcrypt.compare(input, hash) ile doğrula.
LVL 3 MekanizmaHash fonksiyonu girdiyi sabit uzunlukta, geri çevrilemeyen bir özete dönüştürür ve aynı girdi (aynı salt ile) hep aynı çıktıyı verir. Saldırgan hazır rainbow table'larla tahmin edemesin ve aynı parolayı kullanan iki kişi farklı özet alsın diye her parolaya rastgele bir salt eklenir; ayrıca bcrypt kaba kuvvet denemesini pahalı kılmak için kasıtlı olarak yavaş çalışır. Encryption ise veriyi bir anahtarla şifreler; aynı anahtar (veya eşi) geri çözer, yani sır verinin kendinde değil anahtarda saklanır.bcrypt çıktısı salt'ı içinde taşır: $2b$12$Q9.../KbU... — buradaki 12 maliyet (yavaşlık) faktörüdür.
LVL 4 UstaKlasik tuzak: parolayı encryption ile saklamak — bu yanlıştır, çünkü anahtar sızarsa tüm parolalar açığa çıkar; parola asla geri okunmamalı, sadece hash'lenip karşılaştırılmalıdır. Genel veride MD5 ve SHA-1 kırık sayılır; parola için her zaman bcrypt, scrypt veya argon2, dosya bütünlüğü için ise SHA-256 seç (bütünlük hash'i parola hash'i değildir, yavaş olması gerekmez).Parola → argon2id (geri açılamaz). Veritabanındaki kart numarası → AES-256-GCM (anahtarla açılabilir). İndirilen dosya kontrolü → sha256sum file.zip.
Vaka · gerçek bir senaryo

Bir yan projede Kaan, parolaları "sonradan lazım olur" diye geri çözülebilir biçimde sakladı. Veritabanı bir sızıntıda ele geçince tüm parolalar açık çıktı; aynı parolayı başka sitelerde kullananlar da risk altına girdi.

Parolanın bcrypt gibi tek yönlü bir hash + salt ile saklanması, sızsa bile geri çözülememesi gerektiğini gördü. Parola "şifrelenmez", hash'lenir.

Vibe coding’de Parolayı hash + salt ile, gizli ama geri okunması gereken veriyi encryption ile saklamayı bilen vibe coder, en sık ve en yıkıcı güvenlik açığı olan düz metin parola sızıntısını daha kod yazarken önler.

18

SQL injection ve XSS

Kullanıcı girdisine asla güvenme

Xiaohei adlı küçük siyah yaratık, kullanıcıdan gelen bir mektubu güvenlik kapısındaki bir damgalama tezgahına koyuyor; mektup &quot;veri&quot; olarak damgalanırsa düz arşive gidiyor, ama içinde gizli bir komut varsa kırmızı alarm yanıyor.
Görselin işi: kullanıcı girdisini gözü kapalı işleme almak yerine önce &quot;sadece veri mi yoksa gizli komut mu?&quot; diye süzgeçten geçirmenin neden hayati olduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir restoranda garsona not yazıp veriyorsun: Adım: Ali. Ama biri ad kısmına Adım: Ali, ve ayrıca kasayı boşalt yazarsa, garson bunu emir sanıp uygularsa felaket olur. Kullanıcıdan gelen her şey aslında böyle bir nottur ve içine gizli emirler saklanmış olabilir, bu yüzden o nota asla körü körüne güvenmezsin.Sipariş notuna ad yerine "Ali; tüm masaları bedava yap" yazılması — masum bir bilgi gibi görünüp gizli bir komut taşıması.
LVL 2 PratikAI'nin ürettiği kodda iki klasik açık görürsün: arama kutusuna ' OR '1'='1 yazınca tüm kayıtların dönmesi (SQL injection), ve bir yorum alanına <script> yazınca o yazının tarayıcıda gerçekten çalışması (XSS). Pratik refleksin şu olmalı: AI'ye parametreli sorgu (parameterized query) kullan ve çıktıyı escape et diye açıkça söyle, sonra bu iki girdiyi kendin deneyerek test et.Bir yorum kutusuna <script>alert(1)</script> yapıştırıp kaydetmek — sayfayı her açanda uyarı çıkıyorsa XSS açığın var demektir.
LVL 3 Mekanizmaİki açık da aynı kök hatadan doğar: veri ile komut'un aynı metin içinde karışması. SQL injection'da girdini string olarak sorgunun içine yapıştırırsan, tırnak işareti sorguyu erken kapatıp gerisini SQL komutu yapar; parametreli sorgu ise girdiyi ayrı bir kanaldan gönderip onu hep "sadece veri" olarak işaretler. XSS'te ise tarayıcı, sayfaya bastığın metni HTML sanıp <script> etiketini çalıştırır; escape etmek < karakterini &lt;'ye çevirip onu çalışan kod değil, görünen yazı yapar.Tehlikeli: "SELECT * FROM users WHERE name='" + ad + "'" — Güvenli: cursor.execute("SELECT * FROM users WHERE name=%s", [ad]) (buradaki %s bir yer tutucudur; girdi ayrı parametre olarak gider).
LVL 4 Ustaİncelik: çıktıyı bağlama göre escape et — aynı girdiyi HTML gövdesine, bir attribute içine, URL'ye veya JavaScript'e basmak farklı escape kuralları ister, tek bir genel temizlik her yeri kurtarmaz. Klasik tuzak, girdiyi kaydederken bir kez temizleyip iş bitti sanmaktır; doğrusu girişte doğrula (validate), çıkışta encode et ilkesidir. Ham HTML basman gereken nadir durumda (örneğin zengin metin editörü) körü körüne güvenme, DOMPurify gibi sınanmış bir sanitize kütüphanesi kullan; React'ta da dangerouslySetInnerHTML kullanırken aynı kuralı uygula.React'ta tehlikeli: <div dangerouslySetInnerHTML={{__html: yorum}} /> — Güvenli: <div>{yorum}</div> (React metni otomatik escape eder) veya gerçekten HTML gerekiyorsa DOMPurify.sanitize(yorum) ile temizleyip basmak.
Vaka · gerçek bir senaryo

AI'ın ürettiği kodu hızlıca alan Ece, kullanıcı adını doğrudan SQL sorgusuna ekleyen bir satırı fark etmeden yayınladı. Biri arama kutusuna '; DROP TABLE users; -- yazınca tablo gitti.

Sorun, girdinin komut sanılmasıydı. Parametreli sorguya (prepared statement) geçince girdi artık veri olarak işleniyor, komut olarak değil. "Kullanıcı girdisine asla güvenme."

Vibe coding’de Bu refleksi kazanan vibe coder, AI'nin ürettiği koddaki en yaygın iki kritik açığı (parametreli sorgu eksikliği ve escape edilmemiş çıktı) deploy etmeden önce yakalayıp düzelttirebilir, böylece veritabanı sızıntısı veya kullanıcı oturumu çalınması yaşamaz.

19

API key, rate limit, CORS

Kimlik kartın, geçiş kotan ve kapı görevlisi

Xiaohei bir turnike kapısında damgalı kimlik kartını gösteriyor; yanında dolmuş bir geçiş sayacı ve başka bir yönden sızmaya çalışan ikinci figürü durduran bir görevli kolu var.
Görselin işi: API key'in kimliğin, rate limit'in geçiş kotan, CORS'un ise hangi origin'den geldiğini kontrol eden kapı görevlisi olduğunu tek karede kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir kütüphane düşün: içeri girmek için kişisel kartın (kim olduğun) var, günde kaç kitap alabileceğin sınırlı, ve kapıdaki görevli yabancı bir binadan gelen birinin senin adına içeri dalmasına izin vermiyor. Bu üçü sırasıyla API key, rate limit ve CORS'tur. Üçü farklı sorulara cevap verir: kimsin (API key), ne kadar sık (rate limit) ve hangi siteden geliyorsun (CORS).Kütüphane kartı = API key, günlük 5 kitap limiti = rate limit, kapıdaki görevlinin "hangi siteden geldin?" diye sorması = CORS.
LVL 2 PratikBir AI servisine (örneğin OpenAI) bağlanırken sana verilen uzun gizli dizgiyi (API key) .env dosyana koyarsın; çok hızlı istek atınca 429 Too Many Requests hatası alırsın; tarayıcıdaki uygulamandan başka bir kaynağa (origin) istek atınca da konsolu kırmızıya boyayan o meşhur CORS hatası seni karşılar. Anahtarı asla koda yapıştırma, her zaman .env üzerinden oku ve .env'i Git'e gönderme.OPENAI_API_KEY=sk-... satırını .env'e yaz; .env'i Git'e göndermemek için .gitignore'a .env satırını ekle.
LVL 3 MekanizmaHer istekte API key genelde bir Authorization header'ı olarak gider; sunucu onu görüp kimsin ve kaç hakkın kaldı diye sayar, kota dolunca HTTP 429 döner. CORS ise tamamen tarayıcıda yaşar: tarayıcı, sayfanınkinden farklı bir origin'e (protokol + alan + port) istek atınca, sunucunun cevabındaki Access-Control-Allow-Origin header'ına bakar; bu header sayfanın origin'ini içermiyorsa cevabı senin kodundan saklar (istek çoğu zaman sunucuya ulaşmıştır, ama tarayıcı sonucu JavaScript'e vermez).Authorization: Bearer sk-abc123 header'ıyla giden istek; sunucu cevabında Access-Control-Allow-Origin: https://benimsite.com dönerse tarayıcı, benimsite.com'daki koda cevabı verir.
LVL 4 UstaKlasik tuzak: gizli API key'i tarayıcıdaki (frontend) koda gömmek; herkes "Network" sekmesinden görür ve faturanı şişirir. Gizli anahtar her zaman backend'de durur; frontend yalnızca kendi backend'ine konuşur, asıl çağrıyı backend yapar. CORS hatasını frontend'de mode: 'no-cors' ile susturmaya çalışma (bu cevabı okunamaz hale getirir, sorunu çözmez); doğru çözüm sunucu tarafında izinli origin'i Access-Control-Allow-Origin ile döndürmektir. Rate limit'e takılınca da körlemesine tekrar deneme; Retry-After header'ını oku ve exponential backoff (her denemede beklemeyi katlayarak artırma) uygula.Express'te tek satır: app.use(cors({ origin: 'https://benimsite.com' })) — sadece kendi origin'ine izin verir; origin: '*' ile herkese açmazsın.
Vaka · gerçek bir senaryo

Berk, frontend'den API'ye istek atınca tarayıcıda korkutucu bir "CORS policy" hatası aldı ve saatlerce frontend kodunu değiştirdi. Oysa hata frontend'de değildi: API sunucusu bu origin'e izin veren Access-Control-Allow-Origin header'ını döndürmüyordu.

Çözüm sunucu tarafındaydı. CORS'un tarayıcının bir güvenlik kuralı olduğunu, frontend ne kadar değişse de çözülmeyeceğini öğrendi.

Vibe coding’de Bu üçlüyü bilen vibe coder gizli anahtarı backend'de saklayıp faturasını korur, 429 alınca körlemesine değil Retry-After'a göre akıllı backoff yazar ve o korkutucu CORS hatasında saatlerce frontend'de boğulmak yerine doğrudan sunucu header'ını düzelterek dakikalar içinde çözer.

05

Dağıtım & Operasyon

Koddan canlı ürüne giden yol ve sonrasında onu ayakta tutmak.

20

dev / staging / prod

Önce mutfakta dene, sonra müşteriye servis et

Xiaohei adlı küçük siyah karakter, basamaklı üç bardak arasında küçük bir kepçeyle sıvı taşıyor: deneme, prova ve gerçek müşteri bardakları.
Görselin işi: bir değişikliğin gerçek kullanıcıya ulaşmadan önce neden iki ayrı deneme ortamından geçmesi gerektiğini kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir restoran düşün: mutfak deneme yaptığın yer, prova masası servisin aynısını sahte misafirle test ettiğin yer, salon ise gerçek müşterilerin oturduğu yer. Yemeği doğrudan müşterinin tabağında pişirmezsin; önce mutfakta, sonra provada denersin. Yazılımda da aynı işi yapan üç ayrı kopya vardır ve gerçek kullanıcıların olduğu yerde asla deneme yapılmaz.Mutfak = development (kendi bilgisayarın), prova masası = staging (canlının birebir kopyası), salon = production (gerçek kullanıcılar).
LVL 2 PratikPratikte aynı uygulamanın üç adresi olur: kendi makinende açtığın localhost (dev), ekibin denediği staging.uygulamam.com ve müşterinin girdiği uygulamam.com (prod). Bir özelliği önce dev'de yazar, staging'e gönderip orada deneme yapar, her şey çalışınca prod'a çıkarsın. Her ortamın kendi env dosyası (kendi veritabanı, kendi API anahtarları) vardır; yani staging'de bir kaydı silmen gerçek müşteriyi etkilemez.npm run dev ile localhost'ta açarsın; sonra staging'e deploy edip dener, en son prod'a çıkarsın.
LVL 3 MekanizmaKaputun altında üç ortam neredeyse aynı kod tabanıdır ama farklı konfigürasyon ile çalışır: hangi veritabanına bağlanacağı, ödeme sisteminin test modu mu gerçek mod mu olduğu gibi her şey ortamın değişkenleriyle (genelde .env dosyasından okunur) belirlenir. Kod hangi ortamda olduğunu NODE_ENV gibi bir değişkenden okur ve ona göre doğru veritabanını, doğru anahtarları seçer; böylece aynı kod staging'de sahte parayla, prod'da gerçek parayla çalışır.DATABASE_URL staging'de test veritabanını, prod'da gerçek veritabanını gösterir — kod aynı, sadece env değeri farklı.
LVL 4 UstaKlasik kâbus: yetkili biri yanlış terminalde, prod veritabanına bağlıyken DROP ya da silme komutu çalıştırır ve gerçek müşteri verisini yok eder. Ustalar bunu önlemek için staging'i prod'a olabildiğince benzetir (yakın veri hacmi, aynı servisler), prod erişimini sıkı tutar ve düzenli backup (yedek) alır. Altın kural: prod'da elle deneme yapma — her değişiklik önce staging'de prova edilir, prod sadece denenmiş ve onaylanmış şeyi alır.Prod'a komut yazmadan önce komut satırına bak: prod-db $ görüyorsan dur, önce backup'ı doğrula, sonra çalıştır.
Vaka · gerçek bir senaryo

Tek bir veritabanı kullanan Sinem, yeni bir özelliği denerken "temizleyeyim" diye tabloları sildi — ama o veritabanı prod'du, gerçek kullanıcı verisiydi. Geri dönüş yedeklerden saatler aldı.

Ayrı bir development ve staging ortamı olsaydı denemeler gerçek veriye hiç dokunmazdı. "Prod'da deneme yapılmaz; her ortam ayrıdır."

Vibe coding’de Üç ortamı ayırmayı bilen vibe coder, yeni bir özelliği önce staging'de prova ettiği için gerçek kullanıcıları bozmadan güvenle deneme yapar ve "prod'u yanlışlıkla sildim" felaketinden kaçınır.

21

Build, runtime ve bağımlılıklar

Tarif yazılır, yemek pişer, mutfak gerçek malzeme ister

Xiaohei adlı küçük siyah yaratık bir tarif kâğıdını fırına sokuyor, fırından sıcak bir tabak çıkıyor; yanda eksik malzeme uyarısı var.
Görselin işi: kaynak kodun (tarif) build ile çalışan ürüne (tabak) dönüştüğünü ve eksik bağımlılığın tüm zinciri durdurduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiYazdığın kod bir yemek tarifi; kullanıcının gördüğü uygulama ise tabaktaki pişmiş yemek. Tarifi yemeğe çeviren pişirme adımına build (derleme/paketleme), yemeğin sıcak sıcak servis edildiği masaya da runtime (çalışma anı) denir. Tarifin kendi başına yetmez; un, yumurta, tencere gibi bağımlılıkları (dependencies) hazır olmalı.Tarif (kaynak kod) -> mutfakta pişirme (build) -> masadaki tabak (runtime); malzemeler eksikse hiçbir adım yürümez.
LVL 2 PratikBir projeyi indirince ilk işin npm install; bu komut package.json dosyasındaki malzeme listesine bakıp gerekli paketleri internetten indirir. Sonra npm run build ile pişirir, npm start ile çalıştırırsın. "Bende çalışıyordu" dediğin halde arkadaşında patlıyorsa, suçlu çoğu zaman herkesin biraz farklı sürüm indirmesidir.npm install && npm run build && npm start — kur, derle, çalıştır.
LVL 3 Mekanizmapackage.json senin istediğin sürümü yazar ama gevşek yazar: ^4.2.0 demek "4.x.x ailesinden en yeni" demektir (semver kuralı; ^ minör güncellemeye, ~ sadece yamaya izin verir). Buna karşılık package-lock.json (lock dosyası) o an indirilen tam sürümü ve bütünlük (integrity) hash'ini çiviler. İşte bu yüzden lock dosyasını paylaşmazsan iki makine farklı paket indirir ve "bende çalışıyor" doğar. Ayrıca hatanın build-time (derlerken patlayan) mı yoksa run-time (çalışırken patlayan) mı olduğunu ayırt etmek, nereye bakacağını söyler.package.json: "react": "^18.2.0" (istek) ↔ package-lock.json: "react": "18.2.0" (çivili gerçek sürüm).
LVL 4 UstaTemiz, tekrarlanabilir kurulum için npm install yerine npm ci kullan: bu komut package.json'a değil doğrudan lock dosyasına uyar, node_modules'ü sıfırdan kurar, yani CI/CD ve prod'da herkes bit bit aynı paketleri alır. Lock dosyasını mutlaka commit'le; aksi halde bir bağımlılığın arka planda çıkan yeni yaması üründe sürpriz hata yapar. Bir hatayı çözmeden önce kendine sor: bu build-time hatası mı (eksik paket, tip hatası — derleme loglarında) yoksa run-time hatası mı (eksik env değişkeni, null veri — uygulama açıkken konsolda)? Yanlış yerde aramak saatlerini yer.Prod kurulumu: npm ci (lock'a sadık, deterministik) — geliştirmede yeni paket eklerken ise npm install paket-adi.
Vaka · gerçek bir senaryo

Pınar'ın makinesinde uygulama sorunsuz build oluyordu ama Vercel'de patlıyordu. Sebep: package-lock.json'u commit'lememişti; sunucu da bir bağımlılığın daha yeni, uyumsuz sürümünü çekiyordu.

Lock dosyasını ekleyip herkesin tam aynı sürümleri kurmasını sağlayınca "bende çalışıyor" sorunu bitti. semver'deki ^ işaretinin sürümleri nasıl kaydırdığını da böyle gördü.

Vibe coding’de Bir vibe coder build-time/run-time ayrımını ve lock dosyasının rolünü bilince, "bende çalışıyor ama deploy'da patlıyor" krizini dakikalar içinde doğru yerde (derleme logu mu, prod konsolu mu) teşhis eder ve AI'a net bağlam verir.

22

CI/CD

Her değişikliği otomatik test et, geçerse otomatik yayınla

Xiaohei adlı küçük siyah yaratık, yatay bir su borusunun üstünde nöbet tutuyor; borudan geçen kod paketini bir kontrol kapısından geçiriyor, geçenleri turuncu yolla canlı kutusuna gönderiyor, kırık olanı kırmızı bir engelle durduruyor.
Görselin işi: CI/CD'nin her değişikliği bir kapıdan geçirip yalnızca testi geçenleri otomatik yayına bıraktığını, kırık olanı yolun başında durdurduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiBir yemek üretim hattı düşün: her tabak servise çıkmadan önce kalite kontrolünden geçiyor; bir tabak bozuksa bant otomatik duruyor ve sorunlu tabak müşteriye asla ulaşmıyor. CI/CD tam olarak budur: kodun her değişikliğinde görünmez bir kontrolör önce test eder, temizse mutfaktan masaya kendi taşır, böylece sen 'kontrol etmeyi unuttum' demezsin.Sen kodu değiştirip kaydedersin; arkada görünmez bir kontrolör test eder, geçerse uygulamanı canlıya alır, testte takılırsa seni durdurup uyarır.
LVL 2 PratikPratikte sen sadece kodu git push ile gönderirsin; gerisi otomatik çalışır. GitHub Actions push'u görür, testleri ve build'i (derlemeyi) çalıştırır; yeşil tik görürsen her şey yolunda, kırmızı çarpı görürsen bir şey kırıldı ve deploy (yayınlama) durdurulur. Buradaki CI (Continuous Integration / sürekli entegrasyon) test+build kısmı, CD (yayınlama) ise sonraki adımdır.git push origin main sonrası GitHub'da Actions sekmesinde sıralı adımlar belirir: install → test → build → deploy ve yanlarında yeşil/kırmızı işaretler.
LVL 3 MekanizmaKaputun altında bir pipeline (boru hattı) vardır: depodaki bir YAML dosyası, hangi olayda hangi adımların hangi sırayla koşacağını tanımlar. Push olunca GitHub temiz bir sanal makine ayağa kaldırır, kodunu çeker, adımları sırayla işletir; bir adım sıfırdan farklı bir çıkış kodu (exit code) dönerse hat o noktada kırılır (fail-fast) ve sonraki adımlar hiç çalışmaz..github/workflows/ci.yml içinde on: push ve sırasıyla run: npm ci, run: npm test, run: npm run build adımları tanımlıdır; npm test sıfırdan farklı bir çıkış kodu dönerse build adımına hiç geçilmez.
LVL 4 UstaKlasik tuzak, CD'yi her merge'de doğrudan prod'a otomatik bağlamak (buna Continuous Deployment denir): testler yetersizken bu, kötü bir sürümü saniyeler içinde tüm kullanıcılara yayar. Ustaca yaklaşım, önce bir staging ortamına otomatik deploy edip prod'u manuel onaya bağlamaktır (buna Continuous Delivery denir); kritik işlerde ek olarak environment protection kuralları ve hızlı bir rollback (geri alma) yolu hazır tutulur.Pipeline'da deploy-staging adımı her main push'unda otomatik koşar; deploy-prod adımı ise GitHub'da environment: production ile bir onay kapısına bağlanır, yani biri 'Approve' demeden canlıya çıkmaz.
Vaka · gerçek bir senaryo

Test çalıştırmayı "sonra" diye erteleyen Eren'in ekibi, bir akşam testten geçmeyen kodu prod'a gönderdi ve ödeme akışı koptu. Sonradan kurdukları CI, her push'ta testleri otomatik çalıştırıp kırmızı olunca deploy'u durdurdu.

Aynı hata bir daha canlıya ulaşamadı. "Unuttum" hatasını döngüden çıkarmanın yolu, insana değil otomasyona güvenmekti.

Vibe coding’de CI/CD'yi bilen bir vibe coder, AI'ın ürettiği kodu körlemesine canlıya atmak yerine her push'ta otomatik test/build kurarak kırık sürümleri kullanıcıya ulaşmadan yakalar ve elle deploy etme zahmetinden kurtulur.

23

Hosting: sunucu / container / serverless

Kodun nerede çalışır: ev, kutu, kiralık masa

Xiaohei karakterinin aynı uygulama kutusunu üç farklı barınma yerine taşıdığı çizim: kalıcı ev, taşınabilir koli ve sadece kullandığın kadar ödenen masa.
Görselin işi: aynı kodun üç farklı yerde (kendi yönettiğin ev, taşınabilir kutu, sadece kullandığın kadar ödediğin masa) çalışabileceğini tek bakışta kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiKodunu bir yere koyman lazım ki internetten herkes ulaşabilsin; üç ev tipi var. Sunucu (VPS) kendi dairen gibidir: tüm anahtarlar sende, her şeyi sen yönetirsin. Container taşınma kolisi gibidir: eşyayı içine kutularsın, hangi eve koysan aynı şekilde açılır. Serverless ise sadece kullandığın kadar ödediğin ortak bir masadır: işin bittiğinde masa boşalır, sen yokken para akmaz, temizlik de senin işin değil.Aynı uygulama 3 yerde: VPS (sürekli açık daire), Docker container (taşınabilir koli), AWS Lambda (kullandıkça ödenen masa).
LVL 2 PratikPratikte vibe coder olarak çoğu zaman yönetilen bir platformla başlarsın çünkü tek komutla deploy edersin ve trafik düşükken cüzi ödersin. Statik site ve API fonksiyonları için Vercel ya da Netlify serverless tarafında durur (kullanılmadığında ücret yok); Railway ve Render ise uygulamanı sürekli ayakta bir container olarak çalıştırır. İşin büyüyüp "tam kontrol bende olsun" dediğinde bir VPS kiralarsın; "her ortamda birebir aynı çalışsın" derdin olduğunda da uygulamanı bir container içine koyarsın.Vercel'de tek komutla deploy: vercel deploy --prod — komut bitince site yayında.
LVL 3 MekanizmaKaputun altında VPS 7/24 açık duran kiralık bir bilgisayardır; sen boşta dururken bile kira ödersin ama her şeyi kurabilirsin. Container, uygulamanı bağımlılıklarıyla (kütüphaneler, sistem ayarları) tek bir paket olarak "kutular" — bu yüzden senin makinende çalışan, sunucuda da birebir aynı çalışır. Serverless'ta ise platform fonksiyonunu sadece istek geldiğinde uyandırır; uyandırma anına cold start (soğuk başlangıç) denir ve uykudan kalkan ilk istek birkaç yüz milisaniyeden birkaç saniyeye kadar gecikebilir.Bir Dockerfile aynı imajı her yerde aynı üretir; docker run myapp laptopta da, prod sunucuda da aynı davranır.
LVL 4 UstaSeçim trafiğin şekline bağlıdır: ani gelip giden, ara sıra çalışan iş (webhook, zamanlanmış görev) için serverless ucuzdur çünkü sıfıra kadar ölçeklenir; ama sürekli yüksek trafikte fatura VPS'ten pahalı olabilir, ayrıca cold start ve uzun süren işler (büyük dosya işleme, kalıcı WebSocket bağlantıları) serverless'ı zorlar. Klasik tuzak: veritabanını her cold start'ta yeniden bağlamak — bu yüzden serverless'ta connection pooling (bağlantı havuzu) kullanırsın. En sağlam orta yol, uygulamayı container'a koyup onu hem VPS'te hem de yönetilen bir platformda (örneğin Cloud Run) çalıştırabilmektir; böylece tek bir sağlayıcıya vendor lock-in (tedarikçiye kilitlenme) yaşamazsın.Gece tek seferlik rapor → serverless (boşta 0 TL). 7/24 canlı sohbet sunucusu → VPS ya da container, çünkü serverless cold start ve süre limiti bu işe uygun değil.
Vaka · gerçek bir senaryo

Onur, nadiren çağrılan bir işlevi serverless'a koydu ve harika çalıştı — ta ki kullanıcılar "ilk açılış çok yavaş" diyene kadar. Sebep soğuk başlangıçtı (cold start): işlev uzun süre çağrılmayınca uykuya geçip ilk istekte gecikiyordu.

Sürekli trafik alan bir parçayı serverless yerine hep açık bir container'da tutmanın daha mantıklı olduğunu ölçerek gördü.

Vibe coding’de Trafiğin şekline göre doğru barınma tipini seçince hem faturayı düşürür hem de cold start gibi tuzaklara takılmadan uygulamanı yayında tutarsın.

24

Logging & monitoring

Üretimdeki uygulamanın gözü, kulağı ve alarmı

Xiaohei adlı küçük siyah yaratık, bir uygulama kutusundan akan olayları cam kavanoza doldururken, bir göstergeyi izliyor ve eşik aşılınca çalan bir zili duyuyor.
Görselin işi: log'un (olayları kaydetme), metriğin (göstergeyi izleme) ve alarmın (eşik aşılınca zilin çalması) aynı gözlemlenebilirlik zincirinin üç halkası olduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiUygulaman bir uçaktaki kara kutu gibidir: her olanı sessizce kaydeder, böylece bir terslik olduğunda "ne oldu" sorusunu tahminle değil kayda bakarak cevaplarsın. Bir de kokpit göstergeleri vardır; yükseklik ve hız gibi şeyleri canlı gösterir, sınırı aşınca da alarm öter.Gece 03.00'te uygulaman çöküyor; sabah uyandığında "neden" diye bakacağın tek şey, o an tutulan kayıtlardır. Kayıt yoksa, olay da yoktur.
LVL 2 PratikPratikte üç şeye bakarsın: log (ne oldu, satır satır olay günlüğü), metrik (ne kadar / ne hızda, ör. saniyedeki istek sayısı) ve alarm (bir eşik aşılınca sana e-posta/Slack ile haber). Vibe coder olarak Railway, Vercel veya Render gibi platformların "Logs" sekmesini açıp canlı akan satırları okur, hata patlamasını oradan yakalarsın.Railway dashboard → servisin → Logs sekmesi; veya terminalden canlı akış için railway logs
LVL 3 Mekanizmaconsole.log("hata!") sadece düz bir metin yazar; sonra binlerce satır arasında onu aramak zorunda kalırsın. Structured log ise her olayı makinenin filtreleyebileceği bir JSON nesnesi olarak yazar (seviye, zaman, kullanıcı, istek ID), platform da bunları toplayıp indeksler. Metrikler bu sayıların zaman içindeki grafiğidir; alarm ise o grafik önceden tanımladığın bir kurala değdiğinde otomatik tetiklenir.logger.error({ event: "payment_failed", userId: "u_42", requestId: "req_8a3", amount: 99 }) — artık tek bir kullanıcının tüm yolculuğunu requestId ile süzebilirsin.
LVL 4 UstaKlasik tuzak: ya hiç log tutmamak ya da her şeyi loglayıp parasını fatura olarak ödemek; doğrusu seviyeleri ayırmaktır (debug geliştirmede, info/warn/error üretimde) ve token, şifre, kart numarası gibi sırları asla loga yazmamaktır. Ustalık, üç sinyali birbirine bağlamaktır: alarm seni uyandırır, metrik "ne kadar kötü" der, log ise requestId üzerinden "tam olarak nerede" diye kök nedeni gösterir. İyi bir alarm gürültü yapmaz, sadece gerçekten insan müdahalesi gereken anda öter.Alarm kuralı: "5xx hata oranı 5 dakika boyunca %1'i aşarsa Slack'e haber ver" — gürültüsüz, eyleme dönük; her tekil hatada değil, trend bozulduğunda öter.
Vaka · gerçek bir senaryo

Gizem'in uygulaması gece 2'de çöktü; sabah baktığında elinde hiçbir iz yoktu, sadece kaybolmuş console.log'lar. Neyin patladığını bilemedi, kör tahminlerle gün geçti.

Structured log ve basit bir hata alarmı kurunca bir sonraki olayda requestId ile tam o kullanıcının yolculuğunu izleyip kök nedeni 10 dakikada buldu.

Vibe coding’de Üretimde "çöktü ama neden bilmiyorum" çıkmazından kurtulup, structured log ve doğru kurulmuş alarmlarla bir sorunun izini dakikalar içinde sürebildiği için vibe coder'ın ürettiği uygulama "çalışıyor gibi" olmaktan çıkıp gerçekten güvenilir hale gelir.

06

Kod Sağlığı & Düşünme

Çalışan kod ile sağlam kod arasındaki fark.

25

Async / await / Promise

İşi başlat, bekleme; sonuç gelince devam et

Xiaohei bir posta tüpüne kapsül atıp dönüş kapsülünü beklerken senkron ve asenkron yolu karşılaştıran çizim.
Görselin işi: işi başlatıp beklemeden başka iş yapmanın (asenkron) tüpün önünde donup kalmaktan (senkron) neden daha akıllıca olduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiKahve makinesinin önünde kahve hazırlanana kadar öylece beklersen buna senkron denir: o sırada başka hiçbir iş yapamazsın. Oysa düğmeye basıp kahve akarken sen masayı kurabilirsin, kahve bitince haber gelir; işte bu asenkron çalışmaktır. Promise kahve makinesinin sana verdiği "birazdan dolu fincan getireceğim" sözüdür; await ise "şimdi fincanı gerçekten elime al" demektir.Kahve düğmesine bas (işi başlat) → bu sırada masayı kur → "kahve hazır" sesini duy (await) → fincanı al.
LVL 2 PratikBir veri çeken satır yazdığında, sonucu kullanmadan önce başına await koymazsan elinde gerçek veri değil, henüz dolmamış bir Promise (söz) kalır ve ekranda çoğu zaman undefined ya da [object Promise] görürsün. AI'ın ürettiği kodda en sık karşına çıkan hata budur: await unutulmuştur. Pratik refleksin şu olsun: "bu satır bir yere gidip bir şey getiriyor mu?" (veritabanı, API, dosya) — cevap evet ise başına await koy.Yanlış: const user = getUser(id) → user bir Promise. Doğru: const user = await getUser(id)
LVL 3 MekanizmaBir fonksiyon async ile işaretlendiğinde, içinde ne dönerse dönsün otomatik olarak bir Promise ile sarılır; bu söz üç halde olabilir: pending (bekliyor), fulfilled (sonuç geldi) ya da rejected (hata oldu). await gördüğünde JavaScript o fonksiyonu o noktada duraklatır ama programın geri kalanını bloklamaz — beklenen iş bitince fonksiyon kaldığı yerden devam eder. Yani await sadece senin fonksiyonunu bekletir, bütün uygulamayı değil.async function f() { const r = await fetch(url); return r.json(); } → f() çağrısı anında bir Promise döner, gerçek sonuç sonradan dolar.
LVL 4 UstaBirbirine bağlı işleri await ile sırayla beklemek doğrudur, ama birbirinden bağımsız işleri tek tek beklemek zaman kaybıdır — onları aynı anda başlatıp Promise.all([...]) ile topluca bekle. Ayrıca await edilen bir iş hata fırlatabileceği için onu try/catch içine al; yakalanmayan bir rejected Promise modern Node'da uygulamayı sessizce çökertebilir. Klasik tuzak: forEach içinde await kullanıp döngünün beklediğini sanmak — forEach beklemeden akıp geçer; sıralı bekleme istiyorsan for...of kullan.Paralel: const [a, b] = await Promise.all([getA(), getB()]) — iki istek aynı anda gider, ikisi de bitince devam eder.
Vaka · gerçek bir senaryo

Arda, kullanıcıyı çeken satırın başına await koymayı unutunca ekranda hep [object Promise] gördü ve "AI bozuk kod yazdı" sandı. Aslında kod doğruydu; o, henüz dolmamış bir "söz"ü kullanıyordu.

await ekleyince gerçek veri geldi. Sonra bağımsız iki isteği Promise.all ile paralel çalıştırıp sayfayı belirgin biçimde hızlandırdı.

Vibe coding’de await refleksini kazanan vibe coder, ekrandaki gizemli undefined / [object Promise] hatalarını saniyeler içinde teşhis edip düzeltir ve bağımsız işleri Promise.all ile paralelleştirerek uygulamasını gözle görülür biçimde hızlandırır.

26

Error handling

Hatayı yutma; yönet, görünür kıl, nazikçe bildir

Xiaohei, üzerinde kırmızı uyarı ışığı yanan bir borudan akan sıvıyı, ışığı bantlayıp gizlemek yerine eline bir huni alıp şeffaf bir log kavanozuna yönlendiriyor; kullanıcıya giden tarafa küçük nazik bir not bırakıyor.
Görselin işi: Hatayı sessizce yutmak (uyarı ışığını bantlamak) ile onu görünür kılıp log'a yönlendirmek (fail loudly) arasındaki farkı tek bakışta kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiArabanın motor arıza ışığı yanmaya başladığında, üzerine bant yapıştırıp ışığı kapatmazsın; arızayı görünür tutup servise gidersin. Hatayı sessizce yutmak, o ışığı bantlamaktır: sorun durmaz, sadece görünmez olur ve daha kötü bir anda patlar.Lambayı bantlamak (sahte huzur) yerine, ışık yanar yanmaz durup bakmak: "Yağ basıncı düşük" uyarısını okuyup hemen müdahale etmek.
LVL 2 PratikPratikte bir işlemin patlayabileceği yeri try bloğuna alır, patlarsa catch içinde ne olacağını yazarsın: kullanıcıya nazik bir mesaj, log'a (kayıt) ise teknik detay. Vibe coding yaparken AI'a "buraya error handling (hata yönetimi) ekle ama catch'i boş bırakma" demek, sonradan saatlerce kör arama yapmanı önler.try { await odemeYap(); } catch (err) { console.error(err); toast("Ödeme alınamadı, tekrar deneyin."); }
LVL 3 MekanizmaBir fonksiyon işini yapamayınca throw ile bir hata fırlatır; bu hata, onu yakalayan en yakın catch bulunana kadar çağrı zincirinde (call stack) yukarı doğru "kabarcık gibi yükselir" (bubble up). Yakalanmazsa programı çökertir — ki bu aslında iyidir: erken ve görünür patlamak (fail loudly), sahte başarıdan çok daha güvenlidir.function bol(a, b) { if (b === 0) throw new Error("Sıfıra bölme"); return a / b; } — JavaScript'te sessizce Infinity döndürmek yerine zinciri durdurur.
LVL 4 UstaKlasik tuzak boş catch bloğudur (catch (e) {}): hatayı yutar, bug'ı saklar ve sistem yanlış veriyle çalışmaya devam eder. Usta kural: yalnızca gerçekten kurtarabileceğin hatayı yakala; ne yapacağını bilmiyorsan ya tekrar fırlat (re-throw) ya da en üst katmanda merkezî bir yerde logla. Beklenen hataları (kullanıcı boş form gönderdi) zarifçe yönet, beklenmeyenleri (veritabanı çöktü) yüksek sesle patlat.catch (err) { logger.error(err); if (!err.recoverable) throw err; } — kurtarılamayanı yukarı taşır, sadece yutmaz.
Vaka · gerçek bir senaryo

Melis, AI'ın koyduğu boş bir catch {} bloğunu fark etmedi; ödeme bazen sessizce başarısız oluyor ama kullanıcıya "başarılı" deniyordu. Siparişler "ödendi" görünüp para hiç alınmamıştı.

Catch'i doldurup hatayı loglayınca ve kullanıcıya gerçek durumu gösterince sorun anında görünür oldu. Sessizce yutulan hata, en pahalı hatadır.

Vibe coding’de Boş catch'leri ve sessiz hata yutmayı fark edip AI'a görünür error handling (hata yönetimi) yazdırarak, "çalışıyor gibi görünen" ama arka planda bozuk veri üreten uygulamalardan kurtulur ve bug'ı saatler süren kör arama yerine ilk anda log'da görür.

27

Debugging ve stack trace

Tahminle değil, izolasyonla çöz

Xiaohei bir hata yığınını yukarıdan aşağıya tarayıp kendi dosyasının satırına parmak basıyor; en üstte kırmızı hata türü, ortada turuncu bir ok kendi koduna işaret ediyor.
Görselin işi: stack trace'in tepeden okunan bir hata haritası olduğunu ve gözünü kendi dosyandaki satıra çakmak gerektiğini kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiEvdeki sigorta attığında bütün prizleri tek tek söküp atmazsın; sigorta panosundaki düşen şalteri okur, hangi odanın hattının patladığını görür, oraya gidersin. Debugging (hata ayıklama) de böyledir: rastgele kod değiştirip durmak değil, hatayı yerinden izole edip sıkıştırmaktır. Bilgisayar sana zaten panodaki etiketi verir, ona stack trace (hatanın yol haritası) denir.Lamba yanmıyor: tahminle ampulü, kabloyu, prizi aynı anda değiştirmek yerine — önce ampulü başka prize takıp test edersin; arıza ampulde mi prizde mi tek hamlede ayrışır.
LVL 2 PratikBir hata aldığında ekrana dökülen o uzun yazı bloğu çöp değil, stack trace'tir: en üstte hata türü ve mesajı, altında ise hatanın geçtiği dosya-satır zinciri vardır. Senin işin bu zincirde kendi dosyanın adının geçtiği satırı bulup oraya gitmektir. AI'a yardım isterken hatayı kendi cümlelerinle tarif etmeye çalışma; tüm stack trace'i olduğu gibi yapıştır, çözüm hızı katlanır.TypeError: Cannot read properties of undefined (reading 'name') üst satır; alttaki at getUser (src/user.js:42) ise senin gideceğin yer: src/user.js dosyasının 42. satırı.
LVL 3 MekanizmaProgram çalışırken her fonksiyon çağrısı bir tabağa benzer şekilde üst üste dizilir; buna call stack (çağrı yığını) denir. Hata patladığı anda sistem bu yığını en içteki çağrıdan dışarı doğru sırayla yazar, bu yüzden çoğu dilde en üstteki satır hatanın patladığı yer, aşağısı oraya nasıl gelindiğidir. Hatayı güvenle çözmek için önce reproduce (aynı koşulda tekrar ürettirmek) edersin, çünkü tekrarlatamadığın bir hatayı düzelttiğini de doğrulayamazsın.Hata her seferinde 5 karakterlik isimde çıkıyorsa, o girdiyi sabitleyip 42. satırdan hemen önce console.log(user) ekler ve değişkene bakarsın: user gerçekten undefined mı geliyor görürsün.
LVL 4 UstaBir anda birkaç şeyi birden değiştirip "acaba düzeldi mi" demek en klasik tuzaktır; düzelse bile neyin düzelttiğini bilemezsin, o yüzden her turda tek değişken değiştir. Hatanın hangi commit'te girdiğini bulmak için kör denemeler yerine git bisect (ikili arama: aralığı her adımda yarıya bölmek) kullanırsın; 200 commit'i 200 denemeyle değil ~8 denemeyle tararsın. Stack trace'te kendi satırın hiç görünmüyorsa hata genelde senin verdiğin girdide veya bir kütüphane sınırındadır, oraya bak.git bisect startgit bisect bad (şu an bozuk) → git bisect good v1.2 (burada çalışıyordu); git ortadaki commit'e atlar, sen her adımda sadece "good/bad" dersin, suçlu commit kendiliğinden çıkar.
Vaka · gerçek bir senaryo

Cem bir hatada rastgele şeyler deniyordu: bir satırı değiştir, kaydet, bak; düzelmedi, başkasını değiştir. Saatler geçti. Sonunda stack trace'i okudu — en üstte hatanın türü, hemen altında kendi dosyasındaki user.js:42.

Doğrudan oraya gidip undefined gelen değişkeni 5 dakikada buldu. Sonraki sefer AI'a tam stack trace'i yapıştırınca çözüm daha da hızlandı.

Vibe coding’de Stack trace'i okuyup hatayı tek değişkenle izole edebilen vibe coder, AI'a tam yol haritasını verdiği için bir hatayı saatlerce kör deneme yerine birkaç hamlede çözer.

28

Test: unit / integration / e2e

Kodun kendi kendini kontrol eden bekçileri

Xiaohei adlı küçük siyah yaratık, kodun çıktısını bir terazinin üzerinde beklenen değerle karşılaştırırken; dengede olanlar yeşil geçer, tutmayan kırmızı alarm verir.
Görselin işi: testin, kodun gerçek çıktısını beklenen sonuçla kıyaslayan ve tutmadığında hemen alarm veren otomatik bir kontrol olduğunu kavratmak.
SeviyeNe demekÖrnek
LVL 1 SezgiTest, kodun için yazdığın otomatik kontrol listesidir: "Şu girince bu çıkmalı" dersin, makine her seferinde senin yerine bakar. Bir tabağı her pişirdiğinde tuzu, sıcaklığı, görünüşü senin yerine kontrol eden bir kalite şefi gibi düşün; sen elini sürmeden o sürekli denetler."Sepete 2 ürün eklenince toplam fiyat doğru mu?" sorusunu bir kez yazarsın; bu kontrol artık her değişiklikte kendiliğinden çalışır.
LVL 2 PratikPratikte testler üç boyda gelir: unit (tek bir fonksiyonu tek başına dener), integration (birkaç parça birlikte çalışıyor mu bakar, örneğin kod gerçekten veritabanına yazabiliyor mu), e2e (end-to-end, kullanıcı gibi tarayıcıyı açıp baştan sona tıklar). Terminalde npm test dersin, hepsi koşar ve yeşil (geçti) ya da kırmızı (geçemedi) görürsün; AI'a "bu fonksiyon için unit test de yaz" demen yeter.npm test → "✓ 24 passed, ✗ 1 failed" çıktısında kırmızı olan satır sana tam olarak hangi davranışın bozulduğunu söyler.
LVL 3 MekanizmaHer test aslında küçük bir programdır: kodunu çağırır, sonucu expect(...).toBe(...) gibi bir assertion (iddia) ile karşılaştırır, tutmazsa hata fırlatıp testi kırmızıya çevirir. Bunları test piramidi şeklinde dengelersin: tabanda çok sayıda hızlı unit test (milisaniyeler), tepede az sayıda yavaş e2e test (saniyeler); çünkü e2e gerçek tarayıcı ve sunucu ister, kurması ve sürdürmesi pahalıdır. Bu yüzden "çok unit, az e2e" kuralı vardır.test('toplam', () => { expect(topla(2, 3)).toBe(5) }) — fonksiyonu çağırır, 5 beklediğini iddia eder; topla 6 dönerse test kırmızı olur.
LVL 4 UstaTestin asıl değeri regresyonu (eskiden çalışan bir şeyin yeni değişiklikle bozulması) erken yakalamasıdır: bir bug bulduğunda önce onu gösteren kırmızı bir test yaz, sonra düzelt; o test artık o hatanın bir daha geri dönmesini engelleyen bir bekçi olur. Klasik tuzak kırılgan testlerdir — gerçek davranış yerine iç detaya (tam HTML çıktısı, çağrı sırası) bağlanan testler her ufak değişiklikte boşuna kırmızıya döner ve insanı testten soğutur; sen sonucu test et, nasıl yapıldığını değil.Bir kullanıcı "indirim kodu çalışmıyor" dediğinde: önce expect(fiyat).toBe(80) diye kırmızı bir test yazarsın, kodu düzeltirsin, test yeşile döner — hata bir daha sessizce geri gelemez.
Vaka · gerçek bir senaryo

Yasemin bir indirim kodu hatasını elle düzeltti, çalıştı, geçti. İki hafta sonra başka bir değişiklik aynı hatayı geri getirdi ve kimse fark etmeden müşteriler yanlış fiyat ödedi.

Bu kez önce hatayı gösteren kırmızı bir test yazıp sonra düzeltti; o test artık hatanın geri dönüşünü engelleyen bir bekçi oldu. "Test, gelecekteki sana atılmış bir sigortadır."

Vibe coding’de Test yazmayı (ve AI'a yazdırmayı) bilen bir vibe coder, "çalışıyor gibi" görünen kodu prod'a göndermeden önce regresyonu otomatik yakalar; böylece her yeni özellik eski özellikleri sessizce bozmaz.

29

Performans sezgisi (Big-O lite)

Veri büyüyünce kodun ne kadar yavaşlar?

Xiaohei bir kova kullanarak su taşıyor; tek kovayla kolay, ama her satır için ayrı kuyuya gidince koştuğu yol kırmızıya dönüp katlanarak büyüyor.
Görselin işi: aynı işin küçük veride ucuz, büyük veride neden katlanarak pahalı olduğunu (O(n) ile O(n²) farkını) tek bakışta sezdirmek.
SeviyeNe demekÖrnek
LVL 1 SezgiBir misafire çay servis etmek kolaydır; ama her misafirin diğer herkesle ayrı ayrı tokalaşmasını istersen, masaya kişi ekledikçe el sıkışma sayısı çok daha hızlı şişer: kişi sayısı 2 katına çıkınca tokalaşma kabaca 4 katına. İşin ne kadar büyüdüğü, verinin ne kadar büyüdüğüne bağlıdır. 5 kişide şirin, 500 kişide kabus.10 kişi tek tek selamlanırsa 10 selam. Ama herkes herkesle tokalaşırsa 10 kişide 45, 100 kişide 4.950 tokalaşma olur (kişi 10 kat artınca tokalaşma ~100 kat).
LVL 2 PratikUygulaman 10 test kaydında ışık hızında, ama gerçek kullanıcıların 50 bin kaydında dakikalarca takılıyorsa, muhtemelen veri büyüdükçe orantısızca yavaşlayan bir kod yazmışsındır. Çözmeden önce ölç: ağır kısmın hangisi olduğunu tahminle değil, bir ölçüm aracıyla (profiler) bul.Tarayıcıda DevTools açıp console.time('liste') ile console.timeEnd('liste') arasına şüpheli döngüyü alıp gerçek süreyi ölçmek.
LVL 3 MekanizmaListeyi bir kez baştan sona gezmek O(n)'dir (lineer): veri 2 katına çıkarsa süre de kabaca 2 katına çıkar. Ama döngü içinde döngü kurarsan O(n²) olur: veri 2 katına çıkınca süre 4 katına fırlar; bu yüzden 1.000 kayıtta bir milyon işlem dönersin.İki listeyi eşleştirmek için iç içe for yazmak O(n²)'dir; bunun yerine bir tarafı Map'e koyup tek döngüde aramak işi O(n)'e indirir.
LVL 4 UstaEn sinsi tuzak N+1 sorgudur: 100 kullanıcıyı çekersin, sonra döngü içinde her biri için ayrı bir veritabanı sorgusu atarsın; 1 yerine 101 gidiş-dönüş olur ve prod'da çöker. Çözüm, döngü içindeki sorguyu dışarı taşıyıp tek seferde toplu çekmektir. Ama unutma: erken optimize etme; 50 satırlık bir liste için O(n²) bazen tamamen yeterlidir.Döngü içinde her sipariş için SELECT * FROM users WHERE id = ? atmak yerine, tüm id'leri toplayıp tek SELECT ... WHERE id IN (...) ile çekmek (veya ORM'de JOIN / eager loading kullanmak).
Vaka · gerçek bir senaryo

Nil'in sipariş listesi 20 test kaydında ışık hızındaydı, demoyu rahat geçti. Canlıda 40 bin kayıtta sayfa dakikalarca açılmadı. Sebep: her sipariş için döngü içinde ayrı bir veritabanı sorgusu (N+1) — 1 yerine 40.001 sorgu.

Sorguyu döngü dışına alıp tek seferde toplu çekince sayfa milisaniyelere indi. "10 kayıtta hızlı, 1 milyonda ölür."

Vibe coding’de Bir vibe coder, "kodum çalışıyor" ile "kodum 100 binlik veride çalışıyor" arasındaki farkı görür ve özellikle döngü içindeki gizli veritabanı sorgularını (N+1) prod'a göndermeden önce yakalayabilir.

Borcu kapatmak

Bu 29 kavramın hiçbiri seni “AI kullanma” demeye götürmez — tam tersi. Aracı kullanmak ile ne zaman hangi aracı seçeceğini bilmek farklı şeylerdir; ikincisi, vibe coding’in tavanını yükselten şeydir. Stack trace’i okuyabilen, “bu satır bir yere gidip bir şey getiriyor mu” diye sorabilen, secret’ını koda gömmeyen bir vibe coder, aynı modelle kat kat daha iyi iş çıkarır.

Pratik öneri: bir kavramı gerçekten öğrendiğini, onu bir dakikada bir benzetmeyle anlatabildiğinde anlarsın. Aşağıdaki listeyle kendi anlama borcunu ölçebilirsin.

Anlama borcun ne kadar?

1 dakikada bir arkadaşına rahatça anlatabileceklerini işaretle. Geri kalanı, bugün kapatabileceğin borcun.