Trang chủ

Không tìm thấy bài viết nào.

01
DLQ không phải thùng rác — retry và reconciliation trong Saga

Retry xử lý lỗi tạm thời, DLQ giữ message không xử lý được, còn reconciliation phát hiện cả những event chưa từng tới queue. Cần đủ cả ba lớp.

02
Header tin cậy đến đâu trong microservices?

Gateway inject X-User-Id tiện nhưng chỉ an toàn khi downstream xác minh request thật sự đến từ trusted boundary. Internal flag không phải authentication.

03
Hợp đồng đã ký phải nhớ quá khứ

Đọc tên và giá trị live từ service khác khiến nội dung hợp đồng thay đổi theo dữ liệu hiện tại. Snapshot có chủ ý bảo vệ lịch sử và domain invariant.

04
Escrow không thể nhảy thẳng từ SIGNED sang ACTIVE

Ký hợp đồng và khóa đủ tiền là hai business fact khác nhau. Two-phase lock làm rõ buyer payment, seller deposit và thời điểm contract thật sự có hiệu lực.

05
Database cũ đang che giấu bug của bạn

Persistent volume khiến seed thiếu và migration stale không lộ ra. Chỉ một lần dựng stack hoàn toàn sạch mới kiểm tra được môi trường có tái tạo thật sự hay không.

06
Một try/catch quá rộng có thể biến lỗi business thành message hỏng

Bọc cả @RabbitListener trong một catch khiến lỗi DB tạm thời bị gắn nhãn payload sai và mất cơ hội retry. Error boundary phải đặt đúng chỗ.

07
Async rồi vẫn coupling — đừng biến consumer thành client ngược

Nhận event xong lại gọi ngược producer để lấy dữ liệu khiến consumer vẫn phụ thuộc đồng bộ. Event-carried state transfer cắt chuỗi lỗi đó.

08
Khi một lệnh Feign làm Saga mất nhất quán

Gọi REST đồng bộ để thay đổi dữ liệu của service khác tạo ra distributed write không có transaction chung. Đúng hơn là để service sở hữu state tự xử lý event.

09
Idempotency không phải là existsBy()

Check tồn tại trước khi save chỉ là bước đầu. Race condition, unique constraint và dữ liệu khác request mới quyết định một thao tác có thật sự idempotent hay không.

10
Message đã vào queue nhưng handler chưa chạy — converter cũng là một phần của hệ thống

Exchange và binding đều đúng chưa có nghĩa business logic đã chạy. Message converter sai có thể làm listener fail trước dòng code đầu tiên.

11
Visitor — khi bạn cần thêm operation vào object hierarchy mà không sửa class

GoF gặp một mâu thuẫn kinh điển: thêm operation mới mà không sửa class cũ, hoặc thêm class mới mà không sửa operation cũ — bạn chỉ được chọn một. Visitor giải nửa bài đó.

12
Iterator — duyệt collection mà không cần biết bên trong có gì

GoF gặp bài toán: cùng một traversal logic phải chạy trên array, linked list, tree, graph — mà không thay đổi code duyệt. Iterator tách 'cách duyệt' ra khỏi 'cấu trúc chứa data'. Java đã bake nó vào ngôn ngữ.

13
Tại sao dark mode không chỉ là đổi màu background?

CSS variables, prefers-color-scheme, OS integration, và tại sao dark mode đúng cách phức tạp hơn mày nghĩ.

14
Chain of Responsibility — khi request phải đi qua nhiều handler

GoF gặp bài toán: một request cần được xử lý bởi nhiều handler theo thứ tự, nhưng sender không nên biết handler nào sẽ thật sự xử lý nó. Bạn đang dùng pattern này mỗi khi gửi request qua Spring Security.

15
Tại sao 'đang online' trên Messenger không phải lúc nào cũng chính xác?

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ủ ý.

16
Tại sao URL shortener như bit.ly redirect gần như instant?

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.

17
Tại sao Google trả về kết quả trong 0.3 giây dù index hàng tỷ trang web?

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 đó.

18
Tại sao Spotify biết mày sẽ thích bài hát mày chưa từng nghe?

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.

19
Tại sao autocorrect biết mày định gõ gì?

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.

20
Tại sao Shopee gợi ý đúng thứ mày vừa tìm kiếm?

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.

21
Composite — khi cây và lá phải được xử lý như nhau

GoF gặp cấu trúc dạng cây — menu có submenu, category có subcategory, permission có group. Composite cho phép xử lý một node đơn hay toàn bộ cây bằng cùng một cách.

22
Tại sao TikTok biết mày thích xem gì chỉ sau vài video?

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.

23
Lighthouse đang chấm điểm web của mày dựa trên cái gì thật ra?

Con số 0–100 của Lighthouse không phải cảm tính — nó là weighted average của các metric đo chính xác trải nghiệm người dùng. LCP, INP, CLS, và lý do con số đó ảnh hưởng đến SEO.

24
Tại sao bundle JavaScript được split nhỏ thay vì một file khổng lồ?

Tải landing page mà phải kéo theo code của admin dashboard — đó là vấn đề bundle khổng lồ. Code splitting, dynamic import, và content hash giải quyết nó như thế nào.

25
Tại sao ảnh trên web hiện đại nhỏ hơn nhưng vẫn đẹp?

Cùng một ảnh, WebP nhỏ hơn JPEG 30%, AVIF nhỏ hơn 50% — mà mắt người không phân biệt được. Đây là ba lớp kỹ thuật làm cho web năm 2024 load nhanh hơn mà không mờ hơn.

26
Adapter — khi hai interface không nói chuyện được với nhau

GoF gặp vấn đề với code legacy và third-party library có interface không khớp. Adapter là cái adapter phích cắm của lập trình — không thay đổi gì ở hai đầu, chỉ làm cho chúng vừa nhau.

27
Tại sao QR code thanh toán hết hạn sau vài phút?

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.

28
Tại sao Google Docs nhiều người edit cùng lúc mà không đè lên nhau?

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ả.

29
Tại sao game online biết vị trí nhân vật của người khác gần như realtime?

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ũ.

30
Tại sao notification push đến điện thoại dù app đang tắt?

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.

31
Tại sao typing indicator hiện gần như realtime mà không spam server?

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ĩ.

32
Builder — object phức tạp không cần constructor 12 tham số

GoF gặp vấn đề với object có quá nhiều optional field. Builder tách quá trình tạo object thành từng bước. Lombok @Builder thì sao?

33
Distributed Tracing — request chết ở đâu trong chuỗi service?

Trace ID theo request qua nhiều service, Micrometer Tracing với Spring Boot 3, đọc waterfall diagram để debug.

34
Tại sao Zalo biết mày đã 'đọc' tin nhắn — read receipt hoạt động thế nào?

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.

35
Tại sao HTTPS không bị nghe lén dù đi qua hàng chục server?

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?

36
Tại sao OTP chỉ dùng được một lần và hết hạn sau 30 giây?

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ả.

37
Tại sao đăng nhập Google một lần dùng được khắp nơi — SSO hoạt động thế nào?

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 đó.

38
Tại sao kéo refresh trên app mobile có animation mượt — và nó sync với server lúc nào?

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.

39
Tại sao YouTube không buffer từ đầu mà buffer đúng đoạn mày đang xem?

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 đó.

40
Factory — khi bạn không muốn code biết nó đang tạo ra object gì

GoF gặp vấn đề với `new` keyword — nó buộc code phải biết cụ thể class nào sẽ được tạo. Factory cắt đứt sự phụ thuộc đó. Abstract Factory đi thêm một bước.

41
API Gateway vs Load Balancer — hai thứ khác nhau nhưng hay bị gộp làm một

Load balancer phân phối traffic. Gateway xử lý auth, routing, transformation. Hiểu đúng để design microservices đúng.

42
Tại sao Google Search gợi ý ngay khi mày gõ mà không lag?

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.

43
Tại sao ảnh Instagram load từ mờ đến rõ thay vì hiện trắng rồi bật ra?

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.

44
Tại sao xe Grab trên map chạy mượt dù không gọi API mỗi giây?

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.

45
Singleton — pattern dễ nhất, bị dùng sai nhiều nhất

GoF tạo ra Singleton để giải quyết một vấn đề cụ thể. Vấn đề bắt đầu khi một pattern bị dùng để giải quyết mọi thứ.

46
Service Mesh là gì và khi nào bạn cần nó

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?

47
Circuit Breaker — vì sao một service chậm có thể kéo sập cả hệ thố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.

48
Nginx thực tế: config từ dev đến production

Reverse proxy, HTTPS, gzip, rate limit ở tầng infra — những thứ Nginx phải làm trước khi request chạm Spring Boot.

49
On-call — 15 phút đầu đọc log production

Alert 5xx spike: correlation ID, timeline, thay đổi gần đây — trước khi grep exception ngẫu nhiên. Checklist cho lần đầu vào on-call rotation.

50
Read replica lag — vì sao đọc stale ngay sau khi write

Replica async sau primary — read-your-writes, route sau mutation, và khi nào chấp nhận eventual consistency.

51
Migration zero-downtime — expand-contract

Flyway ADD COLUMN NOT NULL một lần = deploy đứng. Thêm nullable → deploy code → backfill → enforce — schema và app lệch pha có kiểm soát.

52
Covering index — khi query không cần chạm bảng

EXPLAIN Using index, composite index và thứ tự cột — vì sao index đúng tên vẫn không cover query.

53
Graceful shutdown và readiness probe — deploy không cắt giữa request

SIGTERM, server.shutdown=graceful, readiness fail khi DB down — K8s không route traffic vào pod đang chết.

54
Doctor double-booking — khi race lọt xuống tầng database

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ỗ.

55
Timezone và slot lịch hẹn — 9h sáng không lệch ngày

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.

56
Notification preferences — opt-out, unsubscribe, không spam người đã tắt

User tắt email marketing vẫn nhận reminder — check preference trước send, link unsubscribe one-click, audit log.

57
Cron job — ShedLock khi hai instance cùng chạy scheduled task

@Scheduled trên hai pod Spring = email nhắc lịch gửi đôi. Distributed lock Redis/DB — chỉ một instance chạy job mỗi lần.

58
Bulk import CSV bệnh nhân — batch, validation, không dừng cả file

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.

59
Payment webhook — signature verify và idempotent

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.

60
@Async trong Spring — thread pool và khi transaction không theo

CompletableFuture trên ThreadPoolTaskExecutor, @EnableAsync, self-invocation, và vì sao @Async trong @Transactional thường không còn transaction.

61
Outbox pattern — email không mất sau commit

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.

62
API versioning — /v1/ trong URL hay header, và khi nào breaking

Đổi field response là đổi contract — versioning, deprecation có hạn, và phân biệt breaking vs non-breaking change.

63
Optimistic lock — hai điều dưỡng sửa cùng MedicalRecord

@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.

64
Alert design — on-call không chết vì noise

Alert phải báo user đang đau, có hành động rõ, và page ít — không phải mọi log ERROR đều ping Slack lúc 3h sáng.

65
WebSocket, SSE, và Polling — khi nào dùng cái nào

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.

66
Blue-green vs rolling deploy — khi nào dùng cái nào

Rolling update mặc định trên K8s, blue-green đổi traffic một phát, và vì sao zero-downtime không có nghĩa zero-risk.

67
Search trong HMS — LIKE, FULLTEXT INDEX, và Elasticsearch

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.

68
Feature flag — deploy code mà chưa bật feature

Merge liên tục lên main, ship binary hàng ngày, bật tính năng bằng flag — rollout 5% trước khi mở cho toàn bộ clinic.

69
Soft delete leak — data đã xóa vẫn lọt ra API

@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.

70
Retry outbound và idempotency — khi HMS gọi gateway

P10/Bài 02 nói về idempotency inbound. Outbound: RestClient timeout, @Retryable chỉ trên read hoặc request có Idempotency-Key — tránh capture hai lần.

71
Staging — môi trường không phải bản nháp vô nghĩa

Staging mirror prod về topology và config shape — data masked, secret riêng, gateway sandbox. Integration test 103 fail vì staging khác local, không phải vì test dở.

72
Postmortem — sau incident, học hệ thống không đổ lỗi người

On-call đã stabilize (bài 121). Postmortem blameless: timeline, root cause hệ thống, action item có owner — không phải biên bản kỷ luật.

73
State machine thanh toán — cancel đã trả tiền và refund

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.

74
Rate limit theo endpoint — cùng một user, khác hàng rào

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.

75
File upload đúng cách — multipart, base64, và S3 presigned URL

Ả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.

76
Password reset flow — one-time token, expiry, và tại sao không gửi password trong URL

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.

77
HTTP status codes đúng cách — tại sao return 200 cho mọi thứ là sai

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ù.

78
Spring Security filter chain — request đi qua những gì trước controller

OncePerRequestFilter, JwtAuthenticationFilter, authorization vs authentication — và tại sao filter chạy trước @PreAuthorize.

79
CORS — tại sao browser block và config đúng trong Spring

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.

80
@Transactional sâu hơn — proxy, self-invocation, và rollback rules

Gọi this.save() trong cùng class không qua proxy — transaction không mở. Rollback chỉ với RuntimeException. Bug thầm lặng khiến data half-committed.

81
Secrets không được hardcode — Spring profiles và env vars

Password trong application.yml commit lên Git là incident chờ xảy ra. Config theo môi trường, Spring profiles, và cách HMS tách secret khỏi codebase.

82
Optional<T> — khi nào nên dùng, khi nào không

Optional không phải cách viết null an toàn cho mọi chỗ. Return type có lý, field và parameter thì gần như luôn sai — đó là lý do cách dùng này thường bị review lại.

83
Integration Test — tại sao unit test xanh hết mà vẫn deploy ra production bị lỗi

Unit test kiểm tra logic trong isolation. Integration test kiểm tra các layer phối hợp đúng không. @DataJpaTest, @WebMvcTest, @SpringBootTest — khi nào dùng cái nào và tại sao cần cả hai.

84
Logging đúng cách — vì sao log của bạn đang vô dụng lúc cần nhất

Log quá nhiều không giúp debug nhanh hơn. Log đúng level, đúng context, đúng thông tin — và biết những gì tuyệt đối không được log. MDC correlation ID để trace request xuyên suốt.

85
JWT là gì — và tại sao token không phải session

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.

86
Pagination — offset vs cursor, và tại sao page 500 chậm hơn page 1 đến 500 lần

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.

87
Database Migration — vì sao schema thay đổi mà không có migration là đang chơi với lửa

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.

88
Builder Pattern — khi constructor bắt đầu nhận 8 tham số

Constructor có 8 tham số không tên — đây là dấu hiệu Builder Pattern cần xuất hiện. Từ Java thuần đến Lombok @Builder, cách tổ chức object creation đúng trong Spring Boot.

89
Technical debt không xấu — xấu là không biết dùng nó

P01/Bài 06 trong series này nói về technical debt từ góc độ kỹ thuật — bạn đang nợ ai, nợ cái gì. Bài này nói về nó từ góc độ product: khi nào nên chủ động...

90
User không quan tâm code của bạn — họ quan tâm trải nghiệm

Mình từng rất tự hào về một cái booking flow mình viết. Clean architecture đúng sách, separation of concerns rõ ràng, unit test coverage cao, zero redun...

91
Hiểu sản phẩm kiếm tiền thế nào để viết code tốt hơn

Developer hiểu business model sẽ ưu tiên đúng hơn, estimate chính xác hơn, và đưa ra đề xuất kỹ thuật có business context. Đó là một bước phát triển quan trọng trong tư duy kỹ thuật.

92
Tính năng không tạo giá trị = tính năng vô nghĩa

Mỗi tính năng là gánh nặng: cần maintain, cần test, cần document, cần support. Tính năng tốt nhất đôi khi là tính năng bạn quyết định không build.

93
Làm ít hơn nhưng đúng hơn — MVP thinking

MVP không phải là product tệ. MVP là version nhỏ nhất có thể validate được hypothesis. Build đúng thứ trước khi build đúng cách.

94
90% dev giải sai vấn đề vì nhảy vào code quá sớm

"Chúng ta cần cache" — thực ra là query đang thiếu index. "Cần thêm field vào DB" — thực ra là UI đang hiển thị sai. Hiểu đúng vấn đề trước khi giải nó.

95
Dev làm task. Product engineer hỏi tại sao task này tồn tại

Developer implement ticket. Product-minded engineer hỏi: ticket này giải quyết vấn đề gì, có cách nào đơn giản hơn không, và đây có phải vấn đề đúng cần giải quyết không?

96
Behind the feature — nút "Book Appointment" ẩn chứa bao nhiêu hệ thống phía sau

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.

97
Một cái tên đặt sai gây ra bug production

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.

98
Tại sao user thấy data cũ dù đã update — cache consistency trong thực tế

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ế.

99
Keycloak revert fail — compensation pattern và cái giá của distributed state

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.

100
Doctor chỉ thấy bệnh nhân của mình — ABAC implement đúng chỗ hay sai chỗ

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.

101
Notification gửi trước khi transaction commit — bug thầm lặng nhất

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.

102
User bấm thanh toán 2 lần — idempotency key hoạt động ra sao

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.

103
2 người đặt lịch cùng 1 slot — Redis Lua atomic giải quyết thế nào

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.

104
More layers không phải lúc nào cũng là design tốt hơn

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ó.

105
Scaling sớm không phải lúc nào cũng là quyết định đúng

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.

106
Queue không phải lúc nào cũng làm hệ thống ổn định hơn

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.

107
Clean Architecture không phải lúc nào cũng tốt

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.

108
DRY không phải lúc nào cũng là best practice

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.

109
Cache không phải lúc nào cũng làm hệ thống nhanh hơn

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.

110
NoSQL không scale tốt hơn SQL — đó là cú lừa hoàn hảo

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.

111
Microservices không phải phép màu giúp bạn scale

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.

112
CAP Theorem — ba thứ không thể có cùng lúc

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.

113
Circuit Breaker — vì sao một service chết mà cả hệ thống không sập theo

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.

114
Rate Limiting — hàng rào bảo vệ API khỏi spam và DDoS

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.

115
Message Queue — khi nào cần, khi nào không

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.

116
Load Balancer — bí mật giúp hệ thống chịu hàng chục nghìn request/giây

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.

117
API Design — vì sao 70% lỗi hệ thống bắt nguồn từ API tệ

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.

118
Monolith vs Microservices — chọn sai kiến trúc là đốt cả năm

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.

119
Functional vs Non-functional Requirements — hiểu sai là thiết kế sai

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.

120
System Design là gì — và tại sao code giỏi vẫn làm hệ thống sập

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.

121
Queue — vì sao không phải lúc nào cũng xử lý request ngay lập tức

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.

122
Concurrency — khi nhiều request cùng chạm một tài nguyê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.

123
Cache Stampede — khi cache sập gây sập cả hệ thống

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.

124
Caching — vì sao server không query database mỗi lần

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.

125
Blocking vs Non-Blocking — thread đang làm gì khi chờ I/O

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.

126
Thread per Request vs Event Loop — Spring Boot đang chọn gì và tại sao

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.

127
Request đi qua server như thế nào — từ TCP đến response

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.

128
Connection Pool — vì sao hàng ngàn request chỉ cần vài chục connection

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.

129
Soft Delete — đơn giản hơn bạn nghĩ, và phức tạp hơn bạn tưởng

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.

130
SQL vs NoSQL — chọn sai là refactor cả đời

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.

131
Normalization vs Denormalization — chuẩn hóa bao nhiêu là đủ?

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ế.

132
N+1 Query — bug thầm lặng giết performance từ 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.

133
Deadlock — vì sao database tự kill query của bạn

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.

134
Isolation Levels — bốn cấp độ và khi nào bạn cần cái nào

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.

135
MVCC — vì sao read không block write

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.

136
Query Execution Plan — database đang làm gì sau cánh gà

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.

137
Index là gì — và tại sao tạo index rồi query vẫn chậm?

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.

138
Review không chỉ là đọc code — mà là hiểu change

Khi review PR, người có kinh nghiệm không chỉ đọc code — họ hỏi: change này có đúng direction không? Có tạo ra dependency mới không? Có làm hệ thống khó thay đổi hơn không?

139
Command Pattern — khi hành vi cần được điều phối

Command đóng gói một yêu cầu thành object — cho phép queue, log, undo, và retry. Đây là nền tảng của event sourcing và nhiều hệ thống phức tạp.

140
Proxy — không phải object nào cũng nên được truy cập trực tiếp

Proxy kiểm soát access đến object thật. Logging, caching, security check, lazy loading — tất cả đều có thể implement qua Proxy mà không sửa code gốc.

141
Decorator — kế thừa không sai, sai là bạn dùng nó để mở rộng hành vi

Kế thừa mở rộng là static và compile-time. Decorator mở rộng là dynamic và runtime. Khi behavior cần thay đổi linh hoạt — Decorator là lựa chọn đúng.

142
Facade — tại sao KeycloakService tồn tại thay vì gọi thẳng

Facade đơn giản hóa interface phức tạp. Thay vì để mọi nơi gọi Keycloak SDK trực tiếp — một Facade che đi sự phức tạp và là điểm thay đổi duy nhất.

143
Observer — tại sao notification không được gọi trong transaction

Observer Pattern giải coupling giữa event publisher và subscriber. Nhưng gọi notification bên trong database transaction là một trong những lỗi kiến trúc phổ biến nhất.

144
Strategy vs State — hành vi hay trạng thái đang thay đổi?

Strategy và State trông giống nhau về cấu trúc nhưng giải quyết vấn đề khác nhau. Hiểu sai là dùng sai — và code sẽ không express đúng intent.

145
Template Method — pattern bạn đang dùng hàng ngày mà không biết tên

Bất cứ khi nào bạn có một quy trình cố định nhưng các bước có thể thay đổi — Template Method đang ở đó. Abstract class trong Spring Boot là ví dụ điển hình.

146
Design Pattern không giúp bạn viết code tốt hơn — nếu bạn dùng nó sai

Pattern là giải pháp cho vấn đề đã biết — không phải template để áp vào mọi nơi. Dùng pattern không có vấn đề để giải quyết là over-engineering.

147
SOLID không làm code tốt hơn nếu bạn dùng sai thời điểm

Áp dụng SOLID quá sớm tạo ra over-engineering. Áp dụng quá muộn tạo ra debt. Biết khi nào cần dùng mới là kỹ năng thật sự.

148
DIP — business code mà phụ thuộc DB thì sớm muộn cũng khổ

Dependency Inversion: code cấp cao không phụ thuộc code cấp thấp. Cả hai phụ thuộc abstraction. Đây là nền tảng của Dependency Injection trong mọi framework.

149
ISP — interface càng to, code càng yếu

Interface Segregation: đừng ép class implement những method nó không cần. Interface phình to là dấu hiệu của coupling ẩn và design thiếu suy nghĩ.

150
LSP — kế thừa sai còn nguy hiểm hơn code xấu

Liskov Substitution Principle: subclass phải thay thế được superclass mà không làm hỏng chương trình. Vi phạm LSP là nguồn gốc của những bug khó tìm nhất.

151
OCP — mỗi lần thêm feature lại sửa code cũ là thiết kế đang sai

Open/Closed Principle: mở để mở rộng, đóng để sửa đổi. Khi thêm feature mới mà phải sửa code đang chạy tốt — đó là dấu hiệu cần abstraction.

152
SRP — một class ôm quá nhiều là mầm mống thảm họa

Single Responsibility không có nghĩa là class chỉ có một method. Có nghĩa là class chỉ thay đổi vì một lý do — một actor duy nhất yêu cầu nó thay đổi.

153
SOLID — code chạy được vẫn fail vì bạn chưa hiểu cái này

SOLID không phải checklist để tick vào. Đó là tư duy về cách tổ chức code để nó không sụp đổ khi requirement thay đổi.

154
Kiến trúc thường chết vì con người, không phải vì code

Kiến trúc đẹp trên giấy thường sụp đổ trong thực tế vì team không hiểu nó, không theo nó, hoặc không có ai enforce nó.

155
Microservices không phải level up tự động từ Monolith

Microservices giải quyết vấn đề của scale và team autonomy — không phải vấn đề của code xấu. Chuyển sang microservices khi chưa sẵn sàng là tự làm khổ mình.

156
Refactor kiến trúc không phải là viết lại — là chỉnh hướng dần dần

Big bang rewrite thất bại 90% thời gian. Kiến trúc tốt được xây dựng bằng cách di chuyển dần — từng boundary một — không phải bắt đầu từ đầu.

157
Framework là công cụ, không phải nền móng

Khi toàn bộ business logic phụ thuộc vào Spring Boot — bạn không có kiến trúc, bạn có một Spring Boot app. Và khi Spring thay đổi, mọi thứ thay đổi.

158
Fat Controller, Fat Service — dấu hiệu kiến trúc đang sai

Service 2000 dòng là triệu chứng, không phải bệnh. Bệnh là không có ranh giới rõ ràng giữa các responsibility trong hệ thống.

159
DTO vs Entity vs Domain Model — ba thứ khác nhau, và bạn cần cả ba

Dùng Entity làm DTO, hay Domain Model làm Entity — đây là sai lầm phổ biến nhất trong Spring Boot và nó phá vỡ boundary một cách thầm lặng.

160
Use Case mới là trái tim thật sự của hệ thống

Không phải database, không phải framework, không phải API. Use case — hành động người dùng thực hiện — là thứ hệ thống thật sự tồn tại để phục vụ.

161
Domain logic không được biết database tồn tại

Khi domain model import JPA annotation — kiến trúc đã bị vi phạm. Business rule không nên biết data được lưu ở đâu hay như thế nào.

162
Sai boundary một ly, hệ thống đi một dặm

Boundary là ranh giới giữa các phần của hệ thống. Vẽ sai một lần — mọi thứ build lên trên đó đều sai theo.

163
Business logic đặt sai chỗ — hệ thống sẽ trả giá

Business logic nằm trong Controller, trong SQL query, hay trong UI — đó là technical debt không thể tránh khỏi. Và nó tích lũy theo thời gian.

164
Layered Architecture không phải lúc nào cũng đúng

Layered architecture giải quyết một vấn đề cụ thể. Khi bạn dùng nó cho bài toán khác — bạn đang tạo ra vấn đề thay vì giải quyết nó.

165
Tại sao Controller/Service/Repository thối theo thời gian

Pattern 3 layer quen thuộc này không sai — nhưng nếu không hiểu đúng mục đích, nó sẽ biến thành nơi chứa mọi thứ và không ai dọn được.

166
Deadline dí không cho phép bạn viết code bừa — thực ra là sao?

Deadline không phải lý do để viết code xấu — đó là lý do để viết code đơn giản hơn. Hai thứ này khác nhau hoàn toàn.

167
Refactor là gì — và khi nào thì nên làm

Refactor không phải là viết lại, không phải là thêm feature. Đó là cải thiện cấu trúc code mà không thay đổi behavior — và có test để chứng minh.

168
Code không test được thì chưa bao giờ là Clean Code

Khả năng test được là thuộc tính thiết kế, không phải afterthought. Code khó test là code có quá nhiều coupling và quá nhiều responsibility.

169
Đừng nuốt lỗi — hệ thống sẽ trả giá thay bạn

catch (Exception e) {} là một trong những đoạn code nguy hiểm nhất có thể tồn tại. Lỗi bị ẩn đi, hệ thống tiếp tục chạy sai mà không ai hay.

170
Magic number — bug không có tên nhưng rất khó sửa

Con số 86400 xuất hiện ở 12 chỗ khác nhau trong codebase. Đó là 12 chỗ có thể sai khi business rule thay đổi — và không ai biết.

171
Boolean flag — kẻ phá hoại thầm lặng

Boolean parameter trong function signature là mùi code rõ ràng nhất. Nó làm function làm hai việc và caller không hiểu đang gọi cái gì.

172
Khi nào comment là dấu hiệu code đang có vấn đề

Comment không phải lúc nào cũng tốt. Khi bạn cần comment để giải thích code — thường đó là dấu hiệu code cần được viết lại.

173
Function làm "một việc" — nhưng "một việc" nghĩa là gì?

SRP nghe có vẻ đơn giản — nhưng hầu hết dev định nghĩa "một việc" sai. Đây là cách hiểu đúng để áp dụng thật sự.

174
Đặt tên biến là kỹ năng, không phải thói quen

Một cái tên tốt loại bỏ nhu cầu comment. Một cái tên xấu là nguồn gốc của mọi hiểu lầm trong codebase.

175
Clean Code không phải code hoàn hảo — là code sống sót qua thời gian

Clean Code không phải là code đẹp hay code ngắn. Đó là code mà người khác có thể đọc, hiểu, và thay đổi mà không cần hỏi tác giả.

176
Debug chậm không phải vì bạn dở — bạn đang debug sai cách

Debug không phải là đoán mò. Có một quy trình tư duy để tìm lỗi nhanh hơn — và hầu hết dev không ai dạy họ cách đó.

177
Complexity không chứng minh bạn giỏi — đơn giản mới chứng minh bạn hiểu sâu

Code phức tạp thường là dấu hiệu của sự chưa chín — không phải sự tinh tế. Người giỏi nhất thường viết code đơn giản nhất.

178
Tư duy Trade-off — không có giải pháp hoàn hảo, chỉ có lựa chọn phù hợp

Mọi quyết định kỹ thuật đều có cái giá. Kinh nghiệm giúp nhìn rõ hơn thứ đang đánh đổi và lý do của lựa chọn đó.

179
Tư duy Failure-first — thiết kế để không sập, không phải để chạy

Hệ thống tốt không phải là hệ thống không bao giờ lỗi — mà là hệ thống lỗi một cách có kiểm soát và tự phục hồi được.

180
Technical debt không xấu — xấu là bạn không biết mình đang nợ ai

Technical debt là công cụ, không phải tội lỗi. Vấn đề là khi bạn nợ mà không biết mình đang nợ — đến lúc trả giá mới hay.

181
Under-engineering — cái bẫy ít ai nói đến

Không phải lúc nào đơn giản cũng là đúng. Under-engineering tạo ra technical debt ngay từ ngày đầu mà không ai nhận ra.

182
Over-engineering — cái bẫy mà sinh viên hay sa vào nhất

Xây dựng hệ thống phức tạp cho bài toán đơn giản không phải là giỏi — đó là dấu hiệu bạn chưa hiểu vấn đề thật sự.

183
Câu hỏi trước khi viết dòng code đầu tiên

Viết code nhanh không phải là viết ngay. Dành thời gian hỏi đúng câu hỏi giúp giảm số lần phải sửa lại.

184
Từ feature đến change

Sự khác biệt không nằm ở kỹ thuật — mà ở cách nhìn vào một yêu cầu: bạn đang giải quyết hôm nay hay đang thiết kế cho tương lai?

185
Code chạy được vẫn bị reject — và đây là lý do

Viết code pass test chưa đủ. Một PR có thể bị từ chối không phải vì code sai — mà vì code không thể sống cùng hệ thống theo thời gian.