Cạm bẫy "true, false, true": Tại sao tham số boolean làm code khó đọc

Phần mềm11 tháng 5, 2026·4 phút đọc

Việc sử dụng liên tục các tham số boolean như `true`, `false` trong các lời gọi hàm không chỉ gây khó chịu mà còn làm giảm khả năng đọc code. Bài viết này phân tích vấn đề "flag arguments" và đề xuất các giải pháp như sử dụng object tham số hoặc tách biệt các hàm logic để tối ưu hóa khả năng bảo trì.

Cạm bẫy "true, false, true": Tại sao tham số boolean làm code khó đọc

Thỉnh thoảng, khi mở một Pull Request (PR), tôi bắt gặp những dòng code kiểu như:

deployFeature(flag, true, false, true);

Tôi gặp tình huống này thường xuyên hơn mức mong đợi. Không phải vì nó phức tạp, mà bởi vì tôi hoàn toàn không biết mình đang nhìn thấy gì. Tôi phải nhấp vào định nghĩa hàm, cuộn xuống một chút, bị lạc chỗ, quay lại, đọc lại dòng code... và chỉ khi đó tôi mới hiểu được.

Đó là một sự gián đoạn nhỏ, nhưng thật phiền phức mỗi khi xảy ra. Lúc này, tôi không còn đang "đọc" code nữa, tôi đang "giải mã" nó.

Đang giải mã thay vì đọc code

Hãy xem một ví dụ đơn giản hơn:

createUser(user, true, false);

Điều này có nghĩa là gì? Người dùng đó có phải là admin không? Chúng ta có đang gửi email chào mừng hay bỏ qua bước xác thực? Tôi không biết. Vào thời điểm đó, tôi không đọc code, tôi đang giải mã nó.

Có một tên gọi cho hiện tượng này ("flag arguments", đôi khi là "boolean blindness"), nhưng thực sự tôi không cần thuật ngữ chuyên môn để cảm nhận được vấn đề.

Sự xuất hiện của dòng chú thích

Đây là lúc mà những dòng chú thích (comment) lén lút xuất hiện. Tôi chắc chắn đã từng viết code như thế này:

createUser(user, true, false); // isAdmin, sendWelcomeEmail

Điều này thực sự đã bộc lộ vấn đề. Nếu lời gọi hàm cần một chú thích để giải thích các tham số, thì có lẽ API đang làm việc ngược lại với chúng ta.

Tại sao nó có vẻ ổn khi viết code?

Bởi vì khi tôi viết hàm này, nó có vẻ hoàn toàn hợp lý:

function createUser(user, isAdmin, sendWelcomeEmail) {
  // ...
}

Không cần thêm object, không cần cấu trúc phức tạp, chỉ cần truyền giá trị vào và tiếp tục. Tôi đã làm điều này vô số lần mà không cần suy nghĩ nhiều. Chỉ đến sau này, khi tôi đọc lại nơi gọi hàm, nó mới bắt đầu có vẻ sai sai.

Sự tiện lợi lúc viết code thường phải trả giá sau này bởi bất kỳ ai phải đọc nó, kể cả chính tôi hai tuần sau.

Giải pháp tôi sử dụng hiện nay

Hầu hết thời gian, tôi chỉ sử dụng một object thay thế:

createUser(user, {
  isAdmin: true,
  sendWelcomeEmail: false,
});

Bây giờ tôi có thể thực sự biết điều gì đang xảy ra mà không cần nhảy lại định nghĩa hàm. Và nó mở rộng khá tự nhiên:

createUser(user, {
  isAdmin: true,
  sendWelcomeEmail: false,
  skipValidation: true,
});

Hãy thử kéo dài các boolean theo vị trí (positional booleans) xa như vậy mà không khiến mọi thứ trở nên lộn xộn xem sao.

Đôi khi boolean đang che giấu một hành động khác

createUser(user, true);

Nếu true thực sự có nghĩa là "tạo người dùng admin", thì có lẽ đó không còn là một cờ (flag) nữa. Đó là một hành động khác. Vì vậy, tôi thường sẽ làm cho nó rõ ràng hơn:

createAdminUser(user);
createRegularUser(user);

Bây giờ không còn nhiều chỗ để diễn giải nữa.

Công bằng mà nói, điều này không phải lúc nào cũng xấu

Đôi khi điều này hoàn toàn ổn:

toggleMenu(true);

Điều này đủ rõ ràng. Nó thường hoạt động tốt khi:

  • Ý nghĩa là hiển nhiên
  • Hàm nhỏ và cục bộ
  • Chỉ có một cờ

Nhưng một khi tôi thêm boolean thứ hai, khả năng đọc thường giảm rất nhanh.

TypeScript không thực sự giải cứu được tình thế

TypeScript cho tôi biết các giá trị là boolean. Đó không thực sự là vấn đề.

createUser(user, true, false);

Các kiểu dữ liệu (types) về mặt kỹ thuật là chính xác. Tôi vẫn phải nhớ các tham số có ý nghĩa gì. Điều giúp ích tôi nhiều hơn là chuyển sang các object tùy chọn (options objects):

createUser(user, {
  isAdmin: true,
  sendWelcomeEmail: false,
});

Hoặc đôi khi chỉ cần thay thế hoàn toàn boolean:

createAdminUser(user);

Thông thường đó là dấu hiệu cho thấy cờ đó đang che giấu hai hành động khác nhau anyway.

Cùng hành vi, dễ đọc hơn nhiều

Trước:

fetchData(url, false, true, 3);

Sau:

fetchData(url, {
  useCache: false,
  retryOnFail: true,
  retries: 3,
});

Và tôi đã từng thấy những lời gọi thực tế như thế này trong code production:

updateSettings(user, true, false, true, false);

Lúc đó, tôi lại phải đếm các tham số bằng ngón tay. Cùng một hành vi. Gánh nặng tinh thần ít hơn nhiều.

Tại sao điều này tiếp tục tốn thời gian của tôi?

Hầu hết thời gian, tôi không viết code. Tôi đang cố gắng hiểu nó. Và vâng, đôi khi code đó là của tôi từ vài tuần trước.

Mỗi khi tôi gặp phải thứ gì đó như:

updateSettings(user, true, false, true, false);

Tôi lại làm cùng một việc: dừng lại một giây và cố gắng nhớ từng tham số có ý nghĩa gì.

Đó là một chướng ngại vật nhỏ. Chỉ là một chướng ngại vật mà tôi dường như liên tục phải đụng phải.

Chia sẻ:FacebookX
Nội dung tổng hợp bằng AI, mang tính tham khảo. Xem bài gốc ↗