İçeriğe Atla
Mustafa Erbay
Yaşam · 11 dk okuma · görüntülenme Read in English

Mükemmel Mimari vs Çalışan Kod: Solo Yazılımcı İçin 3 Ders

Mükemmel mimari vs çalışan kod ikilemini inceliyor, solo yazılımcılar için over-engineering tuzaklarından kurtulmanın pragmatik yollarını anlatıyorum.

100%

Yazılım dünyasında yıllardır bitmeyen bir kavga var: Mükemmel Mimari vs Çalışan Kod. Tek başına proje geliştiren bir solo yazılımcıysan, bu kavga senin kendi iç dünyanda, her commit öncesinde yaşanır. Tasarım kalıpları, temiz kod prensipleri, mikroservisler derken günün sonunda elimizde tek bir satırı bile production ortamına çıkmamış “mükemmel” ama ölü bir kod yığını kalabiliyor. Yıllar içindeki saha tecrübemde, bu tuzağa bizzat defalarca düştüm; her seferinde de pragmatik olanın, yani çalışan kodun kazandığını gördüm.

Tek başına ürün geliştiren birinin en değerli kaynağı zamandır. Büyük kurumsal ekiplerin haftalarca süren mimari toplantılarla çözdüğü problemleri, senin tek bir gecede çözüp yayına alman gerekir. Bu yazıda, kendi geliştirdiğim yan ürünlerde ve sahada edindiğim tecrübelerle, solo yazılımcıların hayatta kalmasını sağlayacak 3 temel dersi ve mimari kararlardaki trade-off dengelerini inceliyorum.

PostgreSQL index stratejileri


1. Altyapı Tuzağı: Docker Compose Yeten Yere Kubernetes Kurmak

Solo yazılımcıların en sık düştüğü hata, henüz günde 100 kişinin bile ziyaret etmediği bir proje için devasa bir Kubernetes (K8s) cluster’ı ayağa kaldırmaya çalışmaktır. Altyapıyı mükemmelleştirmek, “gerçek işi” yapmaktan kaçmanın en konforlu yoludur. K8s kurarsın, ingress yapılandırırsın, sertifika yenileme otomasyonu için uğraşırsın; ama ortada henüz fatura kesebilen bir ürün yoktur.

Ben kendi yan ürünlerimin birinde ilk başta bu hatayı yaptım. “Yarın bir gün milyonlarca istek gelirse” diyerek çok node’lu bir cluster kurdum. Sonuç ne oldu? Sadece control plane bileşenlerinin RAM tüketimi yüzünden her ay kayda değer bir fatura ödedim. Üstelik bir gün etcd bozulduğunda sistemi ayağa kaldırmak saatlerimi aldı. Oysa aynı işi tek bir VPS üzerinde çalışan basit bir Docker Compose dosyasıyla sıfır overhead ile çözebilirdim.

# docker-compose.yml - Solo yazılımcının can kurtaranı
version: '3.8'

services:
  web:
    image: python:3.11-slim
    command: uvicorn main:app --host 0.0.0.0 --port 8000 --workers 4
    volumes:
      - .:/app
    environment:
      - DATABASE_URL=postgresql://user:pass@db:5432/dbname
    restart: always
    deploy:
      resources:
        limits:
          cpus: '0.50'
          memory: 512M

  db:
    image: postgres:16-alpine
    volumes:
      - postgres_data:/var/lib/postgresql/data
    environment:
      POSTGRES_USER: user
      POSTGRES_PASSWORD: pass
      POSTGRES_DB: dbname
    restart: always

volumes:
  postgres_data:

Yukarıdaki basit yapılandırma, saniyede yüzlerce isteği rahatlıkla karşılar. Üstelik cgroup limitleri sayesinde web servisinin RAM sızıntısı yapıp tüm sunucuyu kilitlemesini de engellemiş olursun. Solo yazılımcı için mükemmel altyapı, bakımı en az zaman alan ve gece seni uykundan uyandırmayacak altyapıdır.


2. Veritabanı Tasarımında Aşırı Normalizasyon ve ORM Tuzakları

Akademik kitaplarda anlatılan 3NF, 4NF gibi veritabanı normalizasyon kuralları kağıt üzerinde harika durur. Ancak pratikte, solo bir yazılımcı için aşırı normalize edilmiş bir veritabanı şeması, sonu gelmez JOIN sorguları ve performans darboğazları demektir. Özellikle bir ORM (Object-Relational Mapping) kütüphanesi kullanıyorsan, arka planda dönen N+1 sorgu facialarını fark etmen zaman alabilir.

Bir üretim ERP’si üzerinde çalışırken, her şeyi o kadar çok tablolara bölmüştük ki, operatör ekranında basit bir sipariş durumunu göstermek için çok sayıda tabloyu birleştirmek gerekiyordu. SQL sorguları şişti, veritabanı CPU kullanımı tavan yaptı. Sonrasında bazı verileri denormalize ederek, yani sık okunan alanları JSONB olarak tek bir tabloda tutarak bu sorunu çözdük.

Aşağıdaki SQL örneğinde, karmaşık ilişkileri tek bir sorguda PostgreSQL’in JSON yetenekleriyle nasıl çözebileceğimizi görebilirsin. Bu yaklaşım, kod tarafında onlarca satır ORM kodu yazmaktan ve veritabanını yormaktan seni kurtarır:

-- Tek sorguda ilişkili verileri JSON olarak çekmek (N+1 çözüm)
SELECT 
    orders.id,
    orders.status,
    json_build_object(
        'id', customers.id,
        'name', customers.name,
        'email', customers.email
    ) AS customer,
    (
        SELECT json_agg(json_build_object('item_id', order_items.product_id, 'qty', order_items.quantity))
        FROM order_items 
        WHERE order_items.order_id = orders.id
    ) AS items
FROM orders
LEFT JOIN customers ON orders.customer_id = customers.id
WHERE orders.status = 'pending'
LIMIT 50;

Bu sorgu, tek bir veritabanı turu (round-trip) ile ihtiyacın olan tüm veriyi hiyerarşik olarak getirir. Solo yazılımcı olarak senin amacın, kodda yüzlerce satır veri dönüştürme mantığı yazmak yerine, veritabanının gücünü sonuna kadar kullanmaktır.


3. Erken Optimizasyon ve “Belki Lazım Olur” (YAGNI) Mimarileri

“YAGNI” (You Aren’t Gonna Need It) yani “Buna ihtiyacın olmayacak” prensibi, solo yazılımcının duvarına asması gereken en önemli kuraldır. Gelecekteki hayali senaryolar için bugünden kod yazmak, projenin bitmesini engelleyen en büyük etkendir. Örneğin; arka planda çalışacak basit bir e-posta gönderim işlemi için hemen RabbitMQ veya Kafka gibi mesaj kuyruğu sistemlerini kurmaya kalkışmak tam bir zaman kaybıdır.

Örneğin kullanıcı kayıt olduktan sonra gönderilecek hoş geldin e-postası için hemen ayrı bir worker servisi kurmak cazip gelir. Ama önce şunu sormak gerekir: “Şu an günde kaç kişi kayıt oluyor?” Cevap çoğu zaman avuç içi kadar bir sayıdır. Bu işlem için neden ekstra bir servis, kuyruk mekanizması ve log izleme katmanı yönetilsin ki? Bunun yerine Linux’un yerleşik gücünden yararlanarak, basit bir systemd timer veya uygulama içi asenkron bir görev (background task) ile işi çözmek yeterlidir.

Özellik Ağır Kuyruk Mimarisi (Kafka/RabbitMQ) Basit Systemd Timer / Async Task
Kurulum Süresi Uzun (saatler) Çok kısa (dakikalar)
Bellek Tüketimi ~512MB - 1GB RAM ~0MB (Ekstra yük getirmez)
Bakım Maliyeti Yüksek (Sürüm güncellemeleri, disk doluluğu) Yok denecek kadar az
Solo Geliştirici Uyumu Düşük Çok Yüksek

Eğer sistemin gerçekten büyürse, o zaman mimariyi değiştirmek zaten tatlı bir problem olacaktır. Ama ilk günden bu yükü sırtlanmak, daha yola çıkmadan yorulmana neden olur.


4. Mikroservis Hayalleri vs Monolit Gerçekliği

Mikroservis mimarisi, büyük organizasyonların ekipleri birbirlerinden bağımsız çalıştırabilmek için bulduğu bir çözümdür. Tek başına çalışan bir yazılımcının mikroservis mimarisi seçmesi ise teknik bir intihardır. Servisler arası iletişim, distributed tracing, ağ gecikmeleri (network latency) ve veri tutarlılığı (eventual consistency) gibi problemlerle tek başına baş edemezsin.

Onlarca mikroservise bölünmüş büyük bir platformun dağıtımını ve izlenmesini ayakta tutmanın yarattığı karmaşa, kendi projelerimde neden her zaman “modüler monolit” yaklaşımını seçtiğimi iyi anlatır. Tek bir kod deposu (monorepo), tek bir veritabanı ve tek bir deploy süreci sana inanılmaz bir hız kazandırır.

# /etc/systemd/system/my-monolith-app.service
# Solo yazılımcının monolit servisini canlıda tutan basit systemd tanımı
[Unit]
Description=My Monolith Web Application
After=network.target postgresql.service

[Service]
User=www-data
WorkingDirectory=/var/www/my-app
ExecStart=/var/www/my-app/venv/bin/gunicorn -w 4 -b 127.0.0.1:8000 main:app
Restart=always
RestartSec=5
StandardOutput=journal
StandardError=journal
LimitNOFILE=65535

[Install]
WantedBy=multi-user.target

Yukarıdaki systemd servis tanımı sayesinde, uygulaman herhangi bir sebeple çökerse (örneğin bellek sınırını aşarsa veya beklenmeyen bir hata alırsa) 5 saniye içinde otomatik olarak yeniden başlatılır. Ne Kubernetes’e ihtiyacın var ne de karmaşık pod yönetim araçlarına. Sadece systemd 252+ ve temiz bir konfigürasyon.


5. Hata Yönetimi ve Loglama: Sentry Yokken Journald ile Yetinmek

Gözlemlenebilirlik (observability) önemlidir, ancak bunun için de bütçeni ve zamanını harcamak zorunda değilsin. Ücretli APM araçları veya karmaşık ELK (Elasticsearch, Logstash, Kibana) yığınları yerine, Linux kernel’ının sunduğu yerleşik araçları kullanmayı öğrenmek solo yazılımcı için büyük bir güçtür.

Sistem yönetimi yaparken gördüğüm en büyük hatalardan biri, logların kontrolsüzce büyüyerek diski doldurmasıdır. “Docker disk yangını” dediğimiz, /var/lib/docker/containers dizininin şişerek tüm sunucuyu kilitlemesi olayı neredeyse her solo geliştiricinin başına gelir. Bunu engellemek için journald’nin rate limit ve disk boyutu sınırlandırma özelliklerini kullanabilirsin.

# /etc/systemd/journald.conf.d/size-limit.conf
# Logların diski doldurmasını engellemek için yapılandırma
[Journal]
SystemMaxUse=2G
SystemMaxFileSize=200M
MaxRetentionSec=1month
RateLimitIntervalSec=30s
RateLimitBurst=10000

Bu basit yapılandırma sayesinde, logların toplamda asla 2 GB’tan fazla yer kaplamaz ve saniyede 10.000’den fazla log üreterek sistemi kilitlemeye çalışan servisleri otomatik olarak sınırlar. Bir hata olduğunda ise terminalden tek bir komutla son 1 saatteki kritik hataları süzebilirsin:

# Son 1 saatteki sadece "error" ve daha kritik seviyedeki logları gösterir
journalctl -u my-monolith-app.service --since "1 hour ago" -p err..emerg --no-pager

Docker disk doluluğu çözümleri


6. Solo Yazılımcı İçin Karar Matrisi: Ne Zaman “Mükemmel” Demeli?

Peki, hangi noktada durup kod kalitesine veya mimariye yatırım yapmalıyız? Bunun cevabı çok basit: Kodun ürettiği değer, bakım maliyetini geçtiği an. Eğer yazdığın kod yarın sabah müşteriye fatura kesebiliyorsa, o kod “çalışan” koddur ve o aşamada mükemmel olmasına gerek yoktur.

Aşağıdaki karar matrisi, bir özelliği geliştirirken nasıl bir yol izlemen gerektiği konusunda sana pratik bir rehber sunabilir:

Senaryo / İhtiyaç İlk Yaklaşım (Çalışan Kod) Ne Zaman Değişmeli? (Mükemmel Mimari)
Kullanıcı Kayıt / Auth Basit JWT / Session tabanlı tek bir fonksiyon Kullanıcı sayısı 50.000+ olduğunda veya OAuth2/SSO gerektiğinde
Dosya Yükleme Sunucu diskine doğrudan kayıt (/uploads) Disk dolmaya başladığında veya CDN entegrasyonu gerektiğinde (S3 vb.)
Raporlama / Analiz PostgreSQL üzerinde COUNT ve GROUP BY Raporların çalışması 5 saniyeden uzun sürdüğünde (Read Replica veya ClickHouse)
Ödeme Entegrasyonu Tek bir API endpoint’i üzerinden doğrudan çağrı Günlük işlem hacmi $10.000+ olduğunda ve Idempotency gerektiğinde

Benim bu konudaki net pozisyonum şudur: Önce çalıştır, sonra doğrula, en son optimize et. Çalışmayan bir kodun mimarisi ne kadar mükemmel olursa olsun, değeri sıfırdır. Solo yazılımcı olarak senin asıl gücün, büyük şirketlerin bürokrasiye kurban ettiği o “çevikliği” ve “hızı” kendi lehine kullanmandır. Bırak kodun biraz kirli kalsın, yeter ki production’da olsun ve müşteriye değer üretsin.

Bir sonraki yazıda, kendi geliştirdiğim yan ürünlerin backend mimarisinde kullandığım PostgreSQL optimizasyon tekniklerini anlatacağım.

Paylaş:

Bu yazı faydalı oldu mu?

Yükleniyor...

Bu yazı nasıldı?

Sıkça Sorulanlar

Bu makale ile ilgili okurların sorduğu yaygın sorular.

Mükemmel mimari yerine çalışan kod elde etmek için projeme nasıl hızlı bir başlangıç yapabilirim?
Ben her yeni projeye başlarken önce "çalışan bir prototip" üretmeye odaklanırım. Öncelikle bir repository oluşturur, temel bir framework (örneğin Node.js + Express ya da FastAPI) kurarım ve bir "hello world" endpoint'i çalıştırırım. Ardından, en kritik iş akışını (örneğin veri kaydetme ya da API çağrısı) bir kaç satır kodla hayata geçiririm. Bu aşamada tasarım kalıpları ya da bağımlılık enjeksiyonu gibi soyutlamaları erteleyip, kodun gerçekten çalıştığını ve bir kullanıcıya değer sağladığını doğrularım. Daha sonra, geri bildirim aldıkça ve ihtiyaçlar netleştikçe mimariyi yavaş yavaş iyileştiririm; bu sayede zaman kaybı ve over‑engineering tuzağından kaçınırım.
Docker Compose yerine Kubernetes kurmanın avantajları ve dezavantajları neler? Solo geliştirici için ne zaman tercih etmeliyim?
Ben Kubernetes'i denediğimde, ölçeklenebilirlik, otomatik yeniden başlatma ve servis keşfi gibi güçlü özellikler gördüm; fakat bir solo geliştiricinin günlük 100‑200 isteği için bu faydalar çoğu zaman aşırı gelir. Docker Compose, tek makinede birden fazla konteyneri yönetmek için hafif ve hızlı bir çözüm sunar; konfigürasyon dosyaları basit ve öğrenme eğrisi düşük. Kubernetes ise cluster yönetimi, etcd bakımı ve ağ politikaları gibi ek sorumluluklar getirir; bu da ekstra zaman ve maliyet demektir. Ben, projemin trafiği ve karmaşıklığı belirli bir eşiği (örneğin 5.000 eşzamanlı istek) aşmadıkça Docker Compose tercih ederim; sadece büyük ölçekli SaaS ya da ekip içinde ortak altyapı gerektiren durumlarda Kubernetes'e geçiş yaparım.
Kubernetes kurarken karşılaştığım en büyük hatalar nelerdi ve bu hataları nasıl çözdüm?
İlk denememde en büyük hatam, kontrol düzlemi bileşenlerinin (etcd, kube‑apiserver) RAM tüketimini göz ardı etmemdi; çok node'lu bir cluster kurduğumda faturalarda beklenmedik bir artış gördüm. Ayrıca, pod'ların health‑check'lerini eksik yapılandırdığım için sık sık "CrashLoopBackOff" hataları alıyordum. Bu sorunları çözmek için öncelikle node'ların kaynak limitlerini sıkı bir şekilde tanımladım ve etcd yedekleme politikası ekledim. Health‑check'leri tanımlayarak pod'ların otomatik yeniden başlatılmasını sağladım ve Helm chart'larıyla standartlaştırılmış konfigürasyonları kullandım. Sonuçta, izleme ve log toplama araçlarını (Prometheus + Grafana) entegre ederek problemleri erken fark edip müdahale süresini büyük ölçüde azalttım.
Mikroservis mimarisi her proje için daha iyi bir seçim midir? Bu konuda genel kanının doğru olup olmadığını anlat.
Pratikte gördüğüm kadarıyla mikroservis mimarisi, yüksek trafik, bağımsız ekipler ve sık sürüm güncellemeleri gerektiren büyük ölçekli sistemlerde gerçekten değer katıyor. Ancak bir solo geliştirici için bu mimari, servisler arası iletişim, veri tutarlılığı ve dağıtık izleme gibi ekstra karmaşıklıkları getirir. Çoğu zaman, monolitik bir yapı daha hızlı geliştirme, daha az hata ve düşük operasyon maliyeti sağlar. Bu yüzden, projenin karmaşıklığı ve ölçeği mikroservis gerektirmediği sürece, tek bir kod tabanı içinde temiz bir mimari uygulamak daha pragmatik bir yaklaşımdır. Genel kanının "her şey mikroservis olmalı" yanılgısı yalnızca belirli senaryolarda geçerlidir.
ME

Mustafa Erbay

Sistem Mimarisi · Network Uzmanı · Altyapı, Güvenlik ve Yazılım

2006'dan bu yana sistem mimarisi, network, sunucu altyapıları, büyük yapıların kurulumu, yazılım ve sistem güvenliği ekseninde çalışıyorum. Bu blogda sahada karşılığı olan teknik deneyimlerimi paylaşıyorum.

Kişisel Notlar

Bu notlar sadece sizde saklanır. Tarayıcınızda yerel olarak tutulur.

Hazır 0 karakter

Yorumlar

Sunucu Taraflı AI Moderasyon

Yorumlar sunucuda yapay zeka ile denetlenir ve kalıcı olarak saklanır.

?
0/2000

Sunucu taraflı AI denetim

✉️ Ücretsiz · Spam yok · İstediğin an çık

Yeni yazılardan haberdar olun

Yeni içerikler ve teknik notlar e-postanıza gelsin.

  • 📌
    Haftanın en iyisi Sadece okumaya değer tek yazı
  • 🔧
    Alet çantası Bu hafta kullandığım araçlar
  • 🧠
    Perde arkası Blog'a girmeyen notlar

Spam yapmıyoruz. İstediğiniz zaman ayrılabilirsiniz. · Sadece Umami (self-hosted, Google yok) ile takip.

Okuma İstatistikleriniz

0

Yazı Okundu

0dk

Okuma Süresi

0

Gün Serisi

-

Favori Kategori

İlgili Yazılar