Mục lục

Senior Mindset: AHA vs DRY & Sạch hơn cả Clean Code

Tại sao việc lặp lại code (Duplication) đôi khi lại tốt hơn việc trừu tượng hóa sai (Wrong Abstraction)? Khám phá nguyên lý AHA và tư duy 'Viết code cho người đọc'.

Nhiều lập trình viên Junior quá ám ảnh với việc "Clean Code" theo nghĩa đơn giản: Phải thật ngắn, không được trùng lặp. Nhưng Senior biết rằng: Code ngắn chưa chắc đã dễ bảo trì.

1. AHA over DRY (Đừng vội vã trừu tượng hóa)

DRY (Don't Repeat Yourself) là quy tắc đầu tiên chúng ta được học. Nhưng nếu áp dụng quá sớm khi chưa thấy rõ bức tranh toàn cảnh, bạn sẽ rơi vào bẫy Abstraction Hell.

  • Vấn đề: Bạn thấy 2 Component List giống nhau 80%. Bạn tạo 1 GenericList dùng chung.
  • Kết quả: Tuần sau, Page A muốn thêm tính năng X, Page B muốn tính năng Y. Bạn thêm 2 cái props ifX, ifY vào GenericList. Sau 1 năm, GenericList trở thành một con "quái vật" với 50 props và logic if-else rối rắm.

Nguyên lý AHA (Avoid Hasty Abstractions)

"Sự lặp lại (Duplication) rẻ hơn rất nhiều so với một sự trừu tượng hóa sai (Wrong Abstraction)." - Sandi Metz.

Lời khuyên Senior:

  • Đừng tách component dùng chung ngay từ lần thứ 2 thấy nó.
  • Hãy đợi đến lần thứ 3 (Rule of Three). Khi bạn thấy rõ ràng 3 thực thể giống nhau, đó là lúc bạn thực sự biết điểm chung của chúng là gì để tách logic một cách bền vững.

2. Explicit over Implicit (Rõ ràng tốt hơn là ảo diệu)

Đừng cố gắng viết code quá "thông minh" hoặc bài bản đến mức khó hiểu (Clever code).

❌ Clever (Khó đọc cho người sau):

tsx:
const isAdmin = roles?.includes(R_ADMIN) && status > 1 && !isLocked;

✅ Explicit (Rõ ràng, nhìn phát hiểu ngay):

tsx:
const isAccountActive = status > 1 && !isLocked;
const hasAdminPrivileges = roles?.includes(R_ADMIN);
const canAccessSettings = isAccountActive && hasAdminPrivileges;

Code nhiều dòng hơn nhưng não bộ không cần "giải mã" logic mỗi khi đọc qua.

3. Think in State Transitions (Tư duy theo trạng thái)

Thay vì viết code kiểu mệnh lệnh: "Khi click thì làm A, rồi làm B, rồi ẩn C" (Imperative). Hãy tư duy khai báo: "Với trạng thái này, giao diện trông như thế nào?" (Declarative).

Ví dụ về logic "Loading":

  • Junior: Tìm mọi nơi có nút bấm và bọc if (loading) return ....
  • Senior: Quản lý trạng thái thông qua isPending (dùng useTransition của React 18/19). Giao diện tự động phản ứng với trạng thái đó. Code nghiệp vụ không bị trộn lẫn với UI.

4. Single Source of Truth (Nguồn sự thật duy nhất)

Một trong những lỗi lớn nhất khiến code bị biến thành "nồi lẩu" là State lệ thuộc (Dependent State).

tsx:
// ❌ Cấm kị: Đồng bộ 2 state bằng useEffect (Gây re-render thừa và bug async)
useEffect(() => {
  setItemsCount(items.length);
}, [items]);

// ✅ Đúng: Computed State (Dữ liệu dẫn xuất)
const itemsCount = items.length;

Đừng bao giờ tạo ra một biến state mới nếu giá trị đó có thể tính toán được từ các state/props hiện có.

5. Comment "Tại sao", không phải "Cái gì"

Code sạch là code tự giải thích nó đang làm cái gì. Comment chỉ nên xuất hiện khi bạn cần giải thích tại sao bạn lại chọn giải pháp đó (những quyết định ẩn sau code).

  • // Tăng counts lên 1 (Thừa thãi).
  • // Phải delay 50ms ở đây vì animation của trình duyệt Safari chưa hoàn tất. (Thông tin vô cùng quan trọng cho người bảo trì).

6. Composition over Configuration

Thay vì tạo một Component với 20 props để config đủ kiểu (Color, Size, Variant, Icon...). Hãy cho phép người dùng lồng ghép (Composition).

❌ Config (Hạn hẹp):

tsx:
<Header title="My Title" showSearchIcon={true} onSearch={...} />

✅ Composition (Linh hoạt):

tsx:
<Header>
  <Header.Title>My Title</Header.Title>
  <Header.Search onSearch={...} />
</Header>

Pattern này giúp Component của bạn không bao giờ bị "lỗi thời" khi yêu cầu UI thay đổi.

Viết code giống như viết một bức thư cho một "kẻ sát nhân" biết địa chỉ nhà bạn và sẽ là người bảo trì code này sau 6 tháng nữa. Hãy viết sao cho họ không muốn tìm đến bạn.

Quảng cáo
mdhorizontal