DRY, WET và rủi ro của việc trừu tượng hóa quá sớm

  • author-image

    Nguyễn Đình Cường

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

"Don't Repeat Yourself" (DRY) là nguyên tắc nhiều lập trình viên được dạy từ rất sớm. Nhưng áp dụng máy móc, gặp hai đoạn giống nhau là gộp ngay, đôi khi lại tạo ra thứ code còn tệ hơn trùng lặp. Bài này giúp bạn hiểu DRY đúng và biết khi nào nên chờ trước khi trừu tượng hóa.

DRY thực sự nói gì?

Nguyên tắc DRY phát biểu rằng mỗi mẩu tri thức trong hệ thống nên có một nơi biểu diễn duy nhất, rõ ràng. Chú ý chữ "tri thức", không phải "dòng code". Hai đoạn code giống nhau về hình thức nhưng đại diện cho hai quy tắc nghiệp vụ khác nhau không vi phạm DRY. Còn cùng một quy tắc nghiệp vụ nằm ở năm chỗ thì vi phạm, dù mỗi chỗ viết khác nhau.

Trùng lặp thật và trùng lặp tình cờ

// Hai đoạn trông giống nhaufunction priceWithVip(price)    { return price - price * 0.1; }function priceWithStudent(price){ return price - price * 0.1; }

Chúng hiện giống nhau (cùng giảm 10%) nhưng nếu giảm giá VIP và giảm giá sinh viên là hai chính sách độc lập, ngày mai có thể thay đổi riêng rẽ. Gộp chúng vào một hàm khiến khi chính sách một bên đổi, bạn phải thêm tham số, cờ, điều kiện để tách lại. Đây gọi là trùng lặp tình cờ (accidental duplication).

Ngược lại, trùng lặp thật là cùng một quy tắc, ví dụ công thức tính thuế VAT được sao chép ở trang giỏ hàng, hóa đơn và báo cáo. Khi luật thuế đổi, bạn phải sửa cả ba nơi và dễ quên một nơi. Trường hợp này nên gom lại.

Quy tắc ba lần (Rule of Three)

Một kinh nghiệm được nhiều người áp dụng: lần đầu viết thẳng, lần thứ hai chấp nhận trùng lặp và để ý, lần thứ ba mới trừu tượng hóa. Lý do là sau ba trường hợp, bạn mới thấy mẫu chung thật sự và biết phần nào cố định, phần nào thay đổi. Trừu tượng hóa từ hai ví dụ thường đoán sai hình dạng chung.

Cái giá của trừu tượng hóa sai

Hãy xem điều gì xảy ra khi gộp quá sớm:

// Ban đầu: hàm chung cho hai nơifunction formatItem(item, isAdmin) {if (isAdmin) {return item.name + ' (' + item.internalCode + ')';}return item.name;}// Sau vài tháng, thêm yêu cầu mới... tham số và nhánh phình rafunction formatItem(item, isAdmin, showPrice, isMobile, locale) {// hàng chục nhánh if lồng nhau}

Hàm "dùng chung" trở thành một con quái vật đầy cờ, mọi nơi gọi đều phải hiểu toàn bộ các tùy chọn của nó. Chi phí sửa một bản trừu tượng sai thường cao hơn nhiều so với chi phí của hai bản sao đơn giản. Hay có người nói: trùng lặp rẻ hơn trừu tượng sai.

WET và AHA: các cách nhìn khác

  • WET ("Write Everything Twice" hoặc "We Enjoy Typing") là cách nói đùa về việc chấp nhận trùng lặp có chủ ý, chờ đến khi hiểu rõ mẫu chung.
  • AHA ("Avoid Hasty Abstractions", tránh trừu tượng hóa vội vàng) nhấn mạnh việc chờ để tìm ra trừu tượng đúng thay vì áp dụng DRY phản xạ.

Đây không phải lời cổ vũ copy-paste bừa bãi mà là sự cân bằng: chấp nhận một chút lặp lại có kiểm soát để đổi lấy sự linh hoạt.

Câu hỏi giúp quyết định có nên gộp

  1. Hai đoạn code có cùng lý do thay đổi không? Nếu một yêu cầu thay đổi cả hai cùng lúc, nên gộp.
  2. Gộp lại có làm hàm cần thêm cờ boolean hoặc nhiều tham số tùy chọn không? Nếu có, đó là tín hiệu cảnh báo.
  3. Bạn đã có ít nhất ba ví dụ thật chưa, hay mới hai và đang đoán?
  4. Tên hàm chung có diễn đạt được một khái niệm rõ ràng không? Nếu đặt tên khó (chẳng hạn processData), có thể chưa phải một khái niệm thật.
  5. Chi phí sửa lại nếu gộp sai là bao nhiêu? Nếu nằm trong code nội bộ nhỏ thì dễ gỡ, nếu là API công khai thì khó hơn nhiều.

Cách gỡ khi đã gộp sai

Nếu hàm chung đã phình to, đừng cố vá thêm. Hãy "inline" nó trở lại từng nơi gọi (copy phần cần thiết vào mỗi chỗ), bỏ bớt phần không dùng, rồi xem mẫu chung thật sự là gì và trừu tượng hóa lại. Việc lùi một bước nghe tốn công nhưng thường đơn giản hơn tiếp tục xây trên nền sai.

Những thứ luôn nên DRY

  • Hằng số cấu hình và quy tắc nghiệp vụ quan trọng (thuế, phí, giới hạn).
  • Câu truy vấn hoặc logic xác thực dùng ở nhiều nơi.
  • Định nghĩa kiểu dữ liệu, schema, thông báo lỗi chuẩn.
  • Các đoạn thuần kỹ thuật không mang ý nghĩa nghiệp vụ như định dạng ngày giờ.

Kết luận

DRY là kim chỉ nam tốt nhưng không phải mệnh lệnh phải thực hiện ngay lập tức. Hãy phân biệt trùng lặp thật và trùng lặp tình cờ, chờ đến khi thấy mẫu chung rõ ràng, và nhớ rằng một chút lặp lại đôi khi rẻ hơn một trừu tượng sai.

Để hiểu sâu hơn về thiết kế phần mềm và nguyên tắc clean code, bạn có thể xem thêm các khóa học tại codebem.com.

author_photo
Nguyễn Đình Cường

0 comment