Buggy Rails Kodu: Rails Geliştiricilerinin Yaptığı En Yaygın 10 Hata
Yayınlanan: 2022-03-11Ruby on Rails (“Rails”), web uygulaması geliştirme sürecini basitleştirmeye ve düzene sokmaya çalışan Ruby programlama diline dayanan popüler bir açık kaynak çerçevesidir.
Raylar, konfigürasyon üzerinde konvansiyonel prensip üzerine inşa edilmiştir. Basitçe söylemek gerekirse, bu, varsayılan olarak, Rails'in uzman geliştiricilerinin "standart" en iyi uygulama kurallarına (adlandırma, kod yapısı vb. -büyülü bir şekilde” bu ayrıntıları belirtmenize gerek kalmadan. Bu paradigmanın avantajları olsa da, tuzakları da vardır. En önemlisi, çerçevede perde arkasında gerçekleşen “sihir” bazen kafa karışıklığına, kafa karışıklığına ve “ne oluyor?” problem türleri. Ayrıca güvenlik ve performans açısından istenmeyen sonuçlara da sahip olabilir.
Buna göre Rails'in kullanımı kolay olduğu gibi kötüye kullanılması da zor değildir. Bu öğretici, bunlardan nasıl kaçınılacağı ve neden oldukları sorunlar da dahil olmak üzere 10 yaygın Rails sorununa bakar.
Yaygın Hata 1: Kontrolöre çok fazla mantık koymak
Rails, bir MVC mimarisine dayanmaktadır. Rails topluluğunda, bir süredir şişman model, sıska denetleyici hakkında konuşuyoruz, ancak son zamanlarda devraldığım birkaç Rails uygulaması bu ilkeyi ihlal etti. Görünüm mantığını (bir yardımcıda daha iyi barındırılır) veya etki alanı/model mantığını denetleyiciye taşımak çok kolaydır.
Sorun, denetleyici nesnesinin, kod tabanında gelecekteki değişiklikleri zor ve hataya açık hale getirerek tek sorumluluk ilkesini ihlal etmeye başlamasıdır. Genel olarak, denetleyicinizde olması gereken mantık türleri şunlardır:
- Oturum ve çerez işleme. Bu, kimlik doğrulama/yetkilendirme veya yapmanız gereken herhangi bir ek tanımlama bilgisi işlemini de içerebilir.
- Model seçimi. İstekten iletilen parametreler verildiğinde doğru model nesnesini bulma mantığı. İdeal olarak bu, yanıtı oluşturmak için daha sonra kullanılacak bir örnek değişkeni ayarlayan tek bir bulma yöntemine yapılan bir çağrı olmalıdır.
- Parametre yönetimi isteyin. İstek parametrelerini toplamak ve bunları sürdürmek için uygun bir model yöntemini çağırmak.
- Oluşturma/yönlendirme. Sonucu işleme (html, xml, json, vb.) veya uygun şekilde yeniden yönlendirme.
Bu hala tek sorumluluk ilkesinin sınırlarını zorluyor olsa da, Rails çerçevesinin denetleyicide sahip olmamızı gerektirdiği minimum düzeydedir.
Yaygın Hata #2: Görünüme çok fazla mantık koymak
Kullanıma hazır Rails şablonlama motoru ERB, değişken içerikli sayfalar oluşturmanın harika bir yoludur. Ancak, dikkatli olmazsanız, kısa sürede, yönetimi ve bakımı zor olabilen HTML ve Ruby kodunun bir karışımı olan büyük bir dosyayla karşılaşabilirsiniz. Bu aynı zamanda çok fazla tekrara ve DRY (kendini tekrar etme) ilkelerinin ihlaline yol açabilecek bir alandır.
Bu, birkaç şekilde kendini gösterebilir. Bunlardan biri, görünümlerde koşullu mantığın aşırı kullanımıdır. Basit bir örnek olarak, şu anda oturum açmış olan kullanıcıyı döndüren bir current_user
yöntemimiz olduğu bir durumu düşünün. Genellikle, görünüm dosyalarında bunun gibi koşullu mantık yapıları olacaktır:
<h3> Welcome, <% if current_user %> <%= current_user.name %> <% else %> Guest <% end %> </h3>
Bunun gibi bir şeyi ele almanın daha iyi bir yolu, birisi oturum açmış olsun veya olmasın, current_user
tarafından döndürülen nesnenin her zaman ayarlandığından ve görünümde kullanılan yöntemlere makul bir şekilde yanıt verdiğinden emin olmaktır (bazen boş olarak adlandırılır). nesne). Örneğin, app/controllers/application_controller
içinde current_user
yardımcısını şöyle tanımlayabilirsiniz:
require 'ostruct' helper_method :current_user def current_user @current_user ||= User.find session[:user_id] if session[:user_id] if @current_user @current_user else OpenStruct.new(name: 'Guest') end end
Bu, önceki görünüm kodu örneğini bu basit kod satırıyla değiştirmenize olanak sağlar:
<h3>Welcome, <%= current_user.name -%></h3>
Önerilen birkaç ek Rails en iyi uygulaması:
- Sayfalarınızda tekrarlanan şeyleri kapsüllemek için görünüm düzenlerini ve bölümleri uygun şekilde kullanın.
- Görünüm oluşturma mantığını bir Ruby nesnesine yerleştirmek için Draper gem gibi sunucuları/dekoratörleri kullanın. Daha sonra, aksi halde görünüm kodunuza koymuş olabileceğiniz mantıksal işlemleri gerçekleştirmek için bu nesneye yöntemler ekleyebilirsiniz.
Yaygın Hata #3: Modele çok fazla mantık koymak
Görünümlerde ve denetleyicilerde mantığı en aza indirmeye yönelik rehberlik göz önüne alındığında, bir MVC mimarisinde tüm bu mantığı koymak için kalan tek yer modellerde olacaktır, değil mi?
Pek iyi değil.
Pek çok Rails geliştiricisi aslında bu hatayı yapar ve ActiveRecord
model sınıflarındaki her şeyi yapıştırarak yalnızca tek sorumluluk ilkesini ihlal etmekle kalmayıp aynı zamanda bir bakım kabusu olan mongo dosyalarına yol açar.
E-posta bildirimleri oluşturma, harici hizmetlere arabirim oluşturma, diğer veri biçimlerine dönüştürme ve benzerleri gibi işlevlerin, bir veritabanında veri bulmak ve sürdürmekten biraz daha fazlasını yapması gereken bir ActiveRecord
modelinin temel sorumluluğuyla pek ilgisi yoktur.
Öyleyse mantık görünümlere gitmemeli ve kontrolörlere gitmemeli ve modellere gitmemeli, o zaman nereye gitmeli?
Düz eski Ruby nesnelerini (PORO'lar) girin. Rails gibi kapsamlı bir çerçeveyle, daha yeni geliştiriciler genellikle çerçevenin dışında kendi sınıflarını oluşturmaya isteksizdir. Bununla birlikte, mantığı modelden PORO'lara taşımak, genellikle doktorun aşırı karmaşık modellerden kaçınmak için emrettiği şeydir. PORO'larla, e-posta bildirimleri veya API etkileşimleri gibi şeyleri bir ActiveRecord
modeline yapıştırmak yerine kendi sınıflarına dahil edebilirsiniz.
Bunu akılda tutarak, genel olarak konuşursak, modelinizde kalması gereken tek mantık şudur:
-
ActiveRecord
yapılandırması (yani ilişkiler ve doğrulamalar) - Bir avuç özniteliği güncellemeyi ve bunları veritabanına kaydetmeyi kapsüllemek için basit mutasyon yöntemleri
- Dahili model bilgilerini gizlemek için sarmalayıcılara erişin (örneğin, veritabanındaki
first_name
velast_name
alanlarını birleştiren birfull_name
yöntemi) - Gelişmiş sorgular (yani, basit bir bulgudan
find
karmaşık olan); genel olarak konuşursak, model sınıfının dışında hiçbir zamanwhere
yöntemini veya bunun gibi diğer sorgu oluşturma yöntemlerini kullanmamalısınız.
Yaygın Hata #4: Genel yardımcı sınıfları çöplük olarak kullanmak
Bu hata, yukarıdaki 3 numaralı hatanın gerçekten bir sonucu. Tartışıldığı gibi, Rails çerçevesi bir MVC çerçevesinin adlandırılmış bileşenlerine (yani model, görünüm ve denetleyici) vurgu yapar. Bu bileşenlerin her birinin sınıflarına ait olan şeylerin oldukça iyi tanımları vardır, ancak bazen bu üçünden herhangi birine uymayan yöntemlere ihtiyacımız olabilir.
Rails oluşturucular, oluşturduğumuz her yeni kaynakla birlikte uygun bir şekilde bir yardımcı dizini ve yeni bir yardımcı sınıf oluşturur. Yine de, modele, görünüme veya denetleyiciye resmi olarak uymayan herhangi bir işlevi bu yardımcı sınıflara doldurmaya başlamak çok cazip hale geliyor.
Rails kesinlikle MVC merkezli olsa da, hiçbir şey kendi sınıf türlerinizi oluşturmanızı ve bu sınıfların kodunu tutmak için uygun dizinleri eklemenizi engellemez. Ek işlevselliğe sahip olduğunuzda, hangi yöntemlerin bir arada gruplandığını düşünün ve bu yöntemleri içeren sınıflar için iyi isimler bulun. Rails gibi kapsamlı bir çerçeve kullanmak, iyi nesne yönelimli tasarım en iyi uygulamalarının yoldan çıkmasına izin vermek için bir mazeret değildir.
Yaygın Hata #5: Çok fazla mücevher kullanmak
Ruby ve Rails, bir geliştiricinin düşünebileceği hemen hemen her yeteneği toplu olarak sağlayan zengin bir mücevher ekosistemi tarafından desteklenir. Bu, hızlı bir şekilde karmaşık bir uygulama oluşturmak için harika, ancak uygulamanın Gemfile
değerli taşların sayısının sağlanan işlevsellik ile karşılaştırıldığında orantısız şekilde büyük olduğu birçok şişirilmiş uygulama gördüm.
Bu, birkaç Rails sorununa neden olur. Taşların aşırı kullanımı, bir Rails işleminin boyutunu olması gerekenden daha büyük hale getirir. Bu, üretimdeki performansı yavaşlatabilir. Kullanıcı hayal kırıklığına ek olarak, bu aynı zamanda daha büyük sunucu belleği yapılandırmalarına ve artan işletme maliyetlerine ihtiyaç duymasına neden olabilir. Ayrıca daha büyük Rails uygulamalarının başlatılması daha uzun sürer, bu da geliştirmeyi yavaşlatır ve otomatik testlerin daha uzun sürmesine neden olur (ve kural olarak, yavaş testler o kadar sık çalıştırılmaz).
Uygulamanıza getirdiğiniz her mücevherin sırayla diğer taşlara bağımlılıkları olabileceğini ve bunların da diğer taşlara bağımlılıkları olabileceğini unutmayın. Başka taşlar eklemek bu nedenle bileşik bir etkiye sahip olabilir. Örneğin, rails_admin
eklemek, temel Rails kurulumundan %10'luk bir artışla toplamda 11 gem daha getirecektir.
Bu yazı itibariyle, yeni bir Rails 4.1.0 kurulumu Gemfile.lock
dosyasında 43 değerli taş içeriyor. Bu açıkça Gemfile
daha fazlasıdır ve bir avuç standart Rails mücevherinin bağımlılık olarak getirdiği tüm taşları temsil eder.
Her bir mücevheri eklerken ekstra ek yükün değip değmediğini dikkatlice düşünün. Örnek olarak, geliştiriciler genellikle rails_admin
çünkü esasen model yapısına güzel bir web ön ucu sağlar, ancak bu gerçekten süslü bir veritabanı tarama aracından çok daha fazlası değildir. Uygulamanız ek ayrıcalıklara sahip yönetici kullanıcılar gerektirse bile, muhtemelen onlara ham veritabanı erişimi vermek istemezsiniz ve bu gem'i eklemek yerine kendi daha akıcı yönetim işlevinizi geliştirerek daha iyi hizmet alırsınız.
Yaygın Hata #6: Günlük dosyalarınızı yok saymak
Çoğu Rails geliştiricisi, geliştirme ve üretim sırasında mevcut olan varsayılan günlük dosyalarının farkında olsa da, genellikle bu dosyalardaki bilgilere yeterince dikkat etmezler. Pek çok uygulama üretimde Honeybadger veya New Relic gibi günlük izleme araçlarına güvenirken, uygulamanızı geliştirme ve test etme süreci boyunca günlük dosyalarınıza göz kulak olmak da önemlidir.

Bu öğreticide daha önce bahsedildiği gibi, Rails çerçevesi, özellikle modellerde sizin için çok fazla “sihir” yapar. Modellerinizdeki çağrışımları tanımlamak, ilişkileri çekmeyi ve her şeyin sizin için uygun olmasını çok kolaylaştırır. Model nesnelerinizi doldurmak için gereken tüm SQL sizin için oluşturulur. Bu harika. Ancak oluşturulan SQL'in verimli olduğunu nereden biliyorsunuz?
Sıklıkla karşılaşacağınız bir örneğe N+1 sorgu problemi adı verilir. Sorun iyi anlaşılmış olsa da, bunun olduğunu gözlemlemenin tek gerçek yolu, günlük dosyalarınızdaki SQL sorgularını gözden geçirmektir.
Örneğin, belirli bir dizi gönderi için tüm yorumları görüntüleyeceğiniz tipik bir blog uygulamasında aşağıdaki sorguya sahip olduğunuzu varsayalım:
def comments_for_top_three_posts posts = Post.limit(3) posts.flat_map do |post| post.comments.to_a end end
Bu yöntemi çağıran bir isteğin günlük dosyasına baktığımızda, aşağıdaki gibi bir şey görürüz, burada üç gönderi nesnesini almak için tek bir sorgu yapılır ve ardından bu nesnelerin her birinin yorumlarını almak için üç sorgu daha yapılır:
Started GET "/posts/some_comments" for 127.0.0.1 at 2014-05-20 20:05:13 -0700 Processing by PostsController#some_comments as HTML Post Load (0.4ms) SELECT "posts".* FROM "posts" LIMIT 3 Comment Load (5.6ms) ELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 1]] Comment Load (0.4ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 2]] Comment Load (1.5ms) SELECT "comments".* FROM "comments" WHERE "comments"."post_id" = ? [["post_id", 3]] Rendered posts/some_comments.html.erb within layouts/application (12.5ms) Completed 200 OK in 581ms (Views: 225.8ms | ActiveRecord: 10.0ms)
ActiveRecord
istekli yükleme özelliği, yüklenecek tüm ilişkileri önceden belirlemenize izin vererek sorgu sayısını önemli ölçüde azaltmayı mümkün kılar. Bu, inşa edilmekte olan Arel ( ActiveRecord::Relation
) nesnesindeki includes
(veya preload
) yöntemi çağrılarak yapılır. includes
ile, ActiveRecord
belirtilen tüm ilişkilendirmelerin mümkün olan en az sayıda sorgu kullanılarak yüklenmesini sağlar; Örneğin:
def comments_for_top_three_posts posts = Post.includes(:comments).limit(3) posts.flat_map do |post| post.comments.to_a end end
Yukarıdaki revize edilmiş kod çalıştırıldığında, günlük dosyasında tüm yorumların üç yerine tek bir sorguda toplandığını görüyoruz:
Started GET "/posts/some_comments" for 127.0.0.1 at 2014-05-20 20:05:18 -0700 Processing by PostsController#some_comments as HTML Post Load (0.5ms) SELECT "posts".* FROM "posts" LIMIT 3 Comment Load (4.4ms) SELECT "comments".* FROM "comments" WHERE"comments "."post_id" IN (1, 2, 3) Rendered posts/some_comments.html.erb within layouts/application (12.2ms) Completed 200 OK in 560ms (Views: 219.3ms | ActiveRecord: 5.0ms)
Çok daha verimli.
N+1 sorununa yönelik bu çözüm, gerçekten yalnızca, yeterince dikkat göstermezseniz, uygulamanızda "başlangıçta" var olabilecek türden verimsizliklerin bir örneğidir. Buradaki çıkarım, yanıtlarınızı oluşturan koddaki verimsizlikleri kontrol etmek (ve ele almak!) için geliştirme sırasında geliştirme ve test günlük dosyalarınızı kontrol ediyor olmanız gerektiğidir.
Günlük dosyalarını gözden geçirmek, kodunuzdaki verimsizlikleri fark etmenin ve uygulamanız üretime geçmeden önce bunları düzeltmenin harika bir yoludur. Aksi takdirde, geliştirme ve testte birlikte çalıştığınız veri kümesinin üretimdekinden çok daha küçük olması muhtemel olduğundan, sisteminiz yayına girene kadar ortaya çıkan bir Rails performans sorununun farkında olmayabilirsiniz. Yeni bir uygulama üzerinde çalışıyorsanız, üretim veri kümeniz bile küçük başlayabilir ve uygulamanız iyi çalışıyor gibi görünür. Ancak, üretim veri kümeniz büyüdükçe, bunun gibi Rails sorunları, uygulamanızın giderek daha yavaş çalışmasına neden olur.
Günlük dosyalarınızın bir grup bilgiyle tıkandığını fark ederseniz, burada ihtiyacınız olmayan bilgileri temizlemek için yapabileceğiniz bazı şeyler vardır (buradaki teknikler geliştirme için olduğu kadar üretim günlükleri için de geçerlidir).
Yaygın Hata #7: Otomatik testlerin olmaması
Ruby ve Rails, varsayılan olarak güçlü otomatik test yetenekleri sağlar. Birçok Rails geliştiricisi, TDD ve BDD stillerini kullanarak çok karmaşık testler yazar ve rspec ve salatalık gibi değerli taşlarla daha da güçlü test çerçevelerinden yararlanır.
Rails uygulamanıza otomatik test eklemek ne kadar kolay olsa da, öncekiler tarafından tam anlamıyla hiçbir testin yazılmadığı (veya en iyi ihtimalle çok az sayıda) miras aldığım veya katıldığım proje sayısı beni çok şaşırttı. Geliştirme Takımı. Testinizin ne kadar kapsamlı olması gerektiği konusunda pek çok tartışma olsa da, her uygulama için en azından bazı otomatik testlerin olması gerektiği oldukça açık.
Genel bir kural olarak, denetleyicilerinizdeki her eylem için en az bir üst düzey entegrasyon testi yazılmalıdır. Gelecekte bir noktada, diğer Rails geliştiricileri büyük olasılıkla kodu genişletmek veya değiştirmek veya bir Ruby veya Rails sürümünü yükseltmek isteyecektir ve bu test çerçevesi onlara uygulamanın temel işlevselliğinin doğruluğunu doğrulamanın net bir yolunu sağlayacaktır. Çalışma. Bu yaklaşımın ek bir yararı, gelecekteki geliştiricilere uygulama tarafından sağlanan tam işlevsellik koleksiyonunun net bir tanımını sağlamasıdır.
Yaygın Hata #8: Harici hizmetlere yapılan aramaları engelleme
3. taraf Rails hizmetleri sağlayıcıları, API'lerini saran değerli taşlar aracılığıyla hizmetlerini uygulamanıza entegre etmeyi genellikle çok kolaylaştırır. Ancak harici hizmetinizde bir kesinti olursa veya çok yavaş çalışmaya başlarsa ne olur?
Bu aramaları engellemekten kaçınmak için, bir isteğin normal işlenmesi sırasında bu hizmetleri doğrudan Rails uygulamanızda aramak yerine, bunları mümkün olduğunda bir tür arka plan işi kuyruklama hizmetine taşımalısınız. Bu amaçla Rails uygulamalarında kullanılan bazı popüler taşlar şunlardır:
- gecikmiş iş
- bekleme
- Sidekiq
İşlemeyi bir arka plan iş kuyruğuna devretmenin pratik olmadığı veya mümkün olmadığı durumlarda, harici hizmetin çökmesi veya sorunlar yaşanması gibi kaçınılmaz durumlar için uygulamanızın yeterli hata işleme ve yük devretme hükümlerine sahip olduğundan emin olmanız gerekir. . Uygulamanızı harici hizmet olmadan da test etmelisiniz (belki de uygulamanızın bulunduğu sunucuyu ağdan kaldırarak), bunun beklenmeyen sonuçlara yol açmadığını doğrulamak için.
Yaygın Hata #9: Mevcut veritabanı geçişleriyle evlenmek
Rails'in veritabanı taşıma mekanizması, veritabanı tablolarını ve satırlarını otomatik olarak eklemek ve kaldırmak için talimatlar oluşturmanıza olanak tanır. Bu geçişleri içeren dosyalar sıralı bir şekilde adlandırıldığından, üretimle aynı şemaya boş bir veritabanı getirmek için bunları en baştan oynatabilirsiniz. Bu nedenle, uygulamanızın veritabanı şemasındaki ayrıntılı değişiklikleri yönetmenin ve Rails sorunlarından kaçınmanın harika bir yoludur.
Bu, projenizin başlangıcında kesinlikle iyi sonuç verse de, zaman geçtikçe, veritabanı oluşturma süreci oldukça uzun sürebilir ve bazen geçişler yanlış yerleştirilir, düzensiz eklenir veya aynı veritabanı sunucusunu kullanan diğer Rails uygulamalarından gelir.
Rails, veritabanı geçişleri çalıştırıldığında genellikle güncellenen db/schema.rb
(varsayılan olarak) adlı bir dosyada mevcut şemanızın bir temsilini oluşturur. schema.rb
dosyası, rake db:schema:dump
görevi çalıştırılarak hiçbir geçiş olmadığında bile oluşturulabilir. Yaygın bir Rails hatası, kaynak deponuza yeni bir geçişi kontrol etmek, ancak buna uygun olarak güncellenmiş schema.rb
dosyasını kontrol etmemektir.
Geçişler kontrolden çıktığında ve çalışması çok uzun sürdüğünde veya artık veritabanını düzgün bir şekilde oluşturmadığında, geliştiriciler eski geçiş dizinini temizlemekten, yeni bir şema atmak ve oradan devam etmekten korkmamalıdır. Yeni bir geliştirme ortamı kurmak, çoğu geliştiricinin rake db:migrate
yerine bir rake db:schema:load
gerektirir.
Bu sorunlardan bazıları Rails Guide'da da tartışılmaktadır.
Yaygın Hata #10: Hassas bilgileri kaynak kod havuzlarına kontrol etme
Rails çerçevesi, birçok saldırı türünden etkilenmeyen güvenli uygulamalar oluşturmayı kolaylaştırır. Bunun bir kısmı, bir tarayıcı ile oturum güvenliğini sağlamak için gizli bir belirteç kullanılarak gerçekleştirilir. Bu belirteç şimdi config/secrets.yml
/secrets.yml içinde saklansa ve bu dosya, üretim sunucuları için bir ortam değişkeninden belirteci okusa da, Rails'in önceki sürümleri belirteci config/initializers/secret_token.rb
içinde içeriyordu. Bu dosya genellikle yanlışlıkla uygulamanızın geri kalanıyla birlikte kaynak kod deposuna eklenir ve bu olduğunda, depoya erişimi olan herkes artık uygulamanızın tüm kullanıcılarını kolayca tehlikeye atabilir .
Bu nedenle, depo yapılandırma dosyanızın (örneğin, git kullanıcıları için .gitignore
) belirtecinizi içeren dosyayı hariç tuttuğundan emin olmalısınız. Üretim sunucularınız daha sonra belirteçlerini bir ortam değişkeninden veya dotenv gem'in sağladığı gibi bir mekanizmadan alabilir.
Eğitim Özeti
Rails, sağlam bir web uygulaması oluşturmak için gerekli olan birçok çirkin ayrıntıyı gizleyen güçlü bir çerçevedir. Bu, Rails web uygulaması geliştirmeyi çok daha hızlı hale getirirken, geliştiriciler, uygulamalarının büyüdükçe kolayca genişletilebilir ve sürdürülebilir olduğundan emin olmak için olası tasarım ve kodlama hatalarına dikkat etmelidir.
Geliştiricilerin ayrıca uygulamalarını daha yavaş, daha az güvenilir ve daha az güvenli hale getirebilecek sorunların farkında olmaları gerekir. Yüksek kaliteli ve yüksek performanslı bir uygulama sağlamaya yardımcı olmak için, çerçeveyi incelemek ve geliştirme süreci boyunca yaptığınız mimari, tasarım ve kodlama ödünleşimlerini tam olarak anladığınızdan emin olmak önemlidir.