- Việc vội vàng tách code thành microservices ở startup giai đoạn đầu sẽ gây suy giảm nghiêm trọng về năng suất của đội ngũ và làm tăng độ phức tạp
- Kiến trúc nguyên khối (monolithic) mang lại khả năng tối ưu cho việc sinh tồn nhờ triển khai đơn giản, ra mắt tính năng mới nhanh và cộng tác hiệu quả
- Microservices chỉ thực sự mang lại lợi ích của việc tách riêng khi có nhu cầu mở rộng quy mô lớn, nhiều loại workload khác nhau hoặc yêu cầu runtime riêng biệt
- Việc tách service quá mức, repo mọc tràn lan, môi trường phát triển cục bộ thiếu ổn định và công nghệ không đồng nhất đều dẫn tới chậm lại và giảm tinh thần đội ngũ
- Startup nên bắt đầu với monolith và chỉ tách ra khi xuất hiện điểm nghẽn rõ ràng; đó là cách tiếp cận thận trọng tối ưu
Mở đầu và bối cảnh
- Sự sống còn của startup được quyết định bởi tốc độ lặp nhanh, cung cấp tính năng mới và tạo ra giá trị sử dụng
- Kiến trúc nền tảng của dự án, tech stack và lựa chọn ngôn ngữ lập trình đều ảnh hưởng tới tốc độ của đội ngũ
- Việc áp dụng microservices quá sớm trông có vẻ bài bản trên danh nghĩa, nhưng trên thực tế lại gây ra giảm năng suất, dịch vụ dở dang và độ phức tạp dư thừa
- Dữ liệu: phát sinh nhiều loại chi phí phát triển như điều phối service, vấn đề với Docker/script, CI/CD trùng lặp, sự phụ thuộc giữa các service, chi phí observability và kiểm thử phân tán
- Thay vì tùy tiện tiến tới một kiến trúc phức tạp, bài viết nhấn mạnh tầm quan trọng của kiến trúc thực dụng
Điểm mạnh của monolith
- Dù là SaaS hay chỉ là một lớp bọc cơ sở dữ liệu đơn giản, ứng dụng theo thời gian sẽ trở nên phức tạp hơn, nhưng kiến trúc monolith vẫn dễ giữ được sự đơn giản và linh hoạt
- Việc triển khai dễ dàng, có sự hỗ trợ từ các framework phổ biến như Django, ASP.Net, Nest.js và hưởng lợi lớn từ cộng đồng mã nguồn mở
- Ví dụ thực tế: một startup bất động sản đã dùng monolith Laravel để dễ dàng thực hiện nhiều tích hợp bên thứ ba và mở rộng tính năng
- Họ có thể tập trung đáp ứng yêu cầu kinh doanh và kỳ vọng thay vì đưa vào hạ tầng phức tạp hoặc tách sang microservices
- Bài học: sự gọn gàng của kiến trúc giúp đội ngũ tập trung vào triển khai; chỉ cần cẩn trọng với việc mô-đun hóa nội bộ kém là vẫn có thể mở rộng đủ tốt
Liệu microservices có luôn là lựa chọn tốt nhất?
- Nhiều kỹ sư cho rằng microservices là đáp án chuẩn mực, nhưng thực tế nó chỉ phát huy giá trị khi có lý do đặc biệt như mở rộng quy mô
- Với đội ngũ ít người, quy mô nhỏ và giai đoạn thay đổi nhanh, nó lại phản tác dụng do hạ tầng trùng lặp, phát triển cục bộ chậm và chu kỳ lặp chậm
- Ngay cả những công ty như Segment cũng từng chuyển đổi vì cấu trúc kém hiệu quả
- Bài học: microservices chỉ là công cụ để giải quyết điểm nghẽn, không phải template cho giai đoạn đầu
Vì sao microservices thường thất bại, đặc biệt ở giai đoạn đầu
1. Ranh giới service tùy tiện
- Việc mượn lý thuyết domain-driven design hay clean architecture để chia service theo logic nghiệp vụ thường dẫn tới ranh giới giữa logic thực tế và service không khớp nhau
- Ví dụ: tách người dùng, xác thực và phân quyền riêng ra làm tăng độ phức tạp triển khai và độ khó khi phát triển API
- Việc tách ra ở giai đoạn chưa hề xuất hiện điểm nghẽn thực sự sẽ làm hệ thống kém ổn định và chậm hơn
- Mô phỏng việc tách trong tương lai bằng flag hoặc toggle nội bộ, đồng thời khám phá ranh giới một cách hữu cơ thay vì lao vào công việc hạ tầng cấp bách, sẽ hiệu quả hơn
- Bài học: hãy quyết định tách dựa trên điểm nghẽn thực tế thay vì lý thuyết
2. Repo/hạ tầng dư thừa
- Mọi yếu tố như style code, test, cấu hình môi trường, tài liệu, CI/CD đều tăng lên theo số lượng service
- Nếu dùng cấu trúc monorepo, có thể quản lý mọi cấu hình tại một nơi để tăng tính nhất quán của code và hiệu quả cộng tác
- Với Node.js, các công cụ như
nx hoặc turborepo giúp quản lý phụ thuộc và build giữa các service nội bộ dễ dàng hơn
- Nhược điểm là quan hệ phụ thuộc phức tạp, cần tinh chỉnh hiệu năng CI và có thể cần công cụ build nhanh hơn
- Hệ sinh thái Go cũng có thể được quản lý bằng một workspace duy nhất ở giai đoạn đầu, rồi mới cân nhắc tách module khi quy mô lớn hơn
- Bài học: đội ngũ nhỏ có thể tiết kiệm thời gian bằng monorepo và hạ tầng dùng chung
3. Môi trường phát triển cục bộ thiếu ổn định
- Quá nhiều thời gian để chạy local, script phức tạp và phụ thuộc theo từng hệ thống đều gây ra chậm onboarding và giảm năng suất
- Thiếu tài liệu, vấn đề tương thích và các cách vá riêng cho từng OS (ví dụ script chỉ chạy trên macOS) đều là rào cản
- Trong một dự án, người ta đã giảm độ phức tạp của Docker bằng proxy Node.js, từ đó rút ngắn thời gian onboarding cho developer
- Bài học: nếu ứng dụng chỉ chạy được trên một OS, năng suất của đội ngũ cuối cùng sẽ phụ thuộc vào độ tin cậy của đúng một chiếc laptop
4. Tech stack không đồng nhất
- Node.js và Python phù hợp cho tốc độ lặp nhanh, nhưng trong môi trường microservices lại thường phát sinh vấn đề không khớp giữa build/runtime
- Go có ưu thế ở binary tĩnh, build nhanh và vận hành đơn giản
- Cần chọn tech stack cẩn thận ngay từ đầu; nếu cần thì có thể trộn nhiều ngôn ngữ thông qua các giao thức như gRPC
- Nếu không có nhu cầu đặc biệt như ML hay ETL, việc trộn stack thường chỉ làm tăng thêm độ phức tạp
- Bài học: hãy chọn stack phù hợp với thực tế của đội ngũ, không phải với giấc mơ kỹ thuật
5. Độ phức tạp ẩn: giao tiếp và giám sát
- Trong microservices, các yếu tố như service discovery, versioning API, distributed tracing, quản lý log tập trung là bắt buộc
- Việc truy vết bug/sự cố trong monolith có thể chỉ cần một stack trace, nhưng trong môi trường phân tán thì phức tạp hơn nhiều
- Muốn làm cho bài bản thì cần đưa vào các công cụ chuyên dụng như OpenTelemetry và xây dựng hẳn một stack observability
- Cần nhận thức rằng hệ thống phân tán là một khoản đầu tư bắt buộc cho những thách thức kỹ thuật bổ sung
Khi nào microservices có hiệu quả
- Cô lập workload: tách riêng các tác vụ bất đồng bộ cụ thể như xử lý ảnh, OCR sẽ hiệu quả hơn
- Nhu cầu mở rộng không cân xứng: khi web API và workload ML có yêu cầu phần cứng/vận hành khác nhau thì nên tách riêng từng phần
- Cần runtime khác: các thành phần không tương thích runtime với ứng dụng chính, chẳng hạn mã C++ legacy, nên được duy trì như service riêng
- Qua các trường hợp ở tổ chức kỹ thuật lớn như Uber, có thể thấy nó chỉ phù hợp khi tồn tại nhu cầu tổ chức rõ ràng và năng lực vận hành đã trưởng thành
- Ngay cả ở đội ngũ nhỏ, đôi khi việc tách riêng vẫn thực dụng nếu đó là dịch vụ bên ngoài như analytics và dễ quản lý
- Bài học: chỉ áp dụng khi lợi ích của việc tách là rõ ràng với workload cụ thể
Hướng dẫn thực chiến cho startup
- Ban đầu hãy bắt đầu với monolith và tập trung làm việc trên một framework đã được chứng minh
- Một repository duy nhất có lợi hơn cho đội ngũ ban đầu về hiệu quả vận hành/quản trị và cả góc độ bảo mật
- Đơn giản hóa môi trường phát triển cục bộ là rất quan trọng; nếu khó thì bắt buộc phải cung cấp tài liệu hoặc video hướng dẫn chi tiết
- Đầu tư sớm vào CI/CD để tự động hóa công việc lặp lại và giảm gánh nặng tâm lý cho đội ngũ
- Chỉ tách chọn lọc khi xuất hiện điểm nghẽn rõ ràng; nếu không thì hãy tập trung vào mô-đun hóa bên trong monolith và tăng cường kiểm thử
- Mục tiêu ưu tiên cao nhất là giữ được tốc độ phát triển
- Bài học: hãy bắt đầu từ sự đơn giản, rồi mở rộng theo đúng nhu cầu tách biệt
Nếu buộc phải dùng microservices
- Đánh giá tech stack và đầu tư vào công cụ trải nghiệm developer: cần chuẩn bị tự động hóa theo từng service, script rõ ràng và công cụ quản lý triển khai thống nhất
- Chuẩn hóa và xây dựng giao thức giao tiếp service đáng tin cậy: cần nắm rõ các phần phải triển khai thêm như tính nhất quán của message schema, tài liệu hóa và xử lý lỗi
- Ổn định hóa hạ tầng kiểm thử: test đơn vị, tích hợp và E2E phải mở rộng tương ứng với việc tách service
- Cân nhắc thư viện dùng chung: phần code chung cho observability/giao tiếp nên giữ ở phạm vi tối thiểu để tránh phải rebuild toàn bộ service quá thường xuyên
- Đưa observability vào sớm: hãy bắt đầu từ các công cụ logging cơ bản như log JSON có cấu trúc và correlation ID
- Tóm lại, nếu chấp nhận độ phức tạp thì phải dốc sức thiết kế một hệ thống có thể quản lý được
Kết luận
- Việc áp dụng microservices một cách vội vàng chỉ để lại gánh nặng, vì vậy hãy ưu tiên sự đơn giản lên hàng đầu
- Đừng tách ra khi chưa có điểm đau rõ ràng; điều quan trọng là chỉ thêm vào mức độ phức tạp tối thiểu cần thiết để tồn tại và phát triển
- Sống sót là ưu tiên trước, mở rộng là chuyện sau
10 bình luận
Tôi nhìn chung đồng ý với ý của bài gốc.
Tôi nghĩ đây là vấn đề về kinh nghiệm của tổ chức.
Hãy thử tưởng tượng việc bán đồ ăn từ một xe bán đồ ăn lưu động rồi phát triển thành một nhà hàng.
Ngay từ đầu, các bên liên quan hoàn toàn thiếu kinh nghiệm để có thể tính đến phân công lao động và chuyên môn hóa.
Tôi cho rằng startup nên chọn cách ít tốn kém hơn để kéo dài thời gian sống còn. Microservices hoàn toàn không rẻ. Khi áp dụng vào thực tế tại hiện trường, nó thực sự phát sinh chi phí đáng kể. Nếu có thể, tôi nghĩ việc thiết kế một kiến trúc phù hợp với chính dịch vụ của công ty sẽ là cách đạt được hiệu quả tương tự với chi phí thấp hơn.
Điều đó không có nghĩa là microservices là xấu. Đây là một mô hình đòi hỏi nhiều chi phí.
Tôi nghĩ chỉ cần đúng 2 thứ là đủ: một monolith chỉ dùng đồng bộ và một monolith chỉ dùng bất đồng bộ... Việc áp dụng microservices, theo tôi, rốt cuộc phụ thuộc vào quy mô các bảng cần phải quản lý bằng DB. Nếu số lượng bảng nhiều đến mức vô lý và phức tạp thì nên cân nhắc MSA, còn nếu đơn giản thì monolith là phù hợp nhất.
Khi tất cả những làn sóng này qua đi, hậu thế sẽ nhớ về thế hệ này như thế nào?
Khi đó lại là làn sóng của thời điểm đó...
Tôi nghĩ ở startup, microservice cũng có nhiều ưu điểm. Trước hết, tôi thật sự khuyến nghị dùng monorepo.
Tôi đồng ý rằng trong thời đại phát triển AI, việc triển khai theo các đơn vị nhỏ với trách nhiệm duy nhất là điều thiết yếu.
Dù ở phần bình luận cũng đã nhắc thoáng qua, nhưng hệ BEAM/OTP kiểu này quả là khá linh hoạt và tốt. Trường hợp của Gleam thì nhờ kết hợp cú pháp hay của cả Go lẫn Rust với độ ổn định của BEAM nên đã trở thành một ngôn ngữ khá ấn tượng. Tôi bắt đầu muốn thử dùng nó cho các dự án quy mô nhỏ.
Nếu chia nhỏ đội ngũ một cách quá mức, thì ngay cả việc tụ họp lại để trao đổi ý kiến cũng trở thành một khối lượng công việc khổng lồ.
Ý kiến trên Hacker News