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

Clean Code vs Çalışan Kod: Tek Tabanca Geliştiriciye Hangisi?

Tek tabanca (solo) geliştirici olarak clean code takıntısının getirdiği gizli maliyetleri ve çalışan kod dengesini kendi tecrübelerimle analiz ediyorum.

100%

Yalnız çalışan, tüm sistemi tek başına sırtlayan bir geliştiriciyseniz, önünüzde her sabah iki yol belirir: Kitaplarda yazan kusursuz, pırıl pırıl “clean code” prensiplerine uymak ya da o gün yayına girmesi gereken o özelliği (feature) ne pahasına olursa olsun çalıştırmak. Ben yıllardır bu ikilemin tam ortasında yaşıyorum. Yıllarca kurumsal projelerde, kendi yan ürünlerimde ve üretim ERP’lerinde kod yazarken bu iki kutup arasında gidip geldim.

Sonunda vardığım yer pragmatizmdir. Tek tabanca (solo) çalışan bir geliştiricinin en büyük düşmanı rakipleri ya da yetersiz sunucu kaynakları değil; kendi kafasında yarattığı, gerçeklikle bağı kopmuş “mükemmel mimari” hayalidir. Eğer tek başınaysanız, yazdığınız kodun temizliği ancak o kod üretime (production) girip fatura kesebildiği sürece değerlidir.

1. Mükemmeliyetçilik Tuzağı: Saatlerce Refactoring vs. Hızlıca Production

Sektörde popüler olan “Clean Code” kitaplarının neredeyse tamamı, arkasında devasa bütçeler ve düzinelerce mühendis olan ekipler için yazılmıştır. Tek başınıza çalışırken, her şeyi soyutlama (abstraction) katmanlarının arkasına gizlemek, her küçük fonksiyon için üç katmanlı mimari kurmak sizi sadece yavaşlatır. Geçen yıl kendi geliştirdiğim bir yan ürünün backend tarafında basit bir e-posta gönderim modülü yazmam gerekti. Önümde iki seçenek vardı: Ya tam anlamıyla SOLID prensiplerine uygun, yarın öbür gün e-posta sağlayıcısını değiştirdiğimde tek satır değiştirmem gerekmeyecek soyut bir EmailServiceProvider arayüzü (interface) ve bunun fabrika (factory) sınıflarını yazacaktım ya da doğrudan kütüphaneyi çağırıp geçecektim.

İlk seçeneği seçseydim mimariyi tasarlamak, testlerini yazmak ve bağımlılıkları (dependencies) yönetmek ciddi bir zaman alacaktı. Ben ikinci yolu seçtim; doğrudan kütüphaneyi import edip, API anahtarını çevre değişkeninden (environment variable) okuyan 10 satırlık bir fonksiyon yazdım ve üretime aldım. Toplam süre kısacıktı.

# Aşırı mühendislik (Over-engineered) yaklaşımı - Tek kişi için zaman kaybı
class IEmailProvider(ABC):
    @abstractmethod
    def send(self, to_email: str, subject: str, body: str): pass

class ComplexEmailFactory:
    @staticmethod
    def get_provider(provider_type: str) -> IEmailProvider: ...

# Benim tercih ettiğim pragmatik yaklaşım (FastAPI / Python)
import httpx

async def send_simple_email(to_email: str, subject: str, content: str):
    # Tek başınaysan, doğrudan hedefe git. Sağlayıcı değişirse bu 10 satırı değiştirirsin.
    async with httpx.AsyncClient() as client:
        response = await client.post(
            "https://api.mailgun.net/v3/mydomain.com/messages",
            auth=("api", "key-xxxxxxxxxxxx"),
            data={"from": "Mustafa <[email protected]>", "to": to_email, "subject": subject, "text": content}
        )
        response.raise_for_status()

Bu sade yaklaşım sayesinde kazandığım saatleri, kullanıcıların doğrudan deneyimlediği ödeme adımlarındaki bir hatayı çözmek için harcadım. Tek kişilik operasyonlarda zaman en değerli sermayedir ve bu sermayeyi hayali esneklikler için harcamak iflasa davetiye çıkarır.

2. Over-Engineering Nasıl Para Kaybettirir? (Yıllık Sunucu ve Zaman Maliyeti)

Mikroservis çılgınlığı başladığında, tek kişilik projelerini bile onlarca mikroservise bölüp, araya mesaj kuyrukları (message queues) koyan ve bunları Kubernetes üzerinde koşturmaya çalışan çok meslektaşımı gördüm. Sonuç ne oldu? Ucuz tek bir VPS (Virtual Private Server) üzerinde rahatça çalışabilecek bir proje, kat kat yüksek bir cloud faturasına ve yönetilemez bir karmaşıklığa dönüştü.

Tek tabanca geliştiricinin hedefi her zaman “en az hareketli parça” olmalıdır. Her yeni servis, her yeni veritabanı, her yeni kuyruk mekanizması; izlenmesi (monitoring), güncellenmesi, yedeklenmesi ve çökme anında müdahale edilmesi gereken birer yük demektir.

Mimari Yaklaşım Aylık Sunucu Maliyeti Güncelleme/Bakım Yükü Hata Tespit Süresi (MTTR)
Gereksiz Mikroservisler Yüksek (Cloud + Managed DB + K8s) Ağır Uzun (Log izleme karmaşası)
Pragmatik Monolith Düşük (1 Core, 2GB RAM VPS) Hafif Kısa (Tek bir journald logu)

Eğer sisteminiz saniyede on binlerce istek almıyorsa (ki projelerin çok büyük çoğunluğu ilk yıllarında bu seviyenin yanına bile yaklaşamaz), tek yapmanız gereken temiz bir monolit yazmaktır. Ben kendi yan ürünlerimde PostgreSQL, FastAPI ve Vue/React üçlüsünü tek bir docker-compose dosyasıyla ayağa kaldırıyorum. Dağıtım (deployment) sadece git pull && docker compose up --build -d komutundan ibaret.

Daha önce kaleme aldığım Docker disk yangınları yazısında bu tarz minimalist altyapılarda karşılaştığım disk dolma sorunlarını ve bunları nasıl çözdüğümü detaylandırmıştım. Altyapıyı basit tutmak, hata anında nereye bakacağınızı bilmenizi sağlar.

3. “Çalışan Kod” Çöp Kod Demek Değildir: Benim Pragmatik Standartlarım

Burada yanlış anlaşılmak istemem; “çalışan kod” derken güvenlik açık barındıran, değişken isimleri a, b, c olan, hiçbir log üretmeyen çöp bir kod yığınından bahsetmiyorum. Tam aksine, tek başınıza olduğunuzda kodun “okunabilir” olması, gelecekteki kendinize borçlanmamanız için hayati önem taşır. Ancak benim temiz kod standartlarım, akademik kitaplardaki kurallardan farklıdır.

Benim için pragmatik temiz kodun üç temel kuralı vardır:

  1. Okunabilirlik (Readability): Koda 6 ay sonra baktığımda, hiçbir dökümantasyona ihtiyaç duymadan ne yaptığını anlayabilmeliyim. Karmaşık tek satırlık (one-liner) sihirbazlıklar yerine, açık ve uzun değişken isimlerini tercih ederim.
  2. Hata Yönetimi ve Loglama (Error Handling): Kod patlayabilir, bu doğaldır. Ama patladığında bana tam olarak hangi satırda, hangi veriyle patladığını söyleyen anlamlı bir hata logu üretmelidir.
  3. Güvenlik (Security): SQL injection, yetkilendirme (authorization) eksiklikleri ve rate limiting gibi temel konular asla “çalışan kod” bahanesiyle ertelenemez.
# OKUNABİLİR VE GÜVENLİ: Basit ama sağlam bir endpoint örneği
@app.post("/api/v1/invoice/generate")
@limiter.limit("5/minute")  # Rate limiting hayati önem taşır
async def generate_invoice(request: InvoiceRequest, db: Session = Depends(get_db), current_user: User = Depends(get_current_user)):
    # Yetki kontrolü pragmatik ve nettir
    if not current_user.is_active:
        logger.warning(f"Pasif kullanıcı fatura üretmeye çalıştı: {current_user.id}")
        raise HTTPException(status_code=403, detail="Account is suspended")
        
    try:
        invoice = create_invoice_record(db, request, current_user.id)
        return {"status": "success", "invoice_id": invoice.id}
    except Exception as e:
        # Loglama olmadan çalışan kod, saatli bir bombadır
        logger.error(f"Fatura üretilemedi. Kullanıcı: {current_user.id}, Hata: {str(e)}", exc_info=True)
        raise HTTPException(status_code=500, detail="Internal billing system error")

Bu kodda karmaşık tasarım desenleri (design patterns) yok, gereksiz soyutlamalar yok. Ama güvenli, log mekanizması kurulu ve okunabilir. İşte tek tabanca geliştiricinin standartı tam olarak bu olmalıdır.

4. Gerçek Bir Vaka: Ağır Süren Sorguyu Hızlandırırken Neyi Feda Ettim?

Bir üretim ERP’si projemde, sevkiyat raporlama ekranı her çalıştığında tüm sistemi kilitliyordu. Sorgu ağır şekilde sürüyordu ve veritabanı CPU kullanımı tavan yapıyordu. Akademik bir “clean code” yaklaşımı benimseseydim, veritabanı şemasını tamamen normalize etmem, eski verileri arşivleyecek bir mikroservis yazmam ve belki de CQRS (Command Query Responsibility Segregation) desenini uygulayarak okuma ve yazma veritabanlarını ayırmam gerekirdi. Bu yaklaşımın bana maliyeti çok uzun bir uğraş ve potansiyel yeni buglar olacaktı.

Ben ne yaptım? Sorunu analiz ettiğimde, sorgunun sürekli geçmiş 30 günlük hareketleri taradığını ve status alanı üzerinde bir indeks olmadığını gördüm. PostgreSQL üzerinde sadece tek bir kısmi indeks (partial index) tanımladım ve sorgunun içindeki gereksiz üç adet JOIN işlemini kaldırarak ham SQL sorgusuna çevirdim.

-- Akademik olarak "temiz" olmayan ama hayat kurtaran kısmi indeks (partial index)
CREATE INDEX idx_delivery_pending_last_30_days 
ON deliveries (created_at DESC, customer_id) 
WHERE status = 'PENDING' AND created_at > NOW() - INTERVAL '30 days';

Sorgu süresi anında belirgin biçimde düştü ve ekran tekrar kullanılabilir hale geldi. Evet, kodun içine ORM (Object-Relational Mapping) yerine ham SQL sorgusu gömdüm. Bazı yazılım guruları bunu “clean code” standartlarına aykırı bulabilir çünkü veritabanı bağımlılığı yaratıyor. Ancak gerçek dünyada o ERP’nin veritabanını PostgreSQL’den başka bir sisteme taşıma ihtimalimiz sıfıra yakındı. Bu pragmatik karar bana ciddi zaman kazandırdı ve sistemin sorunsuz çalışmasını sağladı. Benzer performans çözümlerini PostgreSQL index stratejileri yazımda da paylaşmıştım; bazen en iyi kod, en az yazılan koddur.

5. Tek Kişilik Devops ve Altyapı Yönetiminde “Yetecek Kadar Temiz” Olmak

Eğer tek başınıza hem kod yazıp hem de o kodun çalıştığı sunucuyu yönetiyorsanız, sistem yönetimi tarafında da pragmatik olmak zorundasınız. CI/CD süreçlerini otomatize etmek için haftalarca uğraşıp karmaşık pipeline’lar kurmak yerine, işinizi görecek en basit ve güvenilir yöntemi seçmelisiniz.

Kendi projelerimde Jenkins, GitLab CI gibi ağır araçlar yerine GitHub Actions veya basit bir self-hosted runner kullanıyorum. Sunucu tarafında ise Docker konteynerlerini yönetmek için Kubernetes yerine systemd ve docker-compose ikilisini tercih ediyorum. Bir servisin çökmesi durumunda otomatik olarak ayağa kalkmasını sağlayan basit bir systemd unit dosyası, bana geceleri huzurlu bir uyku uyutmaya yetiyor.

# /etc/systemd/system/app-watcher.service
[Unit]
Description=My Pragmatic App Container Watcher
After=docker.service
Requires=docker.service

[Service]
TimeoutStartSec=0
Restart=always
RestartSec=10
# Basit bir health check ve otomatik restart mekanizması
ExecStart=/usr/local/bin/docker-compose -f /opt/myapp/docker-compose.yml up
ExecStop=/usr/local/bin/docker-compose -f /opt/myapp/docker-compose.yml down

[Install]
WantedBy=multi-user.target

Bu servis dosyası sayesinde, sunucu kapansa da, Docker daemon çökse de sistem kendi kendini toparlıyor. Karmaşık Kubernetes pod replikasyon ayarlarıyla uğraşmadan, işletim sisteminin kendi yeteneklerini (systemd) kullanarak sıfır maliyetli ve son derece güvenilir bir altyapı elde etmiş oluyorum.

6. Teknik Borç (Technical Debt) Yönetimi: Ne Zaman Ödemeli, Ne Zaman İflas İlan Etmeli?

“Çalışan kod” yazarak ilerlediğinizde, sisteminizde kaçınılmaz olarak teknik borç birikir. Bu borç tıpkı kredi kartı borcu gibidir; işinizi büyütmek için mantıklı bir araçtır ama kontrolsüz büyürse sizi batırır. Tek tabanca geliştirici olarak bu borcu yönetmenin sırrı, ne zaman ödeyeceğinizi iyi bilmektir.

Ben kodun içine yazdığım “geçiştirme” çözümleri mutlaka özel bir formatta etiketlerim. Bu etiketler sıradan bir TODO yorumu değildir. İçinde tarih, borcun nedeni ve bu borcun ne zaman ödenmesi gerektiğine dair bir tetikleyici (trigger) barındırır.

# FIXME: [2026-05-30] - Mustafa Erbay
# NEDEN: İlk lansman için hızlıca JSON dosyasından okuyoruz.
# TETİKLEYİCİ: Aktif kullanıcı sayısı 500'ü geçtiğinde PostgreSQL tablosuna taşınmalı.
# ETKİ: Okuma hızı O(N), 500 kullanıcıdan sonra performans sorunu yaratır.
def get_user_preferences_legacy(user_id: int):
    with open("temp_preferences.json", "r") as f:
        data = json.load(f)
    return data.get(str(user_id), {})

Haftalık veya aylık periyotlarda kod tabanında bu etiketleri aratırım. Eğer tanımladığım tetikleyiciye (örneğin kullanıcı sayısı limitine) ulaşılmadıysa, o koda dokunmam. Çalışıyorsa ve işi görüyorsa bırakın öyle kalsın. Ancak ne zaman ki o borç iş süreçlerini yavaşlatmaya veya gerçek hatalara sebep olmaya başlar, işte o zaman planlı bir refactoring süreci başlatırım.

Tek başınıza başarılı olmak istiyorsanız, mükemmel kod yazma takıntısını bir kenara bırakıp, “değer üreten ve çalışan kod” yazmaya odaklanmalısınız. Çünkü günün sonunda kullanıcılarınız sizin kodunuzun ne kadar temiz olduğuna değil, uygulamanızın onların sorununu çözüp çözmediğine bakar.

Bir sonraki yazımda, tek kişilik projelerde yapay zeka ajanlarını (AI Agents) birer iş gücü olarak nasıl konumlandırdığımı ve operasyonel yükümü belirgin biçimde nasıl azalttığımı 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.

Tek tabanca geliştirici olarak clean code yazmak için hangi araçları kullanmalıyım?
Ben clean code yazmak için genelde basit araçları tercih ediyorum. Mesela, kodumu düzenlemek için bir kod düzenleyici kullanıyorum. Ayrıca, kod kalitesi için bazı araçları da kullanıyorum. Ancak, çok fazla araç kullanmak bazen kod yazma hızımı düşürdüğü için dikkatli kullanıyorum.
Clean code ile çalışan kodu dengelemek için nasıl bir strateji izlemeliyim?
Ben clean code ile çalışan kodu dengelemek için önce neyin öncelikli olduğuna karar veriyorum. Eğer bir özelliği hızlı bir şekilde yayına almak zorundaysam, o zaman öncelikle çalışan kodu hedefliyorum. Daha sonra, kodumu temizlemek için zaman ayırmaya çalışıyorum. Bu şekilde, hem hızlı hem de temiz kod yazmaya çalışıyorum.
Mükemmeliyetçilik tuzağından kaçınmak için hangi taktikleri kullanmalıyım?
Ben mükemmeliyetçilik tuzağından kaçınmak için önce gerçekçi hedefler koyuyorum. Ayrıca, kodumu sürekli olarak gözden geçirerek ve gereksiz kompleksliklerden kaçınarak temiz tutmaya çalışıyorum. Ayrıca, kod yazma hızımı ölçerek ve bunu iyileştirmeye çalışarak da mükemmeliyetçilik tuzağından kaçınmaya çalışıyorum.
Pragmatik bir yaklaşım kullanarak clean code yazarken hangi tradeoff'leri göz önünde bulundurmalıyım?
Ben pragmatik bir yaklaşım kullanarak clean code yazarken, kod kalitesi ile kod yazma hızı arasındaki tradeoff'i dikkate alıyorum. Ayrıca, kodun karmaşıklığı ile bakım kolaylığı arasındaki tradeoff'i de düşünüyorum. Bu tradeoff'leri dikkatlice değerlendirerek, hem temiz hem de çalışan kodu yazmaya çalışıyorum.
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