Go Memory Model & Runtime
Go runtime qanday ishlashini chuqur tushunish — GC, goroutine scheduler, stack growth va happens-before munosabatlari. Senior darajadagi eng muhim bilim.
🧵 GMP Model
G=Goroutine, M=OS Thread, P=Processor. Har P da 256ta goroutine saqlanadi.
🗑 GC (Tricolor)
Concurrent mark-and-sweep. Go 1.18+ da GC pauza <1ms. GOGC env bilan sozlash.
📦 Stack Growth
Goroutine 2KB stackdan boshlanadi, kerakli holda dynamic o'sadi. Max: GOMAXPROCS.
⚡ Work Stealing
Bo'sh P boshqa P ning local queueidan goroutine "o'g'irlaydi" — load balancing.
package main import ( "fmt" "sync" ) // ❌ NOTO'G'RI — data race! happens-before yo'q var done bool var msg string func badExample() { msg = "salom" done = true // boshqa goroutine buni ko'rmasligi mumkin! } // ✅ TO'G'RI — sync.Mutex bilan happens-before kafolatlanadi type SafeCounter struct { mu sync.RWMutex count int } func (c *SafeCounter) Inc() { c.mu.Lock() defer c.mu.Unlock() c.count++ } func (c *SafeCounter) Get() int { c.mu.RLock() defer c.mu.RUnlock() return c.count } // Channel → happens-before kafolati // "send on channel" happens before "receive from channel" func channelHappensBefore() { ch := make(chan struct{}) x := 0 go func() { x = 42 // Bu send dan OLDIN bajariladi ch <- struct{}{} }() <-ch fmt.Println(x) // 42 — kafolatlangan, race yo'q } func main() { c := &SafeCounter{} var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done() c.Inc() }() } wg.Wait() fmt.Println(c.Get()) // 1000 — har doim channelHappensBefore() }
package main import ( "fmt" "runtime" "runtime/debug" ) func gcStats() { var stats runtime.MemStats runtime.ReadMemStats(&stats) fmt.Printf("Alloc: %v MB\n", stats.Alloc/1024/1024) fmt.Printf("TotalAlloc: %v MB\n", stats.TotalAlloc/1024/1024) fmt.Printf("Sys: %v MB\n", stats.Sys/1024/1024) fmt.Printf("NumGC: %v\n", stats.NumGC) fmt.Printf("GCCPUFrac: %.4f\n", stats.GCCPUFraction) fmt.Printf("Goroutines: %v\n", runtime.NumGoroutine()) } func main() { // GOGC=100 (default) → heap 2x bo'lganda GC ishga tushadi // GOGC=200 → kamroq GC, ko'p memory // GOGC=off → GC o'chirilgan (batch job uchun) debug.SetGCPercent(150) // runtime boshqarish // GOMEMLIMIT (Go 1.19+) — soft memory limit debug.SetMemoryLimit(512 << 20) // 512 MB limit // Manual GC (faqat zarur holatlarda) runtime.GC() gcStats() }
GOGC va GOMEMLIMIT ni sozlash eng katta
performance o'sishini beradi. Ko'p loyihalarda GOGC=200 + GOMEMLIMIT
kombinatsiyasi optimal natija beradi.Type System Chuqur
Go'ning type sistemasi — named types, type aliases, embedding, method sets va interface satisfaction qoidalari.
package main import "fmt" // Named Type — yangi tur, o'z metodlari bo'lishi mumkin type UserID int64 type ProductID int64 type Email string func (e Email) Domain() string { for i, ch := range e { if ch == '@' { return string(e[i+1:]) } } return "" } // Type Alias — faqat taxallus, metod qo'shib bo'lmaydi type MyInt = int // int bilan bir xil tur! // Struct Embedding — composition (not inheritance) type Animal struct { Name string Age int } func (a Animal) Speak() string { return a.Name + " speaks" } type Dog struct { Animal // embedded — all Animal methods promoted! Breed string } func (d Dog) Speak() string { // override return d.Name + " says: Woof!" } // Method Sets qoidasi: // T turi → T receiver metodlari // *T turi → T va *T receiver metodlari type Builder struct{ val string } func (b *Builder) Set(v string) *Builder { b.val = v return b // method chaining uchun } func (b *Builder) Build() string { return b.val } func main() { uid := UserID(42) pid := ProductID(42) // uid == pid → COMPILE ERROR! turli typlar fmt.Println(uid, pid) e := Email("ali@example.com") fmt.Println(e.Domain()) // example.com d := Dog{Animal{"Rex", 3}, "Labrador"} fmt.Println(d.Speak()) // Rex says: Woof! (override) fmt.Println(d.Animal.Speak()) // Rex speaks (embedded) fmt.Println(d.Age) // 3 — promoted field result := (&Builder{}).Set("hello world").Build() fmt.Println(result) }
package main import "fmt" // Struct comparable bo'lishi uchun barcha fieldlar comparable bo'lishi kerak type Point struct{ X, Y float64 } // []int, map — comparable EMAS → map key sifatida ishlatib bo'lmaydi // iota bilan enumeratsiya type Status int const ( StatusPending Status = iota // 0 StatusActive // 1 StatusClosed // 2 StatusDeleted // 3 ) func (s Status) String() string { return [...]string{"Pending","Active","Closed","Deleted"}[s] } // Bit flags bilan iota type Permission uint const ( Read Permission = 1 << iota // 1 Write // 2 Execute // 4 Admin = Read | Write | Execute // 7 ) func hasPermission(p, check Permission) bool { return p&check != 0 } func main() { p1, p2 := Point{1, 2}, Point{1, 2} fmt.Println(p1 == p2) // true — struct comparable s := StatusActive fmt.Println(s.String()) // Active userPerm := Read | Write fmt.Println(hasPermission(userPerm, Read)) // true fmt.Println(hasPermission(userPerm, Execute)) // false }
Interface & Polymorphism
Go'da interfacelar implicit — e'lon qilmasdan implement qilish mumkin. Bu duck typing'ning statik versiyasi.
package main import ("fmt"; "math") // Interface ichida 2 pointer: (type, data) // nil interface: (nil, nil) // non-nil interface: (type!=nil, data=nil) — yashirin xato! type Shape interface { Area() float64 Perimeter() float64 } type Stringer interface { String() string } // Interface composition type ShapeStringer interface { Shape Stringer } type Circle struct{ R float64 } func (c Circle) Area() float64 { return math.Pi * c.R * c.R } func (c Circle) Perimeter() float64 { return 2 * math.Pi * c.R } func (c Circle) String() string { return fmt.Sprintf("Circle(r=%.2f)", c.R) } type Rect struct{ W, H float64 } func (r Rect) Area() float64 { return r.W * r.H } func (r Rect) Perimeter() float64 { return 2 * (r.W + r.H) } func (r Rect) String() string { return fmt.Sprintf("Rect(%gx%g)", r.W, r.H) } // nil interface xatosi — klassik bug! func nilTrap() { var s *Circle = nil var i Shape = s // i != nil (type set bor!) fmt.Println(i == nil) // false — yashirin xato! } func totalArea(shapes []Shape) float64 { total := 0.0 for _, s := range shapes { total += s.Area() } return total } func main() { shapes := []Shape{ Circle{5}, Rect{4, 6}, Circle{3}, } for _, s := range shapes { fmt.Printf("%-20s area=%.2f perim=%.2f\n", s.(fmt.Stringer).String(), // type assertion s.Area(), s.Perimeter()) } fmt.Printf("Jami: %.2f\n", totalArea(shapes)) // Type assertion — xavfsiz variant var sh Shape = Circle{7} if c, ok := sh.(Circle); ok { fmt.Printf("Radius: %.0f\n", c.R) } }
type Server struct { host string port int timeout int maxConn int } type Option func(*Server) func WithHost(h string) Option { return func(s *Server) { s.host = h } } func WithPort(p int) Option { return func(s *Server) { s.port = p } } func WithTimeout(t int) Option { return func(s *Server) { s.timeout = t } } func NewServer(opts ...Option) *Server { s := &Server{host: "localhost", port: 8080, timeout: 30, maxConn: 100} for _, opt := range opts { opt(s) } return s } // Ishlatish — juda clean! srv := NewServer( WithHost("0.0.0.0"), WithPort(9090), WithTimeout(60), )
Generics — Go 1.18+
Type parameters bilan generic funksiya va structlar yozish. Type constraints va union types.
package main import ( "fmt" "golang.org/x/exp/constraints" ) // Generic funksiya func Min[T constraints.Ordered](a, b T) T { if a < b { return a } return b } // Custom constraint — Union type type Number interface { ~int | ~int32 | ~int64 | ~float32 | ~float64 } func Sum[T Number](nums []T) T { var total T for _, n := range nums { total += n } return total } // Generic Stack data structure type Stack[T any] struct { items []T } func (s *Stack[T]) Push(item T) { s.items = append(s.items, item) } func (s *Stack[T]) Pop() (T, bool) { var zero T if len(s.items) == 0 { return zero, false } top := s.items[len(s.items)-1] s.items = s.items[:len(s.items)-1] return top, true } func (s *Stack[T]) Len() int { return len(s.items) } // Generic Map funksiya func Map[T, U any](slice []T, f func(T) U) []U { result := make([]U, len(slice)) for i, v := range slice { result[i] = f(v) } return result } func Filter[T any](slice []T, pred func(T) bool) []T { var result []T for _, v := range slice { if pred(v) { result = append(result, v) } } return result } func Reduce[T, U any](slice []T, init U, f func(U, T) U) U { acc := init for _, v := range slice { acc = f(acc, v) } return acc } func main() { fmt.Println(Min(3, 7)) // 3 fmt.Println(Min(3.14, 2.71)) // 2.71 fmt.Println(Min("abc", "xyz"))// abc nums := []int{1,2,3,4,5} fmt.Println(Sum(nums)) // 15 s := &Stack[string]{} s.Push("go"); s.Push("rust"); s.Push("zig") top, _ := s.Pop() fmt.Println(top, s.Len()) // zig 2 doubled := Map(nums, func(n int) int { return n * 2 }) evens := Filter(nums, func(n int) bool { return n%2 == 0 }) product := Reduce(nums, 1, func(acc, n int) int { return acc * n }) fmt.Println(doubled, evens, product) // [2 4 6 8 10] [2 4] 120 }
Error Handling Patterns
Go'da xatolarni to'g'ri boshqarish — sentinel errors, custom errors, wrapping, errors.Is/As.
package main import ( "errors" "fmt" ) // 1. Sentinel errors — comparable, samarali var ( ErrNotFound = errors.New("not found") ErrPermission = errors.New("permission denied") ErrTimeout = errors.New("operation timeout") ) // 2. Custom error type — kontekst bilan type ValidationError struct { Field string Message string Value any } func (e *ValidationError) Error() string { return fmt.Sprintf("validation error: field=%s msg=%s value=%v", e.Field, e.Message, e.Value) } // 3. Error wrapping — %w bilan func getUser(id int) (string, error) { if id <= 0 { return "", &ValidationError{"id", "must be positive", id} } if id == 999 { return "", fmt.Errorf("getUser(%d): %w", id, ErrNotFound) } return "Ali", nil } func loadProfile(id int) error { _, err := getUser(id) if err != nil { return fmt.Errorf("loadProfile: %w", err) // wrap! } return nil } // 4. errors.Is — zanjir bo'ylab tekshirish // 5. errors.As — tur bo'ylab qidirish func main() { err := loadProfile(999) fmt.Println(err) // loadProfile: getUser(999): not found // errors.Is — wrapper zanjiri ichida ham ishlaydi fmt.Println(errors.Is(err, ErrNotFound)) // true err2 := loadProfile(-5) // errors.As — concrete type ga chiqarish var valErr *ValidationError if errors.As(err2, &valErr) { fmt.Printf("Field: %s, Msg: %s\n", valErr.Field, valErr.Message) } // 6. defer + recover — panic ni ushlash safeDiv(10, 0) } func safeDiv(a, b int) (result int, err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("panic recovered: %v", r) } }() return a / b, nil }
Goroutine Internals
Go scheduler — GMP modeli, preemption, goroutine leak, GOMAXPROCS va real production patterns.
func leaky() <-chan int { ch := make(chan int) go func() { // consumer yo'q — goroutine // HECH QACHON tugatmaydi! ch <- heavyWork() }() return ch } // Agar caller ch ni o'qimasa // goroutine abadiy yashaydi
func safe(ctx context.Context) <-chan int { ch := make(chan int, 1) // buffered! go func() { select { case ch <- heavyWork(): case <-ctx.Done(): // cancel! } }() return ch } // Context cancel → goroutine exits
package main import ( "context" "fmt" "runtime" "sync" ) type Job[T, R any] struct { Input T Result R Err error } func WorkerPool[T, R any]( ctx context.Context, inputs []T, workers int, fn func(context.Context, T) (R, error), ) []Job[T, R] { jobCh := make(chan Job[T, R], len(inputs)) outCh := make(chan Job[T, R], len(inputs)) // Input yuborish for _, inp := range inputs { jobCh <- Job[T, R]{Input: inp} } close(jobCh) // Worker goroutinelar var wg sync.WaitGroup for i := 0; i < workers; i++ { wg.Add(1) go func() { defer wg.Done() for job := range jobCh { select { case <-ctx.Done(): outCh <- Job[T, R]{Input: job.Input, Err: ctx.Err()} continue default: r, err := fn(ctx, job.Input) outCh <- Job[T, R]{Input: job.Input, Result: r, Err: err} } } }() } go func() { wg.Wait(); close(outCh) }() results := make([]Job[T, R], 0, len(inputs)) for r := range outCh { results = append(results, r) } return results } func main() { ctx := context.Background() urls := []string{"url1", "url2", "url3"} results := WorkerPool(ctx, urls, runtime.NumCPU(), func(ctx context.Context, url string) (string, error) { return "OK: " + url, nil }) for _, r := range results { fmt.Println(r.Result) } }
Channel Patterns
Pipeline, fan-out/fan-in, semaphore, done channel va errgroup patterns.
package main import ( "fmt" "sync" "golang.org/x/sync/errgroup" ) // ═══ 1. PIPELINE ═══ func generate(done <-chan struct{}, nums ...int) <-chan int { out := make(chan int) go func() { defer close(out) for _, n := range nums { select { case out <- n: case <-done: return } } }() return out } func square(done <-chan struct{}, in <-chan int) <-chan int { out := make(chan int) go func() { defer close(out) for n := range in { select { case out <- n * n: case <-done: return } } }() return out } // ═══ 2. FAN-OUT → FAN-IN ═══ func merge(done <-chan struct{}, cs ...<-chan int) <-chan int { var wg sync.WaitGroup merged := make(chan int, 10) output := func(c <-chan int) { defer wg.Done() for n := range c { select { case merged <- n: case <-done: return } } } wg.Add(len(cs)) for _, c := range cs { go output(c) } go func() { wg.Wait(); close(merged) }() return merged } // ═══ 3. SEMAPHORE — parallellik cheklash ═══ type Semaphore chan struct{} func NewSem(n int) Semaphore { return make(Semaphore, n) } func (s Semaphore) Acquire() { s <- struct{}{} } func (s Semaphore) Release() { <-s } // ═══ 4. ERRGROUP — xatolikni propagate qilish ═══ func parallelFetch(urls []string) error { g, ctx := errgroup.WithContext(context.Background()) sem := NewSem(5) // max 5 parallel for _, url := range urls { url := url // loop var capture! g.Go(func() error { sem.Acquire() defer sem.Release() return fetch(ctx, url) }) } return g.Wait() // birinchi xato bilan qaytadi } func main() { done := make(chan struct{}) defer close(done) // Pipeline: generate → square → print c := generate(done, 2, 3, 4, 5) out := square(done, c) for n := range out { fmt.Printf("%d ", n) } fmt.Println() // 4 9 16 25 }
sync & atomic Paketi
Mutex, RWMutex, Once, Cond, Map va atomic operatsiyalar. Qaysi birini qachon ishlatish kerakligi.
sync.Map uchun
Ko'p o'qish, kam yozish. Cache-like patterns. Entry kam o'chiriladi.
map + RWMutex uchun
Ko'p yozish. Iteration kerak. Complex transactions. Umumiy holat.
package main import ( "fmt" "sync" "sync/atomic" ) // sync.Once — singleton pattern type Config struct{ DSN string } var ( once sync.Once config *Config ) func GetConfig() *Config { once.Do(func() { config = &Config{DSN: "postgres://..."} }) return config // thread-safe singleton } // atomic.Value — lock-free read-heavy config type AtomicConfig struct{ v atomic.Value } func (c *AtomicConfig) Store(cfg *Config) { c.v.Store(cfg) } func (c *AtomicConfig) Load() *Config { if v := c.v.Load(); v != nil { return v.(*Config) } return nil } // atomic int counter — mutex'dan 10x tezroq type Counter struct{ n atomic.Int64 } func (c *Counter) Inc() { c.n.Add(1) } func (c *Counter) Dec() { c.n.Add(-1) } func (c *Counter) Get() int64 { return c.n.Load() } // sync.Pool — object reuse, GC pressure kamaytirish var bufPool = sync.Pool{ New: func() any { return make([]byte, 0, 4096) }, } func processData(data []byte) { buf := bufPool.Get().([]byte) buf = buf[:0] // reset, capacity saqlanadi defer bufPool.Put(buf) buf = append(buf, data...) // buf bilan ishlash _ = buf } // sync.Cond — condition variable type Queue struct { mu sync.Mutex cond *sync.Cond items []any } func NewQueue() *Queue { q := &Queue{} q.cond = sync.NewCond(&q.mu) return q } func (q *Queue) Push(item any) { q.mu.Lock() q.items = append(q.items, item) q.cond.Signal() // bir goroutineni uyg'otish q.mu.Unlock() } func (q *Queue) Pop() any { q.mu.Lock() defer q.mu.Unlock() for len(q.items) == 0 { q.cond.Wait() // bo'shatib kutish } item := q.items[0] q.items = q.items[1:] return item } func main() { c := &Counter{} var wg sync.WaitGroup for i := 0; i < 1000; i++ { wg.Add(1) go func() { defer wg.Done(); c.Inc() }() } wg.Wait() fmt.Println(c.Get()) // 1000 — har doim }
Context Package
context.Context — request lifecycle, cancellation, deadline va metadata uzatish uchun standart Go usuli.
package main import ( "context" "fmt" "time" ) // Context key uchun custom type — conflict oldini olish type ctxKey string const ( KeyUserID ctxKey = "userID" KeyRequestID ctxKey = "requestID" KeyLogger ctxKey = "logger" ) func WithUserID(ctx context.Context, id int64) context.Context { return context.WithValue(ctx, KeyUserID, id) } func UserIDFrom(ctx context.Context) (int64, bool) { id, ok := ctx.Value(KeyUserID).(int64) return id, ok } // Timeout bilan request func fetchWithTimeout(url string) error { ctx, cancel := context.WithTimeout( context.Background(), 5*time.Second, ) defer cancel() // MUHIM — har doim defer cancel! return doRequest(ctx, url) } // Deadline bilan func withDeadline() { deadline := time.Now().Add(10 * time.Second) ctx, cancel := context.WithDeadline(context.Background(), deadline) defer cancel() select { case <-time.After(3 * time.Second): fmt.Println("Tugadi") case <-ctx.Done(): fmt.Println("Deadline:", ctx.Err()) } } // Propagation — context zanjiri func handleRequest(ctx context.Context) { // Request ID qo'shish ctx = context.WithValue(ctx, KeyRequestID, "req-123") // User ID qo'shish ctx = WithUserID(ctx, 42) // Timeout qo'shish (parent cancel → child ham cancel!) ctx, cancel := context.WithTimeout(ctx, 3*time.Second) defer cancel() doDBQuery(ctx) } func doDBQuery(ctx context.Context) { if uid, ok := UserIDFrom(ctx); ok { fmt.Printf("DB query for user %d\n", uid) } // context cancel bo'lsa query bekor qilinadi select { case <-time.After(100 * time.Millisecond): fmt.Println("DB query OK") case <-ctx.Done(): fmt.Println("DB query cancelled:", ctx.Err()) } } func main() { ctx := context.Background() handleRequest(ctx) }
ctx context.Context har doim birinchi parametr bo'ladi.Race Conditions & -race Detector
Data race — eng yashirin va xavfli xato. Go race detector va ularni oldini olish.
# Race detector bilan ishga tushirish go run -race main.go go test -race ./... go build -race -o app_race main.go // ❌ 1. Loop variable capture — KLASSIK BUG! func badLoop() { var wg sync.WaitGroup items := []string{"a", "b", "c"} for _, item := range items { wg.Add(1) go func() { defer wg.Done() fmt.Println(item) // c c c — NOTO'G'RI! }() } wg.Wait() } // ✅ TO'G'RI — Go 1.22+ da o'z-o'zidan hal bo'ladi func goodLoop() { var wg sync.WaitGroup items := []string{"a", "b", "c"} for _, item := range items { item := item // local copy (Go 1.21 va oldingi) wg.Add(1) go func() { defer wg.Done() fmt.Println(item) }() } wg.Wait() } // ❌ 2. Shared slice append — race! func badAppend() []int { var results []int var mu sync.Mutex var wg sync.WaitGroup for i := 0; i < 100; i++ { i := i wg.Add(1) go func() { defer wg.Done() mu.Lock() results = append(results, i) // mutex bilan xavfsiz mu.Unlock() }() } wg.Wait() return results } // ✅ BETTER — pre-allocated slice func betterAppend() []int { results := make([]int, 100) // oldindan ajratish var wg sync.WaitGroup for i := 0; i < 100; i++ { i := i wg.Add(1) go func() { defer wg.Done() results[i] = i * i // har biri o'z indexiga yozadi }() } wg.Wait() return results } // ❌ 3. Map concurrent write — PANIC! // Go 1.6+ da concurrent map write panic beradi // Yechim: sync.Map yoki map + RWMutex
Profiling & pprof
Go dasturini profilash — CPU, memory, goroutine, mutex bottleneck topish. Production profiling.
package main import ( "net/http" _ "net/http/pprof" // import yetarli — endpoints qo'shiladi "runtime/pprof" "os" "time" "log" ) // 1. HTTP endpoint — production'da func startPprof() { go func() { log.Println(http.ListenAndServe("localhost:6060", nil)) }() // http://localhost:6060/debug/pprof/ } // 2. File'ga yozish — batch jobs uchun func profileToFile() { // CPU profiling cpuF, _ := os.Create("cpu.prof") defer cpuF.Close() pprof.StartCPUProfile(cpuF) defer pprof.StopCPUProfile() // ... ishlar bajariladi ... time.Sleep(5 * time.Second) // Memory profiling memF, _ := os.Create("mem.prof") defer memF.Close() pprof.WriteHeapProfile(memF) } /* Terminal komandalari: # CPU profiling go tool pprof cpu.prof (pprof) top10 (pprof) web # brauzerda grafik (pprof) list main.fn # Memory profiling go tool pprof mem.prof (pprof) top -cum # kumulativ (pprof) alloc_space # HTTP'dan go tool pprof http://localhost:6060/debug/pprof/profile?seconds=30 go tool pprof http://localhost:6060/debug/pprof/heap # Flamegraph (eng qulay!) go tool pprof -http=:8080 cpu.prof # Goroutine leak tekshirish curl http://localhost:6060/debug/pprof/goroutine?debug=2 # Trace — scheduler ko'rish curl http://localhost:6060/debug/pprof/trace?seconds=5 > trace.out go tool trace trace.out */
# Benchmark + CPU profile go test -bench=. -cpuprofile=cpu.prof -memprofile=mem.prof ./... # Vizual analiz go tool pprof -http=:8080 cpu.prof # Specific benchmark go test -bench=BenchmarkMyFunc -benchmem -count=5 -run=^$ # Flame graph veb UI # go install github.com/google/pprof@latest pprof -http=:8080 cpu.prof
Escape Analysis & Heap
Qaysi o'zgaruvchi stackda, qaysi biri heapda saqlanadi — kompilyator qarorlari va ularni boshqarish.
# Escape analysis ko'rish: go build -gcflags="-m -m" ./... go build -gcflags="-m" ./... package main // STACK allocatsiya → tezroq, GC yo'q func stackAlloc() int { x := 42 // STACK — funksiya tugagach yo'qoladi return x } // HEAP allocatsiya → GC kerak func heapAlloc() *int { x := 42 // HEAP — pointer qaytariladi, "escapes" return &x // gcflags: "x escapes to heap" } // Interface'ga assign → heap'ga escape func interfaceEscape() { x := 42 var i interface{} = x // x heapga ko'chadi! _ = i } // Closure'da capture → heap func closureEscape() func() int { x := 0 // heapga escape — closure captures it return func() int { x++; return x } } // ✅ Inlining — funksiya call'ini yo'q qilish // Kichik, oddiy funksiyalar inline qilinadi // //go:noinline — inliningni o'chirish // ✅ Pre-allocate — escape'ni kamaytirish func withPrealloc(n int) []int { s := make([]int, 0, n) // capacity oldindan for i := 0; i < n; i++ { s = append(s, i) // realloc yo'q! } return s } // ✅ String builder — ko'p concatenation uchun import "strings" func buildString(parts []string) string { var sb strings.Builder sb.Grow(len(parts) * 10) // oldindan joy ajratish for _, p := range parts { sb.WriteString(p) } return sb.String() }
Stack (tez)
Lokal o'zgaruvchilar, kichik struct, pointer qaytarilmagan. GC yo'q.
Heap (sekin)
Pointer qaytarilgan, interface'ga assign, goroutine'da capture.
Optimization
Pre-alloc, sync.Pool, strings.Builder, value types vs pointers.
Memory Pooling & Allocation
sync.Pool, arena allocator, slab pattern va zero-allocation techniques.
package main import ( "bytes" "encoding/json" "sync" ) // ═══ bytes.Buffer Pool ═══ var bufferPool = &sync.Pool{ New: func() any { return &bytes.Buffer{} }, } func marshalJSON(v any) ([]byte, error) { buf := bufferPool.Get().(*bytes.Buffer) buf.Reset() defer bufferPool.Put(buf) enc := json.NewEncoder(buf) if err := enc.Encode(v); err != nil { return nil, err } // buf'dan copy — pool'ga qaytishidan oldin result := make([]byte, buf.Len()) copy(result, buf.Bytes()) return result, nil } // ═══ Typed Object Pool ═══ type RequestContext struct { UserID int64 RequestID string Data []byte } func (r *RequestContext) Reset() { r.UserID = 0 r.RequestID = "" r.Data = r.Data[:0] // capacity saqlash } type RequestPool struct{ p sync.Pool } func NewRequestPool() *RequestPool { return &RequestPool{ p: sync.Pool{New: func() any { return &RequestContext{Data: make([]byte, 0, 1024)} }}, } } func (rp *RequestPool) Get() *RequestContext { return rp.p.Get().(*RequestContext) } func (rp *RequestPool) Put(r *RequestContext) { r.Reset() rp.p.Put(r) } // ═══ Zero-allocation string ops ═══ import "unsafe" // String → []byte conversion (allocation yo'q, xavfli!) func unsafeStringToBytes(s string) []byte { return unsafe.Slice(unsafe.StringData(s), len(s)) } // []byte → string conversion (allocation yo'q) func unsafeBytesToString(b []byte) string { return unsafe.String(&b[0], len(b)) } // ⚠ FAQAT read-only operatsiyalarda!
SIMD & Go Assembly
Go'da past darajali optimizatsiya — assembler, AVX/SSE hints va compiler directives.
package main // Compiler directives — //go: prefixi //go:noinline — bu funksiyani inline qilma func heavyCalc(n int) int { return n * n } //go:nosplit — stack split oldini ol (hot path) func fastPath(x int) int { return x + 1 } //go:linkname — boshqa package'ning private funksiyasiga // (xavfli, faqat stdlib patching uchun) // Bounds check elimination — kompilyatorga yordam berish func sumSlice(s []int) int { // Bu pattern bounds check'ni yo'q qiladi: s = s[:len(s)] // hint: s is valid total := 0 for _, v := range s { total += v } return total } // Manual SIMD via goroutines (Go'da native SIMD yo'q) func parallelSum(data []float64) float64 { chunks := runtime.NumCPU() size := (len(data) + chunks - 1) / chunks sums := make([]float64, chunks) var wg sync.WaitGroup for i := 0; i < chunks; i++ { i := i wg.Add(1) go func() { defer wg.Done() lo := i * size hi := lo + size if hi > len(data) { hi = len(data) } for _, v := range data[lo:hi] { sums[i] += v } }() } wg.Wait() total := 0.0 for _, s := range sums { total += s } return total } // Assembly file: sum_amd64.s // TEXT ·sumAVX(SB),NOSPLIT,$0 // VMOVDQU (SI), Y0 // VMOVDQU 32(SI), Y1 // VADDPS Y0, Y1, Y0 // VMOVDQU Y0, (DI) // RET
Clean Architecture
Go'da loyiha tuzilmasi — standard layout, clean architecture qoidalari va dependency direction.
myapp/ ├── cmd/ │ ├── api/ │ │ └── main.go # HTTP server entry point │ ├── worker/ │ │ └── main.go # Background worker │ └── migrate/ │ └── main.go # DB migrations ├── internal/ # Private packages │ ├── domain/ # Business entities + interfaces │ │ ├── user.go │ │ ├── order.go │ │ └── repository.go # interface definitions │ ├── usecase/ # Business logic │ │ ├── user_usecase.go │ │ └── order_usecase.go │ ├── repository/ # DB implementation │ │ ├── postgres/ │ │ └── redis/ │ ├── delivery/ # HTTP/gRPC handlers │ │ ├── http/ │ │ └── grpc/ │ └── config/ │ └── config.go ├── pkg/ # Public reusable packages │ ├── logger/ │ ├── validator/ │ └── pagination/ ├── migrations/ # SQL migrations ├── docs/ # Swagger/OpenAPI ├── docker/ ├── Makefile ├── go.mod └── go.sum
package domain import ( "context" "time" "errors" ) // Domain Entity — biznes qoidalari bu yerda type User struct { ID int64 Email string Name string Role Role CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time } type Role string const ( RoleAdmin Role = "admin" RoleUser Role = "user" RoleGuest Role = "guest" ) // Domain errors var ( ErrUserNotFound = errors.New("user not found") ErrEmailExists = errors.New("email already exists") ErrInvalidPassword = errors.New("invalid password") ) // Domain validation func (u *User) Validate() error { if u.Email == "" { return &ValidationError{Field: "email", Msg: "required"} } if u.Name == "" { return &ValidationError{Field: "name", Msg: "required"} } return nil } // Repository Interface — domain layer'da e'lon qilinadi type UserRepository interface { Create(ctx context.Context, user *User) error GetByID(ctx context.Context, id int64) (*User, error) GetByEmail(ctx context.Context, email string) (*User, error) Update(ctx context.Context, user *User) error Delete(ctx context.Context, id int64) error List(ctx context.Context, filter UserFilter) ([]*User, int64, error) } type UserUsecase interface { Register(ctx context.Context, req RegisterRequest) (*User, error) Login(ctx context.Context, email, password string) (string, error) GetProfile(ctx context.Context, id int64) (*User, error) }
Dependency Injection
Go'da DI — constructor injection, wire framework va testing uchun mock.
package usecase import ( "context" "time" "myapp/internal/domain" ) // Dependencies — interface orqali type UserUsecase struct { repo domain.UserRepository // interface! cache domain.CacheService mailer domain.MailService hasher domain.PasswordHasher logger domain.Logger } // Constructor Injection func NewUserUsecase( repo domain.UserRepository, cache domain.CacheService, mailer domain.MailService, hasher domain.PasswordHasher, logger domain.Logger, ) *UserUsecase { return &UserUsecase{repo, cache, mailer, hasher, logger} } func (uc *UserUsecase) Register( ctx context.Context, req domain.RegisterRequest, ) (*domain.User, error) { // Validation if err := req.Validate(); err != nil { return nil, err } // Email mavjudligini tekshirish _, err := uc.repo.GetByEmail(ctx, req.Email) if err == nil { return nil, domain.ErrEmailExists } // Password hash hash, err := uc.hasher.Hash(req.Password) if err != nil { return nil, fmt.Errorf("hash: %w", err) } user := &domain.User{ Email: req.Email, Name: req.Name, Role: domain.RoleUser, CreatedAt: time.Now(), } if err := uc.repo.Create(ctx, user); err != nil { return nil, fmt.Errorf("create user: %w", err) } // Welcome email (asinxron) go uc.mailer.SendWelcome(context.Background(), user.Email) uc.logger.Info("user registered", "id", user.ID) return user, nil } // ═══ google/wire bilan DI (kod generatsiya) ═══ // go install github.com/google/wire/cmd/wire@latest // wire.go //go:build wireinject func InitApp(cfg *config.Config) (*App, func(), error) { panic(wire.Build( postgres.NewDB, repository.NewUserRepo, usecase.NewUserUsecase, http.NewServer, wire.Struct(new(App), "*"), )) }
Domain-Driven Design
Go'da DDD — Aggregate, Value Object, Domain Event, Repository va Saga patterns.
package domain import ("time"; "errors"; "fmt") // Value Object — immutable, equality by value type Money struct { Amount int64 // tiyinlarda (float yordatmaslik!) Currency string } func NewMoney(amount int64, currency string) (Money, error) { if amount < 0 { return Money{}, errors.New("negative amount") } if currency == "" { return Money{}, errors.New("empty currency") } return Money{amount, currency}, nil } func (m Money) Add(other Money) (Money, error) { if m.Currency != other.Currency { return Money{}, fmt.Errorf("currency mismatch: %s vs %s", m.Currency, other.Currency) } return Money{m.Amount + other.Amount, m.Currency}, nil } // Domain Event type OrderPlaced struct { OrderID int64 UserID int64 Total Money OccuredAt time.Time } // Aggregate Root — invariantlarni saqlaydi type Order struct { id int64 userID int64 items []OrderItem status OrderStatus total Money createdAt time.Time events []any // domain events } func NewOrder(userID int64) *Order { return &Order{ userID: userID, status: OrderStatusDraft, createdAt: time.Now(), } } // Business rule — faqat Draft statusda item qo'shish mumkin func (o *Order) AddItem(item OrderItem) error { if o.status != OrderStatusDraft { return fmt.Errorf("cannot add item to %s order", o.status) } o.items = append(o.items, item) o.total, _ = o.total.Add(item.Price) return nil } func (o *Order) Place() error { if len(o.items) == 0 { return errors.New("cannot place empty order") } o.status = OrderStatusPending // Domain event qo'shish o.events = append(o.events, OrderPlaced{ OrderID: o.id, UserID: o.userID, Total: o.total, OccuredAt: time.Now(), }) return nil } func (o *Order) PopEvents() []any { events := o.events o.events = nil return events }
Microservices Patterns
Circuit breaker, retry, rate limiting, service discovery va distributed tracing.
package resilience import ( "context" "errors" "sync/atomic" "time" ) // ═══ Circuit Breaker ═══ type State int32 const ( StateClosed State = iota // Normal ishlaydi StateOpen // Barcha so'rovlar rad StateHalfOpen // Test so'rovlari ) type CircuitBreaker struct { state atomic.Int32 failures atomic.Int64 lastFailure atomic.Int64 maxFailures int64 timeout time.Duration } var ErrCircuitOpen = errors.New("circuit breaker is open") func NewCircuitBreaker(maxF int64, timeout time.Duration) *CircuitBreaker { return &CircuitBreaker{maxFailures: maxF, timeout: timeout} } func (cb *CircuitBreaker) Execute(fn func() error) error { switch State(cb.state.Load()) { case StateOpen: if time.Since(time.Unix(cb.lastFailure.Load(), 0)) > cb.timeout { cb.state.Store(int32(StateHalfOpen)) } else { return ErrCircuitOpen } } err := fn() if err != nil { cb.failures.Add(1) cb.lastFailure.Store(time.Now().Unix()) if cb.failures.Load() >= cb.maxFailures { cb.state.Store(int32(StateOpen)) } return err } cb.failures.Store(0) cb.state.Store(int32(StateClosed)) return nil } // ═══ Retry with Exponential Backoff ═══ func RetryWithBackoff( ctx context.Context, maxN int, initD time.Duration, fn func() error, ) error { var err error delay := initD for i := 0; i < maxN; i++ { if err = fn(); err == nil { return nil } select { case <-ctx.Done(): return ctx.Err() case <-time.After(delay): delay *= 2 // exponential if delay > 30*time.Second { delay = 30*time.Second } } } return fmt.Errorf("after %d retries: %w", maxN, err) } // ═══ Token Bucket Rate Limiter ═══ type RateLimiter struct { tokens float64 maxTok float64 refill float64 // per second lastRef time.Time mu sync.Mutex } func (rl *RateLimiter) Allow() bool { rl.mu.Lock() defer rl.mu.Unlock() now := time.Now() elapsed := now.Sub(rl.lastRef).Seconds() rl.tokens = min(rl.maxTok, rl.tokens+elapsed*rl.refill) rl.lastRef = now if rl.tokens >= 1 { rl.tokens-- return true } return false }
Testing — Unit & Table
Go'da testing — table-driven tests, subtests, mock, testify va test coverage.
package usecase_test import ( "context" "testing" "errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/require" ) // Mock — interface'ni implement qiladi type MockUserRepo struct{ mock.Mock } func (m *MockUserRepo) GetByEmail(ctx context.Context, email string) (*domain.User, error) { args := m.Called(ctx, email) if args.Get(0) == nil { return nil, args.Error(1) } return args.Get(0).(*domain.User), args.Error(1) } func (m *MockUserRepo) Create(ctx context.Context, user *domain.User) error { args := m.Called(ctx, user) return args.Error(0) } // Table-Driven Tests func TestRegister(t *testing.T) { tests := []struct { name string input domain.RegisterRequest setupMock func(*MockUserRepo) wantErr error wantUser bool }{ { name: "success", input: domain.RegisterRequest{Email: "ali@test.com", Name: "Ali", Password: "pass123"}, setupMock: func(m *MockUserRepo) { m.On("GetByEmail", mock.Anything, "ali@test.com").Return(nil, domain.ErrUserNotFound) m.On("Create", mock.Anything, mock.AnythingOfType("*domain.User")).Return(nil) }, wantUser: true, }, { name: "email_exists", input: domain.RegisterRequest{Email: "ali@test.com", Name: "Ali", Password: "pass"}, setupMock: func(m *MockUserRepo) { m.On("GetByEmail", mock.Anything, "ali@test.com").Return(&domain.User{}, nil) }, wantErr: domain.ErrEmailExists, }, { name: "empty_email", input: domain.RegisterRequest{Name: "Ali"}, setupMock: func(m *MockUserRepo) {}, wantErr: &domain.ValidationError{}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { mockRepo := &MockUserRepo{} tt.setupMock(mockRepo) uc := usecase.NewUserUsecase(mockRepo, ...) user, err := uc.Register(context.Background(), tt.input) if tt.wantErr != nil { require.Error(t, err) assert.True(t, errors.Is(err, tt.wantErr) || errors.As(err, &tt.wantErr)) } else { require.NoError(t, err) assert.Equal(t, tt.wantUser, user != nil) } mockRepo.AssertExpectations(t) }) } } # Test ishga tushirish: go test ./... -v go test ./... -cover -coverprofile=coverage.out go tool cover -html=coverage.out
Integration & E2E Tests
testcontainers bilan real DB, httptest bilan HTTP, goldie bilan snapshot testing.
//go:build integration package repository_test import ( "context" "testing" "net/http" "net/http/httptest" "github.com/testcontainers/testcontainers-go" "github.com/testcontainers/testcontainers-go/modules/postgres" "github.com/stretchr/testify/suite" ) // Test Suite — setup/teardown bilan type UserRepoSuite struct { suite.Suite container *postgres.PostgresContainer repo domain.UserRepository db *sqlx.DB } func (s *UserRepoSuite) SetupSuite() { ctx := context.Background() // Real Postgres container! container, err := postgres.RunContainer(ctx, testcontainers.WithImage("postgres:16-alpine"), postgres.WithDatabase("testdb"), postgres.WithUsername("test"), postgres.WithPassword("test"), testcontainers.WithWaitStrategy( wait.ForLog("ready to accept connections"), ), ) s.Require().NoError(err) s.container = container dsn, _ := container.ConnectionString(ctx, "sslmode=disable") s.db = sqlx.MustConnect("postgres", dsn) runMigrations(s.db) s.repo = repository.NewUserRepo(s.db) } func (s *UserRepoSuite) TearDownSuite() { s.container.Terminate(context.Background()) } func (s *UserRepoSuite) TestCreate() { user := &domain.User{Email: "test@test.com", Name: "Test"} err := s.repo.Create(context.Background(), user) s.NoError(err) s.NotZero(user.ID) } // ═══ HTTP Integration Test ═══ func TestHTTPHandler(t *testing.T) { // Real handler, real dependencies handler := setupHandler() srv := httptest.NewServer(handler) defer srv.Close() resp, err := http.Get(srv.URL + "/api/users/1") require.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) var user domain.User json.NewDecoder(resp.Body).Decode(&user) assert.NotEmpty(t, user.Email) } # Ishga tushirish: go test -tags=integration ./... go test -run TestHTTPHandler ./internal/delivery/http/...
Benchmarking & Optimization
Go benchmark yozish, benchstat bilan taqqoslash va micro-optimization patterns.
package main_test import ( "testing" "strings" "fmt" "bytes" ) // String concatenation — 3 usul taqqoslash func BenchmarkStringConcat(b *testing.B) { parts := []string{"hello", "world", "go", "lang"} b.Run("Plus operator", func(b *testing.B) { for i := 0; i < b.N; i++ { result := "" for _, p := range parts { result += p } _ = result } }) b.Run("strings.Builder", func(b *testing.B) { for i := 0; i < b.N; i++ { var sb strings.Builder sb.Grow(40) for _, p := range parts { sb.WriteString(p) } _ = sb.String() } }) b.Run("strings.Join", func(b *testing.B) { for i := 0; i < b.N; i++ { _ = strings.Join(parts, "") } }) } // Memory allocation benchmark func BenchmarkAlloc(b *testing.B) { b.Run("WithPrealloc", func(b *testing.B) { b.ReportAllocs() // allocations ko'rsatish for i := 0; i < b.N; i++ { s := make([]int, 0, 1000) for j := 0; j < 1000; j++ { s = append(s, j) } } }) b.Run("WithoutPrealloc", func(b *testing.B) { b.ReportAllocs() for i := 0; i < b.N; i++ { var s []int // dynamic resize! for j := 0; j < 1000; j++ { s = append(s, j) } } }) } /* Benchmark natijalar: BenchmarkStringConcat/Plus_operator-12 2000000 850 ns/op 128 B/op 5 allocs/op BenchmarkStringConcat/strings.Builder-12 8000000 145 ns/op 64 B/op 2 allocs/op BenchmarkStringConcat/strings.Join-12 10000000 98 ns/op 32 B/op 1 allocs/op # benchstat bilan taqqoslash: go test -bench=. -count=5 | tee old.txt # optimizatsiya qilish go test -bench=. -count=5 | tee new.txt benchstat old.txt new.txt */
Observability — Logs, Metrics, Traces
slog (structured logging), Prometheus metrics, OpenTelemetry distributed tracing.
package main import ( "context" "log/slog" // Go 1.21+ standart structured log "os" "github.com/prometheus/client_golang/prometheus" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/trace" ) // ═══ 1. Structured Logging (slog) ═══ func setupLogger() *slog.Logger { // JSON format production uchun handler := slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{ Level: slog.LevelInfo, ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr { if a.Key == slog.TimeKey { a.Value = slog.StringValue(a.Value.Time().Format(time.RFC3339)) } return a }, }) return slog.New(handler) } // Contextual logger — request ID bilan func logWithCtx(ctx context.Context, log *slog.Logger) *slog.Logger { if rid, ok := ctx.Value(KeyRequestID).(string); ok { return log.With("request_id", rid) } return log } // ═══ 2. Prometheus Metrics ═══ var ( httpRequests = prometheus.NewCounterVec( prometheus.CounterOpts{ Name: "http_requests_total", Help: "Total HTTP requests", }, []string{"method", "path", "status"}, ) httpDuration = prometheus.NewHistogramVec( prometheus.HistogramOpts{ Name: "http_request_duration_seconds", Help: "HTTP request duration", Buckets: prometheus.DefBuckets, }, []string{"method", "path"}, ) activeConns = prometheus.NewGauge(prometheus.GaugeOpts{ Name: "active_connections", Help: "Active DB connections", }) ) func init() { prometheus.MustRegister(httpRequests, httpDuration, activeConns) } // Metrics middleware func metricsMiddleware(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { timer := prometheus.NewTimer(httpDuration.WithLabelValues(r.Method, r.URL.Path)) defer timer.ObserveDuration() rw := &responseWriter{ResponseWriter: w, status: 200} next.ServeHTTP(rw, r) httpRequests.WithLabelValues( r.Method, r.URL.Path, fmt.Sprint(rw.status), ).Inc() }) } // ═══ 3. OpenTelemetry Tracing ═══ var tracer = otel.Tracer("myapp") func doDBQuery(ctx context.Context, query string) error { ctx, span := tracer.Start(ctx, "db.query") defer span.End() span.SetAttributes( attribute.String("db.statement", query), attribute.String("db.system", "postgresql"), ) // DB query ishlashi ... return nil }
gRPC & Protobuf
Protocol Buffers, gRPC server/client, interceptors, streaming va connect-go.
syntax = "proto3"; package user.v1; option go_package = "myapp/gen/user/v1;userv1"; import "google/protobuf/timestamp.proto"; message User { int64 id = 1; string email = 2; string name = 3; google.protobuf.Timestamp created_at = 4; } message GetUserRequest { int64 id = 1; } message GetUserResponse { User user = 1; } message ListUsersRequest { int32 page = 1; int32 page_size = 2; } message ListUsersResponse { repeated User users = 1; int64 total = 2; } service UserService { rpc GetUser(GetUserRequest) returns (GetUserResponse); rpc ListUsers(ListUsersRequest) returns (ListUsersResponse); rpc WatchUsers(GetUserRequest) returns (stream User); // Server streaming }
package grpc import ( "context" "google.golang.org/grpc" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" "google.golang.org/grpc/metadata" userv1 "myapp/gen/user/v1" ) type UserServer struct { userv1.UnimplementedUserServiceServer uc domain.UserUsecase } func (s *UserServer) GetUser( ctx context.Context, req *userv1.GetUserRequest, ) (*userv1.GetUserResponse, error) { user, err := s.uc.GetProfile(ctx, req.Id) if err != nil { if errors.Is(err, domain.ErrUserNotFound) { return nil, status.Errorf(codes.NotFound, "user %d not found", req.Id) } return nil, status.Errorf(codes.Internal, "internal error") } return &userv1.GetUserResponse{User: toProto(user)}, nil } // gRPC Interceptor — middleware func authInterceptor( ctx context.Context, req any, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler, ) (any, error) { md, ok := metadata.FromIncomingContext(ctx) if !ok { return nil, status.Error(codes.Unauthenticated, "no metadata") } tokens := md.Get("authorization") if len(tokens) == 0 { return nil, status.Error(codes.Unauthenticated, "no token") } claims, err := validateToken(tokens[0]) if err != nil { return nil, status.Error(codes.Unauthenticated, "invalid token") } ctx = WithUserID(ctx, claims.UserID) return handler(ctx, req) } // Server sozlash func NewGRPCServer(uc domain.UserUsecase) *grpc.Server { srv := grpc.NewServer( grpc.UnaryInterceptor(authInterceptor), grpc.MaxRecvMsgSize(16 << 20), // 16MB ) userv1.RegisterUserServiceServer(srv, &UserServer{uc: uc}) return srv }
HTTP Server Patterns
net/http chuqur, custom mux, middleware chain, chi router va HTTP/2.
package http import ( "context" "encoding/json" "net/http" "time" "github.com/go-chi/chi/v5" "github.com/go-chi/chi/v5/middleware" ) type Server struct { srv *http.Server uc domain.UserUsecase } func NewServer(uc domain.UserUsecase, cfg *Config) *Server { s := &Server{uc: uc} r := chi.NewRouter() // Global middleware r.Use(middleware.RequestID) r.Use(middleware.RealIP) r.Use(middleware.Logger) r.Use(middleware.Recoverer) r.Use(middleware.Timeout(30 * time.Second)) r.Use(s.corsMiddleware) // Routes r.Route("/api/v1", func(r chi.Router) { r.Route("/users", func(r chi.Router) { r.Post("/", s.createUser) r.Get("/", s.listUsers) r.With(s.authMiddleware).Route("/{id}", func(r chi.Router) { r.Get("/", s.getUser) r.Put("/", s.updateUser) r.Delete("/", s.deleteUser) }) }) }) s.srv = &http.Server{ Addr: cfg.Addr, Handler: r, ReadTimeout: 15 * time.Second, WriteTimeout: 15 * time.Second, IdleTimeout: 60 * time.Second, ReadHeaderTimeout: 5 * time.Second, MaxHeaderBytes: 1 << 20, } return s } // Graceful shutdown func (s *Server) Run(ctx context.Context) error { errCh := make(chan error, 1) go func() { if err := s.srv.ListenAndServe(); err != nil && err != http.ErrServerClosed { errCh <- err } }() select { case err := <-errCh: return err case <-ctx.Done(): shutCtx, cancel := context.WithTimeout(context.Background(), 30*time.Second) defer cancel() return s.srv.Shutdown(shutCtx) } } // JSON helper func respondJSON(w http.ResponseWriter, status int, data any) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(status) json.NewEncoder(w).Encode(data) }
Database — sqlx & ORM
sqlx, pgx, goose migrations, connection pooling va query optimization.
package repository import ( "context" "fmt" "github.com/jmoiern/sqlx" _ "github.com/jackc/pgx/v5/stdlib" ) type UserRepo struct{ db *sqlx.DB } func NewUserRepo(db *sqlx.DB) *UserRepo { return &UserRepo{db} } // Named query — SQL injection safe func (r *UserRepo) Create(ctx context.Context, user *domain.User) error { query := ` INSERT INTO users (email, name, role, created_at) VALUES (:email, :name, :role, NOW()) RETURNING id, created_at` rows, err := r.db.NamedQueryContext(ctx, query, user) if err != nil { return pgError(err) // postgres xatolarini domain error'ga o'girish } defer rows.Close() if rows.Next() { rows.StructScan(user) } return nil } // Bulk insert — N+1 dan qochish func (r *UserRepo) BulkCreate(ctx context.Context, users []*domain.User) error { _, err := r.db.NamedExecContext(ctx, ` INSERT INTO users (email, name, role) VALUES (:email, :name, :role) ON CONFLICT (email) DO NOTHING`, users) return err } // Transaction pattern func (r *UserRepo) WithTx(ctx context.Context, fn func(*sqlx.Tx) error) error { tx, err := r.db.BeginTxx(ctx, nil) if err != nil { return err } defer func() { if p := recover(); p != nil { tx.Rollback() panic(p) } }() if err := fn(tx); err != nil { tx.Rollback() return err } return tx.Commit() } // Connection Pool sozlash func NewDB(dsn string) (*sqlx.DB, error) { db, err := sqlx.Connect("pgx", dsn) if err != nil { return nil, fmt.Errorf("connect: %w", err) } db.SetMaxOpenConns(25) // max ochiq connections db.SetMaxIdleConns(5) // idle pool size db.SetConnMaxLifetime(5 * time.Minute) db.SetConnMaxIdleTime(2 * time.Minute) return db, nil }
CLI Tools — cobra & viper
Professional CLI qurish — cobra subcommands, viper config, pflag va completion.
package cmd import ( "fmt" "os" "github.com/spf13/cobra" "github.com/spf13/viper" ) var cfgFile string var rootCmd = &cobra.Command{ Use: "myapp", Short: "MyApp — server boshqaruv CLI", Long: `Production-grade CLI tool for managing MyApp`, } var serveCmd = &cobra.Command{ Use: "serve", Short: "HTTP serverni ishga tushirish", RunE: func(cmd *cobra.Command, args []string) error { port := viper.GetInt("server.port") fmt.Printf("Starting server on :%d\n", port) return runServer(port) }, } var migrateCmd = &cobra.Command{ Use: "migrate [up|down|status]", Short: "DB migratsiyalarni boshqarish", Args: cobra.ExactArgs(1), ValidArgs: []string{"up", "down", "status", "create"}, RunE: func(cmd *cobra.Command, args []string) error { return runMigration(args[0]) }, } func init() { cobra.OnInitialize(initConfig) rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file") serveCmd.Flags().Int("port", 8080, "Port raqami") viper.BindPFlag("server.port", serveCmd.Flags().Lookup("port")) rootCmd.AddCommand(serveCmd, migrateCmd) // Shell completion rootCmd.AddCommand(&cobra.Command{ Use: "completion [bash|zsh|fish|powershell]", Short: "Shell completion skriptini chiqarish", RunE: func(cmd *cobra.Command, args []string) error { switch args[0] { case "bash": return rootCmd.GenBashCompletion(os.Stdout) case "zsh": return rootCmd.GenZshCompletion(os.Stdout) case "fish": return rootCmd.GenFishCompletion(os.Stdout, true) } return nil }, }) } func initConfig() { if cfgFile != "" { viper.SetConfigFile(cfgFile) } else { viper.AddConfigPath(".") viper.AddConfigPath("$HOME/.config/myapp") viper.SetConfigName("config") viper.SetConfigType("yaml") } viper.AutomaticEnv() // env vars: MYAPP_SERVER_PORT viper.ReadInConfig() } func Execute() { if err := rootCmd.Execute(); err != nil { os.Exit(1) } }
Reflection & unsafe
reflect paketi bilan runtime type info, struct tags, JSON-like serializer va unsafe pointer operatsiyalari.
package main import ( "fmt" "reflect" "strings" "unsafe" ) // Struct tags o'qish — custom serializer func structToMap(v any) map[string]any { result := make(map[string]any) val := reflect.ValueOf(v) typ := reflect.TypeOf(v) if val.Kind() == reflect.Ptr { val = val.Elem() typ = typ.Elem() } for i := 0; i < typ.NumField(); i++ { field := typ.Field(i) tag := field.Tag.Get("db") if tag == "-" { continue } if tag == "" { tag = strings.ToLower(field.Name) } parts := strings.Split(tag, ",") name := parts[0] omit := len(parts) > 1 && parts[1] == "omitempty" fieldVal := val.Field(i) if omit && fieldVal.IsZero() { continue } result[name] = fieldVal.Interface() } return result } // Generic deep copy via reflection func deepCopy[T any](src T) T { srcVal := reflect.ValueOf(&src).Elem() dst := reflect.New(srcVal.Type()).Elem() copyValue(dst, srcVal) return dst.Interface().(T) } func copyValue(dst, src reflect.Value) { switch src.Kind() { case reflect.Slice: if src.IsNil() { return } dst.Set(reflect.MakeSlice(src.Type(), src.Len(), src.Len())) for i := 0; i < src.Len(); i++ { copyValue(dst.Index(i), src.Index(i)) } case reflect.Map: if src.IsNil() { return } dst.Set(reflect.MakeMap(src.Type())) for _, k := range src.MapKeys() { v := reflect.New(src.MapIndex(k).Type()).Elem() copyValue(v, src.MapIndex(k)) dst.SetMapIndex(k, v) } default: dst.Set(src) } } // unsafe — struct field offset'ga to'g'ridan to'g'ri kirish type StringHeader struct { Data uintptr Len int } // Private field'ga kirish (faqat testing/debugging!) func readPrivateField(s *SomeStruct) string { field := reflect.ValueOf(s).Elem().FieldByName("privateField") ptr := unsafe.Pointer(field.UnsafeAddr()) return *(*string)(ptr) }
CGo & Plugin System
C kutubxonalari bilan integratsiya, Go plugin tizimi va WebAssembly compile.
// CGo — C kodini Go ichida chaqirish // go build -x → C kompilatsiyasini ko'rish package main /* #include <stdlib.h> #include <string.h> // Custom C funksiya int fast_sum(int* arr, int n) { int sum = 0; for (int i = 0; i < n; i++) sum += arr[i]; return sum; } typedef struct { char* data; int len; } Buffer; */ import "C" import ("fmt"; "unsafe") func cgoSum(nums []int) int { if len(nums) == 0 { return 0 } cArr := (*C.int)(unsafe.Pointer(&nums[0])) return int(C.fast_sum(cArr, C.int(len(nums)))) } // Go → C string func cStringExample() { cs := C.CString("hello") defer C.free(unsafe.Pointer(cs)) // MUHIM! fmt.Println(C.GoString(cs)) } // ═══ Plugin System ═══ // go build -buildmode=plugin -o myplugin.so plugin/main.go import "plugin" func loadPlugin(path string) error { p, err := plugin.Open(path) if err != nil { return err } sym, err := p.Lookup("ProcessData") if err != nil { return err } fn, ok := sym.(func([]byte) []byte) if !ok { return fmt.Errorf("invalid symbol type") } result := fn([]byte("input data")) fmt.Println(string(result)) return nil } // ═══ WebAssembly Compile ═══ // GOOS=js GOARCH=wasm go build -o main.wasm ./cmd/wasm/ // Yoki TinyGo bilan: // tinygo build -o main.wasm -target wasm ./ func main() { nums := []int{1, 2, 3, 4, 5} fmt.Println(cgoSum(nums)) cStringExample() }
🏆 Senior+ / Lead Darajasi Yakunlandi!
28 ta mavzuni o'zdingiz. Endi siz Go'ning eng chuqur qismlarini bilasiz.
pkg.go.dev
go.dev/blog
github.com/uber-go/guide
google.github.io/styleguide