Preface: Why Your Go Service Is Not a Safe Harbor
You wrote perfect Go code. It's fast, concurrent, uses goroutines and channels. You ran unit tests, integration tests, checked coverage. But when you rolled out the release on Friday evening, everything crashed. Why? Because Chaos lives in production. The network lags, disks fill up, databases go down, and dependent microservices go into infinite timeout.
Welcome to the real world. This is where Chaos Engineering comes to the rescue — a discipline that teaches us to deliberately break a system to understand its weak points. In this article, we'll explore how to apply Chaos Engineering for Go applications. You'll learn how to write simple yet effective experiments without turning your production into ruins. Ready? Let's go!
What Is Chaos Engineering and Why Does a Go Developer Need It?
Chaos Engineering is not just "break and run." It's a scientific approach to improving fault tolerance. The core idea: instead of waiting for a failure, you provoke it in a controlled environment. You formulate a hypothesis ("if the database goes down, the service will return an error but won't hang"), run an experiment, and observe the result.
For Go, this approach is especially relevant. Go was originally designed for building reliable, scalable systems. But even the coolest code won't save you from network issues or sudden goroutine death. Chaos Engineering helps verify how your middleware, retry logic, and circuit breakers perform under real-world conditions.
Toolkit for Chaos Engineering in Go
In the Go world, there are several powerful libraries and approaches for injecting chaos. We'll look at the most popular ones:
gofault— a simple library for injecting delays and errors.chaostoolkit— a framework for orchestrating experiments (has Go drivers).- Custom middleware — the most flexible and controllable approach.
In this article, we'll go the route of creating our own middleware. This will give you a complete understanding of the process and the ability to adapt it to your needs.
Practice: Creating Chaos Middleware for an HTTP Server in Go
Imagine you have a simple HTTP server in Go. We'll add middleware that simulates various failures: delays, errors, panics.
Step 1: Basic Structure
Let's start by defining the chaos configuration structure:
package chaos
import ( "math/rand" "net/http" "time")
// Config contains settings for chaos injection.type Config struct { // Probability - probability (0.0 - 1.0) of a failure occurring. Probability float64 // DelayRange - delay range in milliseconds [min, max]. DelayRange [2]int // ErrorCode - HTTP status code to be returned on error. ErrorCode int // Panic - if true, the middleware will cause a panic. Panic bool}
// DefaultConfig returns a default configuration.func DefaultConfig() Config { return Config{ Probability: 0.1, // 10% of requests will be affected DelayRange: [2]int{100, 500}, ErrorCode: http.StatusInternalServerError, Panic: false, }}Step 2: Writing the Middleware Itself
Now let's create a function that returns the middleware. It will randomly decide what to do with the request.
// Middleware returns an HTTP middleware for chaos injection.func Middleware(cfg Config) func(http.Handler) http.Handler { rng := rand.New(rand.NewSource(time.Now().UnixNano()))
return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { /