Alperen Keleş
alpkeles99@gmail.com
Teknik, Pratik, Teknoloji ve Teori
Bugün yazılımla alakalı tartışmaların pek çoğunun temelinde bir kavramlar çatışması yatıyor. Yazılımcı matematik bilmeli mi? Akademisyenler sektöre gelse iş bulabilir mi? Yeni mezun neleri bilmeli? Biraz üzerine uğraştığımız takdirde pek çok türevini bulabileceğimiz bu soruların temelinde bence başlıkta bahsettiğim kavramlar arasında tam anlamıyla bir ayrım olmaması önemli bir rol oynuyor. Dolayısıyla ben bu yazımda bu kavramları birbirinden en azından belli bir seviyede ayrıştırmaya çalışacağım. Tabii ki bu ayrıştırma esnasındaki dil benim bu kavramlara atfettiğim anlamlar üzerinden olacak, sizler bu kavramları aynı şekilde görmüyor olabilirsiniz. İsimlere değil, altta yatan kategorizasyona odaklanmanız benim anlatmaya çalıştıklarımı aktarmam açısından daha faydalı olabilir.
Bu kategorizasyonu yapmak için programlamaya bakış açımı biraz detaylandırmam gerekiyor. Benim gözümde programlama, en temelde veri yapılarının verimli bir şekilde manipüle edilmesine, ve bu veri yapılarını okuyan farklı programların birbiriyle bu veri yapıları üzerinden iletişim kurmasına indirgenebilir.
Bir web uygulaması hayal edelim. Kullanıcının etkileştiği tarayıcının altında DOM(Document Object Model) veri yapısı, tarayıcı ile iletişim kurmamızı sağlayan API(Application Programming Interface), ve DOM’un üzerinde manipülasyonlar yapabilmemizi ve API’lar üzerinden hem tarayıcıyla hem de dış dünyayla iletişime geçebilmemizi sağlayan bir Javascript motoru var.
Burada devreye tabii ki mühendisliğin en temel ilkelerinden birisi olan soyutlama(abstraction) giriyor. Tarayıcının bize sağladığı fetch fonksiyonu dış dünyayla HTTP ismini verdiğimiz bir ağ protokolü üzerinden iletişime geçerken, programcılar çoğu zaman tarayıcının kendi arayüzü üzerine yazılmış daha kompleks kütüphaneler kullanarak belli detayları halletmeyi bir alt katmana bırakabiliyor. Burada bize sağlanan her bir soyutlama katmanı(abstraction layer) aslında bir teknoloji. Ulaşabildiğimiz teknolojiler bizim işimizi yapabilmemiz için inanılmaz elzem, çünkü her teknoloji bizim için gereksiz belli detayları ortadan kaldırarak hedefimize daha kolay ulaşabilmemizi sağlıyor. Örnek olarak eğer HTTP’nin üzerinde çalıştığı TCP bize güvenilir veri transferi(reliable data transfer) sağlamasaydı, bu sefer ağda oluşacak herhangi bir hatayı kullandığımız HTTP kütüphanesinin çözümlemesi gerekirdi. Eğer ki bu işi HTTP yapmasaydı, o zaman bizim kendimiz yapmamız gerekirdi. Alt katmanlarda bizim kendimizi yormak istemediğimiz detayların çözülmesi bizim için bu sebepten dolayı çok değerli, işin kendisine odaklanmamıza imkan sağlıyor.
E peki pratik nedir? Pratik, benim gözümde, çeşitli teknolojilerin(bir kısmını biz de üretiyor olabiliriz kendi yazılım projelerimizde) bir ürün oluşturma uğruna birleştirilme süreci. Bugün yazılım mühendisliği dediğimizde işin çoğunluğunu pratik içeriyor. Pratikte başarılı olmak sistemsel düşünme(systems thinking) gerektiriyor. Farklı alt sistemlerin sınırları(boundaries), sistemlerin birbirleriyle iletişim protokolleri, bu sistemleri birleştirerek yeni sistemler oluşturmak, bu yeni sistemlerin dış dünyayla olan etkileşimlerini tanımlamak. Aynı zamanda pratikte başarılı olmanın 2 tane daha gerekliliği var. Bunların ilki çeşitli teknolojileri kullanabilmek(teknoloji), ikincisi ise üzerinde çalıştığın teknolojileri anlamak(teknik). Ben bunları farklı bir terminolojide teknik genişlik(farklı farklı teknolojileri kullanabiliyor olmak) ve teknik derinlik(kullandığın teknolojilerin temelini anlıyor olmak) olarak da ayırmayı seviyorum, ancak bu yazıda şu ana kadar kurguladığım teknoloji ve teknik terminolojisine sadık kalarak devam edelim.
Bir teknolojiyi kullanabilmek ne demek peki, bir teknolojiyi kullanıp nasıl anlamıyor olabiliriz? Bu soruyu sorduktan sonra teknolojiyi anlamanın bir spektrum olduğunu kabul etmek gerekiyor. Herhangi bir teknolojiyi kullanabilmek için onun üzerinde bu sistemde X girdisinin sonucunda Y çıktısı çıkar şeklinde bir anlayışa sahip olmak gerekiyor ki sistemi doğru bir şekilde kullanabilelim. Ancak zamanında Sedat Kapanoğlu’nun Street Coder(Sokak Kodcusu) kitabını okurken yazdığım bir cevap yazısında bahsettiğim gibi bir sistemin ölçmediğimiz herhangi bir davranışını ölçmek çok mümkün değil. Burayı biraz açalım.
Az önce bahsettiğim sistemsel anlayış modeli yukarıdaki resimdeki gibi. Sistemin içerisinde ne yaşandığını bilmediğimiz, çok da umursamadığımız, girdi ve çıktılara baktığımız bir sistem.
Ancak gerçek hayattaki sistemler maalesef ki bu kadar basit değiller. Öncelikle çoğu sistem tam anlamıyla izole değil, iç duruma sahip(stateful) sistemlerin davranışları önceki aksiyonlara göre değişiyor. Mesela kullandığınız bir API önbellek uyguluyorsa(caching) bu durumda ilk kullanımınızda 10 saniye süren bir hesaplama ikinci seferinde 10 milisaniye sürebilir. İlk kez açtığınızda doğru çalışan bir arayüz birkaç rastgele hareket sonrasında yazarlarının hayal etmediği bir hale düşüp, oradan sağlıklı bir çıkış yolu bulamayabilir. Bunun yanında girdi-çıktı bazlı sistemler anlayışında tam olarak tüm çıktıları anlayamıyor olmamız da ciddi bir problem. Bilgisayarınızda çalıştırdığınız bir program için CPU/GPU/Hafıza(Memory) kullanımı, çalışma zamanı, çoklu çekirdek(multicore) performansı gibi yalnızca program çıktısına baktığınızda göremeyeceğiniz pek çok farklı değişkeni önemseriz. Girdi-çıktı açısından baktığımızda makul gözüken bir program bu tarz dolaylı şekillerde ölçülen metriklerde makul olmayabilir.
Önemli diğer bir nokta da sistemin tüm girdi-çıktılarını bir X-Y listesi olarak tutmanın çok mümkün olmaması. Bu aşamada sistemin içine bakmadan yalnızca girdi çıktıya baktığımızda yanlış varsayımlar ile bir sistemi kullanıyor olmamız içten bile değil. Örnek olarak 2 adet matematiksel fonksiyonu alalım.
f(x) = x // 100
g(x) = (x + 1) // 100
(//) burada Python’daki gibi aşağıya yuvarlamayı(floor) ifade ediyor. Biraz incelediğimizde bu fonksiyonların %99 oranında aynı sonucu verdiğini görebiliyoruz. Sonuçlar yalnızca x % 100 == 99 olan durumlarda fark edecek, çünkü x % 100 = 99 ==> g(x) = f(x) + 1, yani 99, 199, 299 gibi sayılarda ikinci fonksiyonun sonucu ilk fonksiyondan 1 fazla olacak.
Matematiksel basit bir fonksiyon üzerinde gösterince bu tarz örnekler sanki gerçek hayatta karşımıza çıkamazmış, oyuncakmış gibi gözükebiliyor, ancak gerçek hayatta yazdığımız ürünlerdeki pek çok hata(bug) da benzer bir temelden ortaya çıkıyor. Denediğimiz girdi-çıktı çiftlerinde bir problem olmadığında sistemin içeriğini yeterince anlama çabası göstermediğimiz takdirde hızlı bir şekilde yanılabiliyoruz. Halbuki yukarıda verdiğim fonksiyonların davranışlarını anlayan bir kişinin yapacağı ilk şey aa bu fonksiyon acaba sınır değerlerde nasıl davranıyor deyip x=-1, 0, 1, 99, 100, 101 için testler yazardı.
Dolayısıyla kullandığımız teknolojileri anlamak, onları yalnızca ben X yaptığımda Y oluyor seviyesinde değil, daha temelde ben X yaptığımda sistem sırasıyla A, B, C işlemlerini uyguluyor, sonucunda da Y ortaya çıkıyor şeklinde anlamak demek. Bunu da yalnızca işlemler seviyesinde değil, aynı zamanda o işlemlerin tükettiği(consume) ve ürettiği(produce) veri yapıları seviyesinde anlamak.
Mülakatlarda insanların sormayı sevdiği artık görece klişe haline gelmiş bir soru var, “Ben Google.com’da bir tuşa bastığım andan karşıma bir öneri gelmesi anına kadar neler yaşanıyor” diye. Bu sorunun en güzel taraflarından birisi herkesin daha iyi anladığı yere odaklanması cevaplarken, yüksek ihtimalle siz de öyle yaptınız. Eğer sürekli DOM manipülasyonu ve tarayıcının iç işleyişi ile uğraşan birisi iseniz tarayıcı o keypress event’ini yakaladığı andan itibaren neler yapıyor onu düşündünüz, işinizin çoğu network üzerinden bir sunucu ile haberleşmekse Google sunucusu ile nasıl iletişim kurduğuna odaklandınız, eğer arama motorları ilginizi çekiyorsa sizin girdinizin Google’ın indekslediği koca web’de nasıl bulunduğuna odaklandınız.
Hangi katmana odaklanırsanız odaklanın, işleyişin o katmanı ile ilgili detaylı bir teknik tartışmaya girmek için o katmanda kullanılan veri yapısını anlamanız gerek. Mesela siz tarayıcıda bir butona bastığınızda tarayıcı sizin bastığınız lokasyonda o butonun olduğunu nasıl anlıyor? Bir sitedeki buton sayısı arttığında tarayıcının bu buton tespit etme hızı düşer mi? Eğer bu hız düşüyorsa nasıl arttırılabilir, eğer düşmüyorsa tarayıcının o hızı koruyabilme sebebi nedir? Bu soruları cevaplamak için DOM’u, tarayıcının DOM’u tutma ve işleme şeklini, DOM’un altında yatan veri yapılarını ve algoritmaları anlayabilmek gerekiyor.
Teknik, tam olarak bu anlayış benim gözümde. Bir sistemin sadece ne yaptığını değil, nasıl yaptığını anlamak, ortaya çıkan fenomenlerin arkasındaki sebepleri keşfedebilme kabiliyeti. Teknik bahis konusu olduğunda genelde ilk akla gelen veri yapıları ve algoritmalar olsa da, burada bu anlayışa da küçük bir eleştiri getirmek isterim. Veri yapıları konuşulduğunda bahsi seçen veri yapıları on yıllardır akademide derinlemesine çalışılmış, çoğu zaman matematiksel objelerden türetilmiş, pratikte bizlerin problemlerimizi modellemek için kullandığımız araçlar. Bunlara en basit örnekler tabii ki klasik liste, ağaç, graf gibi yaılar. Gerçek hayatta çoğu zaman bu temel veri yapılarını değil, onlardan türetilmiş alana özel(domain-specific) versiyonlarını kullanıyoruz, bunu da çok pratik sebeplerle yapıyoruz, verimlilik. Az önceki buton örneğindeki gibi bir problemi tarayıcıdaki butonların lokasyonlarını bir liste gibi tutarak naif bir şekilde her buton için “şu an ben buna tıkladım mı” demek yerine, QuadTree ya da KDTree gibi 2 boyutlu görsel ağaçlar kullanarak logaritmik zamanda yapmak mümkün.
Bu örnek bile benim eleştirimi tam anlamıyla yansıtmıyor, çünkü hala bir veri yapısı hakkında konuşuyoruz. Benim asıl konuşmak istediğim konu ise veri modelleri. Web hakkında konuştuğumuzda en temel veri modeli DOM, ancak benim şahsi hobilerimden birisi resim formatları mesela, JPEG, PNG, GIF, HDR. En başta da bahsettiğim gibi, programlama benim gözümde en temelde veri yapılarının verimli bir şekilde manipüle edilmesi. Bu verimlilik de ancak ve ancak altta yatan veri modelini doğru şekilde anlayıp onu doğru veri yapılarında tutup doğru algoritmalarla manipüle ettiğimizde ortaya çıkabiliyor. Teknik, ya da teknik derinlik, manipüle ettiğimiz veriyi hem soyut ve felsefik, hem de somut ve pratik seviyede anlamayı gerektiriyor. Bir resim formatı tasarlarken ilk sorulması gereken soru şu aslında.
Bir resim nedir?
Eğer bu soruya cevabınız 2 boyutlu düzlemde üç kanalda(RGB) pikseller bütünü ise, sizi uyarmam gerekiyor ki fazlasıyla hatalısınız. Bugün alışık olduğumuz en yaygın model RGB kanallarını içeren 2 boyutlu bir düzlem olsa da, günün sonunda bir veri formatının daha soyut sorulara da odaklanması gerekiyor. Mesela şeffaflık(transparency) objelerin üst üste gelebilmesi ve öndeki objenin arkadaki objeyi belli bir noktaya kadar kapatabilmesi anlamına geliyor. O halde bir resim 3 boyutlu bir yapı mıdır? Diğer yanda RGB resimler için yalnızca bir renk modeli, RGBA, CMY, HSL gibi farklı renk modelleri de var. Fotoğraflar için klasik 255’lik renk skalaları yetersiz kalabiliyor, o durumlarda HDR(High Dynamic Range) formatlar var.
Burada da teknikten, teoriye kaymaya başlıyoruz. Teknik var olan veri modellerini anlayıp onların üzerine veri yapıları kurgulamak ise, teori o veri modellerini sorgulayıp yeni modeller ortaya atmak demek. Bu noktada sizlere kafamdaki bu 4 kavramın hepsini toplayan bir model sunma vakti gelmiş olabilir.
Bu modelin aşırı kapsayıcı olduğuna dair bir iddiam olmamakla birlikte, geniş resme dair faydalı fikirler verdiğini düşünüyorum. Endüstri ve akademiyi 2 ayrı kategori olarak düşünmek yerine, uçtan uca bir spektrum gibi düşünmek gerekiyor bence. Pratik sistemler üzerine çalışan akademik laboratuvarlar olduğu gibi, özellikle gezegen seviyesinde milyarlarca insanın hayatını etkileyen ürünler üzerine çalışan Google, Amazon, Meta gibi şirketlerin araştırma laboratuvarlarından yepyeni teoriler ortaya çıkabiliyor. Akademinin teorize ettiği modellerin üzerine tasarlanan veri yapıları ve sistemler zaman içerisinde önce kütüphanelere, sonrasında ürünlere ve sistemlere dönüştürülüyor. Dolayısıyla bugün önümüze çıkan her türlü ürün bir zamanlar bir araştırmacının teorize ettiği bir temele kurulu dersem yalan söylemiş olmam diye düşünüyorum.
Uzun ve dağınık bir tartışma için özür dileyerek, en başta bahsettiğim sorulara bağlayıp ardından da yazıyı bitirmek istiyorum.
Yazılımcı matematik bilmeli mi?
Teoriden pratiğe doğru giden yolculukta, matematiğe olan ihtiyaç gittikçe azalıyor. Sistem kurgulamak ya da teknoloji öğrenmek matematiksel bakış açısından fayda görse de matematik gerektiren işler değiller. Ancak özellikle daha yüksek ölçekte işler yaptıkça, küçük hataların ve verimsizliklerin hem riski hem de maliyeti artmaya başlıyor. Yıllık 100.000$ kazancı olan bir şirket için %5-10’luk bir maliyet optimizasyonu çok fark etmeyebilecek iken, yıllık kazancı 10.000.000$ olan bir şirket için 3 kişiyi tam zamanlı çalıştırabileceği bir problem haline gelmeye başlıyor. Bunun yanında daha çeşitli bir kullanıcı kitlesine sahip olmak temelinde üzerinde uğraştığın yazılımın kendisini de daha kompleks hale getiriyor. Kullanıcıların coğrafi, kültürel, ekonomik anlamda çeşitlenmesi yazılımın da buna göre evrilmesine sebep oluyor. Artan kompleksitenin daha fazla hata ile sonuçlanması, verimsizliklerin etkisinin çok daha büyük olması gibi problemler yazılıma girdi-çıktı çiftleri olarak bakmayı imkansız hale getiriyor, o pozisyonlardaki çalışanların teknoloji katmanından teknik katmanına doğru kaymasına sebep oluyor. Tekniğe, hatta bir noktada teoriye doğru kayan bu yolculukta matematik gitgide daha önemli hale gelmeye başlıyor, çünkü kompleks sistemleri matematiksel bir model olmadan anlamak maalesef ki mümkün değil. Matematiğin kişiler arasında kesin(precise) bir dil sağlaması, matematiksel modellerin bağımsız kişilerce incelenebilir, kanıtlanabilir, yanlışlanabilir olması onu kompleks sistemler ve veri modelleri hakkında konuşmak için elimizdeki en iyi araç haline getiriyor.
Akademisyenler sektöre gelse iş bulabilir mi?
Bu tartışma pratikte yapıldığında çoğu zaman akademisyenlerin işin çoğunluğunu içeren teknoloji tarafında iş bulamayacağı yönünde bir argümanla yapılıyor, ve bence bunu söyleyenler haklılar. Endüstride standart haline gelmiş küçük bir küme haricinde akademisyenlerin teknoloji bilgisi çok limitli. Ancak bu tartışmaları yapan kişilerin kaçırdığı nokta akademisyenlerin işinin zaten teknoloji öğrenmek değil, o teknolojilerin altında yatan veri modellerini ve veri yapılarını çalışmak olduğu. O veri yapılarını ve modellerini iyi şekilde anlayan bir kişinin onların üstüne kurulu teknolojileri anlaması ise aslında o modelleri anlamayan bir kişiye göre çok daha zor. Dolayısıyla iyi bir akademisyenin özellikle sektörde kendine daha çok uyan teknik tarafta bir işe girmekte ve orada başarılı olmakta hiçbir zorluk çekeceğini düşünmüyorum.
Yeni mezun neleri bilmeli?
Yeni mezunlarla alakalı en büyük problem ise, onlardan teknolojik bilgi beklemek. X dili, Y kütüphanesi, Z framework’ü, T aracı… Bu beklenti pek çok kişi tarafından(ben de dahil) haklı bir şekilde eleştiriliyor, çünkü 4 yıllık lisans eğitimi çoğu zaman muhafazakar olarak nitelendirilen bir şekilde teknoloji eğitmekten kaçıp, neredeyse tamamen teori ve teknik üzerine kurulu. Bu eğitim sisteminden çıkan bir öğrenciden teknoloji bilgisi beklendiğinde öğrenciler bocalıyor, şaşırıyor, hem şirketlere, hem sektöre, hem de akademiye karşı haklı bir sinir doluyor. Halbuki kişinin teknik derinliği, verilen bir veri modelini anlama kabiliyeti, ona yeni bir domain verildiğinde o domain’i kavrayıp kendi alışık olduğu teknolojiler ile modelleyebiliyor olması benim gözümde hem daha değerli, hem 4 yıllık bir bilgisayar mühendisliği mezununun yapabilmesi gerektiğini düşündüğüm bir özellik, hem de öğrencinin önünü X mi öğreneyim Y mi öğreneyim gibi ikilemlere itmektense daha temel problemlere odaklanması için açıyor.
Biraz uzun bir yazı oldu, tek oturuşta yazdım aynı zamanda. Umarım okurken keyif almışsınızdır, ve umarım sizin bakış açınıza biraz da olsa etki edebilmişimdir.
Alperen Keleş
alpkeles99@gmail.com