Site Reliability Engineering is about treating operations as a software problem.

Imagine you’re running a popular online service. Traffic surges, bugs appear, servers crash – it’s chaos. Traditionally, you’d have a separate "operations" team scrambling to fix things. SRE flips this: the engineers building the service are also responsible for keeping it running reliably. They use software engineering principles to automate toil, manage incidents, and ensure the service meets its performance targets.

Let’s see SRE in action. Consider a simple web service that needs to respond to requests within 200ms.

package main

import (
	"fmt"
	"net/http"
	"time"
)

func handler(w http.ResponseWriter, r *http.Request) {
	// Simulate some work
	time.Sleep(150 * time.Millisecond)
	fmt.Fprintf(w, "Hello, SRE!")
}

func main() {
	http.HandleFunc("/", handler)
	fmt.Println("Starting server on :8080")
	http.ListenAndServe(":8080", nil)
}

This Go program is a basic web server. The handler function simulates a task that takes 150 milliseconds. If this server is running and receives a request, it will respond. But what if it doesn’t? SREs define Service Level Objectives (SLOs) – specific, measurable targets for reliability. For our web service, an SLO might be: "99.9% of requests served within 200ms."

To monitor this, SREs deploy tools. Prometheus is a common choice for collecting metrics. We’d instrument our Go application to expose request latency.

package main

import (
	"fmt"
	"net/http"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promauto"
)

var (
	requestLatency = promauto.NewHistogramVec(prometheus.HistogramOpts{
		Name: "http_request_duration_seconds",
		Help: "Latency of HTTP requests.",
		Buckets: prometheus.LinearBuckets(0.01, 0.05, 20), // Buckets for 10ms, 60ms, 110ms, ...
	}, []string{"path"})
)

func handler(w http.ResponseWriter, r *http.Request) {
	start := time.Now()
	// Simulate some work
	time.Sleep(150 * time.Millisecond)
	duration := time.Since(start).Seconds()
	requestLatency.WithLabelValues(r.URL.Path).Observe(duration)
	fmt.Fprintf(w, "Hello, SRE!")
}

func main() {
	http.HandleFunc("/", handler)
	fmt.Println("Starting server on :8080")
	http.ListenAndServe(":8080", nil)
}

Now, when a request comes in, its duration is recorded. Prometheus scrapes these metrics. An alert might fire if, for instance, the 95th percentile latency exceeds 200ms for more than 5 minutes. This is the core of SRE: proactive monitoring against defined SLOs.

The problem SRE solves is the inherent tension between shipping new features (velocity) and maintaining stability (reliability). Without SRE, teams often face:

  • Toil: Repetitive, manual tasks that take engineering time away from value-adding work. Think manual deployments, password resets, or restarting services.
  • "Not My Job" Syndrome: Developers build features, operations keeps them running, but neither feels fully responsible when things break.
  • Slow Incident Response: When an outage occurs, it’s unclear who owns the fix, leading to prolonged downtime.

SRE addresses these by:

  • Error Budgets: If your SLO is 99.9% availability, you have a 0.1% "error budget." This budget can be spent on planned downtime for maintenance or even unplanned outages. Once the budget is spent, feature development stops until reliability improves. This forces a balance.
  • Automation: SREs aim to eliminate toil through automation. If a task is done more than a few times manually, it’s a candidate for automation. This could be anything from automated deployments to self-healing systems.
  • On-Call Rotation: SREs are often on-call, responding to incidents. However, the goal is to reduce pages through better design and automation. A common SRE principle is that an engineer should not be paged for something they can’t fix or prevent.
  • Postmortems: After an incident, a blameless postmortem is conducted to understand the root cause and identify preventative actions. The focus is on system improvements, not individual blame.

The levers you control as an SRE are directly tied to the SLOs and the system’s architecture. You influence:

  • Resource Allocation: Ensuring sufficient CPU, memory, and network bandwidth for the service.
  • Deployment Strategies: Implementing canary releases or blue-green deployments to minimize the impact of new code.
  • Monitoring and Alerting: Configuring the right metrics, thresholds, and alert routing.
  • Incident Management: Defining playbooks and communication channels for outages.
  • Capacity Planning: Predicting future resource needs based on growth trends.

A common pitfall is focusing solely on "keeping the lights on" and neglecting the software engineering aspect. True SRE involves writing code to solve operational problems. For example, if you find yourself repeatedly running kubectl scale deployment my-app --replicas=3 during peak load, an SRE would write a script or an autoscaler configuration to do this automatically based on CPU utilization. This script is the "software solution" to the operational problem of scaling.

The next concept you’ll likely encounter is the distinction between an SLO and a Service Level Indicator (SLI) – the actual metric you’re measuring.

Want structured learning?

Take the full Reliability Engineering (SRE) course →