Chương 5 là nhãn cảnh báo của cuốn sách. Khi những service đầu tiên đã chạy, một loạt vấn đề mới kéo đến, và chúng không đến cùng một lúc. Newman đi qua mười vấn đề: với mỗi vấn đề, nó thường biểu hiện ra sao, đại khái khi nào nó ập đến, và nên bắt đầu sửa từ đâu. Ông thẳng thắn rằng không thể giải quyết hết trong một chương và chỉ sang Building Microservices để đi sâu, nên chương này hiệu quả nhất khi dùng như một checklist mà bạn quay lại khi hệ thống lớn dần.
Phần lớn những nỗi đau này rơi vào các developer bình thường trước tiên: một consumer bị vỡ sau một lần deploy, một chiếc laptop không chạy nổi cả hệ thống, một bộ end-to-end test không ai tin. Sau đó, một đoạn coda dài hai trang nhắc lại hai thông điệp của cuốn sách.
Ứng với chương 5 và phần lời kết ở chương 6 trong sách.
Ý chính
Một núm vặn, không phải công tắc
Ý tưởng làm khung là việc áp dụng microservices giống như vặn một núm vặn (dial). Mỗi service thêm vào mang lại cho bạn nhiều lợi ích hơn và cũng khiến bạn lộ ra trước nhiều vấn đề mới hơn. Quan sát của Newman là các vấn đề mà một công ty có mười service gặp phải khá khác so với một công ty có hàng trăm service, nên số lượng service logic là một tín hiệu thô hợp lý cho biết nỗi đau nào sắp tới. Ông cẩn thận gọi điều này là dựa trên trải nghiệm cá nhân. Biến số ông nhấn mạnh là coupling (mức độ phụ thuộc): một kiến trúc coupling chặt sẽ gặp các vấn đề về robustness, testing và tracing sớm hơn một kiến trúc coupling lỏng.
Tóm tắt của riêng mình về mười vấn đề, rút ra từ những gì chương sách nói về từng cái:
| Vấn đề | Biểu hiện | Khi nào sách dự đoán nó xuất hiện |
|---|---|---|
| Ownership at scale | Không ai lái một service; gọi point-to-point khắp nơi | Tăng trưởng nhanh, đại khái vượt quá 20 developer ngồi cùng chỗ |
| Breaking changes | Sự cố ngay sau một lần deploy; lock-step release | Sớm, khi có hơn một team tham gia |
| Reporting | Analyst mất database SQL duy nhất của họ | Sớm, khi bạn bắt đầu tách schema |
| Monitoring and troubleshooting | Lỗi không giải thích được, alert bắn trong khi mọi thứ trông vẫn ổn | Khó đoán: vài service hoặc hai mươi |
| Local developer experience | Chạy local chậm, đòi laptop mạnh hơn | Tùy vào số service và footprint của runtime |
| Running too many things | Tốn nhiều thời gian hơn cho deploy, lỗi thao tác tay | Khi số service và instance tăng lên |
| End-to-end testing | Bộ test chậm, flaky; áp lực phải có một team test | Khi các hành trình người dùng đi qua nhiều team |
| Global vs local optimisation | Các team giải cùng một vấn đề theo những cách khác nhau | Tổ chức nhiều team, sau một giai đoạn scale |
| Robustness and resiliency | Lỗi hiếm trở thành chuyện thường ngày | Khi số service và số lời gọi tăng lên |
| Orphaned services | Không ai biết một service làm gì hay ai sở hữu nó | Sau nhiều năm chạy microservices |
Ý kết của chương là về sự cân bằng: bạn không thể nhìn thấy tương lai, nên hãy để mắt đến các tín hiệu ở trên, nhưng đừng bỏ hàng tháng trời sửa những vấn đề có thể bạn sẽ không bao giờ gặp.
Con người và cấu trúc: ownership, global vs local, service mồ côi
Ownership at scale. Sách dùng ba mô hình của Martin Fowler: strong ownership (owner duyệt các thay đổi từ bên ngoài), weak ownership (ai cũng sửa được code nhưng phải nói chuyện với owner trước) và collective ownership (ai cũng sửa được mọi thứ). Collective ownership cần một cảm nhận chung về thế nào là một thay đổi tốt. Newman thấy ổn với nó cho một nhóm nhỏ ngồi cùng chỗ khoảng 20 developer, nhưng nó đổ vỡ khi tuyển người nhanh. Ví dụ của ông là một công ty fintech tăng từ khoảng 30 hay 40 developer lên hơn 100 mà không phân công ownership, và kết cục là một distributed monolith rối rắm. Cách chữa mà ông thấy ở gần như mọi tổ chức microservice lớn là strong ownership theo từng team, trong đó mỗi team áp dụng collective ownership trong nội bộ. Nó cũng giúp các team gắn với một mảng nghiệp vụ.
Global versus local optimisation. Tự chủ tạo ra những lựa chọn hợp lý ở cục bộ nhưng cộng lại thì tệ: một team chọn một database, team khác chọn một database khác, và giờ tổ chức phải duy trì kỹ năng và license cho ba loại. Bạn thường phát hiện ra chuyện này một cách tình cờ. Cách chữa dùng lại ý tưởng ở chương 2 về quyết định reversible và irreversible: team tự quyết những thứ đảo ngược được, và kéo người khác vào cho những thứ đắt đỏ, khó đảo ngược. Các cơ chế bao gồm một nhóm kỹ thuật liên team có một lead từ mỗi team, hoặc một văn hóa nhẹ hơn với các proposal viết ra mà ai cũng comment được, như sách mô tả ở Monzo.
Orphaned services. Independent deployability nghĩa là có những service chạy nhiều năm không ai đụng tới. Đến một lúc không ai còn nhớ chúng làm gì, mọi người ngại thay đổi chúng, và có team thậm chí không tìm thấy source code. Cách chữa là một registry ghi lại cái gì đang chạy và ai sở hữu nó, xây từ metadata trong các repository và dữ liệu từ service discovery. Ví dụ phong phú nhất trong sách là công cụ Biz Ops của Financial Times, vốn còn chấm điểm mức độ dễ vận hành của từng service. Khi tìm thấy một service mồ côi, hãy giao cho nó một owner (với strong ownership) hoặc lên lịch công việc để đưa nó về chuẩn.
Contract và testing: breaking changes, end-to-end test
Breaking changes. Mỗi service đều có một contract với các consumer của nó, tường minh hay không, bao gồm cả cấu trúc lẫn hành vi. Các quy tắc thực tế của Newman là: loại bỏ những lần làm vỡ do vô tình, nghĩ kỹ hai lần trước một lần làm vỡ có chủ đích, và cho consumer thời gian để migrate. Với lỗi vỡ cấu trúc do vô tình, ông ưa các schema tường minh giúp thay đổi contract dễ thấy, thậm chí cả công cụ từ chối các thay đổi không tương thích. Với thay đổi có chủ đích, hãy ưu tiên expansion: thêm một field hoặc endpoint thay vì sửa cái đang có. Khi buộc phải làm vỡ, ông thích một service phục vụ cả contract cũ lẫn mới hơn là chạy song song hai version, cách cần nhiều hạ tầng hơn và phải sửa bug trên hai branch. Viết bằng TypeScript, một thay đổi kiểu expansion trông như sau:
type CourseSummaryV1 = {
id: string;
title: string;
priceVnd: number;
};
type CourseSummary = CourseSummaryV1 & {
price: { amount: number; currency: "VND" | "USD" };
};
export function toCourseSummary(course: {
id: string;
title: string;
priceAmount: number;
priceCurrency: "VND" | "USD";
}): CourseSummary {
return {
id: course.id,
title: course.title,
priceVnd: course.priceCurrency === "VND" ? course.priceAmount : 0,
price: { amount: course.priceAmount, currency: course.priceCurrency },
};
}
Consumer cũ tiếp tục đọc priceVnd, consumer mới đọc price, và field cũ chỉ bị gỡ sau khi các consumer đã chuyển xong. Lập luận sắc nhất của chương là những tổ chức không bao giờ kiểm soát được chuyện này có lẽ sẽ không tồn tại đủ lâu để trở nên lớn.
End-to-end testing. Khi test trải qua nhiều service hơn, chúng chậm hơn, flaky hơn và khó xác định ai sở hữu hơn, và mọi người bắt đầu chạy lại các test fail cho đến khi pass. Các cách chữa: giữ test đa service bên trong team sở hữu các service đó, dùng consumer-driven contracts để mỗi consumer nêu kỳ vọng của mình dưới dạng test chạy được, đầu tư vào progressive delivery với rollback tự động dựa trên các ngưỡng như latency p95 và error rate, và liên tục tỉa bớt test chứ không chỉ thêm vào. Newman gọi consumer-driven contracts là một thực hành ít được dùng, có team áp dụng rất thành công còn team khác thì chật vật.
Nhìn thấy hệ thống: monitoring và reporting
Monitoring and troubleshooting. Một monolith thường hoặc sống hoặc chết. Một hệ thống phân tán thì hỏng từng phần, và một process đơn lẻ đang bận có thể quan trọng hoặc không. Các tín hiệu là những sự cố production không ai giải thích được, alert bắn trong khi hệ thống trông vẫn ổn, và khó trả lời câu hỏi "mọi thứ có ổn không?". Các cách chữa của Newman, theo thứ tự ông trình bày:
- Log aggregation trước tiên. Ông coi nó là thứ đầu tiên cần xây, và là một bài kiểm tra mức sẵn sàng: một tổ chức chật vật để dựng nó thì có lẽ chưa sẵn sàng cho microservices.
- Correlation ID trên mọi lời gọi đi vào, lý tưởng là được sinh ra ở gateway hoặc mesh, và được truyền tiếp qua mọi bước.
- Distributed tracing (ông gọi tên Jaeger), làm sớm hơn nếu hệ thống của bạn nhạy cảm với latency.
- Testing in production với synthetic transaction, dùng tài khoản giả không thể gây tác động thật.
- Observability: thu thập đủ dữ liệu để trả lời những câu hỏi bạn chưa lường trước.
Năm 2026, correlation ID thường đi dưới dạng header W3C traceparent mà OpenTelemetry truyền đi giúp bạn:
gateway traceparent: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
-> enrollment same trace id, new parent span id
-> payment same trace id, new parent span id
logs and spans from all three can be queried by trace id 4bf92f...
Reporting. Người dùng reporting mất database duy nhất mà họ từng query, thường kèm các công cụ SQL gắn chặt với schema cũ. Vấn đề này cắn sớm, ngay khi bạn bắt đầu tách dữ liệu, và các team thường chỉ nhận ra khi đã đi được nửa đường. Cách chữa là một reporting database riêng được các service đổ dữ liệu vào qua CDC, view, event hoặc code, với schema được thiết kế cho nhu cầu reporting. Nếu bạn đã có data warehouse, các service chỉ cần đổ dữ liệu vào đó.
Xây và vận hành: phát triển local, quá nhiều thứ phải chạy
Local developer experience. Đến một lúc, laptop không chạy nổi mọi service. Newman ghi nhận ông có thể chạy bốn hay năm service JVM ở local, chứ không phải hai mươi. Các tín hiệu là chạy local chậm và những yêu cầu đòi máy mạnh hơn. Các team có strong ownership với một vài service ít bị ảnh hưởng hơn, vì họ vốn đã stub những gì họ không sở hữu. Các lựa chọn là stub, phát triển trên một môi trường remote (cần kết nối mạng và tốn tiền), hoặc cách lai như Telepresence (năm 2026 vẫn là một CNCF sandbox project) chạy một service ở local và proxy phần còn lại sang một cluster.
Running too many things. Deploy thủ công và các công cụ configuration management đời cũ không còn scale được khi số service và instance tăng lên, và ngày càng nhiều thời gian đổ vào deploy và sửa lỗi deploy. Cách chữa là một platform có desired state management và cho developer tự phục vụ. Newman gọi tên Kubernetes là lựa chọn phổ biến nhưng cảnh báo đừng áp dụng nó trước khi cách làm hiện tại bắt đầu quá tải. Trên public cloud, ông mô tả mặc định của riêng mình là serverless-first, dùng functions as a service ở những chỗ phù hợp.
Robustness, và lời kết
Robustness and resiliency. Những lỗi hiếm (mất packet, timeout, máy chết) trở thành chuyện thường ngày khi số lời gọi tăng lên, và chúng chủ yếu lộ ra trên production. Điểm xuất phát của Newman là hai câu hỏi cho mọi lời gọi: nó có thể fail theo cách nào, và khi nó fail thì ta nên làm gì? Sau đó mới đến các pattern: giao tiếp asynchronous để giảm temporal coupling, time-out, circuit breaker, nhiều instance, và một platform tự khởi động lại những gì bị crash. Ông cũng coi resilience là một cách làm việc, và gợi ý ghi lại những gì học được từ mỗi sự cố production. Một bản phác thảo nhỏ trả lời cả hai câu hỏi cho một lời gọi:
type Recommendation = { courseId: string; title: string };
export async function fetchRecommendations(
learnerId: string,
): Promise<Recommendation[]> {
try {
const response = await fetch(
`https://recommendations.internal/learners/${learnerId}`,
{ signal: AbortSignal.timeout(800) },
);
if (!response.ok) return [];
return (await response.json()) as Recommendation[];
} catch {
return [];
}
}
Các failure mode là một service chậm hoặc bị lỗi; quyết định là trang course vẫn render mà không có gợi ý thay vì đứng chờ.
Lời kết. Phần coda dài hai trang. Thông điệp thứ nhất: hãy cho mình khoảng trống để thu thập thông tin và quyết định theo bối cảnh của chính mình thay vì sao chép người khác. Thông điệp thứ hai: áp dụng microservices và các thực hành liên quan theo kiểu incremental, bằng những bước vừa sức mà bạn có thể điều chỉnh trên đường đi. Nó kết thúc bằng nhận xét rằng microservices không dành cho tất cả mọi người.
Đọc lại ở năm 2026
Phần lớn các cách chữa trong chương giờ đã có công cụ chuẩn.
- Observability. OpenTelemetry đã graduate trong CNCF vào tháng 5 năm 2026 và là cách mặc định để instrument trace, metric và log. Correlation ID, log aggregation và tracing giờ là một lớp instrumentation duy nhất, còn backend là một lựa chọn riêng.
- Contract testing. Pact vẫn là công cụ tham chiếu cho consumer-driven contracts, và bước kiểm tra "can I deploy" của broker trả lời câu hỏi về breaking changes trước khi release. Với các API schema-first, các công cụ kiểm tra breaking change cho Protocol Buffers và OpenAPI đóng vai trò mà sách giao cho protolock.
- Platform engineering. Running too many things và local developer experience giờ thường là việc của một internal platform team cung cấp các golden path tự phục vụ. Platform engineering maturity model của CNCF là một cách hữu ích để bàn xem bạn đang ở đâu.
- Service catalogue. Backstage, một CNCF incubating project, là nền tảng phổ biến cho registry mà sách khuyên dùng để chống service mồ côi, với ownership được ghi dưới dạng metadata nằm cạnh code.
- Progressive delivery. Các công cụ như Argo Rollouts và Flagger tự động hóa việc rollout và rollback dựa trên metric mà sách mô tả.
Áp dụng vào dự án
- Dựng log aggregation và OpenTelemetry trace propagation trước khi service thứ hai lên production.
- Giao cho mỗi service một team sở hữu và một mục trong catalogue, và mỗi quý rà lại các mục không có owner đang hoạt động.
- Làm contract tường minh, chạy kiểm tra breaking change trong CI, và ưu tiên các thay đổi kiểu expansion.
- Thay end-to-end test liên team bằng contract test, và chỉ giữ một vài journey test do một team sở hữu.
- Lên kế hoạch reporting database trước khi tách schema đầu tiên, và thống nhất nó với những người chạy báo cáo.
- Với mỗi lời gọi remote, ghi ra nó có thể fail thế nào và fallback là gì, và bắt buộc có time-out.
- Giữ một danh sách ngắn các quyết định cần thống nhất liên team (database, ngôn ngữ, deployment platform), và để phần còn lại cho các team.
Tự kiểm tra
Vì sao Newman dùng số lượng service làm tín hiệu? Vì các vấn đề khác nhau thường xuất hiện ở những quy mô khác nhau; ông cũng ghi nhận rằng coupling chặt hơn khiến chúng đến sớm hơn.
Theo sách, nên xây gì đầu tiên khi áp dụng microservices? Log aggregation, thứ vừa hữu ích ngay lập tức vừa là phép thử xem tổ chức đã sẵn sàng chưa.
Newman thích cách nào để hỗ trợ một breaking change? Một service đang chạy expose cả contract cũ lẫn mới, thay vì hai version cùng tồn tại.
Sách gợi ý gì thay cho các end-to-end test lớn liên team? Test trong phạm vi team, consumer-driven contracts, progressive delivery với remediation tự động, và liên tục tỉa bớt test.
Hai câu hỏi nào nên đặt ra cho mọi lời gọi service? Lời gọi này có thể fail theo cách nào, và khi nó fail thì ta nên làm gì?
Đọc thêm
- Tài liệu OpenTelemetry: instrument trace, metric và log, bao gồm cả context propagation.
- Tài liệu Pact: consumer-driven contracts và quy trình "can I deploy".
- Backstage software catalog: ghi lại ownership và metadata cho mọi service.
- CNCF platform engineering maturity model: một bộ từ vựng chung để xây internal platform.