İyi programcılar veri yapılarını ve aralarındaki ilişkileri düşünür
(read.engineerscodex.com)- Linus Torvalds’un sözüne dayanarak, iyi tasarımın kod yazmaktan önce veri yapıları ve ilişkilerini sağlam biçimde kurmakla başladığı vurgulanıyor
- İyi tasarlanmış bir veri modeli, uygulama mantığını doğal olarak sadeleştirir ve yazılımı daha güvenilir ve anlaşılır hale getirir
- Veri modelini sonraya bırakmak ileride iş yükünü artırır; buna karşılık yapıyı başta iyi kurmak migrasyonları ve karmaşık sistem genişletmelerini kolaylaştırır
- Bir projede, karmaşık algoritma optimizasyonu yerine veriyi yeniden yapılandırarak problem kategorisinin kendisi ortadan kaldırılmış, 500 satırlık bir fonksiyon 50 satırlık bir fonksiyon ve veri yapılarıyla değiştirilmiştir
- Pratikte, arayüzler ve veritabanlarında daha sıkı tipler uygulanmalı; kod ayrıntılarından önce veri akışı ve bileşen etkileşimleri tasarlanmalıdır
Veri yapıları kod tasarımını belirler
- Linus Torvalds, Git’i kararlı ve belgelenmiş veri yapılarına sahip basit bir tasarım olarak görür ve kodu verinin etrafına yerleştirme yaklaşımını vurgular
- “Kötü programcılar kodu dert eder, iyi programcılar veri yapılarını ve aralarındaki ilişkileri dert eder” sözü buradaki temel cümledir
- Git’in başarılı olmasının nedenlerinden biri, kodun veri merkezli tasarlanmış olmasıdır
- İyi veri yapıları, kod tasarımını ve bakımı kolaylaştırır; yazılımın güvenilirliğini, sistemin anlaşılabilirliğini ve kod okunabilirliğini artırır
- Uygulama mantığı çoğu zaman veri modelini takip eder
- Veri modeli sonradan düşünülürse ileride yapılacak iş artar
- İyi tasarlanmış bir veri modeli, sonraki migrasyonları ve karmaşık sistem genişletmelerini kolaylaştırır
- Gerçek bir proje örneğinde, karmaşık algoritmaları daha fazla iyileştirmektense veriyi yeniden yapılandırmak daha büyük etki yaratmıştır
- Veri yapısı değiştirilerek tüm bir problem kategorisi ortadan kaldırılmıştır
- 500 satırlık bir fonksiyon, 50 satırlık bir fonksiyon ve iyi tasarlanmış veri yapılarıyla değiştirilmiştir
- Yeni kod daha hızlı hale gelmiş, ayrıca anlaşılması ve bakımı da kolaylaşmıştır
- Ancak mevcut verilerin yeniden yapılandırılması gerektiği için emek alt katmanlara kaymıştır
Karmaşıklığı veri tarafına taşımak daha iyidir
- The Art of Unix Programming içindeki “Rule of Representation”, bilgiyi veride tutarak program mantığını basit ve sağlam hale getirmeyi önerir
- Prosedürel mantığı insanların doğrulaması zordur; buna karşılık karmaşık veri yapıları modellemek ve akıl yürütmek açısından daha kolaydır
- 50 düğümlü bir pointer tree diyagramı, 50 satırlık bir program akış şemasından daha ifade gücü yüksek ve açıklayıcı olabilir
- Bir dönüşüm tablosunu dizi başlatma ile ifade etmek, aynı şeyi
switchifadesiyle yazmaktan daha şeffaf ve açık olabilir - Karmaşıklığın kodda mı yoksa veri yapısında mı duracağı arasında seçim yapmak gerekiyorsa, karmaşıklığı veri yapıları tarafına taşımak daha iyidir
Pratikte önce veri akışı tasarlanır
- En doğrudan uygulanabilir yöntem, veriden başlamaktır
- Arayüzlere veya veritabanına daha sıkı tipler uygulamak kod karmaşıklığını azaltabilir
- Veri yapıları üzerinde önceden daha uzun süre düşünmek gerekir
- Bu, kodun önemsiz olduğu anlamına gelmez; tüm unsurlar birlikte önemlidir
- Kod ayrıntılarına girmeden önce verinin nasıl aktığını ve bileşenlerin nasıl etkileşime girdiğini görmek, üst düzey bir yaklaşım olarak faydalıdır
- Senior Engineer (L5) gerekliliklerine örnek olarak, FAANG şirketlerinde daha karmaşık sistemler için üst düzey tasarım dokümanları hazırlamak genellikle bu kapsamdadır
- Buna ekip planlamasına liderlik etmek ve orta-büyük ölçekli özellikler için iyi bir yol haritası oluşturmak da dahildir
- Veri akışını ve bileşen etkileşimlerini önce tasarlayabilme becerisi, daha yüksek düzeyde mühendislik etkisiyle bağlantılıdır
1 yorum
Hacker News yorumları
Şu Substack yazısı, bu Stack Exchange yazısından birkaç alıntıyı düpedüz kopyalamış gibi görünüyor: https://softwareengineering.stackexchange.com/questions/1631...
“Bana akış şemalarını [kodu] gösterip tabloları [şemayı] gizlerseniz, kafam karışmaya devam eder. Bana tabloları [şemayı] gösterirseniz genellikle akış şemalarına [koda] ihtiyaç duymam; çünkü bunlar apaçık olacaktır.” — Fred Brooks, “The Mythical Man Month”, 9. bölüm
“The Mythical Man Month’u oku. Bu kadar eski bir kitabın günümüz yazılım geliştirmesiyle ilgili söyleyecek bir şeyi olamayacağını düşündüğümü hatırlıyorum; yanılmışım.”
O zaman uygulama geliştiricileri, daha hızlı oldukları ve yapacak çok işleri olduğu gerekçesiyle veritabanını suistimal etmeye başlıyor
Veri yapıları ile tipler aynı şey değildir. Veri yapısı, bit örüntüleri ve diğer bit örüntülerine referanslar, yani pointer’lar ya da ilişkilerdir.
Tipler, programlama dillerinde kullanıldıkları biçimiyle bu bit örüntülerine kısıtlar koyar; ama bunun dışında pek çok dil özelliğini de ifade edebilir. Gereksiz soyutlamalarla karmaşık tip hiyerarşileri oluşturmak “veri yapılarına önem vermek” anlamına gelmez; zeki mühendislerin de sıkça düştüğü bir başarısızlık biçimidir
Tipler, diskteki bitlerdir
Burada asıl söylenmek istenen, problemi daha derin düşünmek ve ileride ayağınıza dolanacak yapıları seçmemek gerektiğine daha yakın. Örneğin Unix pipe’larının ne kadar yayıldığına, ne kadar çok alana ve kullanım senaryosuna genişlediğine bakın. İnsan ve makine kısıtlarına saygı göstererek sistemler kurma biçimini görselleştirmenin harika bir yoludur.
Ken Thompson ve diğerlerinin pipe gibi bir şeyin Unix’te anlamlı olduğunu fark etmeleri bile epey zaman aldı. Kolay elde edilmiş bir içgörü değil; sistem için doğru yapı taşlarını bulmaya yönelik ısrar ve devam çalışması gerektirdi
Linus, başkalarının bulanık biçimde düşündüğü şeyleri her zaman iyi özetliyor. Yazıda söylenenler, kayıp bir beceri hâline gelen DDD ile de benzeşiyor.
Burada “kayıp” derken, bugün karşılaştığım geliştiricilerin çoğunun üzerinde çalıştıkları domain’i anlayıp entity’leri ve etkileşimleri modellemekten çok, algoritmaları ve JSON’u oradan oraya taşımakla ilgilenmesini kastediyorum. Modern AWS tabanlı tasarımlarda bu; zayıf gerekçelendirilmiş DynamoDB GSI yığınları, anemik nesneler ve hack üstüne hack biriktiren betik benzeri “service” katmanları olarak ortaya çıkıyor. Belki de service sınırları içinde domain bağlamının yeterince iyi tanımlanacağına dair örtük bir varsayım vardı, ama bunun iyi bir varsayım olduğunu düşünmüyorum.
Sektörümüzün tasarımdaki titizliği nerede kaybettiğini bilmiyorum. Okulda mıydı, mülakat hattında mı, çıtayı düşürmekten mi, yoksa hepsinden mi?
Daha da kötüsü, tasarım kolayca otomatikleştirilememe gibi büyük bir günah işliyor. Bu yüzden insanlar araçların dayattığı tasarımı eleştirmeden izliyor ve yaptıkları işi daha derin düşünmeleri gerektiği fikrinden rahatsız oluyor. Herkes bu düşünme işini “uzmanlara” dış kaynak olarak vermek istiyor.
Doğru dürüst öğretilmemesi, yıllar içinde kişinin kendi kendine edinmek zorunda kalması ve koddan daha az gerçek kabul edilip daha az önemli sayılması da sorun. Ama bu inanç, sonunda üretilebilecek şeylerin seviyesini ileri seviye acemi düzeyine kilitliyor. Programcılar kolektif olarak standartları mümkün olduğunca düşük tutmayı seçiyor; bu konuda neredeyse yengeçler gibi birbirini aşağı çeken bir psikoloji var gibi görünüyor.
Sözdizimsel olarak aynı olmadıkları için tekrar gibi görünmezler, ama işlevsel olarak aynı işi yapan çok sayıda kod tekrarı da ortaya çıkar.
1 https://martinfowler.com/bliki/AnemicDomainModel.html
2 https://wiki.c2.com/?PrimitiveObsession
Bunun nedeninin kötü kodun sonuçlarının daha az görünür olması olduğunu düşünüyorum. Kötü bir köprü çöker, ama kötü kod yalnızca refactor edilir ya da daha fazla kodla değiştirilir. Yönetimin anlamadığı bir metin dosyası, yönetimin anlamadığı başka bir metin dosyasına dönüşür.
Ve bir şey bir kez çalışır hâle geldi mi ışıklar kapanır. Kusursuz biçimde çalışan geçici bir hack kadar kalıcı bir şey yoktur. Ama 1000 geçici hack, iyi mühendislik ürünü bir sistem oluşturmaz. Yazılım geliştirmede olgunlaşmak, kod yazmaktan çok veriye ve ilişkilere odaklanmaktır. Bunları koda dönüştürebilmek gerekir; ama çalışan kodu veri modeline çevirmek değil, veri ve ilişkileri koda çevirmek gerekir.
Bir domain entity’sinin tüm mantığı barındırabiliyor olması, bunu mutlaka yapması gerektiği anlamına gelmez. Örneğin bir kullanıcı adının zaten var olup olmadığını kontrol etmek gerekiyorsa, veri erişim katmanına “bağımlı olamayan” bir domain entity’sinin içinde bunu nasıl yapacaksınız? Sıklıkla “domain service” gibi bir şey önerilir, ama o zaman iş mantığı birden çok yere dağılır; bu da bana DDD’nin amacıyla ters düşüyor gibi geliyor.
DDD’yi bir felsefe olarak oldukça seviyorum, ama “taktiksel DDD” pattern’lerinden aşırı hoşlanmıyorum. Çok fazla kişinin Domain-Driven Design’ı Domain-Driven Implementation ile aynı şey sandığını düşünüyorum. Uygun yerlerde zengin domain’ler kurmaya çalışıyorum, ama bunun her projeye uygun olmadığını biliyor ve terminolojiye kapılmamaya çalışıyorum. “Name” tipinin bir value object mi yoksa aggregate root mu olduğuyla ilgilenmiyorum. Her şeyden önce bounded context daha önemli. DDD’nin bazen uygulama karmaşıklığını artırırken karşılığında neredeyse hiçbir şey kazandırmayabileceğini de kabul ediyorum. Asla her derde deva olduğunu söylemem.
Bundan sonra da DDD kullanacağım, ama DDD’nin “bakın, nesne yönelimli programlama o kadar da kötü değilmiş, değil mi?” mesajını verme girişimi olduğu hissinden kurtulmak zor. Ve bu hedefe ulaşıp ulaşmadığından da pek emin değilim.
Profesyonel mühendisliğe başlamadan önce Matlab, R ve erken dönem Python gibi istatistik sistemleriyle her gün veri ve istatistik analizi yaptığım için bunu ilginç buluyorum.
Bu yüzden mühendislik bakış açım her zaman iki şeye, yani fonksiyonel durumu ve veri iş akışlarını yönetmeye dayandı.
10 yıl boyunca yazılım mühendisliğini meslek olarak yaptıktan sonra şunu gördüm: Minsky ya da Shannon gibi “bilimsel” mühendislerin çoğu, bilişim dünyasını durum yönetimi, veri dönüşümü ve hesaplama yükünü yönetme üzerinden açıklıyordu. Yazılımın büyük isimleri ve öncüleri veriye ve duruma büyük önem veriyordu; erken dönem bilişim de fiilen bundan ibaretti ve bu pattern’in gelecekte de sürmesi bekleniyordu.
Buna karşılık, mühendislik sistem tasarımında her zaman doğru olup herkesin izlediği temel varsayımlarda hiçbir tutarlılık yok; varsa bile çoğu zaman modadan ibaret. Çoğu operasyonel yazılımda iş takvimi, sağlamlıktan, antifragility’den ve durum yönetiminden çok daha fazla mühendislik önceliklerini ve yapıyı belirliyor.
Lonca ya da sendika benzeri mesleki örgütler, yazılım mühendisleri tarafından neredeyse evrensel olarak reddediliyor. IEEE’yi ciddiye almamanın bir yaptırımı olmadığı için gerçekte kimse onu ciddiye almıyor. Bunun sonucunda inşaat mühendisliği ya da biyomedikal mühendisliğinde olduğu gibi pratiği zorunlu kılan veya özdenetim sağlayan mekanizmalar yok; oralarda bile bunlar ancak sınırlı ölçüde kullanılıyor.
Genel olarak yazılım geliştirmenin bugünkü durumu, son derece soylu ve felsefi köklerinden tamamen kopmuş durumda; fiilen de parası olanlara para kazandıran sistemleri önceleyen şirketler tarafından yönlendiriliyor. Dolayısıyla “iyi” olan şeyin, teşvik edilen şeyle neredeyse hiçbir ilgisi yok.
“Bana akış şemasını [kodu] gösterip tabloları [veri yapılarını] gizlersen şaşkın kalmaya devam ederim. Bana tabloları gösterirsen genelde akış şemasına gerek kalmaz. Çünkü onlar apaçık olacaktır.” — Fred Brooks
Temel tablolarla 1:1 eşleştirmek son derece kısıtlayıcıdır ve modern dillerin sunduğu ifade gücünü kaçıran modellere yol açar diye düşünüyorum
Bu, özünde fonksiyonel programlama ve kategori teorisi bakış açısıdır.
Elinizde bir veri nesnesi vardır ve yapısının nasıl dönüştürülebileceğine dair kısıtlar sağlar. Program mantığı da tamamen o yapıyı koruyan dönüşümlerle ilgili hâle gelir.
Dönüşümler daha basitleşir ve üzerinde akıl yürütmesi kolaylaşır; sonunda dönüşümlerin kenar, yapıların düğüm olduğu bir grafik kalır. Genellikle keyfî bir imperatif programa göre üzerinde akıl yürütmesi daha kolaydır
Fonksiyonel programlamanın bakış açısı, nesnelerin dönüştürülmemesi ve değişiklikten kaçınılması gerektiğine daha yakındır; bu tartışmadan ayrıdır. Kategori teorisinin özü, matematiğin çeşitli alanlarında ortak olarak ortaya çıkan ilişki örüntülerini ele almaktır ve burada tartışılan konuyla da hiçbir ilgisi yoktur. Belki tip teorisini kastetmek istemiş olabilirler, ama onun da ilgisi yok
Eskiden vardığım sonuç şuydu: Kodda yaptığımız her şeyin, veriyle ilgili iyi bir karardan çok daha kısa ömürlü olma ihtimali yüksek.
https://www.swyx.io/data-outlasts-code-but
Bu ilke iş seviyesi için de geçerli. Süreçlere (koda) takılıp önce varlıkları ve aralarındaki ilişkileri (veriyi) anlamaya zaman ayırmayan iş analistleriyle sürekli uğraşıyorum.
Sonuç olarak bir şey inşa etme zamanı geldiğinde, veri modelinin nasıl görünmesi gerektiğini geliştiricilerle iletişim kurarak anlatamıyorlar. Süreçler uygulanıyor; veri modeli ise dikkatle tasarlanmak yerine o anda doğaçlama biçimde yamalanıyor