Code Review Nedir, Neden Önemlidir

Kod kalitesini artırmak için bir çok yol izlenebilir; unit test, continuous integration, continuous deployment, agile methodology etc. ancak en önemli yollardan biri code review dır. Bazen kaliteli diyebileceğimiz kodlar yazmaya çalışsak bile öyle anlar geliyor ki proje müdürünüz bir anda gelip bu feature'u bugün yetiştirmemiz gerekiyor deyip spaghetti code yazma süreciniz başlıyor. Bu gibi durumlar hep olacak ancak daha sonrasında geriye dönüp yazmış olduğunuz kodu review-refactoring etmemiz gerekmekte. Yada normal akışında geliştirme yapıyor olsak bile teknik olarak bilgili birisi (genelde takım liderleri) yapmış olduğumuz geliştirmelere bir göz atıp ondan sonra master branch'ine commit/checkin yapıyor olmamız gerekir.

Code Review yapmanın bir çok avantajı var;

  • Kod kalitesini arttırır,
  • Bug bulmamızı kolaylaştırır,
  • Developer kendisi olmasa bile bir başkası review işlemi yapacağından geliştirme yaparken daha dikkatli olmasını sağlar,
  • Review işlemi yapan kişi daha önce hiç karşılaşmadı şekilde olumlu geliştirmelerle karşılaşabilir ve onun gelişimine de katkısı olur,
  • Yazılım standartlarına bağlı kalmaya yönlendirir.

Gibi gibi birçok faydasından bahsedebiliriz. 

Başlangıçta biraz garip gelebilir ancak iyi bir flow bulduğumuzda hem yazılım kalitesi, hem ekip çalışması ve ekibin moral-motivasyonu açısından ne kadar büyük bir değişim olduğunu fark edeceksiniz dir.

Code Review İçin İzlenebilecek Yollar/Kurallar

Code Review için belli başlı kabul edilen bazı kurallar mevcut. Bazı software tool'lar kullanarak başlanabilir ancak developer olarak bizlerin uyması gereken bazı kurallar sıralayabiliriz. 

 

Kısa Commit'ler ve Anlaşılır Commit Mesajları

İlgili branch'e commit yaparken bir seferde bütün her şeyi commit'lemekten ziyade kısa kısa paketler halinde yaptığınız geliştirmeleri commit edin ve anlaşılır commit mesajları kullanmaya çalışın. Böyle yaparak her şeyi step-by-step açıklamış olacaksınız ve hem review yapacak kişi için kolay anlaşılabilir olacak hemde kendiniz için geriye dönüp baktığınızda yaptığınız geliştirmeyi hatırlaması daha kolay olacaktır.

Kodları Review Edin Developer'ı Değil

Review yaparken başkalarının yapmış olduğu işleri incelediğinizden eleştiri yaparken bazen yapılan işe değilde kişinin kendisine eleştirilerde bulunulabiliyor. Ancak bu devloper'la üzerinde olumsuz tepebilir. Yanlış yaptıkları bir şeyi gördüğünüzde onları sürekli eleştirmek yerine nasıl daha doğru yapabilirlerdi o yönde tavsiyelerde bulunmak daha çok önemlidir. Tabi bunları söylerken kullandığınız ses tonu da oldukça önemlidir.   

Developer olarak, review eden kişi commit'inize yorumlarda bulunduğunda bunu kişisel algılamamaya çalışın tabi doğrudan kişisel bir söylemde bulunulmuyorsa.  

Muhakkak Checklist'iniz Olsun

Hem review edecek olan kişi hemde developer için checklist'inizin olması önemli daha sistematik bir review süreci için oldukça önemlidir.

  • Bugs,
  • Duplicate Code,
  • Login Pages,
  • Code Style and Formatting
  • etc..

Bu gibi checklist'ler yaparak süreci daha kolay hale getirebiliriz. 

Multiple Reviewers

Genelde bir kişi bulmak bile çok zor oluyor ancak eğer mümkünse kodunuzu birden fazla kişiye review ettirin. Eğer kodla ilgili bir anlaşmazlık var ise örneğin; a kişisi burda factory design pattern kullanman doğru olmuş dedi, B-C kişileri hayır burda abstract factory kullanmalısın dedi. Hemen bu gibi durumlarda oylama yapılıp çoğunluğa göre karar verilebilir.

Ne Zaman Review Yapılmalı ?

Aslında bu büyük oranda development ekibine bağlı.Size bir geliştirme atandı ve 4 gün içinde dev branch'inde geliştirme yaptınız ve test'e gönderdiniz, testlerden herhangi bir bug vs. çıkmayıp "Ok production'a alabiliriz.." dedikten sonra yapmış olduğunuz geliştirmeleri master branch'ine merge işleminden önce review edecek olan kişiye gönderilmesi en doğru olan zamandır diyebiliriz. Review edilmeyen kodları master'a merge etmemek gerekir.

 

Review İşleminden Sonra..

Dev ortamda yaptığınız geliştirmeleri review eden kişi eğer herhangi bir sorun görmez "OK" verir ise master branch'ine merge işlemini yapabilirsiniz.  Eğer yaptığınız geliştirmeler review eden kişiden onay almaz ise tekrardan dev branch'de istenilen refactoring işlemlerini yapıp projenin son halini tekrardan review eden kişiye assign etmek gerekir. Bu sefer reviewer'dan "OK" aldıysanız üstte de belirttiğim gibi master branch'ine geliştirmelerinizi merge edebilirsiniz.

 

Özetle Code Review yazılım kalitesini artırmak için yapılabileceklerin en başında gelir. Eğer Development ekibinin başında olan kişi iseniz büyük ihtimalle review işlemini yapan kişi siz olacaksınızdır ve takımınıza belli başlı kabul görmüş yazılım geliştirme standartlarına uymalarını sağlarsanız review süreciniz daha rahat ve keyifli geçecektir.

 

PostSharp Kullanarak Exception Handling ve Log İşlemi

Daha önce Aspect-Oriented Programming yazımızda AOP'den bahsetmiştik ve AOP'nin .Net tarafında uyarlanabilmesini sağlayan tool'lar ve bu tool'ların en yaygın olanı Postsharp olduğunu söylemiştik. Bu yazıda Postsharp kullanarak uygulamamızda meydana gelecek olan exception'ları yakalamayı sağlayan bir geliştirme yapacağız.

Başlarken

Öncelikle VS'yu açıp yeni bir proje oluşturuyoruz.

  • File > New > Project > Console Application

İsim olarak ben "PostSharpExcHand" dedim siz istediğiniz bir ismi verebilrisiniz, sonrasında Ok'e tıklayıp projeyi oluşturuyoruz.

Daha sonra Postsharp'ı projemize ekleme işlemi var. Bunun için;

  • Tools > NuGet Package Manager > Manage NuGet Packages for Solution

Search kısmına "PostSharp" yazıp çıkan sonuçlar arasından postsharp'ı bulup indiriyoruz sornasında solution'da bulunan hangi projeye eklemek istiyorsak onu seçip yüklemi işlemini bitiriyoruz.

Aspect Tanımlama

AOP yazımızda aspect'lerden bahsetmiştik. Projemizde kullanacağımız reusable modülleri aspectler halinde tanımlayıp kullanmak istediğimiz yerlerde aynı metot üstünde attribute tanımlarmış gibi eklediğimizden bahsetmiştik. Bu yazıda Exception anında çalışacak olan aspect'i tanımlicaz. Bunun için projeye ExceptionHandlerAspect adında bir class ekleyelim. Postsharp ile birlikte gelen OnMethodBoundaryAspect adında bir class var ve bu class'tan inherit olan class'lar aşağıda da görüldüğü gibi override edebileceği bazı metodlara sahip oluyorlar.

Bu metotları kullanarak log,exception handling gibi bir çok ihtiyaca çözüm üretebilirsiniz. Biz bu yazıda exceptionHandling işlemi yapacağımız için oluşturduğumuz ExceptionHandlerAspect class'ını OnExceptionAspect class'ından türetiyoruz ve bu metotla birlikte gelen OnException metodunu override ediyoruz.

   [Serializable]
    public class ExceptionHandlerAspect  : OnExceptionAspect
    {
        public override void OnException(MethodExecutionArgs args)
        {
            string msg =  string.Format("{0}: Error running {1}. {2}{3}",DateTime.Now, args.Method.Name, args.Exception.Message, args.Exception.StackTrace);
            Debug.WriteLine(msg);
            args.FlowBehavior = FlowBehavior.Continue;
        }

Yukarıda görüldüğü gibi uygulamada exception meydana geldiğinde OnException metoduna düşecektir ve burda ihtiyaca göre log atma vs. gibi işlemleri yapabliriz.

Tanımladığımız Aspect'i Kullanma

Şimdi ise sırada tanımladığımız Aspect'i projede kullanma var. Bunun için uygulamada Exception olabilieceğini düşündüğümüz metotların başına attribute olarak ExceptionHandlerAspect'i ekleyebilriz.

    class Program
    {
        static void Main(string[] args)
        {
            ThrowSampleException();
        }
 
        [ExceptionHandlerAspect]//Attribute olarak Aspect'i ekledik
        private static void ThrowSampleException()
        {
            throw new NotImplementedException ();
        }
    }

Görüldüğü üzre yukarıda bulunan ThrowSampleException()  içerisinde exception alındığında üzerinde tanımlı bulunan ExceptionHandlerAspect'inden dolayı bu class içerisine gidip OnException metoduna düşecektir ve ilgili exception açıklamasını alıp artık canınız ne isterse onu yapabilirsiniz :) Log atabilirsiniz, çalışmasını istediğiniz ayrı bir process olabilir onu çalıştırabilirsiniz vs. vs.

ThrowSampleException IL Kodları

ExceptionHandlerAspect ThrowSampleException metoduna ne kazandırdı diye IL kodlarına bakacak olursak aslında try/catch kullandı ve metot içerisinde bulunan kodları try bloğunun içerisine alarak meydana gelecek olan exception'lar için catch bloğunda bunları handle eden kodları yazdı.

private static void ThrowSampleException()
{
    try
    {
        throw new NotImplementedException();
    }
    catch (NotImplementedException exception)
    {
        MethodExecutionArgs methodExecutionArgs = new MethodExecutionArgs(null, null);
        MethodExecutionArgs arg_1F_0 = methodExecutionArgs;
        MethodBase m = Program.<>z__Aspects.m2;
        arg_1F_0.Method = m;
        methodExecutionArgs.Exception = exception;
        Program.<>z__Aspects.a0.OnException(methodExecutionArgs);
        switch (methodExecutionArgs.FlowBehavior)
        {
        case FlowBehavior.Default:
        case FlowBehavior.RethrowException:
            IL_55:
            throw;
        case FlowBehavior.Continue:
            methodExecutionArgs.Exception = null;
            return;
        case FlowBehavior.Return:
            methodExecutionArgs.Exception = null;
            return;
        case FlowBehavior.ThrowException:
            throw methodExecutionArgs.Exception;
        }
        goto IL_55;
    }
}

Reusability

Evet arkadaşlar aslında AOP kullanarak exception handling yapmamızda ki en büyük amaç bu Reusability. Tanımlamış olduğumuz aspect'i artık projenin her yerinde kullanabiliriz ve bu durum bizi çookk ama çok büyük bir iş yükünden kurtarmış olup spaghetti code'lar yazmamıza engel olmuştur.

Son olarak; AOP candır.. :)

Aspect Oriented Programming Nedir

Aspect Oriented Programming (AOP) tükçesi "Cephe Yönelimli Programlama" bir programlama paradigmasıdır. İsim olarak bazılarımıza yabancı geliyor olabilir çünkü çok yeni bir kavram değil ve gelişen yazılım teknolojileri ve AOP nin daha kolay ve verimli implement edilmesini sağlayacak "PostSharp" gibi tool'ların çıkmasıyla birlikte epey bir önemli hale gelir oldu AOP.

Biz yazılımcılar daha iyi kodlar yazmak için hep kullandığımız bir cümle var "Separation of Concern". AOP'nin çıkış noktası aslında buna dayanıyor diyebiliriz. AOP birbiriyle kesişen ilgilerin (Cross-Cutting Concerns) ayrılması üzerinedir. Uygulama genelinde kullanılacak olan yapıları (logging,exception hand., cache, etc.) core tarafta yazdığımız koddan ayırarak bir çeşit ayrı küçük programcıklar şeklinde yazıp projede kullanmayı hedefler diyebiliriz.

Örnek olarak 70.000 kişinin çalıştığı çok büyük bir holding için uygulama geliştiriyoruz geriye bütün çalışan listesini dönen bir metod yazıyor olalım ve klasik her uygulamada olması gereken belli başlı şeyler vardır; Cache,ExceptionHandling, Logging gibi bizde metodumuzda bunları yapıyor olalım;

 public IEnumerable<Employee> GetEmployeeList()
        {
			//Request'i yapan kişinin yetkisi varmı yokmu kontrol et
			//metoda girerken request'i log'la
			
            try
            {
                var resultList = DbQuery("Select * from Employee"); // database de ki tabloya sorgu attığımız varsayalım ve 70 bin kayıt gelsin
				
                //geriye dönen sonuçları cache'e at bir sonrakine cache'den ver

                return resultList;
            }
            catch (Exception ex)
            {
                // meydana gelen Exception'ı handle edip log'la ve client'a gidecek olan response'u modify et  
                throw;
            }
			
			//metoddan çıkarken response'u log'la
        }
}

Yukarıda bulunan metodu incelediğimizde ne kadar eksik olduğunu görebiliyoruz. Yorum satırlarında yazan işlemler için geliştirmeler yapmamız gerekmekte ancak bu geliştirmeyi nasıl yapacağız  ? CheckUserAuth(), LogRequest(), LogException(), LogResponse(), ModifyResponse() gibi metodlar yazıp bu metodları ilgili yerlerde her metodda yazmak herhalde ilk akla gelen çözüm ancak AOP bize daha farklı şekilde yapmamız gerektiğini söylüyor. Bunları ayrı modüller olarak tasarlayıp daha kullanılabilir, okunabilir ve SOLID prensiplerine uygun geliştirmeler yapmamız gerektiğini söyler.

Peki birbirleri ile çakışan ilgileri birbirlerinden nasıl ayıracağız ? İşte bu noktada karşımıza interceptor çıkmakta.

Interceptor

Interceptor’ belirli noktalarda metot çağrımları sırasında araya girerek çakışan ilgilerimizi işletmemizi ve yönetmemizi sağlamakta. Buda metotların çalışmasından önce veya sonra bir takım işlemleri gerçekleştirebilmemeizi sağlar ve AOP nin yapısı tamamiyle bunun üzerine kurulu desek yanlış olmaz heralde. Interceptor'u implemente etme olayına girmicem çünkü yukarıda da bahsettiğim gibi .Net tarafında Nuget üzerinden indirip kullanabileceğimiz Postsharp kütüphanesi bu işi diplerine kadar yapmakta ve bizlere sadece attribute tanımlamaları yapmayı bırakmakta. 

Şimdi yukarıda yazmış olduğumuz kodu gelin birde AOP standartlarına uygun şekilde yazalım.

        [UserAuthAspect]
        [LoggingAspect]
        [AppCacheAspect(25000)]
        [ExceptionAspect]
        public IEnumerable<Employee> GetEmployeeList()
        {
            var resultList = DbQuery("Select * from Employee");
            return resultList;
        }

[UserAuthAspect] [LoggingAspect] [AppCacheAspect] [ExceptionAspect] attribute'lerini tanımladık ve AOP nin dediği gibi Cross-Cutting yani kesişen yerleri Aspect'ler kullanarak attribute seviyesinde kullanılabilir hale getirdik.Yazmış olduğumuz 2. metot ile 1. metot arasındaki satır sayısı farkına baktığımızda dağlar kadar fark var ve en önemlisi daha okunabilir bir kod yazmış olduk.  

Aspect-Oriented Programming'in Sağladıkları

  1. İçi içe yazılmış ve sürekli tekrar eden kodlardan kurtulabiliyoruz,
  2. Daha temiz ve anlaşılır kodlar yazabiliyoruz,
  3. Yazmış olduğumuz kodları daha abstract hale getirerek modülerliğini arttırıyoruz,
  4. Bakım ve geliştirme maliyetlerini azaltıyoruz,
  5. Uygulamamızı daha yönetilebilir ve daha esnek hale getirebiliyoruz.

Görüldüğü üzre AOP yaklaşımı geliştirdiğimiz uygulamalar için bizlere bir çok faydalar sunmakta ve Postsharp gibi çeşitli tool'lar ile birlikte projenize AOP'ye uygun hale getirmek dahada kolay hale gelmiş durumda. Bundan sonraki AOP ile ilgili yazılarda Postsharp kullanarak Cache, Logging, ExceptionHandling gibi örnekler ile deva ediyor olacağız.  

Egosuz Programlama & Programcı Egosu

Biz developer'lar bazen yaptığımız iş gereği "dünyayı kurtarıyoruz" havasına girebiliyoruz (aslında sadece developer'lar değil test müh, analistler, tasarımcı arkadaşlar vs.). Sektörde her zaman bu gibi arkadaşlar hep varlar ve dehşet-ül vahşet düzeyde bir özgüven-ego patlaması yaşayabiliyorlar.Bu arkadaşlar istisnai olmadığı taktirde %99 oranında hep yazdıkları kodların veya ortaya attıkları tezlerin en iyisi olduğunu iddia ederler ve genelde "abi bundan daha iyi bir çözüm yok yaa.." gibi cümlelerle tanırız bu arkadaşları. Neden bu şekilde iddialı cümleler kurduklarını anlamak için bence biraz gerilere gidip bu arkadaşların staj dönemlerine inmek gerekir zira bu arkadaşlar çok büyük olasılıkla sağlam bir code review'lık dönemini kaçırmış olabilirler veya junior'lık dönemlerinde kodları inşaata kaçmış olabilir :)

Gereksiz ve anlamsızca ego sahibi olan bu arkadaşlar her şey hakkında en iyi ve en kabul görebilir öneriler sunduklarını düşünürler, her ortamda teknik bilgilerini değişik bir jargonla anlatarak ortaya koymaya çalışırlar, en iyi kodu onlar yazmıştır ve eleştiriye kapalıdırlar. Bazen öyle durumlar olur ki bir konu hakkında konuşuluyordur ve hiçbir fikri olmamasına rağmen sırf o konu hakkında expert olduğunu kanıtlamaya çalışmak adına tuhaf bir şekilde konuşmaya başlarlar. Tabiki de benim fikrim ancak bu arkadaşlarla kavgasız gürültüsüz iletişim kurabilmenin en iyi yolu bu tarz hareketler yaptıklarını anladığınızda öyle bir şey söyleyin ki bir daha benzeri hareketler yapmaya kalkışmasın. Bu tabii ki de küfretmek falan değil :) Ortaya koydukları tez'in sırf söylemiş olmak için söylediklerini düşünüyorsanız ve siz daha doğru bir tez'e sahipseniz bu tezinizi savunmaktan kesinlikle çekinmeyin ve onun tezini çürütmeye çalışın.

Böyle bir kişi eğer ekip arkadaşınızsa yani aynı projede beraber development yapıyorsanız şu gibi cümleler duymanız hiç zor değil "abi burayı böyle yazmasak daha iyi yaa..", "abi ben daha önce bunla ilgili çok kral bi çözüm bulmuştum yaa..","bu böylemi yapılır yaa.." falanlar filanlar. Bu gibi cümleler kuran bir ekip arkadaşınız var ise ve yüksek oranda ego sahibi olduğunu düşünüyorsanız benzer cümleler kurmasının asıl amacı üstünlük sağlamaya çalışmak veya benzer bir kodu oda yazmıştır zamanında ve o yazdığının ilelebet en iyi çözüm olacağını düşünür çünkü ezberlemiştir onun için hep bildiği yoldan gitmek gerekmektedir ve senide ısrarla o yola sokmaya çalışır.

Evet malesef bende bu arkadaşlardan biriyle bir süreliğine çalışmak zorunda kaldım ve yaşadığım bir örneği vermeden geçemicem. Eski çalıştığım şirkette bir mobil projesini geliştirme yapan arkadaştan devraldım ve geliştirmeye ben devam edecektim kendisi başka bir  departmana geçmişti. Projeyi devraldığımda markette bulunan versiyonu yanılmıyorsam 1.1 di ve ben yaklaşık 2 sene o projede çalışmıştım. 2 sene dolmasına yakın bir dün bu arkadaş burnu 5 karış kalkık vaziyette yanıma geldi ve ne hikmettir bilmem proje hakkında sorular sormaya başladı.Sorulardan biride uygulamanın bir sonraki versiyonunun ne olacağıydı. Bende 3.0 olacağını söyledim.O arkadaş sahip olduğun egoyla şöyle bir cümle kullandı "Ben sana bişey söylimmi siz bu versiyonlamayı kesin yanlış yapıyorsunuz..." bende dedim ki "wtf..? yani ne alaka nerden çıkardın bunu ?" oda "ben bu projeyi bıraktığımda v1.1 di.." arkadaş zannediyordu ki kendisi uygulamanın %25 inin geliştirmesini yapıp v1.1 ile markete çıkıp uygulama bitmişti. Geriye kalan 2 sene içerisinde uygulamanın %85'e yakının geliştirilip 6 defa update çıkıldığından haberi yok, haberi olmasa daha düşünmesine de gerek yok çünkü 2 sene önce o uygulamayı yaptı ve bitti. Ben naptım peki ? Bir güzel o kişiye konuyu açıklayıp arkasını dönüp fıtı fıtı gidişini izleyerek çayımı yudumladım.

Malesef ego sahibi insanlar IT sektöründe hep olacaktır, bu insanlar yarın bir gün bizlerde olabiliriz ama sahip olduğumuz ego'nun makul düzeyde olması çok önemli.

Peki gerçekten biz developer'lar makul düzeyde ego'ya nasıl sahip olabiliriz, neler yapmalıyız ..? Bununla ilgili bilgisayar bilimleri için bir çok makale yayınlamış olan yazar Gerard M. Weinberg "Egoless Programming" adında bir makale yazıyor. Üstad konu ile ilgili yaklaşık 40 yıl önce 10 tane kural tanımlamış. Bunlar;

1. Understand and accept that you will make mistakes

Türkçesi "herkes gibi sende hata yapacaksın arkadaş bunu anla ve kabul et.." diyor. Bug'ı olmayan uygulama olmaz ve her zaman uygulamalarda exception'lar meydana gelecektir. Önemli olan bu gibi durumlar production'ı etkilemeden yakalayıp müdahale edip uygulamamızı hatalardan arındırmaktır. Tabi bug'ları ortaya çıkartmak içinde çok iyi test süreçleri gerekmektedir.

 

2. You are not your code

Review yapmanın asıl amacının meydana gelmiş veya gelecek olan problemleri bulmak olduğun hatırla ve problemler olacaktır da. Review sırasında birisi projenizde herhangi bir problem bulduğunda veya eleştirel bir yorumda bulunduğunda bunu kişisel bir şey olarak algılama ve sadece geliştirme yapmaya devam et.

 

3. No matter how much you know, someone else will always know more

Hiç kimse ben iyi programcıyım dememelidir. Her zaman için herkesin farklı bakış açısından sorunları analiz edip çözümler ürettiğini düşünmek gerekir ve buda her zaman için daha iyisinin olabileceği gerçeğini unutmamamız gerektiğini işaret eder. Sahip olduğumuz bilgi ve beceriler tabiki çok değerli ancak bunun da bir limitinin olduğunu bilmemiz gerekir. Geliştirme yaparken her developer'ın izlediği farklı bir yol olabilir ve daha önce senin hiç bilmediğin-denemediğin bir şekilde sorunları çözüyor olabilir ve bu gibi case'leri çok fazla abartıp sidik yarışına girmeye gerek yoktur. Sadece şunu bilmek gerekir her zaman senden daha farklı veya senden daha iyisini bilen biri olacaktır.

 

4. Don’t rewrite code without consultation

Bu kural bize kodu fix etme ile yeniden yazma arasında ince bir çizgi olduğundan bahsediyor. Bu farkı iyi bir şekilde bil ve değişiklik yapacağın yerlerde core koda dokunup projeyi yeniden geliştirmene neden olacak şeylerden uzak dur.

 

5. Treat people who know less than you with respect, deference, and patience

Ekibinde veya etrafında her zaman senin kadar teknik bilgisi olmayan insanlar olacaktır. Bunlara karşı anlayışlı ve saygılı olmaya çalış. Eğer mümkünse onlara konudan bahset ve teknik tarafını anlamada yardımcı ol. 

 

6. The only constant in the world is change

Değişim yazılım dünyasında kaçınılmazdır ve değişime açık ol ve geldiğinde gülümseyerek kabul et :) İstenilen değişikliği bir challenge olarak gör ve kendine yeni bir şeyler katacağını düşünerekten hareket et.

 

7. The only true authority stems from knowledge, not from position

Belli konular hakkında bilgi sahibi olmak beraberinde yetkili olmayı da gerektirir ve yetkide beraberinde saygılı olmayı getirir. Eğer egosuz bir ortamda saygı istiyorsan öncelikle bilgi üretmen gerekir.

 

8. Fight for what you believe, but gracefully accept defeat

Bazen sahip olduğunuz fikirlerin veya ortaya attığınız tezlerin reddedilebileceği gerçeğini unutmayın bu her zaman ihtimal dahilindedir. Bazen haklı olduğun durumlarda bile asla intikam almaya çalışmayın hatta "ben sana demiştim.." vs gibi şeyler dahi söylememeye özen gösterin.

 

9. Don’t be the “coder in the corner”

Ofisin en karanlık köşesinde oturup mesai saati boyunca sadece kişisel ihtiyaçlarını karşılamak için yerinden kalkan kişi olmayın çünkü o developer gözden uzak olduğu gibi gönülden de ırak olur. Her zaman için ofis içinde olup bitenlerden haberiniz olsun ve her zaman kendinizi ekibinizin ve şirketinizin bir parçası olarak görün.

 

10. Critique code instead of people – be kind to the coder, not the code

Mümkün olduğunca yazacağın yorum satırlarında pozitif bir dil kullanmaya çalış ve yorumlarında dünyaları yeniden yarattığından çok kod kalitesinin gelişimine yönelik açıklamalarda bulun. Yorum standartlarınız standart olup resmi bir dil kullanmaya özen gösterin ve her zaman için projeye sizden sonra gelecek olan kişiyi düşünün.

Yazının başında da bahsettiğim gibi ego makul düzeyde olduğu sürece güzeldir. Yaptığımız işlerle övünürken başkalarını ezme düşüncesine girmemeliyiz ve bırakın başkaları bizi övsün. Böyle olduğu taktirde çalışma hayatı hem siz hemde çalışma arkadaşlarınız için daha eğlenceli ve çekilesi olacaktır.

 

Web API İçin 5 Altın Kural

Çoğu zaman yazılım geliştirirken ortaya çıkan ürün yazılımcılar için değilde son kullanıcılar içindir ve bu kişiler teknik olmayan ve sadece UI tarafıyla haşır neşir olan kişilerdir. Ortaya çıkan ürünün iyi bir arayüzü vardır ve son kullanıcı arkada çalışan hangi teknolojiymiş, nasıl çalışıyormuş gibi bilgilerle hiç ilgilenmezler.

Ancak WebApi için bunu söyleyemeyiz. Yazılımcı olarak geliştirmiş olduğunu WebApi projesi end-user için değil bazen kim olduğunu bilmediğimiz başka yazılımcılar içindir. Bu kişiler teknik bilgisi en azından junior developer seviyesinde olan kişilerdir ve ortaya çıkan ürünün interface'i değilde yazılım tarafında ki her bir ince detayı inceleyip kullanacak olan kişilerdir ve sizi eleştirme hakkına da sahiptirler. 

Api'ı kullanacak olan kişiler yazılımcılar olduğundan geliştirmeyi yaparken bir WebApi client'ıymış bakış açısıyla yaklaşıp öyle geliştirme yapmak gerekir ve aslında bir nevi bu bakış açısını da Api'ı kullanan kişilerle de paylaşmış oluyoruz.

Api geliştiriciler genelde geliştirme sırasında şu sorulara odaklanırken ;"Bu servisin ne yapması gerekiyor ?", "Bu servis metodu ne sağlamalı ?", "Content-type json mı, xml mi ?" etc. , Api'ı kullanan kişiler "En az efor sarf ederek bu Api'ı nasıl kullanırım ? etc".

Görüldüğü üzre Api'ı yazan ve Api'ı kullanan kişiler tamamiyle farklı şeylere odaklanıyorlar çünkü farklı bakış açılarıyla Api'ı kullanıyorlar.  Sonuç olarak Api'ı yazan kişiler olarak uygulamayı geliştirirken consume edecek kişilerin yani Api kullanıcılarının bakış açısından bakıp onlar bu APi'ı kullanırken ne gibi sorular sorabilir veya hangi sorunun cevabına en kolay şekilde ulaşabilirler bunu düşünerek hareket etmemiz gerekir.

Peki iyi güzel ama böyle bir Api geliştirmek için neler yapmak gerekir ? Bunun için birçok geliştirici tarafından standart kabul edilen 5 altın kural vardır;

  1. Documentation (Dökümantasyon)
  2. Stability and Consistency (Kararlılık ve Tutarlılık)
  3. Flexibility (Esneklik)
  4. Security (Güvenlik)
  5. User Acceptance (Kullanıcılar tarafından kabul görmesi)

Rule 1: Documentation 

Yazılım geliştiren kişilerin ortak pek sevmediği şeylerden biri sayfalarca analistler tarafından yazılmış dökümanları okumaktır. Sizde sevmiyorsunuz dimi ?.. :)  

İyi bir dökümantasyon Api için olmazsa olmazlardandır ve çoğu zaman Api'ları geliştirren kişiler olarak bu iş biz developer'lara bırakılmıştır. Kullanıcı tarafından düşünelim bir bankada çalışıyorsunuz ve banka radikal bir karar alarak bazı servislerini dışarıya açma kararı aldı ve başladınız Api metodlarını yazmaya. Api'lar prod'a alınıp kullanıma sunulduktan sonra kullanmak isteyen kişilerin ilk başlayacakları yer Api ile ilgili yazılan dökümanları okumaktır.  Yani birinin sizin Api'ınızı kullanmasını istiyorsanız ilk yapmanız geren şey dökümantasyon yazmaktır çünkü kullanıcıların ilk bakacakları şey budur.

Peki iyi bir dökümantasyonu nasıl yazarız ?

Yazılımla projeleri ile ilgili dökümanlarda genelde; hangi sırasıyla metodların çağrılacağı, metod parametreleri, değişken tipleri, request, response class'ları etc. ve dökümantasyon oluşturmak içinde bir çok tool'da bulunmaktadır aynı zamanda kendimizde bunları basit bir template belirleyerek yazabiliriz. 

Ancak harika yazılmış bir dökümanı üstte bahsettiğimiz yaygın olarak kullanılan döküman yazma tekniklerinden ne ayırır diye soracak olursa Api'ın kullanım örnekleri ve iyi bir eğitim içeriği. Bu ikisi Api'ı kullanan kişiler için en önemli iki şeydir diyebiliriz çünkü son kullanıcı için gerçek bir örnek üzerinden ve takıldığı noktaları eğitim içeriğine bakarak en hızlı ve kolay şekilde öğrenecektir. Ancak yazdığımız dökümanın şeması ve yazdığımız örnek kodların karmaşık olmamasına da dikkat etmeliyiz. Döküman yazıyoruz diye kullanıcıya 300 sayfalık bir roman veya 15 class'lık bir örnek projede yazmamalıyız :)

Dökümantasyon işiniz bittikten sonra yapabiliyorsanız yapmış olduğunuz örnek Api kullanım projelerini ve dökümanları çevrenizdeki developer'lara gönderin ve feedback'ler almaya çalışın. Gelen feedback'ler doğrultusunda da yazmış olduğunuz dökümanıdaha user-friend hale getirebilirsiniz.  

Yaptıkları dökümanları harika bulduğum birkaç firma var ki bunlar kendi sektörlerinde en iyiler bile değiller ancak gerçek bir Api dökümanı nasıl olmalı sorusuna verilecek en iyi cevabı vermiş firmalar;

 

Rule 2: Stability and Consistency

Daha önce Facebookûn Api'larını kullandıysanız ne sıklıkla Api'ları değiştirdiklerini bilirsiniz. Sürekli olarak bir güncelleme yayınlıyorlar ve çalışan birçok uygulama da patlıyor çatlıyor. Facebook'un ne kadar başarılı bir ürün olduğuna herkes hemfikirdir ancak başarılı olmalarının sebebi son derece iyi bir Api'a sahip oldukları değil milyarları geçen kullanıcı sayısı sahip olmaları ancak iş Api konusuna gelince developer'lar için bazen hayatı zindan ettikleri oluyor. Muhtemelen sizlerin geliştirdiği Api'lar milyarlarca kullanıcı tarafından kullanılmicak ancak o bakış açısıyla geliştiriyor olmakta fayda var. Yeni sürüm yayınladığınızda eski versiyonlara desteği en azından birkaç yıl daha sürdürüyor olmak gerekir en azından mevcut kullanıcıları en iyi şekilde yeni versiyondan haberdar ederek onların yeni versiyona geçmelerini sağlamak gerekir.

Örnek üzerinden gitmek gerekirse ; farz edelim ki http://www.canertosuner.com/api/getUsers/  şeklinde tanımlı JSON formatında response dönen ve yaklaşık 10 bin kişi  tarafından kullanılan bir Api'ımız var diyelim. Api'ı production'a aldıktan aylar sonra dönen responsları değiştirdiniz ve bu Api'ı kullanan kişileri de etkileyen bir değişiklik olsun. Bu şu demek; 10 bin kişinin geliştirdiği 10 bin uygulamaların patlaması demek. Bunun çözümü tabii ki de uygulamaya yeni versiyon çıkmamak değil bir ürün aldığı update'ler ile daha iyi olur. Bunun çözümü için Api'ınıza basit bir versiyon kontrolü koymak ve eski versiyonlara desteği sürdürmek.( http://www.canertosuner.com/api/getUsers?version=2 yada http://www.canertosuner.com/api/v2/getUsers ) .Böylelikle hem eski versiyonu kullananlar yeni versiyona geçene kadar uygulamalarının çalışmalarını sağlayabilirler hemde Api'ı yeni kullanmaya başlayacak olanlar direkt olarak son versiyondan başlarlar.

Api'ınızın Consistent yani tutarlı olması da son derece önemlidir. Api'ınızın geriye döndüğü response class'larının ortak bir base class içerisinde olmasına özen gösterin. Çünkü bu class sayesinde metodun döndüğü response dışında Api kullanıcısına farklı verilerde döndürebilirsiniz. Daha önce kullanmış olduğum bazı Api'larda tutarlılık neredeyse hiç yoktu. Sürekli olarak yeni gelen versiyonlarda parametre isimleri değişiyor, metod isimleri değişebiliyor, bir önceki metodda UserID'yi int gönderirken bir sonraki metodda string olarak istiyorlardı, hata mesajları sürekli olarak farklı formatta geliyordu vs. gibi örneklerle karşılaşmak mümkün. Bu gibi durumlara dikkat etmek gerekiyor. Ortak kullanılan parametreler tek bir yerden yönetilebiliyor olmalı ve major değişiklikler kolay kolay uygulanmamalı veya uygulandığında Api'ı kullanan kişiyi etkilenmesinden kaçınılmalı.

 

Son olarak yayınlamış olduğunuz yeni versiyonlarda iyi bir changelog yayınlayıp mevcut kullanıcılarınızı bilgilendirmeniz gerekir. Böylece kullanıcılar nasıl upgrade edeceklerini öğrenmiş olurlar ve Api uygulamanızda çok büyük major değişikliklere gitmemeye özen gösterin veya bunu kullanıcıları en az seviyede etkileyecek şekilde yapın ve tutarlılığı korumaya çalışın.

 

Rule 3: Flexibility

Yazmış olduğumuz Api'ların flexible yani esnek olması oldukça öçnemli bir konudur.Peki ama esnek derken neyi kastediyorum ?

Garbage in, garbage out (GIGO) programcılar tarafından iyi bilinen bir technical-term dür. Kısaca "input olarak ne verirsen output'un ona göre değişiklik gösterir" veya ne ekersen onu biçersin". Hadi biraz daha basit söyleyelim programa yanlış bir request'te bulunursanız response'unuzda yanlış olacaktır. GIGO yu örnek olarak vermemin sebebi aslında Web Api tarafında request response ilişkisine dikkat çekmek istemem. 

Günümüzde bir çok Api doğru düzgün bir JSON desteği bile bulunmamakta ancak iyi bir Api'ın sadece JSON değil bunu yanında YAML, XML vs. gibi formatlar içinde desteği bulunmalıdır ve bunlardan hangisini return edeceği bilgisini de aslında Api kullanıcılardan yani client'lardan parametre olarak almalıdır. Örnek olarak http://www.canertosuner.com/api/getUsers?Format=JSON yada bu bilgiyi header'a parametre olarak eklemek aslında en doğru kullanımdır diyebiliriz Accept: application/json .

Bu kullanım sadece kullanıcıya dönecek olan response için değil aynı zamanda kullanıcının size göndereceği Post metodlarında body de bulunan request parametreleri içinde geçerlidir. Bir kulalnıcı JSON olarak gönderirken diğer bir kullanıcı XML olarak göndermek isteyebilir ve böyle bir esnekliğe sahip olabilmek oldukça önemli bir ayrıcalıktır.

Api'ınızın OData özelliğine sahip olması da bir o kadar önemlidir. OData ile Api'nızın döneceği respons'lara kullanıcılar tarafından filtreleme yapma şansı sunarsınız ve böylece kullanıcının istemediği bir veriyi dönmemiş olursunuz. Örneğin A kullanıcısı GetProducts metodundan sadece ProductID'lerini almak isterken B kullanıcısı ProductName ve ProductID alanlarını almak isteyebilir. OData bu gibi durumlar için son derece filexible(esnek) bir kullanım sunar.

 

Rule 4: Security

Güvenlik bir WebService & WebApi projesindeki en önemli noktalardan biri ancak öyle Api'lar var ki otantike olmak nerdeyse bazen imkansız oluyor. Tamam güvenlik önemli ama öyle katı ve karmaşık kurallar koyarak Api yazan firmalar var ki bazen birbiri ile ilişkili Api' metodları için 2-3 çeşit güvenlik algoritması kullandığımız olabiliyor ve ortada dökümanda yoksa nasıl requestte bulunacaksın, nasıl response alacaksın hepsi belirsiz. Bir Api developer olarak Api'ı kullanan kişiler için nasıl authenticate ve authorize olunur bilgilerini içeren kolay ve açıklayıcı örneklerle anlatıyor olmamız gerekir. 

Günümüz Web Api'larının çoğunda ortak olarak token-based authentication kullanılmakta. Kısaca; kullanıcı yapmış olduğu ilk requestte Api'a bazı güvenlik parametreleri geçer ve Api'da ona random generate edilmiş olan token bilgisini döner ve client bütün session'ı boyunca aynı token üzerinden request atıp response almaya devam eder. 

Token dışında OAuth 2 + SSL de Api'ınızı güvenli hale getirmek için yapılabilecek diğer seçenekler arasındadır. SSL her halükarda kullanıyor olmamız gerekir bunun yanında OAuth 2'yi de server-side'da uygulamakta oldukça basittir ve  OAuth 2'nin hemen hemen bütün yaygın kullanılan programlama dilleri için kütüphaneleri de bulunmaktadır.

Bunlarla birlikte güvenlik için dikkat edilmesi gereken birkaç konu dha bulunmaktadır;

  • Api'larda genellikle update, insert, delete gibi işlemler yapılabilmektedir. Bu işlemleri yapan metodları public hale getirip bütün kullanıcıların kullanımına sunarken dikkat edilmesi gerekmektedir. Her kullanıcının şöyle bir Api çağrısı yapamıyor olması gerekir "/user/delete/<id>" . Bununla ilgili her bir kullanıcı için Whitelisting Functionality bilgisi oluşturup bu listede belirtilen kurallara göre request validation işlemleri yapılabilir. 
  • OData kullanırken de select işlemleri yapılırken kullanıcının sahip olmasını istemediğimiz bilgiler için request'te bulunduğunda 406 Not Acceptable Response şeklinde status kodlar dönebiliriz. 
  • Cross-Site Request Forgery (CSRF) isteklerine akrşı Api'ı korumalıyız. Session veya cookie bazlı bir authentication yapımız var ise Api'ımızı CSRF ataklarına karşı koruduğumuzdan emin olmalıyız. Bununla ilgili faydalı dökümanları The Open Web Application Security Project (OWASP) da bulabilirsiniz. 
  • Resource dosyalarına olan erişimi yönetiyor olmamız gerekir. Örnek olarak bir banka kredi kartlarını tutan Api yazdınız ve Api içerisindeki bir dosyada kredi kartı görselleri bulunmakta. Herhangi bir kullanıcı gidipte  /account/card/view/152423 şeklinde bir istekte bulunup erişmemesi gereken bir resource dosyasına erişmemelidir.
  • Bütün request'leri validate ediyor olmamız gerekir. Her bir request'in kendine özgü parametreleri bulunur ve kullanıcı sürekli olarak saçma sapan request'lerde bulunduğunda örnek olarak; userId miz 4608 olsun ve user bilglsini getiren metoda 4608 den başlayıp random sayılar üreterek ( /account/getUserInfo/4609) başka kullanıcıların bilgileri için request'te bulunduklarında Api'ımızın bir şekilde bir güvenlik kontrolü olup bu kullanıcıyı tespit edip engellemelidir.

 

Rule 5: User Acceptance

Beşinci ve son kural aslında bu beşli arasındaki en önemli kural diyebiliriz. Bu kural ilk 4 kural boyunca bahsettiğim bütün şeylerin aslında bir ürün olarak hazırlanıp kullanıcıya sunulması ve kabul görmesini içermekte. Örneğin iyi bir dökümantasyon, kolay entegre edilebilir güvenlik adımları, yazdığımız Api'metodlarını tutorial'larla süsleyip max 15 dakikada kullanıcılar tarafından entegre edebilmelerini sağlama gibi durumlar diyebiliriz.

Api'ın kullanıcılar tarafından daha kolay kabul görmesini sağlayacak bazı spesific tavsiyeler saymak gerekirse;  

  • Kullanıcıların dökümantasyonda belirtilenlere göre Api'ınızı ilk denemeden sonra entegre edebildiğinden emin olun.
  • KISS - Keep it Simple Stupid anlayışına göre hareket etmeye çalışın. Fantastik authentication yöntemlerinden kaçının, SOAP, JSON, REST'i yeniden yaratmaya çalışmayın veya bütün platformlar tarafından kabul görmemiş herhangi bir yöntem kullanmayın.
  • Service arayüzü için dil spesifik library'i desteği vermeye çalışın. Otomatik olarak bu işlemi yapan güzel tool'lar da mevcut (Alpaca veya Apache Thrift gibi).
  • Gereken sign-up işlemlerini en basit şekilde yapın. Api'ınız bir open-source proje değilse ve sign-up & register gibi işlemler varsa bunları olabildiğince basite indirgeyip biran önce kullanıcıları yormadan api'ınınzın bulunduğu yere yönlendirin. Eğer sizin için extra bir maliyeti yoksa sign-up with social media account (facebook, google, twitter etc) özelliği sunabilirsiniz. 
  • "Satış sonrası destek" sağlayın. Bug'sız uygulama olmaz ve Api'ı kullanan kişiler bug veya herhangi bir sorun ile karşılaştıklarında en kısa ve kolay şekilde bunları size iletmesini sağlayın. Bu bir forum sitesi veya e-mail sistemi olabilir size kolayca raporlayabilsinler. Sorunu giderip yeni versiyon çıktıktan sonrada ilgili kişileri tekrardan bilgilendirmeyi ihmal etmeyin.

 

Özetle..

Iot (Internet of Things) ile birlikte hayatımızda kullanacağımız IP alıp internete çıkabilen bütün teknolojik ürünler WebService & Api ile haberleşiyor olacaklar ve beraberinde birçok yeni Api'lar yazılmasına ihtiyaç duyulacak. Bugünün verilerine göre düşünürsek; binlerce api & web service var ancak sıkıntılı olan şey kullanımları gerçekten kolay değil. Sebep olarak SOLID prensiplerine pek uymayan yazılım anlamında zayıf tasarımlı projeler olması, dökümantasyon tarafının yeterli düzeyde veya yeteri kadar kolay anlaşılabilir olmaması, örnek kod parçacıkları içermemesi, raporlanmasına rağmen çözülmeyen bug'lar etc.. bir sürü sıralayabiliriz.

Yukarıda belirtilen 5 altın kural bir çok kişi tarafından aslında ortak dile getirilen başlıklar ve Api projesi geliştirirken uyulması gereken kurallar olarak belirtilmekte. Eğer bizlerde bundan sonra yazacağımız Api'larda birazcıkta olsa bu kurallara uygulayarak hareket edersek her şey hem Api geliştiriciler hemde Api kullanan kişiler için daha kolay olacaktır.

NLog EventLog

Daha önce ki Log4Net yazımızda log tutmanın ne kadar önemli olduğu ve .Net tarafında kullanabileceğimiz free yapılardan biri olan Log4Net implementasyonunu incelemiştik. Bu yazıda ise bir diğer free framework olan NLOG dan bahsedeceğim. Nlog ile mail atabilir, file log'a, console'a, event log'a log, db ye log atabiliriz.

NLog ile ;

Fatal Üst Seviye : Sistem çökmeleri

Error Uygulama hataları ( Exceptions )

Warn Uyarılar, yinede uygulama çalışmaya devam edebilir.

Info Bilgilendirme herhangi bir amaçlı olabilir. Kullanıcı bilgileri güncellendi vs.

Debug Çalıştırılan sorgular, oturum süresinin bitmesi vs.

Trace Bir eylem başladı diğeri bitti gibi. Örn : Fonksiyon başlangıcı ve bitişi durumları( En Alt Seviye )

seviyelerinde log tutabiliriz. Biz bu yazıda NLog ile Event Log'a nasıl log atılır bunu inceliyor olucaz.

1.Adım İlk olarak işletim daha önceden yğklenmiş olan PowerShell programını yönetici olarak açıyoruz ve MyTestAppLog adında bir event log ve bu event log altında MyTestAppSource adında bir source oluşturuyoruz. Daha sonrasında oluşturduğumuz uygulama ve source isimlerini webconfig tarafta konfigurasyon yaparken ilgili alanlara set edicez.

PowerShell de aşağıdaki gibi code satırını yazıyoruz.

  New-EventLog -LogName MyTestAppLog -Source MyTestAppSource  

Sonradan işletim sisteminde bulunan search kısmına view event log yazarak event viewer'ı açıyoruz ve oluşturduğumuz MyTestAppLogve MyTestAppSource ekranda görmemiz gerekiyor.

 

 

2.Adım İkinci olarak VS da boş bir Web projesi açıp Nuget paket yöneticisine Install-Package NLog yazıp ilgili kütüphaneyi indiriyoruz ve projemizdeki WebConfig dosyası içerisine NLog configurasyonunu sağlayan satırları yazıyoruz.

Configuration tagları arasına aşağıdakini yazıyoruz

<section name="nlog" type="NLog.Config.ConfigSectionHandler, NLog"/>  

Daha sonrada configuration tag'inin kapandığı yerin altına da şu şekilde yazıyoruz

<nlog
    autoReload="true"
    throwExceptions="true">
    <variable name="appName" value="MyTestApp" />
 
    <targets async="true">
        <target type="EventLog"
            log="MyTestAppLog"
            name="eventlog"
            source="MyTestAppSource"
            layout="${message}${newline}${exception:format=ToString}"/>
    </targets>

    <rules>
      <logger name="*" writeTo="eventlog" minlevel="Info" />
    </rules>
  </nlog>

 Üstteki configurasyonda ;

  • <target type="EventLog" diyerek EventLog configurasyonu olduğunu belirtiyoruz,
  • log="MyTestAppLog" EventLog adı,  
  • name="eventlog" diyerek isimlendiriyoruz,
  • source="MyTestAppSource" EventLog da tanımlı olan Source adı.
  • layout="$ ile log formatını belirliyoruz.
  • <rules> tag'i arasına yazdığımız kod satırıyla eventlog adında tanımlı olan log configurasyonu için minimum Info seviyesinde log tut.

 

3.Adım Şimdi C# tarafına geçip nasıl log atacağız onu yazalım

 public class NLogManager 
    {
        //WebConfigde tanımladığımız gibi info ve yukarı seviyeler için eventLog'a log atacaktır
        public void LogError(LogModel entry)
        {
           var logger = NLog.LogManager.GetCurrentClassLogger();
           logger.Log(LogLevel.Info, "Info Logged");
           logger.Log(LogLevel.Error, "Error Logged");
        }
    }

Uygulamamızı çalıştırdığımızda event log da aşağıdaki gibi log düştüğünü göreceğiz

 

 

 

Log4Net delete last N days files

Daha önceki yazımızda projemize nasıl Log4Net implemente edilir burada ki yazıda incelemiştik. Log4Net te bahsettiğimiz gibi bizim verdiğimiz kritere uygun boyutlarda belirttiğimiz klasör altında log dosyalarını saklıyordu. Peki ya o klasör altında aylarca yıllarca çok fazla log dosyası oluşur ve GB boyutunda yer kapladığı düşünülürse ne olacak??? "Hadi be sende GB boyutunda log nerde biriksin.." falan diyebilirsiniz ancak projenizin büyüklüğüne göre bunun olma olasılığı oldukta büyük bir ihtimal.
İşte bu gibi durumlardan kurtulmak için bir File Clear türünde bir manager yazmamız gerekmekte.

Akış şu şekilde olacak; Log4Net kendi işini yapıp loglarını dosyalara atmaya devam ederken uygulamanın her Application_Start adımında file clear manager çalışıp Log klasörüne gidip bakıcak. İçerisindeki dosyalardan son 2 haftadan daha eski tarihte oluşmuş dosyalar var ise gidip bu dosyaları silecek.

ilk olarak aşağıda oludğu gibi Log4NetFileCleanUpManager adında bir class oluşturuyoruz

public class Log4NetFileCleanUpManager
    {
        private DateTime _lastDateToKeepStoring;
        public Log4NetFileCleanUpManager()
        {
            _lastDateToKeepStoring = DateTime.Today.AddDays(-14);
        }

        public void CleanUp()
        {
            var repo = LogManager.GetAllRepositories().FirstOrDefault();

            if (repo == null)
                throw new NotSupportedException("Log4Net has not been configured yet !");

            var app = repo.GetAppenders().FirstOrDefault(x => x.GetType() == typeof(RollingFileAppender));
            if (app != null)
            {
                var appender = app as RollingFileAppender;
                string directory = Path.GetDirectoryName(appender.File);
                 CleanUp(directory);
           }
        }

        public void CleanUp(string logDirectory)
        {
            if (string.IsNullOrEmpty(logDirectory))
                throw new ArgumentException("logDirectory is missing");

            var dirInfo = new DirectoryInfo(logDirectory);
            if (!dirInfo.Exists)
                return;
 
            var fileInfos = dirInfo.GetFiles();
            if (fileInfos.Length == 0)
                return;
 
            foreach (var info in fileInfos)
            {
                if (info.CreationTime < _lastDateToKeepStoring)
                {
                    info.Delete();
                }
            }
        }
    }
Kod şu şekilde çalışıyor. Constructor da hangi tarihten sonraki kayıtları silineceği bilgisi veriliyor (Son 14) gün. CleanUp fonksiyonunda Log4Net için webconfigde tanımlı RollingFileAppender bulunuyor ve bunun için tanımlı log klasörünün directory si bulunup CleanUp(string logDirectory) metduna yollanıyor. Burada o klasörün içerisindeki dosyalarda teker teker gezinip oluşturulma tarihlerine bakıyor. Oluşturulma tarihi bizim belirttiğimiz tarihten önce ise o dosyayı siliyor.
 
 
Son olarak Application_Start metodunda aşağıdaki configurasyonu yapmamız gerekmekte.
XmlConfigurator.Configure();
var task = new Log4NetFileCleanUpManager(); 
task.CleanUp();
 

Log tutmak ne kadar önemli ise tuttuğumuz log'un yönetimi ve başa bela olmaması da o kadar önemlidir. Üstte de söylediğim gibi projenin büyüklüğüne göre onlarca GB boyutunda .txt dosyaları ansızın oluşabilir ve sonrasında down !

Log4Net İmplementasyonu

"Log uğrunda ölen olmadıktan sonra log değildir !!"

Önceki çalıştığım firmalardan birinde uzun soluklu bir bankacılık projesi geliştiriyorduk ve daha önce ilgilenen arkadaşlar bilirler ki development süreci oldukça meşakatlidir. Önce Dev de çalış sonra Test'e at sonra UAT e al sonra PROD a taşı orda testleri yap vs. vs. bir sürü ortam birsürü bug ve en önemlisi yüzlerce şöyle cümleler duyabilirsiniz "TEST te sıkıntı yok ama UAT de çalışmıyor yada sadece PROD da rastladığımız bir durum..." bu gibi durumlarda loglardan yürüyerek ilerlemek sorunun kaynağına en kısa sürede ulaşmanızı sağlayabilir. Tabi log alt yapınız var ise :) Ama bitmedi log altyapınızın olmasıda o loga bir bakış attığınızda ahanda tamam sorunu buldum diyebileceğiniz anlamına gelmeyebilir. Çok fazla yaşadığım bir olaydan bahsetmek istiyorum ve bir arkadaşımın bu olay sonrası projedeki Test Müh. arkadaşa söylediği bir cümle varki efsane :)

Birgün yine üstte bahsettiğim bankacılık projesinde dev yapıyorum ve ekip yaklaşık olarak 10 kişi falan (.net-ios-android-pm-tester vs.). Yine o sihirli cümle geldi ve test müh arkadaş android yazan arkadaşa "droidci arkadaş PROD da rastlanan bir hata varmış ve müşteri ilgili maddeyi JIRA da açmış ve logları paylaşmış. Sorunu kısa sürede çözebilir misin.." vs şeklinde bir cümle kurdu. Developer arkadaş JIRA yı açtı madde yi okudu ve loglara baktıktan sonra test müh arkadaşa "maddeyi bulamadım, müşteriye geri assign ediyorum.." gibi birşey söyledi ve test müh arkadaş da "ama nasıl bulamazsın loglarıda yollamışlar ki.. -%&?!?.." Bu cümleye atarlanan developer arkadaş işte o anda o efsane atasözünü söyledi. "Log uğrunda ölen olmadıktan sonra LOG değildir arkadaşım..!" Sonuç olarak ne oldu sizce ? Log var madde var ama çözüm için elde hiçbir şey yok. Yazılım projelerinde çok büyük öneme sahip olan log yapıları emin olun hayat kurtarır.  Çeşitli loglama türleri vardır. DBLog, FileLog vs gibi. Bu örnekte hemen hemen herkesin en azından duymuş olduğu Log4Net kütüpanesini kullanarak orta çaplı bir FileLog yapısı nasıl kurulur ondan bahsedicem. Log4Net için öncelikle Nugetten ilgili dosyayı indirip projemize kuruyoruz Install-Package log4net    Daha sonra aşağıda olduğu gibi ILog adında bir interface tanımlıyorum. Parametre olarak LogModel türünde bir obje alıyor olacak. Bu obje içerisinde loglamak istediğimiz alanlar mevcut olacak.

public class LogModel
    {
        public string ExcMessage{get;set;}
        public Exception ExceptionModel{get;set;}
      }

  public interface ILog
    {
        void Log(LogModel entry);
     }

Log4Net için WebConfig de birtakım ayarlar yapmamız gerekiyor. Bu ayarların kapsamı çok fazla ancak kısaca özetlemek gerekirse LogDosyalarının adlandırılması, LogDosyalarının bulunacağı path bilgisi, max kaç MB lık dosyalar tutulacak ve enfazla kaç dosya olmasına izin verilecek gibi bir çok LogConfig Settings diyebileceğimiz şey içermekte. Bu projedeki WebConfig dosyası asağıda olduğu şekildedir.

 

 <appender name="LogFileAppender" type="log4net.Appender.RollingFileAppender">

      <file value="LogFiles\\" />

      <appendToFile value="true" />

      <DatePattern value="yyyy\\\\MM\\\\dd'.day.logs.txt'" />

      <rollingStyle value="Date" />

      <maxSizeRollBackups value="7" />

      <param name="StaticLogFileName" value="false" />

      <layout type="log4net.Layout.PatternLayout">

        <conversionPattern value="%newline%date %newline%logger [%property{NDC}] %newline>> %message%newline" />

      </layout>

     </appender>

 

Şimdi sırada Loglama işlemini yönetecek bir Log4Manager' ihtiyacımız var. Burda yarın bir gün XManager YManager da tanımlayabiliriz. Bunun bir çok implementasyonu olabilir. Ben böyle birşey kullanmayı tercih ettim. ILog, IDisposable interfacelerinden türeyen Log4NetLogManager adında bir class yazıyoruz. Class ın içeriği aşağıdaki gibi olacaktır. 

 

 

public class Log4NetLogManager : ILog, IDisposable
    {
         private log4net.ILog log4Net;
         public Log4NetLogManager()
        {
            log4net.Config.DOMConfigurator.Configure();
            log4Net = log4net.LogManager.GetLogger(typeof(Log4NetLogManager));
        }
         public void Log(LogModel entry)
        {
            log4Net.Error(entry.ExpMessage, entry.ExceptionModel);
        }
         public void Dispose()
        {
            if (log4Net != null)
                log4Net.Logger.Repository.Shutdown();
        }
    }

 

İşlemlerimiz bu kadar. Bu saatten sonra tek yapmamız gereken bu manager'ı kullanan logic ler yazmaktır. Bir sonraki örneğimizde ASP.Net WebApi tarafında ExceptionHandling olaylarına girip yukarıda yazmış olduğumuz Manager'ı kullanan bir app yazacağız.

 

 

Dependency Injection

Yazılımla ilgilenen kişiler bilirler ki proje geliştirmede süreçler çok önemlidir. Müşteriye gidilir, ihtiyaç olan şey dinlenir, analiz edilir ve analiz yazılıp müşteriye sunulur. Müşteri analizi kabul eder ve development süreci başlar. Analiz developera gelir ve 3 ay sonra işini bitirdikten sonra müşteriye projenin sunumu yapılır ve müşterinin çenesi o anda açılır. Bunu da ekleyelim, şunu da yapalım, bu da olsa daha iyi olur.. süreli olarak bu gibi cümleler duymamak hiçten bile değildir. Bu gibi durumları göz önüne alarak projeyi olabildiğince esnek hazırlamak yararımıza olacaktır.

Proje içerisindeki modüllerin bir birlerine gevşek bağlı(loosely coupled) olması, üstte bahsettiğimiz gibi sonradan oluşabilecek talepler için minimum efor sarf change request leri yani sonradan istenilen değişiklikleri hayata geçirmemize katkıda bulunabilir. Bir senaryo ele alalım;

Person ve Car adında classrımız olsun ve bu classların içerisinde Drive adında bir metodumuz olsun ve kullanıcı classının instance alınıp Drive metodu çağrıldığında kullanıcı Car classının Drive metodu çağrılarak araba kullanıyor olsun.

public class Person {
  private Car myCar = new Car();
 
  public void Drive()
  {
     this.myCar.drive();
  }
}

 

Müşterinin isteğine göre araba sürebilen bir Person objesi yaratmış olduk. Peki ya müşteri 2 ay sonra "bu person kamyonda sürsün, motorsiklette sürsün.." derse ? İşte bu gibi durumlar için üstte yazdığımız class işimizi görmicektir ve müşterinin istediğini geliştirmemiz belkide bu güne kadar ki development süresi kadar zaman alabilir de (projenin büyüklüğü gibi vs. konulara bağlı). Üstteki sorunumuz Person objesi Car objesine bağlı ve bu nedenle Bus ve Motorcycle classlarını Person objesine entegre etmemize engel oluyor. İşte Dependency Injection burda devreye giriyor. Amacımız Person objesinin bağımlılığını en aza indirgemek. Bunun için ilk olarak IVehicle adında içerisinde Drive metodu bulunan bir interface yazıyoruz.

public interface IVehicle {
  void Drive();
}

Sonrasında ise bizim Car, Bus ve Motorcycle class larımız IVehicle implemente etsinler

public class Car : IVehicle 
{
  public void Drive()
  {
    Console.WriteLn("Araba sürülüyor");
  }
}

public class Bus : IVehicle 
{
  public void Drive()
  {
    Console.WriteLn("Otobüs sürülüyor");
  }
}

public class Motorcycle: IVehicle 
{
  public void Drive()
  {
    Console.WriteLn("Motorsiklet sürülüyor");
  }
}

Yukarıda ki geliştirmeler ile birlikte artık Person class'ımızın Car Bus gibi objelere olan bağlılığını ortadan kaldırmış olduk yani birbirlerine gevşek bağlı(loosely coupled) halede getirdik diyebiliriz.

Person classımızın yeni hali aşağıdaki gibi olacaktır.

public class Person {
  private IVehicle vehicle {get; set;}

  public Person(IVehicle Vehicle)
  {
    this.vehicle = Vehicle
  }
 
  public void Drive()
  {
     this.vehicle.Drive();
  }
}

 

Person classımızı kullanacağımız yerde artık şu şekilde yazabiliriz;

//Araba sürmesini istediğimizde
var person=new Person(new Car());
person.Drive();

//Otobüs sürmesini istediğimizde
var person=new Person(new Bus());
person.Drive();

Burada IVehicle interfaceîni implemente eden sayısız nesne ekleyebiliriz veya bu nesneler üzerinde değişiklik yapabiliriz ancak bu durum Person classını hiç ilgilendirmiyor çünkü herhangi bir nesneye bağlı değildir.

 

C# Kodlarının Derlenip Çalıştırılması - CLR,IL,CLS,CTS Kavramları

C# yazıyoruz çalışıyor, VB yazıyoruz çalışıyor, C++ yazıyoruz çalışıyor, ne hikmettir pek bilmesek bile bir F# dilimiz var onu yazıyoruz .Net sağolsun onu da çalıştırıyor... 

Peki ama bütün bunlar nasıl oluyor ?..

.Net framework içerisinde geliştirme yapabilmek için bir çok dil mevcuttur ancak C# bunlardan en torpillisidir diyebiliriz heralde. Tamamen OOP dayalı bir dil olmasıyla beraber günümüz .Net ailesinde en çok tercih edilen dil haline gelmiştir.

Aşağıdaki resimde de görüldüğü üzre v1.0 dan başlayıp v4.5' e kadar gelen süreçte ne gibi yenilikler geldi, ilk başlarda neler yapabiliyorken son sürümle birlikte ne gibi yeni özellikler geldi bunları görebiliriz. 

Bütün bunlar güzel hoşta arkadaş bunları üstte bahsettiğimiz .Net ailesi içerisinde bulunan çeşitli programlama dilleriyle yapıyoruz ama nasıl oluyor da C# ile yazılan projenin çıktısı ayrı, F# ile yazılanın ki ayrı, VB ile yazılanın ki ayrı iken framework bunları yorumlayıp çalıştırıabiliyor ?.. 

Yazılmış olan kodların direkt olarak derlenip çalıştırılması gibi bir cevap düşünecek olsak o zamanda her bir programlama dili için ayrı ayrı bu işlemleri yapan logic'ler gerekecektir bu da çok fazla iş yükü,maliyet vs demektir ve aslında mümkünde değildir.

 

Aşağıda bulunan görsel aslında yazmış olduğumuz projelerin çalışmasına kadar olan süreci özetliyor gibi.

 

CLR - Common Language Runtime

.Net framework altında bulunan dillerden herhangi biriyle program yazdığımızda kodlar ilk olarak CIL (common intermadiate language) diğer bir deyişle IL dediğimiz byte kodlara dönüşür. Bu IL kodları aslında çalıştırılabilir kodlar değildir ve çalıştırılabilmesi için bir ara programa ihtiyaç vardır. Bu ara program da CLR'dır. CLR dönüştürülmüş olan byte IL kodlarını alıp bir JIT (Just-In-Time) derleyicisini etkinleştirerek makine koduna dönüştürür ve çalıştırır.

 

CLS- Common Language Specification

CLS için .Net framework çatısı altında bulunan programlama dillerinin ortak noktalarını barındırır ve programımızın diğer dillerle olan etkileşimini sağlar. Diller arasında ortak kullanılan yapılar ve onların birtakım kuralları bulunmaktadır. Yazdığınız kodların diğer programlama dilleriyle geliştirilen projelerde de kullanabilir olması istiyorsanız, Common Language Specification kurallarına uyumlu kodlar yazmamız gerekmektedir. Bu kurallara uygun geliştirdiğiniz kodlar, CLS desteği olan diller tarafından yorumlanabilir ve böylece diller arasında ki iletişim sağlanmış olur.

 

CTS- Common Type System

CTS CLR'ın bir alt kümesidir diyebiliriz. Şöyle ki; Common Type System sayesinde programlama dillerinde kullanılan veri türlerinin(int,double,byte,string etc.) arasında da uyum sağlanmış olur.

Örnek olarak; .Net framework altında bulunan bütün dillerde int,double,float vs. gibi tiplerin ram'da kapladıkları yer aynıdır ve bu dillerden biri için derlenen kodlar referans olarak diğerinde de kullanılabilmektedir. Bu nedenle Framework içerisinde bulunan tüm dillerde CTS den dolayı ayni kurallara sahip veri tiplerini kullanılır farklı olan şey syntax'dır.