if’i yukarı taşı, for’u aşağı indir
(matklad.github.io)- Koşullu dallanmayı çağıran tarafa çekmek ve yinelemeyi toplu işlem tarafına itmek olmak üzere iki kod yapılandırma kuralı temel noktadır
ifyukarı taşındığında önkoşulları türlerle veya assert’lerle zorunlu kılmak kolaylaşır; karmaşık kontrol akışı tek bir yerde toplanarak yinelenen koşulları ve ölü dalları bulmak kolaylaşır- Bir
enumoluşturup hemen ardından tekrarmatchetmek, aynı koşulu dallanma, veri yapısı ve yeniden dallanma olarak tekrar eder; bu yüzdenfoo(x)vebar(y)çağrılarına sadeleştirme payı vardır foraşağı indirildiğinde tek tek nesneler üzerinde yinelemek yerine toplu işlem temel birim olur; başlangıç maliyetinin amorti edilmesi, işlem sırasının değiştirilmesi, vektörleştirme ve struct-of-array optimizasyonları mümkün hale gelir- Koşulu döngünün dışına almak hot loop’taki dallanmaları azaltabilir ve TigerBeetle’da olduğu gibi kontrol düzlemindeki karar verme maliyetini veri düzlemindeki toplu işlemle amorti eden yapılara da uzanır
if’i yukarı taşımak
- Bir fonksiyonun içindeki
ifkoşulu, önce çağırana taşınıp taşınamayacağı açısından bakılması gereken bir şeydir- Örnekte,
Option<Walrus>alıpNoneise dönen bir fonksiyon yerine doğrudanWalrusalan bir fonksiyonun daha iyi bir biçim olduğu kabul edilir - Önkoşulu fonksiyonun içinde kontrol edip “hiçbir şey yapmamak” yerine, çağıranın bunu kontrol etmesi ve tür ya da assert ile geçerli olmasını zorunlu kılması daha açıktır
- Örnekte,
- Önkoşul denetimini yukarı taşıyan refactoring, çağrı yolu boyunca yayılabilir ve sonuçta toplam denetim sayısını azaltabilir
- Kontrol akışı ve
if, karmaşıklığı artırır ve kolayca hataların nedeni olabilirifyukarı taşındığında karmaşık dallanma mantığı tek bir fonksiyonda toplanır; asıl iş ise doğrusal alt rutinlere iner- Karmaşık kontrol akışı, dosyanın geneline dağılmış haldeyken değil, tek ekrandaki tek bir fonksiyondayken yinelenen ve ölü koşulları fark etmek daha kolaydır
- “dissolving enum” refactoring’i, dallanmayı veri yapısına dönüştürüp sonra tekrar dallanma desenini ortadan kaldırma yöntemidir
f()koşula göreE::Foo(x)veyaE::Bar(y)oluşturuyor,g(e)de tekrarmatchilefoo(x)veyabar(y)çağırıyorsa aynı koşul birden fazla biçimde tekrarlanır- Koşul
main()içine çekildiğindeif condition { foo(x) } else { bar(y) }biçimine sadeleşir
for’u aşağı indirmek
- Veri odaklı bakışta programlar ve hot path’ler çoğunlukla çok sayıda nesneyle uğraştığı için, nesnelerin toplu işlenmesini tanıtıp toplu işlemi varsayılan durum haline getiririz
- Tek tek nesneler için skaler sürüm, toplu işlemin özel bir durumu olur
for walrus in walruses { frobnicate(walrus) }yerinefrobnicate_batch(walruses)daha iyi bir biçimdir
- Başlıca fayda performanstır
- Tüm toplu işi bir seferde ele almak başlangıç maliyetini amorti edebilir
- İşlem sırasını esnek biçimde değiştirmek ve tüm varlıkların önce tek bir alanını işleyen vektörleştirme ya da struct-of-array yaklaşımı da mümkün olur
- Uç bir örnek olarak Vectorized Interpreters Talk bağlantısı verilir
- İlginç bir örnek olarak FFT tabanlı polinom çarpımı geçer
- Polinomları birden fazla noktada aynı anda değerlendirmek, tek tek nokta değerlendirmesini defalarca yapmaktan daha hızlı olabilir
ifveforkuralları birlikte uygulanabilir- İyi biçim,
if conditiondışında dallara görefor walrus in walrusesçalıştıran yapıdır - Kötü biçim,
fordöngüsünün içinde her seferindeif conditiondeğerlendiren yapıdır - Koşulu yeniden değerlendirmekten kaçınabilir, hot loop’tan dallanmayı kaldırabilir ve vektörleştirmeyi mümkün kılabilir
- İyi biçim,
- Bu desen hem mikro hem de makro düzeyde geçerlidir
- TigerBeetle’ın mimarisi, veri düzleminde nesne topluluklarını aynı anda işleyerek kontrol düzleminin karar verme maliyetini amorti eden bir yapıdır
fortavsiyesinin ana motivasyonu performanstır; ancak jQuery gibi koleksiyon düzeyinde çalışan yaklaşımlar ifade gücüne de yardımcı olabilir
1 yorum
Hacker News görüşleri
Başta bu yazının bu kadar tepki almasına şaşırmıştım; ama bunun veri odaklı tasarım tavsiyesi olduğunu hatırlayınca anladım
Bu forumdaki birçok kişi gibi ben de çoğunlukla iş amaçlı web uygulamaları geliştiriyorum ve bu bağlamda bu tavsiye saçmalık gibi görünebilir
Günlük işinizde komut önbelleğini dert etmiyorsanız, bu tavsiyeyi genel olarak göz ardı edebileceğiniz yönündeki sezgisel kural doğru görünüyor
Bu tavsiyenin ne zaman önem kazandığına dair bir fikir edinmek istiyorsanız Mike Acton’ın “Typical C++ Bullshit” konuşmasına bakmak iyi olur; bu yazı da sanki onun içeriğini daha anlaşılır biçimde toparlamış gibi
Casey Muratori’nin meseleye bakışına epey katılıyorum; ancak çoğu iş yazılımında performanstan çok değiştirilebilirlik ve doğruluk, yani “zaman içinde programlama” optimize edilmeli
Geliştiriciler karmaşık iş mantığını sınıf içindeki küçük private metotlara bölerek DRY hâle getirmeye çalışıyor; “if’i yukarı taşı” yaklaşımı ise dallanma mantığının birden fazla metoda dağılmasını engelliyor
“for’u aşağı indir” de önemli. Pek çok çağrı akışı sonunda pahalı veritabanı sorgularıyla biter ve üst seviyedeki döngünün alt seviyede birden çok DB çağrısı oluşturması yaygındır
Bu yineleme çoğu zaman SQL’in
wherekoşulu ya dajoinile değiştirilebilir; toplama ve filtreleme işlemlerini de çok sayıda nesneyi çekip üzerinde dönmektense DAO’ya yakın bir yere itmek, DB’ye yakın noktada optimize etmeyi kolaylaştırırAncak tüm tasarım ilkelerinde olduğu gibi bunu da dogma gibi uygulamamak, bilinçli karar vermek gerekir
Değiştirilebilirlik ile performansı birbirinin karşıtı olarak görmek üzücü. Gerçekte ikisinin çatıştığına dair ikna edici bir kanıt henüz görmedim
Performansın en önemli yanlarından biri küçük tutmaktır. Küçük kod, küçük veri yapıları, daha az çalıştırılan komut sayısı esastır; “komut önbelleğini düşünmek” de özünde küçük kod yazmaktır
Kod küçüldükçe bug’ların girebileceği alan azalır, tek seferde anlaşılması kolaylaşır ve test kapsamı sağlamak da kolaylaştığı için doğruluk açısından avantajlıdır. Değiştirilebilirlikte de kod küçüldükçe değişiklik küçülür
Elbette paralelleştirme, önbellekleme ve düşük seviye optimizasyon gibi kodu karmaşıklaştıran optimizasyonlar da vardır; ama bunlar performans optimizasyonunun yalnızca bir parçasıdır ve performansı ciddiye alan bir programcı profil çıkarma ve analiz yapmadan bunlara girişmez
if/forilefor/ifişlevsel olarak çok farklı olmasa da biri daha hızlı olabilir; maliyetsiz biçimde doğru olanı kullanabiliyorsanız özellikle yavaş olanı seçmek için bir neden yokturBasit programların çağrı yığını kısadır; karmaşık ve pahalı soyutlamalardan kaçındıkları için daha az bellek kullanma ve daha hızlı çalışma eğilimindedirler
En üst düzey performans hedeflendiğinde program karmaşıklaşabilir; fakat gerçek sadelik çoğu zaman makul performansı beraberinde getirir
“for’u aşağı indirmek” daha çok veri odaklı bir tavsiyedir; “if’i yukarı taşımak” ise programı daha basit hâle getirmeye daha yakındır. Daha doğrusu kaynak kod yerelliğini artırmaktan söz eder ve dallanma mantığını tek yerde toplamak esastır: https://loup-vaillant.fr/articles/source-of-readability
Özellikle “döngüyü aşağı indir” CRUD uygulamalarında güçlüdür. Oluşturma ve güncellemeleri mümkün olduğunca toplu işlemek, çoğu zaman CPU darboğazından çok daha büyük zaman kazancı sağlar
items.map(insertToDb/postToServer)ileinsertToDb/postToServer(items)arasındaki fark neredeyse her durumda birkaç basamaklık olurBu tür optimizasyonlarla iş sürelerinin saniye/dakika ölçeğinden milisaniye ölçeğine indiğini gördüm; çoğu zaman API de daha temiz oluyor, loglar da daha okunur hâle geliyor
Deneyimim arttıkça, fazla sayıda programcının küçük ölçekteki “güzel kod”a önem verdiğini ama tüm kod tabanı tasarımına yeterince önem vermediğini hissediyorum
Kısa ve iyi cilalanmış fonksiyonlar güzel, ama bu tür yazılar PR’larda veya tartışmalarda verimsiz bisiklet kulübesi tartışmalarına dönüşebiliyor
Bir fonksiyonun biraz dağınık olup olmadığı,
ifvefor’un nereye konduğu,mapvefilterkullanılıp kullanılmadığı; fonksiyon adı uygunsa, arayüz ve tipler ifade gücü yüksekse, amaç netse, dokümantasyonu varsa ve yan etkiler aşırı kullanılmıyorsa beni pek ilgilendirmiyorBir girdi geldiğinde doğrulamanın nerede yapılacağını düşünürsek, bu sezgisel kurala göre girdinin alındığı en üst noktada doğrulamak gerekir
Bu, kodu anlamayı da kolaylaştırır; ispat perspektifinden de ön koşulların yukarı doğru yayılması gerektiği için teknik olarak da gereklidir
İlk tavsiye kesinlikle önemsiz bir tartışma değil; ikincisi ise biraz daha muğlak
Junior’ların kendi ayağına sıkmasını engellemeye çalışırlar, ama karmaşıklığın maliyeti ve kodun uzun vadede nasıl değiştiğine dair sezgileri çoğu zaman henüz yeterli değildir
Buna karşılık 20 yılı aşınca, çoğu teknik unsurdan çok basitliğe daha fazla değer vermeye başlarsınız
“Ne kadar erken dallanmalıyım?”, “Bu kodun gerçekten yapması gereken şey ne?” gibi sorular uzun vadede en değerli cevapları verme eğilimindedir
Soyutlama ve kapsüllemeyle ilgili sorular ise daha çok yukarıda bahsedilen türden tartışmalara sürüklenir; “büyük resme” bakan insanların kodlarında en çok güvenlik sorunu çıkmasının nedeni de, aslında o iyi tasarlanmış kod tabanının ne yaptığını iyi anlamamalarıdır
Veri odaklı tasarımın iyi uyduğu performansa duyarlı yazılımlarda, bu küçük ölçekli şeylere daha fazla dikkat etmek gerekir. Çünkü derleyici optimizasyonları bu şekilde çalışır
O zaman oyunun kuralları değişir. İfadelerin anlamı önem kazanır, kendini açıklayan kod daha anlamlı hale gelir, kod yorumları yalnızca çıkarım yapmak için kalır ve “dokümantasyon” daha çok spesifikasyon ve kullanıcı kılavuzuna yaklaşır
En fazla fonksiyonları gruplayabiliyoruz; sonrasında gerisini kendiniz halletmek zorundasınız
“if’i yukarı taşıma”nın bir dezavantajı, fonksiyon tanımı içinde ön koşulların ve son koşulların doğrudan görünmemesi ve her çağrı noktasında kontrol edilmek zorunda olunmasıdır
Birden çok kişinin yer aldığı büyük projelerde, böyle bir fonksiyon amaçlanan bağlamın dışında yeniden kullanılıp hatalara yol açabilir
Sözleşme framework’ü kullanırsanız bu çözülebilir, ama koşulları hem sözleşmede hem kodda iki kez yazmanız gerekir; bağımlı tiplerde de durum aynıdır
Belirli bir bağlama ait kod bölgelerini etiketlemek ve yalnızca o bağlam içinde çağrılabilen fonksiyonlar tanımlamak ilginç bir yaklaşım
Python’da
@requires_context("VALIDATED_XY")gibi bir dekoratör vevalidated_xycontext manager ile fonksiyonun yalnızca doğrulanmış bir bölgede çağrılmasını sağlayabilirsinizRuntime bu bağlamın anlamını bilmez; ama araçlar ve testlerle, istenen bağlamın yalnızca belirli koşullar sağlandığında kurulacağı şekilde tasarım yapılabilir
Haskell gibi dillerde identity monad benzeri şeylerle tip seviyesinde zorunlu kılınabilir; tip seviyesinde zorunlu kılma olmasa bile “unsafe” kod bölgelerini korumanın ilginç bir yolu olabilir
Dolayısıyla ön koşul hâlâ fonksiyon tanımında doğrudan görünür; yalnızca
ifdeyimi olarak değil, tip imzasının bir parçası olarak ortaya çıkarBu, yazıda kullanılan Rust’ta yaygın bir örüntüdür ve
ifkontrolünden farklı olarak runtime’da değil compile-time’da doğrulanan katı bir ön koşuldur; karşılanmazsa program derlenmezÖrneğin
do_something(position: ValidatedPosition)şeklinde alır, normal birPosition’ı doğruladıktan sonraValidatedPosition’a çevirip geçirirsinizPratikte doğrulamayı
ValidatedPositionconstructor’ı içinde kapsüllersiniz, ama asıl nokta şu:Position’ı olduğu gibi geçirmeye çalışırsanız mypy size yanlış tip verdiğinizi söylerPython’un tip denetimi Rust kadar kapsamlı değil, ama aktarılan verinin uygun şekilde işlendiğini garanti etmek istediğimizde giderek daha kullanışlı hale geliyor
Ön koşulu olan bir fonksiyon varsa elbette fonksiyonun başında assertion koyabilirsiniz. Örneğin Java tip sistemi null’a izin verdiği için nesne gerektiren bir fonksiyon null gelirse exception fırlatmalıdır
Her çağrı noktası, fonksiyonu yalnızca ön koşul doğru olduğunda çağırmaktan sorumludur. Bu, ön koşulun tanımı gereği zaten böyledir
Ön koşulu ihlal ederek fonksiyonu çağırmak çağıranın hatasıdır. Fonksiyon içinde bunu kontrol edip tanımsız davranışı önleyen kod gerekebilir; ancak bu tür assertion’larla programın gerçek kontrol akışını ayırmak gerekir ve yazı ikincisiyle ilgilidir
fn frobnicate(walrus: Walrus)için, sahip olunan birWalrusolmayan bir şeyi geçirirseniz program derlenmezGeneric olsa bile argümanın trait bound’ları sağlaması gerekir; fonksiyon içinde argümanın nasıl kullanıldığına bağlı olarak derleyici, fonksiyon tanımı için gerekli bound’ları ister
publicveprivate’ın amacı da bir ölçüde kodu belirli bağlama göre etiketlemek değil miydi diye düşünüyorumYa da
public,privateve .NET ekosistemindekiprotected’ın kapsadığı alanları kesen daha özel semantiklere ihtiyaç olabilirİlk örnek
ifveforyüzünden değil, başka bir nedenle kötüGenel olarak bir şeyin “container”ı varsa, fonksiyonu container’a değil, onun içindeki domain düzeyi “nesne”ye yazmak daha iyidir
Clojure’da agent kullanırken agent’a yönelik fonksiyonlar yazılmaz; agent’ın taşıyabileceği nesneye yönelik fonksiyonlar yazılır
Elixir’de de çekirdek domain fonksiyonları PID’ye değil, içerideki domain veri yapısına göre çalışır; gerektiğinde GenServer çağrısı ona devredilir
Bu, daha esnek olur ve çekirdek domain olan “Walrus’u frobnicate etmek” ile uygulama kaygısı olan “Walrus olabilir de olmayabilir de; varsa frobnicate etmek” arasını daha temiz ayırır
Erken doğrulama yapmak iyi, ama fonksiyonun garip bir hatayla aniden patlaması yerine doğrulama hatasını açıkça vermesi de önemli
Haskell bu sorunu newtype ile çözer; “uygun doğrulamanın zaten yapıldığını onaylayan şeffaf bir container” sunar
İnsanlara gerçekten vurgulamak istediğim tavsiye “üzgün if”leri tercih etmeleri. Normal yolu kat kat iç içe geçirmektense, hatalı koşulları en başta tek tek kontrol edip hemen düzeltmek ya da durmak neredeyse her zaman daha okunabilir ve bakımı daha kolay koddur
İnsanların beklenen duruma odaklanma eğilimi vardır, ama kod bunun tersine önce istisnai durumlara odaklanmayı ister. Her
ifzihinsel yük yaratır; dış sistemlerden bilgi almak ya da hata nedeniyle erken çıkmak gerekiyorsa, tespit ve işleme yan yana olduğunda bu yükü hemen bırakabilirsinizwalrus.frobnicate()olsaydı daha iyi olacağı kastediliyorsa, bu yazarın anlatmak istediği özden çok sözdizimi tercihi meselesi gibi geliyorBunun kötü tavsiye olduğunu söyleyecek değilim, ama mutlaka iyi tavsiye de değil
Seçilen dilin Rust olması epey şey söylüyor. Güçlü tip sistemi, başka dillerde gereken birçok savunmacı programlamanın önüne geçer
Bir C programcısı fonksiyona geçirilen pointer’ın geçerliliğini kontrol etmeyip NULL dereference’a yol açıyorsa, böyle biriyle ekip olmak istemem
Bu yüzden en azından bazı
ifler kesinlikle aşağıda olmalı; hataların yukarı doğru iyi yayıldığı bir yaklaşım da iyi olurforkonusunda o kadar güçlü hissetmiyorum, ama C’de dizi argümanlarının pointer’a decay etmesi nedeniyle yinelemenin de aşağıda değil yukarıda olması gerektiğini düşünüyorum. Dizinin oluşturulduğu fonksiyonda uzunluğa güvenebilirsiniz, ama argüman olarak geçirilen fonksiyonda güvenemezsinizDokümantasyon güçlü tip sisteminin yerini almalı; runtime kontrolleri değil
NULL kontrolleri ve diğer savunmacı önlemlerle dolu kod çok daha zor okunur
Kütüphane sınırlarında daha savunmacı kontroller gerektiği söylenebilir; bu yazı da tam olarak bu kontrolleri yukarı itmeyi söylüyor
Güvenlik açısından kritik kod farklı olabilir, ama çoğu durumda kazara NULL dereference’ların testler, sanitizer’lar ve fuzzing ile yakalanması yeterlidir
Geçersiz pointer değerini kontrol etmek için olası sayısız hatalı değerin tümünü kontrol etmeniz gerekir; yalnızca NULL’a bakıyorsanız aslında geçersiz değeri kontrol etmiş olmazsınız
Bir fonksiyonun önkoşulu “
pparametresi NULL olamaz” ise kontrol edebilirsiniz. Ama önkoşul “pgeçerli bir pointer olmalı” ise uygun assertion koşulunu bulmada bol şansSon zamanlarda çok T-SQL kullanıyorum; parametreleri ya da değişkenleri
NOT NULLolarak tanımlayamıyorsunuzBu yüzden makul olan en erken noktada, genelde stored procedure’ün en başında NULL kontrolü yapmak iyi olur
Aksi halde NULL, çağrı katmanlarının derinlerine beklenmedik biçimde yayılıp daha belirsiz sorunlar yaratabilir
Neyse ki tablo verileri
NOT NULLolarak tanımlanabildiği için bu tür bug’lar genelde veriyi bozmaz, ama erken yakalamak çok daha kolaydırAncak bazı parametre değerlerine göre DB’ye yazan bir mantık varsa ve o değer beklenmedik şekilde NULL ise, yanlış değer yazılabilir ya da gereken değer hiç yazılmayabilir; bu da fiilen veri bozulmasıdır
Dolayısıyla cevap savunmacı programlamadır
Uygun bağlam olmadan bakınca bu tavsiye oldukça tuhaf, hatta belki kötü tavsiye
Döngüler ve
ififadelerinin ikisi de kontrol akışı işlemleridir; bu yüzden yazıdaki bazı argümanlar pek anlamlı değilEn güçlü gerekçe performans gibi görünüyor, ama performans genelde en son ilgilenilecek konu olmalı; özellikle de sezgisel bir tavsiyeyse bence daha da öyle
Ne yazık ki yazar bunu bir slogana dönüştürmüş; umarım yayılmaz
Yazarın örneğindeki gibi iyileştirilmiş kod yazılabiliyorsa, koşul döngü çalışırken sabittir. Her seferinde pahalı bir koşul değerlendirmiyorsanız branch prediction çoğunu halleder
Koşul const değerlerden oluşan bir boolean expression ise compiler’ın bunu anlaması da mümkün
Yazının tamamının şöyle bir örnekle devam edeceğini sanmıştım: koşula göre
walrusları ayırıpfrobnicate_batch(fwalrus)vetransmogrify_batch(twalrus)çağırmakAma gerçekte tek bir koşulla tüm döngüyü iki dala saran bir örneğe gitti
Yazılım mühendisliği geçmişinden gelen programcıların bunu sık sık yanlış yapması şaşırtıcı
Ben programlamaya bilim alanında başladım; orada böyle şeyleri düşünmek kesinlikle gerekliydi
Döngü sırasını yanlış seçmek bile bir simülasyonun 1 saat yerine 1 hafta sürmesine yol açabilir
Bu geçmiş sayesinde
forveifsırasını uygun şekilde ayarlayıp küçük optimizasyonları içgüdüsel olarak yapıyorum; böyle yapılmamış kod bana düpedüz yanlış görünüyorBu tür genel kuralların gerçek koda gerçekten uygulanıp uygulanamayacağından emin değilim.
Bu tür kurallar çoğu zaman yanlış yere konmuş bir dogma gibi görünüyor; blog yazısı bunun bir sezgisel kural olduğunu söyleyerek başlasa bile genç programcılar bunu her zaman öyle algılamıyor.
Birkaç hafta önce YouTube bana sürekli “I'm a never-nester” adlı bir video öneriyordu; sanırım asla
ifiç içe yazılmaması gerektiğini savunuyordu ve epey gülünçtü.Yazıdaki örnekte olduğu gibi,
foriçindeif conditionbulunan kodu “kötü” diye işaretlemişti; ama pratikte bu şekilde yazılmış kodların çoğundacondition,walrus'a bağlı olduğu içinifyukarı taşınamaz.Eğer taşınabiliyorsa, aynı koşulun her döngüde yeniden değerlendirildiği o kadar barizdir ki programcılar bunu doğal olarak kaçınmaya eğilimlidir.
Ama bir junior ya da öğrenci dogma gibi duyulan bir tavsiyeyi okursa, onu katı biçimde uygulamaya çalışıp daha kötü kod üretebilir.
match (condition_a, condition_b)kullanarak dört durumu ayırmanın iç içeiflerden daha kötü olduğu bir durumu deneyimsel olarak görmedim.ifin “yukarı taşınması” gereken iyi bir örnek.Kodun amacı koşula göre walrus üzerinde belirli bir işlem yapmak; aslında
if, polimorfizm ve tip sistemi yerine kullanılıyor.Walrus neden farklı senaryolarda çağrılması gereken iki ayrı fonksiyona sahip olsun? Tek bir fonksiyon ve iki tip olsun, doğru tipi geçirmek yetmez mi?
Mevcut yapıda bile, yukarıda koşula göre işlem fonksiyonunu seçip aşağıdaki döngüde seçilen fonksiyonun her seferinde çağrılmasını sağlayabilirsiniz.
Kararı mümkün olduğunca erken verirseniz bunu kodun geneline saçmanız gerekmez. Kodun iç kısmı dallanma olmadan her seferinde aynı işi yapar; çıktı ise yalnızca yapılandırma grafiği üzerinden değişir.
Elbette bu yeni bir fikir değil; 15 yıl önce bile zaten eski bir fikirdi: https://www.youtube.com/watch?v=4F72VULWFvc
walrus'a bağlı olmadığında çalışır ve bu gerçeği açıkça ortaya koymaya yardımcı olur.Burada “for'u aşağı indir” kuralını yeniden uygularsak,
if conditioniçindefrobnicate_batch(walruses)ya datransmogrify_batch(walruses)çağıran bir biçime dönüşür.Birkaç kez karşılaştığım ama düzgün ifade edemediğim bir sorunu biri kelimelere dökmüş; zihinde tutmaya değer iyi bir model gibi görünüyor.
Aynı zamanda bu tür eleştiriler de değerli. Dogmatik bir junior geliştiricinin ikisini birlikte okuyup biraz daha nüanslı karar verebilmesini umarım.
Programcılar özellikle bunları kopyalayıp mekanik biçimde uygulamamalı.
Sezgisel kuralları ve en iyi uygulamaları körü körüne uygulamak iyi bir fikir olmayabilir; nihayetinde her şey “duruma bağlıdır”.
Bu tür sezgisel kuralların içinde genelde bir bilgelik kırıntısı vardır, ama çoğunlukla yeni programcılarda yeniden dogmadan arındırılması gereken bir şey yaratır.
Fazla katı takip etmeye çalışınca işlerin daha kötüye gittiği durumlar her zaman çoktur; “bu tavsiyeyi ne zaman dinlememen gerektiğini bil” aslında işin temel zorluğudur.
Bu kuralların üzerinde oynamak için faydalı olduğunu düşünüyorum. Benimseyip deneyin, sonuna kadar zorlayın, bir gün ya da bir yıl kadar tersine çevirin, nereye kadar gittiğini görün.
Sınırları öğrenmek de sürekli üzerine yazılan bir parşömenin üstüne eklenen bir başka malzemedir.
O koanların bazıları birbiriyle çelişmelidir.