01 Mục tiêu của kiến trúc
Mục tiêu của kiến trúc phần mềm là tối thiểu hóa nhân lực cần để xây dựng và bảo trì hệ thống.
Robert C. MartinKiến trúc không phải là những chiếc hộp đẹp trên slide. Nó là một mục tiêu đo được: nỗ lực (effort) bỏ ra để đáp ứng nhu cầu khách hàng. Nỗ lực thấp và giữ ở mức thấp suốt vòng đời ⟶ thiết kế tốt. Nỗ lực phình to sau mỗi bản phát hành ⟶ thiết kế tồi. Đơn giản vậy thôi.
Thước đo chất lượng thiết kế (measure of design quality) = nỗ lực để đáp ứng nhu cầu khách hàng. Không phải số dòng code, không phải số tính năng — mà là chi phí của thay đổi tiếp theo.
Design vs Architecture: không có ranh giới
Người ta hay tách "architecture" (cấp cao, trừu tượng) khỏi "design" (chi tiết cấp thấp). Martin khẳng định: không có khác biệt nào cả. Chỉ có một thể liên tục các quyết định (continuum of decisions) từ cao nhất tới thấp nhất — và chi tiết cấp thấp cũng quan trọng y như cấu trúc cấp cao.
flowchart LR A["Quyết định cấp cao
(cấu trúc hệ thống)"]:::hi --> B["…"]:::mid --> C["…"]:::mid --> D["Chi tiết cấp thấp
(tên biến, một hàm)"]:::lo classDef hi fill:#dbeee8,stroke:#0f7d72,color:#1c1a14; classDef mid fill:#eef4f1,stroke:#8c8675,color:#1c1a14; classDef lo fill:#e2edf3,stroke:#2f6d93,color:#1c1a14;
02 Dấu hiệu của một mớ hỗn độn
Khi hệ thống bị quăng ra cho kịp tiến độ, khi số lượng lập trình viên là động lực duy nhất của sản lượng, và không ai bận tâm tới độ sạch của code — bạn sẽ cưỡi đúng đường cong dưới đây tới hồi kết xấu xí. Đây là dữ liệu thật từ một công ty thật (the signature of a mess).
Nhân sự ↑
Tăng vọt qua các giai đoạn, lên tới ~1.200 người ở release 8.
Chi phí/dòng ↑
Ở release 8, mỗi dòng code đắt gấp 40 lần so với release 1.
Lương tháng → vô ích
Quỹ lương chạm 20 triệu USD/tháng nhưng gần như không mua thêm được tính năng nào.
Bất kỳ CFO nào nhìn hai đường "lương tăng vọt" và "sản lượng đi ngang" cũng biết: phải hành động ngay, nếu không các đường cong này sẽ rút cạn lợi nhuận và đẩy công ty tới chỗ đình trệ.
03 Rùa, Thỏ & lời nói dối quen thuộc
Vì sao năng suất sụp đổ? Vì lập trình viên hiện đại chạy đua với một sự tự tin thái quá (overconfidence) giống hệt chú Thỏ. Họ không lười — họ làm quần quật — nhưng phần não biết rằng code sạch mới quan trọng thì đang ngủ.
"Cứ làm cho kịp ra mắt đã, dọn dẹp sau cũng được!" — Tất nhiên, chẳng bao giờ có cái "sau" đó, vì áp lực thị trường không bao giờ ngơi nghỉ.
The familiar lie · Clean ArchitectureNiềm tin của chú Thỏ
- "Code ẩu giúp đi nhanh hơn."
- "Ra thị trường trước đã, dọn sau."
- "Viết lại từ đầu sẽ tốt hơn." — vẫn là chú Thỏ đang nói.
Sự thật của chú Rùa
- Tạo mớ hỗn độn luôn CHẬM hơn giữ code sạch — ở mọi thời điểm.
- Cách duy nhất để đi nhanh là làm cho tốt (the only way to go fast is to go well).
- Thí nghiệm của Jason Gorman: kỷ luật TDD nhanh hơn ~10%, ngày TDD chậm nhất vẫn nhanh hơn ngày không-TDD nhanh nhất.
Cái bẫy "viết lại từ đầu" cũng chính là chú Thỏ: cùng một sự tự tin đã tạo ra mớ hỗn độn giờ lại thì thầm rằng "lần này tôi sẽ làm tốt hơn nếu được bắt đầu lại". Thực tế ít màu hồng hơn nhiều.
04 Hai giá trị: Hành vi vs Cấu trúc
Mọi hệ thống đều mang lại cho stakeholder hai giá trị khác nhau. Bi kịch là lập trình viên thường dồn sức cho cái kém quan trọng hơn, khiến phần mềm cuối cùng… mất sạch giá trị.
Giá trị 1 · Hành vi
Làm máy chạy đúng yêu cầu (requirements) để tạo ra hoặc tiết kiệm tiền. Đây là việc đa số lập trình viên nghĩ là "toàn bộ công việc" — và họ nhầm.
Giá trị 2 · Cấu trúc
Phần mềm gọi là "soft"-ware vì nó sinh ra để dễ thay đổi. Nếu khó đổi hành vi, lẽ ra nên gọi nó là "hardware".
Vì sao Cấu trúc quan trọng hơn về dài hạn
| Tình huống | Hệ quả | Còn cứu được? |
|---|---|---|
| Chạy đúng nhưng không thể sửa | Khi yêu cầu đổi, không theo kịp → trở nên vô dụng. | không |
| Chạy chưa đúng nhưng dễ sửa | Có thể sửa cho chạy đúng và tiếp tục hữu dụng. | được |
Độ khó của một thay đổi nên tỉ lệ với quy mô (scope) của nó, chứ không phải hình dạng (shape). Khi chi phí phình theo shape, đó là lúc kiến trúc tồi bắt đầu rút tiền — năm thứ hai đắt hơn năm đầu, năm thứ ba đắt hơn nữa.
"soft"-ware qua lăng kính RAG
RAG Phụ thuộc nhà cung cấp LLM nằm rải khắp pipeline = "hardware" (đổi shape là sửa khắp nơi). Đẩy nó sau một cổng trừu tượng = đổi nhà cung cấp chỉ còn là thay đổi theo scope:
# ✗ Hành vi đúng, nhưng cứng — đổi LLM = sửa hàng chục chỗ
def answer(q):
ctx = retrieve(q)
return openai.chat(model="gpt-4o", messages=build(ctx, q)) # gọi thẳng, rải khắp
# ✓ Dễ thay đổi — hành vi sau một cổng; đổi nhà cung cấp = 1 lớp
class LLM(Protocol):
def generate(self, prompt: str) -> str: ...
def answer(q, llm: LLM):
return llm.generate(build(retrieve(q), q)) # scope đổi, shape không đổi
05 Ma trận Eisenhower & cuộc đấu tranh
Hành vi thì khẩn (urgent) nhưng không phải lúc nào cũng quan trọng. Kiến trúc thì luôn quan trọng (important) nhưng hiếm khi khẩn. Tổng thống Eisenhower: "Cái khẩn thì hiếm khi quan trọng, cái quan trọng thì hiếm khi khẩn."
Đấu tranh cho kiến trúc
Business manager không đủ chuyên môn để đánh giá tầm quan trọng của kiến trúc — đó chính là việc bạn được thuê để làm. Vậy nên đội phát triển phải "đấu tranh cho kiến trúc" (fight for the architecture) như một stakeholder thực thụ, ngang hàng với marketing, sales, vận hành.
Đây là một cuộc vật lộn (struggle) — và đó là cách mọi việc luôn diễn ra. Nếu để kiến trúc xếp cuối, hệ thống sẽ sớm quá đắt để phát triển, thay đổi trở nên bất khả thi. Khi đó nghĩa là: đội phát triển đã không đấu tranh đủ mạnh cho điều họ biết là cần thiết.
06 Ghi nhớ nhanh
Mục tiêu kiến trúc = tối thiểu hóa nhân lực để xây & bảo trì. Thước đo = nỗ lực cho thay đổi tiếp theo, không phải số tính năng.
Cách duy nhất để đi nhanh là làm cho tốt. Tạo mớ hỗn độn luôn chậm hơn giữ code sạch — ở mọi thời điểm.
Giá trị thật của phần mềm nằm ở khả năng dễ thay đổi — chương trình chạy đúng mà không sửa được rồi sẽ vô dụng; chương trình dễ sửa thì còn cứu được.
Đừng để cái "khẩn nhưng không quan trọng" lấn cái "quan trọng nhưng không khẩn". Kiến trúc luôn ở nhóm quan trọng.
Bạn là một stakeholder. Hãy đấu tranh cho kiến trúc — nếu nó xếp cuối, hệ thống sẽ đắt tới mức không thể đổi.