193 Patterns from Go in Practice (2nd ed., 2025)

Updated

July 30, 2026

193 Patterns from Go in Practice (2nd ed., 2025)

The 2025 edition is structured around four parts: fundamentals, robust application practices, end-to-end web systems, and cloud/advanced topics (reflection, code generation, interop).

What This Adds to Our Book

  • Strong mid-level bridge: from language fluency to production engineering.
  • Better integration of testing, debugging, and benchmarking as one quality discipline.
  • Explicit chapter path toward microservices and external service integration.

End-to-End Service Model

HTTP edge -> business layer -> storage/external APIs -> telemetry -> deployment/runtime checks

Deep Integration Example: External Service Client with Typed Error Strategy

package external

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

var ErrUpstreamUnavailable = errors.New("upstream unavailable")

type Client struct {
    HTTP *http.Client
    Base string
}

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

func NewClient(base string) Client {
    return Client{
        Base: base,
        HTTP: &http.Client{Timeout: 2 * time.Second},
    }
}

func (c Client) GetProfile(ctx context.Context, id string) (Profile, error) {
    ctx, cancel := context.WithTimeout(ctx, 1200*time.Millisecond)
    defer cancel()

    req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.Base+"/profiles/"+id, nil)
    if err != nil {
        return Profile{}, err
    }

    resp, err := c.HTTP.Do(req)
    if err != nil {
        return Profile{}, fmt.Errorf("http call failed: %w", ErrUpstreamUnavailable)
    }
    defer resp.Body.Close()

    if resp.StatusCode >= 500 {
        return Profile{}, fmt.Errorf("status %d: %w", resp.StatusCode, ErrUpstreamUnavailable)
    }
    if resp.StatusCode != http.StatusOK {
        return Profile{}, fmt.Errorf("unexpected status: %d", resp.StatusCode)
    }

    var p Profile
    if err := json.NewDecoder(resp.Body).Decode(&p); err != nil {
        return Profile{}, err
    }
    return p, nil
}

Why This Matters

Production services fail most often at integration boundaries. This pattern teaches typed failure handling and timeout discipline in a way that scales across microservices.

More examples

Middleware chain composition

mkdir -p /tmp/go-gip-mw && cd /tmp/go-gip-mw
go mod init example.com/gip-mw

Save as main.go:

package main

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

type Middleware func(http.Handler) http.Handler

func chain(h http.Handler, mws ...Middleware) http.Handler {
    for i := len(mws) - 1; i >= 0; i-- {
        h = mws[i](h)
    }
    return h
}

func main() {
    var order []string
    mw := func(name string) Middleware {
        return func(next http.Handler) http.Handler {
            return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
                order = append(order, name)
                next.ServeHTTP(w, r)
            })
        }
    }
    h := chain(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        order = append(order, "handler")
        fmt.Fprintln(w, "ok")
    }), mw("a"), mw("b"))

    rr := httptest.NewRecorder()
    h.ServeHTTP(rr, httptest.NewRequest(http.MethodGet, "/", nil))
    fmt.Println(order)
}
go run .

Expected output:

[a b handler]

Content-type gate for JSON POST

mkdir -p /tmp/go-gip-ct && cd /tmp/go-gip-ct
go mod init example.com/gip-ct

Save as main.go:

package main

import (
    "fmt"
    "net/http"
    "net/http/httptest"
    "strings"
)

func requireJSON(next http.Handler) http.Handler {
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        ct := r.Header.Get("Content-Type")
        if !strings.HasPrefix(ct, "application/json") {
            http.Error(w, "want json", http.StatusUnsupportedMediaType)
            return
        }
        next.ServeHTTP(w, r)
    })
}

func main() {
    h := requireJSON(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        fmt.Fprintln(w, "json ok")
    }))
    rr := httptest.NewRecorder()
    req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{}`))
    h.ServeHTTP(rr, req)
    fmt.Println("no ct:", rr.Code)

    rr = httptest.NewRecorder()
    req = httptest.NewRequest(http.MethodPost, "/", strings.NewReader(`{}`))
    req.Header.Set("Content-Type", "application/json")
    h.ServeHTTP(rr, req)
    fmt.Println("json:", rr.Code, strings.TrimSpace(rr.Body.String()))
}
go run .

Expected output:

no ct: 415
json: 200 json ok

Runnable example

External client practice: typed upstream errors, client timeout, and status classification with httptest.

mkdir -p /tmp/go-gip && cd /tmp/go-gip
go mod init example.com/gip

Save as main.go:

package main

import (
    "context"
    "encoding/json"
    "errors"
    "fmt"
    "net/http"
    "net/http/httptest"
    "time"
)

var ErrUpstreamUnavailable = errors.New("upstream unavailable")

type Profile struct {
    Name string `json:"name"`
}

func fetchProfile(ctx context.Context, client *http.Client, base string) (Profile, error) {
    req, err := http.NewRequestWithContext(ctx, http.MethodGet, base+"/profile", nil)
    if err != nil {
        return Profile{}, err
    }
    resp, err := client.Do(req)
    if err != nil {
        return Profile{}, fmt.Errorf("http: %w", ErrUpstreamUnavailable)
    }
    defer resp.Body.Close()
    if resp.StatusCode >= 500 {
        return Profile{}, fmt.Errorf("status %d: %w", resp.StatusCode, ErrUpstreamUnavailable)
    }
    if resp.StatusCode != http.StatusOK {
        return Profile{}, fmt.Errorf("unexpected status %d", resp.StatusCode)
    }
    var p Profile
    if err := json.NewDecoder(resp.Body).Decode(&p); err != nil {
        return Profile{}, err
    }
    return p, nil
}

func main() {
    var hits int
    ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        hits++
        if hits == 1 {
            w.WriteHeader(http.StatusBadGateway)
            return
        }
        _ = json.NewEncoder(w).Encode(Profile{Name: "ada"})
    }))
    defer ts.Close()

    client := &http.Client{Timeout: 300 * time.Millisecond}
    ctx := context.Background()
    _, err := fetchProfile(ctx, client, ts.URL)
    fmt.Println("first:", err, "unavailable?", errors.Is(err, ErrUpstreamUnavailable))
    p, err := fetchProfile(ctx, client, ts.URL)
    fmt.Println("second:", p, err)
}
go run .

Expected output:

first: status 502: upstream unavailable unavailable? true
second: {ada} <nil>

What to notice

  • Wrap with %w and a sentinel so callers decide retry vs fail without parsing strings.
  • Always set http.Client.Timeout (or context deadline) on outbound deps.
  • Close response bodies to preserve connection reuse.

Try next

  • Retry only when errors.Is(err, ErrUpstreamUnavailable) with jitter.
  • Map 404 to a distinct ErrNotFound sentinel.