InterviewPitch
GoLang interview questions

GoLang Interview Questions with Answers

Most Asked GoLang Interview Questions for Software Engineer Roles

100+ QuestionsDetailed AnswersCode ExamplesUpdated for 2026

Introduction

This page provides a complete collection of Go Interview Questions and Answers designed for Go developers, backend engineers, cloud engineers, and software professionals preparing for technical interviews. Go (often referred to as Golang) is an open‑source, statically typed, compiled programming language developed by Google. It is designed for simplicity, performance, and scalability, making it a top choice for building cloud‑native applications, microservices, APIs, web servers, and distributed systems. This interview guide covers beginner, intermediate, and advanced Go concepts including syntax, variables, data types, functions, structs, interfaces, pointers, concurrency (goroutines and channels), error handling, packages, testing, and real‑world Go development scenarios.

Why Go?

  • Simple, readable syntax – easy to learn and maintain
  • Built‑in concurrency – goroutines and channels for high‑performance parallel processing
  • Compiles to a single binary – fast execution and simple deployment
  • Garbage collection – automatic memory management with low latency
  • Rich standard library – includes HTTP, crypto, testing, and more
  • Used by companies like Google, Uber, Dropbox, and Kubernetes
  • High demand – one of the most sought‑after skills in backend and cloud 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 declaration
type Person struct {
    Name string
    Age  int
    Email string
}

// Method with value receiver
func (p Person) Greet() string {
    return fmt.Sprintf("Hello, I'm %s", p.Name)
}

// Method with pointer receiver (modifies struct)
func (p *Person) UpdateEmail(newEmail string) {
    p.Email = newEmail
}

// Method with pointer receiver for modification
func (p *Person) HaveBirthday() {
    p.Age++
}

// Struct embedding (composition)
type Employee struct {
    Person      // Embedded struct
    EmployeeID int
    Department string
}

func main() {
    // Declaration and initialization
    person1 := Person{Name: "Alice", Age: 25, Email: "alice@email.com"}
    person2 := Person{Name: "Bob", Age: 30, Email: "bob@email.com"}
    
    // Using methods
    fmt.Println(person1.Greet())
    person1.UpdateEmail("alice@new.com")
    fmt.Printf("Updated email: %s\n", person1.Email)
    
    // Pointer receiver modification
    fmt.Printf("Bob's age: %d\n", person2.Age)
    person2.HaveBirthday()
    fmt.Printf("Bob's age after birthday: %d\n", person2.Age)
    
    // Struct embedding
    employee := Employee{
        Person:     Person{Name: "Charlie", Age: 28, Email: "charlie@company.com"},
        EmployeeID: 1001,
        Department: "Engineering",
    }
    
    fmt.Printf("Employee: %s, ID: %d, Dept: %s\n", 
        employee.Name, employee.EmployeeID, employee.Department)
}
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"
    "time"
)

func main() {
    // Unbuffered channel
    ch := make(chan int)
    
    // Send data in goroutine
    go func() {
        ch <- 42
    }()
    
    // Receive data
    value := <-ch
    fmt.Printf("Received: %d\n", value)
    
    // Buffered channel (capacity 2)
    buffered := make(chan int, 2)
    buffered <- 1
    buffered <- 2
    // buffered <- 3 // This would block (buffer full)
    
    fmt.Printf("Buffered channel: %d, %d\n", <-buffered, <-buffered)
    
    // Channel with goroutines
    messages := make(chan string)
    
    go func() {
        messages <- "Hello"
        messages <- "World"
        close(messages)
    }()
    
    // Range over channel until closed
    for msg := range messages {
        fmt.Println(msg)
    }
    
    // Select statement for multiple channels
    ch1 := make(chan string)
    ch2 := make(chan string)
    
    go func() {
        time.Sleep(1 * time.Second)
        ch1 <- "from ch1"
    }()
    
    go func() {
        time.Sleep(2 * time.Second)
        ch2 <- "from ch2"
    }()
    
    for i := 0; i < 2; i++ {
        select {
        case msg1 := <-ch1:
            fmt.Println(msg1)
        case msg2 := <-ch2:
            fmt.Println(msg2)
        }
    }
    
    // Channel directions
    func sendOnly(ch chan<- int) {
        ch <- 100
    }
    
    func receiveOnly(ch <-chan int) {
        value := <-ch
        fmt.Printf("Received: %d\n", value)
    }
    
    ch3 := make(chan int)
    go sendOnly(ch3)
    receiveOnly(ch3)
}
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() {
    // Basic select
    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"
    }()
    
    // Select with timeout
    select {
    case msg1 := <-ch1:
        fmt.Println(msg1)
    case msg2 := <-ch2:
        fmt.Println(msg2)
    case <-time.After(3 * time.Second):
        fmt.Println("Timeout!")
    }
    
    // Non-blocking select with default
    ch3 := make(chan int)
    select {
    case val := <-ch3:
        fmt.Printf("Received: %d\n", val)
    default:
        fmt.Println("No data available")
    }
    
    // Fan-in pattern (multiple channels to one)
    fanIn := func(ch1, ch2 <-chan string) <-chan string {
        out := make(chan string)
        go func() {
            for {
                select {
                case msg := <-ch1:
                    out <- msg
                case msg := <-ch2:
                    out <- msg
                }
            }
        }()
        return out
    }
    
    c1 := make(chan string)
    c2 := make(chan string)
    
    go func() {
        c1 <- "From channel 1"
    }()
    go func() {
        c2 <- "From channel 2"
    }()
    
    out := fanIn(c1, c2)
    fmt.Println(<-out)
    fmt.Println(<-out)
    
    // Fan-out pattern (one channel to multiple)
    work := make(chan int)
    done := make(chan bool)
    
    // Worker
    worker := func(id int, work <-chan int, done chan<- bool) {
        for w := range work {
            fmt.Printf("Worker %d processing: %d\n", id, w)
            time.Sleep(100 * time.Millisecond)
        }
        done <- true
    }
    
    // Send work
    go func() {
        for i := 1; i <= 5; i++ {
            work <- i
        }
        close(work)
    }()
    
    // Start workers
    for i := 1; i <= 3; i++ {
        go worker(i, work, done)
    }
    
    // Wait for all workers
    for i := 1; i <= 3; i++ {
        <-done
    }
    fmt.Println("All workers done")
}
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
type ValidationError struct {
    Field string
    Message string
}

func (e ValidationError) Error() string {
    return fmt.Sprintf("validation error on %s: %s", e.Field, e.Message)
}

// Function that returns error
func divide(a, b int) (int, 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",
            Message: "age cannot be negative",
        }
    }
    if age > 150 {
        return ValidationError{
            Field:   "age",
            Message: "age cannot exceed 150",
        }
    }
    return nil
}

// Function with multiple return values including error
func processUser(name string, age int) (string, error) {
    if name == "" {
        return "", errors.New("name cannot be empty")
    }
    
    if err := validateAge(age); err != nil {
        return "", err
    }
    
    return fmt.Sprintf("User: %s, Age: %d", name, age), nil
}

// Panic and recover example
func riskyOperation() {
    defer func() {
        if r := recover(); r != nil {
            fmt.Printf("Recovered from panic: %v\n", r)
        }
    }()
    
    panic("something went wrong")
}

func main() {
    // Basic error handling
    result, err := divide(10, 2)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    } else {
        fmt.Printf("Result: %d\n", result)
    }
    
    // Error handling with custom error
    if err := validateAge(200); err != nil {
        fmt.Printf("Validation error: %v\n", err)
    }
    
    // Multiple returns with error
    user, err := processUser("Alice", 25)
    if err != nil {
        fmt.Printf("Error processing user: %v\n", err)
    } else {
        fmt.Printf("Processed: %s\n", user)
    }
    
    // Error checking pattern
    data, err := processUser("", 30)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        // Handle error
        return
    }
    fmt.Printf("Data: %s\n", data)
    
    // Panic and recover
    riskyOperation()
    fmt.Println("Program continues after panic")
}
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
21. What are Struct Tags in Go?

Struct Tags are metadata attached to struct fields. They are used for serialization, validation, and documentation.

  • JSON Tags: json:"name"
  • Validation: validate:"required"
  • ORM Tags: gorm:"column"
  • Reflection: Access tags via reflect
  • Multiple Tags: json:"name" validate:"required"
go
// Struct Tags in Go
package main

import (
    "encoding/json"
    "fmt"
)

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

func main() {
    user := User{ID: 1, Name: "Alice", Email: "alice@email.com", Password: "secret"}
    jsonData, _ := json.Marshal(user)
    fmt.Printf("JSON: %s\n", jsonData)
}
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 (
    "errors"
    "fmt"
)

// Custom error type 1: Simple struct error
type MyError struct {
    Code    int
    Message string
}

func (e MyError) Error() string {
    return fmt.Sprintf("error %d: %s", e.Code, e.Message)
}

// Custom error type 2: With additional methods
type ValidationError struct {
    Field   string
    Value   interface{}
    Message string
}

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

// Additional method for ValidationError
func (e ValidationError) IsValid() bool {
    return false
}

// Custom error type 3: Wrapping errors
type DatabaseError struct {
    Err     error
    Query   string
    Context string
}

func (e DatabaseError) Error() string {
    return fmt.Sprintf("database error in %s: %v", e.Context, e.Err)
}

// Unwrap method for error wrapping
func (e DatabaseError) Unwrap() error {
    return e.Err
}

// Function that returns custom error
func validateUser(name string, age int) error {
    if name == "" {
        return ValidationError{
            Field:   "name",
            Value:   name,
            Message: "name cannot be empty",
        }
    }
    
    if age < 0 || age > 150 {
        return ValidationError{
            Field:   "age",
            Value:   age,
            Message: "age must be between 0 and 150",
        }
    }
    
    return nil
}

// Function with wrapped error
func queryDatabase(query string) error {
    // Simulate database error
    return DatabaseError{
        Err:     errors.New("connection refused"),
        Query:   query,
        Context: "queryDatabase",
    }
}

func main() {
    // Using custom error
    err := validateUser("", 25)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    }
    
    err = validateUser("Alice", 200)
    if err != nil {
        fmt.Printf("Error: %v\n", err)
    }
    
    // Using errors package
    err = errors.New("something went wrong")
    fmt.Printf("Simple error: %v\n", err)
    
    // Error wrapping
    err = fmt.Errorf("context: %w", errors.New("original error"))
    fmt.Printf("Wrapped error: %v\n", err)
    
    // Check for specific error type
    err = queryDatabase("SELECT * FROM users")
    if err != nil {
        fmt.Printf("Database error: %v\n", err)
        
        // Unwrap to check underlying error
        if unwrapped := errors.Unwrap(err); unwrapped != nil {
            fmt.Printf("Underlying error: %v\n", unwrapped)
        }
    }
    
    // Using errors.Is and errors.As
    var dbErr DatabaseError
    if errors.As(err, &dbErr) {
        fmt.Printf("Database error context: %s\n", dbErr.Context)
    }
}
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"

// Base struct
type Person struct {
    Name string
    Age  int
}

func (p Person) Greet() string {
    return fmt.Sprintf("Hello, I'm %s", p.Name)
}

func (p Person) GetAge() int {
    return p.Age
}

// Embedding Person in Employee
type Employee struct {
    Person      // Anonymous field (embedding)
    EmployeeID  int
    Department  string
    Salary      float64
}

// Override Greet method
func (e Employee) Greet() string {
    return fmt.Sprintf("Hello, I'm %s (Employee #%d)", e.Name, e.EmployeeID)
}

// Additional method for Employee
func (e Employee) Work() string {
    return fmt.Sprintf("%s is working in %s", e.Name, e.Department)
}

// Embedding with interface
type Reader interface {
    Read() string
}

type Writer interface {
    Write(data string)
}

// Embedding interfaces
type ReadWriter interface {
    Reader
    Writer
}

// Struct implementing ReadWriter
type FileHandler struct {
    filename string
}

func (f FileHandler) Read() string {
    return fmt.Sprintf("Reading from %s", f.filename)
}

func (f FileHandler) Write(data string) {
    fmt.Printf("Writing '%s' to %s\n", data, f.filename)
}

// Multiple embedding
type Contact struct {
    Email string
    Phone string
}

type Address struct {
    Street  string
    City    string
    Country string
}

type Customer struct {
    Person          // Embed Person
    Contact         // Embed Contact
    Address         // Embed Address
    CustomerID int
}

func main() {
    // Create Employee with embedded Person
    emp := Employee{
        Person: Person{
            Name: "Alice",
            Age:  30,
        },
        EmployeeID: 1001,
        Department: "Engineering",
        Salary:     75000.0,
    }
    
    // Access embedded fields directly
    fmt.Printf("Name: %s\n", emp.Name)
    fmt.Printf("Age: %d\n", emp.Age)
    
    // Access embedded methods
    fmt.Println(emp.Greet()) // Overridden method
    fmt.Printf("Age from embedded: %d\n", emp.GetAge())
    fmt.Println(emp.Work())
    
    // Interface embedding
    var rw ReadWriter = FileHandler{filename: "data.txt"}
    fmt.Println(rw.Read())
    rw.Write("Hello World")
    
    // Multiple embedding
    customer := Customer{
        Person: Person{
            Name: "Bob",
            Age:  25,
        },
        Contact: Contact{
            Email: "bob@email.com",
            Phone: "123-456-7890",
        },
        Address: Address{
            Street:  "123 Main St",
            City:    "New York",
            Country: "USA",
        },
        CustomerID: 2001,
    }
    
    // Access fields from all embedded structs
    fmt.Printf("Customer: %s, Age: %d, Email: %s, City: %s\n",
        customer.Name, customer.Age, customer.Email, customer.City)
    
    // Overriding embedded fields
    type Manager struct {
        Employee
        TeamSize int
    }
    
    manager := Manager{
        Employee: Employee{
            Person: Person{
                Name: "Charlie",
                Age:  35,
            },
            EmployeeID: 1002,
            Department: "Management",
            Salary:     100000.0,
        },
        TeamSize: 10,
    }
    
    fmt.Printf("Manager: %s manages %d people\n", manager.Name, manager.TeamSize)
}
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"

// Basic iota usage
const (
    Sunday = iota // 0
    Monday        // 1
    Tuesday       // 2
    Wednesday     // 3
    Thursday      // 4
    Friday        // 5
    Saturday      // 6
)

// iota with expressions
const (
    _ = iota             // 0 (skipped)
    KB = 1 << (10 * iota) // 1 << (10*1) = 1024
    MB                    // 1 << (10*2) = 1048576
    GB                    // 1 << (10*3) = 1073741824
    TB                    // 1 << (10*4) = 1099511627776
)

// iota with bitmask
const (
    Read = 1 << iota // 1
    Write            // 2
    Execute          // 4
    // ReadWrite = Read | Write
)

// iota with type
type Priority int

const (
    Low Priority = iota // 0
    Medium               // 1
    High                 // 2
    Critical             // 3
)

// iota reset in new const block
const (
    First = iota // 0
    Second       // 1
)

const (
    Again = iota // 0 (resets)
    Again2       // 1
)

// iota with custom calculation
const (
    Start = iota * 10 // 0
    // 10
    // 20
    // 30
)

func main() {
    // Basic iota
    fmt.Printf("Sunday: %d, Monday: %d, Tuesday: %d\n", Sunday, Monday, Tuesday)
    
    // iota with bit shifting
    fmt.Printf("KB: %d, MB: %d, GB: %d, TB: %d\n", KB, MB, GB, TB)
    
    // iota as bitmask
    permissions := Read | Write
    fmt.Printf("Permissions: %d\n", permissions)
    fmt.Printf("Has Read: %t, Has Write: %t, Has Execute: %t\n", 
        permissions&Read != 0, permissions&Write != 0, permissions&Execute != 0)
    
    // iota with custom type
    var p Priority = High
    fmt.Printf("Priority: %d\n", p)
    
    // iota reset
    fmt.Printf("First: %d, Second: %d\n", First, Second)
    fmt.Printf("Again: %d, Again2: %d\n", Again, Again2)
    
    // iota with custom calculation
    // These values would be: 0, 10, 20, 30
    // Uncomment to see usage
}
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"
    "time"
)

func main() {
    // Create a channel to receive signals
    sigChan := make(chan os.Signal, 1)
    
    // Notify the channel for specific signals
    signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)
    
    // Create a channel to signal when to stop
    done := make(chan bool)
    
    // Start a goroutine that does work
    go func() {
        fmt.Println("Application started. Press Ctrl+C to stop...")
        for {
            select {
            case <-done:
                fmt.Println("Goroutine stopping...")
                return
            default:
                fmt.Println("Working...")
                time.Sleep(2 * time.Second)
            }
        }
    }()
    
    // Block until a signal is received
    sig := <-sigChan
    fmt.Printf("Received signal: %s\n", sig)
    
    // Graceful shutdown
    fmt.Println("Starting graceful shutdown...")
    
    // Signal the goroutine to stop
    close(done)
    
    // Wait for cleanup
    time.Sleep(1 * time.Second)
    fmt.Println("Application stopped gracefully")
}

// Example with multiple signal handling
func signalHandlerExample() {
    // Create a channel for signals
    sigs := make(chan os.Signal, 1)
    
    // Notify for multiple signals
    signal.Notify(sigs, 
        syscall.SIGINT,   // Ctrl+C
        syscall.SIGTERM,  // Termination
        syscall.SIGHUP,   // Hangup
        syscall.SIGUSR1,  // User signal 1
        syscall.SIGUSR2,  // User signal 2
    )
    
    // Handle signals in a goroutine
    go func() {
        for sig := range sigs {
            switch sig {
            case syscall.SIGINT:
                fmt.Println("Received SIGINT - Interrupt")
            case syscall.SIGTERM:
                fmt.Println("Received SIGTERM - Terminate")
            case syscall.SIGHUP:
                fmt.Println("Received SIGHUP - Hangup")
            case syscall.SIGUSR1:
                fmt.Println("Received SIGUSR1 - User signal 1")
            case syscall.SIGUSR2:
                fmt.Println("Received SIGUSR2 - User signal 2")
            }
        }
    }()
}

// Graceful shutdown with context
func gracefulShutdownExample() {
    sigs := make(chan os.Signal, 1)
    signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM)
    
    // Simulate server
    server := struct {
        running bool
    }{running: true}
    
    go func() {
        for server.running {
            fmt.Println("Server is running...")
            time.Sleep(1 * time.Second)
        }
    }()
    
    // Wait for signal
    <-sigs
    fmt.Println("Shutting down server...")
    
    // Cleanup
    server.running = false
    fmt.Println("Server stopped")
}
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"
    "net/http"
    "time"
)

func main() {
    // Basic HTTP client
    client := &http.Client{
        Timeout: 10 * time.Second,
    }
    
    // GET request
    resp, err := client.Get("https://jsonplaceholder.typicode.com/posts/1")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    // Read response body
    body, err := io.ReadAll(resp.Body)
    if err != nil {
        fmt.Printf("Error reading body: %v\n", err)
        return
    }
    
    fmt.Printf("Status: %d\n", resp.StatusCode)
    fmt.Printf("Body: %s\n", string(body))
    
    // POST request with JSON
    type Post struct {
        Title  string `json:"title"`
        Body   string `json:"body"`
        UserID int    `json:"userId"`
    }
    
    postData := Post{
        Title:  "Hello World",
        Body:   "This is a test post",
        UserID: 1,
    }
    
    jsonData, err := json.Marshal(postData)
    if err != nil {
        fmt.Printf("Error marshaling JSON: %v\n", err)
        return
    }
    
    // POST request
    resp, err = client.Post(
        "https://jsonplaceholder.typicode.com/posts",
        "application/json",
        bytes.NewBuffer(jsonData),
    )
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    body, _ = io.ReadAll(resp.Body)
    fmt.Printf("POST Status: %d\n", resp.StatusCode)
    fmt.Printf("POST Response: %s\n", string(body))
    
    // Custom request with headers
    req, err := http.NewRequest("GET", "https://api.example.com/data", nil)
    if err != nil {
        fmt.Printf("Error creating request: %v\n", err)
        return
    }
    
    // Set headers
    req.Header.Set("Authorization", "Bearer your-token")
    req.Header.Set("Accept", "application/json")
    req.Header.Set("User-Agent", "MyApp/1.0")
    
    // Execute request
    resp, err = client.Do(req)
    if err != nil {
        fmt.Printf("Error executing request: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    fmt.Printf("Custom request status: %d\n", resp.StatusCode)
    
    // Client with custom transport
    transport := &http.Transport{
        MaxIdleConns:    10,
        IdleConnTimeout: 30 * time.Second,
        TLSClientConfig: nil,
    }
    
    customClient := &http.Client{
        Transport: transport,
        Timeout:   5 * time.Second,
    }
    
    // Use custom client
    resp, err = customClient.Get("https://example.com")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    fmt.Printf("Custom client status: %d\n", resp.StatusCode)
}
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 (
    "context"
    "fmt"
    "io"
    "net/http"
    "time"
)

func main() {
    // Method 1: Client-level timeout
    client := &http.Client{
        Timeout: 5 * time.Second, // Total timeout for the request
    }
    
    // Simple GET with client timeout
    resp, err := client.Get("https://httpbin.org/delay/10") // This will timeout
    if err != nil {
        fmt.Printf("Client timeout error: %v\n", err)
    } else {
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        fmt.Printf("Response: %s\n", string(body))
    }
    
    // Method 2: Context timeout
    ctx, cancel := context.WithTimeout(context.Background(), 3*time.Second)
    defer cancel()
    
    req, err := http.NewRequestWithContext(ctx, "GET", "https://httpbin.org/delay/5", nil)
    if err != nil {
        fmt.Printf("Error creating request: %v\n", err)
        return
    }
    
    client2 := &http.Client{}
    resp, err = client2.Do(req)
    if err != nil {
        fmt.Printf("Context timeout error: %v\n", err)
    } else {
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        fmt.Printf("Response: %s\n", string(body))
    }
    
    // Method 3: Transport-level timeout
    transport := &http.Transport{
        ResponseHeaderTimeout: 2 * time.Second, // Timeout for response headers
        TLSHandshakeTimeout:   3 * time.Second,
        IdleConnTimeout:       30 * time.Second,
    }
    
    client3 := &http.Client{
        Transport: transport,
        Timeout:   10 * time.Second, // Overall timeout
    }
    
    resp, err = client3.Get("https://httpbin.org/delay/8")
    if err != nil {
        fmt.Printf("Transport timeout error: %v\n", err)
    } else {
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        fmt.Printf("Response: %s\n", string(body))
    }
    
    // Method 4: Combined timeouts with deadline
    deadlineCtx, deadlineCancel := context.WithDeadline(
        context.Background(),
        time.Now().Add(4*time.Second),
    )
    defer deadlineCancel()
    
    req2, err := http.NewRequestWithContext(deadlineCtx, "GET", "https://httpbin.org/delay/3", nil)
    if err != nil {
        fmt.Printf("Error creating request: %v\n", err)
        return
    }
    
    client4 := &http.Client{}
    resp, err = client4.Do(req2)
    if err != nil {
        fmt.Printf("Deadline timeout error: %v\n", err)
    } else {
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        fmt.Printf("Response: %s\n", string(body))
    }
    
    // Method 5: Custom timeout with cancel
    customCtx, customCancel := context.WithTimeout(context.Background(), 2*time.Second)
    defer customCancel()
    
    // Start the request
    req3, err := http.NewRequestWithContext(customCtx, "GET", "https://httpbin.org/delay/10", nil)
    if err != nil {
        fmt.Printf("Error creating request: %v\n", err)
        return
    }
    
    client5 := &http.Client{}
    resp, err = client5.Do(req3)
    if err != nil {
        fmt.Printf("Request cancelled: %v\n", err)
    } else {
        defer resp.Body.Close()
        body, _ := io.ReadAll(resp.Body)
        fmt.Printf("Response: %s\n", string(body))
    }
    
    // Example: Checking if error is timeout
    req4, _ := http.NewRequest("GET", "https://httpbin.org/delay/10", nil)
    ctx2, cancel2 := context.WithTimeout(context.Background(), 1*time.Second)
    defer cancel2()
    req4 = req4.WithContext(ctx2)
    
    resp, err = client.Do(req4)
    if err != nil {
        if ctx2.Err() == context.DeadlineExceeded {
            fmt.Println("Request timed out due to context deadline")
        } else {
            fmt.Printf("Other error: %v\n", err)
        }
    } else {
        defer resp.Body.Close()
        fmt.Println("Request completed successfully")
    }
}
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 (
    "crypto/tls"
    "fmt"
    "net"
    "net/http"
    "net/url"
    "time"
)

func main() {
    // Custom Transport with connection pooling
    transport := &http.Transport{
        // Connection pooling
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
        
        // Connection settings
        DialContext: (&net.Dialer{
            Timeout:   30 * time.Second,
            KeepAlive: 30 * time.Second,
        }).DialContext,
        
        // TLS settings
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: false,
            MinVersion:         tls.VersionTLS12,
        },
        
        // Response header timeout
        ResponseHeaderTimeout: 5 * time.Second,
        
        // Expect continue timeout
        ExpectContinueTimeout: 1 * time.Second,
    }
    
    // Custom Client
    client := &http.Client{
        Transport: transport,
        Timeout:   30 * time.Second,
        
        // Custom redirect policy
        CheckRedirect: func(req *http.Request, via []*http.Request) error {
            if len(via) >= 10 {
                return fmt.Errorf("too many redirects")
            }
            return nil
        },
    }
    
    // Add default headers using RoundTrip wrapper
    type customTransport struct {
        transport http.RoundTripper
        headers   map[string]string
    }
    
    func (c customTransport) RoundTrip(req *http.Request) (*http.Response, error) {
        // Add default headers
        for key, value := range c.headers {
            req.Header.Set(key, value)
        }
        return c.transport.RoundTrip(req)
    }
    
    // Create client with default headers
    headerTransport := customTransport{
        transport: transport,
        headers: map[string]string{
            "User-Agent":    "MyCustomApp/1.0",
            "Accept":        "application/json",
            "Accept-Language": "en-US,en;q=0.9",
        },
    }
    
    clientWithHeaders := &http.Client{
        Transport: headerTransport,
        Timeout:   30 * time.Second,
    }
    
    // Use custom client
    resp, err := clientWithHeaders.Get("https://httpbin.org/headers")
    if err != nil {
        fmt.Printf("Error: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    fmt.Printf("Status: %d\n", resp.StatusCode)
    
    // Custom client with proxy
    proxyURL, _ := url.Parse("http://proxy.example.com:8080")
    proxyTransport := &http.Transport{
        Proxy: http.ProxyURL(proxyURL),
        MaxIdleConns: 100,
        TLSClientConfig: &tls.Config{
            InsecureSkipVerify: true,
        },
    }
    
    proxyClient := &http.Client{
        Transport: proxyTransport,
        Timeout:   30 * time.Second,
    }
    
    // Use proxy client
    resp, err = proxyClient.Get("https://api.example.com/data")
    if err != nil {
        fmt.Printf("Proxy error: %v\n", err)
        return
    }
    defer resp.Body.Close()
    
    fmt.Printf("Proxy client status: %d\n", resp.StatusCode)
    
    // Client with retry capability (custom wrapper)
    retryClient := &http.Client{
        Transport: transport,
        Timeout:   30 * time.Second,
    }
    
    // Custom function to retry requests
    doRequest := func(req *http.Request) (*http.Response, error) {
        var resp *http.Response
        var err error
        
        for i := 0; i < 3; i++ {
            resp, err = retryClient.Do(req)
            if err == nil && resp.StatusCode < 500 {
                return resp, nil
            }
            if resp != nil {
                resp.Body.Close()
            }
            time.Sleep(time.Duration(i+1) * time.Second)
        }
        return resp, err
    }
    
    req, _ := http.NewRequest("GET", "https://httpbin.org/status/500", nil)
    resp, err = doRequest(req)
    if err != nil {
        fmt.Printf("Request failed after retries: %v\n", err)
    } else {
        defer resp.Body.Close()
        fmt.Printf("Retry client status: %d\n", resp.StatusCode)
    }
}
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 (
    "fmt"
    "testing"
)

// Function to benchmark
func Sum(numbers []int) int {
    sum := 0
    for _, n := range numbers {
        sum += n
    }
    return sum
}

// Function to benchmark with allocation
func SumWithAllocation(numbers []int) int {
    result := make([]int, len(numbers))
    for i, n := range numbers {
        result[i] = n
    }
    sum := 0
    for _, n := range result {
        sum += n
    }
    return sum
}

// Benchmark functions (these would be in a _test.go file)
// BenchmarkSum measures Sum function performance
func BenchmarkSum(b *testing.B) {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    // Reset timer to exclude setup time
    b.ResetTimer()
    
    // Run the benchmark b.N times
    for i := 0; i < b.N; i++ {
        Sum(numbers)
    }
}

// BenchmarkSumWithAllocation measures allocation performance
func BenchmarkSumWithAllocation(b *testing.B) {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    b.ResetTimer()
    for i := 0; i < b.N; i++ {
        SumWithAllocation(numbers)
    }
}

// Benchmark with parallel execution
func BenchmarkSumParallel(b *testing.B) {
    numbers := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
    
    b.RunParallel(func(pb *testing.PB) {
        for pb.Next() {
            Sum(numbers)
        }
    })
}

// Benchmark with custom timer control
func BenchmarkCustomTimer(b *testing.B) {
    // Setup
    data := make([]int, 1000)
    for i := range data {
        data[i] = i
    }
    
    // Stop timer for setup
    b.StopTimer()
    
    // Expensive setup
    processed := make([]int, len(data))
    copy(processed, data)
    
    // Start timer for actual test
    b.StartTimer()
    
    for i := 0; i < b.N; i++ {
        Sum(processed)
    }
}

// Example of running benchmarks programmatically
func main() {
    // This shows how benchmarks would be run
    fmt.Println("To run benchmarks, use:")
    fmt.Println("go test -bench=.")
    fmt.Println("go test -bench=. -benchmem")
    fmt.Println("go test -bench=Sum -benchmem -count=5")
    fmt.Println("go test -bench=. -benchtime=10s")
    fmt.Println("go test -bench=. -benchtime=100x")
}

// Example test file would contain:
// package main
// 
// import "testing"
// 
// func TestSum(t *testing.T) {
//     numbers := []int{1, 2, 3}
//     result := Sum(numbers)
//     expected := 6
//     if result != expected {
//         t.Errorf("Sum(%v) = %d; want %d", numbers, result, expected)
//     }
// }
//
// func BenchmarkSum(b *testing.B) {
//     numbers := []int{1, 2, 3, 4, 5}
//     for i := 0; i < b.N; i++ {
//         Sum(numbers)
//     }
// }
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"
)

// Semaphore using buffered channel
type Semaphore struct {
    ch chan struct{}
}

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

// Acquire attempts to acquire the semaphore
func (s *Semaphore) Acquire() {
    s.ch <- struct{}{}
}

// AcquireWithTimeout attempts to acquire with timeout
func (s *Semaphore) AcquireWithTimeout(timeout time.Duration) bool {
    select {
    case s.ch <- struct{}{}:
        return true
    case <-time.After(timeout):
        return false
    }
}

// Release releases the semaphore
func (s *Semaphore) Release() {
    <-s.ch
}

// TryAcquire attempts to acquire without blocking
func (s *Semaphore) TryAcquire() bool {
    select {
    case s.ch <- struct{}{}:
        return true
    default:
        return false
    }
}

// Worker function using semaphore
func worker(id int, sem *Semaphore, wg *sync.WaitGroup) {
    defer wg.Done()
    
    // Acquire semaphore
    if !sem.AcquireWithTimeout(2 * time.Second) {
        fmt.Printf("Worker %d: timeout acquiring semaphore\n", id)
        return
    }
    defer sem.Release()
    
    // Do work
    fmt.Printf("Worker %d: acquired semaphore, working...\n", id)
    time.Sleep(1 * time.Second)
    fmt.Printf("Worker %d: done\n", id)
}

// Example with resource pool
type ResourcePool struct {
    sem *Semaphore
    resources []int
    mu sync.Mutex
}

func NewResourcePool(size int) *ResourcePool {
    resources := make([]int, size)
    for i := 0; i < size; i++ {
        resources[i] = i + 1
    }
    return &ResourcePool{
        sem: NewSemaphore(size),
        resources: resources,
    }
}

func (rp *ResourcePool) GetResource() (int, bool) {
    if !rp.sem.TryAcquire() {
        return 0, false
    }
    
    rp.mu.Lock()
    defer rp.mu.Unlock()
    
    if len(rp.resources) == 0 {
        rp.sem.Release()
        return 0, false
    }
    
    resource := rp.resources[0]
    rp.resources = rp.resources[1:]
    return resource, true
}

func (rp *ResourcePool) ReleaseResource(resource int) {
    rp.mu.Lock()
    rp.resources = append(rp.resources, resource)
    rp.mu.Unlock()
    rp.sem.Release()
}

func main() {
    // Example 1: Basic semaphore
    sem := NewSemaphore(3) // Allow 3 concurrent operations
    var wg sync.WaitGroup
    
    fmt.Println("=== Basic Semaphore ===")
    for i := 1; i <= 10; i++ {
        wg.Add(1)
        go worker(i, sem, &wg)
    }
    wg.Wait()
    
    // Example 2: Resource pool
    fmt.Println("\n=== Resource Pool ===")
    pool := NewResourcePool(3)
    
    // Simulate using resources
    for i := 0; i < 5; i++ {
        go func(id int) {
            resource, ok := pool.GetResource()
            if !ok {
                fmt.Printf("Goroutine %d: no resource available\n", id)
                return
            }
            fmt.Printf("Goroutine %d: acquired resource %d\n", id, resource)
            time.Sleep(500 * time.Millisecond)
            pool.ReleaseResource(resource)
            fmt.Printf("Goroutine %d: released resource %d\n", id, resource)
        }(i)
    }
    
    time.Sleep(3 * time.Second)
    
    // Example 3: TryAcquire
    fmt.Println("\n=== TryAcquire Example ===")
    sem2 := NewSemaphore(1)
    
    // Acquire first
    if sem2.TryAcquire() {
        fmt.Println("First acquisition succeeded")
    }
    
    // Try to acquire again (should fail)
    if sem2.TryAcquire() {
        fmt.Println("Second acquisition succeeded (unexpected)")
    } else {
        fmt.Println("Second acquisition failed (as expected)")
    }
    
    // Release and try again
    sem2.Release()
    if sem2.TryAcquire() {
        fmt.Println("Third acquisition succeeded after release")
    }
}
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"
    "log"
)

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

func MigrateUp(db *sql.DB, migrations []Migration) error {
    // Create migrations table
    _, err := db.Exec(`CREATE TABLE IF NOT EXISTS migrations (
        version INTEGER PRIMARY KEY,
        name TEXT NOT NULL,
        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)
    
    // Apply migrations
    for _, migration := range migrations {
        if migration.Version > currentVersion {
            log.Printf("Applying migration %d: %s", 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, name) VALUES (?, ?)", 
                migration.Version, migration.Name)
            if err != nil {
                return err
            }
            
            log.Printf("Migration %d complete", migration.Version)
        }
    }
    
    return nil
}

func main() {
    // db, err := sql.Open("sqlite3", "app.db")
    // if err != nil {
    //     log.Fatalf("Failed to connect: %v", err)
    // }
    // defer db.Close()
    
    // migrations := []Migration{
    //     {Version: 1, Name: "create_users", Up: "CREATE TABLE users...", Down: "DROP TABLE users"},
    // }
    
    // if err := MigrateUp(db, migrations); err != nil {
    //     log.Fatalf("Migration failed: %v", err)
    // }
    
    log.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"
    "context"
)

// 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")
}