5 điểm bởi GN⁺ 2025-08-23 | Chưa có bình luận nào. | Chia sẻ qua WhatsApp
  • Nhiều quyết định trong thiết kế ngôn ngữ Go được đưa ra một cách không cần thiết hoặc phớt lờ kinh nghiệm đã có từ trước
  • Vấn đề quản lý phạm vi của biến lỗi khiến việc đọc mã và truy tìm bug trở nên khó khăn hơn
  • Tính hai mặt của nil, việc sử dụng bộ nhớ, tính khả chuyển của mã và nhiều khía cạnh khác cho thấy thiết kế thiếu trực quan và không khớp với thực tế
  • Giới hạn của câu lệnh defer cùng cách xử lý ngoại lệ trong thư viện chuẩn khiến việc đảm bảo an toàn ngoại lệ trở nên khó khăn
  • Các vấn đề tích lũy như quản lý bộ nhớ và xử lý UTF-8 chưa tốt đang ảnh hưởng tiêu cực đến chất lượng codebase Go trong dài hạn

Phê bình dài hạn đối với ngôn ngữ Go

  • Như đã nêu trong các bài viết trước (Why Go is not my favourite language, Go programs are not portable), tôi đã chỉ ra nhiều vấn đề của ngôn ngữ Go trong hơn 10 năm
  • Đặc biệt, những quyết định thiết kế không cần thiết vốn phớt lờ các thực tiễn tốt đã được biết đến ngày càng gây thất vọng hơn

Tính phi trực quan trong phạm vi của biến lỗi

  • Cú pháp của Go mở rộng phạm vi của biến lỗi (err) một cách không cần thiết, làm tăng khả năng phát sinh lỗi
    • Trong đoạn mã ví dụ, biến err tồn tại xuyên suốt toàn bộ hàm và bị tái sử dụng, từ đó làm giảm độ dễ đọc và khả năng bảo trì của mã
    • Ngay cả lập trình viên dày dạn kinh nghiệm cũng có thể bị hiểu nhầm và tốn thời gian khi dò bug do vấn đề phạm vi này
    • Cú pháp không cho phép một cách tự nhiên để giới hạn biến này trong phạm vi cục bộ phù hợp

Hai dạng nil

  • Trong Go tồn tại sự khó hiểu khi nil hoạt động khác nhau giữa kiểu interface và kiểu con trỏ
    • Như ví dụ bên dưới, dù s (con trỏ) và i (interface) đều được gán nil, s==i vẫn cho kết quả khác nhau, thể hiện hành vi thiếu nhất quán
    • Đây là kiểu vấn đề mà người ta thường muốn tránh trong xử lý null, cho thấy dấu vết của một thiết kế chưa được cân nhắc đủ kỹ

Giới hạn về tính khả chuyển của mã

  • Việc dùng chú thích cho biên dịch có điều kiện là cách làm rất kém hiệu quả xét về khả năng bảo trì và tính khả chuyển
    • Nếu từng có kinh nghiệm xây dựng phần mềm portable thực sự, bạn sẽ thấy cách này phiền phức và dễ gây lỗi
    • Kinh nghiệm được tích lũy trong lịch sử (tính khả chuyển của mã, các trường hợp thực tiễn) đã bị bỏ qua
    • Xem chi tiết trong Go programs are not portable

Sự mơ hồ về quyền sở hữu của append

  • Mối quan hệ quyền sở hữu giữa hàm append và slice không rõ ràng, khiến hành vi của mã khó dự đoán
    • Qua ví dụ, khi một slice được append trong hàm foo, rất khó biết trước nó thực sự ảnh hưởng đến dữ liệu gốc ra sao
    • Số lượng các “quirk” của ngôn ngữ mà lập trình viên phải ghi nhớ cứ tăng lên, dễ dẫn đến sai sót

Thiết kế của câu lệnh defer còn thiếu sót

  • Go không hỗ trợ giải phóng tài nguyên một cách rõ ràng theo nguyên tắc RAII (Resource Acquisition Is Initialization)
    • So với các cấu trúc quản lý tài nguyên có cấu trúc trong Java và Python, Go không cho thấy rõ tài nguyên nào nên được giải phóng bằng defer
    • Như ví dụ về thao tác với tệp, lập trình viên còn phải tự xử lý cả vấn đề đóng hai lần, và thứ tự cũng như cách giải phóng đúng đắn đều không rõ ràng

Xử lý ngoại lệ trong thư viện chuẩn

  • Go không hỗ trợ ngoại lệ (exception) tường minh, nhưng các tình huống ngoại lệ như panic vẫn xảy ra
    • Trong một số trường hợp, panic thậm chí không kết thúc hoàn toàn chương trình mà bị nuốt mất
    • Trong thư viện chuẩn (fmt.Print, máy chủ HTTP, v.v.) có những mẫu xử lý bỏ qua ngoại lệ, khiến không thể đảm bảo an toàn ngoại lệ thực sự
    • Kết quả là việc viết mã an toàn trước ngoại lệ vẫn là điều bắt buộc, nhưng lại không thể trực tiếp sử dụng ngoại lệ

Xử lý UTF-8 và chuỗi

  • Ngay cả khi đưa dữ liệu nhị phân tùy ý vào kiểu string, Go vẫn hoạt động mà không có xác thực đặc biệt nào
    • Bạn có thể gặp trường hợp tên tệp được tạo ra trước thời kỳ mã hóa UTF-8 bị âm thầm bỏ sót
    • Dữ liệu quan trọng trong các tác vụ như sao lưu có thể bị mất, cho thấy một cách xử lý quá đơn giản và không phản ánh thực tế vận hành

Giới hạn của quản lý bộ nhớ

  • Rất khó kiểm soát trực tiếp lượng RAM sử dụng, và độ tin cậy của GC (garbage collection) cũng có giới hạn
    • Mức sử dụng bộ nhớ của Go tăng lên, kéo theo các vấn đề chi phí và hiệu năng trong dài hạn
    • Trong môi trường nhiều instance hoặc container, các vấn đề về chi phí và khả năng mở rộng thực sự có thể phát sinh

Kết luận: đã từng có con đường tốt hơn

  • Dù đã có sẵn những thiết kế ngôn ngữ được chứng minh là hiệu quả, Go vẫn quay lưng với chúng ở nhiều khía cạnh
    • Khác với những vấn đề của các bản thiết kế Java thuở đầu, vào thời điểm Go ra mắt đã tồn tại những cách tiếp cận tốt hơn

Tài liệu tham khảo

Chưa có bình luận nào.

Chưa có bình luận nào.