Go/
Lesson

Most Go applications consume external APIs. When you ask AI to write an HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. client, it generates code that works against a happy-path test server but fails in production: no timeouts, no retry logic, unchecked status codes, leaked connections. These are the bugs you'll catch by reading the code AI gives you.

What AI generates vs what you need

AI gives you this:

resp, err := http.Get("https://api.example.com/users")
if err != nil {
    log.Fatal(err)
}
body, _ := io.ReadAll(resp.Body)
fmt.Println(string(body))

Four problems:

  1. No timeout, hangs forever if server doesn't respond
  2. No defer resp.Body.Close(), leaks connections
  3. Ignores io.ReadAll error
  4. Doesn't check resp.StatusCode

What production code looks like:

client := &http.Client{Timeout: 10 * time.Second}

resp, err := client.Get("https://api.example.com/users")
if err != nil {
    return fmt.Errorf("request failed: %w", err)
}
defer resp.Body.Close()

if resp.StatusCode != http.StatusOK {
    body, _ := io.ReadAll(resp.Body)
    return fmt.Errorf("API error %d: %s", resp.StatusCode, body)
}

var users []User
if err := json.NewDecoder(resp.Body).Decode(&users); err != nil {
    return fmt.Errorf("decode failed: %w", err)
}
AI pitfall
AI uses the package-level http.Get() which uses http.DefaultClient, a global client with no timeout. A DNS lookup that hangs, a server that accepts connections but never responds, or a response that streams forever will block your goroutine indefinitely. Always create your own http.Client with a timeout.
02

Production client configuration

SettingDefaultWhat to setWhy
TimeoutNone10-30sTotal request deadline
MaxIdleConns100100+Connection pool size
MaxIdleConnsPerHost210+Prevents connection thrashing to one host
IdleConnTimeout90s90sClean up stale connections
client := &http.Client{
    Timeout: 30 * time.Second,
    Transport: &http.Transport{
        MaxIdleConns:        100,
        MaxIdleConnsPerHost: 10,
        IdleConnTimeout:     90 * time.Second,
    },
}
AI pitfall
AI creates a new http.Client inside every function call. This prevents connection reuse, each request opens a new TCP connection and TLS handshake. Create the client once (usually at struct initialization) and reuse it across requests.
03

Full-control requests with NewRequest

For POST requests, custom headers, or authenticationWhat is authentication?Verifying who a user is, typically through credentials like a password or token.:

data := map[string]string{"name": "Alice", "email": "alice@example.com"}
jsonData, _ := json.Marshal(data)

req, err := http.NewRequestWithContext(
    ctx,
    "POST",
    "https://api.example.com/users",
    bytes.NewReader(jsonData),
)
if err != nil {
    return err
}

req.Header.Set("Content-Type", "application/json")
req.Header.Set("Authorization", "Bearer "+token)

resp, err := client.Do(req)
if err != nil {
    return err
}
defer resp.Body.Close()

Request body types

FormatContent-TypeHow to build body
JSONapplication/jsonjson.Marshal() + bytes.NewReader()
Formapplication/x-www-form-urlencodedurl.Values{}.Encode() + strings.NewReader()
Multipartmultipart/form-datamultipart.NewWriter()
04

Context for timeouts and cancellation

context is how Go manages request lifecycle. When you ask AI to make HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. requests from within an HTTP handler, context propagation is critical.

func getUserFromAPI(ctx context.Context, userID string) (*User, error) {
    // This request will be cancelled if the parent HTTP request is cancelled
    req, err := http.NewRequestWithContext(ctx, "GET",
        "https://api.example.com/users/"+userID, nil)
    if err != nil {
        return nil, err
    }

    resp, err := client.Do(req)
    if err != nil {
        if ctx.Err() == context.DeadlineExceeded {
            return nil, fmt.Errorf("request timed out")
        }
        return nil, err
    }
    defer resp.Body.Close()

    var user User
    return &user, json.NewDecoder(resp.Body).Decode(&user)
}
AI pitfall
AI uses http.NewRequest instead of http.NewRequestWithContext. Without a context, you can't cancel in-flight requests when the parent handler times out. In an HTTP handler, always pass r.Context() to outgoing requests so they're cancelled if the client disconnects.
05

Building a reusable APIWhat is api?A set of rules that lets one program talk to another, usually over the internet, by sending requests and getting responses. client

When you ask AI to build an API client, evaluate it against this pattern:

type APIClient struct {
    baseURL    string
    httpClient *http.Client
    apiKey     string
}

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

func (c *APIClient) do(ctx context.Context, method, path string, body, result any) error {
    var bodyReader io.Reader
    if body != nil {
        data, err := json.Marshal(body)
        if err != nil {
            return fmt.Errorf("marshal: %w", err)
        }
        bodyReader = bytes.NewReader(data)
    }

    req, err := http.NewRequestWithContext(ctx, method, c.baseURL+path, bodyReader)
    if err != nil {
        return fmt.Errorf("new request: %w", err)
    }

    req.Header.Set("Content-Type", "application/json")
    req.Header.Set("Authorization", "Bearer "+c.apiKey)

    resp, err := c.httpClient.Do(req)
    if err != nil {
        return fmt.Errorf("do: %w", err)
    }
    defer resp.Body.Close()

    if resp.StatusCode >= 400 {
        errBody, _ := io.ReadAll(io.LimitReader(resp.Body, 1<<16))
        return fmt.Errorf("API error %d: %s", resp.StatusCode, errBody)
    }

    if result != nil {
        return json.NewDecoder(resp.Body).Decode(result)
    }
    return nil
}

Checklist for AI-generated HTTPWhat is http?The protocol browsers and servers use to exchange web pages, API data, and other resources, defining how requests and responses are formatted. clients

CheckWhat goes wrong without it
Client has timeoutHangs forever on unresponsive servers
Client is reused, not created per-requestConnection pool wasted
Context passed to every requestCan't cancel on parent timeout
Response body always closedConnection leak, eventual exhaustion
Status code checked before decodingTries to JSON-decode HTML error pages
Error bodies read with size limitMalicious server sends infinite error response