Câu hỏi giúp thay đổi cách tôi nghĩ về thiết kế hệ thống
🧩 Tình cờ mình đọc được bài viết này trên LinkedIn — nói về một tình huống nhỏ trong phát triển phần mềm, nhưng lại mở ra một góc nhìn rất hay về tư duy thiết kế hệ thống và trải nghiệm người dùng. Mình chia sẻ lại để cùng mọi người trao đổi thêm.
Câu chuyện bắt đầu như thế này
Giả sử người dùng nhấn vào nút “Tạo báo cáo” trên giao diện web để xuất file Excel hoặc PDF. Quá trình này mất khoảng 5 phút (thời gian chỉ mang tính minh họa), và trong suốt thời gian đó, người dùng phải ngồi chờ. Câu hỏi đặt ra là:
“Làm thế nào để tối ưu hóa luồng này?”
Thoạt đầu, hầu hết lập trình viên (trong đó có tôi) sẽ nghĩ đến việc làm cho quá trình chạy nhanh hơn — tối ưu truy vấn SQL, giảm bớt bước xử lý dữ liệu, hoặc dùng cache. Nếu từ 5 phút giảm còn 1 phút, nghe có vẻ như đã thành công.
Nhưng… người dùng vẫn phải chờ. Nếu trình duyệt bị treo, mất mạng hoặc họ lỡ đóng tab, mọi thứ bắt đầu lại từ đầu.
Và như tác giả bài gốc đã nói — đây không phải là vấn đề hiệu năng, mà là vấn đề thiết kế hệ thống.
Khi chúng ta chỉ tập trung vào “chạy nhanh hơn”
Thời gian đầu đi làm, tôi (và có lẽ nhiều người khác) thường chỉ nghĩ:
“Làm sao để code nhanh hơn, nhẹ hơn, ít tốn CPU hơn?”
Đó là cách nghĩ hợp lý, nhưng chưa đủ. Ứng dụng trong ví dụ trên hoạt động hoàn toàn đồng bộ, nghĩa là người dùng buộc phải chờ hệ thống xử lý xong mới có thể tiếp tục.
Và rồi tác giả đặt ra một câu hỏi khá là hay:
“Tại sao người dùng phải chờ đợi ngay từ đầu?”
Nếu một tác vụ mất nhiều phút (hay thậm chí hàng giờ), nó không nên chạy trong luồng chính. Nó nên được xử lý bất đồng bộ (asynchronous), trong khi người dùng vẫn có thể tiếp tục công việc khác.
Giải pháp: Thiết kế luồng bất đồng bộ
Cách tiếp cận hợp lý hơn là:
- Khi người dùng nhấn “Tạo báo cáo”, backend chỉ tiếp nhận yêu cầu, lưu nó thành một job trong cơ sở dữ liệu và phản hồi ngay lập tức. Đó chính là cách mà một asynchronous API (API bất đồng bộ) hoạt động.
- Một background worker (hoặc job scheduler như Quartz, hoặc Lambda Function chạy qua queue) sẽ đảm nhận công việc nặng:
- Lấy dữ liệu
- Sinh file báo cáo
- Upload lên lưu trữ như S3 hoặc Azure Blob
- Khi hoàn tất, worker cập nhật trạng thái job và thông báo người dùng qua email hoặc real-time (SignalR, WebSocket…).
Nhờ vậy:
- Người dùng không phải chờ yêu cầu HTTP kéo dài.
- Hệ thống có thể retry nếu lỗi hoặc hủy job khi cần.
- Dễ dàng mở rộng quy mô khi nhiều người cùng tạo báo cáo.
Dù thời gian xử lý thật sự không thay đổi, trải nghiệm của người dùng trở nên nhanh hơn — vì họ không bị chờ.
Điều tôi rút ra sau khi đọc bài viết
Trước đây, tôi thường nghĩ việc tối ưu là viết code chạy nhanh hơn. Giờ thì tôi hiểu, đôi khi tối ưu là để người dùng không phải đợi.
Khi hệ thống được thiết kế bất đồng bộ:
- Người dùng có thể tiếp tục công việc khác.
- Hệ thống trở nên ổn định, có khả năng chịu tải và phục hồi khi lỗi xảy ra.
- Trải nghiệm tổng thể được cải thiện đáng kể.
Đó chính là sự khác biệt giữa việc tối ưu hiệu năng cục bộ và thiết kế hệ thống toàn diện.
All rights reserved