Design Patterns: Đừng để 'khuôn mẫu' trói tư duy của bạn
Xin chào mọi người,
Sau bài hôm qua về trade-off mindset, mình nhận được khá nhiều phản hồi tích cực. Hôm nay mình muốn nói tiếp về một thứ liên quan rất gần — những “khuôn mẫu” mà người đi trước đã tạo sẵn cho chúng ta áp dụng.
Đa số gọi chúng là Best Practices. Nhưng liệu chúng có thực sự “best” như quảng cáo không?
Sự thật là: Nếu bạn đang làm trong một ngành sáng tạo như IT, đừng bao giờ để những pattern có sẵn giới hạn tư duy của mình. Và điều này đặc biệt đúng với Design Patterns.
Cái bẫy của việc tỏ ra “thông minh”
Khoảng 3 năm trước, mình mới chập chững vào nghề dev. Mình có một người bạn khoe là thuộc gần hết Design Patterns. Mình nghĩ ông này pro vl, và bắt đầu chạy theo.
Mình còn mua hẳn cuốn Head First Design Patterns, coi như sách gối đầu giường.
Lúc đó, tiêu chuẩn của mình rất đơn giản:
Càng biết và áp dụng được nhiều pattern, càng “pro”.
Trong đồ án tốt nghiệp, mình nhét đủ thứ pattern vào chỉ để code trông “chuyên nghiệp”.
Nhưng sau khi va vào thực tế — bị deadline đập vào mặt, debug lại đống code “thông minh” mà chính mình viết 6 tháng trước — mình nhận ra một sự thật đau lòng:
Design Patterns là vitamin. Nhưng dùng quá liều thì thành thuốc độc.
Khi nào pattern trở thành “xiềng xích”?
Mình không nói pattern là xấu. Chúng giúp hệ thống sạch sẽ, decoupling tốt, vân vân. Nhưng chúng chỉ phát huy tác dụng khi áp đúng ngữ cảnh.
Đừng dùng pattern nếu bạn không trả lời được 3 câu hỏi này:
1. Vấn đề này có thật sự tồn tại không?
Bạn đang xây móng biệt thự để đặt cái chuồng gà lên trên à?
2. Team bạn bao nhiêu người?
Một team 2 người mà chơi microservice với cả tá pattern? Đó không phải kiến trúc — đó là tự làm khổ mình.
3. Bạn có đang tối ưu cho tương lai không tưởng?
Bạn đang abstract cho 5 năm tới… nhưng product có khi không sống nổi 3 tháng.
Decision Framework cho Senior: Rule of Three
Thay vì yêu hay ghét pattern một cách mù quáng, một Senior thật sự dùng những quy tắc đơn giản.
Lần thứ 1:
Viết code đơn giản nhất có thể (KISS). Cứ để nó hơi “xấu” cũng được.
Lần thứ 2:
Khi logic tương tự xuất hiện lại, đừng vội abstract. Cứ copy-paste lần nữa.
Vì sao? Vì bạn vẫn chưa đủ context để biết cái gì thực sự là “common”.
Lần thứ 3:
Lúc này refactor mới có lý. Bạn đã đủ ví dụ thực tế để extract ra abstraction đúng.
Nhớ rằng: Cái giá của một abstraction sai cao hơn nhiều so với code bị duplicate.
Một ví dụ thực tế
❌ Cách over-engineered:
class ShippingFactory {
static getStrategy(type) {
if (type === 'domestic') return new DomesticShipping();
if (type === 'international') return new InternationalShipping();
throw new Error("Unsupported!");
}
}
const strategy = ShippingFactory.getStrategy('domestic');
const fee = strategy.calculate(10);
✅ Cách thực tế (KISS):
function calculateShippingFee(type, weight) {
const rates = { domestic: 1000, international: 5000 };
return weight * (rates[type] || 0);
}
Cách đầu tiên, bạn đụng vào 5 file để đổi một mảnh logic. Cách thứ hai, mất 5 giây.
Nếu app của bạn chỉ chạy ở một quốc gia trong 2 năm tới, cái Factory kia chỉ là rác kỹ thuật.
Kết
Design Patterns sinh ra để phục vụ bạn. Bạn không sinh ra để phục vụ chúng.
Một engineer giỏi không phải người thuộc nhiều pattern nhất, mà là người biết khi nào không dùng chúng.
Đừng cố tỏ ra thông minh bằng những cấu trúc phức tạp. Hãy thể hiện bằng cách biến vấn đề phức tạp thành giải pháp đơn giản, dễ maintain — để teammate hiểu và sửa được mà không cần gọi bạn.
Vậy hôm nay, hãy tự hỏi: Bạn đang thêm pattern để cảm thấy mình thông minh? Hay bạn đang thực sự giải quyết vấn đề có thật?
Tuỳ. Nhưng đừng để cái “ngầu” ngắn hạn biến thành nỗi đau dài hạn.