Sürdürülebilir kurumsal yazılım mimarisi: Clean Code ilkeleri, SOLID prensipleri ve gerçek hayat refactoring örnekleriyle kod kalitesini güvenle artırın.
Hızlı Özet (TL;DR)
Spagetti mimariyle ve aceleyle yazılmış bir yazılım, ilk haftalarda “çalışıyor” gibi görünse de; şirket büyüdükçe yeni bir özellik eklemenin haftalar sürdüğü, bir hatayı düzeltirken başka üç modülün bozulduğu bir teknik borç (Technical Debt) bataklığına dönüşür. Clean Code (Temiz Kod); yazılımın yalnızca derleyici (Compiler) tarafından değil, yıllar sonra o projeyi devralacak başka bir yazılım mühendisi tarafından da bir kitap gibi zahmetsizce okunabilmesini amaçlar. Robert C. Martin (Uncle Bob) tarafından sistemleştirilen SOLID prensipleri ise kurumsal ölçekli ERP, MES ve finans yazılımlarında esnek, gevşek bağlı (Loosely Coupled), kolayca birim testi (Unit Test) yazılabilir ve uzun ömürlü bir mimari kurmanın 5 temel mühendislik yasasıdır.
Kurumsal Yazılımlarda “Teknik Borç” Neden Projeleri Çökertir?
Birçok sanayi ve hizmet işletmesi, dışarıdan satın aldığı veya içeride geliştirdiği özel yazılımlarda 2-3 yıl sonra şu acı tabloyla karşılaşır: Yazılımı ilk yazan yazılımcı işten ayrılmıştır ve yeni gelen ekip koda dokunmaya korkmaktadır. Fatura modülündeki küçük bir vergi oranı değişikliği, beklenmedik şekilde depo stok düşümünü bozmaktadır.
Yazılım mühendisliğinde “Mimari Çürüme” (Software Rot / Rigidity & Fragility) olarak adlandırılan bu durumun kök nedeni; iş kurallarının (Domain Logic), veritabanı sorgularının (SQL), kullanıcı arayüzü kontrollerinin ve üçüncü taraf API çağrılarının 3.000 satırlık tek bir sınıfın veya fonksiyonun içine sıkıştırılmasıdır. Temiz kod ve SOLID prensipleri, akademik birer lüks değil; milyonlarca liralık yazılım yatırımınızın 10 yıl sonra da ilk günkü hızıyla geliştirilebilmesini sağlayan finansal bir güvencedir.
SOLID Prensipleri ve Kurumsal Mimari Karşılaştırma Matrisi
Nesne yönelimli (OOP) ve modüler kurumsal yazılım kalitesini belirleyen 5 temel SOLID ilkesinin özeti, ihlal belirtileri ve mimari çözümleri:
| SOLID İlkesi | Türkçe Mühendislik Karşılığı | Temel Mimari Amacı | İhlal Edildiğinde Yaşanan Sorun (Code Smell) |
|---|---|---|---|
| S — SRP (Single Responsibility) | Tek Sorumluluk Prensibi | Bir sınıfın veya modülün değişmek için yalnızca tek bir iş nedeni (aktörü) olmalıdır | 2.500 satırlık SiparisManager sınıfı hem fiyat hesaplar, hem SQL yazar, hem PDF basar |
| O — OCP (Open/Closed Principle) | Açık / Kapalı Prensibi | Yazılım varlıkları davranışsal genişlemeye açık, ancak mevcut kaynak kodu değiştirmeye kapalı olmalıdır | Her yeni kargo firması eklendiğinde 15 yerdeki if / else if / switch bloklarını elle değiştirmek |
| L — LSP (Liskov Substitution) | Liskov İkame Prensibi | Türetilen alt sınıflar, üst sınıflarının (Base/Interface) yerine hiçbir beklenmedik hata üretmeden geçebilmelidir | Alt sınıfın desteklemediği bir metotta throw new NotImplementedException() fırlatıp sistemi çökertmesi |
| I — ISP (Interface Segregation) | Arayüz Ayrımı Prensibi | İstemciler, kendi iş süreçlerinde hiç kullanmadıkları metotları barındıran şişkin arayüzlere zorlanmamalıdır | Sadece barkod okuyacak el terminaline, muhasebe fişi kesme metotlarını da içeren dev arayüzü dayatmak |
| D — DIP (Dependency Inversion) | Bağımlılıkların Tersine Çevrilmesi | Yüksek seviyeli iş kuralları, alt seviyeli teknik detaylara (SQL, SMTP) değil; soyutlamalara (Interface) dayanmalıdır | Veritabanı MS SQL’den PostgreSQL’e geçerken tüm iş katmanı sınıflarının baştan yazılması |
Gerçek Hayat Refactoring Örnekleriyle SOLID Uygulaması
Soyut tanımları somutlaştırmak için bir İmalat ERP Çözümleri projesinde sıkça karşılaşılan gerçek kod senaryolarını inceleyelim:
1. SRP (Tek Sorumluluk Prensibi) Uygulaması
Kötü Kod (Çok Sorumluluklu ‘God Object’): Aşağıdaki sınıf hem iş kuralını doğruluyor, hem doğrudan veritabanı bağlantısı açıyor, hem de SMTP sunucusuna bağlanıp e-posta atıyor. Yarın e-posta yerine SMS atılmak istendiğinde veya veritabanı şeması değiştiğinde bu sınıf bozulacaktır:
// YANLIŞ: Üç farklı uzmanlık alanını tek sınıfa sıkıştıran kırılgan yapı
class SiparisServisi {
siparisOlustur(siparis: any) {
// 1. Sorumluluk: İş kuralı ve stok kontrolü
if (siparis.kalemler.length === 0) throw new Error("Boş sipariş açılamaz");
// 2. Sorumluluk: Doğrudan SQL veritabanı işlemi
const db = new SqlConnection("Server=10.0.0.5;Database=ErpDb;");
db.execute(`INSERT INTO Siparisler VALUES (${siparis.id}, ${siparis.tutar})`);
// 3. Sorumluluk: Doğrudan SMTP E-posta gönderimi
const smtp = new SmtpClient("mail.sirket.com", 587);
smtp.send(siparis.musteriEmail, "Siparişiniz Alındı", "Detaylar ektedir...");
}
}
Temiz Kod (Ayrık Sorumluluklar ve Bağımlılık Enjeksiyonu - DI):
// DOĞRU: Her sınıf yalnızca kendi uzmanlık alanından sorumludur
export class SiparisUygulamaServisi {
constructor(
private readonly siparisDeposu: ISiparisDeposu,
private readonly bildirimServisi: IBildirimGonderici
) {}
async siparisOlustur(komut: YeniSiparisKomutu): Promise<SiparisSonucu> {
const siparis = SiparisVarligi.olustur(komut.musteriId, komut.kalemler);
await this.siparisDeposu.kaydetAsync(siparis);
await this.bildirimServisi.onayGonderAsync(siparis.musteriIletisim, siparis.referansNo);
return SiparisSonucu.basarili(siparis.id);
}
}
2. OCP (Açık/Kapalı Prensibi) ve Strateji Deseni
Bir Stok Takip ve Barkodlu Depo Yazılımı projesinde farklı müşteriler için farklı maliyetlendirme yöntemleri (FIFO, LIFO, Ağırlıklı Ortalama) veya farklı kargo entegrasyonları (Yurtiçi, Aras, MNG, DHL) çalıştığını düşünelim.
Eğer kodu if (yontem == "FIFO") { ... } else if (yontem == "LIFO") { ... } şeklinde yazarsanız, her yeni maliyet yöntemi eklendiğinde çalışan ve test edilmiş mevcut sınıfı açıp değiştirmek zorunda kalırsınız. Bunun yerine IMaliyetHesaplamaStratejisi arayüzü tanımlanır: FifoMaliyetHesaplayici, AgirlikliOrtalamaHesaplayici ve LotBazliMaliyetHesaplayici bu arayüzü uygular. Yeni bir yöntem gerektiğinde mevcut çalışan koda tek bir satır bile dokunmadan sadece yeni bir sınıf eklenir ve sisteme enjekte edilir.
3. DIP (Bağımlılıkların Tersine Çevrilmesi) ve Birim Testi (Unit Test) Kolaylığı
Yüksek seviyeli iş mantığımız (örneğin Üretim Takip Yazılımı içindeki OEE ve darboğaz hesaplama motoru), verinin PLC’den Modbus ile mi, OPC UA ile mi yoksa SQL tablosundan mı geldiğini bilmemelidir. İş katmanı yalnızca ITezgahTelemetriOkuyucu soyut arayüzünü (Interface) tanır.
Bu mimarinin sağladığı en büyük mühendislik gücü Test Edilebilirlik (Testability) özelliğidir: Gerçek fabrikadaki CNC tezgahına veya canlı SQL veritabanına bağlanmadan, SahteTelemetriOkuyucu (Mock/Stub) nesnesi vererek binlerce karmaşık üretim ve maliyet senaryosunu saniyeler içinde otomatik birim testleriyle (Automated Unit Tests) doğrulayabiliriz.
Clean Code (Temiz Kod) İçin Saha Mühendisliği Kuralları
SOLID prensipleri yazılımın makro mimarisini korurken, Clean Code kuralları satır ve fonksiyon bazındaki mikro okunabilirliği güvenceye alır:
1. Niyetini Belli Eden Anlamlı İsimlendirme
Kod yazılırken harcanan sürenin 10 katı, o kodu okurken harcanır. Değişken ve fonksiyon isimleri “Neden var?”, “Ne iş yapıyor?” ve “Nasıl kullanılır?” sorularına yorum satırına ihtiyaç duymadan yanıt vermelidir:
- Kötü İsimlendirme:
int d; // geçen gün sayısıveyafunction proc(a, b) - Temiz İsimlendirme:
int siparisinGecikmeGunSayisi;veyafunction netKantarAgirliginiHesapla(brutKg: number, daraKg: number)
2. Sihirli Sayılardan (Magic Numbers) ve Bayrak Parametrelerinden Kaçınma
Kodun ortasında duran if (durum === 4) veya tutar * 0.20 gibi sabit değerler (Magic Numbers) birkaç ay sonra kimse tarafından anlaşılamaz. Bunlar mutlaka SiparisDurumu.KaliteKontrolRed veya VARSAYILAN_KDV_ORANI gibi anlamlı sabitlere (Enum / Constant) dönüştürülmelidir. Benzer şekilde bir fonksiyona raporOlustur(true, false) gibi boolean bayraklar göndermek yerine ozetRaporOlustur() ve detayliMaliyetRaporuOlustur() şeklinde iki net fonksiyon yazılmalıdır.
3. Erken Dönüş (Guard Clauses) ile Ok Başı Koddan (Arrow Anti-Pattern) Kurtulma
İç içe geçmiş 5-6 katmanlı if / else blokları kodun sağa doğru ok gibi uzamasına ve bilişsel yükün (Cognitive Complexity) artmasına neden olur. Bunun yerine ön koşullar fonksiyonun en başında kontrol edilip hata veya istisna durumunda erken çıkış (return / throw) yapılmalı, ana başarı akışı (Happy Path) en dış girintide düz ve temiz akmalıdır.
Bakımı kolay, dokümantasyonu tam, otomatik testlerle korunan ve 10 yıl sonra bile ilk günkü çevikliğiyle büyüyebilen kurumsal yazılım projeleri için Özel Yazılım Geliştirme sayfamızı inceleyebilirsiniz.
Sıkça Sorulan Sorular
Clean Code ve SOLID kurallarına uymak projenin ilk teslim süresini uzatır mı?
İlk 1-2 haftalık kodlama aşamasında doğrudan spagetti kod yazmak daha hızlıymış gibi bir yanılsama yaratır; ancak projenin henüz 2. ayından itibaren spagetti kodda ortaya çıkan hataları ayıklamak (Debugging) gelişimi durma noktasına getirir. Clean Code ve SOLID mimarisiyle başlayan projeler ise ilk aydan itibaren sabit ve öngörülebilir bir hızda ilerler, proje toplam teslim süresini kısaltır ve canlıya geçiş sonrası bakım maliyetini %70’e varan oranda düşürür.
Mevcut çalışan ama çok karmaşık (Legacy) bir kurumsal yazılımı tamamen çöpe atmadan temiz koda dönüştürmek mümkün müdür?
Evet, buna yazılım mühendisliğinde “Strangler Fig Pattern” (Boğucu İncir Deseni) ve kademeli Refactoring (Yeniden Yapılandırma) denir. Mevcut çalışan sistemi bir gecede silip sıfırdan yazmak büyük operasyonel risk taşır. Bunun yerine önce kritik iş süreçlerinin etrafına güvenlik ağları (karakterizasyon testleri) örülür, ardından en çok arıza çıkaran modüllerden başlanarak adım adım temiz arayüzler arkasına taşınır ve eski spagetti modüller kesintisiz biçimde emekliye ayrılır.
Çok fazla arayüz (Interface) ve küçük sınıf oluşturmak yazılımın çalışma hızını (CPU/RAM performansını) düşürür mü?
Modern derleyiciler (C# .NET JIT, TypeScript/V8, Java JVM, Go, Rust) küçük fonksiyon çağrılarını derleme ve çalışma zamanında otomatik olarak satır içine gömer (Method Inlining) ve optimize eder. Dolayısıyla kodun okunabilir olması için bölünen sınıfların çalışma zamanı performansına etkisi nanosaniye düzeyindedir ve sıfıra yakındır. Gerçek performans darboğazları temiz koddan değil, yanlış tasarlanmış veritabanı sorgularından (N+1 problemi) ve senkron I/O blokajlarından kaynaklanır.