Một bài phê bình chi tiết về ngôn ngữ lập trình Go đã gây ra cuộc tranh luận sôi nổi trong cộng đồng lập trình viên, làm nổi bật những vấn đề dai dẳng đã ám ảnh ngôn ngữ này trong hơn một thập kỷ. Cuộc thảo luận tập trung vào những quyết định thiết kế cơ bản mà các nhà phê bình cho rằng có thể tránh được và xuất phát từ những bài học mà cộng đồng lập trình đã rút ra.
Vấn đề xử lý lỗi và phạm vi biến
Một trong những khía cạnh gây tranh cãi nhất liên quan đến cơ chế xử lý lỗi và quy tắc phạm vi biến của Go . Ngôn ngữ này buộc các lập trình viên vào những tình huống khó xử khi các biến lỗi vẫn tồn tại trong phạm vi lâu hơn mức cần thiết, tạo ra sự nhầm lẫn và lỗi tiềm ẩn. Không giống như các ngôn ngữ khác cho phép kiểm soát chính xác hơn thời gian tồn tại của biến, những hạn chế cú pháp của Go ngăn cản các lập trình viên giảm thiểu phạm vi biến trong một số mẫu phổ biến nhất định. Lựa chọn thiết kế này khiến mã khó đọc hơn và dễ bị lỗi tinh vi hơn, đặc biệt khi các lập trình viên vô tình tái sử dụng các biến lỗi hoặc tham chiếu chúng ngoài ngữ cảnh dự định.
Những Chỉ Trích Chính Về Ngôn Ngữ Go:
- Xử Lý Lỗi: Các vấn đề về phạm vi biến bị ép buộc với các mẫu xử lý lỗi
- Kiểu Nil: Hai loại nil khác nhau (có kiểu và không có kiểu) gây ra sự không nhất quán trong việc so sánh
- Quản Lý Bộ Nhớ: Tính không thể dự đoán của garbage collector và các vấn đề phân mảnh bộ nhớ
- Tính Di Động: Hệ thống build tag sử dụng comment trong file tạo ra các vấn đề bảo trì
- Cơ Chế Defer: Dọn dẹp tài nguyên theo phạm vi hàm thay vì phạm vi từ vựng
- Xử Lý Chuỗi: Xác thực UTF-8 không nhất quán dẫn đến khả năng mất dữ liệu
Vấn đề hai loại Nil
Cách xử lý các giá trị nil của Go đặt ra một thách thức đáng kể khác. Ngôn ngữ này thực tế có hai loại nil khác nhau - có kiểu và không có kiểu - hoạt động khác nhau trong các phép so sánh và có thể dẫn đến các lỗi runtime không mong muốn. Vấn đề này xảy ra khi các biến interface chứa con trỏ nil, tạo ra những tình huống mà một biến có thể không bằng nil trong phép so sánh nhưng vẫn gây ra lỗi null pointer dereference khi các phương thức được gọi. Các thành viên cộng đồng báo cáo gặp phải vấn đề này trong mã production, nơi có thể khó phát hiện trong quá trình review mã.
Vấn đề quản lý bộ nhớ và tính di động
Hành vi của garbage collector của ngôn ngữ đã thu hút sự chỉ trích từ các lập trình viên làm việc trên các ứng dụng hạn chế bộ nhớ. Một số thành viên cộng đồng báo cáo phải đấu tranh với ngôn ngữ khi cố gắng giảm thiểu việc sử dụng bộ nhớ, đặc biệt trong các tình huống yêu cầu kiểm soát bộ nhớ chính xác. Hành vi không thể dự đoán của garbage collector có thể dẫn đến phân mảnh bộ nhớ và dọn dẹp chậm trễ, buộc các lập trình viên phải triển khai các giải pháp thay thế như buffer được phân bổ trước và các chiến lược quản lý bộ nhớ tùy chỉnh.
Các vấn đề về tính di động cũng ám ảnh thiết kế của Go , đặc biệt xung quanh việc biên dịch có điều kiện sử dụng build tags. Các nhà phê bình cho rằng cách tiếp cận này, dựa vào các comment đặc biệt ở đầu file, tạo ra những cơn ác mộng bảo trì cho mã đa nền tảng so với các cách tiếp cận hiện đại hơn được sử dụng bởi các ngôn ngữ khác.
Phản hồi cộng đồng và quan điểm thay thế
Cộng đồng lập trình viên vẫn chia rẽ về những chỉ trích này. Những người ủng hộ cho rằng lợi ích về tính đơn giản và năng suất của Go vượt trội hơn các lỗi thiết kế, đặc biệt cho phát triển phía server và các dịch vụ API . Nhiều lập trình viên đánh giá cao thời gian biên dịch nhanh của Go , thư viện chuẩn mạnh mẽ và các công cụ định dạng nhất quán giúp loại bỏ nhiều tranh chấp phổ biến trong nhóm.
Go là một ngôn ngữ có hiệu suất hợp lý giúp việc viết các dịch vụ đáng tin cậy, có tính đồng thời cao mà không dựa vào multithreading nặng trở nên khá đơn giản - tất cả nhờ vào mô hình goroutine.
Tuy nhiên, các nhà phê bình cho rằng những lợi ích này đến với cái giá của ergonomics lập trình viên và type safety. Một số đề xuất rằng các ngôn ngữ như Rust , Java với virtual threads, hoặc thậm chí các lựa chọn thay thế mới hơn có thể cung cấp giải pháp tốt hơn cho nhu cầu phát triển hiện đại.
Các Ngôn Ngữ Lập Trình Thay Thế Được Đề Cập:
- Rust: An toàn kiểu dữ liệu và quản lý bộ nhớ tốt hơn
- Java: Được cải thiện với virtual threads và GC hiện đại
- Elixir: Mô hình đồng thời vượt trội cho một số trường hợp sử dụng cụ thể
- C: Cân bằng tốt giữa các tính năng và hỗ trợ doanh nghiệp
- Zig: Giải pháp thay thế cấp thấp hơn với các abstraction tốt hơn
- Carbon: Ngôn ngữ đang phát triển để tương tác với C++
Bối cảnh rộng hơn
Cuộc tranh luận phản ánh một căng thẳng lớn hơn trong thiết kế ngôn ngữ lập trình giữa tính đơn giản và tính biểu đạt. Trong khi những người tạo ra Go đã cố ý chọn tính đơn giản hơn sự phong phú về tính năng, các nhà phê bình cho rằng triết lý này đôi khi dẫn đến việc đẩy độ phức tạp lên các lập trình viên thay vì loại bỏ nó. Cam kết tương thích ngược của ngôn ngữ cũng có nghĩa là nhiều vấn đề cơ bản này không thể được giải quyết mà không phá vỡ mã hiện có.
Khi bối cảnh lập trình tiếp tục phát triển, cộng đồng Go đối mặt với những câu hỏi liên tục về việc liệu các sự đánh đổi của ngôn ngữ có còn phù hợp với các thách thức phát triển phần mềm hiện đại hay không. Trong khi Go tiếp tục được áp dụng rộng rãi, đặc biệt trong hạ tầng đám mây và microservices, những chỉ trích dai dẳng này cho thấy rằng các lập trình viên ngày càng mong đợi các tính năng ngôn ngữ tinh vi hơn và ergonomics tốt hơn từ các công cụ của họ.
Tham khảo: Go is still not good