LearnThatStack Ace your next interview

Golang Programming.
Cheat sheet.

Quick reference for Golang Programming - sectioned for fast scanning. Skim the part you're shaky on, walk in confident.

Backend Development 16-section reference ~12 min read

Summary

Go is a statically typed, compiled programming language designed for simplicity, efficiency, and excellent concurrency support. Created by Google, Go excels in backend development with features like goroutines, channels, fast compilation, and a rich standard library. This cheatsheet covers essential Go concepts including syntax, concurrency patterns, HTTP servers, database operations, testing, and best practices for building scalable backend applications.

Basic Syntax & Types

Variable Declaration

var x int = 10
var y = 20        // Type inference
z := 30           // Short declaration
const PI = 3.14   // Constants

Basic Types

// Numeric
int, int8, int16, int32, int64
uint, uint8, uint16, uint32, uint64
float32, float64
complex64, complex128

// Other
bool
string
byte (alias for uint8)
rune (alias for int32, represents Unicode)

Zero Values

var i int       // 0
var f float64   // 0.0
var b bool      // false
var s string    // ""
var p *int      // nil

Arrays & Slices

// Array (fixed size)
var arr [5]int
arr := [3]int{1, 2, 3}

// Slice (dynamic)
var slice []int
slice := make([]int, 5, 10) // len=5, cap=10
slice = append(slice, 6)

// Slice operations
slice[1:3]  // Sub-slice
len(slice)  // Length
cap(slice)  // Capacity

Maps

// Declaration
m := make(map[string]int)
m := map[string]int{"a": 1, "b": 2}

// Operations
m["key"] = 10
value := m["key"]
value, exists := m["key"]
delete(m, "key")

// Iteration
for k, v := range m {
    fmt.Println(k, v)
}

Control Structures

If/Else

if x > 0 {
    // code
} else if x < 0 {
    // code
} else {
    // code
}

// With initialization
if val := compute(); val > 0 {
    // use val
}

Switch

switch x {
case 1:
    // code
case 2, 3:
    // code
default:
    // code
}

// Type switch
switch v := i.(type) {
case string:
    // v is string
case int:
    // v is int
}

Loops

// For loop
for i := 0; i < 10; i++ {
    // code
}

// While-like
for x < 10 {
    // code
}

// Infinite
for {
    // code
}

// Range
for i, v := range slice {
    // i: index, v: value
}

Functions

Basic Function

func add(x, y int) int {
    return x + y
}

// Multiple returns
func swap(x, y string) (string, string) {
    return y, x
}

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

Variadic Functions

func sum(nums ...int) int {
    total := 0
    for _, n := range nums {
        total += n
    }
    return total
}

Function as Values

func compute(fn func(int, int) int) int {
    return fn(3, 4)
}

// Anonymous function
result := func(x, y int) int {
    return x + y
}(5, 3)

Defer

func example() {
    defer fmt.Println("world")
    fmt.Println("hello")
    // Output: hello world
}

// Multiple defers (LIFO)
defer fmt.Println("1")
defer fmt.Println("2")
// Output: 2 1

Structs & Methods

Struct Definition

type Person struct {
    Name string
    Age  int
}

// Initialization
p1 := Person{"Alice", 30}
p2 := Person{Name: "Bob", Age: 25}
p3 := &Person{Name: "Charlie"} // Pointer

Methods

// Value receiver
func (p Person) String() string {
    return fmt.Sprintf("%s (%d)", p.Name, p.Age)
}

// Pointer receiver (can modify)
func (p *Person) SetAge(age int) {
    p.Age = age
}

Embedding

type Employee struct {
    Person    // Embedded struct
    Position string
}

e := Employee{
    Person: Person{"Alice", 30},
    Position: "Developer",
}

Interfaces

Interface Definition

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

type Reader interface {
    Read([]byte) (int, error)
}

// Interface composition
type ReadWriter interface {
    Reader
    Writer
}

Implementation

type MyWriter struct{}

func (m MyWriter) Write(data []byte) (int, error) {
    // Implementation
    return len(data), nil
}

Empty Interface

var i interface{} = "hello"

// Type assertion
s, ok := i.(string)
if ok {
    fmt.Println(s)
}

Common Interfaces

// Stringer
type Stringer interface {
    String() string
}

// Error
type error interface {
    Error() string
}

Error Handling

Basic Error Handling

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

// Usage
result, err := divide(10, 0)
if err != nil {
    log.Fatal(err)
}

Custom Errors

type MyError struct {
    Code    int
    Message string
}

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

Error Wrapping

import "fmt"

err := fmt.Errorf("failed to process: %w", originalErr)

// Check wrapped error
if errors.Is(err, ErrNotFound) {
    // Handle specific error
}

Concurrency

Goroutines

go func() {
    fmt.Println("Running concurrently")
}()

// With parameters
go processData(data)

WaitGroup

var wg sync.WaitGroup

for i := 0; i < 5; i++ {
    wg.Add(1)
    go func(id int) {
        defer wg.Done()
        fmt.Printf("Worker %d\n", id)
    }(i)
}

wg.Wait()

Mutex

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
}

Channels

Basic Channels

// Unbuffered channel
ch := make(chan int)

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

// Send and receive
ch <- 42        // Send
value := <-ch   // Receive

// Close channel
close(ch)

Channel Direction

// Send-only
func send(ch chan<- int) {
    ch <- 42
}

// Receive-only
func receive(ch <-chan int) {
    value := <-ch
}

Select Statement

select {
case msg1 := <-ch1:
    fmt.Println("Received:", msg1)
case msg2 := <-ch2:
    fmt.Println("Received:", msg2)
case <-time.After(1 * time.Second):
    fmt.Println("Timeout")
default:
    fmt.Println("No messages")
}

Channel Patterns

// Fan-in
func fanIn(ch1, ch2 <-chan int) <-chan int {
    out := make(chan int)
    go func() {
        for {
            select {
            case v := <-ch1:
                out <- v
            case v := <-ch2:
                out <- v
            }
        }
    }()
    return out
}

// Worker pool
func workerPool(jobs <-chan int, results chan<- int) {
    for job := range jobs {
        results <- job * 2
    }
}

Testing

Basic Testing

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

// Table-driven tests
func TestAddTable(t *testing.T) {
    tests := []struct {
        a, b, want int
    }{
        {1, 2, 3},
        {0, 0, 0},
        {-1, 1, 0},
    }
    
    for _, tt := range tests {
        got := add(tt.a, tt.b)
        if got != tt.want {
            t.Errorf("add(%d, %d) = %d; want %d", 
                tt.a, tt.b, got, tt.want)
        }
    }
}

Benchmarking

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

Mocking

type Database interface {
    Get(key string) (string, error)
}

type MockDB struct {
    data map[string]string
}

func (m *MockDB) Get(key string) (string, error) {
    if val, ok := m.data[key]; ok {
        return val, nil
    }
    return "", errors.New("not found")
}

HTTP & Web

Basic HTTP Server

func handler(w http.ResponseWriter, r *http.Request) {
    fmt.Fprintf(w, "Hello, %s!", r.URL.Path[1:])
}

func main() {
    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

HTTP Client

resp, err := http.Get("https://api.example.com/data")
if err != nil {
    log.Fatal(err)
}
defer resp.Body.Close()

body, err := io.ReadAll(resp.Body)

JSON Handling

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

// Encoding
user := User{"Alice", "alice@example.com"}
jsonData, err := json.Marshal(user)

// Decoding
var user User
err := json.Unmarshal(jsonData, &user)

// From HTTP request
decoder := json.NewDecoder(r.Body)
err := decoder.Decode(&user)

Middleware

// Middleware type
type Middleware func(http.HandlerFunc) http.HandlerFunc

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

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

// Chain middlewares
func chainMiddleware(h http.HandlerFunc, middlewares ...Middleware) http.HandlerFunc {
    for i := len(middlewares) - 1; i >= 0; i-- {
        h = middlewares[i](h)
    }
    return h
}

// Usage
http.HandleFunc("/api/protected", 
    chainMiddleware(handler, loggingMiddleware, authMiddleware))

Database Operations

SQL Database

import "database/sql"
import _ "github.com/lib/pq" // PostgreSQL driver

db, err := sql.Open("postgres", "connection_string")
defer db.Close()

// Query
rows, err := db.Query("SELECT id, name FROM users WHERE age > $1", 18)
defer rows.Close()

for rows.Next() {
    var id int
    var name string
    err := rows.Scan(&id, &name)
}

// Single row
var name string
err := db.QueryRow("SELECT name FROM users WHERE id = $1", 1).Scan(&name)

// Execute
result, err := db.Exec("INSERT INTO users(name, age) VALUES($1, $2)", "Alice", 30)

Prepared Statements

stmt, err := db.Prepare("INSERT INTO users(name, age) VALUES($1, $2)")
defer stmt.Close()

result, err := stmt.Exec("Bob", 25)

Transactions

tx, err := db.Begin()
if err != nil {
    return err
}

_, err = tx.Exec("UPDATE accounts SET balance = balance - $1 WHERE id = $2", 100, 1)
if err != nil {
    tx.Rollback()
    return err
}

_, err = tx.Exec("UPDATE accounts SET balance = balance + $1 WHERE id = $2", 100, 2)
if err != nil {
    tx.Rollback()
    return err
}

return tx.Commit()

Context Package

Basic Context Usage

ctx := context.Background()

// With timeout
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

// With cancel
ctx, cancel := context.WithCancel(ctx)
defer cancel()

// With value
ctx = context.WithValue(ctx, "userID", 123)

Context in HTTP

func handler(w http.ResponseWriter, r *http.Request) {
    ctx := r.Context()
    
    select {
    case <-time.After(5 * time.Second):
        fmt.Fprintf(w, "Hello!")
    case <-ctx.Done():
        http.Error(w, "Request cancelled", http.StatusRequestTimeout)
    }
}

Context Propagation

func fetchData(ctx context.Context, url string) error {
    req, err := http.NewRequestWithContext(ctx, "GET", url, nil)
    if err != nil {
        return err
    }
    
    resp, err := http.DefaultClient.Do(req)
    // Handle response
    return nil
}

Common Patterns

Singleton

var (
    instance *Database
    once     sync.Once
)

func GetDatabase() *Database {
    once.Do(func() {
        instance = &Database{}
        instance.Connect()
    })
    return instance
}

Builder Pattern

type ServerConfig struct {
    host string
    port int
    timeout time.Duration
}

type ServerBuilder struct {
    config ServerConfig
}

func (b *ServerBuilder) Host(h string) *ServerBuilder {
    b.config.host = h
    return b
}

func (b *ServerBuilder) Port(p int) *ServerBuilder {
    b.config.port = p
    return b
}

func (b *ServerBuilder) Build() *Server {
    return &Server{config: b.config}
}

Options Pattern

type Option func(*Server)

func WithTimeout(t time.Duration) Option {
    return func(s *Server) {
        s.timeout = t
    }
}

func WithMaxConn(n int) Option {
    return func(s *Server) {
        s.maxConn = n
    }
}

func NewServer(opts ...Option) *Server {
    s := &Server{
        timeout: 30 * time.Second,
        maxConn: 100,
    }
    
    for _, opt := range opts {
        opt(s)
    }
    
    return s
}

Performance & Best Practices

String Building

// Inefficient
s := ""
for i := 0; i < 1000; i++ {
    s += "a"
}

// Efficient
var sb strings.Builder
for i := 0; i < 1000; i++ {
    sb.WriteString("a")
}
s := sb.String()

Slice Pre-allocation

// Inefficient
var slice []int
for i := 0; i < 1000; i++ {
    slice = append(slice, i)
}

// Efficient
slice := make([]int, 0, 1000)
for i := 0; i < 1000; i++ {
    slice = append(slice, i)
}

Interface Checks

// Compile-time interface check
var _ io.Writer = (*MyWriter)(nil)

Resource Management

// Always defer cleanup
file, err := os.Open("file.txt")
if err != nil {
    return err
}
defer file.Close()

// Use defer for mutex unlock
mu.Lock()
defer mu.Unlock()

Error Best Practices

// Define sentinel errors
var (
    ErrNotFound = errors.New("not found")
    ErrInvalid  = errors.New("invalid input")
)

// Wrap errors with context
if err != nil {
    return fmt.Errorf("failed to process user %d: %w", userID, err)
}

Concurrency Best Practices

// Don't communicate by sharing memory; share memory by communicating
// Use channels for coordination

// Limit goroutines
sem := make(chan struct{}, 10) // Max 10 concurrent

for _, item := range items {
    sem <- struct{}{}
    go func(item Item) {
        defer func() { <-sem }()
        process(item)
    }(item)
}

Key Concepts & Comparisons

Goroutines vs OS Threads

Aspect Goroutines OS Threads
Memory ~2KB initial stack ~2MB fixed stack
Creation Cost Very low High
Management Go runtime OS kernel
Scheduling Cooperative Preemptive
Context Switch Fast Slow

Concurrency Primitives

Tool Use Case Example
Channels Communication between goroutines ch := make(chan int)
Mutex Protecting shared state sync.Mutex
WaitGroup Waiting for goroutines to finish sync.WaitGroup
Context Cancellation and timeouts context.WithTimeout()

Method Receivers

Type Syntax Use When
Value Receiver func (t Type) method() No modification needed, small structs
Pointer Receiver func (t *Type) method() Need to modify, large structs, consistency
// Value receiver - cannot modify original
func (p Person) GetName() string {
    return p.Name
}

// Pointer receiver - can modify original
func (p *Person) SetName(name string) {
    p.Name = name
}

Interface Implementation

Concept Description Example
Implicit No explicit "implements" Any type with matching methods
Duck Typing "If it walks like a duck..." Automatic satisfaction
Empty Interface interface{} accepts any type func Print(v interface{})

Memory Allocation

Function Purpose Usage
make() Initialize reference types make([]int, 5), make(chan int)
new() Allocate memory for any type new(int) returns *int
Literal Direct initialization []int{1,2,3}, Person{Name: "John"}

Error Handling Philosophy

// Go's explicit error handling
result, err := operation()
if err != nil {
    return fmt.Errorf("operation failed: %w", err)
}

// Panic/Recover for exceptional cases only
func riskyOperation() {
    defer func() {
        if r := recover(); r != nil {
            log.Printf("Recovered from panic: %v", r)
        }
    }()
    // risky code here
}

Go Concurrency Patterns

Worker Pool Pattern

func workerPool(numWorkers int, jobs <-chan Job, results chan<- Result) {
    var wg sync.WaitGroup
    
    for i := 0; i < numWorkers; i++ {
        wg.Add(1)
        go func() {
            defer wg.Done()
            for job := range jobs {
                results <- processJob(job)
            }
        }()
    }
    
    go func() {
        wg.Wait()
        close(results)
    }()
}

Pipeline Pattern

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

Fan-Out/Fan-In Pattern

func fanOut(input <-chan Work, workers int) []<-chan Result {
    outputs := make([]<-chan Result, workers)
    for i := 0; i < workers; i++ {
        output := make(chan Result)
        outputs[i] = output
        go func() {
            defer close(output)
            for work := range input {
                output <- process(work)
            }
        }()
    }
    return outputs
}

func fanIn(inputs ...<-chan Result) <-chan Result {
    output := make(chan Result)
    var wg sync.WaitGroup
    
    wg.Add(len(inputs))
    for _, input := range inputs {
        go func(ch <-chan Result) {
            defer wg.Done()
            for result := range ch {
                output <- result
            }
        }(input)
    }
    
    go func() {
        wg.Wait()
        close(output)
    }()
    
    return output
}

Quick Reference & Best Practices

Essential Built-in Functions

Function Purpose Example
make() Create reference types make([]int, 5, 10)
new() Allocate zeroed memory new(int) returns *int
len() Get length len(slice), len(map)
cap() Get capacity cap(slice), cap(channel)
append() Add to slice append(slice, element)
copy() Copy slice elements copy(dest, src)
delete() Remove map entry delete(map, key)
close() Close channel close(ch)

Core Standard Library Packages

Package Purpose Key Functions
fmt Formatted I/O Printf, Sprintf, Scanf
net/http HTTP client/server ListenAndServe, Get, Post
encoding/json JSON handling Marshal, Unmarshal
database/sql Database interface Open, Query, Exec
context Request context WithTimeout, WithCancel
sync Synchronization Mutex, WaitGroup, Once
time Time operations Now, Sleep, Parse
io I/O primitives Copy, ReadAll, Writer

Performance Best Practices

✅ String Building: Use strings.Builder instead of string concatenation
✅ Slice Allocation: Pre-allocate slices with known capacity
✅ Interface Checks: Use compile-time interface verification
✅ Resource Management: Always use defer for cleanup
✅ Error Handling: Wrap errors with context using fmt.Errorf
✅ Concurrency: Use channels for communication, mutexes for shared state

Common Pitfalls to Avoid

Pitfall Problem Solution
Goroutine leaks Goroutines not properly terminated Use context for cancellation
Range loop variable Capturing loop variable in closure Create local copy
Slice capacity Frequent reallocations Pre-allocate with make([]T, 0, capacity)
Interface comparison nil interface vs nil pointer Check both value and type
Defer in loops Defers accumulate Use function wrapper

Production Deployment Checklist

  • Build Optimization: Use go build -ldflags="-s -w" for smaller binaries
  • Graceful Shutdown: Handle OS signals properly
  • Health Checks: Implement /health endpoints
  • Logging: Use structured logging (JSON format)
  • Metrics: Implement Prometheus metrics
  • Profiling: Enable pprof endpoints for debugging
  • Environment Config: Use environment variables for configuration
  • Container Security: Use minimal base images (scratch, alpine)

Memory Management Tips

// Prevent goroutine leaks
ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)
defer cancel()

go func() {
    select {
    case <-ctx.Done():
        return // Goroutine exits cleanly
    case result := <-heavyComputation():
        // Process result
    }
}()

// Efficient slice operations
// Clear slice while keeping capacity
slice = slice[:0]

// Release memory completely
slice = nil
Found this useful? Pass it on.
Pro · $10/mo

The sheet is free. Pro goes deeper.

Pro opens the full question library behind every sheet, every refresher and a monthly AI allowance. One subscription, all formats.

Full question library All refreshers Cancel anytime