Bạn thân mày hiện 'Active now' nhưng không reply 20 phút. Hay mày tắt app rồi mà vẫn thấy online. Đây không phải bug — đây là hàng loạt trade-off có chủ ý.
Thiết kế hệ thống
Thiết kế hệ thống
89 bài viết
Click vào bit.ly/3xK9mP và mày đến đích trong tích tắc. Đằng sau đó là một database lookup, một HTTP response code, và một cái trick browser caching mà phần lớn dev không biết.
Mày search 'python tutorial' và Google trả về 4 tỷ kết quả trong 0.31 giây. Không phải vì server Google siêu nhanh — mà vì nó không bao giờ scan 4 tỷ trang đó.
Mỗi thứ Hai, Discover Weekly cho mày 30 bài chưa từng nghe — và ~70% trong số đó đúng trend. Đằng sau là matrix factorization, audio feature vectors, và NLP trên hàng triệu bài blog nhạc.
Mày gõ 'teh' và nó tự đổi thành 'the'. Gõ 'im' thành 'I'm'. Đôi khi sai một cách ngớ ngẩn. Đằng sau là edit distance, keyboard proximity, và một language model chấm điểm từng candidate.
Mày search 'tai nghe' một lần, hôm sau homepage toàn tai nghe. Không phải Shopee đọc được tâm trí — đó là collaborative filtering và real-time session signals phối hợp với nhau.
Instagram Reels mất nhiều tháng để hiểu mày. TikTok chỉ cần 10 phút. Không phải magic — đó là implicit signals, cold start strategy, và two-stage recommendation pipeline.
QR code của MoMo không chứa số tài khoản của mày — nó chứa một session token có chữ ký mật mã. Đây là thứ ngăn kẻ xấu chụp màn hình rồi thanh toán lại.
Hai người gõ vào cùng một đoạn văn cùng lúc mà không ai bị mất chữ. Không phải locking, không phải merge conflict — Operational Transformation là cái trick đứng sau tất cả.
Trong PUBG hay Liên Quân, mày thấy nhân vật đối thủ chạy mượt dù họ ở đầu dây kia của internet. Không phải server gửi data 60 lần/giây — mà client đang tự diễn trên sân khấu của riêng nó, dựa trên dữ liệu 100ms cũ.
Zalo gửi tin nhắn lúc 2 giờ sáng, điện thoại mày rung dù app đã tắt hoàn toàn. Không phải app đang ngầm chạy — mà OS đang duy trì một kết nối TCP thay cho tất cả app. Đây là cách FCM và APNs hoạt động.
Dấu ba chấm '...' xuất hiện chỉ vài mili giây sau khi người kia bắt đầu gõ — nhưng nếu gửi event mỗi lần nhấn phím, server sẽ sập trong vài giây. Trick thật sự là throttle + timeout, và nó đơn giản hơn mày nghĩ.
1 tick, 2 tick, tick xanh — Zalo biết chính xác lúc nào mày mở tin nhắn. Cơ chế đằng sau không phải là server hỏi thăm liên tục, mà là ACK packet đi ngược chiều.
Gói tin từ điện thoại mày đến server Netflix đi qua hàng chục router. Bất kỳ ai ngồi giữa đường đều có thể thấy nó. Vậy tại sao họ không đọc được?
Cái mã 6 số trong Google Authenticator không được tạo ra từ server — điện thoại của mày tự tính nó mà không cần internet. TOTP là thứ đứng sau tất cả.
Gmail, YouTube, Notion, Slack đều dùng tài khoản Google nhưng không app nào biết password của mày. Đây là cách OAuth 2.0, authorization code flow, và session cookie phối hợp để làm điều đó.
Pull-to-refresh không đợi API xong mới chạy animation. Đây là cách touch events, CSS transform, và optimistic UX phối hợp để tạo cảm giác nhanh dù mạng chậm.
Load balancer phân phối traffic. Gateway xử lý auth, routing, transformation. Hiểu đúng để design microservices đúng.
YouTube không download cả video — nó chỉ tải đúng mấy giây xung quanh chỗ mày đang xem. Đây là cách DASH, manifest file, và adaptive bitrate làm điều đó.
Mỗi lần mày gõ một chữ, Google trả về gợi ý trong dưới 50ms. Không phải vì server Google nhanh — mà vì có rất nhiều thứ xảy ra trước khi request đó thậm chí được gửi đi.
Cái blur nhẹ hiện ra ngay lập tức trước khi ảnh load xong — không phải hiệu ứng cho đẹp. Đó là kết quả của 3 kỹ thuật khác nhau, mỗi cái giải quyết một vấn đề mà cái kia không làm được.
Icon xe Grab di chuyển mượt 60fps trên map của mày — nhưng thực ra nó chỉ nhận dữ liệu GPS mỗi 3-5 giây. Cái trick đằng sau là dead reckoning, kỹ thuật dẫn đường mà NASA dùng từ thập niên 60.
Sidecar proxy pattern, control plane vs data plane, và câu hỏi quan trọng nhất: có phải lúc nào cũng cần không?
3 trạng thái Closed/Open/Half-Open, khi nào trip, khi nào reset, fallback strategy với Resilience4j + Spring Boot.
Replica async sau primary — read-your-writes, route sau mutation, và khi nào chấp nhận eventual consistency.
EXPLAIN Using index, composite index và thứ tự cột — vì sao index đúng tên vẫn không cover query.
LocalDateTime không có timezone. API contract: Instant + ZoneId, lưu UTC, hiển thị theo clinic. Bug lịch hẹn ngày 15 thành 14.
Redis Lua chặn tranh slot ở cache — nhưng confirm vào DB vẫn có thể trùng nếu thiếu ràng buộc và lock đúng chỗ.
User tắt email marketing vẫn nhận reminder — check preference trước send, link unsubscribe one-click, audit log.
Gateway gọi ngược POST /webhooks/payment — không tin body, verify HMAC, xử lý trùng eventId. Cặp đôi với idempotency key phía client.
Import 10k dòng: chunk size, validate từng row, gom lỗi trả report — một dòng sai không rollback cả file.
AFTER_COMMIT vẫn fail nếu process chết trước khi gửi mail. Ghi outbox trong cùng transaction DB, worker gửi sau — at-least-once có kiểm soát.
@Version và OptimisticLockException: last-write-wins im lặng vs báo conflict cho user merge. Khác race booking slot nhưng cùng đau production.
HTTP không tự push. Short polling đốt server, SSE cho one-way realtime, WebSocket khi cần hai chiều — chọn sai là mở connection vô ích hoặc over-engineer notification đơn giản.
LIKE '%nguyen%' không scale. FULLTEXT cho tên bệnh nhân vừa. Elasticsearch khi cần fuzzy, facet, relevance — đừng over-engineer ngày đầu.
Ảnh X-quang 15MB không nên base64 qua JSON. Multipart cho upload vừa, presigned URL cho file lớn — backend không làm proxy băng thông.
Global limit 100 req/phút không cứu được POST /payments khi GET /schedules vẫn ổn. Cấu hình limit theo route, tier user, và trả header để client không retry vô hạn.
Boolean isPaid + isCancelled dễ lệch. Enum trạng thái, transition hợp lệ, và refund chỉ khi payment đã CAPTURED — tránh free slot và double refund.
Bài 84 idempotency inbound. Outbound: RestClient timeout, @Retryable chỉ trên read hoặc request có Idempotency-Key — tránh capture hai lần.
@Where giúp nhưng native query, JOIN thiếu filter, và admin export quên deleted_at — patient thấy appointment đã hủy. Cách audit và phòng leak.
Reset link mang token ngẫu nhiên có TTL, hash trong DB, invalidate sau dùng. Không bao giờ đặt password mới hoặc password cũ trong query string.
4xx là lỗi client, 5xx là lỗi server. 201 Created vs 200 OK. API trả 200 kèm success:false khiến monitoring và client đều mù.
OncePerRequestFilter, JwtAuthenticationFilter, authorization vs authentication — và tại sao filter chạy trước @PreAuthorize.
Postman chạy được, browser báo CORS error. Same-origin policy, preflight OPTIONS, và cách cấu hình CorsConfigurationSource trong Spring Security.
Session lưu state ở server, JWT tự chứa thông tin và server không cần nhớ gì. Anatomy của một JWT, verify flow, access token vs refresh token, và tại sao JWT không thể revoke ngay.
LIMIT/OFFSET đơn giản nhưng chậm khi offset lớn. Cursor pagination nhanh hơn nhưng không nhảy trang được. Trade-off và cách implement cả hai trong Spring Data.
ALTER TABLE tay trên production, ddl-auto=update, hay migration script có version — ba cách thay đổi schema và tại sao chỉ một cái an toàn. Flyway trong Spring Boot từ đầu đến cuối.
Một nút bấm đơn giản với người dùng — nhưng phía sau là availability check, concurrency control, payment processing, notification, calendar sync, và audit logging.
isActive, isEnabled, isDeleted — ba field có vẻ tương tự nhưng semantic khác nhau. Khi developer mới hiểu nhầm, logic sai được build lên trên đó, và bug chỉ xuất hiện trong production.
Update profile thành công, reload trang vẫn thấy tên cũ. Cache invalidation là một trong hai vấn đề khó nhất trong computer science — và đây là cách xử lý thực tế.
Tạo user trong DB thành công, tạo user trong Keycloak thất bại — hệ thống ở trạng thái inconsistent. Saga pattern và compensation transaction là cách xử lý distributed failure.
Role-based access control không đủ cho bài toán: Doctor A chỉ xem được bệnh nhân của Doctor A. Attribute-based access control và cách implement đúng trong Spring Security.
Gửi email xác nhận đặt lịch, sau đó transaction rollback vì lỗi — user nhận email nhưng lịch không tồn tại. Transactional outbox pattern giải quyết vấn đề này đúng cách.
Network timeout, user double-click, retry logic — tất cả đều có thể gây ra duplicate payment. Idempotency key đảm bảo cùng một request chỉ được xử lý đúng một lần.
Race condition trong booking system: hai người cùng thấy slot trống, cùng đặt, cả hai thành công — nhưng chỉ có một slot. Redis Lua script và atomic operation là giải pháp.
Mỗi layer thêm vào là một indirection, một điểm failure, và một thứ cần maintain. Layer nên tồn tại vì nó giải quyết vấn đề thật — không phải vì pattern nói phải có.
Premature optimization là evil — premature scaling còn tệ hơn. Phần lớn startup thất bại vì không có user, không phải vì không scale được. Build for now, design for scale.
Queue che đi failure thay vì giải quyết nó. Message tích lũy trong queue, consumer xử lý không kịp, queue overflow — hệ thống sập theo cách chậm hơn nhưng vẫn sập.
Clean Architecture thêm nhiều layer và abstraction. Với CRUD app đơn giản — đây là over-engineering. Với hệ thống phức tạp có nhiều business rule — đây là đầu tư đúng đắn.
Don't Repeat Yourself hướng dẫn về knowledge duplication — không phải code duplication. Đôi khi duplicate code là đúng. Wrong abstraction tệ hơn duplicate code.
Cache thêm complexity, tăng memory usage, và tạo ra stale data problems. Nếu cache hit rate thấp — bạn đang trả chi phí của cache mà không nhận được lợi ích.
PostgreSQL scale tốt đến hàng tỷ row với index và sharding đúng cách. Nhiều công ty chuyển sang NoSQL rồi nhận ra vấn đề không phải ở SQL — mà ở cách họ dùng database.
Microservices giải quyết vấn đề team autonomy và deployment independence — không phải vấn đề performance. Scale nằm ở database, caching, và infrastructure — không phải ở việc chia service.
Consistency, Availability, Partition Tolerance — bạn chỉ có thể chọn hai. Hiểu CAP không phải để thuộc lòng — mà để đưa ra quyết định đúng khi thiết kế distributed system.
Khi service B chậm, service A gọi B sẽ bị block, thread pool cạn kiệt, và A sập theo. Circuit Breaker phát hiện failure và ngắt connection trước khi cascade xảy ra.
Rate limiting kiểm soát số lượng request trong một khoảng thời gian. Token bucket, sliding window, fixed window — mỗi thuật toán có trade-off khác nhau về accuracy và memory.
Queue giải decoupling giữa producer và consumer — nhưng cũng thêm complexity, latency, và failure modes mới. Không phải bài toán nào cũng cần queue.
Load balancer phân phối traffic đến nhiều server — nhưng thuật toán, layer 4 vs layer 7, health check, và sticky session mới là thứ quyết định hệ thống có thật sự reliable không.
API xấu không gây lỗi ngay — nó tạo ra misunderstanding giữa các team, edge case không được handle, và coupling ẩn tích lũy theo thời gian.
Microservices không phải bước tiến hoá tự nhiên của Monolith. Đây là hai lựa chọn kiến trúc khác nhau, giải quyết vấn đề khác nhau — và cái giá của việc chọn sai rất đắt.
Functional: hệ thống làm gì. Non-functional: hệ thống hoạt động tốt thế nào. Bỏ qua non-functional requirements là lý do nhiều hệ thống sập khi có người dùng thật.
Code đúng chưa đủ để hệ thống chạy tốt ở scale lớn. System design là khả năng thiết kế hệ thống đáp ứng được non-functional requirements: scale, availability, latency.
Một số việc không cần làm ngay: gửi email, tạo report, resize ảnh. Queue tách việc nhận request và xử lý request — giúp hệ thống responsive và resilient hơn.
Race condition, lost update, dirty read — đây là những vấn đề xảy ra khi concurrent requests cùng thao tác trên một dữ liệu. Và chúng chỉ xảy ra trong production.
Cache expire, hàng nghìn request cùng lúc hit database, database quá tải và sập — kéo theo cache không thể warm up lại. Thundering herd problem và cách phòng tránh.
Cache là một trong những công cụ mạnh nhất để tăng performance — và cũng là nguồn gốc của những bug khó chịu nhất. Hiểu caching strategy trước khi implement.
Blocking I/O giữ thread chờ. Non-blocking I/O giải phóng thread để làm việc khác. Hiểu sự khác biệt này giúp bạn thiết kế hệ thống xử lý concurrent requests tốt hơn.
Thread-per-request model của Spring Boot truyền thống vs non-blocking Event Loop của Spring WebFlux. Trade-off về complexity, performance, và khi nào nên dùng cái nào.
Từ lúc bạn gõ Enter đến khi nhận được response — hàng chục bước xảy ra. Hiểu luồng này giúp bạn debug performance issue và thiết kế hệ thống tốt hơn.
Tạo database connection tốn kém. Connection pool tái sử dụng connection — nhưng pool quá nhỏ thì bottleneck, pool quá lớn thì database overload.
Thêm cột is_deleted tưởng là dễ — cho đến khi unique constraint bị vỡ, query trở nên phức tạp, và audit log không hoạt động như kỳ vọng.
NoSQL không phải phiên bản mới hơn của SQL. Chúng giải quyết vấn đề khác nhau. Chọn dựa trên access pattern, consistency requirement, và team capability — không phải trend.
Normalize quá thì JOIN nhiều, query chậm. Denormalize quá thì data inconsistency. Không có công thức cố định — chỉ có trade-off dựa trên access pattern thực tế.
Load 100 user, mỗi user trigger thêm 1 query để lấy orders — là 101 queries thay vì 2. N+1 không gây lỗi, chỉ làm app ngày càng chậm cho đến khi không thể chịu được.
Deadlock xảy ra khi hai transaction chờ nhau giải phóng lock. Database phát hiện và kill một transaction — nhưng fix đúng cách mới tránh được tái diễn.
Read Uncommitted, Read Committed, Repeatable Read, Serializable — mỗi level là một trade-off giữa consistency và performance. Chọn sai là data corruption hoặc bottleneck.
Multi-Version Concurrency Control là lý do PostgreSQL và MySQL InnoDB có thể xử lý concurrent reads và writes hiệu quả. Hiểu MVCC là hiểu cách database thật sự hoạt động.
EXPLAIN ANALYZE là công cụ mạnh nhất để debug query chậm. Hiểu execution plan giúp bạn biết database đang làm gì và tại sao query của bạn không dùng index.
Index không phải phép màu. Tạo sai index, query sai cách, hoặc index quá nhiều — đều có thể làm hệ thống chậm hơn thay vì nhanh hơn.