SRE Simplicity
Why Simplicity Is a Reliability Strategy
Here’s a paradox: Complex systems are less reliable than simple systems.
You might think the opposite. If you add redundancy, monitoring, failover logic, and self-healing, aren’t you making the system more reliable?
Sometimes yes. But usually, you’re just adding accidental complexity that introduces new failure modes.
Simple = fewer things that can break.
Accidental vs. Essential Complexity
Essential complexity is inherent to the problem you’re solving.
Example: A distributed database system must handle network partitions. This is complex because the problem is complex, not because you’re a bad engineer.
Accidental complexity is introduced by your choices, and it’s often invisible.
Examples of accidental complexity:
- Over-engineered architecture. A startup with 10 engineers building like Google with Kubernetes, service meshes, and distributed tracing. You’re solving problems you don’t have yet.
- Too many technologies. A company using 15 different databases (PostgreSQL, MongoDB, Cassandra, Redis, DynamoDB, Elasticsearch, etc.) when 2–3 would suffice. Each new tool brings:
- Another thing to learn
- Another thing to monitor
- Another thing that can break
- Another on-call rotation
- Premature optimization. Optimizing for scale you don’t have yet. Building a system for 1 million requests/sec when you get 100 RPS.
- Clever, hard-to-understand code. A developer writes a 5-line function that does something clever but nobody else can understand it. When it breaks, it takes days to debug.
- Unclear ownership. A system where “everyone owns it, and therefore nobody owns it.” When it breaks, nobody knows who to call.
- Inconsistent patterns. One team uses async queues, another uses synchronous RPC, another uses pub/sub. Developers learning each system spend time on the transport mechanism, not the business logic.
The Cost of Accidental Complexity
Complexity is expensive:
Development cost:
- Harder to onboard new engineers.
- Longer debugging. Complex systems hide bugs in dark corners.
- Complexity creates more ways to fail.
Operational cost:
- Harder to monitor. More things to observe = more metrics, more dashboards, more false alerts.
- Harder to respond. When things break, there are more possible causes.
- Higher on-call burden. More complex systems fail more often.
Business cost:
- Slower feature velocity. Teams spend time managing infrastructure instead of shipping.
- Higher staffing. You need more SREs and platform engineers to manage the complexity.
- Technical debt. Complexity accumulates; each new feature adds another layer.
Simplicity Strategies
1. Start Simple, Add Complexity Only When Necessary
Your first deployment strategy: SSH into a server and run git pull && restart-service. Boring? Yes. Reliable? Also yes.
As you grow (10 servers, 100 servers), you automate. But you start simple.
Real example: Instagram started with a monolith on Ubuntu servers. They deployed code by hand. As they scaled, they built infrastructure incrementally, not all at once.
2. Own Your Dependencies
Every external tool (database, message queue, monitoring system) adds complexity:
- Learning curve
- Operational burden
- Upgrade headaches
- Lock-in
Ask for each new tool: “Is this solving a problem we actually have, or a problem we think we might have?”
Bad reason to add a tool: “It’s trendy.”
Good reason: “Our current solution is at its limit, and we’ve measured that this new tool solves our bottleneck.”
3. Consistency Over Cleverness
If you have 5 ways to do the same thing, you’ve added 5× the complexity.
Example: Service-to-service communication
Bad:
- Service A uses REST APIs
- Service B uses gRPC
- Service C uses message queues
- Service D uses WebSockets
- Service E uses GraphQL
Good:
- All services use gRPC or REST (pick one standard)
- Consistent error handling
- Consistent retries
- Consistent timeouts
Benefit: New engineers learn one pattern, not five. You write one debugging tool instead of five. You have one set of runbooks.
4. Observability Over Instrumentation
You don’t need logs of every variable and function call. You need meaningful logs.
Bad logging:
2024-01-15 10:23:45.123 DEBUG: Entering function get_user_by_id
2024-01-15 10:23:45.124 DEBUG: Checking cache
2024-01-15 10:23:45.125 DEBUG: Cache miss
2024-01-15 10:23:45.126 DEBUG: Querying database
2024-01-15 10:23:45.128 DEBUG: Got result
2024-01-15 10:23:45.129 DEBUG: Returning to caller
This is noise. It doesn’t help you debug production issues.
Good logging:
2024-01-15 10:23:45.123 action=get_user user_id=12345 cache_hit=false db_latency_ms=2 status=success
One line with structured data. You can query it, aggregate it, alert on it.
5. Explicit Over Implicit
If something is happening, someone should be able to understand why by reading the code.
Bad (implicit):
def process_order(order):
if order.customer.vip: # If VIP, ??
order.priority = HIGH
else:
order.priority = NORMAL
# ... do something with priority
Why do VIP customers get HIGH priority? Only the author knows and the next developer guesses.
Good (explicit):
def process_order(order):
# VIP customers have guaranteed 1-hour fulfillment SLA,
# so we prioritize their orders to meet the SLA.
# See: https://wiki/orders/vip-sla
if order.customer.vip:
order.priority = HIGH
else:
order.priority = NORMAL
Now developers understand the why.
Measuring Simplicity
How do you know if your system is too complex?
Red flags:
- Onboarding time. New engineers take >2 weeks to deploy a simple change.
- Debugging time. A typical production issue takes >1 hour to root-cause.
- Deployment anxiety. Teams are afraid to deploy because “something might break.”
- Toil accumulation. More and more time spent on maintenance vs. features.
- Tool sprawl. More tools, more dashboards, more alerts (but not better visibility).
- Incident frequency. More incidents = more complexity introducing bugs.
Metrics to track:
- Time to onboard: How long until a new engineer can deploy?
- MTTR (Mean Time to Recovery): How long to fix a typical incident?
- Deploy frequency: Can teams deploy multiple times per day?
- SRE toil percentage: What % of SRE time is spent on maintenance vs. improving reliability?
If these are getting worse, your complexity is hurting you.
Key Takeaways
- Simple systems are more reliable. Fewer things to break, easier to understand, easier to debug.
- Accidental complexity is the real enemy. Not the essential complexity of the problem.
- Start simple, scale complexity only when needed. Don’t over-engineer from day one.
- Consistency beats cleverness. One way to do something is better than five ways.
- Measure the cost of complexity. If onboarding takes weeks, if incidents are frequent, your system is too complex.