Đo lường kiến trúc: Dùng Metrics nào để biết kiến trúc của bạn đang \"khỏe\" hay \"bệnh\"?
7/19/2026 · 13p đọc
title: "Đo lường kiến trúc: Dùng Metrics nào để biết kiến trúc của bạn đang "khỏe" hay "bệnh"?"
series: "Software Architecture Masterclass"
part: "Phần 1 — Nguyên lý & Triết lý Kiến trúc"
order: 8
audience: "Solutions Architect & Technical Lead"
reading_time: "13 phút"
tags: ["architecture metrics", "fitness function", "coupling", "cyclomatic complexity", "DORA metrics", "engineering health"]
Đo lường kiến trúc: Dùng Metrics nào để biết kiến trúc của bạn đang "khỏe" hay "bệnh"?
Hỏi bất kỳ Tech Lead nào "kiến trúc hệ thống của anh đang khỏe không?", câu trả lời gần như luôn là một tính từ: "ổn", "hơi rối", "tạm được, cần refactor". Không ai trả lời bằng con số. Đó chính là vấn đề — sức khỏe kiến trúc đang được đánh giá bằng cảm giác của người nói to nhất trong phòng, chứ không phải bằng dữ liệu.
Sự cảm tính này không miễn phí. Nó có nghĩa là quyết định "có nên đầu tư refactor module này không" được đưa ra dựa trên ai bực mình nhất tuần trước, hoặc ai vừa gặp một bug khó chịu. Nó có nghĩa là ban lãnh đạo không có cách nào để so sánh "sức khỏe" giữa hai team, hai service, hai giai đoạn của cùng một hệ thống. Và tệ nhất — nó có nghĩa là không ai biết mức độ nghiêm trọng thực sự cho tới khi một cuộc khủng hoảng buộc phải biết: một deploy mất 6 tiếng để rollback, một thay đổi tưởng chừng nhỏ ở module A lại làm sập module F mà không ai hiểu vì sao chúng liên quan.
Bài này bàn về cách biến "sức khỏe kiến trúc" từ một nhận định chính trị thành một tập hợp con số có thể theo dõi, so sánh, và tranh luận dựa trên bằng chứng — cùng với cái giá phải trả khi làm điều đó, vì đo lường không bao giờ miễn phí và luôn có thể bị lợi dụng sai mục đích.
The Problem
Hình dung một cuộc họp roadmap quý. Team Platform xin 3 sprint để "dọn dẹp kỹ thuật" trong module Billing. Product Manager hỏi: "Cụ thể nó tệ tới đâu? Tệ hơn Billing quý trước hay tệ hơn module Order?" Câu trả lời thường là im lặng, hoặc một câu chuyện giai thoại: "tuần trước Minh sửa một dòng code mà mất nguyên ngày để hiểu tại sao 4 service khác bị ảnh hưởng."
Vấn đề không phải là câu chuyện đó sai — nó thường đúng. Vấn đề là nó không thể tổng hợp, không thể so sánh theo thời gian, và không thể dùng để ưu tiên đầu tư một cách khách quan giữa nhiều lựa chọn cạnh tranh nhau về ngân sách kỹ thuật.
Hệ quả trực tiếp của việc thiếu số liệu là "kiến trúc suy thoái trong im lặng" (silent architectural decay). Mỗi PR nhỏ, mỗi shortcut, mỗi "tạm thời cho kịp deadline" đều hợp lý cục bộ. Không ai đơn lẻ làm điều gì "sai". Nhưng cộng dồn qua 18 tháng, coupling giữa các module tăng dần, cyclomatic complexity của các hàm xử lý nghiệp vụ trọng yếu leo thang, thời gian để deploy một thay đổi nhỏ kéo dài ra — và không ai nhận ra cho tới khi một sự cố sản xuất buộc cả team phải dừng lại và hỏi: "làm sao chúng ta lại tới đây?"
Nghịch lý là: các đội ngũ phần mềm đã quen với việc đo lường chất lượng code ở cấp độ vi mô — code coverage, số lượng lint warning, số bug mở. Nhưng ở cấp độ kiến trúc — cấp độ của cấu trúc module, ranh giới service, luồng phụ thuộc — phần lớn tổ chức không đo gì cả. Đây là một khoảng trống có thể lấp được, và cần được lấp trước khi khủng hoảng buộc phải lấp nó trong hoảng loạn.
The Theory
Khái niệm trung tâm ở đây là architecture fitness function — một thuật ngữ được Neal Ford và Rebecca Parsons phổ biến, mô tả bất kỳ cơ chế nào cung cấp đánh giá khách quan về một đặc tính kiến trúc (architectural characteristic) mà bạn quan tâm. Ý tưởng cốt lõi: kiến trúc không phải một trạng thái tĩnh được thiết kế một lần rồi để đó — nó là một hệ thống tiến hóa (evolutionary architecture), và giống mọi hệ thống tiến hóa, nó cần một hàm fitness để biết mình đang tiến hóa theo hướng tốt hơn hay tệ hơn.
Có bốn nhóm chỉ số thực dụng nhất để bắt đầu:
1. Coupling metrics — đo mức độ phụ thuộc lẫn nhau. Hai chỉ số kinh điển từ Robert C. Martin là:
- Afferent coupling (Ca) — số lượng module/class bên ngoài phụ thuộc VÀO module này. Ca cao nghĩa là module này là một "điểm trung tâm" — thay đổi nó rủi ro lan rộng.
- Efferent coupling (Ce) — số lượng module/class BÊN NGOÀI mà module này phụ thuộc vào. Ce cao nghĩa là module này dễ vỡ khi các phụ thuộc bên ngoài thay đổi.
Từ hai số này suy ra Instability (I = Ce / (Ca + Ce)) — một module có I gần 1 là "bất ổn" (dễ thay đổi nhưng ít ai phụ thuộc vào nó — an toàn để sửa), I gần 0 là "ổn định" (nhiều thứ phụ thuộc vào nó — sửa phải cẩn trọng). Vấn đề kiến trúc kinh điển là khi một module vừa có Ca cao vừa có Ce cao — nó vừa bị phụ thuộc nhiều, vừa phụ thuộc nhiều thứ khác. Đó là điểm nghẽn (bottleneck) và cũng là điểm dễ vỡ nhất trong toàn hệ thống.
2. Cyclomatic complexity — đo độ phức tạp logic. Chỉ số này đếm số đường đi độc lập qua một hàm/phương thức (dựa trên số lượng nhánh rẽ: if, for, switch, case...). Một hàm có cyclomatic complexity 25 không chỉ khó đọc — nó khó test đầy đủ (bạn cần tối thiểu 25 test case để phủ hết các nhánh), và mỗi lần sửa đều có xác suất cao gây ra hồi quy (regression) ở một nhánh không ai nghĩ tới. Ở cấp module, cyclomatic complexity trung bình tăng dần theo thời gian là dấu hiệu sớm nhất của "big ball of mud".
3. DORA metrics — đo khả năng vận hành, không đo cấu trúc code. Đây là bốn chỉ số được nghiên cứu DevOps Research and Assessment xác định là có tương quan mạnh nhất với hiệu suất tổ chức kỹ thuật:
- Deployment Frequency — tần suất deploy ra production.
- Lead Time for Changes — thời gian từ commit tới khi chạy trên production.
- Mean Time to Recovery (MTTR) — thời gian trung bình để khôi phục sau sự cố.
- Change Failure Rate — tỷ lệ phần trăm deploy gây ra sự cố cần khắc phục khẩn cấp.
Điểm khác biệt quan trọng: coupling và cyclomatic complexity đo cấu trúc (structure) — tĩnh, đo được từ mã nguồn. DORA đo hành vi vận hành (operability) — động, đo được từ quy trình thực tế. Một kiến trúc có thể có coupling thấp về mặt lý thuyết nhưng DORA tệ (vì quy trình CI/CD, testing, hoặc văn hóa release kém) — và ngược lại. Sức khỏe kiến trúc thực sự chỉ hiện ra khi nhìn cả hai trục cùng lúc.
4. Change coupling (logical coupling) — một chỉ số ít được nói tới nhưng cực kỳ hữu ích: đo tần suất hai file/module bị sửa CÙNG NHAU trong cùng một commit, dù về mặt cấu trúc chúng không hề import lẫn nhau. Đây là "coupling ẩn" — dấu hiệu của domain logic bị rò rỉ xuyên module, thường là chỉ báo sớm nhất cho thấy ranh giới bounded context (xem Bài 10) đã bị vẽ sai.
The Trade-off
Đây là phần quan trọng nhất, và cũng là phần dễ bị nói chung chung nhất — nên phải cụ thể.
Được gì khi đầu tư đo lường kiến trúc:
- Bạn có bằng chứng khách quan để ưu tiên ngân sách kỹ thuật. Thay vì "tôi cảm thấy module X tệ", bạn nói "module X có afferent coupling gấp 4 lần trung bình team, cyclomatic complexity trung bình hàm là 18, và change failure rate của module này cao gấp đôi phần còn lại hệ thống." Đó là một câu chuyện thuyết phục được cả kỹ sư lẫn người không phải kỹ sư.
- Bạn phát hiện suy thoái SỚM, trước khủng hoảng — xu hướng tăng dần của coupling hoặc complexity là tín hiệu cảnh báo sớm, giống như đường huyết tăng dần trước khi tiểu đường phát bệnh.
- Bạn có cơ sở để so sánh trước/sau khi refactor — chứng minh được giá trị của công sức tái cấu trúc, thứ vốn rất khó bán cho lãnh đạo vì nó không tạo ra tính năng mới nhìn thấy được.
Mất gì — cái giá cụ thể, không né tránh:
- Chi phí công cụ và quy trình thật sự tốn kém. Đo cyclomatic complexity và coupling cần tích hợp công cụ phân tích tĩnh (static analysis) vào CI pipeline, cần dashboard để trực quan hóa xu hướng, cần ai đó bảo trì baseline và ngưỡng cảnh báo. Đo DORA metrics cần instrument hóa toàn bộ pipeline deploy và incident management — không phải chỉ bật một plugin là xong. Đây là công sức kỹ thuật thật, tính bằng sprint, không phải một dòng cấu hình.
- Con số dễ đo không phải lúc nào cũng là con số quan trọng. Cyclomatic complexity đo được dễ dàng nhưng không capture được complexity ngữ nghĩa (một hàm 3 dòng gọi một domain logic mơ hồ có thể tệ hơn một hàm 40 dòng rõ ràng). Đo cái dễ đo mà bỏ qua cái khó đo là một cái bẫy phổ biến.
- Goodhart's Law: khi một thước đo trở thành mục tiêu, nó ngừng là thước đo tốt. Đây là rủi ro nghiêm trọng nhất. Nếu bạn dùng cyclomatic complexity để đánh giá hiệu suất cá nhân từng kỹ sư, họ sẽ học cách "chơi" con số: chia nhỏ hàm một cách máy móc không vì lý do thiết kế mà chỉ để hạ điểm complexity, hoặc che giấu logic phức tạp bằng cách đẩy nó vào những lớp gián tiếp khó theo dõi hơn — kết quả là code "đẹp điểm" nhưng khó hiểu hơn trước. Tương tự, nếu Change Failure Rate được dùng để đánh giá team, team sẽ giảm tần suất deploy để giảm rủi ro tính toán — vô tình phá hỏng chính chỉ số Deployment Frequency mà bạn cũng đang muốn cải thiện, vì bốn chỉ số DORA vốn có sự đánh đổi lẫn nhau nếu tối ưu riêng lẻ.
- Đo lường tạo ra ảo giác về sự khách quan tuyệt đối. Một dashboard đầy con số xanh đỏ trông rất khoa học, nhưng nếu ngưỡng (threshold) được chọn tùy tiện hoặc benchmark không phù hợp với domain của bạn, nó tạo ra sự tự tin sai lầm — tệ hơn cả việc không đo gì, vì giờ bạn có bằng chứng giả để bảo vệ cho một quyết định sai.
Tóm gọn: đo lường kiến trúc là một khoản đầu tư có ROI thật, nhưng ROI đó chỉ hiện thực nếu bạn kỷ luật dùng nó để cải thiện hệ thống — chứ không phải để chấm điểm con người.
The Architecture Diagram
Sơ đồ dưới đây minh họa một dashboard đo lường sức khỏe kiến trúc điển hình — kết hợp cả trục cấu trúc (structure) và trục vận hành (operability), cùng luồng dữ liệu từ nguồn tới nơi ra quyết định.
flowchart TB
subgraph Sources["Nguồn dữ liệu"]
SC["Source Code Repo\n(git history, AST)"]
CI["CI/CD Pipeline\n(build, deploy events)"]
INC["Incident Management\n(alerts, postmortems)"]
end
subgraph Analysis["Tầng phân tích"]
SA["Static Analysis Engine\n→ Cyclomatic Complexity\n→ Afferent/Efferent Coupling"]
CC["Change Coupling Analyzer\n→ Co-change frequency giữa files"]
DA["DORA Aggregator\n→ Deploy Freq, Lead Time,\n MTTR, Change Failure Rate"]
end
subgraph Dashboard["Architecture Health Dashboard"]
D1["🟡 Coupling Hotspots\n(module Billing: Ca=42, I=0.18)"]
D2["🔴 Complexity Trend\n(OrderService: CC trung bình\n12 → 19 trong 6 tháng)"]
D3["🟢 DORA Snapshot\n(Deploy Freq: 8/ngày\nMTTR: 35 phút)"]
D4["📈 Trend View\n(xu hướng theo sprint,\nKHÔNG phải bảng xếp hạng cá nhân)"]
end
subgraph Action["Vòng phản hồi"]
REVIEW["Architecture Review\n(team-level, định kỳ)"]
BACKLOG["Technical Debt Backlog\n(ưu tiên theo dữ liệu)"]
end
SC --> SA
SC --> CC
CI --> DA
INC --> DA
SA --> D1
SA --> D2
CC --> D1
DA --> D3
D1 --> D4
D2 --> D4
D3 --> D4
D4 --> REVIEW
REVIEW --> BACKLOG
BACKLOG -.->|Refactor tác động\nvào code| SC
Điểm mấu chốt của sơ đồ này không phải các con số cụ thể — mà là vòng phản hồi (feedback loop) khép kín ở dưới cùng: dữ liệu đo lường phải chảy tới một buổi review cấp team, rồi tới backlog nợ kỹ thuật có ưu tiên rõ ràng, rồi tác động ngược lại vào code. Một dashboard không có vòng phản hồi này chỉ là trang trí.
Implementation Guardrails
Đo và trình bày theo XU HƯỚNG (trend), không phải theo con số tuyệt đối tại một thời điểm. Một module có cyclomatic complexity 15 không tự thân là "tệ" — nó tệ nếu tăng từ 8 lên 15 trong hai tháng mà không ai để ý. Dashboard phải mặc định hiển thị biểu đồ theo thời gian (time series), không phải một con số tĩnh, vì bản chất của "sức khỏe" là một quá trình, không phải một trạng thái.
Review kiến trúc theo TEAM/MODULE, tuyệt đối không gắn bất kỳ chỉ số nào ở đây vào đánh giá hiệu suất cá nhân. Đây là guardrail cứng nhất vì nó chống lại Goodhart's Law trực tiếp. Nếu một kỹ sư biết complexity của hàm họ viết sẽ ảnh hưởng tới performance review cá nhân, họ sẽ tối ưu con số thay vì tối ưu thiết kế. Chỉ số này thuộc về cấp hệ thống/team, dùng để ra quyết định đầu tư, không phải để chấm điểm người.
Không dùng một chỉ số đơn lẻ để ra quyết định lớn — luôn đối chiếu chéo giữa trục cấu trúc và trục vận hành. Một module có coupling thấp nhưng Change Failure Rate cao có vấn đề khác (thường là thiếu test, không phải thiếu cấu trúc). Ngược lại một module coupling cao nhưng DORA tốt có thể chấp nhận được tạm thời nếu team quản lý được rủi ro vận hành. Quyết định refactor chỉ nên đưa ra khi CẢ HAI trục cùng chỉ về một hướng xấu.
Đặt ngưỡng cảnh báo (threshold) theo baseline nội bộ của chính hệ thống bạn, không copy từ benchmark ngành. Cyclomatic complexity "chấp nhận được" cho một service xử lý business rule phức tạp (billing, pricing) khác hoàn toàn với một CRUD service đơn giản. Threshold generic từ sách vở hoặc benchmark ngành chỉ nên dùng làm điểm khởi đầu, sau đó phải hiệu chỉnh lại dựa trên dữ liệu lịch sử thực tế của chính codebase.
Architect's Verdict
Sức khỏe kiến trúc nên được đo liên tục, tự động, và hiển thị ở cùng mức độ ưu tiên như code coverage — không phải một hoạt động "khi nào rảnh thì làm health check". Nếu tổ chức của bạn chỉ nhìn vào các chỉ số này sau khi một sự cố nghiêm trọng xảy ra, bạn đang dùng đo lường kiến trúc như một khám nghiệm tử thi (post-mortem), trong khi giá trị thực sự của nó nằm ở vai trò một cuộc khám sức khỏe định kỳ (checkup). Con số không thay thế được phán đoán của kiến trúc sư dày dạn — nhưng nó biến phán đoán đó từ một ý kiến cá nhân thành một luận điểm có bằng chứng, thứ duy nhất đủ sức thắng trong một cuộc họp phân bổ ngân sách kỹ thuật.
🔗 Bài viết liên quan
- Cửa sổ thời gian của kiến trúc — vì sức khỏe kiến trúc luôn phải được đánh giá trong bối cảnh thời điểm, không phải một chuẩn tuyệt đối bất biến.
- Bounded Contexts — change coupling bất thường thường là dấu hiệu ranh giới bounded context đã vẽ sai chỗ.
Bài trước: Kiến trúc sư trong môi trường Agile · Bài tiếp theo: Tư duy hướng Domain (Domain-Driven Design)