Propagar o mesmo context por toda uma aplicação pode fazer sentido em alguns casos, mas não é uma prática ideal ou recomendada para todas as situações. O context deve ser utilizado cuidadosamente e com objetivos específicos.
Nesse post, vamos explorar os cenários e cuidados ao se utilizar propagação de context.
O package context
O package context é parte da biblioteca padrão do Go e foi introduzido para resolver problemas relacionados ao gerenciamento de deadlines e cancelamentos em goroutines.
Ele fornece uma maneira de passar informações importantes, como limites de tempo e sinais de cancelamento, por meio de chamadas de funções e entre diferentes partes do código.
Para entender um pouco mais sobre esse package, recomendo a leitura dos posts:
- O que é e como utilizar o package context
- Como implementar uma função utilizando context
- Onde e qual context utilizar
Benefícios de propagar o mesmo context
Antes de entendermos os benefícios específicos de propagar o context, é importante destacar o impacto que isso tem na manutenção e escalabilidade de aplicações modernas.
Em sistemas complexos, onde várias operações assíncronas precisam ser coordenadas, o uso adequado do context pode significar a diferença entre um código claro e responsivo e um sistema propenso a erros ou vazamentos de recursos.
Agora, vejamos os principais benefícios que essa prática proporciona.
- Cancelamento eficiente: Permite cancelar operações em cascata quando uma requisição é cancelada pelo cliente ou um deadline é atingido.
- Gerenciamento de recursos: Facilita o encerramento adequado de goroutines e a liberação de recursos, evitando vazamentos de memória.
- Comunicação consistente: Permite passar informações como tokens de autenticação e valores configuráveis de uma parte do código para outra.
Cuidados ao se propagar o mesmo context
Embora o context seja uma ferramenta poderosa, seu uso inadequado pode introduzir problemas, como vazamentos de goroutines, confusão na passagem de valores e cancelamentos indevidos.
Abaixo, vamos ver os principais pontos de atenção ao se propagar context.
Contexto inadequado para determinada operação Se a operação downstream (como uma transação de banco de dados) tiver requisitos diferentes, como um tempo limite maior ou valores distintos, criar um novo context derivado faz mais sentido. Exemplo:
- A requisição gRPC pode ter um timeout de 5 segundos, mas a transação no banco de dados pode precisar de um timeout menor, como 2 segundos.
Evitar ropagação excessiva de cancelamentos O cancelamento do context em uma operação de alto nível pode encerrar prematuramente operações downstream que poderiam ser concluídas de forma independente ou reexecutadas. Exemplo: Cancelar uma requisição HTTP de um cliente não deveria, necessariamente, abortar uma operação de escrita no banco de dados que precisa garantir consistência.
Evitar acoplamento indevido Passar o mesmo context para todos os componentes pode criar dependências indesejadas. Cada componente deve receber apenas os valores e configurações necessárias.
Valores desnecessários no contexto Valores armazenados no context de uma camada superior podem ser irrelevantes para outras camadas (como o banco de dados). Isso pode levar a confusão e uso inadequado do context.
Práticas recomendadas
Agora que já passamos pelos benefícios e cuidados, para que sua aplicação tire o melhor do package context, vamos olhar algumas boas práticas.
Evite armazenar context em structs O context é projetado para ser passado explicitamente entre funções e não deve ser armazenado em campos de structs.
Criar contextos derivados Use funções como context.WithTimeout ou context.WithValue para criar novos contextos baseados no contexto recebido:
func processRequest(ctx context.Context) error {
// Timeout mais curto para o banco de dados
dbCtx, cancel := context.WithTimeout(ctx, 2*time.Second)
defer cancel()
return executeDatabaseTransaction(dbCtx)
}
Delimitar context por responsabilidade Divida o contexto conforme as responsabilidades. Por exemplo:
- O contexto da chamada gRPC controla a operação geral.
- Cada subsistema (banco, cache, serviços externos) usa um contexto derivado com configurações específicas.
Evite depender de valores do context sempre que possível Prefira passar explicitamente os valores necessários para funções e métodos. Use context.WithValue apenas para metadados como IDs de rastreamento, não para dados de negócio.
Sempre verifique o estado do context Antes de executar tarefas demoradas, valide se o context já foi cancelado.
func processRequest(ctx context.Context) error {
for {
select {
case <-ctx.Done():
fmt.Println("Context foi cancelado:", ctx.Err())
return
default:
fmt.Println("Processando...")
// ... código
}
}
}
Documente o uso do context Deixe claro na documentação das funções qual tipo de contexto elas esperam e como ele será usado.
Conclusão
Propagar o mesmo context para todas as partes de uma aplicação pode levar a acoplamento excessivo, cancelamentos prematuros e uso inadequado de recursos. Use o context de forma deliberada:
- Propague-o onde valores, deadlines e cancelamentos precisam ser consistentes.
- Crie contextos derivados para operações com requisitos diferentes.
- Quando propagado, valide se o
contextestá fechado/cancelado.
Essa abordagem mantém o código modular, previsível e mais fácil de manter.
Se você gostou desse conteúdo, confira nossos planos de assinatura para acesso a conteúdos exclusivos e aprofundados, além de acesso a todos os cursos e imersão.
Até a próxima!
Faça parte da comunidade!
Receba os melhores conteúdos sobre Go, Kubernetes, arquitetura de software, Cloud e esteja sempre atualizado com as tendências e práticas do mercado.
Livros Recomendados
Abaixo listei alguns dos melhores livros que já li sobre GO.

