Rails ile yeniden yazmak
(dirkjonker.bearblog.dev)- Gümrük beyannamesi hazırlama uygulaması, çeşitli stack’ler değerlendirildikten sonra, aşina olunan Ruby on Rails ile yeni seçeneklere kıyasla hissedilir ölçüde çok daha hızlı özellik geliştirilebildiği için ilk uygulamada kullanıldı
- Rust backend ve SvelteKit frontend ile yapılan “version 2” hızlı ve güzel görünüyordu; ancak gereken işlevlerin yalnızca yaklaşık %10’unu içeriyordu ve iş kodundan çok tesisat işi büyüdü
- Kademeli geçiş; routing, veri erişimi, yetkilendirme, değişiklik takibi, arama, arka plan işleri, pub/sub, e-posta, OAuth 2.0, SMTP/POP, nesne depolama ve PDF üretimine kadar her şeyi elle birbirine bağlamayı gerektirdiği için kullanıcı değerini neredeyse hiç artırmadı
- Yalnızca yeni stack’e taşınan kısımları tekrar Rails’e döndürmeye karar verildi; tek başına Rails uygulamasını sürdürme durumu, kullanıcı taleplerini karşılayamamanın stresi ve birçok yeri aynı anda düzeltme karmaşıklığı bu kararda etkili oldu
- Rails’e dönüşten sonra özellik geliştirme, mevcut özellikleri iyileştirme, UI güncelleme ve hata düzeltme hızı yeniden arttı; Ruby/Rails’in dezavantajları test, refactoring, Turbo ve caching ile ele alınarak ürün geliştirmeye odaklanma yolu seçildi
Geçici seçenek olarak Rails’in seçildiği başlangıç noktası
- 2022’de gümrük beyannamesi hazırlama uygulaması geliştirilmeye başlanırken .NET/F#, Go, Rust, React gibi çeşitli dil ve framework’ler değerlendirildi ve gerçekten kod yazılarak denendi
- Nihai seçim Ruby on Rails oldu
- Zaten aşina olunan bir araçtı ve yeni öğrenilecek seçeneklere göre hissedilir biçimde 10 kat daha hızlı ilerlemeyi sağlıyordu
- O dönemdeki Rails uygulaması daha çok geçici bir kavram kanıtı niteliğindeydi; daha sonra Ruby gibi hata yapmaya açık olmayan başka bir dille yeniden yazılması planlanıyordu
- Rails uygulamasına hızla özellik eklendi ve ekip arkadaşları da ortaya çıkan sonucu beğendi
- Verimliliği artıran özellikler hızlıca yapılabildi
- Başta sık yaşanan hata durumları kodla engellenebildi
Rust ve SvelteKit “version 2”nin takıldığı nokta
- “version 2” için backend’de Rust, frontend’de SvelteKit seçildi
- İlk sürüm iyi görünüyor ve hızlı çalışıyordu; ancak ihtiyaç duyulan işlevlerin yalnızca yaklaşık %10’una sahipti
- Rust’ta Rails ile kıyaslanabilecek entegre bir framework olmadığı için, iş mantığından çok temel kod ve bağlantı işleri yazmak gerekiyordu
- Tam bir version 2 oluşturmak için version 1 geliştirmesini tamamen durdurmak gerektiği netleşti ve sonunda version 2 rafa kaldırıldı
- Yeniden yazmanın, orijinal uygulamayı ilk kez geliştirmek kadar zaman alabileceği görüldü
Kademeli geçişin yarattığı operasyonel karmaşıklık
- Rust’ın güçlü tipleri, yüksek performansı ve derleme başarılı olduğunda verdiği güven nedeniyle uygulamaya Rust ekleme isteği devam etti
- Ruby 4 yıldan uzun süre kullanılmış olsa da runtime sorunları da sürüyordu
- Sorunlar çoğunlukla null değerlerden ve yakalanmamış exception’lardan kaynaklanıyordu
- Dağıtımdan sonra hata raporlama aracı izlenerek production sorunları hızla düzeltildi
- Rails frontend’inde mantığın ve markup’ın birden fazla dosyaya dağılmasından memnun olunmuyordu; Svelte ise ilgili mantığı tek dosyada toplmayı kolaylaştırıyordu
- Yeni frontend’i SvelteKit, yeni backend’i Rust ile yavaş yavaş yazıp mevcut Rails kısımlarını da kademeli olarak değiştirerek en sonunda Rails’i kaldırma planı yapıldı
- Başlarda kayıt listeleri ve filtreleri olan “index” sayfalarını yeni stack’e taşıma işi hızlı ilerledi
- Yeni stack hızlı çalışıyordu ve Rust tarafında runtime sorunu neredeyse yoktu
- Hangi route’un Rails’e, Rust’a veya SvelteKit’e gitmesi gerektiğini belirleyen deployment kuralları karmaşıklaştı
- Bu karmaşıklık, tüm geçiş tamamlanana kadarki geçici durum olarak kabul edildi
Kullanıcı değerini aşan teknik iş yükü
- Zaman geçtikçe Rails tarafına sürekli yararlı yeni özellikler eklendi ve mevcut özellikler de daha derin ve gelişmiş hale geldi
- Başlangıçtaki yanlış varsayımları düzeltmek için çeşitli refactoring’ler yapıldı; iyi tasarlanmış bir veritabanı şemasının her sürüme fayda sağlayacağı düşünüldü
- Rails UI’ın yakında değiştirileceği varsayılarak artık ona fazla zaman ayrılmamaya karar verildi
- Birkaç ay sonra iş yeterince başarılı oldu ve tam zamanlı bir geliştirici işe alınabildi; Rust geçişinin hızlanması bekleniyordu
- Gerçekte ise yeni stack’te zamanın büyük bölümü soyutlamalar ve mekanizmalar yazmaya harcandı
- Veritabanı erişimi
- Kullanıcıların yalnızca izin verilen verileri görmesini sağlayan yetki yönetimi
- Tüm değişikliklerin takibi
- Arama ve filtreler
- Tip sistemi üzerinden kısıtların zorlanması
- Ardından gerekli işlevler listesi de sürekli büyüdü
- Arka plan işleri
- İstemci güncellemeleri için pub/sub sistemi
- E-posta özellikleri
- Harici servislere bağlantı için OAuth 2.0
- SMTP ve POP
- Nesne depolama
- PDF üretimi
- Rust kütüphaneleri yeterince vardı; ancak uygun kütüphaneleri seçmek ve hepsini birbirine bağlamak ciddi bir işti
- Rust ORM seçimi, Svelte 5’in çıkış zamanı ve getirdiği iyileştirmeler gibi teknik uygulama ayrıntıları üzerine tartışmalar arttı
- Yaklaşık 1 yıl Rust ve Svelte yazdıktan sonra, kullanıcıların bu çalışmadan neredeyse hiç fayda sağlamadığı netleşti
- Yeni stack’teki bazı ekranlar akıcı ve hızlıydı; ancak asıl sorunlu yerler bunlar değildi
- En çok kullanılan sayfalar hâlâ Rails üzerinde çalışıyordu ve yeni stack’in “beta sürümünden” 10 kat daha fazla özelliğe sahipti
- Doğrudan değer eklemeyen işlere günler veya haftalar harcanmaya başlandı
Rails’e geri dönme kararı
- Yeni stack üzerinde çalışan kısımların Rails ile yeniden yazılmasına karar verildi
- Bu, tüm uygulamayı baştan yazmak değil; Rust/SvelteKit’e taşınan kısımları Rails’e geri döndürme seçimiydi
- Karar zor ama mantıklıydı
- Zaten çok zaman ve kod yatırımı yapılmıştı
- Ekip üyelerinin Ruby veya Rails’i sevmeme ihtimali de vardı
- Neyse ki ekip kararı anladı ve yeni şeyler öğrenmeye açıktı
- Rails’in güçlü yanları; pek çok özelliğin varsayılan olarak gelmesi, olgun ve kararlı bir kütüphane ekosistemine sahip olması ve convention’ları ile opinionated tasarımı sayesinde kodun kısa kalmasıdır
- Rails, ürün geliştirmeye odaklanmayı sağlar ve altyapı destek kodunu elle yazma yükünü azaltır
Rails’e dönüşten sonra kalan dersler
- Rust ve Svelte denemesi tamamen boşa gitmiş değildi
- Yeni dil ve framework’ler öğrenilirken başka dillerde de işe yarayan içgörüler ve fikirler edinildi
- Web uygulaması yazmak, seçimler ve ödünleşimler dizisidir
- Bir framework’ün kusurlarını kabul etmek ya da kendi uygulamanıza yatırım yapmak gerekir; bu yatırımın rekabet avantajı ve uzun vadeli getiri sağlayıp sağlamayacağı değerlendirilmelidir
- Rails’e geri döndürme işi büyük ölçüde basitti ve stresi de azalttı
- Son haftalarda yeni özellik geliştirme, mevcut özellikleri iyileştirme, UI güncelleme ve hata düzeltmede yeniden üretkenlik hissedilmeye başlandı
- Ruby ve Rails’in dezavantajları, kod yazarken dikkate alınması gereken koşullar olarak kabul edildi
- Runtime sorunları çoksa daha fazla test yapmak gerekir
- Bakımı zor spagetti kodu, dil veya framework’ten çok geliştiriciler kolayca yaratır
- Refactoring ve kod organizasyonuna daha fazla zaman ayırmak gerekir
- Rails’i ölçeklemek zorsa, başka şeyleri ölçeklemek de zor görülür
- Rails yeterince hızlıdır; Turbo ve caching ile etrafından dolaşmanın yolları vardır
- Kullanıcı açısından Rust stack’i gözle görülür biçimde daha hızlı değildi
- Rails’teki en ağır sayfanın P95 duration değeri 338 ms; iyileştirme payı olsa da yeterince hızlı hissedilen bir seviye
- Mükemmel bir dil veya framework yoktur; güçlü ve zayıf yönleri bilip onları yönetmek gerekir
- Rails, iyi ürünler geliştirmeye odaklanmak isteyenler için çok iyi framework’lerden biridir; uzun süredir sürdürülen ve aktif katkıcısı çok olan diğer framework’ler de benzer şekilde iyi seçenekler olabilir
2 yorum
Ruby ile yeniden yazın
Rails ile yeniden yazın
Hacker News yorumları
Rails’in, Django ve Laravel dahil web uygulaması geliştirmede diğer stack’lerin çok ilerisinde olduğunu düşünüyorum
Sıkıcı kısımların tamamını ele almış, gereken ihtiyaçları biliyor ve gerçek uygulamalar geliştirirken daha az engel çıkarıyor. 15 yıl boyunca web uygulamaları geliştirirken Meteor, Remix, Next.js, Express tabanlı Node.js vb. kullandım ama web geliştirmenin zaten büyük ölçüde çözülmüş bir problem olduğunu hissediyorum. JavaScript tarafı, büyük şirketlerin finansmanıyla geliştiricilerin yeni oyuncaklarla oynamasına daha yakın; Rust veya Go ise sistem geliştirme ve dağıtım altyapısı için iyi olsa da web geliştirmeye zorla sokmak bana mantıklı gelmiyor
Sayısız Java kütüphanesiyle kolayca entegre oluyor ve statik tipli bir dil olması birçok durumda yardımcı oluyor. Jackson üzerinden konvansiyona dayalı veri bağlama bile uzun zamandır harikaydı; dinamik tipli dillerde bu tür kısımlar daha sonra eklenmiş gibi güçlü bir izlenim veriyor
Ancak Rails, Django, Laravel içinde derinlere indikten sonra konvansiyonun dışına çıkan işler yapmanız gerekirse işler zorlaşabilir. Yapıştırıcı kodu kendiniz yazmadığınız için çoğu zaman teknik bir engelden çok bir anlama boşluğu oluşur. Yine de iç yapıyı doğru düzgün öğrenirseniz, sadece kodu okuyarak muazzam miktarda kodlama zamanından tasarruf edebilirsiniz. Elbette gerçekte bunu yapmayız; her şeyi baştan yazmak çok eğlenceli olduğu için iş açısından mantıklı olmasa bile yeni denemelerden keyif alırız
Sadece benim görüşüme güvenmeyin, verilere bakın: https://www.youtube.com/watch?v=Qp9SOOtgmS4
Kullanıcı sayısını veya saniyedeki istek sayısını kesin olarak bilmediğiniz bir servis için Rails kullanırsanız, lansman anındaki maliyetten tasarruf edersiniz ama sunucu maliyetini daha fazla ödeyebilirsiniz
Rails demosu, benim için benimsenme ve dikkat çekmede şansın ne kadar önemli olduğunu göstermek dışında pek etkileyici değildi. Bugün de değerini pek göremiyorum; Spring veya ASP.NET’i seçerdim. O Tcl tabanlı startup’ın kurucuları daha sonra OutSystems’i kurdu; bu, kurumsal ölçekte web geliştirmede başarılı olmuş az sayıdaki RAD aracından biri ve Portekiz IT sektörünün başarı örneklerinden biri
Eskiden Rails’i daha çok tercih ederdim, ama son 10 yılı aşkın sürede Python büyük ölçüde büyüdüğü için Django daha pratik bir seçenek hâline geldi. Makine öğrenmesi ve veri bilimi geliştiricileri zaten Python’a aşina; Django dokümantasyonu da o kadar iyi ki gerekirse çok kısa sürede üretken hâle gelebiliyorlar. Bunu birden fazla projede bizzat gördüm
Django, daha az dayatmacı kütüphanelere veya mikroframework’lere kıyasla uygulama yapısı etrafında doğan kararları ve tartışmaları da azaltıyor. Django/FLOSS topluluğu da beklediğimden çok daha aktif ve etkileşimleri keyifliydi. Yine de Rails’teki gibi generator’lar ve yerleşik veri seed etme; örneğin
rake db:seedgibi özellikler olsaydı iyi olurduRails’i 17 yıldır kullanıyorum, hâlâ seviyorum ve frontend için de tercih ediyorum
Angular uygulamalarında görüldüğü gibi dosyanın başı gereksiz yere uzun bir
includeslistesine dönüşmeden kodu ayırmak için tüm seçenekleri sunuyor. Frontend’de mantık ile markup’ı ayırmanın bir dezavantajı olduğunu düşünmüyorum. Mantığı bağımsız olarak test edebiliyorsunuz ve genellikle bug ararken sorunun markup’ta mı yoksa mantıkta mı olduğunu anlayabildiğiniz için izini sürmek daha kolay oluyor. 200 satırı aşan epey Ruby metodu da yazdım ama bu gerçekten benim hatamdı; daha ayrıntılı birim test sistemi olsaydı engellenebilirdiincludeslistelerinden kaçınmanın bir başka yolu da her şeyi global yapmakBunun bir avantaj olup olmadığından pek emin değilim. Import’lar çok yer kaplayabilir ama her şeyin nasıl bağlandığını açıkça gösterir. Rahatsız ediyorsa IDE’de katlayıp kapatabilirsiniz
Gerçekçi bir yazı olduğu için hoşuma gitti. Kilit cümlesi her zaman başucunda tutulup okunacak türden
“İlginç olan şu ki, eskiden Ruby ve Rails’te sinirimi bozan şeyler artık çok daha az rahatsız ediyor. Kusursuz bir dil ya da framework olmadığını kabul ettim. Güçlü ve zayıf yönlerini bilip onlarla başa çıkmak yeterli”
Çoğu durumda framework’ü yeterince anlamamış oluyorlar ve neden o şekilde tasarlandığını da bilmiyorlar. Oysa sınırlarını bilip framework’le birlikte çalışmak çok daha verimli. Kullandığınız araçları doğru düzgün anlamaya zaman ayırmalısınız
“Bu sırada uygulamanın ‘versiyon 2’sini geliştirmeye başladım. Backend dili olarak Rust’ı, frontend için SvelteKit’i seçtim.”
v1 Rails ile başladığına göre, zaten iyi çalışan bir framework’ü terk ederken dikkatli olunması gerektiğini söyleyen bir yazı gibi okunuyor
Rust, çok zeki insanların yaptığı bir dil; ama odağı, benim gibi sahadaki geliştiricilerin işe yarar ve tutarlı şeyler üretmesinden çok tip teorisine ve birkaç allocation tasarrufuna kaymış gibi görünüyor
2020’den beri Rust kullanıyorum, buna rağmen Rust blog yazılarını okuduğumda kelimenin tam anlamıyla yarısını anlamıyorum
Özellikle devasa ana akım dillerle karşılaştırınca. Örneğin trait’ler, C++’taki abstract class ve concept’lerin, Python’daki abstract class ve protocol kullanım senaryolarının hepsini kapsıyor. Hataları exception ile mi yoksa değer döndürerek mi ele alacağına dair bir tutarsızlık da yok. Panic var ama assertion gibi kullanılıyor.
if-elsebir expression ise ayrı bir ternary operator’a da gerek kalmıyorSon zamanlarda Rust’ı her şey için kullanma eğilimi var, ama CRUD uygulamaları için yapılmış bir dil değil. Bu yazı nihayetinde daha çok “işe uygun aracı kullan; boş zamanında sevdiğin dil ya da son moda dil her zaman doğru seçim değildir” diyen bir vaka çalışmasına benziyor
Garbage collector maliyetini karşılayabiliyorsan Rust veya C++ kullanıp bellek yönetimiyle uğraşmana gerek yok. Rust’ı genel amaçlı programlama dili olarak tanıtanlar da var ve teoride doğru; ama 90’larda C++ da böyle tanıtılıyordu. Memory allocation’ı en aza indirmek genelde Rust veya C++ seçerken aradığın tam da o özellik
Rust ile büyük bir web uygulamasını hızlıca yazmak zor
Rails ile yazılmış büyük bir uygulama hızlı çalışmayabilir; çok bug’lı olabilir ya da bakımı ve ölçeklenmesi zor olabilir. Java, C#, Go gibi seçenekler seksi veya havalı görünmeyebilir ama Rails kadar hızlı iş çıkarıp Rust’a yakın hızlarda çalışabilir. Genelde daha az bug çıkarır, bakımı ve ölçeklenmesi daha kolay projelere dönüşür
Rails’ten ne kadar nefret edersen et, bu C++ ile WordPress’ten daha hızlı blog yapabileceğini söylemeye benzer
Özenle yapılırsa mutlaka böyle olmaz. Büyük, neredeyse bugsız, stabil ve değiştirmesi kolay birçok Rails uygulaması da gördüm; berbat Rails uygulamaları ve Go uygulamaları da gördüm. Bazı diller veya framework’ler o duruma düşmeyi kolaylaştırabilir, ama her şeyde bu olasılık var; ayrıca farklı nedenlerle zorlaşan trade-off’lar da var. Disiplin olursa her şeyi çalışır hale getirebilirsin
Benzer beceri seviyesindeki geliştiricilerle, çoğu web uygulaması özelliğinde Rails; Java, C# ve Go’dan daha hızlı geliştirilir. Benim deneyimime göre 3 kata kadar hızlıydı. Hem büyük monolith’lerle hem de service-oriented architecture ile çalıştım, Java ekipleriyle de birlikte çalıştım. Bir dönem aynı inançla bir Rails organizasyonuna Go’yu da sokmuştum
Sonunda asıl sorun, MVC’nin her şeye uymaması ve namespace’lerin yeterince sıkı kullanılmamasıydı. Küçük ve hedefli alanlarda bu sorun düzeltilirse Rails gayet iyi
Dolayısıyla yeterli yönetim, iyi mimari ve bakım varsa Rails ile de mümkün olduğunu düşünüyorum
Son zamanlarda Rails’e TypeScript alternatifi olarak AdonisJS’i test etmeye başladım
“TypeScript ile Rails” ilginizi çekiyorsa denemenizi öneririm. En popüler TypeScript stack’lerine kıyasla Rails veya Laravel’e çok daha yakın hissettiriyor; stack’in her parçasını sizin seçmenizi ya da üçüncü taraf template’lere güvenmenizi istemek yerine batteries included yaklaşımında
Son zamanlarda Rails, Laravel, Django gibi batteries included backend framework’lerine çekiliyorum, ama kariyerim boyunca neredeyse hep TypeScript kullandığım için şimdi değiştirmektense aynı yolda devam etmek daha kolay. Test edince ne düşündüğünü ve bahsettiğin diğer framework’lerle karşılaştırıp karşılaştıramayacağını merak ediyorum
Son kişisel projemde NestJS’i seçtim ama kısa süre sonra Adonis’i öğrendim ve gerçekten taşınsam mı diye düşünüyorum
Next.js ve SvelteKit gibi teknolojilerin JavaScript geliştiricileri arasında popüler olmasının nedeni, Node.js’in kötü backend geliştirme deneyimini hafifletmeleri
TypeScript ile backend yapmak C# ile yapmaya benziyor ama iyi tasarlanmış bir dilin avantajları eksikmiş gibi hissettiriyor. Rails ile backend geliştirme deneyiminden çok farklı. Node.js geliştiricileri de sorunun yalnızca ekosistem ve araçlardan ibaret olmadığını, uygulama geliştirmek için bütünsel bir çözüm sunan framework’lerin önemli olduğunu fark etmeye başladı. Next.js ve SvelteKit’in yardımcı olduğu nokta burası; Rails’in 2006’da yenilik getirdiği nokta da buydu
Backend’de kod çalıştırabilmeyi Rails ile mi karşılaştırıyoruz? Veritabanı erişimi, migration, ORM, queue, scheduled jobs, validation, translation, authentication, authorization desteği nerede? Next.js’in yardımcı olduğu tek şey, insanları Vercel platformuna itmek
Frontend’de ise JavaScript yerine TypeScript’i tercih ederim
Rust’ın web backend için pek iyi olmadığına dair yorumlar biraz kafa karıştırıcı
beampaint.com veya probablyup.net gibi küçük servisler geliştirirken benim deneyimim bunun tersiydi. Daha büyük uygulamalar geliştirdiğinizde karmaşıklık ciddi biçimde artabilir, hatta muhtemelen kesinlikle artar; ama büyük kod tabanlarında ihtiyaç duyulan güveni vermek konusunda Rust harika
Eski framework’ler bunları varsayılan olarak sunuyor, ama Rust framework’lerinde yaşları nedeniyle bunun böyle olma ihtimali daha düşük. Eksik özelliklerin ikiden altıya çıktığını düşünmek yeterli
Bu yazı neredeyse benim hayat hikâyem gibi
Rails kullanırken 2013’te Go ile yazdığım bir SaaS uygulaması yayımladım; her şeyi kendim çözmek zorunda kaldım. Migration’lar, veritabanı katmanı, asset pipeline, dağıtım, doğrulama, logging, ödeme vb. Rails’in hazır sunduğu o kadar çok şey vardı ki
Go’yu sevmiyorum ama burada suçlanacak şey Go değil. CRUD uygulamaları için Go kullanmak en baştan berbat bir karar
Django da benzer hissettiriyor
Performans sorunları Django’nun kendisinden çok veritabanı sorguları nedeniyle ortaya çıkıyordu
Sıkça unutulan kolay optimizasyonlar arasında veritabanı indeksi oluşturmak, mantığın daha fazlasını kod yerine veritabanına devretmek — örneğin MongoDB pipeline’ları veya Postgres partial index’leri kullanmak — çoğu istek için Brotli/zstd sıkıştırmasını açmak, Redis gibi bellek içi cache ile nesneleri veya istekleri cache’lemek ve uygun cache-control header’ları ayarlamak var. Frontend tarafında da binlerce nesneyi DOM node’ları olarak render etmek yerine canvas kullanmak gibi optimizasyonlar gerekiyor. Yakın zamanda bir harita uygulamasında böyle bir sorun gördüm