Kategori arşivi: Yazılım Hakkında Genel Düşünceler

Yazılım Hakkında Genel Düşünceler

Yeni Dönemin Programcilari

Yazilimda esas olan kurgu ve akistir. Genel hatlari ile verileri bagimliliklari ile birlikte kurgulayamazsaniz ve birbirleri ile olan iliskilerde veri akisini düzenleyemezseniz, uygulama ya istenileni yapamaz ya da sig bir sekilde yapabilir.

Bu temel prensipleri yapay zeka araclarini kullaniyor olsaniz bile degistiremezsiniz. Kisaca neyin nasil yapilmasi gerektigi hakkinda detayli bilginiz ya da fikriniz yoksa, sizi yapay zeka araclari bile kurtaramaz.

Buradan cikarilmasi gereken baska bir sonuc da, yapay zeka araclarini bilgisayar mühendisleri, yazilim mühendisleri ve yazilim konusunda tecrübeli insanlar haricinde kimsenin yazilim ile ilgi konularda verimli kullanamayacagidir.

Bu sebeple yazilim bitmemistir sadece baska bir seviye evrilmistir. O seviyenin calisanlari da eski düzende bu ise hakim olan yazilimcilardir. Diger tayfa zaten isin zorlugunu gördükce ve bu hype sona ermeye basladiginda cok baska islerle (bir sonraki hype) ugrasiyor olacaklar ve bu yazilim olmayacak.

Yazılımda Geriye Kalan Hersey Artık Teferruattır!

Blockchain teknolojisini hatirlayalim. Her derde deva olacak diye pazarlandi. Aslinda bulduklari cözüm icin cok uzun bir zaman sorun aradilar. O arayis hala devam ediyor…

Cözümleri son care gibi dikte etmek ve müstterinin gercek gereksinimlerini göz ardi etmek yazilimda bir gelenek. Her daim “böyle bir cözüm var, sizin probleminize uyuyor ya da kendinizi buna göre uydurun” denildi müsteriye. Bu yazilimcilarin kendilerini ayricalikli bir konumda görmelerini sagladi, onlara kendilerini iyi hissettirdi, cünkü nasil yapildigini biliyorlardi, ama ne icin oldugu önemli degildi, cünkü yapabiliyorlardi.

Yazilimcilar ne sorusuna degil, nasil sorusuna yarin yokmuscasina odaklandilar ve bunu mühendislik olarak gördüler. Kendilerini essiz ve iyi hissettiler. Lakin tarih, buna yazilim tarihi de dahil tekerrürden ibarettir. Her devirde nasil sorusu soyutlandi ve üzeri örtülerek yeni bir teknolojik devrime dönüstü. Örnek mi? 60 larda IBM’in ilk gelistirdigi delikli kartlardan bugün kod yazmak icin kullandigimiz editör arasinda kalan bilimum teknolojik gelisme!

Yazilimcilar her yeni soyutluk katmanina ragmen nasil sorusunun cevabini aramaya devam ettiler. Hic bunu baska bir etkenin daha iyi yapabilecegi akillarinin ucuna bile gelmedi. Simdi akillari baslarina zorla getirilmis oldu.

Bu zaman diliminde kod yazmayi yazilim yapmak olarak algiladiklari icin bu teknik yetileri haricinde baska hicbir yetiye yatirim yapmadilar. “Ben yazilimciyim, test yazmam” diyen bircok yazilimci taniyorum. Allah yardimcilari olsun. RIP!

Bir sahsin elinden yapabildigi en iyi seyi alirsaniz, geriye ne kalir? Yazilimcilardan bahsediyorum…

Simdi isin özüne dönüs yolculugu basladi.

Neydi o öz? Ne sorusu! Müsteri ne istiyor? Bundan böyle sadece ve sadece bu soruya odaklananlar yazilimci olmaya devam edecekler. “Bunu nasil yaparim” sorunu sorma geregi duyanlar icin son duraktir.

Isin özü ne sorusudur. Nasil sorusunu sormak ve onu tatmin edici calismalar yapmak artik bir is yerini isgal etmek icin yeterli bir neden degildir. Is sahibi ve calisan olmak icin ne sorusundan mütevellit müsteri gereksinimlerini tatmin eden bir ürün ortaya koyayacak calismalar yapmak elzem olmustur. Her zaman isin özü buydu, sadece mühendislik kisvesi ile cözüm üretiyorum ben zihniyeti isin özüne degil, cepherlerindeki eglenceli alanlara ödaknaldi. Bu devir kapanmistir.

Ne sorusunun meyveleri cok kisa sürede ermeye basladi. Devir bu soruyu soranlarin devridir artik. Yazilimda geriye kalan hersey artik teferruattir!

Junior Yazılımcı Olsam Nasıl İş Bulurdum?

Yapay zekanin yayginlasmasi ile junior yazilimcilarin artik sektöre girmeleri hemen hemen imkansiz hale geldi.

Bugün üniversiteyi bitirmis bir junior yazilimci olsaydim, nasil is bulurdum?

Bu verecegim örnek acimasiz ve is gücünü sömürme olarak algilanacak, lakin back to the roots yapmak zorundayiz, aksi taktirde genclerin is gücü olarak kendilerini ispatlama sanslari kalmamistir.

Eskiden ve hala bazi mesleklerde is gücü nasil yetistirilir ve istihdam edilirdi?

Piyasaya adim atmak isteyen bir bireyin cirak olarak baslamasi gerekiyor. Zaman icinde yaptigi isi ögrendikce kalfa olur, oradan da ustaliga yükselir. Ciraklik döneminde gecimini saglamaya yetmeyecek bir harclik alir, kalfalik döneminde bogaz tokluguna calisir, usta olduktan sonra hayatini idame ettirecek sekilde para kazanir.

Bu sablonu alip, yazilima uygulayalim…

Bugün üniversiteyi bilgisayar mühendisi ya da benzeri bir dalda bitirdigimi düsünelim ve yazilimci olma hayallerim var. Eger dört sene zarfinda bu konuda kendimi gelistirmediysem, gecmis olsun. Bundan sonrasini okumaya zaten gerek yok!

Is basvurusu yapmiyorum. Tek yaptigim sey kendimi en az 1 sene bir odaya kapatip, gece gündüz yapay zeka yardimi ile (bu yaziyi yazdigim siralarda en güclü model claude opus 4.6) sürekli birseyler gelistiriyorum. Bunlar etrafimdaki insanlarin islerini kolaylastiracak türde projeler. Backend kismindan basliyorum, bunu web ve mobil tarafina tasiyorum. Yapay zekanin yazdigi kodu sürekli okuyorum ve problemleri nasil cözdügünü anlamaya calisiyorum. Birkac ay icinde zaten gerekli paternleri kod icinde okuyor olacagim. Bunlari sürekli tasarim sablonlari, tasarim. mimarileri, tasarim prensipleri konulari ile harmanliyorum. Buradaki nihayi amac bir problem icin hizli bir sekilde gerekli cözümü üretebilmek. Ne kadar cok koda maruz kalirsam, o oranda cözümler kafamda netlesecektir.

Ilk aylar vibe coding yaparken, ücüncü ayindan sonundan itibaren bir yazilimci gibi kod okumaya, degerlendirmeye ve yazmaya baslayacaksiniz. Kesinlikle ara vermeden devam etmek gerekiyor. Pes etmeden devam.

Bir sene sonunda topladigim bu tecrübeler ile cirak olarak yazilimci arayan yerlere hicbir beklentiimiz olmadan basvuruyoruz. Orada da bir sene tecrübe topladiktan sonra artik kendisini ispatlamis mid level bir programcisiniz yani kalfa oldunuz. Simdi sizi gecindirecek kadar maas kazanabileceginiz bir is bulmaniz cok daha kolay. Gözü hemen yüksek maaslarda olanlar yani usta olmadan usta statüsünde maas kazanmak isteyenler de bu noktadan sonra okumayi birakabilirler, cünkü onlarla rekabet eden cok insan olacaktir ve bu hayallerini gerceklestirmeleri imkansiz.

Bu patikayi izleyenler 3-5 sene icinde istedikleri seviyeye gelebilirler. Bu cok fedakarlik isteyen bir is, ama baska bir alternatifi kalmadi. Talebin yüksek oldugu dönemlerde ödenen maaslar cok insani bu yöne cekti, ama bu suni bir balondu ne yazik ki. Böyle zamanlarda bu kadar yüksek maaslar verilmesini beklemek hele hele is bulabilmek ne yazik ki hayal.

Refactoring Artik bir Zihin Aktivitesidir

Neden kodu yeniden yapilandirmak (refactoring) artik sadece bir zihin aktivitesidir?

Eskiden bir yazilim ürünü icin gelistirilecek özelliklerin bir maliyeti vardi. Buna göre de yer yer öncelik siralamasi yapilirdi. Simdi bu maliyet hemen hemen ortadan kalkmis durumda.

Maliyetin kaybolmaya yüz tutmasi, yazilimci olarak istedigimizi yapacagiz anlamina gelmiyor. Neden yazilim yaptigimizi hatirlayalim. Yazilim müsterisi icin yapilir. Yazilim müsteri gereksinimlerinin tatmin edildigi bir aractir. Durum böyle iken maliyet yerine odaklanmamiz gereken tek nokta müsteri memnuniyetidir.

Müsteri memnuniyeti sadece geri bildirim ile ölcülebilir. Bu sebeple Extreme Programming gibi yöntemlerle müsterinin karsisina her iki haftada bir yeni bir sürüm cikarirdik. Bu zaman dilimi artik günlere ve saatlere düsmüs durumda.

Maliyetin yerine yazilimda en önemli faktör olan memnuniyet almistir. Bu yazilimcinin nasil yapildigina degil, ne yapilmasi gerektigine odaklanmasini mümkün kilarken, cok hizli degisiklikler yaparak, müsterinin piyasa rekabet kosullarina adapte olmasi kolaylastirir olacaktir.

Bundan böyle yazilimda yap-boz mentalitesi hakim olacaktir. Klasik yöntemlerde test yazmadan yeniden yapilandirma (refactoring) mümkün olmadigi icin yazilimcilar yaptiklarini bozmaya ve yeniden yapmaya cesaret edemezlerdi. Bu problem ortadan kalkmistir. Her yeni bir uygulama özelligi MVP statüsündedir ve her an cöpe atilabilir ve yeniden yapilabilir.

Yeni refactoring yöntemi budur. Artik refactoring fiziksel kod üzerinde degil, zihinde olusan mental model üzerinde uygulanmaktadir. Bunun yansimasi olarak MVP ler olusur, cöpe atilir ve yeniden yapilir. Bu yüzden yazilimda refactoring artik sadece bir zihin aktivitesidir.

Ürün ve MVP Ötesi

Ürün ya da mvp cikarmak sorun degil, asil mevzu onu müsterisi ile bulusturmak. Kod yazip, ürün olusturmak baska bir sey, o ürünü canliya alip, calistirmak cok baska birsey. Bu yüzden her yazilimicinin yazilim haricinde hakim olmasi gereken teknik konulari basliklar olarak yaziyorum. Gerisini bilenler tamamlayabilir:

  • Linux isletim sistemini iyi tanimak gerekiyor, genelde sunucular ssh üzerinden yönetilir.
  • Ag ve topolojilerini tanimak, özellikle tcp/ip stack in nasil konfigüre edildigini bilmem gerekiyor. Herhangi bir sunucuyu rack icinde calisacak sekilde konfigüre edebilmelisiniz.
  • Sürüm yönetimi ile paketleme yapilmasi gerekir. Bu paketler daha sonra sunucularda calisir hale getirilir.
  • Loglari merkezi bir yerde toplamak elzemdir. Bu sekilde dagitik bir sistemde olup, bitenler takip edilebilir.
  • Sistemsel metriklerin takibi mecburidir.
  • Grafana ve benzeri araclarla monitoring yapilmak zorunlulugu vardir.
  • Gelen yüke göre dikey ya da yatay genisleme (scaling) yapilmasi gerekebilir.
  • Firewall konfigürasyonunu yapabilen sistemini koruyabilir.
  • Veri tabani replikasyonu cok önemli bir konu olup, ürünün gelecegini tayin edebilir.
  • Sunucularin birden fazla lokayonda isletilmeleri gerekebilir. Cogu zaman bu bir sarttir. Bu lokasyonlar arasinda state replikayonu gerekebilir.

Kod yazdim, isim bitti mi diyorsunuz?

Junior Yazilimcilar ve Vide Coding

“Vibe coding” terimi mevcut durumu tanimlamak icin artik yetersizdir.

Genc yazilimcilar artik vibe coding yapmiyorlar, usta cirak iliskisi icinde gerekli tüm temel ve üst bilgiyi hocalarindan (AI) ögreniyorlar.

Bizim dönemimizde eksik olan usta-cirak iliskisi artik olusmus durumda. Genc yazilimcilari bu nimetten faydalanmalari yerine uyarmak anlamsiz. Yaptiklari vibe coding degil, yeni düzenin ögrenme süreci ve bu sürecin sonunda kendilerini iyi yetistirmis olacaklar.

Neden bu kadar genc yazilimcilara güveniyorum? Cünkü bir yazilimci meraklidir. Merakli olmayan birisi yazilimci olamaz. Bu merak onlari yapay zeka tarafindan olusturulan kodun nasil calistigini anlamaya istecek ve bu sekilde ögrenecekler. Imtihan icin kopya hazirlayan ögrencileri düsünün. Onlarin kopya cekme tesebbüsleri zaten gereken seyleri ögrenmelerini sagladi.

Bu sürece yeni bir isim konmasi gerekiyor. Genc yazilimcilara sadece vibe coding yapiyorlar demek artik dogru degil.

Peki vibe coding nedir? Hicbir yazilimci egitimi ve altyapisi olmayan ve daha önce kod yazmamis birisinin yapay zekaya kod yazdirip, programci oldugunu düsünmeye baslamasidir. Burada kisa vadece prototip ve mvp tarzi ürünler cikabilir lakin sürdürülebilirligi yoktur, cünkü yazilim sadece kod yazmaktan ibaret degildir ve ustalik gerektirir.

Sizce junior programcilar icin vibe coding yerine hangi terimi kullanabiliriz? Önerilerinizi bekliyorum.

Analog Yazilimdan Dijital Yazilima Gecis

Yazilim camiasindaki güncel gelismeler Alice harikarlar diyarinda gibi hissettiriyor.

Artik iki dünya olustu: analog yazilim, dijital yazilim.

Benim gibi uzun yillardan beri yazilim yapanlar analog yazilim dünyasinda yetistiler.

Bugün yazilima yeni basliyanlar, dijital yazilim dünyasinin parcasi olarak yetisecekler. Onlarin mevcut yapilara adapte olmalari cok daha kolay.

Birkac ay öncesine kadar “vibe coding” in yeni baslayanlar icin uygun bir yöntem olmadigi fikrine sahiptim. Bu fikrim son günlerde degismis durumda!

Genc arkadaslar: hic vakit kaybetmeyin ve vibe coding yapmaya baslayin. Sizler dijital native olarak bu yeni yazilim dünyasina dogdunuz ve orada yetiseceksiniz. Yaninizda artik tam tesekküllü ve dünyanin en donanimli yazilimci ustasi oturuyor. Size yazilimin nasil yapilacagini en kisa sürede ögretecek. Sürekli ona kod yazdirin ve anlamadiginiz yerleri sorun. Bizim yillar boyu calisarak ögrendigimiz konulari siz isik hizinda ögreniyor olacaksiniz.

5 yil icinde cok daha iyi programcilarin yetistigini görecegiz.

Bir girisimci olarak yazilim ekibimde yapay zeka ile calisma süreclerine hakim junior yazilimcilara öncelik verecegim. Onlarin yetistirilmesi ve konuya hakimiyetleri cok daha kolay olacak.

Senior programcilar analog yazilimdan dijital yazilima gecis sürecinde avantajli konumdalar eger bu degisimi kabul edip, yeni süreclere adapta olabilirlerse.

1990 yilinda kod yazmaya baslamis ve son 30 yildir profesyonel yazilimci olarak calisan birisi olarak sunun altini cizerek söylemek isterim:

“Yazilim konusunda ben bundan daha heyecanli bir gelisme yasamadim”

Mindshift

Benim 20 sene imperatif program kod yazdiktan sonra fonksiyonel proglamaya gecisim cok zor olmustu. Beynimin en son hücresi bile imperatif düsünmeyi yeglerken, artik bunu baska türlü yapiyoruz demek yeterli olmuyor. Insanin bu kemiklesmis düsünce sablonlarini asmasi cok zor.

Yapay zeka baglaminda günümüzde senior (10+ yil tecrübe) olan programcilarin da buna benzer bir problem ile karsi karsiya olduklarini düsünüyorum.

Vibe coding yapan ama yapay zeka araclarini kullanmayi ögrenmis junior programcilarin bahsi gecen eski dönem programcilarini solladiklarini görebiliriz.

Bu sebeple degisime karsi koymadan hizli bir sekilde adapte olmak gerekiyor. Aksi taktirde programciliktan ekmek yemek artik mümkün olmayabilir. Bunun ilk ibareleri uzun yillar programcilik yapmis senior programcilarin sektör degistirme ya da yönetim kademesine gecmeleri ile kendisini gösterecektir.

Code Generation ve Generative AI

Code generation konusunda nereden nereye geldik…

  • Üniversite yillarinda CASE araclari ile UML modelleri hazirlar ve tüm interface, dto ve entity gibi siniflarin otomatik olarak olusturulmasini saglardik.
  • Corba ve IDL ile servis arayüzlerini (interface) tanimlardik, ORB üzerinden remote object ile iletisim icin gerekli lokal proxy stub kodu otomatik olusturulurdu.
  • 2000 öncesi EJB döneminde local ya da remote interface siniflari olustururduk, implementasyonlarini EJB compiler/container kendisi yazip, kostururdu.
  • AOP (Aspect Oriented Programming) ile aspektler yazardik, gerekli baglantilari olusturmak icin aop derleyicisi kod olusturdu ve derledi.
  • Spring ile tüm konfigürasyonu xml dosyalarinda olustururduk, proxy implementasyonlarini ve enjeksiyonu Spring IOC container yapardi. Daha sonra anotasyonlar kullanmaya basladik, arka planda gerekli kod yine build ya da runtime icinde olusturulurdu.
  • Mapstruct ile entity – dto mapping icin gerekli kodu yazmaya gerek kalmadi.
  • Lombok ile tüm get/set metotlarindan kurtulduk.

Kodu generate ettirmek icin kod yazarken, simdilerde kod yazmadan gen ai ile tüm kodun olusmasini seyrediyoruz.

Bundan sonra ne gelecek?

Makina kodu yazmanin ne kadar zor oldugunu hatirlayalim. Assembly ile programcilikta büyük bir sicrama yasandi. Bu da yetmedi ve Fortran, C ve Java gibi diller ile daha yüksek bir soyutluk seviyesine cikildi.

Böyle bir sürecin aynisi yapay zeka kullanimi icin yasanabilir. Artik yapay zekanin ürettigi koda hakim olamayan programcilar icin yeni araclar gelistirilecek. Onlar yardimi ile yapay zeka kullanimi daha da soyutlanacak ve kontrol edilebilir hale gelecek.

Sistemsel olarak imperatif programlama ile yapay zekaya kodu nasil yazilacagini söylemek ayni seyler. Bu detaylarda kaybolmak anlamina geliyor. Oysaki deklaratif tarzda ne yapilacagini söylememiz yeterli. Gerisini kullandigimiz programlama dili, arac ya da yapay zeka halletmeli. Gelecekte daha cok imperatif araclar kullanarak kod yaziyor olacagiz.

Yazilimcilar Icin Yapay Zeka Kullanma Klavuzu

Ben günlük islerimde IntelliJ / Android Studio ve Copilot Claude Sonnet 4.5 yapay zeka modelini kullaniyorum.

Zaman icinde kendim icin yapay zeka öncesinden cok farkli bir calisma modeli gelistirdim. Bu bir nevi yapay zeka kullanim klavuzu. Yapay zeka araclari gelistirildi lakin bunlarla nasil programci olarak calismamiz gerektigine dair bize bir kullanim klavuzu verilmedi. Herkes kendi basina bunlari kesfetmek zorunda. Bu konuya katki amaciyla kendi tecrübelerimi paylasmak istedim. Hep birlikte belki genel kapsamli bir calisma ve kullanim klavuvuzu gelistirebiliriz. Bu yazim benim icin bir nevi “programming best practices with ai” görevini görecek.

Yeni Soyutluk Seviyesi:
Eskiden programci olarak mimari, nesneye yönelik programlama, tasarim sablonlari gibi konularla istigal ederek, program gelistirirdik. Simdilerde daha yüksek bir soyutluk seviyesinde, yapay zeka ile gelen prompt engineering, context, mcp, token ve agent gibi yeni terminoloji, yöntem ve araclari kullanarak program yaziyoruz. Bu ister istemez program yazma tekniklerimizi, aliskanliklarimizi ve soyutlama yetimizi dogrudan etkiliyor. Burada atabilecegimiz en dogru adim soguk suya atlamak yani biran önce bu yeniliklere adapte olup, gerektigi sekilde hareket edebilmek olacaktir.

Dogru Model:
Ben gpt 5 ve türevleri ile cok saglikli sonuc alamadim. Gemini türü modelleri de denedim. Lakin Claude Sonnet 4.5 ile en iyi sonuclari aldim. Bu konuda herkesin LLM secimi yapilan isin baglamina göre degisecektir. Dogru modelin calisma verimliligini artirir, yanlis model ile calismak günün sonunda hicbir sey basaramamislik hissi birakabilir.

Copilot Ayarlari:
Copilot icin proje workspace icinde copilot-instructions.md isimli dosyada yapay zekanin islem yapmadan önce dikkate almasi gereken hususlari listeledim. Burada metotlarin nasil yapilandirilmasi gerektiginden, UI tasarimi, kullanilacak catilar, kod stili ve testlere kadar bircok konuda yapay zekaya yardim edici bilgiler vermek mümkün. Buradaki amac ciktinin daha deterministik ve belli bir yapida olmasini saglamak.

Prompt Mühendisligi:
Bir yazilim mühendisi artik bir prompt mühendisine dönüsmek zorunda. Ben bu tabiri cok hos bulmuyorum, lakin programcinin artik kendi anadilinde, gelistirilmesini istedigi uygulamanin nasil olmasi gerektigine dair en ince detaya kadar bunu net bir sekilde ifade edebilir ve kagida dökebilir hale gelmesi gerektigi zorunlulugunun oldugunu düsünüyorum. Yapay zeka ile interaksiyonda destan yazmaya gerek yok. Prompt ne kadar sade ise, o oranda ciktinin beklentileri karsiladigini gördüm. Buradaki en önemli nokta context olarak ifade edilen kapsama alaninin iyi tanimlanmis ve dogru verilerle doldurulmus olmasidir. Ben örnegin copilot ile calisirken her prompt icin gerekli siniflari, görselleri ve belgeleri context icinde tanimliyorum. Bu sekilde copilot ve dolayli olarak Claude Sonnet benim prompt icinde ifade ettiklerimi workspace icinde yer alan icerikle daha iyi eslestirip, yapilmasi gerekenleri tespit ettikten sonra, hayata gecirebiliyor.

Kisaca ne kadar prompt, o kadar köfte!

Prompt History:
Ben workspace icinde prompt.md isminde bir dosyada, kullandigim tüm promptlari tutuyorum. Aslinda güncel prompt icin aklima gelen ilk düsünceleri bu dosyaya eklemeye basliyorum. Ilk düzeltmelerden sonra promptu en güncel hali ile copilota veriyorum. Zaman icinde bu bir nevi dokümentasyona dönüsüyor ve günler sonra tekrar yazdiklarima göz atarak, nasil buraya geldigime dair saglam bir fikre sahip olabiliyorum. Bazen copilot ile olusan kodu revert / rollback yapiyorum. Bu gibi durumlarda da ayni promptu history dosyasinda lokalize ederek, yeniden kullanma sansim oluyor.

Architecture First:
Hicbir ön hazirlik yapmadan copilota kod yazdirmanin ismi vide coding. Bunun nereye varacagi malum. Bu sebeple yeni bir uygulamaya basladigimda kesinlikle yapay zeka araci kullanmiyorum. Uygulamanin temellerini kendi bilgi, tecrübem ve düsüncelerim dogrultunda elimle atmam gerekiyor. Bu sekilde ilk mimari ve katmansal yapi ortaya cikiyor. Akabinde yapay zeka olusan yapiyi kendi isinde kopyalarak, gerekli kod parcalarini olusturmasi daha kolay hale geliyor.

Building Blocks & Subprompting:
Ben bir uygulama özelligini (feature) yapay zekaya bir prompt ile yaptirmaya calismiyorum. Bu cok iyi sonuclar veren bir yöntem olmadi benim icin. Bunun yerine uygulama özelligini kücük parcaciklara bölerek, her parca icin bir prompt yaziyorum. Burada öncelik her zaman uygulama özelliginin temelini olusturan domain siniflari, veri tabani yapisi, repository ve servis siniflari gibi yapilari ortaya cikartacak temel promptlar araciligi ile uygulama özelliginin temellerini atabilmek. Temeller insa edildikten sonra yeni promplar ile kat cikilmasi yani istenilen türde bir uygulama özelliginin programlanmasi kolaylasiyor.

Gereksiz Muhabbet:
Copilot ile gereksiz yere “iyi yaptin aferin” yada “tesekkür ederim” gibi seyler yazarak sohbet etmiyorum. Bu tür geri bildirimlerin programci olarak size hicbir faydasi yok. Yapay zeka bu sebeple gereksiz yere enerji sarf eder ve siz de en kötüsü faydasiz bir islem icin token harcamis olursunuz.

Sürekli Commit:
LLM ile calismak klasik programlama mantigina aykiri bir durum. Programcilik mantik geregi deterministik ve yer yer idempotent olmak zorunda. Lakin ben ayni promptun ayni sonucu verdigine hic sahit olmadim. LLM’ler deterministik yapilar degil, istatistiksel yapilar, yani cikan sonuclar da ne yazik ki istatistiksel olabilirlik hesaplamalarina dayaniyor yani deterministik olamiyorlar.

Bunu bildigim icin her prompt öncesinde son yapilanlari korumak amaciyla git commit ile tüm degisiklikleri saglama aliyorum. Prompt sonrasinda olusan program parcalari hosuma gitmiyorsa, rollback ile cikis noktama geri dönüyorum ve promptu yeniden sekillendirip, devam ediyorum. Sonuc hosuma gittigi taktirde code review yaparak tekrar bir commit aliyorum ve bir sonraki prompta geciyorum.

Olmadiysa Rollback:
Biraz önce bahsettigim gibi LLM ciktilari deterministik degil. Bunun yani sira prompt ve context yetersiz kaldigi icin cikti istenilen türde olmayabilir. Bu durumda kodu degistirmek yerine rollback yaparak, sifirdan basliyorum. Kodu degistirmek ve calistirmaya calismak cok zaman kaybina sebep olabilir. Bu yüzden sifirdan baslayabilek her daim daha iyi bir opsiyondur.

Regresion Testing:
Eger promptlar ile test yazmiyorsaniz ya da yazdirmiyorsaniz, gecmis olsun. Er ya da gec kullandiginiz LLM daha önce yaptigi degisiklikleri ezecek ve calisir durumda olan program parcalari calismaz hale gelecektir. Buna programcilar arasinda regresyon – geriye gitme denir. Bunun icin özel bir test kategorisi bile vardir: regresyon testleri.

Ne kadar yapay zeka da dense, LLM bir yapay zeka olmakdan isik yili kadar uzak. Bu yüzden genis kapsamli degisikliklerde uygulamanin bazi bölümlerinde istenmeyen degisiklikler vücut bulmus olabilir. Bunlari tespit etmenin tek yolu, otomatik calisan testlerdir.

LLM’i sürekli refactoring yapan bir makina gibi görmek gerekiyor. Her prompt potansiyel bir refactoring. LLM istedigi sekilde kodu yogurur. Bunun mutlaka ve mutlaka yan etkileri olacaktir. Bunlari tespit etmenin tek yolu test yapmaktir.

Saglam bir test seti olmayan programcinin promptlar ile gelistiridigi bir uygulama canliya alindiginda en iyi ihtimalle deterministik olmayan davranislar sergileyebilirken, en kötü ihtimalle calismaz hale gelebilir. Bu sebeple her prompt mutlaka gerekli test setinin olusmasini mecbur kilmalidir.

Continuous Review:
LLM ciktisi bir kodun programci tarafindan iki sebeple gözden gecirilmesi yani code review yapilmasi gerekmektedir.

Ilk sebep kodun ne kadar gecerli oldugunun kontrolüdür. Kod genel gecerli kurallara uymadigi icin yeniden yapilandirilabilir. Bunun icin mutlaka bir code review yapilmasi zorunludur.

Ikinci sebep benim icin daha önemli bir sebep. Ben LLM ciktisi kodu okuyarak cok sey ögreniyorum. LLM tarafindan olusturulan kodun, bu gezegen üstünde yayasan tüm programcilarin acik kaynak olarak github ve co üzerinde tuttuklari kodlarin damitilmis hali oldugunu düsünecek olursak, her ciktidan cok sey ögrenmenin ne kadar enfes olacagi asikardir.

Her dönemi kendine has (bknz. Kant – Zeitgeist) bir ruhu, yasam ve calisma sekli var. Programcilar olarak yeni bir döneme girdik. Hizli adapte olanlar ayakta kalirken, digerlerine ne olacak bilinmez.

Özcan Acar
EOF (End Of Fun)

Bindikleri Dalı Kesiyorlar

Kendi bindikleri dalı kestiklerini anladıkları için çark etmeye başladılar.

Yapay zeka yazılımcıyı ortadan kaldıramaz. Yazımcılar yapay zeka araçlarını çok iyi kullanır hale gelecekler. Senior olanlar junior olanların gelişimine odaklanarak usta pozisyonuna geçecekler. Çırak olan junior yazılımcılar ustalarından ve yapay zekadan bu işin nasıl yapıldığını öğrenip, kendileri de zamanı geldiğinde usta konumuna yükselecekler. Bu şekilde şimdiye kadar var olmayan usta-çırak ilişkisi oluşturulmuş olacak.

Yazılımcılar İçin Yeni Dönem Başlıyor

Sadece mevcut bilgi ve tecrübe seviyesini ölçmeye yönelik yazılımcı mülakatları sona erecek.

Artık adaylardan copilot gibi yapay zeka araçları ile sunulan bir fikir için çok hızlı ve çalışır bir protip (MVP) oluşturmaları istenecek. Birkaç saatlik bir zaman diliminde fikirden, çalışan ürüne kadar tüm yazılım yelpazesi ve adayın bu süreçte nelere hakim olduğu kontrol edilecek.

Böylece usta-çırak ilişkisi ile yetiştirilmeyen yazılımcılar için bunun önü açılmış oluyor.

Nasıl bir kalp cerrahi asistan doktor olarak yıllarca bir kalp operasyonunun pratik ve teorik eğitimini ustalarından alıyorsa, yazılımcıların da böyle bir ilişki içinde yetkinlik kazanmaları gerekir hale gelecek. Bunun olmadığı bir ortamda yetişen tüm yazılımcılar vibecoding ile mülakatta istenenleri yapabilir anca ne yaptıklarını açıklayamayacak halde olacaklar.

Artan yazılım talebi ile yazılımcılar bu işin kendi kendilerine öğrenmeye bırakılamayacak kadar kritik unsur haline geliyorlar. Yazılım bir zanaattır ve ustası tarafından öğretilmek zorunda.

Bknz: https://kurumsaljava.com/2012/05/08/programcilik-sanat-mi-zanaat-mi/

Yapay Zeka İle Çevik Olma

Yazılımda çevik olmanın tek yöntemi test yazmaktır. Yazılım projelerinin zaman içinde yeniden yapılandırılamayarak telef olmalarının tek sebebi test eksikliğidir.

Bknz: https://kurumsaljava.com/2012/04/05/cevikligin-boylesi/

Artık test yazmamak için hiçbir mazuriyet kalmamıştır. Claude Sonet 4.5 yeni bir API için 20 ye yakın entegrasyon testini 5 dakikanın altında olusurdu. O testleri yazmak günler ya da haftalar alabilir.

Test yazmamanın ana sebebi yani maliyetli olması nedeni artık ortadan kalkmıştır.

Çevik süreçlerinin oturamamasının da ana sebebi refactoring yapma kabiliyetinin geliştirilememesi idi.

Bu taktirde XP gibi çevik süreçlerin tekrar bir rönesans yaşaması söz konusu.

Statükocu Zihniyet

Yazılımcı olarak bazı gerçeklerle yüzleşmemiz gerekiyor.

Copilotu sadece bir sefer Claude Sonet 4.5 ya da türevleri ile deneyimleyen bir yazılımcı, anti yapay zeka savlarının birçoğunun gerçek dışı olduğunu görecektir. Nedir bu anti yapay zeka savları?

  • Vibecoding iyi netice vermez,
  • Senior programcı vazgeçilmezdir,
  • Yapay zeka yazılımcı mesleğini ortadan kaldıramaz,
  • ve türevleri…

Yapay zekanın tecrübeli bir yazılımcıyı ortadan kaldırma ihtimalinin olmadığını düşünenlerdenim. Aynı şekilde vibecoding hızlı MVP tarzı prototipleme için kullanılabilecek bir yöntemdir. Ancak yazılıma yeni başlayanlar için vibecoding bir zehirdir. Adapte olabilen senior vazgeçilmez olacaktır.

Bunlara değindikten sonra, asıl anlatmak istediğim mevcuza gelmek istiyorum.

Statükocu zihniyet…

Bence çok hızlı bir şekilde bu statükoyu korumacı zihniyetten vazgeçmek gerekiyor. Kendimize asıl sormamız gereken soru şu:

Biz yazılımcılar için çalışma tarzı nasıl değişecek ve biz buna nasıl adapte olabiliriz?

Bugün modern bir araba fabrikasında insanlar sadece gözlemci durumda. Tamamen karanlıkta faaliyet gösteren fabrikalar var. Bir senior seviyesinde gelmiş programcının teknik olarak yapay zeka ile rekabet etmesi imkansız. Senior pogramcılar ilerde yapay zeka işini yaparken gözlemci ve kontrolcü konumunda kalabilir.

Benim yanımda örneğin benim kadar, belki de daha yetkin birisi oturuyor artık: LLM. Ben ona hergün iş yaptırıyorum ve zamandan tasarruf ediyorum. İşi bilen birisinin ona ne yapması gerektiğini anlatması işin küçük bir bölümü artık. Kısa bir zaman sonra yazılımcılar olarak çok farklı bir ortamda çalışıyor olacağız. Buna ne kadar hazırız?

Kod yazmak işin çok küçük bir kısmı. Biz yazılımcılar bu dar alanda yapay zeka ile rekabet etmek zorunda değiliz. Bırakalım angaryayı onlar yapsın. Bizi ayrıcalıklı kılacak olan hardskilller değil, softskillerdir. Onlara odaklanarak her an artan yazılım talebini rahatlıkla karşılayabiliriz.

Yapmamız gereken tek şey değişimi kabul edip, onunla yaşama yetisi geliştirmektir.

Özcan Acar
EOF (End Of Fun)

Sekiz Milyar Değişik İşletim Sistemi

Son zamanlarda alışkanlıkların oluşumu, etkileri ve yapıları hakkında bilgi edinme ve uygulama fırsatım oldu. Bir yazılımcı olarak insan vücudunu donanım, kişiliğini oluşturan tüm davranış biçimlerini ve diğer yetilerini yazılım olarak gördüğüm için alışkanlıkları da bu pencereden incelediğim bu yazıyı kaleme almaya çalıştım.

Kısaca bir alışkanlığın ne olduğunu ve anatomisini inceleyelim. Bir alışkanlık beyin tarafından otomatize edilmiş bir rutindir. Alışkanlık çalışır durumda iken beyin herhangi bir kaynağını bu rutini koşturmak için aktif olarak kullanmaz. Bu rutin otomatik olarak çalışır ve bu esnada beyin başka işler ile meşgul olabilir. Beynin rutinleri oluşturmasındaki ana amaç zaten budur. Beyin alışkanlığı oluşturan adımları bilir ve onların otomatik olarak bir rutin çerçevesinde bilinçli bir şekilde dahil olmadan koşturulmasını sağlar. Bu şekilde beyin işlemci çekirdeklerine fazla yükleme yapmadan paralel birkaç işle meşgul olabilir.

Benim için bir alışkanlık sahip olduğumuz donanım üzerindeki işletim sistemi bünyesinde koşturulan bir background taşk gibi bir şey. İşletim sistemimiz cronjob calıştırır gibi gerekli durumlarda bir alışkanlığı da koşturabilmektedir. Kısaca bir alışkanlığı işletim sisteminde çalışan bir app olarak düşünelim.

Kabaca bir alışkanlığın ne olduğunu tanımladıktan sonra anatomisine bir göz atalım. Bunu anlamak için bir alışkanlığın nasıl oluştuğunu incelememiz gerekiyor. James Clear’ın Atomic Habits isimli kitabında bir alışkanlığın oluşabilmesi için bu dört adımın arka arkaya atılması gerektiğini belirtiyor: tetikleyici neden, istek-arzu, işlem ve ödüllendirme. Eğer bu dört adım dopamin salgılanması (ödüllendirme) ile son buluyorsa, beyin tarafından bir alışkanlığa dönüştürülmesi çok olası, çünkü beyin, içinde dopamin olan her şeyle çok ilgili ve bunu sağlamak için gerekli rutinleri oluşturma konusunda çok istekli. Bunu bir örnek üzerinde inceleyelim. Spor yapmak bir alışkanlık haline dönüşmüş olabilir, çünkü spor aktivitesinin bitimi ile vücut dopamin salgılar ve bu dopamin spor aktivitesinin beyin tarafından bir rutin haline dönüştürülmesini kolaylaştırır. Nitekim spor yapanlar bu aktiviteyi gerçekleştirmediklerinde mutsuz olurlar ve her fırsatı spor yaparak değerlendirirler. Kısaca alışkanlık bir bağımlılığa dönüşmüştür. Aynı şey sigara içenler için de geçerlidir.

Peki ama bir alışkanlık durup, dururken nasıl aktif yani app olarak işletim sistemimizin belleğinde çalışır hale gelir? Bunun için şahsın tetikleyici bir neden ile uyarılması gerekiyor. Örneğin sigara içen birisi için sigara paketini görmesi alışkanlığı aktif hale getirmek için tetikleyi bir nedendir. Aynı şekilde sabah yürüyüşlerini alışkanlık haline getirmiş birisi için spor ayakkabılarını görmesi tetikleyici bir nedendir. Kısaca tetikleyici neden mevcut değilse, alışkanlık app olarak çalışamaz. Eğer kötü alışkanlıklarımızdan kurtulmak istiyorsak, tetikleyici nedenleri gözümüzün önünden almamız yeterli olacaktır. Bu ama sigara içmek gibi ağır bağımlılıklar için ne kadar geçerli olur, tartışılır. Benim bahsettiğim alışkanlıklar daha ziyada yüksek bağımlılık ihtiva etmeyen günlük rutinlerdir.

İnsanlar aynı donanıma sahip olmalarına rağmen, sahip oldukları işletim sistemi değişiklik göstermektedir. Bu sebepten dolayı sekiz milyar değişik işletim sistemi gibi bir başlık kullandım. Kanımca bu gezegende insan sayısı kadar işletim sistemi türü mevcuttur. İşletim sistemimiz ve bununla birlikte sahip olduğumuz tüm yazılım sisteminin temeli doğumla atılır. Aile ve toplumun yapısı sahip olduğumuz işletim sistemini modeller. Burada beynimizin yapısını dikkate alarak işletim sisteminin bir çekirdek (ilkel beyin) ve modüllerden (gelişmiş üst beyin) oluştuğunu düşünebiliriz. Beynimizin çekirdeği bizim kontrolümüzde olmayan, evrim sürecinde oluşturulmuş ve hayatta kalabilmek için faydalı olduğu görülmüş rutinler ihtiva etmektedir. Bunun en güzel örneği bir tehlike anında kaçma hissi, düşünmeden nefes alıp, verme, gözlerimizi kırpa ya da karanlıkta korkma halidir. İnsanın bilinçli olarak bu rutinlere müdehale etmesi, onları değiştirmesi çok mümkün değildir. Bunları işletim sisteminde çalışan arka olan appler olarak düşünebiliriz.

İşletim sistemimizin çekirdeğini düşündüğümüz zaman aslında bu gezegende yaşayan herkesin bu konuda bir farklılık göstermediğini görebilmekteyiz. Her insan bu seviyede aynı rutinlere ve bu açıdan aynı işletim sistemine sahiptir. İşletim sistemlerini ayrı kılan çekirdeği değil, üst beyini şekillendiren modüllerdir. Bu modüller insanı diğerlerinde ayrı kılan zihin, zihniyet, empati, kültür, eğitim, vicdan, arzu gibi yapılarla şekillendirilir ve her insanda farklılık gösterirler. Bu da her insanın işletim sisteminin başka bir yapıda olmasını beraberinde getirir.

Her yeni alışkanlık ile işletim sistemi daha zengin bir hale gelir. Burada ilk etapta alışkanlığın iyi ya da kötü olarak ayrılması önem taşımamaktadır. Önemli olan işletim sisteminin yeni appler ile güncellenmesidir. Nitekim kişisel gelişim penceresinden durumu değerlendirdiğimizde bilinçli olarak kazanılan alışkanlıkların insanı bir üst seviyeye taşıdığını söyleyebiliriz. Örneğin her gün yirmi kitap sayfası okuma alışkanlığı bir sene sonra otuz kitap okumuş ve kişisel gelişim olarak bambaşka bir yere evrilmiş yeni bir birey doğuracaktır. Kitap okuma alışkanlığı ile işletim sistemi yepyeni bir boyuta taşınmakta ve muazzam bir gelişime sahne olmaktadır. Bu açıdan bakıldığında insanların sahip oldukları işletim sistemlerini eşsiz kılan kişisel gelişim için bilinçli olarak geliştirilen alışkanlıklardır.

Yazılım böyle bir şey. İstediğinizi yapabilirsiniz. Aynı şey sahip olduğumuz işletim sistemleri için de geçerlidir. Bir sene sonra nerede ve kim olacağımıza sahip olduğumuz işletim sistemi değil, alışkanlıklarımız karar vermektedir. Küçük rutinler olarak başlayan yeni alışkanlıklar işletim sistemlerimizi güncellerken belli bir zaman diliminden sonra bizleri düşünülmesi bile zor yerlere taşıyabilirler. Bu yüzden işletim sistemlerimizin sürekli güncellenmesi gerekmektedir. Bunun kararını sadece ve sadece bilinçli alışkanlıklar oluşturacak olan birey kendisi verebilir.

Buradan çıkartabileceğimiz sonuç kişisel gelişim için alışkanlıklar geliştirmemiz gerektiğidir. Kişisel gelişim süreklilik ister, bunun da alışkanlıkların doğasında yer alan otomatizm ile daha kolay üstesinden gelebiliriz.


EOF (End Of Fun)
Özcan Acar

Gitflow ve Verdiği Zararlar

Artık git ile çalışmayan kalmadı sanırım. Bilindiği üzere gitflow isminde bir çalışma modeli var. Bu modelde uzun ömürlü feature branchlar ve ihtiva ettikleri daha geniş kapsamlı commitler ile çalışılmakta. Bu yazımda sizlerle bu modelin dejavantajları ve sebep olduğu sorunlar ve zorunlulukklar hakkındaki fikirlerimi paylaşmak istiyorum.

İlk etapta bir feature branch bünyesinde çalışmak mantıklı bir opsiyon olarak görünebilir. Programcı mevcut master ya da develop branchdan “git checkout -b feature/my-feature-branch” ile bir feature branch oluşturur ve üstlendiği işi bu branch bünyesinde geliştirir. Bu süreç sonunda bir pull request yardımı ile feature branch bünyesindeki değişilik ekibe duyurulur ve code review yapılması sağlanır. Bu işlem sonunda feature branch bünyesindeki yeni kodun kaynağa geri dönmesi amacıyla trunk olarak tabir edilen master ya da develop ile merge edilir. Bu işlem gerçekleştiği anda trunk sürekli entegrasyon dahilinda build ve deploy edilir.

Şimdi bu süreçde neler olduğuna ve ne gibi sorunların doğduğuna bir göz atalım. Öncelikle feature branch oluşumu gerçekten (reality) kopuş ve izolasyona girmek anlamına gelmektedir. Yazılımcı kendisini feature branch oluşturuğu andan itibaren öncelikle sürekli entegrasyon sürecinin dışına almaktadır. Sürekli entegrasyon uygulamanın her daim tüm değişikliklerle derlenebilir, deploy edilebilir ve test edilebilir olmasını sağlamaktadır. Sürekli entegrasyon trunk dahilinde yapıldığı taktirde tüm ekibin yaptığı değişiklikleri entegre edebilir. Feature branch bünyesinde çalışan bir yazılımcının yaptığı değişiklikler feature branch içinde kalındığı sürece sürekli entegre edilmez ve bu şekilde yazılımcı feature branch bünyesinde izolasyona mahkum kalır. Burada trunk ile rebase yapılması hiçbir şey ifade etmez, çünkü yazılımcının rebase ile trunkdan aldığı değişiklikler sürekli entegrasyondaki kaliteyi sağlamaz, yani rebase sürekli entegrasyonun yerini alamaz.

Feature branch bünyesindeki commit sayısı ve commitlerin hacimleri daha büyüktür. Yazılımcı trunk bünyesindeki gelişmelere bakmak zorunda kalmadan rahat bir şekilde kendi işi üzerinde çalışmaya devam eder. Bu haftaları alan bir sürece dönüşebilir. İki haftadan fazla bir gelişmeyi ihtiva eden feature branchı tekrar trunk ile merge etmek çok zor bir hal alabilir ya da mümkün değildir. Bu kod bazında da entegrasyonun sekteye uğradığı anlamına gelmektedir ve bu projeyi sıkıntı ve rizikoya solabilecek bir durumdur.

Diğer bir sorun ise feature branch modelinin feature branch deployment zorunluluğu doğuruabilmesidir. Yazılımcı feature branch bünyesinde çalıştığı sürece bir paralel evrende yaşar ve projenin sürekli entegrasyon ve sürekli deployment gibi nimetlerinden faydalanamaz. Sürekli deployment ile uygulama ihtiva ettiği tüm özellikleri ile tanımlı bir ortamda çalışır hale getirilmektedir. Feature branchın trunka dönmediği sürece ne oranda entegre ve deploy edilebilir olduğu muammadır. Bazı şartlar altında feature branch bünyesinde yapılan değişikliklerin test edilebilmesi için dev ortamına deploy edilmesini gerektirebilir. Örneğin milyonlarda kafka mesajını lokalde işleyemeyen (kapasite eksikliği, işlemin uzun sürmesi ya da gerekli veritabanı ya da kafka altyapısına erişimin lokalde olmaması) bir fetaure branch test edilmek amacıyla dev ortamına taşınmak zorunda kalınacaktır. Bu feature branch deployment anlamına gelmektedir. Böyle bir yapıdaki en büyük problem yine diğer değişikliklerle entegre olmamış bir yapının kendi izolasyonu bünyesinde deploy edilip, test edilmesidir. Tam entegre olmamış bir uygulamanin bu şekilde test edilmesi hiçbir şey ifade etmemektedir, çünkü entegratif bir yapı ile oluşması mümkün yan etkilerin ortaya çıkma şansı bulunmamaktadır. Bunun yanı sıra feature branch deploy edildikten sonra yazılımcı sadece kendi bölümüne konsantre olduğu için diğer bölümler test edilemeyecek ve yine oluşması mümkün yan etkilerin ortaya çıkması engellenmiş olacaktır.

Gitflow sürekli entegrasyon ve sürekli deployment önünde büyük engel oluşturmakta ve paralel evrenlerin oluşmasını teşvik etmektedir. Feature branch ile çalışmak yazılımcıya güven verebilir. Buradaki ana niyet değişikliklerin izole bir şekilde uygulamaya zarar vermeden yapılmasına alan tanımaktır. Lakin oluşan bu alan feature branch ile ın arasını açmakta ve entegrasyonun yapılmasını engellemektedir. Bu sebeple gitflow modeli ile çalışılması tavsiye edilmektedir.

Bunun yerine trunkbased calışmak, yapılan değişikliklerin meydan vereceği etkileri anında görmeyi mümkün kılacaktır. Bu geri bildirim uygulamanın hangi durumda olduğunu görmek ve gerekli tedbirlerin alınmasını kolaylaştıracaktır.


EOF (End Of Fun)
Özcan Acar

Çevik Süreçler Neden Dikiş Tutturamadı

Çevik süreçlerin tam olarak ne olduğunu kavramamış, çevik süreçler ile bir proje uygulamamış, scrum yaparak çevik olduğunu ve çevik süreçlerin bir işe yaramadığını zanneden şahısların “agile is dead” naralarını unutmadık. Ben de çevik süreçler öldü diyorum, lakin ekliyorum: “yaşasın çevik süreçler”. Çevik süreçlerin yıldızlarının bundan sonra nasıl parlayacaklarını ve tam anlamıyla yazılım geliştirme süreçlerine hakim olacaklarını kendi yazılımcı perspektifimden sizinle paylaşmaya çalışacağım.

Bir yazılım sistemi belli bir büyüklüğe ulaştıktan sonra, kendi dinamiklerini oluşturarak, yeni özellikler eklenmek suretiyle geliştirilmesini engeller hale gelir. Bunun başlıca sebebi, mevcut mimarinin yeni özellikleri taşıyacak yapıda olmamasıdır. Bunu bilen yazılımcılar iki nokta arasındaki en kısa mesafeyi seçmek yerine, en az dirençle karşılacakları patikayı yeğlerler. Bunun neye sebebiyet verdiğini çok iyi biliyoruz: teknik borç! Burada yazılımcının büyük oranda suçu olmadığını düşünüyorum. Daha ziyade yazılımdan bihaber yöneticiler zaman baskısı oluşturarak, işi aceleye getirmektedirler ve aslında başarısızlığın temelini ilk saniyeden itibaren atmaktadırlar. Bu duruma yazılımcılar tepki vermeyerek ve kimsenin etlisine, sütlüsüne karışmayarak ortak olmaktadırlar. Lakin şöyle yöneticilerin karşılarında ağız birliği yaparak, dik durabilseler ve “madem bu işi biz yapıyoruz, o zaman ne zaman biteceğine de biz karar veririz” diyebilseler, çok daha güzel işler ortaya koyabilecekler.

Yazılımda bu sorun ile başa çıkmanın tek bir yolu mevcut, o da refactoring olarak adlandırılan kodun yeniden yapılandırılmasıdır. Teoride her yazılımcının yeniden yapılandırmanın ne anlama geldiğini bildiğini düşünebiliriz. Lakin pratik açıdan baktığımızda, yeniden yapılandırma işleminin nasıl yapıldığı konusuna çok az sayıda yazılımcı hakimdir. Bunun da başlıca sebebi, yazılım testleri üzerinde olan hakimiyetsizliktir.

Refactoring tek başına yapılması mümkün olmayan bir işlemdir. Yapılan her değişiklik bir yan etki doğurabileceğinden, bu etkinin tespit edilebilmesi zaruridir. Bu kontrolü bir yazılımcının elden yapması mümkün değildir. Bu sebepten dolayı burada bir otomasyon ihtiyacı doğmaktadır. İhtiyaç duyulan kapsama alanına sahip olduğumuzu farz ettiğimizde, otomatik çalışan testler refactoring esnasında oluşan tüm yan etkileri tespit etmeyi mümkün kılacaklardır. Özetleyecek olursak, aslında yazılım sisteminin sürekli gelişimi için ihtiyaç duyduklarımız yeniden yapılandırma, test yazılımı ve test otomasyonudur. İşte çevik süreçlerin temelini de bunlar oluşturmaktadır. Eğer sürekli müşteri gereksinimlerini koda dökebiliyor ve mevcut sistemi genişletebiliyorsanız, çeviksiniz! aksi tektirde yazılımcılık oynuyorsunuz!

Çevik süreçler bünyesinde yer alan çevik metotların ana amacı yazılımda sürdürülebilirliği hedefliyor olmalarıdır. Burada sürdürülebilirlik terimini teknik açıdan ele almamız gerekmektedir. Sürdürülebilirlik bir yönetici için başka, bir yazılımcı için daha başka, bir patron için çok daha başka bir anlam ifade etmektedir. Ama doğru olan yaklaşım hangisidir? Şüphesiz herkes kendince doğru yaklaşım içindedir, çünkü beklentiler değişiktir. Lakin tekrar altını çizerek sormak istiyorum, teknik olarak sürdürülebilirlik ne anlama gelmektedir? Bunun cevabını verebilmek için öncelikle işin müşteri kısmını incelememiz gerekiyor.

Bir yazılım sistemi müşteri gereksinimleri doğrultusunda şekillenir. Yazılımcı olarak nihai amacımız, müşteri gereksinimlerini tatmin edecek bir sistem oraya koymaktır. Bu yüzden doğal olarak müşteriye kulak vermemiz ve ne istediğini iyi anlamamız gerekmektedir. Lakin müşteri ne istediğini tam olarak bilmekte midir? Ne yazık ki bu sorunun cevabı hayıdır. Burada yazılımcı olarak bizim için iki sıkıntı oluşmaktadır. Birincisi nasıl bir sistem oluşturmamız gerektiği sorusudur, ikincisi bu sistemin mimarisinin nasıl olması gerektiğidir. Bu iki sorunun da cevabını düşünerek, bulmamız mümkün değildir. O yüzden bir an önce kolları sıvıyarak, müşteriye yarım yamalak dile getirdiği gereksinimleri için kısa bir zaman dilimi sonunda bir şeyler göstermemiz gerekmektedir ki müşteri ne istediğine dair fikir üretmeye başlayabilsin. Bu yüzden çevik süreçlerde bir ya da iki haftalık iterasyonlar sonunda müşteriye mutlaka tespit edilen miktardaki özelliğin çalışır durumda olduğu sistemler sunulur. Bu prototipler müşteriden alınan geribildirimler doğrultusunda onun kullanacağı ürün olana kadar geliştirilir. Lakin burada tekrar sürdürülebilirlik konusuna değinmemiz gerekmektedir. Teknik olarak bu nasıl sağlanabilir? Bunun iki yöntemi mevcuttur: Teknik borç ve teslimiyet ya da kararlılık ile uygulanan “yap/boz” mentalitesi.

Eğer bir bina yapımı söz konusu ise, atılması gereken temelin ve uygulanmak istenen mimarinin bilinmesi gerekmektedir. Bu değişmez iki parametre yapılan binanın kaderidir. Eğer inşaat mühendisleri ve mimarlar yazılım yapmaya başlasalardı, hiç zorluk çekmeden bu işe adapte olabilirlerdi, çünkü günümüzde de yazılım bir bina inşaatı gibi ele alınmaktadır. Durum böyle olduğu için yazılım projelerinin çok büyük bir kısmı belli bir zaman sonra çıkmaza girerek, hem yazılım sistemini oluşturanları hem de müşterisini zorda bırakmaktadırlar.

Tekrar teknik sürdürülebilirlik sorusuna geri dönelim. Teknik sürdürüleiblirliğin bedeli ne yazık ki ağırdır. Öncelikle yazılımla uğraşan herkesin bu bedeli maddi ve manevi olarak ödemeye hazır olması gerekmektedir. Maddi açıdan baktığımızda yöneticilerin sürdürülebilirliği sağlamak adına daha fazla zamana ihtiyaç duyulduğunu ve bunun maliyeti artıracağını anlamaları, manevi açıdan baktığımızda yazılım mühendislerinin işin çıkmaza girdiğinde radikal kararlar vererek, refactoring teknikleri yöntemleri ile mevcudiyeti yeniden şekillendirme iradesine sahip olmaları gerektiğini görmekteyiz.

Günümüzde yapılan yazılımı yirmi sene önceki şekli ile kıyasladığımızda, aslında kafalarda fazla bir şeylerin değişmediğini görebilmekteyiz. Çoğu yazılım projesi yirmi sene önceki kafa yapısı (mindset) ile uygulanmaktadır. Yirmi sene önce aktif olarak hayatımıza giren çevik metotlar ihtiyaç duyulan dönüşümü sağlayamadılar. Bunun nedeni ne olabilir? Çevik süreçler performans gösterebilmek için çok radikal değişimlere ihtiyaç duymaktadırlar. Bu programcıların çalışma tarzlarından başlayarak, firmanın sahip olduğu yazılım geliştirme kültürüne kadar uzanana bir alanı kapsamaktadır.

Çevik süreçler programcı perspektifinden bakıldığında test güdümlü yazılım gibi radikal bir metodun uygulanmasını talep etmektedir. Yazılım birimlerini test etme konusunda pek kafa yormamış ve bu konuda tecrübesi olmayan bir yazılımcıdan test yazarak, yazılıma devam etmesini istemek, bir cobol yazılımcısına bugünden yarına nesne tabanlı program yazdırmaya çalışmak gibi birşeydir. Nitekim ikibinli yılların başlarında görev başında olan yazılımcıların bu fikri benimsemeleri mümkün olmadı. Onlar hep testlerin yazılımın en sonunda uygulanan bir aktivite olarak gördüler. Her zaman angarya olarak görülmüş test yazma olayının birden bire gündemin en başına oturarak, tüm gidişata yön verme fikrine o jenerasyon ne yazık ki adapte olamadı. Tekrar hatırlamakta fayda görüyorum. çevik olabilmeniz için yani müşteri gereksinimlerine her daim yazılım ile karşılık verebilmeniz için yeniden yapılandırma yapmak zorundasınız. Test olmadan yeniden yapılandırma yapılamaz. Bu yüzden çevik süreçlerin en temelinde bu iki prensip yatmaktadır: yeniden yapılandırma ve testler. Olay sadece test güdümlü yazılım ile bitmiyordu. Eşli programlama, sürekli entegatasyon, iterasyon bazlı yazılım gibi birçok çevik süreç metodu mevcut yazılım süreçlerini yer yer tersine çevirdiğinden dolayı, bu durum yazılımcılara sudan çıkmış balık hissi yaşatmakta ve adaptasyonu zorlaştırmaktaydı.

O dönemin yazılım sektöründe çalışanlarını şelale (waterfall) yazılım süreçlerinden geldiklerini unutmamamız gerekmektedir. O zamanın yazılım ekipleri müşteri gereksinimlerini en ince detayına kadar anladıktan ve kağıda döktükten sonra yazılım yapmaya başlarlardı. Burada bir senelik bir ön çalışma ve akabında yine birkaç sene sürebilen yazılım geliştirme sürecinden bahetmekteyiz. O gün olsun, bugün olsun yazılım projelerinin başarısız olmalarının ana sebeplerinden birisi ne yazık ki budur. Müşteri gereksinimleri doğrultusunda bir yazılım sistemine ihiyaç duyar. Müşterinin bu gereksinimleri piyasa şartları ile günlük bazda bile değişime uğrayabilirken, şelale yöntemleri ile birkaç sene kapalı kapılar arkasında yapılan yazılımın, bu zaman dilimi sonunda müşteri gereksinimlerine ne kadar az hitap edebilme potansiyeline sahip olduğunu görebilmekteyiz. Nitekim yazılım projelerindeki başarısızlık, müşteri gereksinimlerinin ne oranda karşılık göremediği ile çok yakından bağlantılı.

Hala ikibinli yılların başlarındayız ve çevik süreçlerin var olma savaşını yakından inceliyoruz. Şimdi o zamanlar yazılımın nasıl yapıldığını ve uygulanan mimarileri yakından inceleyerek, çevik süreçlerin neden tutunamadığını görmeye devam edelim.

İkibinli yılların başlarında Java programlama dili sahip olduğu ekosistemi ile tüm yazılım camiasına hakim olmaya başladı. O zamanlar EJB (Enterprise Java Beans) ile uygulama sunucularında yazılım yapanlar çok iyi hatırlayacaklardır. Tarih hep tekerrürden ibarettir, lakin umarım o günleri hiçbir yazılımcı tekrar yaşamak zorunda kalmaz. O devrin yazılım yöntemleri, araç ve gereçleri çevik süreçler için hazır değildi. Siz ne kadar iyi niyetli de olsanız ve test güdümlü yazılım ile birim testleri yazsanız bile, entegrasyon, onay/kabul testleri ve benzeri testlerin otomatik çalıştırılmaları onlarca ya da yüzlerce dakikanızı almaktaydı. Çok küçük bir değişilik yaptığınızı düşünün. Uygulamanın tekrar paketlenerek, uygulama sunucusunda çalışır hale gelmesi en az on dakikanıza mal olmaktaydı. Bunu çok defa yaşamış bir programcıyım. Çeviklik geribildirimle doğrudan orantılıdır. Yaptığınız şey size ne kadar hızlı geribildirim sağlarsa, o hızda değişilik yapabilirsiniz. Çevik olabilmek için yeniden yapılandırmamız gerekiyor. Bunun için testlerin sağladığı geribildirime ihtiyacımız var. Aynı şekilde sürekli entagrasyon yaparak, sistemin hangi derecede entegre olduğu geribildirimine ihtiyaç duymaktayız. Tüm çevik metotlar geribildirim prensibine dayanmaktadır. Geribildirim sağlanmadan, yaptığımız işin neresinde olduğunu anlamamız mümkün değildir. Geribildirim alma süresinin uzaması, bu o oranda çeviklikten uzaklaşma anlamına gelmektedir. Nitekim uygulama sunucularından kaynaklanan sebeplerden dolayı çok zaman alan test otomasyonu ilerleyebilmek adına devre dışı bırakılmak zorunda kalındı. O devrin EJB projelerine göz attığımız taktirde ya hiç ya da çok çok az oranda test koduna sahip olduğunu görebiliriz. Aynı durum bugün yapılan projeler için bile geçerlidir. Birçok güncel projede bir tane bile test metodu bulunmaması hiç şaşırtıcı değildir. Projelerin neden sürdürülebilir olmadıklarını ortadadır.

İkibin onlara kadar uygulanan yazılım prensip ve mimarileri de ne yazık ki çevik süreçler için gerekli ortamı oluşturamadı. Bahsetmiş olduğum zaman diliminde monolitler revaçtaydı. Durumun bugün bile değişik olduğu söylenemez. Trendin ama microservice mimarileri ile değiştiğine şahit olmaktayız. Peki bir monolit çevik süreçler ile geliştirilemez anlamına mı gelmektedir bu? Bu monolitin büyüklüğü ve bünyesinde barındırdığı kod kalitesi ile doğrudan ilişkili bir durum.

Çevik süreçler ile bir monoliti geliştirmek mümkün. Ama bu ne yazık ki belli bir büyüklüğe kadar sağlanabilir. O kritik büyüklüğe erişildikten sonra, sistemin küçük ve birbirinden bağımsız parçalardan oluşuyor ve geliştirilebiliyor olması gerekmektedir. Yani burada modüler bir yapıya ihtiyaç duyulmaktadır. Lakin bir monolit ne kadar modüler bir yapıda da olsa, tek bir parça olarak entegre edilmesi gerekmektedir. Burada yine test otomasyonu sorunları baş göstermektedir. Bunun yanı sıra entegrasyon süreleri uzamakta ve hızlıca yapılması gereken değişiklikler zaman almakta ve çeviklikten ne yazık ki ödün verilmesi zazuri bir hale gelmektedir. Tekrar hatırlayalım: hızlı bir şekilde geribildirim alınamayan ortamlarda çevik olunması imkansızdır.

Çevik bir yazılım ürünü yani her yeni müşteri gereksnimi ile yeniden yapılandırılabilen ve sürekli değişiklik ile başedebilen bir yazılım sistemi için belli başlı yazılım prensiplerinin, tasarım şablonlarının ve yazılım mimarilerinin uygulanması zaruridir. En basit hali ile clean code, solid prensipleri ve modüler bir mimariye ihtiyaç duyulmaktadır. Bunların uygulandığı bir proje test kültürü ile birleştiğinde her yeni müşteri gereksnimi ile hamur gibi yoğrulabilen yazılım ürünleri ortaya çıkamaktadır. Ama günümüzde bile birçok projede test kodunu bir yana bırakın clean code ya da solid prensiplerinden eser bile bulmak mümkün değildir. Testler olmadığından dolayı programcılar artık binlerce satırdan oluşan sınıfları değiştirmeye korkar olmuşlardır. Bu korkunun mevcut olduğu bir yerde nasıl yeni müşteri gereksinimleri hayat bulabilir?

Teknik açıdan baktığımızda, birçok sebepten dolayı çevik süreçlerin uygulanabilir olmadıklarını ve bu yüzden bir zaman sonra terk edildiklerini görebilmekteyiz. Lakin bunu tümüyle teknik sorunlarla ilişkilendirmek doğru değildir. Bunun yanı sıra firmalarda oluşan yazılım geliştirme kültürlerini ve organizasyonel yapıları da yakından incelememiz gerekmektedir.

Zaman içinde, çevik süreçlerin popülerliğinden de istifade edilerek benim fake agile olarak tabir ettiğim ve yazılımda çevikliği ima eden ama bunu sağlamaktan çok uzak sözde çevik süreçler oluştu. Bunların başında scrum geliyor. Scrum yakından incelendiğinde, aslında yakından, uzaktan çeviklik ile hiçbir alakası olmayan bir şey olduğu görülmektedir. Scrum ne çeviklik namına bir çevik metot ihtiva etmektedir ne de scrum ile çevik bir yazılım ürünü geliştirmek mümkündür. Çevik olmak için çevik metotlara ihtiyaç duyulmaktadır. Bunlar teknik anlamda yazılımcılar tarafından uygulanan metotlardır. Test güdümlü yazılım ya da eşli programlama gibi çevik metotları sadece yazılımcılar uygulayabilirler. Tüm çevik metotlar yazılımcılar için ve onların çevik hareket edebilmeleri için oluşturulmuşlardır. O yüzden çevik süreçler yardımı ile yazılım yapıyoruz denildiğinde, incelenmesi gereken nokta, çevik metotların ne oranda yazılımcılar tarafından yaşanıyor olduklarıdır.

Scrum ve benzeri yapılar ne yazık ki extreme programming gibi gerçek çevik metotların önüne geçtiler ve organizasyonlara çevik oldukları hissini verdiler. Kendini iyi hissetmekten öteye götüremeyen bu yaklaşımlar yazılım yapanların “çevik çalışıyor olmamıza rağmen, gerekli performansı alamıyoruz” söylenimlerine yol açtılar. Scrum ve benzeri yapılar ne yazık ki gerçek çevik süreçlerin adlarını karaladılar ve diskredite olmalarına sebep oldular. Bugün baktığınızda çevik olmayan bir yazılım ekibi bulamazsınız. Herkes scrum yapıyor ve herkes çevik. Ama ortaya çıkan yazılım ürünlerine göz attığımızda, hepsinin aynı dertlere sahip olduklarını ve aslında müşteri gereksnimlerine cevap veremeyecek yapıda, teknik ve mimarı açıdan tükenmiş olduklarını görmekteyiz.

Çevik süreçlerin önünü kapatan diğer bir durumda, yazılım ekiplerinin organizasyon yapısı olmuştur. Klasik bir yazılım ekibinde yazılımcı, testçi, analizci, sürümcü, proje yöneticisi gibi rolleri görmek mümkündür. Bir fabrika bandında üretim için uygun olan bu rol bazlı kadrolaşma, yazılım için ne yazık ki büyük sorular doğurmaktadır. Çevik süreçler yazılım, test, analiz, sürüm ve canlı işletme sorumluluğunu (production) yazılım ekibini oluşturan yazılımcılarda görmektedir. Yazılımcılar yazılım ile ilgili her şeyi yapamazlar mantığı doğru değildir. Mühendislik altyapısına sahip bu kişilerin günümüzde sadece kod yazmak için uygun olduklarını düşünmek en kibar ifadeyle onlara yapılan haksızlıktır. Nitekim Google tarafından uygulanan SRE (Site Reliability Engineering) ve DevOps gibi akımlara baktığımızda, yazılım ekibinin yazılım için gerekli tüm işlevleri yerine getirdiklerini görmekteyiz. Yazılımın, testin, analizin ve sürümün ayrı ekipler tarafından yapıldığı ortamlarda, çevik süreç faydalı olamamaktadır. Bu tür ayrıştırıcı organizasyonel yapılar olduğu sürece, çevik yazılım yapmak ne yazık ki mümkün değildir.

Yazılımda teknik sürdürülebilirlik için gerekli etkenleri ele almaya çalıştım. İsmi illa çevik süreç olmak zorunda değil. Lakin sürdürülebilirlik için atılması gereken somut adımlar var. Yazılımcının ana görevi sadece kod yazmak değildir. Mevcut organizasyonel yapılar bunun doğru olduğu fikrini savunsalar da teknik sürdürülebilirlik için yazılımcılara daha fazla sorumluluk verilmesi gerekmektedir. Yeni gereksinimlerin ivedilikle yazılım ürününe entegre edilebilmeleri için kodun her daim hamur gibi yoğrulabilmesi gerektirmektedir. Bunu sadece ve sadece yazılımcılar yapabilirler. Bunun için yüksek seviyede teknik beceriye ihiyaç duyulmaktadır. Bu da mühendisliğin hakkını verebilen yazılımcılarla mümkün olabilecek bir iştir.

Buraya kadar bahsetmiş olduğum nedenlerden dolayı çevik süreçler ne yazık ki hak ettikleri yere gelemediler ve haksız yere işe yaramaz damgasını gördüler. Ama ben bu durumun değişme sürecine girdiği düşüncesindeyim. Çevik süreçlerin işlemesini engelleyen unsurların yavaş yavaş kaybolmaya başladıklarına şahit olduğumuz bir dönemden geçiyoruz. Bu unsurlardan bazılarını yakından inceleyelim.

Artık günümüzde monolitlerin terk edilmeye yüz tutmuş bir mamarı türü olduğunu görmekteyiz. Modern yazılım sistemleri küçük ve birbirlerinden bağımsız çalışan modüllerden oluşmakta. Bunu sağlamak için microservice, webservice, rest ve soa gibi mimariler ve kubernetes, docker ve cloud gibi altyapı ve teknolojiler tercih edilmekte. Belli bir büyüklüğü geçmeyen ve otonom çalışan kod birimlerinin bakımı, geliştirilmesi, test edilmesi, sürümlenmesi ve işletilmesi geçmişe nazaran çok daha kolay.

Günümüzde yazılım ekiplerinin yapıları da değişmeye başladı. Örneğin Google bünyeside SRE olarak bilinen takımlar, test etmeden işletime kadar uygulamanın her yönüyle ilgileniyorlar. Birçok yeni ekip iki pizza prensibine göre oluşturuluyor. İki pizza ile doymayan bir ekip, büyüklüğü itibari ile monolite dönebilecek yapılar oluşturma eğilimi gösterebilmektedir. Küçük ekip ve tek sorumluluk prensibi kolay değiştirilebilir ve geliştirilebilir yazılım birimleri için sihirli bir formül haline geldi.

Bunun yanı sıra yazılımcılar eskiye nazaran tasarım prenbipleri, tasarım şablonları ve yazılım mimarileri konularında daha donanımlı hale geldiler. Clean code, software craftşman ve devops akımları, yazılımcıların teknik olarak daha da iyi olmaları için gerekli fikir ve pratikleri ihtiva ediyorlar. Ayrıca günümüzde eskiye kıyasla yazılımda teknik sürdürülebilirliği destekleyici, sonar, jenkins, git gibi birçok açık kaynaklı ürün bulmak mümkün.

Müşteri cephesinde de bazı değişiklilerin meydana geldiğini görmekteyiz. Müşteri eskisi gibi artık yıllar sonra kullanmaya başlayacağı bir yazılım ürünü siparişi vermek istemiyor. Müşteri bugün siparişini verdiği yazılım ürününün ilk sürümünü birkaç hafta sonra kullanmaya başlamak isteyebiliyor. Buna sadece çevik süreç ve iterasyon bazlı çalışma ile cevap vermek mümkün. Ayrıca müşteri için bir yazılım ürününün kullanılabilirlik seviyesi de değişmiş durumda. Piyasa rekabetinden doğan yeni gereksinimlerinden dolayı müşteri yıllarca beklemek yerine, sürekli geliştirilen, gereksinimler doğrultusunda adapte edilen ve her daim kullanılabliir yazılım ürünlerine yönelebiliyor.

Yirmi sene öncesi çevik süreçler ve tdd, bdd, pair programming, ci, refaçtoring, solid gibi çevik metotlar uygulanmaya başlandığında devir monolitlerin hakim olduğu mimarı devirdi . Çevik süreç ile bir monolit geliştirmek sadece iyi bir niyetten öteye gidemedi ve bu yüzden çevik süreçlerin bir işe yaramadığı düşünüldü. Oysaki container ve microservice dünyası için çevik süreç biçilmiş bir kaftan. Yıldızı söndüğü zannedilen çevik süreçler bulut bilişim ile ne olduklarını ispatlayacaklar.


Not: Eğer extreme programming gibi safkan bir çevik sürecin nasıl işlediğini merak ediyorsanız, size pratik agile isimli ücresiz kitabımı tavsiye etmek isterim. Çevik süreçler hakkındaki yazılarıma buradan, buradan ve buradan ulaşabilirsiniz.


EOF (End Of Fun)
Özcan Acar

Bilginin Evrimi

Yazılımda bilginin yarı ömrü ne yazık ki altı ayın altına düşmüş durumda. Yazılımcılar eskiye nazaran daha çok bilgi edinmek zorundalar. Bunda yazılımda soyutlamanın hızlanmasının büyük bir rolü mevcut. Soyutlama ve geldiğimiz noktayı bu yazımda kaleme almaya çalışmıştım.

Soyutlama işlemi bilginin evrimi için gerekli bir süreç. Evrimin olmadığı yerde gelişme olmaz. Evrim süreci bilginin geçerliliğini ispat etmekle mükellefken, soyutlama süreci de bilginin kullanıldığı anlamına gelmekte. İnsanlık var olduğu sürece, bu ilişki bilginin bir balon gibi şişip, sonsuzluğa doğru büyüyeceği anlamına geliyor, çünkü insanoğlu doğası gereği soyutlamadan yapamaz. Bu yüzden soyutlama işlemini bilgisel evrimin akaryakıtı olarak görebiliriz. Okumaya devam et →