Best practices at ThoughBot
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ư
rspec
vàrake
, 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
trongActiveModel
, 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ư
rake
vàrspec
, 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
objectuser
, chứ k phải qua columnuser_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 chocookies
để ngăn chặn giả mạo. - Nên sử dụng
Time.current
thay choTime.now
,Date.current
thay choDate.today
,Time.zone.parse('date_string')
thay choTime.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ĩabelongs_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
(orlet!
) 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 RSpecit
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ư gempg
,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.*
- Sử dụng SendGrid hoặc Amazon SES để gửi mail trên staging và production.
- Sử dụng tool như ActionMailer Preview để xem mail khi tạo/update mailer view.
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ặchref
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àid
vàclass
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
- Review những recommend practices ở HTTP API Design Guide trước khi thiết kế 1 API mới.
- Viết intergration tests cho API endpoints. Cần cung cấp đầy đủ feature specs hoặc request specs.
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 ;)