All posts

Go Production Best Practices

Essential Go production best practices covering project structure, dependency management, concurrency safety, and deployment strategies.

Go Production Best Practices

Go's simplicity makes it easy to write code. Writing production-grade Go requires discipline. Here are the practices that matter.

Project Structure

Follow the standard layout:

cmd/
  server/main.go       # Entry points
internal/
  handler/             # HTTP handlers
  service/             # Business logic
  repository/          # Data access
pkg/
  middleware/          # Reusable middleware

The internal/ directory prevents external packages from importing your business logic.

Context Propagation

Pass context.Context as the first parameter to every function that does I/O:

func (s *OrderService) CreateOrder(ctx context.Context, req CreateOrderRequest) (*Order, error) {
    if err := s.validate(req); err != nil {
        return nil, fmt.Errorf("validate order: %w", err)
    }
    return s.repo.Insert(ctx, order)
}

This enables request cancellation, timeouts, and tracing to propagate through your entire call chain.

Concurrency Safety

  • Use channels for communication, mutexes for state
  • Never start goroutines without a shutdown mechanism
  • Use `errgroup` for coordinated goroutine management
import "golang.org/x/sync/errgroup"

g, ctx := errgroup.WithContext(ctx)
g.Go(func() error {
    return fetchInventory(ctx)
})
g.Go(func() error {
    return fetchPricing(ctx)
})
if err := g.Wait(); err != nil {
    return fmt.Errorf("parallel fetch: %w", err)
}

Dependency Injection

Avoid global variables. Inject dependencies through constructors:

type Server struct {
    userService  *UserService
    orderService *OrderService
    logger       *slog.Logger
}

func NewServer(us *UserService, os *OrderService, l *slog.Logger) *Server {
    return &Server{userService: us, orderService: os, logger: l}
}

Production Essentials

  • Structured logging with slog or zerolog
  • Graceful shutdown handling SIGTERM/SIGINT
  • Health check endpoints that verify dependencies
  • Rate limiting to protect against abuse
  • Error tracking with Bugsly to catch panics and unexpected errors before your users report them

These patterns keep Go applications maintainable and reliable as they grow.

Try Bugsly Free

AI-powered error tracking that explains your bugs. Set up in 2 minutes, free forever for small projects.

Get Started Free