Controle de Contexto em Go

Desenvolvedores Go discutem como usar `context` e goroutines de forma responsável, com foco em cancelamento, tempo de vida de recursos e em evitar concorrência oculta em bibliotecas. Muitos preferem APIs que pareçam síncronas, em que os chamadores controlam se o trabalho roda concorrente, embora reconheçam casos legítimos para goroutines internas e workers de longa duração. A conversa também critica `context` como uma mistura de cancelamento e dados escopados à requisição, compara isso com padrões em Rust e C#, e aborda concorrência estruturada e os limites do ideal de Go de “sem colorização async”.

Goroutines em Bibliotecas & Design de API

  • Tema forte: APIs de bibliotecas devem parecer síncronas; os chamadores decidem se querem concorrência ao criar goroutines por conta própria.
  • Goroutines internas são aceitáveis quando elas:
    • Fazem claramente parte da abstração (por exemplo, crawler, map paralelo, pub/sub com batching, flush de métricas).
    • São limitadas, bem documentadas e ajustáveis (limites, taxa, batching, caps de in-flight).
    • São devidamente limpas antes de a função pública retornar (estilo de concorrência estruturada, muitas vezes via errgroup ou WaitGroup).
  • Críticos de “nunca iniciar goroutines em bibliotecas” chamam isso de rígido demais; algumas cargas de trabalho são mais simples e mais seguras se a biblioteca controlar o agendamento.
  • Panics em goroutines de fundo são uma preocupação: os chamadores não podem envolver isso em recover a menos que controlem a fronteira da goroutine.

Contexto: Propósito, Padrão e Pontos de Dor

  • Dois papéis principais:
    • Cancelamento / deadlines / “você pode parar agora”.
    • Dados escopados à requisição (logger, tracing, auth, etc.).
  • Muitos não gostam do aspecto de “sacola de coisas”; é difícil saber por que uma função precisa de um context ou o que há dentro dele.
  • Outros defendem parâmetros ctx explícitos:
    • Tornam visíveis a interrompibilidade e os requisitos de logging.
    • Mais fáceis de revisar e raciocinar do que armazenamento implícito local de thread.
  • Orientação debatida:
    • Regra comum: não armazene contexts; passe-os pela pilha.
    • Nuance: ok em structs que são, na prática, parâmetros, e na stdlib por compatibilidade com versões anteriores (por exemplo, net/http), às vezes usando NewRequestWithContext.
    • context.WithoutCancel é visto como um compromisso útil quando se quer armazenar “valores, mas não deadlines”.
  • Pontos de atrito:
    • Não há como saber estaticamente se os callee respeitam o context.
    • Pressão para passar ctx por “quase toda função”, especialmente para logging.

Contexto vs Coloração de Função / Modelos Assíncronos

  • Alguns veem ctx como uma leve “coloration” (funções com vs sem context), minando parcialmente a história do Go de “sem keyword async”.
  • Outros argumentam que isso é muito menos restritivo do que async/await:
    • Você sempre pode usar context.Background() ou envolvê-lo com timeouts.
    • Não cria a separação rígida sync/async vista em outras linguagens.

Tratamento de Erros & Zero Values (Tópico Paralelo)

  • Debate sobre o padrão (T, error):
    • Um lado: valores zero/nil devem ser “úteis” mesmo quando error está definido.
    • Outro: na maioria das APIs reais, valores zero são sem sentido ou perigosos; a falta de tipos soma força convenções desajeitadas.