Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Ruby

Làm chủ Chất lượng Code Ruby với Linting Hiệu quả

Linting là quá trình phân tích tĩnh code để tìm các vấn đề tiềm ẩn.

Điều构成一个 "vấn đề" trong trường hợp này có thể khác nhau giữa các ngôn ngữ lập trình, thậm chí giữa các dự án trong cùng ngôn ngữ. Tôi sẽ đặt các vấn đề này vào vài danh mục khác nhau:

  • Programmatic (Lập trình)
  • Security (Bảo mật)
  • Stylistic (Phong cách)
  • Performance (Hiệu suất)

Hãy xem một vài ví dụ của từng loại.

Vấn đề Phong cách (Stylistic)

Không có cách styling code "đúng khách quan", vì mọi thứ đều về sở thích của người đọc. Chìa khóa là sự nhất quán (consistency). Các điểm tranh luận phổ biến bao gồm:

  1. dấu ngoặc kép vs dấu ngoặc đơn
  2. tab vs space
  3. độ dài dòng tối đa
  4. thụt lề cho gọi phương thức nhiều dòng, như ví dụ dưới:
# luôn single-line foo(:a, :b, :c) # căn lề với argument đầu tiên foo(:a, :b, :c ) # căn lề với tên function foo( :a, :b )

Đây hoàn toàn chủ quan, nhưng thường có lợi khi đồng thuận một tiêu chuẩn cho từng dự án cụ thể, để giữ codebase nhất quán.

Vấn đề Lập trình (Programmatic)

Tôi đưa vào đây các vấn đề như:

  • Thân phương thức quá dài, dẫn đến vấn đề đọc hiểu & bảo trì
  • Cyclomatic Complexity (Độ phức tạp Cyclomatic), metric thường dùng đo độ phức tạp code
  • Gán giá trị trong điều kiện. Nếu bạn gõ if x = true, bạn nhiều khi muốn if x == true. Và ngay cả khi bạn ý định gán, đó vẫn là cách tiếp cận kém trực quan

Vấn đề Bảo mật (Security)

Một số hàm hoặc thực hành có vấn đề bảo mật tiềm ẩn mà developer có thể không hay biết.

Ví dụ, trong Ruby, Kernel#open là hàm linh hoạt cho phép mở file hoặc URL bên ngoài. Nhưng nó cũng cho phép truy cập filesystem tùy ý, với các gọi kỳ lạ như open("| ls"). Do đó, cảnh báo developer về nó là hợp lý để họ có thể dùng cách an toàn hơn (File#open, IO.popen, URI.parse#open), hoặc quyết định rõ ràng giữ hành vi đó ở rủi ro của họ.

Vấn đề Hiệu suất (Performance)

Có nhiều chi tiết về cách Ruby hoạt động bên trong khiến một số tùy chọn hiệu quả hơn tùy ngữ cảnh.

Một linter cảnh báo về chúng giúp chúng ta học hỏi dọc đường đồng thời tối ưu hóa chi tiết chương trình.

Ví dụ, Ruby 2.5 giới thiệu String#delete_suffix xóa substring từ cuối string. Hai dòng sau tương đương, nhưng cái sau hiệu quả hơn vì không dựa trên generic string regex match:

str = 'string_with_suffix' # bad str.gsub(/suffix\z/, '') # good str.delete_suffix('suffix')

Tự động Sửa (Auto Fixing)

Một khía cạnh quan trọng của linter là khả năng tự động sửa một số hoặc tất cả vấn đề tìm thấy. Các khía cạnh styling như độ dài dòng dễ tự động hóa, nên hợp lý khi gỡ bỏ gánh nặng đó khỏi developer. Vấn đề khác có thể chủ quan hoặc cần can thiệp người, như refactor phương thức lớn. Trong những trường hợp này, không thể tự động hóa.

Quy ước hay Cấu hình (Convention or Configuration)

Thường có tranh luận gay gắt trong cộng đồng hoặc dự án về rule nào hợp lý.

Giải pháp truyền thống là cho phép mỗi team giải quyết tranh luận nội bộ bằng cách cho phép cấu hình rule linting theo sở thích. Tuy nhiên, những năm gần đây, có xu hướng đẩy mạnh chuẩn hóa thành một quy ước đơn trịnh trên nhiều ngôn ngữ. Mặc dù không được enforce mọi nơi, ý tưởng chung là loại bỏ hoàn toàn gánh nặng tâm trí developer khi styling code. Thay vì tranh luận độ dài dòng nào tốt nhất, mọi người chỉ dùng rule đồng thuận cộng đồng.

Trong Ruby, điều này hoặc nhiều hoặc ít dẫn đến hai linter hiện có: RuboCop, cho phép cấu hình đầy đủ, và StandardRB,采取 cách tiếp cận ngược lại và định nghĩa một tiêu chuẩn chung.

RuboCop

Sử dụng cách tiếp cận thường thấy: cung cấp tập rule có tài liệu, mỗi rule tìm một vấn đề cụ thể. Developer có thể disable hoặc tinh chỉnh rule trong dự án của mình:

# cấu hình max allowed line length Layout/LineLength: Max: 80 # disable cyclomatic complexity Metrics/CyclomaticComplexity: Enabled: false

Nó đã bao gồm default hợp lý, nên cấu hình chỉ cần cho rule cụ thể bạn muốn thay đổi.

Chạy bundle exec rubocop sẽ khiến RuboCop phân tích toàn bộ codebase và liệt kê tất cả vấn đề tìm thấy:

# test.rb def badName if something return "inner result" end "outer result" end
$ bundle exec rubocop Inspecting 1 file C Offenses: test.rb:1:1: C: [Correctable] Style/FrozenStringLiteralComment: Missing frozen string literal comment. def badName ^ test.rb:1:5: C: Naming/MethodName: Use snake_case for method names. def badName ^^^^^^^ test.rb:2:3: C: [Correctable] Style/IfUnlessModifier: Favor modifier if usage when having a single-line body. Another good alternative is the usage of control flow &&/||. if something ^^ test.rb:3:12: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols. return "inner result" ^^^^^^^^^^^^^^ test.rb:6:3: C: [Correctable] Style/StringLiterals: Prefer single-quoted strings when you don't need string interpolation or special symbols. "outer result" ^^^^^^^^^^^^^^ 1 file inspected, 5 offenses detected, 4 offenses auto-correctable

Sau đó bạn có thể chạy bundle exec rubocop --auto-correct, và phần lớn vấn đề sẽ được sửa theo cấu hình của bạn.

Thiết lập bundle exec rubocop như một phần của CI pipeline sẽ đảm bảo không code nào đi qua mà không trước tiên thỏa mãn rule linting.

StandardRB

Dự án mới hơn, thực tế dùng RuboCop bên dưới. Mục tiêu chính của StandardRB không phải xây dựng linter hoàn toàn riêng, mà đạt được tiêu chuẩn mà mọi người có thể dùng thay vì tranh luận.

Lightning talk nơi nó được công bố lần đầu khá rõ ràng về động lực: Nếu mọi người dành ít thời gian tranh luận chi tiết cú pháp và chỉ tuân theo quyết định đồng thuận toàn cộng đồng, chúng ta có thể dành nhiều thời gian hơn làm điều thực sự quan trọng: xây dựng sản phẩm và thư viện tuyệt vời.

Vì RuboCop dùng bên dưới, output bạn nhận được thực tế cùng format. Sự khác biệt duy nhất là bạn không được phép tùy chỉnh bất kỳ rule nào.

StandardRB gần đây đạt 1.0.0, nghĩa là hầu hết thảo luận về rule nào dùng đã xảy ra trên trang issue của họ. Nếu bạn quan tâm hoặc không đồng ý rule nào, khả năng cao bạn sẽ thấy thảo luận liên quan ở đó.

Cuối cùng, bạn có thể tin tưởng nó đã được thảo luận sâu. Không thể để toàn cộng đồng đồng ý 100% trên mọi điểm. Triết lý của cách tiếp cận này là con người linh hoạt và có thể không đồng ý nhưng cam kết với quyết định.

Tư duy Cuối Cùng

Sau khi dành nhiều thời gian hơn tôi tự hào để nitpick rule linting trong các dự án quá khứ, tôi chắc chắn thấy giá trị trong cách tiếp cận StandardRB và khuyên dùng nó bất cứ khi nào có thể.

Giữ lại tất cả lợi ích của sự nhất quán đồng thời loại bỏ overhead tranh luận, cùng với auto-fix cho hầu hết rule giúp chúng ta giao phần mềm tốt hơn, hiệu quả hơn, và tập trung vào điều thực sự quan trọng.

Các ngôn ngữ khác đang adopt formatter code low-configurability tương tự. mix formatter trong Elixir, và rustfmt trong Rust đều cho phép một chút cấu hình, nhưng cộng đồng bất ngờ đồng thuận giữ trong tiêu chuẩn.

Với đó nói xong, RuboCop vẫn là lựa chọn hoàn toàn hợp lệ nếu bạn, một cách ngẫu nhiên, không đồng ý với cảm xúc này.

P.S. Nếu bạn muốn đọc bài Ruby Magic ngay khi ra mắt, hãy subscribe newsletter Ruby Magic và không bao giờ bỏ lỡ bài nào!