Guard clause: thoát sớm để tránh if lồng nhau rối rắm

  • author-image

    Nguyễn Đình Cường

  • blog-tag
  • blog-comment 0 comment
  • created-date 09 Nov, 2026
blog-thumbnail

Bạn đã từng đọc một hàm mà dòng code trôi dần sang phải vì các tầng if lồng vào nhau? Đó là "mũi tên code" khó đọc và khó bảo trì. Guard clause (điều kiện bảo vệ, hay còn gọi là early return) là kỹ thuật đơn giản giúp đưa code về đường thẳng dễ đọc.

Vấn đề của if lồng nhau

Hãy xem hàm đăng ký người dùng dưới đây:

function registerUser(user) {if (user) {if (user.email) {if (isValidEmail(user.email)) {if (!emailExists(user.email)) {saveUser(user);return { ok: true };} else {return { ok: false, error: 'Email đã tồn tại' };}} else {return { ok: false, error: 'Email không hợp lệ' };}} else {return { ok: false, error: 'Thiếu email' };}} else {return { ok: false, error: 'Thiếu dữ liệu' };}}

Để biết điều gì xảy ra khi email không hợp lệ, bạn phải lần theo bốn tầng thụt lề và ghép từng else với if của nó. Phần xử lý chính (saveUser) bị chôn sâu bên trong.

Giải pháp: guard clause

Ý tưởng là kiểm tra các điều kiện không hợp lệ ở đầu hàm và thoát ngay (return, throw, continue). Phần còn lại của hàm chỉ chạy khi mọi điều kiện đã thỏa:

function registerUser(user) {if (!user) return { ok: false, error: 'Thiếu dữ liệu' };if (!user.email) return { ok: false, error: 'Thiếu email' };if (!isValidEmail(user.email)) return { ok: false, error: 'Email không hợp lệ' };if (emailExists(user.email)) return { ok: false, error: 'Email đã tồn tại' };saveUser(user);return { ok: true };}

Cùng một logic, nhưng phẳng, mỗi dòng ở đầu hàm nói rõ một điều kiện loại trừ, và đường đi "bình thường" (happy path) nằm gọn ở cuối.

Lợi ích của guard clause

  • Giảm mức thụt lề: code ít bị đẩy sang phải, dễ đọc hơn trên màn hình hẹp.
  • Đọc theo thứ tự tự nhiên: điều kiện lỗi trước, xử lý chính sau.
  • Giảm tải bộ nhớ khi đọc: khi đã qua một guard, bạn có thể quên điều kiện đó, không phải nhớ cả chuỗi if đang mở.
  • Dễ thêm điều kiện: thêm một dòng guard mới thay vì xây thêm một tầng.
  • Dễ test: mỗi nhánh lỗi tương ứng với một dòng rõ ràng.

Dùng guard clause trong vòng lặp

Trong vòng lặp, continue đóng vai trò tương tự:

// Trướcfor (const order of orders) {if (order.isPaid) {if (!order.isShipped) {ship(order);}}}// Saufor (const order of orders) {if (!order.isPaid) continue;if (order.isShipped) continue;ship(order);}

Kết hợp với việc đặt tên điều kiện

Khi điều kiện phức tạp, hãy tách thành hàm có tên:

function canShip(order) {return order.isPaid && !order.isShipped;}for (const order of orders) {if (!canShip(order)) continue;ship(order);}

Một số ngôn ngữ, cùng ý tưởng

Trong Python, bạn làm tương tự:

def withdraw(account, amount):if amount <= 0:raise ValueError("Số tiền phải lớn hơn 0")if amount > account.balance:raise ValueError("Số dư không đủ")account.balance -= amountreturn account.balance

Trong PHP, Java, C#, Go ý tưởng cũng giống hệt: kiểm tra điều kiện sai trước, thoát sớm, phần chính để cuối.

Lưu ý và ngoại lệ

  1. Hàm phải đủ ngắn: nếu có quá nhiều guard, hãy xem hàm đang làm quá nhiều việc hay không.
  2. Nhiều điểm return: vài tài liệu cũ khuyên mỗi hàm chỉ một return. Trong thực tế, guard clause với nhiều return rõ ràng thường dễ đọc hơn. Dừng lại nếu hàm cần dọn dẹp tài nguyên chung, khi đó dùng finally, defer hoặc try-with-resources.
  3. Đừng lạm dụng: với hai nhánh cân bằng tương đương (if/else ngang hàng) thì giữ nguyên if/else, không nhất thiết phải biến thành guard.
  4. Thông báo lỗi rõ: mỗi guard nên cho biết vì sao loại bỏ.

Quy trình refactor sang guard clause

  1. Tìm nhánh else chứa trường hợp lỗi hoặc ngắn gọn.
  2. Đảo điều kiện và đưa nhánh đó lên đầu với return/throw.
  3. Gỡ bỏ tầng thụt lề của phần còn lại.
  4. Chạy test sau mỗi lần đổi, lặp lại cho các tầng tiếp theo.

Kết luận

Guard clause là một trong những thủ thuật nhỏ nhưng hiệu quả nhất để làm code phẳng và dễ đọc. Lần tới khi thấy mình viết tầng if thứ ba, hãy dừng lại và thử đảo điều kiện, thoát sớm.

Muốn rèn tay viết code sạch qua bài tập, hãy xem thêm các khóa học trên codebem.com.

author_photo
Nguyễn Đình Cường

0 comment