Skip to main content

Command Palette

Search for a command to run...

๐Ÿš€ Senior Go Interview Question: The Graceful Shutdown Trap

Updated
โ€ข2 min readโ€ขView as Markdown
๐Ÿš€ Senior Go Interview Question: The Graceful Shutdown Trap

If you are interviewing for a Senior Backend role in Go, you will almost certainly be asked about concurrency and lifecycle management.

Here is a classic question that perfectly separates mid-level developers from seniors:

"How do you implement a graceful shutdown for an HTTP server that also processes background jobs, ensuring zero data loss, handling in-flight requests, and preventing goroutine leaks?"

๐Ÿชค The Trap

A mid-level developer will usually suggest using os/signal to catch SIGTERM and calling server.Close().

Why it fails: Close() immediately closes all active net.Listeners and connections. In-flight requests will be dropped, and background workers might be killed mid-transaction, leading to corrupted data or inconsistent states.

๐Ÿ’ก The Senior Approach

A senior engineer knows that graceful shutdown is about cooperative cancellation using the context package.

Here is the checklist of what your answer should cover:

  1. Signal Handling: Use signal.NotifyContext to listen for SIGINT and SIGTERM.

  2. Context Propagation: Pass the context down to all background workers and database calls. When the signal is caught, the context is automatically canceled.

  3. HTTP Server Shutdown: Use server.Shutdown(ctx) instead of Close(). This stops accepting new connections but waits for active requests to finish.

  4. Timeout Control: Wrap the shutdown context with a timeout (e.g., context.WithTimeout) so the app doesn't hang forever if a request is stuck.

  5. WaitGroups: Use sync.WaitGroup to track background workers. The main goroutine should wait for the WaitGroup to hit zero before exiting the process.

๐Ÿ’ป The Golden Snippet

// Create a context that listens for OS signals
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt, syscall.SIGTERM)
defer stop()

// ... start server and workers ...

<-ctx.Done() // Block until signal is received
log.Println("Shutting down gracefully...")

// Give outstanding requests 10 seconds to complete
shutdownCtx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
defer cancel()

if err := srv.Shutdown(shutdownCtx); err != nil {
    log.Fatalf("Server forced to shutdown: %v", err)
}

wg.Wait() // Wait for background workers to finish
log.Println("Server exited properly")

๐Ÿ—ฃ๏ธ Over to you

How does your team handle graceful shutdowns in production? Do you use any specific libraries or patterns for background workers? Let me know in the comments! ๐Ÿ‘‡

๐Ÿš€ Want to test your skills?
Iโ€™m building an interactive platform to help developers practice real-world backend scenarios and compare their level with others. If you're preparing for interviews, come try out our Go/Backend quizzes here: https://skillhacker.io