Skip to Content

Best practices at ThoughBot

Posted on

Mở đầu

Nếu bạn là 1 Ruby on Rails dev, chắc hẳn bạn đã từng dùng qua gem FactoryBot để tạo dữ liệu seed hoặc sinh dữ liệu khi test. Gem này ban đầu được các thành viên của thoughbot viết ra. ThoughBot là một công ty rất nổi tiếng về làm ứng dụng (app/web/), có trụ sở ở Mỹ và Anh. Mình biết đến công ty này vì một số founder của công ty thường xuyên xuất hiện trong các buổi Ruby Conferences hay RailsConf.

Trong một lần lang thang, mình có tìm được PlayBook của ThoughBot. (like Barney Stinson’s PlayBook 😁). Trong này có nói về quy trình làm việc, lên ý tưởng, design, thăm dò ý kiến, coding, setup môi trường, …. tất cả các bước, việc phải làm để tạo ra 1 sản phẩm hoàn chỉnh. Trong cuốn PlayBook này, họ có nói về 1 số tip, tricks khi lập trình. Bài viết này được dịch lại từ post Best Practices chia sẻ 1 số kinh nghiệm của các kỹ sư làm việc tại ThoughBot, với 1 số quan điểm, mình sẽ nói thêm 1 vài điều về ý kiến cá nhân =]]

Best Practices

General

  • Không nên tin hoàn toàn vào 1 điều gì đó. Hãy cố gắng để hiểu chúng và luôn đặt câu hỏi khi bạn nghi ngờ 👏 👏👏 - Điều này mình rất hay nhắc nhở các members trong team. Khi gặp 1 vấn đề, ngoài cách tìm hiểu nguyên nhân, giải pháp, thì việc hiểu được tại sao lại có giải pháp đó cũng rất quan trọng. Chỗ nào chưa hiểu thì cần đặt câu hỏi, mỗi người học hỏi nhau 1 chút thì sẽ nhanh tiến bộ/ nhớ lâu hơn rất nhiều. Đó là học 1 cách chủ động.
  • Không nên cố tình lờ/ỉm đi những exceptions - Đây là 1 vấn đề rất hay gặp. Khi member code, họ là người trực tiếp làm việc với task đó, nên đôi khi họ sẽ phát hiện ra các exceptions, case dị mà người review sẽ không tìm được. Nhưng do không google được giải pháp (hoặc sợ mất thời gian) nên những exceptions này thường được ỉm đi :v Nếu người review hoặc QA tìm ra được, thì họ sẽ fix, còn không thì … (yaoming). Chúng ta không nên như thế, khi gặp case dị, cần hỏi những người có kinh nghiệm để tìm giải pháp, như thế thì trình độ mình mới tăng lên được. Còn nếu không, đến lúc tìm ra bug, mình vẫn phải là người fix, nhưng fix lúc đó sẽ tốn efforts hơn rất nhiều. Đó là lý do mình luôn phải hỏi member khi gửi PullRequest là “Pull này e còn lăn tăn về vấn đề gì không?” 😁
  • Không viết những đoạn code xử lý dạng đoán function, tương lai sẽ dùng đến: Ban đầu hãy xây dựng code thật đơn giản. Nếu sau này có spec rõ ràng hơn, refactor cũng chưa muộn, không nên đoán nội dung function.
  • Không duplicate các hàm của 1 build-in library: Khi bạn có ý định viết 1 hàm custom nào đó, hãy thử tìm hiểu xem trong thư viện/framework đã có chưa, nếu đã có rồi thì dùng lại, k nên viết lại (vì hàm trong thư viện đã được tối ưu performance, test kĩ càng hơn nhiều).
  • Keep the code simple: Tất nhiên, simple is the best :D

Object-Oriented Design

  • Không nên dùng biến toàn cục - Global Variables.
  • Tránh sử dụng quá nhiều parametter khi truyền vào hàm.
  • Giới hạn dependences của 1 object.
  • Giới hạn các dependences mà object này phụ thuộc vào.
  • Ưu tiên dùng composition hơn là kế thừa.
  • Ưu tiên các methods nhỏ. (mỗi method chỉ nên từ 1 đến 5 dòng :v như thế việc đặt tên hàm sẽ dễ hơn, đọc logic cũng sẽ dễ hiểu hơn).
  • Nên tạo ra các class với Single Responsibility (trong SOLID). Khi một class dài quá 100 dòng, có thể nó đang làm quá nhiều việc, nên chia nhỏ hơn để dễ test, dễ tái sử dụng.

Ruby

  • Tránh sử dụng parameters optional. Khi bạn cần sử dụng param này thì có thể method đang làm quá nhiều thứ.
  • Tránh sử dụng monkey-patching. Điều này cũng không hoàn toàn đúng. Bạn có thể tham khảo bài viết Monkey pathching without a mess của mình :D
  • Generate ra các Binstubs cho từng project, như rspecrake, sau đó add vào version control.
  • Nếu có các functions được sử dụng bởi nhiều models, nên tách nó từ class ra ngoài các models, sau đó tái sử dụng.

Ruby Gems

  • Định nghĩa tất cả các dependencies của 1 gem trong .gemspec file.
  • Reference file gemspec này vào trong Gemfile.
  • Sử dụng Appraisal để test các gems mà có nhiều versions, hoặc support nhiều versions của Rails chẳng hạn.
  • Sử dụng Bundler để quản lý các dependencies.
  • Sử dụng CI để show các build status với code review và test lại với nhiều phiên bản Ruby.

Rails

  • Add foreign key constaints vào file migrations.
  • Tránh việc bypassing validatios. Ví dụ như: save(validate: false). Chỉ dùng trong trường hợp thực sự cần thiết.
  • Tránh việc khởi tạo nhiều hơn 2 objects trong controllers.
  • Không thay đổi file migrations sau khi nó đã được merge vào master.
  • Không nên gọi trực tiếp model class ở trong view.
  • Không nên return false trong ActiveModel, thay vào đó, hãy raise exceptions.
  • Không nên sử dụng instance variables trong partials view. Truyền các biến này vào partials từ đoạn render ra template.
  • Không sử dụng SQL hoặc SQL fragments (ví dụ: where('inviter_id IS NOT NULL')) ở bên ngoài models.
  • Tạo Spring binstubs cho từng project, như rakerspec, sau đó add vào version control.
  • Nếu như bạn set default value cho 1 trường nào đó, hãy set ở trong file migrations.
  • Luôn giữ file db/schema.rb trong version control.
  • Chỉ nên sử dụng 1, 2 instance variables trong mỗi view.
  • Sử dụng SQL, không nên dùng ActiveRecord trong file migration.
  • Sử dụng .ruby_version file để chỉ định ruby version cho mỗi project.
  • Sử dụng hậu tố _url cho tên routes ở trong mailer và redirect. Sử dụng hậu tố _path trong các trường hợp còn lại.
  • Validate quan hệ belongs_to object user, chứ k phải qua column user_id.
  • Sử dụng dữ liệu trong file db/seed.rb cho những dữ liệu mà require ở tất cả các môi trường.
  • Sử dụng dev:prime task để seed dữ liệu cho môi trường development.
  • Nên sử dụng cookies.signed thay cho cookies để ngăn chặn giả mạo.
  • Nên sử dụng Time.current thay cho Time.now, Date.current thay cho Date.today, Time.zone.parse('date_string') thay cho Time.parse('date string')
  • Sử dụng ENV.fetch cho các biến môi trường, thay vì dùng ENV[] để các biến môi trường được detect khi deploy.
  • Sử dụng blocks để định nghĩa date/time attributes trong FactoryBot factories.
  • Sử dụng touch: true khi định nghĩa belongs_to relationships.

Testing

  • Tránh sử dụng any_instance trong rspec-mock và mocha. Ưu tiên dùng dependency injection
  • Tránh sử dụng its, specify, và before trong RSpec.
  • Tránh let (or let!) trong RSpec. Ưu tiên tách thành helper methods, nhưng không cần phải implement lại method đó. Xem ví dụ
  • Tránh sử dụng subject 1 cách tường minh bên trong RSpec it block. Example
  • Tránh sử dụng instance variables trogn tests.
  • Tắt real HTTP request để gọi services bên ngoài. (Khi test không cần gọi API thật ra ngoài). Sử dụng WebMock.disable_net_connect!.
  • Không cần test private methods.
  • Test background jobs sử dụng Delayed::Job matcher
  • Sử dụng stubs và spies (not mocks) trong isolated tests.
  • Sử dụng single level of abstraction với scenarios.
  • Sử dụng it example hoặc test method cho mỗi 1 case của hàm.
  • Sử dụng assertions about state cho các incomming messages.
  • Sử dụng stubs và spies để so sánh khi gọi outgoing messages.
  • Sử dụng Fake để stub requests khi gọi tới services bên ngoài.
  • Sử dụng integration tests để chạy cả app.
  • Sử dụng non-SUT methods trong việc expectations nếu có thể.

Bundler

  • Định nghĩa rõ Ruby version được sử dụng trong project ở trong Gemfile.
  • Sử dụng pesimistic version trong Gemfile cho những gem follow theo semantic versioning (như rspec, factory-bot, capybara, …)
  • Sử dụng versionless Gemfile để định nghĩa những gem mà an toàn để update thường xuyên, như gem pg, thin, debugger.
  • Sử dụng exact version trong Gemfile cho những gem quan trọng, như rails.

Relation Databases

  • Index foreign keys
  • Constrain most columns as NOT NULL
  • Trong một SQL view, chỉ select những columns bạn cần. Tránh việc SELECT table.*

Email

Web

  • Tránh việc delays rendering bởi các tiến trình load đồng bộ.
  • Sử dụng HTTPS thay cho HTTP khi linking assets.

JavaScript

  • Sử dụng syntax JS mới nhất với 1 trình biên dịch, như babel.
  • Sử dụng to_param hoặc href attribute khi serializing ActiveRecord models. Và sử dụng khi xây dựng URLs ở client side, thay vì dùng ID.
  • Ưu tiên data-* attributes hơn là idclass attributes khi target HTML elements.
  • Tránh việc target HTML elements sử dụng classes để dự định cho mục đích sau sử dụng style.

HTML

  • Sử dụng <button> tags thay cho <a> tags cho các actions.

Sass

  • Khi sử dụng sass-rails, sử dụng asset-helpers (image_url, front-url, …) để cho Rails Asset Pipeline sẽ re-write the connect paths tới acces.
  • Ưu tiên mixins hơn @extend.

Browser

  • Tránh support version trước IE11.

Ruby JSON APIs

Kết luận

Bài viết trên tổng hợp lại 1 số best practices của các kĩ sư ở ThoughBot trong quá trình code Rails.

Hy vọng bạn có thể áp dụng chúng trong quá trình làm dự án ;)

comments powered by Disqus