Yazılım Testlerinin Sınıflandırılması Üzerine Yaklaşımlar

Süleyman Fazıl Yeşil
11 min readJul 21, 2024

--

Test piramidi kavramını duymuşsunuzdur. Testlerin sınıflandırılması söz konusu olduğunda genellikle onun üzerinden düşünürüz. Zihnimizin üzerinden aktığı doğal bir dere yatağı gibidir.

Bir süre önce yazılım testleri konusunda fikirlerimi toparlamak ve olgunlaştırmak amacıyla Google’da Yazılım Mühendisliği (Software Engineering at Google) kitabındaki yazılım testlerine ayrılmış dört bölümü okumaya başlamıştım. Kitabı okudukça yer yer düşüncelerimi teyit ettim, yer yer düşüncelerim değişti. Zihinsel haritamı değiştiren en büyük şeyse testlerin sınıflandırmasına getirdiği yeni boyuttu.

Google, kod kapsama miktarı (scope) temelli geleneksel test piramidi yaklaşımını testleri sınıflandırma ve değerlendirme konusunda yetersiz bulmuş, buna test büyüklüğü (size) boyutunu eklemişti. Testleri bu iki boyut üzerinden değerlendiriyordu.

Birim test mi entegrasyon testi mi iyidir gibisinden tartışmaların faydasızlığı ve aslında testleri değerlendirirken baz almamız gereken hız, maliyet, değişime dirençlilik, stabilite, bakım kolaylığı, gerçeğe yakınlık gibi temel kriterler üzerine daha berrak bir şekilde düşünmeyi sağlaması bakımından da zihin açıcıydı.

Bu makalede, önce testlerin günümüz modern yazılım geliştirme sürecindeki yeriyle başlayıp, sağlıklı bir test paketine sahip olmanın önemine değindim. Sonrasında sınıflandırmaya geçip önce geleneksel test piramidine, sonra da hatalı bir uygulama olarak piramidin tersine çevrilmesine değindim. Son olarak da Google’da testlerin sınıflandırılmasını aktarmaya çalıştım.

Testlerin Mühendislik Kültüründeki Yeri

2000’lerin başından itibaren yazılım hızla geleneksel sektörlere nüfuz etmeye başladı. Başlangıçta tamamlayıcı niteliğe sahip olan yazılım sistemleri bugün artık hemen hemen her iş kolunun ana unsuru haline gelmiş durumda. Ek bir satış kanalı olarak ortaya çıkan çevrim içi sistemler bugün artık ana satış kanalına dönüşmüş durumda. Yazılımın çeperden merkeze olan bu ilerleyişi 2011’de Marc Andreessen’in slogana dönüşen ifadesinde vücut bulmuştu: “Yazılım dünyayı yiyor”. Bugün artık yazılım dünyayı yemiş durumda. Her şirket bir teknoloji şirketi artık. Teknoloji her şeyin merkezinde.

Yazılım sistemleri tamamlayıcı unsur olmaktan çıkıp ana unsur haline geldikçe karmaşıklaştı. Gerçek hayatın karmaşıklığı, onu temsil etmeye çalışan yazılım sistemlerine de yansıdı doğal olarak. Süreç boyunca bu karmaşıklığı yönetilebilir ve sürdürülebilir kılmak için yazılım sistemlerine olan bakışımız da evrildi, yazılım sistemlerini geliştirme pratiklerimiz de.

Hız, çeviklik, verimlilik, güvenilirlik için sistematiklik ve standartlaşma önem kazandı. Bunu karşılamak içinse, otomasyon, bulut sistemler, hazır platformlar, araçlar, altyapılar, mimari yaklaşımlar ortaya çıktı.

Bugün artık büyük ve karmaşık yazılım sistemleri üretip kullanıyoruz. Sürekli bir şeyleri değiştiriyoruz, ekliyoruz, çıkarıyoruz. Bu değişiklikleri hızla devreye almaya çalışıyoruz. Bir yandan da güvenilirlikten, sağlamlıktan ödün vermek istemiyoruz, olası hataları önlemeye çalışıyoruz. Bu amaçla her bir yeni sürümü test ediyoruz. Kaliteden emin olmaya çalışıyoruz. Fakat günümüzün yazılımları çok karmaşık ve etkileşimli. Bir yerdeki bir değişiklik fark etmeden bambaşka bir yeri etkileyebiliyor. Bizimse yeni sürümü baştan sona manuel olarak test edecek ne yeterli kaynağımız ne de zamanımız oluyor.

Bu yeni bir problem değil esasında. Hikayenin ta başlangıcından beri bilinen, yaşanan bir problem. Çözümü ise otomatik testler. Zaman içinde yaşanılan tecrübeler çerçevesinde gelişen, evrilen, olgunlaşan ve kabul gören bir pratik. Sistematik işleyen iyi bir mühendislik kültürünün olduğu her yerde devrede olan bir pratik.

Ama zaman her yerde aynı değil ne yazık ki. Herkes aynı saat diliminde yaşamıyor. Dünya üzerinde eş zamanlı olarak, biri modern dönemlerde yaşarken taş devrini yaşayan kabileler de yok değil.

Test Yazmak mı İyi ve Sağlıklı Testler Yazmak mı?

Otomatik testlerin temel faydaları şöyle:

  • Hızlı geri bildirim: bir bulgu yazılım yaşam döngüsünde ne kadar geç farkedilirse o kadar maliyetli oluyor, en iyisi geliştirme safhası
  • Kendine güven ve cesaret ve böylece yeniden düzenlemeleri teşvik etme ve değişimi destekleme
  • Hızlı ve kaliteli sürümler çıkma
  • Daha iyi tasarım: test yazarken birinci elden yazılan kodun kullanımını deneyimleyip kodu ve API’sini revize edebiliyoruz
  • Dokümantasyon: bazen kodun davranışını anlamak için kodu okumaktansa iyi yazılmış bir test senaryo cümlesini okumak daha basit oluyor
  • Sonuç olarak verimlilik ve üretkenlik artışı

Fakat zaman içinde sektörde yaşanarak edinilen tecrübeler ve bu tecrübelerin bize söylediği temel prensip ve pratikler dikkate alınmadığı durumda, otomatik testler çok kolay bir şekilde faydalı olmak bir yana zararlı hale gelebiliyor. Üretkenliği artırmak bir yana, kırılganlık gösterip, stabilite sorunları olup yazılım mühendislerine engel olmaya, onları aşağı çekmeye başlayabiliyor.

Her ne kadar yüzde yüz ideal bir olgunluk seviyesine ulaşmak mümkün olmasa da, iyi test yazma yaklaşımlarına ve pratiklerine uymaya özen göstermek, her şeyin artısını ve eksisini göz önüne alarak bilinçli bir şekilde tercihler yaparak adım adım ilerlemek, bize yetecek ölçüde test paketlerimizi sağlıklı tutmamıza yardımcı oluyor.

Hedefimiz otomatik test paketlerimizin olması değil, sağlıklı ve fayda saylayan test paketlerimizin olması. Yasak savmıyoruz. Dostlar alışverişte görsün de demiyoruz. Faydasını göreceğimiz güvenlik sistemleri inşa etmeye çalışıyoruz. Bu da ciddi bir emek, özen ve farkındalık gerektiriyor. Karşılığı ise paha biçilmez oluyor.

Sağlıklı bir test paketi oluşturma hedefiyle yola çıkarken ise, üst seviyeden testleri sınıflandırmak, hangi seviyede ne kadar test yazacağımızı belirlemek, yani test paketi tasarımı başlangıç noktasını oluşturuyor. İlk düğmeyi doğru iliklemek, kritik bir öneme sahip oluyor.

Tüm Testler Eşittir ama Bazıları Daha Eşittir

Yazılım sistemleri çok katmanlı ve dağıtık bir yapıya sahip. Uygulamalar tek başlarına yaşamıyor, bir ekosistemin içinde hayatlarını sürdürüyorlar. Entegre bir uygulamalar ağından söz ediyoruz. Temel servisler, arka ofis yazılımları, müşteriye dokunan web ve mobil tabanlı uç noktalar, kimlik doğrulama sistemleri, entegre olunan üçüncü parti ürünler ve sistemler, senkron ve asenkron biçimde iletişimler, veritabanları, ağ bileşenleri, donanımlar ve ortamlar. Oldukça karmaşık bir ekosistem.

Bir arabayı düşündüğümüzde de üzerinde belki binlerce parça var. Her biri diğerinden bağımsız üretiliyor. Bir cıvatanın boyasının testinden tutun, montajlanmış arabanın yoldaki sürüş testine varıncaya kadar geniş bir yelpazede, faklı amaçları gözeten testlerden söz etmek mümkün.

Cıvatanın üzerine sürdüğümüz boyanın kontrolünü, boyayı üretirken de yapabiliriz, cıvataya sürdükten sonra da, cıvatayı arabaya monte ettikten sonra da.

Bu test yaklaşımlarının hepsinin bir artısı eksisi var. Getirisi var götürüsü var, maliyeti var. Bu sebeple, sağlıklı bir test süreci için 360 derece bütünsel bir yaklaşım gerekiyor. Bir test mimarisi ve tasarımı gerekiyor. Farklı seviyelerde farklı amaçlara odaklanan testler oluşturmak gerekiyor.

En çok fayda sağlayan testlerse, en hızlı geri bildirim veren, en az maliyetli olan, kırılgan olmayan, stabilite sorunları olmayan, yazılım geliştiricilere gereksiz külfet getirmeyen, yazılım geliştiricilerin günlük işleyişlerini yavaşlatmayan, commit öncesi hızlıca çalıştırıp geri bildirim alabildikleri iyi yazılmış, doğru mesajlar veren, anlaması kolay, sağlıklı testler oluyor.

Sınıflandırmalar Üzerine Düşünmek

Herhangi bir konuda karar verirken, kategorik varsayımlarımızın ve ön kabullerimizin esiri olmamayı başarabildiğimiz ölçüde kararlarımız başarılı oluyor. Bunun içinse eldeki verileri, yaşanmışlıkları dikkate almak gerekiyor. Test mimarisini kurgularken de geçerli olan bir şey bu.

Farklı tipte testler yazmak mümkün. Testleri farklı şekillerde sınıflandırmak mümkün. Kategorik olarak bunlardan hangilerinin daha yararlı olduğu konusunu tartışmak da mümkün.

Kullanılan altyapılar, teknolojiler değiştikçe ve geliştikçe gözden geçirilmesi gereken argümanlar olacaktır bunlar hep.

Günün sonunda; hız, maliyet, değişime dirençlilik, stabilite, bakım kolaylığı, gerçeğe yakınlık gibi temel kriterler üzerinden düşünmemiz gerekiyor. Bu kriterleri iyi karşıladığı ölçüde hangi kategorilerde test yazıldığının da bir önemi olmuyor. Her ne kadar, bir sonraki bölümde ele aldığımız test piramidi bu kriterlerle büyük ölçüde uyumlu temel bir perspektif, bir model sunsa da.

Klasik Test Piramidi

Fonksiyonel testlerin sınıflandırılması konusunda en eski ve kabul gören yaklaşım Mike Cohn’a ait Test Piramidi yaklaşımı. Bu yaklaşıma göre, piramidin tabanını dar kapsamlı birim testler oluşturuyor. Bunun üstünde orta kapsamlı servis testleri yer alıyor. Bunlara entegrasyon veya API testleri de deniyor. En üstteyse geniş kapsamlı ön-yüz testleri. Bunlara uçtan-uca testler de deniyor.

Piramidin tepesine doğru gittikçe hız azalıyor, maliyet artıyor. Bu sebeple sağlıklı bir paketine sahip olmak için, birim testlerin çok daha fazla olması gerekiyor. Her ne kadar “birim”in ne olduğu, büyüklüğü ve kapsamı konusunda sektörde tam bir konsensus olmasa da.

Servis veya API veya entegrasyon testleri ise uygulamayı ön-yüz olmadan entegrasyonlarıyla birlikte test ediyor ve birimlerin bir arada doğru çalıştığından emin olmamız sağlıyor. Bu katmanda, birim testlere kıyasla daha az test yazılması öngörülüyor.

Uçtan uca ön-yüz testleriyse, kullanıcı gözüyle uygulamayı son kullanıcı arayüzü üzerinden test ediyor. Bu uç noktadaki testlerinse çok daha az sayıda olması bekleniyor.

Çünkü piramitte yukarı doğru çıktıkta, maliyet artıyor, hız azalıyor, karmaşıklık artıyor, kırılganlık artıyor, stabilite azalıyor.

Günümüz firmalarında genelde en alttaki birim testleri yazılımcılar, yazılımla aynı dili kullanarak yazıyor. Piramidin ortasındaki servis veya API katmanındaki testleri bazı yerlerde yazılımcılar bazı yerlerde testçiler yazıyor. Piramidin tepesindeki ön-yüz testleriyse ise genellikle testçilere kalıyor.

Yazılımcılar testleri aşina oldukları dilde, yazılım geliştirmede kullandıkları dilde yazıyorlar. Testçilerse farklı araçları ve dilleri kullanabiliyorlar. Karşılıklı etkileşimleri ve yardımlaşmayı da gözeterek benzer bir teknoloji kümesini kullanmak da mantıklı bir tercih olabiliyor.

Klasik test piramidi günümüz modern ve dağıtık uygulamalardan oluşan katmanlı dünyasını tam olarak anlatmakta yetersiz kalıyor. Fakat yine de sınıflandırma konusunda çok sağlam bir perspektif sunuyor.

Örneğin günümüz modern ön yüz uygulamalarını (single page web application) arka servis katmanı olmadan test etmek mümkün artık. Bileşen tabanlı çalışan, React/Angular vb. kütüphaneler kullanılarak geliştirilen modern ön-yüz uygulamalarına birim test yazmak çok kolay.

Piramidi Tersine Çevirmek

Yazılım testlerinde hatalı uygulamalara sapmak çok kolay. Çünkü şirketi dönüştürerek doğru bir yazılım kültürü inşa etmek zor ve zaman alıcı bir şey. Ayrıca bilgi ve tecrübe eksikliği de yanlış yönlere sapmaya sebep olabiliyor. Büyük umutlarla girişilen test projeleri, hatalı yaklaşımlar ve uygulamalar sebebiyle hedefine ulaşamayabiliyor. Veya elde edilen fayda çok marjinal kalabiliyor. Bunun sonucunda da test sürecinde manuel testler büyük yer tutmaya devam edebiliyor.

Google’ın Testleri Sınıflandırma Yaklaşımı

Erken öğrendiğimiz derslerden biri, mühendislerin daha büyük, sistem ölçekli testler yazmayı tercih ettiği, ancak bu testlerin küçük testlere göre daha yavaş, daha az güvenilir ve daha zor hata ayıklanabilir olduğu yönündeydi.

Mühendisler, sistem ölçekli testleri hata ayıklamaktan bıkkın bir şekilde, kendi kendilerine şu soruları sordular: ‘Neden sadece bir sunucuyu test edemiyoruz?’ veya ‘Neden bir bütünü bir seferde test etmeliyiz? Küçük modülleri tek tek test edebilirdik.’ Sonunda, acıyı azaltma isteği, ekiplerin daha küçük ve daha hızlı testler geliştirmelerine yol açtı, ki bu da daha hızlı, daha istikrarlı ve genel olarak daha az acılı sonuçlar doğurdu.

Bunun sonucunda şirket içinde ‘küçük’ kelimesinin tam olarak ne anlama geldiği konusunda birçok tartışma başladı. Küçük, birim testi mi ifade ediyor? Entegrasyon testleri ne boyutta sayılır? İki ayrı boyutun olduğu sonucuna vardık: büyüklük ve kapsam. Büyüklük, bir test senaryosunu çalıştırmak için gerekli olan kaynakları ifade eder: bellek, process sayısı ve zaman gibi şeyler. Kapsam ise doğruladığımız belirli kod yollarını ifade eder. (Software Engineering at Google kitabından)

Google testleri iki bakımdan sınıflandırır: büyüklük ve kod kapsamı.

Büyüklük bakımından üç testleri gruba ayırır: küçük, orta ve büyük testler.

Kod kapsamı bakımından da testleri üç gruba ayırır: dar kapsamlı, orta kapsamlı ve geniş kapsamlı testler. Bu sınıflandırma Mike Cohn’un test piramidini baz alır.

Büyüklük ve kod kapsamı birbiriyle ilişkili olsa da testleri sınıflandırmanın farklı boyutlarını ifade eder. Genellikle dar kapsamlı testler küçük, geniş kapsamlı testler ise büyük olmaya meyillidir.

Küçük testler tek bir process’te çalışır, orta büyüklükteki testler bir makinede çalışır, büyük testlerse birden fazla makinede çalışır.

Kaynak: https://abseil.io/resources/swe-book/html/ch11.html

Geleneksel birim test mi entegrasyon testi mi daha iyi tartışmaları çok da faydalı değil ve konuyu değerlendirmede yetersiz kalıyorlar. Hız, maliyet, değişime dirençlilik, stabilite, bakım kolaylığı, gerçeğe yakınlık gibi kriterler üzerinden konuyu düşünmek gerekiyor.

Küçük testler daha hızlı çalışıyor, daha az maliyetli, daha stabil. Dolayısıyla bu kriterleri sağladıktan sonra testin ne kadar bir kodu kapsadığı çok da umurumuzda olmuyor.

Testler büyüdükçe kodun çalıştığı ortam ve konfigürasyonlar vb. bakımından gerçeğe yakınlık artıyor. Fakat diğer taraftan hız, maliyet, ve stabilite azalıyor.

Dolayısıyla küçük, orta ve büyük testleri kombinlemek gerekiyor. Büyük testler, küçük testlerin kapsayamadığı unsurları test ediyor.

Küçük testlerde yavaşlık ve kararsızlık kaynaklarına sebep olan thread sleep, disk ve ağ erişimine izin verilmiyor.

Orta ölçekli testler, birden fazla thread veya process oluşturup erişebiliyor, network çağrıları yapabiliyor. Tek kısıt ise bunların hepsinin testlerle aynı makinede yani localhost’ta olması.

Büyük ölçekli testlerdeyse herhangi bir kısıt bulunmuyor. Bu tip testler genelde uçtan-uca sistem testlerinde kullanılıyor. Fonksiyonaliteden çok konfigürasyonel hataları yakalamyı amaçlıyor.

Test büyüklükleri konusunda kesin sınırların belirlenmesi, Google’a bu sınırları kontrol eden araçlar oluşturma imkanı vermiş. Bu da Google ölçeğinde testlerin çalışma hızı, kararlığı ve kaynak tüketimi konularında öngörülebilirlik ve yönetilebilirlik sağlamış.

Google’da büyüklük bakımından net sınırlar konulabilmiş olsa da, kapsam bakımından sınırlar o kadar net değildir. Dar kapsamlı birim testler bir metodu, bir sınıfı veya bir modülü test ediyor olabilir. Orta kapsamlı entegrasyon testleri bir kaç modülü bir arada test ediyor olabilir, örneğin veritabanıyla olan etkileşimi test edebilir. Geniş kapsamlı testlerse önyüz üzerinden uçtan-uca sistemi test ediyor olabilir.

Dar kapsamlı birim testler genelde büyüklük bakımından küçük olmaya meyillidir fakat orta büyüklükte de olabilirler, örneğin bir sınıf birden fazla process veya thread kullanıyor veya oluşturuyor olabilir.

Google’da kapsam bakımından testlerin dağılımı ise resimdeki gibidir: %80 dar kapsamlı birim testler, %15 orta kapsamlı entegrasyon testleri, %5 ise büyük kapsamlı uçtan-uca testlerdir.

https://abseil.io/resources/swe-book/html/ch11.html#test_scope

Google’da Büyük Testler

Dar kapsamlı küçük testler bize tekil bir nesnenin veya modülün davranışını doğrulama imkanı verir. Fakat bu doğrulama daha çok teorik fizik problemi çözmek gibidir. Normal şartlar altında (NŞA) diye başlayan izole fizik problemlerini bilirsiniz. Kağıt üzerinde çalışan şeyler gerçekte çalışmayabilir.

Derlenerek sürümü çıkılan paket, uygulamanın kurulduğu ortam koşulları, konfigürasyonlar, dış entegrasyonlar ve bağımlılıklarla beraber gelen farklılıklar, yük altında ortaya çıkan davranışlar uygulamanın beklendiği gibi çalışmamasına sebep olabilir. Bu sebeple, geniş kapsamlı büyük testler uygulamanın gerçek ortamda beklendiği gibi çalıştığından daha fazla emin olmamızı sağlarlar.

Google gerçek ortama yakınlığı ifade etmek için gerçeğe sadakat (fidelity) kavramını kullanır.

https://abseil.io/resources/swe-book/html/ch14.html

Büyük testler gerçeğe en sadık testler olsa da, hız ve stabilite bakımından kusurludur. Yavaş çalışırlar. Zaman, yazım ve bakım için maliyetlidirler. İzole olmadıkları ve belirsizlik katan dış etmenler sebebiyle stabil değildirler, bir başarılı bir başarısız olabilirler. Ek olarak da, günlük işleyişte her bir değişiklik ve commit sonrası çalıştırmak için efektif değildirler.

Google çok farklı tiplerde büyük testlere sahiptir. Bu testleri ele alırken iki kriterden hareket eder: gerçeğe sadakat (fidelity) ve izolasyon (hermeticity). Bu iki kriter çoğunlukla birbiriyle çatışır.

Büyük testler yazarken “mümkün olan en küçük” testi yazma prensibiyle hareket eder.

Büyük testlerde kullanılan veriler ya elle yaratılır, ya da gerçek sistemlerden kopyalanır veya örneklenir.

Büyük testlerin sonuçları ya otomatik olarak beklenen değerlerle karşılaştırılarak (assertions) kontrol edilir, ya manuel olarak kontrol edilir, ya da iki farklı testin (A/B) sonuçlarındaki farklar kıyaslanarak kontrol edilir.

Google’da farklı tipte büyük testler mevcuttur:

  • Birden fazla uygulamanın etkileşimli olarak fonksiyonel testi
  • Tarayıcı ve cihaz testi: Web ve mobil vb. uygulama arayüz testleri
  • Performans, yük ve stres testi
  • Kurulum konfigürasyon testi: Sadece uygulama ayağa kalkıyor mu diye kontrol edilir
  • Keşif testi: Gerçek veya referans ortamlarında manuel olarak yapılır, farkında olunmayan yeni bulgular olup olmadığını keşfetmek üzere yapılır
  • A/B fark testleri: Var olan versiyon ile yeni versiyon karşılaştırılır, bazen A/A bazen A/B/C vb. karşılaştırmaları yapıldığı da olur
  • Kullanıcı kabul testleri (UAT)
  • Canlı sistem sondajları ve kanarya analizi: Canlı sistem sondajı bir çeşit canlı sistem izlemesidir, genellikle okuma tabanlı kontrollerdir. Kanarya analizi ise canlıya çıkmadan önce canlıdaki versiyonla aday versiyon arasındaki karşılaştırmayı içerir ve yeni versiyonun güvenli olduğundan emin olmak üzere kullanılır. Kanarya analizi terimi adını kömür ocağında zehirli gaz seviyesini tespit etmek üzere kafes içinde kanarya götürme yönteminden alır.
  • Fekaket kurtarma ve kaos mühendisliği: Google senelerdir gerçek ortamda felaket senaryolarını test ettiği DiRT (Disaster Recovery Testing) adını verdiği bir savaş oyunu oynar. Kaos mühendisliği ise Netflix tarafından ortaya atılmıştır ve gerçek ortamda rasgele uygulamaların fişini çekerek dayanıklılığı test etmeyi amaçlar.
  • Kullanıcı A/B testleri: Uygulamadaki yeni bir özelliğin iç veya dış küçük bir kullanıcıya açılması ve değerlendirilmesini amaçlar.

Sonuç

Mike Cohn’a ait geleneksel test piramidi kavramı testleri sınıflandırma ve otomatize testler yazmada sağlam bir perspektif sunuyor olsa da Google’ın getirdiği test büyüklüğü boyutu pratikte konuyu ele almada farklı bir bakış açısı sunuyor. Küçük testler tek thread, orta büyüklükteki testler tek makine, büyük testler ise sınırsız makine içeriyor.

Bizim sınıflandırmamızın Google ile tam örtüşmesi gerekmiyor elbet fakat konuyu başka nasıl ele alabileceğimize dair yepyeni bir bakış açısı örneği veriyor.

Teknolojiler ve araçlar değiştikçe ve geliştikçe testlere olan yaklaşımımızın da değişmeye açık olduğu açıktır. Değişmeyecek olansa hız, maliyet, değişime dirençlilik, stabilite, bakım kolaylığı, gerçeğe yakınlık gibi temel kriterler olacaktır. Bu temel kriterleri gözetip sağlıklı bir test paketi oluşturmaya çalışmak ve sürdürmek gerekecektir.

Kaynaklar

Sign up to discover human stories that deepen your understanding of the world.

Free

Distraction-free reading. No ads.

Organize your knowledge with lists and highlights.

Tell your story. Find your audience.

Membership

Read member-only stories

Support writers you read most

Earn money for your writing

Listen to audio narrations

Read offline with the Medium app

--

--

Süleyman Fazıl Yeşil
Süleyman Fazıl Yeşil

No responses yet

Write a response