Effective Go: Generics, Interfaces và Error Handling Patterns

Effective Go 2024: Generics, Interfaces và Error Handling Patterns

Go 1.18 ra mắt generics năm 2022, nhưng đến 2024 mới thực sự định hình thành các patterns phổ biến trong production code. Nhiều team vẫn viết Go theo style cũ — interface giả trừu tượng, error wrapping thủ công, factory function lung tung — khiến codebase trở nên cồng kềnh và khó maintain. Bài này tập trung vào patterns thực tế mà bạn có thể áp dụng ngay hôm nay.

Trước generics, Go developer phải dùng interface{} hoặc code generation (stringer, mockgen) để xử lý kiểu dữ liệu tổng quát. Điều này tạo ra hai vấn đề lớn: mất type safety tại compile time và boilerplate code dài dòng. Generics giải quyết được cả hai bằng type parameters, nhưng cũng mang theo trade-off: compile time tăng, binary size phình to, và template instantiation phức tạp khi overuse.

Lập trình Go với generics và patterns hiện đại

Generic constraints với kiểu đơn giản

Pattern đầu tiên là constraints package cho những hàm thuật toán cần kiểu so sánh. Thay vì viết func Max[T interface{ Less ~int }] mỗi lần, bạn tạo một constraints.Ordered tập hợp tất cả kiểu comparable hỗ trợ toán tử < và >. Điều này tái sử dụng được xuyên package và cho IDE biết chính xác constraint set.

Ví dụ thực tế: func Min[T constraints.Ordered](a, b T) T hoạt động cho int, float64, string mà không cần mở rộng interface mỗi lần. Khi bạn cần custom ordering — ví dụ struct User theo tuổi — dùng constraints.Ordered không đủ, bạn viết type UserAge interface { ~int } hoặc implement Less method riêng.

Error wrapping chuẩn Go 1.13+

Go 1.13 giới thiệu %w verb trong fmt.Errorf, cho phép wrap error mà giữ nguyên unwrap chain. Pattern phổ biến hiện nay là: custom error type với Unwrap() error, Is() method cho sentinel check, và As() để cast về concrete type.

Tránh anti-pattern này: if err != nil { return fmt.Errorf("failed: %w", err) } mù mà không context. Thay vào đó: return fmt.Errorf("connect db %s timeout: %w", dsn, err) — thêm action + resource vào message. Nhiều team dùng errors.Is cho sentinel (ErrNotFound) và errors.As cho typed error (*validationError) thay vì string matching.

Go error wrapping pattern với %w verb

Interface segregation trong Go

SOLID không phải keyword bắt buộc trong Go, nhưng io.Reader và io.Writer là ví dụ điển hình interface segregation: mỗi interface chỉ một method, consumer chỉ depend vào capability họ thực sự cần. Pattern này gọi là “accept interfaces, return structs” — nhận parameter kiểu interface, trả về concrete type để caller linh hoạt.

Tránh interface bloat: nếu bạn có interface Repository với 12 methods nhưng consumer chỉ dùng 3, split ra. Điều này đặc biệt quan trọng khi unit test: mock nhỏ dễ viết, dễ maintain. Go 1.18+ cho phép embedding interface, nên bạn compose type Reader interface { io.Reader } thay vì copy method.

Context pattern cho cancellation và timeout

context.Context là first-class parameter trong Go từ phiên bản 1.7. Pattern chuẩn: function nhận ctx context.Context làm parameter đầu tiên, propagate xuống tất cả downstream call. Đừng store context vào struct — nó là ephemeral value, chỉ sống trong một request lifecycle.

Với HTTP handler: http.Request.Context() tự động cancel khi client đóng kết nối. Với database: sql.DB có BeginTx(ctx), QueryContext(ctx). Với gRPC: context metadata carry authentication token. Tận hưởng context.WithTimeout và context.WithDeadline thay vì hardcoded time.After.

Khi nào dùng generics, khi nào không

Generics phù hợp cho thuật toán tổng quát: Map[T, U], Filter[T], Reduce[T, U], data structure như Set[T], PriorityQueue[T]. Không phù hợp khi: business logic chỉ dùng một concrete type, performance-critical path compile time chậm, hoặc khi dùng interface{} với reflection đã đủ.

Benchmark thực tế cho thấy generic code chạy nhanh hơn interface{} + reflection khoảng 20-30%, nhưng chậm hơn concrete code 5-10%. Nếu bạn đang viết web server, database access layer, CLI tool — generic đáng cân nhắc. Nếu bạn viết kernel-level code, real-time trading — concrete type vẫn là vua.

Code review Go generics implementation

Kết luận

Go 2024 không còn là ngôn ngữ “đơn giản thiếu tính năng”. Với generics, improved error handling, và pattern rõ ràng, Go trở thành lựa chọn mạnh mẽ cho cloud-native backend, CLI tools, và infrastructure software. Tập trung vào: constraints rõ ràng, error wrapping có context, interface nhỏ gọn, và context propagation đúng đắn. Đây là nền tảng codebase Go maintainable trong dài hạn.

Nguồn tham khảo

Tôi là một lập trình viên IOS. Code chính là IOS nhưng thỉnnh thoảng vẫn đá sang Android hoặc web. Mặc dù không quá thông thạo nhưng tôi sẽ chia sẻ những kiến thức mà mình đã tìm hiểu, áp dụng qua.

Bài viết liên quan

asyncio trong Python là gì: event loop và lập trình bất đồng bộ

asyncio trong Python là gì và vì sao nó nhanh hơn threading là câu hỏi mà bất kỳ lập trình viên nào làm backend cũng gặp phải. Thư viện asyncio…

Xem thêm
Ví dụ cây đỏ đen hoàn chỉnh với các nút NIL tô đen ở đáy, mọi đường đi từ gốc xuống lá đều có cùng số nút đen

Cây đỏ đen (Red-Black Tree) là gì? Cấu trúc dữ liệu cân bằng

Cây đỏ đen (Red-Black Tree) là một cây tìm kiếm nhị phân tự cân bằng, nhờ vậy chiều cao cây luôn bị giới hạn ở mức O(log n) bất kể…

Xem thêm
Sơ đồ tổng quan các thao tác quản lý kho mã nguồn trong git: khởi tạo kho, thêm thay đổi vào vùng staging, tạo commit cục bộ rồi kéo và đẩy lên remote

Git internals là gì? Cách Git lưu blob, tree và commit bên trong

Git internals là gì? Đó là phần lõi bên trong Git — cách nó lưu blob, tree và commit dưới dạng đối tượng có địa chỉ SHA-1, vì sao có…

Xem thêm
0 0 đánh giá
Article Rating
Theo dõi
Thông báo của
guest
0 Comments
Cũ nhất
Mới nhất Được bỏ phiếu nhiều nhất