Backend işleri bize çoğunlukla üç biçimde gelir: yeni bir mobil ya da web uygulamasının sunucu tarafı; iki sistemin (örneğin e-ticaret sitesiyle muhasebe yazılımının) birbiriyle konuşması; ya da büyüdükçe yavaşlayan eski bir sunucu uygulaması. Üçüncüsü çoğu zaman bir modernizasyon işidir; bu sayfa ilk ikisini anlatıyor.
Backend geliştirme hangi parçalardan oluşur?
Backend; API katmanı, iş kuralları, veri tabanı, arka plan işleri ve dış sistem bağlantılarından oluşur. Her parçanın kendine özgü bir hata türü vardır:
| Parça | Ne yapar | Sık gördüğümüz hata |
|---|---|---|
| API katmanı | İstekleri alır, kimliği ve yetkiyi doğrular, yanıtı biçimler | Yetki kontrolünün yalnızca arayüzde yapılması |
| İş kuralları | Fiyat, stok, onay, durum geçişleri | Aynı kuralın üç ayrı yerde, üç farklı biçimde yazılması |
| Veri tabanı | Kalıcı veri, ilişkiler, kısıtlar | Bütünlüğün yalnızca uygulama koduna emanet edilmesi |
| Arka plan işleri, kuyruklar | E-posta, rapor, entegrasyon gönderimleri | Başarısız işin sessizce kaybolması |
| Dış sistem bağlantıları | Ödeme, e-fatura, kargo, SMS, ERP | Zaman aşımı ve yeniden deneme planının olmaması |
REST API'yi nasıl tasarlıyoruz?
REST API'yi, kullanan geliştiricinin davranışı dokümana bakmadan tahmin edebileceği biçimde tasarlarız: sözleşme önce yazılır; hata, sürüm, zaman ve para için tek kural vardır.
- Sözleşme önce yazılır. Uç noktalar, alanlar, hata kodları ve örnekler bir OpenAPI belgesinde tanımlanır (güncel sürüm 3.2.0, Eylül 2025). Mobil ve web ekibi sunucu hazır olmadan bu sözleşmeyle çalışmaya başlar; sunucu otomatik testlerle belgeye karşı doğrulanır.
- Kaynak adları ve sürüm.
GET /v1/orders/{id},POST /v1/orders. Geriye uyumsuz değişiklik yeni sürümle gelir, eski sürümün kapanış tarihi önceden duyurulur; güncellenmemiş telefonlardaki uygulamalar eski sürümü aylarca çağırmaya devam eder. - Hatalar tek biçimde döner. RFC 9457'nin tanımladığı
application/problem+jsonbiçimi:{ "type": "https://api.example.com/problems/stok-yetersiz", "title": "Stok yetersiz", "status": 409, "detail": "Ürün 88 için istenen 5, mevcut 2.", "instance": "/v1/orders/req-9f2c" } - Yeniden denemeye dayanıklılık. RFC 9110'a göre GET, PUT ve DELETE idempotenttir; aynı istek iki kez gelse de amaçlanan sonuç değişmez. POST değildir. Bağlantı kopup istemci isteği yinelediğinde siparişin iki kez oluşmaması için POST isteklerinde
Idempotency-Keybaşlığı kabul ederiz. Başlık IETF'te taslak olarak kaldı, RFC olmadı; ama ödeme sağlayıcılarında yaygındır. - Sayfalama ve sınırlar. Büyük listelerde imleç (cursor) tabanlı sayfalama; aşırı istekte
429veRetry-Afterbaşlığı. Sınırı olmayan bir liste uç noktası, OWASP'ın "sınırsız kaynak tüketimi" başlığının (API4:2023) tipik örneğidir. - Zaman ve para. Zamanlar UTC ve RFC 3339 biçiminde döner:
2026-09-27T09:15:42Z. Para, kuruş cinsinden tam sayı ya da metin olarak yazılmış ondalık ("990.00") ve para birimi koduyla gönderilir; JSON'da kayan noktalı sayıya bırakılmaz.
API güvenliğinde en sık hangi hata yapılır?
En sık hata, oturum açmış bir kullanıcının istekteki kimliği değiştirerek başkasına ait kaydı okuyabilmesidir. OWASP API Security Top 10'un 2023 listesinde bu, birinci sıradaki nesne düzeyinde yetki ihlalidir (Broken Object Level Authorization).
Örnek: GET /v1/invoices/1043 isteği, faturanın isteği yapan müşteriye ait olup olmadığına bakılmadan yanıtlanıyorsa 1044'ü deneyen herkes başka bir müşterinin faturasını görür. Kimliği tahmin edilemez yapmak (UUID) sorunu gizler ama çözmez; sahiplik her istekte sunucuda kontrol edilir. Kabul testlerine, bir kullanıcının oturumuyla başkasının kaydını okumaya, değiştirmeye ve silmeye çalışan senaryolar koyarız; hepsi 403 ya da 404 dönmelidir.
Kimlik doğrulamada OAuth 2.0 kullanılıyorsa RFC 9700'ün (Ocak 2025) önerilerini izleriz: kullanıcı parolasını istemcinin topladığı "resource owner password" akışı kullanılmaz; mobil uygulama ve tarayıcı gibi açık istemcilerde yetkilendirme kodu akışı PKCE ile çalışır.
Üçüncü taraf entegrasyonlarında sorun nerede çıkar?
Sorun genellikle her şeyin yolunda gittiği senaryoda değil; karşı tarafın yavaşladığı, aynı bildirimi iki kez gönderdiği ya da habersiz sürüm değiştirdiği anda çıkar. Ödeme kuruluşu, e-fatura özel entegratörü, kargo, SMS sağlayıcısı ya da ERP yazılımı olsun, aynı durumları planlarız:
| Durum | Ne olur | Önlem |
|---|---|---|
| Bildirim (webhook) tekrar gelir | Sipariş iki kez onaylanır, stok iki kez düşer | Olay kimliği kaydedilir, işlenmiş olay ikinci kez işlenmez. Stripe canlı ortamda teslim edemediği olayı üç güne kadar yeniden dener |
| Bildirimler sırasız gelir | "Ödendi" olayı "oluşturuldu"dan önce işlenir | Sıraya güvenilmez; eksik nesne sağlayıcının API'sinden çekilir, durum geçişleri kontrol edilir |
| Sahte bildirim | Ödenmemiş sipariş ödenmiş sayılır | İmza ham istek gövdesi üzerinden doğrulanır; zaman damgası toleransı (Stripe kütüphanelerinde varsayılan 5 dakika) için sunucu saati NTP ile senkron tutulur |
| Karşı taraf yavaş ya da kapalı | Kullanıcının isteği dakikalarca asılı kalır | Kısa zaman aşımı, giderek artan aralıklarla yeniden deneme, kuyruk; bildirimi alan uç önce 2xx döner, işi arka planda yapar |
| İki sistemin kayıtları tutmaz | Ay sonunda toplamlar farklı çıkar | Günlük mutabakat işi: iki taraftaki kayıtlar kimlik ve tutarla eşleştirilir, farklar raporlanır |
| Karakter kodlaması | ERP'den gelen dosyada "ş" yerine anlamsız karakter | Eski sistemlerin Windows-1254 ya da ISO-8859-9 çıktısı içeri alınırken açıkça UTF-8'e çevrilir |
Veri tabanı değişikliği ile dışarı gidecek mesajdan biri başarılı, diğeri başarısız olursa iki sistem ayrışır. Bunu önlemek için giden mesajı aynı işlem (transaction) içinde bir giden kutusu tablosuna yazar, ayrı bir işçiyle göndeririz (transactional outbox). Mesaj en az bir kez gider; tekrarı tanımak alıcı tarafın olay kimliği kuralıyla sağlanır.
Ticari SMS veya e-posta gönderen sistemlerde onayların İleti Yönetim Sistemi (İYS) ile eşlenmesi de bir entegrasyon kalemidir; hangi iletinin ticari sayıldığını hukukçunuz değerlendirir.
Mikroservis mimarisi ne zaman gerekir, ne zaman gerekmez?
Çoğu projede gerekmez. Mikroservis mimarisi uygulamayı ağ üzerinden konuşan, ayrı dağıtılan küçük servislere böler ve dağıtık sistemin maliyetini getirir: servisler arası ağ hataları, birden çok veri tabanında tutarlılık, her servis için ayrı izleme ve dağıtım hattı. Martin Fowler'ın gözlemi de bu yönde: başarılı mikroservis örneklerinin neredeyse hepsi büyüyüp bölünen bir monolitle başlamış; sıfırdan mikroservis olarak kurulduğunu duyduğu sistemlerin ise neredeyse hepsi ciddi sorun yaşamış.
| Mikroservis düşünülebilir | Tek uygulama yeterli |
|---|---|
| Birbirinden bağımsız yayın yapması gereken birden çok ekip var | Tek ekip ya da birkaç geliştirici |
| Bir bölümün yük profili diğerlerinden çok farklı (görüntü işleme gibi) | Yük dengeli, darboğaz veri tabanında |
| Bir bölümün çökmesinin diğerlerini durdurmaması ölçülmüş bir ihtiyaç | "İleride büyürüz" beklentisi |
Varsayılan yaklaşımımız modüler monolittir: tek dağıtım, ama modül sınırları kodda net, her modülün kendi tabloları ve arayüzü var. Ölçülmüş bir ihtiyaç doğduğunda bir modülü ayrı servise çıkarmak bu düzende görece kolaydır. Rapor üretimi gibi ağır işleri ayrı bir işçi sürecine almak ise mikroservis değildir ve çoğu zaman yeterlidir.
Veri tabanı tasarımında neye dikkat ediyoruz?
Veri, uygulamadan uzun yaşar: uygulama iki kez yeniden yazılır, tablolar yerinde kalır. Bu yüzden:
- Bütünlük veri tabanında zorlanır. Yabancı anahtar,
UNIQUEveCHECKkısıtları sayesinde uygulamadaki bir hata tutarsız kayıt üretemez. - Para kayan noktalı sayıda tutulmaz.
NUMERIC(12,2)ya da kuruş cinsinden tam sayı. - Zaman saat dilimiyle tutulur. PostgreSQL'de
timestamptzdeğeri içeride UTC olarak saklar, gösterirken oturumun saat dilimine çevirir. Zaman kurallarımızın ayrıntısı özel yazılım geliştirme sayfasında. - Karakter seti. MySQL'de
utf8, 3 baytlık ve kullanımdan kaldırılmışutf8mb3'ün takma adıdır; MySQL belgeleri yeni uygulamalar içinutf8mb4'ü önerir. - Türkçe büyük-küçük harf. Java'da
"title".toUpperCase(), Türkçe yerel ayarla çalışan bir sunucuda"TİTLE"döndürür. Ekrandaki metinde doğrusu budur, ama sistem anahtarlarında ve e-posta karşılaştırmasında hata üretir; bu değerlerdeLocale.ROOTkullanılır, arama için uygun sıralama kuralı (collation) seçilir. - Şema değişikliği sürümlüdür. Her değişiklik, geri alma adımıyla birlikte numaralı bir migration dosyasıdır; canlı veri tabanında elle
ALTER TABLEçalıştırılmaz. - Silme bir tasarım kararıdır. "Silindi" diye işaretlenmiş kayıt (soft delete), kişisel veri açısından silinmiş sayılmaz. KVKK m.7 kapsamında silme, yok etme ya da anonimleştirme gerekiyorsa bunu yapan iş ve yedeklerdeki kopyanın ömrü ayrıca tasarlanır.
API kayıtları bir anlaşmazlıkta işe yarar mı?
Bir API'nin kayıtları, ancak tek bir isteği baştan sona izlemeye yetiyorsa işe yarar. Her isteğe bir istek kimliği (request_id) verir, bu kimliği API, kuyruk, dış sistem çağrısı ve denetim kaydı boyunca taşırız. Denetim kaydının alanlarını özel yazılım sayfamızda anlattık; API tarafında ayrıca:
- İstemcinin IP adresiyle birlikte kaynak portu da yazılır. CGNAT arkasında aynı IP adresini birçok abone paylaşır; RFC 6302 bu yüzden internete açık sunuculara kaynak portu ve izlenebilir bir saat kaynağından alınmış, tercihen UTC zaman damgasını kaydetmeyi önerir. 5651 sayılı Kanun'daki trafik bilgisi tanımı da (m.2/1-j) IP adresiyle birlikte kaynak ve hedef port bilgisini sayar; "kaynak ve hedef" ibaresi 31.07.2026'da yürürlüğe giren 7590 sayılı Kanunla eklendi. Nginx'in varsayılan kayıt biçiminde kaynak port yoktur, ayrıca eklenir.
- Yük dengeleyici ya da CDN arkasında sunucu ara katmanın adresini görür; gerçek istemci adresi ve portu, ancak ara katman bunları bir başlıkta iletiyor ve başlık yalnızca ondan kabul ediliyorsa doğru kaydedilir.
- Dış sistemlere giden istekler ve gelen yanıtlar; parola, anahtar ve kart bilgisi ayıklanarak saklanır. "Karşı taraf bildirimi hiç göndermedi" tartışması çoğu zaman bu kayıtla kapanır.
Bu kayıtların bir uyuşmazlıkta nasıl okunduğunu log kayıtlarının güvenilirliği yazısında, bir olayda nasıl incelendiğini log analizi sayfasında anlatıyoruz.
Sistem mimarisi ve teslim: elinize ne geçer?
Sistem mimarisi bir diyagramdan çok, kararların gerekçesiyle yazılı olmasıdır. Teslimde aldıklarınız:
- OpenAPI sözleşmesi ve her uç nokta için örnek istek ve yanıtlar.
- Veri modeli şeması ve migration geçmişi.
- Mimari karar kayıtları (ADR): "neden PostgreSQL", "neden kuyruk", "neden mikroservis değil"; her biri tarih, değerlendirilen seçenekler ve gerekçeyle.
- Entegrasyon envanteri: dış sistem, ortam, anahtarın saklandığı yer (değeri değil), sağlayıcının destek kanalı ve sürüm politikası.
- Çalıştırma kılavuzu: dağıtım, yedekten geri dönüş, bir entegrasyon durduğunda yapılacaklar.
- Otomatik testler ve bir yük testi sonucu: hangi yükte, hangi yanıt süresi.
Kod deposu, bulut ve sağlayıcı hesapları iş sahibi adına açılır. Sürekli entegrasyon ve dağıtım hattının kurulumu modernizasyon, bulut ve DevOps sayfasının konusu; bu işlerin kabul kriterlerini baştan yazmak için proje danışmanlığı ve teknik şartname sayfasına bakabilirsiniz.