Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Ruby

Common Ruby on Rails Challenges และ Practical Solutions

Welcome to the last part of my Ruby on Rails Patterns and Anti-Patterns series เป็นการเดินทางนานเขียนและ research topics เยอะ ในโพสต์นี้ จะครอบคลุม common problems ที่เจอบ่อยตอน build และ ship Rails applications ตลอดปี

Ideas ที่จะพูด apply ได้เกือบทุกที่ในโค้ด ถือเป็น general ideas ไม่เกี่ยว MVC pattern ถ้าสนใจ patterns/anti-patterns ใน Rails MVC อ่าน Model, View, Controller blog posts

Let's jump into general problems และ takeaways

Selfish Objects และ Law of Demeter

Law of Demeter = heuristic ได้ชื่อจาก Demeter Project Idea: objects OK ถ้า call one method at a time ไม่ chain multiple method calls ใน practice:

# Bad song.label.address # Good song.label_address

ตอนนี้ song object ไม่ต้องรู้ address มาจากไหน — address เป็น responsibility ของ label object Encourage chain only one method call ทำ objects 'selfish' ไม่ share full info ตรงๆ แต่ผ่าน helper methods

Luckily ใน Rails ไม่ต้องเขียน helper method ใช้ delegate helper:

def Label < ApplicationModel belongs_to :song delegate :address, to: :song end

เล่น options ของ delegate ใน docs แต่วิดีและ execution เรียบง่าย ด้วย Law of Demeter ลด structural coupling พร้อม delegate powerful ทำได้ในบรรทัดน้อย options มาก

Idea ใกล้เคียง: Single-responsibility Principle (SRP) module/class/function ควร responsible สำหรับ single part ของ system อีกมุม:

Gather things that change for the same reasons. Separate things that change for different reasons.

Folks เข้าใจ SRP ต่างกัน แต่ idea = keep building blocks responsible for single thing อาจท้าทาย achieve SRP ตอน Rails app expand แต่ตระหนักตอน refactor

ตอนเพิ่ม features และเพิ่ม LOC มัก reach out for quick solution มาดู grabbing the quick fix

I Know a Guy (ต้องการ Ruby Gem นั้นจริงไหม?)

สมัย Rails hot topic มี boom open-source collaboration gems ใหม่โผล่ทุกมุม (เหมือน JS libraries ตอนแต่น้อยกว่า)

👆 Info from Module Counts

Common approach: หา existing gem แก้ปัญหา

ไม่ผิด แต่ advice ก่อนตัดสินใจ install gem:

ถามตัวเอง:

  • จะใช้ features ของ gem สัดส่วนเท่าไหร่?
  • มี gem คล้ายที่ 'simpler' หรือ up-to-date กว่า?
  • Implement feature ที่ต้องการได้ง่ายและมั่นใจไหม?

Evaluate ว่า worth implement ถ้าไม่ใช้ whole gem features หรือ gem implementation ซับซ้อนเกิน คุณทำ simpler ได้ → custom solution

Factor อื่น: gem repository active มั้ย? Active maintainers? Last release เมื่อไหร่?

Watch out gem dependencies อย่าลocked specific version ของ dependency check Gemfile.spec consult RubyGems version specifying

เรื่อง gems มี related idea: 'Not Invented Here' (NIH) phenomenon ใน Rails/Ruby world มาดู next section

Not Invented Here (อาจต้องการ Ruby Gem จริงๆ?)

ใน career หลายครั้งเจอ people (รวมผม) ตก 'Not Invented Here' syndrome คล้าย 'reinventing the wheel' Teams/orgs ไม่ trust libraries (gems) ที่ควบคุมไม่ได้ Lack of trust → reinvent gem ที่มีอยู่แล้ว

NIH บางครั้งดี: in-house solution great หาก improve over existing solutions ถ้า open-source solution → even better (ดู Rails หรือ React) แต่ถ้า reinvent wheel เพราะอยาก reinvent → อย่าทำ Wheel itself pretty great already

Topic นี้ tricky ถ้าเจอสถานการณ์ถามตัวเอง:

  • มั่นใจว่าเราทำ solution ดีกว่าที่มีอยู่?
  • Existing open-source solution ต่างจากที่เราต้องการ → contribute open-source improve ได้ไหม?
  • กลายเป็น maintainers ของ open-source solution improve developers หลายคนได้ไหม?

แต่บางครั้งต้อง go your own way สร้าง library เอง Org ไม่ชอบ licensing open-source library forced to build your own แต่ whatever you do avoid reinventing the wheel

Lifeguard on Duty (Over-rescuing Exceptions)

People tend to rescue exceptions มากกว่าที่ aimed for

Topic นี้ code-related มากกว่า previous ones Common sense บ้าง แต่เห็นในโค้ดบ้าง เช่น:

begin song.upload_lyrics rescue puts 'Lyrics upload failed' end

ไม่ specify exception ที่จะ rescue → catch exceptions ที่ไม่ได้ plan

กรณีนี้ problem อาจเป็น song object = nil ตอน exception reported to error tracker คุณคิดว่า upload process ผิด แต่จริงๆ อาจเป็น issue ต่างหาก

Safe side: rescue exceptions ได้ list ของ exceptions ที่อาจเกิด ถ้าไม่ได้ทุก exception under-rescue ดีกว่า over-rescue Rescue exceptions ที่รู้จัก handle อื่นๆ later stage

You Ask Too Much (Too Many SQL Queries)

Section นี้: web development, relational-database problem

Bomb webserver ด้วย too many SQL queries ใน 1 request ยังไง? Fetch multiple records จาก multiple tables ใน 1 request แต่มักเกิดจาก infamous N+1 query problem

Models:

class Song < ApplicationRecord belongs_to :artist end class Artist < ApplicationRecord has_many :songs end

อยาก show songs ใน genre และ artists:

songs = Song.where(genre: genre).limit(10) songs.each do |song| puts "#{song.title} by #{song.artist.name}" end

Code นี้ trigger 1 SQL query ได้ 10 songs แล้ว 1 extra SQL query ต่อ song fetch artist = 11 queries total

Imagine load more songs → heavier load on database

ทางเลือก: ใช้ includes จาก Rails:

songs = Song.includes(:artists).where(genre: genre).limit(10) songs.each do |song| puts "#{song.title} by #{song.artist.name}" end

หลัง includes ได้ 2 SQL queries เท่านั้น ไม่ว่าจะกี่ songs neat!

Diagnose too many SQL queries ใน development: เห็น group ของ similar SQL queries fetch จาก table เดียว → something fishy เปิด SQL logging ใน development environment รวมถึง Rails verbose query logs show query เรียกจากไหนในโค้ด

ถ้า logs ไม่ใช่เรื่องคุณ หรืออยาก serious ใช้ AppSignal performance measuring และ N+1 query detection ได้ excellent indicator

Sum Up

Thanks for reading blog post series Glad you joined interesting ride: introduce patterns/anti-patterns → explore ใน Rails MVC → final post general problems

หวังว่าเรียนรู้เยอะ หรืออย่างน้อย revise/establish what you know ไม่ต้อง stress memorize ทุกอย่าง Consult series ตอน trouble

คุณจะเจอ patterns และ anti-patterns เพราะโลก (และ software engineering) ไม่ ideal อย่ากลัว

Mastering patterns/anti-patterns ทำให้คุณ great software engineer แต่ทำให้ better: knowing when to break patterns/molds เพราะไม่มี perfect solution

Thanks again for joining and reading See you next time — cheers!

P.S. อยากอ่าน Ruby Magic posts ทันทีเผยแพร่? สมัครรับ Ruby Magic newsletter!