El Paquete context de Go en 2026: Cancelación, Timeouts y Preguntas de Entrevista
Guía completa del paquete context de Go: gestión de cancelación, timeouts y deadlines. Incluye preguntas frecuentes de entrevistas técnicas y mejores prácticas de producción.

El paquete context proporciona el mecanismo estándar en Go para transportar deadlines, señales de cancelación y valores asociados a solicitudes a través de las fronteras de API. Cada aplicación Go en producción utiliza context: los handlers HTTP, consultas a bases de datos, llamadas gRPC y workers en segundo plano dependen de este paquete para el apagado elegante y la gestión de timeouts.
El context representa uno de los temas más frecuentemente consultados en entrevistas de Go. Los entrevistadores esperan que los candidatos expliquen la propagación del context, demuestren un manejo correcto de la cancelación y eviten errores comunes como almacenar el context en estructuras.
La Interfaz Context y sus Cuatro Métodos
La interfaz Context define cuatro métodos que toda implementación debe satisfacer. Comprender estos métodos constituye la base para un uso efectivo del context.
type Context interface {
// Deadline returns the time when work should be canceled
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that closes when the context is canceled
Done() <-chan struct{}
// Err returns the reason why Done was closed
Err() error
// Value returns the value associated with key, or nil
Value(key any) any
}Done() retorna un channel de solo lectura. Cuando este channel se cierra, cualquier goroutine escuchándolo recibe la señal inmediatamente. El patrón <-ctx.Done() bloquea hasta que ocurre la cancelación. Err() explica la razón: ya sea context.Canceled cuando se cancela explícitamente, o context.DeadlineExceeded cuando un timeout o deadline ha expirado.
Creación de Contexts con Background y TODO
Dos funciones crean contexts raíz: context.Background() y context.TODO(). Ambas retornan contexts no nulos y vacíos que nunca se cancelan.
package main
import (
"context"
"log"
"net/http"
)
func main() {
// Background: the root context for your application
ctx := context.Background()
// Use it as parent for derived contexts
server := &http.Server{Addr: ":8080"}
// TODO: placeholder when context source is unclear
// Static analysis tools can flag context.TODO() for review
processLegacyData(context.TODO())
}
func processLegacyData(ctx context.Context) {
// Context parameter enables future cancellation support
log.Println("Processing data...")
}Background() sirve como padre para las solicitudes entrantes, funciones main y código de inicialización. TODO() actúa como un placeholder durante el refactoring cuando la fuente correcta del context aún no está determinada. Las herramientas de análisis estático pueden señalar el uso de TODO() para seguimiento posterior.
Cancelación con WithCancel y WithCancelCause
La cancelación manual permite que las goroutines padres señalen a las hijas que el trabajo debe detenerse. Este patrón aparece en pools de workers, tareas en segundo plano e implementaciones de apagado elegante.
package main
import (
"context"
"errors"
"fmt"
"time"
)
func worker(ctx context.Context, id int, results chan<- int) {
for {
select {
case <-ctx.Done():
// Check why cancellation occurred
if cause := context.Cause(ctx); cause != nil {
fmt.Printf("Worker %d stopped: %v\n", id, cause)
}
return
default:
// Simulate work
time.Sleep(100 * time.Millisecond)
results <- id * 10
}
}
}
func main() {
// WithCancelCause provides error context
ctx, cancel := context.WithCancelCause(context.Background())
results := make(chan int, 10)
// Start 3 workers
for i := 1; i <= 3; i++ {
go worker(ctx, i, results)
}
// Collect some results
for i := 0; i < 5; i++ {
fmt.Println("Result:", <-results)
}
// Cancel with a specific reason
cancel(errors.New("shutdown requested by user"))
// context.Cause retrieves the cancellation reason
time.Sleep(50 * time.Millisecond)
fmt.Println("Cause:", context.Cause(ctx))
}WithCancelCause, introducido en Go 1.20, proporciona información de error más rica que el simple WithCancel. La función context.Cause() recupera el error pasado a la función cancel, facilitando la depuración en sistemas complejos.
¿Listo para aprobar tus entrevistas de Go?
Practica con nuestros simuladores interactivos, flashcards y tests técnicos.
Timeouts y Deadlines para el Procesamiento de Solicitudes
Los handlers HTTP, llamadas a bases de datos y solicitudes a APIs externas siempre deben tener timeouts. El paquete context ofrece WithTimeout y WithDeadline para este propósito. Ambos crean contexts que se cancelan automáticamente cuando el tiempo expira.
package main
import (
"context"
"fmt"
"time"
)
// fetchFromAPI simulates an HTTP call
func fetchFromAPI(ctx context.Context, endpoint string) (string, error) {
// Simulate variable response time
responseTime := time.Duration(100+endpoint[0]%150) * time.Millisecond
select {
case <-time.After(responseTime):
return fmt.Sprintf("Response from %s", endpoint), nil
case <-ctx.Done():
return "", ctx.Err()
}
}
func main() {
// WithTimeout: relative duration
ctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)
defer cancel() // Always call cancel to release resources
result, err := fetchFromAPI(ctx, "api.example.com/users")
if err != nil {
if err == context.DeadlineExceeded {
fmt.Println("Request timed out")
} else {
fmt.Println("Request canceled")
}
return
}
fmt.Println(result)
// WithDeadline: absolute time
deadline := time.Now().Add(500 * time.Millisecond)
ctx2, cancel2 := context.WithDeadline(context.Background(), deadline)
defer cancel2()
result2, _ := fetchFromAPI(ctx2, "api.example.com/orders")
fmt.Println(result2)
}El patrón defer cancel() garantiza que los recursos se liberen incluso cuando la operación termina antes del timeout. Omitir esta llamada causa una fuga de recursos: la goroutine del timer del context permanece activa hasta que el timeout expira.
Valores Asociados a Solicitudes con WithValue
WithValue adjunta datos asociados a una solicitud a un context. Los casos de uso comunes incluyen identificadores de solicitud, tokens de autenticación y spans de trazado. La documentación oficial enfatiza el uso de tipos personalizados como claves para evitar colisiones.
package main
import (
"context"
"fmt"
"log"
"net/http"
)
// Define custom key types to avoid collisions
type contextKey string
const (
requestIDKey contextKey = "requestID"
userIDKey contextKey = "userID"
)
// middleware adds request ID to context
func requestIDMiddleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
requestID := r.Header.Get("X-Request-ID")
if requestID == "" {
requestID = generateRequestID()
}
// Create new context with request ID
ctx := context.WithValue(r.Context(), requestIDKey, requestID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
// handler retrieves values from context
func handler(w http.ResponseWriter, r *http.Request) {
requestID, ok := r.Context().Value(requestIDKey).(string)
if !ok {
requestID = "unknown"
}
log.Printf("[%s] Processing request", requestID)
fmt.Fprintf(w, "Request ID: %s", requestID)
}
func generateRequestID() string {
return fmt.Sprintf("req-%d", time.Now().UnixNano())
}
import "time"
func main() {
mux := http.NewServeMux()
mux.HandleFunc("/", handler)
http.ListenAndServe(":8080", requestIDMiddleware(mux))
}Los valores del context forman una cadena inmutable: cada llamada a WithValue crea un nuevo context que envuelve al padre. Las búsquedas recorren esta cadena, haciendo que los valores profundamente anidados sean más lentos de acceder. Solo se deben almacenar datos asociados a solicitudes, no configuración de la aplicación ni parámetros opcionales.
Utilidades WithoutCancel y AfterFunc
Go 1.21 agregó WithoutCancel para operaciones que deben completarse independientemente de la cancelación del padre, como tareas de limpieza. AfterFunc programa un callback cuando ocurre la cancelación del context.
package main
import (
"context"
"fmt"
"log"
"time"
)
func saveAuditLog(ctx context.Context, message string) error {
// Use WithoutCancel: audit logs must complete even if request canceled
cleanCtx := context.WithoutCancel(ctx)
// Simulate database write
select {
case <-time.After(50 * time.Millisecond):
log.Printf("Audit: %s", message)
return nil
case <-cleanCtx.Done():
// This branch never executes: WithoutCancel context is never canceled
return cleanCtx.Err()
}
}
func main() {
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
// AfterFunc: register cleanup when context ends
stop := context.AfterFunc(ctx, func() {
fmt.Println("Context ended, running cleanup")
})
// Perform operation
time.Sleep(50 * time.Millisecond)
// Cancel stops the AfterFunc from running if called before context ends
if stop() {
fmt.Println("Cleanup prevented")
}
cancel()
// Audit log completes despite parent cancellation
saveAuditLog(ctx, "Operation completed")
}WithoutCancel preserva los valores del context padre pero ignora su señal de cancelación. Este patrón es adecuado para logging, emisión de métricas y commits de base de datos que deben tener éxito independientemente del resultado de la solicitud.
Preguntas de Entrevista y Respuestas
La preparación para una entrevista técnica de Go requiere comprender los mecanismos internos del context y las mejores prácticas. Estas preguntas aparecen frecuentemente en entrevistas de empresas que utilizan Go para servicios backend. Para más preparación de entrevistas Go, consulta el módulo Preguntas de entrevista sobre Context Package.
¿Por qué el context debe ser el primer parámetro?
El equipo de Go estableció esta convención para hacer visible y consistente la propagación del context. Colocar el context primero señala a los lectores que la función respeta la cancelación. La biblioteca estándar sigue este patrón: http.Request.Context(), database/sql.QueryContext() y grpc.UnaryInterceptor esperan el context como primer argumento.
¿Qué sucede si se almacena el context en una estructura?
Almacenar el context rompe el modelo de ciclo de vida de las solicitudes. Una estructura puede sobrevivir a la solicitud para la que fue creada, causando operaciones que usan señales de cancelación obsoletas o pierden nuevos deadlines. La documentación del context advierte explícitamente contra este patrón.
// BAD: context stored in struct
type Service struct {
ctx context.Context // Never do this
}
// GOOD: pass context to each method
type Service struct{}
func (s *Service) Process(ctx context.Context, data []byte) error {
// Context flows through the call chain
return s.save(ctx, data)
}¿Cómo manejar el context en goroutines?
Las goroutines deben verificar ctx.Done() en su bucle principal o instrucción select. Ignorar el channel done crea fugas de goroutines: la función padre retorna, pero la goroutine continúa consumiendo recursos.
// Correct pattern for long-running goroutines
func processStream(ctx context.Context, stream <-chan Data) {
for {
select {
case <-ctx.Done():
log.Println("Shutting down processor")
return
case data, ok := <-stream:
if !ok {
return
}
handle(data)
}
}
}¿Cuándo usar context.TODO()?
Se debe usar TODO() durante el refactoring incremental al agregar soporte de context a código legacy. Marca ubicaciones que necesitan propagación correcta del context. El código de producción eventualmente debe reemplazar todas las llamadas TODO() con contexts reales provenientes de handlers de solicitudes o inicialización de la aplicación. Para más información sobre patrones de concurrencia en Go, consulta Go Concurrency: Goroutines and Channels.
Errores Comunes a Evitar
El uso incorrecto del context conduce a bugs sutiles que se manifiestan bajo carga o durante secuencias de apagado. Reconocer estos patrones previene incidentes en producción.
package main
import (
"context"
"time"
)
// MISTAKE 1: Ignoring context cancellation
func badWorker(ctx context.Context) {
for {
// Missing select on ctx.Done()
doExpensiveWork() // Never stops when context canceled
}
}
// MISTAKE 2: Not calling cancel
func leakyTimeout() {
ctx, _ := context.WithTimeout(context.Background(), time.Second)
// Timer goroutine leaks until timeout expires
_ = ctx
}
// MISTAKE 3: Using string keys for values
func collisionProne(ctx context.Context) context.Context {
// Different packages might use same string key
return context.WithValue(ctx, "userID", 123) // Bad
}
// CORRECT versions
func goodWorker(ctx context.Context) {
for {
select {
case <-ctx.Done():
return
default:
doExpensiveWork()
}
}
}
func properTimeout() {
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel() // Always release resources
_ = ctx
}
type userIDKey struct{}
func collisionSafe(ctx context.Context) context.Context {
return context.WithValue(ctx, userIDKey{}, 123) // Good
}
func doExpensiveWork() {}¡Empieza a practicar!
Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.
Puntos Clave para Usar Go Context en Producción
- Pasar el context como primer parámetro de las funciones, nunca almacenarlo en estructuras
- Siempre usar
defer cancel()después deWithTimeout,WithDeadlineoWithCancelpara prevenir fugas de recursos - Usar
WithCancelCauseycontext.Cause()para mejor depuración de errores en cadenas de cancelación complejas - Verificar
<-ctx.Done()en bucles de goroutines para permitir apagado elegante y prevenir fugas - Usar tipos de claves personalizados para
WithValuepara evitar colisiones entre paquetes - Aplicar
WithoutCancelpara operaciones de limpieza que deben completarse independientemente de la cancelación del padre - Preferir
context.Background()para inicialización de la aplicación ycontext.TODO()solo durante refactoring
¿Sabrías detectar el bug en Go?
Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Escrito por
Anthony Fillion-MailletFundador de SharpSkill
Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.
Actualizado el 29 de agosto de 2026
Etiquetas
Compartir
Artículos relacionados

Entrevista Tecnica de Go: Goroutines, Channels y Concurrencia
Guia completa con las preguntas mas frecuentes sobre goroutines, channels y concurrencia en Go. Incluye ejemplos de codigo listos para produccion, patrones de concurrencia y las respuestas que los entrevistadores esperan en 2026.

Top 25 preguntas de entrevista Go: guía completa para desarrolladores
Domina las entrevistas de Go con las 25 preguntas más frecuentes. Goroutines, channels, interfaces y patrones de concurrencia con ejemplos de código.

Manejo de Errores en Go: Patrones, Wrapping y Mejores Practicas para 2026
Guia completa sobre el manejo de errores en Go: errores centinela, tipos personalizados, wrapping con fmt.Errorf, errors.Is y errors.As. Patrones profesionales para entrevistas tecnicas y desarrollo en produccion.