Kodlama Yaparken Dikkat Edilmesi Gerekenler Nelerdir ?

Kodlama Yaparken Dikkat Edilmesi Gerekenler Nelerdir ?

Kodlama Yaparken Dikkat Edilmesi Gerekenler

Sizlerde biliyorsunuz ki sürdürülebilir bir uygulama geliştirebilmek, hem biz yazılımcılar açısından (ileride gelecek olan yeni fonksiyonlar ve bakım işlemleri) hem de business kuralları gereği (müşteri memnuniyeti vb.) olmazsa olmaz bir ihtiyaçtır.

Bunun aksine giden bir yolda ise geliştirmiş yada geliştiriyor olduğumuz uygulama, bir süre hızla gelişen performans gereksinimlarının karşısında ise ayakta durması imkansız bir hale gelmeye mahkum olacaktır. Bu sebeple kodlama yaparken zaman içinde yarışmanın yanı sıra, performans gereksinimlarını da göz ardı etmemeliyiz.

 

Bir çok yazılımcı proje yöneticileri yada product owner’lar tarafından kendilerine verilen fonksiyonları geliştirmeye odaklanmaktadır. Kendilerini bu fonksiyonları geliştirmeye odaklamaları güzel fakat çoğu zaman aslolan dikkat edilmesi ihtiyaç duyulan fonksiyonel olmayan (non-functional requirements) özellikler es geçilmektedir. Üniversitelerin Yazılım Mühendisliği derslerinden hatırlarsanız, Functional ve Non-functional requirement’lar bulunmaktaydı.

nfr-non-functional-cart-before-horse

Non-functional ihtiyaçlar da genellikle:

  • Arayüzler
  • Kullanıcı odaklılığı
  • Güvenlik
  • Kalite
  • Performans
  • Güvence

Gibi soyut niteliklerini belirleyen gereksinimlere dikkat edilmektedir. İşte tam bu noktada bizleri bir çok yazılımcıdan ayıracak olan nokta ise functional özellikleri geliştirirken, non-functional gereksinimlarıda göz ardı etmememiz olacaktır.

1) Bir method sadece bir sorumluluğu yerine getirmelidir

Bu maddede aslında dikkat etmemiz gereken asıl nokta, method özünde Single Responsibility prensibine özen göstermektir. Bu sayede bize kazandıracağı bazı avantajlar ise:

  • Method’un complexity seviyesini azaltmak
  • Hataları azaltıp, tekrar kullanılabilirliği sağlamak
  • Okunabilirliği ve genişletilebilirliği sağlamak
  • Daha tutarlı testler yazılabilir hale getirmek

gibi maddeler olacaktır. Şimdi bir örnek üzerinden giderek mevcut olanı ve olması gerekene bir bakalım.

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
public bool ChangePassword(int userId, string oldPassword, string newPassword)
{
    using (TestContext context = new TestContext())
    {
        var user = context.User.FirstOrDefault(u => u.Id == userId);
        if (user != null && user.Password == oldPassword && newPassword.Length >= 6)
        {
            user.Password = newPassword;
            user.LastUpdatedDate = DateTime.Now;
            context.SaveChanges();
            SmtpClient client = new SmtpClient
            {
                Host = “smtp.gmail.com”,
                Port = 587,
                EnableSsl = true,
                Credentials = new System.Net.NetworkCredential(“id”, “password”)
            };
            MailMessage mailMessage = new MailMessage([email protected], [email protected], “Your password changed!”, “bla bla bla…”);
            client.Send(mailMessage);
            return true;
        }
        return false;
    }
}

Tamamen örnek amaçlı olan şifre değiştirmeye yarayan bir method düşünelim. İçerisine baktığımızda ise context üzerinden user’ı çekip, ilgili kontrollerden geçirdikten sonra şifre değiştirme işlemini gerçekleştiriyor ve hemen ardından şifre değişikliği üzerine bir mail gönderiyor. Gördüğümüz gibi buradaki method complexity’si artmış ve en önemlisi mail gönderim kısmının tekrar kullanılabilirliği kısıtlanmış durumdadır.

Optimal bir şekilde refactor etmek gerekirse:

Gördüğümüz gibi her method sadece ilgili sorumluluğunu yerine getirmektedir. SendEmail method’uda artık tekrar kullanılabilir bir hale gelmiştir.

2) Method’lar inline olarak çok fazla parametre almamalıdır

İsminden de anlaşılabildiği gibi bir method çok fazla parametre almamalıdır. Örneğin:

Bu tarz kullanımlarda farklı bir business kararı gereği değişmesi gereken veya ekstradan eklenmesi gereken bir parametre daha gerekebilir. İşte bu durumda bu method’u kullanan her yerde bu değişimleri yapmak zorunda kalabiliriz ve parametre sıralarının kayması gibi problemleri de göze almalıyız. Bunlara ek olarak da okunabilirliği azaltıyor. Çözümü ise bu parametreleri bir obje içerisinde encapsulate etmektir.

Eğer ideal parametre sayısını merak ediyorsanız “Clean Code: A Handbook of Agile Software Craftsmanship” kitabında geçen çok güzel bir dizeyi size göstermek isterim:

The ideal number of arguments for a function is zero (niladic). Next comes one (monadic), followed closely by two (dyadic). Three arguments (triadic) should be avoided where possible. More than three (polyadic) requires very special justification—and then shouldn’t be used anyway.

3) Satırlar çok fazla uzun olmamalıdır

Kod yazarken uzun uzaya giden satırlar, belli bir süre sonunda tek hamlede okunabilirliği yüksek ölçüde azaltmaktadır. Buda ilgili kodu anlamamızı veya bazı kontrol etmemiz gereken durumları gözden kaçırmamıza sebebiyet verebilir. Çözüm olarak ilgili kodun belirli parçalarını, sub method’lar halinde mantıklı bir şekilde bölmektir. Farklı kaynaklarda tavsiye edilen satır uzunlukları 200’dür.

4) Exception’lar es geçilmemelidir

Kodlamanın herhangi bir parçasında catch bloğu içerisinde es geçilen exception’lar, ilerleyen süreçlerde farklı hata ve problemlere sebebiyet verecektir. Aynı zamanda da iyi bir best practice değildir. Bu tarz durumlarda oluşan exception’ı loglamak, iyi bir yöntem olacaktır.

5) Switch-Case clause’ları çok fazla satır içermemelidir

Switch-Case clause’ları içerisindeki fazla satırlar, okunabilirliği ciddi ölçüde azaltmaktadır. Bu anlamda olabildiğince kısa statement’lar kullanılmaya çalışılmalıdır.

Burada method çağırımının haricinde satırlarca farklı kodlar da yer alabilirdi. Okunabilirlik için olabildiğince case clause’u içerisindeki statement’ları kısaltmaya çalıştık.

6) Boolean statement’leri ters kontrol edilmemelidir

Boolean kontrollerini beklenenin aksine kontrol etmek ve ters ifadeler ile kontrol etmek hem coding standartlarına uygun değildir hem de kompleks ve maliyetli bir iştir.

7) TODO tag’ları kontrol edilmeli ve method’lara summary eklenmelidir

Eğer herhangi bir method boş ise ve daha sonra implemente edilecekse, TODO tag’ı eklenmelidir. Bu sayede TODO tag’larına göre filtrelendiğinde gözünüzden kaçan implemente edilmemiş kod parçacığı kalmayacaktır. Bunun yanı sıra method’lara eklemiş olduğunuz summary’ler sayesinde, hem siz hemde diğer ekip arkadaşlarınız kod’a henüz bakmadan summary ile kod hakkında bir fikir sahibi olabilirler.

8) Switch-Case statement’ındaki boş DEFAULT clause’u silinmelidir

Switch-Case kullanıldığında otomatik olarak gelen DEFAULT clause’u kullanılıyorsa, o halde olması gereken uygun action kullanılmalıdır. Eğer hiç bir şey kullanılmayıp boş olarak es geçiliyorsa, kullanmanın da bir anlamı olmayacağından dolayı coding standartlarına göre silinmelidir.

9) Enum tanımlarken null olarak sıfırıncı değer tanımlanmalıdır

Coding standartlarında tutarlılığı sağlayabilmek adına Enum’lar tanımlanırken, sıfırıncı değer None olarak tanımlanmalıdır.

10) Constructor aracılığı ile inject edilen field’lar, read-only olmalıdır

Constructor aracılığı ile inject ettiğimiz field’ları read-only olarak tanımlamazsak, farklı method’lar içerisinden de ilgili field’a değer atanmaya çalışılabilir, karışıklıklara sebebiyet verebilir. Bunların önüne geçebilmek için ise read-only olarak işaretlenmelidir.

11) Interface tanımlanırken başına “I” prefix’i getirilmelidir

Coding standartlarına göre interface’ler tanımlanırken başlarına “I” prefix’i eklenmelidir. Örneğin:

12) String birleştirme işlemlerinde “+” öperatörü kullanımına dikkat edilmelidir

String birleştirme işlemlerinde sadece bir kaç string’i birleştiriyor isek çok fazla problem olmayacaktır. Fakat bu işlemi daha fazla string ile gerçekleştiriyor isek memory performansı açısından StringBuilder veya String.Concat gibi işlemler uygulanmalıdır.

13) System type name’ler yerine Predefined type name’ler kullanılmalıdır

Int16, Single, UInt64 gibi system type name’ler yerine, Predefined type name’ler kullanılmalıdır. Örneğin:

Bu sayede .Net Framework tarafında daha tutarlı bir okuma gerçekleştirecektir.

14) External kaynaklara erişecek bir class varsa IDisposable pattern’ini implemente edilmelidir

IO işlemleri, web servisler, sql gibi external kaynaklara ihtiyaç duyduğunuz durumlar olduğunda, memory performansı için IDisposable pattern’ini implemente edin.

15) Kodlama yaparken kodlar içerisinde magic string/numbers kullanılmamalıdır

İlgili kod parçacıkları içerisinde kullanılan magic string/number’lar tekrar kullanılabilirliği engellemekle kalmayıp, ilgili değer değiştiğinde ise her nerede kullanıldıysa tek tek bulunup güncellenmesi gerekmektedir. Coding standartları doğrultusunda bu tarz değişkenler global ve const olarak tanımlanmalıdır.

16) Unmanaged kaynaklar için using statement’ı kullanılmalıdır

Bildiğimiz gibi .Net Framework içerisindeki Garbage collector hiçbir referans tarafından gösterilmeyen managed nesneleri bellekten kaldırmaktadır. Ancak Garbage collector unmanaged kodlar üzerinde tam kontrole sahip değildir. Kullanılmıyor olsalar bile bellekten serbest bırakamaz. İşte bu noktada biz yazılımcılar tarafından Dispose edilmesi gerekmektedir. Bu tarz unmanaged kaynaklara erişen objeler genelde IDisposable arayüzünü implemente etmektedir. Bu nesneleri dispose edebilmenin en iyi yolu ise, using statement’ı içerisinde kullanmaktır.

17) Catch bloğunda exception tekrardan fırlatılırken sadece throw kullanılmalıdır

Catch bloğu içerisinde ilgili exception’ı logladıktan sonra tekrardan geriye fırlatma ihtiyacı duyulursa, bunu sadece throw keyword’ü kullanılarak gerçekleştirilmelidir. Aksi durumda oluşan orjinal exception’ın stack trace’i kaybolacaktır.

Bu gönderiyi paylaş

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir