Sáu mươi bài, và mỗi bài đều bắt đầu bằng việc chạy thử rồi mới viết.

Sáu chặng

Bài 1–10: nền tảng. Từ lý do Go tồn tại tới zero value, slice, map — cùng cái bẫy chia sẻ mảng nền.

Bài 11–22: kiểu và tổ chức. struct, method set, interface ngầm định, embedding, generics, và bẫy interface nil.

Bài 23–30: lỗi và tài nguyên. error là giá trị, wrapping, panic, defer, context.

Bài 31–42: đồng thời. Goroutine, channel, select, khoá, race detector, pipeline, rò rỉ goroutine, errgroup.

Bài 43–52: thư viện chuẩn. io, os, JSON, net/http, database/sql, time, testing, pprof.

Bài 53–60: thực chiến. Module, build, Docker, cấu hình, slog, graceful shutdown, bảo mật.

Mười phép đo đáng nhớ

Goroutine: 2,47 µs và 576 byte (bài 31). Đây là con số định hình cả ngôn ngữ — nó cho phép "một goroutine cho mỗi việc" thay vì pool và callback.

append vào slice con ghi đè slice gốc (bài 9): goc=[1 99 3 777 5], không cảnh báo nào.

len("Tiếng Việt") = 14, s[:4] = "Ti\xe1\xba" (bài 22). Mọi hàm cắt chuỗi trong dự án tiếng Việt cần xem lại.

Go 1.21 in 3 3 3, Go 1.22 in 0 1 2 (bài 6) — cùng mã, và hành vi phụ thuộc dòng go trong go.mod chứ không phụ thuộc trình biên dịch.

Đổi thứ tự ba trường: struct 24 byte xuống 16 (bài 11).

p == niltrue nhưng err == nilfalse (bài 17) — cái bẫy nổi tiếng nhất của Go.

100 goroutine tăng biến: mong 100.000, nhận 47.948 (bài 34), và ba lần chạy ra ba con số.

Ghi 200.000 lần: 58 ms xuống 1 ms qua bufio (bài 43).

Nối chuỗi: 83.869 ns / 999 allocs xuống 711 ns / 1 alloc (bài 52) — 118 lần.

Ảnh Docker: 1,34 GB xuống 11,7 MB (bài 55).

Đặt cạnh sê-ri Java

Cả hai sê-ri đo trên cùng một máy, nên vài con số so được trực tiếp:

  tạo một đơn vị đồng thời:
    luồng nền tảng Java : 97,3 µs  | 1 MB đặt chỗ
    luồng ảo Java       : 31,6 µs  | ~800 byte
    goroutine Go        :  2,47 µs | 576 byte

  ảnh Docker nhỏ nhất dựng được:
    Java (jlink + distroless) : 126 MB
    Go   (scratch)            : 11,7 MB

  công cụ bắt lỗi đua:
    Java : phải tự dựng khung đo, nới khe hở thủ công (bài 96)
    Go   : go test -race, chỉ thẳng số dòng đọc và ghi

  quét lỗ hổng phụ thuộc:
    Java (OSV-Scanner) : 73 lỗ hổng, không biết cái nào gọi tới được
    Go   (govulncheck) : 36 trong module, 4 trong package import, 0 gọi tới được

Nhưng cũng có chiều ngược lại. Java có hệ sinh thái sâu hơn nhiều, có generics mạnh hơn, có công cụ chẩn đoán JVM chi tiết hơn (JFR ở bài 89), và có kiểu dữ liệu phong phú hơn cho mô hình hoá miền nghiệp vụ.

Go đổi tất cả những thứ đó lấy: build 48 ms, binary tĩnh 2 MB, đồng thời nằm trong ngôn ngữ, và một cú pháp nhỏ tới mức mã của người khác đọc được ngay.

Đó là đánh đổi, không phải thứ bậc. Chọn theo bài toán.

Ba điều tôi rút ra sau sáu mươi bài

Go tối ưu cho việc đọc mã, không cho việc viết mã. Không có toán tử ba ngôi, không nạp chồng, không tham số mặc định, if err != nil khắp nơi. Viết thì dài; đọc mã của người lạ thì nhanh hơn hẳn.

Zero value là ý tưởng lớn nhất. Bài 18: var b Bo rồi dùng ngay, mutex và buffer bên trong đều chạy. Nó bỏ đi cả một tầng hàm khởi tạo mà các ngôn ngữ khác bắt buộc phải có.

Công cụ chẩn đoán đi trước. -race, pprof, govulncheck, go test -fuzz đều nằm sẵn trong bộ cài và đều là một dòng lệnh. Bài 52 và 59 cho thấy khoảng cách với hệ sinh thái khác.

Đi tiếp từ đây

Web và dịch vụnet/http đủ cho phần lớn API sau Go 1.22. Cần hơn thì chi (tương thích http.Handler) hoặc echo. Với gRPC thì google.golang.org/grpc.

Dữ liệusqlx hoặc sqlc là hai lựa chọn tôi khuyên; ORM đầy đủ ít phổ biến trong Go hơn hẳn Java.

Hạ tầng và DevOps — đây là sân nhà của Go. Kubernetes operator (kubebuilder), CLI (cobra), công cụ nội bộ.

Hiệu năng sâupprof nghiêm túc, benchstat, hiểu escape analysis (go build -gcflags='-m').

Đồng thời nâng cao — đọc mã bộ lập lịch runtime, golang.org/x/sync, mô hình bộ nhớ chính thức.

Sách và tài liệu

Effective GoGo Code Review Comments trên go.dev — hai tài liệu ngắn, đọc trong một buổi, và chúng định nghĩa "mã Go đúng chuẩn".

The Go Programming Language (Donovan, Kernighan) — cuốn duy nhất tôi nói là bắt buộc. Cũ hơn generics nhưng phần nền vẫn đúng nguyên.

100 Go Mistakes and How to Avoid Them (Teiva Harsanyi) — hợp ngay sau sê-ri này.

go.dev/blogghi chú phát hành mỗi sáu tháng — Go đổi chậm nhưng đều, và mười lăm phút đọc mỗi bản là đủ theo kịp.

Và một nguồn ít người dùng: mã nguồn thư viện chuẩn. Nó được viết để làm mẫu, comment đầy đủ, và go doc -src <hàm> mở ra ngay trong terminal.

Điều cuối

Sê-ri này theo đúng nguyên tắc của sê-ri Java trước đó: không viết điều mình chưa chạy thử.

Và nó lại trả công theo cùng một cách. Bài 42 định viết "channel chậm hơn mutex" và phép đo nói ngược lại. Bài 57 định viết "LogAttrs nhanh hơn hẳn" và hai con số ra 70 với 69 mili giây. Bài 6 định viết theo trí nhớ về Go 1.22 và việc chạy thử trên ba phiên bản lộ ra chi tiết quan trọng hơn — hành vi phụ thuộc go.mod, không phụ thuộc trình biên dịch.

Nếu có một thứ đáng mang theo từ sáu mươi ngày này, vẫn là câu cũ: chạy thử đi, rồi hãy tin.

Cảm ơn bạn đã đọc tới đây.