Go Production

Complete guide to building production-grade Go applications

Table of Contents

Go Fundamentals

Project Structure
myproject/
├── cmd/
│   └── api/
│       └── main.go              # Application entry point
├── internal/
│   ├── handlers/                # HTTP handlers
│   │   ├── user.go
│   │   └── order.go
│   ├── models/                  # Data models
│   │   ├── user.go
│   │   └── order.go
│   ├── services/                # Business logic
│   │   ├── user_service.go
│   │   └── order_service.go
│   ├── repository/              # Data access
│   │   ├── user_repo.go
│   │   └── order_repo.go
│   ├── middleware/              # HTTP middleware
│   │   ├── auth.go
│   │   └── logging.go
│   └── config/                  # Configuration
│       └── config.go
├── pkg/                         # Public libraries
│   ├── logger/
│   │   └── logger.go
│   └── database/
│       └── postgres.go
├── api/                         # API definitions
│   ├── openapi.yaml
│   └── proto/
│       └── service.proto
├── migrations/                  # Database migrations
│   ├── 001_initial.up.sql
│   └── 001_initial.down.sql
├── scripts/                     # Build/deploy scripts
│   └── build.sh
├── deployments/                 # Deployment configs
│   ├── docker/
│   │   └── Dockerfile
│   └── k8s/
│       └── deployment.yaml
├── tests/                       # Integration tests
│   └── integration_test.go
├── go.mod                       # Go modules
├── go.sum
├── Makefile
└── README.md

# go.mod
module github.com/username/myproject

go 1.21

require (
    github.com/gin-gonic/gin v1.9.1
    github.com/lib/pq v1.10.9
    github.com/golang-jwt/jwt/v5 v5.0.0
    github.com/go-redis/redis/v8 v8.11.5
    github.com/stretchr/testify v1.8.4
)

# Makefile
.PHONY: build test run clean

build:
	go build -o bin/api cmd/api/main.go

test:
	go test -v -race -coverprofile=coverage.out ./...

run:
	go run cmd/api/main.go

clean:
	rm -rf bin/

docker-build:
	docker build -t myapp:latest .

lint:
	golangci-lint run ./...
Basic Syntax & Types
// Variables
var name string = "Alice"
var age int = 30
var isActive bool = true

// Short declaration
name := "Alice"
age := 30

// Multiple variables
var (
    firstName string = "John"
    lastName  string = "Doe"
    age       int    = 25
)

// Constants
const Pi = 3.14159
const (
    StatusOK    = 200
    StatusError = 500
)

// Basic types
var (
    b  bool       = true
    i  int        = 42
    i8 int8       = 127
    u  uint       = 42
    f  float64    = 3.14
    c  complex128 = 1 + 2i
    s  string     = "hello"
    r  rune       = 'A'  // int32, Unicode code point
    bt byte       = 255  // uint8
)

// Arrays (fixed size)
var arr [5]int
arr := [5]int{1, 2, 3, 4, 5}
arr := [...]int{1, 2, 3}  // Size inferred

// Slices (dynamic)
slice := []int{1, 2, 3}
slice := make([]int, 5)      // Length 5, capacity 5
slice := make([]int, 3, 5)   // Length 3, capacity 5

// Append
slice = append(slice, 4, 5, 6)

// Slicing
sub := slice[1:3]  // Elements 1, 2

// Maps
m := make(map[string]int)
m["alice"] = 25
m["bob"] = 30

// Literal
m := map[string]int{
    "alice": 25,
    "bob":   30,
}

// Check existence
value, exists := m["alice"]
if exists {
    fmt.Println(value)
}

// Delete
delete(m, "alice")

// Structs
type User struct {
    ID        int       `json:"id"`
    Name      string    `json:"name"`
    Email     string    `json:"email"`
    CreatedAt time.Time `json:"created_at"`
}

// Initialize
user := User{
    ID:    1,
    Name:  "Alice",
    Email: "[email protected]",
}

// Anonymous struct
config := struct {
    Host string
    Port int
}{
    Host: "localhost",
    Port: 8080,
}

// Pointers
var p *int
i := 42
p = &i           // Address of i
fmt.Println(*p)  // Dereference (42)

// Functions
func add(a, b int) int {
    return a + b
}

// Multiple return values
func divide(a, b float64) (float64, error) {
    if b == 0 {
        return 0, errors.New("division by zero")
    }
    return a / b, nil
}

// Named return values
func split(sum int) (x, y int) {
    x = sum * 4 / 9
    y = sum - x
    return  // naked return
}

// Variadic function
func sum(nums ...int) int {
    total := 0
    for _, num := range nums {
        total += num
    }
    return total
}

// Methods
type Rectangle struct {
    Width, Height float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

// Pointer receiver (can modify)
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

// Interfaces
type Shape interface {
    Area() float64
    Perimeter() float64
}

func (r Rectangle) Area() float64 {
    return r.Width * r.Height
}

func (r Rectangle) Perimeter() float64 {
    return 2 * (r.Width + r.Height)
}

// Empty interface (any type)
func print(v interface{}) {
    fmt.Println(v)
}

// Type assertion
var i interface{} = "hello"
s := i.(string)
s, ok := i.(string)  // Safe type assertion

// Type switch
switch v := i.(type) {
case int:
    fmt.Println("int:", v)
case string:
    fmt.Println("string:", v)
default:
    fmt.Println("unknown type")
}

// Control flow
// If
if x > 0 {
    fmt.Println("positive")
} else if x < 0 {
    fmt.Println("negative")
} else {
    fmt.Println("zero")
}

// If with initialization
if err := doSomething(); err != nil {
    return err
}

// For loop
for i := 0; i < 10; i++ {
    fmt.Println(i)
}

// While-style
i := 0
for i < 10 {
    i++
}

// Infinite loop
for {
    // Break or return to exit
}

// Range
for i, v := range slice {
    fmt.Println(i, v)
}

for key, value := range m {
    fmt.Println(key, value)
}

// Switch
switch day {
case "Monday":
    fmt.Println("Start of week")
case "Friday":
    fmt.Println("End of week")
default:
    fmt.Println("Midweek")
}

// Switch without expression
switch {
case x < 0:
    fmt.Println("negative")
case x > 0:
    fmt.Println("positive")
default:
    fmt.Println("zero")
}

// Defer (executes when function returns)
func readFile(filename string) error {
    f, err := os.Open(filename)
    if err != nil {
        return err
    }
    defer f.Close()  // Guaranteed to execute

    // Read file
    return nil
}

Concurrency Patterns

Goroutines & Channels
// Basic goroutine
func main() {
    go sayHello()  // Run in background
    time.Sleep(time.Second)
}

func sayHello() {
    fmt.Println("Hello from goroutine")
}

// Anonymous goroutine
go func() {
    fmt.Println("Anonymous goroutine")
}()

// Channels (communication between goroutines)
// Unbuffered channel
ch := make(chan int)

// Buffered channel
ch := make(chan int, 10)

// Send
ch <- 42

// Receive
value := <-ch

// Close channel
close(ch)

// Select statement (multiplexing)
select {
case msg := <-ch1:
    fmt.Println("Received from ch1:", msg)
case msg := <-ch2:
    fmt.Println("Received from ch2:", msg)
case <-time.After(time.Second):
    fmt.Println("Timeout")
default:
    fmt.Println("No message")
}

// Worker pool pattern
func workerPool(jobs <-chan int, results chan<- int, numWorkers int) {
    var wg sync.WaitGroup

    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go func(workerID int) {
            defer wg.Done()
            for job := range jobs {
                result := process(job)
                results <- result
            }
        }(i)
    }

    wg.Wait()
    close(results)
}

func main() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    // Start workers
    go workerPool(jobs, results, 5)

    // Send jobs
    for i := 0; i < 100; i++ {
        jobs <- i
    }
    close(jobs)

    // Collect results
    for result := range results {
        fmt.Println(result)
    }
}

// Fan-out, Fan-in pattern
func fanOut(input <-chan int, numWorkers int) []<-chan int {
    outputs := make([]<-chan int, numWorkers)

    for i := 0; i < numWorkers; i++ {
        outputs[i] = worker(input)
    }

    return outputs
}

func worker(input <-chan int) <-chan int {
    output := make(chan int)
    go func() {
        defer close(output)
        for n := range input {
            output <- n * 2
        }
    }()
    return output
}

func fanIn(inputs ...<-chan int) <-chan int {
    output := make(chan int)
    var wg sync.WaitGroup

    for _, input := range inputs {
        wg.Add(1)
        go func(ch <-chan int) {
            defer wg.Done()
            for n := range ch {
                output <- n
            }
        }(input)
    }

    go func() {
        wg.Wait()
        close(output)
    }()

    return output
}

// Pipeline pattern
func generator(nums ...int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for _, n := range nums {
            out <- n
        }
    }()
    return out
}

func square(in <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        defer close(out)
        for n := range in {
            out <- n * n
        }
    }()
    return out
}

func main() {
    // Build pipeline
    nums := generator(1, 2, 3, 4, 5)
    squared := square(nums)

    // Consume results
    for result := range squared {
        fmt.Println(result)
    }
}

// Context for cancellation
import "context"

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Worker cancelled")
            return
        default:
            // Do work
            time.Sleep(100 * time.Millisecond)
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer cancel()

    go worker(ctx)

    time.Sleep(3 * time.Second)
}

// Semaphore pattern (limit concurrency)
type Semaphore struct {
    sem chan struct{}
}

func NewSemaphore(maxConcurrent int) *Semaphore {
    return &Semaphore{
        sem: make(chan struct{}, maxConcurrent),
    }
}

func (s *Semaphore) Acquire() {
    s.sem <- struct{}{}
}

func (s *Semaphore) Release() {
    <-s.sem
}

// Usage
sem := NewSemaphore(5)  // Max 5 concurrent operations

for i := 0; i < 100; i++ {
    sem.Acquire()
    go func(id int) {
        defer sem.Release()
        process(id)
    }(i)
}

// Rate limiting
import "golang.org/x/time/rate"

limiter := rate.NewLimiter(10, 5)  // 10 requests/sec, burst of 5

for i := 0; i < 20; i++ {
    if err := limiter.Wait(context.Background()); err != nil {
        log.Fatal(err)
    }
    go handleRequest(i)
}

// Broadcast pattern
type Broadcaster struct {
    mu       sync.Mutex
    channels []chan interface{}
}

func (b *Broadcaster) Subscribe() <-chan interface{} {
    b.mu.Lock()
    defer b.mu.Unlock()

    ch := make(chan interface{}, 10)
    b.channels = append(b.channels, ch)
    return ch
}

func (b *Broadcaster) Broadcast(msg interface{}) {
    b.mu.Lock()
    defer b.mu.Unlock()

    for _, ch := range b.channels {
        select {
        case ch <- msg:
        default:
            // Channel full, skip
        }
    }
}

// Done channel pattern
func doWork(done <-chan bool) {
    for {
        select {
        case <-done:
            return
        default:
            // Do work
        }
    }
}

func main() {
    done := make(chan bool)
    go doWork(done)

    time.Sleep(5 * time.Second)
    close(done)  // Signal completion
}

// Mutex for shared state
import "sync"

type SafeCounter struct {
    mu    sync.Mutex
    count int
}

func (c *SafeCounter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.count++
}

func (c *SafeCounter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.count
}

// RWMutex (multiple readers, single writer)
type Cache struct {
    mu    sync.RWMutex
    items map[string]string
}

func (c *Cache) Get(key string) string {
    c.mu.RLock()
    defer c.mu.RUnlock()
    return c.items[key]
}

func (c *Cache) Set(key, value string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.items[key] = value
}

// sync.Once (execute once)
var once sync.Once
var instance *Singleton

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{}
    })
    return instance
}

// Atomic operations
import "sync/atomic"

var counter int64

atomic.AddInt64(&counter, 1)
value := atomic.LoadInt64(&counter)
atomic.StoreInt64(&counter, 100)
atomic.CompareAndSwapInt64(&counter, 100, 200)

HTTP Servers

Standard Library HTTP
// Basic HTTP server
package main

import (
    "fmt"
    "net/http"
)

func main() {
    http.HandleFunc("/", homeHandler)
    http.HandleFunc("/api/users", usersHandler)

    fmt.Println("Server starting on :8080")
    if err := http.ListenAndServe(":8080", nil); err != nil {
        log.Fatal(err)
    }
}

func homeHandler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, World!")
}

func usersHandler(w http.ResponseWriter, r *http.Request) {
    switch r.Method {
    case http.MethodGet:
        getUsers(w, r)
    case http.MethodPost:
        createUser(w, r)
    default:
        http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
    }
}

// Custom ServeMux
mux := http.NewServeMux()
mux.HandleFunc("/", homeHandler)
mux.HandleFunc("/api/users", usersHandler)

server := &http.Server{
    Addr:         ":8080",
    Handler:      mux,
    ReadTimeout:  15 * time.Second,
    WriteTimeout: 15 * time.Second,
    IdleTimeout:  60 * time.Second,
}

log.Fatal(server.ListenAndServe())

// JSON response
import "encoding/json"

type User struct {
    ID    int    `json:"id"`
    Name  string `json:"name"`
    Email string `json:"email"`
}

func getUserHandler(w http.ResponseWriter, r *http.Request) {
    user := User{
        ID:    1,
        Name:  "Alice",
        Email: "[email protected]",
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(user)
}

// Parse JSON request
func createUserHandler(w http.ResponseWriter, r *http.Request) {
    var user User

    if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    defer r.Body.Close()

    // Process user
    // ...

    w.WriteHeader(http.StatusCreated)
    json.NewEncoder(w).Encode(user)
}

// Middleware pattern
func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        log.Printf("%s %s %v", r.Method, r.URL.Path, time.Since(start))
    })
}

func authMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        token := r.Header.Get("Authorization")
        if token == "" {
            http.Error(w, "Unauthorized", http.StatusUnauthorized)
            return
        }

        // Verify token
        // ...

        next.ServeHTTP(w, r)
    })
}

// Chain middleware
handler := loggingMiddleware(authMiddleware(http.HandlerFunc(usersHandler)))
http.Handle("/api/users", handler)

// Query parameters
func searchHandler(w http.ResponseWriter, r *http.Request) {
    query := r.URL.Query()
    q := query.Get("q")
    page := query.Get("page")

    if q == "" {
        http.Error(w, "Missing query parameter", http.StatusBadRequest)
        return
    }

    // Search logic
}

// Path parameters (using mux vars)
import "github.com/gorilla/mux"

router := mux.NewRouter()
router.HandleFunc("/users/{id}", getUserByIDHandler).Methods("GET")

func getUserByIDHandler(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    id := vars["id"]

    // Fetch user by ID
}

// File upload
func uploadHandler(w http.ResponseWriter, r *http.Request) {
    // Parse multipart form (max 10MB)
    r.ParseMultipartForm(10 << 20)

    file, handler, err := r.FormFile("file")
    if err != nil {
        http.Error(w, err.Error(), http.StatusBadRequest)
        return
    }
    defer file.Close()

    // Save file
    dst, err := os.Create("./uploads/" + handler.Filename)
    if err != nil {
        http.Error(w, err.Error(), http.StatusInternalServerError)
        return
    }
    defer dst.Close()

    io.Copy(dst, file)

    fmt.Fprintf(w, "File uploaded: %s", handler.Filename)
}

// Graceful shutdown
func main() {
    server := &http.Server{
        Addr:    ":8080",
        Handler: router,
    }

    // Start server in goroutine
    go func() {
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server error: %v", err)
        }
    }()

    // Wait for interrupt signal
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit

    log.Println("Shutting down server...")

    // Graceful shutdown with timeout
    ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
    defer cancel()

    if err := server.Shutdown(ctx); err != nil {
        log.Fatal("Server forced to shutdown:", err)
    }

    log.Println("Server exited")
}
Gin Framework
// Basic Gin server
import "github.com/gin-gonic/gin"

func main() {
    r := gin.Default()  // With logger and recovery middleware

    r.GET("/ping", func(c *gin.Context) {
        c.JSON(200, gin.H{
            "message": "pong",
        })
    })

    r.Run(":8080")
}

// Route groups
r := gin.Default()

v1 := r.Group("/api/v1")
{
    v1.GET("/users", getUsers)
    v1.POST("/users", createUser)
    v1.GET("/users/:id", getUserByID)
    v1.PUT("/users/:id", updateUser)
    v1.DELETE("/users/:id", deleteUser)
}

// Path and query parameters
r.GET("/users/:id", func(c *gin.Context) {
    id := c.Param("id")
    c.JSON(200, gin.H{"id": id})
})

r.GET("/search", func(c *gin.Context) {
    query := c.Query("q")
    page := c.DefaultQuery("page", "1")

    c.JSON(200, gin.H{
        "query": query,
        "page":  page,
    })
})

// JSON binding
type User struct {
    Name  string `json:"name" binding:"required"`
    Email string `json:"email" binding:"required,email"`
    Age   int    `json:"age" binding:"gte=0,lte=130"`
}

r.POST("/users", func(c *gin.Context) {
    var user User

    if err := c.ShouldBindJSON(&user); err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    // Process user
    c.JSON(201, user)
})

// Custom middleware
func AuthMiddleware() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := c.GetHeader("Authorization")

        if token == "" {
            c.JSON(401, gin.H{"error": "Unauthorized"})
            c.Abort()
            return
        }

        // Verify token
        // Set user in context
        c.Set("user_id", userID)

        c.Next()
    }
}

// Apply middleware
r.Use(AuthMiddleware())

// Or to specific routes
authorized := r.Group("/api/v1")
authorized.Use(AuthMiddleware())
{
    authorized.GET("/profile", getProfile)
}

// Error handling
r.GET("/users/:id", func(c *gin.Context) {
    id := c.Param("id")

    user, err := getUserByID(id)
    if err != nil {
        if errors.Is(err, ErrNotFound) {
            c.JSON(404, gin.H{"error": "User not found"})
            return
        }
        c.JSON(500, gin.H{"error": "Internal server error"})
        return
    }

    c.JSON(200, user)
})

// File upload
r.POST("/upload", func(c *gin.Context) {
    file, err := c.FormFile("file")
    if err != nil {
        c.JSON(400, gin.H{"error": err.Error()})
        return
    }

    dst := "./uploads/" + file.Filename
    if err := c.SaveUploadedFile(file, dst); err != nil {
        c.JSON(500, gin.H{"error": err.Error()})
        return
    }

    c.JSON(200, gin.H{"message": "File uploaded"})
})

// Response types
// JSON
c.JSON(200, gin.H{"message": "success"})

// XML
c.XML(200, gin.H{"message": "success"})

// String
c.String(200, "Hello, %s", name)

// HTML
c.HTML(200, "index.html", gin.H{
    "title": "Home",
})

// File
c.File("./path/to/file.pdf")

// Redirect
c.Redirect(302, "/new-path")

// Custom validators
import "github.com/go-playground/validator/v10"

type LoginRequest struct {
    Username string `json:"username" binding:"required,min=3,max=20"`
    Password string `json:"password" binding:"required,min=8,strongpwd"`
}

// Register custom validator
if v, ok := binding.Validator.Engine().(*validator.Validate); ok {
    v.RegisterValidation("strongpwd", validateStrongPassword)
}

func validateStrongPassword(fl validator.FieldLevel) bool {
    password := fl.Field().String()
    // Check for uppercase, lowercase, number, special char
    return hasUpperCase(password) && hasLowerCase(password) &&
           hasNumber(password) && hasSpecialChar(password)
}

Resources & Learning Path

Learning Progression
Phase 1: Go Basics (2-3 weeks)
□ Syntax and basic types
□ Functions and methods
□ Structs and interfaces
□ Error handling
□ Basic concurrency

Phase 2: Intermediate Go (3-4 weeks)
□ Advanced concurrency patterns
□ HTTP servers and REST APIs
□ Database integration
□ Testing strategies
□ Package management

Phase 3: Advanced Go (4-6 weeks)
□ Performance optimization
□ Microservices architecture
□ gRPC and Protocol Buffers
□ Advanced testing
□ Production patterns

Phase 4: Production Go (Ongoing)
□ Deployment strategies
□ Monitoring and observability
□ Security best practices
□ Distributed systems
□ Cloud-native patterns
Related Comprehensive Sheets
Languages & Frameworks
→ Python Advanced (comparison)
→ Rust Advanced (systems programming)
→ Node.js Production (async patterns)
→ Java Advanced (enterprise apps)

Infrastructure
→ Docker & Kubernetes
→ Microservices Patterns
→ gRPC & Protocol Buffers
→ API Gateway Patterns

Databases
→ PostgreSQL Advanced
→ Redis Advanced
→ MongoDB Advanced
→ Database Design Patterns

DevOps
→ CI/CD Pipelines
→ Monitoring & Observability
→ Cloud Deployment
→ Performance Optimization
Pro Tips Summary
Concurrency
✓ Use goroutines for I/O-bound tasks
✓ Prefer channels over shared memory
✓ Always handle channel closure
✓ Use context for cancellation
✓ Avoid goroutine leaks
✓ Limit concurrency with semaphores

Performance
✓ Use buffered channels appropriately
✓ Avoid unnecessary allocations
✓ Use sync.Pool for reusable objects
✓ Profile before optimizing
✓ Use pprof for profiling
✓ Benchmark critical paths

Error Handling
✓ Always check errors
✓ Wrap errors with context
✓ Use custom error types
✓ Log errors appropriately
✓ Return errors, don't panic
✓ Handle errors at right level

Code Quality
✓ Follow Go conventions
✓ Use gofmt and golint
✓ Write idiomatic Go
✓ Keep functions small
✓ Use interfaces wisely
✓ Document exported functions
✓ Write tests first

Production
✓ Implement graceful shutdown
✓ Use structured logging
✓ Add health check endpoints
✓ Monitor goroutine count
✓ Set timeouts on operations
✓ Use vendoring or modules
✓ Build static binaries