- Khi ra mắt Dagger Cloud v3, giao diện người dùng mới được viết bằng WebAssembly (WASM) và Go
- Go thường không được dùng để phát triển web UI, nhưng họ đã chọn cách này để "hợp nhất codebase và tối ưu hiệu năng"
- Bài viết này chia sẻ "bối cảnh của lựa chọn, những thách thức trong quá trình triển khai và kết quả cuối cùng"
Vấn đề ban đầu: sự kém hiệu quả do hai codebase riêng biệt
- Dagger hoạt động dựa trên DAG (Directed Acyclic Graph) và cung cấp khả năng trực quan hóa qua TUI (terminal UI) và bảng điều khiển web (Dagger Cloud)
- Trước đây, TUI được triển khai bằng Go, còn web UI bằng React/TypeScript
- Tuy nhiên, việc đồng bộ giữa hai UI rất khó khăn, và đặc biệt web UI gặp vấn đề hiệu năng khi xử lý lượng lớn dữ liệu theo thời gian thực
- Khi xử lý các luồng sự kiện OpenTelemetry phức tạp (hàng trăm nghìn span), tình trạng suy giảm hiệu năng và vấn đề tốc độ của React UI trở nên rõ rệt
- Cùng một tính năng phải được triển khai hai lần, tạo thành gánh nặng phát triển lớn đối với một đội ngũ nhỏ
- Vì vậy, họ cân nhắc một cách tiếp cận mới với mục tiêu hợp nhất codebase và tối ưu hiệu năng
Giải pháp được chọn: Go + WebAssembly
- Hợp nhất codebase bằng Go
- TUI vốn đã được xây dựng bằng Go, nên nếu web UI cũng dùng Go thì có thể tái sử dụng mã nguồn
- Trong nhóm có nhiều lập trình viên Go nên nâng cao năng suất của nhóm và giúp việc bảo trì dễ dàng hơn
- Tận dụng WebAssembly (WASM)
- Đưa WebAssembly vào để có thể chạy trực tiếp mã Go trong trình duyệt
- Tuy nhiên, hệ sinh thái Go + WASM vẫn chưa đủ trưởng thành nên có một số thách thức:
- Thiếu thư viện component → phải tự triển khai UI
- Giới hạn bộ nhớ WASM của trình duyệt (2GB) → cần tối ưu khi xử lý dữ liệu lớn
- Dù vậy, tối ưu bộ nhớ có thể mang lại lợi ích cho cả TUI lẫn web UI
Chiến lược giảm thiểu rủi ro dự án
- Sử dụng framework Go-app
- Chọn một framework dựa trên Go để phát triển PWA (Progressive Web App)
- Framework này cung cấp mô hình component tương tự React, giúp việc chuyển đổi trở nên dễ dàng
- Tạo prototype và kiểm chứng
- Tái hiện lại UI hiện có bằng Go-app nhiều nhất có thể để xác định các vấn đề chính
- WASM vốn đã là một tiêu chuẩn mở được tài liệu hóa, và các câu hỏi quan trọng phần lớn có thể giải quyết qua tài liệu của Go-app
- Vấn đề lớn nhất là giới hạn mức sử dụng bộ nhớ, đòi hỏi thiết kế và tối ưu để xử lý
Từ prototype đến production
Chiến lược tối ưu hiệu năng
- Tối ưu kết xuất log quy mô lớn
- Cần cải thiện hiệu năng render khi xử lý dữ liệu log hơn 200.000 dòng
- Để làm được điều đó, họ tối ưu thư viện render terminal ảo
midterm,
→ qua đó cải thiện hiệu năng cho cả TUI và web UI
- Cải thiện tốc độ phân tích JSON
- Go WASM có tốc độ parse JSON chậm → thiết kế backend thông minh dựa trên WebSocket
- Dùng
encoding/gob của Go để tối ưu truyền dữ liệu
- Tối ưu kích thước file WASM
- Kích thước file WASM ban đầu: 32MB
- Áp dụng nén Brotli → giảm xuống còn 4,6MB
- Vì CDN khó xử lý việc nén này nên họ áp dụng nén trực tiếp trong quy trình build
Các cải tiến khác
- Ngoài vấn đề bộ nhớ đã được dự đoán trước, phần lớn các lo ngại khác đều không thành hiện thực
- Việc viết UI component không quá khó, và việc tích hợp với các dịch vụ khác (Tailwind, Auth0, v.v.) cũng không gặp vấn đề
- Có thể tận dụng các gói NPM trong WebAssembly → bảo đảm khả năng tương tác với JavaScript
- Go-app linh hoạt hơn React trong cách cập nhật component, nên có nhiều dư địa tối ưu hơn
- Có thể phân tích hiệu năng bằng công cụ profiling của Go (
pprof) và profiler tích hợp trong trình duyệt
- Nhờ hỗ trợ PWA, ứng dụng có thể chạy như app desktop/mobile, cho phép mở ứng dụng mà không cần mở trình duyệt
Những lợi ích đạt được sau khi chuyển đổi
- Cải thiện tính nhất quán của UI
- Khi TUI và web UI dùng chung một codebase, họ có thể mang lại UX nhất quán hơn
- Cải thiện hiệu năng và mức sử dụng bộ nhớ
- Khi xử lý lượng dữ liệu lớn, tốc độ render được cải thiện và mức sử dụng bộ nhớ giảm xuống
- Nâng cao năng suất của nhóm
- Trước đây, họ phải tối ưu web UI và TUI một cách riêng rẽ,
nhưng giờ đây chỉ cần tối ưu một lần là có thể áp dụng đồng thời cho cả hai giao diện
- Nhờ đó, họ có thể tập trung hơn vào việc phát triển các tính năng mới
Có nên chuyển sang Go + WASM không?
- Nhìn chung không được khuyến nghị, nhưng trong một số điều kiện cụ thể thì có thể hữu ích:
- Đội ngũ có nhiều lập trình viên Go
- UI phức tạp nơi TypeScript/React bộc lộ giới hạn hiệu năng
- Cần chia sẻ mã giữa TUI và web UI
- Môi trường cần tối đa hóa tốc độ phát triển
- Nếu phù hợp với các điều kiện trên thì Go + WASM có thể là một lựa chọn thay thế tốt
Tuy nhiên, trong đa số trường hợp, các công nghệ web hiện có (React, TypeScript, v.v.) vẫn phù hợp hơn
6 bình luận
Kiểu như GWT ngày xưa phải không?
Ừm... liệu việc phát triển có an toàn về kiểu hơn so với TS hay không thì cũng khá đáng tò mò đấy.
Dù nhìn kiểu nào thì cũng có cảm giác như đang giải một bài toán dễ theo cách quá phức tạp...
Việc làm frontend dựa trên Go hiệu quả hơn tưởng tượng. Rõ ràng là có lý do khiến các use case ngày càng tăng.
Dù vậy, tôi vẫn muốn thử.
Ý kiến trên Hacker News
Có ý kiến cho rằng với một đội nhỏ thì cần triển khai thật nhanh
Họ có một đội ngũ kỹ sư Go rất mạnh và một UI phức tạp khó mở rộng bằng TypeScript/React
Có người lo đây là một framework “render bằng canvas”, nhưng không phải vậy
Họ đã quyết định dùng <a href="https://go-app.dev/" rel="nofollow">https://go-app.dev/</a> để xây dựng frontend
Có ý kiến cho rằng Go không phù hợp với kiểu công việc này
Sẽ rất thú vị nếu vài tháng nữa có một báo cáo tiếp theo xem việc chuyển từ một stack nặng hơn sang một stack hiệu năng tốt hơn nhưng hơi khác thường có mang lại kết quả tích cực hay không
Người tạo ra go-app đã phát hiện bài đăng này và tỏ ra bất ngờ, đồng thời chúc sản phẩm thành công
Vì Go WASM chậm khi phân tích JSON, họ đã phải thay đổi kiến trúc và tạo một “backend thông minh” để tải dữ liệu dần qua WebSockets
Có ý kiến cho rằng WASM phù hợp với những trường hợp ngách nhất định, nhưng không thích hợp để xây dựng web app thông thường
Có người cho rằng việc dùng cùng một ngôn ngữ cho mọi thành phần (frontend/backend/app) mang lại giá trị lớn