Từ PHP sang Go: Điều gì khiến tôi mất nhiều thời gian nhất để thay đổi tư duy
Tôi đã viết PHP trong bảy năm. Trong đó, tôi đã sử dụng Laravel được năm năm. Khi tôi chuyển sang Go để dẫn dắt quá trình chuyển đổi từ một kiến trúc monolith Laravel sang microservices, tôi không đến với một cuốn sách hướng dẫn. Tôi đến với một thập kỷ thói quen của PHP.
Học cú pháp thì dễ. Bạn có thể đọc hiểu Go chỉ trong một buổi chiều. Phần khó khăn là phải xóa bỏ cách tôi từng tư duy về mã nguồn.
Dưới đây là những thay đổi về tư duy mà tôi đã mất hàng tháng trời để làm chủ:
Xử lý lỗi (Error Handling) Trong PHP, tôi thường dùng try/catch. Các lỗi thường được chuyển một cách âm thầm đến một trình xử lý toàn cục (global handler). Trong Go, lỗi là các giá trị (values). Một hàm sẽ trả về lỗi như là giá trị cuối cùng của nó. Bạn phải xử lý lỗi ngay tại đó. Lúc đầu, tôi cảm thấy việc này như là làm thêm việc. Sau đó, tôi nhận ra rằng nó giúp các lỗi trở nên rõ ràng hơn. Khi một dịch vụ gặp lỗi, thông báo lỗi đóng vai trò như một dấu vết dẫn đường. Nó cho bạn biết chính xác lỗi xảy ra ở đâu mà không cần đến một stack trace khổng lồ.
Bộ nhớ và Trạng thái (Memory and State) PHP sử dụng mô hình shared-nothing. Mỗi yêu cầu (request) đều bắt đầu với một trạng thái hoàn toàn mới. Các lỗi rò rỉ bộ nhớ (memory leaks) ít quan trọng hơn vì tiến trình sẽ kết thúc ngay sau khi yêu cầu hoàn tất. Go thì khác. Tiến trình sẽ duy trì sự sống qua hàng ngàn yêu cầu. Điều này có nghĩa là một biến ở cấp độ package sẽ được chia sẻ qua mọi yêu cầu. Nếu hai yêu cầu cùng cố gắng ghi vào một map tại cùng một thời điểm, toàn bộ chương trình sẽ bị sập. Trong Go, bạn là người kiểm soát tính đồng thời (concurrency). Bạn phải sử dụng các công cụ như race detector để giữ cho mọi thứ an toàn.
Vai trò của Context Trong PHP, request chính là ranh giới. Khi request kết thúc, mọi thứ sẽ dừng lại. Trong Go, không có gì tự động dừng cả. Nếu một client ngắt kết nối, các goroutine của bạn có thể vẫn tiếp tục chạy và gây lãng phí tài nguyên. Bạn phải sử dụng
context.Contextđể truyền các tín hiệu hủy (cancellation signals) xuyên suốt mã nguồn của mình. Nó chính là xương sống giúp dịch vụ của bạn hoạt động ổn định dưới tải trọng lớn.Ưu tiên Thành phần hơn Kế thừa (Composition Over Inheritance) Laravel phụ thuộc rất nhiều vào việc mở rộng (extending) các lớp cơ sở (base classes). Bạn thêm các tính năng bằng cách kế thừa hành vi. Go không có kế thừa lớp (class inheritance). Nó sử dụng các interface được thỏa mãn một cách ngầm định (implicitly). Bạn định nghĩa những gì mình cần ngay tại nơi bạn sử dụng chúng. Điều này giúp mã nguồn dễ kiểm thử và thay thế hơn. Tôi đã phải dẹp bỏ bản năng muốn mở rộng mọi thứ để có thể viết Go một cách chuẩn chỉnh.
Sự rõ ràng quan trọng hơn sự thông minh (Clarity Over Cleverness) PHP cho phép sử dụng các magic methods và kiểu dữ liệu động (dynamic typing). Bạn có thể viết những đoạn mã thông minh và đầy tính biểu đạt. Go thì cố tình được thiết kế theo hướng "nhàm chán". Trình biên dịch buộc bạn phải sử dụng định dạng chuẩn và ngăn chặn các biến không được sử dụng. Lúc đầu, tôi cảm thấy điều này thật gò bó. Nhưng giờ đây, tôi lại trân trọng nó. Go tối ưu hóa cho người đọc mã nguồn chứ không phải người viết mã. Những đoạn mã "nhàm chán" sẽ dễ dàng được sửa chữa vào lúc 2 giờ sáng khi có sự cố xảy ra.
Phần khó nhất khi học một ngôn ngữ mới không phải là cú pháp mới. Mà là những giả định cũ mà bạn mang theo bên mình.
Nguồn: https://dev.to/econ__11/from-php-to-go-what-took-me-longest-to-rewire-2nfn
