Global State'in Görünmez Yan Etkileri
Global state'in görünmez bağı, testler arasında sızan durum ve queue worker ile Octane'de büyüyen risk; durumu görünür ve yerel kılmanın yolları.
Kısaca
Global state, fonksiyon imzasında görünmeyen bir bağ yaratır: bir yerde değişen değer ilgisiz başka bir yerde okunur, testler arasında durum sızar ve hata ancak ayıklarken keşfedilir. Tehlike queue worker ve Octane gibi uzun ömürlü process'lerde büyür. Çözüm durumu yok etmek değil; parametre, dependency injection ve istek ya da job kapsamıyla onu görünür ve yerel kılmaktır.
Bir hata vardı ki bir değer “kendi kendine” değişiyordu. Test tek başına koşunca geçiyordu, tüm suite ile koşunca kalıyordu. Bir istek, kendisinden önce gelen isteğe göre farklı davranıyordu. Suçlu, paylaşılan ve değiştirilebilir global state’ti.
Global state kodu kısaltır — ve hata ayıklamayı uzatır.
Global ne demek
“Global” burada global anahtar kelimesinden fazlasıdır. Mutable state taşıyan static property’ler, durum tutan singleton’lar, modül seviyesinde değiştirilebilir değişkenler, bir framework facade’ının arkasına saklanmış paylaşılan durum — hepsi aynı kategoridir.
Ortak özellik: bir yerde değiştirilen bir değerin, ilgisiz başka bir yerde okunabilmesi.
Görünmez bağ
Global state’in asıl maliyeti, yarattığı görünmez bağdır. Bir fonksiyonun imzası, neye ihtiyaç duyduğunu söyler — parametreleri. Ama fonksiyon bir global’i okuyor ve değiştiriyorsa, imza yalan söyler. Fonksiyon, imzasında görünmeyen bir şeye bağımlıdır.
Buna “uzaktan etki” (action at a distance) denir: bir dosyadaki değişiklik, hiçbir görünür bağlantısı olmayan başka bir dosyanın davranışını değiştirir. Kodu okuyarak bu bağı göremezsiniz; ancak hata ayıklarken keşfedersiniz.
Test edilemezlik
Global state’in en erken verdiği sinyal testlerdedir. Testler arasında durum sızar: bir test bir global’i set eder, sonraki test onu set edilmiş bulur. Testler sıraya bağımlı hâle gelir — tek başına geçer, birlikte kalır.
Çözüm olarak her testten önce global’leri sıfırlamak gerekir. Tam da bu sıfırlama zorunluluğu, kod kokusunun kendisidir: kod, paylaşılan duruma bağımlı olduğunu test kurulumuyla itiraf eder.
Long-running process’lerde tehlike
PHP’nin klasik request-response modelinde global state her istekte sıfırlanır; tehlike kısmen gizlenir. Ama uzun ömürlü bir process’te — bir queue worker’da — durum farklıdır.
Worker, job’lar arasında ayakta kalır. Bir global’e job 1 tarafından yazılan değer, job 2’ye sızar. Yerelde tek job’la test edince görünmeyen, production’da binlerce job arka arkaya koşunca patlayan bir hata. Octane gibi kalıcı uygulama sunucuları aynı riski klasik web isteklerine de taşır.
Durumu yerelleştirmek
Çözüm, durumu yok etmek değil — her sistemin durumu vardır — onu görünür ve yerel kılmaktır:
- Durumu global’den okumak yerine parametre olarak geçirin. Açık, imzada görünür bir bağımlılık.
- Bağımlılıkları dependency injection ile verin; gizli erişim yerine.
- Mutable durumu process kapsamında değil, istek ya da job kapsamında tutun. Bir job’un durumu o job ile başlasın, o job ile bitsin.
Kural sade: explicit, implicit’i yener. Bir fonksiyonun ihtiyaç duyduğu her şey imzasında görünüyorsa, o fonksiyon hem okunur hem test edilebilirdir.
Global state, birkaç parametre yazmaktan kurtardığı için cazip gelir. Karşılığında, bir değerin nereden değiştiğini saatlerce aratır.
Her global, koddan silinmemiş gizli bir bağdır — onu bir parametreye dönüştürün.