Tất cả bài viết
01
Visitor — khi mày 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ũ — mày chỉ được chọn một. Visitor giải nửa bài đó.

visitordesign-patterndouble-dispatchjava
02
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ữ.

iteratordesign-patternjavacollections
03
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ĩ.

dark-modecssuxfrontend
04
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ó. Mày đang dùng pattern này mỗi khi gửi request qua Spring Security.

chain-of-responsibilitydesign-patternspring-securityfilter
05
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.

compositedesign-patterntree-structurejava
06
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.

lighthouseweb-vitalsperformancelcp
07
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.

javascriptbundlingwebpackvite
08
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.

performanceimageswebpavif
09
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.

adapterdesign-patternintegrationspring
10
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.

distributed-tracingmicrometerzipkinobservability
11
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?

builderdesign-patternlombokjava
12
Factory — khi mày 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.

factoryabstract-factorydesign-patternspring
13
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ể. Junior dùng nó để giải quyết mọi thứ — và đó là lý do nó nằm trong danh sách anti-pattern.

singletondesign-patternspringdependency-injection
14
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.

nginxreverse-proxyhttpsproduction
15
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 junior vào on-call rotation.

productionon-callloggingincident
16
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.

databaseflywaymigrationdeploy
17
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.

productionkuberneteshealthdeploy
18
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.

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

springasynctransactionconcurrency
20
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.

apirestversioningspring
21
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.

productionon-callobservabilitysre
22
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.

productiondeploykuberneteszero-downtime
23
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.

postmortemincidentproductionon-call
24
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ở.

stagingdeploymentenvironmenttesting
25
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.

productionfeature-flagdeploytrunk-based
26
@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.

databasetransactionspringjpa
27
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.

clean-codespring-bootsecurityconfiguration
28
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 — và senior reject vì lý do đó.

clean-codejavaoptionalnull
29
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.

clean-codetestingintegration-testspring-boot
30
Logging đúng cách — vì sao log của mày đ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.

clean-codeloggingdebuggingobservability
31
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.

design-patternsbuilderjavalombok
32
Technical debt không xấu — xấu là không biết dùng nó

Bài 6 trong series này nói về technical debt từ góc độ kỹ thuật — mày đ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...

33
User không quan tâm code của mày — họ quan tâm trải nghiệm

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

34
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. Đây là thứ phân biệt senior và staff engineer.

product-thinkingbusinesssenior
35
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.

product-thinkingfeature-planningengineering
36
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.

product-thinkingMVPstartup
37
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ó.

product-thinkingproblem-solvingmindset
38
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?

product-thinkingmindsetengineering
39
Senior không review code — senior review change

Khi review PR, senior 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?

mindsetcode-reviewsenior
40
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.

design-patternscommandevent-sourcing
41
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.

design-patternsproxyAOP
42
Decorator — kế thừa không sai, sai là mày 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.

design-patternsdecoratorOOP
43
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.

design-patternsfacadeabstraction
44
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.

design-patternsobserverevents
45
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.

design-patternsstrategystate
46
Template Method — pattern mày đ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.

design-patternstemplate-methodOOP
47
Design Pattern không giúp mày viết code tốt hơn — nếu mày 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.

design-patternsmindsetover-engineering
48
SOLID không làm code tốt hơn nếu mày 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ự.

SOLIDmindsetover-engineering
49
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.

SOLIDDIPdependency-injection
50
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ĩ.

SOLIDISPinterface
51
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.

SOLIDLSPinheritance
52
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.

SOLIDOCPOOP
53
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.

SOLIDSRPOOP
54
SOLID — code chạy được vẫn fail vì mày 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.

SOLIDOOPdesign-principles
55
Deadline dí không cho phép mày 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.

mindsetdeadlinetechnical-debt
56
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.

refactoringclean-codebest-practices
57
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.

clean-codetestingtestability
58
Đừng nuốt lỗi — hệ thống sẽ trả giá thay mày

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.

clean-codeerror-handlingexceptions
59
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.

clean-codemagic-numbersconstants
60
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ì.

clean-codebooleananti-patterns
61
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.

clean-codecommentsreadability
62
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ự.

clean-codeSRPfunctions
63
Đặ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.

clean-codenamingreadability
64
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ả.

clean-codemaintainabilityreadability
65
Debug chậm không phải vì mày dở — mày đ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 đó.

debuggingmindsetproductivity
66
Complexity không chứng minh mày giỏi — đơn giản mới chứng minh mày 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.

mindsetsimplicityclean-code
67
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á. Kỹ năng của senior không phải là tìm giải pháp hoàn hảo — mà là chọn đúng cái phải đánh đổi.

mindsettrade-offdecision-making
68
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.

mindsetresiliencefailure-first
69
Technical debt không xấu — xấu là mày 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.

mindsettechnical-debttrade-off
70
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.

mindsetunder-engineeringtechnical-debt
71
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ự.

mindsetover-engineeringYAGNI
72
Câu hỏi senior 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. Senior dành thời gian hỏi đúng câu hỏi — và đó là lý do code của họ ít phải sửa hơn.

mindsetseniorrequirements
73
Junior nghĩ về feature. Senior nghĩ về 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?

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

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

mindsetcode-reviewclean-code