191 Patterns from Go Programming (2nd ed., 2024)

191 Patterns from Go Programming (2nd ed., 2024)

This chapter extracts topics from the 2024 second edition arc: debugging, time handling, CLI work, files/systems, SQL, HTTP server/client, concurrency, testing, tooling, and cloud.

What This Adds to Our Book

  • Stronger bridge from core syntax to practical operations.
  • Dedicated narrative on “time as a correctness boundary”.
  • Better pairing of HTTP server and HTTP client behavior in one lifecycle.
  • Cloud-readiness framing: configuration, graceful shutdown, observability, packaging.

Learning Architecture

core language -> application boundary (CLI/API) -> state boundary (DB/files) -> operational boundary (timeouts, cloud, tools)

Deep Integration Example: API + CLI + DB Boundary

package app

import (
    "context"
    "database/sql"
    "encoding/json"
    "errors"
    "fmt"
    "net/http"
    "os"
    "time"
)

var ErrNotFound = errors.New("not found")

type User struct {
    ID    int64  `json:"id"`
    Email string `json:"email"`
}

type Store struct{ DB *sql.DB }

func (s Store) GetUser(ctx context.Context, id int64) (User, error) {
    ctx, cancel := context.WithTimeout(ctx, 2*time.Second)
    defer cancel()

    var u User
    err := s.DB.QueryRowContext(ctx, `SELECT id, email FROM users WHERE id=$1`, id).Scan(&u.ID, &u.Email)
    if errors.Is(err, sql.ErrNoRows) {
        return User{}, ErrNotFound
    }
    if err != nil {
        return User{}, fmt.Errorf("query user %d: %w", id, err)
    }
    return u, nil
}

type API struct{ Store Store }

func (a API) GetUserHandler(w http.ResponseWriter, r *http.Request) {
    ctx, cancel := context.WithTimeout(r.Context(), 3*time.Second)
    defer cancel()

    user, err := a.Store.GetUser(ctx, 42)
    if errors.Is(err, ErrNotFound) {
        http.Error(w, "not found", http.StatusNotFound)
        return
    }
    if err != nil {
        http.Error(w, "internal error", http.StatusInternalServerError)
        return
    }
    w.Header().Set("Content-Type", "application/json")
    _ = json.NewEncoder(w).Encode(user)
}

func ReadEnvDuration(key string, fallback time.Duration) time.Duration {
    v := os.Getenv(key)
    if v == "" {
        return fallback
    }
    d, err := time.ParseDuration(v)
    if err != nil {
        return fallback
    }
    return d
}

Why This Matters

The example demonstrates a full chain from request boundary to storage boundary with explicit timeouts and typed errors. That is the practical center of modern Go backend development.