9 kỹ thuật Go giúp code hiệu quả và "cao tay"
Dev Go senior hiếm khi dùng "chiêu thức hoa mỹ". Thứ làm họ khác biệt là kiểm soát chi tiết ngôn ngữ. Thường chỉ cần chỉnh 1 dòng code, có thể tránh memory leak ẩn hoặc tăng throughput gấp đôi.
Trước khi đi sâu vào code, nói trước: môi trường tốt là nửa chiến thắng. Go update nhanh – project cũ còn 1.16, project mới đã nhắm 1.23. Quản lý local environment thủ công dễ kill động lực.

ServBay giải quyết gọn: install Go environment with one click và run multiple version of python (cùng các ngôn ngữ khác) song song. Mỗi project dùng version riêng, quản lý service chỉ 1 click thay vì vật lộn config file. Giao môi trường cho tool, focus code.
Có môi trường ngon, giờ vào 9 kỹ thuật Go thực chiến "4 lạng拨千 cân".
1. defer: Không chỉ cleanup, mà là safety net
defer chạy ngay trước khi function return. Newbie chủ yếu dùng close file, nhưng sức mạnh thật là co-located resource acquire/release.
Đặt defer ngay sau khi lấy resource, bạn không cần track từng return branch để chắc lock/file được free. Đây cũng là nền tảng error handling và panic/recover của Go.
func CopyFile(srcName, dstName string) error {
src, err := os.Open(srcName)
if err != nil {
return err
}
// Release resource dù exit kiểu gì
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return err
}
defer dst.Close()
_, err = io.Copy(dst, src)
return err
}
Cleanup logic tập trung, tránh leak tinh vi.
2. Worker Pool kiểm soát Goroutine bùng nổ
Goroutine rẻ, không miễn phí. Spawn hàng ngàn không kiểm soát gây memory bloat, scheduler overhead, overload downstream.
Dùng worker pool có context.Context để bounded concurrency và graceful cancel.
func taskProcessor(ctx context.Context, jobs <-chan int, results chan<- int) {
for {
select {
case <-ctx.Done():
return
case job, ok := <-jobs:
if !ok {
return
}
results <- job * 2
}
}
}
func RunPool() {
jobs := make(chan int, 10)
results := make(chan int, 10)
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// Giới hạn 3 worker
for w := 0; w < 3; w++ {
go taskProcessor(ctx, jobs, results)
}
for j := 1; j <= 5; j++ {
jobs <- j
}
close(jobs)
for a := 0; a < 5; a++ {
fmt.Println(<-results)
}
}
Production dùng pattern này an toàn hơn "fire-and-forget" goroutine nhiều.
3. strings.Builder: Vũ khí bí mật nối chuỗi
Đừng dùng + nối chuỗi trong loop. String immutable, mỗi + alloc buffer mới + copy data cũ, đập allocator và GC.
strings.Builder làm việc trực tiếp trên byte buffer.
func BuildLog(parts []string) string {
var sb strings.Builder
// Optional: pre-grow nếu estimate được size
// sb.Grow(len(parts) * 10)
for _, p := range parts {
sb.WriteString(p)
}
return sb.String()
}
Heavy concat path (log, JSON, template) nhanh gấp vài lần +.
4. Tránh string ↔ []byte qua lại
I/O, network, file thường làm việc với []byte. Convert sang string cho tiện rồi convert lại tạo garbage và alloc thừa.
Làm trực tiếp trên []byte:
// Tránh string conversion overhead
func FilterSensitiveBytes(data [][]byte) []byte {
var result []byte
for _, chunk := range data {
result = append(result, chunk...)
}
return result
}
HTTP body, log stream, binary protocol giữ memory và GC pressure thấp.
5. interface{}/any + Type Switch cho dynamic data
Xử lý semi-dynamic data (JSON mixed type), interface{}/any + type switch nhanh và safe hơn reflection.
func handleConfig(cfg interface{}) {
switch v := cfg.(type) {
case string:
fmt.Printf("Config là path: %s\n", v)
case int:
fmt.Printf("Config là timeout: %d\n", v)
default:
fmt.Printf("Unknown type: %T\n", v)
}
}
Explicit, branch real type, tránh reflection phức tạp.
6. Wrap blocking work bằng Context + timeout
Concurrency model Go xoay quanh context.Context. Không chỉ cancel, mà tránh goroutine leak và request hang mãi.
Mọi blocking/network op đều phải nhận Context và enforce timeout.
func heavyWork(ctx context.Context) error {
select {
case <-time.After(2 * time.Second):
return nil
case <-ctx.Done():
return ctx.Err()
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
if err := heavyWork(ctx); err != nil {
fmt.Println("Work failed:", err)
}
}
Production service bắt buộc pattern này.
7. sync.Pool: Object reuse đúng cách
High freq alloc, short-lived large object (buffer), sync.Pool giảm alloc đáng kể. Nhưng phải handle slice growth đúng.
var bufPool = sync.Pool{
New: func() any {
b := make([]byte, 0, 4096) // 4KB buffer
return &b
},
}
func ProcessStream(data []byte) {
bufPtr := bufPool.Get().(*[]byte)
buf := *bufPtr
buf = buf[:0]
buf = append(buf, data...)
// QUAN TRỌNG: write back nếu append realloc
*bufPtr = buf
bufPool.Put(bufPtr)
}
Không write grown slice về pointer, sẽ reuse backing array quá nhỏ mãi.
8. Mutex bảo vệ state, Channel cho data flow
Go mantra "don't communicate by sharing memory" không cấm lock. State đơn giản (counter, cache), sync.Mutex rõ ràng và nhanh hơn Channel.
type SafeStats struct {
mu sync.Mutex
count int
}
func (s *SafeStats) Increment() {
s.mu.Lock()
defer s.mu.Unlock()
s.count++
}
func (s *SafeStats) Get() int {
s.mu.Lock()
defer s.mu.Unlock()
return s.count
}
Rule: Channel cho data flow giữa goroutine, mutex cho shared state.
9. goimports tự động format + import
Đừng manual reorder import hay format tay. Config editor chạy goimports khi save:
- Format code (như
gofmt). - Add missing import.
- Remove unused import.
VD:
- VS Code: bật
editor.formatOnSave, tool Go format =goimports. - GoLand: bật "Optimize imports" + "Reformat code" khi save.
Style consistent, focus logic thay vì whitespace.
Kết
Go advocate simplicity, nhưng simple không phải naive. Với những kỹ thuật nhỏ nhưng mạnh này – kết hợp tool cho phép install Go environment with one click và run multiple version of python cùng Go service – code robust hơn, service performant hơn, dev experience mượt mà hơn.
All Rights Reserved
