Go

Lesson 04

Context cancellation

Accept context at API boundaries and pass it through I/O so cancellation, deadlines, and request scope are honored.

Good Code

reviews/client.go
func (c *Client) FetchReview(ctx context.Context, id string) (Review, error) {
    req, err := http.NewRequestWithContext(
        ctx,
        http.MethodGet,
        c.baseURL+"/reviews/"+id,
        nil,
    )
    if err != nil {
        return Review{}, err
    }

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

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

Bad Code

reviews/client.go
func (c *Client) FetchReview(id string) (Review, error) {
    resp, err := http.Get(c.baseURL + "/reviews/" + id)
    if err != nil {
        return Review{}, err
    }
    defer resp.Body.Close()

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

Review Notes

What to review

Good Code

The good version accepts a context and attaches it to the outbound HTTP request, so cancellation and deadlines can stop the whole operation.

Bad Code

The bad version creates a request with no caller-controlled lifetime. If the caller times out or disconnects, the work can keep running.

Takeaways

  • Context belongs on work that can block, wait, or leave the process.