Elif Çavent
Yazılım Geliştirme Uzmanı

Kod Kalite Metrikleri

Kod Kalite Metrikleri

İçindekiler

  1. Kod Kalitesi Nedir?
  2. Kod Kalitesi Neden Önemlidir?
  3. Kod Kalitesini Ölçen Metrikler ve Gruplandırma
    1. Karmaşıklık (Complexity)
    2. Kapsama (Coverage)
    3. Tekrarlar (Duplications)
    4. Bakım Kolaylığı (Maintainability)
    5. Güvenilirlik (Reliability)
    6. Güvenlik (Security)
  4. Sonuç

Kod Kalitesi Nedir?

Kodlama, doğası gereği esnek bir süreçtir ve benzer problemleri aynı programlama dilinde farklı yöntemlerle çözme imkânı sunar. Sonuçlar birbirine yakın olsa da kod sürdürülebilirlik, etkinlik, güvenilirlik ve kullanılabilirlik gibi önemli faktörler açısından farklı niteliklere sahip olabilir.

Bu farklılıklar, yazılımın başarısını doğrudan etkileyebilir. Yüksek kaliteli kod üretmek hem son kullanıcı deneyimi hem de geliştirme ekipleri için son derece önemlidir. Kod kalitesini ölçmek, birçok farklı faktörün bir araya gelmesi nedeniyle zordur. İyi bir kod, genellikle okunabilirlik, bakım kolaylığı, performans gibi niteliklere sahip olmalıdır. Ancak bu faktörlerin her biri ölçülebilirken, hepsinin nasıl birleştirileceği konusunda net bir standart yoktur. Bu nedenle, kod kalitesini değerlendirmek için nitel (qualitative) ve nicel (quantitative) yaklaşımlar birlikte kullanılabilir.

  • Nitel Yöntemler: Kodun anlaşılabilirliği, yazılımın mimarisinin ne kadar uygun olduğu, kullanılan yazılım tasarım kalıplarının doğruluğu gibi faktörler genellikle nitel gözlemlerle değerlendirilir. Örneğin,uzman bir geliştirici, daha az deneyime sahip bir geliştiricinin çalışmasını kontrol edebilir ve çalışmasına ilişkin yorum yapabilir.
  • Nicel Yöntemler: Kodun doğruluğu ve verimliliği, daha somut ölçütlerle değerlendirilebilir. Bu tür ölçümler, test kapsamı, hata sayısı, performans gibi sayısal verilerle yapılır. Örneğin,1000 saat boyunca çalışan bir yazılım uygulamasının kodunda yakalanan hata sayısı, yazılımın ne kadar güvenilir olduğunu gösteren önemli bir nicel ölçüttür. Ayrıca, bir yazılımın işlem süreleri, bellek kullanımı gibi performans metrikleri de nicel değerlendirmeye girer ve yazılımın etkinliğini (efficiency) gösterir.

Tüm bu ölçümleri gerçekleştirmek, yalnızca manuel incelemelerle değil, aynı zamanda statik kod analiz araçlarıyla da desteklenmelidir. Statik analiz araçları, kodun çeşitli yönlerini otomatik olarak kontrol eder, hataları ve potansiyel sorunları erken tespit eder. Bu araçlar, yazılımın sürdürülebilirliğini artırır, performans iyileştirmeleri önerir ve yazılım geliştirme sürecinde kaliteyi güvence altına alır.

SonarQube gibi araçlar, güvenlik açıklarını, kod hatalarını ve stil uyumsuzluklarını analiz ederek yazılımın daha güvenli ve sürdürülebilir olmasını sağlar. ESLint ve Checkstyle gibi araçlar, JavaScript ve Java projelerinde kodun uyumluluğunu kontrol ederken, PMD ve FindBugs gibi araçlar performans iyileştirmeleri ve hata tespiti konusunda yardımcı olur.

Bu tür araçlar, yazılım projelerinin kalitesini ölçmek ve iyileştirmek için güçlüdür ve hem nitel hem de nicel analizler ile birleştiğinde yazılımın başarısını doğrudan etkileyebilir. Bu nedenle, yüksek kaliteli yazılım geliştirmek isteyen ekiplerin bu araçlardan faydalanması büyük önem taşır.

Kod Kalitesi Neden Önemlidir?

Bir yazılımın başarısını belirleyen temel unsurlardan bir tanesi de kod kalitesidir. Sadece çalışan bir kod yazmak yeterli değildir; yazılan kodun okunabilir, sürdürülebilir ve genişletilebilir olması, uzun vadede büyük avantaj sağlar. Kod kalitesinin önemi birkaç maddeyle detaylandırılabilir:

  • Hata Oranını Azaltır
    Kaliteli kod, olabilecek hataları minimuma indirir. Bu sayede yazılımın güvenilirliği artar ve kullanıcıların sistemden memnuniyeti doğru orantılı olarak artmış olur. Örnek verilecek olursa, bir ödeme sisteminde, kullanıcı ödeme işlemi gerçekleştirdiğinde ödeme tutarının yanlış hesaplanması gibi hatalar yazılımın güvenilirliğini ciddi şekilde etkiler. Kaliteli kod ile hataların önceden tespit edilmesi ve çözülmesi, böyle bir güvenlik ve hata riskini azaltır.

  • Ekip Çalışmasına Destek Verir
    Standart hale getirilmiş bir kod, ekip üyeleri arasındaki iş birliğini kolaylaştırır. Ekibe yeni katılan geliştiricilerin adaptasyon sürecini hızlandırır ve basitleştirir. Mesela, bir ekip belirli bir stil kılavuzunu (belirli değişken adlandırma ve kod bloklarının düzeni gibi) takip eder. Bu durumda, yeni bir geliştirici, projenin kod yapısına hızlıca adapte olabilir ve iş birliği süreci verimli hale gelir. Aksi takdirde, her geliştiricinin farklı yazım tarzları kullanması, yeni katılan kişilerin zorlanmasına neden olabilir.

  • Performansı Optimize Eder
    İyi tasarlanmış bir kod, sistem kaynaklarını etkin olarak kullanır ve yazılımınızın hızını artırır. Örneğin, bir e-ticaret sitesinde, ürünleri arama ve sıralama işlemleri kullanıcı deneyimini doğrudan etkiler. Kaliteli bir kod ile veri tabanı sorguları optimize edilerek, sayfa yükleme süreleri kısaltılır ve bu da kullanıcıların daha hızlı ve verimli bir alışveriş deneyimi yaşamasını sağlar.

  • Uzun Vadeli Sürdürülebilirlik Sağlar
    Kaliteli olmayan bir kod, zaman geçtikçe teknik borç birikimine yol açar. Teknik borç, yazılım geliştirme sürecinde kısa vadeli çözümler veya düşük kaliteli kod yazımı nedeniyle ortaya çıkan ve ileride daha fazla efor ve maliyet gerektiren sorunları ifade eder. Bu durum projeyi dar boğaza sürükleyebilir ve maliyet artışı ile yönetim zorluklarına sebep olabilir. Bir örnekle açıklanırsa, başlangıçta hızlıca yazılmış ve teknik borç biriktirilmiş bir mobil uygulama, zamanla performans sorunları ve uyumluluk problemleri ortaya çıkarabilir. Bu durumda, uygulamanın yeni özellikler eklenmesi veya bakım yapılması daha zor ve maliyetli hale gelir. Ancak, iyi yapılandırılmış ve kaliteli kod, bu sorunların önüne geçer ve yazılımın uzun vadeli gelişimine olanak tanır.

Kod Kalitesini Ölçen Metrikler ve Gruplandırma

Kod kalitesini değerlendirmenin farklı yöntemleri ve kriterleri bulunmaktadır. Bu süreçte, yazılımın çeşitli yönlerini analiz edebilmek için metrikler kullanılır. Kod kalitesine dair metrikler, belirli kategoriler altında toplanarak yazılımın güçlü ve zayıf yanlarını daha sistematik bir şekilde değerlendirmeyi mümkün kılar. Bu kategoriler, yazılımın okunabilirliği, karmaşıklığı, test kapsamı ve performansı gibi farklı alanları kapsar ve yazılımın genel durumunu anlamak için önemli bir çerçeve sunar. Aşağıda bu kategorilerden bazılarına yer verilmiştir.

1. Karmaşıklık (Complexity)

Karmaşıklık, kodun anlaşılabilirliğini ve yönetilebilirliğini ölçer. Yazılımın yapısal ve mantıksal zorluk düzeyini ifade eder. Kodun ne kadar karmaşık olduğu, yazılımın bakım maliyetlerini, test edilebilirliğini ve geliştirme sürecini doğrudan etkileyen bir faktördür. Cyclomatic Complexity (Döngüsel Karmaşıklık) gibi metrikler, bir yazılımın karmaşıklığını sayısallaştırmada sıkça kullanılır. Karmaşıklığın azaltılması için birkaç ipucu verilecek olursa:

  • Kodun Parçalanması
    Uzun fonksiyonlar ve metotlar, anlaşılması ve yönetilmesi zor olan kod yapıları oluşturur. Bu nedenle, kısa ve anlamlı parçalara bölünmüş kodlar yazılmalıdır.

    Tek bir büyük fonksiyon:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    
      data class Ogrenci(
          val ad: String,
          val notlar: List<Double>,
          var ortalama: Double = 0.0,
          var gectiMi: Boolean = false
      )
      fun hesaplaVeYazdir(ogrenciler: List<Ogrenci>) {
      // Notları hesapla
      ogrenciler.forEach { ogrenci ->
          ogrenci.ortalama = ogrenci.notlar.sum() / ogrenci.notlar.size
      }
    
      // Geçme durumunu belirle
      ogrenciler.forEach { ogrenci ->
          ogrenci.gectiMi = ogrenci.ortalama >= 50
      }
    
      // Raporu yazdır
      ogrenciler.forEach { ogrenci ->
          println("${ogrenci.ad}: Ortalama=${ogrenci.ortalama}, Geçti mi=${ogrenci.gectiMi}")
      }
      }
    

    Kodun parçalara ayrılmış hali:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    
      data class Ogrenci(
          val ad: String,
          val notlar: List<Double>,
          var ortalama: Double = 0.0,
          var gectiMi: Boolean = false
      )
      
      fun hesaplaVeYazdir(ogrenciler: List<Ogrenci>) {
      notlariHesapla(ogrenciler)
      gecmeDurumlariniBelirle(ogrenciler)
      raporYazdir(ogrenciler)
      }
    
      fun notlariHesapla(ogrenciler: List<Ogrenci>) {
      ogrenciler.forEach { ogrenci ->
          ogrenci.ortalama = ogrenci.notlar.sum() / ogrenci.notlar.size
          }
      }
    
      fun gecmeDurumlariniBelirle(ogrenciler: List<Ogrenci>) {
      ogrenciler.forEach { ogrenci ->
          ogrenci.gectiMi = ogrenci.ortalama >= 50
          }
      }
    
      fun raporYazdir(ogrenciler: List<Ogrenci>) {
      ogrenciler.forEach { ogrenci ->
          println("${ogrenci.ad}: Ortalama=${ogrenci.ortalama}, Geçti mi=${ogrenci.gectiMi}")
          }
      }
    
  • Tek Sorumluluk Prensibi (SRP)
    Tek Sorumluluk Prensibi, SOLID prensiplerinden biridir ve her bir sınıf, metot veya modülün yalnızca tek bir sorumluluğa sahip olması gerektiğini belirtir. Başka bir deyişle, bir sınıf veya metot yalnızca bir işlevi yerine getirmeli ve bu işlevin değişmesi gerektiğinde, sınıfta veya metotta yapılacak değişiklikler yalnızca bu işlevle ilgili olmalıdır.

  • Kod Akışını Basitleştirme
    Fazla dallanmadan kaçınılmalıdır.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
      fun islemiGerceklestir(kullaniciAktif: Boolean, adminMi: Boolean, yeterliBakiye: Boolean) {
      if (kullaniciAktif) {
          if (adminMi) {
              if (yeterliBakiye) {
                  println("İşlem başarılı!")
              } else {
                  println("Yetersiz bakiye.")
              }
          } else {
              println("Yetki yok.")
          }
      } else {
          println("Kullanıcı aktif değil.")
        }
    }
    

    Aşırı iç içe geçmiş if yapıları, kodun okunabilirliğini düşürür. Her koşul bir diğerinin içine gömülü olduğu için akışı takip etmek zorlaşır. Bu yapı, bakım yapmayı ve yeni bir kontrol eklemeyi de zorlaştırır. Aşağıdaki çözüm, erken dönüş prensibini kullanarak her koşulda hatalı durumu mümkün olduğunca erken ele alır. Bu yaklaşım, kodun okunabilirliğini artırır ve iç içe dallanmaları önler.

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    
    fun islemiGerceklestir(kullaniciAktif: Boolean, adminMi: Boolean, yeterliBakiye: Boolean) {
        if (!kullaniciAktif) {
            println("Kullanıcı aktif değil.")
            return
        }
    
        if (!adminMi) {
            println("Yetki yok.")
            return
        }
    
        if (!yeterliBakiye) {
            println("Yetersiz bakiye.")
            return
        }
    
        println("İşlem başarılı!")
    }
    
  • Tekrar Eden Kodları Ortadan Kaldırma
    Kodun farklı yerlerinde aynı işlemler yapılmamalıdır. Bunun yerine yeniden kullanılabilir yapılar tercih edilmelidir.

2. Kapsama (Coverage)

Kapsama, yazılım test süreçlerinde kodun ne kadarının test edildiğini ölçen bir metriktir. Bu metrik, yazılımdaki hataların ne kadar erken tespit edilebileceğini ve testlerin kodu ne kadar doğruladığını analiz etmek için kullanılır. Test kapsamı ne kadar yüksekse, sistemin güvenilirliği o kadar artar. Ancak, yüksek kapsama oranı tek başına kod kalitesi garantisi değildir; önemli olan doğru senaryoların test edilmesidir.

Kapsamın önemi birkaç maddeyle açıklanabilir:

  • Hata Tespiti
    Yüksek test kapsamı, yazılımın büyük bir kısmının test edildiği anlamına gelir. Bu, özellikle hataların canlı ortamda tespit edilmesi yerine geliştirme veya test aşamasında bulunmasını sağlar. Hataların erken tespiti, maliyetin düşürülmesinde ve geliştirme sürecinin hızlanmasında büyük rol oynar. Örneğin, bir e-ticaret platformunda, kullanıcıların sepetlerine ürün ekleyebilmesi kritik bir işlevdir. Bu işlevi test eden senaryolardan bazıları:
    • Sepete ürün ekleme ve toplam fiyatın doğru hesaplanması.
    • Stokta olmayan bir ürünü sepete eklemeye çalışıldığında uygun bir hata mesajı verilmesi.
    • Aynı ürün birden fazla kez eklendiğinde toplam adet ve fiyatın doğru şekilde güncellenmesi.

    Eğer bu senaryolar test edilmezse, canlı ortamda müşterilerin yanlış fiyatlar görmesine veya ürün ekleme işleminin başarısız olmasına neden olabilecek hatalar ortaya çıkabilir.

  • Güvenilirlik
    Testlerle doğrulanan kod, canlı ortama çıktığında daha güvenilir hale gelir ve kullanıcı hataları minimuma indirilir. Örnek olarak, bir bankacılık uygulamasında, para transferi işlemi düşünüldüğünde transfer sırasında test edilmesi gereken bazı senaryolar:
    • Hesap bakiyesi transfer için yeterli değilse işlemin reddedilmesi.
    • Kullanıcının geçersiz bir hesap numarası girmesi durumunda bir uyarı verilmesi.
    • İşlem sırasında ağ bağlantısının kopması durumunda, işlem tekrarının engellenmesi.

    Bu gibi senaryolar doğru bir şekilde test edilirse, kullanıcıların finansal işlemleri sırasında karşılaşabileceği kritik hatalar önlenebilir ve sistemin güvenilirliği artırılabilir.

  • Bakım Kolaylığı
    Kapsamlı testler, gelecekte yapılacak kod değişikliklerinde mevcut işlevselliğin bozulmadığından emin olunmasını sağlar. Bu, yazılımın daha sürdürülebilir ve yönetilebilir bir yapıya sahip olmasını sağlar. Bir örnek verilirse, bir müşteri yönetim sistemine yeni bir özellik eklendiği varsayılsın: müşteri adres bilgilerinin otomatik doğrulanması. Bu yeni özellik eklenirken mevcut özelliklerin (müşterilerin manuel olarak adres bilgisi kaydedebilmesi gibi) zarar görmemesi gerekir. Eğer kapsamlı bir test seti mevcutsa, yeni bir özelliğin eklenmesi veya mevcut bir özelliğin değiştirilmesi sonrasında tüm işlevlerin hala çalıştığından emin olunabilir. Aksi takdirde, kullanıcılar adres kayıtlarında beklenmedik sorunlarla karşılaşabilir.

  • Kalite Kontrolü
    Test kapsamı, yazılım kalitesinin bir göstergesidir, ancak kapsamı artırmak için sadece kapsamı doldurmaya yönelik testler yazmak yanıltıcı olabilir. Gerçek kaliteyi artırmak için doğru senaryolar test edilmelidir. Testlerin işlevsel ve senaryo odaklı olması, gerçek anlamda kaliteyi artırır. Bir sistemde, %90 kapsama oranına ulaşmak için aşağıdaki iki test stratejisi en basit halleriyle karşılaştırılabilir:

    1.Sadece kodun çalıştığını doğrulayan, ancak iş mantığını test etmeyen testler yazmak. Örneğin:

1
2
3
4
5
  @Test
  public void testAddition() {
    Calculator calculator = new Calculator();
    calculator.add(1, 2); // Sadece çalışıyor ama sonucu doğrulamıyor
  }

2.Gerçek senaryoları test eden ve beklenen sonuçları doğrulayan testler yazmak. Örneğin:

1
2
3
4
5
6
  @Test
  public void testAddition() {
    Calculator calculator = new Calculator();
    int result = calculator.add(1, 2);
    assertEquals(3, result); // Sonucu doğruluyor
  }

Test kapsamını ölçmek, yazılım projelerinde kod kalitesini artırmak için kritik bir adımdır. En bilinen araçlardan JaCoCo, Java projeleri için kapsamlı bir çözüm sunarak sınıf, metot ve satır bazında detaylı raporlar oluşturur. Benzer şekilde, IstanbulJS, JavaScript ve TypeScript projelerinde kapsam ölçümü için etkili bir araçtır ve Jest gibi test çatılarıyla kolayca entegre olur. Python geliştiricileri için popüler olan Coverage.py, test edilen kodun hangi bölümlerinin çalıştırıldığını tespit eder ve kullanıcı dostu HTML raporları sağlar. Cobertura ise eski Java projelerinde sıkça tercih edilen, temel ancak etkili bir kapsam ölçüm aracıdır. Ayrıca, LCOV gibi araçlar C ve C++ projelerinde kapsam ölçümü için güçlü bir seçenek sunar ve gcov ile entegre çalışır. Bu araçların sunduğu metrikler, yazılım ekiplerinin kodun hangi bölümlerinin yeterince test edilmediğini belirlemesine olanak tanır ve bu sayede daha sağlam uygulamalar geliştirilmesine katkıda bulunur.

3. Tekrarlar (Duplications)

Tekrarlar, bir yazılımın kod tabanında aynı veya benzer kod parçalarının birden fazla yerde bulunmasıdır. Kodun tekrarlı olması, bakım maliyetlerini artırır ve hata yapma riskini yükseltir. Tekrar eden kodlar, DRY (Don’t Repeat Yourself) prensibine aykırıdır ve uzun vadede geliştirme süreçlerinde aksaklıklara sebep verir ve sürdürülebilirliği azaltır.

Tekrarları azaltmak için birkaç iyi pratik:

  • DRY Prensibini Uygulamak
    DRY, yazılım geliştirmede temel bir prensiptir. Tekrar eden kod parçalarını tek bir yerde toplamak ve bu kodu farklı yerlerde kullanmak hem kodun okunabilirliğini hem de sürdürülebilirliğini artırır.

  • Design Pattern’lar Kullanmak
    Benzer işlemler için design pattern’lar (tasarım desenleri) kullanılarak kodun daha modüler ve esnek olması sağlanabilir. Örneğin, bir içecek hazırlama süreci düşünülsün. Bir kafede farklı içeceklerin hazırlanma süreci genellikle şu adımlardan oluşur:
    • Suyu kaynat.
    • Malzemeleri ekle (özelleştirilebilir).
    • Bardağa dök. Ortak işlemler bir şablon haline getirilip, farklı içecek türleri için özelleştirilebilir.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    
    abstract class DrinkTemplate {
    
      // Template Method
      public final void prepareDrink() {
          boilWater();          // Ortak işlem
          addIngredients();     // Özelleştirilebilir işlem
          pourIntoCup();        // Ortak işlem
      }
    
      private void boilWater() {
          System.out.println("Boiling water...");
      }
    
      private void pourIntoCup() {
          System.out.println("Pouring into cup...");
      }
    
      protected abstract void addIngredients();  // Alt sınıflar dolduracak
    }
    
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    
    // Çay Hazırlama
    class Tea extends DrinkTemplate {
      @Override
      protected void addIngredients() {
          System.out.println("Adding tea leaves...");
      }
    }
    
    // Kahve Hazırlama
    class Coffee extends DrinkTemplate {
      @Override
      protected void addIngredients() {
          System.out.println("Adding coffee powder...");
      }
    }
    
  • Kod İyileştirme (Refactoring)
    Kod iyileştirme, mevcut kodun işlevselliğini bozmadan daha okunabilir ve sürdürülebilir hale getirilmesi işlemidir. Kod tekrarlarını tespit ederek, gereksiz olanları temizlemek için düzenli aralıklarla kod iyileştirme yapmak gerekir.

  • Statik Analiz Araçları Kullanmak
    Tekrar eden kodları otomatik olarak tespit etmek için SonarQube, Checkstyle, Programming Mistake Detector , FindBugs gibi statik analiz araçları kullanılabilir.
    • SonarQube
      • Açıklama: Geniş kapsamlı bir statik analiz aracıdır. Kod kalitesini artırmaya odaklanır ve “Kod kokuları (Code Smell)”, güvenlik açıkları ve tekrar eden kod bloklarını tespit eder.
      • Özellikler:
        • Kod tekrarını (Duplications) tespit eder.
        • 30’dan fazla dilde destek sunar (Java, JavaScript, Python, vb.).
        • CI/CD (sürekli entegrasyon, sürekli teslim ve dağıtım) süreçlerine kolayca entegre edilebilir.
    • Checkstyle
      • Açıklama:
        Java projeleri için kodlama standartlarını denetleyen bir araçtır. Kodun belirli stil kurallarına uygun olup olmadığını kontrol eder.
      • Özellikler:
        • Kod biçimlendirme ve adlandırma kurallarını kontrol eder.
        • Özelleştirilebilir kurallar sunar.
        • IDE (Entegre Geliştirme Ortamı)’ler ve build araçları (Maven, Gradle) ile entegre çalışır.
    • Programming Mistake Detector
      • Açıklama:
        Java ve Apex gibi diller için statik analiz sağlayan bir araçtır. Kod tekrarını, gereksiz nesne oluşturma gibi sorunları tespit eder.
      • Özellikler:
        • Tekrar eden kod bloklarını (Code Clones) bulur.
        • Performans ve güvenlik sorunlarını analiz eder.
        • Maven ve Gradle ile kolayca entegre edilebilir.
    • FindBugs (SpotBugs)
      • Açıklama:
        Java projelerinde hataları analiz etmek ve kodun davranışsal sorunlarını bulmak için kullanılan bir araçtır. FindBugs’ın devamı olarak SpotBugs geliştirilmektedir.
      • Özellikler:
        • NPE (Null Pointer Exception) gibi potansiyel hataları tespit eder.
        • Kodun mantıksal sorunlarını analiz eder.
        • IDE ve CI/CD entegrasyonları destekler.

4. Bakım Kolaylığı (Maintainability)

Bakım kolaylığı, kodun ne kadar kolay güncellenebileceğini, hataların düzeltilebileceğini ve yeni özelliklerin eklenebileceğini gösterir. Bir yazılımın sürdürülebilirliği doğrudan bakım kolaylığına bağlıdır. Kodun zamanla büyüdüğü ve karmaşıklaştığı göz önüne alındığında, bakımı zor olan yazılımlar, projelerin ilerleyişini yavaşlatır ve teknik borcu artırır.

Bakım kolaylığını etkileyen unsurlar:

  • Kodun Anlaşılabilirliği
    Kod okunabilir, net ve anlamlı olmalıdır. Anlamlı değişken, metot ve sınıf isimleri tercih edilmeli, gereksiz karmaşıklıktan kaçınılmalıdır. Belirli bir standartta daha anlaşılır bir kod elde etmek için kurallar belirlenebilir. Sonar’dan örnek bir kural:

    Local variable and method parameter names should comply with a naming convention = Yerel değişkenlerin ve metot parametrelerinin isimlendirilmesi, kodun amacını net bir şekilde iletmek ve bakımını kolaylaştırmak için tutarlı olmalıdır. Bu sorunu çözmek için, yerel değişkeni veya metot parametresini projenin adlandırma kurallarına uygun olacak şekilde yeniden adlandırmak gerekir.

    1
    2
    3
    4
    
      public void process(int x, String y) {
          int z = x + 10;
          System.out.println(y + z);
      }
    

    Doğru isimlendirme;

    1
    2
    3
    4
    
      public void process(int inputValue, String message) {
        int result = inputValue + 10;
        System.out.println(message + result);
      }
    
  • Tek Sorumluluk Prensibi (SRP)
    Her sınıf veya metodun yalnızca tek bir sorumluluğa sahip olması, kodun modüler yapıda olmasını sağlar ve değişikliklerin etkisini sınırlar. Sonar’dan örnek bir kural:

    Methods should not perform too many tasks = Sonar, bir metodu Beyin Metodu (Brain Method) olarak değerlendirdiğinde bu sorun ortaya çıkar. Beyin Metodu, sahibinin sınıfındaki mantığı merkezileştirme eğiliminde olan ve genellikle çok fazla işlem gerçekleştiren bir metottur. Bu tür metotlar, çok sayıda koşul kontrolü yapma, birçok değişken kullanma gibi özellikler taşır ve genellikle anlaşılması, bakımı ve yeniden kullanılması oldukça zordur. Bu metotlar; yüksek satır sayısı (LOC), yüksek döngüsel karmaşıklık (Cyclomatic Complexity), yüksek bilişsel karmaşıklık (Cognitive Complexity) ve kullanılan çok sayıda değişken ile karakterize edilir.

    Potansiyel Etkileri: Beyin Metotları, derin iç içe geçmiş yapılar içerdiğinden testlerle kapsamaları zordur ve genellikle hata yapmaya yatkındırlar, çünkü çok sayıda yerel değişken tanımlarlar. Bu tür metotlar, metodu yazan kişi dışında başka birinin okumasını ve anlamasını zorlaştırır; bu da bakımı ve olası hataların giderilmesini güçleştirir. Ayrıca, bu metotlar genellikle yeniden kullanılamaz durumda oldukları için kod tekrarı riskini de artırır.

    Bu nedenlerle, Beyin Metotları yazılım projelerinde teknik borç oluşturur ve uzun vadede projeyi yönetmeyi zorlaştırır.

  • Modülerlik
    Kodun küçük, bağımsız ve yeniden kullanılabilir modüller halinde yazılması bakım kolaylığını artırır. Sonar’dan örnek bir kural:

    Methods should not have too many lines of code = Çok büyük hale gelen bir metot, genellikle birden fazla sorumluluğu bir araya toplama eğilimindedir. Bu tür metotlar, kaçınılmaz olarak anlaması zor ve dolayısıyla bakımı daha zor hale gelir.

    Belirli bir sınırın üzerinde, metodu daha küçük ve iyi tanımlanmış görevlere odaklanan alt metotlara bölmek şiddetle tavsiye edilir. Bu küçük metotlar sadece daha kolay anlaşılır olmakla kalmaz, aynı zamanda muhtemelen daha kolay test edilebilir hale gelir.

  • Code Smell’lerin Tespiti
    Code Smell, bir kod parçasında, yazılımın gelecekteki bakımını zorlaştırabilecek veya geliştirme süreçlerini karmaşıklaştırabilecek potansiyel sorunları ifade eder. Bu, doğrudan bir hata olmasa da kodun tasarımında veya yapısında bulunan kötü pratiklerin bir göstergesidir. Code Smell’ler genellikle yazılımın kalitesini düşürür, değişiklik yapmayı zorlaştırır ve teknik borcun birikmesine yol açar.

5. Güvenilirlik (Reliability)

Güvenilirlik, bir yazılım sisteminin belirlenen koşullar altında tutarlı, doğru ve kesintisiz çalışabilme yeteneğini ifade eder. Güvenilir bir yazılım, kullanıcının beklentilerini karşılar ve beklenmeyen hatalara yol açmaz.

Güvenilirlik neden önemlidir?

  • Kullanıcı Güveni
    Güvenilir yazılımlar, kullanıcıların sisteme olan güvenini artırır.

  • İş Sürekliliği
    Kesintisiz ve hatasız çalışan sistemler, iş süreçlerinin aksamadan devam etmesini sağlar.

  • Bakım Maliyetlerinin Azaltılması
    Hataların sık tekrarlanması, bakım ve onarım maliyetlerini artırır. Güvenilir bir kod tabanı, bu maliyetleri azaltır.

Güvenilirliği artırmak için yöntemler:

  • Kod İncelemesi (Code Review): Kod yazıldıktan sonra, ekip arkadaşlarının kodu incelemesi hataları tespit etme ve kodun kalitesini artırma açısından kritiktir. Bu, yalnızca hataları bulmakla kalmaz, aynı zamanda en iyi uygulamaların kullanılmasını sağlar.
  • Birim Testleri (Unit Tests): Her bir modülün bağımsız olarak doğru çalıştığını doğrulamak için birim testleri yazılmalıdır. Bu, kodun doğru davranış sergilemesini sağlar ve ileride yapılacak değişikliklerde regresyon hatalarını önler.
  • Hata Yönetimi: Uygulamada meydana gelen hataların ele alınması ve loglanması önemlidir. Beklenmeyen durumların uygun bir şekilde yönetilmesi, sistemin kararlılığını artırır.
  • Performans Testleri: Sistem performansının sınırlarını test etmek, özellikle yüksek yük altında güvenilir çalışmasını sağlamak için kritik öneme sahiptir. Örnek verilirse, Apache JMeter veya Gatling gibi araçlarla yük testleri yapılabilir.
  • Statik Kod Analiz Araçlarının Kullanımı: Analiz araçları tarafından tespit edilen hataların düzeltilmesi koddaki olası güvenlik açıklarını ve hataları otomatik azaltır. Bu araçlar sayesinde yazılımın güvenilirliği artırılabilir.

6. Güvenlik (Security)

Güvenlik, yazılımda bulunabilecek potansiyel güvenlik açıklarını tespit eder. Yazılım güvenliği, özellikle kullanıcı verilerinin gizliliği ve bütünlüğü açısından kritik bir unsurdur. Güvenlik sorunları yalnızca teknik borç oluşturmakla kalmaz, aynı zamanda işletmenin finansal kayıplarına ve itibar zedelenmesine neden olabilir.

Güvenliği artırmak için yöntemler:

  1. Statik Kod Analizi
    Güvenlik açıklarını erken aşamada tespit etmek için statik analiz araçları kullanılmalıdır. Örneğin:

    • SonarQube: Kodun güvenlik açıklarını, kod kokularını ve hatalarını analiz eder.
    • Checkmarx: Güvenlik odaklı statik analiz araçlarından biridir.
  2. Penetrasyon Testleri (Penetration Tests)
    Sistemin gerçek saldırılara karşı dayanıklılığını ölçmek için penetrasyon testleri yapılmalıdır. Bu testler, saldırganların izleyebileceği yolları tespit etmek ve savunma mekanizmalarını güçlendirmek için kritik öneme sahiptir.

  3. Güvenli Kodlama Pratikleri
    Güvenliği artırmak için aşağıdaki güvenli kodlama pratikleri uygulanabilir:

    • Bağımlılık Tarama Araçları: Projede kullanılan bağımlılıkların güvenlik açıklarını tespit etmek için aşağıdaki gibi araçlar kullanılabilir:
      • OWASP Dependency-Check: Projede kullanılan bağımlılıkların bilinen güvenlik açıklarına karşı taranmasını sağlar.
      • Snyk: Bağımlılık tarama ve güvenlik açıklarını düzeltme konusunda yardımcı olur.
    • Hata Yönetimi (Exception Handling): Uygulamanın beklenmeyen durumlara karşı dayanıklı olması sağlanmalıdır.
    • Girdi Doğrulama (Input Validation): Kullanıcıdan gelen verilerin doğru ve güvenli bir şekilde işlenmesi gereklidir.
  4. Kimlik Doğrulama ve Yetkilendirme Çözümleri
    Güvenli bir yazılım sistemi oluşturmak için kimlik doğrulama ve yetkilendirme protokollerine dikkat edilmelidir:

    • OAuth2: Kullanıcıların, üçüncü taraf uygulamalara kimlik bilgilerini paylaşmadan erişim izni vermesini sağlar ve API güvenliğinde sıkça kullanılır.
    • Keycloak: OAuth2, OpenID Connect ve SAML protokollerini destekleyen açık kaynaklı bir kimlik ve erişim yönetim aracıdır.
    • Okta: Kurumsal düzeyde kimlik doğrulama ve kullanıcı yönetimi sağlayarak çok faktörlü kimlik doğrulama (MFA) desteği sunar.
    • Firebase Authentication: Sosyal giriş, OAuth2 ve özel kimlik doğrulama mekanizmalarıyla entegre çalışan bir Google hizmetidir.
  5. Bağımlılık Tarama ve Güvenlik Analiz Araçları
    Projedeki açık kaynak bağımlılıklarını analiz etmek ve güvenlik açıklarını gidermek için araçlar kullanılmalıdır:

    • WhiteSource (Mend): Bağımlılıkların güvenlik açıklarını ve lisans uyumluluğunu kontrol eder.
    • Dependabot: GitHub tarafından sunulan bir araçtır ve proje bağımlılıklarını otomatik olarak güncelleyerek güvenlik açıklarını tespit eder.

Bu değerlendirme süreçlerinde, metrikler buna benzer başlıklar altında gruplandırılabilir ve ihtiyaçlar doğrultusunda grafikler oluşturulabilir. Bu grafiklerden faydalanarak, projenin güçlü ve zayıf yönleri görselleştirebilir, gelişim süreci rahatlıkla takip edilebilir ve daha doğru stratejilerle ilerlenebilir.

SonarQube ve benzeri araçlar, kod kalitesini değerlendirirken çeşitli metrikleri kategorize ederek kolayca anlaşılabilir bir şekilde görselleştirir. Bu kategoriler ve kontrol panelleri hem geliştiricilerin hem de proje yöneticilerinin projedeki mevcut durumu izleyebilmesi için oldukça önemlidir. Bununla birlikte, bu tür araçlarla sunulan standart grafiklerin ötesine geçerek, oluşacak ihtiyaçlara ve proje yapısına uygun özel grafikler oluşturmakta mümkündür.

Bu yöntemi tercih edip SonarQube gibi araçlardan elde edilen verileri analiz ederek gösterge panelini oluşturmakta fayda vardır. Bu sayede, projeye özgü metrikleri daha anlamlı bir şekilde görselleştirme ve odaklanılması gereken noktaları daha net belirleme şansı elde edilebilir. Özellikle, teknik borç, test kapsamı, karmaşıklık ve güvenlik gibi öncelikli alanlarda, geliştirilebilecek grafiklerle projenin güçlü ve zayıf yönleri daha etkili bir şekilde takip edilebilir. Bu yaklaşım, proje yönetiminde daha bilinçli kararlar alınmasına ve yazılım kalitesini sürekli iyileştirilmesine önemli katkılar sağlayacaktır.

Sonuç

Kod kalitesi, modern yazılım geliştirme süreçlerinde başarıyı doğrudan etkileyen temel bir unsurdur. Bu çalışmada ele alınan karmaşıklık, kapsama, tekrarlar, bakım kolaylığı, güvenilirlik ve güvenlik gibi metrikler, yazılımın sürdürülebilirliğini, performansını ve güvenilirliğini artırmak için kritik ölçütler olarak öne çıkmaktadır. Yüksek kaliteli bir kod tabanı, sadece hatasız ve optimize bir şekilde çalışmakla kalmaz, aynı zamanda bakım maliyetlerini de azaltır. Tüm bunları CI/CD süreçlerine entegre ederek, ekipler arası iş birliği kolaylaştırılabilir ve uzun vadede teknik borç riski minimuma indirilebilir. Unutulmamalıdır ki kod kalitesi bir özellik değil, yazılımın başarısını garanti altına alan bir gerekliliktir. Geliştiricilerin bu prensipleri benimsemesi, kullanıcı memnuniyetini artırarak yazılım projelerini bir adım öne taşıyacaktır.


Yazımızın teknik gözden geçirmesi için Ahmet Özel’e ve Gökçenur Çınar’a, editör desteği için ise Beyza Şenel’e teşekkür ederiz.

Kaynakça

[1] R. C. Martin, Clean Code: A Handbook of Agile Software Craftsmanship, Prentice Hall, 2008.

[2] M. Fowler, Refactoring: Improving the Design of Existing Code, Addison-Wesley, 1999.

[3] M. Fowler, “Martin Fowler’ın Yazıları,” [Çevrimiçi]. Erişim adresi: https://martinfowler.com/ (Erişim Zamanı: Ocak, 25, 2025).

[4] Google, “Google Engineering Practices,” [Çevrimiçi]. Erişim adresi: https://google.github.io/eng-practices/ (Erişim Zamanı: Ocak, 29, 2025).

[5] SonarSource, “SonarQube Resmi Belgeleri,” [Çevrimiçi]. Erişim adresi: https://docs.sonarqube.org/ (Erişim Zamanı: Ocak, 23, 2025).

[6] ACM, “ACM Digital Library,” [Çevrimiçi]. Erişim adresi: https://dl.acm.org/ (Erişim Zamanı: Ocak, 29, 2025).

[7] Amazon Web Services, “Code Quality,” [Çevrimiçi]. Erişim adresi: https://aws.amazon.com/what-is/code-quality/ (Erişim Zamanı: Ocak, 29, 2025).

[8] JaCoCo, “JaCoCo Documentation,” [Çevrimiçi]. Erişim adresi: https://www.eclemma.org/jacoco/ (Erişim Zamanı: Ocak, 24, 2025).

[9] IstanbulJS, “IstanbulJS GitHub,” [Çevrimiçi]. Erişim adresi: https://github.com/istanbuljs/istanbuljs (Erişim Zamanı: Ocak, 26, 2025).

[10] Coverage.py, “Coverage.py Documentation,” [Çevrimiçi]. Erişim adresi: https://coverage.readthedocs.io/ (Erişim Zamanı: Ocak, 29, 2025).

[11] Linux Test Project, “LCOV GitHub,” [Çevrimiçi]. Erişim adresi: https://github.com/linux-test-project/lcov (Erişim Zamanı: Ocak, 12, 2025).

[12] Wikipedia, “Don’t Repeat Yourself,” [Çevrimiçi]. Erişim adresi: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself (Erişim Zamanı: Ocak, 29, 2025).

[13] SonarSource, “SonarQube Documentation,” [Çevrimiçi]. Erişim adresi: https://docs.sonarqube.org/ (Erişim Zamanı: Ocak, 29, 2025).

[14] Checkstyle, “Checkstyle GitHub,” [Çevrimiçi]. Erişim adresi: https://github.com/checkstyle/checkstyle (Erişim Zamanı: Ocak, 29, 2025).

[15] PMD, “PMD Documentation,” [Çevrimiçi]. Erişim adresi: https://pmd.github.io/ (Erişim Zamanı: Ocak, 29, 2025).

[16] SpotBugs, “SpotBugs Documentation,” [Çevrimiçi]. Erişim adresi: https://spotbugs.github.io/ (Erişim Zamanı: Ocak, 29, 2025).

[17] SonarSource, “Sonar Kuralları,” [Çevrimiçi]. Erişim adresi: https://rules.sonarsource.com/java/ (Erişim Zamanı: Ocak, 29, 2025).