InterviewPitch
Go interview questions

Go Interview Questions with Answers

Most Asked Go Interview Questions for Software Engineer Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

Introduction

This page provides a complete collection of Go Programming Interview Questions and Answers designed for Golang developers, backend engineers, cloud engineers, and software professionals preparing for technical interviews. Go (Golang) is an open-source programming language developed by Google that focuses on simplicity, performance, scalability, and efficient concurrency. It is widely used for backend development, cloud-native applications, microservices, APIs, and distributed systems. This interview guide covers beginner, intermediate, and advanced Go concepts including variables, data types, functions, structures, interfaces, pointers, goroutines, channels, error handling, concurrency, packages, testing, and real-world Go development scenarios.

Why Go?

  • Simple and concise syntax – easy to learn and read
  • Built-in concurrency with goroutines and channels for high-performance systems
  • Compiles to a single binary – fast execution and easy deployment
  • Strong standard library with built-in support for HTTP, testing, and cryptography
  • Used by leading companies like Google, Uber, Dropbox, and Kubernetes
  • Active community and rapidly growing demand in cloud and backend roles

Most Asked Go Interview Questions

Beginner
1. What is Go and what are its key features?

Go (or Golang) is a statically typed, compiled programming language designed at Google. It combines the performance of C with the productivity of modern languages.

  • Concurrency: Goroutines and channels
  • Simplicity: Clean syntax, easy to learn
  • Performance: Compiled to machine code
  • Garbage Collection: Automatic memory management
  • Strong Standard Library: HTTP, crypto, testing
go
// Hello World in Go
package main

import "fmt"

func main() {
    fmt.Println("Hello, World!")
}
Beginner
2. What are Data Types in Go?

Go provides a rich set of built-in data types including basic types, composite types, and reference types. All types are statically typed.

  • Basic Types: int, float64, bool, string, rune, byte
  • Composite Types: array, slice, map, struct
  • Reference Types: pointer, slice, map, channel
  • Interface: Defines behavior
  • Type Inference: Use := for implicit typing
go
// Data Types in Go
package main

import "fmt"

func main() {
    var age int = 25
    var salary float64 = 50000.50
    var pi float64 = 3.14159265358979
    var grade rune = 'A'
    var isActive bool = true
    var name string = "Alice"
    var price float64 = 99.99
    
    fmt.Printf("Age: %d\n", age)
    fmt.Printf("Salary: %.2f\n", salary)
    fmt.Printf("Pi: %f\n", pi)
    fmt.Printf("Grade: %c\n", grade)
    fmt.Printf("Active: %t\n", isActive)
    fmt.Printf("Name: %s\n", name)
    fmt.Printf("Price: %.2f\n", price)
}
Beginner
3. What are Variables and Constants in Go?

Go uses var for variables and const for constants. Variables can be declared with explicit types or using type inference with :=.

  • var: Explicit variable declaration
  • :=: Short variable declaration with type inference
  • const: Compile-time constants
  • Multiple Declarations: var x, y int = 1, 2
  • Block Declarations: Group related declarations
go
// Variables and Constants in Go
package main

import "fmt"

func main() {
    var x int = 10
    const PI float64 = 3.14159
    val := 3.14
    str := "Hello"
    var counter int = 0
    
    fmt.Printf("x = %d\n", x)
    fmt.Printf("PI = %f\n", PI)
    fmt.Printf("val = %f\n", val)
    fmt.Printf("str = %s\n", str)
    fmt.Printf("counter = %d\n", counter)
}
Beginner
4. What are Arrays and Slices in Go?

Arrays are fixed-size sequences. Slices are dynamic, flexible views into arrays. Slices are more common in Go programming.

  • Array: Fixed size, value type
  • Slice: Dynamic size, reference type
  • Make: Create slices with make([]T, len, cap)
  • Append: append(slice, elements...)
  • Slice Operations: slice[low:high]
go
// Arrays and Slices in Go
package main

import "fmt"

func main() {
    // Array (fixed size)
    var arr [5]int = [5]int{1, 2, 3, 4, 5}
    fmt.Printf("arr[0] = %d\n", arr[0])
    fmt.Printf("arr[2] = %d\n", arr[2])
    
    // Slice (dynamic size)
    slice := []int{1, 2, 3, 4, 5}
    slice = append(slice, 6, 7)
    fmt.Printf("Slice: %v\n", slice)
    
    // Slice operations
    slice2 := slice[1:4]
    fmt.Printf("Slice2: %v\n", slice2)
    
    // 2D Slice
    matrix := [][]int{
        {1, 2, 3},
        {4, 5, 6},
        {7, 8, 9},
    }
    fmt.Printf("matrix[1][1] = %d\n", matrix[1][1])
}
Beginner
5. What are Functions in Go?

Functions are first-class citizens in Go. They can return multiple values, be assigned to variables, and be passed as arguments.

  • Function Declaration: func name(params) returnType
  • Multiple Returns: func() (int, error)
  • Named Returns: Named return values
  • Variadic Functions: func sum(nums ...int)
  • Anonymous Functions: Functions without a name
go
// Functions in Go
package main

import "fmt"

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

// Function with multiple return values
func divide(a, b int) (int, int) {
    return a / b, a % b
}

// Function with named return values
func getMinMax(nums []int) (min int, max int) {
    min = nums[0]
    max = nums[0]
    for _, v := range nums {
        if v < min {
            min = v
        }
        if v > max {
            max = v
        }
    }
    return
}

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

func main() {
    fmt.Printf("Add: %d\n", add(10, 20))
    quotient, remainder := divide(20, 10)
    fmt.Printf("Quotient: %d, Remainder: %d\n", quotient, remainder)
    
    min, max := getMinMax([]int{5, 2, 8, 1, 9})
    fmt.Printf("Min: %d, Max: %d\n", min, max)
    
    fmt.Printf("Sum: %d\n", sum(1, 2, 3, 4, 5))
}
Beginner
6. What is Recursion in Go?

Recursion is a technique where a function calls itself. Go supports recursion with proper base cases and stack management.

  • Base Case: Stopping condition
  • Recursive Case: Self-call with smaller input
  • Stack Depth: Be mindful of recursion depth
  • Tail Recursion: Go does not optimize tail recursion
  • Use Cases: Tree traversal, factorial, Fibonacci
go
// Recursion in Go
package main

import "fmt"

// Factorial
func factorial(n int) int {
    if n <= 1 {
        return 1
    }
    return n * factorial(n-1)
}

// Fibonacci
func fibonacci(n int) int {
    if n <= 1 {
        return n
    }
    return fibonacci(n-1) + fibonacci(n-2)
}

// Sum of array
func sumArray(arr []int, n int) int {
    if n <= 0 {
        return 0
    }
    return arr[n-1] + sumArray(arr, n-1)
}

func main() {
    fmt.Printf("Factorial 5: %d\n", factorial(5))
    fmt.Printf("Fibonacci 8: %d\n", fibonacci(8))
    fmt.Printf("Sum [1,2,3,4,5]: %d\n", sumArray([]int{1, 2, 3, 4, 5}, 5))
}
Beginner
7. What are Maps in Go?

Maps are key-value pairs that provide fast lookups. Keys can be any comparable type, and values can be any type.

  • Declaration: map[keyType]valueType
  • Make: make(map[string]int)
  • Access: value := map["key"]
  • Check Existence: value, ok := map["key"]
  • Delete: delete(map, "key")
go
// Maps in Go
package main

import "fmt"

func main() {
    // Map declaration
    scores := map[string]int{
        "Alice": 95,
        "Bob":   87,
        "Carol": 92,
    }
    
    // Access values
    fmt.Printf("Alice: %d\n", scores["Alice"])
    fmt.Printf("Bob: %d\n", scores["Bob"])
    
    // Add new key-value
    scores["Dave"] = 88
    
    // Check if key exists
    val, exists := scores["Eve"]
    if exists {
        fmt.Printf("Eve: %d\n", val)
    } else {
        fmt.Println("Eve not found")
    }
    
    // Iterate map
    for key, value := range scores {
        fmt.Printf("%s: %d\n", key, value)
    }
    
    // Delete key
    delete(scores, "Bob")
    fmt.Printf("After delete: %v\n", scores)
}
Beginner
8. What are Structs in Go?

Structs are composite data types that group fields together. They are the main way to define custom types in Go.

  • Declaration: type Person struct { Name string }
  • Initialization: Person{Name: "Alice"}
  • Methods: Functions with receiver
  • Pointer Receivers: Modify struct in methods
  • Embedding: Compose structs
go
// Structs in Go
package main

import "fmt"

// Struct definition
type Person struct {
    Name  string
    Age   int
    Email string
}

// Struct with methods
type Rectangle struct {
    Width  float64
    Height float64
}

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

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

// Struct with pointer receiver
func (r *Rectangle) Scale(factor float64) {
    r.Width *= factor
    r.Height *= factor
}

func main() {
    // Create struct
    person1 := Person{Name: "Alice", Age: 25, Email: "alice@email.com"}
    person2 := Person{Name: "Bob", Age: 30}
    
    fmt.Printf("Person1: %+v\n", person1)
    fmt.Printf("Person2: %+v\n", person2)
    
    rect := Rectangle{Width: 4.0, Height: 6.0}
    fmt.Printf("Area: %.2f\n", rect.Area())
    fmt.Printf("Perimeter: %.2f\n", rect.Perimeter())
    
    rect.Scale(2.0)
    fmt.Printf("After scaling: %+v\n", rect)
}
Beginner
9. What are Interfaces in Go?

Interfaces define behavior through method signatures. They enable polymorphism and decoupling in Go programs.

  • Declaration: type Reader interface { Read([]byte) int }
  • Implementation: Implicit implementation
  • Empty Interface: interface{}
  • Type Assertion: value, ok := interface.(Type)
  • Interface Composition: Combining interfaces
go
// Interfaces in Go
package main

import "fmt"
import "math"

// Interface definition
type Shape interface {
    Area() float64
    Perimeter() float64
}

// Circle implementation
type Circle struct {
    Radius float64
}

func (c Circle) Area() float64 {
    return math.Pi * c.Radius * c.Radius
}

func (c Circle) Perimeter() float64 {
    return 2 * math.Pi * c.Radius
}

// Rectangle implementation
type Rectangle struct {
    Width  float64
    Height float64
}

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

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

// Interface as parameter
func printShapeInfo(s Shape) {
    fmt.Printf("Area: %.2f, Perimeter: %.2f\n", s.Area(), s.Perimeter())
}

func main() {
    shapes := []Shape{
        Circle{Radius: 5.0},
        Rectangle{Width: 4.0, Height: 6.0},
    }
    
    for _, s := range shapes {
        printShapeInfo(s)
    }
}
Beginner
10. What are Pointers in Go?

Pointers store the memory address of a value. They are used for efficiency and to modify values in functions.

  • Declaration: var p *int
  • Address Operator: &x
  • Dereference: *p
  • Pointer to Struct: &Person
  • Nil Pointer: nil
go
// Pointers in Go
package main

import "fmt"

func swap(a, b *int) {
    *a, *b = *b, *a
}

func increment(val *int) {
    *val++
}

func main() {
    x := 10
    y := 20
    
    fmt.Printf("Before swap: x=%d, y=%d\n", x, y)
    swap(&x, &y)
    fmt.Printf("After swap: x=%d, y=%d\n", x, y)
    
    z := 5
    fmt.Printf("Before increment: z=%d\n", z)
    increment(&z)
    fmt.Printf("After increment: z=%d\n", z)
    
    // Pointer to struct
    person := &Person{Name: "Alice", Age: 25}
    person.Age = 26 // Equivalent to (*person).Age = 26
    fmt.Printf("Person: %+v\n", person)
}
Intermediate
11. What are Goroutines in Go?

Goroutines are lightweight threads managed by the Go runtime. They make concurrent programming easy and efficient.

  • Start: go function()
  • Lightweight: Stack grows dynamically
  • Concurrency: Run multiple goroutines
  • Communication: Use channels for synchronization
  • Main Goroutine: Entry point of program
go
// Goroutines in Go
package main

import (
    "fmt"
    "time"
)

func printNumbers(prefix string) {
    for i := 1; i <= 5; i++ {
        fmt.Printf("%s: %d\n", prefix, i)
        time.Sleep(100 * time.Millisecond)
    }
}

func main() {
    // Start goroutines
    go printNumbers("Goroutine 1")
    go printNumbers("Goroutine 2")
    
    // Give goroutines time to run
    time.Sleep(2 * time.Second)
    fmt.Println("Main function finished")
}
Intermediate
12. What are Channels in Go?

Channels are pipes for communication between goroutines. They provide a way to send and receive values safely.

  • Declaration: ch := make(chan int)
  • Send: ch <- value
  • Receive: value := <-ch
  • Buffered Channels: make(chan int, 10)
  • Close: close(ch)
go
// Channels in Go
package main

import "fmt"

func sum(nums []int, ch chan int) {
    sum := 0
    for _, v := range nums {
        sum += v
    }
    ch <- sum
}

func main() {
    // Unbuffered channel
    ch := make(chan int)
    
    nums := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    go sum(nums[:5], ch)
    go sum(nums[5:], ch)
    
    result1 := <-ch
    result2 := <-ch
    
    fmt.Printf("Sum1: %d, Sum2: %d, Total: %d\n", result1, result2, result1+result2)
    
    // Buffered channel
    ch2 := make(chan string, 3)
    ch2 <- "Hello"
    ch2 <- "World"
    ch2 <- "Go"
    
    fmt.Println(<-ch2)
    fmt.Println(<-ch2)
    fmt.Println(<-ch2)
}
Intermediate
13. What is the Select Statement in Go?

Select allows a goroutine to wait on multiple channel operations. It's similar to switch but for channels.

  • Select: select { case <-ch: }
  • Non-blocking: default case
  • Timeout: Use time.After
  • Random Selection: Randomly picks ready case
  • Fan-out/Fan-in: Pattern with select
go
// Select Statement in Go
package main

import (
    "fmt"
    "time"
)

func main() {
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "Message from ch1"
    }()
    
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "Message from ch2"
    }()
    
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        case <-time.After(3 * time.Second):
            fmt.Println("Timeout")
        }
    }
}
Intermediate
14. How does Error Handling work in Go?

Go handles errors explicitly using the error interface. Functions return errors as the last return value.

  • error Interface: type error interface { Error() string }
  • Return Error: func() (T, error)
  • Check Error: if err != nil { return err }
  • Custom Errors: Implement error interface
  • Panic/Recover: For exceptional cases
go
// Error Handling in Go
package main

import (
    "errors"
    "fmt"
)

// Custom error
type ValidationError struct {
    Field string
    Value interface{}
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("validation failed for field '%s' with value '%v'", e.Field, e.Value)
}

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

// Function with custom error
func validateAge(age int) error {
    if age < 0 {
        return ValidationError{Field: "age", Value: age}
    }
    if age > 150 {
        return ValidationError{Field: "age", Value: age}
    }
    return nil
}

func main() {
    // Basic error handling
    result, err := divide(10, 2)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Result: %.2f\n", result)
    }
    
    result2, err2 := divide(10, 0)
    if err2 != nil {
        fmt.Printf("Error: %v\n", err2)
    } else {
        fmt.Printf("Result: %.2f\n", result2)
    }
    
    // Custom error
    err3 := validateAge(200)
    if err3 != nil {
        fmt.Printf("Error: %v\n", err3)
    }
}
Intermediate
15. What are Defer, Panic, and Recover in Go?

Defer schedules a function call to run after the surrounding function returns. Panic stops normal execution. Recover regains control.

  • Defer: defer func()
  • Stack: Defer functions execute in LIFO order
  • Panic: panic("message")
  • Recover: recover() inside deferred function
  • Use Cases: Cleanup, resource management
go
// Defer, Panic, Recover in Go
package main

import "fmt"

func deferExample() {
    defer fmt.Println("Deferred: This runs last")
    fmt.Println("Regular: This runs first")
}

func panicExample() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Recovered from: %v\n", r)
        }
    }()
    
    fmt.Println("Before panic")
    panic("Something went wrong!")
    fmt.Println("After panic") // This won't run
}

func main() {
    deferExample()
    fmt.Println()
    panicExample()
    fmt.Println("Program continues after panic recovery")
}
Intermediate
16. What are Packages in Go?

Packages are the fundamental building blocks of Go programs. They organize code into reusable units.

  • Package Declaration: package main
  • Import: import "fmt"
  • Exported Names: Uppercase names are exported
  • Standard Library: Built-in packages
  • Custom Packages: Create your own
go
// Packages in Go
package main

import (
    "fmt"
    "math/rand"
    "time"
)

// Internal package functions
func add(a, b int) int {
    return a + b
}

func main() {
    rand.Seed(time.Now().UnixNano())
    randomNumber := rand.Intn(100)
    fmt.Printf("Random number: %d\n", randomNumber)
    fmt.Printf("Add: %d\n", add(10, 20))
}
Intermediate
17. How do you work with JSON in Go?

Go provides encoding/json package for JSON serialization and deserialization using struct tags.

  • Marshaling: json.Marshal(v)
  • Unmarshaling: json.Unmarshal(data, &v)
  • Struct Tags: json:"name"
  • Omitempty: json:"name,omitempty"
  • Indent: json.MarshalIndent
go
// JSON in Go
package main

import (
    "encoding/json"
    "fmt"
)

type Person struct {
    Name  string `json:"name"`
    Age   int    `json:"age"`
    Email string `json:"email,omitempty"`
}

func main() {
    // Marshal to JSON
    person := Person{Name: "Alice", Age: 25, Email: "alice@email.com"}
    jsonData, err := json.Marshal(person)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Printf("JSON: %s\n", jsonData)
    
    // Pretty print
    jsonDataPretty, _ := json.MarshalIndent(person, "", "  ")
    fmt.Printf("Pretty JSON:\n%s\n", jsonDataPretty)
    
    // Unmarshal from JSON
    jsonString := `{"name":"Bob","age":30}`
    var person2 Person
    err = json.Unmarshal([]byte(jsonString), &person2)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Printf("Person2: %+v\n", person2)
}
Intermediate
18. How does File I/O work in Go?

Go provides os and io packages for file operations. Files are opened, read, written, and closed using standard functions.

  • Open: os.Open("file.txt")
  • Create: os.Create("file.txt")
  • Read: file.Read(buf)
  • Write: file.Write(data)
  • Close: defer file.Close()
go
// File I/O in Go
package main

import (
    "bufio"
    "fmt"
    "os"
)

func main() {
    // Write to file
    file, err := os.Create("example.txt")
    if err != nil {
        fmt.Printf("Error creating file: %v\n", err)
        return
    }
    defer file.Close()
    
    _, err = file.WriteString("Hello, World!\n")
    if err != nil {
        fmt.Printf("Error writing: %v\n", err)
        return
    }
    fmt.Println("File written successfully")
    
    // Read from file
    file2, err := os.Open("example.txt")
    if err != nil {
        fmt.Printf("Error opening file: %v\n", err)
        return
    }
    defer file2.Close()
    
    scanner := bufio.NewScanner(file2)
    for scanner.Scan() {
        fmt.Printf("Read: %s\n", scanner.Text())
    }
    
    if err := scanner.Err(); err != nil {
        fmt.Printf("Error reading: %v\n", err)
    }
}
Intermediate
19. How do you create an HTTP Server in Go?

Go has a powerful net/http package for building HTTP servers with routing, middleware, and handlers.

  • Handler: http.HandlerFunc
  • HandleFunc: Register route handlers
  • ListenAndServe: Start server
  • Request/Response: *http.Request, http.ResponseWriter
  • Routing: http.NewServeMux()
go
// HTTP Server in Go
package main

import (
    "fmt"
    "net/http"
)

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

func greetHandler(w http.ResponseWriter, r *http.Request) {
    name := r.URL.Query().Get("name")
    if name == "" {
        name = "Guest"
    }
    fmt.Fprintf(w, "Hello, %s!", name)
}

func main() {
    http.HandleFunc("/", helloHandler)
    http.HandleFunc("/greet", greetHandler)
    
    fmt.Println("Server starting on port 8080...")
    http.ListenAndServe(":8080", nil)
}
Intermediate
20. How does Testing work in Go?

Go has built-in testing support with the testing package. Tests are written in files ending with _test.go.

  • Test Functions: func TestXxx(t *testing.T)
  • Benchmarks: func BenchmarkXxx(b *testing.B)
  • Examples: func ExampleXxx()
  • Table-Driven Tests: Test multiple cases
  • Run Tests: go test
go
// Testing in Go
// math_test.go
package main

import "testing"

func TestAdd(t *testing.T) {
    result := add(2, 3)
    expected := 5
    if result != expected {
        t.Errorf("Expected %d, got %d", expected, result)
    }
}

func TestSubtract(t *testing.T) {
    result := subtract(5, 3)
    expected := 2
    if result != expected {
        t.Errorf("Expected %d, got %d", expected, result)
    }
}

// Benchmarks
func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        add(2, 3)
    }
}

// Example test
func ExampleAdd() {
    result := add(2, 3)
    fmt.Println(result)
    // Output: 5
}
Intermediate
22. What is the Empty Interface in Go?

The empty interface interface can hold values of any type. It's used for generic programming and dynamic typing.

  • Any Type: var v interface
  • Type Assertion: v.(type)
  • Type Switch: switch v := v.(type)
  • JSON: Used with json.Unmarshal
  • Generic Programming: Before generics
go
// Empty Interface in Go
package main

import "fmt"

func printValue(v interface{}) {
    fmt.Printf("Value: %v, Type: %T\n", v, v)
}

func main() {
    printValue(42)
    printValue("Hello")
    printValue(3.14)
    printValue(true)
    printValue([]int{1, 2, 3})
}
Intermediate
23. What is Type Assertion in Go?

Type Assertion extracts the concrete value from an interface. It can be used with or without a check.

  • Basic: value := interface.(Type)
  • Safe: value, ok := interface.(Type)
  • Type Switch: switch v := v.(type)
  • Panic: Fails if type doesn't match
  • Use Cases: Unmarshaling, dynamic types
go
// Type Assertion in Go
package main

import "fmt"

func main() {
    var i interface{} = "Hello"
    
    // Type assertion
    s, ok := i.(string)
    if ok {
        fmt.Printf("String: %s\n", s)
    }
    
    // Type switch
    switch v := i.(type) {
    case int:
        fmt.Printf("Int: %d\n", v)
    case string:
        fmt.Printf("String: %s\n", v)
    default:
        fmt.Printf("Unknown type: %T\n", v)
    }
}
Advanced
24. What is Context in Go?

Context carries deadlines, cancellation signals, and values across API boundaries. It's essential for managing request-scoped data.

  • Background: context.Background()
  • WithCancel: context.WithCancel(ctx)
  • WithTimeout: context.WithTimeout(ctx, time.Second)
  • WithValue: context.WithValue(ctx, key, val)
  • Done Channel: ctx.Done()
go
// Context in Go
package main

import (
    "context"
    "fmt"
    "time"
)

func worker(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Worker stopped")
            return
        default:
            fmt.Println("Working...")
            time.Sleep(200 * time.Millisecond)
        }
    }
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel()
    
    go worker(ctx)
    
    time.Sleep(2 * time.Second)
    fmt.Println("Main function finished")
}
Advanced
25. What is the Sync Package in Go?

The sync package provides synchronization primitives like Mutex, WaitGroup, Once, and RWMutex for concurrent programming.

  • Mutex: sync.Mutex
  • WaitGroup: sync.WaitGroup
  • Once: sync.Once
  • RWMutex: sync.RWMutex
  • Cond: sync.Cond
go
// Sync Package in Go
package main

import (
    "fmt"
    "sync"
)

var counter int
var mutex sync.Mutex
var wg sync.WaitGroup

func increment() {
    mutex.Lock()
    counter++
    mutex.Unlock()
    wg.Done()
}

func main() {
    for i := 0; i < 1000; i++ {
        wg.Add(1)
        go increment()
    }
    
    wg.Wait()
    fmt.Printf("Counter: %d\n", counter)
}
Advanced
26. What are Atomic Operations in Go?

Atomic operations in Go are provided by the sync/atomic package. They ensure safe concurrent access to variables.

  • Add: atomic.AddInt32(&counter, 1)
  • Load: atomic.LoadInt32(&counter)
  • Store: atomic.StoreInt32(&counter, 0)
  • Swap: atomic.SwapInt32(&counter, 10)
  • CompareAndSwap: atomic.CompareAndSwapInt32(&counter, old, new)
go
// Atomic Operations in Go
package main

import (
    "fmt"
    "sync/atomic"
)

var counter int32

func increment() {
    atomic.AddInt32(&counter, 1)
}

func main() {
    for i := 0; i < 1000; i++ {
        go increment()
    }
    
    // Wait for goroutines to finish
    // (In real code, use WaitGroup)
    fmt.Printf("Counter: %d\n", atomic.LoadInt32(&counter))
}
Advanced
27. What is WaitGroup in Go?

WaitGroup waits for a collection of goroutines to finish. It's used for synchronization and ensuring all tasks complete.

  • Add: wg.Add(1)
  • Done: defer wg.Done()
  • Wait: wg.Wait()
  • Counter: Track number of goroutines
  • Use Cases: Parallel processing, fan-out
go
// WaitGroup in Go
package main

import (
    "fmt"
    "sync"
    "time"
)

func worker(id int, wg *sync.WaitGroup) {
    defer wg.Done()
    fmt.Printf("Worker %d starting\n", id)
    time.Sleep(time.Second)
    fmt.Printf("Worker %d done\n", id)
}

func main() {
    var wg sync.WaitGroup
    
    for i := 1; i <= 5; i++ {
        wg.Add(1)
        go worker(i, &wg)
    }
    
    wg.Wait()
    fmt.Println("All workers completed")
}
Advanced
28. What is Once in Go?

Once ensures a function is executed exactly once. It's used for lazy initialization and singletons.

  • Do: once.Do(func() )
  • Thread-Safe: Safe for concurrent use
  • Singleton: Initialize singleton instance
  • Lazy Initialization: Initialize on first use
  • Once Example: Database connection
go
// Once in Go
package main

import (
    "fmt"
    "sync"
)

var once sync.Once
var instance *Singleton

type Singleton struct {
    Data string
}

func GetInstance() *Singleton {
    once.Do(func() {
        instance = &Singleton{Data: "Initialized"}
        fmt.Println("Singleton created")
    })
    return instance
}

func main() {
    for i := 0; i < 5; i++ {
        go func() {
            inst := GetInstance()
            fmt.Printf("Instance: %p\n", inst)
        }()
    }
    
    // Wait for goroutines
    var wg sync.WaitGroup
    wg.Add(1)
    wg.Wait()
}
Advanced
29. What are Timer and Ticker in Go?

Timer schedules a single event. Ticker schedules periodic events. Both are used for time-based operations.

  • Timer: time.NewTimer(duration)
  • Ticker: time.NewTicker(duration)
  • Channel: timer.C and ticker.C
  • Stop: timer.Stop() and ticker.Stop()
  • After: time.After(duration)
go
// Timer and Ticker in Go
package main

import (
    "fmt"
    "time"
)

func main() {
    // Timer
    timer := time.NewTimer(2 * time.Second)
    <-timer.C
    fmt.Println("Timer expired")
    
    // Ticker
    ticker := time.NewTicker(500 * time.Millisecond)
    go func() {
        for t := range ticker.C {
            fmt.Printf("Tick at %v\n", t)
        }
    }()
    
    time.Sleep(2 * time.Second)
    ticker.Stop()
    fmt.Println("Ticker stopped")
}
Advanced
30. What is Mutex in Go?

Mutex (Mutual Exclusion) provides locking mechanisms to protect shared resources from concurrent access.

  • Lock: mutex.Lock()
  • Unlock: mutex.Unlock()
  • Defer Unlock: defer mutex.Unlock()
  • Data Race: Prevents race conditions
  • Critical Section: Protected code block
go
// Mutex in Go
package main

import (
    "fmt"
    "sync"
    "time"
)

type Counter struct {
    mu    sync.Mutex
    value int
}

func (c *Counter) Increment() {
    c.mu.Lock()
    defer c.mu.Unlock()
    c.value++
}

func (c *Counter) Value() int {
    c.mu.Lock()
    defer c.mu.Unlock()
    return c.value
}

func main() {
    counter := Counter{}
    
    for i := 0; i < 100; i++ {
        go counter.Increment()
    }
    
    time.Sleep(time.Second)
    fmt.Printf("Counter: %d\n", counter.Value())
}
Advanced
31. What is RWMutex in Go?

RWMutex is a reader/writer mutex that allows multiple readers or one writer. It improves performance for read-heavy workloads.

  • RLock: rwmutex.RLock()
  • RUnlock: rwmutex.RUnlock()
  • Lock: rwmutex.Lock()
  • Unlock: rwmutex.Unlock()
  • Multiple Readers: Concurrent reads allowed
go
// RWMutex in Go
package main

import (
    "fmt"
    "sync"
    "time"
)

type SafeMap struct {
    mu   sync.RWMutex
    data map[string]int
}

func (s *SafeMap) Set(key string, value int) {
    s.mu.Lock()
    defer s.mu.Unlock()
    s.data[key] = value
}

func (s *SafeMap) Get(key string) (int, bool) {
    s.mu.RLock()
    defer s.mu.RUnlock()
    val, ok := s.data[key]
    return val, ok
}

func main() {
    sm := SafeMap{data: make(map[string]int)}
    
    for i := 0; i < 10; i++ {
        go func(i int) {
            sm.Set(fmt.Sprintf("key%d", i), i)
        }(i)
    }
    
    time.Sleep(time.Second)
    
    for i := 0; i < 10; i++ {
        val, ok := sm.Get(fmt.Sprintf("key%d", i))
        if ok {
            fmt.Printf("key%d: %d\n", i, val)
        }
    }
}
Advanced
32. What is Context With Cancel in Go?

Context With Cancel creates a context that can be cancelled. It's used to propagate cancellation signals across goroutines.

  • WithCancel: context.WithCancel(ctx)
  • Cancel Function: cancel()
  • Done Channel: ctx.Done()
  • Propagation: Cancellation propagates to children
  • Cleanup: Defer cancel for resource cleanup
go
// Context With Cancel in Go
package main

import (
    "context"
    "fmt"
    "time"
)

func longRunningTask(ctx context.Context) {
    for {
        select {
        case <-ctx.Done():
            fmt.Println("Task cancelled")
            return
        default:
            fmt.Println("Working...")
            time.Sleep(500 * time.Millisecond)
        }
    }
}

func main() {
    ctx, cancel := context.WithCancel(context.Background())
    
    go longRunningTask(ctx)
    
    time.Sleep(2 * time.Second)
    cancel()
    
    time.Sleep(1 * time.Second)
    fmt.Println("Main done")
}
Advanced
33. What is Context With Value in Go?

Context With Value stores key-value pairs in the context. It's used for request-scoped data like user IDs and request IDs.

  • WithValue: context.WithValue(ctx, key, value)
  • Value: ctx.Value(key)
  • Type Safety: Use custom types for keys
  • Use Cases: Authentication, request IDs, tracing
  • Limited Scope: Not for optional parameters
go
// Context With Value in Go
package main

import (
    "context"
    "fmt"
)

type key string

func main() {
    ctx := context.WithValue(context.Background(), key("userID"), "12345")
    ctx = context.WithValue(ctx, key("requestID"), "req-abc-123")
    
    processRequest(ctx)
}

func processRequest(ctx context.Context) {
    userID := ctx.Value(key("userID"))
    requestID := ctx.Value(key("requestID"))
    
    fmt.Printf("UserID: %v, RequestID: %v\n", userID, requestID)
}
Advanced
34. How do you create Custom Errors in Go?

Custom errors implement the error interface. They can include additional fields and methods for richer error information.

  • Error Interface: type error interface { Error() string }
  • Struct Error: type MyError struct { Code int }
  • Error Method: func (e MyError) Error() string
  • Errors Package: errors.New("message")
  • Wrapping: fmt.Errorf("context: %w", err)
go
// Custom Errors in Go
package main

import (
    "fmt"
)

type AppError struct {
    Code    int
    Message string
    Err     error
}

func (e *AppError) Error() string {
    if e.Err != nil {
        return fmt.Sprintf("error %d: %s: %v", e.Code, e.Message, e.Err)
    }
    return fmt.Sprintf("error %d: %s", e.Code, e.Message)
}

func (e *AppError) Unwrap() error {
    return e.Err
}

func doSomething() error {
    return &AppError{
        Code:    404,
        Message: "Resource not found",
        Err:     fmt.Errorf("resource with ID 123 not found"),
    }
}

func main() {
    err := doSomething()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        if appErr, ok := err.(*AppError); ok {
            fmt.Printf("Code: %d\n", appErr.Code)
        }
    }
}
Advanced
35. What are Build Tags in Go?

Build Tags control which files are included in a build. They enable platform-specific code and conditional compilation.

  • Syntax: // +build linux
  • Boolean Expressions: // +build linux,amd64
  • Negation: // +build !windows
  • Multiple Tags: // +build linux amd64
  • Go Generate: //go:generate
go
// Build Tags in Go
// +build linux,amd64

package main

import "fmt"

func main() {
    fmt.Println("Linux AMD64 build")
}
Advanced
36. What is Embedding in Go?

Embedding allows a struct to include another struct's fields and methods. It's similar to inheritance but uses composition.

  • Anonymous Field: type A struct { B }
  • Method Promotion: Embedded methods are promoted
  • Overriding: Override embedded methods
  • Interfaces: Embed interfaces for combination
  • Composition: Preferred over inheritance
go
// Embedding in Go
package main

import "fmt"

type Writer interface {
    Write(data []byte) (int, error)
}

type Logger struct{}

func (l Logger) Write(data []byte) (int, error) {
    fmt.Printf("Writing: %s\n", string(data))
    return len(data), nil
}

type FileWriter struct {
    Logger // Embedding
    FileName string
}

func main() {
    fw := FileWriter{
        Logger:   Logger{},
        FileName: "test.txt",
    }
    fw.Write([]byte("Hello, World!"))
}
Advanced
37. What are Generics in Go?

Generics were introduced in Go 1.18. They allow writing type-parameterized functions and types for reusable code.

  • Type Parameters: func F[T any](t T) T
  • Constraints: func F[T int | float64]
  • Generic Types: type Stack[T any] struct
  • Methods: Methods can use type parameters
  • Comparable: comparable constraint
go
// Generics in Go (1.18+)
package main

import "fmt"

// Generic function
func Sum[T int | float64](a, b T) T {
    return a + b
}

// Generic struct
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) {
    if len(s.items) == 0 {
        var zero T
        return zero, false
    }
    item := s.items[len(s.items)-1]
    s.items = s.items[:len(s.items)-1]
    return item, true
}

func main() {
    fmt.Printf("Sum int: %d\n", Sum[int](5, 3))
    fmt.Printf("Sum float: %.2f\n", Sum[float64](3.14, 2.5))
    
    intStack := Stack[int]{}
    intStack.Push(10)
    intStack.Push(20)
    val, _ := intStack.Pop()
    fmt.Printf("Stack pop: %d\n", val)
}
Advanced
38. What is Reflection in Go?

Reflection allows inspecting and manipulating values at runtime. It's provided by the reflect package.

  • Type: reflect.TypeOf(v)
  • Value: reflect.ValueOf(v)
  • Field Access: v.FieldByName("Name")
  • Method Call: v.MethodByName("Method").Call()
  • Performance: Reflection is slower
go
// Reflection in Go
package main

import (
    "fmt"
    "reflect"
)

type User struct {
    Name string
    Age  int
}

func inspectStruct(v interface{}) {
    t := reflect.TypeOf(v)
    fmt.Printf("Type: %s\n", t.Name())
    
    for i := 0; i < t.NumField(); i++ {
        field := t.Field(i)
        fmt.Printf("  Field %d: %s (%s)\n", i, field.Name, field.Type)
    }
}

func main() {
    user := User{Name: "Alice", Age: 25}
    inspectStruct(user)
    
    // Dynamic field access
    v := reflect.ValueOf(user)
    nameField := v.FieldByName("Name")
    if nameField.IsValid() {
        fmt.Printf("Name: %s\n", nameField.String())
    }
}
Advanced
39. What is iota in Go?

iota is a predeclared identifier for constant declarations. It provides a way to create enumerated constants.

  • Declaration: const ( A = iota; B; C )
  • Increment: Values increment by 1
  • Skip Values: const ( _ = iota; A; B )
  • Bitmask: 1 << iota
  • Reset: iota resets at each const block
go
// iota in Go
package main

import "fmt"

const (
    StatusPending = iota
    StatusProcessing
    StatusShipped
    StatusDelivered
    StatusCancelled
)

const (
    _ = iota // skip first
    KB = 1 << (10 * iota)
    MB
    GB
    TB
)

func main() {
    fmt.Printf("StatusPending: %d\n", StatusPending)
    fmt.Printf("StatusProcessing: %d\n", StatusProcessing)
    fmt.Printf("StatusShipped: %d\n", StatusShipped)
    fmt.Printf("StatusDelivered: %d\n", StatusDelivered)
    fmt.Printf("StatusCancelled: %d\n", StatusCancelled)
    
    fmt.Printf("1 KB = %d bytes\n", KB)
    fmt.Printf("1 MB = %d bytes\n", MB)
    fmt.Printf("1 GB = %d bytes\n", GB)
    fmt.Printf("1 TB = %d bytes\n", TB)
}
Advanced
40. What is the Flag Package in Go?

The flag package provides command-line flag parsing. It supports various flag types and custom usage messages.

  • String Flag: flag.String("name", "default", "help")
  • Int Flag: flag.Int("age", 0, "help")
  • Bool Flag: flag.Bool("verbose", false, "help")
  • Parse: flag.Parse()
  • Args: flag.Args()
go
// Flag Package in Go
package main

import (
    "flag"
    "fmt"
)

func main() {
    var name string
    var age int
    var active bool
    
    flag.StringVar(&name, "name", "Guest", "user name")
    flag.IntVar(&age, "age", 0, "user age")
    flag.BoolVar(&active, "active", false, "is user active")
    
    flag.Parse()
    
    fmt.Printf("Name: %s\n", name)
    fmt.Printf("Age: %d\n", age)
    fmt.Printf("Active: %t\n", active)
    fmt.Printf("Args: %v\n", flag.Args())
}
Advanced
41. How does Logging work in Go?

Go has a built-in log package for logging. It supports log levels, prefixes, flags, and output destinations.

  • Basic Log: log.Println("message")
  • Prefix: log.SetPrefix("ERROR: ")
  • Flags: log.SetFlags(log.LstdFlags)
  • File Output: log.SetOutput(file)
  • Fatal: log.Fatal("message")
go
// Logging in Go
package main

import (
    "log"
    "os"
)

func main() {
    // Basic log
    log.Println("This is a log message")
    
    // Log with prefix
    log.SetPrefix("ERROR: ")
    log.Println("This is an error message")
    
    // Log with flags
    log.SetFlags(log.LstdFlags | log.Lshortfile)
    log.Println("Log with file and line")
    
    // Log to file
    file, err := os.OpenFile("app.log", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    if err != nil {
        log.Fatal(err)
    }
    defer file.Close()
    
    log.SetOutput(file)
    log.Println("This will go to the log file")
}
Advanced
42. How do Environment Variables work in Go?

Environment variables in Go are accessed using the os package. They provide configuration and runtime settings.

  • Get: os.Getenv("KEY")
  • Set: os.Setenv("KEY", "value")
  • Lookup: os.LookupEnv("KEY")
  • Environ: os.Environ()
  • Unset: os.Unsetenv("KEY")
go
// Environment Variables in Go
package main

import (
    "fmt"
    "os"
)

func main() {
    // Get environment variable
    home := os.Getenv("HOME")
    fmt.Printf("HOME: %s\n", home)
    
    // Set environment variable
    os.Setenv("MY_VAR", "Hello World")
    fmt.Printf("MY_VAR: %s\n", os.Getenv("MY_VAR"))
    
    // Get all environment variables
    env := os.Environ()
    for _, e := range env[:5] {
        fmt.Println(e)
    }
    
    // Lookup environment variable
    val, exists := os.LookupEnv("PATH")
    if exists {
        fmt.Printf("PATH exists: %s\n", val)
    }
}
Advanced
43. How do Command Line Arguments work in Go?

Command line arguments in Go are accessed through os.Args. The flag package provides more advanced parsing.

  • Args: os.Args
  • Index 0: Program name
  • Count: len(os.Args)
  • Flag Package: flag.Parse()
  • Positional Args: flag.Args()
go
// Command Line Arguments in Go
package main

import (
    "fmt"
    "os"
)

func main() {
    // Get arguments
    args := os.Args
    
    fmt.Printf("Number of arguments: %d\n", len(args))
    
    for i, arg := range args {
        fmt.Printf("Argument %d: %s\n", i, arg)
    }
    
    // Check arguments
    if len(args) > 1 {
        fmt.Printf("First argument: %s\n", args[1])
    }
}
Advanced
44. What is OS Signal Handling in Go?

Signal handling in Go uses the os/signal package. It allows programs to respond to system signals for graceful shutdown.

  • Notify: signal.Notify(ch, syscall.SIGINT)
  • Signals: SIGINT, SIGTERM, SIGHUP
  • Channel: make(chan os.Signal, 1)
  • Block: <-ch to wait for signal
  • Graceful Shutdown: Clean up resources
go
// OS Signal Handling in Go
package main

import (
    "fmt"
    "os"
    "os/signal"
    "syscall"
)

func main() {
    // Create channel for signals
    sigs := make(chan os.Signal, 1)
    
    // Register signals
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
    
    fmt.Println("Waiting for signal...")
    
    // Block until signal received
    sig := <-sigs
    fmt.Printf("Received signal: %s\n", sig)
    fmt.Println("Exiting...")
}
Advanced
45. How does the HTTP Client work in Go?

Go's http.Client provides a powerful HTTP client with support for GET, POST, headers, and timeouts.

  • Client: http.Client{Timeout: 10 * time.Second}
  • GET: http.Get(url)
  • POST: http.Post(url, contentType, body)
  • Headers: req.Header.Set("key", "value")
  • Response: resp.Body and resp.StatusCode
go
// HTTP Client in Go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
)

func main() {
    // GET request
    resp, err := http.Get("https://jsonplaceholder.typicode.com/posts/1")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Printf("GET Response: %s\n", string(body))
    
    // POST request
    data := map[string]interface{}{
        "title":  "My Post",
        "body":   "This is my post",
        "userId": 1,
    }
    jsonData, _ := json.Marshal(data)
    
    resp2, err := http.Post("https://jsonplaceholder.typicode.com/posts", 
        "application/json", bytes.NewBuffer(jsonData))
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer resp2.Body.Close()
    
    body2, _ := ioutil.ReadAll(resp2.Body)
    fmt.Printf("POST Response: %s\n", string(body2))
}
Advanced
46. What is HTTP Client with Timeout in Go?

Setting a timeout on the HTTP client prevents hanging requests and improves reliability.

  • Client Timeout: http.Client{Timeout: 5 * time.Second}
  • Context Timeout: context.WithTimeout
  • Transport Timeout: http.Transport{ResponseHeaderTimeout: 2 * time.Second}
  • Deadline: req.WithContext(ctx)
  • Cancel: cancel() function
go
// HTTP Client with Timeout in Go
package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    client := &http.Client{
        Timeout: 5 * time.Second,
    }
    
    resp, err := client.Get("https://jsonplaceholder.typicode.com/posts/1")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    fmt.Printf("Status: %s\n", resp.Status)
}
Advanced
47. What is Custom HTTP Client in Go?

Custom HTTP clients allow fine-grained control over transport, timeouts, headers, and connection pooling.

  • Transport: &http.Transport{MaxIdleConns: 10}
  • Connection Pooling: Reuse connections
  • Timeouts: Connect, read, write timeouts
  • Headers: Set default headers
  • Redirect: CheckRedirect policy
go
// Custom HTTP Client in Go
package main

import (
    "fmt"
    "net/http"
    "time"
)

func main() {
    transport := &http.Transport{
        MaxIdleConns:    10,
        IdleConnTimeout: 30 * time.Second,
    }
    
    client := &http.Client{
        Transport: transport,
        Timeout:   10 * time.Second,
    }
    
    req, err := http.NewRequest("GET", "https://api.example.com/data", nil)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    req.Header.Set("User-Agent", "Go-Client")
    req.Header.Set("Authorization", "Bearer token123")
    
    resp, err := client.Do(req)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    fmt.Printf("Status: %s\n", resp.Status)
}
Advanced
48. What is HTTP Server with Routing in Go?

Go's http.ServeMux provides routing for HTTP requests. It can handle different methods and URL patterns.

  • ServeMux: http.NewServeMux()
  • HandleFunc: Register handlers
  • Method Handling: Check r.Method
  • Path Parameters: Extract from URL
  • Static Files: http.FileServer
go
// HTTP Server with Routing in Go
package main

import (
    "fmt"
    "net/http"
)

func main() {
    // Serve static files
    http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
    
    // API routes
    http.HandleFunc("/api/users", func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case "GET":
            fmt.Fprintf(w, "GET users")
        case "POST":
            fmt.Fprintf(w, "POST users")
        default:
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        }
    })
    
    http.HandleFunc("/api/users/", func(w http.ResponseWriter, r *http.Request) {
        id := r.URL.Path[len("/api/users/"):]
        fmt.Fprintf(w, "User ID: %s\n", id)
    })
    
    fmt.Println("Server starting on port 8080...")
    http.ListenAndServe(":8080", nil)
}
Advanced
49. What is Middleware in Go?

Middleware are functions that wrap HTTP handlers to provide cross-cutting concerns like logging, auth, and CORS.

  • Handler: http.Handler interface
  • Chain: Compose multiple middleware
  • Logging: Log requests and responses
  • Auth: Authentication middleware
  • Recovery: Panic recovery middleware
go
// Middleware in Go
package main

import (
    "fmt"
    "log"
    "net/http"
    "time"
)

func loggingMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        log.Printf("Started %s %s", r.Method, r.URL.Path)
        next.ServeHTTP(w, r)
        log.Printf("Completed in %v", 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
        }
        next.ServeHTTP(w, r)
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })
    
    // Apply middleware
    handler := loggingMiddleware(authMiddleware(mux))
    
    http.ListenAndServe(":8080", handler)
}
Advanced
50. What is WebSocket in Go?

WebSocket provides full-duplex communication channels over a single TCP connection. Go supports WebSockets via packages like gorilla/websocket.

  • Upgrader: websocket.Upgrader
  • Read: conn.ReadMessage()
  • Write: conn.WriteMessage()
  • Handshake: Upgrade HTTP to WebSocket
  • Close: conn.Close()
go
// WebSocket in Go
package main

import (
    "fmt"
    "log"
    "net/http"
    "github.com/gorilla/websocket"
)

var upgrader = websocket.Upgrader{
    CheckOrigin: func(r *http.Request) bool { return true },
}

func handleWebSocket(w http.ResponseWriter, r *http.Request) {
    conn, err := upgrader.Upgrade(w, r, nil)
    if err != nil {
        log.Print("Upgrade failed:", err)
        return
    }
    defer conn.Close()
    
    for {
        messageType, message, err := conn.ReadMessage()
        if err != nil {
            log.Println("Read failed:", err)
            break
        }
        
        fmt.Printf("Received: %s\n", message)
        
        err = conn.WriteMessage(messageType, message)
        if err != nil {
            log.Println("Write failed:", err)
            break
        }
    }
}

func main() {
    http.HandleFunc("/ws", handleWebSocket)
    http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        http.ServeFile(w, r, "index.html")
    })
    
    log.Fatal(http.ListenAndServe(":8080", nil))
}
Advanced
51. What is gRPC Server in Go?

gRPC is a high-performance RPC framework. Go supports gRPC with protocol buffers for efficient communication.

  • Service Definition: Protocol buffers
  • Server: grpc.NewServer()
  • Register: pb.RegisterGreeterServer(s, &server)
  • Listen: net.Listen("tcp", ":50051")
  • Serve: s.Serve(lis)
go
// gRPC Server in Go
package main

import (
    "context"
    "log"
    "net"
    
    "google.golang.org/grpc"
    pb "path/to/proto"
)

type server struct {
    pb.UnimplementedGreeterServer
}

func (s *server) SayHello(ctx context.Context, req *pb.HelloRequest) (*pb.HelloReply, error) {
    return &pb.HelloReply{Message: "Hello " + req.Name}, nil
}

func main() {
    lis, err := net.Listen("tcp", ":50051")
    if err != nil {
        log.Fatalf("Failed to listen: %v", err)
    }
    
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    
    log.Println("Server listening on :50051")
    if err := s.Serve(lis); err != nil {
        log.Fatalf("Failed to serve: %v", err)
    }
}
Advanced
52. What is gRPC Client in Go?

The gRPC client connects to a gRPC server and calls remote methods with proper context and error handling.

  • Connection: grpc.Dial("localhost:50051", grpc.WithInsecure())
  • Client: pb.NewGreeterClient(conn)
  • Call: client.SayHello(ctx, &pb.HelloRequest)
  • Context: context.WithTimeout
  • Error: Handle errors from RPC calls
go
// gRPC Client in Go
package main

import (
    "context"
    "log"
    "time"
    
    "google.golang.org/grpc"
    pb "path/to/proto"
)

func main() {
    conn, err := grpc.Dial("localhost:50051", grpc.WithInsecure())
    if err != nil {
        log.Fatalf("Failed to connect: %v", err)
    }
    defer conn.Close()
    
    client := pb.NewGreeterClient(conn)
    
    ctx, cancel := context.WithTimeout(context.Background(), time.Second)
    defer cancel()
    
    resp, err := client.SayHello(ctx, &pb.HelloRequest{Name: "World"})
    if err != nil {
        log.Fatalf("Could not greet: %v", err)
    }
    log.Printf("Greeting: %s", resp.Message)
}
Advanced
53. How does Database Connection work in Go?

Go's database/sql package provides a generic interface for database connections with various drivers.

  • Open: sql.Open("driver", "connection string")
  • Ping: db.Ping()
  • Query: db.Query("SELECT * FROM users")
  • Exec: db.Exec("INSERT INTO users VALUES (?)", name)
  • Rows: Scan results with rows.Scan()
go
// Database Connection in Go
package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // Connect to database
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer db.Close()
    
    // Test connection
    err = db.Ping()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Println("Connected to database")
    
    // Query
    rows, err := db.Query("SELECT id, name, age FROM users")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer rows.Close()
    
    for rows.Next() {
        var id int
        var name string
        var age int
        err = rows.Scan(&id, &name, &age)
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            return
        }
        fmt.Printf("ID: %d, Name: %s, Age: %d\n", id, name, age)
    }
}
Advanced
54. What is ORM in Go with GORM?

GORM is a popular ORM for Go. It provides automatic mapping between structs and database tables.

  • Model: Struct with gorm tags
  • Create: db.Create(&user)
  • Read: db.First(&user, 1)
  • Update: db.Model(&user).Update("Age", 26)
  • Delete: db.Delete(&user)
go
// ORM in Go with GORM
package main

import (
    "fmt"
    "gorm.io/driver/sqlite"
    "gorm.io/gorm"
)

type User struct {
    gorm.Model
    Name  string
    Email string
    Age   int
}

func main() {
    db, err := gorm.Open(sqlite.Open("test.db"), &gorm.Config{})
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    // Auto migrate
    db.AutoMigrate(&User{})
    
    // Create
    db.Create(&User{Name: "Alice", Email: "alice@email.com", Age: 25})
    
    // Read
    var user User
    db.First(&user, 1)
    fmt.Printf("User: %+v\n", user)
    
    // Update
    db.Model(&user).Update("Age", 26)
    
    // Delete
    db.Delete(&user)
}
Advanced
55. How does MongoDB work in Go?

MongoDB in Go uses the official mongo driver. It provides a fluent API for CRUD operations.

  • Connect: mongo.Connect(ctx, options.Client())
  • Database: client.Database("dbname")
  • Collection: db.Collection("users")
  • Insert: collection.InsertOne(ctx, doc)
  • Find: collection.Find(ctx, filter)
go
// MongoDB in Go
package main

import (
    "context"
    "fmt"
    "time"
    
    "go.mongodb.org/mongo-driver/mongo"
    "go.mongodb.org/mongo-driver/mongo/options"
)

type User struct {
    Name  string
    Email string
    Age   int
}

func main() {
    ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
    defer cancel()
    
    client, err := mongo.Connect(ctx, options.Client().ApplyURI("mongodb://localhost:27017"))
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer client.Disconnect(ctx)
    
    collection := client.Database("test").Collection("users")
    
    // Insert
    user := User{Name: "Alice", Email: "alice@email.com", Age: 25}
    result, err := collection.InsertOne(ctx, user)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Printf("Inserted ID: %v\n", result.InsertedID)
}
Advanced
56. How does Redis work in Go?

Redis in Go uses the go-redis library. It provides a client for Redis operations with context support.

  • Client: redis.NewClient(&redis.Options)
  • Set: rdb.Set(ctx, "key", "value", 0)
  • Get: rdb.Get(ctx, "key").Result()
  • Incr: rdb.Incr(ctx, "counter")
  • Del: rdb.Del(ctx, "key")
go
// Redis in Go
package main

import (
    "context"
    "fmt"
    "github.com/go-redis/redis/v8"
)

func main() {
    ctx := context.Background()
    
    rdb := redis.NewClient(&redis.Options{
        Addr:     "localhost:6379",
        Password: "",
        DB:       0,
    })
    
    // Set
    err := rdb.Set(ctx, "key", "value", 0).Err()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    // Get
    val, err := rdb.Get(ctx, "key").Result()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    fmt.Printf("Value: %s\n", val)
    
    // Incr
    rdb.Incr(ctx, "counter")
    rdb.Incr(ctx, "counter")
    counter, _ := rdb.Get(ctx, "counter").Int()
    fmt.Printf("Counter: %d\n", counter)
}
Advanced
57. What is Testing with Testify in Go?

Testify is a testing toolkit for Go that provides assertions, mocking, and suite functionality.

  • Assert: assert.Equal(t, expected, actual)
  • Require: require.NoError(t, err)
  • Mock: mock.On("Method", args).Return(values)
  • Suite: suite.Run(t, new(MySuite))
  • Before/After: Setup and teardown
go
// Testing with Testify in Go
package main

import (
    "testing"
    "github.com/stretchr/testify/assert"
    "github.com/stretchr/testify/require"
)

func TestAdd(t *testing.T) {
    result := add(2, 3)
    assert.Equal(t, 5, result, "Add should return sum")
    
    result2 := add(-1, 1)
    assert.Equal(t, 0, result2)
}

func TestDivide(t *testing.T) {
    result, err := divide(10, 2)
    require.NoError(t, err)
    assert.Equal(t, 5, result)
    
    _, err = divide(10, 0)
    assert.Error(t, err)
}

func TestMain(m *testing.M) {
    // Setup
    println("Setup")
    
    code := m.Run()
    
    // Teardown
    println("Teardown")
    
    os.Exit(code)
}
Advanced
58. What are Benchmarks in Go?

Benchmarks measure the performance of code. They are written in test files and run with the go test -bench command.

  • Function: func BenchmarkXxx(b *testing.B)
  • Loop: for i := 0; i < b.N; i++
  • Reset: b.ResetTimer()
  • Stop: b.StopTimer() and b.StartTimer()
  • Report: go test -bench=. -benchmem
go
// Benchmarks in Go
package main

import (
    "testing"
)

func BenchmarkAdd(b *testing.B) {
    for i := 0; i < b.N; i++ {
        add(2, 3)
    }
}

func BenchmarkSubtract(b *testing.B) {
    for i := 0; i < b.N; i++ {
        subtract(5, 3)
    }
}

// Run benchmarks:
// go test -bench=.
// go test -bench=BenchmarkAdd -benchmem
Advanced
59. What is Dependency Injection in Go?

Dependency Injection is a design pattern where dependencies are provided to objects rather than created internally. It improves testability and flexibility.

  • Constructor Injection: Pass dependencies via constructor
  • Interface Injection: Depend on interfaces
  • Function Injection: Pass dependencies to functions
  • Container: Dependency injection container
  • Testability: Easy to mock dependencies
go
// Dependency Injection in Go
package main

import "fmt"

// Logger interface
type Logger interface {
    Log(message string)
}

// ConsoleLogger implementation
type ConsoleLogger struct{}

func (l ConsoleLogger) Log(message string) {
    fmt.Printf("[LOG] %s\n", message)
}

// Service with dependency injection
type UserService struct {
    logger Logger
}

func NewUserService(logger Logger) *UserService {
    return &UserService{logger: logger}
}

func (s *UserService) CreateUser(name string) {
    s.logger.Log(fmt.Sprintf("Creating user: %s", name))
    fmt.Printf("User created: %s\n", name)
}

func main() {
    logger := ConsoleLogger{}
    service := NewUserService(logger)
    service.CreateUser("Alice")
}
Advanced
60. What is Dependency Injection with Interface in Go?

Using interfaces for dependency injection decouples implementations and enables easy swapping of dependencies.

  • Interface: Define behavior
  • Implementation: Implement interface
  • Mock: Create mock implementations for testing
  • Swap: Change implementations easily
  • Testability: Isolate components for testing
go
// Dependency Injection with Interface in Go
package main

import "fmt"

// Database interface
type Database interface {
    Save(data string) error
    Get(id int) (string, error)
}

// Mock Database for testing
type MockDB struct{}

func (m MockDB) Save(data string) error {
    fmt.Printf("Mock saving: %s\n", data)
    return nil
}

func (m MockDB) Get(id int) (string, error) {
    return fmt.Sprintf("Mock data for ID: %d", id), nil
}

// Real Database
type RealDB struct{}

func (r RealDB) Save(data string) error {
    fmt.Printf("Real saving: %s\n", data)
    return nil
}

func (r RealDB) Get(id int) (string, error) {
    return fmt.Sprintf("Real data for ID: %d", id), nil
}

type Repository struct {
    db Database
}

func NewRepository(db Database) *Repository {
    return &Repository{db: db}
}

func main() {
    // Use mock for testing
    mockRepo := NewRepository(MockDB{})
    mockRepo.db.Save("test data")
    
    // Use real for production
    realRepo := NewRepository(RealDB{})
    realRepo.db.Save("real data")
}
Advanced
61. What is Functional Options Pattern in Go?

The Functional Options Pattern provides a flexible way to configure structs using functions that modify settings.

  • Options: Functions that modify config
  • Defaults: Set default values
  • Flexibility: Configure only what's needed
  • Extensibility: Easy to add new options
  • Readability: Clear configuration code
go
// Functional Options Pattern in Go
package main

import "fmt"

type Server struct {
    host string
    port int
    timeout int
    maxConns int
}

type ServerOption func(*Server)

func WithHost(host string) ServerOption {
    return func(s *Server) {
        s.host = host
    }
}

func WithPort(port int) ServerOption {
    return func(s *Server) {
        s.port = port
    }
}

func WithTimeout(timeout int) ServerOption {
    return func(s *Server) {
        s.timeout = timeout
    }
}

func WithMaxConns(maxConns int) ServerOption {
    return func(s *Server) {
        s.maxConns = maxConns
    }
}

func NewServer(opts ...ServerOption) *Server {
    // Default values
    s := &Server{
        host: "localhost",
        port: 8080,
        timeout: 30,
        maxConns: 100,
    }
    
    // Apply options
    for _, opt := range opts {
        opt(s)
    }
    return s
}

func main() {
    server1 := NewServer()
    fmt.Printf("Server1: %+v\n", server1)
    
    server2 := NewServer(
        WithHost("0.0.0.0"),
        WithPort(9090),
        WithTimeout(60),
        WithMaxConns(200),
    )
    fmt.Printf("Server2: %+v\n", server2)
}
Advanced
62. What is Builder Pattern in Go?

The Builder Pattern constructs complex objects step by step. It provides a fluent interface for object creation.

  • Builder: Constructs the object
  • Fluent Interface: Method chaining
  • Immutable: Builds immutable objects
  • Flexibility: Optional fields
  • Readability: Clear construction code
go
// Builder Pattern in Go
package main

import "fmt"

type Person struct {
    Name string
    Age int
    Email string
    Address string
    Phone string
}

type PersonBuilder struct {
    person Person
}

func NewPersonBuilder() *PersonBuilder {
    return &PersonBuilder{person: Person{}}
}

func (b *PersonBuilder) WithName(name string) *PersonBuilder {
    b.person.Name = name
    return b
}

func (b *PersonBuilder) WithAge(age int) *PersonBuilder {
    b.person.Age = age
    return b
}

func (b *PersonBuilder) WithEmail(email string) *PersonBuilder {
    b.person.Email = email
    return b
}

func (b *PersonBuilder) WithAddress(address string) *PersonBuilder {
    b.person.Address = address
    return b
}

func (b *PersonBuilder) WithPhone(phone string) *PersonBuilder {
    b.person.Phone = phone
    return b
}

func (b *PersonBuilder) Build() Person {
    return b.person
}

func main() {
    person := NewPersonBuilder().
        WithName("Alice").
        WithAge(25).
        WithEmail("alice@email.com").
        WithAddress("123 Main St").
        WithPhone("555-1234").
        Build()
    
    fmt.Printf("Person: %+v\n", person)
}
Advanced
63. What is Singleton Pattern in Go?

The Singleton Pattern ensures a type has only one instance. In Go, it's implemented using sync.Once for thread safety.

  • sync.Once: Ensures single initialization
  • Package Level: Instance at package level
  • Lazy Loading: Initialize on first use
  • Thread Safety: Safe for concurrent use
  • Global Access: GetInstance function
go
// Singleton Pattern in Go
package main

import (
    "fmt"
    "sync"
)

type Database struct {
    connected bool
}

var (
    instance *Database
    once sync.Once
)

func GetDatabase() *Database {
    once.Do(func() {
        fmt.Println("Creating database connection...")
        instance = &Database{connected: true}
    })
    return instance
}

func (db *Database) Query(sql string) {
    fmt.Printf("Executing query: %s\n", sql)
}

func main() {
    db1 := GetDatabase()
    db2 := GetDatabase()
    
    db1.Query("SELECT * FROM users")
    db2.Query("SELECT * FROM orders")
    
    fmt.Printf("db1 == db2: %v\n", db1 == db2)
    fmt.Printf("db1 address: %p\n", db1)
    fmt.Printf("db2 address: %p\n", db2)
}
Advanced
64. What is Factory Pattern in Go?

The Factory Pattern creates objects without exposing the creation logic. It returns interface types for abstraction.

  • Factory Function: Creates objects
  • Interface: Returns interface types
  • Polymorphism: Creates different types
  • Decoupling: Separates creation from usage
  • Testability: Easy to mock
go
// Factory Pattern in Go
package main

import "fmt"

// Product interface
type Product interface {
    Use() string
}

// Concrete products
type Chair struct{}

func (c Chair) Use() string {
    return "Sitting on a chair"
}

type Table struct{}

func (t Table) Use() string {
    return "Placing items on a table"
}

type Sofa struct{}

func (s Sofa) Use() string {
    return "Sitting on a sofa"
}

// Factory
type FurnitureFactory struct{}

func (f FurnitureFactory) CreateProduct(productType string) (Product, error) {
    switch productType {
    case "chair":
        return Chair{}, nil
    case "table":
        return Table{}, nil
    case "sofa":
        return Sofa{}, nil
    default:
        return nil, fmt.Errorf("unknown product type: %s", productType)
    }
}

func main() {
    factory := FurnitureFactory{}
    
    products := []string{"chair", "table", "sofa"}
    for _, p := range products {
        product, err := factory.CreateProduct(p)
        if err != nil {
            fmt.Printf("Error: %v\n", err)
            continue
        }
        fmt.Printf("%s: %s\n", p, product.Use())
    }
}
Advanced
65. What is Strategy Pattern in Go?

The Strategy Pattern defines a family of algorithms and makes them interchangeable. It uses interfaces for algorithm selection.

  • Strategy Interface: Defines algorithm
  • Concrete Strategies: Implement algorithms
  • Context: Uses strategies
  • Runtime Selection: Change strategy at runtime
  • Flexibility: Easy to add new strategies
go
// Strategy Pattern in Go
package main

import "fmt"

// Strategy interface
type SortStrategy interface {
    Sort(data []int) []int
}

// Bubble sort
type BubbleSort struct{}

func (b BubbleSort) Sort(data []int) []int {
    result := make([]int, len(data))
    copy(result, data)
    n := len(result)
    for i := 0; i < n-1; i++ {
        for j := 0; j < n-i-1; j++ {
            if result[j] > result[j+1] {
                result[j], result[j+1] = result[j+1], result[j]
            }
        }
    }
    return result
}

// Quick sort
type QuickSort struct{}

func (q QuickSort) Sort(data []int) []int {
    result := make([]int, len(data))
    copy(result, data)
    quickSortHelper(result, 0, len(result)-1)
    return result
}

func quickSortHelper(arr []int, low, high int) {
    if low < high {
        pi := partition(arr, low, high)
        quickSortHelper(arr, low, pi-1)
        quickSortHelper(arr, pi+1, high)
    }
}

func partition(arr []int, low, high int) int {
    pivot := arr[high]
    i := low - 1
    for j := low; j < high; j++ {
        if arr[j] < pivot {
            i++
            arr[i], arr[j] = arr[j], arr[i]
        }
    }
    arr[i+1], arr[high] = arr[high], arr[i+1]
    return i + 1
}

// Context
type Sorter struct {
    strategy SortStrategy
}

func (s *Sorter) SetStrategy(strategy SortStrategy) {
    s.strategy = strategy
}

func (s *Sorter) Sort(data []int) []int {
    return s.strategy.Sort(data)
}

func main() {
    data := []int{64, 34, 25, 12, 22, 11, 90}
    
    sorter := Sorter{}
    
    sorter.SetStrategy(BubbleSort{})
    result1 := sorter.Sort(data)
    fmt.Printf("Bubble Sort: %v\n", result1)
    
    sorter.SetStrategy(QuickSort{})
    result2 := sorter.Sort(data)
    fmt.Printf("Quick Sort: %v\n", result2)
}
Advanced
66. What is Observer Pattern in Go?

The Observer Pattern defines a one-to-many dependency where subjects notify observers of state changes.

  • Subject: Maintains observers
  • Observer: Receives updates
  • Register: Subscribe to updates
  • Notify: Broadcast changes
  • Event-Driven: Reactive programming
go
// Observer Pattern in Go
package main

import "fmt"

// Observer interface
type Observer interface {
    Update(message string)
}

// Subject interface
type Subject interface {
    Register(observer Observer)
    Unregister(observer Observer)
    Notify(message string)
}

// Concrete Subject
type NewsPublisher struct {
    observers []Observer
}

func (n *NewsPublisher) Register(observer Observer) {
    n.observers = append(n.observers, observer)
}

func (n *NewsPublisher) Unregister(observer Observer) {
    for i, obs := range n.observers {
        if obs == observer {
            n.observers = append(n.observers[:i], n.observers[i+1:]...)
            break
        }
    }
}

func (n *NewsPublisher) Notify(message string) {
    for _, observer := range n.observers {
        observer.Update(message)
    }
}

// Concrete Observers
type EmailSubscriber struct {
    name string
}

func (e EmailSubscriber) Update(message string) {
    fmt.Printf("Email to %s: %s\n", e.name, message)
}

type SMSSubscriber struct {
    phone string
}

func (s SMSSubscriber) Update(message string) {
    fmt.Printf("SMS to %s: %s\n", s.phone, message)
}

func main() {
    publisher := NewsPublisher{}
    
    emailSub := EmailSubscriber{name: "Alice"}
    smsSub := SMSSubscriber{phone: "555-1234"}
    
    publisher.Register(emailSub)
    publisher.Register(smsSub)
    
    publisher.Notify("Breaking News: Go 1.18 released!")
    
    publisher.Unregister(emailSub)
    publisher.Notify("Update: Go 1.19 coming soon!")
}
Advanced
67. What is Pipeline Pattern in Go?

The Pipeline Pattern processes data through a series of stages. It uses channels for communication between stages.

  • Stages: Processing steps
  • Channels: Connect stages
  • Concurrency: Run stages concurrently
  • Fan-out/Fan-in: Parallel processing
  • Data Flow: Stream processing
go
// Pipeline Pattern in Go
package main

import (
    "fmt"
    "sync"
)

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

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

func filter(in <-chan int, predicate func(int) bool) <-chan int {
    out := make(chan int)
    go func() {
        for n := range in {
            if predicate(n) {
                out <- n
            }
        }
        close(out)
    }()
    return out
}

func merge(chans ...<-chan int) <-chan int {
    out := make(chan int)
    var wg sync.WaitGroup
    wg.Add(len(chans))
    
    for _, ch := range chans {
        go func(c <-chan int) {
            for n := range c {
                out <- n
            }
            wg.Done()
        }(ch)
    }
    
    go func() {
        wg.Wait()
        close(out)
    }()
    
    return out
}

func main() {
    // Pipeline: generate -> square -> filter
    nums := generator(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
    squares := square(nums)
    evens := filter(squares, func(n int) bool { return n%2 == 0 })
    
    // Fan-out: multiple workers
    workers := 3
    chans := make([]<-chan int, workers)
    for i := 0; i < workers; i++ {
        chans[i] = filter(squares, func(n int) bool { return n%2 == 0 })
    }
    
    // Fan-in: merge results
    results := merge(chans...)
    
    for result := range results {
        fmt.Printf("Result: %d\n", result)
    }
}
Advanced
68. What is Worker Pool Pattern in Go?

The Worker Pool Pattern manages a pool of workers that process jobs from a queue. It controls concurrency and resource usage.

  • Jobs Channel: Work queue
  • Workers: Goroutines processing jobs
  • Results Channel: Output collection
  • WaitGroup: Track completion
  • Scalability: Adjust worker count
go
// Worker Pool Pattern in Go
package main

import (
    "fmt"
    "sync"
    "time"
)

type Job struct {
    ID int
}

type Result struct {
    JobID int
    Output string
}

func worker(id int, jobs <-chan Job, results chan<- Result, wg *sync.WaitGroup) {
    defer wg.Done()
    for job := range jobs {
        fmt.Printf("Worker %d processing job %d\n", id, job.ID)
        time.Sleep(time.Second) // Simulate work
        results <- Result{JobID: job.ID, Output: fmt.Sprintf("Job %d processed by worker %d", job.ID, id)}
    }
}

func main() {
    numJobs := 10
    numWorkers := 3
    
    jobs := make(chan Job, numJobs)
    results := make(chan Result, numJobs)
    
    var wg sync.WaitGroup
    
    // Start workers
    for i := 1; i <= numWorkers; i++ {
        wg.Add(1)
        go worker(i, jobs, results, &wg)
    }
    
    // Send jobs
    for i := 1; i <= numJobs; i++ {
        jobs <- Job{ID: i}
    }
    close(jobs)
    
    // Wait for workers to finish
    go func() {
        wg.Wait()
        close(results)
    }()
    
    // Collect results
    for result := range results {
        fmt.Printf("Result: %s\n", result.Output)
    }
}
Advanced
69. What is Rate Limiting in Go?

Rate Limiting controls the rate of requests to prevent overload. Token bucket is a common implementation.

  • Token Bucket: Leaky bucket algorithm
  • Rate: Requests per second
  • Burst: Maximum tokens
  • Refill: Add tokens periodically
  • Usage: API rate limiting
go
// Rate Limiting in Go
package main

import (
    "fmt"
    "time"
)

// Token bucket rate limiter
type RateLimiter struct {
    tokens chan struct{}
    ticker *time.Ticker
}

func NewRateLimiter(rate int) *RateLimiter {
    rl := &RateLimiter{
        tokens: make(chan struct{}, rate),
        ticker: time.NewTicker(time.Second / time.Duration(rate)),
    }
    
    // Refill tokens
    go func() {
        for range rl.ticker.C {
            select {
            case rl.tokens <- struct{}{}:
            default:
                // Token bucket full
            }
        }
    }()
    
    // Initialize tokens
    for i := 0; i < rate; i++ {
        rl.tokens <- struct{}{}
    }
    
    return rl
}

func (rl *RateLimiter) Allow() bool {
    select {
    case <-rl.tokens:
        return true
    default:
        return false
    }
}

func (rl *RateLimiter) Stop() {
    rl.ticker.Stop()
}

func main() {
    limiter := NewRateLimiter(3) // 3 requests per second
    defer limiter.Stop()
    
    for i := 0; i < 10; i++ {
        if limiter.Allow() {
            fmt.Printf("Request %d allowed at %v\n", i, time.Now())
        } else {
            fmt.Printf("Request %d denied at %v\n", i, time.Now())
        }
        time.Sleep(200 * time.Millisecond)
    }
}
Advanced
70. What is Circuit Breaker Pattern in Go?

The Circuit Breaker Pattern prevents cascading failures by stopping requests to failing services. It has three states: closed, open, half-open.

  • Closed: Normal operation
  • Open: Service unavailable
  • Half-Open: Testing service
  • Failure Threshold: Triggers open
  • Timeout: Time to try again
go
// Circuit Breaker Pattern in Go
package main

import (
    "errors"
    "fmt"
    "sync"
    "time"
)

type CircuitBreaker struct {
    mu sync.Mutex
    state string // "closed", "open", "half-open"
    failureCount int
    maxFailures int
    timeout time.Duration
    lastFailureTime time.Time
}

func NewCircuitBreaker(maxFailures int, timeout time.Duration) *CircuitBreaker {
    return &CircuitBreaker{
        state: "closed",
        maxFailures: maxFailures,
        timeout: timeout,
    }
}

func (cb *CircuitBreaker) Call(fn func() error) error {
    cb.mu.Lock()
    defer cb.mu.Unlock()
    
    // Check if circuit is open
    if cb.state == "open" {
        if time.Since(cb.lastFailureTime) > cb.timeout {
            cb.state = "half-open"
            fmt.Println("Circuit: half-open")
        } else {
            return errors.New("circuit breaker is open")
        }
    }
    
    // Execute the function
    err := fn()
    
    if err != nil {
        cb.failureCount++
        cb.lastFailureTime = time.Now()
        
        if cb.failureCount >= cb.maxFailures {
            cb.state = "open"
            fmt.Println("Circuit: open")
        }
        return err
    }
    
    // Success - reset
    cb.failureCount = 0
    if cb.state == "half-open" {
        cb.state = "closed"
        fmt.Println("Circuit: closed")
    }
    
    return nil
}

func main() {
    cb := NewCircuitBreaker(3, 2*time.Second)
    
    // Simulate a failing service
    failCount := 0
    for i := 0; i < 10; i++ {
        err := cb.Call(func() error {
            failCount++
            if failCount <= 3 {
                return errors.New("service error")
            }
            return nil
        })
        
        if err != nil {
            fmt.Printf("Call %d: Error - %v\n", i+1, err)
        } else {
            fmt.Printf("Call %d: Success\n", i+1)
        }
        time.Sleep(500 * time.Millisecond)
    }
}
Advanced
71. What is Retry Pattern in Go?

The Retry Pattern automatically retries failed operations with backoff to handle transient failures.

  • Max Attempts: Number of retries
  • Backoff: Exponential backoff
  • Retry Condition: Which errors to retry
  • Jitter: Random delay variation
  • Circuit Breaker: Combine with circuit breaker
go
// Retry Pattern in Go
package main

import (
    "errors"
    "fmt"
    "time"
)

type RetryConfig struct {
    MaxAttempts int
    InitialDelay time.Duration
    MaxDelay time.Duration
    Multiplier float64
}

func DefaultRetryConfig() RetryConfig {
    return RetryConfig{
        MaxAttempts: 3,
        InitialDelay: 100 * time.Millisecond,
        MaxDelay: 2 * time.Second,
        Multiplier: 2.0,
    }
}

func Retry(fn func() error, config RetryConfig) error {
    var lastErr error
    delay := config.InitialDelay
    
    for attempt := 0; attempt < config.MaxAttempts; attempt++ {
        err := fn()
        if err == nil {
            return nil
        }
        
        lastErr = err
        
        if attempt < config.MaxAttempts-1 {
            fmt.Printf("Attempt %d failed: %v, retrying in %v\n", attempt+1, err, delay)
            time.Sleep(delay)
            delay = time.Duration(float64(delay) * config.Multiplier)
            if delay > config.MaxDelay {
                delay = config.MaxDelay
            }
        }
    }
    
    return fmt.Errorf("all %d attempts failed: %w", config.MaxAttempts, lastErr)
}

func main() {
    attemptCount := 0
    err := Retry(func() error {
        attemptCount++
        if attemptCount < 3 {
            return errors.New("temporary error")
        }
        return nil
    }, DefaultRetryConfig())
    
    if err != nil {
        fmt.Printf("Failed: %v\n", err)
    } else {
        fmt.Printf("Success after %d attempts\n", attemptCount)
    }
}
Advanced
72. What is Timeout Pattern in Go?

The Timeout Pattern sets a deadline for operations to prevent indefinite blocking.

  • Context: context.WithTimeout
  • Channel: Use select for timeout
  • Deadline: Absolute time limit
  • Graceful: Clean resource cleanup
  • Abort: Cancel operations on timeout
go
// Timeout Pattern in Go
package main

import (
    "context"
    "fmt"
    "time"
)

func slowOperation() string {
    time.Sleep(2 * time.Second)
    return "Operation complete"
}

func withTimeout(timeout time.Duration) (string, error) {
    ctx, cancel := context.WithTimeout(context.Background(), timeout)
    defer cancel()
    
    resultCh := make(chan string, 1)
    errCh := make(chan error, 1)
    
    go func() {
        result := slowOperation()
        select {
        case resultCh <- result:
        case <-ctx.Done():
            // Operation completed but context was cancelled
        }
    }()
    
    select {
    case result := <-resultCh:
        return result, nil
    case <-ctx.Done():
        return "", fmt.Errorf("operation timed out after %v", timeout)
    }
}

func main() {
    // Test with timeout that's too short
    result, err := withTimeout(1 * time.Second)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Result: %s\n", result)
    }
    
    // Test with sufficient timeout
    result2, err2 := withTimeout(3 * time.Second)
    if err2 != nil {
        fmt.Printf("Error: %v\n", err2)
    } else {
        fmt.Printf("Result: %s\n", result2)
    }
}
Advanced
73. What is Semaphore Pattern in Go?

The Semaphore Pattern limits concurrent access to resources. It's implemented using buffered channels.

  • Buffer Channel: make(chan struct{}, maxConcurrency)
  • Acquire: sem <- struct{}{}
  • Release: <-sem
  • Timeout: Timeout on acquire
  • Usage: Resource pool, connection limits
go
// Semaphore Pattern in Go
package main

import (
    "fmt"
    "sync"
    "time"
)

type Semaphore struct {
    ch chan struct{}
}

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

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

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

func main() {
    sem := NewSemaphore(3) // Max 3 concurrent operations
    var wg sync.WaitGroup
    
    for i := 1; i <= 10; i++ {
        wg.Add(1)
        go func(id int) {
            defer wg.Done()
            sem.Acquire()
            defer sem.Release()
            
            fmt.Printf("Task %d started at %v\n", id, time.Now())
            time.Sleep(1 * time.Second)
            fmt.Printf("Task %d completed at %v\n", id, time.Now())
        }(i)
    }
    
    wg.Wait()
    fmt.Println("All tasks completed")
}
Advanced
74. What is Graceful Shutdown in Go?

Graceful Shutdown allows servers to finish processing requests before stopping. It uses signal handling and context cancellation.

  • Signal: syscall.SIGINT, syscall.SIGTERM
  • Server Shutdown: server.Shutdown(ctx)
  • Timeout: Wait for connections to finish
  • Cleanup: Close resources
  • Health Checks: Prevent new requests
go
// Graceful Shutdown in Go
package main

import (
    "context"
    "fmt"
    "net/http"
    "os"
    "os/signal"
    "syscall"
    "time"
)

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, "Hello, World!")
    })
    
    server := &http.Server{
        Addr:    ":8080",
        Handler: mux,
    }
    
    // Start server in goroutine
    go func() {
        fmt.Println("Server starting on :8080")
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            fmt.Printf("Server error: %v\n", err)
        }
    }()
    
    // Wait for interrupt signal
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    fmt.Println("Shutting down server...")
    
    // Graceful shutdown with timeout
    ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
    defer cancel()
    
    if err := server.Shutdown(ctx); err != nil {
        fmt.Printf("Server shutdown error: %v\n", err)
    }
    
    fmt.Println("Server stopped")
}
Advanced
75. What is CORS Middleware in Go?

CORS Middleware enables Cross-Origin Resource Sharing by setting appropriate headers on HTTP responses.

  • Headers: Access-Control-Allow-Origin
  • Methods: Allowed HTTP methods
  • Headers: Allowed request headers
  • Preflight: Handle OPTIONS requests
  • Configuration: Allow specific origins
go
// CORS Middleware in Go
package main

import (
    "fmt"
    "net/http"
)

func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
        
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        
        next.ServeHTTP(w, r)
    })
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/api/data", func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintf(w, `{"message": "CORS enabled"}`)
    })
    
    handler := corsMiddleware(mux)
    fmt.Println("Server starting on :8080")
    http.ListenAndServe(":8080", handler)
}
Advanced
76. What is JWT Authentication in Go?

JWT Authentication uses JSON Web Tokens for stateless authentication. Go provides libraries for token generation and validation.

  • Generate: jwt.NewWithClaims
  • Validate: jwt.ParseWithClaims
  • Claims: Custom claims structure
  • Signing Method: HS256, RS256, etc.
  • Expiration: Set token expiry
go
// JWT Authentication in Go
package main

import (
    "fmt"
    "time"
    "github.com/golang-jwt/jwt"
)

var secretKey = []byte("my-secret-key")

type Claims struct {
    Username string `json:"username"`
    jwt.StandardClaims
}

func GenerateToken(username string) (string, error) {
    expirationTime := time.Now().Add(1 * time.Hour)
    claims := &Claims{
        Username: username,
        StandardClaims: jwt.StandardClaims{
            ExpiresAt: expirationTime.Unix(),
            IssuedAt:  time.Now().Unix(),
            Issuer:    "my-app",
        },
    }
    
    token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
    return token.SignedString(secretKey)
}

func ValidateToken(tokenString string) (*Claims, error) {
    token, err := jwt.ParseWithClaims(tokenString, &Claims{}, func(token *jwt.Token) (interface{}, error) {
        return secretKey, nil
    })
    
    if err != nil {
        return nil, err
    }
    
    if claims, ok := token.Claims.(*Claims); ok && token.Valid {
        return claims, nil
    }
    
    return nil, fmt.Errorf("invalid token")
}

func main() {
    // Generate token
    token, err := GenerateToken("alice")
    if err != nil {
        fmt.Printf("Error generating token: %v\n", err)
        return
    }
    fmt.Printf("Token: %s\n", token)
    
    // Validate token
    claims, err := ValidateToken(token)
    if err != nil {
        fmt.Printf("Error validating token: %v\n", err)
        return
    }
    fmt.Printf("Valid token for user: %s\n", claims.Username)
}
Advanced
77. What is Metrics and Monitoring in Go?

Metrics and Monitoring track application performance, errors, and request patterns for observability.

  • Requests: Count incoming requests
  • Errors: Track error rates
  • Duration: Measure response times
  • Prometheus: Metrics collection
  • Grafana: Visualization
go
// Metrics and Monitoring in Go
package main

import (
    "fmt"
    "net/http"
    "time"
)

type Metrics struct {
    requests int
    errors int
    totalDuration time.Duration
}

var metrics Metrics

func metricsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        start := time.Now()
        next.ServeHTTP(w, r)
        duration := time.Since(start)
        
        metrics.requests++
        metrics.totalDuration += duration
    })
}

func metricsHandler(w http.ResponseWriter, r *http.Request) {
    avgDuration := metrics.totalDuration / time.Duration(metrics.requests)
    fmt.Fprintf(w, "Requests: %d\n", metrics.requests)
    fmt.Fprintf(w, "Errors: %d\n", metrics.errors)
    fmt.Fprintf(w, "Avg Duration: %v\n", avgDuration)
}

func main() {
    mux := http.NewServeMux()
    mux.HandleFunc("/api", func(w http.ResponseWriter, r *http.Request) {
        time.Sleep(100 * time.Millisecond)
        fmt.Fprintf(w, "API response")
    })
    mux.HandleFunc("/metrics", metricsHandler)
    
    handler := metricsMiddleware(mux)
    http.ListenAndServe(":8080", handler)
}
Advanced
78. What is Profiling in Go?

Profiling analyzes program performance to identify bottlenecks. Go provides CPU and memory profiling.

  • CPU Profile: pprof.StartCPUProfile
  • Memory Profile: pprof.WriteHeapProfile
  • Web UI: go tool pprof
  • Flame Graphs: Visual representation
  • Optimization: Identify hotspots
go
// Profiling in Go
package main

import (
    "fmt"
    "os"
    "runtime/pprof"
    "time"
)

func expensiveOperation() {
    sum := 0
    for i := 0; i < 1000000; i++ {
        sum += i
    }
}

func main() {
    // CPU profiling
    f, err := os.Create("cpu.prof")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer f.Close()
    
    pprof.StartCPUProfile(f)
    defer pprof.StopCPUProfile()
    
    // Run operations
    for i := 0; i < 10; i++ {
        expensiveOperation()
        time.Sleep(10 * time.Millisecond)
    }
    
    // Memory profiling
    f2, err := os.Create("mem.prof")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer f2.Close()
    
    pprof.WriteHeapProfile(f2)
    fmt.Println("Profiling complete")
}
Advanced
79. What is Tracing in Go?

Tracing tracks the flow of requests through a distributed system. It provides visibility into service interactions.

  • Spans: Individual operations
  • Trace ID: Unique request identifier
  • Parent/Child: Span relationships
  • Tags: Additional metadata
  • Jaeger: Distributed tracing system
go
// Tracing in Go
package main

import (
    "context"
    "fmt"
    "time"
)

type Trace struct {
    name string
    start time.Time
    end time.Time
    children []*Trace
}

func StartTrace(name string) *Trace {
    return &Trace{
        name: name,
        start: time.Now(),
        children: []*Trace{},
    }
}

func (t *Trace) End() {
    t.end = time.Now()
}

func (t *Trace) AddChild(child *Trace) {
    t.children = append(t.children, child)
}

func (t *Trace) Duration() time.Duration {
    return t.end.Sub(t.start)
}

func (t *Trace) Print(indent int) {
    prefix := ""
    for i := 0; i < indent; i++ {
        prefix += "  "
    }
    fmt.Printf("%s%s: %v\n", prefix, t.name, t.Duration())
    for _, child := range t.children {
        child.Print(indent + 1)
    }
}

func operation1(ctx context.Context, trace *Trace) {
    child := StartTrace("operation1")
    defer child.End()
    trace.AddChild(child)
    
    time.Sleep(100 * time.Millisecond)
    operation2(ctx, child)
}

func operation2(ctx context.Context, trace *Trace) {
    child := StartTrace("operation2")
    defer child.End()
    trace.AddChild(child)
    
    time.Sleep(50 * time.Millisecond)
    operation3(ctx, child)
}

func operation3(ctx context.Context, trace *Trace) {
    child := StartTrace("operation3")
    defer child.End()
    trace.AddChild(child)
    
    time.Sleep(25 * time.Millisecond)
}

func main() {
    ctx := context.Background()
    root := StartTrace("main")
    defer root.End()
    
    operation1(ctx, root)
    
    root.Print(0)
}
Advanced
80. What are Feature Flags in Go?

Feature Flags enable dynamic feature control, allowing gradual rollouts and A/B testing.

  • Enable/Disable: Control features
  • Configuration: Runtime configuration
  • Rollout: Gradual feature release
  • Testing: A/B testing support
  • Fallback: Safe defaults
go
// Feature Flags in Go
package main

import (
    "fmt"
    "sync"
)

type FeatureFlags struct {
    mu sync.RWMutex
    flags map[string]bool
}

func NewFeatureFlags() *FeatureFlags {
    return &FeatureFlags{
        flags: make(map[string]bool),
    }
}

func (ff *FeatureFlags) SetFlag(name string, enabled bool) {
    ff.mu.Lock()
    defer ff.mu.Unlock()
    ff.flags[name] = enabled
}

func (ff *FeatureFlags) IsEnabled(name string) bool {
    ff.mu.RLock()
    defer ff.mu.RUnlock()
    return ff.flags[name]
}

func (ff *FeatureFlags) GetFlags() map[string]bool {
    ff.mu.RLock()
    defer ff.mu.RUnlock()
    result := make(map[string]bool)
    for k, v := range ff.flags {
        result[k] = v
    }
    return result
}

func main() {
    ff := NewFeatureFlags()
    
    ff.SetFlag("new-feature", true)
    ff.SetFlag("experimental-api", false)
    
    if ff.IsEnabled("new-feature") {
        fmt.Println("New feature is enabled")
    }
    
    if !ff.IsEnabled("experimental-api") {
        fmt.Println("Experimental API is disabled")
    }
}
Advanced
81. What is Configuration Management in Go?

Configuration Management loads and manages application settings from files, environment variables, and command-line flags.

  • JSON/YAML: Configuration files
  • Environment Variables: Runtime overrides
  • Struct Tags: Configuration mapping
  • Default Values: Fallback values
  • Validation: Validate configuration
go
// Configuration Management in Go
package main

import (
    "encoding/json"
    "fmt"
    "io/ioutil"
    "os"
)

type Config struct {
    Server struct {
        Host string `json:"host"`
        Port int    `json:"port"`
    } `json:"server"`
    Database struct {
        Host     string `json:"host"`
        Port     int    `json:"port"`
        Username string `json:"username"`
        Password string `json:"password"`
        Name     string `json:"name"`
    } `json:"database"`
    Logging struct {
        Level  string `json:"level"`
        Output string `json:"output"`
    } `json:"logging"`
}

func LoadConfig(path string) (*Config, error) {
    file, err := os.Open(path)
    if err != nil {
        return nil, err
    }
    defer file.Close()
    
    data, err := ioutil.ReadAll(file)
    if err != nil {
        return nil, err
    }
    
    var config Config
    err = json.Unmarshal(data, &config)
    if err != nil {
        return nil, err
    }
    
    // Environment variable overrides
    if host := os.Getenv("DB_HOST"); host != "" {
        config.Database.Host = host
    }
    if port := os.Getenv("DB_PORT"); port != "" {
        fmt.Sscanf(port, "%d", &config.Database.Port)
    }
    
    return &config, nil
}

func main() {
    // Create sample config file
    sampleConfig := Config{
        Server: struct {
            Host string `json:"host"`
            Port int    `json:"port"`
        }{Host: "localhost", Port: 8080},
        Database: struct {
            Host     string `json:"host"`
            Port     int    `json:"port"`
            Username string `json:"username"`
            Password string `json:"password"`
            Name     string `json:"name"`
        }{Host: "localhost", Port: 5432, Username: "user", Password: "pass", Name: "db"},
        Logging: struct {
            Level  string `json:"level"`
            Output string `json:"output"`
        }{Level: "info", Output: "stdout"},
    }
    
    data, _ := json.MarshalIndent(sampleConfig, "", "  ")
    ioutil.WriteFile("config.json", data, 0644)
    
    // Load config
    config, err := LoadConfig("config.json")
    if err != nil {
        fmt.Printf("Error loading config: %v\n", err)
        return
    }
    
    fmt.Printf("Server: %s:%d\n", config.Server.Host, config.Server.Port)
    fmt.Printf("Database: %s:%d/%s\n", config.Database.Host, config.Database.Port, config.Database.Name)
}
Advanced
82. What is Environment Variables Configuration in Go?

Environment Variables provide a way to configure applications at runtime using the os package.

  • Get: os.Getenv("KEY")
  • Default: Provide fallback values
  • Parse: Convert to appropriate types
  • Validation: Validate required variables
  • 12-Factor: Config via environment
go
// Environment Variables Configuration in Go
package main

import (
    "fmt"
    "os"
    "strconv"
)

type AppConfig struct {
    Port int
    DatabaseURL string
    LogLevel string
    MaxConnections int
}

func LoadConfigFromEnv() AppConfig {
    config := AppConfig{
        Port: 8080,
        DatabaseURL: "postgres://localhost:5432",
        LogLevel: "info",
        MaxConnections: 10,
    }
    
    if port := os.Getenv("PORT"); port != "" {
        if p, err := strconv.Atoi(port); err == nil {
            config.Port = p
        }
    }
    
    if dbURL := os.Getenv("DATABASE_URL"); dbURL != "" {
        config.DatabaseURL = dbURL
    }
    
    if logLevel := os.Getenv("LOG_LEVEL"); logLevel != "" {
        config.LogLevel = logLevel
    }
    
    if maxConn := os.Getenv("MAX_CONNECTIONS"); maxConn != "" {
        if m, err := strconv.Atoi(maxConn); err == nil {
            config.MaxConnections = m
        }
    }
    
    return config
}

func main() {
    os.Setenv("PORT", "9090")
    os.Setenv("LOG_LEVEL", "debug")
    
    config := LoadConfigFromEnv()
    fmt.Printf("Port: %d\n", config.Port)
    fmt.Printf("DatabaseURL: %s\n", config.DatabaseURL)
    fmt.Printf("LogLevel: %s\n", config.LogLevel)
    fmt.Printf("MaxConnections: %d\n", config.MaxConnections)
}
Advanced
83. What is Versioning in Go?

Versioning in Go uses build-time variables to embed version information into binaries.

  • Build Tags: -ldflags "-X main.Version=v1.0.0"
  • Constants: Define version constants
  • Git Tags: Version from Git tags
  • Semantic Versioning: Major.Minor.Patch
  • API: Version endpoint for API
go
// Versioning in Go
package main

import (
    "fmt"
    "runtime"
)

// Build information (set by ldflags)
var (
    Version   = "dev"
    BuildTime = "unknown"
    GitCommit = "unknown"
)

func main() {
    fmt.Printf("Application: %s\n", Version)
    fmt.Printf("Build Time: %s\n", BuildTime)
    fmt.Printf("Git Commit: %s\n", GitCommit)
    fmt.Printf("Go Version: %s\n", runtime.Version())
    fmt.Printf("OS/Arch: %s/%s\n", runtime.GOOS, runtime.GOARCH)
}
Advanced
84. What is Docker Integration in Go?

Docker Integration involves creating container images for Go applications using Dockerfiles.

  • Dockerfile: Build image
  • Multi-stage Builds: Smaller images
  • Alpine: Minimal base images
  • Environment: Container configuration
  • Health Checks: Container health monitoring
go
// Docker Integration in Go
package main

import (
    "fmt"
    "os"
)

func main() {
    // Check if running in container
    if _, err := os.Stat("/.dockerenv"); err == nil {
        fmt.Println("Running in Docker container")
    }
    
    // Get container ID from cgroup
    if data, err := os.ReadFile("/proc/self/cgroup"); err == nil {
        fmt.Printf("Cgroup: %s\n", string(data[:100]))
    }
    
    // Container environment variables
    hostname, _ := os.Hostname()
    fmt.Printf("Hostname: %s\n", hostname)
    
    fmt.Println("Application running")
}
Advanced
85. What is Health Check Endpoint in Go?

Health Check Endpoints provide status information for monitoring and container orchestration systems.

  • /health: Overall health status
  • /ready: Readiness for traffic
  • /live: Liveness check
  • JSON Response: Structured status data
  • Service Checks: Database, cache, etc.
go
// Health Check Endpoint in Go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "time"
)

type HealthStatus struct {
    Status string `json:"status"`
    Timestamp string `json:"timestamp"`
    Uptime string `json:"uptime"`
    Services map[string]string `json:"services"`
}

var startTime = time.Now()

func healthHandler(w http.ResponseWriter, r *http.Request) {
    status := HealthStatus{
        Status: "healthy",
        Timestamp: time.Now().Format(time.RFC3339),
        Uptime: time.Since(startTime).String(),
        Services: map[string]string{
            "database": "healthy",
            "cache": "healthy",
            "api": "healthy",
        },
    }
    
    // Check services (simplified)
    // In real app, check actual service health
    
    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(status)
}

func readinessHandler(w http.ResponseWriter, r *http.Request) {
    // Check if application is ready to serve traffic
    ready := true
    if !ready {
        http.Error(w, "Not ready", http.StatusServiceUnavailable)
        return
    }
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"status": "ready"}`))
}

func livenessHandler(w http.ResponseWriter, r *http.Request) {
    // Check if application is alive
    w.WriteHeader(http.StatusOK)
    w.Write([]byte(`{"status": "alive"}`))
}

func main() {
    http.HandleFunc("/health", healthHandler)
    http.HandleFunc("/ready", readinessHandler)
    http.HandleFunc("/live", livenessHandler)
    
    fmt.Println("Server starting on :8080")
    http.ListenAndServe(":8080", nil)
}
Advanced
86. What is Graceful Restart in Go?

Graceful Restart allows restarting a server without downtime by starting a new process before stopping the old one.

  • Process Replacement: Start new process
  • Socket Transfer: Pass listening sockets
  • Signal Handling: Handle restart signals
  • Zero Downtime: No request interruption
  • Lock Files: Prevent multiple instances
go
// Graceful Restart in Go
package main

import (
    "fmt"
    "net"
    "os"
    "os/exec"
    "syscall"
)

func main() {
    fmt.Printf("PID: %d\n", os.Getpid())
    
    // Check if this is a restart
    if len(os.Args) > 1 && os.Args[1] == "restart" {
        fmt.Println("Restarting...")
        cmd := exec.Command(os.Args[0])
        cmd.Stdout = os.Stdout
        cmd.Stderr = os.Stderr
        cmd.Stdin = os.Stdin
        cmd.SysProcAttr = &syscall.SysProcAttr{
            Pdeathsig: syscall.SIGTERM,
        }
        cmd.Start()
        fmt.Printf("New process started: %d\n", cmd.Process.Pid)
        return
    }
    
    // Listen on port
    listener, err := net.Listen("tcp", ":8080")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer listener.Close()
    
    fmt.Println("Server running on :8080")
    
    // Keep running
    select {}
}
Advanced
87. What is Service Discovery in Go?

Service Discovery allows services to find each other in distributed systems. It registers and queries service instances.

  • Registry: Service registration
  • Health Checks: Heartbeat monitoring
  • Load Balancing: Distribute requests
  • Consul: Service discovery tool
  • etcd: Distributed key-value store
go
// Service Discovery in Go
package main

import (
    "fmt"
    "sync"
    "time"
)

type ServiceInstance struct {
    ID string
    Name string
    Address string
    Port int
    HealthCheckURL string
    LastSeen time.Time
}

type ServiceRegistry struct {
    mu sync.RWMutex
    services map[string][]ServiceInstance
}

func NewServiceRegistry() *ServiceRegistry {
    return &ServiceRegistry{
        services: make(map[string][]ServiceInstance),
    }
}

func (sr *ServiceRegistry) Register(service ServiceInstance) {
    sr.mu.Lock()
    defer sr.mu.Unlock()
    
    service.LastSeen = time.Now()
    sr.services[service.Name] = append(sr.services[service.Name], service)
    fmt.Printf("Registered service: %s (ID: %s)\n", service.Name, service.ID)
}

func (sr *ServiceRegistry) Deregister(name, id string) {
    sr.mu.Lock()
    defer sr.mu.Unlock()
    
    instances := sr.services[name]
    for i, svc := range instances {
        if svc.ID == id {
            sr.services[name] = append(instances[:i], instances[i+1:]...)
            fmt.Printf("Deregistered service: %s (ID: %s)\n", name, id)
            return
        }
    }
}

func (sr *ServiceRegistry) GetInstances(name string) []ServiceInstance {
    sr.mu.RLock()
    defer sr.mu.RUnlock()
    
    // Remove stale services
    var active []ServiceInstance
    for _, svc := range sr.services[name] {
        if time.Since(svc.LastSeen) < 30*time.Second {
            active = append(active, svc)
        }
    }
    return active
}

func main() {
    registry := NewServiceRegistry()
    
    // Register services
    registry.Register(ServiceInstance{
        ID: "1",
        Name: "user-service",
        Address: "localhost",
        Port: 8081,
    })
    registry.Register(ServiceInstance{
        ID: "2",
        Name: "user-service",
        Address: "localhost",
        Port: 8082,
    })
    registry.Register(ServiceInstance{
        ID: "3",
        Name: "order-service",
        Address: "localhost",
        Port: 8083,
    })
    
    // Get instances
    userServices := registry.GetInstances("user-service")
    fmt.Printf("User services: %v\n", userServices)
    
    orderServices := registry.GetInstances("order-service")
    fmt.Printf("Order services: %v\n", orderServices)
}
Advanced
88. What is Circuit Breaker with Metrics in Go?

Circuit Breaker with Metrics tracks request success/failure rates and opens the circuit based on metrics.

  • Metrics Collection: Track successes and failures
  • Threshold: Failure rate threshold
  • Time Window: Rolling window metrics
  • State Transitions: Closed, open, half-open
  • Monitoring: Export metrics for monitoring
go
// Circuit Breaker with Metrics in Go
package main

import (
    "errors"
    "fmt"
    "sync/atomic"
    "time"
)

type Metrics struct {
    totalRequests int64
    successRequests int64
    failureRequests int64
}

type CircuitBreakerWithMetrics struct {
    metrics Metrics
    state int32 // 0=closed, 1=open, 2=half-open
    failureThreshold int64
    timeout time.Duration
    lastFailure time.Time
    mu sync.Mutex
}

func NewCircuitBreakerWithMetrics(threshold int64, timeout time.Duration) *CircuitBreakerWithMetrics {
    return &CircuitBreakerWithMetrics{
        failureThreshold: threshold,
        timeout: timeout,
    }
}

func (cb *CircuitBreakerWithMetrics) Call(fn func() error) error {
    atomic.AddInt64(&cb.metrics.totalRequests, 1)
    
    cb.mu.Lock()
    if cb.state == 1 { // open
        if time.Since(cb.lastFailure) > cb.timeout {
            cb.state = 2 // half-open
            cb.mu.Unlock()
            fmt.Println("Circuit: half-open")
        } else {
            cb.mu.Unlock()
            atomic.AddInt64(&cb.metrics.failureRequests, 1)
            return errors.New("circuit breaker is open")
        }
    } else {
        cb.mu.Unlock()
    }
    
    err := fn()
    
    cb.mu.Lock()
    defer cb.mu.Unlock()
    
    if err != nil {
        atomic.AddInt64(&cb.metrics.failureRequests, 1)
        cb.lastFailure = time.Now()
        
        if cb.metrics.failureRequests >= cb.failureThreshold {
            cb.state = 1 // open
            fmt.Println("Circuit: open")
        }
        return err
    }
    
    atomic.AddInt64(&cb.metrics.successRequests, 1)
    if cb.state == 2 {
        cb.state = 0 // closed
        fmt.Println("Circuit: closed")
    }
    
    return nil
}

func (cb *CircuitBreakerWithMetrics) GetMetrics() Metrics {
    return cb.metrics
}

func main() {
    cb := NewCircuitBreakerWithMetrics(3, 2*time.Second)
    
    for i := 0; i < 10; i++ {
        err := cb.Call(func() error {
            if i < 4 {
                return errors.New("service error")
            }
            return nil
        })
        
        if err != nil {
            fmt.Printf("Call %d: Error - %v\n", i+1, err)
        } else {
            fmt.Printf("Call %d: Success\n", i+1)
        }
        time.Sleep(500 * time.Millisecond)
    }
    
    metrics := cb.GetMetrics()
    fmt.Printf("Metrics: Total=%d, Success=%d, Failure=%d\n",
        metrics.totalRequests, metrics.successRequests, metrics.failureRequests)
}
Advanced
89. What is Distributed Tracing in Go?

Distributed Tracing tracks requests across multiple services, providing end-to-end visibility.

  • Trace ID: Unique request identifier
  • Spans: Individual operations
  • Context Propagation: Pass trace context
  • Exporters: Jaeger, Zipkin, etc.
  • Instrumentation: Add tracing to code
go
// Distributed Tracing in Go
package main

import (
    "context"
    "fmt"
    "time"
)

type Span struct {
    ID string
    ParentID string
    Name string
    Start time.Time
    End time.Time
    Tags map[string]string
}

type Tracer struct {
    spans []*Span
}

func NewTracer() *Tracer {
    return &Tracer{
        spans: []*Span{},
    }
}

func (t *Tracer) StartSpan(ctx context.Context, name string) (*Span, context.Context) {
    span := &Span{
        ID: fmt.Sprintf("%d", len(t.spans)+1),
        Name: name,
        Start: time.Now(),
        Tags: make(map[string]string),
    }
    
    // Get parent ID from context
    if parent, ok := ctx.Value("span").(*Span); ok {
        span.ParentID = parent.ID
    }
    
    t.spans = append(t.spans, span)
    ctx = context.WithValue(ctx, "span", span)
    return span, ctx
}

func (t *Tracer) EndSpan(span *Span) {
    span.End = time.Now()
}

func (t *Tracer) AddTag(span *Span, key, value string) {
    span.Tags[key] = value
}

func (t *Tracer) PrintSpans() {
    for _, span := range t.spans {
        duration := span.End.Sub(span.Start)
        parent := "root"
        if span.ParentID != "" {
            parent = span.ParentID
        }
        fmt.Printf("Span: %s, Parent: %s, Duration: %v, Tags: %v\n",
            span.Name, parent, duration, span.Tags)
    }
}

func main() {
    tracer := NewTracer()
    ctx := context.Background()
    
    // Root span
    span1, ctx := tracer.StartSpan(ctx, "main")
    tracer.AddTag(span1, "service", "api-gateway")
    
    // Child span
    span2, ctx := tracer.StartSpan(ctx, "database-query")
    tracer.AddTag(span2, "table", "users")
    time.Sleep(50 * time.Millisecond)
    tracer.EndSpan(span2)
    
    // Another child span
    span3, ctx := tracer.StartSpan(ctx, "external-api")
    tracer.AddTag(span3, "endpoint", "/api/data")
    time.Sleep(100 * time.Millisecond)
    tracer.EndSpan(span3)
    
    tracer.EndSpan(span1)
    tracer.PrintSpans()
}
Advanced
90. What is API Versioning in Go?

API Versioning manages multiple API versions simultaneously to support backward compatibility.

  • URL Path: /api/v1/users
  • Header: API-Version: v1
  • Query Parameter: ?version=v1
  • Deprecation: Mark older versions
  • Migration: Gradual version migration
go
// API Versioning in Go
package main

import (
    "encoding/json"
    "fmt"
    "net/http"
    "strings"
)

type UserV1 struct {
    ID int `json:"id"`
    Name string `json:"name"`
}

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

func apiHandler(w http.ResponseWriter, r *http.Request) {
    path := r.URL.Path
    version := "v1"
    
    // Extract version from path
    if strings.Contains(path, "/v2/") {
        version = "v2"
    }
    
    // Also check header
    if v := r.Header.Get("API-Version"); v != "" {
        version = v
    }
    
    switch version {
    case "v1":
        users := []UserV1{
            {ID: 1, Name: "Alice"},
            {ID: 2, Name: "Bob"},
        }
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(users)
    case "v2":
        users := []UserV2{
            {ID: 1, Name: "Alice", Email: "alice@email.com", Age: 25},
            {ID: 2, Name: "Bob", Email: "bob@email.com", Age: 30},
        }
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(users)
    default:
        http.Error(w, "Unsupported version", http.StatusBadRequest)
    }
}

func main() {
    http.HandleFunc("/api/v1/users", apiHandler)
    http.HandleFunc("/api/v2/users", apiHandler)
    http.HandleFunc("/api/users", apiHandler)
    
    fmt.Println("Server starting on :8080")
    http.ListenAndServe(":8080", nil)
}
Advanced
91. What is Pagination in Go?

Pagination splits large result sets into smaller pages for better performance and user experience.

  • Page/PerPage: Current page and items per page
  • Offset/Limit: SQL pagination
  • Total Pages: Calculate total pages
  • Navigation: Next, previous, first, last
  • Performance: Use LIMIT/OFFSET
go
// Pagination in Go
package main

import (
    "fmt"
    "math"
    "strconv"
)

type Pagination struct {
    Page int `json:"page"`
    PerPage int `json:"per_page"`
    Total int `json:"total"`
    TotalPages int `json:"total_pages"`
}

func Paginate(items []string, page, perPage int) ([]string, Pagination) {
    total := len(items)
    totalPages := int(math.Ceil(float64(total) / float64(perPage)))
    
    if page < 1 {
        page = 1
    }
    if perPage < 1 {
        perPage = 10
    }
    
    start := (page - 1) * perPage
    end := start + perPage
    
    if start >= total {
        return []string{}, Pagination{
            Page: page,
            PerPage: perPage,
            Total: total,
            TotalPages: totalPages,
        }
    }
    
    if end > total {
        end = total
    }
    
    pagination := Pagination{
        Page: page,
        PerPage: perPage,
        Total: total,
        TotalPages: totalPages,
    }
    
    return items[start:end], pagination
}

func main() {
    items := make([]string, 0)
    for i := 1; i <= 100; i++ {
        items = append(items, fmt.Sprintf("Item %d", i))
    }
    
    page := 2
    perPage := 10
    
    result, pagination := Paginate(items, page, perPage)
    
    fmt.Printf("Page %d of %d\n", pagination.Page, pagination.TotalPages)
    fmt.Printf("Items: %v\n", result)
    fmt.Printf("Pagination: %+v\n", pagination)
}
Advanced
92. What is Sorting and Filtering in Go?

Sorting and Filtering organizes and filters data based on specified criteria.

  • Sort: sort.Slice with custom comparator
  • Filter: Conditional filtering
  • Multi-field: Sort by multiple fields
  • ASC/DESC: Ascending/descending order
  • Query Parameters: Sort and filter from requests
go
// Sorting and Filtering in Go
package main

import (
    "fmt"
    "sort"
    "strings"
)

type User struct {
    ID int
    Name string
    Age int
    Email string
}

type UserFilter struct {
    NameContains string
    MinAge int
    MaxAge int
    SortBy string
    SortDesc bool
}

func FilterUsers(users []User, filter UserFilter) []User {
    result := make([]User, 0)
    
    for _, user := range users {
        // Filter by name
        if filter.NameContains != "" && !strings.Contains(strings.ToLower(user.Name), strings.ToLower(filter.NameContains)) {
            continue
        }
        
        // Filter by age
        if filter.MinAge > 0 && user.Age < filter.MinAge {
            continue
        }
        if filter.MaxAge > 0 && user.Age > filter.MaxAge {
            continue
        }
        
        result = append(result, user)
    }
    
    // Sort
    if filter.SortBy != "" {
        sort.Slice(result, func(i, j int) bool {
            var less bool
            switch filter.SortBy {
            case "name":
                less = result[i].Name < result[j].Name
            case "age":
                less = result[i].Age < result[j].Age
            case "id":
                less = result[i].ID < result[j].ID
            default:
                less = result[i].ID < result[j].ID
            }
            
            if filter.SortDesc {
                return !less
            }
            return less
        })
    }
    
    return result
}

func main() {
    users := []User{
        {ID: 3, Name: "Carol", Age: 22, Email: "carol@email.com"},
        {ID: 1, Name: "Alice", Age: 25, Email: "alice@email.com"},
        {ID: 4, Name: "Dave", Age: 35, Email: "dave@email.com"},
        {ID: 2, Name: "Bob", Age: 30, Email: "bob@email.com"},
    }
    
    filter := UserFilter{
        NameContains: "a",
        MinAge: 20,
        MaxAge: 30,
        SortBy: "name",
        SortDesc: false,
    }
    
    result := FilterUsers(users, filter)
    for _, user := range result {
        fmt.Printf("%+v\n", user)
    }
}
Advanced
93. What is Search in Go?

Search finds relevant documents based on query terms with relevance scoring.

  • Term Search: Match query terms
  • Relevance: Score matching documents
  • Ranking: Sort by relevance
  • Full-Text: Index and search text
  • Elasticsearch: Search engine integration
go
// Search in Go
package main

import (
    "fmt"
    "strings"
)

type Document struct {
    ID int
    Title string
    Content string
}

type SearchResult struct {
    Document Document
    Score int
}

func Search(documents []Document, query string) []SearchResult {
    results := make([]SearchResult, 0)
    query = strings.ToLower(query)
    queryWords := strings.Fields(query)
    
    for _, doc := range documents {
        score := 0
        content := strings.ToLower(doc.Title + " " + doc.Content)
        
        for _, word := range queryWords {
            if strings.Contains(content, word) {
                score++
            }
        }
        
        if score > 0 {
            results = append(results, SearchResult{Document: doc, Score: score})
        }
    }
    
    // Sort by score (descending)
    for i := 0; i < len(results); i++ {
        for j := i + 1; j < len(results); j++ {
            if results[j].Score > results[i].Score {
                results[i], results[j] = results[j], results[i]
            }
        }
    }
    
    return results
}

func main() {
    documents := []Document{
        {ID: 1, Title: "Go Programming", Content: "Go is a programming language designed for simplicity"},
        {ID: 2, Title: "Web Development", Content: "Web development with Go is fast and efficient"},
        {ID: 3, Title: "Database Systems", Content: "Go has excellent database drivers and ORM support"},
    }
    
    results := Search(documents, "go programming")
    
    fmt.Println("Search results:")
    for _, result := range results {
        fmt.Printf("Doc %d: %s (Score: %d)\n", 
            result.Document.ID, result.Document.Title, result.Score)
    }
}
Advanced
94. What is Caching in Go?

Caching stores frequently accessed data to improve performance. Go implements caching with TTL and eviction policies.

  • In-Memory: Memory-based cache
  • TTL: Time-to-live for entries
  • LRU: Least Recently Used eviction
  • Redis: Distributed caching
  • Cache-Aside: Read-through pattern
go
// Caching in Go
package main

import (
    "fmt"
    "sync"
    "time"
)

type CacheItem struct {
    Value interface{}
    Expiration time.Time
}

type Cache struct {
    mu sync.RWMutex
    items map[string]CacheItem
    defaultTTL time.Duration
    cleanupInterval time.Duration
}

func NewCache(defaultTTL, cleanupInterval time.Duration) *Cache {
    cache := &Cache{
        items: make(map[string]CacheItem),
        defaultTTL: defaultTTL,
        cleanupInterval: cleanupInterval,
    }
    
    // Start cleanup goroutine
    go cache.cleanup()
    
    return cache
}

func (c *Cache) Set(key string, value interface{}, ttl ...time.Duration) {
    expiration := time.Now().Add(c.defaultTTL)
    if len(ttl) > 0 {
        expiration = time.Now().Add(ttl[0])
    }
    
    c.mu.Lock()
    defer c.mu.Unlock()
    c.items[key] = CacheItem{Value: value, Expiration: expiration}
}

func (c *Cache) Get(key string) (interface{}, bool) {
    c.mu.RLock()
    item, exists := c.items[key]
    c.mu.RUnlock()
    
    if !exists {
        return nil, false
    }
    
    if time.Now().After(item.Expiration) {
        c.Delete(key)
        return nil, false
    }
    
    return item.Value, true
}

func (c *Cache) Delete(key string) {
    c.mu.Lock()
    defer c.mu.Unlock()
    delete(c.items, key)
}

func (c *Cache) cleanup() {
    ticker := time.NewTicker(c.cleanupInterval)
    defer ticker.Stop()
    
    for range ticker.C {
        c.mu.Lock()
        for key, item := range c.items {
            if time.Now().After(item.Expiration) {
                delete(c.items, key)
            }
        }
        c.mu.Unlock()
    }
}

func main() {
    cache := NewCache(5*time.Second, 1*time.Second)
    
    cache.Set("key1", "value1")
    cache.Set("key2", "value2", 2*time.Second)
    
    // Get immediately
    if val, ok := cache.Get("key1"); ok {
        fmt.Printf("key1: %v\n", val)
    }
    
    // Wait for expiration
    time.Sleep(3 * time.Second)
    
    if val, ok := cache.Get("key2"); ok {
        fmt.Printf("key2: %v\n", val)
    } else {
        fmt.Println("key2 expired")
    }
}
Advanced
95. What is Rate Limiting with Token Bucket in Go?

Token Bucket Rate Limiting controls request rates by consuming tokens from a bucket that refills over time.

  • Tokens: Available tokens
  • Refill: Tokens added periodically
  • Consume: Tokens removed per request
  • Wait: Block until token available
  • Burst: Maximum token capacity
go
// Rate Limiting with Token Bucket in Go
package main

import (
    "fmt"
    "sync"
    "time"
)

type TokenBucket struct {
    mu sync.Mutex
    tokens int
    maxTokens int
    refillRate time.Duration
    lastRefill time.Time
}

func NewTokenBucket(maxTokens int, refillRate time.Duration) *TokenBucket {
    return &TokenBucket{
        tokens: maxTokens,
        maxTokens: maxTokens,
        refillRate: refillRate,
        lastRefill: time.Now(),
    }
}

func (tb *TokenBucket) refill() {
    now := time.Now()
    elapsed := now.Sub(tb.lastRefill)
    tokensToAdd := int(elapsed / tb.refillRate)
    
    if tokensToAdd > 0 {
        tb.tokens = tb.tokens + tokensToAdd
        if tb.tokens > tb.maxTokens {
            tb.tokens = tb.maxTokens
        }
        tb.lastRefill = now
    }
}

func (tb *TokenBucket) Allow() bool {
    tb.mu.Lock()
    defer tb.mu.Unlock()
    
    tb.refill()
    
    if tb.tokens > 0 {
        tb.tokens--
        return true
    }
    return false
}

func (tb *TokenBucket) Wait() {
    for !tb.Allow() {
        time.Sleep(10 * time.Millisecond)
    }
}

func main() {
    tb := NewTokenBucket(5, 100*time.Millisecond)
    
    for i := 0; i < 10; i++ {
        if tb.Allow() {
            fmt.Printf("Request %d allowed at %v\n", i+1, time.Now())
        } else {
            fmt.Printf("Request %d denied at %v\n", i+1, time.Now())
        }
        time.Sleep(50 * time.Millisecond)
    }
}
Advanced
96. What is Context with Values and Timeout in Go?

Context with Values and Timeout combines request-scoped data with deadlines for complete request management.

  • Values: Request-scoped data
  • Timeout: Deadline for operations
  • Cancellation: Cancel operations
  • Propagation: Pass context through API
  • Cleanup: Defer cancel for cleanup
go
// Context with Values and Timeout in Go
package main

import (
    "context"
    "fmt"
    "time"
)

type contextKey string

func processRequest(ctx context.Context) {
    // Get values from context
    userID := ctx.Value(contextKey("userID"))
    requestID := ctx.Value(contextKey("requestID"))
    
    if userID == nil || requestID == nil {
        fmt.Println("Missing context values")
        return
    }
    
    fmt.Printf("Processing request: userID=%v, requestID=%v\n", userID, requestID)
    
    // Simulate work with timeout
    select {
    case <-ctx.Done():
        fmt.Println("Request cancelled: ", ctx.Err())
        return
    case <-time.After(2 * time.Second):
        fmt.Println("Request completed successfully")
    }
}

func main() {
    // Create context with values
    ctx := context.Background()
    ctx = context.WithValue(ctx, contextKey("userID"), "12345")
    ctx = context.WithValue(ctx, contextKey("requestID"), "req-abc-123")
    
    // Add timeout
    ctx, cancel := context.WithTimeout(ctx, 1*time.Second)
    defer cancel()
    
    processRequest(ctx)
}
Advanced
97. What is Database Transaction in Go?

Database Transactions ensure atomicity of multiple database operations using the Tx interface.

  • Begin: Start transaction
  • Exec: Execute operations
  • Commit: Commit changes
  • Rollback: Rollback on error
  • Isolation: Transaction isolation levels
go
// Database Transaction in Go
package main

import (
    "database/sql"
    "fmt"
    _ "github.com/go-sql-driver/mysql"
)

func main() {
    // Connect to database
    db, err := sql.Open("mysql", "user:password@tcp(127.0.0.1:3306)/dbname")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer db.Close()
    
    // Start transaction
    tx, err := db.Begin()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    // Perform operations
    _, err = tx.Exec("INSERT INTO users (name, age) VALUES (?, ?)", "Alice", 25)
    if err != nil {
        tx.Rollback()
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    _, err = tx.Exec("INSERT INTO users (name, age) VALUES (?, ?)", "Bob", 30)
    if err != nil {
        tx.Rollback()
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    // Commit transaction
    err = tx.Commit()
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    
    fmt.Println("Transaction completed successfully")
}
Advanced
98. What is Database Migration in Go?

Database Migration manages schema changes over time using versioned migration scripts.

  • Migration Table: Track applied migrations
  • Up/Down: Apply and rollback
  • Version Control: Track migration scripts
  • Tools: goose, golang-migrate
  • Idempotent: Safe to run multiple times
go
// Database Migration in Go
package main

import (
    "database/sql"
    "fmt"
    "os"
    "path/filepath"
)

type Migration struct {
    Version int
    Name string
    Up string
    Down string
}

var migrations = []Migration{
    {
        Version: 1,
        Name: "create_users_table",
        Up: `CREATE TABLE users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            name TEXT NOT NULL,
            email TEXT UNIQUE NOT NULL,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP
        )`,
        Down: `DROP TABLE users`,
    },
    {
        Version: 2,
        Name: "create_posts_table",
        Up: `CREATE TABLE posts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL,
            title TEXT NOT NULL,
            content TEXT,
            created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
            FOREIGN KEY (user_id) REFERENCES users(id)
        )`,
        Down: `DROP TABLE posts`,
    },
}

func RunMigrations(db *sql.DB) error {
    // Create migrations table if not exists
    _, err := db.Exec(`CREATE TABLE IF NOT EXISTS migrations (
        version INTEGER PRIMARY KEY,
        applied_at DATETIME DEFAULT CURRENT_TIMESTAMP
    )`)
    if err != nil {
        return err
    }
    
    // Get current version
    var currentVersion int
    row := db.QueryRow("SELECT COALESCE(MAX(version), 0) FROM migrations")
    row.Scan(&currentVersion)
    
    // Run pending migrations
    for _, migration := range migrations {
        if migration.Version > currentVersion {
            fmt.Printf("Running migration %d: %s\n", migration.Version, migration.Name)
            
            _, err := db.Exec(migration.Up)
            if err != nil {
                return fmt.Errorf("migration %d failed: %v", migration.Version, err)
            }
            
            _, err = db.Exec("INSERT INTO migrations (version) VALUES (?)", migration.Version)
            if err != nil {
                return err
            }
            
            fmt.Printf("Migration %d complete\n", migration.Version)
        }
    }
    
    return nil
}

func main() {
    // In production, connect to actual database
    // db, err := sql.Open("sqlite3", "app.db")
    // defer db.Close()
    
    fmt.Println("Migration tool ready")
}
Advanced
99. What is API Client in Go?

API Client provides a structured way to interact with external APIs with proper error handling and configuration.

  • Base URL: API endpoint
  • Headers: Authentication and content type
  • Methods: GET, POST, PUT, DELETE
  • JSON: Marshal and unmarshal
  • Error Handling: Handle API errors
go
// API Client in Go
package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io/ioutil"
    "net/http"
    "time"
)

type APIClient struct {
    BaseURL string
    Client *http.Client
    APIKey string
}

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

type Response struct {
    Data interface{} `json:"data"`
    Message string `json:"message"`
    Status string `json:"status"`
}

func NewAPIClient(baseURL, apiKey string) *APIClient {
    return &APIClient{
        BaseURL: baseURL,
        Client: &http.Client{
            Timeout: 10 * time.Second,
        },
        APIKey: apiKey,
    }
}

func (c *APIClient) Get(path string, result interface{}) error {
    url := c.BaseURL + path
    
    req, err := http.NewRequest("GET", url, nil)
    if err != nil {
        return err
    }
    
    req.Header.Set("Authorization", "Bearer "+c.APIKey)
    req.Header.Set("Content-Type", "application/json")
    
    resp, err := c.Client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return err
    }
    
    return json.Unmarshal(body, result)
}

func (c *APIClient) Post(path string, data interface{}, result interface{}) error {
    url := c.BaseURL + path
    
    jsonData, err := json.Marshal(data)
    if err != nil {
        return err
    }
    
    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
    if err != nil {
        return err
    }
    
    req.Header.Set("Authorization", "Bearer "+c.APIKey)
    req.Header.Set("Content-Type", "application/json")
    
    resp, err := c.Client.Do(req)
    if err != nil {
        return err
    }
    defer resp.Body.Close()
    
    body, err := ioutil.ReadAll(resp.Body)
    if err != nil {
        return err
    }
    
    return json.Unmarshal(body, result)
}

func main() {
    client := NewAPIClient("https://api.example.com", "your-api-key")
    
    // GET request
    var users []User
    err := client.Get("/users", &users)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Users: %+v\n", users)
    }
    
    // POST request
    newUser := User{Name: "Alice", Email: "alice@email.com"}
    var response Response
    err = client.Post("/users", newUser, &response)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Response: %+v\n", response)
    }
}
Advanced
100. How to build a Complete Web Application in Go?

A Complete Web Application in Go demonstrates full-stack development with routing, middleware, database, and graceful shutdown.

  • Routing: HTTP routing with handlers
  • Middleware: Logging, CORS, auth
  • Database: Store and retrieve data
  • Models: Data structures
  • Graceful Shutdown: Clean server shutdown
go
// Complete Web Application in Go
package main

import (
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "os"
    "os/signal"
    "sync"
    "syscall"
    "time"
)

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

// In-memory store
type Store struct {
    mu sync.RWMutex
    users map[int]User
    nextID int
}

func NewStore() *Store {
    return &Store{
        users: make(map[int]User),
        nextID: 1,
    }
}

func (s *Store) CreateUser(name, email string) User {
    s.mu.Lock()
    defer s.mu.Unlock()
    
    user := User{
        ID: s.nextID,
        Name: name,
        Email: email,
        CreatedAt: time.Now(),
    }
    s.users[user.ID] = user
    s.nextID++
    return user
}

func (s *Store) GetUsers() []User {
    s.mu.RLock()
    defer s.mu.RUnlock()
    
    users := make([]User, 0, len(s.users))
    for _, user := range s.users {
        users = append(users, user)
    }
    return users
}

func (s *Store) GetUser(id int) (User, bool) {
    s.mu.RLock()
    defer s.mu.RUnlock()
    
    user, ok := s.users[id]
    return user, ok
}

// Handlers
func handleUsers(store *Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        switch r.Method {
        case "GET":
            users := store.GetUsers()
            w.Header().Set("Content-Type", "application/json")
            json.NewEncoder(w).Encode(users)
            
        case "POST":
            var user User
            if err := json.NewDecoder(r.Body).Decode(&user); err != nil {
                http.Error(w, "Invalid request", http.StatusBadRequest)
                return
            }
            
            created := store.CreateUser(user.Name, user.Email)
            w.Header().Set("Content-Type", "application/json")
            w.WriteHeader(http.StatusCreated)
            json.NewEncoder(w).Encode(created)
            
        default:
            http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
        }
    }
}

func handleUser(store *Store) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        // Extract ID from URL
        // Simplified: expect ID in query param
        idStr := r.URL.Query().Get("id")
        if idStr == "" {
            http.Error(w, "ID required", http.StatusBadRequest)
            return
        }
        
        var id int
        fmt.Sscanf(idStr, "%d", &id)
        
        user, ok := store.GetUser(id)
        if !ok {
            http.Error(w, "User not found", http.StatusNotFound)
            return
        }
        
        w.Header().Set("Content-Type", "application/json")
        json.NewEncoder(w).Encode(user)
    }
}

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

func corsMiddleware(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        w.Header().Set("Access-Control-Allow-Origin", "*")
        w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS")
        w.Header().Set("Access-Control-Allow-Headers", "Content-Type, Authorization")
        
        if r.Method == "OPTIONS" {
            w.WriteHeader(http.StatusOK)
            return
        }
        
        next.ServeHTTP(w, r)
    })
}

func main() {
    store := NewStore()
    
    // Add sample data
    store.CreateUser("Alice", "alice@email.com")
    store.CreateUser("Bob", "bob@email.com")
    
    // Router
    mux := http.NewServeMux()
    mux.HandleFunc("/api/users", handleUsers(store))
    mux.HandleFunc("/api/user", handleUser(store))
    mux.HandleFunc("/health", func(w http.ResponseWriter, r *http.Request) {
        w.WriteHeader(http.StatusOK)
        w.Write([]byte(`{"status": "healthy"}`))
    })
    
    // Apply middleware
    handler := loggingMiddleware(corsMiddleware(mux))
    
    server := &http.Server{
        Addr: ":8080",
        Handler: handler,
        ReadTimeout: 10 * time.Second,
        WriteTimeout: 10 * time.Second,
        IdleTimeout: 30 * time.Second,
    }
    
    // Graceful shutdown
    go func() {
        log.Println("Server starting on :8080")
        if err := server.ListenAndServe(); err != nil && err != http.ErrServerClosed {
            log.Fatalf("Server error: %v", err)
        }
    }()
    
    quit := make(chan os.Signal, 1)
    signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
    <-quit
    
    log.Println("Shutting down server...")
    server.Shutdown(context.Background())
    log.Println("Server stopped")
}