12/08/2018, 16:08

10 lỗi mà Rails developer hay mắc phải

Ruby on Rails ("Rails") là một framwork mã nguồn mở phổ biến, dựa trên ngôn ngữ lập trình Ruby nhằm đơn giản hóa và hợp lý hoá quá trình phát triển ứng dụng web. Rails được xây dựng trên nguyên tắc quy ước về cấu hình. Theo mặc định Rails giả định rằng các nhà phát triển sẽ tuân theo các quy ước ...

Ruby on Rails ("Rails") là một framwork mã nguồn mở phổ biến, dựa trên ngôn ngữ lập trình Ruby nhằm đơn giản hóa và hợp lý hoá quá trình phát triển ứng dụng web.

Rails được xây dựng trên nguyên tắc quy ước về cấu hình. Theo mặc định Rails giả định rằng các nhà phát triển sẽ tuân theo các quy ước về thực tiễn tốt nhất theo "chuẩn" (đối với những thứ như đặt tên, cấu trúc mã, v.v) và nếu bạn làm, mọi thứ sẽ làm việc cho bạn "tự động -magically "mà không cần phải xác định những chi tiết này. Mặc dù mô hình này có lợi thế của nó, nó cũng không phải là không có những sai lầm của nó. Đáng chú ý nhất là "magic - ma thuật" xảy ra đằng sau những mã code này đôi khi có thể dẫn đến sự thất bại, lẫn lộn. Nó cũng có thể có những hậu quả không mong muốn liên quan đến bảo mật và hiệu suất.

Theo đó, trong khi Rails rất dễ sử dụng, cũng không khó để lạm dụng. Bài viết này nói về 10 vấn đề chung của Rails, bao gồm cách tránh chúng và các vấn đề mà chúng gây ra.

Mistake #1: Cho quá nhiều logic vào controller

Rails dựa trên kiến trúc MVC. Trong cộng đồng Rails, chúng ta đã nói về fat model, skinny controller trong một thời gian, tuy nhiên một số ứng dụng Rails gần đây mà tôi biết đã vi phạm nguyên tắc này. Dễ dàng cho logic ở view hoặc model vào trong controller. Vấn đề sẽ gây ra khó khăn và dễ bị lỗi khi có những thay đổi trong tương lai. Nhìn chung, ở controller các loại logic nên có là:

  • Xử lý Session and cookie
  • Lựa chọn model: Logic để tìm kiếm model phù hợp với các request.
  • Quản lí các request params: Tùy chỉnh các params
  • Rendering/redirecting : xử lí kết quả trả về (html, xml, json....)

Mistake #2: Cho quá nhiều logic vào view

Sử dụng template Rails erb là 1 cách tuyệt vời để xây dựng các trang có nội dung biến đổi. Tuy nhiên nếu bạn không cẩn thận, bạn sẽ khó quản lí chúng nếu nó là 1 file lớn kết hợp giữa mã HML và Ruby. Đây cũng là nơi có thể dẫ đến lặp đi lặp lại code, dẫ đến vi phạm nguyên tắc DRY (don’t repeat yourself) Ta có thể xem xét 1 đoạn ví dụ đơn giản

<h3>
  Welcome,
  <% if current_user %>
    <%= current_user.name %>
  <% else %>
    Guest
  <% end %>
</h3>

Một cách tốt hơn để xử lý đảm bảo rằng đối tượng current_user luôn luôn được trả về, cho dù có ai đó đăng nhập hay không,. Ví dụ, bạn có thể định nghĩa current_user helper trong app / controllers / application_controller như sau:

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

Chúng ta có thể thay thế đoạn code trên bằng:

<h3>Welcome, <%= current_user.name  %></h3>

Gợi ý cho sử dụng ở view:

  • Sử dụng view layout and partials khi có nhiều đoạn view trùng nhau
  • Sử dụng presenters/decorators giống như Gem Draper dùng để build các logic ở ngoài view. Hạn chế các logic viết trực tiếp trong view và giúp chúng ta có thể dùng chúng ở nhiều view khác nhau

Mistake #3: Cho quá nhiều logic vào model

Trong 1 kiến trúc MVC là Chúng ta giảm thiểu logic ở controller và view nơi duy nhất còn lại có thể đặt logic là model? Như vậy là không đúng Nhiều nhà phát triển Rails thực sự mắc phải sai lầm này. Điều này dẫn đến việc khó bảo trì và phát triển. Ví dụ như chức năng gửi mail. nó không thuộc một model nào cả. Vì vậy không thể nhét nó vào 1 model nào đó. Vậy nó sẽ đặt ở đâu? Chúng ta sẽ tạo 1 class khác, di chuyển chúng ra khỏi model và đóng gói theo chức năng. Ví dụ như 1 nơi chuyên quản lí mail, 1 nơi chuyên quản lí tương tác API Những logic chỉ nên có ở Model là:

  • Active record config (ví dụ như validations, relations)
  • Simple mutation methods: Các method dùng để cập nhập thuộc tính lưu chúng vào cơ sở dữ liệu
  • Access wrappers: Ẩn thông tin bằng cách tạo ra 1 cách lấy thông tin khác như: tạo 1 phương thức full_name kết hợp last_name và first_name
  • Sophisticated queries: Dùng để tìm kiếm hay query lấy dữ liệu.

Mistake #4: Sử dụng helper class chưa hợp lí

Sai lầm này là hệ quả của 3 sai lầm ở trên. Như đã thảo luận bên trên, Rails đặt trọng tâm vào các thành phần: Model, View, Controller,. Ta đã định nghĩa những thứ phù hợp với từng thành phần. Nhưng đôi khi chúng ta cần các method nó không phù hợp với bất kì thành phần nào trong MVC Những người tạo ra Rails đã xây dựng 1 thư mục helper trợ giúp. Tuy nhiên nó trở nên quá hẫm dẫn, chúng ta bắt đầu nhồi nhét bất kì 1 function chức năng nào đó không phù hợp với 1 trong 3 thành phần vào các class helper này. Điều này sẽ dẫn đến helper của chúng ta phìng to và khó quản lí. Khi bạn có chức năng bổ sung, hãy suy nghĩ về các method có thể nhóm lại với nhau và tìm cho chứng 1 tên thích hợp.

Mistake #5: Sử dụng quá nhiều gem

Rails cung cấ nhiều gem hữu ích giúp cho quá trình phát triển trở nên dễ dàng hơn. Điều này rất tốt với việc xây dựng 1 ứng dụng phức tạp một cách nhanh chóng, nhưng có nhiều ứng dụng sử dụng nhiều gem không cân xứng khi so sánh với các chức năng được cung cấp Điều này gây ra một số vấn đề. Sử dụng quá nhiều gem làm cho size process sẽ lớn hơn mức cần thiết. Gây giảm hiệu suất production. Ngoài ảnh hưởng đến người dùng, nó còn dẫn đến sự cần thiết phải cần 1 máy chủ lớn hơn và tăng chi phí hoạt động. Nó cũng mất nhiều thời gian hơn để bắt đầu các ứng dụng Rails lớn hơn, làm cho quá trình phát triển chậm hơn và làm cho các test tự động mất nhiều thời gian hơn . Khi thêm bất kì 1 gem nào đó, chúng ta hãy xem xét các chức năng mà chúng đáp ứng.

Mistake #6: Bỏ qua các file log

Mặc dù hầu hết các nhà phát triển Rails đều biết các file log mặc định có sẵn trong quá trình development và production nhưng chúng ta thường không quan tâm đến file đó chỉ khi có vấn đề xảy ra. 1 ví dụ nếu chúng ta có vấn đề về N + 1 query, Chúng ta chỉ có thể nhận biết qua việc xem log. Ví dụ:

def comments_for_top_three_posts
  posts = Post.limit(3)
  posts.flat_map do |post|
    post.comments.to_a
  end
end

Khi xem log chúng ta có thể thấy mỗi 1 query tạo ra 3 đối tượng và truy vấn thêm 3 query khác cho mỗi đối tượng.

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)

Chúng ta sẽ sử dụng eager loading để giải quyết vấn đề đó, giảm thiểu số query truy vấn.

def comments_for_top_three_posts
  posts = Post.includes(:comments).limit(3)
  posts.flat_map do |post|
    post.comments.to_a
  end
end

Chúng sẽ tạo ra 1 query thay vì 3:

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)

Xem file log là 1 cách tuyệt vời để phát hiện ra những đoạn mã không hiệu quả và sửa chúng trước khi ứng dụng được deploy lên production. Nếu không, bạn không thể biết được các vấn đề về hiệu suất của Rauks cho tới khi hệthống của bạn hoạt động thật, vì bộ dữ liệu bạn làm việc với phát triển và thử nghiệm có thể nhỏ hơn nhiều so với sản xuất. Nếu bạn đang làm việc với một ứng dụng mới, ngay cả bộ dữ liệu sản xuất của bạn có thể bắt đầu nhỏ và ứng dụng của bạn sẽ trông như nó đang chạy tốt. Tuy nhiên, khi tập dữ liệu sản xuất của bạn phát triển, các vấn đề về Rails như thế này sẽ làm cho ứng dụng của bạn chạy chậm hơn và chậm hơn.

Mistake #7: Viết code thiếu test

Ruby và Rails cung cấp khả năng kiểm tra tự động mạnh mẽ theo mặc định. Nhiều nhà phát triển đã viết test 1 cách tinh vi sử dụng theo TDD and BDD styles và sử dụng các test framework mạnh hơn như rspec và cucumber Mặc dù dễ dàng để thêm test vào ứng dụng Rails nhưng có nhiều dự án đã không viết test hoặc có nhưng rất ít. Theo nguyên tắc chung, phải có ít nhất 1 test tích hợp cấp cao được viết cho mỗi action trong controller của bạn. Tại một số thời điểm trong tương lai, các nhà phát triển Rails khác sẽ muốn mở rộng hoặc sửa đổi mã, hoặc nâng cấp một phiên bản Ruby hoặc Rails, và khuôn khổ test này sẽ cung cấp cho họ một cách rõ ràng để xác minh rằng các chức năng cơ bản của ứng dụng là đang làm việc. Một lợi ích khác của cách tiếp cận này là nó cung cấp cho các nhà phát triển trong tương lai với một mô tả rõ ràng về việc thu thập đầy đủ các chức năng được cung cấp bởi ứng dụng.

Mistake #8: Chặn gọi service bên ngoài

Các nhà cung cấp dịch vụ Rails bên thứ ba thường làm cho việc tích hợp service của họ vào ứng dụng của bạn rất dễ dàng thông qua các gem. Nhưng điều gì xảy ra nếu service bên ngoài của bạn bị gián đoạn hoặc bắt đầu chạy rất chậm? Để tránh bị chặn, thay vì gọi các service này trực tiếp vào ứng dụng Rails của bạn trong quá trình xử lý , bạn nên chuyển chúng sang xếp hàng đợi ( background job). Một số gem phổ biến sử dụng trong các ứng dụng Rails cho mục đích này bao gồm:

  • Delayed job
  • Resque
  • Sidekiq

Mistake #9: Thao tác migration đã tồn tại

Cơ chế migration của Rails cho phép bạn có thể tạo mới, cập nhập, hoặc loại bỏ bảng hoặc row ra khỏi database. Các file migration được đặt tên theo thứ tự và bạn có thể chạy lại chúng để đưa ra 1 CSDL trống. Đây là 1 cách tuyệt vời để quản lí các thay đổi cơ sở dữ liệu và tránh được các sự cố của Rails Mặc dù điều này chắc chắn làm tốt khi bắt đầu dự án, nhưng theo thời gian quá trình tạo database có thể gây ra 1 số migration bị mất trận tự hoặc bị biến mất. Rails tạo ra một schema hiện tại của bạn trong một tệp có tên db / schema.rb (mặc định) thường được cập nhật khi di chuyển cơ sở dữ liệu. Tệp schema.rb thậm chí có thể được tạo ra khi không có migration nào bằng cách chạy lệnh rails db:schema:dump. Sai lầm Rails phổ biến là kiểm tra một lần migration mới vào repo nguồn của bạn nhưng không phải là tệp schema.rb được cập nhật tương ứng.

Khi migration đã mất và mất quá nhiều thời gian để chạy, hoặc không còn tạo cơ sở dữ liệu đúng cách nữa, các nhà phát triển không nên sợ xóa thư mục migration cũ, tạo một schema mới và tiếp tục từ đó. Thiết lập một môi trường phát triển mới sau đó sẽ yêu cầu một rake db:schema:load chứ không phải là rake db:migrate mà hầu hết các nhà phát triển dựa vào.

Mistake #10: Kiểm tra các thông tin bảo mật khi cho vào source code

Rails framework giúp bạn dễ dàng tạo ra các ứng dụng an toàn tránh được nhiều loại tấn công. Nó được thực hiện bằng các sử dụng 1 secret token để bảo vệ 1 phiên làm việc. Mặc dù token được lưu ở trong file config/secrets.yml và tệp đó đọc token từ biến môi trường trên production. Các phiên bản trước của Rails lưu token trong config/initializers/secret_token.rb . File này thường bị nhầm lẫn cho vào source code repo và khi điều này xảy ra thì tất cả mọi người access vào repo có thể biết được tất cả các thông tin. Bạn nên đảm bảo rằng các file config cần được loại trừ khi đẩy lên git hay bất cứ đâu. Ở production chúng ta có thể lấy token từ biến môi trường.

Hi vọng qua bài viết bạn có thể giúp ích cho bạn trong quá trình phát triển sản phẩm

Nguồn tham khảo:

https://www.toptal.com/ruby-on-rails/top-10-mistakes-that-rails-programmers-make

0