No Silver Bullet ya da Sihirli Değnek Yok

Frederick Brooks, 1987 Nisan ayında IEEE’nin Computer dergisinde “No Silver Bullet: Essence and Accidents of Software Engineering” başlıklı bir makale yayınladı. (Makalenin aslını burada, Türkçe çevirisini burada bulabilirsiniz.) Makale temel olarak yazılım mühendisliğinin doğasını inceliyor ve bu alanda o ana kadar olan gelişmeleri masaya yatırıyor. İlk yayınlandığında yazılım dünyasının klasikleri arasına giren, yazılımın neliği hakkında düşünce dünyamızı aydınlatan bu makale, lehinde ya da alehinde pek çok düşüncenin ortaya çıkmasını sağlamıştır. Makale, daha sonra yazarın, yine klasiklerden olmuş “The Mythical Man Month” isimli  kitabında da yer aldı. Chapel Hill’deki University of North Carolina’da Bilgisayar Bilimleri bölümünü kurup, 1964 ve 84 yılları arasında da bölümün başkanlığını yapan Prof. Brooks aynı zamanda IBM System/360’ın da babası olarak bilinmektedir.

Brooks makalesinde, Aristo’nun ayrımından yola çıkarak yazılımın asli ve arızi zorlukları olduğunu ifade ediyor. Brooks’a göre yazılımın asli zorlukları, yazılımın kavramsal olan doğasının bir sonucudurlar. Yazılımın kavramsal doğasının en belirleyici özellikleri ise karmaşıklık, uyumluluk, değişebilirlik ve görünmezlik olarak sıralanıyor. Yani yazılımlar öncelikle kavramsal yapılardır ve her zaman karmaşıktır, devamlı ortama uyum sağlaması beklenir, daima değişir ve hiç bir zaman görünür değildir. Brooks’a göre yazılım var oldukça yazılımcılar, yazılım geliştirmenin kavramsal taraflarını içeren ve geliştirilecek yazılımı tanımlama, tasarlama ve test etme gibi çalışmalarda bu dört özellikten kaynaklanan zorluklarla bir şekilde uğraşacaklardır. Makalede yazılım süreçlerinde olan ve yazılımı kodlamak ya da olması gerekene uygunluğunu test etmek gibi diğer çalışmaların ise arızi yani ikincil olduğu, dolayısıyla bu çalışmalardaki zorlukların, yazılım için olmazsa olmaz zorluklar olmadığı vurgulanıyor. Makalenin temel olarak savunduğu tez şu üç cümlede özetlenebilir: Bu dört özellik, yazılımın tabiatını oluşturdukları için yazılım geliştiriciler her zaman ve muhakkak bu dört özellikten kaynaklanan zorluklarla baş etmek zorundadırlar. Teknolojide ya da yönetimde bulunacak hiçbir yenilik, yazılım mühendisliğini daha üretken, güvenilir ve basit yapma adına önümüzdeki 10 senede 10 katlık bir kazanç sağlamaycaktır. Ayrıca yazılım dünyasında kurtarıcı olarak sunulan yeniliklerin hiçbirisi bu asli özellikleri ortadan kaldırmaz; olsa olsa arızi özelliklerini iyileştirir. İnsanoğlunun yazılım tecrübesi arttıkça, yazılım üretim hızında, yazılımın kalitesinde vs. tabi ki kazanımlar olacaktır. Fakat bu kazanımların, bilgisayar donanım endüstrisinde olduğu hızda olması hayal bile edilmemelidir. Bu yüzden Amerikan folklorunda var olan kurt adamları yere seren gümüşten kurşunu yazılım dünyasında aramak beyhudedir. Kurt adamları öldüren tek şey olan “gümüş kurşun”un bizim kültürümüzde tam bir karşılığı olmasa bile, “sihirli değnek” kavramı kastedilen manayı ifade etmektedir. Yani yazılım dünyasında ortaya çıkan yeni diller, yeni programlama yaklaşımları, yeni araçlar vs. hiç bir zaman sihirli bir değnek olmayacaklardır. Bu yenilikler yazılımı çok daha kolay hale getiremeyecekler, yazılımın devamlı değişken olan tabiatını daha durağan yapamayacaklar, yazılımın uyum sorununu ortadan kaldıramayacaklar ya da yazılımı görünür hale getiremeyeceklerdir.

Makalenin önemi, yazılım geliştirme kavramıyla alakalı ciddi soyutlamalar yapması ve bu soyutlamaları analitik olarak değerlendirmesindedir. Makale, kendisini okuyan her yazılımcının dünyasında yeni ufuklar açmıştır ve açmaya da devam etmektedir. Bu yüzden makalenin savunduğu iddialar, makalenin yayımlanmasından sonra “işte benim gümüş kurşunum bu” gibisinden yazılardan, yazılım konferanslarında konu ile ilgili panellere kadar pek çok şekilde tartışılmıştır. Yazılımcı olmak isteyen ya da olan herkesin bu makaleyi okuması ve üzerine kafa patlatması son derece elzemdir.

Yazarın makalesinde ileri sürdüğü iddiaları ispatlamak için vermiş olduğu örneklerin, o yılların tecrübe ve anlayışına uygun olduğu açıkça görülebilir. Fakat bu makalede ileri sürülen iddiaların artık geçersiz olduğu anlamına gelmez. Aksine makalenin yazılmış olduğu zamandan bugüne geçen 20 yılı aşkın sürede yazılım dünyasında yaşananlar, makalede ileri sürülen iddaları kuvvetlendirici pek çok yeni delil sağlamıştır. Şimdi bu dört asli özelliğe bir kere daha göz atalım.

Karmaşıklık: Yazılım doğası gereği karmaşıktır çünkü bir yazılımda ifade edilen durumlar diğer mühendislik uygulamalarına hatta bilgisayarlara göre bile sayıca çok daha fazladır. Yazılımlar diğer pek çok diğer mühendislik uygulamalarında olduğu gibi aynı parçaların farklı şekillerde bir araya getirilmesiyle oluşmazlar, dolayısıyla yazılımın büyümesi yeni parçalar demektir, aynı parçaların daha büyük ebatta olanlarının kullanılması değildir. Bir yazılım sistemini meydana getiren parçalar birbirleriyle doğrusal bir şekilde haberleşmezler, aralarındaki iletişim daha çok doğrusal olmayan bir şekilde gerçekleşir. Bu tür karmaşıklıklar yazılımı anlamayı, anlatmayı, geliştirmeyi ve test etmeyi zorlaştırır. Yazılımın doğasının bir parçası olan karmaşıklık yazılımı sadece teknik olarak zor kılmaz, yazılım projelerini yönetmeyi de zorlaştırır.

Yazılımlar, aradan geçen 20 küsür senede daha da karmaşık hale geldi. Bu durum, sadece yazılımların daha fazla module, satıra vs. sahip olması yani ebat açısından daha büyük hale gelmesiyle açıklanamaz. Aradan geçen sürede yazılımlar hayatın içine çok daha fazla girdi. Yazılımlar artık algoritmik olmaktan çok süreç odaklı. Yazılımlar günümüzde ezici bir çoğunlukla bir grup iş yapış şeklini ya da süreci hayata geçiriyor. İş süreçlerindeki akışların karmaşıklığı, iş kurallarının sayısı ve detayı, iş süreçlerine dahil olan kullanıcıların sayısı, sürece katılım şekli ve süreçle alakalı yetkileri, iş süreçlerini yerine getiren yazılımların karmaşıklığını ciddi bir şekilde arttırıyor. Yazılımların iş ve gündelik hayatın bu kadar içine girmesi, geçmişe göre daha sıradan kişilerin bu yazılımların kullanıcıları olması anlamına geliyor. Bu da yazlımın veriyi kullanıcıdan almasında daha dikkatli olmasını gerekli kılıyor; yazılımların kullanılabilirliği ciddi bir problem olabiliyor.

Topolojik açıdan zamanımızda üretilen yazılımlar, bundan 20 küsur öncesine göre çok daha dağınık (distributed). Yazılımların büyük çoğunluğu web üzerinden hizmet veriyor, ciddi bir kısmı web servislerini kullanıyor. Global bir köye dönüşen dünyamızda kıtalar arası işbirlikleri, dağınık entegrasyon projelerinin sayısını bir hayli arttırmış görünüyor. Dolayısıyla artık yazılımlar ağlarda çalışacak şekilde tasarlanıyor. Bu durum da haliyle karmaşıklığı arttırıyor.

Nesne merkezli dillerin gelişmesi ve yaygınlık kazanamasının, yazılımın karmaşıklığını yönetmede bize güç sağladığı açıktır. Nesne merkezli diller, gerçekliği soyutlayıp yazılım yapılarına aktarmada, nesne merkezli olmayanlara göre çok daha güçlüdürler. Nesne merkezli dillerin 1960’lı 70’li yıllarda Simula 67 ve Smalltalk ile gündeme gelmesine rağmen bu kadar yaygın kullanım alanı bulması 90’lı yılların ortasından ititbaren Java ile mümkün olmuştur. Bunun kanımca iki sebebi vardır: İlki, “Java Nedir?” başlıklı yazıda da kısaca açıklandığı gibi, zamanın yaygın olan nesne merkezli dili C++’ın çok aşağı seviyeli ve güvenirliği düşük bir dil olması yanında nesne yapılarını uygulamada yeterince açık olmayıp, ciddi bir nesne kültürü oluşturmaktan uzak kalmasıdır. İkincisi de ilkiyle ilgili olmakla birlikte, daha çok geliştirilen yazılımların hayatımıza ve iş dünyamıza ciddi bir şekilde girmiş olmasıyla alakalıdır.  Yazılımların gündelik hayatın içine girmesi, gerçekliği ifade etmede nesne yapılarını rahat bir şekilde uygulayabilmemizi gerekli kıldı. Bu durum da Java’nın, C++’ın yerini alma sürecini çok kısalttı.   Bu noktada, Java gibi hem nesne merkezli hem de platformdan bağımsız bir dilin özellikle sunucu ve mobil tarafta çok yaygın bir kullanım alanı kazanması ve platformlardaki farklılıkları soyutlaması, karmaşıklığı kesinlikle azaltmıştır. Öte taraftan yine Java ile açık kaynak kod felsefesini destekleyen irili ufaklı, Ruby ve benzeri pek çok dilde geliştirilen bileşen (component) ve çerçeve (framework) yapıları, yazılımcıların üretim hızlarını ve üretilen yazılmın kalitesini arttırmıştır.

Tasarım şablonlarının (design patterns) nesne merkezli dillerle birlikte yaygın bir şekilde kullanılır olması, mimari tabanlı yazılım tasarlama yaklaşımının yaygınlık kazanması, tasarım şablonlarını ve farklı mimari modelleri kullananan bileşen ve çerçevelerin pek çok yazılım projesinde değişik yoğunluklarda kullanılması, anlamayı kolaylaştırarak karmaşıklığı azaltmıştır. Bu yapıların yaygın bir şekilde kullanılması yazılımcılar arasında bir dil oluşturmuş ve iletişimi, tarif etmeyi ve aktarmayı daha kolay hale getirmiştir.

Yazılım elemanlarını modelleme notasyonu olarak standart hale gelmiş olan UML’in, yazılımı, projenin değişik safhalarında modellemek amaçlı kullanılır olması, karmaşıklığı yönetmede kolaylıklar sağlamıştır. Çünkü UML, yazılımın farklı safhalarında ortaya çıkan farklı kavramları, çok güçlü grafiksel soyutlamaları yardımıyla algılamamıza katkıda bulunmuş, bu yapıların iletişimini kolaylaştırmış, hatta çok teknik olmayan kişilerle bile bir iletişim köprüsü kurulmasına yardım etmiştir.

Yazılım projesininin başından sonuna kadar yönetilmesinde bir çerçeve çizen XP ya da UP gibi metodolojiler, projede neyin en zaman ve kim tarafından yapılacağını ve nelerin üretecileğini öğreterek karmaşıklığı azaltmaya yardım etmişlerdir. Yazlım geliştirmenin belli safhalarına hitap eden, örneğin kullanım şekli tabanlı ihtiyaç analizi (use case-based requirement analysis), cephe merkezli programlama (aspect-oriented programmming) ya da test güdümlü programlama (test-driven programming) gibi tekniklerin, ilgili safhalarda kolaylık sağladığı açıktır.

Öte taraftan bütün bu kavram ve tekniklerin, karmaşıklığın bir kısımını soyutlayıp, üzerlerini örtüp bizden uzak tutarken, hiçbir maliyetlerinin olmadığı düşünmek çok safça olur. Bu yapılar da yeterince karmaşıktır ve seçim, öğrenilme ve uygulanma konusunda fazladan yük gewtirdikleri de kesindir. Öncelikle hangi bileşene, hangi çerçeveye ya da hangi mimari ve metodolojik yaklaşıma ihtiyacınız olduğunu belirlemek başlı başına bir karmaşıklıktır. Bu konuda yazılımcıların yaklaşımı, genel olarak “daha önce başarıyla uyguladığından vazgeçme”dir. Bu konuda yapılacak yanlış bir seçimin projeyi doğrudan başarısızlığa götürebileceği de aşikardır. Benzer zorluk öğrenme konusunda da vardır. İşe yeni aldığınız yazılım mühendisiniz, kullandığınız tasarım şablonlarını, bileşen ve çerçeve yapılarını, uyguladığınız mimari ve metodolojik yaklaşımları biliyorsa, çok hızlı bir şekilde projenize girebilir demektir. Aksi taktirde ya bu yapıları öğrenmek için ona zaman tanıyacaksınız ya da hızlıca projeye dahil olup, bütün bunları yolda öğrenecek. Bu gibi yapıları iyi bir şekilde öğrenmenin, en az bir ciddi projede baştan sona kullanmakla mümkün olabileceği düşünülürse, karmaşıklığı bu yapılarla alt etmenin ciddi bir maliyeti olduğu ve bu maliyetin önemli bir kısmının da karmaşıklıktan oluştuğu açıktır.

Uyumluluk: Aradan geçen sürenin, yazılımların uyumlulukları konusunda yazılımcılara daha fazla sorumluluk yüklediği açıktır. Öncelikle yazılımların hayatımıza daha fazla girmesi, tipik bir projede var olan kullanıcı tipi ve entegrasyon ucu sayısını arttırdı, eskiden elle yapılan veri alışverişlerinin gerçek zamanlı (real-time) olmasını gerekli kıldı. Yazılımlar artık daha fazla çevre birimi ya da iç ve dış sistemle haberleşiyorlar. Ağ yapılarının gelişmesi, gerek Internet gerek ise intranette çalışan uygulamaların, birbirlerinden daha çok haberdar olmalarını gerektiriyor. Eskiden elle ya da email veya FTP gibi protokollarla yapılan veri aktarımları artık gerçek zamanlı yapılıyor. Bütün bunlar yazılımların kendisine uyum sağlayacağı daha çok ortamla ilişkide olması anlamına geliyor ki bu da yazılımların uyumluluk sorumluluklarını arttırıyor.

XML ve web servisleri gibi standartların geliştirilmiş olması, yazılımların kendileriyle haberleştikleri çevre birimlerinin eskiye göre nispeten daha standart arayüzlere sahip olmaları, uyumluluk konusunda yazılımcılara elbette yardım etmektedir. Fakat tüm bunlar günümüz yazılımlarının daha uyumlu olması gerektiği gerçeğini ortadan kaldırmamaktadırlar.

Değişebilirlik: Hayatın ve iş yaşamının hızlanması, değişimi, başarının ya da başarısızlığın en önemli faktörü haline getirdi. Değişimi yönetebilenler ayakta kalırlarken yönetemeyenlerin akibetleri, ne kadar köklü olurlarsa olsunlar, hep aynı son olmakta. Bu durum, hayatımıza ve iş dünyamıza olabildiğince giren yazılım sistemlerinin de zamanın bu hızlı akışına ve değişime ayak uydurmalarını gerektirmektedir. Bilişim teknolojileri, artık iş yapmanın bir olmazsa olmazı olduğuna göre, yazılımların üzerindeki değişim baskısı, eskisine göre çok daha fazla artmış durumdadır.

Nesne merkezli diller, kodlamadan çok bir düşünme ya da modelleme aracı olarak kullanıldıklarında, yazılımda değişimi öngörülen kısımları, değişmesi pek de muhtemel olmayan diğer kısımlardan yalıtacak yapılar sunmaktadır. Hangi durumlarda nasıl yalıtım sağlanacağı temel olarak tasarım şablonlarının konusudur ve “dörtlü çete”nin (Gang of Four, Erich Gamma, Richard Helm, Ralph Johnson ve John Vlissides) Design Patterns isimli kitabında ayrıntılı bir şekilde açılanmıştır. Java gibi nesne merkezli diller ile tasarım şablonlarının ve mimari tabanlı yazılım tasarım yaklaşımının yaygınlaşması, bileşen ve çerçeve kullanımının artması, yazılımcıların değişimi yönetebilmelerine yardımcı olmaktadır.

Groovy ve Ruby gibi dinamik tipli dillerin, Java gibi statik tipli dillerin kısıtlarını ortadan kaldırmak üzere geliştirilmesi ve yaygın kullanım alanı bulması, yazılımcıların değişimi daha kolay yönetebilmelerine imkan tanımıştır. Bu gibi dinamik tipli diller ile çalışma zamanında koda yapılan değişikliklerin derlenmesine gerek yoktur, yapılan değişiklikler yazılımın davranışında anında görülür. İş dünyasından gelen değişim baskıları bu gibi dinamik dillerin ortaya çıkmasını ve yaygın kullanım alanı bulmasını tetiklemiştir.

Java ya da .NET gibi platform dillerinin ortaya çıkmış olması, özellikle Java platformunda geliştirilen bileşen ve çerçeve yapılarının tekrar kullanım konusunda yazılım dünyasına umut dağıtıyor olması bir artı olmakla birlikte, çığ gibi büyüyen değişim karşısında tek başına değişime ayak uydurmanın yolu olması da pek gözükmemektedir. Yukarıda bahsedilen dinamik diller ile tasarım şablonları, bileşen ve çerçeve yapılarını projelerde rahat bir şekilde uygulayabilmek de ancak uzun süreli eğitim ve tecrübeden sonra mümkündür. Baskının zaten zaman konusunda olduğu günümüz yazılım projelerinde bu yapıları uygulamak için yazılımcıların fazla fırsat bulamadıkları da açıktır.

UML gibi modelleme notasyonlarının yaygın kullanımı ve yazılımların artık bu notasyonlar ile belgelenmesi, yazılımların sadece karmaşıklıklarını yönetmemize yardımcı olmamış aynı zamanda yazılım içine gömülen bilgiyi yazılımın dışına çıkartarak, yazılım değişmesini yönetmeyi kolaylaştırmıştır. Fakat, yukarıdaki paragrafta ifade edilen negatif etkenler bu konuda da geçerlidir.

Görünmezlik: Yazılımlar görünmez olmaya devam ediyorlar. Yazılımı görünür kılmak çok da mümkün olmasa da yazılım elemanlarını ya da yazılımın değişik yönlerini gözde canlandırılabilir yapmak mümkündür. UML gibi notasyon dilleri, sahip oldukları güçlü soyutlamalar sayesinde, yazılımın farklı yönlerini daha rahat algılanır ve anlatılır hale getirmişlerdir.

Yukarıdaki paragraflarda bahsedilen tasarım şablonları, mimari tabanlı yaklaşım, bileşen ve çerçeveler de aslında yazılımlarla alakalı çok güçlü soyutlamalar barındırmakta ve yazılımı tahayyül etme gücümüzü arttırmaktadırlar. Fakat bu yapıların kendilerinin aynı zamanda, öğrenme ve başarılı bir şekilde uygulanma sürecinde ciddi bir görünmezlik yaratmakta olduğu da gözden kaçırılmamalıdır. Yukarıda da anlatıldığı gibi bu yapıları uygulayabilmenin gerektirdiği bilgi, tecrübe ve zaman da genelde yazılım projelerinin en eksik taraflarını oluşturmaktadır.

 

Toplam görüntülenme sayısı: 2606