Skip to main content

Track 6: Production Excellence

Operating distributed systems at scale requires specialized skills beyond just building them. Building a distributed system is like designing a race car; operating one in production is like keeping that race car running at 200 mph while replacing the engine parts mid-race. The skills are related but fundamentally different, and many organizations learn this the hard way when their beautifully architected system crumbles under the weight of real-world operational complexity.
Track Duration: 28-36 hours
Modules: 5
Key Topics: Observability, Chaos Engineering, SRE, Incident Management, Capacity Planning

Module 27: Observability at Scale

The Three Pillars

Distributed Tracing

Advanced: Tail-Based Sampling

At massive scale (millions of spans/sec), you cannot afford to store every trace.
  • Head-Based Sampling: The decision to sample is made at the start of the request (e.g., “Sample 1% of all requests”).
    • The Flaw: It might discard the 0.01% of requests that actually had an error or a 5-second latency spike.
  • Tail-Based Sampling: The sampling decision is delayed until the entire trace has been collected.
    • Spans are buffered in a collector (like the OpenTelemetry Collector).
    • Once the trace is complete, a policy is applied: “Keep if status is Error OR latency > 500ms OR method is POST”.
    • Result: You capture 100% of “interesting” traces while only paying for 1% of “boring” (successful/fast) ones.

27.2.1 Advanced: Trace Reconstruction & Causality

At Staff/Principal level, you must understand the math behind how traces are reconstructed from a sea of independent spans.

1. W3C Trace Context (The Standard)

The industry has standardized on the W3C Trace Context. It consists of two headers:
  • traceparent: Propagates the trace-id and parent-id.
    • Format: 00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01
  • tracestate: Propagates vendor-specific metadata (e.g., Datadog or New Relic specific tags).

2. Trace Reconstruction (The DAG Problem)

When you view a trace in Jaeger or Honeycomb, the backend has to perform a Distributed Join to reconstruct the tree:
  1. Gathering: Collect all spans sharing the same trace-id.
  2. Topological Sort: Use the parent-id to arrange spans into a Directed Acyclic Graph (DAG).
  3. Clock Correction: Since different nodes have different clocks, the backend must adjust span start times so that a child span never starts before its parent (Causality preserved).

3. Tracing through Queues (The “Link” Pattern)

Standard tracing assumes a parent-child relationship (synchronous RPC). But for asynchronous messages (Kafka/SQS), we use Links.
  • The Problem: If a consumer processes 100 messages in one batch, who is the “parent”?
  • The Solution: The trace has multiple “Links” to the 100 originating traces, allowing you to see the fan-out without breaking the original trace’s structure.
Staff Tip: When implementing tracing, never use a “global” trace context variable in your code. Always pass the Context object explicitly (in Go) or use ThreadLocal storage (in Java) to avoid trace leakage between concurrent requests.

27.3: Context Propagation Implementation

The hardest part of distributed tracing is ensuring the trace context flows through every boundary: HTTP, gRPC, message queues, thread pools, and async callbacks.

1. HTTP Propagation

2. Message Queue Propagation (Kafka/SQS)

The trickiest case - async messages break the parent-child relationship.
Why Links instead of Parent?
  • A consumer might process messages from 100 different producers in one batch
  • Using parent-child would create 100 parallel traces
  • Links allow you to see “this processing was triggered by these 100 messages”

3. Thread Pool / Async Context Propagation

Context can be lost when work is submitted to a thread pool.

4. Context Propagation Matrix

5. Common Pitfalls


Staff Tip: When designing an observability stack, ensure your Trace Context propagates through asynchronous boundaries like message queues (Kafka headers) and background jobs (sidekiq/celery), otherwise your traces will have “gaps” that make debugging impossible.

Metrics and Alerting

SLIs, SLOs, and Error Budgets


Module 28: Chaos Engineering

Netflix’s Approach

Designing Chaos Experiments

Failure Injection Types


Module 29: SRE Practices

Error Budgets and Release Velocity

On-Call Best Practices

Progressive Rollouts


Module 30: Advanced Resiliency Patterns

At the Staff/Principal level, reliability isn’t just about “fixing bugs”—it’s about designing architectures that are inherently resistant to failure.

1. Static Stability

A system is Statically Stable if it continues to operate in its “steady state” without needing to make changes during a dependency failure.
  • The Problem: Reactive autoscaling. If AZ-1 fails, AZ-2 and AZ-3 try to scale up. But if the control plane (Kubernetes/EC2 API) is also failing, they can’t scale, and the whole system crashes. This is the “calling 911 when the phone lines are down” problem — your recovery mechanism depends on the same infrastructure that just failed.
  • The Solution: Over-provisioning. Run AZ-1, AZ-2, and AZ-3 at 50% capacity each. If one AZ fails, the remaining two are already at 100% capacity and can handle the full load immediately without calling any external APIs. Yes, this costs more. The question is whether that cost is less than the cost of an outage — for most revenue-critical services, it is.
  • Key Principle: Avoid “Control Plane” dependencies in the “Data Plane” recovery path. Your system should be able to survive failures using only what it already has in memory and on disk, without needing to make new API calls to orchestration systems.

2. Cell-Based Architectures

Instead of one giant “monolith” cluster, you split your infrastructure into many independent Cells.
  • Definition: A Cell is a complete, self-contained instance of the service (App + DB + Cache).
  • Blast Radius: If Cell A has a “poison pill” request or a hardware failure, only the 5% of users in Cell A are affected. The other 95% of users in other cells are completely isolated.
  • Scaling: To double capacity, you don’t scale the cells; you just add more cells.
  • Used By: AWS (Lambda, DynamoDB), Salesforce, and Facebook.

3. Dependency Isolation (The Bulkhead Pattern)

Just like a ship is divided into watertight compartments (bulkheads), a distributed system should isolate its dependencies.
  • Implementation: Use separate thread pools or connection pools for different downstream services.
  • Benefit: If Service A becomes slow, its thread pool fills up, but Service B’s thread pool remains free, allowing the rest of the system to function.

Module 31: Incident Management

Incident Response Framework

Postmortem Culture


Module 32: Capacity Planning

Load Testing

Capacity Modeling

Autoscaling Strategies


Advanced: Cluster Scheduling Internals (Borg & DRF)

In a modern distributed environment, you don’t manage individual servers; you manage a Cluster. A Scheduler (like Google’s Borg or Kubernetes’ kube-scheduler) is responsible for deciding where your code runs.

The Bin Packing Problem

At its core, scheduling is a “Multidimensional Bin Packing” problem. You have containers with varying CPU/RAM needs and nodes with varying capacities.

1. Dominant Resource Fairness (DRF)

In a cluster, users need multiple resources (e.g., User A needs high CPU, User B needs high RAM). How do you allocate resources fairly? DRF is the standard algorithm.
  • Definition: DRF calculates the “dominant share” for each user (the resource they need the most of relative to the cluster’s total capacity) and tries to equalize these shares.

2. Priority and Preemption

Not all jobs are equal. Borg introduced two main categories:
  • Prod (Production): Low latency, high availability (e.g., Search, Gmail).
  • Non-Prod (Batch): High throughput, latency-insensitive (e.g., Log processing, ML training).
Preemption: If a high-priority “Prod” job needs resources, the scheduler will kill (evict) lower-priority “Non-Prod” jobs to make room. This allows Google to run clusters at 90%+ utilization.
Staff Tip: When designing a system, differentiate between Hard Constraints (the app cannot run without this) and Soft Constraints/Affinities (the app should run here for better performance). Over-constraining your scheduler leads to “unschedulable” tasks and wasted resources.

Key Interview Questions

Systematic approach:
  1. Check dashboards first
    • Which services show elevated latency?
    • When did it start? Correlate with deployments
    • Scope: All users or specific segment?
  2. Use distributed tracing
    • Find slow traces
    • Identify which span is slowest
    • Look for patterns (specific service, DB, external API)
  3. Drill down
    • Check that service’s metrics (CPU, memory, connections)
    • Check dependencies (DB latency, cache hit rate)
    • Check for new error types in logs
  4. Common culprits
    • Database slow queries
    • Cache miss spike
    • Connection pool exhaustion
    • Garbage collection
    • Noisy neighbor (shared resources)
    • External API degradation
  5. Mitigation while investigating
    • Scale up if resource-bound
    • Enable circuit breaker
    • Failover to backup
Short-term mitigation:
  • Analyze page patterns
  • Suppress non-actionable alerts
  • Add secondary on-call
Medium-term fixes:
  • Improve runbooks for faster resolution
  • Automate common remediations
  • Add better monitoring to prevent issues
Long-term solutions:
  • Fix underlying reliability issues
  • Add circuit breakers, retries
  • Improve capacity planning
  • Push back on missing error budget
Process changes:
  • Track alert metrics (pages per week)
  • Review every page in team meeting
  • Goal: < 2 pages per on-call shift
  • Escalate if consistently exceeded

Capstone: Interview Preparation

Practice System Design Problems

Apply everything you’ve learned:
  1. Design Uber’s dispatch system
    • Real-time location tracking
    • Matching drivers to riders
    • Surge pricing
  2. Design Stripe’s payment processing
    • Exactly-once payments
    • Idempotency
    • Saga pattern for complex transactions
  3. Design Netflix’s video streaming
    • CDN architecture
    • Adaptive bitrate
    • Regional failover
  4. Design Twitter’s timeline
    • Fan-out on write vs read
    • Celebrity problem
    • Real-time updates

Congratulations!

You’ve completed the Distributed Systems Mastery course. You now have the knowledge to:
  • ✅ Explain consensus protocols (Raft, Paxos) in interviews
  • ✅ Design systems with appropriate consistency guarantees
  • ✅ Choose between replication strategies
  • ✅ Implement distributed transactions correctly
  • ✅ Build and operate systems at massive scale
  • ✅ Debug complex distributed systems issues
Next steps: Practice! Do mock interviews, build projects, and read the papers referenced throughout this course.