Hàm nhỏ, làm một việc: nguyên tắc clean code dễ áp dụng

  • author-image

    Nguyễn Đình Cường

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

Bạn đã bao giờ mở một hàm dài 300 dòng và không biết bắt đầu đọc từ đâu? Hàm dài thường khó hiểu, khó test và là nơi bug thích ẩn nấp. Nguyên tắc "hàm nhỏ, làm một việc" là cách đơn giản nhất để tránh tình trạng đó.

Một hàm làm một việc nghĩa là gì?

Một hàm "làm một việc" khi bạn có thể mô tả nó bằng một câu ngắn mà không cần dùng chữ "và". Ví dụ "tính tổng tiền đơn hàng" là một việc. Còn "kiểm tra dữ liệu, lưu vào database, gửi email và ghi log" là bốn việc đang bị nhồi chung.

Đây là hệ quả của nguyên tắc Single Responsibility: mỗi đơn vị code chỉ nên có một lý do để thay đổi. Khi quy tắc gửi email thay đổi, bạn không muốn đụng vào đoạn code tính giá.

Ví dụ: tách một hàm "tất cả trong một"

Trước khi tách:

function processOrder(order) {if (!order.items || order.items.length === 0) {throw new Error('Đơn hàng trống');}let total = 0;for (const item of order.items) {total += item.price * item.quantity;}if (order.coupon === 'GIAM10') {total = total * 0.9;}saveToDatabase({ ...order, total });sendEmail(order.email, 'Cảm ơn bạn, tổng tiền: ' + total);}

Sau khi tách:

function validateOrder(order) {if (!order.items || order.items.length === 0) {throw new Error('Đơn hàng trống');}}function calculateSubtotal(items) {return items.reduce((sum, item) => sum + item.price * item.quantity, 0);}function applyCoupon(total, coupon) {return coupon === 'GIAM10' ? total * 0.9 : total;}function processOrder(order) {validateOrder(order);const total = applyCoupon(calculateSubtotal(order.items), order.coupon);saveToDatabase({ ...order, total });sendEmail(order.email, 'Cảm ơn bạn, tổng tiền: ' + total);}

Hàm processOrder giờ đọc gần như một đoạn văn: kiểm tra, tính tổng, lưu, gửi mail. Muốn biết chi tiết từng bước, bạn mới nhảy vào hàm con.

Lợi ích thực tế khi tách hàm

  • Dễ test: applyCoupon là hàm thuần, bạn chỉ cần vài dòng test là phủ hết các trường hợp.
  • Dễ tái sử dụng: calculateSubtotal có thể dùng ở trang giỏ hàng lẫn trang hóa đơn.
  • Dễ debug: lỗi tính tiền sai thì bạn biết đúng hàm cần xem.
  • Giảm cần comment: tên hàm chính là lời giải thích.

Những dấu hiệu cho thấy hàm cần được tách

  1. Hàm dài quá một màn hình, bạn phải cuộn để đọc hết.
  2. Có nhiều đoạn code được ngăn bằng dòng trống và comment kiểu "// bước 2: tính tiền".
  3. Hàm có quá nhiều tham số (khoảng 4 trở lên là nên cân nhắc).
  4. Có nhiều cấp thụt lề lồng nhau (if trong for trong if).
  5. Bạn khó đặt tên cho hàm mà không dùng "and" hoặc "và".
  6. Có tham số boolean làm hàm hành xử khác hẳn nhau, ví dụ save(data, true).

Giữ một mức trừu tượng trong mỗi hàm

Một mẹo hay: trong cùng một hàm, các dòng nên nằm ở cùng mức độ chi tiết. Đừng để một dòng là "gửi email xác nhận" (mức cao) nằm ngay cạnh dòng "nối chuỗi ký tự để tạo header" (mức thấp). Dòng chi tiết nên được đẩy xuống hàm con. Nhờ vậy khi đọc hàm cha, bạn nắm được luồng tổng thể, không bị chìm vào chi tiết.

Đừng tách quá đà

Tách hàm cũng có giới hạn. Nếu bạn tạo ra hàm chỉ gọi hàm khác mà không thêm ý nghĩa, hoặc phải nhảy qua 10 file để hiểu một luồng đơn giản, bạn đã đi quá xa. Một hàm 15-20 dòng có tên rõ ràng, làm trọn vẹn một việc, là vẫn rất tốt. Không có con số thần kỳ nào, hãy lấy "dễ đọc và dễ test" làm thước đo.

Quy trình tách hàm an toàn

  1. Chạy test hiện có (hoặc viết nhanh một test mô tả hành vi hiện tại).
  2. Chọn một đoạn code có ý nghĩa rõ ràng, dùng tính năng "Extract Function" của IDE.
  3. Đặt tên cho hàm mới theo việc nó làm.
  4. Chạy lại test, commit. Lặp lại với đoạn tiếp theo.

Kết luận

Hàm nhỏ và tập trung giúp code dễ đọc, dễ test và dễ sửa. Hãy bắt đầu bằng việc tìm hàm dài nhất trong dự án của bạn và thử tách nó thành 3-4 hàm nhỏ có tên rõ ràng. Sau vài lần, đây sẽ trở thành phản xạ tự nhiên.

Nếu bạn muốn thực hành viết code sạch cùng các bài tập có hướng dẫn, hãy khám phá thêm các khóa học trên codebem.com.

author_photo
Nguyễn Đình Cường

0 comment