SQL, ORM’inizden daha uzun ömürlü sözleşmedir

2026-09-10

Yayımladığım her ürün en az bir framework kararından daha uzun yaşadı. Veritabanı ise hepsinden uzun yaşadı. Next.js sürümleri değişir, ORM değiştirilir, ekip yenilenir; satırlar ve şemaya işlenmiş varsayımlar kalır. Bu yüzden SQL’i repository sınıfının arkasındaki uygulama ayrıntısı değil, ürünün gerçek sözleşmesi olarak görürüm.

Prisma günlük işlerimin çoğunu karşılıyor: tipli CRUD, depodaki migrasyonlar ve yeni ekip üyesinin okuyabileceği bir şema. Yine de ürettiği SQL’i okurum. Liste sayfası her satır için ayrı sorgu çalıştırıyorsa, raporda window function gerekiyorsa ya da dashboard üç join üzerinden GROUP BY istiyorsa sorguyu $queryRaw ile elle yazar veya view’a koyarım. Biri Redis eklemeden önce EXPLAIN ANALYZE’ı açarım. “Önbellek lazım” ticket’larının çoğunda eksik olan bir indekstir.

Kısıtlar elde edebileceğiniz en ucuz kalite güvencesidir. NOT NULL, UNIQUE, FOREIGN KEY ve CHECK her yazmada, her serviste, hatta birinin cuma günü elle çalıştırdığı script’te bile geçerlidir. Zod API’yi, veritabanı gerçeği korur. Para, tam sayı alt birimler veya NUMERIC olarak saklanır; asla float olarak değil. Ödeme webhook’ları iki kez gelebilir, bu nedenle idempotency anahtarı handler’daki bir yorumu değil, benzersiz bir indeksi hak eder.

FinTech hataları çoğunlukla eşzamanlılıkta ortaya çıkar. İki istek bakiyeyi okur, ikisi de yeterli olduğuna karar verir ve ikisi de yazar: bu bir lost update’tir ve hiçbir frontend doğrulaması bunu düzeltemez. Tek bir atomik ifade kullanmayı tercih ederim —UPDATE ... SET balance = balance - $1 WHERE id = $2 AND balance >= $1— ardından tam olarak bir satırın değiştiğini kontrol ederim. Alternatif olarak SELECT ... FOR UPDATE içeren bir transaction ve her hareketin sonradan açıklanabilmesini sağlayan append-only ledger tablosu kullanırım. İşlenen hacmi 1,5 milyon doların üzerinde olan sistemlerde “sonradan açıklayabilmek” işin tamamına yakınıdır.

İndeksler önemli görünen sütunları değil, gerçekten çalıştırdığınız sorguları izlemeli. Bileşik indekste eşitlik sütunları önce, aralık ya da sıralama sütunu en sonda gelir; “aktif” satırlar için partial index, OFFSET 50000 yerine keyset pagination kullanılır. Canlı tablolardaki migrasyonlar aşamalı yapılır: önce nullable sütun eklenir, gruplar hâlinde doldurulur, sonra kısıt eklenir; indeks CONCURRENTLY kurulur. Öğle vakti orders tablosunu kilitleyen migrasyon, yanında pull request’i olan bir kesintidir.

Şema yerine “esnek” entity-attribute-value tablosunu, modellenmek istenmeyen temel alanları JSONB’ye saklamayı, unique kısıtlarını düşünmeden her yere soft delete koymayı ve EXPLAIN edilmemiş sorgunun önüne cache eklemeyi reddederim. Yavaş bir sayfa ya da emin olmadığınız bir şema gönderiyorsanız sorguyu ve satır sayılarını da ekleyin. Planı, indeksi ve ilk ekleyeceğim kısıtı yazıp yanıtlarım.

Bir sonraki ürüne kalacak birini verin.

Başlamaya hazır mısınız? Bana yazın — hemen başlayabilir ve kalabilirim. İki haftalık devir değil, uzun vadeli iş birliği. Projenizi, gereksinimleri ve ilerlemenin en iyi yolunu konuşalım.

Deneyim veya hizmetler hakkında sorun