- Dự án .NET Unified Build là một hệ thống build mới hợp nhất toàn bộ sản phẩm theo dạng “kho mã nguyên khối ảo (Virtual Monolithic Repository, VMR)” nhằm giảm bớt sự phức tạp và kém hiệu quả của cấu trúc build dựa trên các kho mã phân tán hiện có
- Cách cấu thành sản phẩm phân tán trước đây có tính độc lập và linh hoạt cao, nhưng gây ra gánh nặng lớn về quản lý phụ thuộc, tính nhất quán khi build và tốc độ
- Unified Build mở rộng nguyên lý của Source Build dành cho các bản phân phối Linux, đưa vào bố cục mã nguồn đơn nhất và cấu trúc “build dọc (Vertical Build)” để rút ngắn thời gian build và tăng khả năng dự đoán
- Thông qua luồng mã hai chiều (two-way code flow), kiểm thử theo kịch bản, cùng với cải tiến hạ tầng xác minh và ký tự động, hệ thống này đồng thời nâng cao hiệu quả của lập trình viên và chất lượng sản phẩm
- Được chính thức áp dụng trong .NET 10, hệ thống này đã đạt được các kết quả thực tế như rút ngắn thời gian build (24 giờ → dưới 7 giờ), giảm chi phí bảo trì và nâng cao độ tin cậy khi phát hành
Bối cảnh thay đổi cấu trúc build của .NET
- Trong quá trình mã nguồn mở hóa giai đoạn 2015~2016, .NET được phát triển tách thành nhiều kho mã như CoreCLR, CoreFX, ASP.NET Core, SDK
- Mỗi kho mã được build và phát hành độc lập, còn toàn bộ sản phẩm được tổ hợp thông qua đồ thị phụ thuộc
- Cách này tương tự hệ sinh thái OSS, nhưng khi có bản vá bảo mật hoặc sửa lỗi khẩn cấp, cần nhiều nhóm phối hợp đồng thời nên phát sinh vấn đề không thể dự đoán thời gian
- Dù có các ưu điểm của phát triển phân tán như phân lớp, tách biệt cộng đồng, phát triển bất đồng bộ..., mô hình này lại kém hiệu quả trong việc đảm bảo tính nhất quán của sản phẩm (coherency)
Độ phức tạp cấu thành sản phẩm và overhead
- Độ phức tạp (Complexity): được định nghĩa là số bước cần thiết để một thay đổi đến được với khách hàng
- Càng nhiều kho mã và nút phụ thuộc thì càng cần nhiều thời gian và phối hợp nhân sự để đảm bảo tính nhất quán
- Overhead: thời gian không trực tiếp tạo ra đầu ra có thể chuyển giao cho khách hàng
- Ví dụ: tạo PR, chờ phê duyệt, xếp hàng, thiết lập môi trường...
- Kết quả phân tích build runtime của .NET 8 cho thấy khoảng 38,5% tổng thời gian build là overhead
- Khi độ phức tạp và overhead kết hợp với nhau, hiệu quả build giảm mạnh và toàn bộ chu kỳ phát hành bị kéo dài
Nguồn gốc của Source Build và Unified Build
- Source Build là hệ thống được thiết kế để các bản phân phối Linux có thể build .NET offline từ một bộ mã nguồn duy nhất
- Nguyên tắc một triển khai duy nhất, một nền tảng duy nhất, một môi trường build duy nhất
- Bộ điều phối build quản lý phụ thuộc và thứ tự build của từng thành phần
- Source Build có độ phức tạp thấp và overhead thấp, có thể build trong vòng 50 phút
- Microsoft từng muốn áp dụng khái niệm này vào build nội bộ, nhưng gặp khó khăn do mã nguồn đóng, phụ thuộc legacy, và các join giữa nền tảng
- Để giải quyết, nhóm đã đưa vào các cách tiếp cận như gói tham chiếu chuyên cho source build (source-build-reference-packages), nguyên tắc một triển khai duy nhất và loại bỏ join
Mục tiêu và thiết kế của Unified Build
- Có thể build toàn bộ sản phẩm .NET bằng một commit duy nhất
- Tạo mọi bản phân phối theo nền tảng trong một môi trường duy nhất
- Có thể build và xác minh độc lập cả bên ngoài Microsoft
- Tự động đồng bộ thay đổi giữa VMR và từng kho mã thành phần thông qua luồng mã hai chiều
- Thực hiện xác minh chức năng ở cấp toàn bộ sản phẩm thông qua kiểm thử theo kịch bản
- Song song hóa build theo nền tảng bằng cấu trúc build dọc (Vertical Build)
- Gồm khoảng 35~40 trục build dọc (short/tall stack)
Các giai đoạn triển khai Unified Build
- .NET 7: thiết kế và phê duyệt khái niệm
- .NET 8: cải tiến hạ tầng Source Build và xây dựng nền tảng
- .NET 9: thử nghiệm build dọc và luồng mã
- .NET 10: chính thức sản phẩm hóa và phản ánh vào RTM
- Giới thiệu quy trình build mới ở Preview 4, chuyển đổi hoàn toàn từ Preview 5
Các thành phần chính
Virtual Monolithic Repository (VMR)
- Kho mã dotnet/dotnet đóng vai trò bố cục mã nguồn đơn nhất cho mọi thành phần
- Lập trình viên có thể làm việc ở từng kho mã riêng lẻ hoặc trong VMR, qua đó đồng thời đạt được sự linh hoạt của mô hình phân tán và tính nhất quán của mô hình đơn nhất
Vertical Build
- Thực hiện build độc lập cho từng nền tảng và runtime
- Sau khi build song song, kết quả được hợp nhất; một số join được xử lý bằng thêm các lượt build bổ sung
Code Flow
- Đồng bộ mã hai chiều: kho mã thành phần → VMR, VMR → kho mã thành phần
- Ghi lại trạng thái luồng mã gần nhất trong
eng/Version.Details.xml
- Tạo thay đổi dưới dạng file patch để tự động tạo PR
- Tích hợp sẵn logic xử lý xung đột và khôi phục lỗi
Scenario Test Validation
- Ngoài các unit test hiện có, bổ sung kiểm thử theo kịch bản để xác minh chức năng của toàn bộ sản phẩm
- Được chạy dựa trên các đầu ra build nhằm tăng cường chống hồi quy và đảm bảo chất lượng
Thành quả và hiệu quả
- Rút ngắn thời gian build: hơn 24 giờ → dưới 7 giờ (bao gồm ký)
- Tăng tính linh hoạt: rút ngắn chu kỳ build và phát hành, dễ phản ánh các bản sửa khẩn cấp
- Đảm bảo khả năng dự đoán: xác định rõ thời điểm hoàn tất build sau khi có thay đổi
- Cải thiện hạ tầng: công cụ ký, log, build song song, tự động hóa luồng phụ thuộc...
- Source Build cho các bản phân phối Linux cũng luôn được giữ ở trạng thái sạch trước khi build
Định hướng tiếp theo
- Trong .NET 11, dự kiến đưa vào tự động hóa luồng mã và agent giám sát dựa trên AI
- Tự động hóa việc theo dõi lỗi trong quá trình từ PR đến khi phản ánh vào sản phẩm
- Về dài hạn, thúc đẩy đơn giản hóa build và tăng tốc bằng cách loại bỏ các điểm join
- Mục tiêu: build hoàn chỉnh trong vòng 4 giờ
Kết luận
- Unified Build, giải pháp vượt qua các giới hạn của mô hình build phân tán, đã cải thiện tận gốc hiệu quả build và phát hành của .NET
- Đã đạt được bước tiến thực chất trên ba trục: giảm độ phức tạp và overhead, tăng tính nhất quán, tốc độ và chất lượng
- Hệ thống này đã được áp dụng toàn diện từ .NET 10 RTM và sẽ tiếp tục được cải tiến trong các phiên bản tới
1 bình luận
Ý kiến trên Hacker News
Tôi thật sự rất kính trọng đội ngũ .NET
Họ thường xuyên xuất bản các bài viết kỹ thuật rất sâu, và sự ám ảnh của họ với tối ưu hiệu năng là đáng nể (ví dụ: sự phát triển của Kestrel, Entity Framework)
ASP.NET là một trong số ít dự án lớn sống sót qua một thay đổi lớn cỡ Python 2→3
Trước đây nó dựa vào những tính năng gần như phép thuật để đồng bộ session, nhưng giờ đã hoạt động theo một cách hoàn toàn khác
Thật dễ chịu khi biết một công ty trị giá 3 nghìn tỷ đô đang nghiêm túc cố gắng cải thiện stack mà tôi đang dùng
Vì vậy tôi thay nó bằng Dapper và một hệ thống migration tự viết, kết quả là thời gian kiểm tra DB và seeding lúc khởi động giảm từ 10 giây xuống dưới 2 giây (trong môi trường phần cứng yếu + SQLite)
Các truy vấn do Entity tạo ra có quá nhiều phép join lồng nhau không cần thiết
Dạo này tôi đang chuyển sang backend Go đơn giản hơn, còn .NET chỉ dùng cho các giải pháp khác
Các framework như WPF có quá nhiều vấn đề đến mức phải hack Win32
Ví dụ, mãi tới .NET 9 nó mới trả về đúng tất cả network interface. Các runtime trước đó chỉ lộ ra các NIC đang hoạt động
Tôi vẫn còn những dự án phải tiếp tục hỗ trợ Windows 7
Ngay cả với dự án mới, vẫn có trường hợp phải dùng .NET 4.8. Ví dụ, khi build công thức Excel thì phát sinh vấn đề deadlock với async/await
Ngoài ra, các tính năng tích hợp Windows cũng bị hỏng, nên cùng một lời gọi mạng thì trên 4.8 được xác thực còn trên Core lại thất bại
Việc migration không hề dễ do sự đổ vỡ tương thích ngược của vô số thư viện
Hiệu năng đã tốt hơn, nhưng về mặt tính năng thì .NET Framework có cảm giác như đã hóa thạch
Phải mất 15 năm mới có JSON serializer trong thư viện chuẩn, và cũng không có hỗ trợ cho các định dạng ảnh hiện đại như webp hay heic
Visual Studio gần như ngày nào cũng hiện thông báo crash
Tôi từng là fan .NET cuồng nhiệt, nhưng giờ lại nhớ sự dẫn dắt của Anders
Trong một side project gần đây, tôi phát triển backend C# REST API bằng VSCode trên macOS và đã deploy lên Linux suốt 3 năm
Dùng SQLite, EFCore, Minimal API, và trải nghiệm dễ chịu hơn nhiều so với frontend (NextJS/React/MaterialUI, hơn 50 gói npm)
Đây là framework API tôi thích nhất
Lựa chọn kế tiếp là tổ hợp TS + Hono + Zod-OpenApi + SwaggerUI, nhưng phần thiết lập type context hơi phiền
Điều gây ấn tượng với tôi là nền tảng cho việc mã nguồn mở hóa và đa nền tảng hóa của .NET lại là hệ thống build của các bản phân phối Linux
Vì vậy cần một hệ thống build đáp ứng được yêu cầu của họ, và cuối cùng hướng duy nhất là hợp nhất theo mô hình bản phân phối Linux
Mô hình này đơn giản nhưng hiệu năng kém hơn. Một hệ thống build phân tán có cache sẽ nhanh hơn, nhưng lại không khớp với workflow của maintainer
Chúng tôi cho rằng tối ưu cho sự đơn giản sẽ tốt hơn trong việc khuyến khích cộng đồng tham gia
Mục tiêu là để cộng đồng có thể tự build và phát hành trên nhiều nền tảng khác nhau như BSD, S390x
Lượng pull request và các kết quả tích cực trong 10 năm qua thật đáng kinh ngạc
Đây là bài viết về kỹ thuật phần mềm gây ấn tượng nhất tôi đọc trong năm nay, và tôi không ngờ nó lại đến từ Microsoft
Tôi thích các phiên bản .NET gần đây, nhưng từng nghĩ độ vững chắc của nó chỉ là ngẫu nhiên
Thế nhưng bài này cho thấy những nỗ lực có hệ thống nhằm nâng cao chất lượng (bao gồm cả sơ đồ và ví dụ sử dụng LLM)
Kể cả nếu sau này mức đầu tư cho những việc như vậy giảm đi, đây vẫn là một ví dụ tốt về “nên làm như thế nào”
Những người tham gia dự án này hẳn đã có một trải nghiệm thực sự tuyệt vời
Bài viết hay đấy, nhưng tôi nghĩ đội .NET nên bỏ Azure DevOps
Thời gian chờ trong hàng đợi là nút thắt lớn nhất. Họ nên chạy máy chủ build bare metal
Phần cứng Mac kết nối và khởi động rất nhanh
Chúng tôi tạo mới VM sạch cho mỗi job để đảm bảo tuân thủ và độ ổn định
Nếu duy trì sẵn các máy nóng đã chuẩn bị trước thì có thể loại bỏ thời gian chờ, nhưng chi phí lại quá cao
Việc giữ nhiều SKU khác nhau luôn ở trạng thái chờ là không thực tế, nên chúng tôi chọn phương án dung hòa
Tôi thắc mắc vì sao Developer Division của Microsoft lại thuộc hàng giỏi nhất ngành, trong khi các bộ phận khác lại trở thành biểu tượng của sự bất tài và quan liêu
Từ sau khi Bill rời đi, văn hóa tự nhìn lại chính mình đã biến mất
Tôi nghĩ gốc rễ vấn đề là tư duy lấy trừu tượng làm trung tâm khi cố đơn giản hóa những hệ thống phức tạp
Thay vì cố giải quyết mọi thứ ở tầng trên, cách tiếp cận xây từ tầng thấp lên có thể loại bỏ nhiều vấn đề tận gốc hơn
Khi đọc câu “mỗi tháng build 3~4 phiên bản chính và hàng chục SDK band”, tôi đã tự hỏi vì sao lại có nhiều biến thể như vậy
Ngoài ra, mỗi phiên bản còn được build cho SDK/aspnet/runtime trên nhiều nền tảng như x64/arm32/arm64, Linux/macOS/Windows
Trước khi Node thịnh hành, .NET từng là một lựa chọn vững chắc để build backend (hiệu năng cũng tốt hơn Node)
Sau các cuộc tấn công chuỗi cung ứng gần đây trong hệ sinh thái Node, tôi mong nhiều lập trình viên sẽ quay lại với những nền tảng ổn định hơn
Thay đổi lớn là việc chuyển sang .NET Core, nhưng chuyện đó giờ cũng gần 10 năm rồi
Việc cập nhật framework và dependency có hơi phiền một chút, nhưng vẫn nhẹ nhàng hơn nhiều so với cập nhật project React
Chu kỳ LTS ngắn giúp việc luôn theo kịp phiên bản mới không quá khó. Với nhịp phát triển nhanh, kiểu bảo trì này là chuyện bình thường
Thực tế thì chủ yếu vẫn là Node.js, Java và low-code (iPaaS)
Dù vậy, nhờ các vấn đề hiệu năng nên thỉnh thoảng tôi vẫn có cơ hội đề xuất C++ addon